@agimon-ai/po-web-dashboard 0.0.0-alpha.4 → 0.0.0-alpha.6

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.
package/dist/views.cjs CHANGED
@@ -61,6 +61,7 @@ exports.Composer = require_Composer.Composer;
61
61
  exports.ComposerPrompt = require_ComposerPrompt.ComposerPrompt;
62
62
  exports.ContextItemDialog = require_ContextItemDialog.ContextItemDialog;
63
63
  exports.ContextPanel = require_ContextPanel.ContextPanel;
64
+ exports.ContextPanelView = require_ContextPanel.ContextPanelView;
64
65
  exports.ContributedSettings = require_ContributedSettings.ContributedSettings;
65
66
  exports.DevProxyTargets = require_DevProxyTargets.DevProxyTargets;
66
67
  exports.DialogOverlay = require_DialogOverlay.DialogOverlay;
package/dist/views.d.mts CHANGED
@@ -5,7 +5,7 @@ import { QrCode, QrCodeProps } from "./web/components/QrCode.mjs";
5
5
  import { SessionMcp, SessionMcpTools } from "./web/components/SessionMcp.mjs";
6
6
  import { ActivityDock } from "./web/features/activity/ActivityDock.mjs";
7
7
  import { ContextItemDialog, ContextItemDialogProps } from "./web/features/activity/ContextItemDialog.mjs";
8
- import { ContextPanel } from "./web/features/activity/ContextPanel.mjs";
8
+ import { ContextPanel, ContextPanelView } from "./web/features/activity/ContextPanel.mjs";
9
9
  import { DockTabs } from "./web/features/activity/DockTabs.mjs";
10
10
  import { RefusedCard } from "./web/features/connection/RefusedCard.mjs";
11
11
  import { DialogOverlay } from "./web/features/dialogs/DialogOverlay.mjs";
@@ -53,4 +53,4 @@ import { SettingsPanelHost } from "./web/features/settings/SettingsPanelHost.mjs
53
53
  import { TemplateSettings } from "./web/features/settings/TemplateSettings.mjs";
54
54
  import { TunnelSettings } from "./web/features/settings/TunnelSettings.mjs";
55
55
  import { TopBar } from "./web/features/status/TopBar.mjs";
56
- export { ActivityDock, AppearanceSettings, CommandPalette, Composer, ComposerPrompt, ContextItemDialog, ContextItemDialogProps, ContextPanel, ContributedSettings, DevProxyTargets, DialogOverlay, DockTabs, DormantPanel, HandoverProgress, ImageSettings, LoginFlowDialog, METHOD_LABEL, MascotMark, MascotMarkProps, MentionPreviewAsset, MentionPreviews, MessageFileLinkHandler, MessageMarkdown, ModelPopup, NewSessionDialog, NotificationSettings, PairedDeviceList, PairingApprovalDialog, PluginSettings, PluginSurface, ProviderSettings, QrCode, QrCodeProps, QueueSheet, RefusedCard, RemoteAccessDialog, RemoteAccessOptions, RemoteAccessPairing, RemoteControlSettings, RepositorySettings, RepositoryWorkspace, ResumeSessionDialog, SandboxWorkspaces, SandboxWorkspacesProps, SelectionBar, SessionActivity, SessionActivityView, SessionMcp, SessionMcpSettings, SessionMcpTools, SessionRailDialogs, SessionTranscriptView, SettingsMenu, SettingsPanelHost, TemplateSettings, ThreadView, Timeline, ToolCard, ToolRendererBoundary, TopBar, Transcript, TrustedDeviceList, TrustedDeviceListView, TrustedDeviceListViewProps, TunnelSettings, WelcomePanel, addComposerFiles, readComposerFiles, sessionActivity, useNewSessionActions, useSessionRail };
56
+ export { ActivityDock, AppearanceSettings, CommandPalette, Composer, ComposerPrompt, ContextItemDialog, ContextItemDialogProps, ContextPanel, ContextPanelView, ContributedSettings, DevProxyTargets, DialogOverlay, DockTabs, DormantPanel, HandoverProgress, ImageSettings, LoginFlowDialog, METHOD_LABEL, MascotMark, MascotMarkProps, MentionPreviewAsset, MentionPreviews, MessageFileLinkHandler, MessageMarkdown, ModelPopup, NewSessionDialog, NotificationSettings, PairedDeviceList, PairingApprovalDialog, PluginSettings, PluginSurface, ProviderSettings, QrCode, QrCodeProps, QueueSheet, RefusedCard, RemoteAccessDialog, RemoteAccessOptions, RemoteAccessPairing, RemoteControlSettings, RepositorySettings, RepositoryWorkspace, ResumeSessionDialog, SandboxWorkspaces, SandboxWorkspacesProps, SelectionBar, SessionActivity, SessionActivityView, SessionMcp, SessionMcpSettings, SessionMcpTools, SessionRailDialogs, SessionTranscriptView, SettingsMenu, SettingsPanelHost, TemplateSettings, ThreadView, Timeline, ToolCard, ToolRendererBoundary, TopBar, Transcript, TrustedDeviceList, TrustedDeviceListView, TrustedDeviceListViewProps, TunnelSettings, WelcomePanel, addComposerFiles, readComposerFiles, sessionActivity, useNewSessionActions, useSessionRail };
package/dist/views.mjs CHANGED
@@ -4,7 +4,7 @@ import { PluginSurface } from "./web/components/PluginSurface.mjs";
4
4
  import { QrCode } from "./web/components/QrCode.mjs";
5
5
  import { SessionMcp, SessionMcpTools } from "./web/components/SessionMcp.mjs";
6
6
  import { ContextItemDialog } from "./web/features/activity/ContextItemDialog.mjs";
7
- import { ContextPanel } from "./web/features/activity/ContextPanel.mjs";
7
+ import { ContextPanel, ContextPanelView } from "./web/features/activity/ContextPanel.mjs";
8
8
  import { DockTabs } from "./web/features/activity/DockTabs.mjs";
9
9
  import { ActivityDock } from "./web/features/activity/ActivityDock.mjs";
10
10
  import { RefusedCard } from "./web/features/connection/RefusedCard.mjs";
@@ -53,4 +53,4 @@ import { SettingsMenu } from "./web/features/settings/SettingsMenu.mjs";
53
53
  import { RepositoryWorkspace } from "./web/features/settings/RepositoryWorkspace.mjs";
54
54
  import { SettingsPanelHost } from "./web/features/settings/SettingsPanelHost.mjs";
55
55
  import { TopBar } from "./web/features/status/TopBar.mjs";
56
- export { ActivityDock, AppearanceSettings, CommandPalette, Composer, ComposerPrompt, ContextItemDialog, ContextPanel, ContributedSettings, DevProxyTargets, DialogOverlay, DockTabs, DormantPanel, HandoverProgress, ImageSettings, LoginFlowDialog, METHOD_LABEL, MascotMark, MentionPreviewAsset, MentionPreviews, MessageMarkdown, ModelPopup, NewSessionDialog, NotificationSettings, PairedDeviceList, PairingApprovalDialog, PluginSettings, PluginSurface, ProviderSettings, QrCode, QueueSheet, RefusedCard, RemoteAccessDialog, RemoteAccessOptions, RemoteAccessPairing, RemoteControlSettings, RepositorySettings, RepositoryWorkspace, ResumeSessionDialog, SandboxWorkspaces, SelectionBar, SessionActivity, SessionMcp, SessionMcpSettings, SessionMcpTools, SessionRailDialogs, SessionTranscriptView, SettingsMenu, SettingsPanelHost, TemplateSettings, ThreadView, Timeline, ToolCard, ToolRendererBoundary, TopBar, Transcript, TrustedDeviceList, TrustedDeviceListView, TunnelSettings, WelcomePanel, addComposerFiles, readComposerFiles, sessionActivity, useNewSessionActions, useSessionRail };
56
+ export { ActivityDock, AppearanceSettings, CommandPalette, Composer, ComposerPrompt, ContextItemDialog, ContextPanel, ContextPanelView, ContributedSettings, DevProxyTargets, DialogOverlay, DockTabs, DormantPanel, HandoverProgress, ImageSettings, LoginFlowDialog, METHOD_LABEL, MascotMark, MentionPreviewAsset, MentionPreviews, MessageMarkdown, ModelPopup, NewSessionDialog, NotificationSettings, PairedDeviceList, PairingApprovalDialog, PluginSettings, PluginSurface, ProviderSettings, QrCode, QueueSheet, RefusedCard, RemoteAccessDialog, RemoteAccessOptions, RemoteAccessPairing, RemoteControlSettings, RepositorySettings, RepositoryWorkspace, ResumeSessionDialog, SandboxWorkspaces, SelectionBar, SessionActivity, SessionMcp, SessionMcpSettings, SessionMcpTools, SessionRailDialogs, SessionTranscriptView, SettingsMenu, SettingsPanelHost, TemplateSettings, ThreadView, Timeline, ToolCard, ToolRendererBoundary, TopBar, Transcript, TrustedDeviceList, TrustedDeviceListView, TunnelSettings, WelcomePanel, addComposerFiles, readComposerFiles, sessionActivity, useNewSessionActions, useSessionRail };
@@ -9,27 +9,9 @@ let _agimon_ai_po_core_contextApi = require("@agimon-ai/po-core/contextApi");
9
9
  let react_jsx_runtime = require("react/jsx-runtime");
10
10
  let _agimon_ai_po_web_components = require("@agimon-ai/po-web-components");
11
11
  //#region src/web/features/activity/ContextPanel.tsx
12
- /** Short enough to sit in a fixed column without wrapping the tool's own name. */
13
- const SOURCE_LABEL = {
14
- extension: "ext",
15
- mcp: "mcp",
16
- plugin: "plug",
17
- core: "core"
18
- };
19
- /** The tilde is the whole caveat in one character: this is arithmetic, not a bill. */
20
12
  function tokens(value) {
21
13
  return value === null ? "-" : `~${value.toLocaleString()}`;
22
14
  }
23
- /**
24
- * What the session is carrying.
25
- *
26
- * Every tool and skill in here is spending context before the first message,
27
- * so the surface is built around the one question worth asking of it: what is
28
- * this costing, and what do I switch off to stop paying it. Modes head the
29
- * groups because a mode is what you switch; packages head the rows inside them
30
- * because a package is what you add or remove. It reads only; switching stays
31
- * with the composer chips that already own it.
32
- */
33
15
  function withoutMcpInventory(groups) {
34
16
  return groups.map((group) => {
35
17
  const mcp = group.items.filter((item) => item.source === "mcp");
@@ -57,192 +39,246 @@ function ContextPanel() {
57
39
  }
58
40
  const warnings = (0, _agimon_ai_po_core_contextApi.parseContextToolWarnings)(statuses[_agimon_ai_po_core_contextApi.CONTEXT_TOOL_WARNINGS_STATUS_KEY]);
59
41
  const groups = context ? require_contextComposition.projectedGroups(context) : require_contextComposition.contextGroups(statuses, widgets, projection);
60
- const total = require_contextComposition.totalTokens(groups);
61
- const idle = require_contextComposition.inactiveTotal(groups);
62
- const visibleGroups = withoutMcpInventory(groups);
42
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextPanelView, {
43
+ groups: withoutMcpInventory(groups),
44
+ total: require_contextComposition.totalTokens(groups),
45
+ idle: require_contextComposition.inactiveTotal(groups),
46
+ systemPrompt: context?.systemPrompt,
47
+ estimator: context?.estimator,
48
+ warnings,
49
+ onSelect: setTarget,
50
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_PluginSurface.PluginSurface, {
51
+ slot: require_pluginRegistry.HOST_SLOTS.context,
52
+ sessionId
53
+ })
54
+ }, sessionId), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ContextItemDialog.ContextItemDialog, {
55
+ sessionId,
56
+ target,
57
+ warnings: target?.itemKind === "tool" && Object.hasOwn(warnings, target.name) ? warnings[target.name] : void 0,
58
+ onClose: () => setTarget(null)
59
+ })] });
60
+ }
61
+ /** Presentation shared with the deterministic design stories. Totals never depend on the search. */
62
+ function ContextPanelView({ groups, total, idle, systemPrompt, estimator = "gpt-tokenizer", warnings = {}, onSelect, children }) {
63
+ const [search, setSearch] = (0, react.useState)("");
64
+ const query = search.trim().toLowerCase();
65
+ const matches = (value) => value.toLowerCase().includes(query);
66
+ const visibleGroups = groups.filter((group) => matches(group.label) || group.items.some((item) => matches(item.owner) || matches(item.name)));
63
67
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
64
68
  "data-testid": "context-panel",
65
69
  className: "flex min-h-0 min-w-0 flex-1 flex-col",
66
70
  children: [
71
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
72
+ className: "shrink-0 space-y-3 p-3",
73
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
74
+ className: "flex items-center gap-3 rounded-lg border border-po-border-soft bg-po-panel p-3",
75
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
76
+ className: "min-w-0 flex-1",
77
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
78
+ className: "text-sm text-po-dim",
79
+ children: "Active context"
80
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
81
+ className: "flex flex-wrap items-baseline gap-2",
82
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
83
+ "data-testid": "context-total",
84
+ className: "text-3xl font-semibold text-po-hi tabular-nums",
85
+ children: tokens(total)
86
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
87
+ className: "text-sm text-po-dim",
88
+ children: "tokens"
89
+ })]
90
+ })]
91
+ }), idle > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
92
+ className: "shrink-0 border-l border-po-border pl-3",
93
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
94
+ className: "text-sm text-po-dim",
95
+ children: "Inactive"
96
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
97
+ "data-testid": "context-inactive",
98
+ className: "text-lg font-medium text-po-dim tabular-nums",
99
+ children: `+${tokens(idle)}`
100
+ })]
101
+ }) : null]
102
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
103
+ className: "relative",
104
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.SearchIcon, {
105
+ "aria-hidden": true,
106
+ className: "pointer-events-none absolute top-2 left-3 size-4 text-po-dim"
107
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.Input, {
108
+ type: "search",
109
+ "aria-label": "Find in context",
110
+ placeholder: "Find in context",
111
+ value: search,
112
+ onChange: (event) => setSearch(event.target.value),
113
+ className: "w-full pl-9 placeholder:text-po-dim"
114
+ })]
115
+ })]
116
+ }),
67
117
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
68
118
  "data-testid": "context-scroll",
