Skip to content

feat(members): dock the chat SidePanel beside the DM thread (first tab: Crew summary) - #9437

Open
CrysisDeu wants to merge 1 commit into
mainfrom
feat/members-sidepanel-tabs
Open

feat(members): dock the chat SidePanel beside the DM thread (first tab: Crew summary)#9437
CrysisDeu wants to merge 1 commit into
mainfrom
feat/members-sidepanel-tabs

Conversation

@CrysisDeu

@CrysisDeu CrysisDeu commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Problem / Motivation

The Crew Members page's right column is a closable DetailPanel drawer (X in its header, a Details toggle in the thread header, one fixed body). The chat page's right column is the tabbed SidePanel: pinned Changes / Artifacts / Files, a + menu of further views (Summary, Pins, Issues, Links, Subagents, Workflows, Git, Side chat, Browser, per-chat Terminal — #8703), document tabs, reorder, resize, per-slot tab memory. A member DM thread is a chat slot, yet the two surfaces teach two different right-panel models. Zezhen: "这个侧边栏去掉关闭按钮,做成和 session 一样的 tab 形式,第一个 tab 是 crew summary".

Why it matters

The Members page is the perpetual-agent roster: it is where a member's patrol (#8936), the sessions it drives (#8591) and its escalations (#8614) surface. Today that surface has none of the tools a chat has beside it — no Files browser, no Terminal in the member's project, no artifacts — and it is a hand-built copy of chat infrastructure, which is the drift class this page keeps producing (drag handle, list background, steer badge, Quote/Ask were each one PR).

What changed (motivation → approach → change)

Approach: reuse SidePanel, do not build a second one. The only things the panel lacked were a way to host a page-owned first tab and a way to be permanent.

  • pages/chat/SidePanel.tsx

    • leadingTab?: { id, title, icon, render } — a host-owned tab rendered AHEAD of the pinned block as a pinned-style chip: non-closable, not a Reorder.Item, never in the + menu, body rendered by the host only while active. Its id must not collide with a TabKind ('summary' is the chat's session summary — a different thing; this one is 'crew-summary', and its icon is the member's avatar, not a kind glyph).
    • onClose becomes optional. Absent ⇒ permanent: no close control in the strip (the trailing divider goes with it) and the views' Escape does nothing. ChatPage passes it as before; the Members docked column omits it; the Members narrow-window overlay passes it.
    • extraReserveW?: number — a sibling column's live width kept clear on top of measureSidePanelReservedW() (the Members roster), so a drag can never fold the thread to nothing — the contract the drawer's reserveWidth carried.
    • TabChip accepts an icon override and a kind-less tab for the leading chip.
  • hooks/usePanelTabs.tsusePanelTabs(slot, descriptors, { leadingId }): a fresh strip's focus is the leading tab (not the first pinned view syncPinned would otherwise pick), syncPinned never "repairs" a leading-tab focus away for not being a stored tab, and closing the last tab / closeAll lands back on it. No change without leadingId.

  • hooks/usePanelDocumentActions.ts (new, at the shared hooks layer — it is a two-host module from birth) — file open / artifact open / file save, extracted verbatim from useChatPageResourcesController (which now calls it with onOpened = reveal dock + close find pane). One implementation for both hosts, so the 404-vs-error read contract, the artifact involvement breadcrumb and the save-baseline reconcile cannot drift.

  • pages/members/MembersPage.tsx

    • DetailPanel drawer, drawerOpen, DRAWER_* and the mc-members-drawer-width key are gone. The panel is the chat SidePanel on the chat page's right-dock motion, canDockBottom={false}, slot = the member's DM slot, projectDir from the live slot (Files root / Terminal cwd), tab strip bucketed per member (usePanelTabs(memberKey, …, { leadingId: CREW_SUMMARY_TAB_ID })) so switching members swaps the strip and switching back restores it.
    • panelSitsBeside({ winW, rosterW, isMobile }) (pure, exported) decides the placement: wide enough (shell reserve + roster + gaps + panel minimum) ⇒ permanent column, no header toggle, no close; narrower ⇒ an overlay the header button opens, sliding in over a full-bleed scrim (bg-bg/60 + blur) that dims the whole chat column — no sliver of thread peeking beside the panel — and dismissed by the panel's own close control or a click on the scrim, the drawer convention. A failed document read while the overlay is open closes the overlay as the notice appears above the thread, so the failure is never hidden behind the panel that caused it. Below md is always overlay.
    • The drawer body moved into the Crew summary tab unchanged, with an identity row (avatar + name + live status) where the drawer header used to carry it. Its data reads and the patrol countdown are gated on the summary being on screen (previously on drawerOpen).
    • Failed document reads from the panel's Files / Artifacts tabs are reported above the thread through the shared ErrorNotice (askAgent; nothing there holds a draft).
  • i18n: one new key pages.membersPage.crew_summary in en.json + 12 locales, en-XA regenerated (pure-additive, 13 lines).

  • docs/feature-map/README.md: Crew Members / Monitor loops rows updated.

  • Views this host cannot feed are withheld, not shipped empty (SidePanel.hiddenViews, applied to the pinned block and the + menu alike; MEMBERS_UNFED_VIEWS): Changes / Issues / Links / Pins read ChatPage-owned transcript indexes this page does not run, so an empty Changes chip on the monitoring page would assert "nothing changed" while a member edits. The chat page's session Summary is withheld too — one click from the "Crew summary" chip it is an indistinguishable sibling label. The withdrawal is a RENDER-time filter over the bucket, never a deletion: syncPinned still receives the whole PINNED_VIEWS list, a withheld pinned view (Changes) simply is not drawn, and tabs already in the slot's bucket (a member DM opened on the chat page can have stored Pins / Issues) are neither rendered nor focused here but stay in storage for the chat page; a stored focus on such a tab falls back to the leading tab in the STRIP only — the store keeps the focus (a withdrawal can be temporary: every slot view is withheld for the moment a re-open POST is in flight, and a stored focus on Files must come back as Files) — and the panel reports what it shows through a new onActiveTabChange, which is what the Members page gates its summary reads on. The unfed set is derived, not enumerated: usePanelTabs.VIEW_DATA_SOURCE classifies every ViewKind as slot or chat-transcript (exhaustive Record, so a new view kind cannot exist unclassified) and MEMBERS_UNFED_VIEWS = [...CHAT_TRANSCRIPT_VIEWS, 'summary']. Body-owning tabs (Browser, Terminal, editors) stay MOUNTED and hidden while withheld — the body loop walks every stored tab and a withheld one is simply never active — so a temporary withdrawal cannot destroy a live WebContentsView. The persisted bucket keeps a leading-tab focus (serializeBucket only refocuses when the focused tab was itself dropped), so a reload lands on Crew summary when that is what was selected. They return when the extraction moves below ChatPage. Files / Artifacts / Terminal / Browser / Side chat / Subagents / Workflows / Git work against the DM slot.

  • The panel binds only the POST-confirmed slot. The tab bucket and SidePanel.slot use activeSlot (set by POST /api/members/{slug}/thread), never the roster's derived slot_key — that key is a stale binding until confirmed, and an ordinary slot can occupy it after a restart. Until the thread is confirmed (or when the POST is refused), every slot-bound view is withheld; only the slot-free Crew summary (and Terminal, a PTY in a directory) is offered.

  • One SidePanel instance across placements. Docked and overlay share one keyed motion element (only the wrapper class and motion axis differ), so a placement flip never remounts the panel; a closed overlay stays mounted-and-hidden while a live Browser / app tab exists (shouldMountSidePanel / isSidePanelHidden, the chat page's rule). overlayOpen is reset whenever the panel docks, and a document open only reveals the overlay when the panel IS an overlay — so an open made while docked cannot pop the overlay over the thread on a later narrowing.

  • The panel is unbound for the whole in-flight window of a re-open, not just after a refusal. confirmedSlot is empty from the moment a thread POST is issued until it confirms (a request sequence makes only the NEWEST POST's answer count — an older answer, success or refusal, is dropped whole, so a stale success can never rebind a key the latest refusal unbound; nothing is lost because every open re-POSTs); the thread column keeps its cached key as a render hint, the panel offers only the Crew summary and its document actions carry no slot, so nothing can be recorded against a key the endpoint is about to refuse.

  • A refused re-open unbinds. activate always re-POSTs (the endpoint is the idempotent creator/repairer); a refusal now clears the cached slot as well as showing the error, so a key confirmed by an earlier open cannot keep the thread or any panel view aimed at a session that is no longer this member's. Terminal additionally waits for the WS slots frame that carries the confirmed slot's record (its project is the shell's cwd) — the POST answers before that frame, and a shell spawned in the gap would take the backend's HOME fallback with no re-rooting; a member with no project still gets its shell in the fallback cwd, as a project-less chat does. Until confirmation, Terminal and every app-contributed tab are withheld too (hiddenViews accepts 'terminal' and the 'app' switch) — a PTY or an AppHost opened into the provisional (no-slot) bucket would be orphaned / remounted when the strip re-keys. The feature map's App-contributed panel tabs row now names /members as a second path. openFile captures the initiating slot before awaiting the read, like saveFile already did.

  • Width is shared with the chat panel on purpose: the panel persists under the chat panel's mc-side-panel-width, so resizing it on either page resizes both. It is the same panel; one width is the consistency the request asked for. The old mc-members-drawer-width key is orphaned (a stale localStorage entry, nothing reads it).

Tests

Written, not run locally (this session's rule: tsc / eslint / i18n gate only — all green; CI runs vitest).

  • src/test/sidePanelLeadingTab.test.tsx (new): leading chip renders first, ahead of the pinned block, with the host icon and no close control, and is not in the Reorder tablist; fresh bucket focuses it (body shows); it is absent from the + menu while Terminal is offered; closing the last dynamic tab refocuses the neighbour, closeAll lands on the leading tab; no close control without onClose, one with it; focus is per slot; hiddenViews withdraws a view from the pinned block and the + menu alike.
  • pages/members/MembersPage.test.tsx: the drawer cases now assert the docked SidePanel (permanent, no Details toggle, no close control, resize splitter present); Crew summary is tabs[0], aria-selected, named, without nested buttons, CREW_SUMMARY_TAB_ID !== 'summary'; selecting Artifacts swaps the body and the chip brings the summary back; the + menu lists Terminal and not Crew summary; narrow window ⇒ closed overlay, toggle opens, close control dismisses; panelSitsBeside boundary at 1168px for a 264px roster, mobile never; the withheld set is pinned (MEMBERS_UNFED_VIEWS) and absent from strip + menu; a refused thread POST leaves only Crew summary on the strip and nothing slot-bound in the menu; an overlay opened narrow is closed again after dock → re-narrow. member-drawer test id → member-crew-summary; every existing summary-content case (config, memory disclosure, driving sessions, patrol, activity, wake sources, edit exits) is kept and re-pointed.
  • Existing sidePanelPinnedAlwaysPresent / sidePanelAddMenu contracts are unchanged (no leadingTab ⇒ identical strip).

Manual verification

Against a live pod (kirocrew pod up kc-wt-members-sidepanel --seed <dir> with a seed config of four fictional crews; real gateway, real SPA dist, real POST /api/members/{slug}/thread, no stubbed API): docked panel at 1440×900, Crew summary selected on open, no close / toggle, + menu offers Terminal, member switch swaps the strip and the summary body, Workflows opens from +, chip returns to the summary. Theme flipped through the pod's own PUT /api/config/theme.

Screenshots / video

recorded from 925acb8 · origin/kirodotdev/KiroCrew · mode: pod (kirocrew pod up, dark/light via PUT /api/config/theme) · real server + model, no fixtures

Before — closable drawer (X in header, Details toggle in the thread header):

before dark

After — the chat SidePanel docked, Crew summary as the permanent first tab, no close control:

after dark

+ menu on a member DM — the chat panel's views, Terminal included:

plus menu dark

Switch member → strip and summary follow; + → a view opens; chip → back to the summary (6.7s):

tabs follow the member

Narrow window (1000px): closed by default with the header toggle; the overlay carries the panel's own close control:

overlay closed
overlay open

Docked → overlay → docked across the panelSitsBeside boundary (window resized 1440 → 1000 → 1440; one keyed element, no remount; 6.9s):

resize flip

Not captured: the member-panel-action-error notice. I tried to force it (route-intercept /api/file-read → 500 and open a file from the Files tab), but a member DM slot in the pod has no project directory, so the Files tab has no rows to open; its render is the shared ErrorNotice already shown for the star-write failure on this page.

Light theme + before/light

before light
after light
plus menu light
overlay open light

Related Issues

Closes #9432. Builds on #8936, #8591, #8614 (summary content), #8703 (Terminal in the + menu), #8718 (state-transition continuity).

Pattern harvest

Not generalizable as a rule — but it is the same class as the week's other Members-page fixes: the Members page was hand-built by copying chat surfaces instead of mounting them, and each copy drifts (drag handle, list background, steer badge, Quote/Ask, and this drawer). This PR retires the largest copy by mounting the real SidePanel and extracting the document actions into a shared hook. The remaining copy is the left column; the follow-up "Members page: reuse ChatSidebar list infrastructure" (being filed by the React Query migration work) closes the class at its source.

Checklist

  • At most two commits (one), Conventional Commits title
  • New tests added; existing tests re-pointed (CI runs them)
  • Self-review completed
  • Documentation updated (feature map)
  • No secrets, credentials, or internal references in the diff (the GIF deliberately opens Workflows, not a real Terminal — a PTY paints the host's login/hostname into the frame)

@CrysisDeu
CrysisDeu requested a review from a team September 8, 2026 09:24
@CrysisDeu
CrysisDeu requested a review from a team as a code owner September 8, 2026 09:24
@CrysisDeu
CrysisDeu requested a review from pepmach September 8, 2026 09:24
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — ✅ PASS

Design-level review of 925acb8135c6d269a098237c80e2a5323e681436 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

Design-Verdict: PASS

Retires a drifting hand-copy by mounting the real component, with the hard edges (slot binding, withheld views, remount survival) reasoned and pinned by tests.

The consolidation direction is right: the panel gap was previously closed by copying chat infrastructure, and the PR's own history (drag handle, steer badge, Quote/Ask) shows that class drifts. The new host surface on SidePanel (leadingTab, hiddenViews, extraReserveW, optional onClose) is proportionate for a second host and each prop carries a named cause; the derived VIEW_DATA_SOURCE classification makes the withheld set a type-checked decision rather than an enumerated list, so a future view kind cannot arrive unfed. The slot-binding rule (bind only the POST-confirmed slot, unbind for the whole in-flight window, newest-request-wins) closes a real misroute against a foreign session. Migration story is honest (orphaned mc-members-drawer-width, shared width key by design), docs and feature map updated in the same commit, and cross-page focus fallout (a stored crew-summary focus reaching the chat page) resolves through the existing render-level fallback.

[DESIGN-REVIEWED] 925acb8

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5) — ✅ PASS

Premise-level review of 925acb8135c6d269a098237c80e2a5323e681436 — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

All checks done — the change reuses the existing SidePanel and deletes the hand-built drawer, the screenshots follow the repo's committed-deliverables convention, and every new prop has a real consumer. Final review:

First-Principles-Verdict: PASS

Verify one undeclared coupling: mc-members-drawer-width is gone, so resizing the panel on /members now writes the chat page's shared WIDTH_KEY.

What this change ships

Intent: give the Crew Members page the same tabbed right-panel model as chat (first tab: Crew summary), per a quoted user request — an ADDITION whose core is a deletion (the hand-built DetailPanel drawer, the drift class the description documents). Counted: usePanelDocumentActions has 2 production consumers and deletes the ChatPage copy; leadingTab/hiddenViews/extraReserveW/onActiveTabChange each have 1 consumer (MembersPage) but no smaller form exists — the singular alternative would couple SidePanel to the members page; CHAT_TRANSCRIPT_VIEWS has 1 consumer and its exhaustive Record is a type-level constraint, not surface.

  1. Members right column is the chat page's tabbed SidePanel, replacing the closable Details drawer — justified
  2. Old drawer body + identity row becomes the permanent first "Crew summary" tab — justified
  3. Wide windows: panel is permanent; Details toggle and close control removed — justified
  4. Narrow windows: panel is a header-opened overlay over a scrim that dims the thread — justified
  5. Page gains chat's tools against the DM slot (Files, Terminal, Browser, Side chat, Git, app tabs…) — justified
  6. Changes / Issues / Links / Pins / session Summary withheld (no transcript index feeds them here) — justified
  7. Slot-bound views withheld until the thread POST confirms; refusals and stale answers unbind — justified
  8. Panel width on /members now shares the chat panel's persisted width; the old drawer width key is abandoned — undeclared
  9. Document open/save logic extracted to one shared hook; the chat-page copy deleted — justified
  10. Feature-map rows updated + one i18n key in 13 locales — justified

[FIRST-PRINCIPLES-REVIEWED] 925acb8

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed 925acb8135c6d269a098237c80e2a5323e681436 — this comment is updated in place on each push.

Review details

No findings.

[OPUS-REVIEWED] 925acb8

Verdict parsed from the review's SHA-scoped output markers for commit 925acb8135c6d269a098237c80e2a5323e681436.

False positive or not applicable? A repository writer can comment:
/ai-review override fable 925acb8135c6d269a098237c80e2a5323e681436: <one-sentence reason>

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

UX Review (Fable 5) — 🟡 CONCERNS

UX-level review of 925acb8135c6d269a098237c80e2a5323e681436 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

All evidence is reconciled. The blind reader correctly identified the primary Crew summary chip, the + menu, the narrow-window toggle, the overlay scrim and its close control; the two recordings cover the member-switch and dock↔overlay transitions, and the single-keyed-element mechanism is verifiable in the diff. The remaining signals are the "Crew summary" label confusion the reader called out, the icon-only pinned chips they couldn't name, and the one state the author admits was never captured.

UX-Verdict: CONCERNS

Reused chat panel reads coherently, but "Crew summary" summarizes one member — the blind reader flagged it — and the new error notice was never shown.

Watch

  • "Crew summary" names a one-member card on a page that calls the group a crew: blind reader — "the name confuses me because it shows one member, not the whole crew." Every visit, comprehension friction, permanent chip. Fix: rename pages.membersPage.crew_summary to "Member summary" (the avatar icon already carries identity).
  • Icon-only pinned chips (Artifacts, Files) next to the labeled Crew summary chip: reader — "no idea what they contain." Inherited chat-strip idiom (hover title exists), but on this page the strip is permanent, so the unknowns sit in view every session; a human should decide whether the members strip warrants labeled-when-inactive chips.

Evidence gaps

  • member-panel-action-error (failed Files/Artifacts read → ErrorNotice above the thread, overlay auto-closes) appears in no screenshot; author confirms "Not captured." Close with one screenshot of a forced read failure on a member with a project dir — the overlay-auto-close behavior especially.

[UX-REVIEWED] 925acb8

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of 925acb8135c6d269a098237c80e2a5323e681436 and found no blocking issues.

This comment is updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] 925acb8

False positive or not applicable? A repository writer can comment:
/ai-review override gpt 925acb8135c6d269a098237c80e2a5323e681436: <one-sentence reason>

@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Sep 8, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/members-sidepanel-tabs branch from 0282876 to 39bca34 Compare September 8, 2026 10:08
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 8, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/members-sidepanel-tabs branch from 39bca34 to 5a976ac Compare September 8, 2026 10:35
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 8, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/members-sidepanel-tabs branch from 5a976ac to 95cce10 Compare September 8, 2026 10:56
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 8, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/members-sidepanel-tabs branch from 95cce10 to b47fd1b Compare September 8, 2026 11:17
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 8, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/members-sidepanel-tabs branch from 2f7200c to 7e4cfd1 Compare September 8, 2026 13:51
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: passed Eligible automated validation passed for the current revision readiness: checking Automated validation is still running labels Sep 8, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/members-sidepanel-tabs branch from 7e4cfd1 to fdeefd3 Compare September 8, 2026 14:10
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 8, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/members-sidepanel-tabs branch from fdeefd3 to 574d78c Compare September 8, 2026 14:29
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 8, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/members-sidepanel-tabs branch from 574d78c to 3793dbc Compare September 8, 2026 15:17
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Sep 8, 2026
@CrysisDeu

Copy link
Copy Markdown
Collaborator Author

Re UX Review on 574d78c79 — dispositions in 3793dbc32:

  • Stored tab focus wiped on every member switch — real, and mine (the round-6 normalisation write). Fixed: the fallback is no longer written into the bucket; SidePanel reports what it shows through a new onActiveTabChange, the Members page gates its summary reads on that, and the stored focus survives the in-flight withdrawal. Test: Artifacts focused → re-open round-trip → Artifacts focused again.
  • "Crew summary" shows one member — kept as is, deliberately. The label is the requester's own wording ("第一个 tab 是 crew summary"), and in this product a crew IS one configured member (the roster is the list of crews; the crew manager edits one crew), so "Crew summary" names exactly what the tab shows. The blind reader's reading of "crew" as "the whole team" is the vocabulary tension the product already carries in "Crew Members"; renaming one tab here would not resolve it and would contradict the request.
  • Evidence gap (member-panel-action-error) — still not forceable against the pod: the Files tab has no rows without a project dir on the member slot, and the artifact path does not go through /api/file-read. The notice is the shared ErrorNotice already shown for the star-write failure on this page.

Design Review's Watch (hand-enumerated unfed set) is applied in the same push: VIEW_DATA_SOURCE classifies every ViewKind where kinds are defined (exhaustive Record, so a new kind is a type error until classified) and the Members set derives from it.

@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Sep 8, 2026
…b Crew summary

The Members page's right column was a closable DetailPanel drawer — a
hand-built lookalike of the chat page's right column. It is now the chat
page's own tabbed SidePanel, permanent on wide windows (no close control,
no header toggle), whose first tab is the member's Crew summary carrying
what the drawer showed (identity + status, counters, driving sessions,
auto patrol, recent activity, wake sources, configuration, memory note,
edit exits). The + menu is the chat panel's (Files / Artifacts / Terminal
/ Browser / Side chat …) against the member's DM slot; the strip is
bucketed per member so it follows the roster selection.

SidePanel gains `leadingTab` (a host-owned, non-closable, non-draggable
tab ahead of the pinned block whose body the host renders), an optional
`onClose` (absent ⇒ no close control, Escape inert) and `extraReserveW`
(a sibling column's live width kept clear on drag). `usePanelTabs` takes
`opts.leadingId` so a fresh strip opens on the leading tab and an emptied
strip falls back to it. File / artifact open and file save move out of
useChatPageResourcesController into the shared `usePanelDocumentActions`
so both hosts run one implementation. Narrow windows (roster + thread +
panel cannot seat) keep the panel as an overlay the header button opens.

Closes #9432
@CrysisDeu
CrysisDeu force-pushed the feat/members-sidepanel-tabs branch from 3793dbc to 925acb8 Compare September 8, 2026 15:56
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

readiness: passed Eligible automated validation passed for the current revision

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[insider] Members detail pane: replace the closable drawer with the chat SidePanel tab model (first tab: Crew summary)

1 participant