@shipfox/client-logs 17.0.0 → 22.0.0

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 (42) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-type.log +1 -0
  3. package/CHANGELOG.md +28 -0
  4. package/dist/components/agent-session-rows.d.ts +2 -1
  5. package/dist/components/agent-session-rows.d.ts.map +1 -1
  6. package/dist/components/agent-session-rows.js +34 -19
  7. package/dist/components/agent-session-rows.js.map +1 -1
  8. package/dist/components/log-group.d.ts +3 -2
  9. package/dist/components/log-group.d.ts.map +1 -1
  10. package/dist/components/log-group.js +12 -4
  11. package/dist/components/log-group.js.map +1 -1
  12. package/dist/components/log-view.d.ts +2 -1
  13. package/dist/components/log-view.d.ts.map +1 -1
  14. package/dist/components/log-view.js +76 -23
  15. package/dist/components/log-view.js.map +1 -1
  16. package/dist/components/output-log-row.d.ts.map +1 -1
  17. package/dist/components/output-log-row.js +1 -1
  18. package/dist/components/output-log-row.js.map +1 -1
  19. package/dist/components/system-markers.d.ts.map +1 -1
  20. package/dist/components/system-markers.js +9 -4
  21. package/dist/components/system-markers.js.map +1 -1
  22. package/dist/core/log-search.d.ts +7 -0
  23. package/dist/core/log-search.d.ts.map +1 -0
  24. package/dist/core/log-search.js +124 -0
  25. package/dist/core/log-search.js.map +1 -0
  26. package/dist/hooks/api/step-logs.d.ts +169 -1
  27. package/dist/hooks/api/step-logs.d.ts.map +1 -1
  28. package/dist/hooks/api/step-logs.js +23 -3
  29. package/dist/hooks/api/step-logs.js.map +1 -1
  30. package/dist/tsconfig.test.tsbuildinfo +1 -1
  31. package/package.json +3 -3
  32. package/src/components/agent-session-rows.tsx +36 -24
  33. package/src/components/log-group.tsx +18 -5
  34. package/src/components/log-view.test.tsx +146 -18
  35. package/src/components/log-view.tsx +81 -18
  36. package/src/components/output-log-row.tsx +5 -1
  37. package/src/components/system-markers.tsx +14 -4
  38. package/src/core/log-search.test.ts +96 -0
  39. package/src/core/log-search.ts +121 -0
  40. package/src/hooks/api/step-logs-query.test.tsx +42 -0
  41. package/src/hooks/api/step-logs.ts +19 -3
  42. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 13 files with swc (324.22ms)
2
+ Successfully compiled: 14 files with swc (200.33ms)
@@ -1 +1,2 @@
1
1
  $ shipfox-tsc-check
2
+ context canceled
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @shipfox/client-logs
2
2
 
3
+ ## 22.0.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [00c1cb8]
8
+ - Updated dependencies [7693eb3]
9
+ - Updated dependencies [00c1cb8]
10
+ - Updated dependencies [56f4526]
11
+ - @shipfox/react-ui@2.1.0
12
+
13
+ ## 21.0.0
14
+
15
+ ### Minor Changes
16
+
17
+ - 7db4171: Composes job detail logs into the Panel surface with searchable output and display controls.
18
+
19
+ ### Patch Changes
20
+
21
+ - f1d127e: Use dark contrast surfaces with readable semantic foregrounds across code and log views, preserve status on log-row edge accents, and add shared highlight and tooltip tokens for code and keyboard affordances.
22
+ - Updated dependencies [0f4abe4]
23
+ - Updated dependencies [71d0c44]
24
+ - Updated dependencies [30beb8f]
25
+ - Updated dependencies [16733a7]
26
+ - Updated dependencies [163c40a]
27
+ - Updated dependencies [6703982]
28
+ - Updated dependencies [f1d127e]
29
+ - @shipfox/react-ui@2.0.0
30
+
3
31
  ## 17.0.0
4
32
 
5
33
  ### Minor Changes
@@ -4,6 +4,7 @@ export interface AgentSessionRowsProps {
4
4
  resolvedToolCallIds: ReadonlySet<string>;
5
5
  toolCallNames: ReadonlyMap<string, string>;
6
6
  indent: number;
7
+ forceOpen?: boolean;
7
8
  }
8
- export declare function AgentSessionRows({ rows, resolvedToolCallIds, toolCallNames, indent, }: AgentSessionRowsProps): import("react").JSX.Element[];
9
+ export declare function AgentSessionRows({ rows, resolvedToolCallIds, toolCallNames, indent, forceOpen, }: AgentSessionRowsProps): import("react").JSX.Element[];
9
10
  //# sourceMappingURL=agent-session-rows.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-session-rows.d.ts","sourceRoot":"","sources":["../../src/components/agent-session-rows.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAC,cAAc,EAAqB,MAAM,oBAAoB,CAAC;AAO3E,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,SAAS,cAAc,EAAE,CAAC;IAChC,mBAAmB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACzC,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,mBAAmB,EACnB,aAAa,EACb,MAAM,GACP,EAAE,qBAAqB,iCAWvB"}