69
- className: "min-h-0 flex-1 overflow-y-auto",
70
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_PluginSurface.PluginSurface, {
71
- slot: require_pluginRegistry.HOST_SLOTS.context,
72
- sessionId
73
- }), groups.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.EmptyState, {
119
+ className: "min-h-0 flex-1 overflow-y-auto px-3 pb-3",
120
+ children: [children, groups.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.EmptyState, {
74
121
  "data-testid": "context-empty",
75
122
  className: "px-4 py-5",
76
- title: "no composition yet",
77
- description: "the session reports its major mode, minor modes, and domains once it has started."
78
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
79
- className: "flex flex-col",
80
- children: visibleGroups.map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextGroupView, {
123
+ title: "No composition yet",
124
+ description: "The session reports its major mode, minor modes, and domains once it has started."
125
+ }) : visibleGroups.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.EmptyState, {
126
+ title: "No matches",
127
+ description: "Try a mode, package, tool, or skill name."
128
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
129
+ className: "overflow-hidden rounded-lg border border-po-border-soft",
130
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
131
+ className: "flex items-center justify-between gap-2 border-b border-po-border-soft px-3 py-3 text-sm text-po-dim",
132
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Loaded context" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Est. tokens" })]
133
+ }), visibleGroups.map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextGroupView, {
81
134
  group,
135
+ query,
82
136
  warnings,
83
- onSelect: setTarget
84
- }, `${group.kind}:${group.id}`))
137
+ onSelect
138
+ }, `${group.kind}:${group.id}:${query ? "search" : "browse"}`))]
85
139
  })]
86
140
  }),
87
141
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
88
- className: "shrink-0 border-t border-po-border px-4 py-3",
89
- children: [
90
- context?.systemPrompt ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_agimon_ai_po_web_components.Button, {
91
- variant: "ghost",
92
- size: "card",
93
- "data-testid": "context-system-prompt",
94
- title: "read the system prompt this session is running under",
95
- onClick: () => setTarget({
96
- itemKind: "prompt",
97
- name: "system",
98
- owner: "session"
99
- }),
100
- className: "flex-row items-center justify-between gap-2 rounded-none px-0 py-px",
101
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
102
- className: "text-sm text-po-dim",
103
- children: context.systemPrompt.stage === "base" ? "system prompt (base)" : "system prompt"
104
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
105
- className: "text-sm text-po-dim",
106
- children: tokens(context.systemPrompt.tokens)
107
- })]
108
- }) : null,
109
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
110
- className: "flex items-center justify-between",
111
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
112
- className: "text-sm font-semibold text-po-text",
113
- children: "active"
114
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
115
- "data-testid": "context-total",
116
- className: "text-sm font-semibold text-po-text",
117
- children: tokens(total)
118
- })]
142
+ className: "shrink-0 space-y-2 border-t border-po-border px-4 py-3",
143
+ children: [systemPrompt ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_agimon_ai_po_web_components.Button, {
144
+ variant: "ghost",
145
+ size: "card",
146
+ "data-testid": "context-system-prompt",
147
+ title: "Read the system prompt this session is running under",
148
+ onClick: () => onSelect({
149
+ itemKind: "prompt",
150
+ name: "system",
151
+ owner: "session"
119
152
  }),
120
- idle > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
121
- className: "flex items-center justify-between",
122
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
123
- className: "text-sm text-po-dim",
124
- children: "inactive"
125
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
126
- "data-testid": "context-inactive",
127
- className: "text-sm text-po-dim",
128
- children: `+${tokens(idle)}`
129
- })]
130
- }) : null,
131
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
132
- className: "text-sm leading-relaxed text-po-dim",
133
- children: `estimated · ${context?.estimator ?? "gpt-tokenizer"} BPE · not a billed total`
134
- })
135
- ]
136
- }),
137
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ContextItemDialog.ContextItemDialog, {
138
- sessionId,
139
- target,
140
- warnings: target?.itemKind === "tool" && Object.hasOwn(warnings, target.name) ? warnings[target.name] : void 0,
141
- onClose: () => setTarget(null)
153
+ className: "flex-row items-center justify-between gap-2 px-0 py-1",
154
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
155
+ className: "text-sm text-po-text",
156
+ children: systemPrompt.stage === "base" ? "System prompt (base)" : "System prompt"
157
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
158
+ className: "shrink-0 text-sm text-po-text tabular-nums",
159
+ children: tokens(systemPrompt.tokens)
160
+ })]
161
+ }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
162
+ className: "text-sm leading-relaxed text-po-dim",
163
+ children: [
164
+ "Token counts are estimates.",
165
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("br", {}),
166
+ `${estimator} BPE · Not a billed total`
167
+ ]
168
+ })]
142
169
  })
143
170
  ]
144
171
  });
145
172
  }
146
- /** Active carries the bright ink, inactive the faint one, so what the model sees reads at a glance. */
147
- function ink(active) {
148
- return active ? "text-po-hi" : "text-po-dim";
149
- }
150
- function ContextGroupView({ group, warnings, onSelect }) {
173
+ function ContextGroupView({ group, query, warnings, onSelect }) {
151
174
  const groupActive = group.items.length === 0 || group.items.some((item) => item.active);
152
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
175
+ const groupMatches = group.label.toLowerCase().includes(query);
176
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
177
+ open: true,
153
178
  "data-testid": `context-group-${group.id}`,
154
179
  "data-kind": group.kind,
155
180
  "data-active": groupActive,
156
- className: "flex flex-col gap-1 border-b border-po-border-soft px-3 py-3",
157
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
158
- className: "flex items-center gap-2 px-1",
181
+ className: "group/mode border-b border-po-border-soft last:border-b-0",
182
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
183
+ className: "flex cursor-pointer list-none items-center gap-2 px-3 py-3 focus-visible:outline-2 focus-visible:outline-po-selected [&::-webkit-details-marker]:hidden",
159
184
  children: [
160
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
185
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.ChevronRightIcon, {
161
186
  "aria-hidden": true,
162
- className: "text-sm font-semibold text-po-faint",
163
- children: "#"
187
+ className: "size-3 shrink-0 text-po-dim group-open/mode:rotate-90"
164
188
  }),
165
189
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
166
- className: `min-w-0 flex-1 truncate text-lg font-semibold text-po-hi`,
190
+ className: `min-w-0 flex-1 truncate text-base font-semibold ${groupActive ? "text-po-hi" : "text-po-dim"}`,
167
191
  children: group.label
168
192
  }),
169
193
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
170
- className: "inline-block text-sm text-po-dim first-letter:uppercase",
194
+ className: "rounded-md border border-po-border px-1.5 text-sm text-po-dim first-letter:uppercase",
171
195
  children: group.kind
172
196
  }),
173
197
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
174
198
  "data-testid": `context-subtotal-${group.id}`,
175
- className: "w-14 text-right text-sm text-po-dim",
199
+ className: "w-16 shrink-0 text-right text-sm font-semibold text-po-text tabular-nums",
176
200
  children: tokens(group.tokens)
177
201
  })
178
202
  ]
179
203
  }), group.items.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
180
204
  "data-testid": `context-pending-${group.id}`,
181
- className: "px-1 text-sm text-po-dim",
182
- children: group.detail || "no tools or skills reported"
205
+ className: "px-3 pb-3 text-sm text-po-dim",
206
+ children: group.detail || "No tools or skills reported"
183
207
  }) : require_contextComposition.ownersOf(group).map((owner) => {
208
+ const ownerMatches = groupMatches || owner.owner.toLowerCase().includes(query);
209
+ const items = owner.items.filter((item) => ownerMatches || item.name.toLowerCase().includes(query));
210
+ if (items.length === 0) return null;
184
211
  const ownerActive = owner.items.some((item) => item.active);
185
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
186
- className: "flex flex-col",
187
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
212
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
213
+ open: query ? true : owner.items.length > 1,
214
+ className: "group/owner border-t border-po-border-soft",
215
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
188
216
  "data-testid": `context-owner-${owner.owner}`,
189
217
  "data-active": ownerActive,
190
- title: owner.owner,
191
- className: "flex items-center gap-2 px-1 pt-1",
218
+ title: `${owner.owner} (${owner.source})`,
219
+ className: "flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 focus-visible:outline-2 focus-visible:outline-po-selected [&::-webkit-details-marker]:hidden",
192
220
  children: [
221
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_agimon_ai_po_web_components.ChevronRightIcon, {
222
+ "aria-hidden": true,
223
+ className: "size-3 shrink-0 text-po-dim group-open/owner:rotate-90"
224
+ }),
193
225
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
194
- className: `min-w-0 flex-1 truncate text-base font-semibold text-po-hi`,
226
+ className: `min-w-0 flex-1 truncate text-base font-semibold ${ownerActive ? "text-po-hi" : "text-po-dim"}`,
195
227
  children: require_contextComposition.ownerLabel(owner.owner)
196
228
  }),
197
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
229
+ owner.source !== "extension" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
198
230
  className: "text-sm text-po-dim",
199
- children: SOURCE_LABEL[owner.source]
200
- }),
231
+ children: owner.source
232
+ }) : null,
201
233
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
202
- className: "w-14 text-right text-sm text-po-dim",
234
+ className: "w-16 shrink-0 text-right text-sm text-po-dim tabular-nums",
203
235
  children: tokens(owner.tokens)
204
236
  })
205
237
  ]
