Studio Shell

The application around the editing component: the menubar, the lesson model, panel navigation, view modes, identity and realm assertion, the target-silo selector, the source-normalization pipeline, and the diagnostics surface. Verified against POC build 213 (20260910T123348).

The shell is instructor-studio/js/app.js, with api.js for authenticated fetch and canvas.js for mounting component assemblies. For the editing component itself see Editor Component; for the programmatic surfaces the shell exposes see Formats and Contracts.

Contents

This table is the canonical record of every user-visible control in the Studio. Both this guide and the instructor tutorials cite it rather than paraphrasing the source independently — the two document sets drifted apart once already, and a single owned vocabulary is the fix.

File

Label Shortcut Behavior
Current lesson — Static row showing the open lesson, or “No lesson selected”
New… mod+N Empty lesson
Generate… — Generate Lesson wizard
Open… mod+O Realm/folder browser
Save mod+S Writes the authoring tree
Save as… mod+shift+S Location modal, including New folder…
Publish… — Publish wizard — disabled while the lesson is unsaved or modified since the last save
Assign… — Assign wizard — gated on a challenge-bearing publication
Reference Library & Spaces… — Spaces modal

Edit

Label Shortcut
Undo mod+Z
Redo ⌘⇧Z on Mac, Ctrl+Y elsewhere
Cut mod+X
Copy mod+C
Paste mod+V

Modifier glyphs are platform-resolved at render time (⌘/⇧/⌥ on Mac, Ctrl+/Shift+/Alt+ elsewhere). These items drive the focused component through its public API; the editor also binds its own equivalents (see Editor Component).

Lesson

Whole-lesson ĀYŌDÈ Intelligence. Each label’s verb is recomputed every time the menu opens — “Generate” when the target panel does not exist, “Update” when it does.

Label Target panel
Generate/Update summary panel summary
Generate/Update learning objectives panel objectives
Generate/Update key takeaways panel takeaways

See ĀYŌDÈ Intelligence Operations.

Library

Label Behavior
Upload resource… File upload into the Reference Library
Upload resource from URL… Fetch-and-upload by URL

See the reference library.

Control Values
View mode toggle Instructor View · Student View
Target silo Development · Production
Identity chip The signed-in username; opens the identity dialog

The lesson model

A lesson is a tree of typed panels, nested up to five levels (MAX_PANEL_LEVELS). Every panel owns its MyST source and carries a frontmatter title.

panel.type Nav chip Student-visible?
myst Content Yes
mission Mission Yes
challenge Challenge Yes — the student attempts it
file File No

“Panel” is the category for student-visible tabs. Content, Mission and Challenge are all Panels — the student sees or attempts them. A File deliberately is not: it is bundled, compiled or executed behind the scenes and never rendered to the student directly. The editor toolbar badge mirrors the focused tab’s chip, using the same four-way vocabulary.

Challenge panels additionally carry an assessment model (panel.challenge) joined to published shells by an authored, minted-once challengeRef; see Challenges and Grading.

Panel navigation

The left pane lists the panel tree. Each row carries, left to right: outdent ‹, a drag handle ☰, indent ›, an outline number (2.1.3), the panel title, its kind chip, and a delete button. A + Panel button sits below the tree.

Affordance Behavior
Click Select the panel
Double-click Rename in place
‹ / › Outdent / indent. ‹ is hidden at the outermost level; › is hidden at the innermost level and on a first child, so an indent can never orphan a row
☰ drag Reorder; a panel cannot be dropped into its own descendants
Delete Removes the panel and its children
+ Panel Menu: New Content · New Mission
Right-click Menu: New challenge · Open… · Save · Save as…

New challenge is gated on the selected panel (not the clicked row) having a mission ancestor — where a mission counts as its own ancestor — and the new challenge is appended to that mission’s children. Challenges therefore cannot be created at the top level or nested inside one another.

The pane is resizable by a pointer-capture drag handle between 12 and 22 rem, with a 320 px content floor reserved for the editor. The width persists in localStorage and survives the ‹‹/›› drawer collapse, which also makes the handle inert. Dragging can never blur the editor, so normalization cannot fire mid-drag. (Student Lab mirrors the affordance on its progress pane: 16–34 rem, 384 px floor.)

View modes

Instructor View shows the authoring surface. Student View switches the mounted components into student mode — the editor collapses to preview-only, and challenge panels present their response controls instead of their options pane. Entering student mode re-fetches components with no-cache and deals a fresh choice pool, so a stale student view cannot persist across a switch.

Identity and realm assertion

Sign-in is Cognito SRP, direct against the user pool, from the identity chip. The dialog shows the decoded claims; a quick-switch affordance moves between saved identities without a full sign-in. Saved identities are silo-independent in principle — only the session bound to one is not.

