From 3b620713ef7e316c6d4e2860fe059c4e476df371 Mon Sep 17 00:00:00 2001 From: henz Date: Sun, 11 Oct 2026 12:50:24 +0800 Subject: [PATCH 1/2] e-stepper: keep the layout for the page, not the plugin: a Run makes a new plugin Co-Authored-By: Claude Sonnet 5.5 --- .changeset/estepper-layout-module.md | 5 +++++ src/web/e-stepper/src/EStepperHostPlugin.tsx | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 .changeset/estepper-layout-module.md diff --git a/.changeset/estepper-layout-module.md b/.changeset/estepper-layout-module.md new file mode 100644 index 0000000..a268df9 --- /dev/null +++ b/.changeset/estepper-layout-module.md @@ -0,0 +1,5 @@ +--- +"@sourceacademy/web-e-stepper": patch +--- + +e-stepper: the arrangement of the panes and the divider survive a Run (the host makes a new plugin on every Run, so they are kept for the page, not for the plugin) diff --git a/src/web/e-stepper/src/EStepperHostPlugin.tsx b/src/web/e-stepper/src/EStepperHostPlugin.tsx index fdcf846..b5d6169 100644 --- a/src/web/e-stepper/src/EStepperHostPlugin.tsx +++ b/src/web/e-stepper/src/EStepperHostPlugin.tsx @@ -20,6 +20,12 @@ import EStepperView, { type PaneLayout } from "./EStepperView"; /** The side-content tab id used by the host to show/hide the e-stepper tab. */ const TAB_ID = "e-stepper"; +/** + * The user's arrangement of the panes. Module-level, not per plugin: the host terminates the + * conduit and makes a new plugin on every Run, and the divider is to stay where the user put it. + */ +const layout: PaneLayout = {}; + interface State { steps: EStepperStep[]; profile?: SyntaxProfile; @@ -65,8 +71,6 @@ export class EStepperHostPlugin implements IPlugin { const subscribe = (listener: () => void) => this.subscribe(listener); const getState = () => this.getState(); - // Kept here, not in the view: a run may mount the view anew, and the user's divider stays. - const layout: PaneLayout = {}; const cseDiagram: ICseDiagramService | undefined = hostServices?.cseDiagram; function EStepperTab() { const state = useSyncExternalStore(subscribe, getState); From 00d6826b48a2301dd3a56097aaf64cd610eb97a8 Mon Sep 17 00:00:00 2001 From: henz Date: Sun, 11 Oct 2026 13:07:57 +0800 Subject: [PATCH 2/2] e-stepper: keep the layout on globalThis: the bundle is a factory called again on every Run Co-Authored-By: Claude Sonnet 5.5 --- .changeset/estepper-layout-module.md | 2 +- src/web/e-stepper/src/EStepperHostPlugin.tsx | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.changeset/estepper-layout-module.md b/.changeset/estepper-layout-module.md index a268df9..4584b94 100644 --- a/.changeset/estepper-layout-module.md +++ b/.changeset/estepper-layout-module.md @@ -2,4 +2,4 @@ "@sourceacademy/web-e-stepper": patch --- -e-stepper: the arrangement of the panes and the divider survive a Run (the host makes a new plugin on every Run, so they are kept for the page, not for the plugin) +e-stepper: the arrangement of the panes and the divider survive a Run (the host makes a new plugin on every Run, and calls the bundle's factory again, so they are kept for the page on `globalThis`) diff --git a/src/web/e-stepper/src/EStepperHostPlugin.tsx b/src/web/e-stepper/src/EStepperHostPlugin.tsx index b5d6169..ebc76a2 100644 --- a/src/web/e-stepper/src/EStepperHostPlugin.tsx +++ b/src/web/e-stepper/src/EStepperHostPlugin.tsx @@ -21,10 +21,13 @@ import EStepperView, { type PaneLayout } from "./EStepperView"; const TAB_ID = "e-stepper"; /** - * The user's arrangement of the panes. Module-level, not per plugin: the host terminates the - * conduit and makes a new plugin on every Run, and the divider is to stay where the user put it. + * The user's arrangement of the panes, kept on `globalThis` for the page. Neither the plugin nor this + * module will do: the host terminates the conduit and makes a new plugin on every Run, and the + * bundle is wrapped in a factory (see `wrap.mjs`) that the host calls again each time, so even a + * module-level variable is new on every Run. The divider is to stay where the user put it. */ -const layout: PaneLayout = {}; +const LAYOUT_KEY = Symbol.for("sourceacademy.e-stepper.layout"); +const layout: PaneLayout = ((globalThis as Record)[LAYOUT_KEY] ??= {}); interface State { steps: EStepperStep[];