206
- }), owner.items.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_agimon_ai_po_web_components.Button, {
207
- variant: "ghost",
208
- size: "card",
209
- "data-testid": `context-row-${item.name}`,
210
- "data-active": item.active,
211
- onClick: () => onSelect({
212
- itemKind: item.itemKind,
213
- name: item.name,
214
- owner: item.owner
215
- }),
216
- className: "flex-row items-center gap-2 rounded-none py-px pr-1 pl-4",
217
- children: [
218
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("abbr", {
219
- title: item.itemKind,
220
- className: `w-4 shrink-0 text-center text-2xs font-semibold no-underline ${item.active ? "text-po-violet" : "text-po-faint"}`,
221
- children: item.itemKind === "skill" ? "S" : "T"
222
- }),
223
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
224
- className: `min-w-0 flex-1 truncate text-base font-medium ${ink(item.active)}`,
225
- children: item.name
238
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
239
+ className: "mb-2 ml-6 border-l border-po-border pl-2",
240
+ children: items.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_agimon_ai_po_web_components.Button, {
241
+ variant: "ghost",
242
+ size: "card",
243
+ "data-testid": `context-row-${item.name}`,
244
+ "data-active": item.active,
245
+ title: item.name,
246
+ onClick: () => onSelect({
247
+ itemKind: item.itemKind,
248
+ name: item.name,
249
+ owner: item.owner
226
250
  }),
227
- item.itemKind === "tool" && Object.hasOwn(warnings, item.name) && (warnings[item.name]?.length ?? 0) > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("abbr", {
228
- "data-testid": `context-warning-${item.name}`,
229
- "aria-label": `${item.name} has warnings`,
230
- title: "warnings: open tool details for origins",
231
- className: "shrink-0 text-xs font-semibold text-po-yellow no-underline",
232
- children: "!"
233
- }) : null,
234
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
235
- title: item.active ? void 0 : "not sent to the model; costs nothing until switched on",
236
- className: `w-14 text-right text-sm ${item.active ? "text-po-text" : "text-po-dim"}`,
237
- children: item.active ? tokens(item.tokens) : `(${tokens(item.tokens)})`
238
- })
239
- ]
240
- }, `${item.itemKind}:${item.name}`))]
251
+ className: "flex-row items-center gap-2 rounded-md py-2 pr-3 pl-2",
252
+ children: [
253
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
254
+ className: "shrink-0 rounded-md border border-po-border px-1.5 text-sm text-po-dim first-letter:uppercase",
255
+ children: item.itemKind
256
+ }),
257
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
258
+ className: `min-w-0 flex-1 truncate text-base ${item.active ? "text-po-hi" : "text-po-dim"}`,
259
+ children: item.name
260
+ }),
261
+ item.itemKind === "tool" && Object.hasOwn(warnings, item.name) && (warnings[item.name]?.length ?? 0) > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("abbr", {
262
+ "data-testid": `context-warning-${item.name}`,
263
+ "aria-label": `${item.name} has warnings`,
264
+ title: "Warnings: open tool details for origins",
265
+ className: "shrink-0 text-sm font-semibold text-po-yellow no-underline",
266
+ children: "!"
267
+ }) : null,
268
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
269
+ title: item.active ? void 0 : "Not sent to the model; costs nothing until switched on",
270
+ className: `w-16 shrink-0 text-right text-sm tabular-nums ${item.active ? "text-po-text" : "text-po-dim"}`,
271
+ children: item.active ? tokens(item.tokens) : `(${tokens(item.tokens)})`
272
+ })
273
+ ]
274
+ }, `${item.itemKind}:${item.name}`))
275
+ })]
241
276
  }, owner.owner);
242
277
  })]
243
278
  });
244
279
  }
245
280
  //#endregion
246
281
  exports.ContextPanel = ContextPanel;
282
+ exports.ContextPanelView = ContextPanelView;
247
283
 
