diff --git a/tests/test_coordinator_page.py b/tests/test_coordinator_page.py index 350cb6a4..63a8e7b3 100644 --- a/tests/test_coordinator_page.py +++ b/tests/test_coordinator_page.py @@ -503,7 +503,14 @@ def test_coordinator_de_globalized_to_pane_factory(): index_html = (base / "index.html").read_text(encoding="utf-8") assert "function createCoordinatorPane(root, wsId, opts) {" in coord_js - assert "window.createCoordinatorPane = createCoordinatorPane;" in coord_js + # Step 5e.0: coordinator.js is a real ES module the shell imports — the bare + # `export` is its only seam. No `window.*` bridge (unlike interactive.js, + # whose classic ui/static/app.js still needs the global): both the console + # shell and the standalone bootstrap import the factory. + assert "export { createCoordinatorPane };" in coord_js + assert "window.createCoordinatorPane" not in coord_js, ( + "no dead window bridge — both consumers import the factory" + ) assert "function destroy() {" in coord_js, "a pane must have a teardown path" # ws_id is a constructor arg now, not read off ; lookups are root-scoped. assert "document.documentElement.dataset.wsId" not in coord_js @@ -513,7 +520,13 @@ def test_coordinator_de_globalized_to_pane_factory(): # No page-global collision points (multi-instance safe). for gone in ("window.coordSend", "window.coordCloseSession", "window.onLoginSuccess"): assert gone not in coord_js, f"de-globalized: {gone} must be gone" - # Standalone page = one pane filling the body; the inline close onclick is gone. + # Standalone page = one pane filling the body, bootstrapped by a MODULE that + # imports the factory (a classic eager IIFE would run before the deferred + # coordinator module loaded it); the inline close onclick is gone. + assert ' - - diff --git a/turnstone/console/static/index.html b/turnstone/console/static/index.html index 2472694c..742f5aa0 100644 --- a/turnstone/console/static/index.html +++ b/turnstone/console/static/index.html @@ -4296,9 +4296,9 @@ - + above and drives window.TS_APP.boot(). It imports the coordinator + + interactive panes (both ES modules), so neither is diff --git a/turnstone/shared_static/shell.js b/turnstone/shared_static/shell.js index 7226862c..53ecce69 100644 --- a/turnstone/shared_static/shell.js +++ b/turnstone/shared_static/shell.js @@ -21,10 +21,12 @@ import { PaneManager, ShellPane } from "./pane.js"; import { mountRail, mountManage } from "./rail.js"; -// The interactive pane is a real ES module (step 5a) — import it directly. The -// coordinator pane stays a classic legacy script read off `window.*` below; this -// asymmetry is the incremental "pulled by the adopting pane" modernization. +// Both conversational panes are real ES modules the shell imports directly. The +// interactive pane lives beside us in /shared (step 5a); the coordinator pane is +// at an absolute /static path (step 5e.0 lifted it off `window.*`), so it imports +// by URL. import { createInteractivePane } from "./interactive.js"; +import { createCoordinatorPane } from "/static/coordinator/coordinator.js"; function make(tag, className, text) { const node = document.createElement(tag); @@ -232,11 +234,9 @@ function mountShell() { glyph: "◆", }); pane.onMount = function () { - if (typeof window.createCoordinatorPane === "function") { - this._ctl = window.createCoordinatorPane(this.bodyEl, id, { - onClose: () => pm.close(pane.id), - }); - } + this._ctl = createCoordinatorPane(this.bodyEl, id, { + onClose: () => pm.close(pane.id), + }); }; pane.onActivate = function () { if (this._ctl && !this._connected) {