@flanksource/clicky-ui 0.3.4 → 0.3.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.
Files changed (77) hide show
  1. package/dist/ai.cjs +15 -0
  2. package/dist/ai.cjs.map +1 -1
  3. package/dist/ai.js +15 -0
  4. package/dist/ai.js.map +1 -1
  5. package/dist/data/ProgressBars.cjs +194 -0
  6. package/dist/data/ProgressBars.cjs.map +1 -0
  7. package/dist/data/ProgressBars.d.ts +85 -0
  8. package/dist/data/ProgressBars.d.ts.map +1 -0
  9. package/dist/data/ProgressBars.js +194 -0
  10. package/dist/data/ProgressBars.js.map +1 -0
  11. package/dist/data/ProgressBars.model.cjs +27 -0
  12. package/dist/data/ProgressBars.model.cjs.map +1 -0
  13. package/dist/data/ProgressBars.model.d.ts +20 -0
  14. package/dist/data/ProgressBars.model.d.ts.map +1 -0
  15. package/dist/data/ProgressBars.model.js +27 -0
  16. package/dist/data/ProgressBars.model.js.map +1 -0
  17. package/dist/data/TimeseriesCoreBars.cjs +19 -173
  18. package/dist/data/TimeseriesCoreBars.cjs.map +1 -1
  19. package/dist/data/TimeseriesCoreBars.d.ts +9 -26
  20. package/dist/data/TimeseriesCoreBars.d.ts.map +1 -1
  21. package/dist/data/TimeseriesCoreBars.js +20 -174
  22. package/dist/data/TimeseriesCoreBars.js.map +1 -1
  23. package/dist/data/WorkloadCard.model.cjs.map +1 -1
  24. package/dist/data/WorkloadCard.model.d.ts +2 -2
  25. package/dist/data/WorkloadCard.model.d.ts.map +1 -1
  26. package/dist/data/WorkloadCard.model.js.map +1 -1
  27. package/dist/data/ai/SessionViewer.cjs +275 -0
  28. package/dist/data/ai/SessionViewer.cjs.map +1 -0
  29. package/dist/data/ai/SessionViewer.d.ts +33 -0
  30. package/dist/data/ai/SessionViewer.d.ts.map +1 -0
  31. package/dist/data/ai/SessionViewer.fixtures.d.ts +4 -0
  32. package/dist/data/ai/SessionViewer.fixtures.d.ts.map +1 -0
  33. package/dist/data/ai/SessionViewer.js +275 -0
  34. package/dist/data/ai/SessionViewer.js.map +1 -0
  35. package/dist/data/ai/SessionViewer.model.cjs +275 -0
  36. package/dist/data/ai/SessionViewer.model.cjs.map +1 -0
  37. package/dist/data/ai/SessionViewer.model.d.ts +87 -0
  38. package/dist/data/ai/SessionViewer.model.d.ts.map +1 -0
  39. package/dist/data/ai/SessionViewer.model.js +275 -0
  40. package/dist/data/ai/SessionViewer.model.js.map +1 -0
  41. package/dist/data/ai/SessionViewerMenu.cjs +190 -0
  42. package/dist/data/ai/SessionViewerMenu.cjs.map +1 -0
  43. package/dist/data/ai/SessionViewerMenu.d.ts +24 -0
  44. package/dist/data/ai/SessionViewerMenu.d.ts.map +1 -0
  45. package/dist/data/ai/SessionViewerMenu.js +190 -0
  46. package/dist/data/ai/SessionViewerMenu.js.map +1 -0
  47. package/dist/data/ai/index.d.ts +3 -0
  48. package/dist/data/ai/index.d.ts.map +1 -1
  49. package/dist/data/ai/session-categories.cjs +219 -0
  50. package/dist/data/ai/session-categories.cjs.map +1 -0
  51. package/dist/data/ai/session-categories.d.ts +36 -0
  52. package/dist/data/ai/session-categories.d.ts.map +1 -0
  53. package/dist/data/ai/session-categories.js +219 -0
  54. package/dist/data/ai/session-categories.js.map +1 -0
  55. package/dist/data/test-runner/TestRunner.fixtures.d.ts.map +1 -1
  56. package/dist/data/version-info.cjs +3 -3
  57. package/dist/data/version-info.cjs.map +1 -1
  58. package/dist/data/version-info.js +3 -3
  59. package/dist/data/version-info.js.map +1 -1
  60. package/dist/data.cjs +4 -2
  61. package/dist/data.cjs.map +1 -1
  62. package/dist/data.d.ts +3 -2
  63. package/dist/data.d.ts.map +1 -1
  64. package/dist/data.js +4 -2
  65. package/dist/data.js.map +1 -1
  66. package/dist/index.cjs +4 -2
  67. package/dist/index.cjs.map +1 -1
  68. package/dist/index.js +4 -2
  69. package/dist/index.js.map +1 -1
  70. package/dist/styles.css +1 -1
  71. package/package.json +1 -1
  72. package/dist/data/TimeseriesCoreBars.model.cjs +0 -27
  73. package/dist/data/TimeseriesCoreBars.model.cjs.map +0 -1
  74. package/dist/data/TimeseriesCoreBars.model.d.ts +0 -20
  75. package/dist/data/TimeseriesCoreBars.model.d.ts.map +0 -1
  76. package/dist/data/TimeseriesCoreBars.model.js +0 -27
  77. package/dist/data/TimeseriesCoreBars.model.js.map +0 -1