1
+ {"version":3,"file":"agent-session-rows.d.ts","sourceRoot":"","sources":["../../src/components/agent-session-rows.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAC,cAAc,EAAqB,MAAM,oBAAoB,CAAC;AAO3E,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,SAAS,cAAc,EAAE,CAAC;IAChC,mBAAmB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACzC,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,mBAAmB,EACnB,aAAa,EACb,MAAM,EACN,SAAiB,GAClB,EAAE,qBAAqB,iCAYvB"}
@@ -4,20 +4,31 @@ import { Icon } from '@shipfox/react-ui/icon';
4
4
  import { LogContent, LogDisclosure, LogDisclosureContent, LogDisclosureTrigger, LogRow } from '@shipfox/react-ui/log';
5
5
  import { Tooltip, TooltipContent, TooltipTrigger } from '@shipfox/react-ui/tooltip';
6
6
  import { cn } from '@shipfox/react-ui/utils';
7
- import { Fragment, useState } from 'react';
7
+ import { Fragment, useEffect, useState } from 'react';
8
8
  const PREVIEW_CHAR_LIMIT = 1200;
9
9
  const WORD_SUMMARY_CHAR_LIMIT = 5000;
10
10
  const WHITESPACE = /\s+/g;
11
11
  const WORD_SEPARATOR = /\s+/;
12
- export function AgentSessionRows({ rows, resolvedToolCallIds, toolCallNames, indent }) {
12
+ export function AgentSessionRows({ rows, resolvedToolCallIds, toolCallNames, indent, forceOpen = false }) {
13
13
  return rows.map((row, index)=>/*#__PURE__*/ _jsx(AgentSessionRowView, {
14
14
  row: row,
15
15
  resolvedToolCallIds: resolvedToolCallIds,
16
16
  toolCallNames: toolCallNames,
17
- indent: indent
17
+ indent: indent,
18
+ forceOpen: forceOpen
18
19
  }, `${row.kind}-${index}`));
19
20
  }
20
- function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent }) {
21
+ function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent, forceOpen }) {
22
+ const [open, setOpen] = useState(false);
23
+ useEffect(()=>{
24
+ if (forceOpen) setOpen(true);
25
+ }, [
26
+ forceOpen
27
+ ]);
28
+ const disclosureProps = {
29
+ open: forceOpen || open,
30
+ onOpenChange: setOpen
31
+ };
21
32
  switch(row.kind){
22
33
  case 'message':
23
34
  return /*#__PURE__*/ _jsx(LogRow, {
@@ -27,7 +38,7 @@ function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent }
27
38
  tone: row.terminalFailure ? 'error' : 'default',
28
39
  "data-log-terminal-failure": row.terminalFailure ? 'true' : undefined,
29
40
  children: /*#__PURE__*/ _jsx(LogContent, {
30
- className: "text-foreground-neutral-base",
41
+ className: "text-foreground-contrast-primary",
31
42
  children: /*#__PURE__*/ _jsxs("span", {
32
43
  className: "flex min-w-0 items-start gap-inline",
33
44
  children: [
@@ -66,17 +77,18 @@ function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent }
66
77
  case 'thinking':
67
78
  return /*#__PURE__*/ _jsxs(LogDisclosure, {
68
79
  indent: indent,
80
+ ...disclosureProps,
69
81
  children: [
70
82
  /*#__PURE__*/ _jsx(LogDisclosureTrigger, {
71
83
  summary: wordSummary(row.text),
72
84
  timestamp: new Date(row.timestamp),
73
- className: "text-foreground-neutral-subtle",
85
+ className: "text-foreground-contrast-secondary",
74
86
  children: "thinking"
75
87
  }),
76
88
  /*#__PURE__*/ _jsx(LogDisclosureContent, {
77
- className: "text-foreground-neutral-subtle",
89
+ className: "text-foreground-contrast-secondary",
78
90
  children: /*#__PURE__*/ _jsx(LogContent, {
79
- className: "text-foreground-neutral-subtle",
91
+ className: "text-foreground-contrast-secondary",
80
92
  children: /*#__PURE__*/ _jsx(PreviewText, {
81
93
  text: row.text
82
94
  })
@@ -89,6 +101,7 @@ function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent }
89
101
  const awaitingResult = row.id != null && !resolvedToolCallIds.has(row.id);
90
102
  return /*#__PURE__*/ _jsxs(LogDisclosure, {
91
103
  indent: indent,
104
+ ...disclosureProps,
92
105
  children: [
93
106
  /*#__PURE__*/ _jsx(LogDisclosureTrigger, {
94
107
  timestamp: new Date(row.timestamp),
@@ -152,12 +165,13 @@ function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent }
152
165
  const toolName = row.toolName === 'tool' ? (row.toolCallId != null ? toolCallNames.get(row.toolCallId) : undefined) ?? '(unmatched)' : row.toolName;
153
166
  return /*#__PURE__*/ _jsxs(LogDisclosure, {
154
167
  indent: indent,
168
+ ...disclosureProps,
155
169
  children: [
156
170
  /*#__PURE__*/ _jsx(LogDisclosureTrigger, {
157
171
  timestamp: new Date(row.timestamp),
158
172
  summary: compactPreview(row.output),
159
173
  trailing: /*#__PURE__*/ _jsxs("span", {
160
- className: cn('inline-flex items-center gap-tight', row.isError ? 'text-red-600 dark:text-red-400' : 'text-foreground-neutral-muted'),
174
+ className: cn('inline-flex items-center gap-tight', row.isError ? 'text-tag-error-icon' : 'text-foreground-contrast-secondary'),
161
175
  children: [
162
176
  /*#__PURE__*/ _jsx(Icon, {
163
177
  name: row.isError ? 'closeCircleLine' : 'checkLine',
@@ -188,7 +202,7 @@ function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent }
188
202
  /*#__PURE__*/ _jsx(LogDisclosureContent, {
189
203
  children: /*#__PURE__*/ _jsx(LogContent, {
190
204
  variant: "code",
191
- className: cn(row.isError && 'text-red-600 dark:text-red-400'),
205
+ className: "text-foreground-contrast-primary",
192
206
  children: /*#__PURE__*/ _jsx(PreviewText, {
193
207
  text: row.output
194
208
  })
@@ -205,7 +219,7 @@ function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent }
205
219
  tone: row.tone,
206
220
  "data-log-terminal-failure": row.terminalFailure ? 'true' : undefined,
207
221
  children: /*#__PURE__*/ _jsx(LogContent, {
208
- className: "text-foreground-neutral-muted",
222
+ className: "text-foreground-contrast-secondary",
209
223
  children: /*#__PURE__*/ _jsxs("span", {
210
224
  className: "inline-flex w-full items-center gap-inline",
211
225
  children: [
@@ -225,7 +239,7 @@ function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent }
225
239
  children: [
226
240
  ' · ',
227
241
  /*#__PURE__*/ _jsx("span", {
228
- className: "text-foreground-neutral-subtle",
242
+ className: "text-foreground-contrast-secondary",
229
243
  children: row.detail
230
244
  })
231
245
  ]
@@ -246,17 +260,18 @@ function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent }
246
260
  case 'raw':
247
261
  return /*#__PURE__*/ _jsxs(LogDisclosure, {
248
262
  indent: indent,
263
+ ...disclosureProps,
249
264
  children: [
250
265
  /*#__PURE__*/ _jsx(LogDisclosureTrigger, {
251
266
  timestamp: new Date(row.timestamp),
252
267
  summary: compactPreview(row.raw),
253
- className: "text-orange-600 dark:text-orange-400",
268
+ className: "text-foreground-contrast-primary",
254
269
  children: /*#__PURE__*/ _jsxs("span", {
255
270
  className: "inline-flex min-w-0 items-center gap-inline",
256
271
  children: [
257
272
  /*#__PURE__*/ _jsx(Icon, {
258
273
  name: "errorWarningLine",
259
- className: "size-14 flex-none",
274
+ className: "size-14 flex-none text-tag-warning-icon",
260
275
  "aria-hidden": "true"
261
276
  }),
262
277
  /*#__PURE__*/ _jsx("span", {
@@ -284,13 +299,13 @@ function MessageIcon({ role, terminalFailure }) {
284
299
  const name = terminalFailure ? 'closeCircleLine' : role === 'user' ? 'userLine' : role === 'assistant' ? 'robot2Line' : 'message2Line';
285
300
  return /*#__PURE__*/ _jsx(Icon, {
286
301
  name: name,
287
- className: cn('mt-[2px] size-14 flex-none', terminalFailure ? 'text-red-600 dark:text-red-400' : 'text-foreground-neutral-muted'),
302
+ className: cn('mt-[2px] size-14 flex-none', terminalFailure ? 'text-tag-error-icon' : 'text-foreground-contrast-secondary'),
288
303
  "aria-hidden": "true"
289
304
  });
290
305
  }
291
306
  function MessageRoleLabel({ label, terminalFailure }) {
292
307
  return /*#__PURE__*/ _jsx("span", {
293
- className: cn('min-w-0 font-code text-foreground-neutral-muted', terminalFailure && 'text-foreground-highlight-error'),
308
+ className: cn('min-w-0 font-code text-foreground-contrast-secondary', terminalFailure && 'text-foreground-contrast-primary'),
294
309
  children: /*#__PURE__*/ _jsx("span", {
295
310
  className: "truncate",
296
311
  children: label
@@ -302,7 +317,7 @@ function RowMetadata({ meta, className }) {
302
317
  const inlineMeta = meta.length === 1 && meta[0]?.inline !== false ? meta[0] : null;
303
318
  if (inlineMeta != null) {
304
319
  return /*#__PURE__*/ _jsx("span", {
305
- className: cn('font-code text-xs text-foreground-neutral-muted', className),
320
+ className: cn('font-code text-xs text-foreground-contrast-secondary', className),
306
321
  title: `${inlineMeta.label}: ${inlineMeta.value}`,
307
322
  children: inlineMeta.value
308
323
  });
@@ -321,7 +336,7 @@ function MetadataTrigger({ meta }) {
321
336
  asChild: true,
322
337
  children: /*#__PURE__*/ _jsx("button", {
323
338
  type: "button",
324
- className: "inline-flex size-20 flex-none items-center justify-center rounded-4 text-foreground-neutral-muted opacity-60 transition-opacity hover:bg-background-components-hover hover:text-foreground-neutral-base hover:opacity-100 focus-visible:opacity-100 focus-visible:shadow-[inset_0_0_0_2px_var(--color-primary-500)] group-hover/log-row:opacity-100",
339
+ className: "inline-flex size-20 flex-none items-center justify-center rounded-4 text-foreground-contrast-secondary opacity-60 transition-opacity hover:bg-background-components-hover hover:text-foreground-contrast-primary hover:opacity-100 focus-visible:opacity-100 focus-visible:shadow-focus-inset group-hover/log-row:opacity-100",
325
340
  "aria-label": "Show message metadata",
326
341
  children: /*#__PURE__*/ _jsx(Icon, {
327
342
  name: "informationLine",
@@ -363,7 +378,7 @@ function PreviewText({ text }) {
363
378
  truncated ? /*#__PURE__*/ _jsx("button", {
364
379
  type: "button",
365
380
  "aria-expanded": expanded,
366
- className: "ms-inline inline-flex min-h-24 items-center rounded-4 px-tight font-display text-xs text-foreground-highlight-interactive focus-visible:shadow-[inset_0_0_0_2px_var(--color-primary-500)]",
381
+ className: "ms-inline inline-flex min-h-24 items-center rounded-4 px-tight font-display text-xs text-foreground-highlight-interactive focus-visible:shadow-focus-inset",
367
382
  onClick: ()=>setExpanded((value)=>!value),
368
383
  children: expanded ? 'show less' : 'show more'
369
384
  }) : null
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/agent-session-rows.tsx"],"sourcesContent":["'use client';\n\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {\n LogContent,\n LogDisclosure,\n LogDisclosureContent,\n LogDisclosureTrigger,\n LogRow,\n} from '@shipfox/react-ui/log';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Fragment, useState} from 'react';\nimport type {SessionViewRow, SessionViewRowMeta} from '#core/log-model.js';\n\nconst PREVIEW_CHAR_LIMIT = 1200;\nconst WORD_SUMMARY_CHAR_LIMIT = 5000;\nconst WHITESPACE = /\\s+/g;\nconst WORD_SEPARATOR = /\\s+/;\n\nexport interface AgentSessionRowsProps {\n rows: readonly SessionViewRow[];\n resolvedToolCallIds: ReadonlySet<string>;\n toolCallNames: ReadonlyMap<string, string>;\n indent: number;\n}\n\nexport function AgentSessionRows({\n rows,\n resolvedToolCallIds,\n toolCallNames,\n indent,\n}: AgentSessionRowsProps) {\n return rows.map((row, index) => (\n <AgentSessionRowView\n // biome-ignore lint/suspicious/noArrayIndexKey: session rows are immutable and never reordered, so the index is stable; content keys would balloon to megabyte strings and collide on repeated id-less tool calls.\n key={`${row.kind}-${index}`}\n row={row}\n resolvedToolCallIds={resolvedToolCallIds}\n toolCallNames={toolCallNames}\n indent={indent}\n />\n ));\n}\n\nfunction AgentSessionRowView({\n row,\n resolvedToolCallIds,\n toolCallNames,\n indent,\n}: {\n row: SessionViewRow;\n resolvedToolCallIds: ReadonlySet<string>;\n toolCallNames: ReadonlyMap<string, string>;\n indent: number;\n}) {\n switch (row.kind) {\n case 'message':\n return (\n <LogRow\n lineNumber={null}\n timestamp={new Date(row.timestamp)}\n indent={indent}\n tone={row.terminalFailure ? 'error' : 'default'}\n data-log-terminal-failure={row.terminalFailure ? 'true' : undefined}\n >\n <LogContent className=\"text-foreground-neutral-base\">\n <span className=\"flex min-w-0 items-start gap-inline\">\n <MessageIcon role={row.role} terminalFailure={row.terminalFailure} />\n <span className=\"flex min-w-0 flex-1 flex-col gap-tight\">\n <span className=\"flex min-w-0 items-center gap-inline\">\n <MessageRoleLabel label={row.label} terminalFailure={row.terminalFailure} />\n <RowMetadata meta={row.meta} className=\"ml-auto flex-none\" />\n </span>\n <span className=\"block min-w-0\">\n <PreviewText text={row.text} />\n </span>\n </span>\n </span>\n </LogContent>\n </LogRow>\n );\n case 'thinking':\n return (\n <LogDisclosure indent={indent}>\n <LogDisclosureTrigger\n summary={wordSummary(row.text)}\n timestamp={new Date(row.timestamp)}\n className=\"text-foreground-neutral-subtle\"\n >\n thinking\n </LogDisclosureTrigger>\n <LogDisclosureContent className=\"text-foreground-neutral-subtle\">\n <LogContent className=\"text-foreground-neutral-subtle\">\n <PreviewText text={row.text} />\n </LogContent>\n </LogDisclosureContent>\n </LogDisclosure>\n );\n case 'tool-call': {\n const awaitingResult = row.id != null && !resolvedToolCallIds.has(row.id);\n return (\n <LogDisclosure indent={indent}>\n <LogDisclosureTrigger\n timestamp={new Date(row.timestamp)}\n summary={compactPreview(row.summary ?? row.input)}\n trailing={\n awaitingResult ? (\n <span className=\"inline-flex items-center gap-tight\">\n <Icon\n name=\"loader4Line\"\n className=\"size-12 motion-safe:animate-spin\"\n aria-hidden=\"true\"\n />\n awaiting result\n </span>\n ) : null\n }\n >\n <span className=\"inline-flex min-w-0 items-center gap-inline\">\n <Icon name=\"terminalBoxLine\" className=\"size-14 flex-none\" aria-hidden=\"true\" />\n <span className=\"truncate\">tool {row.name}</span>\n </span>\n </LogDisclosureTrigger>\n <LogDisclosureContent>\n {row.summary != null ? (\n <>\n <LogContent>\n <PreviewText text={row.summary} />\n </LogContent>\n <LogContent variant=\"code\">\n <PreviewText text={row.input} />\n </LogContent>\n </>\n ) : (\n <LogContent variant=\"code\">\n <PreviewText text={row.input} />\n </LogContent>\n )}\n </LogDisclosureContent>\n </LogDisclosure>\n );\n }\n case 'tool-result': {\n const toolName =\n row.toolName === 'tool'\n ? ((row.toolCallId != null ? toolCallNames.get(row.toolCallId) : undefined) ??\n '(unmatched)')\n : row.toolName;\n return (\n <LogDisclosure indent={indent}>\n <LogDisclosureTrigger\n timestamp={new Date(row.timestamp)}\n summary={compactPreview(row.output)}\n trailing={\n <span\n className={cn(\n 'inline-flex items-center gap-tight',\n row.isError ? 'text-red-600 dark:text-red-400' : 'text-foreground-neutral-muted',\n )}\n >\n <Icon\n name={row.isError ? 'closeCircleLine' : 'checkLine'}\n className=\"size-12\"\n aria-hidden=\"true\"\n />\n {row.isError ? 'error' : 'ok'}\n </span>\n }\n >\n <span className=\"inline-flex min-w-0 items-center gap-inline\">\n <Icon name=\"terminalWindowLine\" className=\"size-14 flex-none\" aria-hidden=\"true\" />\n <span className=\"truncate\">result {toolName}</span>\n </span>\n </LogDisclosureTrigger>\n <LogDisclosureContent>\n <LogContent\n variant=\"code\"\n className={cn(row.isError && 'text-red-600 dark:text-red-400')}\n >\n <PreviewText text={row.output} />\n </LogContent>\n </LogDisclosureContent>\n </LogDisclosure>\n );\n }\n case 'lifecycle':\n return (\n <LogRow\n lineNumber={null}\n timestamp={new Date(row.timestamp)}\n indent={indent}\n tone={row.tone}\n data-log-terminal-failure={row.terminalFailure ? 'true' : undefined}\n >\n <LogContent className=\"text-foreground-neutral-muted\">\n <span className=\"inline-flex w-full items-center gap-inline\">\n <Icon name=\"informationLine\" className=\"size-14 flex-none\" aria-hidden=\"true\" />\n <span className=\"min-w-0\">\n <span className=\"font-medium\">{row.label}</span>\n {row.detail != null ? (\n <>\n {' · '}\n <span className=\"text-foreground-neutral-subtle\">{row.detail}</span>\n </>\n ) : null}\n </span>\n <span\n aria-hidden=\"true\"\n className=\"h-px flex-1 border-t border-dashed border-current opacity-30\"\n />\n <RowMetadata meta={row.meta} />\n </span>\n </LogContent>\n </LogRow>\n );\n case 'raw':\n return (\n <LogDisclosure indent={indent}>\n <LogDisclosureTrigger\n timestamp={new Date(row.timestamp)}\n summary={compactPreview(row.raw)}\n className=\"text-orange-600 dark:text-orange-400\"\n >\n <span className=\"inline-flex min-w-0 items-center gap-inline\">\n <Icon name=\"errorWarningLine\" className=\"size-14 flex-none\" aria-hidden=\"true\" />\n <span className=\"truncate\">{row.label}</span>\n </span>\n </LogDisclosureTrigger>\n <LogDisclosureContent>\n <LogContent variant=\"code\">\n <PreviewText text={row.raw} />\n </LogContent>\n </LogDisclosureContent>\n </LogDisclosure>\n );\n default:\n return assertNever(row);\n }\n}\n\nfunction MessageIcon({role, terminalFailure}: {role: string; terminalFailure: boolean}) {\n const name = terminalFailure\n ? 'closeCircleLine'\n : role === 'user'\n ? 'userLine'\n : role === 'assistant'\n ? 'robot2Line'\n : 'message2Line';\n\n return (\n <Icon\n name={name}\n className={cn(\n 'mt-[2px] size-14 flex-none',\n terminalFailure ? 'text-red-600 dark:text-red-400' : 'text-foreground-neutral-muted',\n )}\n aria-hidden=\"true\"\n />\n );\n}\n\nfunction MessageRoleLabel({label, terminalFailure}: {label: string; terminalFailure: boolean}) {\n return (\n <span\n className={cn(\n 'min-w-0 font-code text-foreground-neutral-muted',\n terminalFailure && 'text-foreground-highlight-error',\n )}\n >\n <span className=\"truncate\">{label}</span>\n </span>\n );\n}\n\nfunction RowMetadata({meta, className}: {meta: readonly SessionViewRowMeta[]; className?: string}) {\n if (meta.length === 0) return null;\n\n const inlineMeta = meta.length === 1 && meta[0]?.inline !== false ? meta[0] : null;\n if (inlineMeta != null) {\n return (\n <span\n className={cn('font-code text-xs text-foreground-neutral-muted', className)}\n title={`${inlineMeta.label}: ${inlineMeta.value}`}\n >\n {inlineMeta.value}\n </span>\n );\n }\n\n return (\n <span className={className}>\n <MetadataTrigger meta={meta} />\n </span>\n );\n}\n\nfunction MetadataTrigger({meta}: {meta: readonly SessionViewRowMeta[]}) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n className=\"inline-flex size-20 flex-none items-center justify-center rounded-4 text-foreground-neutral-muted opacity-60 transition-opacity hover:bg-background-components-hover hover:text-foreground-neutral-base hover:opacity-100 focus-visible:opacity-100 focus-visible:shadow-[inset_0_0_0_2px_var(--color-primary-500)] group-hover/log-row:opacity-100\"\n aria-label=\"Show message metadata\"\n >\n <Icon name=\"informationLine\" className=\"size-12\" aria-hidden=\"true\" />\n </button>\n </TooltipTrigger>\n <TooltipContent align=\"end\" variant=\"inverted\" className=\"max-w-360 p-tight\">\n <span className=\"grid grid-cols-[max-content_minmax(0,1fr)] gap-x-[var(--space-inline)] gap-y-[var(--space-tight)] font-code text-xs\">\n {meta.map((item) => (\n <Fragment key={`${item.label}-${item.value}`}>\n <span className=\"text-foreground-contrast-secondary\">{item.label}</span>\n <span className=\"min-w-0 break-all text-foreground-contrast-primary\">\n {item.value}\n </span>\n </Fragment>\n ))}\n </span>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction PreviewText({text}: {text: string}) {\n const [expanded, setExpanded] = useState(false);\n const truncated = text.length > PREVIEW_CHAR_LIMIT;\n const visible = truncated && !expanded ? `${text.slice(0, PREVIEW_CHAR_LIMIT)}…` : text;\n\n return (\n <>\n {visible}\n {truncated ? (\n <button\n type=\"button\"\n aria-expanded={expanded}\n className=\"ms-inline inline-flex min-h-24 items-center rounded-4 px-tight font-display text-xs text-foreground-highlight-interactive focus-visible:shadow-[inset_0_0_0_2px_var(--color-primary-500)]\"\n onClick={() => setExpanded((value) => !value)}\n >\n {expanded ? 'show less' : 'show more'}\n </button>\n ) : null}\n </>\n );\n}\n\nfunction compactPreview(value: string): string {\n // Normalize only a bounded head: tool output can be megabytes, and this runs\n // per render for every disclosure trigger.\n const head = value.length > 200 ? value.slice(0, 200) : value;\n const singleLine = head.replace(WHITESPACE, ' ').trim();\n if (singleLine.length <= 80) return singleLine;\n return `${singleLine.slice(0, 80)}…`;\n}\n\nfunction wordSummary(value: string): string {\n const truncated = value.length > WORD_SUMMARY_CHAR_LIMIT;\n const head = truncated ? value.slice(0, WORD_SUMMARY_CHAR_LIMIT) : value;\n const count = head.trim().split(WORD_SEPARATOR).filter(Boolean).length;\n const marker = truncated ? '+' : '';\n return `${count}${marker} ${count === 1 ? 'word' : 'words'}`;\n}\n\nfunction assertNever(value: never): never {\n throw new Error(`unexpected agent session row: ${JSON.stringify(value)}`);\n}\n"],"names":["Icon","LogContent","LogDisclosure","LogDisclosureContent","LogDisclosureTrigger","LogRow","Tooltip","TooltipContent","TooltipTrigger","cn","Fragment","useState","PREVIEW_CHAR_LIMIT","WORD_SUMMARY_CHAR_LIMIT","WHITESPACE","WORD_SEPARATOR","AgentSessionRows","rows","resolvedToolCallIds","toolCallNames","indent","map","row","index","AgentSessionRowView","kind","lineNumber","timestamp","Date","tone","terminalFailure","data-log-terminal-failure","undefined","className","span","MessageIcon","role","MessageRoleLabel","label","RowMetadata","meta","PreviewText","text","summary","wordSummary","awaitingResult","id","has","compactPreview","input","trailing","name","aria-hidden","variant","toolName","toolCallId","get","output","isError","detail","raw","assertNever","length","inlineMeta","inline","title","value","MetadataTrigger","asChild","button","type","aria-label","align","item","expanded","setExpanded","truncated","visible","slice","aria-expanded","onClick","head","singleLine","replace","trim","count","split","filter","Boolean","marker","Error","JSON","stringify"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/components/agent-session-rows.tsx"],"sourcesContent":["'use client';\n\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {\n LogContent,\n LogDisclosure,\n LogDisclosureContent,\n LogDisclosureTrigger,\n LogRow,\n} from '@shipfox/react-ui/log';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Fragment, useEffect, useState} from 'react';\nimport type {SessionViewRow, SessionViewRowMeta} from '#core/log-model.js';\n\nconst PREVIEW_CHAR_LIMIT = 1200;\nconst WORD_SUMMARY_CHAR_LIMIT = 5000;\nconst WHITESPACE = /\\s+/g;\nconst WORD_SEPARATOR = /\\s+/;\n\nexport interface AgentSessionRowsProps {\n rows: readonly SessionViewRow[];\n resolvedToolCallIds: ReadonlySet<string>;\n toolCallNames: ReadonlyMap<string, string>;\n indent: number;\n forceOpen?: boolean;\n}\n\nexport function AgentSessionRows({\n rows,\n resolvedToolCallIds,\n toolCallNames,\n indent,\n forceOpen = false,\n}: AgentSessionRowsProps) {\n return rows.map((row, index) => (\n <AgentSessionRowView\n // biome-ignore lint/suspicious/noArrayIndexKey: session rows are immutable and never reordered, so the index is stable; content keys would balloon to megabyte strings and collide on repeated id-less tool calls.\n key={`${row.kind}-${index}`}\n row={row}\n resolvedToolCallIds={resolvedToolCallIds}\n toolCallNames={toolCallNames}\n indent={indent}\n forceOpen={forceOpen}\n />\n ));\n}\n\nfunction AgentSessionRowView({\n row,\n resolvedToolCallIds,\n toolCallNames,\n indent,\n forceOpen,\n}: {\n row: SessionViewRow;\n resolvedToolCallIds: ReadonlySet<string>;\n toolCallNames: ReadonlyMap<string, string>;\n indent: number;\n forceOpen: boolean;\n}) {\n const [open, setOpen] = useState(false);\n useEffect(() => {\n if (forceOpen) setOpen(true);\n }, [forceOpen]);\n const disclosureProps = {open: forceOpen || open, onOpenChange: setOpen};\n\n switch (row.kind) {\n case 'message':\n return (\n <LogRow\n lineNumber={null}\n timestamp={new Date(row.timestamp)}\n indent={indent}\n tone={row.terminalFailure ? 'error' : 'default'}\n data-log-terminal-failure={row.terminalFailure ? 'true' : undefined}\n >\n <LogContent className=\"text-foreground-contrast-primary\">\n <span className=\"flex min-w-0 items-start gap-inline\">\n <MessageIcon role={row.role} terminalFailure={row.terminalFailure} />\n <span className=\"flex min-w-0 flex-1 flex-col gap-tight\">\n <span className=\"flex min-w-0 items-center gap-inline\">\n <MessageRoleLabel label={row.label} terminalFailure={row.terminalFailure} />\n <RowMetadata meta={row.meta} className=\"ml-auto flex-none\" />\n </span>\n <span className=\"block min-w-0\">\n <PreviewText text={row.text} />\n </span>\n </span>\n </span>\n </LogContent>\n </LogRow>\n );\n case 'thinking':\n return (\n <LogDisclosure indent={indent} {...disclosureProps}>\n <LogDisclosureTrigger\n summary={wordSummary(row.text)}\n timestamp={new Date(row.timestamp)}\n className=\"text-foreground-contrast-secondary\"\n >\n thinking\n </LogDisclosureTrigger>\n <LogDisclosureContent className=\"text-foreground-contrast-secondary\">\n <LogContent className=\"text-foreground-contrast-secondary\">\n <PreviewText text={row.text} />\n </LogContent>\n </LogDisclosureContent>\n </LogDisclosure>\n );\n case 'tool-call': {\n const awaitingResult = row.id != null && !resolvedToolCallIds.has(row.id);\n return (\n <LogDisclosure indent={indent} {...disclosureProps}>\n <LogDisclosureTrigger\n timestamp={new Date(row.timestamp)}\n summary={compactPreview(row.summary ?? row.input)}\n trailing={\n awaitingResult ? (\n <span className=\"inline-flex items-center gap-tight\">\n <Icon\n name=\"loader4Line\"\n className=\"size-12 motion-safe:animate-spin\"\n aria-hidden=\"true\"\n />\n awaiting result\n </span>\n ) : null\n }\n >\n <span className=\"inline-flex min-w-0 items-center gap-inline\">\n <Icon name=\"terminalBoxLine\" className=\"size-14 flex-none\" aria-hidden=\"true\" />\n <span className=\"truncate\">tool {row.name}</span>\n </span>\n </LogDisclosureTrigger>\n <LogDisclosureContent>\n {row.summary != null ? (\n <>\n <LogContent>\n <PreviewText text={row.summary} />\n </LogContent>\n <LogContent variant=\"code\">\n <PreviewText text={row.input} />\n </LogContent>\n </>\n ) : (\n <LogContent variant=\"code\">\n <PreviewText text={row.input} />\n </LogContent>\n )}\n </LogDisclosureContent>\n </LogDisclosure>\n );\n }\n case 'tool-result': {\n const toolName =\n row.toolName === 'tool'\n ? ((row.toolCallId != null ? toolCallNames.get(row.toolCallId) : undefined) ??\n '(unmatched)')\n : row.toolName;\n return (\n <LogDisclosure indent={indent} {...disclosureProps}>\n <LogDisclosureTrigger\n timestamp={new Date(row.timestamp)}\n summary={compactPreview(row.output)}\n trailing={\n <span\n className={cn(\n 'inline-flex items-center gap-tight',\n row.isError ? 'text-tag-error-icon' : 'text-foreground-contrast-secondary',\n )}\n >\n <Icon\n name={row.isError ? 'closeCircleLine' : 'checkLine'}\n className=\"size-12\"\n aria-hidden=\"true\"\n />\n {row.isError ? 'error' : 'ok'}\n </span>\n }\n >\n <span className=\"inline-flex min-w-0 items-center gap-inline\">\n <Icon name=\"terminalWindowLine\" className=\"size-14 flex-none\" aria-hidden=\"true\" />\n <span className=\"truncate\">result {toolName}</span>\n </span>\n </LogDisclosureTrigger>\n <LogDisclosureContent>\n <LogContent variant=\"code\" className=\"text-foreground-contrast-primary\">\n <PreviewText text={row.output} />\n </LogContent>\n </LogDisclosureContent>\n </LogDisclosure>\n );\n }\n case 'lifecycle':\n return (\n <LogRow\n lineNumber={null}\n timestamp={new Date(row.timestamp)}\n indent={indent}\n tone={row.tone}\n data-log-terminal-failure={row.terminalFailure ? 'true' : undefined}\n >\n <LogContent className=\"text-foreground-contrast-secondary\">\n <span className=\"inline-flex w-full items-center gap-inline\">\n <Icon name=\"informationLine\" className=\"size-14 flex-none\" aria-hidden=\"true\" />\n <span className=\"min-w-0\">\n <span className=\"font-medium\">{row.label}</span>\n {row.detail != null ? (\n <>\n {' · '}\n <span className=\"text-foreground-contrast-secondary\">{row.detail}</span>\n </>\n ) : null}\n </span>\n <span\n aria-hidden=\"true\"\n className=\"h-px flex-1 border-t border-dashed border-current opacity-30\"\n />\n <RowMetadata meta={row.meta} />\n </span>\n </LogContent>\n </LogRow>\n );\n case 'raw':\n return (\n <LogDisclosure indent={indent} {...disclosureProps}>\n <LogDisclosureTrigger\n timestamp={new Date(row.timestamp)}\n summary={compactPreview(row.raw)}\n className=\"text-foreground-contrast-primary\"\n >\n <span className=\"inline-flex min-w-0 items-center gap-inline\">\n <Icon\n name=\"errorWarningLine\"\n className=\"size-14 flex-none text-tag-warning-icon\"\n aria-hidden=\"true\"\n />\n <span className=\"truncate\">{row.label}</span>\n </span>\n </LogDisclosureTrigger>\n <LogDisclosureContent>\n <LogContent variant=\"code\">\n <PreviewText text={row.raw} />\n </LogContent>\n </LogDisclosureContent>\n </LogDisclosure>\n );\n default:\n return assertNever(row);\n }\n}\n\nfunction MessageIcon({role, terminalFailure}: {role: string; terminalFailure: boolean}) {\n const name = terminalFailure\n ? 'closeCircleLine'\n : role === 'user'\n ? 'userLine'\n : role === 'assistant'\n ? 'robot2Line'\n : 'message2Line';\n\n return (\n <Icon\n name={name}\n className={cn(\n 'mt-[2px] size-14 flex-none',\n terminalFailure ? 'text-tag-error-icon' : 'text-foreground-contrast-secondary',\n )}\n aria-hidden=\"true\"\n />\n );\n}\n\nfunction MessageRoleLabel({label, terminalFailure}: {label: string; terminalFailure: boolean}) {\n return (\n <span\n className={cn(\n 'min-w-0 font-code text-foreground-contrast-secondary',\n terminalFailure && 'text-foreground-contrast-primary',\n )}\n >\n <span className=\"truncate\">{label}</span>\n </span>\n );\n}\n\nfunction RowMetadata({meta, className}: {meta: readonly SessionViewRowMeta[]; className?: string}) {\n if (meta.length === 0) return null;\n\n const inlineMeta = meta.length === 1 && meta[0]?.inline !== false ? meta[0] : null;\n if (inlineMeta != null) {\n return (\n <span\n className={cn('font-code text-xs text-foreground-contrast-secondary', className)}\n title={`${inlineMeta.label}: ${inlineMeta.value}`}\n >\n {inlineMeta.value}\n </span>\n );\n }\n\n return (\n <span className={className}>\n <MetadataTrigger meta={meta} />\n </span>\n );\n}\n\nfunction MetadataTrigger({meta}: {meta: readonly SessionViewRowMeta[]}) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n className=\"inline-flex size-20 flex-none items-center justify-center rounded-4 text-foreground-contrast-secondary opacity-60 transition-opacity hover:bg-background-components-hover hover:text-foreground-contrast-primary hover:opacity-100 focus-visible:opacity-100 focus-visible:shadow-focus-inset group-hover/log-row:opacity-100\"\n aria-label=\"Show message metadata\"\n >\n <Icon name=\"informationLine\" className=\"size-12\" aria-hidden=\"true\" />\n </button>\n </TooltipTrigger>\n <TooltipContent align=\"end\" variant=\"inverted\" className=\"max-w-360 p-tight\">\n <span className=\"grid grid-cols-[max-content_minmax(0,1fr)] gap-x-[var(--space-inline)] gap-y-[var(--space-tight)] font-code text-xs\">\n {meta.map((item) => (\n <Fragment key={`${item.label}-${item.value}`}>\n <span className=\"text-foreground-contrast-secondary\">{item.label}</span>\n <span className=\"min-w-0 break-all text-foreground-contrast-primary\">\n {item.value}\n </span>\n </Fragment>\n ))}\n </span>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction PreviewText({text}: {text: string}) {\n const [expanded, setExpanded] = useState(false);\n const truncated = text.length > PREVIEW_CHAR_LIMIT;\n const visible = truncated && !expanded ? `${text.slice(0, PREVIEW_CHAR_LIMIT)}…` : text;\n\n return (\n <>\n {visible}\n {truncated ? (\n <button\n type=\"button\"\n aria-expanded={expanded}\n className=\"ms-inline inline-flex min-h-24 items-center rounded-4 px-tight font-display text-xs text-foreground-highlight-interactive focus-visible:shadow-focus-inset\"\n onClick={() => setExpanded((value) => !value)}\n >\n {expanded ? 'show less' : 'show more'}\n </button>\n ) : null}\n </>\n );\n}\n\nfunction compactPreview(value: string): string {\n // Normalize only a bounded head: tool output can be megabytes, and this runs\n // per render for every disclosure trigger.\n const head = value.length > 200 ? value.slice(0, 200) : value;\n const singleLine = head.replace(WHITESPACE, ' ').trim();\n if (singleLine.length <= 80) return singleLine;\n return `${singleLine.slice(0, 80)}…`;\n}\n\nfunction wordSummary(value: string): string {\n const truncated = value.length > WORD_SUMMARY_CHAR_LIMIT;\n const head = truncated ? value.slice(0, WORD_SUMMARY_CHAR_LIMIT) : value;\n const count = head.trim().split(WORD_SEPARATOR).filter(Boolean).length;\n const marker = truncated ? '+' : '';\n return `${count}${marker} ${count === 1 ? 'word' : 'words'}`;\n}\n\nfunction assertNever(value: never): never {\n throw new Error(`unexpected agent session row: ${JSON.stringify(value)}`);\n}\n"],"names":["Icon","LogContent","LogDisclosure","LogDisclosureContent","LogDisclosureTrigger","LogRow","Tooltip","TooltipContent","TooltipTrigger","cn","Fragment","useEffect","useState","PREVIEW_CHAR_LIMIT","WORD_SUMMARY_CHAR_LIMIT","WHITESPACE","WORD_SEPARATOR","AgentSessionRows","rows","resolvedToolCallIds","toolCallNames","indent","forceOpen","map","row","index","AgentSessionRowView","kind","open","setOpen","disclosureProps","onOpenChange","lineNumber","timestamp","Date","tone","terminalFailure","data-log-terminal-failure","undefined","className","span","MessageIcon","role","MessageRoleLabel","label","RowMetadata","meta","PreviewText","text","summary","wordSummary","awaitingResult","id","has","compactPreview","input","trailing","name","aria-hidden","variant","toolName","toolCallId","get","output","isError","detail","raw","assertNever","length","inlineMeta","inline","title","value","MetadataTrigger","asChild","button","type","aria-label","align","item","expanded","setExpanded","truncated","visible","slice","aria-expanded","onClick","head","singleLine","replace","trim","count","split","filter","Boolean","marker","Error","JSON","stringify"],"mappings":"AAAA;;AAEA,SAAQA,IAAI,QAAO,yBAAyB;AAC5C,SACEC,UAAU,EACVC,aAAa,EACbC,oBAAoB,EACpBC,oBAAoB,EACpBC,MAAM,QACD,wBAAwB;AAC/B,SAAQC,OAAO,EAAEC,cAAc,EAAEC,cAAc,QAAO,4BAA4B;AAClF,SAAQC,EAAE,QAAO,0BAA0B;AAC3C,SAAQC,QAAQ,EAAEC,SAAS,EAAEC,QAAQ,QAAO,QAAQ;AAGpD,MAAMC,qBAAqB;AAC3B,MAAMC,0BAA0B;AAChC,MAAMC,aAAa;AACnB,MAAMC,iBAAiB;AAUvB,OAAO,SAASC,iBAAiB,EAC/BC,IAAI,EACJC,mBAAmB,EACnBC,aAAa,EACbC,MAAM,EACNC,YAAY,KAAK,EACK;IACtB,OAAOJ,KAAKK,GAAG,CAAC,CAACC,KAAKC,sBACpB,KAACC;YAGCF,KAAKA;YACLL,qBAAqBA;YACrBC,eAAeA;YACfC,QAAQA;YACRC,WAAWA;WALN,GAAGE,IAAIG,IAAI,CAAC,CAAC,EAAEF,OAAO;AAQjC;AAEA,SAASC,oBAAoB,EAC3BF,GAAG,EACHL,mBAAmB,EACnBC,aAAa,EACbC,MAAM,EACNC,SAAS,EAOV;IACC,MAAM,CAACM,MAAMC,QAAQ,GAAGjB,SAAS;IACjCD,UAAU;QACR,IAAIW,WAAWO,QAAQ;IACzB,GAAG;QAACP;KAAU;IACd,MAAMQ,kBAAkB;QAACF,MAAMN,aAAaM;QAAMG,cAAcF;IAAO;IAEvE,OAAQL,IAAIG,IAAI;QACd,KAAK;YACH,qBACE,KAACtB;gBACC2B,YAAY;gBACZC,WAAW,IAAIC,KAAKV,IAAIS,SAAS;gBACjCZ,QAAQA;gBACRc,MAAMX,IAAIY,eAAe,GAAG,UAAU;gBACtCC,6BAA2Bb,IAAIY,eAAe,GAAG,SAASE;0BAE1D,cAAA,KAACrC;oBAAWsC,WAAU;8BACpB,cAAA,MAACC;wBAAKD,WAAU;;0CACd,KAACE;gCAAYC,MAAMlB,IAAIkB,IAAI;gCAAEN,iBAAiBZ,IAAIY,eAAe;;0CACjE,MAACI;gCAAKD,WAAU;;kDACd,MAACC;wCAAKD,WAAU;;0DACd,KAACI;gDAAiBC,OAAOpB,IAAIoB,KAAK;gDAAER,iBAAiBZ,IAAIY,eAAe;;0DACxE,KAACS;gDAAYC,MAAMtB,IAAIsB,IAAI;gDAAEP,WAAU;;;;kDAEzC,KAACC;wCAAKD,WAAU;kDACd,cAAA,KAACQ;4CAAYC,MAAMxB,IAAIwB,IAAI;;;;;;;;;QAOzC,KAAK;YACH,qBACE,MAAC9C;gBAAcmB,QAAQA;gBAAS,GAAGS,eAAe;;kCAChD,KAAC1B;wBACC6C,SAASC,YAAY1B,IAAIwB,IAAI;wBAC7Bf,WAAW,IAAIC,KAAKV,IAAIS,SAAS;wBACjCM,WAAU;kCACX;;kCAGD,KAACpC;wBAAqBoC,WAAU;kCAC9B,cAAA,KAACtC;4BAAWsC,WAAU;sCACpB,cAAA,KAACQ;gCAAYC,MAAMxB,IAAIwB,IAAI;;;;;;QAKrC,KAAK;YAAa;gBAChB,MAAMG,iBAAiB3B,IAAI4B,EAAE,IAAI,QAAQ,CAACjC,oBAAoBkC,GAAG,CAAC7B,IAAI4B,EAAE;gBACxE,qBACE,MAAClD;oBAAcmB,QAAQA;oBAAS,GAAGS,eAAe;;sCAChD,KAAC1B;4BACC6B,WAAW,IAAIC,KAAKV,IAAIS,SAAS;4BACjCgB,SAASK,eAAe9B,IAAIyB,OAAO,IAAIzB,IAAI+B,KAAK;4BAChDC,UACEL,+BACE,MAACX;gCAAKD,WAAU;;kDACd,KAACvC;wCACCyD,MAAK;wCACLlB,WAAU;wCACVmB,eAAY;;oCACZ;;iCAGF;sCAGN,cAAA,MAAClB;gCAAKD,WAAU;;kDACd,KAACvC;wCAAKyD,MAAK;wCAAkBlB,WAAU;wCAAoBmB,eAAY;;kDACvE,MAAClB;wCAAKD,WAAU;;4CAAW;4CAAMf,IAAIiC,IAAI;;;;;;sCAG7C,KAACtD;sCACEqB,IAAIyB,OAAO,IAAI,qBACd;;kDACE,KAAChD;kDACC,cAAA,KAAC8C;4CAAYC,MAAMxB,IAAIyB,OAAO;;;kDAEhC,KAAChD;wCAAW0D,SAAQ;kDAClB,cAAA,KAACZ;4CAAYC,MAAMxB,IAAI+B,KAAK;;;;+CAIhC,KAACtD;gCAAW0D,SAAQ;0CAClB,cAAA,KAACZ;oCAAYC,MAAMxB,IAAI+B,KAAK;;;;;;YAMxC;QACA,KAAK;YAAe;gBAClB,MAAMK,WACJpC,IAAIoC,QAAQ,KAAK,SACZ,AAACpC,CAAAA,IAAIqC,UAAU,IAAI,OAAOzC,cAAc0C,GAAG,CAACtC,IAAIqC,UAAU,IAAIvB,SAAQ,KACvE,gBACAd,IAAIoC,QAAQ;gBAClB,qBACE,MAAC1D;oBAAcmB,QAAQA;oBAAS,GAAGS,eAAe;;sCAChD,KAAC1B;4BACC6B,WAAW,IAAIC,KAAKV,IAAIS,SAAS;4BACjCgB,SAASK,eAAe9B,IAAIuC,MAAM;4BAClCP,wBACE,MAAChB;gCACCD,WAAW9B,GACT,sCACAe,IAAIwC,OAAO,GAAG,wBAAwB;;kDAGxC,KAAChE;wCACCyD,MAAMjC,IAAIwC,OAAO,GAAG,oBAAoB;wCACxCzB,WAAU;wCACVmB,eAAY;;oCAEblC,IAAIwC,OAAO,GAAG,UAAU;;;sCAI7B,cAAA,MAACxB;gCAAKD,WAAU;;kDACd,KAACvC;wCAAKyD,MAAK;wCAAqBlB,WAAU;wCAAoBmB,eAAY;;kDAC1E,MAAClB;wCAAKD,WAAU;;4CAAW;4CAAQqB;;;;;;sCAGvC,KAACzD;sCACC,cAAA,KAACF;gCAAW0D,SAAQ;gCAAOpB,WAAU;0CACnC,cAAA,KAACQ;oCAAYC,MAAMxB,IAAIuC,MAAM;;;;;;YAKvC;QACA,KAAK;YACH,qBACE,KAAC1D;gBACC2B,YAAY;gBACZC,WAAW,IAAIC,KAAKV,IAAIS,SAAS;gBACjCZ,QAAQA;gBACRc,MAAMX,IAAIW,IAAI;gBACdE,6BAA2Bb,IAAIY,eAAe,GAAG,SAASE;0BAE1D,cAAA,KAACrC;oBAAWsC,WAAU;8BACpB,cAAA,MAACC;wBAAKD,WAAU;;0CACd,KAACvC;gCAAKyD,MAAK;gCAAkBlB,WAAU;gCAAoBmB,eAAY;;0CACvE,MAAClB;gCAAKD,WAAU;;kDACd,KAACC;wCAAKD,WAAU;kDAAef,IAAIoB,KAAK;;oCACvCpB,IAAIyC,MAAM,IAAI,qBACb;;4CACG;0DACD,KAACzB;gDAAKD,WAAU;0DAAsCf,IAAIyC,MAAM;;;yCAEhE;;;0CAEN,KAACzB;gCACCkB,eAAY;gCACZnB,WAAU;;0CAEZ,KAACM;gCAAYC,MAAMtB,IAAIsB,IAAI;;;;;;QAKrC,KAAK;YACH,qBACE,MAAC5C;gBAAcmB,QAAQA;gBAAS,GAAGS,eAAe;;kCAChD,KAAC1B;wBACC6B,WAAW,IAAIC,KAAKV,IAAIS,SAAS;wBACjCgB,SAASK,eAAe9B,IAAI0C,GAAG;wBAC/B3B,WAAU;kCAEV,cAAA,MAACC;4BAAKD,WAAU;;8CACd,KAACvC;oCACCyD,MAAK;oCACLlB,WAAU;oCACVmB,eAAY;;8CAEd,KAAClB;oCAAKD,WAAU;8CAAYf,IAAIoB,KAAK;;;;;kCAGzC,KAACzC;kCACC,cAAA,KAACF;4BAAW0D,SAAQ;sCAClB,cAAA,KAACZ;gCAAYC,MAAMxB,IAAI0C,GAAG;;;;;;QAKpC;YACE,OAAOC,YAAY3C;IACvB;AACF;AAEA,SAASiB,YAAY,EAACC,IAAI,EAAEN,eAAe,EAA2C;IACpF,MAAMqB,OAAOrB,kBACT,oBACAM,SAAS,SACP,aACAA,SAAS,cACP,eACA;IAER,qBACE,KAAC1C;QACCyD,MAAMA;QACNlB,WAAW9B,GACT,8BACA2B,kBAAkB,wBAAwB;QAE5CsB,eAAY;;AAGlB;AAEA,SAASf,iBAAiB,EAACC,KAAK,EAAER,eAAe,EAA4C;IAC3F,qBACE,KAACI;QACCD,WAAW9B,GACT,wDACA2B,mBAAmB;kBAGrB,cAAA,KAACI;YAAKD,WAAU;sBAAYK;;;AAGlC;AAEA,SAASC,YAAY,EAACC,IAAI,EAAEP,SAAS,EAA4D;IAC/F,IAAIO,KAAKsB,MAAM,KAAK,GAAG,OAAO;IAE9B,MAAMC,aAAavB,KAAKsB,MAAM,KAAK,KAAKtB,IAAI,CAAC,EAAE,EAAEwB,WAAW,QAAQxB,IAAI,CAAC,EAAE,GAAG;IAC9E,IAAIuB,cAAc,MAAM;QACtB,qBACE,KAAC7B;YACCD,WAAW9B,GAAG,wDAAwD8B;YACtEgC,OAAO,GAAGF,WAAWzB,KAAK,CAAC,EAAE,EAAEyB,WAAWG,KAAK,EAAE;sBAEhDH,WAAWG,KAAK;;IAGvB;IAEA,qBACE,KAAChC;QAAKD,WAAWA;kBACf,cAAA,KAACkC;YAAgB3B,MAAMA;;;AAG7B;AAEA,SAAS2B,gBAAgB,EAAC3B,IAAI,EAAwC;IACpE,qBACE,MAACxC;;0BACC,KAACE;gBAAekE,OAAO;0BACrB,cAAA,KAACC;oBACCC,MAAK;oBACLrC,WAAU;oBACVsC,cAAW;8BAEX,cAAA,KAAC7E;wBAAKyD,MAAK;wBAAkBlB,WAAU;wBAAUmB,eAAY;;;;0BAGjE,KAACnD;gBAAeuE,OAAM;gBAAMnB,SAAQ;gBAAWpB,WAAU;0BACvD,cAAA,KAACC;oBAAKD,WAAU;8BACbO,KAAKvB,GAAG,CAAC,CAACwD,qBACT,MAACrE;;8CACC,KAAC8B;oCAAKD,WAAU;8CAAsCwC,KAAKnC,KAAK;;8CAChE,KAACJ;oCAAKD,WAAU;8CACbwC,KAAKP,KAAK;;;2BAHA,GAAGO,KAAKnC,KAAK,CAAC,CAAC,EAAEmC,KAAKP,KAAK,EAAE;;;;;AAWxD;AAEA,SAASzB,YAAY,EAACC,IAAI,EAAiB;IACzC,MAAM,CAACgC,UAAUC,YAAY,GAAGrE,SAAS;IACzC,MAAMsE,YAAYlC,KAAKoB,MAAM,GAAGvD;IAChC,MAAMsE,UAAUD,aAAa,CAACF,WAAW,GAAGhC,KAAKoC,KAAK,CAAC,GAAGvE,oBAAoB,CAAC,CAAC,GAAGmC;IAEnF,qBACE;;YACGmC;YACAD,0BACC,KAACP;gBACCC,MAAK;gBACLS,iBAAeL;gBACfzC,WAAU;gBACV+C,SAAS,IAAML,YAAY,CAACT,QAAU,CAACA;0BAEtCQ,WAAW,cAAc;iBAE1B;;;AAGV;AAEA,SAAS1B,eAAekB,KAAa;IACnC,6EAA6E;IAC7E,2CAA2C;IAC3C,MAAMe,OAAOf,MAAMJ,MAAM,GAAG,MAAMI,MAAMY,KAAK,CAAC,GAAG,OAAOZ;IACxD,MAAMgB,aAAaD,KAAKE,OAAO,CAAC1E,YAAY,KAAK2E,IAAI;IACrD,IAAIF,WAAWpB,MAAM,IAAI,IAAI,OAAOoB;IACpC,OAAO,GAAGA,WAAWJ,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;AACtC;AAEA,SAASlC,YAAYsB,KAAa;IAChC,MAAMU,YAAYV,MAAMJ,MAAM,GAAGtD;IACjC,MAAMyE,OAAOL,YAAYV,MAAMY,KAAK,CAAC,GAAGtE,2BAA2B0D;IACnE,MAAMmB,QAAQJ,KAAKG,IAAI,GAAGE,KAAK,CAAC5E,gBAAgB6E,MAAM,CAACC,SAAS1B,MAAM;IACtE,MAAM2B,SAASb,YAAY,MAAM;IACjC,OAAO,GAAGS,QAAQI,OAAO,CAAC,EAAEJ,UAAU,IAAI,SAAS,SAAS;AAC9D;AAEA,SAASxB,YAAYK,KAAY;IAC/B,MAAM,IAAIwB,MAAM,CAAC,8BAA8B,EAAEC,KAAKC,SAAS,CAAC1B,QAAQ;AAC1E"}
@@ -1,4 +1,4 @@
1
- import type { ReactNode } from 'react';
1
+ import { type ReactNode } from 'react';
2
2
  import type { GroupLogNode } from '#core/log-tree.js';
3
3
  export interface LogGroupProps {
4
4
  node: GroupLogNode;
@@ -6,6 +6,7 @@ export interface LogGroupProps {
6
6
  terminated: boolean;
7
7
  children: ReactNode;
8
8
  defaultOpen?: boolean;
9
+ forceOpen?: boolean;
9
10
  }
10
- export declare function LogGroup({ node, depth, terminated, children, defaultOpen }: LogGroupProps): import("react").JSX.Element;
11
+ export declare function LogGroup({ node, depth, terminated, children, defaultOpen, forceOpen, }: LogGroupProps): import("react").JSX.Element;
11
12
  //# sourceMappingURL=log-group.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"log-group.d.ts","sourceRoot":"","sources":["../../src/components/log-group.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AACrC,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AAEpD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,QAAQ,CAAC,EAAC,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAmB,EAAC,EAAE,aAAa,+BAe/F"}
1
+ {"version":3,"file":"log-group.d.ts","sourceRoot":"","sources":["../../src/components/log-group.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAC,KAAK,SAAS,EAAsB,MAAM,OAAO,CAAC;AAC1D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AAEpD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EACJ,KAAK,EACL,UAAU,EACV,QAAQ,EACR,WAAmB,EACnB,SAAiB,GAClB,EAAE,aAAa,+BAoBf"}
@@ -3,11 +3,19 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Icon } from '@shipfox/react-ui/icon';
4
4
  import { LogDisclosure, LogDisclosureContent, LogDisclosureTrigger } from '@shipfox/react-ui/log';
5
5
  import { cn, formatDuration } from '@shipfox/react-ui/utils';
6
- export function LogGroup({ node, depth, terminated, children, defaultOpen = false }) {
6
+ import { useEffect, useState } from 'react';
7
+ export function LogGroup({ node, depth, terminated, children, defaultOpen = false, forceOpen = false }) {
7
8
  const lineLabel = `${node.lineCount} ${node.lineCount === 1 ? 'line' : 'lines'}`;
9
+ const [open, setOpen] = useState(defaultOpen);
10
+ useEffect(()=>{
11
+ if (forceOpen) setOpen(true);
12
+ }, [
13
+ forceOpen
14
+ ]);
8
15
  return /*#__PURE__*/ _jsxs(LogDisclosure, {
9
16
  indent: depth,
10
- defaultOpen: defaultOpen,
17
+ open: forceOpen || open,
18
+ onOpenChange: setOpen,
11
19
  children: [
12
20
  /*#__PURE__*/ _jsx(LogDisclosureTrigger, {
13
21
  summary: lineLabel,
@@ -37,12 +45,12 @@ function GroupStatus({ node, terminated }) {
37
45
  // is null). Either way show "incomplete" rather than a blank slot or a forever spinner.
38
46
  if (node.closed || terminated) {
39
47
  return /*#__PURE__*/ _jsx("span", {
40
- className: "text-foreground-neutral-muted",
48
+ className: "text-foreground-contrast-secondary",
41
49
  children: "incomplete"
42
50
  });
43
51
  }
44
52
  return /*#__PURE__*/ _jsxs("span", {
45
- className: "inline-flex items-center gap-tight text-foreground-neutral-muted",
53
+ className: "inline-flex items-center gap-tight text-foreground-contrast-secondary",
46
54
  children: [
47
55
  /*#__PURE__*/ _jsx(Icon, {
48
56
  name: "loader4Line",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/log-group.tsx"],"sourcesContent":["'use client';\n\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {LogDisclosure, LogDisclosureContent, LogDisclosureTrigger} from '@shipfox/react-ui/log';\nimport {cn, formatDuration} from '@shipfox/react-ui/utils';\nimport type {ReactNode} from 'react';\nimport type {GroupLogNode} from '#core/log-tree.js';\n\nexport interface LogGroupProps {\n node: GroupLogNode;\n depth: number;\n terminated: boolean;\n children: ReactNode;\n defaultOpen?: boolean;\n}\n\nexport function LogGroup({node, depth, terminated, children, defaultOpen = false}: LogGroupProps) {\n const lineLabel = `${node.lineCount} ${node.lineCount === 1 ? 'line' : 'lines'}`;\n\n return (\n <LogDisclosure indent={depth} defaultOpen={defaultOpen}>\n <LogDisclosureTrigger\n summary={lineLabel}\n trailing={<GroupStatus node={node} terminated={terminated} />}\n className={cn(node.hasError && 'shadow-[inset_2px_0_0_var(--color-red-500)]')}\n >\n {node.record.name}\n </LogDisclosureTrigger>\n <LogDisclosureContent rail={false}>{children}</LogDisclosureContent>\n </LogDisclosure>\n );\n}\n\nfunction GroupStatus({node, terminated}: {node: GroupLogNode; terminated: boolean}): ReactNode {\n if (node.closed && node.endTs != null) {\n return (\n <span className=\"font-code tabular-nums\">{formatDuration(node.endTs - node.record.ts)}</span>\n );\n }\n\n // No clean end: either still open under a terminated stream (runner died mid-group), or\n // closed only because an ancestor's group_end cascaded (its own end was dropped, so endTs\n // is null). Either way show \"incomplete\" rather than a blank slot or a forever spinner.\n if (node.closed || terminated) {\n return <span className=\"text-foreground-neutral-muted\">incomplete</span>;\n }\n\n return (\n <span className=\"inline-flex items-center gap-tight text-foreground-neutral-muted\">\n <Icon name=\"loader4Line\" className=\"size-12 motion-safe:animate-spin\" aria-hidden=\"true\" />\n running\n </span>\n );\n}\n"],"names":["Icon","LogDisclosure","LogDisclosureContent","LogDisclosureTrigger","cn","formatDuration","LogGroup","node","depth","terminated","children","defaultOpen","lineLabel","lineCount","indent","summary","trailing","GroupStatus","className","hasError","record","name","rail","closed","endTs","span","ts","aria-hidden"],"mappings":"AAAA;;AAEA,SAAQA,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,aAAa,EAAEC,oBAAoB,EAAEC,oBAAoB,QAAO,wBAAwB;AAChG,SAAQC,EAAE,EAAEC,cAAc,QAAO,0BAA0B;AAY3D,OAAO,SAASC,SAAS,EAACC,IAAI,EAAEC,KAAK,EAAEC,UAAU,EAAEC,QAAQ,EAAEC,cAAc,KAAK,EAAgB;IAC9F,MAAMC,YAAY,GAAGL,KAAKM,SAAS,CAAC,CAAC,EAAEN,KAAKM,SAAS,KAAK,IAAI,SAAS,SAAS;IAEhF,qBACE,MAACZ;QAAca,QAAQN;QAAOG,aAAaA;;0BACzC,KAACR;gBACCY,SAASH;gBACTI,wBAAU,KAACC;oBAAYV,MAAMA;oBAAME,YAAYA;;gBAC/CS,WAAWd,GAAGG,KAAKY,QAAQ,IAAI;0BAE9BZ,KAAKa,MAAM,CAACC,IAAI;;0BAEnB,KAACnB;gBAAqBoB,MAAM;0BAAQZ;;;;AAG1C;AAEA,SAASO,YAAY,EAACV,IAAI,EAAEE,UAAU,EAA4C;IAChF,IAAIF,KAAKgB,MAAM,IAAIhB,KAAKiB,KAAK,IAAI,MAAM;QACrC,qBACE,KAACC;YAAKP,WAAU;sBAA0Bb,eAAeE,KAAKiB,KAAK,GAAGjB,KAAKa,MAAM,CAACM,EAAE;;IAExF;IAEA,wFAAwF;IACxF,0FAA0F;IAC1F,wFAAwF;IACxF,IAAInB,KAAKgB,MAAM,IAAId,YAAY;QAC7B,qBAAO,KAACgB;YAAKP,WAAU;sBAAgC;;IACzD;IAEA,qBACE,MAACO;QAAKP,WAAU;;0BACd,KAAClB;gBAAKqB,MAAK;gBAAcH,WAAU;gBAAmCS,eAAY;;YAAS;;;AAIjG"}
1
+ {"version":3,"sources":["../../src/components/log-group.tsx"],"sourcesContent":["'use client';\n\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {LogDisclosure, LogDisclosureContent, LogDisclosureTrigger} from '@shipfox/react-ui/log';\nimport {cn, formatDuration} from '@shipfox/react-ui/utils';\nimport {type ReactNode, useEffect, useState} from 'react';\nimport type {GroupLogNode} from '#core/log-tree.js';\n\nexport interface LogGroupProps {\n node: GroupLogNode;\n depth: number;\n terminated: boolean;\n children: ReactNode;\n defaultOpen?: boolean;\n forceOpen?: boolean;\n}\n\nexport function LogGroup({\n node,\n depth,\n terminated,\n children,\n defaultOpen = false,\n forceOpen = false,\n}: LogGroupProps) {\n const lineLabel = `${node.lineCount} ${node.lineCount === 1 ? 'line' : 'lines'}`;\n const [open, setOpen] = useState(defaultOpen);\n\n useEffect(() => {\n if (forceOpen) setOpen(true);\n }, [forceOpen]);\n\n return (\n <LogDisclosure indent={depth} open={forceOpen || open} onOpenChange={setOpen}>\n <LogDisclosureTrigger\n summary={lineLabel}\n trailing={<GroupStatus node={node} terminated={terminated} />}\n className={cn(node.hasError && 'shadow-[inset_2px_0_0_var(--color-red-500)]')}\n >\n {node.record.name}\n </LogDisclosureTrigger>\n <LogDisclosureContent rail={false}>{children}</LogDisclosureContent>\n </LogDisclosure>\n );\n}\n\nfunction GroupStatus({node, terminated}: {node: GroupLogNode; terminated: boolean}): ReactNode {\n if (node.closed && node.endTs != null) {\n return (\n <span className=\"font-code tabular-nums\">{formatDuration(node.endTs - node.record.ts)}</span>\n );\n }\n\n // No clean end: either still open under a terminated stream (runner died mid-group), or\n // closed only because an ancestor's group_end cascaded (its own end was dropped, so endTs\n // is null). Either way show \"incomplete\" rather than a blank slot or a forever spinner.\n if (node.closed || terminated) {\n return <span className=\"text-foreground-contrast-secondary\">incomplete</span>;\n }\n\n return (\n <span className=\"inline-flex items-center gap-tight text-foreground-contrast-secondary\">\n <Icon name=\"loader4Line\" className=\"size-12 motion-safe:animate-spin\" aria-hidden=\"true\" />\n running\n </span>\n );\n}\n"],"names":["Icon","LogDisclosure","LogDisclosureContent","LogDisclosureTrigger","cn","formatDuration","useEffect","useState","LogGroup","node","depth","terminated","children","defaultOpen","forceOpen","lineLabel","lineCount","open","setOpen","indent","onOpenChange","summary","trailing","GroupStatus","className","hasError","record","name","rail","closed","endTs","span","ts","aria-hidden"],"mappings":"AAAA;;AAEA,SAAQA,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,aAAa,EAAEC,oBAAoB,EAAEC,oBAAoB,QAAO,wBAAwB;AAChG,SAAQC,EAAE,EAAEC,cAAc,QAAO,0BAA0B;AAC3D,SAAwBC,SAAS,EAAEC,QAAQ,QAAO,QAAQ;AAY1D,OAAO,SAASC,SAAS,EACvBC,IAAI,EACJC,KAAK,EACLC,UAAU,EACVC,QAAQ,EACRC,cAAc,KAAK,EACnBC,YAAY,KAAK,EACH;IACd,MAAMC,YAAY,GAAGN,KAAKO,SAAS,CAAC,CAAC,EAAEP,KAAKO,SAAS,KAAK,IAAI,SAAS,SAAS;IAChF,MAAM,CAACC,MAAMC,QAAQ,GAAGX,SAASM;IAEjCP,UAAU;QACR,IAAIQ,WAAWI,QAAQ;IACzB,GAAG;QAACJ;KAAU;IAEd,qBACE,MAACb;QAAckB,QAAQT;QAAOO,MAAMH,aAAaG;QAAMG,cAAcF;;0BACnE,KAACf;gBACCkB,SAASN;gBACTO,wBAAU,KAACC;oBAAYd,MAAMA;oBAAME,YAAYA;;gBAC/Ca,WAAWpB,GAAGK,KAAKgB,QAAQ,IAAI;0BAE9BhB,KAAKiB,MAAM,CAACC,IAAI;;0BAEnB,KAACzB;gBAAqB0B,MAAM;0BAAQhB;;;;AAG1C;AAEA,SAASW,YAAY,EAACd,IAAI,EAAEE,UAAU,EAA4C;IAChF,IAAIF,KAAKoB,MAAM,IAAIpB,KAAKqB,KAAK,IAAI,MAAM;QACrC,qBACE,KAACC;YAAKP,WAAU;sBAA0BnB,eAAeI,KAAKqB,KAAK,GAAGrB,KAAKiB,MAAM,CAACM,EAAE;;IAExF;IAEA,wFAAwF;IACxF,0FAA0F;IAC1F,wFAAwF;IACxF,IAAIvB,KAAKoB,MAAM,IAAIlB,YAAY;QAC7B,qBAAO,KAACoB;YAAKP,WAAU;sBAAqC;;IAC9D;IAEA,qBACE,MAACO;QAAKP,WAAU;;0BACd,KAACxB;gBAAK2B,MAAK;gBAAcH,WAAU;gBAAmCS,eAAY;;YAAS;;;AAIjG"}
@@ -9,6 +9,7 @@ export interface LogViewProps {
9
9
  emptyState?: 'complete' | 'pending';
10
10
  defaultGroupsOpen?: boolean;
11
11
  anchorToFailure?: boolean;
12
+ search?: string;
12
13
  ariaLive?: 'off' | 'polite' | 'assertive';
13
14
  className?: string | undefined;
14
15
  onScroll?: UIEventHandler<HTMLDivElement> | undefined;
@@ -16,6 +17,6 @@ export interface LogViewProps {
16
17
  export interface LogViewSkeletonProps extends Pick<LogViewProps, 'timestamps' | 'wrap' | 'showLineNumbers' | 'className'> {
17
18
  rows?: number;
18
19
  }
19
- export declare function LogView({ records, timestamps, wrap, showLineNumbers, emptyState, defaultGroupsOpen, anchorToFailure, ariaLive, className, onScroll, }: LogViewProps): import("react").JSX.Element;
20
+ export declare function LogView({ records, timestamps, wrap, showLineNumbers, emptyState, defaultGroupsOpen, anchorToFailure, search, ariaLive, className, onScroll, }: LogViewProps): import("react").JSX.Element;
20
21
  export declare function LogViewSkeleton({ rows, timestamps, wrap, showLineNumbers, className, }: LogViewSkeletonProps): import("react").JSX.Element;
21
22
  //# sourceMappingURL=log-view.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"log-view.d.ts","sourceRoot":"","sources":["../../src/components/log-view.tsx"],"names":[],"mappings":"AAGA,OAAO,EAA8B,KAAK,gBAAgB,EAAC,MAAM,uBAAuB,CAAC;AAEzF,OAAO,EAAiB,KAAK,cAAc,EAA6B,MAAM,OAAO,CAAC;AACtF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAalD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IAC9B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IACpC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,QAAQ,CAAC,EAAE,cAAc,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;CACvD;AAED,MAAM,WAAW,oBACf,SAAQ,IAAI,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM,GAAG,iBAAiB,GAAG,WAAW,CAAC;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,OAAO,CAAC,EACtB,OAAO,EACP,UAAkB,EAClB,IAAY,EACZ,eAAsB,EACtB,UAAuB,EACvB,iBAAyB,EACzB,eAAuB,EACvB,QAAmB,EACnB,SAAS,EACT,QAAQ,GACT,EAAE,YAAY,+BA0Cd;AAED,wBAAgB,eAAe,CAAC,EAC9B,IAAQ,EACR,UAAkB,EAClB,IAAY,EACZ,eAAsB,EACtB,SAAS,GACV,EAAE,oBAAoB,+BAuBtB"}
1
+ {"version":3,"file":"log-view.d.ts","sourceRoot":"","sources":["../../src/components/log-view.tsx"],"names":[],"mappings":"AAGA,OAAO,EAA8B,KAAK,gBAAgB,EAAC,MAAM,uBAAuB,CAAC;AAEzF,OAAO,EAEL,KAAK,cAAc,EAKpB,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAclD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IAC9B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IACpC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,QAAQ,CAAC,EAAE,cAAc,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;CACvD;AAED,MAAM,WAAW,oBACf,SAAQ,IAAI,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM,GAAG,iBAAiB,GAAG,WAAW,CAAC;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,OAAO,CAAC,EACtB,OAAO,EACP,UAAkB,EAClB,IAAY,EACZ,eAAsB,EACtB,UAAuB,EACvB,iBAAyB,EACzB,eAAuB,EACvB,MAAW,EACX,QAAmB,EACnB,SAAS,EACT,QAAQ,GACT,EAAE,YAAY,+BAwEd;AAED,wBAAgB,eAAe,CAAC,EAC9B,IAAQ,EACR,UAAkB,EAClB,IAAY,EACZ,eAAsB,EACtB,SAAS,GACV,EAAE,oBAAoB,+BAuBtB"}
@@ -1,24 +1,36 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { Icon } from '@shipfox/react-ui/icon';
4
4
  import { LogContent, LogRow, LogRows } from '@shipfox/react-ui/log';
5
5
  import { Skeleton } from '@shipfox/react-ui/skeleton';
6
- import { useEffect, useMemo, useRef } from 'react';
6
+ import { useDeferredValue, useEffect, useMemo, useRef } from 'react';
7
+ import { buildLogSearchIndex, filterLogNodes } from '#core/log-search.js';
7
8
  import { assertNever, buildLogTree } from '#core/log-tree.js';
8
9
  import { AgentSessionRows } from './agent-session-rows.js';
9
10
  import { LogGroup } from './log-group.js';
10
11
  import { OutputLogRow } from './output-log-row.js';
11
12
  import { CappedMarker, EndMarker, GapMarker, RunnerLostMarker } from './system-markers.js';
12
- export function LogView({ records, timestamps = 'off', wrap = false, showLineNumbers = true, emptyState = 'complete', defaultGroupsOpen = false, anchorToFailure = false, ariaLive = 'polite', className, onScroll }) {
13
+ export function LogView({ records, timestamps = 'off', wrap = false, showLineNumbers = true, emptyState = 'complete', defaultGroupsOpen = false, anchorToFailure = false, search = '', ariaLive = 'polite', className, onScroll }) {
13
14
  const rowsRef = useRef(null);
14
15
  const tree = useMemo(()=>buildLogTree(records), [
15
16
  records
16
17
  ]);
18
+ const deferredSearch = useDeferredValue(search);
19
+ const normalizedSearch = deferredSearch.trim().toLowerCase();
20
+ const searchIndex = useMemo(()=>buildLogSearchIndex(tree.nodes), [
21
+ tree.nodes
22
+ ]);
23
+ const visibleNodes = useMemo(()=>normalizedSearch ? filterLogNodes(tree.nodes, normalizedSearch, searchIndex) : tree.nodes, [
24
+ normalizedSearch,
25
+ searchIndex,
26
+ tree.nodes
27
+ ]);
17
28
  const resolvedToolCalls = useMemo(()=>collectResolvedToolCalls(tree.nodes), [
18
29
  tree.nodes
19
30
  ]);
20
- const noOutputState = getNoOutputState(tree, emptyState);
31
+ const noOutputState = normalizedSearch ? null : getNoOutputState(tree, emptyState);
21
32
  const anchorRecordCount = records.length;
33
+ const searchStatus = normalizedSearch ? visibleNodes.length === 0 ? `No log lines match “${deferredSearch.trim()}”.` : `Log search updated for “${deferredSearch.trim()}”.` : null;
22
34
  useEffect(()=>{
23
35
  if (!anchorToFailure) return;
24
36
  if (anchorRecordCount === 0) return;
@@ -39,22 +51,36 @@ export function LogView({ records, timestamps = 'off', wrap = false, showLineNum
39
51
  anchorToFailure,
40
52
  anchorRecordCount
41
53
  ]);
42
- return /*#__PURE__*/ _jsxs(LogRows, {
43
- ref: rowsRef,
44
- timestamps: timestamps,
45
- wrap: wrap,
46
- showLineNumbers: showLineNumbers,
47
- "aria-live": ariaLive,
48
- className: className,
49
- onScroll: onScroll,
50
- ...tree.originTs != null ? {
51
- timestampOrigin: new Date(tree.originTs)
52
- } : {},
54
+ return /*#__PURE__*/ _jsxs(_Fragment, {
53
55
  children: [
54
- noOutputState ? /*#__PURE__*/ _jsx(NoOutputRow, {
55
- state: noOutputState
56
+ searchStatus ? /*#__PURE__*/ _jsx("div", {
57
+ role: "status",
58
+ "aria-live": "polite",
59
+ "aria-atomic": "true",
60
+ className: "sr-only",
61
+ children: searchStatus
56
62
  }) : null,
57
- renderNodes(tree.nodes, 0, tree, defaultGroupsOpen, resolvedToolCalls)
63
+ /*#__PURE__*/ _jsxs(LogRows, {
64
+ ref: rowsRef,
65
+ timestamps: timestamps,
66
+ wrap: wrap,
67
+ showLineNumbers: showLineNumbers,
68
+ "aria-live": normalizedSearch ? 'off' : ariaLive,
69
+ className: className,
70
+ onScroll: onScroll,
71
+ ...tree.originTs != null ? {
72
+ timestampOrigin: new Date(tree.originTs)
73
+ } : {},
74
+ children: [
75
+ noOutputState ? /*#__PURE__*/ _jsx(NoOutputRow, {
76
+ state: noOutputState
77
+ }) : null,
78
+ normalizedSearch && visibleNodes.length === 0 ? /*#__PURE__*/ _jsx(NoSearchMatchesRow, {
79
+ query: deferredSearch.trim()
80
+ }) : null,
81
+ renderNodes(visibleNodes, 0, tree, defaultGroupsOpen, Boolean(normalizedSearch), resolvedToolCalls)
82
+ ]
83
+ })
58
84
  ]
59
85
  });
60
86
  }
@@ -113,7 +139,7 @@ function NoOutputRow({ state }) {
113
139
  return /*#__PURE__*/ _jsx(LogRow, {
114
140
  lineNumber: null,
115
141
  children: /*#__PURE__*/ _jsx(LogContent, {
116
- className: "text-foreground-neutral-muted",
142
+ className: "text-foreground-contrast-secondary",
117
143
  children: /*#__PURE__*/ _jsxs("span", {
118
144
  className: "inline-flex min-w-0 items-center gap-inline",
119
145
  children: [
@@ -131,7 +157,7 @@ function NoOutputRow({ state }) {
131
157
  }),
132
158
  ' · ',
133
159
  /*#__PURE__*/ _jsx("span", {
134
- className: "text-foreground-neutral-subtle",
160
+ className: "text-foreground-contrast-secondary",
135
161
  children: copy.detail
136
162
  })
137
163
  ]
@@ -141,7 +167,32 @@ function NoOutputRow({ state }) {
141
167
  })
142
168
  });
143
169
  }
144
- function renderNodes(nodes, depth, tree, defaultGroupsOpen, resolvedToolCalls) {
170
+ function NoSearchMatchesRow({ query }) {
171
+ return /*#__PURE__*/ _jsx(LogRow, {
172
+ lineNumber: null,
173
+ children: /*#__PURE__*/ _jsx(LogContent, {
174
+ className: "text-foreground-contrast-secondary",
175
+ children: /*#__PURE__*/ _jsxs("span", {
176
+ className: "inline-flex min-w-0 items-center gap-inline",
177
+ children: [
178
+ /*#__PURE__*/ _jsx(Icon, {
179
+ name: "searchLine",
180
+ className: "size-14 flex-none",
181
+ "aria-hidden": "true"
182
+ }),
183
+ /*#__PURE__*/ _jsxs("span", {
184
+ children: [
185
+ "No log lines match “",
186
+ query,
187
+ "”."
188
+ ]
189
+ })
190
+ ]
191
+ })
192
+ })
193
+ });
194
+ }
195
+ function renderNodes(nodes, depth, tree, defaultGroupsOpen, forceOpen, resolvedToolCalls) {
145
196
  // `node.seq` is the stable, unique render key (see `LogNodeBase`): a concatenated
146
197
  // multi-step/retry stream can repeat a `group_id` or a marker's `(type, ts)` at one
147
198
  // level, which a key derived from those fields would collide on.
@@ -159,7 +210,8 @@ function renderNodes(nodes, depth, tree, defaultGroupsOpen, resolvedToolCalls) {
159
210
  depth: depth,
160
211
  terminated: tree.terminated,
161
212
  defaultOpen: defaultGroupsOpen,
162
- children: renderNodes(node.children, depth + 1, tree, defaultGroupsOpen, resolvedToolCalls)
213
+ forceOpen: forceOpen,
214
+ children: renderNodes(node.children, depth + 1, tree, defaultGroupsOpen, forceOpen, resolvedToolCalls)
163
215
  }, node.seq);
164
216
  case 'marker':
165
217
  return /*#__PURE__*/ _jsx(MarkerRow, {
@@ -173,7 +225,8 @@ function renderNodes(nodes, depth, tree, defaultGroupsOpen, resolvedToolCalls) {
173
225
  ],
174
226
  resolvedToolCallIds: resolvedToolCalls.ids,
175
227
  toolCallNames: resolvedToolCalls.names,
176
- indent: depth
228
+ indent: depth,
229
+ forceOpen: forceOpen
177
230
  }, node.seq);
178
231
  default:
179
232
  return assertNever(node);