@shipfox/client-workflows 16.0.0 → 17.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.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +20 -0
- package/dist/components/identifier/identifier.js +2 -2
- package/dist/components/identifier/identifier.js.map +1 -1
- package/dist/components/job-detail/agent-config-failure-callout.d.ts.map +1 -1
- package/dist/components/job-detail/agent-config-failure-callout.js +1 -1
- package/dist/components/job-detail/agent-config-failure-callout.js.map +1 -1
- package/dist/components/job-detail/job-context-panel.d.ts +6 -0
- package/dist/components/job-detail/job-context-panel.d.ts.map +1 -0
- package/dist/components/job-detail/job-context-panel.js +224 -0
- package/dist/components/job-detail/job-context-panel.js.map +1 -0
- package/dist/components/job-detail/job-detail-header.d.ts +3 -1
- package/dist/components/job-detail/job-detail-header.d.ts.map +1 -1
- package/dist/components/job-detail/job-detail-header.js +54 -48
- package/dist/components/job-detail/job-detail-header.js.map +1 -1
- package/dist/components/job-detail/job-detail-view.d.ts +1 -1
- package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
- package/dist/components/job-detail/job-detail-view.js +80 -44
- package/dist/components/job-detail/job-detail-view.js.map +1 -1
- package/dist/components/job-detail/job-empty-states.d.ts.map +1 -1
- package/dist/components/job-detail/job-empty-states.js +19 -10
- package/dist/components/job-detail/job-empty-states.js.map +1 -1
- package/dist/components/job-detail/job-execution-switcher.js +4 -4
- package/dist/components/job-detail/job-execution-switcher.js.map +1 -1
- package/dist/components/job-detail/json-code.d.ts +14 -0
- package/dist/components/job-detail/json-code.d.ts.map +1 -0
- package/dist/components/job-detail/json-code.js +93 -0
- package/dist/components/job-detail/json-code.js.map +1 -0
- package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -1
- package/dist/components/job-detail/step-attempt-log-panel.js +9 -6
- package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -1
- package/dist/components/job-detail/step-troubleshooting.d.ts +18 -0
- package/dist/components/job-detail/step-troubleshooting.d.ts.map +1 -0
- package/dist/components/job-detail/step-troubleshooting.js +493 -0
- package/dist/components/job-detail/step-troubleshooting.js.map +1 -0
- package/dist/components/job-graph/job-graph-content.d.ts.map +1 -1
- package/dist/components/job-graph/job-graph-content.js +5 -4
- package/dist/components/job-graph/job-graph-content.js.map +1 -1
- package/dist/components/job-graph/job-node.js +3 -3
- package/dist/components/job-graph/job-node.js.map +1 -1
- package/dist/components/step-list/index.d.ts +1 -0
- package/dist/components/step-list/index.d.ts.map +1 -1
- package/dist/components/step-list/index.js.map +1 -1
- package/dist/components/step-list/step-list-model.d.ts.map +1 -1
- package/dist/components/step-list/step-list-model.js +5 -0
- package/dist/components/step-list/step-list-model.js.map +1 -1
- package/dist/components/step-list/step-list.d.ts +5 -1
- package/dist/components/step-list/step-list.d.ts.map +1 -1
- package/dist/components/step-list/step-list.js +117 -82
- package/dist/components/step-list/step-list.js.map +1 -1
- package/dist/components/workflow-run-duration-label.js +1 -1
- package/dist/components/workflow-run-duration-label.js.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.js +6 -6
- package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js +1 -1
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filters.js +4 -4
- package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.js +6 -6
- package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +7 -7
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +0 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +8 -13
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-tabs/index.d.ts +1 -1
- package/dist/components/workflow-run-tabs/index.d.ts.map +1 -1
- package/dist/components/workflow-run-tabs/index.js.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-count-chip.js +2 -2
- package/dist/components/workflow-run-tabs/run-annotation-count-chip.js.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-list.d.ts +9 -2
- package/dist/components/workflow-run-tabs/run-annotation-list.d.ts.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-list.js +28 -19
- package/dist/components/workflow-run-tabs/run-annotation-list.js.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +5 -5
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.js +7 -7
- package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.js +6 -6
- package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +72 -12
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/core/entities/job-execution.d.ts +7 -0
- package/dist/core/entities/job-execution.d.ts.map +1 -1
- package/dist/core/entities/job-execution.js.map +1 -1
- package/dist/core/entities/job.d.ts +9 -0
- package/dist/core/entities/job.d.ts.map +1 -1
- package/dist/core/entities/job.js.map +1 -1
- package/dist/core/entities/step-attempt.d.ts +29 -0
- package/dist/core/entities/step-attempt.d.ts.map +1 -1
- package/dist/core/entities/step-attempt.js.map +1 -1
- package/dist/core/entities/step.d.ts +3 -1
- package/dist/core/entities/step.d.ts.map +1 -1
- package/dist/core/entities/step.js.map +1 -1
- package/dist/core/run-annotation.d.ts +6 -0
- package/dist/core/run-annotation.d.ts.map +1 -1
- package/dist/core/run-annotation.js.map +1 -1
- package/dist/core/workflow-run.d.ts +1 -1
- package/dist/core/workflow-run.d.ts.map +1 -1
- package/dist/core/workflow-run.js.map +1 -1
- package/dist/hooks/api/annotations.d.ts +63 -0
- package/dist/hooks/api/annotations.d.ts.map +1 -0
- package/dist/hooks/api/annotations.js +145 -0
- package/dist/hooks/api/annotations.js.map +1 -0
- package/dist/hooks/api/step-attempt-detail.d.ts +14 -0
- package/dist/hooks/api/step-attempt-detail.d.ts.map +1 -0
- package/dist/hooks/api/step-attempt-detail.js +43 -0
- package/dist/hooks/api/step-attempt-detail.js.map +1 -0
- package/dist/hooks/api/workflow-run-mapper.d.ts +9 -2
- package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.js +47 -0
- package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
- package/dist/pages/project-workflows-page.js +19 -19
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/pages/workflow-run-list-page.js +1 -1
- package/dist/pages/workflow-run-list-page.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/components/identifier/identifier.tsx +2 -2
- package/src/components/job-detail/agent-config-failure-callout.tsx +5 -1
- package/src/components/job-detail/job-context-panel.test.tsx +72 -0
- package/src/components/job-detail/job-context-panel.tsx +237 -0
- package/src/components/job-detail/job-detail-header.tsx +52 -46
- package/src/components/job-detail/job-detail-view.stories.tsx +555 -53
- package/src/components/job-detail/job-detail-view.tsx +87 -45
- package/src/components/job-detail/job-empty-states.tsx +23 -10
- package/src/components/job-detail/job-execution-switcher.tsx +5 -5
- package/src/components/job-detail/json-code.tsx +120 -0
- package/src/components/job-detail/step-attempt-log-panel.test.tsx +1 -0
- package/src/components/job-detail/step-attempt-log-panel.tsx +17 -6
- package/src/components/job-detail/step-troubleshooting.stories.tsx +120 -0
- package/src/components/job-detail/step-troubleshooting.test.tsx +337 -0
- package/src/components/job-detail/step-troubleshooting.tsx +537 -0
- package/src/components/job-graph/job-graph-content.tsx +4 -3
- package/src/components/job-graph/job-node.tsx +3 -3
- package/src/components/step-list/index.ts +1 -0
- package/src/components/step-list/step-list-model.ts +5 -0
- package/src/components/step-list/step-list.test.tsx +75 -2
- package/src/components/step-list/step-list.tsx +73 -20
- package/src/components/workflow-run-duration-label.tsx +1 -1
- package/src/components/workflow-run-list/job-status-strip.tsx +6 -6
- package/src/components/workflow-run-list/workflow-run-filter-menu.tsx +1 -1
- package/src/components/workflow-run-list/workflow-run-filters.tsx +4 -4
- package/src/components/workflow-run-list/workflow-run-list-states.tsx +6 -6
- package/src/components/workflow-run-list/workflow-run-list-view.tsx +1 -1
- package/src/components/workflow-run-list/workflow-run-row.tsx +7 -7
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +1 -1
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +1 -1
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +11 -11
- package/src/components/workflow-run-tabs/index.ts +5 -1
- package/src/components/workflow-run-tabs/run-annotation-count-chip.tsx +2 -2
- package/src/components/workflow-run-tabs/run-annotation-list.tsx +25 -10
- package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +5 -5
- package/src/components/workflow-run-view/run-workspace-nav.tsx +7 -7
- package/src/components/workflow-run-view/workflow-run-states.tsx +6 -6
- package/src/components/workflow-run-view/workflow-run-view.tsx +114 -14
- package/src/core/entities/job-execution.ts +7 -0
- package/src/core/entities/job.ts +9 -0
- package/src/core/entities/step-attempt.ts +33 -0
- package/src/core/entities/step.ts +3 -1
- package/src/core/run-annotation.ts +6 -0
- package/src/core/workflow-run.ts +4 -0
- package/src/hooks/api/annotations.test.ts +84 -0
- package/src/hooks/api/annotations.ts +240 -0
- package/src/hooks/api/step-attempt-detail.ts +67 -0
- package/src/hooks/api/workflow-run-mapper.ts +46 -0
- package/src/hooks/api/workflow-runs.test.tsx +61 -1
- package/src/pages/project-workflows-page.tsx +19 -19
- package/src/pages/workflow-job-detail-page.test.tsx +52 -1
- package/src/pages/workflow-run-list-page.tsx +1 -1
- package/test/fixtures/workflow-run.ts +7 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/job-detail/job-empty-states.tsx"],"sourcesContent":["import {EmptyState} from '@shipfox/react-ui/empty-state';\nimport type {Job, JobExecution, Step, StepError} from '#core/workflow-run.js';\nimport {\n AGENT_CONFIG_ISSUES,\n deriveJobDisplayStatus,\n deriveJobExecutionDisplayStatus,\n STEP_ERROR_REASONS,\n} from '#core/workflow-run.js';\nimport type {StepListEmptyState} from '../step-list/index.js';\nimport {formatJobExecutionTime} from './job-execution-time-text.js';\n\nexport function emptyStateForJob(\n job: Job,\n jobExecution: JobExecution,\n): StepListEmptyState | undefined {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n const displayStatus =\n job.mode === 'listening'\n ? deriveJobExecutionDisplayStatus(jobExecution)\n : deriveJobDisplayStatus(job);\n\n if (jobExecution.status === 'running' && displayStatus === 'pending') {\n return {\n title: 'Runner preparing job',\n description: 'A runner is assigned. Steps will appear here when work begins.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'Steps will appear here once the job starts.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'running') {\n return {\n title: 'Waiting for the first step',\n description: 'The first running step will appear here shortly.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before any step started.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'failed') {\n return {\n title: 'Job failed before its first step started',\n description: preStepFailureDescription(jobExecution.statusReason ?? job.statusReason),\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'succeeded') {\n return {\n title: 'Completed without recorded steps',\n description: `Execution #${jobExecution.sequence} succeeded before any step output was recorded.`,\n status: displayStatus,\n };\n }\n\n return undefined;\n}\n\nexport function emptyStateForMissingExecution(job: Job): StepListEmptyState {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'listening') {\n return {\n title: 'Waiting for trigger events',\n description: 'Matching trigger events will create job executions here.',\n status: deriveJobDisplayStatus(job),\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'resolved') {\n return {\n title: 'Listener resolved without executions',\n description: 'No matching trigger event created a job execution before the listener stopped.',\n status: job.status,\n };\n }\n\n if (job.status === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'No execution has been created for this job yet.',\n status: 'pending',\n };\n }\n\n if (job.status === 'skipped') {\n return {\n title: 'This job was skipped',\n description: skippedJobDescription(job.statusReason),\n status: 'skipped',\n };\n }\n\n if (job.status === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before an execution was created.',\n status: 'cancelled',\n };\n }\n\n return {\n title: 'Execution details unavailable',\n description: 'This job finished, but no job execution record is available.',\n status: job.status,\n };\n}\n\nexport function skippedJobDescription(reason: Job['statusReason']): string {\n switch (reason) {\n case 'dependency_not_completed':\n return 'A required job did not complete, so this job was skipped.';\n case 'default_gate_rejected':\n return 'A required job did not succeed, so this job was skipped.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not match, so this job was skipped.';\n case 'condition_errored':\n return 'The job condition could not be evaluated, so this job was skipped.';\n case 'user_cancelled':\n case 'run_cancelled':\n case 'timed_out':\n case 'runner_lost':\n case 'step_failed':\n case 'unknown':\n case null:\n return 'This job did not start.';\n }\n}\n\nfunction preStepFailureDescription(reason: string | null): string {\n switch (reason) {\n case 'runner_lost':\n return 'The runner stopped responding before work began. Check runner availability before re-running the workflow.';\n case 'timed_out':\n return 'The job timed out before work began. Check runner capacity and timeout configuration before re-running the workflow.';\n case 'user_cancelled':\n case 'run_cancelled':\n return 'The execution ended while the run was being cancelled.';\n case 'step_failed':\n return 'The execution failed before step details were recorded. Review run annotations before re-running the workflow.';\n case 'condition_errored':\n return 'The job condition could not be evaluated. Review run annotations before re-running the workflow.';\n case 'dependency_not_completed':\n case 'default_gate_rejected':\n return 'A required job did not complete successfully. Resolve that failure before re-running the workflow.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not match, so no work started.';\n case 'unknown':\n case null:\n return 'Shipfox did not record a failure reason. Re-run only after checking the runner and workflow configuration.';\n default:\n return `The execution ended because ${humanizeFailureReason(reason)}. Resolve that condition before re-running the workflow.`;\n }\n}\n\nfunction humanizeFailureReason(reason: string): string {\n return reason.replaceAll('_', ' ').replaceAll('-', ' ');\n}\n\nexport function CarriedOverStepPanel() {\n return (\n <EmptyState\n className=\"min-h-120 rounded-8 border border-border-neutral-base bg-background-components-base px-16 py-20\"\n icon=\"componentLine\"\n title=\"Carried over from a previous attempt\"\n description=\"Not executed in this run.\"\n variant=\"compact\"\n />\n );\n}\n\nexport function toSelectedAttemptError(\n step: Step,\n error: Record<string, unknown> | null,\n): StepError | null {\n if (error === null) return null;\n\n const rawReason = error.reason;\n const parsedReason =\n typeof rawReason === 'string' &&\n STEP_ERROR_REASONS.has(rawReason as StepError['reason'] & string)\n ? (rawReason as NonNullable<StepError['reason']>)\n : undefined;\n const rawAgentConfigIssue = error.agentConfigIssue ?? error.agent_config_issue;\n const agentConfigIssue =\n typeof rawAgentConfigIssue === 'string' &&\n AGENT_CONFIG_ISSUES.has(rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n ? (rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n : undefined;\n const exitCode = error.exitCode ?? error.exit_code;\n const resolvedReason = parsedReason\n ? parsedReason\n : agentConfigIssue\n ? 'agent_config_invalid'\n : undefined;\n\n if (resolvedReason === undefined) return null;\n\n return {\n message: typeof error.message === 'string' ? error.message : '',\n exitCode: exitCode === null || typeof exitCode === 'number' ? exitCode : null,\n signal: typeof error.signal === 'string' ? error.signal : undefined,\n reason: resolvedReason,\n agentConfigIssue,\n category: step.type === 'setup' || step.type === 'checkout' ? 'setup' : 'user',\n };\n}\n\nexport function isAgentConfigFailure(step: Step, error: StepError | null): boolean {\n return step.type === 'agent' && error?.reason === 'agent_config_invalid';\n}\n\nexport function jobSucceededSummary(job: Job, execution: JobExecution): string | undefined {\n if (job.carriedOver || execution.status !== 'succeeded') return undefined;\n const stepCount = execution.steps.filter((step) => step.status === 'succeeded').length;\n if (stepCount === 0) return undefined;\n const duration = execution.displayDuration;\n return `${stepCount} step${stepCount === 1 ? '' : 's'} succeeded${duration ? ` in ${formatJobExecutionTime(duration)}` : ''}`;\n}\n"],"names":["EmptyState","AGENT_CONFIG_ISSUES","deriveJobDisplayStatus","deriveJobExecutionDisplayStatus","STEP_ERROR_REASONS","formatJobExecutionTime","emptyStateForJob","job","jobExecution","carriedOver","title","description","status","displayStatus","mode","preStepFailureDescription","statusReason","sequence","undefined","emptyStateForMissingExecution","listenerStatus","skippedJobDescription","reason","humanizeFailureReason","replaceAll","CarriedOverStepPanel","className","icon","variant","toSelectedAttemptError","step","error","rawReason","parsedReason","has","rawAgentConfigIssue","agentConfigIssue","agent_config_issue","exitCode","exit_code","resolvedReason","message","signal","category","type","isAgentConfigFailure","jobSucceededSummary","execution","stepCount","steps","filter","length","duration","displayDuration"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/job-detail/job-empty-states.tsx"],"sourcesContent":["import {EmptyState} from '@shipfox/react-ui/empty-state';\nimport type {Job, JobExecution, Step, StepError} from '#core/workflow-run.js';\nimport {\n AGENT_CONFIG_ISSUES,\n deriveJobDisplayStatus,\n deriveJobExecutionDisplayStatus,\n STEP_ERROR_REASONS,\n} from '#core/workflow-run.js';\nimport type {StepListEmptyState} from '../step-list/index.js';\nimport {formatJobExecutionTime} from './job-execution-time-text.js';\n\nexport function emptyStateForJob(\n job: Job,\n jobExecution: JobExecution,\n): StepListEmptyState | undefined {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n const displayStatus =\n job.mode === 'listening'\n ? deriveJobExecutionDisplayStatus(jobExecution)\n : deriveJobDisplayStatus(job);\n const runner = jobExecution.runner?.length ? jobExecution.runner : job.runner;\n\n if (jobExecution.status === 'running' && displayStatus === 'pending') {\n return {\n title: runner?.length ? 'Runner preparing job' : 'Waiting for a runner',\n description: runner?.length\n ? `Runner ${runner.join(', ')} is preparing the job. Steps will appear here when work begins.`\n : 'No runner has claimed this job yet. Steps will appear here when a runner starts work.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'Steps will appear here once the job starts.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'running') {\n return {\n title: 'Waiting for the first step',\n description: 'The first running step will appear here shortly.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before any step started.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'failed') {\n return {\n title: 'Job failed before its first step started',\n description: preStepFailureDescription(jobExecution.statusReason ?? job.statusReason, runner),\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'succeeded') {\n return {\n title: 'Completed without recorded steps',\n description: `Execution #${jobExecution.sequence} succeeded before any step output was recorded.`,\n status: displayStatus,\n };\n }\n\n return undefined;\n}\n\nexport function emptyStateForMissingExecution(job: Job): StepListEmptyState {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'listening') {\n return {\n title: 'Waiting for trigger events',\n description: 'Matching trigger events will create job executions here.',\n status: deriveJobDisplayStatus(job),\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'resolved') {\n return {\n title: 'Listener resolved without executions',\n description: 'No matching trigger event created a job execution before the listener stopped.',\n status: job.status,\n };\n }\n\n if (job.status === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'No execution has been created for this job yet.',\n status: 'pending',\n };\n }\n\n if (job.status === 'skipped') {\n return {\n title: 'This job was skipped',\n description: skippedJobDescription(job.statusReason),\n status: 'skipped',\n };\n }\n\n if (job.status === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before an execution was created.',\n status: 'cancelled',\n };\n }\n\n if (job.status === 'failed') {\n return {\n title: 'Job failed before an execution was created',\n description: preStepFailureDescription(job.statusReason, job.runner),\n status: 'failed',\n };\n }\n\n return {\n title: 'Execution details unavailable',\n description: 'This job finished, but no job execution record is available.',\n status: job.status,\n };\n}\n\nexport function skippedJobDescription(reason: Job['statusReason']): string {\n switch (reason) {\n case 'dependency_not_completed':\n return 'A required job did not complete, so this job was skipped.';\n case 'default_gate_rejected':\n return 'A required job did not succeed, so this job was skipped.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not match, so this job was skipped.';\n case 'condition_errored':\n return 'The job condition could not be evaluated, so this job was skipped.';\n case 'user_cancelled':\n case 'run_cancelled':\n case 'timed_out':\n case 'runner_lost':\n case 'step_failed':\n case 'unknown':\n case null:\n return 'This job did not start.';\n }\n}\n\nfunction preStepFailureDescription(reason: string | null, runner: string[] | null = null): string {\n const runnerCopy = runner?.length ? ` Required runner labels: ${runner.join(', ')}.` : '';\n\n switch (reason) {\n case 'runner_lost':\n return `The runner stopped responding before work began.${runnerCopy} Check runner availability before re-running the workflow.`;\n case 'timed_out':\n return `The job timed out before work began.${runnerCopy} Check runner capacity and timeout configuration before re-running the workflow.`;\n case 'user_cancelled':\n case 'run_cancelled':\n return 'The execution ended while the run was being cancelled.';\n case 'step_failed':\n return `The execution failed before step details were recorded.${runnerCopy} Review run annotations before re-running the workflow.`;\n case 'condition_errored':\n return 'The job condition could not be evaluated. Review run annotations before re-running the workflow.';\n case 'dependency_not_completed':\n case 'default_gate_rejected':\n return 'A required job did not complete successfully. Resolve that failure before re-running the workflow.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not match, so no work started.';\n case 'unknown':\n case null:\n return `Shipfox did not record a failure reason.${runnerCopy} Re-run only after checking the runner and workflow configuration.`;\n default:\n return `The execution ended because ${humanizeFailureReason(reason)}.${runnerCopy} Resolve that condition before re-running the workflow.`;\n }\n}\n\nfunction humanizeFailureReason(reason: string): string {\n return reason.replaceAll('_', ' ').replaceAll('-', ' ');\n}\n\nexport function CarriedOverStepPanel() {\n return (\n <EmptyState\n className=\"min-h-120 rounded-8 border border-border-neutral-base bg-background-components-base p-panel\"\n icon=\"componentLine\"\n title=\"Carried over from a previous attempt\"\n description=\"Not executed in this run.\"\n variant=\"compact\"\n />\n );\n}\n\nexport function toSelectedAttemptError(\n step: Step,\n error: Record<string, unknown> | null,\n): StepError | null {\n if (error === null) return null;\n\n const rawReason = error.reason;\n const parsedReason =\n typeof rawReason === 'string' &&\n STEP_ERROR_REASONS.has(rawReason as StepError['reason'] & string)\n ? (rawReason as NonNullable<StepError['reason']>)\n : undefined;\n const rawAgentConfigIssue = error.agentConfigIssue ?? error.agent_config_issue;\n const agentConfigIssue =\n typeof rawAgentConfigIssue === 'string' &&\n AGENT_CONFIG_ISSUES.has(rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n ? (rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n : undefined;\n const exitCode = error.exitCode ?? error.exit_code;\n const resolvedReason = parsedReason\n ? parsedReason\n : agentConfigIssue\n ? 'agent_config_invalid'\n : undefined;\n\n if (resolvedReason === undefined) return null;\n\n return {\n message: typeof error.message === 'string' ? error.message : '',\n exitCode: exitCode === null || typeof exitCode === 'number' ? exitCode : null,\n signal: typeof error.signal === 'string' ? error.signal : undefined,\n reason: resolvedReason,\n agentConfigIssue,\n category: step.type === 'setup' || step.type === 'checkout' ? 'setup' : 'user',\n };\n}\n\nexport function isAgentConfigFailure(step: Step, error: StepError | null): boolean {\n return step.type === 'agent' && error?.reason === 'agent_config_invalid';\n}\n\nexport function jobSucceededSummary(job: Job, execution: JobExecution): string | undefined {\n if (job.carriedOver || execution.status !== 'succeeded') return undefined;\n const stepCount = execution.steps.filter((step) => step.status === 'succeeded').length;\n if (stepCount === 0) return undefined;\n const duration = execution.displayDuration;\n return `${stepCount} step${stepCount === 1 ? '' : 's'} succeeded${duration ? ` in ${formatJobExecutionTime(duration)}` : ''}`;\n}\n"],"names":["EmptyState","AGENT_CONFIG_ISSUES","deriveJobDisplayStatus","deriveJobExecutionDisplayStatus","STEP_ERROR_REASONS","formatJobExecutionTime","emptyStateForJob","job","jobExecution","carriedOver","title","description","status","displayStatus","mode","runner","length","join","preStepFailureDescription","statusReason","sequence","undefined","emptyStateForMissingExecution","listenerStatus","skippedJobDescription","reason","runnerCopy","humanizeFailureReason","replaceAll","CarriedOverStepPanel","className","icon","variant","toSelectedAttemptError","step","error","rawReason","parsedReason","has","rawAgentConfigIssue","agentConfigIssue","agent_config_issue","exitCode","exit_code","resolvedReason","message","signal","category","type","isAgentConfigFailure","jobSucceededSummary","execution","stepCount","steps","filter","duration","displayDuration"],"mappings":";AAAA,SAAQA,UAAU,QAAO,gCAAgC;AAEzD,SACEC,mBAAmB,EACnBC,sBAAsB,EACtBC,+BAA+B,EAC/BC,kBAAkB,QACb,wBAAwB;AAE/B,SAAQC,sBAAsB,QAAO,+BAA+B;AAEpE,OAAO,SAASC,iBACdC,GAAQ,EACRC,YAA0B;IAE1B,IAAID,IAAIE,WAAW,EAAE;QACnB,OAAO;YACLC,OAAO;YACPC,aAAa;YACbC,QAAQ;QACV;IACF;IAEA,MAAMC,gBACJN,IAAIO,IAAI,KAAK,cACTX,gCAAgCK,gBAChCN,uBAAuBK;IAC7B,MAAMQ,SAASP,aAAaO,MAAM,EAAEC,SAASR,aAAaO,MAAM,GAAGR,IAAIQ,MAAM;IAE7E,IAAIP,aAAaI,MAAM,KAAK,aAAaC,kBAAkB,WAAW;QACpE,OAAO;YACLH,OAAOK,QAAQC,SAAS,yBAAyB;YACjDL,aAAaI,QAAQC,SACjB,CAAC,OAAO,EAAED,OAAOE,IAAI,CAAC,MAAM,+DAA+D,CAAC,GAC5F;YACJL,QAAQC;QACV;IACF;IAEA,IAAIA,kBAAkB,WAAW;QAC/B,OAAO;YACLH,OAAO;YACPC,aAAa;YACbC,QAAQC;QACV;IACF;IAEA,IAAIA,kBAAkB,WAAW;QAC/B,OAAO;YACLH,OAAO;YACPC,aAAa;YACbC,QAAQC;QACV;IACF;IAEA,IAAIA,kBAAkB,aAAa;QACjC,OAAO;YACLH,OAAO;YACPC,aAAa;YACbC,QAAQC;QACV;IACF;IAEA,IAAIA,kBAAkB,UAAU;QAC9B,OAAO;YACLH,OAAO;YACPC,aAAaO,0BAA0BV,aAAaW,YAAY,IAAIZ,IAAIY,YAAY,EAAEJ;YACtFH,QAAQC;QACV;IACF;IAEA,IAAIA,kBAAkB,aAAa;QACjC,OAAO;YACLH,OAAO;YACPC,aAAa,CAAC,WAAW,EAAEH,aAAaY,QAAQ,CAAC,+CAA+C,CAAC;YACjGR,QAAQC;QACV;IACF;IAEA,OAAOQ;AACT;AAEA,OAAO,SAASC,8BAA8Bf,GAAQ;IACpD,IAAIA,IAAIE,WAAW,EAAE;QACnB,OAAO;YACLC,OAAO;YACPC,aAAa;YACbC,QAAQ;QACV;IACF;IAEA,IAAIL,IAAIO,IAAI,KAAK,eAAeP,IAAIgB,cAAc,KAAK,aAAa;QAClE,OAAO;YACLb,OAAO;YACPC,aAAa;YACbC,QAAQV,uBAAuBK;QACjC;IACF;IAEA,IAAIA,IAAIO,IAAI,KAAK,eAAeP,IAAIgB,cAAc,KAAK,YAAY;QACjE,OAAO;YACLb,OAAO;YACPC,aAAa;YACbC,QAAQL,IAAIK,MAAM;QACpB;IACF;IAEA,IAAIL,IAAIK,MAAM,KAAK,WAAW;QAC5B,OAAO;YACLF,OAAO;YACPC,aAAa;YACbC,QAAQ;QACV;IACF;IAEA,IAAIL,IAAIK,MAAM,KAAK,WAAW;QAC5B,OAAO;YACLF,OAAO;YACPC,aAAaa,sBAAsBjB,IAAIY,YAAY;YACnDP,QAAQ;QACV;IACF;IAEA,IAAIL,IAAIK,MAAM,KAAK,aAAa;QAC9B,OAAO;YACLF,OAAO;YACPC,aAAa;YACbC,QAAQ;QACV;IACF;IAEA,IAAIL,IAAIK,MAAM,KAAK,UAAU;QAC3B,OAAO;YACLF,OAAO;YACPC,aAAaO,0BAA0BX,IAAIY,YAAY,EAAEZ,IAAIQ,MAAM;YACnEH,QAAQ;QACV;IACF;IAEA,OAAO;QACLF,OAAO;QACPC,aAAa;QACbC,QAAQL,IAAIK,MAAM;IACpB;AACF;AAEA,OAAO,SAASY,sBAAsBC,MAA2B;IAC/D,OAAQA;QACN,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;QACL,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;YACH,OAAO;IACX;AACF;AAEA,SAASP,0BAA0BO,MAAqB,EAAEV,SAA0B,IAAI;IACtF,MAAMW,aAAaX,QAAQC,SAAS,CAAC,yBAAyB,EAAED,OAAOE,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG;IAEvF,OAAQQ;QACN,KAAK;YACH,OAAO,CAAC,gDAAgD,EAAEC,WAAW,0DAA0D,CAAC;QAClI,KAAK;YACH,OAAO,CAAC,oCAAoC,EAAEA,WAAW,gFAAgF,CAAC;QAC5I,KAAK;QACL,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO,CAAC,uDAAuD,EAAEA,WAAW,uDAAuD,CAAC;QACtI,KAAK;YACH,OAAO;QACT,KAAK;QACL,KAAK;YACH,OAAO;QACT,KAAK;QACL,KAAK;YACH,OAAO;QACT,KAAK;QACL,KAAK;YACH,OAAO,CAAC,wCAAwC,EAAEA,WAAW,kEAAkE,CAAC;QAClI;YACE,OAAO,CAAC,4BAA4B,EAAEC,sBAAsBF,QAAQ,CAAC,EAAEC,WAAW,uDAAuD,CAAC;IAC9I;AACF;AAEA,SAASC,sBAAsBF,MAAc;IAC3C,OAAOA,OAAOG,UAAU,CAAC,KAAK,KAAKA,UAAU,CAAC,KAAK;AACrD;AAEA,OAAO,SAASC;IACd,qBACE,KAAC7B;QACC8B,WAAU;QACVC,MAAK;QACLrB,OAAM;QACNC,aAAY;QACZqB,SAAQ;;AAGd;AAEA,OAAO,SAASC,uBACdC,IAAU,EACVC,KAAqC;IAErC,IAAIA,UAAU,MAAM,OAAO;IAE3B,MAAMC,YAAYD,MAAMV,MAAM;IAC9B,MAAMY,eACJ,OAAOD,cAAc,YACrBhC,mBAAmBkC,GAAG,CAACF,aAClBA,YACDf;IACN,MAAMkB,sBAAsBJ,MAAMK,gBAAgB,IAAIL,MAAMM,kBAAkB;IAC9E,MAAMD,mBACJ,OAAOD,wBAAwB,YAC/BtC,oBAAoBqC,GAAG,CAACC,uBACnBA,sBACDlB;IACN,MAAMqB,WAAWP,MAAMO,QAAQ,IAAIP,MAAMQ,SAAS;IAClD,MAAMC,iBAAiBP,eACnBA,eACAG,mBACE,yBACAnB;IAEN,IAAIuB,mBAAmBvB,WAAW,OAAO;IAEzC,OAAO;QACLwB,SAAS,OAAOV,MAAMU,OAAO,KAAK,WAAWV,MAAMU,OAAO,GAAG;QAC7DH,UAAUA,aAAa,QAAQ,OAAOA,aAAa,WAAWA,WAAW;QACzEI,QAAQ,OAAOX,MAAMW,MAAM,KAAK,WAAWX,MAAMW,MAAM,GAAGzB;QAC1DI,QAAQmB;QACRJ;QACAO,UAAUb,KAAKc,IAAI,KAAK,WAAWd,KAAKc,IAAI,KAAK,aAAa,UAAU;IAC1E;AACF;AAEA,OAAO,SAASC,qBAAqBf,IAAU,EAAEC,KAAuB;IACtE,OAAOD,KAAKc,IAAI,KAAK,WAAWb,OAAOV,WAAW;AACpD;AAEA,OAAO,SAASyB,oBAAoB3C,GAAQ,EAAE4C,SAAuB;IACnE,IAAI5C,IAAIE,WAAW,IAAI0C,UAAUvC,MAAM,KAAK,aAAa,OAAOS;IAChE,MAAM+B,YAAYD,UAAUE,KAAK,CAACC,MAAM,CAAC,CAACpB,OAASA,KAAKtB,MAAM,KAAK,aAAaI,MAAM;IACtF,IAAIoC,cAAc,GAAG,OAAO/B;IAC5B,MAAMkC,WAAWJ,UAAUK,eAAe;IAC1C,OAAO,GAAGJ,UAAU,KAAK,EAAEA,cAAc,IAAI,KAAK,IAAI,UAAU,EAAEG,WAAW,CAAC,IAAI,EAAElD,uBAAuBkD,WAAW,GAAG,IAAI;AAC/H"}
|
|
@@ -18,7 +18,7 @@ export function JobExecutionSwitcher({ job, selectedJobExecution, onSelectedJobE
|
|
|
18
18
|
if (selected === null) return null;
|
|
19
19
|
if (!job.executionCountVisible) {
|
|
20
20
|
return /*#__PURE__*/ _jsx("div", {
|
|
21
|
-
className: cn('flex min-w-0 items-center gap-
|
|
21
|
+
className: cn('flex min-w-0 items-center gap-inline text-sm leading-20 text-foreground-neutral-subtle', className),
|
|
22
22
|
children: /*#__PURE__*/ _jsx(ExecutionSummary, {
|
|
23
23
|
execution: selected
|
|
24
24
|
})
|
|
@@ -27,7 +27,7 @@ export function JobExecutionSwitcher({ job, selectedJobExecution, onSelectedJobE
|
|
|
27
27
|
return /*#__PURE__*/ _jsxs(DropdownMenu, {
|
|
28
28
|
children: [
|
|
29
29
|
/*#__PURE__*/ _jsxs(DropdownMenuTrigger, {
|
|
30
|
-
className: cn('inline-flex min-w-0 max-w-full items-center rounded-6 text-left transition-colors focus-visible:shadow-border-interactive-with-active focus-visible:outline-none', variant === 'title' ? '-mx-
|
|
30
|
+
className: cn('inline-flex min-w-0 max-w-full items-center rounded-6 text-left transition-colors focus-visible:shadow-border-interactive-with-active focus-visible:outline-none', variant === 'title' ? '-mx-inline -my-[4px] gap-inline px-tight py-[4px] hover:bg-background-components-hover' : 'min-h-28 gap-inline px-tight py-[4px] text-sm leading-20 text-foreground-neutral-subtle hover:bg-background-components-hover', className),
|
|
31
31
|
"aria-label": `Switch job execution, currently execution ${selected.sequence}: ${selected.displayName}`,
|
|
32
32
|
children: [
|
|
33
33
|
variant === 'title' ? /*#__PURE__*/ _jsx(TitleExecutionSummary, {
|
|
@@ -114,7 +114,7 @@ function executionAccessibleLabel(execution) {
|
|
|
114
114
|
}
|
|
115
115
|
function TitleExecutionSummary({ execution }) {
|
|
116
116
|
return /*#__PURE__*/ _jsxs("span", {
|
|
117
|
-
className: "flex min-w-0 items-center gap-
|
|
117
|
+
className: "flex min-w-0 items-center gap-inline",
|
|
118
118
|
children: [
|
|
119
119
|
/*#__PURE__*/ _jsxs(Text, {
|
|
120
120
|
as: "span",
|
|
@@ -139,7 +139,7 @@ function TitleExecutionSummary({ execution }) {
|
|
|
139
139
|
function ExecutionSummary({ execution }) {
|
|
140
140
|
const displayStatus = deriveJobExecutionDisplayStatus(execution);
|
|
141
141
|
return /*#__PURE__*/ _jsxs("span", {
|
|
142
|
-
className: "flex min-w-0 items-center gap-
|
|
142
|
+
className: "flex min-w-0 items-center gap-inline",
|
|
143
143
|
children: [
|
|
144
144
|
/*#__PURE__*/ _jsx(WorkflowStatusIcon, {
|
|
145
145
|
status: displayStatus,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/job-detail/job-execution-switcher.tsx"],"sourcesContent":["import {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {useMemo} from 'react';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {deriveJobExecutionDisplayStatus, type Job, type JobExecution} from '#core/workflow-run.js';\nimport {formatJobExecutionTime, JobExecutionTimeText} from './job-execution-time-text.js';\n\nexport interface JobExecutionSwitcherProps {\n job: Job;\n selectedJobExecution: string | null;\n onSelectedJobExecutionChange: (jobExecutionId: string) => void;\n variant?: 'compact' | 'title' | undefined;\n className?: string | undefined;\n}\n\nexport function JobExecutionSwitcher({\n job,\n selectedJobExecution,\n onSelectedJobExecutionChange,\n variant = 'compact',\n className,\n}: JobExecutionSwitcherProps) {\n const executions = useMemo(\n () => [...job.jobExecutions].sort((left, right) => right.sequence - left.sequence),\n [job.jobExecutions],\n );\n const selected =\n executions.find((jobExecution) => jobExecution.id === selectedJobExecution) ??\n executions[0] ??\n null;\n\n if (selected === null) return null;\n\n if (!job.executionCountVisible) {\n return (\n <div\n className={cn(\n 'flex min-w-0 items-center gap-6 text-sm leading-20 text-foreground-neutral-subtle',\n className,\n )}\n >\n <ExecutionSummary execution={selected} />\n </div>\n );\n }\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger\n className={cn(\n 'inline-flex min-w-0 max-w-full items-center rounded-6 text-left transition-colors focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n variant === 'title'\n ? '-mx-6 -my-4 gap-8 px-6 py-4 hover:bg-background-components-hover'\n : 'min-h-28 gap-6 px-8 py-4 text-sm leading-20 text-foreground-neutral-subtle hover:bg-background-components-hover',\n className,\n )}\n aria-label={`Switch job execution, currently execution ${selected.sequence}: ${selected.displayName}`}\n >\n {variant === 'title' ? (\n <TitleExecutionSummary execution={selected} />\n ) : (\n <ExecutionSummary execution={selected} />\n )}\n <Icon name=\"arrowDownSLine\" className=\"size-14 shrink-0 text-foreground-neutral-muted\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" size=\"lg\" className=\"max-h-[320px] overflow-y-auto\">\n <DropdownMenuLabel>\n {executions.length} execution{executions.length === 1 ? '' : 's'}\n </DropdownMenuLabel>\n {executions.map((jobExecution) => {\n const isSelected = jobExecution.id === selected.id;\n\n return (\n <DropdownMenuItem\n key={jobExecution.id}\n asChild\n onSelect={() => onSelectedJobExecutionChange(jobExecution.id)}\n >\n <button\n type=\"button\"\n aria-current={isSelected ? 'true' : undefined}\n aria-label={executionAccessibleLabel(jobExecution)}\n className=\"w-full text-left\"\n >\n <WorkflowStatusIcon\n status={deriveJobExecutionDisplayStatus(jobExecution)}\n size={14}\n tooltip={false}\n />\n <span className=\"font-code text-xs leading-20 text-foreground-neutral-base tabular-nums\">\n #{jobExecution.sequence}\n </span>\n <span className=\"min-w-0 truncate text-xs leading-20 text-foreground-neutral-base\">\n {jobExecution.displayName}\n </span>\n {!isSelected && jobExecution.statusReason ? (\n <span className=\"min-w-0 flex-1 truncate text-xs leading-20 text-foreground-neutral-muted\">\n {jobExecution.statusReason}\n </span>\n ) : (\n <span className=\"min-w-0 flex-1\" />\n )}\n {isSelected ? (\n <Icon name=\"check\" className=\"size-14 shrink-0 text-foreground-neutral-base\" />\n ) : null}\n <JobExecutionDuration\n execution={jobExecution}\n className=\"ml-auto shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums\"\n />\n </button>\n </DropdownMenuItem>\n );\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nfunction executionAccessibleLabel(execution: JobExecution): string {\n const status = getWorkflowStatusVisual(deriveJobExecutionDisplayStatus(execution));\n return [\n `Execution #${execution.sequence}: ${execution.displayName}`,\n status.label,\n execution.statusReason ?? undefined,\n execution.displayDuration\n ? `duration ${formatJobExecutionTime(execution.displayDuration)}`\n : undefined,\n ]\n .filter((part): part is string => Boolean(part))\n .join(', ');\n}\n\nfunction TitleExecutionSummary({execution}: {execution: JobExecution}) {\n return (\n <span className=\"flex min-w-0 items-center gap-8\">\n <Text as=\"span\" size=\"sm\" bold className=\"shrink-0 text-foreground-neutral-base\">\n #{execution.sequence}\n </Text>\n <Text as=\"span\" size=\"sm\" bold className=\"min-w-0 truncate text-foreground-neutral-base\">\n {execution.displayName}\n </Text>\n </span>\n );\n}\n\nfunction ExecutionSummary({execution}: {execution: JobExecution}) {\n const displayStatus = deriveJobExecutionDisplayStatus(execution);\n\n return (\n <span className=\"flex min-w-0 items-center gap-6\">\n <WorkflowStatusIcon status={displayStatus} size={14} tooltip={false} />\n <span className=\"shrink-0 font-code text-xs leading-20 text-foreground-neutral-base tabular-nums\">\n Execution #{execution.sequence}\n </span>\n {execution.statusReason ? (\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate text-foreground-neutral-muted\">\n · {execution.statusReason}\n </Text>\n ) : null}\n <JobExecutionDuration\n execution={execution}\n className=\"shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums\"\n />\n </span>\n );\n}\n\nfunction JobExecutionDuration({\n execution,\n className,\n}: {\n execution: JobExecution;\n className?: string | undefined;\n}) {\n const duration = execution.displayDuration;\n if (!duration) return null;\n\n return (\n <span className={className}>\n <JobExecutionTimeText time={duration} />\n </span>\n );\n}\n"],"names":["DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuLabel","DropdownMenuTrigger","Icon","Text","cn","useMemo","getWorkflowStatusVisual","WorkflowStatusIcon","deriveJobExecutionDisplayStatus","formatJobExecutionTime","JobExecutionTimeText","JobExecutionSwitcher","job","selectedJobExecution","onSelectedJobExecutionChange","variant","className","executions","jobExecutions","sort","left","right","sequence","selected","find","jobExecution","id","executionCountVisible","div","ExecutionSummary","execution","aria-label","displayName","TitleExecutionSummary","name","align","size","length","map","isSelected","asChild","onSelect","button","type","aria-current","undefined","executionAccessibleLabel","status","tooltip","span","statusReason","JobExecutionDuration","label","displayDuration","filter","part","Boolean","join","as","bold","displayStatus","duration","time"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/job-detail/job-execution-switcher.tsx"],"sourcesContent":["import {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {useMemo} from 'react';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {deriveJobExecutionDisplayStatus, type Job, type JobExecution} from '#core/workflow-run.js';\nimport {formatJobExecutionTime, JobExecutionTimeText} from './job-execution-time-text.js';\n\nexport interface JobExecutionSwitcherProps {\n job: Job;\n selectedJobExecution: string | null;\n onSelectedJobExecutionChange: (jobExecutionId: string) => void;\n variant?: 'compact' | 'title' | undefined;\n className?: string | undefined;\n}\n\nexport function JobExecutionSwitcher({\n job,\n selectedJobExecution,\n onSelectedJobExecutionChange,\n variant = 'compact',\n className,\n}: JobExecutionSwitcherProps) {\n const executions = useMemo(\n () => [...job.jobExecutions].sort((left, right) => right.sequence - left.sequence),\n [job.jobExecutions],\n );\n const selected =\n executions.find((jobExecution) => jobExecution.id === selectedJobExecution) ??\n executions[0] ??\n null;\n\n if (selected === null) return null;\n\n if (!job.executionCountVisible) {\n return (\n <div\n className={cn(\n 'flex min-w-0 items-center gap-inline text-sm leading-20 text-foreground-neutral-subtle',\n className,\n )}\n >\n <ExecutionSummary execution={selected} />\n </div>\n );\n }\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger\n className={cn(\n 'inline-flex min-w-0 max-w-full items-center rounded-6 text-left transition-colors focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n variant === 'title'\n ? '-mx-inline -my-[4px] gap-inline px-tight py-[4px] hover:bg-background-components-hover'\n : 'min-h-28 gap-inline px-tight py-[4px] text-sm leading-20 text-foreground-neutral-subtle hover:bg-background-components-hover',\n className,\n )}\n aria-label={`Switch job execution, currently execution ${selected.sequence}: ${selected.displayName}`}\n >\n {variant === 'title' ? (\n <TitleExecutionSummary execution={selected} />\n ) : (\n <ExecutionSummary execution={selected} />\n )}\n <Icon name=\"arrowDownSLine\" className=\"size-14 shrink-0 text-foreground-neutral-muted\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" size=\"lg\" className=\"max-h-[320px] overflow-y-auto\">\n <DropdownMenuLabel>\n {executions.length} execution{executions.length === 1 ? '' : 's'}\n </DropdownMenuLabel>\n {executions.map((jobExecution) => {\n const isSelected = jobExecution.id === selected.id;\n\n return (\n <DropdownMenuItem\n key={jobExecution.id}\n asChild\n onSelect={() => onSelectedJobExecutionChange(jobExecution.id)}\n >\n <button\n type=\"button\"\n aria-current={isSelected ? 'true' : undefined}\n aria-label={executionAccessibleLabel(jobExecution)}\n className=\"w-full text-left\"\n >\n <WorkflowStatusIcon\n status={deriveJobExecutionDisplayStatus(jobExecution)}\n size={14}\n tooltip={false}\n />\n <span className=\"font-code text-xs leading-20 text-foreground-neutral-base tabular-nums\">\n #{jobExecution.sequence}\n </span>\n <span className=\"min-w-0 truncate text-xs leading-20 text-foreground-neutral-base\">\n {jobExecution.displayName}\n </span>\n {!isSelected && jobExecution.statusReason ? (\n <span className=\"min-w-0 flex-1 truncate text-xs leading-20 text-foreground-neutral-muted\">\n {jobExecution.statusReason}\n </span>\n ) : (\n <span className=\"min-w-0 flex-1\" />\n )}\n {isSelected ? (\n <Icon name=\"check\" className=\"size-14 shrink-0 text-foreground-neutral-base\" />\n ) : null}\n <JobExecutionDuration\n execution={jobExecution}\n className=\"ml-auto shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums\"\n />\n </button>\n </DropdownMenuItem>\n );\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nfunction executionAccessibleLabel(execution: JobExecution): string {\n const status = getWorkflowStatusVisual(deriveJobExecutionDisplayStatus(execution));\n return [\n `Execution #${execution.sequence}: ${execution.displayName}`,\n status.label,\n execution.statusReason ?? undefined,\n execution.displayDuration\n ? `duration ${formatJobExecutionTime(execution.displayDuration)}`\n : undefined,\n ]\n .filter((part): part is string => Boolean(part))\n .join(', ');\n}\n\nfunction TitleExecutionSummary({execution}: {execution: JobExecution}) {\n return (\n <span className=\"flex min-w-0 items-center gap-inline\">\n <Text as=\"span\" size=\"sm\" bold className=\"shrink-0 text-foreground-neutral-base\">\n #{execution.sequence}\n </Text>\n <Text as=\"span\" size=\"sm\" bold className=\"min-w-0 truncate text-foreground-neutral-base\">\n {execution.displayName}\n </Text>\n </span>\n );\n}\n\nfunction ExecutionSummary({execution}: {execution: JobExecution}) {\n const displayStatus = deriveJobExecutionDisplayStatus(execution);\n\n return (\n <span className=\"flex min-w-0 items-center gap-inline\">\n <WorkflowStatusIcon status={displayStatus} size={14} tooltip={false} />\n <span className=\"shrink-0 font-code text-xs leading-20 text-foreground-neutral-base tabular-nums\">\n Execution #{execution.sequence}\n </span>\n {execution.statusReason ? (\n <Text as=\"span\" size=\"xs\" className=\"min-w-0 truncate text-foreground-neutral-muted\">\n · {execution.statusReason}\n </Text>\n ) : null}\n <JobExecutionDuration\n execution={execution}\n className=\"shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums\"\n />\n </span>\n );\n}\n\nfunction JobExecutionDuration({\n execution,\n className,\n}: {\n execution: JobExecution;\n className?: string | undefined;\n}) {\n const duration = execution.displayDuration;\n if (!duration) return null;\n\n return (\n <span className={className}>\n <JobExecutionTimeText time={duration} />\n </span>\n );\n}\n"],"names":["DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuLabel","DropdownMenuTrigger","Icon","Text","cn","useMemo","getWorkflowStatusVisual","WorkflowStatusIcon","deriveJobExecutionDisplayStatus","formatJobExecutionTime","JobExecutionTimeText","JobExecutionSwitcher","job","selectedJobExecution","onSelectedJobExecutionChange","variant","className","executions","jobExecutions","sort","left","right","sequence","selected","find","jobExecution","id","executionCountVisible","div","ExecutionSummary","execution","aria-label","displayName","TitleExecutionSummary","name","align","size","length","map","isSelected","asChild","onSelect","button","type","aria-current","undefined","executionAccessibleLabel","status","tooltip","span","statusReason","JobExecutionDuration","label","displayDuration","filter","part","Boolean","join","as","bold","displayStatus","duration","time"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface JsonCodeEntry {
|
|
2
|
+
filename: string;
|
|
3
|
+
label?: string | undefined;
|
|
4
|
+
value: unknown;
|
|
5
|
+
}
|
|
6
|
+
export declare function JsonCode({ title, value, emptyMessage, }: {
|
|
7
|
+
title?: string | undefined;
|
|
8
|
+
value: unknown;
|
|
9
|
+
emptyMessage?: string | undefined;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
11
|
+
export declare function JsonCodeTabs({ entries }: {
|
|
12
|
+
entries: readonly JsonCodeEntry[];
|
|
13
|
+
}): import("react").JSX.Element;
|
|
14
|
+
//# sourceMappingURL=json-code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-code.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/json-code.tsx"],"names":[],"mappings":"AAcA,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,KAAK,EACL,YAAY,GACb,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC,+BAmBA;AAED,wBAAgB,YAAY,CAAC,EAAC,OAAO,EAAC,EAAE;IAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAA;CAAC,+BAkB1E"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CodeBlock, CodeBlockBody, CodeBlockContent, CodeBlockCopyButton, CodeBlockFilename, CodeBlockFiles, CodeBlockHeader, CodeBlockItem, CodeTabs } from '@shipfox/react-ui/code-block';
|
|
3
|
+
import { Text } from '@shipfox/react-ui/typography';
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
export function JsonCode({ title, value, emptyMessage }) {
|
|
6
|
+
const filename = title ?? 'value.json';
|
|
7
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
8
|
+
className: "flex min-w-0 flex-col gap-tight",
|
|
9
|
+
children: [
|
|
10
|
+
title ? /*#__PURE__*/ _jsx(Text, {
|
|
11
|
+
size: "xs",
|
|
12
|
+
className: "text-foreground-neutral-muted",
|
|
13
|
+
children: title
|
|
14
|
+
}) : null,
|
|
15
|
+
emptyMessage && isEmptyObject(value) ? /*#__PURE__*/ _jsx(Text, {
|
|
16
|
+
size: "xs",
|
|
17
|
+
className: "text-foreground-neutral-muted",
|
|
18
|
+
children: emptyMessage
|
|
19
|
+
}) : /*#__PURE__*/ _jsx(JsonCodeBlock, {
|
|
20
|
+
entries: [
|
|
21
|
+
{
|
|
22
|
+
filename,
|
|
23
|
+
value
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
})
|
|
27
|
+
]
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
export function JsonCodeTabs({ entries }) {
|
|
31
|
+
const entryKeys = entries.map((entry)=>entry.label ?? entry.filename);
|
|
32
|
+
const [activeKey, setActiveKey] = useState(entryKeys[0] ?? '');
|
|
33
|
+
const activeValue = entryKeys.includes(activeKey) ? activeKey : entryKeys[0] ?? '';
|
|
34
|
+
const codes = Object.fromEntries(entries.map((entry)=>[
|
|
35
|
+
entry.label ?? entry.filename,
|
|
36
|
+
stringifyJson(entry.value)
|
|
37
|
+
]));
|
|
38
|
+
return /*#__PURE__*/ _jsx(CodeTabs, {
|
|
39
|
+
value: activeValue,
|
|
40
|
+
onValueChange: setActiveKey,
|
|
41
|
+
codes: codes,
|
|
42
|
+
lang: "json",
|
|
43
|
+
syntaxHighlighting: false,
|
|
44
|
+
className: "h-auto min-h-0 rounded-8 bg-background-contrast-base shadow-button-neutral"
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function JsonCodeBlock({ entries }) {
|
|
48
|
+
const data = entries.map((entry)=>({
|
|
49
|
+
filename: entry.filename,
|
|
50
|
+
language: 'json',
|
|
51
|
+
code: stringifyJson(entry.value)
|
|
52
|
+
}));
|
|
53
|
+
return /*#__PURE__*/ _jsxs(CodeBlock, {
|
|
54
|
+
data: data,
|
|
55
|
+
className: "h-auto min-h-0 rounded-6 bg-background-contrast-base shadow-button-neutral",
|
|
56
|
+
children: [
|
|
57
|
+
/*#__PURE__*/ _jsxs(CodeBlockHeader, {
|
|
58
|
+
className: "bg-background-contrast-base p-tight",
|
|
59
|
+
children: [
|
|
60
|
+
/*#__PURE__*/ _jsx(CodeBlockFiles, {
|
|
61
|
+
children: (item)=>{
|
|
62
|
+
const entry = entries.find((candidate)=>candidate.filename === item.filename);
|
|
63
|
+
return /*#__PURE__*/ _jsx(CodeBlockFilename, {
|
|
64
|
+
value: item.filename,
|
|
65
|
+
children: entry?.label ?? item.filename
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}),
|
|
69
|
+
/*#__PURE__*/ _jsx(CodeBlockCopyButton, {})
|
|
70
|
+
]
|
|
71
|
+
}),
|
|
72
|
+
/*#__PURE__*/ _jsx(CodeBlockBody, {
|
|
73
|
+
children: (item)=>/*#__PURE__*/ _jsx(CodeBlockItem, {
|
|
74
|
+
value: item.filename,
|
|
75
|
+
children: /*#__PURE__*/ _jsx(CodeBlockContent, {
|
|
76
|
+
language: "json",
|
|
77
|
+
syntaxHighlighting: false,
|
|
78
|
+
children: item.code
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
]
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function isEmptyObject(value) {
|
|
86
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value) && Object.keys(value).length === 0;
|
|
87
|
+
}
|
|
88
|
+
function stringifyJson(value) {
|
|
89
|
+
const serialized = JSON.stringify(value, null, 2);
|
|
90
|
+
return serialized === undefined ? String(value) : serialized;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
//# sourceMappingURL=json-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/job-detail/json-code.tsx"],"sourcesContent":["import {\n CodeBlock,\n CodeBlockBody,\n CodeBlockContent,\n CodeBlockCopyButton,\n CodeBlockFilename,\n CodeBlockFiles,\n CodeBlockHeader,\n CodeBlockItem,\n CodeTabs,\n} from '@shipfox/react-ui/code-block';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useState} from 'react';\n\nexport interface JsonCodeEntry {\n filename: string;\n label?: string | undefined;\n value: unknown;\n}\n\nexport function JsonCode({\n title,\n value,\n emptyMessage,\n}: {\n title?: string | undefined;\n value: unknown;\n emptyMessage?: string | undefined;\n}) {\n const filename = title ?? 'value.json';\n\n return (\n <div className=\"flex min-w-0 flex-col gap-tight\">\n {title ? (\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n {title}\n </Text>\n ) : null}\n {emptyMessage && isEmptyObject(value) ? (\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n {emptyMessage}\n </Text>\n ) : (\n <JsonCodeBlock entries={[{filename, value}]} />\n )}\n </div>\n );\n}\n\nexport function JsonCodeTabs({entries}: {entries: readonly JsonCodeEntry[]}) {\n const entryKeys = entries.map((entry) => entry.label ?? entry.filename);\n const [activeKey, setActiveKey] = useState(entryKeys[0] ?? '');\n const activeValue = entryKeys.includes(activeKey) ? activeKey : (entryKeys[0] ?? '');\n const codes = Object.fromEntries(\n entries.map((entry) => [entry.label ?? entry.filename, stringifyJson(entry.value)]),\n );\n\n return (\n <CodeTabs\n value={activeValue}\n onValueChange={setActiveKey}\n codes={codes}\n lang=\"json\"\n syntaxHighlighting={false}\n className=\"h-auto min-h-0 rounded-8 bg-background-contrast-base shadow-button-neutral\"\n />\n );\n}\n\nfunction JsonCodeBlock({entries}: {entries: readonly JsonCodeEntry[]}) {\n const data = entries.map((entry) => ({\n filename: entry.filename,\n language: 'json',\n code: stringifyJson(entry.value),\n }));\n\n return (\n <CodeBlock\n data={data}\n className=\"h-auto min-h-0 rounded-6 bg-background-contrast-base shadow-button-neutral\"\n >\n <CodeBlockHeader className=\"bg-background-contrast-base p-tight\">\n <CodeBlockFiles>\n {(item) => {\n const entry = entries.find((candidate) => candidate.filename === item.filename);\n return (\n <CodeBlockFilename value={item.filename}>\n {entry?.label ?? item.filename}\n </CodeBlockFilename>\n );\n }}\n </CodeBlockFiles>\n <CodeBlockCopyButton />\n </CodeBlockHeader>\n <CodeBlockBody>\n {(item) => (\n <CodeBlockItem value={item.filename}>\n <CodeBlockContent language=\"json\" syntaxHighlighting={false}>\n {item.code}\n </CodeBlockContent>\n </CodeBlockItem>\n )}\n </CodeBlockBody>\n </CodeBlock>\n );\n}\n\nfunction isEmptyObject(value: unknown): boolean {\n return (\n typeof value === 'object' &&\n value !== null &&\n !Array.isArray(value) &&\n Object.keys(value).length === 0\n );\n}\n\nfunction stringifyJson(value: unknown): string {\n const serialized = JSON.stringify(value, null, 2);\n return serialized === undefined ? String(value) : serialized;\n}\n"],"names":["CodeBlock","CodeBlockBody","CodeBlockContent","CodeBlockCopyButton","CodeBlockFilename","CodeBlockFiles","CodeBlockHeader","CodeBlockItem","CodeTabs","Text","useState","JsonCode","title","value","emptyMessage","filename","div","className","size","isEmptyObject","JsonCodeBlock","entries","JsonCodeTabs","entryKeys","map","entry","label","activeKey","setActiveKey","activeValue","includes","codes","Object","fromEntries","stringifyJson","onValueChange","lang","syntaxHighlighting","data","language","code","item","find","candidate","Array","isArray","keys","length","serialized","JSON","stringify","undefined","String"],"mappings":";AAAA,SACEA,SAAS,EACTC,aAAa,EACbC,gBAAgB,EAChBC,mBAAmB,EACnBC,iBAAiB,EACjBC,cAAc,EACdC,eAAe,EACfC,aAAa,EACbC,QAAQ,QACH,+BAA+B;AACtC,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,QAAQ,QAAO,QAAQ;AAQ/B,OAAO,SAASC,SAAS,EACvBC,KAAK,EACLC,KAAK,EACLC,YAAY,EAKb;IACC,MAAMC,WAAWH,SAAS;IAE1B,qBACE,MAACI;QAAIC,WAAU;;YACZL,sBACC,KAACH;gBAAKS,MAAK;gBAAKD,WAAU;0BACvBL;iBAED;YACHE,gBAAgBK,cAAcN,uBAC7B,KAACJ;gBAAKS,MAAK;gBAAKD,WAAU;0BACvBH;+BAGH,KAACM;gBAAcC,SAAS;oBAAC;wBAACN;wBAAUF;oBAAK;iBAAE;;;;AAInD;AAEA,OAAO,SAASS,aAAa,EAACD,OAAO,EAAsC;IACzE,MAAME,YAAYF,QAAQG,GAAG,CAAC,CAACC,QAAUA,MAAMC,KAAK,IAAID,MAAMV,QAAQ;IACtE,MAAM,CAACY,WAAWC,aAAa,GAAGlB,SAASa,SAAS,CAAC,EAAE,IAAI;IAC3D,MAAMM,cAAcN,UAAUO,QAAQ,CAACH,aAAaA,YAAaJ,SAAS,CAAC,EAAE,IAAI;IACjF,MAAMQ,QAAQC,OAAOC,WAAW,CAC9BZ,QAAQG,GAAG,CAAC,CAACC,QAAU;YAACA,MAAMC,KAAK,IAAID,MAAMV,QAAQ;YAAEmB,cAAcT,MAAMZ,KAAK;SAAE;IAGpF,qBACE,KAACL;QACCK,OAAOgB;QACPM,eAAeP;QACfG,OAAOA;QACPK,MAAK;QACLC,oBAAoB;QACpBpB,WAAU;;AAGhB;AAEA,SAASG,cAAc,EAACC,OAAO,EAAsC;IACnE,MAAMiB,OAAOjB,QAAQG,GAAG,CAAC,CAACC,QAAW,CAAA;YACnCV,UAAUU,MAAMV,QAAQ;YACxBwB,UAAU;YACVC,MAAMN,cAAcT,MAAMZ,KAAK;QACjC,CAAA;IAEA,qBACE,MAACb;QACCsC,MAAMA;QACNrB,WAAU;;0BAEV,MAACX;gBAAgBW,WAAU;;kCACzB,KAACZ;kCACE,CAACoC;4BACA,MAAMhB,QAAQJ,QAAQqB,IAAI,CAAC,CAACC,YAAcA,UAAU5B,QAAQ,KAAK0B,KAAK1B,QAAQ;4BAC9E,qBACE,KAACX;gCAAkBS,OAAO4B,KAAK1B,QAAQ;0CACpCU,OAAOC,SAASe,KAAK1B,QAAQ;;wBAGpC;;kCAEF,KAACZ;;;0BAEH,KAACF;0BACE,CAACwC,qBACA,KAAClC;wBAAcM,OAAO4B,KAAK1B,QAAQ;kCACjC,cAAA,KAACb;4BAAiBqC,UAAS;4BAAOF,oBAAoB;sCACnDI,KAAKD,IAAI;;;;;;AAOxB;AAEA,SAASrB,cAAcN,KAAc;IACnC,OACE,OAAOA,UAAU,YACjBA,UAAU,QACV,CAAC+B,MAAMC,OAAO,CAAChC,UACfmB,OAAOc,IAAI,CAACjC,OAAOkC,MAAM,KAAK;AAElC;AAEA,SAASb,cAAcrB,KAAc;IACnC,MAAMmC,aAAaC,KAAKC,SAAS,CAACrC,OAAO,MAAM;IAC/C,OAAOmC,eAAeG,YAAYC,OAAOvC,SAASmC;AACpD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"step-attempt-log-panel.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/step-attempt-log-panel.tsx"],"names":[],"mappings":"AASA,OAAO,EAAC,KAAK,SAAS,EAAoB,MAAM,OAAO,CAAC;AAQxD,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,0EAA0E;IAC1E,aAAa,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC7C,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C;AAED,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,OAAO,EACP,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,gBAA2C,EAC3C,sBAAsD,EACtD,wBAA2D,GAC5D,EAAE,wBAAwB,+
|
|
1
|
+
{"version":3,"file":"step-attempt-log-panel.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/step-attempt-log-panel.tsx"],"names":[],"mappings":"AASA,OAAO,EAAC,KAAK,SAAS,EAAoB,MAAM,OAAO,CAAC;AAQxD,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,0EAA0E;IAC1E,aAAa,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC7C,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C;AAED,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,OAAO,EACP,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,gBAA2C,EAC3C,sBAAsD,EACtD,wBAA2D,GAC5D,EAAE,wBAAwB,+BA8F1B"}
|
|
@@ -82,14 +82,15 @@ export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptSta
|
|
|
82
82
|
});
|
|
83
83
|
}
|
|
84
84
|
return /*#__PURE__*/ _jsxs("div", {
|
|
85
|
-
className: "flex min-w-0 flex-col gap-
|
|
85
|
+
className: "flex min-w-0 flex-col gap-inline",
|
|
86
86
|
children: [
|
|
87
87
|
staleError ? /*#__PURE__*/ _jsx(Callout, {
|
|
88
88
|
role: "alert",
|
|
89
89
|
type: "warning",
|
|
90
|
-
|
|
90
|
+
variant: "secondary",
|
|
91
|
+
className: "rounded-none border-b border-border-neutral-base px-0 py-row shadow-none",
|
|
91
92
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
92
|
-
className: "flex min-w-0 flex-1 items-center justify-between gap-
|
|
93
|
+
className: "flex min-w-0 flex-1 items-center justify-between gap-inline",
|
|
93
94
|
children: [
|
|
94
95
|
/*#__PURE__*/ _jsx(Text, {
|
|
95
96
|
size: "xs",
|
|
@@ -110,6 +111,7 @@ export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptSta
|
|
|
110
111
|
records: records,
|
|
111
112
|
emptyState: query.data?.complete ? 'complete' : 'pending',
|
|
112
113
|
anchorToFailure: anchorToFailure,
|
|
114
|
+
ariaLive: attemptStatus === 'running' ? 'polite' : 'off',
|
|
113
115
|
className: surfaceClassName
|
|
114
116
|
})
|
|
115
117
|
]
|
|
@@ -131,7 +133,7 @@ function StepLogsWaitingSurface({ startedAt, className }) {
|
|
|
131
133
|
className: className,
|
|
132
134
|
children: /*#__PURE__*/ _jsxs(Text, {
|
|
133
135
|
size: "xs",
|
|
134
|
-
className: "px-
|
|
136
|
+
className: "px-tight py-row text-foreground-neutral-muted",
|
|
135
137
|
children: [
|
|
136
138
|
"Waiting for output ·",
|
|
137
139
|
' ',
|
|
@@ -173,9 +175,10 @@ function StepLogsError({ retrying, onRetry }) {
|
|
|
173
175
|
return /*#__PURE__*/ _jsx(Callout, {
|
|
174
176
|
role: "alert",
|
|
175
177
|
type: "error",
|
|
176
|
-
|
|
178
|
+
variant: "secondary",
|
|
179
|
+
className: "rounded-none border-b border-border-neutral-base px-0 py-row shadow-none",
|
|
177
180
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
178
|
-
className: "flex min-w-0 flex-1 items-center justify-between gap-
|
|
181
|
+
className: "flex min-w-0 flex-1 items-center justify-between gap-inline",
|
|
179
182
|
children: [
|
|
180
183
|
/*#__PURE__*/ _jsx(Text, {
|
|
181
184
|
size: "xs",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/job-detail/step-attempt-log-panel.tsx"],"sourcesContent":["import {\n isMissingStepLogStreamError,\n LogView,\n LogViewSkeleton,\n useStepAttemptLogsQuery,\n} from '@shipfox/client-logs';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {type RefObject, useEffect, useRef} from 'react';\nimport {JobExecutionTimeText} from './job-execution-time-text.js';\n\nconst TAIL_FOLLOW_THRESHOLD_PX = 24;\nconst INITIAL_LOG_ERROR_RETRY_COUNT = 5;\nconst INITIAL_LOG_ERROR_RETRY_DELAY_MS = 1_500;\nconst defaultLogSurfaceClasses = 'rounded-8';\n\nexport interface StepAttemptLogPanelProps {\n stepId: string;\n attempt: number;\n attemptStatus: string;\n /** The start time used by the page's quiet waiting-for-output state. */\n attemptStartedAt?: string | undefined;\n /** The page scroll column used by the uncapped job-detail log surface. */\n pageScrollRef: RefObject<HTMLElement | null>;\n surfaceClassName?: string | undefined;\n initialErrorRetryCount?: number | undefined;\n initialErrorRetryDelayMs?: number | undefined;\n}\n\nexport function StepAttemptLogPanel({\n stepId,\n attempt,\n attemptStatus,\n attemptStartedAt,\n pageScrollRef,\n surfaceClassName = defaultLogSurfaceClasses,\n initialErrorRetryCount = INITIAL_LOG_ERROR_RETRY_COUNT,\n initialErrorRetryDelayMs = INITIAL_LOG_ERROR_RETRY_DELAY_MS,\n}: StepAttemptLogPanelProps) {\n const shouldFollowTailRef = useRef(true);\n const missingStreamRetryCount = attemptStatus === 'running' ? undefined : initialErrorRetryCount;\n const retryMissingStream = attemptStatus === 'running' || isTerminalAttemptStatus(attemptStatus);\n const query = useStepAttemptLogsQuery(stepId, attempt, {\n retryMissingStream,\n missingStreamRetryCount,\n missingStreamRetryDelayMs: initialErrorRetryDelayMs,\n initialErrorRetryCount,\n initialErrorRetryDelayMs,\n });\n const records = query.data?.records ?? [];\n const recordCount = records.length;\n const anchorToFailure = attemptStatus === 'failed';\n const missingActiveStream =\n retryMissingStream && query.data === undefined && isMissingStepLogStreamError(query.error);\n const initialError = query.isError && query.data === undefined && !missingActiveStream;\n const staleError = query.isError && query.data !== undefined;\n\n useEffect(() => {\n const scrollElement = pageScrollRef.current;\n if (!scrollElement) return undefined;\n\n const onScroll = () => {\n shouldFollowTailRef.current = isNearPageBottom(scrollElement);\n };\n scrollElement.addEventListener('scroll', onScroll, {passive: true});\n onScroll();\n return () => scrollElement.removeEventListener('scroll', onScroll);\n }, [pageScrollRef]);\n\n useEffect(() => {\n if (recordCount === 0) return undefined;\n if (anchorToFailure) return undefined;\n if (!shouldFollowTailRef.current) return undefined;\n\n const frame = scheduleAnimationFrame(() => {\n const scrollElement = pageScrollRef.current;\n if (!scrollElement) return;\n scrollElement.scrollTop = scrollElement.scrollHeight;\n });\n\n return () => {\n cancelScheduledFrame(frame);\n };\n }, [anchorToFailure, pageScrollRef, recordCount]);\n\n if (query.isPending) {\n return <StepLogsLoadingSurface label=\"Loading logs\" className={surfaceClassName} />;\n }\n\n if (missingActiveStream) {\n if (attemptStatus === 'running' && attemptStartedAt) {\n return <StepLogsWaitingSurface startedAt={attemptStartedAt} className={surfaceClassName} />;\n }\n return <StepLogsLoadingSurface label=\"Waiting for logs\" className={surfaceClassName} />;\n }\n\n if (initialError) {\n return <StepLogsError retrying={query.isFetching} onRetry={() => void query.refetch()} />;\n }\n\n return (\n <div className=\"flex min-w-0 flex-col gap-8\">\n {staleError ? (\n <Callout role=\"alert\" type=\"warning\" className=\"px-10 py-8\">\n <div className=\"flex min-w-0 flex-1 items-center justify-between gap-8\">\n <Text size=\"xs\">Could not refresh logs.</Text>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n isLoading={query.isFetching}\n onClick={() => void query.refetch()}\n >\n Retry\n </Button>\n </div>\n </Callout>\n ) : null}\n <LogView\n records={records}\n emptyState={query.data?.complete ? 'complete' : 'pending'}\n anchorToFailure={anchorToFailure}\n className={surfaceClassName}\n />\n </div>\n );\n}\n\nfunction StepLogsLoadingSurface({label, className}: {label: string; className: string}) {\n return (\n <div role=\"status\" aria-label={label}>\n <LogViewSkeleton className={className} />\n </div>\n );\n}\n\nfunction StepLogsWaitingSurface({startedAt, className}: {startedAt: string; className: string}) {\n return (\n <div role=\"status\" aria-label=\"Waiting for logs\" className={className}>\n <Text size=\"xs\" className=\"px-10 py-12 text-foreground-neutral-muted\">\n Waiting for output ·{' '}\n <span className=\"font-code tabular-nums\" aria-hidden=\"true\">\n <JobExecutionTimeText time={{state: 'live', fromIso: startedAt}} />\n </span>\n </Text>\n </div>\n );\n}\n\nfunction scheduleAnimationFrame(callback: FrameRequestCallback): number {\n if (typeof globalThis.requestAnimationFrame === 'function') {\n return globalThis.requestAnimationFrame(callback);\n }\n return window.setTimeout(() => callback(Date.now()), 0);\n}\n\nfunction cancelScheduledFrame(frame: number) {\n if (typeof globalThis.cancelAnimationFrame === 'function') {\n globalThis.cancelAnimationFrame(frame);\n return;\n }\n window.clearTimeout(frame);\n}\n\nfunction isNearPageBottom(element: HTMLElement): boolean {\n const distanceFromBottom = element.scrollHeight - element.scrollTop - element.clientHeight;\n return distanceFromBottom <= TAIL_FOLLOW_THRESHOLD_PX;\n}\n\nfunction isTerminalAttemptStatus(status: string): boolean {\n return status === 'succeeded' || status === 'failed' || status === 'cancelled';\n}\n\nfunction StepLogsError({retrying, onRetry}: {retrying: boolean; onRetry: () => void}) {\n return (\n <Callout role=\"alert\" type=\"error\" className=\"px-10 py-8\">\n <div className=\"flex min-w-0 flex-1 items-center justify-between gap-8\">\n <Text size=\"xs\">Could not load logs.</Text>\n <Button type=\"button\" size=\"2xs\" variant=\"secondary\" isLoading={retrying} onClick={onRetry}>\n Retry\n </Button>\n </div>\n </Callout>\n );\n}\n"],"names":["isMissingStepLogStreamError","LogView","LogViewSkeleton","useStepAttemptLogsQuery","Button","Callout","Text","useEffect","useRef","JobExecutionTimeText","TAIL_FOLLOW_THRESHOLD_PX","INITIAL_LOG_ERROR_RETRY_COUNT","INITIAL_LOG_ERROR_RETRY_DELAY_MS","defaultLogSurfaceClasses","StepAttemptLogPanel","stepId","attempt","attemptStatus","attemptStartedAt","pageScrollRef","surfaceClassName","initialErrorRetryCount","initialErrorRetryDelayMs","shouldFollowTailRef","missingStreamRetryCount","undefined","retryMissingStream","isTerminalAttemptStatus","query","missingStreamRetryDelayMs","records","data","recordCount","length","anchorToFailure","missingActiveStream","error","initialError","isError","staleError","scrollElement","current","onScroll","isNearPageBottom","addEventListener","passive","removeEventListener","frame","scheduleAnimationFrame","scrollTop","scrollHeight","cancelScheduledFrame","isPending","StepLogsLoadingSurface","label","className","StepLogsWaitingSurface","startedAt","StepLogsError","retrying","isFetching","onRetry","refetch","div","role","type","size","variant","isLoading","onClick","emptyState","complete","aria-label","span","aria-hidden","time","state","fromIso","callback","globalThis","requestAnimationFrame","window","setTimeout","Date","now","cancelAnimationFrame","clearTimeout","element","distanceFromBottom","clientHeight","status"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/job-detail/step-attempt-log-panel.tsx"],"sourcesContent":["import {\n isMissingStepLogStreamError,\n LogView,\n LogViewSkeleton,\n useStepAttemptLogsQuery,\n} from '@shipfox/client-logs';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {type RefObject, useEffect, useRef} from 'react';\nimport {JobExecutionTimeText} from './job-execution-time-text.js';\n\nconst TAIL_FOLLOW_THRESHOLD_PX = 24;\nconst INITIAL_LOG_ERROR_RETRY_COUNT = 5;\nconst INITIAL_LOG_ERROR_RETRY_DELAY_MS = 1_500;\nconst defaultLogSurfaceClasses = 'rounded-8';\n\nexport interface StepAttemptLogPanelProps {\n stepId: string;\n attempt: number;\n attemptStatus: string;\n /** The start time used by the page's quiet waiting-for-output state. */\n attemptStartedAt?: string | undefined;\n /** The page scroll column used by the uncapped job-detail log surface. */\n pageScrollRef: RefObject<HTMLElement | null>;\n surfaceClassName?: string | undefined;\n initialErrorRetryCount?: number | undefined;\n initialErrorRetryDelayMs?: number | undefined;\n}\n\nexport function StepAttemptLogPanel({\n stepId,\n attempt,\n attemptStatus,\n attemptStartedAt,\n pageScrollRef,\n surfaceClassName = defaultLogSurfaceClasses,\n initialErrorRetryCount = INITIAL_LOG_ERROR_RETRY_COUNT,\n initialErrorRetryDelayMs = INITIAL_LOG_ERROR_RETRY_DELAY_MS,\n}: StepAttemptLogPanelProps) {\n const shouldFollowTailRef = useRef(true);\n const missingStreamRetryCount = attemptStatus === 'running' ? undefined : initialErrorRetryCount;\n const retryMissingStream = attemptStatus === 'running' || isTerminalAttemptStatus(attemptStatus);\n const query = useStepAttemptLogsQuery(stepId, attempt, {\n retryMissingStream,\n missingStreamRetryCount,\n missingStreamRetryDelayMs: initialErrorRetryDelayMs,\n initialErrorRetryCount,\n initialErrorRetryDelayMs,\n });\n const records = query.data?.records ?? [];\n const recordCount = records.length;\n const anchorToFailure = attemptStatus === 'failed';\n const missingActiveStream =\n retryMissingStream && query.data === undefined && isMissingStepLogStreamError(query.error);\n const initialError = query.isError && query.data === undefined && !missingActiveStream;\n const staleError = query.isError && query.data !== undefined;\n\n useEffect(() => {\n const scrollElement = pageScrollRef.current;\n if (!scrollElement) return undefined;\n\n const onScroll = () => {\n shouldFollowTailRef.current = isNearPageBottom(scrollElement);\n };\n scrollElement.addEventListener('scroll', onScroll, {passive: true});\n onScroll();\n return () => scrollElement.removeEventListener('scroll', onScroll);\n }, [pageScrollRef]);\n\n useEffect(() => {\n if (recordCount === 0) return undefined;\n if (anchorToFailure) return undefined;\n if (!shouldFollowTailRef.current) return undefined;\n\n const frame = scheduleAnimationFrame(() => {\n const scrollElement = pageScrollRef.current;\n if (!scrollElement) return;\n scrollElement.scrollTop = scrollElement.scrollHeight;\n });\n\n return () => {\n cancelScheduledFrame(frame);\n };\n }, [anchorToFailure, pageScrollRef, recordCount]);\n\n if (query.isPending) {\n return <StepLogsLoadingSurface label=\"Loading logs\" className={surfaceClassName} />;\n }\n\n if (missingActiveStream) {\n if (attemptStatus === 'running' && attemptStartedAt) {\n return <StepLogsWaitingSurface startedAt={attemptStartedAt} className={surfaceClassName} />;\n }\n return <StepLogsLoadingSurface label=\"Waiting for logs\" className={surfaceClassName} />;\n }\n\n if (initialError) {\n return <StepLogsError retrying={query.isFetching} onRetry={() => void query.refetch()} />;\n }\n\n return (\n <div className=\"flex min-w-0 flex-col gap-inline\">\n {staleError ? (\n <Callout\n role=\"alert\"\n type=\"warning\"\n variant=\"secondary\"\n className=\"rounded-none border-b border-border-neutral-base px-0 py-row shadow-none\"\n >\n <div className=\"flex min-w-0 flex-1 items-center justify-between gap-inline\">\n <Text size=\"xs\">Could not refresh logs.</Text>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n isLoading={query.isFetching}\n onClick={() => void query.refetch()}\n >\n Retry\n </Button>\n </div>\n </Callout>\n ) : null}\n <LogView\n records={records}\n emptyState={query.data?.complete ? 'complete' : 'pending'}\n anchorToFailure={anchorToFailure}\n ariaLive={attemptStatus === 'running' ? 'polite' : 'off'}\n className={surfaceClassName}\n />\n </div>\n );\n}\n\nfunction StepLogsLoadingSurface({label, className}: {label: string; className: string}) {\n return (\n <div role=\"status\" aria-label={label}>\n <LogViewSkeleton className={className} />\n </div>\n );\n}\n\nfunction StepLogsWaitingSurface({startedAt, className}: {startedAt: string; className: string}) {\n return (\n <div role=\"status\" aria-label=\"Waiting for logs\" className={className}>\n <Text size=\"xs\" className=\"px-tight py-row text-foreground-neutral-muted\">\n Waiting for output ·{' '}\n <span className=\"font-code tabular-nums\" aria-hidden=\"true\">\n <JobExecutionTimeText time={{state: 'live', fromIso: startedAt}} />\n </span>\n </Text>\n </div>\n );\n}\n\nfunction scheduleAnimationFrame(callback: FrameRequestCallback): number {\n if (typeof globalThis.requestAnimationFrame === 'function') {\n return globalThis.requestAnimationFrame(callback);\n }\n return window.setTimeout(() => callback(Date.now()), 0);\n}\n\nfunction cancelScheduledFrame(frame: number) {\n if (typeof globalThis.cancelAnimationFrame === 'function') {\n globalThis.cancelAnimationFrame(frame);\n return;\n }\n window.clearTimeout(frame);\n}\n\nfunction isNearPageBottom(element: HTMLElement): boolean {\n const distanceFromBottom = element.scrollHeight - element.scrollTop - element.clientHeight;\n return distanceFromBottom <= TAIL_FOLLOW_THRESHOLD_PX;\n}\n\nfunction isTerminalAttemptStatus(status: string): boolean {\n return status === 'succeeded' || status === 'failed' || status === 'cancelled';\n}\n\nfunction StepLogsError({retrying, onRetry}: {retrying: boolean; onRetry: () => void}) {\n return (\n <Callout\n role=\"alert\"\n type=\"error\"\n variant=\"secondary\"\n className=\"rounded-none border-b border-border-neutral-base px-0 py-row shadow-none\"\n >\n <div className=\"flex min-w-0 flex-1 items-center justify-between gap-inline\">\n <Text size=\"xs\">Could not load logs.</Text>\n <Button type=\"button\" size=\"2xs\" variant=\"secondary\" isLoading={retrying} onClick={onRetry}>\n Retry\n </Button>\n </div>\n </Callout>\n );\n}\n"],"names":["isMissingStepLogStreamError","LogView","LogViewSkeleton","useStepAttemptLogsQuery","Button","Callout","Text","useEffect","useRef","JobExecutionTimeText","TAIL_FOLLOW_THRESHOLD_PX","INITIAL_LOG_ERROR_RETRY_COUNT","INITIAL_LOG_ERROR_RETRY_DELAY_MS","defaultLogSurfaceClasses","StepAttemptLogPanel","stepId","attempt","attemptStatus","attemptStartedAt","pageScrollRef","surfaceClassName","initialErrorRetryCount","initialErrorRetryDelayMs","shouldFollowTailRef","missingStreamRetryCount","undefined","retryMissingStream","isTerminalAttemptStatus","query","missingStreamRetryDelayMs","records","data","recordCount","length","anchorToFailure","missingActiveStream","error","initialError","isError","staleError","scrollElement","current","onScroll","isNearPageBottom","addEventListener","passive","removeEventListener","frame","scheduleAnimationFrame","scrollTop","scrollHeight","cancelScheduledFrame","isPending","StepLogsLoadingSurface","label","className","StepLogsWaitingSurface","startedAt","StepLogsError","retrying","isFetching","onRetry","refetch","div","role","type","variant","size","isLoading","onClick","emptyState","complete","ariaLive","aria-label","span","aria-hidden","time","state","fromIso","callback","globalThis","requestAnimationFrame","window","setTimeout","Date","now","cancelAnimationFrame","clearTimeout","element","distanceFromBottom","clientHeight","status"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { EvaluationTraceEntry } from '#core/workflow-run.js';
|
|
2
|
+
import { type StepListEntryModel } from '../step-list/step-list-model.js';
|
|
3
|
+
export interface StepInspectorSheetProps {
|
|
4
|
+
entry: StepListEntryModel;
|
|
5
|
+
open: boolean;
|
|
6
|
+
onOpenChange: (open: boolean) => void;
|
|
7
|
+
workspaceSlug: string;
|
|
8
|
+
projectSlug: string;
|
|
9
|
+
workflowRunId: string;
|
|
10
|
+
runAttempt: number;
|
|
11
|
+
jobId: string;
|
|
12
|
+
annotationCount?: number | undefined;
|
|
13
|
+
}
|
|
14
|
+
export declare function StepInspectorSheet({ entry, open, onOpenChange, workspaceSlug, projectSlug, workflowRunId, runAttempt, jobId, annotationCount, }: StepInspectorSheetProps): import("react").JSX.Element;
|
|
15
|
+
export declare function EvaluationTrace({ trace }: {
|
|
16
|
+
trace: readonly EvaluationTraceEntry[];
|
|
17
|
+
}): import("react").JSX.Element;
|
|
18
|
+
//# sourceMappingURL=step-troubleshooting.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"step-troubleshooting.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/step-troubleshooting.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,oBAAoB,EAKrB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAiB,KAAK,kBAAkB,EAAC,MAAM,iCAAiC,CAAC;AAKxF,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,kBAAkB,CAAC;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAED,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,aAAa,EACb,WAAW,EACX,aAAa,EACb,UAAU,EACV,KAAK,EACL,eAAe,GAChB,EAAE,uBAAuB,+BAiCzB;AAoPD,wBAAgB,eAAe,CAAC,EAAC,KAAK,EAAC,EAAE;IAAC,KAAK,EAAE,SAAS,oBAAoB,EAAE,CAAA;CAAC,+BAwDhF"}
|