diff --git a/.changeset/estepper-layout-module.md b/.changeset/estepper-layout-module.md new file mode 100644 index 0000000..4584b94 --- /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, 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 fdcf846..ebc76a2 100644 --- a/src/web/e-stepper/src/EStepperHostPlugin.tsx +++ b/src/web/e-stepper/src/EStepperHostPlugin.tsx @@ -20,6 +20,15 @@ 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, 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_KEY = Symbol.for("sourceacademy.e-stepper.layout"); +const layout: PaneLayout = ((globalThis as Record)[LAYOUT_KEY] ??= {}); + interface State { steps: EStepperStep[]; profile?: SyntaxProfile; @@ -65,8 +74,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);