248
284
  //# sourceMappingURL=ContextPanel.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ContextPanel.cjs","names":["useActiveSession","useActiveSessionMeta","useState","parseContextToolWarnings","CONTEXT_TOOL_WARNINGS_STATUS_KEY","projectedGroups","contextGroups","totalTokens","inactiveTotal","PluginSurface","HOST_SLOTS","EmptyState","Button","ContextItemDialog","ownersOf","ownerLabel"],"sources":["../../../../src/web/features/activity/ContextPanel.tsx"],"sourcesContent":["import {\n CONTEXT_TOOL_WARNINGS_STATUS_KEY,\n parseContextToolWarnings,\n type ContextToolWarning,\n type ContextItemKind,\n} from '@agimon-ai/po-core/contextApi';\nimport { Button, EmptyState } from '@agimon-ai/po-web-components';\nimport { useState } from 'react';\n\nimport { PluginSurface } from '../../components/PluginSurface';\nimport {\n type ContextGroup,\n type ContextItemSource,\n contextGroups,\n inactiveTotal,\n ownerLabel,\n ownersOf,\n projectedGroups,\n totalTokens,\n} from '../../lib/contextComposition';\nimport { HOST_SLOTS } from '../../lib/pluginRegistry';\nimport { useActiveSessionMeta } from '../../stores/sessionsStore';\nimport { useActiveSession } from '../../stores/sessionStore';\nimport { ContextItemDialog } from './ContextItemDialog';\n\n/** The row a reader clicked, which is all the detail request is made of. */\ninterface ItemTarget {\n itemKind: ContextItemKind;\n name: string;\n owner: string;\n}\n\n/** Short enough to sit in a fixed column without wrapping the tool's own name. */\nconst SOURCE_LABEL: Record<ContextItemSource, string> = {\n extension: 'ext',\n mcp: 'mcp',\n plugin: 'plug',\n core: 'core',\n};\n\n/** The tilde is the whole caveat in one character: this is arithmetic, not a bill. */\nfunction tokens(value: number | null): string {\n return value === null ? '-' : `~${value.toLocaleString()}`;\n}\n\n/**\n * What the session is carrying.\n *\n * Every tool and skill in here is spending context before the first message,\n * so the surface is built around the one question worth asking of it: what is\n * this costing, and what do I switch off to stop paying it. Modes head the\n * groups because a mode is what you switch; packages head the rows inside them\n * because a package is what you add or remove. It reads only; switching stays\n * with the composer chips that already own it.\n */\nfunction withoutMcpInventory(groups: readonly ContextGroup[]): ContextGroup[] {\n return groups.map((group) => {\n const mcp = group.items.filter((item) => item.source === 'mcp');\n const activeMcpTokens = mcp.reduce((sum, item) => sum + (item.active ? (item.tokens ?? 0) : 0), 0);\n const inactiveMcpTokens = mcp.reduce((sum, item) => sum + (item.active ? 0 : (item.tokens ?? 0)), 0);\n return {\n ...group,\n items: group.items.filter((item) => item.source !== 'mcp'),\n tokens: group.tokens === null ? null : Math.max(0, group.tokens - activeMcpTokens),\n inactiveTokens: Math.max(0, group.inactiveTokens - inactiveMcpTokens),\n };\n });\n}\n\nexport function ContextPanel() {\n const statuses = useActiveSession((state) => state.statuses);\n const widgets = useActiveSession((state) => state.widgets);\n const projection = useActiveSession((state) => state.minorModes);\n const context = useActiveSession((state) => state.context);\n const sessionId = useActiveSessionMeta()?.summary.id ?? null;\n const [target, setTarget] = useState<ItemTarget | null>(null);\n const [targetSession, setTargetSession] = useState(sessionId);\n if (targetSession !== sessionId) {\n setTargetSession(sessionId);\n setTarget(null);\n }\n const warnings = parseContextToolWarnings(statuses[CONTEXT_TOOL_WARNINGS_STATUS_KEY]);\n // The runtime's grouping wins once it arrives; the status line only has to\n // carry the surface until the session has reported its inventory.\n const groups = context ? projectedGroups(context) : contextGroups(statuses, widgets, projection);\n const total = totalTokens(groups);\n const idle = inactiveTotal(groups);\n const visibleGroups = withoutMcpInventory(groups);\n return (\n <div data-testid=\"context-panel\" className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n <div data-testid=\"context-scroll\" className=\"min-h-0 flex-1 overflow-y-auto\">\n <PluginSurface slot={HOST_SLOTS.context} sessionId={sessionId} />\n {groups.length === 0 ? (\n <EmptyState\n data-testid=\"context-empty\"\n className=\"px-4 py-5\"\n title=\"no composition yet\"\n description=\"the session reports its major mode, minor modes, and domains once it has started.\"\n />\n ) : (\n <div className=\"flex flex-col\">\n {visibleGroups.map((group) => (\n <ContextGroupView\n key={`${group.kind}:${group.id}`}\n group={group}\n warnings={warnings}\n onSelect={setTarget}\n />\n ))}\n </div>\n )}\n </div>\n\n <div className=\"shrink-0 border-t border-po-border px-4 py-3\">\n {/* Above the totals rather than inside them. The prompt carries every\n tool's prose and every skill's listing, which the group subtotals\n have already counted, so the two figures overlap and adding them\n would bill the same text twice. */}\n {context?.systemPrompt ? (\n <Button\n variant=\"ghost\"\n size=\"card\"\n data-testid=\"context-system-prompt\"\n title=\"read the system prompt this session is running under\"\n onClick={() => setTarget({ itemKind: 'prompt', name: 'system', owner: 'session' })}\n className=\"flex-row items-center justify-between gap-2 rounded-none px-0 py-px\"\n >\n <span className=\"text-sm text-po-dim\">\n {context.systemPrompt.stage === 'base' ? 'system prompt (base)' : 'system prompt'}\n </span>\n <span className=\"text-sm text-po-dim\">{tokens(context.systemPrompt.tokens)}</span>\n </Button>\n ) : null}\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-semibold text-po-text\">active</span>\n <span data-testid=\"context-total\" className=\"text-sm font-semibold text-po-text\">\n {tokens(total)}\n </span>\n </div>\n {idle > 0 ? (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-po-dim\">inactive</span>\n <span data-testid=\"context-inactive\" className=\"text-sm text-po-dim\">\n {`+${tokens(idle)}`}\n </span>\n </div>\n ) : null}\n <span className=\"text-sm leading-relaxed text-po-dim\">\n {`estimated · ${context?.estimator ?? 'gpt-tokenizer'} BPE · not a billed total`}\n </span>\n </div>\n\n <ContextItemDialog\n sessionId={sessionId}\n target={target}\n warnings={\n target?.itemKind === 'tool' && Object.hasOwn(warnings, target.name) ? warnings[target.name] : undefined\n }\n onClose={() => setTarget(null)}\n />\n </div>\n );\n}\n\n/** Active carries the bright ink, inactive the faint one, so what the model sees reads at a glance. */\nfunction ink(active: boolean): string {\n return active ? 'text-po-hi' : 'text-po-dim';\n}\n\nfunction ContextGroupView({\n group,\n warnings,\n onSelect,\n}: {\n group: ContextGroup;\n warnings: Readonly<Record<string, readonly ContextToolWarning[]>>;\n onSelect: (target: ItemTarget) => void;\n}) {\n // A group that reported rows is active when any of them is; one still pending keeps the normal head.\n const groupActive = group.items.length === 0 || group.items.some((item) => item.active);\n return (\n <div\n data-testid={`context-group-${group.id}`}\n data-kind={group.kind}\n data-active={groupActive}\n className=\"flex flex-col gap-1 border-b border-po-border-soft px-3 py-3\"\n >\n {/* The same head the activity dock uses, for the same reason: the glyph\n says \"section\", so a mode never reads as one more row among the tools\n it brought with it. */}\n <div className=\"flex items-center gap-2 px-1\">\n <span aria-hidden className=\"text-sm font-semibold text-po-faint\">\n #\n </span>\n <span className={`min-w-0 flex-1 truncate text-lg font-semibold text-po-hi`}>{group.label}</span>\n <span className=\"inline-block text-sm text-po-dim first-letter:uppercase\">{group.kind}</span>\n <span data-testid={`context-subtotal-${group.id}`} className=\"w-14 text-right text-sm text-po-dim\">\n {tokens(group.tokens)}\n </span>\n </div>\n\n {group.items.length === 0 ? (\n <p data-testid={`context-pending-${group.id}`} className=\"px-1 text-sm text-po-dim\">\n {group.detail || 'no tools or skills reported'}\n </p>\n ) : (\n ownersOf(group).map((owner) => {\n const ownerActive = owner.items.some((item) => item.active);\n return (\n <div key={owner.owner} className=\"flex flex-col\">\n {/* The package heads its own rows, so the source tag sits here\n rather than repeating on every tool that shares it. */}\n <div\n data-testid={`context-owner-${owner.owner}`}\n data-active={ownerActive}\n title={owner.owner}\n className=\"flex items-center gap-2 px-1 pt-1\"\n >\n <span className={`min-w-0 flex-1 truncate text-base font-semibold text-po-hi`}>\n {ownerLabel(owner.owner)}\n </span>\n <span className=\"text-sm text-po-dim\">{SOURCE_LABEL[owner.source]}</span>\n <span className=\"w-14 text-right text-sm text-po-dim\">{tokens(owner.tokens)}</span>\n </div>\n {owner.items.map((item) => (\n // A row is a question as much as a figure: what am I paying for.\n // The answer is too large to have travelled with the panel, so\n // the row asks for it rather than carrying it.\n <Button\n variant=\"ghost\"\n size=\"card\"\n key={`${item.itemKind}:${item.name}`}\n data-testid={`context-row-${item.name}`}\n data-active={item.active}\n onClick={() => onSelect({ itemKind: item.itemKind, name: item.name, owner: item.owner })}\n className=\"flex-row items-center gap-2 rounded-none py-px pr-1 pl-4\"\n >\n <abbr\n title={item.itemKind}\n className={`w-4 shrink-0 text-center text-2xs font-semibold no-underline ${item.active ? 'text-po-violet' : 'text-po-faint'}`}\n >\n {item.itemKind === 'skill' ? 'S' : 'T'}\n </abbr>\n <span className={`min-w-0 flex-1 truncate text-base font-medium ${ink(item.active)}`}>\n {item.name}\n </span>\n {item.itemKind === 'tool' &&\n Object.hasOwn(warnings, item.name) &&\n (warnings[item.name]?.length ?? 0) > 0 ? (\n <abbr\n data-testid={`context-warning-${item.name}`}\n aria-label={`${item.name} has warnings`}\n title=\"warnings: open tool details for origins\"\n className=\"shrink-0 text-xs font-semibold text-po-yellow no-underline\"\n >\n !\n </abbr>\n ) : null}\n <span\n title={item.active ? undefined : 'not sent to the model; costs nothing until switched on'}\n className={`w-14 text-right text-sm ${item.active ? 'text-po-text' : 'text-po-dim'}`}\n >\n {item.active ? tokens(item.tokens) : `(${tokens(item.tokens)})`}\n </span>\n </Button>\n ))}\n </div>\n );\n })\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ContextPanel.cjs","names":["useActiveSession","useActiveSessionMeta","useState","parseContextToolWarnings","CONTEXT_TOOL_WARNINGS_STATUS_KEY","projectedGroups","contextGroups","totalTokens","inactiveTotal","PluginSurface","HOST_SLOTS","ContextItemDialog","SearchIcon","Input","EmptyState","Button","ChevronRightIcon","ownersOf","ownerLabel"],"sources":["../../../../src/web/features/activity/ContextPanel.tsx"],"sourcesContent":["import {\n CONTEXT_TOOL_WARNINGS_STATUS_KEY,\n parseContextToolWarnings,\n type ContextToolWarning,\n type ContextItemKind,\n} from '@agimon-ai/po-core/contextApi';\nimport { Button, ChevronRightIcon, EmptyState, Input, SearchIcon } from '@agimon-ai/po-web-components';\nimport { type ReactNode, useState } from 'react';\n\nimport { PluginSurface } from '../../components/PluginSurface';\nimport {\n type ContextGroup,\n contextGroups,\n inactiveTotal,\n ownerLabel,\n ownersOf,\n projectedGroups,\n totalTokens,\n} from '../../lib/contextComposition';\nimport { HOST_SLOTS } from '../../lib/pluginRegistry';\nimport { useActiveSessionMeta } from '../../stores/sessionsStore';\nimport { useActiveSession } from '../../stores/sessionStore';\nimport { ContextItemDialog } from './ContextItemDialog';\n\ninterface ItemTarget {\n itemKind: ContextItemKind;\n name: string;\n owner: string;\n}\n\nfunction tokens(value: number | null): string {\n return value === null ? '-' : `~${value.toLocaleString()}`;\n}\n\nfunction withoutMcpInventory(groups: readonly ContextGroup[]): ContextGroup[] {\n return groups.map((group) => {\n const mcp = group.items.filter((item) => item.source === 'mcp');\n const activeMcpTokens = mcp.reduce((sum, item) => sum + (item.active ? (item.tokens ?? 0) : 0), 0);\n const inactiveMcpTokens = mcp.reduce((sum, item) => sum + (item.active ? 0 : (item.tokens ?? 0)), 0);\n return {\n ...group,\n items: group.items.filter((item) => item.source !== 'mcp'),\n tokens: group.tokens === null ? null : Math.max(0, group.tokens - activeMcpTokens),\n inactiveTokens: Math.max(0, group.inactiveTokens - inactiveMcpTokens),\n };\n });\n}\n\nexport function ContextPanel() {\n const statuses = useActiveSession((state) => state.statuses);\n const widgets = useActiveSession((state) => state.widgets);\n const projection = useActiveSession((state) => state.minorModes);\n const context = useActiveSession((state) => state.context);\n const sessionId = useActiveSessionMeta()?.summary.id ?? null;\n const [target, setTarget] = useState<ItemTarget | null>(null);\n const [targetSession, setTargetSession] = useState(sessionId);\n if (targetSession !== sessionId) {\n setTargetSession(sessionId);\n setTarget(null);\n }\n const warnings = parseContextToolWarnings(statuses[CONTEXT_TOOL_WARNINGS_STATUS_KEY]);\n const groups = context ? projectedGroups(context) : contextGroups(statuses, widgets, projection);\n return (\n <>\n <ContextPanelView\n key={sessionId}\n groups={withoutMcpInventory(groups)}\n total={totalTokens(groups)}\n idle={inactiveTotal(groups)}\n systemPrompt={context?.systemPrompt}\n estimator={context?.estimator}\n warnings={warnings}\n onSelect={setTarget}\n >\n <PluginSurface slot={HOST_SLOTS.context} sessionId={sessionId} />\n </ContextPanelView>\n <ContextItemDialog\n sessionId={sessionId}\n target={target}\n warnings={\n target?.itemKind === 'tool' && Object.hasOwn(warnings, target.name) ? warnings[target.name] : undefined\n }\n onClose={() => setTarget(null)}\n />\n </>\n );\n}\n\n/** Presentation shared with the deterministic design stories. Totals never depend on the search. */\nexport function ContextPanelView({\n groups,\n total,\n idle,\n systemPrompt,\n estimator = 'gpt-tokenizer',\n warnings = {},\n onSelect,\n children,\n}: {\n groups: readonly ContextGroup[];\n total: number | null;\n idle: number;\n systemPrompt?: { stage: string; tokens: number } | null;\n estimator?: string;\n warnings?: Readonly<Record<string, readonly ContextToolWarning[]>>;\n onSelect: (target: ItemTarget) => void;\n children?: ReactNode;\n}) {\n const [search, setSearch] = useState('');\n const query = search.trim().toLowerCase();\n const matches = (value: string) => value.toLowerCase().includes(query);\n const visibleGroups = groups.filter(\n (group) => matches(group.label) || group.items.some((item) => matches(item.owner) || matches(item.name)),\n );\n return (\n <div data-testid=\"context-panel\" className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n <div className=\"shrink-0 space-y-3 p-3\">\n <div className=\"flex items-center gap-3 rounded-lg border border-po-border-soft bg-po-panel p-3\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-sm text-po-dim\">Active context</div>\n <div className=\"flex flex-wrap items-baseline gap-2\">\n <span data-testid=\"context-total\" className=\"text-3xl font-semibold text-po-hi tabular-nums\">\n {tokens(total)}\n </span>\n <span className=\"text-sm text-po-dim\">tokens</span>\n </div>\n </div>\n {idle > 0 ? (\n <div className=\"shrink-0 border-l border-po-border pl-3\">\n <div className=\"text-sm text-po-dim\">Inactive</div>\n <span data-testid=\"context-inactive\" className=\"text-lg font-medium text-po-dim tabular-nums\">\n {`+${tokens(idle)}`}\n </span>\n </div>\n ) : null}\n </div>\n <div className=\"relative\">\n <SearchIcon aria-hidden className=\"pointer-events-none absolute top-2 left-3 size-4 text-po-dim\" />\n <Input\n type=\"search\"\n aria-label=\"Find in context\"\n placeholder=\"Find in context\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n className=\"w-full pl-9 placeholder:text-po-dim\"\n />\n </div>\n </div>\n <div data-testid=\"context-scroll\" className=\"min-h-0 flex-1 overflow-y-auto px-3 pb-3\">\n {children}\n {groups.length === 0 ? (\n <EmptyState\n data-testid=\"context-empty\"\n className=\"px-4 py-5\"\n title=\"No composition yet\"\n description=\"The session reports its major mode, minor modes, and domains once it has started.\"\n />\n ) : visibleGroups.length === 0 ? (\n <EmptyState title=\"No matches\" description=\"Try a mode, package, tool, or skill name.\" />\n ) : (\n <div className=\"overflow-hidden rounded-lg border border-po-border-soft\">\n <div className=\"flex items-center justify-between gap-2 border-b border-po-border-soft px-3 py-3 text-sm text-po-dim\">\n <span>Loaded context</span>\n <span>Est. tokens</span>\n </div>\n {visibleGroups.map((group) => (\n <ContextGroupView\n key={`${group.kind}:${group.id}:${query ? 'search' : 'browse'}`}\n group={group}\n query={query}\n warnings={warnings}\n onSelect={onSelect}\n />\n ))}\n </div>\n )}\n </div>\n <div className=\"shrink-0 space-y-2 border-t border-po-border px-4 py-3\">\n {/* Prompt tokens overlap the group subtotals. Do not add them to the active total. */}\n {systemPrompt ? (\n <Button\n variant=\"ghost\"\n size=\"card\"\n data-testid=\"context-system-prompt\"\n title=\"Read the system prompt this session is running under\"\n onClick={() => onSelect({ itemKind: 'prompt', name: 'system', owner: 'session' })}\n className=\"flex-row items-center justify-between gap-2 px-0 py-1\"\n >\n <span className=\"text-sm text-po-text\">\n {systemPrompt.stage === 'base' ? 'System prompt (base)' : 'System prompt'}\n </span>\n <span className=\"shrink-0 text-sm text-po-text tabular-nums\">{tokens(systemPrompt.tokens)}</span>\n </Button>\n ) : null}\n <p className=\"text-sm leading-relaxed text-po-dim\">\n Token counts are estimates.\n <br />\n {`${estimator} BPE · Not a billed total`}\n </p>\n </div>\n </div>\n );\n}\n\nfunction ContextGroupView({\n group,\n query,\n warnings,\n onSelect,\n}: {\n group: ContextGroup;\n query: string;\n warnings: Readonly<Record<string, readonly ContextToolWarning[]>>;\n onSelect: (target: ItemTarget) => void;\n}) {\n const groupActive = group.items.length === 0 || group.items.some((item) => item.active);\n const groupMatches = group.label.toLowerCase().includes(query);\n return (\n <details\n open\n data-testid={`context-group-${group.id}`}\n data-kind={group.kind}\n data-active={groupActive}\n className=\"group/mode border-b border-po-border-soft last:border-b-0\"\n >\n <summary className=\"flex cursor-pointer list-none items-center gap-2 px-3 py-3 focus-visible:outline-2 focus-visible:outline-po-selected [&::-webkit-details-marker]:hidden\">\n <ChevronRightIcon aria-hidden className=\"size-3 shrink-0 text-po-dim group-open/mode:rotate-90\" />\n <span\n className={`min-w-0 flex-1 truncate text-base font-semibold ${groupActive ? 'text-po-hi' : 'text-po-dim'}`}\n >\n {group.label}\n </span>\n <span className=\"rounded-md border border-po-border px-1.5 text-sm text-po-dim first-letter:uppercase\">\n {group.kind}\n </span>\n <span\n data-testid={`context-subtotal-${group.id}`}\n className=\"w-16 shrink-0 text-right text-sm font-semibold text-po-text tabular-nums\"\n >\n {tokens(group.tokens)}\n </span>\n </summary>\n {group.items.length === 0 ? (\n <p data-testid={`context-pending-${group.id}`} className=\"px-3 pb-3 text-sm text-po-dim\">\n {group.detail || 'No tools or skills reported'}\n </p>\n ) : (\n ownersOf(group).map((owner) => {\n const ownerMatches = groupMatches || owner.owner.toLowerCase().includes(query);\n const items = owner.items.filter((item) => ownerMatches || item.name.toLowerCase().includes(query));\n if (items.length === 0) return null;\n const ownerActive = owner.items.some((item) => item.active);\n return (\n <details\n key={owner.owner}\n open={query ? true : owner.items.length > 1}\n className=\"group/owner border-t border-po-border-soft\"\n >\n <summary\n data-testid={`context-owner-${owner.owner}`}\n data-active={ownerActive}\n title={`${owner.owner} (${owner.source})`}\n className=\"flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 focus-visible:outline-2 focus-visible:outline-po-selected [&::-webkit-details-marker]:hidden\"\n >\n <ChevronRightIcon aria-hidden className=\"size-3 shrink-0 text-po-dim group-open/owner:rotate-90\" />\n <span\n className={`min-w-0 flex-1 truncate text-base font-semibold ${ownerActive ? 'text-po-hi' : 'text-po-dim'}`}\n >\n {ownerLabel(owner.owner)}\n </span>\n {owner.source !== 'extension' ? <span className=\"text-sm text-po-dim\">{owner.source}</span> : null}\n <span className=\"w-16 shrink-0 text-right text-sm text-po-dim tabular-nums\">\n {tokens(owner.tokens)}\n </span>\n </summary>\n <div className=\"mb-2 ml-6 border-l border-po-border pl-2\">\n {items.map((item) => (\n <Button\n variant=\"ghost\"\n size=\"card\"\n key={`${item.itemKind}:${item.name}`}\n data-testid={`context-row-${item.name}`}\n data-active={item.active}\n title={item.name}\n onClick={() => onSelect({ itemKind: item.itemKind, name: item.name, owner: item.owner })}\n className=\"flex-row items-center gap-2 rounded-md py-2 pr-3 pl-2\"\n >\n <span className=\"shrink-0 rounded-md border border-po-border px-1.5 text-sm text-po-dim first-letter:uppercase\">\n {item.itemKind}\n </span>\n <span className={`min-w-0 flex-1 truncate text-base ${item.active ? 'text-po-hi' : 'text-po-dim'}`}>\n {item.name}\n </span>\n {item.itemKind === 'tool' &&\n Object.hasOwn(warnings, item.name) &&\n (warnings[item.name]?.length ?? 0) > 0 ? (\n <abbr\n data-testid={`context-warning-${item.name}`}\n aria-label={`${item.name} has warnings`}\n title=\"Warnings: open tool details for origins\"\n className=\"shrink-0 text-sm font-semibold text-po-yellow no-underline\"\n >\n !\n </abbr>\n ) : null}\n <span\n title={item.active ? undefined : 'Not sent to the model; costs nothing until switched on'}\n className={`w-16 shrink-0 text-right text-sm tabular-nums ${item.active ? 'text-po-text' : 'text-po-dim'}`}\n >\n {item.active ? tokens(item.tokens) : `(${tokens(item.tokens)})`}\n </span>\n </Button>\n ))}\n </div>\n </details>\n );\n })\n )}\n </details>\n );\n}\n"],"mappings":"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"}
