Skip to content

Commit fe7ad48

Browse files
kazemek21codex
andcommitted
Add page-owned sidebar contribution
Co-authored-by: Codex (gpt-6.1-sol) <[email protected]> Co-authored-by: Codex (gpt-6-luna) <[email protected]> Signed-off-by: Kyle Zemek <[email protected]>
1 parent 1311492 commit fe7ad48

22 files changed

Lines changed: 1301 additions & 91 deletions

‎docs/plugin-architecture.md‎

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,15 @@ bundled/inbox/ builtin Inbox page for unread conversations and mentions
7676
```
7777

7878
The host composes the conversation sidebar (Solo and Team inventories) beside
79-
independently mounted pages; Settings supplies its own sidebar. The sidebar reuses
80-
session-owned roster, unread, creation and preferences capabilities; it does not
79+
independently mounted pages. Settings supplies its own modal navigation; a selected
80+
page can replace the conversation sidebar through its optional `sidebar` contribution.
81+
The conversation sidebar reuses session-owned roster, unread, creation and
82+
preferences capabilities; it does not
8183
retain a hidden Channels page or message reader. Sidebar and page render errors
82-
have separate boundaries. Sidebar presentation helpers currently remain importable
83-
from `bundled/channels`; no public sidebar contribution contract is introduced.
84+
have separate boundaries. A page sidebar is isolated by the same owned-contribution
85+
boundary, so a failure falls back to a concise alert without taking down the page,
86+
Settings, or shell. The host owns the slot, collapse state, labels, and narrow drawer;
87+
the page owns its sidebar content and local interaction state.
8488

8589
Channels is the page-authoring example, not a thin registration wrapper over a
8690
host-owned product page. Keep page-specific components, styles, interactions and tests
@@ -97,7 +101,7 @@ source imports are not a versioned external SDK. See
97101
## Starting contracts
98102

99103
A plugin exports `inject` and `apply(ctx)`. Pages register with
100-
`ctx.pages.register({ id, title, layout?, companion?, primary?, placement?, icon?, component })`. Panels register with
104+
`ctx.pages.register({ id, title, layout?, companion?, primary?, placement?, icon?, component, sidebar? })`. Panels register with
101105
`ctx.panels.register({ id, title, matches, order?, launcher?, toolbar?, component })`. An optional `toolbar` supplies live controls beside shell search in place of the static launcher. Its lifetime stays with its plugin, and shell render failures are isolated. Huddles is the first caller. IDs are local to the
102106
plugin; the registry adds installation identity and revision and removes the
103107
contribution when its Cordis scope ends. `primary: true` opts a page into shell
@@ -148,6 +152,52 @@ ctx.pages.register({
148152
});
149153
```
150154

