feat(editor): host-registered sidebar modules, rail control, panel layout (0.3.8) - #21
feat(editor): host-registered sidebar modules, rail control, panel layout (0.3.8)#21lightninglu10 wants to merge 5 commits into
Conversation
EditorHost gains sidebarModules: entries ({ id, label, icon, Panel }) join
the MediaSidebar rail as host:<id> tabs after the built-in categories, with
host-owned labels and panels rendered in the sidebar panel area. Module
panels mount on first activation and stay mounted across tab switches and
authoritative snapshot installs so in-flight host work (job polling,
proposal lists) survives.
FreeCutEditorSurface also accepts an apiRef that receives a
FreeCutEditorSurfaceApi — openSidebarModule(id) selects a registered
module's tab and opens the panel (unregistered ids fail closed), and
closeSidebar() closes the panel — so the host can surface a module exactly
when host-side work needs attention.
Publishes as @quantfive/freecut-editor-surface 0.3.6.
|
Starting initial review of |
There was a problem hiding this comment.
The ownership boundary is sound: hosts provide registered module metadata and panel components, while the editor owns the rail/tab lifecycle. The host:<id> identity is applied consistently across rendering, persisted-layout coercion, authoritative snapshot reconciliation, and the imperative surface API.
I found no actionable issues in this change. The feature is cohesive and ready to merge.
Reviewed `ad5b52ca`...`8ed1231a`. Diff collection and GitHub posting were handled by the automation.|
Review head |
…layout
EditorHost gains sidebarRail: an explicit ordered list of rail tabs, as
built-in ids and `host:<id>` module ids. Anything omitted is hidden, so one
field both reorders the rail and suppresses built-ins a host does not want.
Capability gating still runs first — a rail can only subtract from and reorder
what the capabilities already allow, never add to it — and ids matching no
available tab are dropped, as are repeats after the first. A rail that matches
nothing falls back to the default rather than leaving the editor with no
navigation.
The resolution lives in one place (hostRailTabIds) that both the sidebar and
the runtime read, replacing runtime.ts's parallel tabVisibleInHostMode copy of
the same capability rules. That is what keeps a suppressed tab from surviving
an authoritative snapshot install, and it makes the post-install fallback the
rail's first entry instead of a hardcoded 'media' — a host is free to drop
media from its rail. openSidebarModule now fails closed for any tab the rail
does not show, not just for unregistered ids.
Module panels receive { active, collapsed, width } instead of { active }, so a
panel can pause work while it is not selected and adapt to a sidebar resize
without measuring the DOM. Widening the props is source-compatible with an
existing ({ active }) => … panel.
Publishes as 0.3.7: 0.3.6 is already on npm from the release-0-3-6 branch
without the sidebar-module work, and npm versions are immutable.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…idebar-modules # Conflicts: # packages/freecut-editor/package.json
|
Heads up for re-review: the approval above predates the last two commits. Since then this PR gained
Version moved 0.3.6 -> 0.3.7 because 0.3.6 is already on npm from "Verify and package baseline" failed once on the first push and passed on re-run with no code change — it is the build-twice-and- |
codepress-main published 0.3.7 from its own release commit while this branch was open, and that build carries none of the sidebar-module work. npm versions are immutable, so this cannot ship as 0.3.7 — the publish workflow would skip a version that already exists. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Rebumped 0.3.7 -> 0.3.8. While this branch was open, Note for whoever merges: this is the second time a release commit on CodePress is currently pinned to 0.3.7, which installs fine but has none of this. It repins to 0.3.8 after this merges and publishes. |
What
Lets a host own the editor's left sidebar: register its own modules into the rail, decide the rail's order, suppress built-ins it does not want, and give module panels the layout context they need.
Modules.
EditorHost.sidebarModulesentries ({ id, label, icon, Panel }) join the MediaSidebar rail ashost:<id>tabs, with host-owned labels and host-rendered panel content. Icons and panels cross the package boundary as React components — react/react-dom are peer dependencies, so host and surface share one React copy.Rail control.
EditorHost.sidebarRaildeclares the exact tabs to show, in the exact order, as built-in ids andhost:<id>module ids. Anything omitted is hidden, so one field both reorders the rail and suppresses built-ins. Capability gating still runs first — a rail can only reorder and subtract from what the capabilities already allow, never add to it. Ids matching no available tab are dropped, repeats collapse to the first mention, and a rail matching nothing at all falls back to the default rather than leaving the editor with no navigation.Imperative handle.
FreeCutEditorSurfaceaccepts an optionalapiRefreceiving aFreeCutEditorSurfaceApi:openSidebarModule(id)selects a module's tab and opens the panel,closeSidebar()closes it. This lets the host surface a module exactly when host-side work needs attention — e.g. auto-opening a transcription control when an agent proposal is waiting on approval.Behavior
{ active, collapsed, width }rather than{ active }, so a panel can pause work while it is not selected and adapt to a sidebar resize without measuring the DOM. Widening the props is source-compatible with an existing({ active }) => …panel.EditorSidebarTabineditor-workspaces.tsgains ahost:${string}member so persisted-layout coercion recognizes host tabs.sidebarModules/sidebarRailare unchanged.One resolver, two readers
runtime.tspreviously carried its own copy of the capability rules (tabVisibleInHostMode) to decide whether a persistedactiveTabsurvives a snapshot install. WithsidebarRailin play that copy would drift — a suppressed tab could survive an install and sit there with no rail button to leave it.Both readers now share one resolver,
hostRailTabIds(src/features/editor/host/sidebar-rail.ts): the sidebar renders from it, the runtime tests against it. Two consequences worth reviewing:'media'— a host may drop media from its rail entirely.openSidebarModulefails closed for any tab the rail does not show, not just for unregistered ids. Opening a tab with no rail button would strand the user, and the next snapshot install would reset it anyway.Validation
media-sidebar-modules.test.tsxcases against the real MediaSidebar: rail registration, panel rendering,{ collapsed, width }propagation across resize and collapse, snapshot-install survival, unregistered-tab reset, apiRef open/close, plus sixsidebarRailcases (declared order, built-in suppression, capability denial, unknown-id and repeat dropping, empty-match fallback, rail-head fallback on snapshot install, and fail-closedopenSidebarModulefor a suppressed module).npm run check,check:boundaries,check:deps-contracts,check:edge-budgets,check:unused-exports, and the fallow changed-health gate all pass. MediaSidebar keeps its pre-existing tab-orchestrator complexity suppression; the module logic stays extracted intoHostModulePanels/visibleRailCategories, and the rail resolution moved out entirely intosidebar-rail.ts.npm run package:editor-surface+npm run test:editor-surface:consumerpass against the packed 0.3.8 tarball, confirming the hand-writtenindex.d.tsshipssidebarRail,EditorSidebarTab, andEditorSidebarModulePanelProps.Release
Bumps the package to 0.3.8. Both 0.3.6 and 0.3.7 are already published to npm from
codepress-mainrelease commits without any of this work, and npm versions are immutable — the publish workflow skips a version that already exists, so this cannot reuse either.codepress-mainis merged into the branch (twice, as it kept releasing) to stay current.Merging to
codepress-mainpublishes 0.3.8 via the trusted-publishing workflow. First consumer: CodePress repins to 0.3.8 and moves its route-owned Transcribe panel into this rail (separate PR in quantfive/codepress, which cannot install until 0.3.8 is on npm).