From 23f6d0663284f26e417941dae3264bada590c5fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Tue, 25 Aug 2026 18:30:55 +0000 Subject: [PATCH] the chat panel gets the header and the history it should have had MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ChatPanelWrapper, not EmbeddableChat — one level up, and the session persistence, the agent identity bar, the spawned-by bar and resume-the-newest all come with it. mounting the composer directly meant reimplementing those or going without. and a header: the conversation picker, scoped to this plugin's own directory, with new chat, inline rename and delete. none of it is new — it is what a dashboard's chat panel has always had, pointed at a context instead of a path. Co-Authored-By: Claude Opus 5 Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043 --- web/ChatPanel.tsx | 43 +++++++++++++++++++++++++++++-------------- web/panels.ts | 12 ++++++++++-- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/web/ChatPanel.tsx b/web/ChatPanel.tsx index adb97b1..346610a 100644 --- a/web/ChatPanel.tsx +++ b/web/ChatPanel.tsx @@ -1,4 +1,4 @@ -import { EmbeddableChat } from 'officerdev'; +import { ChatPanelWrapper, ChatHeader } from 'officerdev'; /** * A chat, sitting under the two lists. @@ -40,22 +40,37 @@ const PREFIX = [ * the same reason. */ /** - * `context`/`contextId` name the plugin; the platform turns that into a working directory — - * `$DATA_PATH//plugins/proxy-manager/chats` — and the path is never sent from here. See - * `resolveChatCwd`, and `docs/commander-directory.md` for why a client does not choose a cwd. + * The context this plugin's chats belong to. * - * What that buys beyond tidiness: Claude Code reads a `CLAUDE.md` from its cwd at session start, so - * anything these chats should know can live in that directory instead of in every conversation on the - * machine. + * The platform turns it into `$DATA_PATH//plugins/proxy-manager/chats` — for the turn, and for the + * header's session list — and the path is never sent from here. `docs/commander-directory.md` has why a + * client does not choose a cwd; `cwdOf` in `api/chat/chat.ts` has the listing half. + * + * What the directory buys beyond tidiness: Claude Code reads a `CLAUDE.md` from its cwd at session start, + * so anything these chats should know can live there instead of in every conversation on the machine. + */ +const CHAT_CONTEXT = { context: 'plugin', contextId: 'proxy-manager' } as const; + +/** + * `ChatPanelWrapper`, not `EmbeddableChat` — one level up, and everything below it comes free. + * + * The wrapper already carries the session persistence (`usePanelSession`, so this panel stays on its own + * conversation across a reload), the agent identity bar, the spawned-by bar, and the resume-the-newest + * behaviour. Mounting the composer directly meant reimplementing all of that or going without it, which is + * what this panel was doing. */ export const ChatPanel = ({ panelId }: { panelId: string }) => (
- +
); + +/** + * The panel's header: the conversation picker, scoped to this plugin's directory. + * + * A registry `header` is mounted with `{ panelId }` only, so the context is bound here rather than passed + * — which is the whole reason this wrapper exists. + */ +export const ChatPanelHeader = ({ panelId }: { panelId: string }) => ( + +); diff --git a/web/panels.ts b/web/panels.ts index c2a4ba9..ea888d0 100644 --- a/web/panels.ts +++ b/web/panels.ts @@ -2,7 +2,7 @@ import { MessageSquare, Route, ShieldCheck } from 'lucide-react'; import type { AppRegistryMeta } from 'officerdev'; import { HostsPanel } from './HostsPanel'; import { CertificatesPanel } from './CertificatesPanel'; -import { ChatPanel } from './ChatPanel'; +import { ChatPanel, ChatPanelHeader } from './ChatPanel'; // Three panels. Hosts is what the plugin is for; certificates is the thing that quietly breaks and is // worth seeing beside it rather than a click away; chat is where you ask about either of them. @@ -21,5 +21,13 @@ export const appRegistryMetas: AppRegistryMeta[] = [ component: CertificatesPanel, availableOnPanel: false, }, - { key: 'proxy-manager-chat', name: 'Chat', icon: MessageSquare, component: ChatPanel, availableOnPanel: false }, + { + key: 'proxy-manager-chat', + name: 'Chat', + icon: MessageSquare, + component: ChatPanel, + // The conversation picker, scoped to this plugin's own chat directory. + header: ChatPanelHeader, + availableOnPanel: false, + }, ];