@@ -1,4 +1,27 @@
1
+ import { ContextGroup } from "../../lib/contextComposition.mjs";
2
+ import { ReactNode } from "react";
3
+ import { ContextItemKind, ContextToolWarning } from "@agimon-ai/po-core/contextApi";
1
4
  //#region src/web/features/activity/ContextPanel.d.ts
5
+ interface ItemTarget {
6
+ itemKind: ContextItemKind;
7
+ name: string;
8
+ owner: string;
9
+ }
2
10
  export declare function ContextPanel(): import("react").JSX.Element;
11
+ /** Presentation shared with the deterministic design stories. Totals never depend on the search. */
12
+ export declare function ContextPanelView({ groups, total, idle, systemPrompt, estimator, warnings, onSelect, children }: {
13
+ groups: readonly ContextGroup[];
14
+ total: number | null;
15
+ idle: number;
16
+ systemPrompt?: {
17
+ stage: string;
18
+ tokens: number;
19
+ } | null;
20
+ estimator?: string;
21
+ warnings?: Readonly<Record<string, readonly ContextToolWarning[]>>;
22
+ onSelect: (target: ItemTarget) => void;
23
+ children?: ReactNode;
24
+ }): import("react").JSX.Element;
3
25
  //#endregion
26
+ export {};
4
27
  //# sourceMappingURL=ContextPanel.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ContextPanel.d.mts","names":[],"sources":["../../../../src/web/features/activity/ContextPanel.tsx"],"mappings":";wBAqEgB,gCAAY,IAAA"}
1
+ {"version":3,"file":"ContextPanel.d.mts","names":[],"sources":["../../../../src/web/features/activity/ContextPanel.tsx"],"mappings":";;;;UAwBU;EACR,UAAU;EACV;EACA;;wBAqBc,gCAAY,IAAA;;wBAyCZ,mBACd,QACA,OACA,MACA,cACA,WACA,UACA,UACA;EAEA,iBAAiB;EACjB;EACA;EACA;IAAiB;IAAe;;EAChC;EACA,WAAW,SAAS,wBAAwB;EAC5C,WAAW,QAAQ;EACnB,WAAW;oBACZ,IAAA"}
@@ -6,30 +6,12 @@ import { PluginSurface } from "../../components/PluginSurface.mjs";
6
6
  import { ContextItemDialog } from "./ContextItemDialog.mjs";
7
7
  import { useState } from "react";
8
8
  import { CONTEXT_TOOL_WARNINGS_STATUS_KEY, parseContextToolWarnings } from "@agimon-ai/po-core/contextApi";
9
- import { jsx, jsxs } from "react/jsx-runtime";
10
- import { Button, EmptyState } from "@agimon-ai/po-web-components";
9
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
+ import { Button, ChevronRightIcon, EmptyState, Input, SearchIcon } from "@agimon-ai/po-web-components";
11
11
  //#region src/web/features/activity/ContextPanel.tsx
12
- /** Short enough to sit in a fixed column without wrapping the tool's own name. */
13
- const SOURCE_LABEL = {
14
- extension: "ext",
15
- mcp: "mcp",
16
- plugin: "plug",
17
- core: "core"
18
- };
19
- /** The tilde is the whole caveat in one character: this is arithmetic, not a bill. */
20
12
  function tokens(value) {
21
13
  return value === null ? "-" : `~${value.toLocaleString()}`;
22
14
  }
23
- /**
24
- * What the session is carrying.
25
- *
26
- * Every tool and skill in here is spending context before the first message,
27
- * so the surface is built around the one question worth asking of it: what is
28
- * this costing, and what do I switch off to stop paying it. Modes head the
29
- * groups because a mode is what you switch; packages head the rows inside them
30
- * because a package is what you add or remove. It reads only; switching stays
31
- * with the composer chips that already own it.
32
- */
33
15
  function withoutMcpInventory(groups) {
34
16
  return groups.map((group) => {
35
17
  const mcp = group.items.filter((item) => item.source === "mcp");
@@ -57,192 +39,245 @@ function ContextPanel() {
57
39
  }
58
40
  const warnings = parseContextToolWarnings(statuses[CONTEXT_TOOL_WARNINGS_STATUS_KEY]);
59
41
  const groups = context ? projectedGroups(context) : contextGroups(statuses, widgets, projection);
60
- const total = totalTokens(groups);
61
- const idle = inactiveTotal(groups);
62
- const visibleGroups = withoutMcpInventory(groups);
42
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ContextPanelView, {
43
+ groups: withoutMcpInventory(groups),
44
+ total: totalTokens(groups),
45
+ idle: inactiveTotal(groups),
46
+ systemPrompt: context?.systemPrompt,
47
+ estimator: context?.estimator,
48
+ warnings,
49
+ onSelect: setTarget,
50
+ children: /* @__PURE__ */ jsx(PluginSurface, {
51
+ slot: HOST_SLOTS.context,
52
+ sessionId
53
+ })
54
+ }, sessionId), /* @__PURE__ */ jsx(ContextItemDialog, {
55
+ sessionId,
56
+ target,
57
+ warnings: target?.itemKind === "tool" && Object.hasOwn(warnings, target.name) ? warnings[target.name] : void 0,
58
+ onClose: () => setTarget(null)
59
+ })] });
60
+ }
61
+ /** Presentation shared with the deterministic design stories. Totals never depend on the search. */
62
+ function ContextPanelView({ groups, total, idle, systemPrompt, estimator = "gpt-tokenizer", warnings = {}, onSelect, children }) {
63
+ const [search, setSearch] = useState("");
64
+ const query = search.trim().toLowerCase();
65
+ const matches = (value) => value.toLowerCase().includes(query);
66
+ const visibleGroups = groups.filter((group) => matches(group.label) || group.items.some((item) => matches(item.owner) || matches(item.name)));
63
67
  return /* @__PURE__ */ jsxs("div", {
64
68
  "data-testid": "context-panel",
65
69
  className: "flex min-h-0 min-w-0 flex-1 flex-col",
66
70
  children: [
71
+ /* @__PURE__ */ jsxs("div", {
72
+ className: "shrink-0 space-y-3 p-3",
73
+ children: [/* @__PURE__ */ jsxs("div", {
74
+ className: "flex items-center gap-3 rounded-lg border border-po-border-soft bg-po-panel p-3",
75
+ children: [/* @__PURE__ */ jsxs("div", {
76
+ className: "min-w-0 flex-1",
77
+ children: [/* @__PURE__ */ jsx("div", {
78
+ className: "text-sm text-po-dim",
79
+ children: "Active context"
80
+ }), /* @__PURE__ */ jsxs("div", {
81
+ className: "flex flex-wrap items-baseline gap-2",
82
+ children: [/* @__PURE__ */ jsx("span", {
83
+ "data-testid": "context-total",
84
+ className: "text-3xl font-semibold text-po-hi tabular-nums",
85
+ children: tokens(total)
86
+ }), /* @__PURE__ */ jsx("span", {
87
+ className: "text-sm text-po-dim",
88
+ children: "tokens"
89
+ })]
90
+ })]
91
+ }), idle > 0 ? /* @__PURE__ */ jsxs("div", {
92
+ className: "shrink-0 border-l border-po-border pl-3",
93
+ children: [/* @__PURE__ */ jsx("div", {
94
+ className: "text-sm text-po-dim",
95
+ children: "Inactive"
96
+ }), /* @__PURE__ */ jsx("span", {
97
+ "data-testid": "context-inactive",
98
+ className: "text-lg font-medium text-po-dim tabular-nums",
99
+ children: `+${tokens(idle)}`
100
+ })]
101
+ }) : null]
102
+ }), /* @__PURE__ */ jsxs("div", {
103
+ className: "relative",
104
+ children: [/* @__PURE__ */ jsx(SearchIcon, {
105
+ "aria-hidden": true,
106
+ className: "pointer-events-none absolute top-2 left-3 size-4 text-po-dim"
107
+ }), /* @__PURE__ */ jsx(Input, {
108
+ type: "search",
109
+ "aria-label": "Find in context",
110
+ placeholder: "Find in context",
111
+ value: search,
112
+ onChange: (event) => setSearch(event.target.value),
113
+ className: "w-full pl-9 placeholder:text-po-dim"
114
+ })]
115
+ })]
116
+ }),
67
117
  /* @__PURE__ */ jsxs("div", {
68
118
  "data-testid": "context-scroll",
69
- className: "min-h-0 flex-1 overflow-y-auto",
70
- children: [/* @__PURE__ */ jsx(PluginSurface, {
71
- slot: HOST_SLOTS.context,
72
- sessionId
73
- }), groups.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
119
+ className: "min-h-0 flex-1 overflow-y-auto px-3 pb-3",
120
+ children: [children, groups.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
74
121
  "data-testid": "context-empty",
75
122
  className: "px-4 py-5",
76
- title: "no composition yet",
77
- description: "the session reports its major mode, minor modes, and domains once it has started."
78
- }) : /* @__PURE__ */ jsx("div", {
79
- className: "flex flex-col",
80
- children: visibleGroups.map((group) => /* @__PURE__ */ jsx(ContextGroupView, {
123
+ title: "No composition yet",
124
+ description: "The session reports its major mode, minor modes, and domains once it has started."
125
+ }) : visibleGroups.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
126
+ title: "No matches",
127
+ description: "Try a mode, package, tool, or skill name."
128
+ }) : /* @__PURE__ */ jsxs("div", {
129
+ className: "overflow-hidden rounded-lg border border-po-border-soft",
130
+ children: [/* @__PURE__ */ jsxs("div", {
131
+ className: "flex items-center justify-between gap-2 border-b border-po-border-soft px-3 py-3 text-sm text-po-dim",
132
+ children: [/* @__PURE__ */ jsx("span", { children: "Loaded context" }), /* @__PURE__ */ jsx("span", { children: "Est. tokens" })]
133
+ }), visibleGroups.map((group) => /* @__PURE__ */ jsx(ContextGroupView, {
81
134
  group,
135
+ query,
82
136
  warnings,
83
- onSelect: setTarget
84
- }, `${group.kind}:${group.id}`))
137
+ onSelect
138
+ }, `${group.kind}:${group.id}:${query ? "search" : "browse"}`))]
85
139
  })]
86
140
  }),
87
141
  /* @__PURE__ */ jsxs("div", {
88
- className: "shrink-0 border-t border-po-border px-4 py-3",
89
- children: [
90
- context?.systemPrompt ? /* @__PURE__ */ jsxs(Button, {
91
- variant: "ghost",
92
- size: "card",
93
- "data-testid": "context-system-prompt",
94
- title: "read the system prompt this session is running under",
95
- onClick: () => setTarget({
96
- itemKind: "prompt",
97
- name: "system",
98
- owner: "session"
99
- }),
100
- className: "flex-row items-center justify-between gap-2 rounded-none px-0 py-px",
101
- children: [/* @__PURE__ */ jsx("span", {
102
- className: "text-sm text-po-dim",
103
- children: context.systemPrompt.stage === "base" ? "system prompt (base)" : "system prompt"
104
- }), /* @__PURE__ */ jsx("span", {
105
- className: "text-sm text-po-dim",
106
- children: tokens(context.systemPrompt.tokens)
107
- })]
108
- }) : null,
109
- /* @__PURE__ */ jsxs("div", {
110
- className: "flex items-center justify-between",
111
- children: [/* @__PURE__ */ jsx("span", {
112
- className: "text-sm font-semibold text-po-text",
113
- children: "active"
114
- }), /* @__PURE__ */ jsx("span", {
115
- "data-testid": "context-total",
116
- className: "text-sm font-semibold text-po-text",
117
- children: tokens(total)
118
- })]
142
+ className: "shrink-0 space-y-2 border-t border-po-border px-4 py-3",
143
+ children: [systemPrompt ? /* @__PURE__ */ jsxs(Button, {
144
+ variant: "ghost",
145
+ size: "card",
146
+ "data-testid": "context-system-prompt",
147
+ title: "Read the system prompt this session is running under",
148
+ onClick: () => onSelect({
149
+ itemKind: "prompt",
150
+ name: "system",
151
+ owner: "session"
119
152
  }),
120
- idle > 0 ? /* @__PURE__ */ jsxs("div", {
121
- className: "flex items-center justify-between",
122
- children: [/* @__PURE__ */ jsx("span", {
123
- className: "text-sm text-po-dim",
124
- children: "inactive"
125
- }), /* @__PURE__ */ jsx("span", {
126
- "data-testid": "context-inactive",
127
- className: "text-sm text-po-dim",
128
- children: `+${tokens(idle)}`
129
- })]
130
- }) : null,
131
- /* @__PURE__ */ jsx("span", {
132
- className: "text-sm leading-relaxed text-po-dim",
133
- children: `estimated · ${context?.estimator ?? "gpt-tokenizer"} BPE · not a billed total`
134
- })
135
- ]
136
- }),
137
- /* @__PURE__ */ jsx(ContextItemDialog, {
138
- sessionId,
139
- target,
140
- warnings: target?.itemKind === "tool" && Object.hasOwn(warnings, target.name) ? warnings[target.name] : void 0,
141
- onClose: () => setTarget(null)
153
+ className: "flex-row items-center justify-between gap-2 px-0 py-1",
154
+ children: [/* @__PURE__ */ jsx("span", {
155
+ className: "text-sm text-po-text",
156
+ children: systemPrompt.stage === "base" ? "System prompt (base)" : "System prompt"
157
+ }), /* @__PURE__ */ jsx("span", {
158
+ className: "shrink-0 text-sm text-po-text tabular-nums",
159
+ children: tokens(systemPrompt.tokens)
160
+ })]
161
+ }) : null, /* @__PURE__ */ jsxs("p", {
162
+ className: "text-sm leading-relaxed text-po-dim",
163
+ children: [
164
+ "Token counts are estimates.",
165
+ /* @__PURE__ */ jsx("br", {}),
166
+ `${estimator} BPE · Not a billed total`
167
+ ]
168
+ })]
142
169
  })
143
170
  ]
144
171
  });
