feat(shell): tabbed central panel with pinned home for embedding hosts#60
Merged
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
atelier-preview | 5de946d | Commit Preview URL Branch Preview URL |
Jul 21 2026, 09:34 PM |
Adds an opt-in centralPanel option to createAtelier: - centralPanel.tabs renders the central tab strip and keeps multiple content views open (browser-style: open navigates the active tab in place, newTab appends, close lands on the neighbor) - centralPanel.home pins a host extension as the permanent first tab (never closable/draggable; compacts to its icon when inactive) - documents.open gains a newTab option; new instance.views.open() places host extension views (e.g. folder views) as central content tabs - extension manifests gain placement/hidden so hosts can declare central-capable, menu-hidden views - central_view_activated event lets routing hosts mirror the active central view into the URL Defaults are unchanged: without centralPanel the central island remains the single-document slot with no tab strip. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review-driven hardening of the central-tabs embedding API:
- centralPanel is now a mode-discriminated union ({ mode: "document" } |
{ mode: "tabs", home? }) so a home cannot be configured without tabs
- the tab-model rules (canHost/normalize/place/closeFallback) move into a
CentralSlotBehavior strategy object with one implementation per mode
(src/shell/central-slot-behavior.ts), replacing scattered boolean
branches; the behavior parameter is required so no call site can
silently fall back to document-slot semantics
- views.open: instanceKey renamed to instanceId (it is the id reported
back on views and events); "central-home" is rejected for non-home
extensions; completion now waits for the view to become active
- manifest placement is enforced everywhere: add-view menus, drag&drop,
and programmatic side-panel opens
- a pinned home forces the Files view into the sidebar instead of
dropping it under the default landing mode
- document_closed now reports the tab that actually activates (neighbor,
or null for the home) instead of the last view
- central_view_activated fires in both modes and resets cleanly when the
central panel empties; reopening an existing tab merges state instead
of resetting it; extension_opened no longer fires for no-op resyncs
- isPinned is validated on persisted state
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
samuelstroschein
force-pushed
the
central-panel-embedding
branch
from
July 20, 2026 17:40
bc7926b to
2f892cd
Compare
Member
Author
|
Rebased onto main (post checkpoint-removal) and applied the review round:
838 tests green, lint clean. 🤖 Generated with Claude Code |
Two additions to the slots family, shaped by current host requirements: - navbarCenter replaces the built-in top-bar title (hosts render e.g. a workspace breadcrumb; the read-only chip is preserved) - centralTabStrip is a headless render-prop for the central tab strip: Atelier keeps the tab rules (pinned home, selection, closing, new-tab) and exposes them as AtelierTabStripContext; the host owns the chips. Custom strips forgo the built-in drag-reorder. The built-in strip remains the default; hosts that pass neither slot are unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A custom centralTabStrip now renders outside the island's bordered container, on the app background — the host's chips float over the canvas and the island starts below them. The built-in strip is unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The built-in tab row now renders outside the island's bordered container
for every panel — chips sit on the app background with the island below,
matching the islands design ('its own chip group over its own island').
The row loses its inside-chrome divider and edge padding so chips align
flush with the island edge. Applies to side panels and the central strip
alike; custom central strips already floated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The visible view's chip is always clearly selected — white island-card look (bg-panel + border) in both active and keyboard-focused states — matching the floating-strip design. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The toolbar aligns to the content column (same centering as the ProseMirror body, block-select text flush with the H1) instead of rendering as a full-width bar with its own border and background. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The add-view trigger stays visible with a muted 'No views available' item instead of vanishing (e.g. after the bundled History removal left side panels with nothing addable). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Side-panel chips now match the central spec (12.5px/500 idle, 600 active, 12px padding) and the ✕/+ affordances share the same sizes. The add-view trigger always renders, disabled with a 'No views available' item when the registry has nothing placeable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Closing the last chip of a side panel collapses the island instead of removing the view — the view survives, so reopening the panel restores it (and Files canonicalization no longer silently undoes the close). Also resolves the active-chip border: border-transparent in the base classes fought the state border color at equal specificity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Symmetric vertical centering (the pt offset crowded the open pill against the island edge), the open block-select pill uses the quiet hover tone, and the whole bar rests at 70% opacity until hovered, focused, or holding an open menu. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New semantic tokens from the design review: canvas-level hover (bg-hover-canvas), a quaternary text/icon tier for metadata and quiet affordances, bg-active for tonal chips, and the comp's secondary-action border/shadow recipe. Applied to side-panel chips, ✕/+ affordances, top-bar toggles, and the formatting bar's icon tones. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The comp tints the trigger only on hover; the open state now reads through the rotated chevron and the popup, not a filled pill. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
min-w-0 on the host-strip wrapper so many tabs shrink/scroll inside the panel instead of widening it past the viewport. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The cursor is usually still over the trigger right after opening, so the hover tint read as a stuck pill. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The trigger's -ml-2.25 alignment margin pulled it left of its overflow-x-auto scroll container's content edge, which clips — the hover/open pill rendered with its left side sliced flat. The margin moves to the scroll container so alignment is preserved without putting a child outside the scrollport. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Back to the text/csv view pattern: an edge-to-edge row at panel-header height with a hairline bottom border, controls left, copy right — no content-column centering, no negative-margin alignment, no rest-state fade. The floating-bar experiments kept producing clipping and stuck-pill artifacts; the plain header row avoids the whole class. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A | B | + with A active produced A | C | B; new tabs now join at the end regardless of which tab is active, browser-style. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Removes the document/tabs mode union: the central panel is always browser-style tabs with a pinned home. Hosts that configure no home get the Files view as the pinned home tab — same rules, zero config; a custom home banishes Files to the sidebar. - centralPanel options lose the mode discriminant (breaking). - DOCUMENT_SLOT_BEHAVIOR and every tabs/document branch deleted; createCentralSlotBehavior is the single strategy. - documents.startNew reveals a hidden Files home and starts the draft there instead of silently creating an untitled file. - Panel interactions activate the view they happen in, not whatever was active (fixes programmatic focus racing tab reveals). - Compact pinned tabs gain an accessible name. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- The active chip's permanent selection ring is gone — white card + border is the active treatment; keyboard focus keeps focus-visible. - Central document tabs show their file-type glyph, browser-favicon style, instead of the generic extension icon. - + opens the pinned home view (a view opener, read-safe) instead of instantly creating a markdown file; the headless context.newTab gets the same semantics. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The top-bar toggles edge-aligned their icons inside square buttons, so the hover pill looked off-center. And the central tab strip loses its + entirely — new tabs come only from explicit intent (⌘-click, newly created documents); the headless context drops newTab. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Refines the single tabs primitive per host feedback: homeless hosts (FlashType) get NO pinned center tab — the Files view lives in the left sidebar (open by default on fresh state), documents open as central tabs, and closing the last tab shows the empty state. A configured home (lixray) behaves as before. filesViewMode is now fully derived; stored panel sizes always win over defaults. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds AtelierOptions.filesView so hosts can feed un-imported "watched" entries into the bundled Files view and lazily import them on first interaction: - filesView.watchEntries subscribes with the currently expanded directories (root always included) and is resubscribed whenever the expanded set changes; pushed entries merge into the tree as source: "watched" with lix rows winning path collisions and missing ancestor directories synthesized. - filesView.resolveFileForInteraction imports a watched path to a canonical lix file before open and rename; resolving null is a graceful no-op and a newer interaction supersedes an older resolve. - Watched rows keep the existing non-destructive rendering contract: no delete, no drag-and-drop, watched directories stay read-only. - Hosts without the option see zero behavior change. This unblocks FlashType deleting its ~5,400-line files-extension fork that exists only to surface watcher-backed disk files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adding Files from any side panel routes to its canonical left-sidebar home instead of being silently stripped by canonicalization. Closing a side chip removes the view — the last one leaves the panel's empty state with its add-view affordance; only the ensured left Files view (which removal would resurrect) collapses the island instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The right panel's add-view menu offered Files, but canonicalization pins it to the left sidebar — the item was a silent no-op (or a surprising left-sidebar reveal). JSON manifests learn the placement field, react definitions propagate it, and the bundled Files manifest declares ["left"], so only menus that can honor it list it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The continuous canonicalization that resurrected Files made its ✕ a no-op whenever it shared a panel. Files now seeds once into the fresh left-sidebar state and behaves like any view: chips remove it, the left + menu offers it back, and removing a panel's last view also closes the island. No resurrection, no special cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With the resurrection logic gone there is nothing pinning Files to the left — both side + menus offer it, and adding it to the right panel opens a second tree there. The files→left redirect is removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
samuelstroschein
added a commit
to opral/flashtype
that referenced
this pull request
Jul 21, 2026
* feat!: adopt atelier's single central-tabs primitive Bumps submodule/atelier to 4396919 (central-panel-embedding): the central panel is now always browser-style tabs with a pinned home. FlashType configures no custom home, so the Files view becomes the pinned home tab — documents open as content tabs beside it and closing the last content tab lands back on Files. - drop filesViewMode: "sidebar" (derived by atelier now; a no-op) - closeFileViews closes by path via the new documents.close(path) so deleting a file also closes its background tab (the tree lives in the pinned home tab, so the doc view is rarely active during deletion) - drop the revisions wiring deleted upstream by atelier#59 (checkpoint removal): history view props, checkpointBranchId, atelier_history builtin id; local types keep the optional diff-viewer props compilable - e2e: ensureFilesViewOpenInLeftPanel -> ensureFilesHomeTabActive; the single-central-document invariant becomes "pinned Files home first, one active document content tab beside it"; tree interactions re-activate the home tab; the central empty state is gone (landing on the Files home instead); the two checkpoint-diff tests are skipped with pointers to atelier#59 - unit mocks: documents.close added, revisions removed, closeFileViews assertions carry filePath - pnpm-lock: additive update for new atelier deps BLOCKED on opral/atelier#60 — the submodule pointer targets the central-panel-embedding branch, so the submodule-branch validation in CI fails until it merges; bump to the merge commit afterwards. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat!: remove the checkpoint feature, mirroring atelier#59 Atelier removed its checkpoint implementation upstream (opral/atelier#59, pulled in by the central-tabs submodule bump); FlashType mirrors that removal instead of keeping a dormant, half-working feature. Deleted: - src/extensions/history/ — the checkpoint UI (list, create, rename, switch, delete) and its registration - electron/checkpoint-name.mjs (+test), the terminal:generateCheckpointName IPC handler, preload method, and DesktopGenerateCheckpointName* types - src/shell/checkpoint-diff.ts (+test) and src/extension-runtime/checkpoint-diff.ts - e2e/branch-switcher.spec.ts — all four tests exercised checkpoint UI or checkpoint diffs (incl. the two previously skipped) Stripped checkpoint branches from the Files view (checkpoint-diff virtual tree, checkpointBranchId/checkpointDiff context, the "checkpoint-diff" entry source), the file tree (reviewStatuses map, "recreated" status styling), the legacy MarkdownView/CsvView (checkpoint-diff rendering; commit-snapshot/diff modes stay), the top bar (isReviewingCheckpoint badge), and editor-revision-state (checkpoint state keys). Kept: shared lix branch plumbing (createBranch/switchBranch/ syncDiskToLix, the lix_workspace_branch_id active-branch key) and the agent-review machinery — separate features that read them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: bump atelier to central-panel-embedding tip Picks up the built-in strip UX parity (file-type glyphs, no active ring) and the central + removal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat!: adopt the final central-panel shape — Files stays in the sidebar Bumps submodule/atelier to c8c9a58 (central-panel-embedding tip): pinned home only when configured. FlashType passes no centralPanel.home, so the Files view lives in the left sidebar (open by default on fresh state), the central panel is tabs of documents only, and closing the last tab shows the central empty state again. Restores the e2e specs to their origin/main shape — the sidebar-Files assumptions (tree visible beside documents, "Start writing" empty state, in-place tab navigation) hold again under the tab model — minus the deleted checkpoint helper. The documents.close(path) deletion fix stays. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor!: replace the files-extension fork with atelier's bundled Files view Bumps submodule/atelier to 207d18e, which adds the host watched-files data source (createAtelier filesView options), and deletes FlashType's ~5,450-line files-extension fork — it existed to surface watcher-backed disk files for transient workspaces, which the bundled view now covers. - src/lib/atelier-files-view.ts relocates the transient-workspace machinery: watchEntries bridges setEphemeralWatchedDirectories (keyed to the tree's expanded directories) plus the onEphemeralWatchedFileTreeChanged subscription, and resolveFileForInteraction imports a watched path into lix before open/rename. Only ephemeral workspaces pass filesView. - Review overlays and deletion-closes-tab are native to the bundled view (it computes pending review paths itself and closes by path), so no host wiring remains. - Deletes src/extensions/files/*, the fork-only ExtensionContext/ WorkspaceContext/PanelSide types, and the dead src/queries.ts. - e2e: the bundled tree auto-reveals the active document's directory, so the grouped-transient-workspace test no longer clicks it (the click would collapse it); all other selectors held. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: bump atelier — side-panel add/close semantics fix Adding Files from a side panel focuses the left sidebar; closing the last removable side view leaves the empty state instead of collapsing the island. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: bump atelier — Files left-only placement The right panel's add-view menu no longer offers Files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: bump atelier — Files is a normal, removable view Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: bump atelier — Files placeable in both side panels Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: point submodule/atelier at main atelier#60 merged; the pointer moves to the squash commit on main. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
An opt-in embedding API for hosts that need browser-style tabs in the central panel (LixRay's home flow), while keeping the current single-document slot as the default (FlashType is unaffected — no changes needed there).
Full write-up in docs/embedding.md.
The tab model (browser-like)
documents.open(path)activates an existing tab for that path, otherwise navigates the active content tab in place (label follows the location). If home is active, a content tab is appended.documents.open(path, { newTab: true })(hosts map ⌘-click) and the tab-strip + append new tabs.API surface
AtelierOptions.centralPanel(tabs,home)AtelierDocumentOpenOptions.newTabinstance.views.open(extensionId, { state, instanceKey, newTab, panel })— places host extension views (e.g. folder views) as central content tabs; also onAtelierExtensionRuntime.viewsExtensionManifest.placement/.hidden— declare central-capable, menu-hidden viewscentral_view_activatedevent — fired for every active-central-view change so routing hosts can mirror the active tab into the URLCompatibility
All additive. Without
centralPanelthe shell behaves exactly as before (verified by the existing 838-test suite); new behavior is covered bysrc/shell/central-tabs.test.tsx.🤖 Generated with Claude Code