@shipfox/client-workflows 17.0.0 → 19.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 (59) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-type.log +0 -1
  3. package/CHANGELOG.md +23 -0
  4. package/dist/components/job-detail/job-context-panel.d.ts.map +1 -1
  5. package/dist/components/job-detail/job-context-panel.js +5 -1
  6. package/dist/components/job-detail/job-context-panel.js.map +1 -1
  7. package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
  8. package/dist/components/job-detail/job-detail-view.js +34 -27
  9. package/dist/components/job-detail/job-detail-view.js.map +1 -1
  10. package/dist/components/job-detail/job-empty-states.d.ts +4 -0
  11. package/dist/components/job-detail/job-empty-states.d.ts.map +1 -1
  12. package/dist/components/job-detail/job-empty-states.js +43 -3
  13. package/dist/components/job-detail/job-empty-states.js.map +1 -1
  14. package/dist/components/job-detail/step-troubleshooting.js +4 -0
  15. package/dist/components/job-detail/step-troubleshooting.js.map +1 -1
  16. package/dist/components/step-list/step-list-model.d.ts.map +1 -1
  17. package/dist/components/step-list/step-list-model.js +1 -0
  18. package/dist/components/step-list/step-list-model.js.map +1 -1
  19. package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -1
  20. package/dist/components/workflow-run-list/job-status-strip.js +20 -7
  21. package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
  22. package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
  23. package/dist/components/workflow-run-view/run-workspace-nav.js +6 -5
  24. package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
  25. package/dist/core/entities/job-execution.d.ts +2 -0
  26. package/dist/core/entities/job-execution.d.ts.map +1 -1
  27. package/dist/core/entities/job-execution.js.map +1 -1
  28. package/dist/core/entities/job.d.ts +6 -3
  29. package/dist/core/entities/job.d.ts.map +1 -1
  30. package/dist/core/entities/job.js +8 -1
  31. package/dist/core/entities/job.js.map +1 -1
  32. package/dist/core/entities/workflow-run.d.ts +7 -4
  33. package/dist/core/entities/workflow-run.d.ts.map +1 -1
  34. package/dist/core/entities/workflow-run.js.map +1 -1
  35. package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
  36. package/dist/hooks/api/workflow-run-mapper.js +12 -1
  37. package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
  38. package/dist/tsconfig.test.tsbuildinfo +1 -1
  39. package/package.json +2 -2
  40. package/src/components/job-detail/job-context-panel.tsx +4 -0
  41. package/src/components/job-detail/job-detail-view.tsx +37 -33
  42. package/src/components/job-detail/job-empty-states.test.ts +105 -1
  43. package/src/components/job-detail/job-empty-states.tsx +56 -2
  44. package/src/components/job-detail/step-troubleshooting.tsx +4 -0
  45. package/src/components/job-graph/job-node.test.tsx +2 -2
  46. package/src/components/step-list/step-list-model.ts +1 -0
  47. package/src/components/workflow-run-list/job-status-strip.tsx +25 -12
  48. package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +73 -0
  49. package/src/components/workflow-run-list/workflow-run-list.stories.tsx +31 -0
  50. package/src/components/workflow-run-view/run-workspace-nav.tsx +13 -6
  51. package/src/core/entities/job-execution.ts +2 -0
  52. package/src/core/entities/job-status.test.ts +47 -1
  53. package/src/core/entities/job.ts +18 -4
  54. package/src/core/entities/workflow-run-display.test.ts +28 -0
  55. package/src/core/entities/workflow-run.ts +18 -4
  56. package/src/core/workflow-run.test.ts +13 -0
  57. package/src/hooks/api/workflow-run-mapper.ts +19 -1
  58. package/test/fixtures/workflow-run.ts +32 -12
  59. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/job-detail/step-troubleshooting.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {Callout, CalloutContent, CalloutDescription, CalloutTitle} from '@shipfox/react-ui/callout';\nimport {\n Sheet,\n SheetBody,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n} from '@shipfox/react-ui/sheet';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport type {ReactNode} from 'react';\nimport type {\n EvaluationTraceEntry,\n JobStatusReason,\n Step,\n StepAttempt,\n StepError,\n} from '#core/workflow-run.js';\nimport {useStepAttemptDetailQuery} from '#hooks/api/step-attempt-detail.js';\nimport {workflowRunSearchParams} from '#routes/inputs.js';\nimport {humanizeStatus, type StepListEntryModel} from '../step-list/step-list-model.js';\nimport {AgentConfigFailureCallout} from './agent-config-failure-callout.js';\nimport {toSelectedAttemptError} from './job-empty-states.js';\nimport {JsonCode, type JsonCodeEntry, JsonCodeTabs} from './json-code.js';\n\nexport interface StepInspectorSheetProps {\n entry: StepListEntryModel;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n runAttempt: number;\n jobId: string;\n annotationCount?: number | undefined;\n}\n\nexport function StepInspectorSheet({\n entry,\n open,\n onOpenChange,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n runAttempt,\n jobId,\n annotationCount,\n}: StepInspectorSheetProps) {\n const error = selectedStepError(entry.step, entry.error);\n const inspectorQuery = useStepAttemptDetailQuery(entry.step.id, entry.attempt, {\n enabled: open,\n });\n\n return (\n <Sheet open={open} onOpenChange={onOpenChange}>\n <SheetContent side=\"right\" className=\"w-full sm:max-w-[560px]\">\n <SheetHeader>\n <SheetTitle>{entry.step.label}</SheetTitle>\n <SheetDescription>\n Attempt #{entry.attempt} · {humanizeStatus(entry.statusVisual.kind)}\n </SheetDescription>\n </SheetHeader>\n <SheetBody className=\"gap-section\">\n <StepInspector\n step={entry.step}\n attempt={entry}\n error={error}\n showFailure={entry.statusVisual.kind === 'failed' || entry.error !== null}\n query={inspectorQuery}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n runAttempt={runAttempt}\n jobId={jobId}\n annotationCount={annotationCount}\n />\n </SheetBody>\n </SheetContent>\n </Sheet>\n );\n}\n\nfunction StepFailureCallout({\n step,\n attempt,\n error,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n runAttempt,\n}: {\n step: Step;\n attempt: StepAttempt;\n error: StepError | null;\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n runAttempt: number;\n}) {\n const reason = error?.reason ?? step.statusReason ?? 'unknown';\n const title = failureTitle(reason);\n const sourceLink = sourceLinkForFailure(reason) && step.sourceLocation;\n\n if (step.type === 'agent' && reason === 'agent_config_invalid') {\n return (\n <AgentConfigFailureCallout\n workspaceSlug={workspaceSlug}\n config={step.agentConfig}\n error={error}\n />\n );\n }\n\n return (\n <Callout\n role=\"alert\"\n type=\"error\"\n variant=\"secondary\"\n className=\"rounded-8 border border-tag-error-border p-panel-compact shadow-none\"\n >\n <CalloutContent>\n <CalloutTitle>{title}</CalloutTitle>\n <CalloutDescription>\n <div className=\"flex min-w-0 flex-wrap items-center gap-x-inline gap-y-tight\">\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <span>{failureDescription(reason)}</span>\n {error?.message ? (\n <span className=\"text-foreground-neutral-muted\">{error.message}</span>\n ) : null}\n </div>\n <Code as=\"span\" variant=\"label\" className=\"text-tag-error-text\">\n {reason}\n </Code>\n {sourceLink ? (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {tab: 'source'},\n {stepId: step.id, stepAttemptId: attempt.id, runAttempt},\n ) as never\n }\n className=\"font-medium text-foreground-highlight-interactive underline-offset-2 hover:underline\"\n >\n View in source\n </Link>\n ) : null}\n </div>\n </CalloutDescription>\n </CalloutContent>\n </Callout>\n );\n}\n\nfunction StepInspector({\n step,\n attempt,\n error,\n showFailure,\n query,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n runAttempt,\n jobId,\n annotationCount,\n}: {\n step: Step;\n attempt: StepAttempt;\n error: StepError | null;\n showFailure: boolean;\n query: ReturnType<typeof useStepAttemptDetailQuery>;\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n runAttempt: number;\n jobId: string;\n annotationCount: number | undefined;\n}) {\n const detail = query.data;\n const trace = detail?.evaluationTrace ?? null;\n const resolvedConfig = detail?.config ?? null;\n const hasInputs =\n detail !== undefined &&\n (countConfigValues(detail.authoredConfig) > 0 || countConfigValues(resolvedConfig) > 0);\n const hasOutputs =\n attempt.outputs !== null || attempt.output !== null || attempt.response !== null;\n const hasTrace = trace !== null && trace.length > 0;\n const hasAnnotations = annotationCount !== undefined && annotationCount > 0;\n const detailCount = Number(hasInputs) + Number(hasOutputs) + Number(hasTrace);\n\n return (\n <div className=\"flex min-w-0 flex-col gap-section\">\n {showFailure ? (\n <StepFailureCallout\n step={step}\n attempt={attempt}\n error={error}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n runAttempt={runAttempt}\n />\n ) : null}\n {query.isPending ? <InspectorLoading /> : null}\n {query.isError ? (\n <Callout\n role=\"alert\"\n type=\"warning\"\n variant=\"secondary\"\n className=\"rounded-8 border border-tag-warning-border p-panel-compact shadow-none\"\n >\n <CalloutContent>\n <CalloutTitle>Details unavailable</CalloutTitle>\n <CalloutDescription className=\"flex items-center justify-between gap-inline\">\n <span>We could not load the resolved configuration for this attempt.</span>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n onClick={() => void query.refetch()}\n >\n Retry\n </Button>\n </CalloutDescription>\n </CalloutContent>\n </Callout>\n ) : null}\n {detail ? (\n <div className=\"flex min-w-0 flex-col gap-group\">\n {hasInputs ? (\n <InspectorSection title=\"Inputs\">\n <ConfigCode authoredConfig={detail.authoredConfig} resolvedConfig={resolvedConfig} />\n </InspectorSection>\n ) : null}\n {hasOutputs ? (\n <InspectorSection title=\"Outputs\">\n {attempt.outputs !== null || attempt.output !== null ? (\n <JsonCode\n value={attempt.outputs ?? attempt.output ?? {}}\n emptyMessage=\"No outputs declared; the `outputs:` mapping is empty.\"\n />\n ) : null}\n {attempt.response !== null ? (\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n Response\n </Text>\n <pre className=\"max-h-160 min-w-0 overflow-auto rounded-6 border border-border-neutral-base bg-background-neutral-subtle p-tight font-code text-xs leading-18 text-foreground-neutral-muted scrollbar\">\n {attempt.response}\n </pre>\n </div>\n ) : null}\n </InspectorSection>\n ) : null}\n {hasTrace ? (\n <InspectorSection title=\"Evaluation\">\n <EvaluationTrace trace={trace ?? []} />\n </InspectorSection>\n ) : null}\n {detailCount === 0 && !showFailure && !hasAnnotations ? <EmptyInspector /> : null}\n </div>\n ) : null}\n {hasAnnotations ? (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={workflowRunSearchParams({tab: 'annotations'}, {jobId, runAttempt}) as never}\n className=\"inline-flex w-fit rounded-4 text-xs text-foreground-highlight-interactive underline-offset-2 hover:underline focus-visible:shadow-button-neutral-focus\"\n >\n View {annotationCount} annotation{annotationCount === 1 ? '' : 's'}\n </Link>\n ) : null}\n {!query.isPending && !query.isError && !detail && !showFailure && !hasAnnotations ? (\n <EmptyInspector />\n ) : null}\n </div>\n );\n}\n\nfunction InspectorSection({title, children}: {title: string; children: ReactNode}) {\n return (\n <section className=\"flex min-w-0 flex-col gap-inline\" aria-label={title}>\n <Text size=\"xs\" bold className=\"text-foreground-neutral-base\">\n {title}\n </Text>\n {children}\n </section>\n );\n}\n\nfunction ConfigCode({\n authoredConfig,\n resolvedConfig,\n}: {\n authoredConfig: Record<string, unknown> | null;\n resolvedConfig: Record<string, unknown> | null;\n}) {\n const entries: JsonCodeEntry[] = [\n ...(authoredConfig\n ? [\n {\n filename: 'authored.json',\n label: 'Authored configuration',\n value: authoredConfig,\n },\n ]\n : []),\n ...(resolvedConfig\n ? [\n {\n filename: 'resolved.json',\n label: 'Resolved configuration',\n value: resolvedConfig,\n },\n ]\n : []),\n ];\n\n if (entries.length === 0) return null;\n return <JsonCodeTabs entries={entries} />;\n}\n\nexport function EvaluationTrace({trace}: {trace: readonly EvaluationTraceEntry[]}) {\n const keyCounts = new Map<string, number>();\n\n return (\n <dl className=\"flex min-w-0 flex-col divide-y divide-border-neutral-base rounded-6 border border-border-neutral-base\">\n {trace.map((entry) => {\n if ('dropped' in entry) {\n const keyBase = `limit-${entry.dropped}`;\n const occurrence = keyCounts.get(keyBase) ?? 0;\n keyCounts.set(keyBase, occurrence + 1);\n return (\n <div\n key={`${keyBase}-${occurrence}`}\n className=\"px-row py-row text-xs text-foreground-neutral-muted\"\n >\n {entry.dropped} more evaluation{entry.dropped === 1 ? '' : 's'} not recorded\n </div>\n );\n }\n\n const empty = entry.value === undefined || entry.value === '';\n const keyBase = `evaluation-${entry.field}-${entry.expression}-${entry.evaluatedAt}-${entry.fillTarget}`;\n const occurrence = keyCounts.get(keyBase) ?? 0;\n keyCounts.set(keyBase, occurrence + 1);\n return (\n <div\n key={`${keyBase}-${occurrence}`}\n className={cn(\n 'grid min-w-0 grid-cols-1 gap-inline px-row py-row min-[768px]:grid-cols-[160px_minmax(0,1fr)]',\n entry.degraded && 'border-l border-tag-error-icon',\n )}\n >\n <dt\n className=\"flex min-w-0 flex-col gap-tight font-code text-xs text-foreground-neutral-muted\"\n title={entry.field}\n >\n <span className=\"block truncate\">{entry.field}</span>\n <span className=\"block break-all text-foreground-neutral-subtle\">\n {entry.expression}\n </span>\n </dt>\n <dd className=\"flex min-w-0 flex-col gap-tight text-xs text-foreground-neutral-base\">\n {entry.degraded ? <span className=\"sr-only\">Degraded evaluation</span> : null}\n <div className=\"break-words font-code\">\n {empty ? <span className=\"text-tag-error-text\">(empty)</span> : entry.value}\n </div>\n <div className=\"flex min-w-0 flex-wrap gap-x-inline gap-y-tight text-foreground-neutral-muted\">\n {entry.degraded ? <span className=\"text-tag-error-text\">degraded</span> : null}\n {entry.truncated || entry.exprTruncated ? <span>truncated</span> : null}\n </div>\n </dd>\n </div>\n );\n })}\n </dl>\n );\n}\n\nfunction InspectorLoading() {\n return (\n <div\n role=\"status\"\n aria-label=\"Loading troubleshooting details\"\n className=\"flex flex-col gap-inline\"\n >\n <Skeleton className=\"h-16 w-120\" />\n <Skeleton className=\"h-120 w-full\" />\n </div>\n );\n}\n\nfunction EmptyInspector() {\n return (\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n No additional troubleshooting details were recorded.\n </Text>\n );\n}\n\nfunction selectedStepError(\n step: Step,\n attemptError: Record<string, unknown> | null,\n): StepError | null {\n return toSelectedAttemptError(step, attemptError) ?? step.error;\n}\n\nfunction failureTitle(reason: string | JobStatusReason): string {\n switch (reason) {\n case 'checkout_failed':\n return 'Checkout failed';\n case 'checkout_auth_failed':\n return 'Checkout authentication failed';\n case 'checkout_unavailable':\n return 'Checkout service unavailable';\n case 'checkout_path_invalid':\n return 'Checkout path is invalid';\n case 'checkout_destination_occupied':\n return 'Checkout destination is already occupied';\n case 'git_unavailable':\n return 'Git was unavailable';\n case 'workspace_prep_failed':\n return 'Workspace preparation failed';\n case 'setup_aborted':\n return 'Step setup was aborted';\n case 'config_unresolvable':\n return 'Step configuration could not be resolved';\n case 'output_invalid':\n return 'Step output was invalid';\n case 'agent_config_invalid':\n return 'Agent configuration is invalid';\n case 'agent_invocation_failed':\n return 'Agent invocation failed';\n case 'agent_harness_unavailable':\n return 'Agent harness was unavailable';\n case 'runner_lost':\n return 'Runner stopped responding';\n case 'timed_out':\n return 'Step timed out';\n case 'step_failed':\n return 'A step failed';\n case 'dependency_not_completed':\n return 'A dependency did not complete';\n case 'condition_false':\n return 'The job condition was false';\n case 'default_gate_rejected':\n return 'The default gate rejected this job';\n case 'condition_rejected':\n return 'The job condition rejected this job';\n case 'condition_errored':\n return 'The job condition could not be evaluated';\n case 'user_cancelled':\n return 'The job was cancelled by a user';\n case 'run_cancelled':\n return 'The run was cancelled';\n case 'unknown':\n return 'The failure reason was not recorded';\n default:\n return 'Step failed';\n }\n}\n\nfunction failureDescription(reason: string | JobStatusReason): string {\n switch (reason) {\n case 'checkout_auth_failed':\n return 'Checkout credentials were rejected. Verify repository access before re-running.';\n case 'checkout_unavailable':\n return 'The checkout service was unavailable. Retry after the service recovers.';\n case 'git_unavailable':\n return 'The runner could not start Git. Check the runner image before re-running.';\n case 'workspace_prep_failed':\n return 'The runner could not prepare its workspace. Review the runner setup details.';\n case 'config_unresolvable':\n return 'The resolved configuration contains a value that could not be evaluated.';\n case 'output_invalid':\n return 'The step returned output that did not match the declared contract.';\n case 'agent_config_invalid':\n return 'The agent configuration is not valid for this step.';\n case 'agent_invocation_failed':\n return 'The agent invocation failed after configuration was accepted.';\n case 'agent_harness_unavailable':\n return 'The runner could not start the agent harness.';\n case 'runner_lost':\n return 'The runner stopped responding before the step completed.';\n case 'timed_out':\n return 'The step exceeded its configured time limit.';\n case 'dependency_not_completed':\n return 'A required job did not complete, so this job could not start.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not allow this job to run.';\n case 'condition_errored':\n return 'The job condition could not be evaluated.';\n case 'default_gate_rejected':\n return 'A required job did not succeed, so this job was not allowed to run.';\n case 'step_failed':\n return 'A step failed before this job could complete.';\n case 'user_cancelled':\n case 'run_cancelled':\n return 'The run was cancelled before this work completed.';\n case 'unknown':\n return 'No machine-readable failure reason was recorded.';\n default:\n return `${humanize(reason)}. Review the details below and re-run after resolving the cause.`;\n }\n}\n\nfunction sourceLinkForFailure(reason: string | JobStatusReason): boolean {\n return (\n reason === 'config_unresolvable' ||\n reason === 'agent_config_invalid' ||\n reason === 'output_invalid' ||\n reason === 'default_gate_rejected' ||\n reason === 'condition_rejected' ||\n reason === 'condition_errored'\n );\n}\n\nfunction humanize(value: string): string {\n return value.replaceAll('_', ' ').replace(/\\b\\w/g, (letter) => letter.toUpperCase());\n}\n\nfunction countConfigValues(value: unknown): number {\n if (Array.isArray(value))\n return value.reduce((total, item) => total + countConfigValues(item), 0);\n if (value !== null && typeof value === 'object') {\n return Object.values(value).reduce((total, item) => total + countConfigValues(item), 0);\n }\n return value === undefined ? 0 : 1;\n}\n"],"names":["Button","Callout","CalloutContent","CalloutDescription","CalloutTitle","Sheet","SheetBody","SheetContent","SheetDescription","SheetHeader","SheetTitle","Skeleton","Code","Text","cn","Link","useStepAttemptDetailQuery","workflowRunSearchParams","humanizeStatus","AgentConfigFailureCallout","toSelectedAttemptError","JsonCode","JsonCodeTabs","StepInspectorSheet","entry","open","onOpenChange","workspaceSlug","projectSlug","workflowRunId","runAttempt","jobId","annotationCount","error","selectedStepError","step","inspectorQuery","id","attempt","enabled","side","className","label","statusVisual","kind","StepInspector","showFailure","query","StepFailureCallout","reason","statusReason","title","failureTitle","sourceLink","sourceLinkForFailure","sourceLocation","type","config","agentConfig","role","variant","div","span","failureDescription","message","as","to","params","search","tab","stepId","stepAttemptId","detail","data","trace","evaluationTrace","resolvedConfig","hasInputs","undefined","countConfigValues","authoredConfig","hasOutputs","outputs","output","response","hasTrace","length","hasAnnotations","detailCount","Number","isPending","InspectorLoading","isError","size","onClick","refetch","InspectorSection","ConfigCode","value","emptyMessage","pre","EvaluationTrace","EmptyInspector","children","section","aria-label","bold","entries","filename","keyCounts","Map","dl","map","keyBase","dropped","occurrence","get","set","empty","field","expression","evaluatedAt","fillTarget","degraded","dt","dd","truncated","exprTruncated","attemptError","humanize","replaceAll","replace","letter","toUpperCase","Array","isArray","reduce","total","item","Object","values"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/job-detail/step-troubleshooting.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {Callout, CalloutContent, CalloutDescription, CalloutTitle} from '@shipfox/react-ui/callout';\nimport {\n Sheet,\n SheetBody,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n} from '@shipfox/react-ui/sheet';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport type {ReactNode} from 'react';\nimport type {\n EvaluationTraceEntry,\n JobStatusReason,\n Step,\n StepAttempt,\n StepError,\n} from '#core/workflow-run.js';\nimport {useStepAttemptDetailQuery} from '#hooks/api/step-attempt-detail.js';\nimport {workflowRunSearchParams} from '#routes/inputs.js';\nimport {humanizeStatus, type StepListEntryModel} from '../step-list/step-list-model.js';\nimport {AgentConfigFailureCallout} from './agent-config-failure-callout.js';\nimport {toSelectedAttemptError} from './job-empty-states.js';\nimport {JsonCode, type JsonCodeEntry, JsonCodeTabs} from './json-code.js';\n\nexport interface StepInspectorSheetProps {\n entry: StepListEntryModel;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n runAttempt: number;\n jobId: string;\n annotationCount?: number | undefined;\n}\n\nexport function StepInspectorSheet({\n entry,\n open,\n onOpenChange,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n runAttempt,\n jobId,\n annotationCount,\n}: StepInspectorSheetProps) {\n const error = selectedStepError(entry.step, entry.error);\n const inspectorQuery = useStepAttemptDetailQuery(entry.step.id, entry.attempt, {\n enabled: open,\n });\n\n return (\n <Sheet open={open} onOpenChange={onOpenChange}>\n <SheetContent side=\"right\" className=\"w-full sm:max-w-[560px]\">\n <SheetHeader>\n <SheetTitle>{entry.step.label}</SheetTitle>\n <SheetDescription>\n Attempt #{entry.attempt} · {humanizeStatus(entry.statusVisual.kind)}\n </SheetDescription>\n </SheetHeader>\n <SheetBody className=\"gap-section\">\n <StepInspector\n step={entry.step}\n attempt={entry}\n error={error}\n showFailure={entry.statusVisual.kind === 'failed' || entry.error !== null}\n query={inspectorQuery}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n runAttempt={runAttempt}\n jobId={jobId}\n annotationCount={annotationCount}\n />\n </SheetBody>\n </SheetContent>\n </Sheet>\n );\n}\n\nfunction StepFailureCallout({\n step,\n attempt,\n error,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n runAttempt,\n}: {\n step: Step;\n attempt: StepAttempt;\n error: StepError | null;\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n runAttempt: number;\n}) {\n const reason = error?.reason ?? step.statusReason ?? 'unknown';\n const title = failureTitle(reason);\n const sourceLink = sourceLinkForFailure(reason) && step.sourceLocation;\n\n if (step.type === 'agent' && reason === 'agent_config_invalid') {\n return (\n <AgentConfigFailureCallout\n workspaceSlug={workspaceSlug}\n config={step.agentConfig}\n error={error}\n />\n );\n }\n\n return (\n <Callout\n role=\"alert\"\n type=\"error\"\n variant=\"secondary\"\n className=\"rounded-8 border border-tag-error-border p-panel-compact shadow-none\"\n >\n <CalloutContent>\n <CalloutTitle>{title}</CalloutTitle>\n <CalloutDescription>\n <div className=\"flex min-w-0 flex-wrap items-center gap-x-inline gap-y-tight\">\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <span>{failureDescription(reason)}</span>\n {error?.message ? (\n <span className=\"text-foreground-neutral-muted\">{error.message}</span>\n ) : null}\n </div>\n <Code as=\"span\" variant=\"label\" className=\"text-tag-error-text\">\n {reason}\n </Code>\n {sourceLink ? (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {tab: 'source'},\n {stepId: step.id, stepAttemptId: attempt.id, runAttempt},\n ) as never\n }\n className=\"font-medium text-foreground-highlight-interactive underline-offset-2 hover:underline\"\n >\n View in source\n </Link>\n ) : null}\n </div>\n </CalloutDescription>\n </CalloutContent>\n </Callout>\n );\n}\n\nfunction StepInspector({\n step,\n attempt,\n error,\n showFailure,\n query,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n runAttempt,\n jobId,\n annotationCount,\n}: {\n step: Step;\n attempt: StepAttempt;\n error: StepError | null;\n showFailure: boolean;\n query: ReturnType<typeof useStepAttemptDetailQuery>;\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n runAttempt: number;\n jobId: string;\n annotationCount: number | undefined;\n}) {\n const detail = query.data;\n const trace = detail?.evaluationTrace ?? null;\n const resolvedConfig = detail?.config ?? null;\n const hasInputs =\n detail !== undefined &&\n (countConfigValues(detail.authoredConfig) > 0 || countConfigValues(resolvedConfig) > 0);\n const hasOutputs =\n attempt.outputs !== null || attempt.output !== null || attempt.response !== null;\n const hasTrace = trace !== null && trace.length > 0;\n const hasAnnotations = annotationCount !== undefined && annotationCount > 0;\n const detailCount = Number(hasInputs) + Number(hasOutputs) + Number(hasTrace);\n\n return (\n <div className=\"flex min-w-0 flex-col gap-section\">\n {showFailure ? (\n <StepFailureCallout\n step={step}\n attempt={attempt}\n error={error}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n runAttempt={runAttempt}\n />\n ) : null}\n {query.isPending ? <InspectorLoading /> : null}\n {query.isError ? (\n <Callout\n role=\"alert\"\n type=\"warning\"\n variant=\"secondary\"\n className=\"rounded-8 border border-tag-warning-border p-panel-compact shadow-none\"\n >\n <CalloutContent>\n <CalloutTitle>Details unavailable</CalloutTitle>\n <CalloutDescription className=\"flex items-center justify-between gap-inline\">\n <span>We could not load the resolved configuration for this attempt.</span>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n onClick={() => void query.refetch()}\n >\n Retry\n </Button>\n </CalloutDescription>\n </CalloutContent>\n </Callout>\n ) : null}\n {detail ? (\n <div className=\"flex min-w-0 flex-col gap-group\">\n {hasInputs ? (\n <InspectorSection title=\"Inputs\">\n <ConfigCode authoredConfig={detail.authoredConfig} resolvedConfig={resolvedConfig} />\n </InspectorSection>\n ) : null}\n {hasOutputs ? (\n <InspectorSection title=\"Outputs\">\n {attempt.outputs !== null || attempt.output !== null ? (\n <JsonCode\n value={attempt.outputs ?? attempt.output ?? {}}\n emptyMessage=\"No outputs declared; the `outputs:` mapping is empty.\"\n />\n ) : null}\n {attempt.response !== null ? (\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n Response\n </Text>\n <pre className=\"max-h-160 min-w-0 overflow-auto rounded-6 border border-border-neutral-base bg-background-neutral-subtle p-tight font-code text-xs leading-18 text-foreground-neutral-muted scrollbar\">\n {attempt.response}\n </pre>\n </div>\n ) : null}\n </InspectorSection>\n ) : null}\n {hasTrace ? (\n <InspectorSection title=\"Evaluation\">\n <EvaluationTrace trace={trace ?? []} />\n </InspectorSection>\n ) : null}\n {detailCount === 0 && !showFailure && !hasAnnotations ? <EmptyInspector /> : null}\n </div>\n ) : null}\n {hasAnnotations ? (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={workflowRunSearchParams({tab: 'annotations'}, {jobId, runAttempt}) as never}\n className=\"inline-flex w-fit rounded-4 text-xs text-foreground-highlight-interactive underline-offset-2 hover:underline focus-visible:shadow-button-neutral-focus\"\n >\n View {annotationCount} annotation{annotationCount === 1 ? '' : 's'}\n </Link>\n ) : null}\n {!query.isPending && !query.isError && !detail && !showFailure && !hasAnnotations ? (\n <EmptyInspector />\n ) : null}\n </div>\n );\n}\n\nfunction InspectorSection({title, children}: {title: string; children: ReactNode}) {\n return (\n <section className=\"flex min-w-0 flex-col gap-inline\" aria-label={title}>\n <Text size=\"xs\" bold className=\"text-foreground-neutral-base\">\n {title}\n </Text>\n {children}\n </section>\n );\n}\n\nfunction ConfigCode({\n authoredConfig,\n resolvedConfig,\n}: {\n authoredConfig: Record<string, unknown> | null;\n resolvedConfig: Record<string, unknown> | null;\n}) {\n const entries: JsonCodeEntry[] = [\n ...(authoredConfig\n ? [\n {\n filename: 'authored.json',\n label: 'Authored configuration',\n value: authoredConfig,\n },\n ]\n : []),\n ...(resolvedConfig\n ? [\n {\n filename: 'resolved.json',\n label: 'Resolved configuration',\n value: resolvedConfig,\n },\n ]\n : []),\n ];\n\n if (entries.length === 0) return null;\n return <JsonCodeTabs entries={entries} />;\n}\n\nexport function EvaluationTrace({trace}: {trace: readonly EvaluationTraceEntry[]}) {\n const keyCounts = new Map<string, number>();\n\n return (\n <dl className=\"flex min-w-0 flex-col divide-y divide-border-neutral-base rounded-6 border border-border-neutral-base\">\n {trace.map((entry) => {\n if ('dropped' in entry) {\n const keyBase = `limit-${entry.dropped}`;\n const occurrence = keyCounts.get(keyBase) ?? 0;\n keyCounts.set(keyBase, occurrence + 1);\n return (\n <div\n key={`${keyBase}-${occurrence}`}\n className=\"px-row py-row text-xs text-foreground-neutral-muted\"\n >\n {entry.dropped} more evaluation{entry.dropped === 1 ? '' : 's'} not recorded\n </div>\n );\n }\n\n const empty = entry.value === undefined || entry.value === '';\n const keyBase = `evaluation-${entry.field}-${entry.expression}-${entry.evaluatedAt}-${entry.fillTarget}`;\n const occurrence = keyCounts.get(keyBase) ?? 0;\n keyCounts.set(keyBase, occurrence + 1);\n return (\n <div\n key={`${keyBase}-${occurrence}`}\n className={cn(\n 'grid min-w-0 grid-cols-1 gap-inline px-row py-row min-[768px]:grid-cols-[160px_minmax(0,1fr)]',\n entry.degraded && 'border-l border-tag-error-icon',\n )}\n >\n <dt\n className=\"flex min-w-0 flex-col gap-tight font-code text-xs text-foreground-neutral-muted\"\n title={entry.field}\n >\n <span className=\"block truncate\">{entry.field}</span>\n <span className=\"block break-all text-foreground-neutral-subtle\">\n {entry.expression}\n </span>\n </dt>\n <dd className=\"flex min-w-0 flex-col gap-tight text-xs text-foreground-neutral-base\">\n {entry.degraded ? <span className=\"sr-only\">Degraded evaluation</span> : null}\n <div className=\"break-words font-code\">\n {empty ? <span className=\"text-tag-error-text\">(empty)</span> : entry.value}\n </div>\n <div className=\"flex min-w-0 flex-wrap gap-x-inline gap-y-tight text-foreground-neutral-muted\">\n {entry.degraded ? <span className=\"text-tag-error-text\">degraded</span> : null}\n {entry.truncated || entry.exprTruncated ? <span>truncated</span> : null}\n </div>\n </dd>\n </div>\n );\n })}\n </dl>\n );\n}\n\nfunction InspectorLoading() {\n return (\n <div\n role=\"status\"\n aria-label=\"Loading troubleshooting details\"\n className=\"flex flex-col gap-inline\"\n >\n <Skeleton className=\"h-16 w-120\" />\n <Skeleton className=\"h-120 w-full\" />\n </div>\n );\n}\n\nfunction EmptyInspector() {\n return (\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n No additional troubleshooting details were recorded.\n </Text>\n );\n}\n\nfunction selectedStepError(\n step: Step,\n attemptError: Record<string, unknown> | null,\n): StepError | null {\n return toSelectedAttemptError(step, attemptError) ?? step.error;\n}\n\nfunction failureTitle(reason: string | JobStatusReason): string {\n switch (reason) {\n case 'checkout_failed':\n return 'Checkout failed';\n case 'checkout_auth_failed':\n return 'Checkout authentication failed';\n case 'checkout_unavailable':\n return 'Checkout service unavailable';\n case 'checkout_path_invalid':\n return 'Checkout path is invalid';\n case 'checkout_destination_occupied':\n return 'Checkout destination is already occupied';\n case 'git_unavailable':\n return 'Git was unavailable';\n case 'workspace_prep_failed':\n return 'Workspace preparation failed';\n case 'setup_aborted':\n return 'Step setup was aborted';\n case 'config_unresolvable':\n return 'Step configuration could not be resolved';\n case 'output_invalid':\n return 'Step output was invalid';\n case 'agent_config_invalid':\n return 'Agent configuration is invalid';\n case 'agent_invocation_failed':\n return 'Agent invocation failed';\n case 'agent_harness_unavailable':\n return 'Agent harness was unavailable';\n case 'runner_lost':\n return 'Runner stopped responding';\n case 'output_too_large':\n return 'Job output exceeded its size limit';\n case 'timed_out':\n return 'Step timed out';\n case 'step_failed':\n return 'A step failed';\n case 'dependency_not_completed':\n return 'A dependency did not complete';\n case 'condition_false':\n return 'The job condition was false';\n case 'default_gate_rejected':\n return 'The default gate rejected this job';\n case 'condition_rejected':\n return 'The job condition rejected this job';\n case 'condition_errored':\n return 'The job condition could not be evaluated';\n case 'user_cancelled':\n return 'The job was cancelled by a user';\n case 'run_cancelled':\n return 'The run was cancelled';\n case 'unknown':\n return 'The failure reason was not recorded';\n default:\n return 'Step failed';\n }\n}\n\nfunction failureDescription(reason: string | JobStatusReason): string {\n switch (reason) {\n case 'checkout_auth_failed':\n return 'Checkout credentials were rejected. Verify repository access before re-running.';\n case 'checkout_unavailable':\n return 'The checkout service was unavailable. Retry after the service recovers.';\n case 'git_unavailable':\n return 'The runner could not start Git. Check the runner image before re-running.';\n case 'workspace_prep_failed':\n return 'The runner could not prepare its workspace. Review the runner setup details.';\n case 'config_unresolvable':\n return 'The resolved configuration contains a value that could not be evaluated.';\n case 'output_invalid':\n return 'The step returned output that did not match the declared contract.';\n case 'agent_config_invalid':\n return 'The agent configuration is not valid for this step.';\n case 'agent_invocation_failed':\n return 'The agent invocation failed after configuration was accepted.';\n case 'agent_harness_unavailable':\n return 'The runner could not start the agent harness.';\n case 'runner_lost':\n return 'The runner stopped responding before the step completed.';\n case 'output_too_large':\n return 'The materialized job output exceeded its configured size limit.';\n case 'timed_out':\n return 'The step exceeded its configured time limit.';\n case 'dependency_not_completed':\n return 'A required job did not complete, so this job could not start.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not allow this job to run.';\n case 'condition_errored':\n return 'The job condition could not be evaluated.';\n case 'default_gate_rejected':\n return 'A required job did not succeed, so this job was not allowed to run.';\n case 'step_failed':\n return 'A step failed before this job could complete.';\n case 'user_cancelled':\n case 'run_cancelled':\n return 'The run was cancelled before this work completed.';\n case 'unknown':\n return 'No machine-readable failure reason was recorded.';\n default:\n return `${humanize(reason)}. Review the details below and re-run after resolving the cause.`;\n }\n}\n\nfunction sourceLinkForFailure(reason: string | JobStatusReason): boolean {\n return (\n reason === 'config_unresolvable' ||\n reason === 'agent_config_invalid' ||\n reason === 'output_invalid' ||\n reason === 'default_gate_rejected' ||\n reason === 'condition_rejected' ||\n reason === 'condition_errored'\n );\n}\n\nfunction humanize(value: string): string {\n return value.replaceAll('_', ' ').replace(/\\b\\w/g, (letter) => letter.toUpperCase());\n}\n\nfunction countConfigValues(value: unknown): number {\n if (Array.isArray(value))\n return value.reduce((total, item) => total + countConfigValues(item), 0);\n if (value !== null && typeof value === 'object') {\n return Object.values(value).reduce((total, item) => total + countConfigValues(item), 0);\n }\n return value === undefined ? 0 : 1;\n}\n"],"names":["Button","Callout","CalloutContent","CalloutDescription","CalloutTitle","Sheet","SheetBody","SheetContent","SheetDescription","SheetHeader","SheetTitle","Skeleton","Code","Text","cn","Link","useStepAttemptDetailQuery","workflowRunSearchParams","humanizeStatus","AgentConfigFailureCallout","toSelectedAttemptError","JsonCode","JsonCodeTabs","StepInspectorSheet","entry","open","onOpenChange","workspaceSlug","projectSlug","workflowRunId","runAttempt","jobId","annotationCount","error","selectedStepError","step","inspectorQuery","id","attempt","enabled","side","className","label","statusVisual","kind","StepInspector","showFailure","query","StepFailureCallout","reason","statusReason","title","failureTitle","sourceLink","sourceLinkForFailure","sourceLocation","type","config","agentConfig","role","variant","div","span","failureDescription","message","as","to","params","search","tab","stepId","stepAttemptId","detail","data","trace","evaluationTrace","resolvedConfig","hasInputs","undefined","countConfigValues","authoredConfig","hasOutputs","outputs","output","response","hasTrace","length","hasAnnotations","detailCount","Number","isPending","InspectorLoading","isError","size","onClick","refetch","InspectorSection","ConfigCode","value","emptyMessage","pre","EvaluationTrace","EmptyInspector","children","section","aria-label","bold","entries","filename","keyCounts","Map","dl","map","keyBase","dropped","occurrence","get","set","empty","field","expression","evaluatedAt","fillTarget","degraded","dt","dd","truncated","exprTruncated","attemptError","humanize","replaceAll","replace","letter","toUpperCase","Array","isArray","reduce","total","item","Object","values"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"step-list-model.d.ts","sourceRoot":"","sources":["../../../src/components/step-list/step-list-model.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAGL,KAAK,GAAG,EACR,YAAY,EACZ,KAAK,IAAI,EACT,KAAK,WAAW,EAChB,KAAK,0BAA0B,EAChC,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,eAAe,EAAE,0BAA0B,GAAG,IAAI,CAAC;IACnD,YAAY,EAAE,gBAAgB,CAAC;IAC/B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC1D,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B;AAED,wBAAgB,kBAAkB,CAAC,EACjC,GAAG,EACH,YAAY,GACb,EAAE;IACD,GAAG,EAAE,GAAG,CAAC;IACT,YAAY,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;CACzC,GAAG,aAAa,CAehB;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,GAAG,GAAG,YAAY,CAElE;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,GAAG,GAAG,YAAY,CAoB9D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAepE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKrD"}
1
+ {"version":3,"file":"step-list-model.d.ts","sourceRoot":"","sources":["../../../src/components/step-list/step-list-model.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAGL,KAAK,GAAG,EACR,YAAY,EACZ,KAAK,IAAI,EACT,KAAK,WAAW,EAChB,KAAK,0BAA0B,EAChC,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,eAAe,EAAE,0BAA0B,GAAG,IAAI,CAAC;IACnD,YAAY,EAAE,gBAAgB,CAAC;IAC/B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC1D,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B;AAED,wBAAgB,kBAAkB,CAAC,EACjC,GAAG,EACH,YAAY,GACb,EAAE;IACD,GAAG,EAAE,GAAG,CAAC;IACT,YAAY,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;CACzC,GAAG,aAAa,CAehB;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,GAAG,GAAG,YAAY,CAElE;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,GAAG,GAAG,YAAY,CAqB9D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAepE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKrD"}
@@ -26,6 +26,7 @@ export function emptyJobExecutionForJob(job) {
26
26
  name: job.name ?? job.key,
27
27
  status: job.status === 'skipped' ? 'cancelled' : job.status,
28
28
  statusReason: job.statusReason,
29
+ statusReasonMessage: null,
29
30
  runner: job.runner,
30
31
  outputs: job.outputs,
31
32
  triggerEvents: [],
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/step-list/step-list-model.ts"],"sourcesContent":["import {\n getWorkflowStatusVisual,\n type WorkflowStatusVisual,\n} from '#components/workflow-status/status-visuals.js';\nimport {\n defaultJobExecution,\n isWorkflowStatus as isKnownWorkflowStatus,\n type Job,\n JobExecution,\n type Step,\n type StepAttempt,\n type StepAttemptDisplayDuration,\n} from '#core/workflow-run.js';\n\nexport interface StepStatusVisual extends Omit<WorkflowStatusVisual, 'kind'> {\n kind: string;\n ripple: boolean;\n}\n\nexport interface StepAttemptModel extends StepAttempt {\n displayDuration: StepAttemptDisplayDuration | null;\n statusVisual: StepStatusVisual;\n carriedOver: boolean;\n}\n\nexport interface StepModel extends Omit<Step, 'attempts'> {\n index: number;\n label: string;\n attempts: StepAttemptModel[];\n}\n\nexport interface StepListEntryModel extends StepAttemptModel {\n step: StepModel;\n}\n\nexport interface StepListModel {\n jobId: string;\n jobName: string;\n jobExecutionId: string;\n stepCount: number;\n activeEntryId: string | undefined;\n entries: StepListEntryModel[];\n}\n\nexport function buildStepListModel({\n job,\n jobExecution,\n}: {\n job: Job;\n jobExecution?: JobExecution | undefined;\n}): StepListModel {\n const selectedJobExecution = jobExecution ?? defaultStepListJobExecution(job);\n const steps = [...selectedJobExecution.steps].sort(compareSteps).map(toStepModel);\n const entries = steps\n .flatMap((step) => toStepEntries(step, job.carriedOver))\n .sort(compareEntries);\n\n return {\n jobId: job.id,\n jobName: job.name ?? job.key,\n jobExecutionId: selectedJobExecution.id,\n stepCount: steps.length,\n activeEntryId: landingEntryId(entries),\n entries,\n };\n}\n\nexport function defaultStepListJobExecution(job: Job): JobExecution {\n return defaultJobExecution(job) ?? emptyJobExecutionForJob(job);\n}\n\nexport function emptyJobExecutionForJob(job: Job): JobExecution {\n return new JobExecution({\n id: `missing:${job.id}`,\n jobId: job.id,\n sequence: 1,\n name: job.name ?? job.key,\n status: job.status === 'skipped' ? 'cancelled' : job.status,\n statusReason: job.statusReason,\n runner: job.runner,\n outputs: job.outputs,\n triggerEvents: [],\n queuedAt: null,\n startedAt: null,\n finishedAt: null,\n timedOutAt: null,\n evaluationTrace: job.evaluationTrace,\n createdAt: job.createdAt,\n updatedAt: job.updatedAt,\n steps: [],\n });\n}\n\nexport function getStepStatusVisual(status: string): StepStatusVisual {\n const normalized = normalizeStatus(status);\n\n if (isKnownWorkflowStatus(normalized)) {\n const visual = getWorkflowStatusVisual(normalized);\n return {...visual, ripple: normalized === 'running'};\n }\n\n return {\n kind: status,\n label: humanizeStatus(status),\n dot: 'neutral',\n badge: 'neutral',\n ripple: false,\n };\n}\n\nexport function humanizeStatus(status: string): string {\n const words = status.trim().replace(/[_-]+/g, ' ').replace(/\\s+/g, ' ').toLowerCase();\n if (!words) return 'Unknown';\n const firstLetter = words.at(0);\n return firstLetter === undefined ? 'Unknown' : firstLetter.toUpperCase() + words.slice(1);\n}\n\nfunction toStepModel(step: Step, index: number): StepModel {\n const attempts = [...step.attempts].sort(compareAttempts).map((attempt) => ({\n ...attempt,\n displayDuration: attempt.displayDuration,\n statusVisual: getStepStatusVisual(attempt.status),\n carriedOver: false,\n }));\n\n return {\n ...step,\n index: index + 1,\n label: stepLabel(step, index),\n attempts,\n };\n}\n\nfunction stepLabel(step: Step, index: number): string {\n const name = step.name.trim();\n if (name) return name;\n\n const key = step.key?.trim();\n if (key) return key;\n\n return `Step ${index + 1}`;\n}\n\nfunction toStepEntries(step: StepModel, carriedOverJob: boolean): StepListEntryModel[] {\n if (step.attempts.length > 0) {\n return step.attempts.map((attempt) => ({\n ...attempt,\n step,\n }));\n }\n\n if (!carriedOverJob) return [];\n\n return [\n {\n id: `carried-over:${step.id}`,\n stepId: step.id,\n jobExecutionId: step.jobExecutionId,\n attempt: step.currentAttempt,\n executionOrder: step.position,\n status: step.status,\n exitCode: null,\n output: null,\n outputs: null,\n response: null,\n error: null,\n gateResult: null,\n restartFeedback: null,\n startedAt: step.createdAt,\n finishedAt: null,\n displayDuration: null,\n statusVisual: getStepStatusVisual(step.status),\n carriedOver: true,\n step,\n },\n ];\n}\n\nfunction compareSteps(left: Step, right: Step): number {\n return (\n left.position - right.position ||\n (left.name ?? '').localeCompare(right.name ?? '') ||\n left.id.localeCompare(right.id)\n );\n}\n\nfunction compareAttempts(left: Step['attempts'][number], right: Step['attempts'][number]) {\n return left.attempt - right.attempt || left.id.localeCompare(right.id);\n}\n\nfunction compareEntries(left: StepListEntryModel, right: StepListEntryModel): number {\n return (\n left.executionOrder - right.executionOrder ||\n left.step.index - right.step.index ||\n left.attempt - right.attempt ||\n left.id.localeCompare(right.id)\n );\n}\n\nfunction landingEntryId(entries: readonly StepListEntryModel[]): string | undefined {\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index];\n if (entry?.statusVisual.kind === 'running') return entry.id;\n }\n return undefined;\n}\n\nfunction normalizeStatus(status: string): string {\n return status.trim().toLowerCase().replace(/-/g, '_');\n}\n"],"names":["getWorkflowStatusVisual","defaultJobExecution","isWorkflowStatus","isKnownWorkflowStatus","JobExecution","buildStepListModel","job","jobExecution","selectedJobExecution","defaultStepListJobExecution","steps","sort","compareSteps","map","toStepModel","entries","flatMap","step","toStepEntries","carriedOver","compareEntries","jobId","id","jobName","name","key","jobExecutionId","stepCount","length","activeEntryId","landingEntryId","emptyJobExecutionForJob","sequence","status","statusReason","runner","outputs","triggerEvents","queuedAt","startedAt","finishedAt","timedOutAt","evaluationTrace","createdAt","updatedAt","getStepStatusVisual","normalized","normalizeStatus","visual","ripple","kind","label","humanizeStatus","dot","badge","words","trim","replace","toLowerCase","firstLetter","at","undefined","toUpperCase","slice","index","attempts","compareAttempts","attempt","displayDuration","statusVisual","stepLabel","carriedOverJob","stepId","currentAttempt","executionOrder","position","exitCode","output","response","error","gateResult","restartFeedback","left","right","localeCompare","entry"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/step-list/step-list-model.ts"],"sourcesContent":["import {\n getWorkflowStatusVisual,\n type WorkflowStatusVisual,\n} from '#components/workflow-status/status-visuals.js';\nimport {\n defaultJobExecution,\n isWorkflowStatus as isKnownWorkflowStatus,\n type Job,\n JobExecution,\n type Step,\n type StepAttempt,\n type StepAttemptDisplayDuration,\n} from '#core/workflow-run.js';\n\nexport interface StepStatusVisual extends Omit<WorkflowStatusVisual, 'kind'> {\n kind: string;\n ripple: boolean;\n}\n\nexport interface StepAttemptModel extends StepAttempt {\n displayDuration: StepAttemptDisplayDuration | null;\n statusVisual: StepStatusVisual;\n carriedOver: boolean;\n}\n\nexport interface StepModel extends Omit<Step, 'attempts'> {\n index: number;\n label: string;\n attempts: StepAttemptModel[];\n}\n\nexport interface StepListEntryModel extends StepAttemptModel {\n step: StepModel;\n}\n\nexport interface StepListModel {\n jobId: string;\n jobName: string;\n jobExecutionId: string;\n stepCount: number;\n activeEntryId: string | undefined;\n entries: StepListEntryModel[];\n}\n\nexport function buildStepListModel({\n job,\n jobExecution,\n}: {\n job: Job;\n jobExecution?: JobExecution | undefined;\n}): StepListModel {\n const selectedJobExecution = jobExecution ?? defaultStepListJobExecution(job);\n const steps = [...selectedJobExecution.steps].sort(compareSteps).map(toStepModel);\n const entries = steps\n .flatMap((step) => toStepEntries(step, job.carriedOver))\n .sort(compareEntries);\n\n return {\n jobId: job.id,\n jobName: job.name ?? job.key,\n jobExecutionId: selectedJobExecution.id,\n stepCount: steps.length,\n activeEntryId: landingEntryId(entries),\n entries,\n };\n}\n\nexport function defaultStepListJobExecution(job: Job): JobExecution {\n return defaultJobExecution(job) ?? emptyJobExecutionForJob(job);\n}\n\nexport function emptyJobExecutionForJob(job: Job): JobExecution {\n return new JobExecution({\n id: `missing:${job.id}`,\n jobId: job.id,\n sequence: 1,\n name: job.name ?? job.key,\n status: job.status === 'skipped' ? 'cancelled' : job.status,\n statusReason: job.statusReason,\n statusReasonMessage: null,\n runner: job.runner,\n outputs: job.outputs,\n triggerEvents: [],\n queuedAt: null,\n startedAt: null,\n finishedAt: null,\n timedOutAt: null,\n evaluationTrace: job.evaluationTrace,\n createdAt: job.createdAt,\n updatedAt: job.updatedAt,\n steps: [],\n });\n}\n\nexport function getStepStatusVisual(status: string): StepStatusVisual {\n const normalized = normalizeStatus(status);\n\n if (isKnownWorkflowStatus(normalized)) {\n const visual = getWorkflowStatusVisual(normalized);\n return {...visual, ripple: normalized === 'running'};\n }\n\n return {\n kind: status,\n label: humanizeStatus(status),\n dot: 'neutral',\n badge: 'neutral',\n ripple: false,\n };\n}\n\nexport function humanizeStatus(status: string): string {\n const words = status.trim().replace(/[_-]+/g, ' ').replace(/\\s+/g, ' ').toLowerCase();\n if (!words) return 'Unknown';\n const firstLetter = words.at(0);\n return firstLetter === undefined ? 'Unknown' : firstLetter.toUpperCase() + words.slice(1);\n}\n\nfunction toStepModel(step: Step, index: number): StepModel {\n const attempts = [...step.attempts].sort(compareAttempts).map((attempt) => ({\n ...attempt,\n displayDuration: attempt.displayDuration,\n statusVisual: getStepStatusVisual(attempt.status),\n carriedOver: false,\n }));\n\n return {\n ...step,\n index: index + 1,\n label: stepLabel(step, index),\n attempts,\n };\n}\n\nfunction stepLabel(step: Step, index: number): string {\n const name = step.name.trim();\n if (name) return name;\n\n const key = step.key?.trim();\n if (key) return key;\n\n return `Step ${index + 1}`;\n}\n\nfunction toStepEntries(step: StepModel, carriedOverJob: boolean): StepListEntryModel[] {\n if (step.attempts.length > 0) {\n return step.attempts.map((attempt) => ({\n ...attempt,\n step,\n }));\n }\n\n if (!carriedOverJob) return [];\n\n return [\n {\n id: `carried-over:${step.id}`,\n stepId: step.id,\n jobExecutionId: step.jobExecutionId,\n attempt: step.currentAttempt,\n executionOrder: step.position,\n status: step.status,\n exitCode: null,\n output: null,\n outputs: null,\n response: null,\n error: null,\n gateResult: null,\n restartFeedback: null,\n startedAt: step.createdAt,\n finishedAt: null,\n displayDuration: null,\n statusVisual: getStepStatusVisual(step.status),\n carriedOver: true,\n step,\n },\n ];\n}\n\nfunction compareSteps(left: Step, right: Step): number {\n return (\n left.position - right.position ||\n (left.name ?? '').localeCompare(right.name ?? '') ||\n left.id.localeCompare(right.id)\n );\n}\n\nfunction compareAttempts(left: Step['attempts'][number], right: Step['attempts'][number]) {\n return left.attempt - right.attempt || left.id.localeCompare(right.id);\n}\n\nfunction compareEntries(left: StepListEntryModel, right: StepListEntryModel): number {\n return (\n left.executionOrder - right.executionOrder ||\n left.step.index - right.step.index ||\n left.attempt - right.attempt ||\n left.id.localeCompare(right.id)\n );\n}\n\nfunction landingEntryId(entries: readonly StepListEntryModel[]): string | undefined {\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index];\n if (entry?.statusVisual.kind === 'running') return entry.id;\n }\n return undefined;\n}\n\nfunction normalizeStatus(status: string): string {\n return status.trim().toLowerCase().replace(/-/g, '_');\n}\n"],"names":["getWorkflowStatusVisual","defaultJobExecution","isWorkflowStatus","isKnownWorkflowStatus","JobExecution","buildStepListModel","job","jobExecution","selectedJobExecution","defaultStepListJobExecution","steps","sort","compareSteps","map","toStepModel","entries","flatMap","step","toStepEntries","carriedOver","compareEntries","jobId","id","jobName","name","key","jobExecutionId","stepCount","length","activeEntryId","landingEntryId","emptyJobExecutionForJob","sequence","status","statusReason","statusReasonMessage","runner","outputs","triggerEvents","queuedAt","startedAt","finishedAt","timedOutAt","evaluationTrace","createdAt","updatedAt","getStepStatusVisual","normalized","normalizeStatus","visual","ripple","kind","label","humanizeStatus","dot","badge","words","trim","replace","toLowerCase","firstLetter","at","undefined","toUpperCase","slice","index","attempts","compareAttempts","attempt","displayDuration","statusVisual","stepLabel","carriedOverJob","stepId","currentAttempt","executionOrder","position","exitCode","output","response","error","gateResult","restartFeedback","left","right","localeCompare","entry"],"mappings":"AAAA,SACEA,uBAAuB,QAElB,gDAAgD;AACvD,SACEC,mBAAmB,EACnBC,oBAAoBC,qBAAqB,EAEzCC,YAAY,QAIP,wBAAwB;AAgC/B,OAAO,SAASC,mBAAmB,EACjCC,GAAG,EACHC,YAAY,EAIb;IACC,MAAMC,uBAAuBD,gBAAgBE,4BAA4BH;IACzE,MAAMI,QAAQ;WAAIF,qBAAqBE,KAAK;KAAC,CAACC,IAAI,CAACC,cAAcC,GAAG,CAACC;IACrE,MAAMC,UAAUL,MACbM,OAAO,CAAC,CAACC,OAASC,cAAcD,MAAMX,IAAIa,WAAW,GACrDR,IAAI,CAACS;IAER,OAAO;QACLC,OAAOf,IAAIgB,EAAE;QACbC,SAASjB,IAAIkB,IAAI,IAAIlB,IAAImB,GAAG;QAC5BC,gBAAgBlB,qBAAqBc,EAAE;QACvCK,WAAWjB,MAAMkB,MAAM;QACvBC,eAAeC,eAAef;QAC9BA;IACF;AACF;AAEA,OAAO,SAASN,4BAA4BH,GAAQ;IAClD,OAAOL,oBAAoBK,QAAQyB,wBAAwBzB;AAC7D;AAEA,OAAO,SAASyB,wBAAwBzB,GAAQ;IAC9C,OAAO,IAAIF,aAAa;QACtBkB,IAAI,CAAC,QAAQ,EAAEhB,IAAIgB,EAAE,EAAE;QACvBD,OAAOf,IAAIgB,EAAE;QACbU,UAAU;QACVR,MAAMlB,IAAIkB,IAAI,IAAIlB,IAAImB,GAAG;QACzBQ,QAAQ3B,IAAI2B,MAAM,KAAK,YAAY,cAAc3B,IAAI2B,MAAM;QAC3DC,cAAc5B,IAAI4B,YAAY;QAC9BC,qBAAqB;QACrBC,QAAQ9B,IAAI8B,MAAM;QAClBC,SAAS/B,IAAI+B,OAAO;QACpBC,eAAe,EAAE;QACjBC,UAAU;QACVC,WAAW;QACXC,YAAY;QACZC,YAAY;QACZC,iBAAiBrC,IAAIqC,eAAe;QACpCC,WAAWtC,IAAIsC,SAAS;QACxBC,WAAWvC,IAAIuC,SAAS;QACxBnC,OAAO,EAAE;IACX;AACF;AAEA,OAAO,SAASoC,oBAAoBb,MAAc;IAChD,MAAMc,aAAaC,gBAAgBf;IAEnC,IAAI9B,sBAAsB4C,aAAa;QACrC,MAAME,SAASjD,wBAAwB+C;QACvC,OAAO;YAAC,GAAGE,MAAM;YAAEC,QAAQH,eAAe;QAAS;IACrD;IAEA,OAAO;QACLI,MAAMlB;QACNmB,OAAOC,eAAepB;QACtBqB,KAAK;QACLC,OAAO;QACPL,QAAQ;IACV;AACF;AAEA,OAAO,SAASG,eAAepB,MAAc;IAC3C,MAAMuB,QAAQvB,OAAOwB,IAAI,GAAGC,OAAO,CAAC,UAAU,KAAKA,OAAO,CAAC,QAAQ,KAAKC,WAAW;IACnF,IAAI,CAACH,OAAO,OAAO;IACnB,MAAMI,cAAcJ,MAAMK,EAAE,CAAC;IAC7B,OAAOD,gBAAgBE,YAAY,YAAYF,YAAYG,WAAW,KAAKP,MAAMQ,KAAK,CAAC;AACzF;AAEA,SAASlD,YAAYG,IAAU,EAAEgD,KAAa;IAC5C,MAAMC,WAAW;WAAIjD,KAAKiD,QAAQ;KAAC,CAACvD,IAAI,CAACwD,iBAAiBtD,GAAG,CAAC,CAACuD,UAAa,CAAA;YAC1E,GAAGA,OAAO;YACVC,iBAAiBD,QAAQC,eAAe;YACxCC,cAAcxB,oBAAoBsB,QAAQnC,MAAM;YAChDd,aAAa;QACf,CAAA;IAEA,OAAO;QACL,GAAGF,IAAI;QACPgD,OAAOA,QAAQ;QACfb,OAAOmB,UAAUtD,MAAMgD;QACvBC;IACF;AACF;AAEA,SAASK,UAAUtD,IAAU,EAAEgD,KAAa;IAC1C,MAAMzC,OAAOP,KAAKO,IAAI,CAACiC,IAAI;IAC3B,IAAIjC,MAAM,OAAOA;IAEjB,MAAMC,MAAMR,KAAKQ,GAAG,EAAEgC;IACtB,IAAIhC,KAAK,OAAOA;IAEhB,OAAO,CAAC,KAAK,EAAEwC,QAAQ,GAAG;AAC5B;AAEA,SAAS/C,cAAcD,IAAe,EAAEuD,cAAuB;IAC7D,IAAIvD,KAAKiD,QAAQ,CAACtC,MAAM,GAAG,GAAG;QAC5B,OAAOX,KAAKiD,QAAQ,CAACrD,GAAG,CAAC,CAACuD,UAAa,CAAA;gBACrC,GAAGA,OAAO;gBACVnD;YACF,CAAA;IACF;IAEA,IAAI,CAACuD,gBAAgB,OAAO,EAAE;IAE9B,OAAO;QACL;YACElD,IAAI,CAAC,aAAa,EAAEL,KAAKK,EAAE,EAAE;YAC7BmD,QAAQxD,KAAKK,EAAE;YACfI,gBAAgBT,KAAKS,cAAc;YACnC0C,SAASnD,KAAKyD,cAAc;YAC5BC,gBAAgB1D,KAAK2D,QAAQ;YAC7B3C,QAAQhB,KAAKgB,MAAM;YACnB4C,UAAU;YACVC,QAAQ;YACRzC,SAAS;YACT0C,UAAU;YACVC,OAAO;YACPC,YAAY;YACZC,iBAAiB;YACjB1C,WAAWvB,KAAK2B,SAAS;YACzBH,YAAY;YACZ4B,iBAAiB;YACjBC,cAAcxB,oBAAoB7B,KAAKgB,MAAM;YAC7Cd,aAAa;YACbF;QACF;KACD;AACH;AAEA,SAASL,aAAauE,IAAU,EAAEC,KAAW;IAC3C,OACED,KAAKP,QAAQ,GAAGQ,MAAMR,QAAQ,IAC9B,AAACO,CAAAA,KAAK3D,IAAI,IAAI,EAAC,EAAG6D,aAAa,CAACD,MAAM5D,IAAI,IAAI,OAC9C2D,KAAK7D,EAAE,CAAC+D,aAAa,CAACD,MAAM9D,EAAE;AAElC;AAEA,SAAS6C,gBAAgBgB,IAA8B,EAAEC,KAA+B;IACtF,OAAOD,KAAKf,OAAO,GAAGgB,MAAMhB,OAAO,IAAIe,KAAK7D,EAAE,CAAC+D,aAAa,CAACD,MAAM9D,EAAE;AACvE;AAEA,SAASF,eAAe+D,IAAwB,EAAEC,KAAyB;IACzE,OACED,KAAKR,cAAc,GAAGS,MAAMT,cAAc,IAC1CQ,KAAKlE,IAAI,CAACgD,KAAK,GAAGmB,MAAMnE,IAAI,CAACgD,KAAK,IAClCkB,KAAKf,OAAO,GAAGgB,MAAMhB,OAAO,IAC5Be,KAAK7D,EAAE,CAAC+D,aAAa,CAACD,MAAM9D,EAAE;AAElC;AAEA,SAASQ,eAAef,OAAsC;IAC5D,IAAK,IAAIkD,QAAQlD,QAAQa,MAAM,GAAG,GAAGqC,SAAS,GAAGA,SAAS,EAAG;QAC3D,MAAMqB,QAAQvE,OAAO,CAACkD,MAAM;QAC5B,IAAIqB,OAAOhB,aAAanB,SAAS,WAAW,OAAOmC,MAAMhE,EAAE;IAC7D;IACA,OAAOwC;AACT;AAEA,SAASd,gBAAgBf,MAAc;IACrC,OAAOA,OAAOwB,IAAI,GAAGE,WAAW,GAAGD,OAAO,CAAC,MAAM;AACnD"}
@@ -1 +1 @@
1
- {"version":3,"file":"job-status-strip.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/job-status-strip.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAmC,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAyC7F,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,EAAC,IAAI,EAAE,SAAS,EAAC,EAAE,mBAAmB,sCAqDpE;AAgCD,kGAAkG;AAClG,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAO9D;AAyBD,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAI9D"}
1
+ {"version":3,"file":"job-status-strip.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-list/job-status-strip.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAwB,eAAe,EAAC,MAAM,uBAAuB,CAAC;AA0ClF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,EAAC,IAAI,EAAE,SAAS,EAAC,EAAE,mBAAmB,sCAqDpE;AAgCD,kGAAkG;AAClG,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAO9D;AA0BD,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAI9D"}
@@ -4,6 +4,7 @@ import { Code, Text } from '@shipfox/react-ui/typography';
4
4
  import { cn } from '@shipfox/react-ui/utils';
5
5
  import { getWorkflowStatusVisual } from '#components/workflow-status/status-visuals.js';
6
6
  import { WorkflowStatusIcon } from '#components/workflow-status/workflow-status-icon.js';
7
+ import { deriveJobDisplayStatus } from '#core/entities/job.js';
7
8
  /**
8
9
  * How many glyphs fit the strip's column before it starts costing the run name width.
9
10
  *
@@ -31,6 +32,7 @@ const COMPACT_COUNT_FORMAT = new Intl.NumberFormat('en', {
31
32
  const STATUS_SEVERITY = {
32
33
  failed: 5,
33
34
  running: 4,
35
+ listening: 4,
34
36
  pending: 3,
35
37
  cancelled: 2,
36
38
  skipped: 1,
@@ -38,7 +40,8 @@ const STATUS_SEVERITY = {
38
40
  };
39
41
  const NOTABLE_STATUSES = [
40
42
  'failed',
41
- 'running'
43
+ 'running',
44
+ 'listening'
42
45
  ];
43
46
  /**
44
47
  * A run's jobs as a row of status glyphs, in graph order.
@@ -69,7 +72,7 @@ const NOTABLE_STATUSES = [
69
72
  className: "inline-flex items-center gap-[3px]",
70
73
  children: [
71
74
  visible.map((job)=>/*#__PURE__*/ _jsx(WorkflowStatusIcon, {
72
- status: job.status,
75
+ status: displayStatus(job),
73
76
  size: GLYPH_SIZE,
74
77
  // One ripple per running job, on every row, would turn a calm list into a
75
78
  // field of pulses. The run's own glyph already carries the live edge.
@@ -114,7 +117,7 @@ function JobStatusStripTooltip({ jobs, summary }) {
114
117
  // left to the counts so the tooltip stays a glance rather than a list. Only previewed jobs
115
118
  // can be named, but the count of those left over is read off the totals, so a failure past
116
119
  // the preview is still accounted for rather than silently missing.
117
- const named = jobs.preview.filter((job)=>NOTABLE_STATUSES.includes(job.status)).slice(0, MAX_TOOLTIP_JOB_NAMES);
120
+ const named = jobs.preview.filter((job)=>NOTABLE_STATUSES.includes(displayStatus(job))).slice(0, MAX_TOOLTIP_JOB_NAMES);
118
121
  const notableTotal = NOTABLE_STATUSES.reduce((total, status)=>total + countOf(jobs, status), 0);
119
122
  const remaining = notableTotal - named.length;
120
123
  return /*#__PURE__*/ _jsxs("span", {
@@ -131,7 +134,7 @@ function JobStatusStripTooltip({ jobs, summary }) {
131
134
  variant: "label",
132
135
  className: "block truncate",
133
136
  children: [
134
- getWorkflowStatusVisual(job.status).label.toLowerCase(),
137
+ getWorkflowStatusVisual(displayStatus(job)).label.toLowerCase(),
135
138
  " · ",
136
139
  job.name ?? job.key
137
140
  ]
@@ -164,9 +167,10 @@ function JobStatusStripTooltip({ jobs, summary }) {
164
167
  count
165
168
  ]));
166
169
  for (const job of visible){
167
- const remaining = (hidden.get(job.status) ?? 0) - 1;
168
- if (remaining > 0) hidden.set(job.status, remaining);
169
- else hidden.delete(job.status);
170
+ const status = displayStatus(job);
171
+ const remaining = (hidden.get(status) ?? 0) - 1;
172
+ if (remaining > 0) hidden.set(status, remaining);
173
+ else hidden.delete(status);
170
174
  }
171
175
  let worst = null;
172
176
  for (const [status, count] of hidden){
@@ -181,5 +185,14 @@ export function overflowCountLabel(hiddenCount) {
181
185
  function countOf(jobs, status) {
182
186
  return jobs.statusCounts.find((entry)=>entry.status === status)?.count ?? 0;
183
187
  }
188
+ function displayStatus(job) {
189
+ return deriveJobDisplayStatus({
190
+ mode: job.mode,
191
+ status: job.status,
192
+ listenerStatus: job.listenerStatus,
193
+ executionStatus: job.executionStatus,
194
+ jobExecutions: []
195
+ });
196
+ }
184
197
 
185
198
  //# sourceMappingURL=job-status-strip.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-run-list/job-status-strip.tsx"],"sourcesContent":["import {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport type {JobStatus, WorkflowRunJobSummary, WorkflowRunJobs} from '#core/workflow-run.js';\n\n/**\n * How many glyphs fit the strip's column before it starts costing the run name width.\n *\n * Sized so the widest case still fits the row's 160px strip column: seven 12px glyphs with\n * 3px gaps is 102px, and the overflow indicator adds a glyph plus a count of at most five\n * monospace characters, about 56px. Overshooting does not wrap, it paints over the duration\n * column. This sits under the API's preview bound, so resizing the strip is a change here.\n */\nconst MAX_VISIBLE_JOBS = 7;\nconst GLYPH_SIZE = 12;\nconst MAX_TOOLTIP_JOB_NAMES = 6;\n\n/**\n * Above this the overflow count is abbreviated.\n *\n * Nothing caps a workflow's job count, so an exact count is unbounded in width and would\n * eventually push the strip over its column. Abbreviating keeps the label at most five\n * characters for any magnitude, and the exact figure stays in the tooltip and the strip's\n * accessible name, which is where a precise number is actually read.\n */\nconst MAX_EXACT_OVERFLOW_COUNT = 999;\nconst COMPACT_COUNT_FORMAT = new Intl.NumberFormat('en', {\n notation: 'compact',\n maximumFractionDigits: 1,\n});\n\n// Worst-first, so the overflow glyph reports the most alarming thing it is standing in for.\n// A strip that hides a failure behind eight green discs would be worse than no strip at all.\nconst STATUS_SEVERITY: Record<JobStatus, number> = {\n failed: 5,\n running: 4,\n pending: 3,\n cancelled: 2,\n skipped: 1,\n succeeded: 0,\n};\n\nconst NOTABLE_STATUSES: readonly JobStatus[] = ['failed', 'running'];\n\nexport interface JobStatusStripProps {\n jobs: WorkflowRunJobs;\n className?: string | undefined;\n}\n\n/**\n * A run's jobs as a row of status glyphs, in graph order.\n *\n * This is the reason the list is worth its width: it answers \"where did this run fail\"\n * without opening the run. Glyphs are silent to assistive tech and carry no tooltip of their\n * own; the strip speaks once, as a summary, so a 40-job run does not read out 40 labels.\n *\n * What it draws comes from the preview; what it says comes from the counts, which cover every\n * job including those the server never sent. That split is what lets the overflow glyph\n * report a failure sitting past the preview instead of quietly dropping it.\n */\nexport function JobStatusStrip({jobs, className}: JobStatusStripProps) {\n if (jobs.total === 0) return null;\n\n const visible = jobs.preview.slice(0, MAX_VISIBLE_JOBS);\n const hiddenCount = jobs.total - visible.length;\n const overflowStatus = worstHiddenStatus(jobs, visible);\n const summary = jobStatusSummary(jobs);\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span\n role=\"img\"\n aria-label={summary}\n className={cn('inline-flex items-center gap-[3px]', className)}\n >\n <span aria-hidden=\"true\" className=\"inline-flex items-center gap-[3px]\">\n {visible.map((job) => (\n <WorkflowStatusIcon\n key={job.id}\n status={job.status}\n size={GLYPH_SIZE}\n // One ripple per running job, on every row, would turn a calm list into a\n // field of pulses. The run's own glyph already carries the live edge.\n ripple={false}\n tooltip={false}\n />\n ))}\n {hiddenCount > 0 && overflowStatus ? (\n <span className=\"inline-flex items-center gap-[2px]\">\n <WorkflowStatusIcon\n status={overflowStatus}\n size={GLYPH_SIZE}\n ripple={false}\n tooltip={false}\n />\n <Code\n as=\"span\"\n variant=\"label\"\n className=\"tabular-nums text-foreground-neutral-muted\"\n >\n +{overflowCountLabel(hiddenCount)}\n </Code>\n </span>\n ) : null}\n </span>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n <JobStatusStripTooltip jobs={jobs} summary={summary} />\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction JobStatusStripTooltip({jobs, summary}: {jobs: WorkflowRunJobs; summary: string}) {\n // Naming the jobs that need attention is the whole point of hovering; succeeded jobs are\n // left to the counts so the tooltip stays a glance rather than a list. Only previewed jobs\n // can be named, but the count of those left over is read off the totals, so a failure past\n // the preview is still accounted for rather than silently missing.\n const named = jobs.preview\n .filter((job) => NOTABLE_STATUSES.includes(job.status))\n .slice(0, MAX_TOOLTIP_JOB_NAMES);\n const notableTotal = NOTABLE_STATUSES.reduce((total, status) => total + countOf(jobs, status), 0);\n const remaining = notableTotal - named.length;\n\n return (\n <span className=\"flex max-w-[280px] flex-col gap-tight\">\n <Text as=\"span\" size=\"xs\" className=\"block\">\n {summary}\n </Text>\n {named.map((job) => (\n <Code as=\"span\" variant=\"label\" key={job.id} className=\"block truncate\">\n {getWorkflowStatusVisual(job.status).label.toLowerCase()} · {job.name ?? job.key}\n </Code>\n ))}\n {remaining > 0 ? (\n <Text as=\"span\" size=\"xs\" className=\"block text-foreground-neutral-muted\">\n and {remaining} more\n </Text>\n ) : null}\n </span>\n );\n}\n\n/** \"12 jobs: 9 succeeded, 2 failed, 1 running\" — a count is not an accessible name on its own. */\nexport function jobStatusSummary(jobs: WorkflowRunJobs): string {\n const breakdown = [...jobs.statusCounts]\n .sort((left, right) => STATUS_SEVERITY[right.status] - STATUS_SEVERITY[left.status])\n .map(({status, count}) => `${count} ${getWorkflowStatusVisual(status).label.toLowerCase()}`)\n .join(', ');\n\n return `${jobs.total} ${jobs.total === 1 ? 'job' : 'jobs'}: ${breakdown}`;\n}\n\n/**\n * The worst status among the jobs the strip is not drawing, found by subtracting what it\n * draws from the run's totals rather than by inspecting jobs it was never sent.\n */\nfunction worstHiddenStatus(\n jobs: WorkflowRunJobs,\n visible: readonly WorkflowRunJobSummary[],\n): JobStatus | null {\n const hidden = new Map(jobs.statusCounts.map(({status, count}) => [status, count]));\n for (const job of visible) {\n const remaining = (hidden.get(job.status) ?? 0) - 1;\n if (remaining > 0) hidden.set(job.status, remaining);\n else hidden.delete(job.status);\n }\n\n let worst: JobStatus | null = null;\n for (const [status, count] of hidden) {\n if (count <= 0) continue;\n if (worst === null || STATUS_SEVERITY[status] > STATUS_SEVERITY[worst]) worst = status;\n }\n return worst;\n}\n\nexport function overflowCountLabel(hiddenCount: number): string {\n return hiddenCount <= MAX_EXACT_OVERFLOW_COUNT\n ? String(hiddenCount)\n : COMPACT_COUNT_FORMAT.format(hiddenCount);\n}\n\nfunction countOf(jobs: WorkflowRunJobs, status: JobStatus): number {\n return jobs.statusCounts.find((entry) => entry.status === status)?.count ?? 0;\n}\n"],"names":["Tooltip","TooltipContent","TooltipTrigger","Code","Text","cn","getWorkflowStatusVisual","WorkflowStatusIcon","MAX_VISIBLE_JOBS","GLYPH_SIZE","MAX_TOOLTIP_JOB_NAMES","MAX_EXACT_OVERFLOW_COUNT","COMPACT_COUNT_FORMAT","Intl","NumberFormat","notation","maximumFractionDigits","STATUS_SEVERITY","failed","running","pending","cancelled","skipped","succeeded","NOTABLE_STATUSES","JobStatusStrip","jobs","className","total","visible","preview","slice","hiddenCount","length","overflowStatus","worstHiddenStatus","summary","jobStatusSummary","asChild","span","role","aria-label","aria-hidden","map","job","status","size","ripple","tooltip","id","as","variant","overflowCountLabel","JobStatusStripTooltip","named","filter","includes","notableTotal","reduce","countOf","remaining","label","toLowerCase","name","key","breakdown","statusCounts","sort","left","right","count","join","hidden","Map","get","set","delete","worst","String","format","find","entry"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/workflow-run-list/job-status-strip.tsx"],"sourcesContent":["import {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {deriveJobDisplayStatus, type JobDisplayStatus} from '#core/entities/job.js';\nimport type {WorkflowRunJobSummary, WorkflowRunJobs} from '#core/workflow-run.js';\n\n/**\n * How many glyphs fit the strip's column before it starts costing the run name width.\n *\n * Sized so the widest case still fits the row's 160px strip column: seven 12px glyphs with\n * 3px gaps is 102px, and the overflow indicator adds a glyph plus a count of at most five\n * monospace characters, about 56px. Overshooting does not wrap, it paints over the duration\n * column. This sits under the API's preview bound, so resizing the strip is a change here.\n */\nconst MAX_VISIBLE_JOBS = 7;\nconst GLYPH_SIZE = 12;\nconst MAX_TOOLTIP_JOB_NAMES = 6;\n\n/**\n * Above this the overflow count is abbreviated.\n *\n * Nothing caps a workflow's job count, so an exact count is unbounded in width and would\n * eventually push the strip over its column. Abbreviating keeps the label at most five\n * characters for any magnitude, and the exact figure stays in the tooltip and the strip's\n * accessible name, which is where a precise number is actually read.\n */\nconst MAX_EXACT_OVERFLOW_COUNT = 999;\nconst COMPACT_COUNT_FORMAT = new Intl.NumberFormat('en', {\n notation: 'compact',\n maximumFractionDigits: 1,\n});\n\n// Worst-first, so the overflow glyph reports the most alarming thing it is standing in for.\n// A strip that hides a failure behind eight green discs would be worse than no strip at all.\nconst STATUS_SEVERITY: Record<JobDisplayStatus, number> = {\n failed: 5,\n running: 4,\n listening: 4,\n pending: 3,\n cancelled: 2,\n skipped: 1,\n succeeded: 0,\n};\n\nconst NOTABLE_STATUSES: readonly JobDisplayStatus[] = ['failed', 'running', 'listening'];\n\nexport interface JobStatusStripProps {\n jobs: WorkflowRunJobs;\n className?: string | undefined;\n}\n\n/**\n * A run's jobs as a row of status glyphs, in graph order.\n *\n * This is the reason the list is worth its width: it answers \"where did this run fail\"\n * without opening the run. Glyphs are silent to assistive tech and carry no tooltip of their\n * own; the strip speaks once, as a summary, so a 40-job run does not read out 40 labels.\n *\n * What it draws comes from the preview; what it says comes from the counts, which cover every\n * job including those the server never sent. That split is what lets the overflow glyph\n * report a failure sitting past the preview instead of quietly dropping it.\n */\nexport function JobStatusStrip({jobs, className}: JobStatusStripProps) {\n if (jobs.total === 0) return null;\n\n const visible = jobs.preview.slice(0, MAX_VISIBLE_JOBS);\n const hiddenCount = jobs.total - visible.length;\n const overflowStatus = worstHiddenStatus(jobs, visible);\n const summary = jobStatusSummary(jobs);\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span\n role=\"img\"\n aria-label={summary}\n className={cn('inline-flex items-center gap-[3px]', className)}\n >\n <span aria-hidden=\"true\" className=\"inline-flex items-center gap-[3px]\">\n {visible.map((job) => (\n <WorkflowStatusIcon\n key={job.id}\n status={displayStatus(job)}\n size={GLYPH_SIZE}\n // One ripple per running job, on every row, would turn a calm list into a\n // field of pulses. The run's own glyph already carries the live edge.\n ripple={false}\n tooltip={false}\n />\n ))}\n {hiddenCount > 0 && overflowStatus ? (\n <span className=\"inline-flex items-center gap-[2px]\">\n <WorkflowStatusIcon\n status={overflowStatus}\n size={GLYPH_SIZE}\n ripple={false}\n tooltip={false}\n />\n <Code\n as=\"span\"\n variant=\"label\"\n className=\"tabular-nums text-foreground-neutral-muted\"\n >\n +{overflowCountLabel(hiddenCount)}\n </Code>\n </span>\n ) : null}\n </span>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n <JobStatusStripTooltip jobs={jobs} summary={summary} />\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction JobStatusStripTooltip({jobs, summary}: {jobs: WorkflowRunJobs; summary: string}) {\n // Naming the jobs that need attention is the whole point of hovering; succeeded jobs are\n // left to the counts so the tooltip stays a glance rather than a list. Only previewed jobs\n // can be named, but the count of those left over is read off the totals, so a failure past\n // the preview is still accounted for rather than silently missing.\n const named = jobs.preview\n .filter((job) => NOTABLE_STATUSES.includes(displayStatus(job)))\n .slice(0, MAX_TOOLTIP_JOB_NAMES);\n const notableTotal = NOTABLE_STATUSES.reduce((total, status) => total + countOf(jobs, status), 0);\n const remaining = notableTotal - named.length;\n\n return (\n <span className=\"flex max-w-[280px] flex-col gap-tight\">\n <Text as=\"span\" size=\"xs\" className=\"block\">\n {summary}\n </Text>\n {named.map((job) => (\n <Code as=\"span\" variant=\"label\" key={job.id} className=\"block truncate\">\n {getWorkflowStatusVisual(displayStatus(job)).label.toLowerCase()} · {job.name ?? job.key}\n </Code>\n ))}\n {remaining > 0 ? (\n <Text as=\"span\" size=\"xs\" className=\"block text-foreground-neutral-muted\">\n and {remaining} more\n </Text>\n ) : null}\n </span>\n );\n}\n\n/** \"12 jobs: 9 succeeded, 2 failed, 1 running\" — a count is not an accessible name on its own. */\nexport function jobStatusSummary(jobs: WorkflowRunJobs): string {\n const breakdown = [...jobs.statusCounts]\n .sort((left, right) => STATUS_SEVERITY[right.status] - STATUS_SEVERITY[left.status])\n .map(({status, count}) => `${count} ${getWorkflowStatusVisual(status).label.toLowerCase()}`)\n .join(', ');\n\n return `${jobs.total} ${jobs.total === 1 ? 'job' : 'jobs'}: ${breakdown}`;\n}\n\n/**\n * The worst status among the jobs the strip is not drawing, found by subtracting what it\n * draws from the run's totals rather than by inspecting jobs it was never sent.\n */\nfunction worstHiddenStatus(\n jobs: WorkflowRunJobs,\n visible: readonly WorkflowRunJobSummary[],\n): JobDisplayStatus | null {\n const hidden = new Map(jobs.statusCounts.map(({status, count}) => [status, count]));\n for (const job of visible) {\n const status = displayStatus(job);\n const remaining = (hidden.get(status) ?? 0) - 1;\n if (remaining > 0) hidden.set(status, remaining);\n else hidden.delete(status);\n }\n\n let worst: JobDisplayStatus | null = null;\n for (const [status, count] of hidden) {\n if (count <= 0) continue;\n if (worst === null || STATUS_SEVERITY[status] > STATUS_SEVERITY[worst]) worst = status;\n }\n return worst;\n}\n\nexport function overflowCountLabel(hiddenCount: number): string {\n return hiddenCount <= MAX_EXACT_OVERFLOW_COUNT\n ? String(hiddenCount)\n : COMPACT_COUNT_FORMAT.format(hiddenCount);\n}\n\nfunction countOf(jobs: WorkflowRunJobs, status: JobDisplayStatus): number {\n return jobs.statusCounts.find((entry) => entry.status === status)?.count ?? 0;\n}\n\nfunction displayStatus(job: WorkflowRunJobSummary): JobDisplayStatus {\n return deriveJobDisplayStatus({\n mode: job.mode,\n status: job.status,\n listenerStatus: job.listenerStatus,\n executionStatus: job.executionStatus,\n jobExecutions: [],\n });\n}\n"],"names":["Tooltip","TooltipContent","TooltipTrigger","Code","Text","cn","getWorkflowStatusVisual","WorkflowStatusIcon","deriveJobDisplayStatus","MAX_VISIBLE_JOBS","GLYPH_SIZE","MAX_TOOLTIP_JOB_NAMES","MAX_EXACT_OVERFLOW_COUNT","COMPACT_COUNT_FORMAT","Intl","NumberFormat","notation","maximumFractionDigits","STATUS_SEVERITY","failed","running","listening","pending","cancelled","skipped","succeeded","NOTABLE_STATUSES","JobStatusStrip","jobs","className","total","visible","preview","slice","hiddenCount","length","overflowStatus","worstHiddenStatus","summary","jobStatusSummary","asChild","span","role","aria-label","aria-hidden","map","job","status","displayStatus","size","ripple","tooltip","id","as","variant","overflowCountLabel","JobStatusStripTooltip","named","filter","includes","notableTotal","reduce","countOf","remaining","label","toLowerCase","name","key","breakdown","statusCounts","sort","left","right","count","join","hidden","Map","get","set","delete","worst","String","format","find","entry","mode","listenerStatus","executionStatus","jobExecutions"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"run-workspace-nav.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/run-workspace-nav.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAGpB,MAAM,mBAAmB,CAAC;AAG3B,KAAK,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;AAE3D,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,iBAAiB,CAAC;IACvB,aAAa,EAAE,mBAAmB,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,SAAS,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAC1C,iBAAiB,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;CACtD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAC9B,aAAa,EACb,WAAW,EACX,GAAG,EACH,aAAa,EACb,YAAY,EACZ,SAAc,EACd,iBAAiB,GAClB,EAAE,oBAAoB,+BA0DtB"}
1
+ {"version":3,"file":"run-workspace-nav.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/run-workspace-nav.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAGpB,MAAM,mBAAmB,CAAC;AAG3B,KAAK,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;AAE3D,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,iBAAiB,CAAC;IACvB,aAAa,EAAE,mBAAmB,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,SAAS,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAC1C,iBAAiB,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;CACtD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAC9B,aAAa,EACb,WAAW,EACX,GAAG,EACH,aAAa,EACb,YAAY,EACZ,SAAc,EACd,iBAAiB,GAClB,EAAE,oBAAoB,+BA8DtB"}
@@ -26,10 +26,11 @@ import { JobExecutionTimeText } from '../job-detail/job-execution-time-text.js';
26
26
  intervalMs: 1000,
27
27
  reducedMotionIntervalMs: 10_000,
28
28
  children: /*#__PURE__*/ _jsx("aside", {
29
- className: "w-full shrink-0 border-b border-border-neutral-base bg-background-neutral-background min-[768px]:w-240 min-[768px]:border-b-0 min-[768px]:border-r",
29
+ className: "flex min-h-0 w-full shrink-0 flex-col border-b border-border-neutral-base bg-background-neutral-background min-[768px]:w-240 min-[768px]:border-b-0 min-[768px]:border-r",
30
30
  children: /*#__PURE__*/ _jsxs(Collapsible, {
31
31
  open: mobileOpen,
32
32
  onOpenChange: setMobileOpen,
33
+ className: "flex min-h-0 flex-col min-[768px]:flex-1",
33
34
  children: [
34
35
  /*#__PURE__*/ _jsx(CollapsibleTrigger, {
35
36
  asChild: true,
@@ -63,7 +64,7 @@ import { JobExecutionTimeText } from '../job-detail/job-execution-time-text.js';
63
64
  })
64
65
  }),
65
66
  /*#__PURE__*/ _jsx("div", {
66
- className: cn('max-h-[50vh] overflow-y-auto p-tight min-[768px]:block min-[768px]:max-h-none min-[768px]:p-[12px]', !mobileOpen && 'hidden'),
67
+ className: cn('flex min-h-0 max-h-[50vh] flex-col overflow-y-auto p-tight scrollbar min-[768px]:min-h-0 min-[768px]:max-h-none min-[768px]:flex-1 min-[768px]:flex min-[768px]:overflow-hidden min-[768px]:p-[12px]', !mobileOpen && 'hidden'),
67
68
  children: /*#__PURE__*/ _jsx(RunWorkspaceNavContent, {
68
69
  workspaceSlug: workspaceSlug,
69
70
  projectSlug: projectSlug,
@@ -105,7 +106,7 @@ function RunWorkspaceNavContent({ workspaceSlug, projectSlug, run, jobs, activeS
105
106
  ]);
106
107
  return /*#__PURE__*/ _jsxs("nav", {
107
108
  "aria-label": "Run workspace",
108
- className: "flex min-w-0 flex-col gap-group",
109
+ className: "flex min-h-0 min-w-0 flex-1 flex-col gap-group",
109
110
  children: [
110
111
  /*#__PURE__*/ _jsx("ul", {
111
112
  children: /*#__PURE__*/ _jsx("li", {
@@ -122,7 +123,7 @@ function RunWorkspaceNavContent({ workspaceSlug, projectSlug, run, jobs, activeS
122
123
  }),
123
124
  /*#__PURE__*/ _jsxs("section", {
124
125
  "aria-labelledby": "run-workspace-jobs-heading",
125
- className: "min-w-0",
126
+ className: "flex min-h-0 min-w-0 flex-1 flex-col",
126
127
  children: [
127
128
  /*#__PURE__*/ _jsxs("div", {
128
129
  className: "flex items-center justify-between gap-inline px-tight pb-[6px]",
@@ -144,7 +145,7 @@ function RunWorkspaceNavContent({ workspaceSlug, projectSlug, run, jobs, activeS
144
145
  ]
145
146
  }),
146
147
  /*#__PURE__*/ _jsx("ol", {
147
- className: "max-h-[320px] overflow-y-auto",
148
+ className: "min-h-0 flex-1 overflow-y-auto scrollbar",
148
149
  children: jobs.map((job)=>{
149
150
  const current = job.id === currentJobId;
150
151
  const execution = defaultJobExecution(job);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-run-view/run-workspace-nav.tsx"],"sourcesContent":["import {Collapsible, CollapsibleTrigger} from '@shipfox/react-ui/collapsible';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport {useEffect, useMemo, useRef, useState} from 'react';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport type {RunAnnotationSummary} from '#core/run-annotation.js';\nimport {\n defaultJobExecution,\n deriveJobDisplayStatus,\n type Job,\n type WorkflowRunDetail,\n} from '#core/workflow-run.js';\nimport {\n type WorkflowJobSearch,\n type WorkflowRunTab,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {JobExecutionTimeText} from '../job-detail/job-execution-time-text.js';\n\ntype RunWorkspaceSection = Exclude<WorkflowRunTab, 'jobs'>;\n\nexport interface RunWorkspaceNavProps {\n workspaceSlug: string;\n projectSlug: string;\n run: WorkflowRunDetail;\n activeSection: RunWorkspaceSection;\n currentJobId?: string | undefined;\n jobSearch?: WorkflowJobSearch | undefined;\n annotationSummary?: RunAnnotationSummary | undefined;\n}\n\n/**\n * The stable navigation model shared by run-level and job-level routes. The graph explains\n * topology; this compact index makes moving between dedicated job pages predictable.\n */\nexport function RunWorkspaceNav({\n workspaceSlug,\n projectSlug,\n run,\n activeSection,\n currentJobId,\n jobSearch = {},\n annotationSummary,\n}: RunWorkspaceNavProps) {\n const jobs = useMemo(() => [...run.jobs].sort(compareJobs), [run.jobs]);\n const [mobileOpen, setMobileOpen] = useState(false);\n const currentJob = jobs.find((job) => job.id === currentJobId);\n const currentLabel = currentJob?.displayName ?? sectionLabel(activeSection);\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <aside className=\"w-full shrink-0 border-b border-border-neutral-base bg-background-neutral-background min-[768px]:w-240 min-[768px]:border-b-0 min-[768px]:border-r\">\n <Collapsible open={mobileOpen} onOpenChange={setMobileOpen}>\n <CollapsibleTrigger asChild>\n <button\n type=\"button\"\n className=\"flex min-h-44 w-full items-center justify-between gap-cluster px-row text-left outline-none focus-visible:shadow-border-interactive-with-active min-[768px]:hidden\"\n aria-label=\"Toggle run navigation\"\n >\n <span className=\"min-w-0\">\n <Text as=\"span\" size=\"xs\" className=\"block text-foreground-neutral-muted\">\n Run navigation\n </Text>\n <span className=\"block truncate font-code text-xs leading-20 text-foreground-neutral-base\">\n {currentLabel}\n </span>\n </span>\n <Icon\n name=\"chevronDown\"\n size={14}\n aria-hidden=\"true\"\n className={cn(\n 'shrink-0 text-foreground-neutral-muted transition-transform',\n mobileOpen && 'rotate-180',\n )}\n />\n </button>\n </CollapsibleTrigger>\n <div\n className={cn(\n 'max-h-[50vh] overflow-y-auto p-tight min-[768px]:block min-[768px]:max-h-none min-[768px]:p-[12px]',\n !mobileOpen && 'hidden',\n )}\n >\n <RunWorkspaceNavContent\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n run={run}\n jobs={jobs}\n activeSection={activeSection}\n currentJobId={currentJobId}\n jobSearch={jobSearch}\n annotationSummary={annotationSummary}\n mobileOpen={mobileOpen}\n onNavigate={() => setMobileOpen(false)}\n />\n </div>\n </Collapsible>\n </aside>\n </TimeTickerProvider>\n );\n}\n\nfunction RunWorkspaceNavContent({\n workspaceSlug,\n projectSlug,\n run,\n jobs,\n activeSection,\n currentJobId,\n jobSearch,\n annotationSummary,\n mobileOpen,\n onNavigate,\n}: RunWorkspaceNavProps & {\n jobs: Job[];\n jobSearch: WorkflowJobSearch;\n mobileOpen: boolean;\n onNavigate: () => void;\n}) {\n const currentRowRef = useRef<HTMLAnchorElement>(null);\n const runAttempt = jobSearch.runAttempt ?? run.runAttempt.attempt;\n\n useEffect(() => {\n if (!currentJobId || mobileOpen) return;\n currentRowRef.current?.scrollIntoView({block: 'nearest'});\n }, [currentJobId, mobileOpen]);\n\n useEffect(() => {\n if (!currentJobId || !mobileOpen) return;\n currentRowRef.current?.scrollIntoView({block: 'nearest'});\n }, [currentJobId, mobileOpen]);\n\n return (\n <nav aria-label=\"Run workspace\" className=\"flex min-w-0 flex-col gap-group\">\n <ul>\n <li>\n <RunSectionLink\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n runAttempt={runAttempt}\n section=\"summary\"\n current={!currentJobId && activeSection === 'summary'}\n onNavigate={onNavigate}\n />\n </li>\n </ul>\n\n <section aria-labelledby=\"run-workspace-jobs-heading\" className=\"min-w-0\">\n <div className=\"flex items-center justify-between gap-inline px-tight pb-[6px]\">\n <Text\n as=\"h2\"\n id=\"run-workspace-jobs-heading\"\n size=\"xs\"\n bold\n className=\"text-foreground-neutral-muted\"\n >\n Jobs\n </Text>\n <Text as=\"span\" size=\"xs\" className=\"font-code text-foreground-neutral-subtle\">\n {jobs.length}\n </Text>\n </div>\n <ol className=\"max-h-[320px] overflow-y-auto\">\n {jobs.map((job) => {\n const current = job.id === currentJobId;\n const execution = defaultJobExecution(job);\n const duration = execution?.displayDuration ?? job.displayDuration;\n\n return (\n <li key={job.id}>\n <Link\n ref={current ? currentRowRef : undefined}\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId\"\n params={{workspaceSlug, projectSlug, workflowRunId: run.id, jobId: job.id}}\n search={workflowJobSearchParams({runAttempt}) as never}\n onClick={onNavigate}\n aria-current={current ? 'page' : undefined}\n className={cn(\n 'flex min-h-32 min-w-0 items-center gap-inline rounded-4 px-tight text-left outline-none transition-colors hover:bg-background-neutral-hover focus-visible:shadow-border-interactive-with-active @max-[767px]:min-h-44 [@media(pointer:coarse)]:min-h-44',\n current && 'bg-background-neutral-hover',\n )}\n >\n <WorkflowStatusIcon\n status={deriveJobDisplayStatus(job)}\n size={12}\n tooltip={false}\n />\n <span className=\"min-w-0 truncate font-code text-xs leading-20 text-foreground-neutral-base\">\n {job.displayName}\n </span>\n <span className=\"ml-auto shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums\">\n {duration ? <JobExecutionTimeText time={duration} /> : '—'}\n </span>\n </Link>\n </li>\n );\n })}\n </ol>\n </section>\n\n <section aria-labelledby=\"run-workspace-details-heading\">\n <Text\n as=\"h2\"\n id=\"run-workspace-details-heading\"\n size=\"xs\"\n bold\n className=\"px-tight pb-[6px] text-foreground-neutral-muted\"\n >\n Run details\n </Text>\n <ul>\n <li>\n <RunSectionLink\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n runAttempt={runAttempt}\n section=\"annotations\"\n current={!currentJobId && activeSection === 'annotations'}\n count={annotationSummary?.total}\n countTruncated={annotationSummary?.truncated}\n onNavigate={onNavigate}\n />\n </li>\n <li>\n <RunSectionLink\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n runAttempt={runAttempt}\n section=\"source\"\n current={!currentJobId && activeSection === 'source'}\n onNavigate={onNavigate}\n />\n </li>\n </ul>\n </section>\n </nav>\n );\n}\n\nfunction RunSectionLink({\n workspaceSlug,\n projectSlug,\n runId,\n runAttempt,\n section,\n current,\n count,\n countTruncated = false,\n onNavigate,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n runId: string;\n runAttempt: number;\n section: RunWorkspaceSection;\n current: boolean;\n count?: number | undefined;\n /** The read hit its page budget, so the count is a lower bound and renders as `N+`. */\n countTruncated?: boolean | undefined;\n onNavigate?: (() => void) | undefined;\n}) {\n return (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId: runId}}\n search={\n workflowRunSearchParams(\n {runAttempt, ...(section === 'summary' ? {} : {tab: section})},\n {runAttempt},\n ) as never\n }\n onClick={onNavigate}\n aria-current={current ? 'page' : undefined}\n aria-label={\n count\n ? `${sectionLabel(section)}, ${countTruncated ? `${count} or more` : count}`\n : undefined\n }\n className={cn(\n 'flex min-h-32 items-center gap-inline rounded-4 px-tight text-xs font-medium text-foreground-neutral-base outline-none transition-colors hover:bg-background-neutral-hover focus-visible:shadow-border-interactive-with-active @max-[767px]:min-h-44 [@media(pointer:coarse)]:min-h-44',\n current && 'bg-background-neutral-hover',\n )}\n >\n {sectionLabel(section)}\n {count ? (\n <span\n aria-hidden=\"true\"\n className=\"ml-auto font-code text-xs text-foreground-neutral-muted tabular-nums\"\n >\n {count}\n {countTruncated ? '+' : ''}\n </span>\n ) : null}\n </Link>\n );\n}\n\nfunction sectionLabel(section: RunWorkspaceSection): string {\n if (section === 'annotations') return 'Annotations';\n if (section === 'source') return 'Source';\n return 'Summary';\n}\n\nfunction compareJobs(left: Job, right: Job): number {\n return left.position - right.position || left.id.localeCompare(right.id);\n}\n"],"names":["Collapsible","CollapsibleTrigger","Icon","TimeTickerProvider","Text","cn","Link","useEffect","useMemo","useRef","useState","WorkflowStatusIcon","defaultJobExecution","deriveJobDisplayStatus","workflowJobSearchParams","workflowRunSearchParams","JobExecutionTimeText","RunWorkspaceNav","workspaceSlug","projectSlug","run","activeSection","currentJobId","jobSearch","annotationSummary","jobs","sort","compareJobs","mobileOpen","setMobileOpen","currentJob","find","job","id","currentLabel","displayName","sectionLabel","intervalMs","reducedMotionIntervalMs","aside","className","open","onOpenChange","asChild","button","type","aria-label","span","as","size","name","aria-hidden","div","RunWorkspaceNavContent","onNavigate","currentRowRef","runAttempt","attempt","current","scrollIntoView","block","nav","ul","li","RunSectionLink","runId","section","aria-labelledby","bold","length","ol","map","execution","duration","displayDuration","ref","undefined","to","params","workflowRunId","jobId","search","onClick","aria-current","status","tooltip","time","count","total","countTruncated","truncated","tab","left","right","position","localeCompare"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/workflow-run-view/run-workspace-nav.tsx"],"sourcesContent":["import {Collapsible, CollapsibleTrigger} from '@shipfox/react-ui/collapsible';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport {useEffect, useMemo, useRef, useState} from 'react';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport type {RunAnnotationSummary} from '#core/run-annotation.js';\nimport {\n defaultJobExecution,\n deriveJobDisplayStatus,\n type Job,\n type WorkflowRunDetail,\n} from '#core/workflow-run.js';\nimport {\n type WorkflowJobSearch,\n type WorkflowRunTab,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {JobExecutionTimeText} from '../job-detail/job-execution-time-text.js';\n\ntype RunWorkspaceSection = Exclude<WorkflowRunTab, 'jobs'>;\n\nexport interface RunWorkspaceNavProps {\n workspaceSlug: string;\n projectSlug: string;\n run: WorkflowRunDetail;\n activeSection: RunWorkspaceSection;\n currentJobId?: string | undefined;\n jobSearch?: WorkflowJobSearch | undefined;\n annotationSummary?: RunAnnotationSummary | undefined;\n}\n\n/**\n * The stable navigation model shared by run-level and job-level routes. The graph explains\n * topology; this compact index makes moving between dedicated job pages predictable.\n */\nexport function RunWorkspaceNav({\n workspaceSlug,\n projectSlug,\n run,\n activeSection,\n currentJobId,\n jobSearch = {},\n annotationSummary,\n}: RunWorkspaceNavProps) {\n const jobs = useMemo(() => [...run.jobs].sort(compareJobs), [run.jobs]);\n const [mobileOpen, setMobileOpen] = useState(false);\n const currentJob = jobs.find((job) => job.id === currentJobId);\n const currentLabel = currentJob?.displayName ?? sectionLabel(activeSection);\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <aside className=\"flex min-h-0 w-full shrink-0 flex-col border-b border-border-neutral-base bg-background-neutral-background min-[768px]:w-240 min-[768px]:border-b-0 min-[768px]:border-r\">\n <Collapsible\n open={mobileOpen}\n onOpenChange={setMobileOpen}\n className=\"flex min-h-0 flex-col min-[768px]:flex-1\"\n >\n <CollapsibleTrigger asChild>\n <button\n type=\"button\"\n className=\"flex min-h-44 w-full items-center justify-between gap-cluster px-row text-left outline-none focus-visible:shadow-border-interactive-with-active min-[768px]:hidden\"\n aria-label=\"Toggle run navigation\"\n >\n <span className=\"min-w-0\">\n <Text as=\"span\" size=\"xs\" className=\"block text-foreground-neutral-muted\">\n Run navigation\n </Text>\n <span className=\"block truncate font-code text-xs leading-20 text-foreground-neutral-base\">\n {currentLabel}\n </span>\n </span>\n <Icon\n name=\"chevronDown\"\n size={14}\n aria-hidden=\"true\"\n className={cn(\n 'shrink-0 text-foreground-neutral-muted transition-transform',\n mobileOpen && 'rotate-180',\n )}\n />\n </button>\n </CollapsibleTrigger>\n <div\n className={cn(\n 'flex min-h-0 max-h-[50vh] flex-col overflow-y-auto p-tight scrollbar min-[768px]:min-h-0 min-[768px]:max-h-none min-[768px]:flex-1 min-[768px]:flex min-[768px]:overflow-hidden min-[768px]:p-[12px]',\n !mobileOpen && 'hidden',\n )}\n >\n <RunWorkspaceNavContent\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n run={run}\n jobs={jobs}\n activeSection={activeSection}\n currentJobId={currentJobId}\n jobSearch={jobSearch}\n annotationSummary={annotationSummary}\n mobileOpen={mobileOpen}\n onNavigate={() => setMobileOpen(false)}\n />\n </div>\n </Collapsible>\n </aside>\n </TimeTickerProvider>\n );\n}\n\nfunction RunWorkspaceNavContent({\n workspaceSlug,\n projectSlug,\n run,\n jobs,\n activeSection,\n currentJobId,\n jobSearch,\n annotationSummary,\n mobileOpen,\n onNavigate,\n}: RunWorkspaceNavProps & {\n jobs: Job[];\n jobSearch: WorkflowJobSearch;\n mobileOpen: boolean;\n onNavigate: () => void;\n}) {\n const currentRowRef = useRef<HTMLAnchorElement>(null);\n const runAttempt = jobSearch.runAttempt ?? run.runAttempt.attempt;\n\n useEffect(() => {\n if (!currentJobId || mobileOpen) return;\n currentRowRef.current?.scrollIntoView({block: 'nearest'});\n }, [currentJobId, mobileOpen]);\n\n useEffect(() => {\n if (!currentJobId || !mobileOpen) return;\n currentRowRef.current?.scrollIntoView({block: 'nearest'});\n }, [currentJobId, mobileOpen]);\n\n return (\n <nav aria-label=\"Run workspace\" className=\"flex min-h-0 min-w-0 flex-1 flex-col gap-group\">\n <ul>\n <li>\n <RunSectionLink\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n runAttempt={runAttempt}\n section=\"summary\"\n current={!currentJobId && activeSection === 'summary'}\n onNavigate={onNavigate}\n />\n </li>\n </ul>\n\n <section\n aria-labelledby=\"run-workspace-jobs-heading\"\n className=\"flex min-h-0 min-w-0 flex-1 flex-col\"\n >\n <div className=\"flex items-center justify-between gap-inline px-tight pb-[6px]\">\n <Text\n as=\"h2\"\n id=\"run-workspace-jobs-heading\"\n size=\"xs\"\n bold\n className=\"text-foreground-neutral-muted\"\n >\n Jobs\n </Text>\n <Text as=\"span\" size=\"xs\" className=\"font-code text-foreground-neutral-subtle\">\n {jobs.length}\n </Text>\n </div>\n <ol className=\"min-h-0 flex-1 overflow-y-auto scrollbar\">\n {jobs.map((job) => {\n const current = job.id === currentJobId;\n const execution = defaultJobExecution(job);\n const duration = execution?.displayDuration ?? job.displayDuration;\n\n return (\n <li key={job.id}>\n <Link\n ref={current ? currentRowRef : undefined}\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId\"\n params={{workspaceSlug, projectSlug, workflowRunId: run.id, jobId: job.id}}\n search={workflowJobSearchParams({runAttempt}) as never}\n onClick={onNavigate}\n aria-current={current ? 'page' : undefined}\n className={cn(\n 'flex min-h-32 min-w-0 items-center gap-inline rounded-4 px-tight text-left outline-none transition-colors hover:bg-background-neutral-hover focus-visible:shadow-border-interactive-with-active @max-[767px]:min-h-44 [@media(pointer:coarse)]:min-h-44',\n current && 'bg-background-neutral-hover',\n )}\n >\n <WorkflowStatusIcon\n status={deriveJobDisplayStatus(job)}\n size={12}\n tooltip={false}\n />\n <span className=\"min-w-0 truncate font-code text-xs leading-20 text-foreground-neutral-base\">\n {job.displayName}\n </span>\n <span className=\"ml-auto shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums\">\n {duration ? <JobExecutionTimeText time={duration} /> : '—'}\n </span>\n </Link>\n </li>\n );\n })}\n </ol>\n </section>\n\n <section aria-labelledby=\"run-workspace-details-heading\">\n <Text\n as=\"h2\"\n id=\"run-workspace-details-heading\"\n size=\"xs\"\n bold\n className=\"px-tight pb-[6px] text-foreground-neutral-muted\"\n >\n Run details\n </Text>\n <ul>\n <li>\n <RunSectionLink\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n runAttempt={runAttempt}\n section=\"annotations\"\n current={!currentJobId && activeSection === 'annotations'}\n count={annotationSummary?.total}\n countTruncated={annotationSummary?.truncated}\n onNavigate={onNavigate}\n />\n </li>\n <li>\n <RunSectionLink\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n runAttempt={runAttempt}\n section=\"source\"\n current={!currentJobId && activeSection === 'source'}\n onNavigate={onNavigate}\n />\n </li>\n </ul>\n </section>\n </nav>\n );\n}\n\nfunction RunSectionLink({\n workspaceSlug,\n projectSlug,\n runId,\n runAttempt,\n section,\n current,\n count,\n countTruncated = false,\n onNavigate,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n runId: string;\n runAttempt: number;\n section: RunWorkspaceSection;\n current: boolean;\n count?: number | undefined;\n /** The read hit its page budget, so the count is a lower bound and renders as `N+`. */\n countTruncated?: boolean | undefined;\n onNavigate?: (() => void) | undefined;\n}) {\n return (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId: runId}}\n search={\n workflowRunSearchParams(\n {runAttempt, ...(section === 'summary' ? {} : {tab: section})},\n {runAttempt},\n ) as never\n }\n onClick={onNavigate}\n aria-current={current ? 'page' : undefined}\n aria-label={\n count\n ? `${sectionLabel(section)}, ${countTruncated ? `${count} or more` : count}`\n : undefined\n }\n className={cn(\n 'flex min-h-32 items-center gap-inline rounded-4 px-tight text-xs font-medium text-foreground-neutral-base outline-none transition-colors hover:bg-background-neutral-hover focus-visible:shadow-border-interactive-with-active @max-[767px]:min-h-44 [@media(pointer:coarse)]:min-h-44',\n current && 'bg-background-neutral-hover',\n )}\n >\n {sectionLabel(section)}\n {count ? (\n <span\n aria-hidden=\"true\"\n className=\"ml-auto font-code text-xs text-foreground-neutral-muted tabular-nums\"\n >\n {count}\n {countTruncated ? '+' : ''}\n </span>\n ) : null}\n </Link>\n );\n}\n\nfunction sectionLabel(section: RunWorkspaceSection): string {\n if (section === 'annotations') return 'Annotations';\n if (section === 'source') return 'Source';\n return 'Summary';\n}\n\nfunction compareJobs(left: Job, right: Job): number {\n return left.position - right.position || left.id.localeCompare(right.id);\n}\n"],"names":["Collapsible","CollapsibleTrigger","Icon","TimeTickerProvider","Text","cn","Link","useEffect","useMemo","useRef","useState","WorkflowStatusIcon","defaultJobExecution","deriveJobDisplayStatus","workflowJobSearchParams","workflowRunSearchParams","JobExecutionTimeText","RunWorkspaceNav","workspaceSlug","projectSlug","run","activeSection","currentJobId","jobSearch","annotationSummary","jobs","sort","compareJobs","mobileOpen","setMobileOpen","currentJob","find","job","id","currentLabel","displayName","sectionLabel","intervalMs","reducedMotionIntervalMs","aside","className","open","onOpenChange","asChild","button","type","aria-label","span","as","size","name","aria-hidden","div","RunWorkspaceNavContent","onNavigate","currentRowRef","runAttempt","attempt","current","scrollIntoView","block","nav","ul","li","RunSectionLink","runId","section","aria-labelledby","bold","length","ol","map","execution","duration","displayDuration","ref","undefined","to","params","workflowRunId","jobId","search","onClick","aria-current","status","tooltip","time","count","total","countTruncated","truncated","tab","left","right","position","localeCompare"],"mappings":"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"}
@@ -35,6 +35,7 @@ interface JobExecutionFields {
35
35
  name: string;
36
36
  status: JobExecutionStatus;
37
37
  statusReason: string | null;
38
+ statusReasonMessage: string | null;
38
39
  runner: string[] | null;
39
40
  outputs: Record<string, unknown> | null;
40
41
  triggerEvents: WorkflowExecutionEvent[];
@@ -54,6 +55,7 @@ export declare class JobExecution {
54
55
  name: string;
55
56
  status: JobExecutionStatus;
56
57
  statusReason: string | null;
58
+ statusReasonMessage: string | null;
57
59
  runner: string[] | null;
58
60
  outputs: Record<string, unknown> | null;
59
61
  triggerEvents: WorkflowExecutionEvent[];
@@ -1 +1 @@
1
- {"version":3,"file":"job-execution.d.ts","sourceRoot":"","sources":["../../../src/core/entities/job-execution.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"job-execution.d.ts","sourceRoot":"","sources":["../../../src/core/entities/job-execution.ts"],"names":[],"mappings":"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"}