@shipfox/client-logs 14.0.0 → 16.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.
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 13 files with swc (299.74ms)
2
+ Successfully compiled: 13 files with swc (385.25ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @shipfox/client-logs
2
2
 
3
+ ## 16.0.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [80cde6b]
8
+ - @shipfox/react-ui@1.1.0
9
+
10
+ ## 14.0.1
11
+
12
+ ### Patch Changes
13
+
14
+ - 88bf8e8: Migrates the shell, auth, secrets, logs, workspace-settings, config, and invitations
15
+ surfaces to semantic spacing roles and brings them under the `no-raw-spacing` Biome
16
+ plugin. Adds shared menu-surface and edge-specific panel roles for existing spacing
17
+ contracts.
18
+ - Updated dependencies [88bf8e8]
19
+ - Updated dependencies [6aa6c7a]
20
+ - Updated dependencies [5c56ba6]
21
+ - @shipfox/react-ui@1.0.0
22
+
3
23
  ## 14.0.0
4
24
 
5
25
  ### Patch Changes
@@ -29,17 +29,17 @@ function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent }
29
29
  children: /*#__PURE__*/ _jsx(LogContent, {
30
30
  className: "text-foreground-neutral-base",
31
31
  children: /*#__PURE__*/ _jsxs("span", {
32
- className: "flex min-w-0 items-start gap-8",
32
+ className: "flex min-w-0 items-start gap-inline",
33
33
  children: [
34
34
  /*#__PURE__*/ _jsx(MessageIcon, {
35
35
  role: row.role,
36
36
  terminalFailure: row.terminalFailure
37
37
  }),
38
38
  /*#__PURE__*/ _jsxs("span", {
39
- className: "flex min-w-0 flex-1 flex-col gap-2",
39
+ className: "flex min-w-0 flex-1 flex-col gap-tight",
40
40
  children: [
41
41
  /*#__PURE__*/ _jsxs("span", {
42
- className: "flex min-w-0 items-center gap-8",
42
+ className: "flex min-w-0 items-center gap-inline",
43
43
  children: [
44
44
  /*#__PURE__*/ _jsx(MessageRoleLabel, {
45
45
  label: row.label,
@@ -94,7 +94,7 @@ function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent }
94
94
  timestamp: new Date(row.timestamp),
95
95
  summary: compactPreview(row.summary ?? row.input),
96
96
  trailing: awaitingResult ? /*#__PURE__*/ _jsxs("span", {
97
- className: "inline-flex items-center gap-4",
97
+ className: "inline-flex items-center gap-tight",
98
98
  children: [
99
99
  /*#__PURE__*/ _jsx(Icon, {
100
100
  name: "loader4Line",
@@ -105,7 +105,7 @@ function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent }
105
105
  ]
106
106
  }) : null,
107
107
  children: /*#__PURE__*/ _jsxs("span", {
108
- className: "inline-flex min-w-0 items-center gap-6",
108
+ className: "inline-flex min-w-0 items-center gap-inline",
109
109
  children: [
110
110
  /*#__PURE__*/ _jsx(Icon, {
111
111
  name: "terminalBoxLine",
@@ -157,7 +157,7 @@ function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent }
157
157
  timestamp: new Date(row.timestamp),
158
158
  summary: compactPreview(row.output),
159
159
  trailing: /*#__PURE__*/ _jsxs("span", {
160
- className: cn('inline-flex items-center gap-4', row.isError ? 'text-red-600 dark:text-red-400' : 'text-foreground-neutral-muted'),
160
+ className: cn('inline-flex items-center gap-tight', row.isError ? 'text-red-600 dark:text-red-400' : 'text-foreground-neutral-muted'),
161
161
  children: [
162
162
  /*#__PURE__*/ _jsx(Icon, {
163
163
  name: row.isError ? 'closeCircleLine' : 'checkLine',
@@ -168,7 +168,7 @@ function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent }
168
168
  ]
169
169
  }),
170
170
  children: /*#__PURE__*/ _jsxs("span", {
171
- className: "inline-flex min-w-0 items-center gap-6",
171
+ className: "inline-flex min-w-0 items-center gap-inline",
172
172
  children: [
173
173
  /*#__PURE__*/ _jsx(Icon, {
174
174
  name: "terminalWindowLine",
@@ -207,7 +207,7 @@ function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent }
207
207
  children: /*#__PURE__*/ _jsx(LogContent, {
208
208
  className: "text-foreground-neutral-muted",
209
209
  children: /*#__PURE__*/ _jsxs("span", {
210
- className: "inline-flex w-full items-center gap-8",
210
+ className: "inline-flex w-full items-center gap-inline",
211
211
  children: [
212
212
  /*#__PURE__*/ _jsx(Icon, {
213
213
  name: "informationLine",
@@ -252,7 +252,7 @@ function AgentSessionRowView({ row, resolvedToolCallIds, toolCallNames, indent }
252
252
  summary: compactPreview(row.raw),
253
253
  className: "text-orange-600 dark:text-orange-400",
254
254
  children: /*#__PURE__*/ _jsxs("span", {
255
- className: "inline-flex min-w-0 items-center gap-6",
255
+ className: "inline-flex min-w-0 items-center gap-inline",
256
256
  children: [
257
257
  /*#__PURE__*/ _jsx(Icon, {
258
258
  name: "errorWarningLine",
@@ -284,7 +284,7 @@ function MessageIcon({ role, terminalFailure }) {
284
284
  const name = terminalFailure ? 'closeCircleLine' : role === 'user' ? 'userLine' : role === 'assistant' ? 'robot2Line' : 'message2Line';
285
285
  return /*#__PURE__*/ _jsx(Icon, {
286
286
  name: name,
287
- className: cn('mt-2 size-14 flex-none', terminalFailure ? 'text-red-600 dark:text-red-400' : 'text-foreground-neutral-muted'),
287
+ className: cn('mt-[2px] size-14 flex-none', terminalFailure ? 'text-red-600 dark:text-red-400' : 'text-foreground-neutral-muted'),
288
288
  "aria-hidden": "true"
289
289
  });
290
290
  }
@@ -333,9 +333,9 @@ function MetadataTrigger({ meta }) {
333
333
  /*#__PURE__*/ _jsx(TooltipContent, {
334
334
  align: "end",
335
335
  variant: "inverted",
336
- className: "max-w-360 px-8 py-6",
336
+ className: "max-w-360 p-tight",
337
337
  children: /*#__PURE__*/ _jsx("span", {
338
- className: "grid grid-cols-[max-content_minmax(0,1fr)] gap-x-8 gap-y-2 font-code text-xs",
338
+ className: "grid grid-cols-[max-content_minmax(0,1fr)] gap-x-[var(--space-inline)] gap-y-[var(--space-tight)] font-code text-xs",
339
339
  children: meta.map((item)=>/*#__PURE__*/ _jsxs(Fragment, {
340
340
  children: [
341
341
  /*#__PURE__*/ _jsx("span", {
@@ -363,7 +363,7 @@ function PreviewText({ text }) {
363
363
  truncated ? /*#__PURE__*/ _jsx("button", {
364
364
  type: "button",
365
365
  "aria-expanded": expanded,
366
- className: "ml-8 inline-flex min-h-24 items-center rounded-4 px-6 font-display text-xs text-foreground-highlight-interactive focus-visible:shadow-[inset_0_0_0_2px_var(--color-primary-500)]",
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)]",
367
367
  onClick: ()=>setExpanded((value)=>!value),
368
368
  children: expanded ? 'show less' : 'show more'
369
369
  }) : 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-8\">\n <MessageIcon role={row.role} terminalFailure={row.terminalFailure} />\n <span className=\"flex min-w-0 flex-1 flex-col gap-2\">\n <span className=\"flex min-w-0 items-center gap-8\">\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-4\">\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-6\">\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-4',\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-6\">\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-8\">\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-6\">\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-2 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 px-8 py-6\">\n <span className=\"grid grid-cols-[max-content_minmax(0,1fr)] gap-x-8 gap-y-2 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=\"ml-8 inline-flex min-h-24 items-center rounded-4 px-6 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, 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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"}
@@ -42,7 +42,7 @@ function GroupStatus({ node, terminated }) {
42
42
  });
43
43
  }
44
44
  return /*#__PURE__*/ _jsxs("span", {
45
- className: "inline-flex items-center gap-4 text-foreground-neutral-muted",
45
+ className: "inline-flex items-center gap-tight text-foreground-neutral-muted",
46
46
  children: [
47
47
  /*#__PURE__*/ _jsx(Icon, {
48
48
  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-4 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} 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"}
@@ -77,7 +77,7 @@ export function LogViewSkeleton({ rows = 5, timestamps = 'off', wrap = false, sh
77
77
  children: skeletonRows.map((row)=>/*#__PURE__*/ _jsx(LogRow, {
78
78
  lineNumber: row.lineNumber,
79
79
  children: /*#__PURE__*/ _jsx(Skeleton, {
80
- className: `my-4 h-12 ${widths[(row.lineNumber - 1) % widths.length] ?? 'w-[48%]'}`
80
+ className: `my-[4px] h-12 ${widths[(row.lineNumber - 1) % widths.length] ?? 'w-[48%]'}`
81
81
  })
82
82
  }, row.id))
83
83
  });
@@ -114,7 +114,7 @@ function NoOutputRow({ state }) {
114
114
  children: /*#__PURE__*/ _jsx(LogContent, {
115
115
  className: "text-foreground-neutral-muted",
116
116
  children: /*#__PURE__*/ _jsxs("span", {
117
- className: "inline-flex min-w-0 items-center gap-8",
117
+ className: "inline-flex min-w-0 items-center gap-inline",
118
118
  children: [
119
119
  /*#__PURE__*/ _jsx(Icon, {
120
120
  name: "info",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/log-view.tsx"],"sourcesContent":["'use client';\n\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {LogContent, LogRow, LogRows, type LogTimestampMode} from '@shipfox/react-ui/log';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {type ReactNode, type UIEventHandler, useEffect, useMemo, useRef} from 'react';\nimport type {LogRecord} from '#core/log-model.js';\nimport {\n assertNever,\n buildLogTree,\n type LogNode,\n type LogTree,\n type MarkerLogRecord,\n} from '#core/log-tree.js';\nimport {AgentSessionRows} from './agent-session-rows.js';\nimport {LogGroup} from './log-group.js';\nimport {OutputLogRow} from './output-log-row.js';\nimport {CappedMarker, EndMarker, GapMarker, RunnerLostMarker} from './system-markers.js';\n\nexport interface LogViewProps {\n records: readonly LogRecord[];\n timestamps?: LogTimestampMode;\n wrap?: boolean;\n showLineNumbers?: boolean;\n emptyState?: 'complete' | 'pending';\n defaultGroupsOpen?: boolean;\n anchorToFailure?: boolean;\n className?: string | undefined;\n onScroll?: UIEventHandler<HTMLDivElement> | undefined;\n}\n\nexport interface LogViewSkeletonProps\n extends Pick<LogViewProps, 'timestamps' | 'wrap' | 'showLineNumbers' | 'className'> {\n rows?: number;\n}\n\nexport function LogView({\n records,\n timestamps = 'off',\n wrap = false,\n showLineNumbers = true,\n emptyState = 'complete',\n defaultGroupsOpen = false,\n anchorToFailure = false,\n className,\n onScroll,\n}: LogViewProps) {\n const rowsRef = useRef<HTMLDivElement>(null);\n const tree = useMemo(() => buildLogTree(records), [records]);\n const resolvedToolCalls = useMemo(() => collectResolvedToolCalls(tree.nodes), [tree.nodes]);\n const noOutputState = getNoOutputState(tree, emptyState);\n const anchorRecordCount = records.length;\n\n useEffect(() => {\n if (!anchorToFailure) return;\n if (anchorRecordCount === 0) return;\n\n const frame = scheduleAnimationFrame(() => {\n const rows = rowsRef.current;\n if (!rows) return;\n\n const failure = rows.querySelector<HTMLElement>('[data-log-terminal-failure=\"true\"]');\n if (failure) {\n failure.scrollIntoView({block: 'center'});\n return;\n }\n\n rows.scrollTop = rows.scrollHeight;\n });\n\n return () => cancelScheduledFrame(frame);\n }, [anchorToFailure, anchorRecordCount]);\n\n return (\n <LogRows\n ref={rowsRef}\n timestamps={timestamps}\n wrap={wrap}\n showLineNumbers={showLineNumbers}\n className={className}\n onScroll={onScroll}\n {...(tree.originTs != null ? {timestampOrigin: new Date(tree.originTs)} : {})}\n >\n {noOutputState ? <NoOutputRow state={noOutputState} /> : null}\n {renderNodes(tree.nodes, 0, tree, defaultGroupsOpen, resolvedToolCalls)}\n </LogRows>\n );\n}\n\nexport function LogViewSkeleton({\n rows = 5,\n timestamps = 'off',\n wrap = false,\n showLineNumbers = true,\n className,\n}: LogViewSkeletonProps) {\n const widths = ['w-[62%]', 'w-[44%]', 'w-[74%]', 'w-[36%]', 'w-[55%]'];\n const skeletonRows = getSkeletonRows(rows);\n\n return (\n <LogRows\n timestamps={timestamps}\n wrap={wrap}\n showLineNumbers={showLineNumbers}\n className={className}\n role=\"presentation\"\n aria-live=\"off\"\n aria-hidden=\"true\"\n >\n {skeletonRows.map((row) => (\n <LogRow key={row.id} lineNumber={row.lineNumber}>\n <Skeleton\n className={`my-4 h-12 ${widths[(row.lineNumber - 1) % widths.length] ?? 'w-[48%]'}`}\n />\n </LogRow>\n ))}\n </LogRows>\n );\n}\n\nfunction getSkeletonRows(rows: number): {id: string; lineNumber: number}[] {\n return Array.from({length: rows}, (_, index) => {\n const lineNumber = index + 1;\n return {id: `log-view-skeleton-row-${lineNumber}`, lineNumber};\n });\n}\n\nfunction getNoOutputState(\n tree: LogTree,\n emptyState: NonNullable<LogViewProps['emptyState']>,\n): LogViewProps['emptyState'] | null {\n if (tree.nodes.length === 0) return emptyState;\n\n if (tree.lineCount !== 0) return null;\n if (tree.nodes.length !== 1) return null;\n\n const [node] = tree.nodes;\n if (node?.kind === 'marker' && node.record.type === 'end') return 'complete';\n\n return null;\n}\n\nfunction NoOutputRow({state}: {state: NonNullable<LogViewProps['emptyState']>}) {\n const copy =\n state === 'pending'\n ? {\n title: 'No output yet',\n detail: 'New lines will appear here as the step writes them.',\n }\n : {\n title: 'Step produced no output',\n detail: 'This log stream closed without session entries or process output.',\n };\n\n return (\n <LogRow lineNumber={null}>\n <LogContent className=\"text-foreground-neutral-muted\">\n <span className=\"inline-flex min-w-0 items-center gap-8\">\n <Icon name=\"info\" className=\"size-14 flex-none\" aria-hidden=\"true\" />\n <span className=\"min-w-0\">\n <span className=\"font-medium\">{copy.title}</span>\n {' · '}\n <span className=\"text-foreground-neutral-subtle\">{copy.detail}</span>\n </span>\n </span>\n </LogContent>\n </LogRow>\n );\n}\n\nfunction renderNodes(\n nodes: readonly LogNode[],\n depth: number,\n tree: LogTree,\n defaultGroupsOpen: boolean,\n resolvedToolCalls: ResolvedToolCalls,\n): ReactNode[] {\n // `node.seq` is the stable, unique render key (see `LogNodeBase`): a concatenated\n // multi-step/retry stream can repeat a `group_id` or a marker's `(type, ts)` at one\n // level, which a key derived from those fields would collide on.\n return nodes.map((node): ReactNode => {\n switch (node.kind) {\n case 'output':\n return (\n <OutputLogRow\n key={node.seq}\n record={node.record}\n lineNumber={node.lineNumber}\n indent={depth}\n />\n );\n case 'group':\n return (\n <LogGroup\n key={node.seq}\n node={node}\n depth={depth}\n terminated={tree.terminated}\n defaultOpen={defaultGroupsOpen}\n >\n {renderNodes(node.children, depth + 1, tree, defaultGroupsOpen, resolvedToolCalls)}\n </LogGroup>\n );\n case 'marker':\n return <MarkerRow key={node.seq} record={node.record} tree={tree} />;\n case 'session':\n return (\n <AgentSessionRows\n key={node.seq}\n rows={[node.record.row]}\n resolvedToolCallIds={resolvedToolCalls.ids}\n toolCallNames={resolvedToolCalls.names}\n indent={depth}\n />\n );\n default:\n return assertNever(node);\n }\n });\n}\n\ninterface ResolvedToolCalls {\n ids: ReadonlySet<string>;\n names: ReadonlyMap<string, string>;\n}\n\nfunction collectResolvedToolCalls(nodes: readonly LogNode[]): ResolvedToolCalls {\n const ids = new Set<string>();\n const names = new Map<string, string>();\n collectResolvedToolCallsInto(nodes, ids, names);\n return {ids, names};\n}\n\nfunction collectResolvedToolCallsInto(\n nodes: readonly LogNode[],\n ids: Set<string>,\n names: Map<string, string>,\n): void {\n for (const node of nodes) {\n switch (node.kind) {\n case 'session':\n if (node.record.row.kind === 'tool-call' && node.record.row.id != null) {\n names.set(node.record.row.id, node.record.row.name);\n } else if (node.record.row.kind === 'tool-result' && node.record.row.toolCallId != null) {\n ids.add(node.record.row.toolCallId);\n }\n break;\n case 'group':\n collectResolvedToolCallsInto(node.children, ids, names);\n break;\n case 'output':\n case 'marker':\n break;\n default:\n assertNever(node);\n }\n }\n}\n\nfunction scheduleAnimationFrame(callback: FrameRequestCallback): number {\n if (typeof globalThis.requestAnimationFrame === 'function') {\n return globalThis.requestAnimationFrame(callback);\n }\n return window.setTimeout(() => callback(Date.now()), 0);\n}\n\nfunction cancelScheduledFrame(frame: number) {\n if (typeof globalThis.cancelAnimationFrame === 'function') {\n globalThis.cancelAnimationFrame(frame);\n return;\n }\n window.clearTimeout(frame);\n}\n\nfunction MarkerRow({record, tree}: {record: MarkerLogRecord; tree: LogTree}): ReactNode {\n switch (record.type) {\n case 'end':\n return (\n <EndMarker\n record={record}\n lineCount={tree.lineCount}\n durationMs={tree.originTs != null ? record.ts - tree.originTs : null}\n />\n );\n case 'gap':\n return <GapMarker record={record} />;\n case 'capped':\n return <CappedMarker record={record} />;\n case 'runner_lost':\n return <RunnerLostMarker record={record} />;\n default:\n return assertNever(record);\n }\n}\n"],"names":["Icon","LogContent","LogRow","LogRows","Skeleton","useEffect","useMemo","useRef","assertNever","buildLogTree","AgentSessionRows","LogGroup","OutputLogRow","CappedMarker","EndMarker","GapMarker","RunnerLostMarker","LogView","records","timestamps","wrap","showLineNumbers","emptyState","defaultGroupsOpen","anchorToFailure","className","onScroll","rowsRef","tree","resolvedToolCalls","collectResolvedToolCalls","nodes","noOutputState","getNoOutputState","anchorRecordCount","length","frame","scheduleAnimationFrame","rows","current","failure","querySelector","scrollIntoView","block","scrollTop","scrollHeight","cancelScheduledFrame","ref","originTs","timestampOrigin","Date","NoOutputRow","state","renderNodes","LogViewSkeleton","widths","skeletonRows","getSkeletonRows","role","aria-live","aria-hidden","map","row","lineNumber","id","Array","from","_","index","lineCount","node","kind","record","type","copy","title","detail","span","name","depth","indent","seq","terminated","defaultOpen","children","MarkerRow","resolvedToolCallIds","ids","toolCallNames","names","Set","Map","collectResolvedToolCallsInto","set","toolCallId","add","callback","globalThis","requestAnimationFrame","window","setTimeout","now","cancelAnimationFrame","clearTimeout","durationMs","ts"],"mappings":"AAAA;;AAEA,SAAQA,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,UAAU,EAAEC,MAAM,EAAEC,OAAO,QAA8B,wBAAwB;AACzF,SAAQC,QAAQ,QAAO,6BAA6B;AACpD,SAA6CC,SAAS,EAAEC,OAAO,EAAEC,MAAM,QAAO,QAAQ;AAEtF,SACEC,WAAW,EACXC,YAAY,QAIP,oBAAoB;AAC3B,SAAQC,gBAAgB,QAAO,0BAA0B;AACzD,SAAQC,QAAQ,QAAO,iBAAiB;AACxC,SAAQC,YAAY,QAAO,sBAAsB;AACjD,SAAQC,YAAY,EAAEC,SAAS,EAAEC,SAAS,EAAEC,gBAAgB,QAAO,sBAAsB;AAmBzF,OAAO,SAASC,QAAQ,EACtBC,OAAO,EACPC,aAAa,KAAK,EAClBC,OAAO,KAAK,EACZC,kBAAkB,IAAI,EACtBC,aAAa,UAAU,EACvBC,oBAAoB,KAAK,EACzBC,kBAAkB,KAAK,EACvBC,SAAS,EACTC,QAAQ,EACK;IACb,MAAMC,UAAUpB,OAAuB;IACvC,MAAMqB,OAAOtB,QAAQ,IAAMG,aAAaS,UAAU;QAACA;KAAQ;IAC3D,MAAMW,oBAAoBvB,QAAQ,IAAMwB,yBAAyBF,KAAKG,KAAK,GAAG;QAACH,KAAKG,KAAK;KAAC;IAC1F,MAAMC,gBAAgBC,iBAAiBL,MAAMN;IAC7C,MAAMY,oBAAoBhB,QAAQiB,MAAM;IAExC9B,UAAU;QACR,IAAI,CAACmB,iBAAiB;QACtB,IAAIU,sBAAsB,GAAG;QAE7B,MAAME,QAAQC,uBAAuB;YACnC,MAAMC,OAAOX,QAAQY,OAAO;YAC5B,IAAI,CAACD,MAAM;YAEX,MAAME,UAAUF,KAAKG,aAAa,CAAc;YAChD,IAAID,SAAS;gBACXA,QAAQE,cAAc,CAAC;oBAACC,OAAO;gBAAQ;gBACvC;YACF;YAEAL,KAAKM,SAAS,GAAGN,KAAKO,YAAY;QACpC;QAEA,OAAO,IAAMC,qBAAqBV;IACpC,GAAG;QAACZ;QAAiBU;KAAkB;IAEvC,qBACE,MAAC/B;QACC4C,KAAKpB;QACLR,YAAYA;QACZC,MAAMA;QACNC,iBAAiBA;QACjBI,WAAWA;QACXC,UAAUA;QACT,GAAIE,KAAKoB,QAAQ,IAAI,OAAO;YAACC,iBAAiB,IAAIC,KAAKtB,KAAKoB,QAAQ;QAAC,IAAI,CAAC,CAAC;;YAE3EhB,8BAAgB,KAACmB;gBAAYC,OAAOpB;iBAAoB;YACxDqB,YAAYzB,KAAKG,KAAK,EAAE,GAAGH,MAAML,mBAAmBM;;;AAG3D;AAEA,OAAO,SAASyB,gBAAgB,EAC9BhB,OAAO,CAAC,EACRnB,aAAa,KAAK,EAClBC,OAAO,KAAK,EACZC,kBAAkB,IAAI,EACtBI,SAAS,EACY;IACrB,MAAM8B,SAAS;QAAC;QAAW;QAAW;QAAW;QAAW;KAAU;IACtE,MAAMC,eAAeC,gBAAgBnB;IAErC,qBACE,KAACnC;QACCgB,YAAYA;QACZC,MAAMA;QACNC,iBAAiBA;QACjBI,WAAWA;QACXiC,MAAK;QACLC,aAAU;QACVC,eAAY;kBAEXJ,aAAaK,GAAG,CAAC,CAACC,oBACjB,KAAC5D;gBAAoB6D,YAAYD,IAAIC,UAAU;0BAC7C,cAAA,KAAC3D;oBACCqB,WAAW,CAAC,UAAU,EAAE8B,MAAM,CAAC,AAACO,CAAAA,IAAIC,UAAU,GAAG,CAAA,IAAKR,OAAOpB,MAAM,CAAC,IAAI,WAAW;;eAF1E2B,IAAIE,EAAE;;AAQ3B;AAEA,SAASP,gBAAgBnB,IAAY;IACnC,OAAO2B,MAAMC,IAAI,CAAC;QAAC/B,QAAQG;IAAI,GAAG,CAAC6B,GAAGC;QACpC,MAAML,aAAaK,QAAQ;QAC3B,OAAO;YAACJ,IAAI,CAAC,sBAAsB,EAAED,YAAY;YAAEA;QAAU;IAC/D;AACF;AAEA,SAAS9B,iBACPL,IAAa,EACbN,UAAmD;IAEnD,IAAIM,KAAKG,KAAK,CAACI,MAAM,KAAK,GAAG,OAAOb;IAEpC,IAAIM,KAAKyC,SAAS,KAAK,GAAG,OAAO;IACjC,IAAIzC,KAAKG,KAAK,CAACI,MAAM,KAAK,GAAG,OAAO;IAEpC,MAAM,CAACmC,KAAK,GAAG1C,KAAKG,KAAK;IACzB,IAAIuC,MAAMC,SAAS,YAAYD,KAAKE,MAAM,CAACC,IAAI,KAAK,OAAO,OAAO;IAElE,OAAO;AACT;AAEA,SAAStB,YAAY,EAACC,KAAK,EAAmD;IAC5E,MAAMsB,OACJtB,UAAU,YACN;QACEuB,OAAO;QACPC,QAAQ;IACV,IACA;QACED,OAAO;QACPC,QAAQ;IACV;IAEN,qBACE,KAAC1E;QAAO6D,YAAY;kBAClB,cAAA,KAAC9D;YAAWwB,WAAU;sBACpB,cAAA,MAACoD;gBAAKpD,WAAU;;kCACd,KAACzB;wBAAK8E,MAAK;wBAAOrD,WAAU;wBAAoBmC,eAAY;;kCAC5D,MAACiB;wBAAKpD,WAAU;;0CACd,KAACoD;gCAAKpD,WAAU;0CAAeiD,KAAKC,KAAK;;4BACxC;0CACD,KAACE;gCAAKpD,WAAU;0CAAkCiD,KAAKE,MAAM;;;;;;;;AAMzE;AAEA,SAASvB,YACPtB,KAAyB,EACzBgD,KAAa,EACbnD,IAAa,EACbL,iBAA0B,EAC1BM,iBAAoC;IAEpC,kFAAkF;IAClF,oFAAoF;IACpF,iEAAiE;IACjE,OAAOE,MAAM8B,GAAG,CAAC,CAACS;QAChB,OAAQA,KAAKC,IAAI;YACf,KAAK;gBACH,qBACE,KAAC3D;oBAEC4D,QAAQF,KAAKE,MAAM;oBACnBT,YAAYO,KAAKP,UAAU;oBAC3BiB,QAAQD;mBAHHT,KAAKW,GAAG;YAMnB,KAAK;gBACH,qBACE,KAACtE;oBAEC2D,MAAMA;oBACNS,OAAOA;oBACPG,YAAYtD,KAAKsD,UAAU;oBAC3BC,aAAa5D;8BAEZ8B,YAAYiB,KAAKc,QAAQ,EAAEL,QAAQ,GAAGnD,MAAML,mBAAmBM;mBAN3DyC,KAAKW,GAAG;YASnB,KAAK;gBACH,qBAAO,KAACI;oBAAyBb,QAAQF,KAAKE,MAAM;oBAAE5C,MAAMA;mBAArC0C,KAAKW,GAAG;YACjC,KAAK;gBACH,qBACE,KAACvE;oBAEC4B,MAAM;wBAACgC,KAAKE,MAAM,CAACV,GAAG;qBAAC;oBACvBwB,qBAAqBzD,kBAAkB0D,GAAG;oBAC1CC,eAAe3D,kBAAkB4D,KAAK;oBACtCT,QAAQD;mBAJHT,KAAKW,GAAG;YAOnB;gBACE,OAAOzE,YAAY8D;QACvB;IACF;AACF;AAOA,SAASxC,yBAAyBC,KAAyB;IACzD,MAAMwD,MAAM,IAAIG;IAChB,MAAMD,QAAQ,IAAIE;IAClBC,6BAA6B7D,OAAOwD,KAAKE;IACzC,OAAO;QAACF;QAAKE;IAAK;AACpB;AAEA,SAASG,6BACP7D,KAAyB,EACzBwD,GAAgB,EAChBE,KAA0B;IAE1B,KAAK,MAAMnB,QAAQvC,MAAO;QACxB,OAAQuC,KAAKC,IAAI;YACf,KAAK;gBACH,IAAID,KAAKE,MAAM,CAACV,GAAG,CAACS,IAAI,KAAK,eAAeD,KAAKE,MAAM,CAACV,GAAG,CAACE,EAAE,IAAI,MAAM;oBACtEyB,MAAMI,GAAG,CAACvB,KAAKE,MAAM,CAACV,GAAG,CAACE,EAAE,EAAEM,KAAKE,MAAM,CAACV,GAAG,CAACgB,IAAI;gBACpD,OAAO,IAAIR,KAAKE,MAAM,CAACV,GAAG,CAACS,IAAI,KAAK,iBAAiBD,KAAKE,MAAM,CAACV,GAAG,CAACgC,UAAU,IAAI,MAAM;oBACvFP,IAAIQ,GAAG,CAACzB,KAAKE,MAAM,CAACV,GAAG,CAACgC,UAAU;gBACpC;gBACA;YACF,KAAK;gBACHF,6BAA6BtB,KAAKc,QAAQ,EAAEG,KAAKE;gBACjD;YACF,KAAK;YACL,KAAK;gBACH;YACF;gBACEjF,YAAY8D;QAChB;IACF;AACF;AAEA,SAASjC,uBAAuB2D,QAA8B;IAC5D,IAAI,OAAOC,WAAWC,qBAAqB,KAAK,YAAY;QAC1D,OAAOD,WAAWC,qBAAqB,CAACF;IAC1C;IACA,OAAOG,OAAOC,UAAU,CAAC,IAAMJ,SAAS9C,KAAKmD,GAAG,KAAK;AACvD;AAEA,SAASvD,qBAAqBV,KAAa;IACzC,IAAI,OAAO6D,WAAWK,oBAAoB,KAAK,YAAY;QACzDL,WAAWK,oBAAoB,CAAClE;QAChC;IACF;IACA+D,OAAOI,YAAY,CAACnE;AACtB;AAEA,SAASiD,UAAU,EAACb,MAAM,EAAE5C,IAAI,EAA2C;IACzE,OAAQ4C,OAAOC,IAAI;QACjB,KAAK;YACH,qBACE,KAAC3D;gBACC0D,QAAQA;gBACRH,WAAWzC,KAAKyC,SAAS;gBACzBmC,YAAY5E,KAAKoB,QAAQ,IAAI,OAAOwB,OAAOiC,EAAE,GAAG7E,KAAKoB,QAAQ,GAAG;;QAGtE,KAAK;YACH,qBAAO,KAACjC;gBAAUyD,QAAQA;;QAC5B,KAAK;YACH,qBAAO,KAAC3D;gBAAa2D,QAAQA;;QAC/B,KAAK;YACH,qBAAO,KAACxD;gBAAiBwD,QAAQA;;QACnC;YACE,OAAOhE,YAAYgE;IACvB;AACF"}
1
+ {"version":3,"sources":["../../src/components/log-view.tsx"],"sourcesContent":["'use client';\n\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {LogContent, LogRow, LogRows, type LogTimestampMode} from '@shipfox/react-ui/log';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {type ReactNode, type UIEventHandler, useEffect, useMemo, useRef} from 'react';\nimport type {LogRecord} from '#core/log-model.js';\nimport {\n assertNever,\n buildLogTree,\n type LogNode,\n type LogTree,\n type MarkerLogRecord,\n} from '#core/log-tree.js';\nimport {AgentSessionRows} from './agent-session-rows.js';\nimport {LogGroup} from './log-group.js';\nimport {OutputLogRow} from './output-log-row.js';\nimport {CappedMarker, EndMarker, GapMarker, RunnerLostMarker} from './system-markers.js';\n\nexport interface LogViewProps {\n records: readonly LogRecord[];\n timestamps?: LogTimestampMode;\n wrap?: boolean;\n showLineNumbers?: boolean;\n emptyState?: 'complete' | 'pending';\n defaultGroupsOpen?: boolean;\n anchorToFailure?: boolean;\n className?: string | undefined;\n onScroll?: UIEventHandler<HTMLDivElement> | undefined;\n}\n\nexport interface LogViewSkeletonProps\n extends Pick<LogViewProps, 'timestamps' | 'wrap' | 'showLineNumbers' | 'className'> {\n rows?: number;\n}\n\nexport function LogView({\n records,\n timestamps = 'off',\n wrap = false,\n showLineNumbers = true,\n emptyState = 'complete',\n defaultGroupsOpen = false,\n anchorToFailure = false,\n className,\n onScroll,\n}: LogViewProps) {\n const rowsRef = useRef<HTMLDivElement>(null);\n const tree = useMemo(() => buildLogTree(records), [records]);\n const resolvedToolCalls = useMemo(() => collectResolvedToolCalls(tree.nodes), [tree.nodes]);\n const noOutputState = getNoOutputState(tree, emptyState);\n const anchorRecordCount = records.length;\n\n useEffect(() => {\n if (!anchorToFailure) return;\n if (anchorRecordCount === 0) return;\n\n const frame = scheduleAnimationFrame(() => {\n const rows = rowsRef.current;\n if (!rows) return;\n\n const failure = rows.querySelector<HTMLElement>('[data-log-terminal-failure=\"true\"]');\n if (failure) {\n failure.scrollIntoView({block: 'center'});\n return;\n }\n\n rows.scrollTop = rows.scrollHeight;\n });\n\n return () => cancelScheduledFrame(frame);\n }, [anchorToFailure, anchorRecordCount]);\n\n return (\n <LogRows\n ref={rowsRef}\n timestamps={timestamps}\n wrap={wrap}\n showLineNumbers={showLineNumbers}\n className={className}\n onScroll={onScroll}\n {...(tree.originTs != null ? {timestampOrigin: new Date(tree.originTs)} : {})}\n >\n {noOutputState ? <NoOutputRow state={noOutputState} /> : null}\n {renderNodes(tree.nodes, 0, tree, defaultGroupsOpen, resolvedToolCalls)}\n </LogRows>\n );\n}\n\nexport function LogViewSkeleton({\n rows = 5,\n timestamps = 'off',\n wrap = false,\n showLineNumbers = true,\n className,\n}: LogViewSkeletonProps) {\n const widths = ['w-[62%]', 'w-[44%]', 'w-[74%]', 'w-[36%]', 'w-[55%]'];\n const skeletonRows = getSkeletonRows(rows);\n\n return (\n <LogRows\n timestamps={timestamps}\n wrap={wrap}\n showLineNumbers={showLineNumbers}\n className={className}\n role=\"presentation\"\n aria-live=\"off\"\n aria-hidden=\"true\"\n >\n {skeletonRows.map((row) => (\n <LogRow key={row.id} lineNumber={row.lineNumber}>\n <Skeleton\n className={`my-[4px] h-12 ${widths[(row.lineNumber - 1) % widths.length] ?? 'w-[48%]'}`}\n />\n </LogRow>\n ))}\n </LogRows>\n );\n}\n\nfunction getSkeletonRows(rows: number): {id: string; lineNumber: number}[] {\n return Array.from({length: rows}, (_, index) => {\n const lineNumber = index + 1;\n return {id: `log-view-skeleton-row-${lineNumber}`, lineNumber};\n });\n}\n\nfunction getNoOutputState(\n tree: LogTree,\n emptyState: NonNullable<LogViewProps['emptyState']>,\n): LogViewProps['emptyState'] | null {\n if (tree.nodes.length === 0) return emptyState;\n\n if (tree.lineCount !== 0) return null;\n if (tree.nodes.length !== 1) return null;\n\n const [node] = tree.nodes;\n if (node?.kind === 'marker' && node.record.type === 'end') return 'complete';\n\n return null;\n}\n\nfunction NoOutputRow({state}: {state: NonNullable<LogViewProps['emptyState']>}) {\n const copy =\n state === 'pending'\n ? {\n title: 'No output yet',\n detail: 'New lines will appear here as the step writes them.',\n }\n : {\n title: 'Step produced no output',\n detail: 'This log stream closed without session entries or process output.',\n };\n\n return (\n <LogRow lineNumber={null}>\n <LogContent className=\"text-foreground-neutral-muted\">\n <span className=\"inline-flex min-w-0 items-center gap-inline\">\n <Icon name=\"info\" className=\"size-14 flex-none\" aria-hidden=\"true\" />\n <span className=\"min-w-0\">\n <span className=\"font-medium\">{copy.title}</span>\n {' · '}\n <span className=\"text-foreground-neutral-subtle\">{copy.detail}</span>\n </span>\n </span>\n </LogContent>\n </LogRow>\n );\n}\n\nfunction renderNodes(\n nodes: readonly LogNode[],\n depth: number,\n tree: LogTree,\n defaultGroupsOpen: boolean,\n resolvedToolCalls: ResolvedToolCalls,\n): ReactNode[] {\n // `node.seq` is the stable, unique render key (see `LogNodeBase`): a concatenated\n // multi-step/retry stream can repeat a `group_id` or a marker's `(type, ts)` at one\n // level, which a key derived from those fields would collide on.\n return nodes.map((node): ReactNode => {\n switch (node.kind) {\n case 'output':\n return (\n <OutputLogRow\n key={node.seq}\n record={node.record}\n lineNumber={node.lineNumber}\n indent={depth}\n />\n );\n case 'group':\n return (\n <LogGroup\n key={node.seq}\n node={node}\n depth={depth}\n terminated={tree.terminated}\n defaultOpen={defaultGroupsOpen}\n >\n {renderNodes(node.children, depth + 1, tree, defaultGroupsOpen, resolvedToolCalls)}\n </LogGroup>\n );\n case 'marker':\n return <MarkerRow key={node.seq} record={node.record} tree={tree} />;\n case 'session':\n return (\n <AgentSessionRows\n key={node.seq}\n rows={[node.record.row]}\n resolvedToolCallIds={resolvedToolCalls.ids}\n toolCallNames={resolvedToolCalls.names}\n indent={depth}\n />\n );\n default:\n return assertNever(node);\n }\n });\n}\n\ninterface ResolvedToolCalls {\n ids: ReadonlySet<string>;\n names: ReadonlyMap<string, string>;\n}\n\nfunction collectResolvedToolCalls(nodes: readonly LogNode[]): ResolvedToolCalls {\n const ids = new Set<string>();\n const names = new Map<string, string>();\n collectResolvedToolCallsInto(nodes, ids, names);\n return {ids, names};\n}\n\nfunction collectResolvedToolCallsInto(\n nodes: readonly LogNode[],\n ids: Set<string>,\n names: Map<string, string>,\n): void {\n for (const node of nodes) {\n switch (node.kind) {\n case 'session':\n if (node.record.row.kind === 'tool-call' && node.record.row.id != null) {\n names.set(node.record.row.id, node.record.row.name);\n } else if (node.record.row.kind === 'tool-result' && node.record.row.toolCallId != null) {\n ids.add(node.record.row.toolCallId);\n }\n break;\n case 'group':\n collectResolvedToolCallsInto(node.children, ids, names);\n break;\n case 'output':\n case 'marker':\n break;\n default:\n assertNever(node);\n }\n }\n}\n\nfunction scheduleAnimationFrame(callback: FrameRequestCallback): number {\n if (typeof globalThis.requestAnimationFrame === 'function') {\n return globalThis.requestAnimationFrame(callback);\n }\n return window.setTimeout(() => callback(Date.now()), 0);\n}\n\nfunction cancelScheduledFrame(frame: number) {\n if (typeof globalThis.cancelAnimationFrame === 'function') {\n globalThis.cancelAnimationFrame(frame);\n return;\n }\n window.clearTimeout(frame);\n}\n\nfunction MarkerRow({record, tree}: {record: MarkerLogRecord; tree: LogTree}): ReactNode {\n switch (record.type) {\n case 'end':\n return (\n <EndMarker\n record={record}\n lineCount={tree.lineCount}\n durationMs={tree.originTs != null ? record.ts - tree.originTs : null}\n />\n );\n case 'gap':\n return <GapMarker record={record} />;\n case 'capped':\n return <CappedMarker record={record} />;\n case 'runner_lost':\n return <RunnerLostMarker record={record} />;\n default:\n return assertNever(record);\n }\n}\n"],"names":["Icon","LogContent","LogRow","LogRows","Skeleton","useEffect","useMemo","useRef","assertNever","buildLogTree","AgentSessionRows","LogGroup","OutputLogRow","CappedMarker","EndMarker","GapMarker","RunnerLostMarker","LogView","records","timestamps","wrap","showLineNumbers","emptyState","defaultGroupsOpen","anchorToFailure","className","onScroll","rowsRef","tree","resolvedToolCalls","collectResolvedToolCalls","nodes","noOutputState","getNoOutputState","anchorRecordCount","length","frame","scheduleAnimationFrame","rows","current","failure","querySelector","scrollIntoView","block","scrollTop","scrollHeight","cancelScheduledFrame","ref","originTs","timestampOrigin","Date","NoOutputRow","state","renderNodes","LogViewSkeleton","widths","skeletonRows","getSkeletonRows","role","aria-live","aria-hidden","map","row","lineNumber","id","Array","from","_","index","lineCount","node","kind","record","type","copy","title","detail","span","name","depth","indent","seq","terminated","defaultOpen","children","MarkerRow","resolvedToolCallIds","ids","toolCallNames","names","Set","Map","collectResolvedToolCallsInto","set","toolCallId","add","callback","globalThis","requestAnimationFrame","window","setTimeout","now","cancelAnimationFrame","clearTimeout","durationMs","ts"],"mappings":"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"}
@@ -22,7 +22,7 @@ const toneText = {
22
22
  children: /*#__PURE__*/ _jsx(LogContent, {
23
23
  className: cn('block', toneText[tone]),
24
24
  children: /*#__PURE__*/ _jsxs("span", {
25
- className: "inline-flex w-full items-center gap-8",
25
+ className: "inline-flex w-full items-center gap-inline",
26
26
  children: [
27
27
  /*#__PURE__*/ _jsx(Icon, {
28
28
  name: icon,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/system-markers.tsx"],"sourcesContent":["import {Icon, type IconName} from '@shipfox/react-ui/icon';\nimport {LogContent, LogRow} from '@shipfox/react-ui/log';\nimport {cn, formatBytes, formatDuration} from '@shipfox/react-ui/utils';\nimport type {\n CappedLogRecord,\n EndLogRecord,\n GapLogRecord,\n RunnerLostLogRecord,\n} from '#core/log-tree.js';\n\ntype MarkerTone = 'default' | 'warning' | 'error';\n\nconst toneText: Record<MarkerTone, string> = {\n default: 'text-foreground-neutral-muted',\n warning: 'text-orange-600 dark:text-orange-400',\n error: 'text-red-600 dark:text-red-400',\n};\n\ninterface LogMarkerRowProps {\n icon: IconName;\n tone: MarkerTone;\n timestamp?: Date | null;\n /** Plain-language clause after the label: what it means / what to do. */\n detail?: string;\n /** Right-aligned `font-code` figures (bytes, line count, duration). */\n meta?: string;\n terminalFailure?: boolean;\n children: string;\n}\n\n/**\n * Shared timeline-marker row: a non-numbered line with a leading icon, a bold label, an\n * optional plain-language detail clause, a dashed divider, and optional right-aligned\n * monospace figures. The detail explains the consequence to the operator (the label\n * names the event, the icon/tone carry severity), so the copy reads helpfully rather\n * than mechanically.\n */\nfunction LogMarkerRow({\n icon,\n tone,\n timestamp = null,\n detail,\n meta,\n terminalFailure = false,\n children,\n}: LogMarkerRowProps) {\n return (\n <LogRow\n lineNumber={null}\n timestamp={timestamp}\n tone={tone}\n data-log-terminal-failure={terminalFailure ? 'true' : undefined}\n >\n <LogContent className={cn('block', toneText[tone])}>\n <span className=\"inline-flex w-full items-center gap-8\">\n <Icon name={icon} className=\"size-14 flex-none\" aria-hidden=\"true\" />\n {/* Label, detail, and figures share one text cluster joined by a literal\n \" · \". Flex `gap` is visual only and would copy with no separator, so the\n separators are real inline text. The dashed rule trails after the text\n (an aria-hidden flex filler) and contributes nothing to a selection. */}\n <span className=\"min-w-0\">\n <span className=\"font-medium\">{children}</span>\n {detail != null && (\n <>\n {' · '}\n <span className=\"font-normal opacity-80\">{detail}</span>\n </>\n )}\n {meta != null && (\n <>\n {' · '}\n <span className=\"font-code tabular-nums opacity-80\">{meta}</span>\n </>\n )}\n </span>\n <span\n aria-hidden=\"true\"\n className=\"h-px flex-1 border-t border-dashed border-current opacity-30\"\n />\n </span>\n </LogContent>\n </LogRow>\n );\n}\n\nexport interface EndMarkerProps {\n record: EndLogRecord;\n lineCount: number;\n durationMs?: number | null;\n}\n\n/** Clean end of the log: line count + output bytes (+ overall duration). `total_bytes` is payload bytes. */\nexport function EndMarker({record, lineCount, durationMs = null}: EndMarkerProps) {\n const meta = [\n `${lineCount} ${lineCount === 1 ? 'line' : 'lines'}`,\n formatBytes(record.totalBytes),\n ...(durationMs != null ? [formatDuration(durationMs)] : []),\n ].join(' · ');\n\n return (\n <LogMarkerRow icon=\"flagLine\" tone=\"default\" timestamp={new Date(record.ts)} meta={meta}>\n End of log\n </LogMarkerRow>\n );\n}\n\n/** The runner's local backlog shed bytes before upload, so some output never arrived. A warning. */\nexport function GapMarker({record}: {record: GapLogRecord}) {\n return (\n <LogMarkerRow\n icon=\"errorWarningLine\"\n tone=\"warning\"\n timestamp={new Date(record.ts)}\n detail={`the runner fell behind and dropped ${formatBytes(record.droppedBytes)}`}\n >\n Output missing\n </LogMarkerRow>\n );\n}\n\n/** The job hit its shared log size limit; logging stopped but the step kept running. A warning. */\nexport function CappedMarker({record}: {record: CappedLogRecord}) {\n return (\n <LogMarkerRow\n icon=\"forbidLine\"\n tone=\"warning\"\n timestamp={new Date(record.ts)}\n detail=\"later output isn't shown; the step kept running\"\n >\n Log size limit reached\n </LogMarkerRow>\n );\n}\n\n/** The runner disappeared and the stream was force-closed. A terminal failure (status taxonomy §9). */\nexport function RunnerLostMarker({record}: {record: RunnerLostLogRecord}) {\n return (\n <LogMarkerRow\n icon=\"closeCircleLine\"\n tone=\"error\"\n timestamp={new Date(record.ts)}\n detail=\"the log ends here and may be incomplete\"\n terminalFailure\n >\n Runner disconnected\n </LogMarkerRow>\n );\n}\n"],"names":["Icon","LogContent","LogRow","cn","formatBytes","formatDuration","toneText","default","warning","error","LogMarkerRow","icon","tone","timestamp","detail","meta","terminalFailure","children","lineNumber","data-log-terminal-failure","undefined","className","span","name","aria-hidden","EndMarker","record","lineCount","durationMs","totalBytes","join","Date","ts","GapMarker","droppedBytes","CappedMarker","RunnerLostMarker"],"mappings":";AAAA,SAAQA,IAAI,QAAsB,yBAAyB;AAC3D,SAAQC,UAAU,EAAEC,MAAM,QAAO,wBAAwB;AACzD,SAAQC,EAAE,EAAEC,WAAW,EAAEC,cAAc,QAAO,0BAA0B;AAUxE,MAAMC,WAAuC;IAC3CC,SAAS;IACTC,SAAS;IACTC,OAAO;AACT;AAcA;;;;;;CAMC,GACD,SAASC,aAAa,EACpBC,IAAI,EACJC,IAAI,EACJC,YAAY,IAAI,EAChBC,MAAM,EACNC,IAAI,EACJC,kBAAkB,KAAK,EACvBC,QAAQ,EACU;IAClB,qBACE,KAACf;QACCgB,YAAY;QACZL,WAAWA;QACXD,MAAMA;QACNO,6BAA2BH,kBAAkB,SAASI;kBAEtD,cAAA,KAACnB;YAAWoB,WAAWlB,GAAG,SAASG,QAAQ,CAACM,KAAK;sBAC/C,cAAA,MAACU;gBAAKD,WAAU;;kCACd,KAACrB;wBAAKuB,MAAMZ;wBAAMU,WAAU;wBAAoBG,eAAY;;kCAK5D,MAACF;wBAAKD,WAAU;;0CACd,KAACC;gCAAKD,WAAU;0CAAeJ;;4BAC9BH,UAAU,sBACT;;oCACG;kDACD,KAACQ;wCAAKD,WAAU;kDAA0BP;;;;4BAG7CC,QAAQ,sBACP;;oCACG;kDACD,KAACO;wCAAKD,WAAU;kDAAqCN;;;;;;kCAI3D,KAACO;wBACCE,eAAY;wBACZH,WAAU;;;;;;AAMtB;AAQA,0GAA0G,GAC1G,OAAO,SAASI,UAAU,EAACC,MAAM,EAAEC,SAAS,EAAEC,aAAa,IAAI,EAAiB;IAC9E,MAAMb,OAAO;QACX,GAAGY,UAAU,CAAC,EAAEA,cAAc,IAAI,SAAS,SAAS;QACpDvB,YAAYsB,OAAOG,UAAU;WACzBD,cAAc,OAAO;YAACvB,eAAeuB;SAAY,GAAG,EAAE;KAC3D,CAACE,IAAI,CAAC;IAEP,qBACE,KAACpB;QAAaC,MAAK;QAAWC,MAAK;QAAUC,WAAW,IAAIkB,KAAKL,OAAOM,EAAE;QAAGjB,MAAMA;kBAAM;;AAI7F;AAEA,kGAAkG,GAClG,OAAO,SAASkB,UAAU,EAACP,MAAM,EAAyB;IACxD,qBACE,KAAChB;QACCC,MAAK;QACLC,MAAK;QACLC,WAAW,IAAIkB,KAAKL,OAAOM,EAAE;QAC7BlB,QAAQ,CAAC,mCAAmC,EAAEV,YAAYsB,OAAOQ,YAAY,GAAG;kBACjF;;AAIL;AAEA,iGAAiG,GACjG,OAAO,SAASC,aAAa,EAACT,MAAM,EAA4B;IAC9D,qBACE,KAAChB;QACCC,MAAK;QACLC,MAAK;QACLC,WAAW,IAAIkB,KAAKL,OAAOM,EAAE;QAC7BlB,QAAO;kBACR;;AAIL;AAEA,qGAAqG,GACrG,OAAO,SAASsB,iBAAiB,EAACV,MAAM,EAAgC;IACtE,qBACE,KAAChB;QACCC,MAAK;QACLC,MAAK;QACLC,WAAW,IAAIkB,KAAKL,OAAOM,EAAE;QAC7BlB,QAAO;QACPE,eAAe;kBAChB;;AAIL"}
1
+ {"version":3,"sources":["../../src/components/system-markers.tsx"],"sourcesContent":["import {Icon, type IconName} from '@shipfox/react-ui/icon';\nimport {LogContent, LogRow} from '@shipfox/react-ui/log';\nimport {cn, formatBytes, formatDuration} from '@shipfox/react-ui/utils';\nimport type {\n CappedLogRecord,\n EndLogRecord,\n GapLogRecord,\n RunnerLostLogRecord,\n} from '#core/log-tree.js';\n\ntype MarkerTone = 'default' | 'warning' | 'error';\n\nconst toneText: Record<MarkerTone, string> = {\n default: 'text-foreground-neutral-muted',\n warning: 'text-orange-600 dark:text-orange-400',\n error: 'text-red-600 dark:text-red-400',\n};\n\ninterface LogMarkerRowProps {\n icon: IconName;\n tone: MarkerTone;\n timestamp?: Date | null;\n /** Plain-language clause after the label: what it means / what to do. */\n detail?: string;\n /** Right-aligned `font-code` figures (bytes, line count, duration). */\n meta?: string;\n terminalFailure?: boolean;\n children: string;\n}\n\n/**\n * Shared timeline-marker row: a non-numbered line with a leading icon, a bold label, an\n * optional plain-language detail clause, a dashed divider, and optional right-aligned\n * monospace figures. The detail explains the consequence to the operator (the label\n * names the event, the icon/tone carry severity), so the copy reads helpfully rather\n * than mechanically.\n */\nfunction LogMarkerRow({\n icon,\n tone,\n timestamp = null,\n detail,\n meta,\n terminalFailure = false,\n children,\n}: LogMarkerRowProps) {\n return (\n <LogRow\n lineNumber={null}\n timestamp={timestamp}\n tone={tone}\n data-log-terminal-failure={terminalFailure ? 'true' : undefined}\n >\n <LogContent className={cn('block', toneText[tone])}>\n <span className=\"inline-flex w-full items-center gap-inline\">\n <Icon name={icon} className=\"size-14 flex-none\" aria-hidden=\"true\" />\n {/* Label, detail, and figures share one text cluster joined by a literal\n \" · \". Flex `gap` is visual only and would copy with no separator, so the\n separators are real inline text. The dashed rule trails after the text\n (an aria-hidden flex filler) and contributes nothing to a selection. */}\n <span className=\"min-w-0\">\n <span className=\"font-medium\">{children}</span>\n {detail != null && (\n <>\n {' · '}\n <span className=\"font-normal opacity-80\">{detail}</span>\n </>\n )}\n {meta != null && (\n <>\n {' · '}\n <span className=\"font-code tabular-nums opacity-80\">{meta}</span>\n </>\n )}\n </span>\n <span\n aria-hidden=\"true\"\n className=\"h-px flex-1 border-t border-dashed border-current opacity-30\"\n />\n </span>\n </LogContent>\n </LogRow>\n );\n}\n\nexport interface EndMarkerProps {\n record: EndLogRecord;\n lineCount: number;\n durationMs?: number | null;\n}\n\n/** Clean end of the log: line count + output bytes (+ overall duration). `total_bytes` is payload bytes. */\nexport function EndMarker({record, lineCount, durationMs = null}: EndMarkerProps) {\n const meta = [\n `${lineCount} ${lineCount === 1 ? 'line' : 'lines'}`,\n formatBytes(record.totalBytes),\n ...(durationMs != null ? [formatDuration(durationMs)] : []),\n ].join(' · ');\n\n return (\n <LogMarkerRow icon=\"flagLine\" tone=\"default\" timestamp={new Date(record.ts)} meta={meta}>\n End of log\n </LogMarkerRow>\n );\n}\n\n/** The runner's local backlog shed bytes before upload, so some output never arrived. A warning. */\nexport function GapMarker({record}: {record: GapLogRecord}) {\n return (\n <LogMarkerRow\n icon=\"errorWarningLine\"\n tone=\"warning\"\n timestamp={new Date(record.ts)}\n detail={`the runner fell behind and dropped ${formatBytes(record.droppedBytes)}`}\n >\n Output missing\n </LogMarkerRow>\n );\n}\n\n/** The job hit its shared log size limit; logging stopped but the step kept running. A warning. */\nexport function CappedMarker({record}: {record: CappedLogRecord}) {\n return (\n <LogMarkerRow\n icon=\"forbidLine\"\n tone=\"warning\"\n timestamp={new Date(record.ts)}\n detail=\"later output isn't shown; the step kept running\"\n >\n Log size limit reached\n </LogMarkerRow>\n );\n}\n\n/** The runner disappeared and the stream was force-closed. A terminal failure (status taxonomy §9). */\nexport function RunnerLostMarker({record}: {record: RunnerLostLogRecord}) {\n return (\n <LogMarkerRow\n icon=\"closeCircleLine\"\n tone=\"error\"\n timestamp={new Date(record.ts)}\n detail=\"the log ends here and may be incomplete\"\n terminalFailure\n >\n Runner disconnected\n </LogMarkerRow>\n );\n}\n"],"names":["Icon","LogContent","LogRow","cn","formatBytes","formatDuration","toneText","default","warning","error","LogMarkerRow","icon","tone","timestamp","detail","meta","terminalFailure","children","lineNumber","data-log-terminal-failure","undefined","className","span","name","aria-hidden","EndMarker","record","lineCount","durationMs","totalBytes","join","Date","ts","GapMarker","droppedBytes","CappedMarker","RunnerLostMarker"],"mappings":";AAAA,SAAQA,IAAI,QAAsB,yBAAyB;AAC3D,SAAQC,UAAU,EAAEC,MAAM,QAAO,wBAAwB;AACzD,SAAQC,EAAE,EAAEC,WAAW,EAAEC,cAAc,QAAO,0BAA0B;AAUxE,MAAMC,WAAuC;IAC3CC,SAAS;IACTC,SAAS;IACTC,OAAO;AACT;AAcA;;;;;;CAMC,GACD,SAASC,aAAa,EACpBC,IAAI,EACJC,IAAI,EACJC,YAAY,IAAI,EAChBC,MAAM,EACNC,IAAI,EACJC,kBAAkB,KAAK,EACvBC,QAAQ,EACU;IAClB,qBACE,KAACf;QACCgB,YAAY;QACZL,WAAWA;QACXD,MAAMA;QACNO,6BAA2BH,kBAAkB,SAASI;kBAEtD,cAAA,KAACnB;YAAWoB,WAAWlB,GAAG,SAASG,QAAQ,CAACM,KAAK;sBAC/C,cAAA,MAACU;gBAAKD,WAAU;;kCACd,KAACrB;wBAAKuB,MAAMZ;wBAAMU,WAAU;wBAAoBG,eAAY;;kCAK5D,MAACF;wBAAKD,WAAU;;0CACd,KAACC;gCAAKD,WAAU;0CAAeJ;;4BAC9BH,UAAU,sBACT;;oCACG;kDACD,KAACQ;wCAAKD,WAAU;kDAA0BP;;;;4BAG7CC,QAAQ,sBACP;;oCACG;kDACD,KAACO;wCAAKD,WAAU;kDAAqCN;;;;;;kCAI3D,KAACO;wBACCE,eAAY;wBACZH,WAAU;;;;;;AAMtB;AAQA,0GAA0G,GAC1G,OAAO,SAASI,UAAU,EAACC,MAAM,EAAEC,SAAS,EAAEC,aAAa,IAAI,EAAiB;IAC9E,MAAMb,OAAO;QACX,GAAGY,UAAU,CAAC,EAAEA,cAAc,IAAI,SAAS,SAAS;QACpDvB,YAAYsB,OAAOG,UAAU;WACzBD,cAAc,OAAO;YAACvB,eAAeuB;SAAY,GAAG,EAAE;KAC3D,CAACE,IAAI,CAAC;IAEP,qBACE,KAACpB;QAAaC,MAAK;QAAWC,MAAK;QAAUC,WAAW,IAAIkB,KAAKL,OAAOM,EAAE;QAAGjB,MAAMA;kBAAM;;AAI7F;AAEA,kGAAkG,GAClG,OAAO,SAASkB,UAAU,EAACP,MAAM,EAAyB;IACxD,qBACE,KAAChB;QACCC,MAAK;QACLC,MAAK;QACLC,WAAW,IAAIkB,KAAKL,OAAOM,EAAE;QAC7BlB,QAAQ,CAAC,mCAAmC,EAAEV,YAAYsB,OAAOQ,YAAY,GAAG;kBACjF;;AAIL;AAEA,iGAAiG,GACjG,OAAO,SAASC,aAAa,EAACT,MAAM,EAA4B;IAC9D,qBACE,KAAChB;QACCC,MAAK;QACLC,MAAK;QACLC,WAAW,IAAIkB,KAAKL,OAAOM,EAAE;QAC7BlB,QAAO;kBACR;;AAIL;AAEA,qGAAqG,GACrG,OAAO,SAASsB,iBAAiB,EAACV,MAAM,EAAgC;IACtE,qBACE,KAAChB;QACCC,MAAK;QACLC,MAAK;QACLC,WAAW,IAAIkB,KAAKL,OAAOM,EAAE;QAC7BlB,QAAO;QACPE,eAAe;kBAChB;;AAIL"}
@@ -1,6 +1,6 @@
1
1
  import { type StepLogSnapshot } from '#core/log-read.js';
2
2
  export declare const stepLogsQueryKeys: {
3
- all: readonly ["step-logs"];
3
+ all: readonly ['step-logs'];
4
4
  detail: (stepId: string, attempt: number) => readonly ["step-logs", "detail", string, number];
5
5
  };
6
6
  interface ReadStepAttemptLogsPageParams {
@@ -1 +1 @@
1
- {"version":3,"file":"step-logs.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/step-logs.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,eAAe,EAErB,MAAM,mBAAmB,CAAC;AAG3B,eAAO,MAAM,iBAAiB;;qBAEX,MAAM,WAAW,MAAM;CAEzC,CAAC;AAEF,UAAU,6BAA6B;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,wBAAsB,uBAAuB,CAAC,EAC5C,MAAM,EACN,OAAO,EACP,MAAM,EACN,MAAM,GACP,EAAE,6BAA6B,iHAQ/B;AAkBD,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEnE;AAED,MAAM,WAAW,8BAA8B;IAC7C,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,uBAAuB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,yBAAyB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/C,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,GAAE,8BAAmC,mFA2F7C"}
1
+ {"version":3,"file":"step-logs.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/step-logs.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,eAAe,EAErB,MAAM,mBAAmB,CAAC;AAG3B,eAAO,MAAM,iBAAiB;IAC5B,GAAG,YAAG,WAAW;IACjB,MAAM,WAAW,MAAM,WAAW,MAAM;CAEzC,CAAC;AAEF,UAAU,6BAA6B;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,wBAAsB,uBAAuB,CAAC,EAC5C,MAAM,EACN,OAAO,EACP,MAAM,EACN,MAAM,GACP,EAAE,6BAA6B,iHAQ/B;AAkBD,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEnE;AAED,MAAM,WAAW,8BAA8B;IAC7C,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,uBAAuB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,yBAAyB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/C,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,GAAE,8BAAmC,mFA2F7C"}