Revert "Prevent WebUI extension loading stalls"

This reverts commit bf2046ff87.
This commit is contained in:
Alessandro 2026-07-18 13:32:52 +02:00
parent d1d48bc9c0
commit 9e2a1b6324
4 changed files with 14 additions and 43 deletions

View file

@ -1,19 +0,0 @@
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[1]
def test_extension_discovery_requests_are_serialized() -> None:
source = (PROJECT_ROOT / "webui/js/extensions.js").read_text(encoding="utf-8")
assert "let extensionRequestQueue = Promise.resolve();" in source
assert "extensionRequestQueue = request.catch(() => {});" in source
assert source.count("await requestExtensionPaths(") == 2
def test_component_placeholder_is_removed_after_failure() -> None:
source = (PROJECT_ROOT / "webui/js/components.js").read_text(encoding="utf-8")
finally_block = source.rsplit("} finally {", maxsplit=1)[1]
assert "targetElement.querySelector(':scope > .loading')?.remove();" in finally_block

View file

@ -37,10 +37,8 @@
- `scrollModal(id)` scrolls inside the top modal's `.modal-scroll`.
- Keep extension loader cache keys and extension point names stable for plugins.
- HTML extension loading turns discovered HTML files into `<x-component>` tags; JavaScript extensions must export a default function.
- Serialize frontend extension-discovery API requests so a page with many extension points cannot exhaust embedded Chromium request resources.
- `<x-component>` loading must process component `style`, `script`, and stylesheet-link assets only once, even when a component keeps its scoped `<style>` inside `<body>`.
- Every `<x-component>` instance must await cached module-load promises before markup is appended so Alpine bindings only run after imported stores exist.
- Every `<x-component>` loading placeholder must be removed after success or failure; a failed asset must never leave a perpetual shimmer.
- Frontend extension hooks such as `confirm_dialog_after_render` and `get_tool_message_handler` must preserve their mutable context contracts.
- Sanitize or safely render user/model-provided HTML and markdown.
- Convert standard TeX delimiters before Markdown parsing without touching inline or fenced code. Keep thought-card math rendering local to the agent-message handler rather than adding math flags to generic process-step or key/value rendering.

View file

@ -170,6 +170,12 @@ export async function importComponent(path, targetElement) {
targetElement.appendChild(deferred);
}
// Remove loading indicator
const loadingEl = targetElement.querySelector(':scope > .loading');
if (loadingEl) {
targetElement.removeChild(loadingEl);
}
// // Load any nested components
// await loadComponents([targetElement]);
@ -179,7 +185,6 @@ export async function importComponent(path, targetElement) {
console.error("Error importing component:", error);
throw error;
} finally {
targetElement.querySelector(':scope > .loading')?.remove();
// Release the lock when done, regardless of success or failure
importLocks.delete(lockKey);
}

View file

@ -20,7 +20,6 @@ import * as cache from "./cache.js";
const JS_CACHE_AREA = "frontend_extensions_js(extensions)(plugins)";
const HTML_CACHE_AREA = "frontend_extensions_html(extensions)(plugins)";
let extensionRequestQueue = Promise.resolve();
export const API_EXTENSION_EXCLUDED_ENDPOINTS = new Set([
"/api/load_webui_extensions",
@ -31,17 +30,6 @@ export function clearCache() {
cache.clear(HTML_CACHE_AREA);
}
function requestExtensionPaths(extensionPoint, filters) {
const request = extensionRequestQueue.then(() =>
api.callJsonApi(`/api/load_webui_extensions`, {
extension_point: extensionPoint,
filters,
}),
);
extensionRequestQueue = request.catch(() => {});
return request;
}
/**
* Call all JS extensions for a given extension point.
*
@ -72,10 +60,10 @@ export async function loadJsExtensions(extensionPoint) {
if (cached != null) return cached;
/** @type {LoadWebuiExtensionsResponse} */
const response = await requestExtensionPaths(extensionPoint, [
"*.js",
"*.mjs",
]);
const response = await api.callJsonApi(`/api/load_webui_extensions`, {
extension_point: extensionPoint,
filters: ["*.js", "*.mjs"],
});
/** @type {JsExtensionImport[]} */
const imports = await Promise.all(
response.extensions.map(async (path) => ({
@ -179,11 +167,10 @@ export async function importHtmlExtensions(extensionPoint, targetElement) {
}
/** @type {LoadWebuiExtensionsResponse} */
const response = await requestExtensionPaths(extensionPoint, [
"*.html",
"*.htm",
"*.xhtml",
]);
const response = await api.callJsonApi(`/api/load_webui_extensions`, {
extension_point: extensionPoint,
filters: ["*.html", "*.htm", "*.xhtml"],
});
let combinedHTML = "";
for (const extension of response.extensions) {
const path = normalizePath(extension);