@agimon-ai/po-web-dashboard 0.0.0-alpha.78 → 0.0.0-alpha.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -173,13 +173,15 @@ function TopBar({ view = "conversation", mcpEnabled = false, onShowSessions, onS
|
|
|
173
173
|
const noSessions = require_sessionsStore.useNoSessions();
|
|
174
174
|
const ownerId = (0, _tanstack_react_store.useStore)(require_sessionsStore.sessionsStore, (state) => state.tabOwnerId ?? state.activeId);
|
|
175
175
|
const transientTabs = require_transientTabsStore.useTransientTabs(ownerId);
|
|
176
|
-
const
|
|
177
|
-
const
|
|
178
|
-
const
|
|
176
|
+
const ownerName = (0, _tanstack_react_store.useStore)(require_sessionsStore.sessionsStore, (state) => ownerId === null ? void 0 : state.byId[ownerId]?.summary.name);
|
|
177
|
+
const ownerAgentName = (0, _tanstack_react_store.useStore)(require_sessionStore.sessionStoreFor(ownerId), (state) => state.agent?.sessionName);
|
|
178
|
+
const title = ownerName || ownerAgentName || "Untitled";
|
|
179
|
+
const [renaming, setRenaming] = require_dashboardStateStore.useDashboardState(`session-title/${ownerId}/open`, false);
|
|
180
|
+
const [draft, setDraft] = require_dashboardStateStore.useDashboardState(`session-title/${ownerId}/draft`, "");
|
|
179
181
|
const backgroundWorkActive = require_composition.useActivityGroups(session.statuses, session.widgets, activeId).some((group) => group.active);
|
|
180
182
|
const commitRename = () => {
|
|
181
183
|
const next = draft.trim();
|
|
182
|
-
if (next && next !== title) require_sessionStore.renameSession(next,
|
|
184
|
+
if (next && next !== title) require_sessionStore.renameSession(next, ownerId);
|
|
183
185
|
setRenaming(false);
|
|
184
186
|
};
|
|
185
187
|
const lockAgent = activeId !== null && meta?.summary.dormant !== true && !session.agentLocked ? () => require_sessionStore.setAgentLock(true, activeId) : void 0;
|
|
@@ -200,7 +202,7 @@ function TopBar({ view = "conversation", mcpEnabled = false, onShowSessions, onS
|
|
|
200
202
|
className: `shrink-0 text-po-dim ${navigationToggle === "mobile" ? "md:hidden" : ""}`,
|
|
201
203
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.FolderIcon, { className: "size-4" })
|
|
202
204
|
}) : null,
|
|
203
|
-
noSessions ? null : renaming &&
|
|
205
|
+
noSessions ? null : renaming && ownerId !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.Input, {
|
|
204
206
|
"data-testid": "session-title-input",
|
|
205
207
|
value: draft,
|
|
206
208
|
autoFocus: true,
|
|
@@ -215,8 +217,8 @@ function TopBar({ view = "conversation", mcpEnabled = false, onShowSessions, onS
|
|
|
215
217
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.Button, {
|
|
216
218
|
variant: "ghost",
|
|
217
219
|
"data-testid": "session-title",
|
|
218
|
-
title:
|
|
219
|
-
disabled:
|
|
220
|
+
title: ownerId === null ? void 0 : "Rename this session",
|
|
221
|
+
disabled: ownerId === null,
|
|
220
222
|
onClick: () => {
|
|
221
223
|
setDraft(title);
|
|
222
224
|
setRenaming(true);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TopBar.cjs","names":["NavTab","Link","NavTabBadge","Button","closeTransientTab","CloseIcon","useNavigate","webTabs","DropdownMenu","DropdownMenuTrigger","GridIcon","DropdownMenuContent","DropdownMenuLabel","DropdownMenuRadioGroup","DropdownMenuRadioItem","DropdownMenuSeparator","DropdownMenuItem","LockIcon","useActiveSessionMeta","useActiveSession","useStore","sessionsStore","useNoSessions","useTransientTabs","useDashboardState","useActivityGroups","setAgentLock","FolderIcon","Input","MessageIcon","ActivityIcon"],"sources":["../../../../src/web/features/status/TopBar.tsx"],"sourcesContent":["import type { TabContribution, TransientTab } from '@agimon-ai/po-core/web';\nimport {\n ActivityIcon,\n Button,\n CloseIcon,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n FolderIcon,\n GridIcon,\n Input,\n LockIcon,\n MessageIcon,\n NavTab,\n NavTabBadge,\n} from '@agimon-ai/po-web-components';\nimport { Link, useNavigate } from '@tanstack/react-router';\nimport { useStore } from '@tanstack/react-store';\n\nimport { useActivityGroups } from '../../lib/composition';\nimport { webTabs } from '../../lib/pluginRegistry';\nimport { useDashboardState } from '../../stores/dashboardStateStore';\nimport { sessionsStore, useActiveSessionMeta, useNoSessions } from '../../stores/sessionsStore';\nimport { renameSession, setAgentLock, useActiveSession } from '../../stores/sessionStore';\nimport { closeTransientTab, useTransientTabs } from '../../stores/transientTabsStore';\n/** One registry tab: the badge hook is stable per tab, so the call is unconditional. */\nfunction PluginTab({ tab, sessionId, active }: { tab: TabContribution; sessionId: string; active: boolean }) {\n const useBadge = tab.useBadge ?? noBadge;\n const count = useBadge(sessionId);\n return (\n <NavTab asChild active={active}>\n <Link\n to=\"/session/$sessionId/$tabId\"\n params={{ sessionId, tabId: tab.id }}\n data-testid={`tab-${tab.id}`}\n className=\"shrink-0\"\n >\n {tab.label}\n {count > 0 ? (\n <NavTabBadge active={active} data-testid={`tab-${tab.id}-count`}>\n {count}\n </NavTabBadge>\n ) : null}\n </Link>\n </NavTab>\n );\n}\n\nfunction noBadge(): number {\n return 0;\n}\n\n/**\n * A tab a plugin opened at runtime: its link with the close beside it, since\n * a button cannot live inside a link. Closing the shown tab needs no\n * navigation of its own; the page falls back to the conversation once the\n * id is gone.\n */\nfunction TransientTabChip({ tab, sessionId, active }: { tab: TransientTab; sessionId: string; active: boolean }) {\n return (\n <span data-testid={`tab-${tab.id}-chip`} data-active={active} className=\"flex shrink-0 items-center gap-0.5\">\n <NavTab asChild active={active}>\n <Link\n to=\"/session/$sessionId/$tabId\"\n params={{ sessionId, tabId: tab.id }}\n data-testid={`tab-${tab.id}`}\n title={tab.label}\n className=\"block min-w-0 max-w-[160px] truncate\"\n >\n {tab.label}\n </Link>\n </NavTab>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n data-testid={`tab-${tab.id}-close`}\n title=\"close this tab\"\n onClick={() => closeTransientTab(sessionId, tab.id)}\n className=\"h-5 w-5 shrink-0\"\n >\n <CloseIcon className=\"h-2.5 w-2.5\" />\n </Button>\n </span>\n );\n}\n\n/** A plugin tab's count inside the menu; one component per tab keeps the badge hook unconditional. */\nfunction MenuBadge({ tab, sessionId }: { tab: TabContribution; sessionId: string }) {\n const count = (tab.useBadge ?? noBadge)(sessionId);\n return count > 0 ? <NavTabBadge className=\"ml-auto\">{count}</NavTabBadge> : null;\n}\n\n// The shown tab reads jade like the strip's active tab; 44px rows for touch.\nconst MENU_TAB_CLASS = 'min-h-11 data-[state=checked]:font-semibold data-[state=checked]:text-po-green';\n/**\n * Below sm the strip keeps only the shown tab, so the title, that tab and the\n * actions share one row; every tab, and the lock, are one tap away here.\n */\nfunction MobileTabsMenu({\n sessionId,\n view,\n mcpEnabled,\n transientTabs,\n onLock,\n}: {\n sessionId: string;\n view: string;\n mcpEnabled: boolean;\n transientTabs: readonly TransientTab[];\n onLock?: () => void;\n}) {\n const navigate = useNavigate();\n const pluginTabs = webTabs(sessionId);\n const open = (tabId: string): void => {\n if (tabId === 'conversation') void navigate({ to: '/session/$sessionId', params: { sessionId } });\n else void navigate({ to: '/session/$sessionId/$tabId', params: { sessionId, tabId } });\n };\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-md\"\n data-testid=\"mobile-tabs-open\"\n title=\"Show tabs\"\n aria-label=\"Show tabs\"\n className=\"shrink-0 text-po-dim sm:hidden\"\n >\n <GridIcon className=\"size-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent data-testid=\"mobile-tabs-menu\" className=\"min-w-56 max-w-[calc(100vw-24px)]\">\n <DropdownMenuLabel>Tabs</DropdownMenuLabel>\n <DropdownMenuRadioGroup value={view} onValueChange={open}>\n <DropdownMenuRadioItem value=\"conversation\" data-testid=\"mobile-tab-conversation\" className={MENU_TAB_CLASS}>\n Conversation\n </DropdownMenuRadioItem>\n {mcpEnabled ? (\n <DropdownMenuRadioItem value=\"mcp\" data-testid=\"mobile-tab-mcp\" className={MENU_TAB_CLASS}>\n MCP\n </DropdownMenuRadioItem>\n ) : null}\n {pluginTabs.map((tab) => (\n <DropdownMenuRadioItem\n key={tab.id}\n value={tab.id}\n data-testid={`mobile-tab-${tab.id}`}\n className={MENU_TAB_CLASS}\n >\n <span className=\"min-w-0 truncate\">{tab.label}</span>\n <MenuBadge tab={tab} sessionId={sessionId} />\n </DropdownMenuRadioItem>\n ))}\n {transientTabs.map((tab) => (\n <DropdownMenuRadioItem\n key={tab.id}\n value={tab.id}\n data-testid={`mobile-tab-${tab.id}`}\n className={MENU_TAB_CLASS}\n >\n <span className=\"min-w-0 truncate\">{tab.label}</span>\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n {onLock ? (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem data-testid=\"mobile-agent-lock\" onSelect={onLock} className=\"min-h-11 pl-2\">\n <LockIcon aria-hidden className=\"size-4 shrink-0\" />\n Lock agent\n </DropdownMenuItem>\n </>\n ) : null}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\nexport function TopBar({\n view = 'conversation',\n mcpEnabled = false,\n onShowSessions,\n onShowActivity,\n navigationToggle = 'mobile',\n activityToggle = 'mobile',\n}: {\n view?: string;\n mcpEnabled?: boolean;\n onShowSessions?: () => void;\n onShowActivity?: () => void;\n navigationToggle?: 'mobile' | 'always';\n activityToggle?: 'mobile' | 'always';\n}) {\n const meta = useActiveSessionMeta();\n const session = useActiveSession((state) => state);\n const activeId = useStore(sessionsStore, (state) => state.activeId);\n const noSessions = useNoSessions();\n const ownerId = useStore(sessionsStore, (state) => state.tabOwnerId ?? state.activeId);\n const transientTabs = useTransientTabs(ownerId);\n const title = meta?.summary.name || session.agent?.sessionName || 'Untitled';\n const [renaming, setRenaming] = useDashboardState(`session-title/${activeId}/open`, false);\n const [draft, setDraft] = useDashboardState(`session-title/${activeId}/draft`, '');\n const backgroundWorkActive = useActivityGroups(session.statuses, session.widgets, activeId).some(\n (group) => group.active,\n );\n // The name lives in the agent, so the rail's rename and this one are the\n // same act through the same door; the new name arrives back on the socket.\n const commitRename = (): void => {\n const next = draft.trim();\n if (next && next !== title) renameSession(next, activeId);\n setRenaming(false);\n };\n const lockAgent =\n activeId !== null && meta?.summary.dormant !== true && !session.agentLocked\n ? () => setAgentLock(true, activeId)\n : undefined;\n\n return (\n <header\n data-testid=\"top-bar\"\n className=\"flex min-h-16 shrink-0 items-center justify-between gap-2 border-b border-po-border-soft px-3 py-2 sm:gap-3 sm:px-6\"\n >\n <div data-testid=\"session-switcher\" className=\"flex min-w-0 flex-1 items-center gap-2 sm:gap-3\">\n {onShowSessions ? (\n <Button\n variant=\"ghost\"\n size=\"icon-md\"\n data-testid=\"mobile-sessions-open\"\n title=\"Show sessions\"\n aria-label=\"Show sessions\"\n onClick={onShowSessions}\n className={`shrink-0 text-po-dim ${navigationToggle === 'mobile' ? 'md:hidden' : ''}`}\n >\n <FolderIcon className=\"size-4\" />\n </Button>\n ) : null}\n {/* With no session there is no name to show, so the bar does not report\n 'Untitled' for something that was never started. */}\n {noSessions ? null : renaming && activeId !== null ? (\n <Input\n data-testid=\"session-title-input\"\n value={draft}\n autoFocus\n aria-label=\"Session name\"\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') commitRename();\n if (event.key === 'Escape') setRenaming(false);\n }}\n onBlur={commitRename}\n className=\"h-9 w-56 shrink-0 border-po-blue/60 px-1.5 text-lg font-semibold\"\n />\n ) : (\n <Button\n variant=\"ghost\"\n data-testid=\"session-title\"\n title={activeId === null ? undefined : 'Rename this session'}\n disabled={activeId === null}\n onClick={() => {\n setDraft(title);\n setRenaming(true);\n }}\n className=\"block h-9 min-w-0 shrink truncate px-1.5 text-lg leading-9 font-semibold text-po-hi sm:max-w-80 sm:shrink-0\"\n >\n {title}\n </Button>\n )}\n {ownerId !== null ? (\n // Tabs never shrink; past the width the badges leave them, the strip\n // scrolls sideways (scrollbar hidden) instead of running under them.\n // On a phone only the shown tab stays; the rest are in the tabs menu.\n <nav\n aria-label=\"Conversation views\"\n className=\"flex min-w-0 items-center gap-1 overflow-x-auto [scrollbar-width:none] max-sm:shrink-0 max-sm:[&>*:not([data-active=true])]:hidden [&::-webkit-scrollbar]:hidden [&>*]:min-h-8 [&>*]:rounded-md [&>*]:px-3\"\n >\n <NavTab asChild active={view === 'conversation'}>\n <Link\n to=\"/session/$sessionId\"\n params={{ sessionId: ownerId }}\n data-testid=\"tab-conversation\"\n className=\"shrink-0 gap-1.5\"\n >\n <MessageIcon aria-hidden className=\"size-4\" />\n Conversation\n </Link>\n </NavTab>\n {mcpEnabled ? (\n <NavTab asChild active={view === 'mcp'}>\n <Link\n to=\"/session/$sessionId/$tabId\"\n params={{ sessionId: ownerId, tabId: 'mcp' }}\n data-testid=\"tab-mcp\"\n className=\"shrink-0\"\n >\n MCP\n </Link>\n </NavTab>\n ) : null}\n {webTabs(ownerId).map((tab) => (\n <PluginTab key={tab.id} tab={tab} sessionId={ownerId} active={view === tab.id} />\n ))}\n {transientTabs.map((tab) => (\n <TransientTabChip key={tab.id} tab={tab} sessionId={ownerId} active={view === tab.id} />\n ))}\n </nav>\n ) : null}\n </div>\n\n <div className=\"flex shrink-0 items-center\">\n {lockAgent ? (\n <Button\n variant=\"ghost\"\n data-testid=\"agent-lock\"\n size=\"lg\"\n title=\"Stop the local agent and keep it from starting turns\"\n onClick={lockAgent}\n className=\"hidden shrink-0 gap-2 text-po-text sm:inline-flex\"\n >\n <LockIcon className=\"size-4\" />\n Lock agent\n </Button>\n ) : null}\n {ownerId !== null ? (\n <MobileTabsMenu\n sessionId={ownerId}\n view={view}\n mcpEnabled={mcpEnabled}\n transientTabs={transientTabs}\n onLock={lockAgent}\n />\n ) : null}\n {onShowActivity ? (\n <Button\n variant=\"ghost\"\n size=\"icon-md\"\n data-testid=\"mobile-activity-open\"\n data-active={backgroundWorkActive}\n title={backgroundWorkActive ? 'Background work is running' : 'Show activity'}\n aria-label=\"Show activity\"\n onClick={onShowActivity}\n className={`shrink-0 ${activityToggle === 'mobile' ? 'lg:hidden' : ''} ${backgroundWorkActive ? 'text-po-yellow' : 'text-po-dim'}`}\n >\n <ActivityIcon className=\"size-4\" />\n </Button>\n ) : null}\n </div>\n </header>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TopBar.cjs","names":["NavTab","Link","NavTabBadge","Button","closeTransientTab","CloseIcon","useNavigate","webTabs","DropdownMenu","DropdownMenuTrigger","GridIcon","DropdownMenuContent","DropdownMenuLabel","DropdownMenuRadioGroup","DropdownMenuRadioItem","DropdownMenuSeparator","DropdownMenuItem","LockIcon","useActiveSessionMeta","useActiveSession","useStore","sessionsStore","useNoSessions","useTransientTabs","sessionStoreFor","useDashboardState","useActivityGroups","setAgentLock","FolderIcon","Input","MessageIcon","ActivityIcon"],"sources":["../../../../src/web/features/status/TopBar.tsx"],"sourcesContent":["import type { TabContribution, TransientTab } from '@agimon-ai/po-core/web';\nimport {\n ActivityIcon,\n Button,\n CloseIcon,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n FolderIcon,\n GridIcon,\n Input,\n LockIcon,\n MessageIcon,\n NavTab,\n NavTabBadge,\n} from '@agimon-ai/po-web-components';\nimport { Link, useNavigate } from '@tanstack/react-router';\nimport { useStore } from '@tanstack/react-store';\n\nimport { useActivityGroups } from '../../lib/composition';\nimport { webTabs } from '../../lib/pluginRegistry';\nimport { useDashboardState } from '../../stores/dashboardStateStore';\nimport { sessionsStore, useActiveSessionMeta, useNoSessions } from '../../stores/sessionsStore';\nimport { renameSession, sessionStoreFor, setAgentLock, useActiveSession } from '../../stores/sessionStore';\nimport { closeTransientTab, useTransientTabs } from '../../stores/transientTabsStore';\n/** One registry tab: the badge hook is stable per tab, so the call is unconditional. */\nfunction PluginTab({ tab, sessionId, active }: { tab: TabContribution; sessionId: string; active: boolean }) {\n const useBadge = tab.useBadge ?? noBadge;\n const count = useBadge(sessionId);\n return (\n <NavTab asChild active={active}>\n <Link\n to=\"/session/$sessionId/$tabId\"\n params={{ sessionId, tabId: tab.id }}\n data-testid={`tab-${tab.id}`}\n className=\"shrink-0\"\n >\n {tab.label}\n {count > 0 ? (\n <NavTabBadge active={active} data-testid={`tab-${tab.id}-count`}>\n {count}\n </NavTabBadge>\n ) : null}\n </Link>\n </NavTab>\n );\n}\n\nfunction noBadge(): number {\n return 0;\n}\n\n/**\n * A tab a plugin opened at runtime: its link with the close beside it, since\n * a button cannot live inside a link. Closing the shown tab needs no\n * navigation of its own; the page falls back to the conversation once the\n * id is gone.\n */\nfunction TransientTabChip({ tab, sessionId, active }: { tab: TransientTab; sessionId: string; active: boolean }) {\n return (\n <span data-testid={`tab-${tab.id}-chip`} data-active={active} className=\"flex shrink-0 items-center gap-0.5\">\n <NavTab asChild active={active}>\n <Link\n to=\"/session/$sessionId/$tabId\"\n params={{ sessionId, tabId: tab.id }}\n data-testid={`tab-${tab.id}`}\n title={tab.label}\n className=\"block min-w-0 max-w-[160px] truncate\"\n >\n {tab.label}\n </Link>\n </NavTab>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n data-testid={`tab-${tab.id}-close`}\n title=\"close this tab\"\n onClick={() => closeTransientTab(sessionId, tab.id)}\n className=\"h-5 w-5 shrink-0\"\n >\n <CloseIcon className=\"h-2.5 w-2.5\" />\n </Button>\n </span>\n );\n}\n\n/** A plugin tab's count inside the menu; one component per tab keeps the badge hook unconditional. */\nfunction MenuBadge({ tab, sessionId }: { tab: TabContribution; sessionId: string }) {\n const count = (tab.useBadge ?? noBadge)(sessionId);\n return count > 0 ? <NavTabBadge className=\"ml-auto\">{count}</NavTabBadge> : null;\n}\n\n// The shown tab reads jade like the strip's active tab; 44px rows for touch.\nconst MENU_TAB_CLASS = 'min-h-11 data-[state=checked]:font-semibold data-[state=checked]:text-po-green';\n/**\n * Below sm the strip keeps only the shown tab, so the title, that tab and the\n * actions share one row; every tab, and the lock, are one tap away here.\n */\nfunction MobileTabsMenu({\n sessionId,\n view,\n mcpEnabled,\n transientTabs,\n onLock,\n}: {\n sessionId: string;\n view: string;\n mcpEnabled: boolean;\n transientTabs: readonly TransientTab[];\n onLock?: () => void;\n}) {\n const navigate = useNavigate();\n const pluginTabs = webTabs(sessionId);\n const open = (tabId: string): void => {\n if (tabId === 'conversation') void navigate({ to: '/session/$sessionId', params: { sessionId } });\n else void navigate({ to: '/session/$sessionId/$tabId', params: { sessionId, tabId } });\n };\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-md\"\n data-testid=\"mobile-tabs-open\"\n title=\"Show tabs\"\n aria-label=\"Show tabs\"\n className=\"shrink-0 text-po-dim sm:hidden\"\n >\n <GridIcon className=\"size-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent data-testid=\"mobile-tabs-menu\" className=\"min-w-56 max-w-[calc(100vw-24px)]\">\n <DropdownMenuLabel>Tabs</DropdownMenuLabel>\n <DropdownMenuRadioGroup value={view} onValueChange={open}>\n <DropdownMenuRadioItem value=\"conversation\" data-testid=\"mobile-tab-conversation\" className={MENU_TAB_CLASS}>\n Conversation\n </DropdownMenuRadioItem>\n {mcpEnabled ? (\n <DropdownMenuRadioItem value=\"mcp\" data-testid=\"mobile-tab-mcp\" className={MENU_TAB_CLASS}>\n MCP\n </DropdownMenuRadioItem>\n ) : null}\n {pluginTabs.map((tab) => (\n <DropdownMenuRadioItem\n key={tab.id}\n value={tab.id}\n data-testid={`mobile-tab-${tab.id}`}\n className={MENU_TAB_CLASS}\n >\n <span className=\"min-w-0 truncate\">{tab.label}</span>\n <MenuBadge tab={tab} sessionId={sessionId} />\n </DropdownMenuRadioItem>\n ))}\n {transientTabs.map((tab) => (\n <DropdownMenuRadioItem\n key={tab.id}\n value={tab.id}\n data-testid={`mobile-tab-${tab.id}`}\n className={MENU_TAB_CLASS}\n >\n <span className=\"min-w-0 truncate\">{tab.label}</span>\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n {onLock ? (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem data-testid=\"mobile-agent-lock\" onSelect={onLock} className=\"min-h-11 pl-2\">\n <LockIcon aria-hidden className=\"size-4 shrink-0\" />\n Lock agent\n </DropdownMenuItem>\n </>\n ) : null}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\nexport function TopBar({\n view = 'conversation',\n mcpEnabled = false,\n onShowSessions,\n onShowActivity,\n navigationToggle = 'mobile',\n activityToggle = 'mobile',\n}: {\n view?: string;\n mcpEnabled?: boolean;\n onShowSessions?: () => void;\n onShowActivity?: () => void;\n navigationToggle?: 'mobile' | 'always';\n activityToggle?: 'mobile' | 'always';\n}) {\n const meta = useActiveSessionMeta();\n const session = useActiveSession((state) => state);\n const activeId = useStore(sessionsStore, (state) => state.activeId);\n const noSessions = useNoSessions();\n const ownerId = useStore(sessionsStore, (state) => state.tabOwnerId ?? state.activeId);\n const transientTabs = useTransientTabs(ownerId);\n const ownerName = useStore(sessionsStore, (state) =>\n ownerId === null ? undefined : state.byId[ownerId]?.summary.name,\n );\n const ownerAgentName = useStore(sessionStoreFor(ownerId), (state) => state.agent?.sessionName);\n const title = ownerName || ownerAgentName || 'Untitled';\n const [renaming, setRenaming] = useDashboardState(`session-title/${ownerId}/open`, false);\n const [draft, setDraft] = useDashboardState(`session-title/${ownerId}/draft`, '');\n const backgroundWorkActive = useActivityGroups(session.statuses, session.widgets, activeId).some(\n (group) => group.active,\n );\n // The name lives in the agent, so the rail's rename and this one are the\n // same act through the same door; the new name arrives back on the socket.\n const commitRename = (): void => {\n const next = draft.trim();\n if (next && next !== title) renameSession(next, ownerId);\n setRenaming(false);\n };\n const lockAgent =\n activeId !== null && meta?.summary.dormant !== true && !session.agentLocked\n ? () => setAgentLock(true, activeId)\n : undefined;\n\n return (\n <header\n data-testid=\"top-bar\"\n className=\"flex min-h-16 shrink-0 items-center justify-between gap-2 border-b border-po-border-soft px-3 py-2 sm:gap-3 sm:px-6\"\n >\n <div data-testid=\"session-switcher\" className=\"flex min-w-0 flex-1 items-center gap-2 sm:gap-3\">\n {onShowSessions ? (\n <Button\n variant=\"ghost\"\n size=\"icon-md\"\n data-testid=\"mobile-sessions-open\"\n title=\"Show sessions\"\n aria-label=\"Show sessions\"\n onClick={onShowSessions}\n className={`shrink-0 text-po-dim ${navigationToggle === 'mobile' ? 'md:hidden' : ''}`}\n >\n <FolderIcon className=\"size-4\" />\n </Button>\n ) : null}\n {/* With no session there is no name to show, so the bar does not report\n 'Untitled' for something that was never started. */}\n {noSessions ? null : renaming && ownerId !== null ? (\n <Input\n data-testid=\"session-title-input\"\n value={draft}\n autoFocus\n aria-label=\"Session name\"\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') commitRename();\n if (event.key === 'Escape') setRenaming(false);\n }}\n onBlur={commitRename}\n className=\"h-9 w-56 shrink-0 border-po-blue/60 px-1.5 text-lg font-semibold\"\n />\n ) : (\n <Button\n variant=\"ghost\"\n data-testid=\"session-title\"\n title={ownerId === null ? undefined : 'Rename this session'}\n disabled={ownerId === null}\n onClick={() => {\n setDraft(title);\n setRenaming(true);\n }}\n className=\"block h-9 min-w-0 shrink truncate px-1.5 text-lg leading-9 font-semibold text-po-hi sm:max-w-80 sm:shrink-0\"\n >\n {title}\n </Button>\n )}\n {ownerId !== null ? (\n // Tabs never shrink; past the width the badges leave them, the strip\n // scrolls sideways (scrollbar hidden) instead of running under them.\n // On a phone only the shown tab stays; the rest are in the tabs menu.\n <nav\n aria-label=\"Conversation views\"\n className=\"flex min-w-0 items-center gap-1 overflow-x-auto [scrollbar-width:none] max-sm:shrink-0 max-sm:[&>*:not([data-active=true])]:hidden [&::-webkit-scrollbar]:hidden [&>*]:min-h-8 [&>*]:rounded-md [&>*]:px-3\"\n >\n <NavTab asChild active={view === 'conversation'}>\n <Link\n to=\"/session/$sessionId\"\n params={{ sessionId: ownerId }}\n data-testid=\"tab-conversation\"\n className=\"shrink-0 gap-1.5\"\n >\n <MessageIcon aria-hidden className=\"size-4\" />\n Conversation\n </Link>\n </NavTab>\n {mcpEnabled ? (\n <NavTab asChild active={view === 'mcp'}>\n <Link\n to=\"/session/$sessionId/$tabId\"\n params={{ sessionId: ownerId, tabId: 'mcp' }}\n data-testid=\"tab-mcp\"\n className=\"shrink-0\"\n >\n MCP\n </Link>\n </NavTab>\n ) : null}\n {webTabs(ownerId).map((tab) => (\n <PluginTab key={tab.id} tab={tab} sessionId={ownerId} active={view === tab.id} />\n ))}\n {transientTabs.map((tab) => (\n <TransientTabChip key={tab.id} tab={tab} sessionId={ownerId} active={view === tab.id} />\n ))}\n </nav>\n ) : null}\n </div>\n\n <div className=\"flex shrink-0 items-center\">\n {lockAgent ? (\n <Button\n variant=\"ghost\"\n data-testid=\"agent-lock\"\n size=\"lg\"\n title=\"Stop the local agent and keep it from starting turns\"\n onClick={lockAgent}\n className=\"hidden shrink-0 gap-2 text-po-text sm:inline-flex\"\n >\n <LockIcon className=\"size-4\" />\n Lock agent\n </Button>\n ) : null}\n {ownerId !== null ? (\n <MobileTabsMenu\n sessionId={ownerId}\n view={view}\n mcpEnabled={mcpEnabled}\n transientTabs={transientTabs}\n onLock={lockAgent}\n />\n ) : null}\n {onShowActivity ? (\n <Button\n variant=\"ghost\"\n size=\"icon-md\"\n data-testid=\"mobile-activity-open\"\n data-active={backgroundWorkActive}\n title={backgroundWorkActive ? 'Background work is running' : 'Show activity'}\n aria-label=\"Show activity\"\n onClick={onShowActivity}\n className={`shrink-0 ${activityToggle === 'mobile' ? 'lg:hidden' : ''} ${backgroundWorkActive ? 'text-po-yellow' : 'text-po-dim'}`}\n >\n <ActivityIcon className=\"size-4\" />\n </Button>\n ) : null}\n </div>\n </header>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { webTabs } from "../../lib/pluginRegistry.mjs";
|
|
2
2
|
import { useActivityGroups } from "../../lib/composition.mjs";
|
|
3
3
|
import { sessionsStore, useActiveSessionMeta, useNoSessions } from "../../stores/sessionsStore.mjs";
|
|
4
|
-
import { renameSession, setAgentLock, useActiveSession } from "../../stores/sessionStore.mjs";
|
|
4
|
+
import { renameSession, sessionStoreFor, setAgentLock, useActiveSession } from "../../stores/sessionStore.mjs";
|
|
5
5
|
import { closeTransientTab, useTransientTabs } from "../../stores/transientTabsStore.mjs";
|
|
6
6
|
import { useDashboardState } from "../../stores/dashboardStateStore.mjs";
|
|
7
7
|
import { Link, useNavigate } from "@tanstack/react-router";
|
|
@@ -173,13 +173,15 @@ function TopBar({ view = "conversation", mcpEnabled = false, onShowSessions, onS
|
|
|
173
173
|
const noSessions = useNoSessions();
|
|
174
174
|
const ownerId = useStore(sessionsStore, (state) => state.tabOwnerId ?? state.activeId);
|
|
175
175
|
const transientTabs = useTransientTabs(ownerId);
|
|
176
|
-
const
|
|
177
|
-
const
|
|
178
|
-
const
|
|
176
|
+
const ownerName = useStore(sessionsStore, (state) => ownerId === null ? void 0 : state.byId[ownerId]?.summary.name);
|
|
177
|
+
const ownerAgentName = useStore(sessionStoreFor(ownerId), (state) => state.agent?.sessionName);
|
|
178
|
+
const title = ownerName || ownerAgentName || "Untitled";
|
|
179
|
+
const [renaming, setRenaming] = useDashboardState(`session-title/${ownerId}/open`, false);
|
|
180
|
+
const [draft, setDraft] = useDashboardState(`session-title/${ownerId}/draft`, "");
|
|
179
181
|
const backgroundWorkActive = useActivityGroups(session.statuses, session.widgets, activeId).some((group) => group.active);
|
|
180
182
|
const commitRename = () => {
|
|
181
183
|
const next = draft.trim();
|
|
182
|
-
if (next && next !== title) renameSession(next,
|
|
184
|
+
if (next && next !== title) renameSession(next, ownerId);
|
|
183
185
|
setRenaming(false);
|
|
184
186
|
};
|
|
185
187
|
const lockAgent = activeId !== null && meta?.summary.dormant !== true && !session.agentLocked ? () => setAgentLock(true, activeId) : void 0;
|
|
@@ -200,7 +202,7 @@ function TopBar({ view = "conversation", mcpEnabled = false, onShowSessions, onS
|
|
|
200
202
|
className: `shrink-0 text-po-dim ${navigationToggle === "mobile" ? "md:hidden" : ""}`,
|
|
201
203
|
children: /* @__PURE__ */ jsx(FolderIcon, { className: "size-4" })
|
|
202
204
|
}) : null,
|
|
203
|
-
noSessions ? null : renaming &&
|
|
205
|
+
noSessions ? null : renaming && ownerId !== null ? /* @__PURE__ */ jsx(Input, {
|
|
204
206
|
"data-testid": "session-title-input",
|
|
205
207
|
value: draft,
|
|
206
208
|
autoFocus: true,
|
|
@@ -215,8 +217,8 @@ function TopBar({ view = "conversation", mcpEnabled = false, onShowSessions, onS
|
|
|
215
217
|
}) : /* @__PURE__ */ jsx(Button, {
|
|
216
218
|
variant: "ghost",
|
|
217
219
|
"data-testid": "session-title",
|
|
218
|
-
title:
|
|
219
|
-
disabled:
|
|
220
|
+
title: ownerId === null ? void 0 : "Rename this session",
|
|
221
|
+
disabled: ownerId === null,
|
|
220
222
|
onClick: () => {
|
|
221
223
|
setDraft(title);
|
|
222
224
|
setRenaming(true);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TopBar.mjs","names":[],"sources":["../../../../src/web/features/status/TopBar.tsx"],"sourcesContent":["import type { TabContribution, TransientTab } from '@agimon-ai/po-core/web';\nimport {\n ActivityIcon,\n Button,\n CloseIcon,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n FolderIcon,\n GridIcon,\n Input,\n LockIcon,\n MessageIcon,\n NavTab,\n NavTabBadge,\n} from '@agimon-ai/po-web-components';\nimport { Link, useNavigate } from '@tanstack/react-router';\nimport { useStore } from '@tanstack/react-store';\n\nimport { useActivityGroups } from '../../lib/composition';\nimport { webTabs } from '../../lib/pluginRegistry';\nimport { useDashboardState } from '../../stores/dashboardStateStore';\nimport { sessionsStore, useActiveSessionMeta, useNoSessions } from '../../stores/sessionsStore';\nimport { renameSession, setAgentLock, useActiveSession } from '../../stores/sessionStore';\nimport { closeTransientTab, useTransientTabs } from '../../stores/transientTabsStore';\n/** One registry tab: the badge hook is stable per tab, so the call is unconditional. */\nfunction PluginTab({ tab, sessionId, active }: { tab: TabContribution; sessionId: string; active: boolean }) {\n const useBadge = tab.useBadge ?? noBadge;\n const count = useBadge(sessionId);\n return (\n <NavTab asChild active={active}>\n <Link\n to=\"/session/$sessionId/$tabId\"\n params={{ sessionId, tabId: tab.id }}\n data-testid={`tab-${tab.id}`}\n className=\"shrink-0\"\n >\n {tab.label}\n {count > 0 ? (\n <NavTabBadge active={active} data-testid={`tab-${tab.id}-count`}>\n {count}\n </NavTabBadge>\n ) : null}\n </Link>\n </NavTab>\n );\n}\n\nfunction noBadge(): number {\n return 0;\n}\n\n/**\n * A tab a plugin opened at runtime: its link with the close beside it, since\n * a button cannot live inside a link. Closing the shown tab needs no\n * navigation of its own; the page falls back to the conversation once the\n * id is gone.\n */\nfunction TransientTabChip({ tab, sessionId, active }: { tab: TransientTab; sessionId: string; active: boolean }) {\n return (\n <span data-testid={`tab-${tab.id}-chip`} data-active={active} className=\"flex shrink-0 items-center gap-0.5\">\n <NavTab asChild active={active}>\n <Link\n to=\"/session/$sessionId/$tabId\"\n params={{ sessionId, tabId: tab.id }}\n data-testid={`tab-${tab.id}`}\n title={tab.label}\n className=\"block min-w-0 max-w-[160px] truncate\"\n >\n {tab.label}\n </Link>\n </NavTab>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n data-testid={`tab-${tab.id}-close`}\n title=\"close this tab\"\n onClick={() => closeTransientTab(sessionId, tab.id)}\n className=\"h-5 w-5 shrink-0\"\n >\n <CloseIcon className=\"h-2.5 w-2.5\" />\n </Button>\n </span>\n );\n}\n\n/** A plugin tab's count inside the menu; one component per tab keeps the badge hook unconditional. */\nfunction MenuBadge({ tab, sessionId }: { tab: TabContribution; sessionId: string }) {\n const count = (tab.useBadge ?? noBadge)(sessionId);\n return count > 0 ? <NavTabBadge className=\"ml-auto\">{count}</NavTabBadge> : null;\n}\n\n// The shown tab reads jade like the strip's active tab; 44px rows for touch.\nconst MENU_TAB_CLASS = 'min-h-11 data-[state=checked]:font-semibold data-[state=checked]:text-po-green';\n/**\n * Below sm the strip keeps only the shown tab, so the title, that tab and the\n * actions share one row; every tab, and the lock, are one tap away here.\n */\nfunction MobileTabsMenu({\n sessionId,\n view,\n mcpEnabled,\n transientTabs,\n onLock,\n}: {\n sessionId: string;\n view: string;\n mcpEnabled: boolean;\n transientTabs: readonly TransientTab[];\n onLock?: () => void;\n}) {\n const navigate = useNavigate();\n const pluginTabs = webTabs(sessionId);\n const open = (tabId: string): void => {\n if (tabId === 'conversation') void navigate({ to: '/session/$sessionId', params: { sessionId } });\n else void navigate({ to: '/session/$sessionId/$tabId', params: { sessionId, tabId } });\n };\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-md\"\n data-testid=\"mobile-tabs-open\"\n title=\"Show tabs\"\n aria-label=\"Show tabs\"\n className=\"shrink-0 text-po-dim sm:hidden\"\n >\n <GridIcon className=\"size-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent data-testid=\"mobile-tabs-menu\" className=\"min-w-56 max-w-[calc(100vw-24px)]\">\n <DropdownMenuLabel>Tabs</DropdownMenuLabel>\n <DropdownMenuRadioGroup value={view} onValueChange={open}>\n <DropdownMenuRadioItem value=\"conversation\" data-testid=\"mobile-tab-conversation\" className={MENU_TAB_CLASS}>\n Conversation\n </DropdownMenuRadioItem>\n {mcpEnabled ? (\n <DropdownMenuRadioItem value=\"mcp\" data-testid=\"mobile-tab-mcp\" className={MENU_TAB_CLASS}>\n MCP\n </DropdownMenuRadioItem>\n ) : null}\n {pluginTabs.map((tab) => (\n <DropdownMenuRadioItem\n key={tab.id}\n value={tab.id}\n data-testid={`mobile-tab-${tab.id}`}\n className={MENU_TAB_CLASS}\n >\n <span className=\"min-w-0 truncate\">{tab.label}</span>\n <MenuBadge tab={tab} sessionId={sessionId} />\n </DropdownMenuRadioItem>\n ))}\n {transientTabs.map((tab) => (\n <DropdownMenuRadioItem\n key={tab.id}\n value={tab.id}\n data-testid={`mobile-tab-${tab.id}`}\n className={MENU_TAB_CLASS}\n >\n <span className=\"min-w-0 truncate\">{tab.label}</span>\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n {onLock ? (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem data-testid=\"mobile-agent-lock\" onSelect={onLock} className=\"min-h-11 pl-2\">\n <LockIcon aria-hidden className=\"size-4 shrink-0\" />\n Lock agent\n </DropdownMenuItem>\n </>\n ) : null}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\nexport function TopBar({\n view = 'conversation',\n mcpEnabled = false,\n onShowSessions,\n onShowActivity,\n navigationToggle = 'mobile',\n activityToggle = 'mobile',\n}: {\n view?: string;\n mcpEnabled?: boolean;\n onShowSessions?: () => void;\n onShowActivity?: () => void;\n navigationToggle?: 'mobile' | 'always';\n activityToggle?: 'mobile' | 'always';\n}) {\n const meta = useActiveSessionMeta();\n const session = useActiveSession((state) => state);\n const activeId = useStore(sessionsStore, (state) => state.activeId);\n const noSessions = useNoSessions();\n const ownerId = useStore(sessionsStore, (state) => state.tabOwnerId ?? state.activeId);\n const transientTabs = useTransientTabs(ownerId);\n const title = meta?.summary.name || session.agent?.sessionName || 'Untitled';\n const [renaming, setRenaming] = useDashboardState(`session-title/${activeId}/open`, false);\n const [draft, setDraft] = useDashboardState(`session-title/${activeId}/draft`, '');\n const backgroundWorkActive = useActivityGroups(session.statuses, session.widgets, activeId).some(\n (group) => group.active,\n );\n // The name lives in the agent, so the rail's rename and this one are the\n // same act through the same door; the new name arrives back on the socket.\n const commitRename = (): void => {\n const next = draft.trim();\n if (next && next !== title) renameSession(next, activeId);\n setRenaming(false);\n };\n const lockAgent =\n activeId !== null && meta?.summary.dormant !== true && !session.agentLocked\n ? () => setAgentLock(true, activeId)\n : undefined;\n\n return (\n <header\n data-testid=\"top-bar\"\n className=\"flex min-h-16 shrink-0 items-center justify-between gap-2 border-b border-po-border-soft px-3 py-2 sm:gap-3 sm:px-6\"\n >\n <div data-testid=\"session-switcher\" className=\"flex min-w-0 flex-1 items-center gap-2 sm:gap-3\">\n {onShowSessions ? (\n <Button\n variant=\"ghost\"\n size=\"icon-md\"\n data-testid=\"mobile-sessions-open\"\n title=\"Show sessions\"\n aria-label=\"Show sessions\"\n onClick={onShowSessions}\n className={`shrink-0 text-po-dim ${navigationToggle === 'mobile' ? 'md:hidden' : ''}`}\n >\n <FolderIcon className=\"size-4\" />\n </Button>\n ) : null}\n {/* With no session there is no name to show, so the bar does not report\n 'Untitled' for something that was never started. */}\n {noSessions ? null : renaming && activeId !== null ? (\n <Input\n data-testid=\"session-title-input\"\n value={draft}\n autoFocus\n aria-label=\"Session name\"\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') commitRename();\n if (event.key === 'Escape') setRenaming(false);\n }}\n onBlur={commitRename}\n className=\"h-9 w-56 shrink-0 border-po-blue/60 px-1.5 text-lg font-semibold\"\n />\n ) : (\n <Button\n variant=\"ghost\"\n data-testid=\"session-title\"\n title={activeId === null ? undefined : 'Rename this session'}\n disabled={activeId === null}\n onClick={() => {\n setDraft(title);\n setRenaming(true);\n }}\n className=\"block h-9 min-w-0 shrink truncate px-1.5 text-lg leading-9 font-semibold text-po-hi sm:max-w-80 sm:shrink-0\"\n >\n {title}\n </Button>\n )}\n {ownerId !== null ? (\n // Tabs never shrink; past the width the badges leave them, the strip\n // scrolls sideways (scrollbar hidden) instead of running under them.\n // On a phone only the shown tab stays; the rest are in the tabs menu.\n <nav\n aria-label=\"Conversation views\"\n className=\"flex min-w-0 items-center gap-1 overflow-x-auto [scrollbar-width:none] max-sm:shrink-0 max-sm:[&>*:not([data-active=true])]:hidden [&::-webkit-scrollbar]:hidden [&>*]:min-h-8 [&>*]:rounded-md [&>*]:px-3\"\n >\n <NavTab asChild active={view === 'conversation'}>\n <Link\n to=\"/session/$sessionId\"\n params={{ sessionId: ownerId }}\n data-testid=\"tab-conversation\"\n className=\"shrink-0 gap-1.5\"\n >\n <MessageIcon aria-hidden className=\"size-4\" />\n Conversation\n </Link>\n </NavTab>\n {mcpEnabled ? (\n <NavTab asChild active={view === 'mcp'}>\n <Link\n to=\"/session/$sessionId/$tabId\"\n params={{ sessionId: ownerId, tabId: 'mcp' }}\n data-testid=\"tab-mcp\"\n className=\"shrink-0\"\n >\n MCP\n </Link>\n </NavTab>\n ) : null}\n {webTabs(ownerId).map((tab) => (\n <PluginTab key={tab.id} tab={tab} sessionId={ownerId} active={view === tab.id} />\n ))}\n {transientTabs.map((tab) => (\n <TransientTabChip key={tab.id} tab={tab} sessionId={ownerId} active={view === tab.id} />\n ))}\n </nav>\n ) : null}\n </div>\n\n <div className=\"flex shrink-0 items-center\">\n {lockAgent ? (\n <Button\n variant=\"ghost\"\n data-testid=\"agent-lock\"\n size=\"lg\"\n title=\"Stop the local agent and keep it from starting turns\"\n onClick={lockAgent}\n className=\"hidden shrink-0 gap-2 text-po-text sm:inline-flex\"\n >\n <LockIcon className=\"size-4\" />\n Lock agent\n </Button>\n ) : null}\n {ownerId !== null ? (\n <MobileTabsMenu\n sessionId={ownerId}\n view={view}\n mcpEnabled={mcpEnabled}\n transientTabs={transientTabs}\n onLock={lockAgent}\n />\n ) : null}\n {onShowActivity ? (\n <Button\n variant=\"ghost\"\n size=\"icon-md\"\n data-testid=\"mobile-activity-open\"\n data-active={backgroundWorkActive}\n title={backgroundWorkActive ? 'Background work is running' : 'Show activity'}\n aria-label=\"Show activity\"\n onClick={onShowActivity}\n className={`shrink-0 ${activityToggle === 'mobile' ? 'lg:hidden' : ''} ${backgroundWorkActive ? 'text-po-yellow' : 'text-po-dim'}`}\n >\n <ActivityIcon className=\"size-4\" />\n </Button>\n ) : null}\n </div>\n </header>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TopBar.mjs","names":[],"sources":["../../../../src/web/features/status/TopBar.tsx"],"sourcesContent":["import type { TabContribution, TransientTab } from '@agimon-ai/po-core/web';\nimport {\n ActivityIcon,\n Button,\n CloseIcon,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n FolderIcon,\n GridIcon,\n Input,\n LockIcon,\n MessageIcon,\n NavTab,\n NavTabBadge,\n} from '@agimon-ai/po-web-components';\nimport { Link, useNavigate } from '@tanstack/react-router';\nimport { useStore } from '@tanstack/react-store';\n\nimport { useActivityGroups } from '../../lib/composition';\nimport { webTabs } from '../../lib/pluginRegistry';\nimport { useDashboardState } from '../../stores/dashboardStateStore';\nimport { sessionsStore, useActiveSessionMeta, useNoSessions } from '../../stores/sessionsStore';\nimport { renameSession, sessionStoreFor, setAgentLock, useActiveSession } from '../../stores/sessionStore';\nimport { closeTransientTab, useTransientTabs } from '../../stores/transientTabsStore';\n/** One registry tab: the badge hook is stable per tab, so the call is unconditional. */\nfunction PluginTab({ tab, sessionId, active }: { tab: TabContribution; sessionId: string; active: boolean }) {\n const useBadge = tab.useBadge ?? noBadge;\n const count = useBadge(sessionId);\n return (\n <NavTab asChild active={active}>\n <Link\n to=\"/session/$sessionId/$tabId\"\n params={{ sessionId, tabId: tab.id }}\n data-testid={`tab-${tab.id}`}\n className=\"shrink-0\"\n >\n {tab.label}\n {count > 0 ? (\n <NavTabBadge active={active} data-testid={`tab-${tab.id}-count`}>\n {count}\n </NavTabBadge>\n ) : null}\n </Link>\n </NavTab>\n );\n}\n\nfunction noBadge(): number {\n return 0;\n}\n\n/**\n * A tab a plugin opened at runtime: its link with the close beside it, since\n * a button cannot live inside a link. Closing the shown tab needs no\n * navigation of its own; the page falls back to the conversation once the\n * id is gone.\n */\nfunction TransientTabChip({ tab, sessionId, active }: { tab: TransientTab; sessionId: string; active: boolean }) {\n return (\n <span data-testid={`tab-${tab.id}-chip`} data-active={active} className=\"flex shrink-0 items-center gap-0.5\">\n <NavTab asChild active={active}>\n <Link\n to=\"/session/$sessionId/$tabId\"\n params={{ sessionId, tabId: tab.id }}\n data-testid={`tab-${tab.id}`}\n title={tab.label}\n className=\"block min-w-0 max-w-[160px] truncate\"\n >\n {tab.label}\n </Link>\n </NavTab>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n data-testid={`tab-${tab.id}-close`}\n title=\"close this tab\"\n onClick={() => closeTransientTab(sessionId, tab.id)}\n className=\"h-5 w-5 shrink-0\"\n >\n <CloseIcon className=\"h-2.5 w-2.5\" />\n </Button>\n </span>\n );\n}\n\n/** A plugin tab's count inside the menu; one component per tab keeps the badge hook unconditional. */\nfunction MenuBadge({ tab, sessionId }: { tab: TabContribution; sessionId: string }) {\n const count = (tab.useBadge ?? noBadge)(sessionId);\n return count > 0 ? <NavTabBadge className=\"ml-auto\">{count}</NavTabBadge> : null;\n}\n\n// The shown tab reads jade like the strip's active tab; 44px rows for touch.\nconst MENU_TAB_CLASS = 'min-h-11 data-[state=checked]:font-semibold data-[state=checked]:text-po-green';\n/**\n * Below sm the strip keeps only the shown tab, so the title, that tab and the\n * actions share one row; every tab, and the lock, are one tap away here.\n */\nfunction MobileTabsMenu({\n sessionId,\n view,\n mcpEnabled,\n transientTabs,\n onLock,\n}: {\n sessionId: string;\n view: string;\n mcpEnabled: boolean;\n transientTabs: readonly TransientTab[];\n onLock?: () => void;\n}) {\n const navigate = useNavigate();\n const pluginTabs = webTabs(sessionId);\n const open = (tabId: string): void => {\n if (tabId === 'conversation') void navigate({ to: '/session/$sessionId', params: { sessionId } });\n else void navigate({ to: '/session/$sessionId/$tabId', params: { sessionId, tabId } });\n };\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-md\"\n data-testid=\"mobile-tabs-open\"\n title=\"Show tabs\"\n aria-label=\"Show tabs\"\n className=\"shrink-0 text-po-dim sm:hidden\"\n >\n <GridIcon className=\"size-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent data-testid=\"mobile-tabs-menu\" className=\"min-w-56 max-w-[calc(100vw-24px)]\">\n <DropdownMenuLabel>Tabs</DropdownMenuLabel>\n <DropdownMenuRadioGroup value={view} onValueChange={open}>\n <DropdownMenuRadioItem value=\"conversation\" data-testid=\"mobile-tab-conversation\" className={MENU_TAB_CLASS}>\n Conversation\n </DropdownMenuRadioItem>\n {mcpEnabled ? (\n <DropdownMenuRadioItem value=\"mcp\" data-testid=\"mobile-tab-mcp\" className={MENU_TAB_CLASS}>\n MCP\n </DropdownMenuRadioItem>\n ) : null}\n {pluginTabs.map((tab) => (\n <DropdownMenuRadioItem\n key={tab.id}\n value={tab.id}\n data-testid={`mobile-tab-${tab.id}`}\n className={MENU_TAB_CLASS}\n >\n <span className=\"min-w-0 truncate\">{tab.label}</span>\n <MenuBadge tab={tab} sessionId={sessionId} />\n </DropdownMenuRadioItem>\n ))}\n {transientTabs.map((tab) => (\n <DropdownMenuRadioItem\n key={tab.id}\n value={tab.id}\n data-testid={`mobile-tab-${tab.id}`}\n className={MENU_TAB_CLASS}\n >\n <span className=\"min-w-0 truncate\">{tab.label}</span>\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n {onLock ? (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem data-testid=\"mobile-agent-lock\" onSelect={onLock} className=\"min-h-11 pl-2\">\n <LockIcon aria-hidden className=\"size-4 shrink-0\" />\n Lock agent\n </DropdownMenuItem>\n </>\n ) : null}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\nexport function TopBar({\n view = 'conversation',\n mcpEnabled = false,\n onShowSessions,\n onShowActivity,\n navigationToggle = 'mobile',\n activityToggle = 'mobile',\n}: {\n view?: string;\n mcpEnabled?: boolean;\n onShowSessions?: () => void;\n onShowActivity?: () => void;\n navigationToggle?: 'mobile' | 'always';\n activityToggle?: 'mobile' | 'always';\n}) {\n const meta = useActiveSessionMeta();\n const session = useActiveSession((state) => state);\n const activeId = useStore(sessionsStore, (state) => state.activeId);\n const noSessions = useNoSessions();\n const ownerId = useStore(sessionsStore, (state) => state.tabOwnerId ?? state.activeId);\n const transientTabs = useTransientTabs(ownerId);\n const ownerName = useStore(sessionsStore, (state) =>\n ownerId === null ? undefined : state.byId[ownerId]?.summary.name,\n );\n const ownerAgentName = useStore(sessionStoreFor(ownerId), (state) => state.agent?.sessionName);\n const title = ownerName || ownerAgentName || 'Untitled';\n const [renaming, setRenaming] = useDashboardState(`session-title/${ownerId}/open`, false);\n const [draft, setDraft] = useDashboardState(`session-title/${ownerId}/draft`, '');\n const backgroundWorkActive = useActivityGroups(session.statuses, session.widgets, activeId).some(\n (group) => group.active,\n );\n // The name lives in the agent, so the rail's rename and this one are the\n // same act through the same door; the new name arrives back on the socket.\n const commitRename = (): void => {\n const next = draft.trim();\n if (next && next !== title) renameSession(next, ownerId);\n setRenaming(false);\n };\n const lockAgent =\n activeId !== null && meta?.summary.dormant !== true && !session.agentLocked\n ? () => setAgentLock(true, activeId)\n : undefined;\n\n return (\n <header\n data-testid=\"top-bar\"\n className=\"flex min-h-16 shrink-0 items-center justify-between gap-2 border-b border-po-border-soft px-3 py-2 sm:gap-3 sm:px-6\"\n >\n <div data-testid=\"session-switcher\" className=\"flex min-w-0 flex-1 items-center gap-2 sm:gap-3\">\n {onShowSessions ? (\n <Button\n variant=\"ghost\"\n size=\"icon-md\"\n data-testid=\"mobile-sessions-open\"\n title=\"Show sessions\"\n aria-label=\"Show sessions\"\n onClick={onShowSessions}\n className={`shrink-0 text-po-dim ${navigationToggle === 'mobile' ? 'md:hidden' : ''}`}\n >\n <FolderIcon className=\"size-4\" />\n </Button>\n ) : null}\n {/* With no session there is no name to show, so the bar does not report\n 'Untitled' for something that was never started. */}\n {noSessions ? null : renaming && ownerId !== null ? (\n <Input\n data-testid=\"session-title-input\"\n value={draft}\n autoFocus\n aria-label=\"Session name\"\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') commitRename();\n if (event.key === 'Escape') setRenaming(false);\n }}\n onBlur={commitRename}\n className=\"h-9 w-56 shrink-0 border-po-blue/60 px-1.5 text-lg font-semibold\"\n />\n ) : (\n <Button\n variant=\"ghost\"\n data-testid=\"session-title\"\n title={ownerId === null ? undefined : 'Rename this session'}\n disabled={ownerId === null}\n onClick={() => {\n setDraft(title);\n setRenaming(true);\n }}\n className=\"block h-9 min-w-0 shrink truncate px-1.5 text-lg leading-9 font-semibold text-po-hi sm:max-w-80 sm:shrink-0\"\n >\n {title}\n </Button>\n )}\n {ownerId !== null ? (\n // Tabs never shrink; past the width the badges leave them, the strip\n // scrolls sideways (scrollbar hidden) instead of running under them.\n // On a phone only the shown tab stays; the rest are in the tabs menu.\n <nav\n aria-label=\"Conversation views\"\n className=\"flex min-w-0 items-center gap-1 overflow-x-auto [scrollbar-width:none] max-sm:shrink-0 max-sm:[&>*:not([data-active=true])]:hidden [&::-webkit-scrollbar]:hidden [&>*]:min-h-8 [&>*]:rounded-md [&>*]:px-3\"\n >\n <NavTab asChild active={view === 'conversation'}>\n <Link\n to=\"/session/$sessionId\"\n params={{ sessionId: ownerId }}\n data-testid=\"tab-conversation\"\n className=\"shrink-0 gap-1.5\"\n >\n <MessageIcon aria-hidden className=\"size-4\" />\n Conversation\n </Link>\n </NavTab>\n {mcpEnabled ? (\n <NavTab asChild active={view === 'mcp'}>\n <Link\n to=\"/session/$sessionId/$tabId\"\n params={{ sessionId: ownerId, tabId: 'mcp' }}\n data-testid=\"tab-mcp\"\n className=\"shrink-0\"\n >\n MCP\n </Link>\n </NavTab>\n ) : null}\n {webTabs(ownerId).map((tab) => (\n <PluginTab key={tab.id} tab={tab} sessionId={ownerId} active={view === tab.id} />\n ))}\n {transientTabs.map((tab) => (\n <TransientTabChip key={tab.id} tab={tab} sessionId={ownerId} active={view === tab.id} />\n ))}\n </nav>\n ) : null}\n </div>\n\n <div className=\"flex shrink-0 items-center\">\n {lockAgent ? (\n <Button\n variant=\"ghost\"\n data-testid=\"agent-lock\"\n size=\"lg\"\n title=\"Stop the local agent and keep it from starting turns\"\n onClick={lockAgent}\n className=\"hidden shrink-0 gap-2 text-po-text sm:inline-flex\"\n >\n <LockIcon className=\"size-4\" />\n Lock agent\n </Button>\n ) : null}\n {ownerId !== null ? (\n <MobileTabsMenu\n sessionId={ownerId}\n view={view}\n mcpEnabled={mcpEnabled}\n transientTabs={transientTabs}\n onLock={lockAgent}\n />\n ) : null}\n {onShowActivity ? (\n <Button\n variant=\"ghost\"\n size=\"icon-md\"\n data-testid=\"mobile-activity-open\"\n data-active={backgroundWorkActive}\n title={backgroundWorkActive ? 'Background work is running' : 'Show activity'}\n aria-label=\"Show activity\"\n onClick={onShowActivity}\n className={`shrink-0 ${activityToggle === 'mobile' ? 'lg:hidden' : ''} ${backgroundWorkActive ? 'text-po-yellow' : 'text-po-dim'}`}\n >\n <ActivityIcon className=\"size-4\" />\n </Button>\n ) : null}\n </div>\n </header>\n );\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/po-web-dashboard",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.79",
|
|
4
4
|
"description": "Shared browser state, actions and optional views for Po dashboard templates.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -61,9 +61,9 @@
|
|
|
61
61
|
"access": "public"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@agimon-ai/po-core": "0.0.0-alpha.
|
|
65
|
-
"@agimon-ai/po-web-components": "0.0.0-alpha.
|
|
66
|
-
"@agimon-ai/po-web-security": "0.0.0-alpha.
|
|
64
|
+
"@agimon-ai/po-core": "0.0.0-alpha.79",
|
|
65
|
+
"@agimon-ai/po-web-components": "0.0.0-alpha.79",
|
|
66
|
+
"@agimon-ai/po-web-security": "0.0.0-alpha.79",
|
|
67
67
|
"@earendil-works/chord": "1.4.0",
|
|
68
68
|
"@simplewebauthn/browser": "^14.0.0",
|
|
69
69
|
"@tanstack/react-router": "1.170.45",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"qrcode-generator": "^2.0.4"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
|
-
"@agimon-ai/vibe-lint-plugin-po-web": "0.0.0-alpha.
|
|
76
|
+
"@agimon-ai/vibe-lint-plugin-po-web": "0.0.0-alpha.79",
|
|
77
77
|
"@types/node": "26.6.5",
|
|
78
78
|
"@types/react": "19.3.0",
|
|
79
79
|
"@types/react-dom": "19.3.0",
|