Every API call carries a bearer token plus an asserted realm EID. Two behaviors are worth knowing when reading error paths:

API errors surface the envelope’s userMessage, with the deepest embedded error detail preferred over the outermost wrapper, and never a raw JSON dump.

Target silo

A menubar selector chooses Development or Production as the target silo. The choice persists, and the selector is colour-highlighted so the active silo is unmistakable.

Switching silos discards state loaded from the old one. The Studio holds the richest state of the four POCs, and all of it is silo-bound:

Clearing the token also clears the active identity key as a side effect, since there is no session against the new silo yet. Clearing the Reference Library selection then forces the Spaces modal, which is the same branch boot takes on a first load — so the post-switch experience matches first-run rather than needing its own path.

Running locally, the Caddyfile proxies silo-scoped paths (/__silo/development/..., /__silo/production/...) so switching the selector keeps the two silos’ data genuinely isolated.

Spaces

Three persisted realm selections, set from File ▸ Reference Library & Spaces… through a realm-tree picker:

Space Role
Reference Library Where uploaded reference material lives, and the RAG corpus AI operations draw on
Authoring Space Where the authoring lesson tree is saved
Publishing Space Where published lessons and their assets are written

Each persists in localStorage under its own key. With no Reference Library set the modal is forced — its close is a no-op until a library is actually picked — which is how both first boot and a silo switch guarantee the Studio has somewhere to read from.

Source normalization

An ordered, idempotent, single-flight pipeline normalizes panel sources before they are persisted. Two transforms run, in this order:

  1. glossary-termify — writes explicit {term}`…` roles into content-panel sources.
  2. remove-redundant-whitespace — applies the editor component’s wsEdits oracle. Despite the name this also unwraps hard-wrapped paragraphs and strips embedded HTML; see what the tidy pass actually removes.

The order matters: term-ification runs first because freshly written {term} role bodies parse as inline code, which the whitespace pass is required to leave alone.

Triggers: editor focus loss, panel switch, before every save, before every publish, and the explicit menu commands. Challenge panels, mission panels and the Glossary panel are excluded from the focus-loss trigger.

Invariants:

Consequence: persisted and published sources are always normalized and always carry their {term} roles. Saving a hard-wrapped document, or one containing inert HTML, deliberately changes its bytes and therefore its content hash relative to what the author typed.

Generate Lesson wizard

File ▸ Generate… chains three real AI configurators into a drafted lesson: objectives (synchronous) → outline (async) → lesson text (async) → straight into the MyST editor.

Step Collects
1. Select Authoring Space Target realm for the drafted lesson
2. Curriculum Alignment Region → subregion → standard authority → standard, drilled to a leaf
3. Structure Depth (introductory / intermediate / advanced), scope, duration in minutes, and which components to include — objectives, warm-up, instruction, practice, assessment
4. Lesson Objectives Generated knowledge/reasoning/performance objectives, for review
5. Lesson Outline Generated outline, for review
6. Lesson Text Generated lesson, loaded into the editor

Depth maps to a grade band for the prompts: introductory → middle school, intermediate → high school, advanced → undergraduate.

Stage Configurator
Objectives curriculum-standards-to-knowledge-reasoning-performance-v2
Outline knowledge-reasoning-performance-to-lesson-outline-v2
Lesson lesson-outline-to-lesson-v2

Version badge and diagnostics

The bottom-right badge is the only on-page confirmation of which build is running, and it is assembled from four CSS custom properties:

| Property | Set by | Meaning | |—|—|—| | --poc-version | css/styles.css | build N · timestamp — bumped on every change | | --poc-js | app.js, at module start | ` · js — proves the module executed, independently of the build string | | –poc-local | app.js, when overridden | · LOCAL COMPONENTS | | –poc-cdn` | the content-delivery probe | CDN state |

Because --poc-version comes from CSS, the build number still shows if the module fails to execute — and the absence of ` · js` is then the diagnosis.

Gesture Effect
Click Copies the structured boot/error log to the clipboard and flashes the badge; reports the line count, or an explicit failure if the clipboard is unavailable
Right-click Opens a one-item menu: Use local components for testing, with a ✓ when active

The local-component override is URL state only — ?useLocalComponentForTesting=1, deliberately never persisted — so the right-click menu toggles it by rewriting the URL and reloading. Navigation is the toggle. There is no fallback in either direction: a component that cannot load from its selected source is a hard, visible failure rather than a silent downgrade.

window.__studioDev exposes headless hooks (panel trees, the assembled publish package, editor state) for verifying publish invariants without driving the UI.