@@ -0,0 +1,275 @@
1
+ import { UiClose } from "../../icons/components/UiClose.js";
2
+ import { UiHourglass } from "../../icons/components/UiHourglass.js";
3
+ import { UiNetwork } from "../../icons/components/UiNetwork.js";
4
+ import { UiCode2 } from "../../icons/components/UiCode2.js";
5
+ import { UiCamera } from "../../icons/components/UiCamera.js";
6
+ import { UiEye } from "../../icons/components/UiEye.js";
7
+ import { UiCommand } from "../../icons/components/UiCommand.js";
8
+ import { UiTerminal } from "../../icons/components/UiTerminal.js";
9
+ import { UiSelect } from "../../icons/components/UiSelect.js";
10
+ import { UiGlobe } from "../../icons/components/UiGlobe.js";
11
+ import { UiCloudDownload } from "../../icons/components/UiCloudDownload.js";
12
+ import { UiKanban } from "../../icons/components/UiKanban.js";
13
+ import { UiQuestion } from "../../icons/components/UiQuestion.js";
14
+ import { UiSearch } from "../../icons/components/UiSearch.js";
15
+ import { UiStop } from "../../icons/components/UiStop.js";
16
+ import { UiMagicWand } from "../../icons/components/UiMagicWand.js";
17
+ import { UiRobotAi } from "../../icons/components/UiRobotAi.js";
18
+ import { UiFileSearch } from "../../icons/components/UiFileSearch.js";
19
+ import { UiEdit } from "../../icons/components/UiEdit.js";
20
+ import { UiLayers } from "../../icons/components/UiLayers.js";
21
+ import { UiSave } from "../../icons/components/UiSave.js";
22
+ import { UiFileText } from "../../icons/components/UiFileText.js";
23
+ import { UiImage } from "../../icons/components/UiImage.js";
24
+ import { UiSparkles } from "../../icons/components/UiSparkles.js";
25
+ import { UiPuzzle } from "../../icons/components/UiPuzzle.js";
26
+ import { UiWrench } from "../../icons/components/UiWrench.js";
27
+ function truncate(text, max) {
28
+ return text.length <= max ? text : `${text.slice(0, max)}…`;
29
+ }
30
+ function parseEntries(input) {
31
+ if (Array.isArray(input)) return input;
32
+ const text = input.trim();
33
+ if (!text) return [];
34
+ try {
35
+ const parsed = JSON.parse(text);
36
+ return Array.isArray(parsed) ? parsed : [parsed];
37
+ } catch {
38
+ }
39
+ return text.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => {
40
+ try {
41
+ return JSON.parse(line);
42
+ } catch {
43
+ throw new Error(`SessionViewer: invalid session JSON line: ${truncate(line, 80)}`);
44
+ }
45
+ });
46
+ }
47
+ function roleFromType(type) {
48
+ return type === "user" || type === "assistant" ? type : "assistant";
49
+ }
50
+ function toolEvent(id, tool, input, response, timestamp, meta = {}) {
51
+ return {
52
+ id,
53
+ kind: "tool",
54
+ tool,
55
+ ...input ? { toolInput: input } : {},
56
+ ...response ? { toolResponse: response } : {},
57
+ ...timestamp ? { timestamp } : {},
58
+ ...meta.model ? { model: meta.model } : {},
59
+ ...meta.reasoningEffort ? { reasoningEffort: meta.reasoningEffort } : {},
60
+ ...meta.source ? { source: meta.source } : {}
61
+ };
62
+ }
63
+ function blockEvent(block, role, id, timestamp) {
64
+ if (block.type === "tool_use" || block.name && block.input) {
65
+ if (!block.name) return null;
66
+ return toolEvent(id, block.name, block.input, void 0, timestamp);
67
+ }
68
+ if (block.type === "thinking" || block.thinking) {
69
+ const text = block.thinking ?? block.text;
70
+ return text ? { id, kind: "thinking", text, ...timestamp ? { timestamp } : {} } : null;
71
+ }
72
+ if (block.text) {
73
+ return {
74
+ id,
75
+ kind: role === "user" ? "user" : "assistant",
76
+ text: block.text,
77
+ ...timestamp ? { timestamp } : {}
78
+ };
79
+ }
80
+ return null;
81
+ }
82
+ function normalizeSession(input) {
83
+ const entries = parseEntries(input);
84
+ const events = [];
85
+ entries.forEach((entry, seq) => {
86
+ var _a, _b;
87
+ const baseId = entry.uuid ?? `e${seq}`;
88
+ if (entry.isApiErrorMessage) {
89
+ events.push({
90
+ id: `${baseId}-err`,
91
+ kind: "error",
92
+ text: errorMessage(entry),
93
+ ...entry.error ? { errorType: entry.error } : {},
94
+ ...entry.apiErrorStatus ? { errorStatus: entry.apiErrorStatus } : {},
95
+ ...entry.timestamp ? { timestamp: entry.timestamp } : {}
96
+ });
97
+ return;
98
+ }
99
+ const tu = entry.tool_use;
100
+ if (tu == null ? void 0 : tu.tool) {
101
+ events.push(
102
+ toolEvent(`${baseId}-tool`, tu.tool, tu.input, tu.response, tu.timestamp ?? entry.timestamp, {
103
+ ...tu.model ? { model: tu.model } : {},
104
+ ...tu.reasoning_effort ? { reasoningEffort: tu.reasoning_effort } : {},
105
+ ...tu.source ? { source: tu.source } : {}
106
+ })
107
+ );
108
+ return;
109
+ }
110
+ const role = roleFromType(((_a = entry.message) == null ? void 0 : _a.role) ?? entry.type);
111
+ for (const [i, block] of (((_b = entry.message) == null ? void 0 : _b.content) ?? []).entries()) {
112
+ const ev = blockEvent(block, role, `${baseId}-${i}`, entry.timestamp);
113
+ if (ev) events.push(ev);
114
+ }
115
+ });
116
+ return events;
117
+ }
118
+ function errorMessage(entry) {
119
+ const status = entry.apiErrorStatus ? ` (HTTP ${entry.apiErrorStatus})` : "";
120
+ return `${entry.error ?? "API error"}${status}`;
121
+ }
122
+ const ACTIONS = {
123
+ // File operations
124
+ Read: { icon: UiFileText, tone: "sky", label: "Read file" },
125
+ Write: { icon: UiSave, tone: "amber", label: "Write file" },
126
+ Edit: { icon: UiEdit, tone: "violet", label: "Edit file" },
127
+ MultiEdit: { icon: UiLayers, tone: "violet", label: "Multi-edit" },
128
+ NotebookEdit: { icon: UiEdit, tone: "violet", label: "Edit notebook" },
129
+ // Search & navigation
130
+ Grep: { icon: UiSearch, tone: "amber", label: "Grep" },
131
+ Glob: { icon: UiFileSearch, tone: "sky", label: "Glob" },
132
+ // Execution & shell
133
+ Bash: { icon: UiTerminal, tone: "emerald", label: "Run command" },
134
+ BashOutput: { icon: UiCode2, tone: "emerald", label: "Shell output" },
135
+ KillShell: { icon: UiStop, tone: "rose", label: "Kill shell" },
136
+ KillBash: { icon: UiStop, tone: "rose", label: "Kill shell" },
137
+ // Agents & planning
138
+ Task: { icon: UiRobotAi, tone: "indigo", label: "Sub-agent task" },
139
+ Agent: { icon: UiRobotAi, tone: "indigo", label: "Sub-agent task" },
140
+ Skill: { icon: UiMagicWand, tone: "fuchsia", label: "Invoke skill" },
141
+ TodoWrite: { icon: UiKanban, tone: "sky", label: "Update todos" },
142
+ TaskCreate: { icon: UiKanban, tone: "sky", label: "Create task" },
143
+ TaskUpdate: { icon: UiKanban, tone: "sky", label: "Update task" },
144
+ TaskList: { icon: UiKanban, tone: "sky", label: "List tasks" },
145
+ TaskGet: { icon: UiKanban, tone: "sky", label: "Get task" },
146
+ TaskOutput: { icon: UiKanban, tone: "sky", label: "Task output" },
147
+ TaskStop: { icon: UiStop, tone: "rose", label: "Stop task" },
148
+ ToolSearch: { icon: UiSearch, tone: "amber", label: "Search tools" },
149
+ AskUserQuestion: { icon: UiQuestion, tone: "sky", label: "Ask user" },
150
+ EnterPlanMode: { icon: UiKanban, tone: "sky", label: "Enter plan mode" },
151
+ ExitPlanMode: { icon: UiKanban, tone: "sky", label: "Exit plan mode" },
152
+ // Web & browser automation
153
+ WebFetch: { icon: UiCloudDownload, tone: "sky", label: "Fetch URL" },
154
+ WebSearch: { icon: UiGlobe, tone: "sky", label: "Web search" },
155
+ browser_navigate: { icon: UiGlobe, tone: "sky", label: "Navigate" },
156
+ browser_navigate_back: { icon: UiGlobe, tone: "sky", label: "Navigate back" },
157
+ browser_click: { icon: UiSelect, tone: "sky", label: "Click" },
158
+ browser_triple_click: { icon: UiSelect, tone: "sky", label: "Triple click" },
159
+ browser_type: { icon: UiTerminal, tone: "emerald", label: "Type text" },
160
+ browser_press_key: { icon: UiCommand, tone: "amber", label: "Press key" },
161
+ browser_snapshot: { icon: UiEye, tone: "violet", label: "Snapshot" },
162
+ browser_take_screenshot: { icon: UiCamera, tone: "violet", label: "Screenshot" },
163
+ browser_evaluate: { icon: UiCode2, tone: "violet", label: "Evaluate JS" },
164
+ browser_network_requests: { icon: UiNetwork, tone: "emerald", label: "Network" },
165
+ browser_wait_for: { icon: UiHourglass, tone: "amber", label: "Wait" },
166
+ browser_close: { icon: UiClose, tone: "rose", label: "Close browser" }
167
+ };
168
+ const MCP_ICON_SERVERS = /* @__PURE__ */ new Set(["iconify", "icons8", "lucide", "react-icons"]);
169
+ const MCP_GEN_SERVERS = /* @__PURE__ */ new Set(["gemini", "openai", "anthropic"]);
170
+ function splitMcpTool(tool) {
171
+ const rest = tool.startsWith("mcp__") ? tool.slice("mcp__".length) : tool;
172
+ const idx = rest.indexOf("__");
173
+ if (idx < 0) return null;
174
+ return { server: rest.slice(0, idx), name: rest.slice(idx + 2) };
175
+ }
176
+ function getSessionAction(tool) {
177
+ const exact = ACTIONS[tool];
178
+ if (exact) return exact;
179
+ const mcp = splitMcpTool(tool);
180
+ if (mcp) {
181
+ const label = `${mcp.server}: ${mcp.name.replace(/_/g, " ")}`;
182
+ if (MCP_ICON_SERVERS.has(mcp.server)) return { icon: UiImage, tone: "pink", label };
183
+ if (MCP_GEN_SERVERS.has(mcp.server)) return { icon: UiSparkles, tone: "violet", label };
184
+ return { icon: UiPuzzle, tone: "violet", label };
185
+ }
186
+ return { icon: UiWrench, tone: "slate", label: tool };
187
+ }
188
+ function summarizeToolInput(tool, input) {
189
+ if (!input) return "";
190
+ const str = (key) => typeof input[key] === "string" ? input[key] : "";
191
+ const first = (...keys) => {
192
+ for (const key of keys) {
193
+ const value = str(key);
194
+ if (value) return value;
195
+ }
196
+ return "";
197
+ };
198
+ const mcp = splitMcpTool(tool);
199
+ if (mcp) {
200
+ const arg = first("url", "sql", "query", "element", "method");
201
+ return arg ? truncate(arg, 80) : "";
202
+ }
203
+ switch (tool) {
204
+ case "Bash":
205
+ case "browser_type":
206
+ return truncate(str("command") || str("text"), 80);
207
+ case "Read":
208
+ case "Write":
209
+ case "Edit":
210
+ case "MultiEdit":
211
+ case "NotebookEdit":
212
+ return first("file_path", "notebook_path", "path");
213
+ case "Grep":
214
+ return [str("pattern"), str("path")].filter(Boolean).join(" ");
215
+ case "Glob":
216
+ return str("pattern");
217
+ case "Task":
218
+ case "Agent":
219
+ return truncate(first("description", "prompt"), 80);
220
+ case "Skill":
221
+ return first("skill", "command", "name");
222
+ case "TaskCreate":
223
+ return truncate(first("subject", "description"), 80);
224
+ case "TaskUpdate": {
225
+ const id = first("taskId", "task_id", "id");
226
+ const status = str("status");
227
+ return status ? `${id} ${status}`.trim() : id;
228
+ }
229
+ case "TaskGet":
230
+ case "TaskOutput":
231
+ case "TaskStop":
232
+ return first("task_id", "taskId", "id");
233
+ case "ToolSearch":
234
+ case "WebSearch":
235
+ return truncate(str("query"), 80);
236
+ case "WebFetch":
237
+ case "browser_navigate":
238
+ return str("url");
239
+ case "AskUserQuestion": {
240
+ const questions = input["questions"];
241
+ return Array.isArray(questions) ? `${questions.length} questions` : "";
242
+ }
243
+ case "ExitPlanMode":
244
+ return str("planFilePath");
245
+ default:
246
+ return "";
247
+ }
248
+ }
249
+ function summarizeSession(events) {
250
+ let toolCount = 0;
251
+ let messageCount = 0;
252
+ const modelCounts = /* @__PURE__ */ new Map();
253
+ for (const event of events) {
254
+ if (event.kind === "tool") toolCount += 1;
255
+ if (event.kind === "user" || event.kind === "assistant") messageCount += 1;
256
+ if (event.model) modelCounts.set(event.model, (modelCounts.get(event.model) ?? 0) + 1);
257
+ }
258
+ let model;
259
+ let best = 0;
260
+ for (const [name, count] of modelCounts) {
261
+ if (count > best) {
262
+ best = count;
263
+ model = name;
264
+ }
265
+ }
266
+ return { toolCount, messageCount, ...model ? { model } : {} };
267
+ }
268
+ export {
269
+ getSessionAction,
270
+ normalizeSession,
271
+ splitMcpTool,
272
+ summarizeSession,
273
+ summarizeToolInput
274
+ };
275
+ //# sourceMappingURL=SessionViewer.model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionViewer.model.js","sources":["../../../src/data/ai/SessionViewer.model.ts"],"sourcesContent":["import type { StaticIconComponent } from \"../Icon\";\nimport {\n UiCamera,\n UiClose,\n UiCloudDownload,\n UiCode2,\n UiCommand,\n UiEdit,\n UiEye,\n UiFileSearch,\n UiFileText,\n UiGlobe,\n UiHourglass,\n UiImage,\n UiKanban,\n UiLayers,\n UiMagicWand,\n UiNetwork,\n UiPuzzle,\n UiQuestion,\n UiRobotAi,\n UiSave,\n UiSearch,\n UiSelect,\n UiSparkles,\n UiStop,\n UiTerminal,\n UiWrench,\n} from \"../../icons\";\n\n// ── Captain session schema ──────────────────────────────────────────────────\n// Mirrors github.com/flanksource/captain pkg/ai/history types. A session is a\n// list of SessionEntry records (the rows of a Claude Code / Codex JSONL log).\n\nexport interface SessionToolUse {\n tool?: string;\n input?: Record<string, unknown>;\n timestamp?: string;\n cwd?: string;\n session_id?: string;\n tool_use_id?: string;\n /** \"claude\" or \"codex\". */\n source?: string;\n model?: string;\n reasoning_effort?: string;\n response?: string;\n}\n\nexport interface SessionContent {\n /** Block kind: \"text\", \"thinking\", \"tool_use\", \"tool_result\", … */\n type?: string;\n text?: string;\n thinking?: string;\n name?: string;\n input?: Record<string, unknown>;\n id?: string;\n}\n\nexport interface SessionMessage {\n role?: string;\n stop_reason?: string;\n content?: SessionContent[];\n}\n\nexport interface SessionEntry {\n /** Top-level entry kind: assistant, user, system, … */\n type?: string;\n tool_use?: SessionToolUse;\n message?: SessionMessage;\n timestamp?: string;\n cwd?: string;\n sessionId?: string;\n uuid?: string;\n /** Synthetic assistant entry Claude Code writes when an API request fails. */\n isApiErrorMessage?: boolean;\n apiErrorStatus?: number;\n /** Claude Code's error classification (e.g. \"rate_limit\"). */\n error?: string;\n}\n\n/** Either a parsed list of entries or the raw log text (JSON array or JSONL). */\nexport type SessionInput = string | SessionEntry[];\n\n// ── Normalized events ───────────────────────────────────────────────────────\n// The raw schema interleaves consolidated `tool_use` rows with `message`\n// content blocks. normalizeSession flattens both shapes into a single ordered\n// list the viewer can render row-by-row.\n\nexport type SessionEventKind = \"user\" | \"assistant\" | \"thinking\" | \"tool\" | \"error\";\n\nexport interface SessionEvent {\n id: string;\n kind: SessionEventKind;\n /** Tool name (kind === \"tool\"). */\n tool?: string;\n toolInput?: Record<string, unknown>;\n toolResponse?: string;\n /** Prose for user/assistant/thinking, or the error message for errors. */\n text?: string;\n timestamp?: string;\n model?: string;\n reasoningEffort?: string;\n source?: string;\n errorType?: string;\n errorStatus?: number;\n}\n\nfunction truncate(text: string, max: number): string {\n return text.length <= max ? text : `${text.slice(0, max)}…`;\n}\n\n// parseEntries accepts a parsed array as-is, or parses a string as a whole-\n// document JSON (array or single object, possibly pretty-printed) first, then\n// falls back to JSONL (one object per line). The whole-document parse error is\n// caught only to attempt the JSONL fallback — a genuinely malformed JSONL line\n// throws loudly rather than being silently dropped.\nfunction parseEntries(input: SessionInput): SessionEntry[] {\n if (Array.isArray(input)) return input;\n const text = input.trim();\n if (!text) return [];\n try {\n const parsed: unknown = JSON.parse(text);\n return Array.isArray(parsed) ? (parsed as SessionEntry[]) : [parsed as SessionEntry];\n } catch {\n // not a single JSON document — treat as JSONL below\n }\n return text\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean)\n .map((line) => {\n try {\n return JSON.parse(line) as SessionEntry;\n } catch {\n throw new Error(`SessionViewer: invalid session JSON line: ${truncate(line, 80)}`);\n }\n });\n}\n\nfunction roleFromType(type: string | undefined): string {\n return type === \"user\" || type === \"assistant\" ? type : \"assistant\";\n}\n\nfunction toolEvent(\n id: string,\n tool: string,\n input: Record<string, unknown> | undefined,\n response: string | undefined,\n timestamp: string | undefined,\n meta: { model?: string; reasoningEffort?: string; source?: string } = {},\n): SessionEvent {\n return {\n id,\n kind: \"tool\",\n tool,\n ...(input ? { toolInput: input } : {}),\n ...(response ? { toolResponse: response } : {}),\n ...(timestamp ? { timestamp } : {}),\n ...(meta.model ? { model: meta.model } : {}),\n ...(meta.reasoningEffort ? { reasoningEffort: meta.reasoningEffort } : {}),\n ...(meta.source ? { source: meta.source } : {}),\n };\n}\n\nfunction blockEvent(\n block: SessionContent,\n role: string,\n id: string,\n timestamp: string | undefined,\n): SessionEvent | null {\n if (block.type === \"tool_use\" || (block.name && block.input)) {\n if (!block.name) return null;\n return toolEvent(id, block.name, block.input, undefined, timestamp);\n }\n if (block.type === \"thinking\" || block.thinking) {\n const text = block.thinking ?? block.text;\n return text ? { id, kind: \"thinking\", text, ...(timestamp ? { timestamp } : {}) } : null;\n }\n if (block.text) {\n return {\n id,\n kind: role === \"user\" ? \"user\" : \"assistant\",\n text: block.text,\n ...(timestamp ? { timestamp } : {}),\n };\n }\n return null;\n}\n\n/** Flatten a captain session (entries or raw log text) into ordered events. */\nexport function normalizeSession(input: SessionInput): SessionEvent[] {\n const entries = parseEntries(input);\n const events: SessionEvent[] = [];\n entries.forEach((entry, seq) => {\n const baseId = entry.uuid ?? `e${seq}`;\n\n if (entry.isApiErrorMessage) {\n events.push({\n id: `${baseId}-err`,\n kind: \"error\",\n text: errorMessage(entry),\n ...(entry.error ? { errorType: entry.error } : {}),\n ...(entry.apiErrorStatus ? { errorStatus: entry.apiErrorStatus } : {}),\n ...(entry.timestamp ? { timestamp: entry.timestamp } : {}),\n });\n return;\n }\n\n const tu = entry.tool_use;\n if (tu?.tool) {\n events.push(\n toolEvent(`${baseId}-tool`, tu.tool, tu.input, tu.response, tu.timestamp ?? entry.timestamp, {\n ...(tu.model ? { model: tu.model } : {}),\n ...(tu.reasoning_effort ? { reasoningEffort: tu.reasoning_effort } : {}),\n ...(tu.source ? { source: tu.source } : {}),\n }),\n );\n return;\n }\n\n const role = roleFromType(entry.message?.role ?? entry.type);\n for (const [i, block] of (entry.message?.content ?? []).entries()) {\n const ev = blockEvent(block, role, `${baseId}-${i}`, entry.timestamp);\n if (ev) events.push(ev);\n }\n });\n return events;\n}\n\nfunction errorMessage(entry: SessionEntry): string {\n const status = entry.apiErrorStatus ? ` (HTTP ${entry.apiErrorStatus})` : \"\";\n return `${entry.error ?? \"API error\"}${status}`;\n}\n\n// ── Action icon registry ────────────────────────────────────────────────────\n// Implements the Flanksource \"Coding agent action icons\" design: every agent\n// tool maps to a Phosphor-derived generated icon (Ui*) and a semantic tone so a\n// scan of the log reads at a glance. Tones group by surface — file reads (sky),\n// writes (amber), edits (violet), execution (emerald), agents (indigo),\n// destructive (rose), assets (pink/fuchsia).\n\nexport type SessionTone =\n | \"sky\"\n | \"amber\"\n | \"violet\"\n | \"emerald\"\n | \"rose\"\n | \"indigo\"\n | \"fuchsia\"\n | \"pink\"\n | \"slate\";\n\nexport interface SessionActionMeta {\n icon: StaticIconComponent;\n tone: SessionTone;\n /** Human-readable verb shown as the row heading. */\n label: string;\n}\n\nconst ACTIONS: Record<string, SessionActionMeta> = {\n // File operations\n Read: { icon: UiFileText, tone: \"sky\", label: \"Read file\" },\n Write: { icon: UiSave, tone: \"amber\", label: \"Write file\" },\n Edit: { icon: UiEdit, tone: \"violet\", label: \"Edit file\" },\n MultiEdit: { icon: UiLayers, tone: \"violet\", label: \"Multi-edit\" },\n NotebookEdit: { icon: UiEdit, tone: \"violet\", label: \"Edit notebook\" },\n // Search & navigation\n Grep: { icon: UiSearch, tone: \"amber\", label: \"Grep\" },\n Glob: { icon: UiFileSearch, tone: \"sky\", label: \"Glob\" },\n // Execution & shell\n Bash: { icon: UiTerminal, tone: \"emerald\", label: \"Run command\" },\n BashOutput: { icon: UiCode2, tone: \"emerald\", label: \"Shell output\" },\n KillShell: { icon: UiStop, tone: \"rose\", label: \"Kill shell\" },\n KillBash: { icon: UiStop, tone: \"rose\", label: \"Kill shell\" },\n // Agents & planning\n Task: { icon: UiRobotAi, tone: \"indigo\", label: \"Sub-agent task\" },\n Agent: { icon: UiRobotAi, tone: \"indigo\", label: \"Sub-agent task\" },\n Skill: { icon: UiMagicWand, tone: \"fuchsia\", label: \"Invoke skill\" },\n TodoWrite: { icon: UiKanban, tone: \"sky\", label: \"Update todos\" },\n TaskCreate: { icon: UiKanban, tone: \"sky\", label: \"Create task\" },\n TaskUpdate: { icon: UiKanban, tone: \"sky\", label: \"Update task\" },\n TaskList: { icon: UiKanban, tone: \"sky\", label: \"List tasks\" },\n TaskGet: { icon: UiKanban, tone: \"sky\", label: \"Get task\" },\n TaskOutput: { icon: UiKanban, tone: \"sky\", label: \"Task output\" },\n TaskStop: { icon: UiStop, tone: \"rose\", label: \"Stop task\" },\n ToolSearch: { icon: UiSearch, tone: \"amber\", label: \"Search tools\" },\n AskUserQuestion: { icon: UiQuestion, tone: \"sky\", label: \"Ask user\" },\n EnterPlanMode: { icon: UiKanban, tone: \"sky\", label: \"Enter plan mode\" },\n ExitPlanMode: { icon: UiKanban, tone: \"sky\", label: \"Exit plan mode\" },\n // Web & browser automation\n WebFetch: { icon: UiCloudDownload, tone: \"sky\", label: \"Fetch URL\" },\n WebSearch: { icon: UiGlobe, tone: \"sky\", label: \"Web search\" },\n browser_navigate: { icon: UiGlobe, tone: \"sky\", label: \"Navigate\" },\n browser_navigate_back: { icon: UiGlobe, tone: \"sky\", label: \"Navigate back\" },\n browser_click: { icon: UiSelect, tone: \"sky\", label: \"Click\" },\n browser_triple_click: { icon: UiSelect, tone: \"sky\", label: \"Triple click\" },\n browser_type: { icon: UiTerminal, tone: \"emerald\", label: \"Type text\" },\n browser_press_key: { icon: UiCommand, tone: \"amber\", label: \"Press key\" },\n browser_snapshot: { icon: UiEye, tone: \"violet\", label: \"Snapshot\" },\n browser_take_screenshot: { icon: UiCamera, tone: \"violet\", label: \"Screenshot\" },\n browser_evaluate: { icon: UiCode2, tone: \"violet\", label: \"Evaluate JS\" },\n browser_network_requests: { icon: UiNetwork, tone: \"emerald\", label: \"Network\" },\n browser_wait_for: { icon: UiHourglass, tone: \"amber\", label: \"Wait\" },\n browser_close: { icon: UiClose, tone: \"rose\", label: \"Close browser\" },\n};\n\n// MCP tools surface as `mcp__<server>__<name>` or `<server>__<name>`. Icon\n// servers get a distinct accent; generative servers reuse the sparkle; the rest\n// fall back to the puzzle piece.\nconst MCP_ICON_SERVERS = new Set([\"iconify\", \"icons8\", \"lucide\", \"react-icons\"]);\nconst MCP_GEN_SERVERS = new Set([\"gemini\", \"openai\", \"anthropic\"]);\n\ninterface McpTool {\n server: string;\n name: string;\n}\n\nexport function splitMcpTool(tool: string): McpTool | null {\n const rest = tool.startsWith(\"mcp__\") ? tool.slice(\"mcp__\".length) : tool;\n const idx = rest.indexOf(\"__\");\n if (idx < 0) return null;\n return { server: rest.slice(0, idx), name: rest.slice(idx + 2) };\n}\n\n/** Resolve the icon, tone, and label for a tool name. */\nexport function getSessionAction(tool: string): SessionActionMeta {\n const exact = ACTIONS[tool];\n if (exact) return exact;\n\n const mcp = splitMcpTool(tool);\n if (mcp) {\n const label = `${mcp.server}: ${mcp.name.replace(/_/g, \" \")}`;\n if (MCP_ICON_SERVERS.has(mcp.server)) return { icon: UiImage, tone: \"pink\", label };\n if (MCP_GEN_SERVERS.has(mcp.server)) return { icon: UiSparkles, tone: \"violet\", label };\n return { icon: UiPuzzle, tone: \"violet\", label };\n }\n\n return { icon: UiWrench, tone: \"slate\", label: tool };\n}\n\n// summarizeToolInput condenses a tool's input into a one-line preview, ported\n// from captain's history.FormatToolUseSummary so the viewer reads like the CLI.\nexport function summarizeToolInput(tool: string, input?: Record<string, unknown>): string {\n if (!input) return \"\";\n const str = (key: string): string => (typeof input[key] === \"string\" ? (input[key] as string) : \"\");\n const first = (...keys: string[]): string => {\n for (const key of keys) {\n const value = str(key);\n if (value) return value;\n }\n return \"\";\n };\n\n const mcp = splitMcpTool(tool);\n if (mcp) {\n const arg = first(\"url\", \"sql\", \"query\", \"element\", \"method\");\n return arg ? truncate(arg, 80) : \"\";\n }\n\n switch (tool) {\n case \"Bash\":\n case \"browser_type\":\n return truncate(str(\"command\") || str(\"text\"), 80);\n case \"Read\":\n case \"Write\":\n case \"Edit\":\n case \"MultiEdit\":\n case \"NotebookEdit\":\n return first(\"file_path\", \"notebook_path\", \"path\");\n case \"Grep\":\n return [str(\"pattern\"), str(\"path\")].filter(Boolean).join(\" \");\n case \"Glob\":\n return str(\"pattern\");\n case \"Task\":\n case \"Agent\":\n return truncate(first(\"description\", \"prompt\"), 80);\n case \"Skill\":\n return first(\"skill\", \"command\", \"name\");\n case \"TaskCreate\":\n return truncate(first(\"subject\", \"description\"), 80);\n case \"TaskUpdate\": {\n const id = first(\"taskId\", \"task_id\", \"id\");\n const status = str(\"status\");\n return status ? `${id} ${status}`.trim() : id;\n }\n case \"TaskGet\":\n case \"TaskOutput\":\n case \"TaskStop\":\n return first(\"task_id\", \"taskId\", \"id\");\n case \"ToolSearch\":\n case \"WebSearch\":\n return truncate(str(\"query\"), 80);\n case \"WebFetch\":\n case \"browser_navigate\":\n return str(\"url\");\n case \"AskUserQuestion\": {\n const questions = input[\"questions\"];\n return Array.isArray(questions) ? `${questions.length} questions` : \"\";\n }\n case \"ExitPlanMode\":\n return str(\"planFilePath\");\n default:\n return \"\";\n }\n}\n\n/** Count tool-call events, the dominant model, and totals for the header. */\nexport function summarizeSession(events: SessionEvent[]): {\n toolCount: number;\n messageCount: number;\n model?: string;\n} {\n let toolCount = 0;\n let messageCount = 0;\n const modelCounts = new Map<string, number>();\n for (const event of events) {\n if (event.kind === \"tool\") toolCount += 1;\n if (event.kind === \"user\" || event.kind === \"assistant\") messageCount += 1;\n if (event.model) modelCounts.set(event.model, (modelCounts.get(event.model) ?? 0) + 1);\n }\n let model: string | undefined;\n let best = 0;\n for (const [name, count] of modelCounts) {\n if (count > best) {\n best = count;\n model = name;\n }\n }\n return { toolCount, messageCount, ...(model ? { model } : {}) };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA2GA,SAAS,SAAS,MAAc,KAAqB;AACnD,SAAO,KAAK,UAAU,MAAM,OAAO,GAAG,KAAK,MAAM,GAAG,GAAG,CAAC;AAC1D;AAOA,SAAS,aAAa,OAAqC;AACzD,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO;AACjC,QAAM,OAAO,MAAM,KAAA;AACnB,MAAI,CAAC,KAAM,QAAO,CAAA;AAClB,MAAI;AACF,UAAM,SAAkB,KAAK,MAAM,IAAI;AACvC,WAAO,MAAM,QAAQ,MAAM,IAAK,SAA4B,CAAC,MAAsB;AAAA,EACrF,QAAQ;AAAA,EAER;AACA,SAAO,KACJ,MAAM,IAAI,EACV,IAAI,CAAC,SAAS,KAAK,KAAA,CAAM,EACzB,OAAO,OAAO,EACd,IAAI,CAAC,SAAS;AACb,QAAI;AACF,aAAO,KAAK,MAAM,IAAI;AAAA,IACxB,QAAQ;AACN,YAAM,IAAI,MAAM,6CAA6C,SAAS,MAAM,EAAE,CAAC,EAAE;AAAA,IACnF;AAAA,EACF,CAAC;AACL;AAEA,SAAS,aAAa,MAAkC;AACtD,SAAO,SAAS,UAAU,SAAS,cAAc,OAAO;AAC1D;AAEA,SAAS,UACP,IACA,MACA,OACA,UACA,WACA,OAAsE,IACxD;AACd,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA,GAAI,QAAQ,EAAE,WAAW,MAAA,IAAU,CAAA;AAAA,IACnC,GAAI,WAAW,EAAE,cAAc,SAAA,IAAa,CAAA;AAAA,IAC5C,GAAI,YAAY,EAAE,UAAA,IAAc,CAAA;AAAA,IAChC,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAA,IAAU,CAAA;AAAA,IACzC,GAAI,KAAK,kBAAkB,EAAE,iBAAiB,KAAK,gBAAA,IAAoB,CAAA;AAAA,IACvE,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAA,IAAW,CAAA;AAAA,EAAC;AAEjD;AAEA,SAAS,WACP,OACA,MACA,IACA,WACqB;AACrB,MAAI,MAAM,SAAS,cAAe,MAAM,QAAQ,MAAM,OAAQ;AAC5D,QAAI,CAAC,MAAM,KAAM,QAAO;AACxB,WAAO,UAAU,IAAI,MAAM,MAAM,MAAM,OAAO,QAAW,SAAS;AAAA,EACpE;AACA,MAAI,MAAM,SAAS,cAAc,MAAM,UAAU;AAC/C,UAAM,OAAO,MAAM,YAAY,MAAM;AACrC,WAAO,OAAO,EAAE,IAAI,MAAM,YAAY,MAAM,GAAI,YAAY,EAAE,cAAc,CAAA,MAAQ;AAAA,EACtF;AACA,MAAI,MAAM,MAAM;AACd,WAAO;AAAA,MACL;AAAA,MACA,MAAM,SAAS,SAAS,SAAS;AAAA,MACjC,MAAM,MAAM;AAAA,MACZ,GAAI,YAAY,EAAE,cAAc,CAAA;AAAA,IAAC;AAAA,EAErC;AACA,SAAO;AACT;AAGO,SAAS,iBAAiB,OAAqC;AACpE,QAAM,UAAU,aAAa,KAAK;AAClC,QAAM,SAAyB,CAAA;AAC/B,UAAQ,QAAQ,CAAC,OAAO,QAAQ;;AAC9B,UAAM,SAAS,MAAM,QAAQ,IAAI,GAAG;AAEpC,QAAI,MAAM,mBAAmB;AAC3B,aAAO,KAAK;AAAA,QACV,IAAI,GAAG,MAAM;AAAA,QACb,MAAM;AAAA,QACN,MAAM,aAAa,KAAK;AAAA,QACxB,GAAI,MAAM,QAAQ,EAAE,WAAW,MAAM,MAAA,IAAU,CAAA;AAAA,QAC/C,GAAI,MAAM,iBAAiB,EAAE,aAAa,MAAM,eAAA,IAAmB,CAAA;AAAA,QACnE,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAA,IAAc,CAAA;AAAA,MAAC,CACzD;AACD;AAAA,IACF;AAEA,UAAM,KAAK,MAAM;AACjB,QAAI,yBAAI,MAAM;AACZ,aAAO;AAAA,QACL,UAAU,GAAG,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,aAAa,MAAM,WAAW;AAAA,UAC3F,GAAI,GAAG,QAAQ,EAAE,OAAO,GAAG,MAAA,IAAU,CAAA;AAAA,UACrC,GAAI,GAAG,mBAAmB,EAAE,iBAAiB,GAAG,iBAAA,IAAqB,CAAA;AAAA,UACrE,GAAI,GAAG,SAAS,EAAE,QAAQ,GAAG,OAAA,IAAW,CAAA;AAAA,QAAC,CAC1C;AAAA,MAAA;AAEH;AAAA,IACF;AAEA,UAAM,OAAO,eAAa,WAAM,YAAN,mBAAe,SAAQ,MAAM,IAAI;AAC3D,eAAW,CAAC,GAAG,KAAK,QAAM,WAAM,YAAN,mBAAe,YAAW,IAAI,WAAW;AACjE,YAAM,KAAK,WAAW,OAAO,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,MAAM,SAAS;AACpE,UAAI,GAAI,QAAO,KAAK,EAAE;AAAA,IACxB;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEA,SAAS,aAAa,OAA6B;AACjD,QAAM,SAAS,MAAM,iBAAiB,UAAU,MAAM,cAAc,MAAM;AAC1E,SAAO,GAAG,MAAM,SAAS,WAAW,GAAG,MAAM;AAC/C;AA2BA,MAAM,UAA6C;AAAA;AAAA,EAEjD,MAAM,EAAE,MAAM,YAAY,MAAM,OAAO,OAAO,YAAA;AAAA,EAC9C,OAAO,EAAE,MAAM,QAAQ,MAAM,SAAS,OAAO,aAAA;AAAA,EAC7C,MAAM,EAAE,MAAM,QAAQ,MAAM,UAAU,OAAO,YAAA;AAAA,EAC7C,WAAW,EAAE,MAAM,UAAU,MAAM,UAAU,OAAO,aAAA;AAAA,EACpD,cAAc,EAAE,MAAM,QAAQ,MAAM,UAAU,OAAO,gBAAA;AAAA;AAAA,EAErD,MAAM,EAAE,MAAM,UAAU,MAAM,SAAS,OAAO,OAAA;AAAA,EAC9C,MAAM,EAAE,MAAM,cAAc,MAAM,OAAO,OAAO,OAAA;AAAA;AAAA,EAEhD,MAAM,EAAE,MAAM,YAAY,MAAM,WAAW,OAAO,cAAA;AAAA,EAClD,YAAY,EAAE,MAAM,SAAS,MAAM,WAAW,OAAO,eAAA;AAAA,EACrD,WAAW,EAAE,MAAM,QAAQ,MAAM,QAAQ,OAAO,aAAA;AAAA,EAChD,UAAU,EAAE,MAAM,QAAQ,MAAM,QAAQ,OAAO,aAAA;AAAA;AAAA,EAE/C,MAAM,EAAE,MAAM,WAAW,MAAM,UAAU,OAAO,iBAAA;AAAA,EAChD,OAAO,EAAE,MAAM,WAAW,MAAM,UAAU,OAAO,iBAAA;AAAA,EACjD,OAAO,EAAE,MAAM,aAAa,MAAM,WAAW,OAAO,eAAA;AAAA,EACpD,WAAW,EAAE,MAAM,UAAU,MAAM,OAAO,OAAO,eAAA;AAAA,EACjD,YAAY,EAAE,MAAM,UAAU,MAAM,OAAO,OAAO,cAAA;AAAA,EAClD,YAAY,EAAE,MAAM,UAAU,MAAM,OAAO,OAAO,cAAA;AAAA,EAClD,UAAU,EAAE,MAAM,UAAU,MAAM,OAAO,OAAO,aAAA;AAAA,EAChD,SAAS,EAAE,MAAM,UAAU,MAAM,OAAO,OAAO,WAAA;AAAA,EAC/C,YAAY,EAAE,MAAM,UAAU,MAAM,OAAO,OAAO,cAAA;AAAA,EAClD,UAAU,EAAE,MAAM,QAAQ,MAAM,QAAQ,OAAO,YAAA;AAAA,EAC/C,YAAY,EAAE,MAAM,UAAU,MAAM,SAAS,OAAO,eAAA;AAAA,EACpD,iBAAiB,EAAE,MAAM,YAAY,MAAM,OAAO,OAAO,WAAA;AAAA,EACzD,eAAe,EAAE,MAAM,UAAU,MAAM,OAAO,OAAO,kBAAA;AAAA,EACrD,cAAc,EAAE,MAAM,UAAU,MAAM,OAAO,OAAO,iBAAA;AAAA;AAAA,EAEpD,UAAU,EAAE,MAAM,iBAAiB,MAAM,OAAO,OAAO,YAAA;AAAA,EACvD,WAAW,EAAE,MAAM,SAAS,MAAM,OAAO,OAAO,aAAA;AAAA,EAChD,kBAAkB,EAAE,MAAM,SAAS,MAAM,OAAO,OAAO,WAAA;AAAA,EACvD,uBAAuB,EAAE,MAAM,SAAS,MAAM,OAAO,OAAO,gBAAA;AAAA,EAC5D,eAAe,EAAE,MAAM,UAAU,MAAM,OAAO,OAAO,QAAA;AAAA,EACrD,sBAAsB,EAAE,MAAM,UAAU,MAAM,OAAO,OAAO,eAAA;AAAA,EAC5D,cAAc,EAAE,MAAM,YAAY,MAAM,WAAW,OAAO,YAAA;AAAA,EAC1D,mBAAmB,EAAE,MAAM,WAAW,MAAM,SAAS,OAAO,YAAA;AAAA,EAC5D,kBAAkB,EAAE,MAAM,OAAO,MAAM,UAAU,OAAO,WAAA;AAAA,EACxD,yBAAyB,EAAE,MAAM,UAAU,MAAM,UAAU,OAAO,aAAA;AAAA,EAClE,kBAAkB,EAAE,MAAM,SAAS,MAAM,UAAU,OAAO,cAAA;AAAA,EAC1D,0BAA0B,EAAE,MAAM,WAAW,MAAM,WAAW,OAAO,UAAA;AAAA,EACrE,kBAAkB,EAAE,MAAM,aAAa,MAAM,SAAS,OAAO,OAAA;AAAA,EAC7D,eAAe,EAAE,MAAM,SAAS,MAAM,QAAQ,OAAO,gBAAA;AACvD;AAKA,MAAM,uCAAuB,IAAI,CAAC,WAAW,UAAU,UAAU,aAAa,CAAC;AAC/E,MAAM,kBAAkB,oBAAI,IAAI,CAAC,UAAU,UAAU,WAAW,CAAC;AAO1D,SAAS,aAAa,MAA8B;AACzD,QAAM,OAAO,KAAK,WAAW,OAAO,IAAI,KAAK,MAAM,QAAQ,MAAM,IAAI;AACrE,QAAM,MAAM,KAAK,QAAQ,IAAI;AAC7B,MAAI,MAAM,EAAG,QAAO;AACpB,SAAO,EAAE,QAAQ,KAAK,MAAM,GAAG,GAAG,GAAG,MAAM,KAAK,MAAM,MAAM,CAAC,EAAA;AAC/D;AAGO,SAAS,iBAAiB,MAAiC;AAChE,QAAM,QAAQ,QAAQ,IAAI;AAC1B,MAAI,MAAO,QAAO;AAElB,QAAM,MAAM,aAAa,IAAI;AAC7B,MAAI,KAAK;AACP,UAAM,QAAQ,GAAG,IAAI,MAAM,KAAK,IAAI,KAAK,QAAQ,MAAM,GAAG,CAAC;AAC3D,QAAI,iBAAiB,IAAI,IAAI,MAAM,EAAG,QAAO,EAAE,MAAM,SAAS,MAAM,QAAQ,MAAA;AAC5E,QAAI,gBAAgB,IAAI,IAAI,MAAM,EAAG,QAAO,EAAE,MAAM,YAAY,MAAM,UAAU,MAAA;AAChF,WAAO,EAAE,MAAM,UAAU,MAAM,UAAU,MAAA;AAAA,EAC3C;AAEA,SAAO,EAAE,MAAM,UAAU,MAAM,SAAS,OAAO,KAAA;AACjD;AAIO,SAAS,mBAAmB,MAAc,OAAyC;AACxF,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,MAAM,CAAC,QAAyB,OAAO,MAAM,GAAG,MAAM,WAAY,MAAM,GAAG,IAAe;AAChG,QAAM,QAAQ,IAAI,SAA2B;AAC3C,eAAW,OAAO,MAAM;AACtB,YAAM,QAAQ,IAAI,GAAG;AACrB,UAAI,MAAO,QAAO;AAAA,IACpB;AACA,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,aAAa,IAAI;AAC7B,MAAI,KAAK;AACP,UAAM,MAAM,MAAM,OAAO,OAAO,SAAS,WAAW,QAAQ;AAC5D,WAAO,MAAM,SAAS,KAAK,EAAE,IAAI;AAAA,EACnC;AAEA,UAAQ,MAAA;AAAA,IACN,KAAK;AAAA,IACL,KAAK;AACH,aAAO,SAAS,IAAI,SAAS,KAAK,IAAI,MAAM,GAAG,EAAE;AAAA,IACnD,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,MAAM,aAAa,iBAAiB,MAAM;AAAA,IACnD,KAAK;AACH,aAAO,CAAC,IAAI,SAAS,GAAG,IAAI,MAAM,CAAC,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAAA,IAC/D,KAAK;AACH,aAAO,IAAI,SAAS;AAAA,IACtB,KAAK;AAAA,IACL,KAAK;AACH,aAAO,SAAS,MAAM,eAAe,QAAQ,GAAG,EAAE;AAAA,IACpD,KAAK;AACH,aAAO,MAAM,SAAS,WAAW,MAAM;AAAA,IACzC,KAAK;AACH,aAAO,SAAS,MAAM,WAAW,aAAa,GAAG,EAAE;AAAA,IACrD,KAAK,cAAc;AACjB,YAAM,KAAK,MAAM,UAAU,WAAW,IAAI;AAC1C,YAAM,SAAS,IAAI,QAAQ;AAC3B,aAAO,SAAS,GAAG,EAAE,IAAI,MAAM,GAAG,SAAS;AAAA,IAC7C;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,MAAM,WAAW,UAAU,IAAI;AAAA,IACxC,KAAK;AAAA,IACL,KAAK;AACH,aAAO,SAAS,IAAI,OAAO,GAAG,EAAE;AAAA,IAClC,KAAK;AAAA,IACL,KAAK;AACH,aAAO,IAAI,KAAK;AAAA,IAClB,KAAK,mBAAmB;AACtB,YAAM,YAAY,MAAM,WAAW;AACnC,aAAO,MAAM,QAAQ,SAAS,IAAI,GAAG,UAAU,MAAM,eAAe;AAAA,IACtE;AAAA,IACA,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B;AACE,aAAO;AAAA,EAAA;AAEb;AAGO,SAAS,iBAAiB,QAI/B;AACA,MAAI,YAAY;AAChB,MAAI,eAAe;AACnB,QAAM,kCAAkB,IAAA;AACxB,aAAW,SAAS,QAAQ;AAC1B,QAAI,MAAM,SAAS,OAAQ,cAAa;AACxC,QAAI,MAAM,SAAS,UAAU,MAAM,SAAS,YAAa,iBAAgB;AACzE,QAAI,MAAM,MAAO,aAAY,IAAI,MAAM,QAAQ,YAAY,IAAI,MAAM,KAAK,KAAK,KAAK,CAAC;AAAA,EACvF;AACA,MAAI;AACJ,MAAI,OAAO;AACX,aAAW,CAAC,MAAM,KAAK,KAAK,aAAa;AACvC,QAAI,QAAQ,MAAM;AAChB,aAAO;AACP,cAAQ;AAAA,IACV;AAAA,EACF;AACA,SAAO,EAAE,WAAW,cAAc,GAAI,QAAQ,EAAE,MAAA,IAAU,GAAC;AAC7D;"}
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const utils = require("../../lib/utils.cjs");
5
+ const DropdownMenu = require("../../overlay/DropdownMenu.cjs");
6
+ const Icon = require("../Icon.cjs");
7
+ const sessionCategories = require("./session-categories.cjs");
8
+ const UiDotsVertical = require("../../icons/components/UiDotsVertical.cjs");
9
+ const UiResizeVertical = require("../../icons/components/UiResizeVertical.cjs");
10
+ const UiRows = require("../../icons/components/UiRows.cjs");
11
+ const UiListFlat = require("../../icons/components/UiListFlat.cjs");
12
+ const UiListDashes = require("../../icons/components/UiListDashes.cjs");
13
+ const UiDesktop = require("../../icons/components/UiDesktop.cjs");
14
+ const UiSun = require("../../icons/components/UiSun.cjs");
15
+ const UiMoon = require("../../icons/components/UiMoon.cjs");
16
+ const UiCheck = require("../../icons/components/UiCheck.cjs");
17
+ const DENSITY_OPTIONS = [
18
+ { value: "compact", icon: UiRows.UiRows, label: "Compact" },
19
+ { value: "comfortable", icon: UiListFlat.UiListFlat, label: "Comfortable" },
20
+ { value: "spacious", icon: UiListDashes.UiListDashes, label: "Spacious" }
21
+ ];
22
+ const THEME_OPTIONS = [
23
+ { value: "light", icon: UiSun.UiSun, label: "Light" },
24
+ { value: "dark", icon: UiMoon.UiMoon, label: "Dark" }
25
+ ];
26
+ function SessionViewerMenu({
27
+ density,
28
+ onDensityChange,
29
+ theme,
30
+ onThemeChange,
31
+ filters,
32
+ hiddenCategories,
33
+ hiddenTools,
34
+ hiddenSources,
35
+ onToggleCategory,
36
+ onToggleTool,
37
+ onToggleSource,
38
+ showThinking,
39
+ onToggleThinking,
40
+ hasThinking
41
+ }) {
42
+ return /* @__PURE__ */ jsxRuntime.jsx(
43
+ DropdownMenu.DropdownMenu,
44
+ {
45
+ icon: UiDotsVertical.UiDotsVertical,
46
+ hideChevron: true,
47
+ variant: "ghost",
48
+ size: "icon",
49
+ align: "right",
50
+ title: "Session options",
51
+ menuLabel: "Session options",
52
+ menuClassName: "min-w-[14rem] max-h-[70vh] overflow-auto px-1",
53
+ children: () => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-popover-foreground", children: [
54
+ /* @__PURE__ */ jsxRuntime.jsx(MenuHeading, { children: "Density" }),
55
+ /* @__PURE__ */ jsxRuntime.jsx(
56
+ RadioRow,
57
+ {
58
+ icon: UiResizeVertical.UiResizeVertical,
59
+ label: "Use page density",
60
+ active: density === void 0,
61
+ onSelect: () => onDensityChange(void 0)
62
+ }
63
+ ),
64
+ DENSITY_OPTIONS.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
65
+ RadioRow,
66
+ {
67
+ icon: option.icon,
68
+ label: option.label,
69
+ active: density === option.value,
70
+ onSelect: () => onDensityChange(option.value)
71
+ },
72
+ option.value
73
+ )),
74
+ /* @__PURE__ */ jsxRuntime.jsxs(Section, { heading: "Theme", children: [
75
+ /* @__PURE__ */ jsxRuntime.jsx(
76
+ RadioRow,
77
+ {
78
+ icon: UiDesktop.UiDesktop,
79
+ label: "Use page theme",
80
+ active: theme === void 0,
81
+ onSelect: () => onThemeChange(void 0)
82
+ }
83
+ ),
84
+ THEME_OPTIONS.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
85
+ RadioRow,
86
+ {
87
+ icon: option.icon,
88
+ label: option.label,
89
+ active: theme === option.value,
90
+ onSelect: () => onThemeChange(option.value)
91
+ },
92
+ option.value
93
+ ))
94
+ ] }),
95
+ filters.categories.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Section, { heading: "Categories", children: filters.categories.map((category) => /* @__PURE__ */ jsxRuntime.jsx(
96
+ CheckRow,
97
+ {
98
+ label: sessionCategories.CATEGORY_LABELS[category],
99
+ checked: !hiddenCategories.has(category),
100
+ onToggle: () => onToggleCategory(category)
101
+ },
102
+ category
103
+ )) }),
104
+ filters.tools.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Section, { heading: "Tools", children: filters.tools.map((tool) => /* @__PURE__ */ jsxRuntime.jsx(
105
+ CheckRow,
106
+ {
107
+ label: tool.label,
108
+ checked: !hiddenTools.has(tool.key),
109
+ onToggle: () => onToggleTool(tool.key)
110
+ },
111
+ tool.key
112
+ )) }),
113
+ filters.sources.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(Section, { heading: "Source", children: filters.sources.map((source) => /* @__PURE__ */ jsxRuntime.jsx(
114
+ CheckRow,
115
+ {
116
+ label: source,
117
+ checked: !hiddenSources.has(source),
118
+ onToggle: () => onToggleSource(source)
119
+ },
120
+ source
121
+ )) }),
122
+ hasThinking && /* @__PURE__ */ jsxRuntime.jsx(Section, { heading: "Other", children: /* @__PURE__ */ jsxRuntime.jsx(CheckRow, { label: "Reasoning", checked: showThinking, onToggle: onToggleThinking }) })
123
+ ] })
124
+ }
125
+ );
126
+ }
127
+ function MenuHeading({ children }) {
128
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 py-1.5 text-xs font-medium text-muted-foreground", children });
129
+ }
130
+ function Section({ heading, children }) {
131
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 border-t border-border pt-1", children: [
132
+ /* @__PURE__ */ jsxRuntime.jsx(MenuHeading, { children: heading }),
133
+ children
134
+ ] });
135
+ }
136
+ const ROW_CLASS = "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:outline-none";
137
+ function RadioRow({
138
+ icon,
139
+ label,
140
+ active,
141
+ onSelect
142
+ }) {
143
+ return /* @__PURE__ */ jsxRuntime.jsxs(
144
+ "button",
145
+ {
146
+ type: "button",
147
+ role: "menuitemradio",
148
+ "aria-checked": active,
149
+ className: utils.cn(ROW_CLASS, active && "text-foreground"),
150
+ onClick: onSelect,
151
+ children: [
152
+ /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon, className: "text-sm text-muted-foreground" }),
153
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: label }),
154
+ active ? /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiCheck.UiCheck, className: "text-sm text-foreground" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block h-4 w-4", "aria-hidden": true })
155
+ ]
156
+ }
157
+ );
158
+ }
159
+ function CheckRow({
160
+ label,
161
+ checked,
162
+ onToggle
163
+ }) {
164
+ return /* @__PURE__ */ jsxRuntime.jsxs(
165
+ "button",
166
+ {
167
+ type: "button",
168
+ role: "menuitemcheckbox",
169
+ "aria-checked": checked,
170
+ className: utils.cn(ROW_CLASS, !checked && "text-muted-foreground"),
171
+ onClick: onToggle,
172
+ children: [
173
+ /* @__PURE__ */ jsxRuntime.jsx(
174
+ "span",
175
+ {
176
+ "aria-hidden": true,
177
+ className: utils.cn(
178
+ "flex h-4 w-4 shrink-0 items-center justify-center rounded border",
179
+ checked ? "border-primary bg-primary text-primary-foreground" : "border-input"
180
+ ),
181
+ children: checked && /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiCheck.UiCheck, className: "text-[0.7rem]" })
182
+ }
183
+ ),
184
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: label })
185
+ ]
186
+ }
187
+ );
188
+ }
189
+ exports.SessionViewerMenu = SessionViewerMenu;
190
+ //# sourceMappingURL=SessionViewerMenu.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionViewerMenu.cjs","sources":["../../../src/data/ai/SessionViewerMenu.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { DropdownMenu } from \"../../overlay/DropdownMenu\";\nimport { Icon, type StaticIconComponent } from \"../Icon\";\nimport {\n UiCheck,\n UiDesktop,\n UiDotsVertical,\n UiListDashes,\n UiListFlat,\n UiMoon,\n UiResizeVertical,\n UiRows,\n UiSun,\n} from \"../../icons\";\nimport { type Density } from \"../../hooks/use-density\";\nimport { CATEGORY_LABELS, type SessionCategory, type SessionFilters } from \"./session-categories\";\n\nexport type SessionThemeOverride = \"light\" | \"dark\";\n\nconst DENSITY_OPTIONS: Array<{ value: Density; icon: StaticIconComponent; label: string }> = [\n { value: \"compact\", icon: UiRows, label: \"Compact\" },\n { value: \"comfortable\", icon: UiListFlat, label: \"Comfortable\" },\n { value: \"spacious\", icon: UiListDashes, label: \"Spacious\" },\n];\n\nconst THEME_OPTIONS: Array<{ value: SessionThemeOverride; icon: StaticIconComponent; label: string }> = [\n { value: \"light\", icon: UiSun, label: \"Light\" },\n { value: \"dark\", icon: UiMoon, label: \"Dark\" },\n];\n\nexport interface SessionViewerMenuProps {\n density: Density | undefined;\n onDensityChange: (density: Density | undefined) => void;\n theme: SessionThemeOverride | undefined;\n onThemeChange: (theme: SessionThemeOverride | undefined) => void;\n filters: SessionFilters;\n hiddenCategories: ReadonlySet<SessionCategory>;\n hiddenTools: ReadonlySet<string>;\n hiddenSources: ReadonlySet<string>;\n onToggleCategory: (category: SessionCategory) => void;\n onToggleTool: (tool: string) => void;\n onToggleSource: (source: string) => void;\n showThinking: boolean;\n onToggleThinking: () => void;\n hasThinking: boolean;\n}\n\n/** The SessionViewer's \"3-dot\" menu: a density override (matching DataTable's\n * pattern) plus visibility toggles for the captain category / tool / source\n * facets present in the session. */\nexport function SessionViewerMenu({\n density,\n onDensityChange,\n theme,\n onThemeChange,\n filters,\n hiddenCategories,\n hiddenTools,\n hiddenSources,\n onToggleCategory,\n onToggleTool,\n onToggleSource,\n showThinking,\n onToggleThinking,\n hasThinking,\n}: SessionViewerMenuProps) {\n return (\n <DropdownMenu\n icon={UiDotsVertical}\n hideChevron\n variant=\"ghost\"\n size=\"icon\"\n align=\"right\"\n title=\"Session options\"\n menuLabel=\"Session options\"\n menuClassName=\"min-w-[14rem] max-h-[70vh] overflow-auto px-1\"\n >\n {() => (\n <div className=\"text-popover-foreground\">\n <MenuHeading>Density</MenuHeading>\n <RadioRow\n icon={UiResizeVertical}\n label=\"Use page density\"\n active={density === undefined}\n onSelect={() => onDensityChange(undefined)}\n />\n {DENSITY_OPTIONS.map((option) => (\n <RadioRow\n key={option.value}\n icon={option.icon}\n label={option.label}\n active={density === option.value}\n onSelect={() => onDensityChange(option.value)}\n />\n ))}\n\n <Section heading=\"Theme\">\n <RadioRow\n icon={UiDesktop}\n label=\"Use page theme\"\n active={theme === undefined}\n onSelect={() => onThemeChange(undefined)}\n />\n {THEME_OPTIONS.map((option) => (\n <RadioRow\n key={option.value}\n icon={option.icon}\n label={option.label}\n active={theme === option.value}\n onSelect={() => onThemeChange(option.value)}\n />\n ))}\n </Section>\n\n {filters.categories.length > 0 && (\n <Section heading=\"Categories\">\n {filters.categories.map((category) => (\n <CheckRow\n key={category}\n label={CATEGORY_LABELS[category]}\n checked={!hiddenCategories.has(category)}\n onToggle={() => onToggleCategory(category)}\n />\n ))}\n </Section>\n )}\n\n {filters.tools.length > 0 && (\n <Section heading=\"Tools\">\n {filters.tools.map((tool) => (\n <CheckRow\n key={tool.key}\n label={tool.label}\n checked={!hiddenTools.has(tool.key)}\n onToggle={() => onToggleTool(tool.key)}\n />\n ))}\n </Section>\n )}\n\n {filters.sources.length > 1 && (\n <Section heading=\"Source\">\n {filters.sources.map((source) => (\n <CheckRow\n key={source}\n label={source}\n checked={!hiddenSources.has(source)}\n onToggle={() => onToggleSource(source)}\n />\n ))}\n </Section>\n )}\n\n {hasThinking && (\n <Section heading=\"Other\">\n <CheckRow label=\"Reasoning\" checked={showThinking} onToggle={onToggleThinking} />\n </Section>\n )}\n </div>\n )}\n </DropdownMenu>\n );\n}\n\nfunction MenuHeading({ children }: { children: ReactNode }) {\n return <div className=\"px-2 py-1.5 text-xs font-medium text-muted-foreground\">{children}</div>;\n}\n\nfunction Section({ heading, children }: { heading: string; children: ReactNode }) {\n return (\n <div className=\"mt-1 border-t border-border pt-1\">\n <MenuHeading>{heading}</MenuHeading>\n {children}\n </div>\n );\n}\n\nconst ROW_CLASS =\n \"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:outline-none\";\n\nfunction RadioRow({\n icon,\n label,\n active,\n onSelect,\n}: {\n icon: StaticIconComponent;\n label: string;\n active: boolean;\n onSelect: () => void;\n}) {\n return (\n <button\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={active}\n className={cn(ROW_CLASS, active && \"text-foreground\")}\n onClick={onSelect}\n >\n <Icon icon={icon} className=\"text-sm text-muted-foreground\" />\n <span className=\"min-w-0 flex-1 truncate\">{label}</span>\n {active ? (\n <Icon icon={UiCheck} className=\"text-sm text-foreground\" />\n ) : (\n <span className=\"inline-block h-4 w-4\" aria-hidden />\n )}\n </button>\n );\n}\n\nfunction CheckRow({\n label,\n checked,\n onToggle,\n}: {\n label: string;\n checked: boolean;\n onToggle: () => void;\n}) {\n return (\n <button\n type=\"button\"\n role=\"menuitemcheckbox\"\n aria-checked={checked}\n className={cn(ROW_CLASS, !checked && \"text-muted-foreground\")}\n onClick={onToggle}\n >\n <span\n aria-hidden\n className={cn(\n \"flex h-4 w-4 shrink-0 items-center justify-center rounded border\",\n checked ? \"border-primary bg-primary text-primary-foreground\" : \"border-input\",\n )}\n >\n {checked && <Icon icon={UiCheck} className=\"text-[0.7rem]\" />}\n </span>\n <span className=\"min-w-0 flex-1 truncate\">{label}</span>\n </button>\n );\n}\n"],"names":["UiRows","UiListFlat","UiListDashes","UiSun","UiMoon","jsx","DropdownMenu","UiDotsVertical","jsxs","UiResizeVertical","UiDesktop","CATEGORY_LABELS","cn","Icon","UiCheck"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { Density } from '../../hooks/use-density';
2
+ import { SessionCategory, SessionFilters } from './session-categories';
3
+ export type SessionThemeOverride = "light" | "dark";
4
+ export interface SessionViewerMenuProps {
5
+ density: Density | undefined;
6
+ onDensityChange: (density: Density | undefined) => void;
7
+ theme: SessionThemeOverride | undefined;
8
+ onThemeChange: (theme: SessionThemeOverride | undefined) => void;
9
+ filters: SessionFilters;
10
+ hiddenCategories: ReadonlySet<SessionCategory>;
11
+ hiddenTools: ReadonlySet<string>;
12
+ hiddenSources: ReadonlySet<string>;
13
+ onToggleCategory: (category: SessionCategory) => void;
14
+ onToggleTool: (tool: string) => void;
15
+ onToggleSource: (source: string) => void;
16
+ showThinking: boolean;
17
+ onToggleThinking: () => void;
18
+ hasThinking: boolean;
19
+ }
20
+ /** The SessionViewer's "3-dot" menu: a density override (matching DataTable's
21
+ * pattern) plus visibility toggles for the captain category / tool / source
22
+ * facets present in the session. */
23
+ export declare function SessionViewerMenu({ density, onDensityChange, theme, onThemeChange, filters, hiddenCategories, hiddenTools, hiddenSources, onToggleCategory, onToggleTool, onToggleSource, showThinking, onToggleThinking, hasThinking, }: SessionViewerMenuProps): import("react/jsx-runtime").JSX.Element;
24
+ //# sourceMappingURL=SessionViewerMenu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionViewerMenu.d.ts","sourceRoot":"","sources":["../../../src/data/ai/SessionViewerMenu.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAmB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAElG,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,MAAM,CAAC;AAapD,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,eAAe,EAAE,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,KAAK,IAAI,CAAC;IACxD,KAAK,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACxC,aAAa,EAAE,CAAC,KAAK,EAAE,oBAAoB,GAAG,SAAS,KAAK,IAAI,CAAC;IACjE,OAAO,EAAE,cAAc,CAAC;IACxB,gBAAgB,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC;IAC/C,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACjC,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,IAAI,CAAC;IACtD,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,YAAY,EAAE,OAAO,CAAC;IACtB,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;qCAEqC;AACrC,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,eAAe,EACf,KAAK,EACL,aAAa,EACb,OAAO,EACP,gBAAgB,EAChB,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,WAAW,GACZ,EAAE,sBAAsB,2CAiGxB"}