145
172
  }
146
- /** Active carries the bright ink, inactive the faint one, so what the model sees reads at a glance. */
147
- function ink(active) {
148
- return active ? "text-po-hi" : "text-po-dim";
149
- }
150
- function ContextGroupView({ group, warnings, onSelect }) {
173
+ function ContextGroupView({ group, query, warnings, onSelect }) {
151
174
  const groupActive = group.items.length === 0 || group.items.some((item) => item.active);
152
- return /* @__PURE__ */ jsxs("div", {
175
+ const groupMatches = group.label.toLowerCase().includes(query);
176
+ return /* @__PURE__ */ jsxs("details", {
177
+ open: true,
153
178
  "data-testid": `context-group-${group.id}`,
154
179
  "data-kind": group.kind,
155
180
  "data-active": groupActive,
156
- className: "flex flex-col gap-1 border-b border-po-border-soft px-3 py-3",
157
- children: [/* @__PURE__ */ jsxs("div", {
158
- className: "flex items-center gap-2 px-1",
181
+ className: "group/mode border-b border-po-border-soft last:border-b-0",
182
+ children: [/* @__PURE__ */ jsxs("summary", {
183
+ className: "flex cursor-pointer list-none items-center gap-2 px-3 py-3 focus-visible:outline-2 focus-visible:outline-po-selected [&::-webkit-details-marker]:hidden",
159
184
  children: [
160
- /* @__PURE__ */ jsx("span", {
185
+ /* @__PURE__ */ jsx(ChevronRightIcon, {
161
186
  "aria-hidden": true,
162
- className: "text-sm font-semibold text-po-faint",
163
- children: "#"
187
+ className: "size-3 shrink-0 text-po-dim group-open/mode:rotate-90"
164
188
  }),
165
189
  /* @__PURE__ */ jsx("span", {
166
- className: `min-w-0 flex-1 truncate text-lg font-semibold text-po-hi`,
190
+ className: `min-w-0 flex-1 truncate text-base font-semibold ${groupActive ? "text-po-hi" : "text-po-dim"}`,
167
191
  children: group.label
168
192
  }),
169
193
  /* @__PURE__ */ jsx("span", {
170
- className: "inline-block text-sm text-po-dim first-letter:uppercase",
194
+ className: "rounded-md border border-po-border px-1.5 text-sm text-po-dim first-letter:uppercase",
171
195
  children: group.kind
172
196
  }),
173
197
  /* @__PURE__ */ jsx("span", {
174
198
  "data-testid": `context-subtotal-${group.id}`,
175
- className: "w-14 text-right text-sm text-po-dim",
199
+ className: "w-16 shrink-0 text-right text-sm font-semibold text-po-text tabular-nums",
176
200
  children: tokens(group.tokens)
177
201
  })
178
202
  ]
179
203
  }), group.items.length === 0 ? /* @__PURE__ */ jsx("p", {
180
204
  "data-testid": `context-pending-${group.id}`,
181
- className: "px-1 text-sm text-po-dim",
182
- children: group.detail || "no tools or skills reported"
205
+ className: "px-3 pb-3 text-sm text-po-dim",
206
+ children: group.detail || "No tools or skills reported"
183
207
  }) : ownersOf(group).map((owner) => {
208
+ const ownerMatches = groupMatches || owner.owner.toLowerCase().includes(query);
209
+ const items = owner.items.filter((item) => ownerMatches || item.name.toLowerCase().includes(query));
210
+ if (items.length === 0) return null;
184
211
  const ownerActive = owner.items.some((item) => item.active);
185
- return /* @__PURE__ */ jsxs("div", {
186
- className: "flex flex-col",
187
- children: [/* @__PURE__ */ jsxs("div", {
212
+ return /* @__PURE__ */ jsxs("details", {
213
+ open: query ? true : owner.items.length > 1,
214
+ className: "group/owner border-t border-po-border-soft",
215
+ children: [/* @__PURE__ */ jsxs("summary", {
188
216
  "data-testid": `context-owner-${owner.owner}`,
189
217
  "data-active": ownerActive,
190
- title: owner.owner,
191
- className: "flex items-center gap-2 px-1 pt-1",
218
+ title: `${owner.owner} (${owner.source})`,
219
+ className: "flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 focus-visible:outline-2 focus-visible:outline-po-selected [&::-webkit-details-marker]:hidden",
192
220
  children: [
221
+ /* @__PURE__ */ jsx(ChevronRightIcon, {
222
+ "aria-hidden": true,
223
+ className: "size-3 shrink-0 text-po-dim group-open/owner:rotate-90"
224
+ }),
193
225
  /* @__PURE__ */ jsx("span", {
194
- className: `min-w-0 flex-1 truncate text-base font-semibold text-po-hi`,
226
+ className: `min-w-0 flex-1 truncate text-base font-semibold ${ownerActive ? "text-po-hi" : "text-po-dim"}`,
195
227
  children: ownerLabel(owner.owner)
196
228
  }),
197
- /* @__PURE__ */ jsx("span", {
229
+ owner.source !== "extension" ? /* @__PURE__ */ jsx("span", {
198
230
  className: "text-sm text-po-dim",
199
- children: SOURCE_LABEL[owner.source]
200
- }),
231
+ children: owner.source
232
+ }) : null,
201
233
  /* @__PURE__ */ jsx("span", {
202
- className: "w-14 text-right text-sm text-po-dim",
234
+ className: "w-16 shrink-0 text-right text-sm text-po-dim tabular-nums",
203
235
  children: tokens(owner.tokens)
204
236
  })
205
237
  ]
206
- }), owner.items.map((item) => /* @__PURE__ */ jsxs(Button, {
207
- variant: "ghost",
208
- size: "card",
209
- "data-testid": `context-row-${item.name}`,
210
- "data-active": item.active,
211
- onClick: () => onSelect({
212
- itemKind: item.itemKind,
213
- name: item.name,
214
- owner: item.owner
215
- }),
216
- className: "flex-row items-center gap-2 rounded-none py-px pr-1 pl-4",
217
- children: [
218
- /* @__PURE__ */ jsx("abbr", {
219
- title: item.itemKind,
220
- className: `w-4 shrink-0 text-center text-2xs font-semibold no-underline ${item.active ? "text-po-violet" : "text-po-faint"}`,
221
- children: item.itemKind === "skill" ? "S" : "T"
222
- }),
223
- /* @__PURE__ */ jsx("span", {
224
- className: `min-w-0 flex-1 truncate text-base font-medium ${ink(item.active)}`,
225
- children: item.name
238
+ }), /* @__PURE__ */ jsx("div", {
239
+ className: "mb-2 ml-6 border-l border-po-border pl-2",
240
+ children: items.map((item) => /* @__PURE__ */ jsxs(Button, {
241
+ variant: "ghost",
242
+ size: "card",
243
+ "data-testid": `context-row-${item.name}`,
244
+ "data-active": item.active,
245
+ title: item.name,
246
+ onClick: () => onSelect({
247
+ itemKind: item.itemKind,
248
+ name: item.name,
249
+ owner: item.owner
226
250
  }),
227
- item.itemKind === "tool" && Object.hasOwn(warnings, item.name) && (warnings[item.name]?.length ?? 0) > 0 ? /* @__PURE__ */ jsx("abbr", {
228
- "data-testid": `context-warning-${item.name}`,
229
- "aria-label": `${item.name} has warnings`,
230
- title: "warnings: open tool details for origins",
231
- className: "shrink-0 text-xs font-semibold text-po-yellow no-underline",
232
- children: "!"
233
- }) : null,
234
- /* @__PURE__ */ jsx("span", {
235
- title: item.active ? void 0 : "not sent to the model; costs nothing until switched on",
236
- className: `w-14 text-right text-sm ${item.active ? "text-po-text" : "text-po-dim"}`,
237
- children: item.active ? tokens(item.tokens) : `(${tokens(item.tokens)})`
238
- })
239
- ]
240
- }, `${item.itemKind}:${item.name}`))]
251
+ className: "flex-row items-center gap-2 rounded-md py-2 pr-3 pl-2",
252
+ children: [
253
+ /* @__PURE__ */ jsx("span", {
254
+ className: "shrink-0 rounded-md border border-po-border px-1.5 text-sm text-po-dim first-letter:uppercase",
255
+ children: item.itemKind
256
+ }),
257
+ /* @__PURE__ */ jsx("span", {
258
+ className: `min-w-0 flex-1 truncate text-base ${item.active ? "text-po-hi" : "text-po-dim"}`,
259
+ children: item.name
260
+ }),
261
+ item.itemKind === "tool" && Object.hasOwn(warnings, item.name) && (warnings[item.name]?.length ?? 0) > 0 ? /* @__PURE__ */ jsx("abbr", {
262
+ "data-testid": `context-warning-${item.name}`,
263
+ "aria-label": `${item.name} has warnings`,
264
+ title: "Warnings: open tool details for origins",
265
+ className: "shrink-0 text-sm font-semibold text-po-yellow no-underline",
266
+ children: "!"
267
+ }) : null,
268
+ /* @__PURE__ */ jsx("span", {
269
+ title: item.active ? void 0 : "Not sent to the model; costs nothing until switched on",
270
+ className: `w-16 shrink-0 text-right text-sm tabular-nums ${item.active ? "text-po-text" : "text-po-dim"}`,
271
+ children: item.active ? tokens(item.tokens) : `(${tokens(item.tokens)})`
272
+ })
273
+ ]
274
+ }, `${item.itemKind}:${item.name}`))
275
+ })]
241
276
  }, owner.owner);
242
277
  })]
243
278
  });
244
279
  }
245
280
  //#endregion
246
- export { ContextPanel };
281
+ export { ContextPanel, ContextPanelView };
247
282
 
248
283
  //# sourceMappingURL=ContextPanel.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ContextPanel.mjs","names":[],"sources":["../../../../src/web/features/activity/ContextPanel.tsx"],"sourcesContent":["import {\n CONTEXT_TOOL_WARNINGS_STATUS_KEY,\n parseContextToolWarnings,\n type ContextToolWarning,\n type ContextItemKind,\n} from '@agimon-ai/po-core/contextApi';\nimport { Button, EmptyState } from '@agimon-ai/po-web-components';\nimport { useState } from 'react';\n\nimport { PluginSurface } from '../../components/PluginSurface';\nimport {\n type ContextGroup,\n type ContextItemSource,\n contextGroups,\n inactiveTotal,\n ownerLabel,\n ownersOf,\n projectedGroups,\n totalTokens,\n} from '../../lib/contextComposition';\nimport { HOST_SLOTS } from '../../lib/pluginRegistry';\nimport { useActiveSessionMeta } from '../../stores/sessionsStore';\nimport { useActiveSession } from '../../stores/sessionStore';\nimport { ContextItemDialog } from './ContextItemDialog';\n\n/** The row a reader clicked, which is all the detail request is made of. */\ninterface ItemTarget {\n itemKind: ContextItemKind;\n name: string;\n owner: string;\n}\n\n/** Short enough to sit in a fixed column without wrapping the tool's own name. */\nconst SOURCE_LABEL: Record<ContextItemSource, string> = {\n extension: 'ext',\n mcp: 'mcp',\n plugin: 'plug',\n core: 'core',\n};\n\n/** The tilde is the whole caveat in one character: this is arithmetic, not a bill. */\nfunction tokens(value: number | null): string {\n return value === null ? '-' : `~${value.toLocaleString()}`;\n}\n\n/**\n * What the session is carrying.\n *\n * Every tool and skill in here is spending context before the first message,\n * so the surface is built around the one question worth asking of it: what is\n * this costing, and what do I switch off to stop paying it. Modes head the\n * groups because a mode is what you switch; packages head the rows inside them\n * because a package is what you add or remove. It reads only; switching stays\n * with the composer chips that already own it.\n */\nfunction withoutMcpInventory(groups: readonly ContextGroup[]): ContextGroup[] {\n return groups.map((group) => {\n const mcp = group.items.filter((item) => item.source === 'mcp');\n const activeMcpTokens = mcp.reduce((sum, item) => sum + (item.active ? (item.tokens ?? 0) : 0), 0);\n const inactiveMcpTokens = mcp.reduce((sum, item) => sum + (item.active ? 0 : (item.tokens ?? 0)), 0);\n return {\n ...group,\n items: group.items.filter((item) => item.source !== 'mcp'),\n tokens: group.tokens === null ? null : Math.max(0, group.tokens - activeMcpTokens),\n inactiveTokens: Math.max(0, group.inactiveTokens - inactiveMcpTokens),\n };\n });\n}\n\nexport function ContextPanel() {\n const statuses = useActiveSession((state) => state.statuses);\n const widgets = useActiveSession((state) => state.widgets);\n const projection = useActiveSession((state) => state.minorModes);\n const context = useActiveSession((state) => state.context);\n const sessionId = useActiveSessionMeta()?.summary.id ?? null;\n const [target, setTarget] = useState<ItemTarget | null>(null);\n const [targetSession, setTargetSession] = useState(sessionId);\n if (targetSession !== sessionId) {\n setTargetSession(sessionId);\n setTarget(null);\n }\n const warnings = parseContextToolWarnings(statuses[CONTEXT_TOOL_WARNINGS_STATUS_KEY]);\n // The runtime's grouping wins once it arrives; the status line only has to\n // carry the surface until the session has reported its inventory.\n const groups = context ? projectedGroups(context) : contextGroups(statuses, widgets, projection);\n const total = totalTokens(groups);\n const idle = inactiveTotal(groups);\n const visibleGroups = withoutMcpInventory(groups);\n return (\n <div data-testid=\"context-panel\" className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n <div data-testid=\"context-scroll\" className=\"min-h-0 flex-1 overflow-y-auto\">\n <PluginSurface slot={HOST_SLOTS.context} sessionId={sessionId} />\n {groups.length === 0 ? (\n <EmptyState\n data-testid=\"context-empty\"\n className=\"px-4 py-5\"\n title=\"no composition yet\"\n description=\"the session reports its major mode, minor modes, and domains once it has started.\"\n />\n ) : (\n <div className=\"flex flex-col\">\n {visibleGroups.map((group) => (\n <ContextGroupView\n key={`${group.kind}:${group.id}`}\n group={group}\n warnings={warnings}\n onSelect={setTarget}\n />\n ))}\n </div>\n )}\n </div>\n\n <div className=\"shrink-0 border-t border-po-border px-4 py-3\">\n {/* Above the totals rather than inside them. The prompt carries every\n tool's prose and every skill's listing, which the group subtotals\n have already counted, so the two figures overlap and adding them\n would bill the same text twice. */}\n {context?.systemPrompt ? (\n <Button\n variant=\"ghost\"\n size=\"card\"\n data-testid=\"context-system-prompt\"\n title=\"read the system prompt this session is running under\"\n onClick={() => setTarget({ itemKind: 'prompt', name: 'system', owner: 'session' })}\n className=\"flex-row items-center justify-between gap-2 rounded-none px-0 py-px\"\n >\n <span className=\"text-sm text-po-dim\">\n {context.systemPrompt.stage === 'base' ? 'system prompt (base)' : 'system prompt'}\n </span>\n <span className=\"text-sm text-po-dim\">{tokens(context.systemPrompt.tokens)}</span>\n </Button>\n ) : null}\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-semibold text-po-text\">active</span>\n <span data-testid=\"context-total\" className=\"text-sm font-semibold text-po-text\">\n {tokens(total)}\n </span>\n </div>\n {idle > 0 ? (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-po-dim\">inactive</span>\n <span data-testid=\"context-inactive\" className=\"text-sm text-po-dim\">\n {`+${tokens(idle)}`}\n </span>\n </div>\n ) : null}\n <span className=\"text-sm leading-relaxed text-po-dim\">\n {`estimated · ${context?.estimator ?? 'gpt-tokenizer'} BPE · not a billed total`}\n </span>\n </div>\n\n <ContextItemDialog\n sessionId={sessionId}\n target={target}\n warnings={\n target?.itemKind === 'tool' && Object.hasOwn(warnings, target.name) ? warnings[target.name] : undefined\n }\n onClose={() => setTarget(null)}\n />\n </div>\n );\n}\n\n/** Active carries the bright ink, inactive the faint one, so what the model sees reads at a glance. */\nfunction ink(active: boolean): string {\n return active ? 'text-po-hi' : 'text-po-dim';\n}\n\nfunction ContextGroupView({\n group,\n warnings,\n onSelect,\n}: {\n group: ContextGroup;\n warnings: Readonly<Record<string, readonly ContextToolWarning[]>>;\n onSelect: (target: ItemTarget) => void;\n}) {\n // A group that reported rows is active when any of them is; one still pending keeps the normal head.\n const groupActive = group.items.length === 0 || group.items.some((item) => item.active);\n return (\n <div\n data-testid={`context-group-${group.id}`}\n data-kind={group.kind}\n data-active={groupActive}\n className=\"flex flex-col gap-1 border-b border-po-border-soft px-3 py-3\"\n >\n {/* The same head the activity dock uses, for the same reason: the glyph\n says \"section\", so a mode never reads as one more row among the tools\n it brought with it. */}\n <div className=\"flex items-center gap-2 px-1\">\n <span aria-hidden className=\"text-sm font-semibold text-po-faint\">\n #\n </span>\n <span className={`min-w-0 flex-1 truncate text-lg font-semibold text-po-hi`}>{group.label}</span>\n <span className=\"inline-block text-sm text-po-dim first-letter:uppercase\">{group.kind}</span>\n <span data-testid={`context-subtotal-${group.id}`} className=\"w-14 text-right text-sm text-po-dim\">\n {tokens(group.tokens)}\n </span>\n </div>\n\n {group.items.length === 0 ? (\n <p data-testid={`context-pending-${group.id}`} className=\"px-1 text-sm text-po-dim\">\n {group.detail || 'no tools or skills reported'}\n </p>\n ) : (\n ownersOf(group).map((owner) => {\n const ownerActive = owner.items.some((item) => item.active);\n return (\n <div key={owner.owner} className=\"flex flex-col\">\n {/* The package heads its own rows, so the source tag sits here\n rather than repeating on every tool that shares it. */}\n <div\n data-testid={`context-owner-${owner.owner}`}\n data-active={ownerActive}\n title={owner.owner}\n className=\"flex items-center gap-2 px-1 pt-1\"\n >\n <span className={`min-w-0 flex-1 truncate text-base font-semibold text-po-hi`}>\n {ownerLabel(owner.owner)}\n </span>\n <span className=\"text-sm text-po-dim\">{SOURCE_LABEL[owner.source]}</span>\n <span className=\"w-14 text-right text-sm text-po-dim\">{tokens(owner.tokens)}</span>\n </div>\n {owner.items.map((item) => (\n // A row is a question as much as a figure: what am I paying for.\n // The answer is too large to have travelled with the panel, so\n // the row asks for it rather than carrying it.\n <Button\n variant=\"ghost\"\n size=\"card\"\n key={`${item.itemKind}:${item.name}`}\n data-testid={`context-row-${item.name}`}\n data-active={item.active}\n onClick={() => onSelect({ itemKind: item.itemKind, name: item.name, owner: item.owner })}\n className=\"flex-row items-center gap-2 rounded-none py-px pr-1 pl-4\"\n >\n <abbr\n title={item.itemKind}\n className={`w-4 shrink-0 text-center text-2xs font-semibold no-underline ${item.active ? 'text-po-violet' : 'text-po-faint'}`}\n >\n {item.itemKind === 'skill' ? 'S' : 'T'}\n </abbr>\n <span className={`min-w-0 flex-1 truncate text-base font-medium ${ink(item.active)}`}>\n {item.name}\n </span>\n {item.itemKind === 'tool' &&\n Object.hasOwn(warnings, item.name) &&\n (warnings[item.name]?.length ?? 0) > 0 ? (\n <abbr\n data-testid={`context-warning-${item.name}`}\n aria-label={`${item.name} has warnings`}\n title=\"warnings: open tool details for origins\"\n className=\"shrink-0 text-xs font-semibold text-po-yellow no-underline\"\n >\n !\n </abbr>\n ) : null}\n <span\n title={item.active ? undefined : 'not sent to the model; costs nothing until switched on'}\n className={`w-14 text-right text-sm ${item.active ? 'text-po-text' : 'text-po-dim'}`}\n >\n {item.active ? tokens(item.tokens) : `(${tokens(item.tokens)})`}\n </span>\n </Button>\n ))}\n </div>\n );\n })\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ContextPanel.mjs","names":[],"sources":["../../../../src/web/features/activity/ContextPanel.tsx"],"sourcesContent":["import {\n CONTEXT_TOOL_WARNINGS_STATUS_KEY,\n parseContextToolWarnings,\n type ContextToolWarning,\n type ContextItemKind,\n} from '@agimon-ai/po-core/contextApi';\nimport { Button, ChevronRightIcon, EmptyState, Input, SearchIcon } from '@agimon-ai/po-web-components';\nimport { type ReactNode, useState } from 'react';\n\nimport { PluginSurface } from '../../components/PluginSurface';\nimport {\n type ContextGroup,\n contextGroups,\n inactiveTotal,\n ownerLabel,\n ownersOf,\n projectedGroups,\n totalTokens,\n} from '../../lib/contextComposition';\nimport { HOST_SLOTS } from '../../lib/pluginRegistry';\nimport { useActiveSessionMeta } from '../../stores/sessionsStore';\nimport { useActiveSession } from '../../stores/sessionStore';\nimport { ContextItemDialog } from './ContextItemDialog';\n\ninterface ItemTarget {\n itemKind: ContextItemKind;\n name: string;\n owner: string;\n}\n\nfunction tokens(value: number | null): string {\n return value === null ? '-' : `~${value.toLocaleString()}`;\n}\n\nfunction withoutMcpInventory(groups: readonly ContextGroup[]): ContextGroup[] {\n return groups.map((group) => {\n const mcp = group.items.filter((item) => item.source === 'mcp');\n const activeMcpTokens = mcp.reduce((sum, item) => sum + (item.active ? (item.tokens ?? 0) : 0), 0);\n const inactiveMcpTokens = mcp.reduce((sum, item) => sum + (item.active ? 0 : (item.tokens ?? 0)), 0);\n return {\n ...group,\n items: group.items.filter((item) => item.source !== 'mcp'),\n tokens: group.tokens === null ? null : Math.max(0, group.tokens - activeMcpTokens),\n inactiveTokens: Math.max(0, group.inactiveTokens - inactiveMcpTokens),\n };\n });\n}\n\nexport function ContextPanel() {\n const statuses = useActiveSession((state) => state.statuses);\n const widgets = useActiveSession((state) => state.widgets);\n const projection = useActiveSession((state) => state.minorModes);\n const context = useActiveSession((state) => state.context);\n const sessionId = useActiveSessionMeta()?.summary.id ?? null;\n const [target, setTarget] = useState<ItemTarget | null>(null);\n const [targetSession, setTargetSession] = useState(sessionId);\n if (targetSession !== sessionId) {\n setTargetSession(sessionId);\n setTarget(null);\n }\n const warnings = parseContextToolWarnings(statuses[CONTEXT_TOOL_WARNINGS_STATUS_KEY]);\n const groups = context ? projectedGroups(context) : contextGroups(statuses, widgets, projection);\n return (\n <>\n <ContextPanelView\n key={sessionId}\n groups={withoutMcpInventory(groups)}\n total={totalTokens(groups)}\n idle={inactiveTotal(groups)}\n systemPrompt={context?.systemPrompt}\n estimator={context?.estimator}\n warnings={warnings}\n onSelect={setTarget}\n >\n <PluginSurface slot={HOST_SLOTS.context} sessionId={sessionId} />\n </ContextPanelView>\n <ContextItemDialog\n sessionId={sessionId}\n target={target}\n warnings={\n target?.itemKind === 'tool' && Object.hasOwn(warnings, target.name) ? warnings[target.name] : undefined\n }\n onClose={() => setTarget(null)}\n />\n </>\n );\n}\n\n/** Presentation shared with the deterministic design stories. Totals never depend on the search. */\nexport function ContextPanelView({\n groups,\n total,\n idle,\n systemPrompt,\n estimator = 'gpt-tokenizer',\n warnings = {},\n onSelect,\n children,\n}: {\n groups: readonly ContextGroup[];\n total: number | null;\n idle: number;\n systemPrompt?: { stage: string; tokens: number } | null;\n estimator?: string;\n warnings?: Readonly<Record<string, readonly ContextToolWarning[]>>;\n onSelect: (target: ItemTarget) => void;\n children?: ReactNode;\n}) {\n const [search, setSearch] = useState('');\n const query = search.trim().toLowerCase();\n const matches = (value: string) => value.toLowerCase().includes(query);\n const visibleGroups = groups.filter(\n (group) => matches(group.label) || group.items.some((item) => matches(item.owner) || matches(item.name)),\n );\n return (\n <div data-testid=\"context-panel\" className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n <div className=\"shrink-0 space-y-3 p-3\">\n <div className=\"flex items-center gap-3 rounded-lg border border-po-border-soft bg-po-panel p-3\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-sm text-po-dim\">Active context</div>\n <div className=\"flex flex-wrap items-baseline gap-2\">\n <span data-testid=\"context-total\" className=\"text-3xl font-semibold text-po-hi tabular-nums\">\n {tokens(total)}\n </span>\n <span className=\"text-sm text-po-dim\">tokens</span>\n </div>\n </div>\n {idle > 0 ? (\n <div className=\"shrink-0 border-l border-po-border pl-3\">\n <div className=\"text-sm text-po-dim\">Inactive</div>\n <span data-testid=\"context-inactive\" className=\"text-lg font-medium text-po-dim tabular-nums\">\n {`+${tokens(idle)}`}\n </span>\n </div>\n ) : null}\n </div>\n <div className=\"relative\">\n <SearchIcon aria-hidden className=\"pointer-events-none absolute top-2 left-3 size-4 text-po-dim\" />\n <Input\n type=\"search\"\n aria-label=\"Find in context\"\n placeholder=\"Find in context\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n className=\"w-full pl-9 placeholder:text-po-dim\"\n />\n </div>\n </div>\n <div data-testid=\"context-scroll\" className=\"min-h-0 flex-1 overflow-y-auto px-3 pb-3\">\n {children}\n {groups.length === 0 ? (\n <EmptyState\n data-testid=\"context-empty\"\n className=\"px-4 py-5\"\n title=\"No composition yet\"\n description=\"The session reports its major mode, minor modes, and domains once it has started.\"\n />\n ) : visibleGroups.length === 0 ? (\n <EmptyState title=\"No matches\" description=\"Try a mode, package, tool, or skill name.\" />\n ) : (\n <div className=\"overflow-hidden rounded-lg border border-po-border-soft\">\n <div className=\"flex items-center justify-between gap-2 border-b border-po-border-soft px-3 py-3 text-sm text-po-dim\">\n <span>Loaded context</span>\n <span>Est. tokens</span>\n </div>\n {visibleGroups.map((group) => (\n <ContextGroupView\n key={`${group.kind}:${group.id}:${query ? 'search' : 'browse'}`}\n group={group}\n query={query}\n warnings={warnings}\n onSelect={onSelect}\n />\n ))}\n </div>\n )}\n </div>\n <div className=\"shrink-0 space-y-2 border-t border-po-border px-4 py-3\">\n {/* Prompt tokens overlap the group subtotals. Do not add them to the active total. */}\n {systemPrompt ? (\n <Button\n variant=\"ghost\"\n size=\"card\"\n data-testid=\"context-system-prompt\"\n title=\"Read the system prompt this session is running under\"\n onClick={() => onSelect({ itemKind: 'prompt', name: 'system', owner: 'session' })}\n className=\"flex-row items-center justify-between gap-2 px-0 py-1\"\n >\n <span className=\"text-sm text-po-text\">\n {systemPrompt.stage === 'base' ? 'System prompt (base)' : 'System prompt'}\n </span>\n <span className=\"shrink-0 text-sm text-po-text tabular-nums\">{tokens(systemPrompt.tokens)}</span>\n </Button>\n ) : null}\n <p className=\"text-sm leading-relaxed text-po-dim\">\n Token counts are estimates.\n <br />\n {`${estimator} BPE · Not a billed total`}\n </p>\n </div>\n </div>\n );\n}\n\nfunction ContextGroupView({\n group,\n query,\n warnings,\n onSelect,\n}: {\n group: ContextGroup;\n query: string;\n warnings: Readonly<Record<string, readonly ContextToolWarning[]>>;\n onSelect: (target: ItemTarget) => void;\n}) {\n const groupActive = group.items.length === 0 || group.items.some((item) => item.active);\n const groupMatches = group.label.toLowerCase().includes(query);\n return (\n <details\n open\n data-testid={`context-group-${group.id}`}\n data-kind={group.kind}\n data-active={groupActive}\n className=\"group/mode border-b border-po-border-soft last:border-b-0\"\n >\n <summary className=\"flex cursor-pointer list-none items-center gap-2 px-3 py-3 focus-visible:outline-2 focus-visible:outline-po-selected [&::-webkit-details-marker]:hidden\">\n <ChevronRightIcon aria-hidden className=\"size-3 shrink-0 text-po-dim group-open/mode:rotate-90\" />\n <span\n className={`min-w-0 flex-1 truncate text-base font-semibold ${groupActive ? 'text-po-hi' : 'text-po-dim'}`}\n >\n {group.label}\n </span>\n <span className=\"rounded-md border border-po-border px-1.5 text-sm text-po-dim first-letter:uppercase\">\n {group.kind}\n </span>\n <span\n data-testid={`context-subtotal-${group.id}`}\n className=\"w-16 shrink-0 text-right text-sm font-semibold text-po-text tabular-nums\"\n >\n {tokens(group.tokens)}\n </span>\n </summary>\n {group.items.length === 0 ? (\n <p data-testid={`context-pending-${group.id}`} className=\"px-3 pb-3 text-sm text-po-dim\">\n {group.detail || 'No tools or skills reported'}\n </p>\n ) : (\n ownersOf(group).map((owner) => {\n const ownerMatches = groupMatches || owner.owner.toLowerCase().includes(query);\n const items = owner.items.filter((item) => ownerMatches || item.name.toLowerCase().includes(query));\n if (items.length === 0) return null;\n const ownerActive = owner.items.some((item) => item.active);\n return (\n <details\n key={owner.owner}\n open={query ? true : owner.items.length > 1}\n className=\"group/owner border-t border-po-border-soft\"\n >\n <summary\n data-testid={`context-owner-${owner.owner}`}\n data-active={ownerActive}\n title={`${owner.owner} (${owner.source})`}\n className=\"flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 focus-visible:outline-2 focus-visible:outline-po-selected [&::-webkit-details-marker]:hidden\"\n >\n <ChevronRightIcon aria-hidden className=\"size-3 shrink-0 text-po-dim group-open/owner:rotate-90\" />\n <span\n className={`min-w-0 flex-1 truncate text-base font-semibold ${ownerActive ? 'text-po-hi' : 'text-po-dim'}`}\n >\n {ownerLabel(owner.owner)}\n </span>\n {owner.source !== 'extension' ? <span className=\"text-sm text-po-dim\">{owner.source}</span> : null}\n <span className=\"w-16 shrink-0 text-right text-sm text-po-dim tabular-nums\">\n {tokens(owner.tokens)}\n </span>\n </summary>\n <div className=\"mb-2 ml-6 border-l border-po-border pl-2\">\n {items.map((item) => (\n <Button\n variant=\"ghost\"\n size=\"card\"\n key={`${item.itemKind}:${item.name}`}\n data-testid={`context-row-${item.name}`}\n data-active={item.active}\n title={item.name}\n onClick={() => onSelect({ itemKind: item.itemKind, name: item.name, owner: item.owner })}\n className=\"flex-row items-center gap-2 rounded-md py-2 pr-3 pl-2\"\n >\n <span className=\"shrink-0 rounded-md border border-po-border px-1.5 text-sm text-po-dim first-letter:uppercase\">\n {item.itemKind}\n </span>\n <span className={`min-w-0 flex-1 truncate text-base ${item.active ? 'text-po-hi' : 'text-po-dim'}`}>\n {item.name}\n </span>\n {item.itemKind === 'tool' &&\n Object.hasOwn(warnings, item.name) &&\n (warnings[item.name]?.length ?? 0) > 0 ? (\n <abbr\n data-testid={`context-warning-${item.name}`}\n aria-label={`${item.name} has warnings`}\n title=\"Warnings: open tool details for origins\"\n className=\"shrink-0 text-sm font-semibold text-po-yellow no-underline\"\n >\n !\n </abbr>\n ) : null}\n <span\n title={item.active ? undefined : 'Not sent to the model; costs nothing until switched on'}\n className={`w-16 shrink-0 text-right text-sm tabular-nums ${item.active ? 'text-po-text' : 'text-po-dim'}`}\n >\n {item.active ? tokens(item.tokens) : `(${tokens(item.tokens)})`}\n </span>\n </Button>\n ))}\n </div>\n </details>\n );\n })\n )}\n </details>\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.4",
3
+ "version": "0.0.0-alpha.6",
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.4",
65
- "@agimon-ai/po-web-components": "0.0.0-alpha.4",
66
- "@agimon-ai/po-web-security": "0.0.0-alpha.4",
64
+ "@agimon-ai/po-core": "0.0.0-alpha.6",
65
+ "@agimon-ai/po-web-components": "0.0.0-alpha.6",
66
+ "@agimon-ai/po-web-security": "0.0.0-alpha.6",
67
67
  "@earendil-works/chord": "1.2.0",
68
68
  "@simplewebauthn/browser": "^14.0.0",
69
69
  "@tanstack/react-router": "1.170.44",
@@ -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.4",
76
+ "@agimon-ai/vibe-lint-plugin-po-web": "0.0.0-alpha.6",
77
77
  "@types/node": "26.6.5",
78
78
  "@types/react": "19.3.0",
79
79
  "@types/react-dom": "19.3.0",