155+
A page may replace the conversation sidebar while selected. Its component receives
156+
the current read-only `OpenTarget` and, when present, shell-generated primary
157+
Pages navigation as `children`. It supplies only its sidebar contents within the
158+
existing sidebar slot. Wrap that content in the existing `shell-sidebar-default`
159+
layout role to receive the default sidebar width and shell sizing constraints
160+
(260px default width, 220px minimum, with the shell's available-width clamp). That
161+
role provides outer slot geometry and its divider; it does not provide an inner
162+
surface, frame, padding, or content layout. The contributor owns those details.
163+
External authors use this CSS role and their own markup; internal source components
164+
such as `SidebarFrame` are not part of the author SDK. The host owns the slot and
165+
collapse/drawer interaction. A page may manage its own width preference. Pages do
166+
not receive navigation completion authority or own routing. On wide layouts the
167+
shell keeps its generic Show/Hide sidebar toggle; on narrow layouts the existing
168+
Show/Hide navigation drawer, focus handoff, Escape, inert, and ARIA behavior remain
169+
shell-owned. Pages without `sidebar` continue to
170+
use the conversation sidebar. The host isolates sidebar rendering with
171+
`OwnedContribution`; a throwing sidebar shows a concise alert with scrollable page
172+
navigation inside the standard fallback frame while the selected page and shell
173+
remain usable. A new page navigation request retries a failed sidebar without
174+
remounting healthy sidebar state. The sidebar shares the page's admitted workspace
175+
target and stays mounted beneath Settings; invalid destinations are not delivered
176+
to it.
177+
178+
```tsx
179+
import type { PageSidebarProps } from "@buzz/author";
180+
181+
function ActivitySidebar({ target, children }: PageSidebarProps) {
182+
return (
183+
<div className="shell-sidebar-default">
184+
<aside className="activity-sidebar" aria-label="Activity sidebar">
185+
<header>Activity</header>
186+
<p>Current destination: {target.kind}</p>
187+
{children}
188+
</aside>
189+
</div>
190+
);
191+
}
192+
193+
ctx.pages.register({
194+
id: "activity",
195+
title: "Activity",
196+
component: ActivityPage,
197+
sidebar: ActivitySidebar,
198+
});
199+
```
200+
151201
Header entries move into the labelled More pages popover when they cannot fit,
152202
including at narrow widths. Toolbar and overflow entries use native-button
153203
navigation and `aria-current`, not companion-panel toggles. The wide topbar uses

‎docs/shell-design.md‎

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,18 @@ semantic tokens, UI authoring rules and the local component reference.
7979
the entire webview. Native traffic lights remain in their reserved titlebar
8080
area. The shared compact dialog header centers its title and Close action
8181
without a large empty band. Close and Escape return to the original workspace
82-
visit, including after navigating through several Settings sections. `App.tsx` composes `PrimarySidebar` around `features/channel-navigation/ChannelInventory`. The inventory supplies channel rows,
82+
visit, including after navigating through several Settings sections.
83+
A selected page may replace the conversation sidebar through `Page.sidebar`.
84+
It receives the admitted workspace target and shell-generated Pages navigation
85+
as children, without navigation completion authority. Healthy sidebar state
86+
survives target changes and stays mounted beneath Settings. A failed sidebar
87+
shows an alert and scrollable page navigation in the standard frame; a new
88+
workspace navigation request retries it independently of the page body.
89+
Pages without a sidebar contribution keep the conversation sidebar.
90+
`App.tsx` composes `PrimarySidebar` around `features/channel-navigation/ChannelInventory`. The inventory supplies channel rows,
8391
notices, dialogs and scroll bindings through a render prop; PrimarySidebar owns
8492
the common frame, Solo/Team arrangement and error fallback. The fallback retains
85-
page navigation without remounting a failed inventory. There is no portal or plugin contract expansion.
93+
page navigation without remounting a failed inventory. There is no portal.
8694
Sidebar session state resets on scope/connection generation without remounting
8795
unrelated pages. Its own error boundary keeps page navigation and Settings usable.
8896
Page buttons use shared navigation rows and focus the main region on selection.
@@ -92,11 +100,12 @@ semantic tokens, UI authoring rules and the local component reference.
92100
220px disclosure overlays content, supports Escape, and keeps sidebar state
93101
mounted. A navigation selection closes the phone drawer and hands focus to the
94102
main content for conversation selections. Settings owns its navigation inside
95-
the modal instead. Messages, Agents and any selected primary page share an
103+
the modal instead. Messages, Agents, any selected primary page, and pages with
104+
a sidebar contribution share an
96105
animated header toggle; hiding the sidebar preserves its mounted state and saved
97106
width. The toggle uses "Hide sidebar" / "Show sidebar" on every page, and the
98107
combined Solo and Team sidebar is labelled "Sidebar". Reduced motion disables the transition. Non-primary
99-
desktop destinations retain the visible sidebar.
108+
desktop destinations without a sidebar contribution retain the visible sidebar.
100109
The same header keeps history and account/search actions, plus a segmented
101110
navigation strip and full-page toolbar icons. Real control widths, native insets
102111
and text scale determine whether header pages fit without overlap. Otherwise

0 commit comments

Comments
 (0)