@cat-factory/app 0.309.0 → 0.310.1

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 (133) hide show
  1. package/README.md +11 -2
  2. package/app/components/assistant/AssistantModal.vue +5 -4
  3. package/app/components/auth/BackendMisconfiguredScreen.vue +4 -3
  4. package/app/components/auth/LoginScreen.vue +40 -26
  5. package/app/components/auth/ResetPasswordScreen.vue +9 -4
  6. package/app/components/auth/UserMenu.vue +4 -3
  7. package/app/components/binaryCandidates/BinaryCandidatesWindow.vue +34 -23
  8. package/app/components/binaryOutput/StoredAssetView.vue +8 -6
  9. package/app/components/board/AddTaskModal.vue +16 -15
  10. package/app/components/board/AgentFailureCard.vue +12 -9
  11. package/app/components/board/AgentFailureHistory.vue +18 -9
  12. package/app/components/board/CreateInitiativeModal.vue +5 -4
  13. package/app/components/board/FailureDetail.vue +22 -12
  14. package/app/components/board/ReviewFrictionDialog.vue +4 -3
  15. package/app/components/board/nodes/BlockNode.vue +17 -15
  16. package/app/components/board/nodes/DecisionBadge.vue +4 -3
  17. package/app/components/board/nodes/FrameSwimlanes.vue +4 -3
  18. package/app/components/board/nodes/TaskCard.vue +12 -7
  19. package/app/components/board/nodes/TaskPipelineMini.vue +9 -7
  20. package/app/components/bootstrap/AdoptionReviewModal.vue +30 -21
  21. package/app/components/bootstrap/BootstrapModal.vue +3 -3
  22. package/app/components/brainstorm/BrainstormWindow.vue +5 -3
  23. package/app/components/bugFishing/BugFishingWindow.vue +49 -27
  24. package/app/components/clarity/ClarityReviewWindow.vue +5 -3
  25. package/app/components/common/CopyButton.vue +12 -11
  26. package/app/components/common/IconButton.vue +34 -9
  27. package/app/components/context/ContextAttachmentFields.vue +23 -12
  28. package/app/components/documents/ContextDocumentPicker.vue +12 -9
  29. package/app/components/documents/DocumentImportModal.vue +4 -3
  30. package/app/components/documents/DocumentOriginLink.vue +12 -9
  31. package/app/components/documents/DocumentSyncState.vue +3 -3
  32. package/app/components/documents/RepoContextDocPicker.vue +4 -3
  33. package/app/components/environments/EnvironmentStatusPanel.vue +4 -3
  34. package/app/components/followUp/FollowUpWindow.vue +9 -6
  35. package/app/components/forkDecision/ForkDecisionWindow.vue +71 -80
  36. package/app/components/foundational/FoundationalServiceCatalogList.vue +3 -2
  37. package/app/components/foundational/FoundationalServiceRegistry.vue +7 -6
  38. package/app/components/foundational/FoundationalServiceSources.vue +7 -6
  39. package/app/components/fragments/FragmentLibraryManager.vue +5 -4
  40. package/app/components/fragments/FragmentSelector.vue +12 -9
  41. package/app/components/gates/GateFailingCheckList.vue +4 -3
  42. package/app/components/gates/GateResultView.vue +17 -12
  43. package/app/components/github/AddServiceFromRepoModal.vue +11 -7
  44. package/app/components/github/GitHubOnboarding.vue +7 -2
  45. package/app/components/github/GitHubPanel.vue +9 -6
  46. package/app/components/github/RepoTreeBrowser.vue +15 -12
  47. package/app/components/humanTest/HumanTestWindow.vue +13 -9
  48. package/app/components/initiative/InitiativePlanReview.vue +28 -18
  49. package/app/components/initiative/InitiativeTrackerWindow.vue +171 -124
  50. package/app/components/judge/JudgeResultView.vue +5 -3
  51. package/app/components/kaizen/KaizenPanel.vue +67 -53
  52. package/app/components/layout/AccountTeamSettings.vue +9 -9
  53. package/app/components/layout/AppearanceSwitcher.vue +4 -3
  54. package/app/components/layout/BoardSwitcher.vue +10 -8
  55. package/app/components/layout/BoardToolbar.vue +5 -4
  56. package/app/components/layout/CommandBar.vue +4 -3
  57. package/app/components/layout/DefaultTestEnvBanner.vue +4 -3
  58. package/app/components/layout/IntegrationsHub.vue +8 -6
  59. package/app/components/layout/LanguageSwitcher.vue +4 -3
  60. package/app/components/layout/ModelProvidersHub.vue +4 -3
  61. package/app/components/layout/NotificationsInbox.vue +40 -30
  62. package/app/components/layout/PersonalSetupModal.vue +4 -3
  63. package/app/components/layout/RolePrompt.vue +4 -3
  64. package/app/components/layout/SideBar.vue +4 -3
  65. package/app/components/layout/TranslationWarningBanner.vue +4 -3
  66. package/app/components/layout/UiModeSwitcher.vue +8 -6
  67. package/app/components/layout/UiRoleSwitcher.vue +8 -6
  68. package/app/components/layout/WorkspaceMembersSettings.vue +2 -2
  69. package/app/components/media/ArtifactLightbox.vue +69 -39
  70. package/app/components/media/ImageCompare.vue +69 -74
  71. package/app/components/observability/OutcomeFilterChips.vue +5 -4
  72. package/app/components/observability/RunFailureSummary.vue +8 -6
  73. package/app/components/observability/StepModelActivity.vue +5 -3
  74. package/app/components/observability/ToolCallList.vue +4 -2
  75. package/app/components/outcome/OutcomeSummaryWindow.vue +5 -4
  76. package/app/components/palettes/AgentPalette.vue +18 -14
  77. package/app/components/panels/AgentStepDetail.vue +30 -19
  78. package/app/components/panels/ObservabilityPanel.vue +149 -114
  79. package/app/components/panels/OperatorDashboardPanel.vue +99 -91
  80. package/app/components/panels/ReportsPanel.vue +64 -61
  81. package/app/components/panels/ResultWindowShell.vue +148 -134
  82. package/app/components/panels/StepContainerStatus.vue +9 -9
  83. package/app/components/panels/StepDelegatedStatus.vue +11 -9
  84. package/app/components/panels/StepRestartControl.vue +3 -2
  85. package/app/components/panels/inspector/EpicChildren.vue +4 -3
  86. package/app/components/panels/inspector/FrontendConfig.vue +3 -2
  87. package/app/components/panels/inspector/InspectorSection.vue +22 -17
  88. package/app/components/panels/inspector/ServiceConnections.vue +3 -2
  89. package/app/components/panels/inspector/ServiceTestConfig.vue +5 -4
  90. package/app/components/panels/inspector/TaskExecution.vue +5 -4
  91. package/app/components/panels/inspector/TaskRunSettings.vue +10 -6
  92. package/app/components/pipeline/EstimateThresholdFields.vue +13 -10
  93. package/app/components/pipeline/GateConfigFields.vue +14 -18
  94. package/app/components/pipeline/PipelineBuilder.vue +90 -77
  95. package/app/components/pipeline/PipelinePicker.vue +8 -6
  96. package/app/components/pipeline/PipelineProgress.vue +26 -21
  97. package/app/components/prReview/PrReviewWindow.vue +46 -25
  98. package/app/components/providers/ApiKeysSection.vue +4 -3
  99. package/app/components/ralph/RalphLoopResultView.vue +4 -3
  100. package/app/components/requirements/RequirementsReviewWindow.vue +10 -6
  101. package/app/components/riskPolicy/RiskPolicyPicker.vue +8 -6
  102. package/app/components/sandbox/SandboxPanel.vue +73 -54
  103. package/app/components/settings/CloudflareHandlerSection.vue +33 -30
  104. package/app/components/settings/ConsensusGroupsSection.vue +40 -30
  105. package/app/components/settings/InfraHandlersConfigurator.vue +16 -13
  106. package/app/components/settings/InfrastructureBackendPicker.vue +17 -17
  107. package/app/components/settings/IssueTrackerPanel.vue +21 -6
  108. package/app/components/settings/LocalModelEndpointsPanel.vue +3 -2
  109. package/app/components/settings/ModelConfigurationPanel.vue +9 -8
  110. package/app/components/settings/OpenRouterCatalogPanel.vue +4 -3
  111. package/app/components/settings/ProviderPreferenceEditor.vue +5 -6
  112. package/app/components/settings/RiskPolicyEditorRow.vue +3 -2
  113. package/app/components/settings/ServiceFragmentDefaultsPanel.vue +10 -8
  114. package/app/components/skills/ReviewSkillQueue.vue +4 -3
  115. package/app/components/skills/SkillLibraryManager.vue +7 -6
  116. package/app/components/slack/SlackPanel.vue +9 -5
  117. package/app/components/spec/ServiceSpecWindow.vue +4 -3
  118. package/app/components/tasks/ContextIssuePicker.vue +12 -9
  119. package/app/components/tasks/TaskContextIssues.vue +4 -3
  120. package/app/components/testing/TestReportWindow.vue +14 -8
  121. package/app/components/theme/ThemeImportModal.vue +2 -2
  122. package/app/components/visualConfirm/VisualConfirmationWindow.vue +62 -35
  123. package/i18n/locales/de.json +7 -3
  124. package/i18n/locales/en.json +7 -3
  125. package/i18n/locales/es.json +7 -3
  126. package/i18n/locales/fr.json +7 -3
  127. package/i18n/locales/he.json +7 -3
  128. package/i18n/locales/it.json +7 -3
  129. package/i18n/locales/ja.json +7 -3
  130. package/i18n/locales/pl.json +7 -3
  131. package/i18n/locales/tr.json +7 -3
  132. package/i18n/locales/uk.json +7 -3
  133. package/package.json +2 -2
@@ -22,6 +22,7 @@
22
22
  // self-assessment (see the footer block below), so a window never renders it itself.
23
23
  import { computed, ref, watch } from 'vue'
24
24
  import { useModalBehavior } from '@modular-vue/core'
25
+ import IconButton from '~/components/common/IconButton.vue'
25
26
  import StepRestartControl from '~/components/panels/StepRestartControl.vue'
26
27
  import RunDetailLoadState from '~/components/panels/RunDetailLoadState.vue'
27
28
  import StepEffortReport from '~/components/panels/StepEffortReport.vue'
@@ -300,14 +301,15 @@ const panelClass = computed(() => [
300
301
  :step-index="stepRef.stepIndex"
301
302
  @restarted="requestClose"
302
303
  />
303
- <button
304
- class="rounded-md p-1.5 text-muted hover:bg-elevated hover:text-default"
304
+ <IconButton
305
+ icon="i-lucide-x"
306
+ color="neutral"
307
+ variant="ghost"
308
+ size="sm"
305
309
  data-testid="result-window-close"
306
- :aria-label="t('common.close')"
310
+ :label="t('common.close')"
307
311
  @click="requestClose"
308
- >
309
- <UIcon name="i-lucide-x" class="h-4 w-4" />
310
- </button>
312
+ />
311
313
  </header>
312
314
  <!-- Whether the whole-run fetch behind this window's prose has landed. -->
313
315
  <RunDetailLoadState :instance-id="activeInstanceId" />
@@ -322,34 +324,37 @@ const panelClass = computed(() => [
322
324
  class="shrink-0 border-t border-default bg-default/60"
323
325
  data-testid="result-window-effort"
324
326
  >
325
- <button
326
- type="button"
327
- class="flex w-full items-center gap-2 px-5 py-2 text-start hover:bg-elevated/40"
328
- :aria-expanded="effortOpen"
329
- data-testid="result-window-effort-toggle"
330
- @click="effortOpen = !effortOpen"
331
- >
332
- <UIcon name="i-lucide-gauge" class="h-3.5 w-3.5 shrink-0 text-muted" />
333
- <SectionLabel as="span">
334
- {{ t('panels.stepDetail.effort.heading') }}
335
- </SectionLabel>
336
- <span
337
- class="shrink-0 rounded-sm px-1.5 py-0.5 text-2xs font-medium tabular-nums"
338
- :class="chipClass"
327
+ <UCollapsible v-model:open="effortOpen">
328
+ <UButton
329
+ color="neutral"
330
+ variant="ghost"
331
+ class="flex w-full items-center gap-2 px-5 py-2 text-start hover:bg-elevated/40"
332
+ data-testid="result-window-effort-toggle"
339
333
  >
340
- {{ t('panels.stepDetail.effort.outOfTen', { value: effortReport.difficulty }) }}
341
- </span>
342
- <span v-if="hint" class="min-w-0 flex-1 truncate text-xs text-muted">
343
- {{ hint }}
344
- </span>
345
- <UIcon
346
- :name="effortOpen ? 'i-lucide-chevron-down' : 'i-lucide-chevron-up'"
347
- class="ms-auto h-3.5 w-3.5 shrink-0 text-dimmed"
348
- />
349
- </button>
350
- <div v-if="effortOpen" class="max-h-56 overflow-y-auto px-5 pb-3">
351
- <StepEffortReport :report="effortReport" variant="flat" />
352
- </div>
334
+ <UIcon name="i-lucide-gauge" class="h-3.5 w-3.5 shrink-0 text-muted" />
335
+ <SectionLabel as="span">
336
+ {{ t('panels.stepDetail.effort.heading') }}
337
+ </SectionLabel>
338
+ <span
339
+ class="shrink-0 rounded-sm px-1.5 py-0.5 text-2xs font-medium tabular-nums"
340
+ :class="chipClass"
341
+ >
342
+ {{ t('panels.stepDetail.effort.outOfTen', { value: effortReport.difficulty }) }}
343
+ </span>
344
+ <span v-if="hint" class="min-w-0 flex-1 truncate text-xs text-muted">
345
+ {{ hint }}
346
+ </span>
347
+ <UIcon
348
+ :name="effortOpen ? 'i-lucide-chevron-down' : 'i-lucide-chevron-up'"
349
+ class="ms-auto h-3.5 w-3.5 shrink-0 text-dimmed"
350
+ />
351
+ </UButton>
352
+ <template #content>
353
+ <div class="max-h-56 overflow-y-auto px-5 pb-3">
354
+ <StepEffortReport :report="effortReport" variant="flat" />
355
+ </div>
356
+ </template>
357
+ </UCollapsible>
353
358
  </section>
354
359
 
355
360
  <!-- Shared trailing section: the pre-PR validation report (the service's check commands
@@ -360,51 +365,54 @@ const panelClass = computed(() => [
360
365
  class="shrink-0 border-t border-default bg-default/60"
361
366
  data-testid="result-window-validation"
362
367
  >
363
- <button
364
- type="button"
365
- class="flex w-full items-center gap-2 px-5 py-2 text-start hover:bg-elevated/40"
366
- :aria-expanded="validationOpen"
367
- data-testid="result-window-validation-toggle"
368
- @click="validationOpen = !validationOpen"
369
- >
370
- <UIcon
371
- :name="validationReport.passed ? 'i-lucide-shield-check' : 'i-lucide-shield-alert'"
372
- class="h-3.5 w-3.5 shrink-0"
373
- :class="validationReport.passed ? 'text-app-success-400' : 'text-app-error-400'"
374
- />
375
- <SectionLabel as="span">
376
- {{ t('panels.stepDetail.validation.heading') }}
377
- </SectionLabel>
378
- <span
379
- class="shrink-0 rounded-sm px-1.5 py-0.5 text-2xs font-medium tabular-nums"
380
- :class="
381
- validationReport.passed
382
- ? 'bg-app-success-500/15 text-app-success-300'
383
- : 'bg-app-error-500/15 text-app-error-300'
384
- "
368
+ <UCollapsible v-model:open="validationOpen">
369
+ <UButton
370
+ color="neutral"
371
+ variant="ghost"
372
+ class="flex w-full items-center gap-2 px-5 py-2 text-start hover:bg-elevated/40"
373
+ data-testid="result-window-validation-toggle"
385
374
  >
386
- {{
387
- validationReport.passed
388
- ? t('panels.stepDetail.validation.passed')
389
- : t('panels.stepDetail.validation.failed')
390
- }}
391
- </span>
392
- <span class="min-w-0 flex-1 truncate text-xs text-muted">
393
- {{
394
- t('panels.stepDetail.validation.attempts', {
395
- attempts: validationReport.attempts,
396
- maxAttempts: validationReport.maxAttempts,
397
- })
398
- }}
399
- </span>
400
- <UIcon
401
- :name="validationOpen ? 'i-lucide-chevron-down' : 'i-lucide-chevron-up'"
402
- class="ms-auto h-3.5 w-3.5 shrink-0 text-dimmed"
403
- />
404
- </button>
405
- <div v-if="validationOpen" class="max-h-72 overflow-y-auto px-5 pb-3">
406
- <StepValidationReport :report="validationReport" />
407
- </div>
375
+ <UIcon
376
+ :name="validationReport.passed ? 'i-lucide-shield-check' : 'i-lucide-shield-alert'"
377
+ class="h-3.5 w-3.5 shrink-0"
378
+ :class="validationReport.passed ? 'text-app-success-400' : 'text-app-error-400'"
379
+ />
380
+ <SectionLabel as="span">
381
+ {{ t('panels.stepDetail.validation.heading') }}
382
+ </SectionLabel>
383
+ <span
384
+ class="shrink-0 rounded-sm px-1.5 py-0.5 text-2xs font-medium tabular-nums"
385
+ :class="
386
+ validationReport.passed
387
+ ? 'bg-app-success-500/15 text-app-success-300'
388
+ : 'bg-app-error-500/15 text-app-error-300'
389
+ "
390
+ >
391
+ {{
392
+ validationReport.passed
393
+ ? t('panels.stepDetail.validation.passed')
394
+ : t('panels.stepDetail.validation.failed')
395
+ }}
396
+ </span>
397
+ <span class="min-w-0 flex-1 truncate text-xs text-muted">
398
+ {{
399
+ t('panels.stepDetail.validation.attempts', {
400
+ attempts: validationReport.attempts,
401
+ maxAttempts: validationReport.maxAttempts,
402
+ })
403
+ }}
404
+ </span>
405
+ <UIcon
406
+ :name="validationOpen ? 'i-lucide-chevron-down' : 'i-lucide-chevron-up'"
407
+ class="ms-auto h-3.5 w-3.5 shrink-0 text-dimmed"
408
+ />
409
+ </UButton>
410
+ <template #content>
411
+ <div class="max-h-72 overflow-y-auto px-5 pb-3">
412
+ <StepValidationReport :report="validationReport" />
413
+ </div>
414
+ </template>
415
+ </UCollapsible>
408
416
  </section>
409
417
 
410
418
  <!-- Shared trailing section: the bugfix reproduction proof (the declared check run against
@@ -416,42 +424,45 @@ const panelClass = computed(() => [
416
424
  class="shrink-0 border-t border-default bg-default/60"
417
425
  data-testid="result-window-reproduction"
418
426
  >
419
- <button
420
- type="button"
421
- class="flex w-full items-center gap-2 px-5 py-2 text-start hover:bg-elevated/40"
422
- :aria-expanded="reproductionOpen"
423
- data-testid="result-window-reproduction-toggle"
424
- @click="reproductionOpen = !reproductionOpen"
425
- >
426
- <UIcon
427
- :name="reproductionKeys!.icon"
428
- class="h-3.5 w-3.5 shrink-0"
429
- :class="reproductionKeys!.proven ? 'text-app-success-400' : 'text-app-warning-400'"
430
- />
431
- <SectionLabel as="span">
432
- {{ t('panels.stepDetail.reproduction.heading') }}
433
- </SectionLabel>
434
- <span
435
- class="shrink-0 rounded-sm px-1.5 py-0.5 text-2xs font-medium"
436
- :class="
437
- reproductionKeys!.proven
438
- ? 'bg-app-success-500/15 text-app-success-300'
439
- : 'bg-app-warning-500/15 text-app-warning-300'
440
- "
427
+ <UCollapsible v-model:open="reproductionOpen">
428
+ <UButton
429
+ color="neutral"
430
+ variant="ghost"
431
+ class="flex w-full items-center gap-2 px-5 py-2 text-start hover:bg-elevated/40"
432
+ data-testid="result-window-reproduction-toggle"
441
433
  >
442
- {{ t(reproductionKeys!.chip) }}
443
- </span>
444
- <span class="min-w-0 flex-1 truncate text-xs text-muted">
445
- {{ t(reproductionKeys!.verdict) }}
446
- </span>
447
- <UIcon
448
- :name="reproductionOpen ? 'i-lucide-chevron-down' : 'i-lucide-chevron-up'"
449
- class="ms-auto h-3.5 w-3.5 shrink-0 text-dimmed"
450
- />
451
- </button>
452
- <div v-if="reproductionOpen" class="max-h-72 overflow-y-auto px-5 pb-3">
453
- <StepReproductionReport :report="reproductionReport" />
454
- </div>
434
+ <UIcon
435
+ :name="reproductionKeys!.icon"
436
+ class="h-3.5 w-3.5 shrink-0"
437
+ :class="reproductionKeys!.proven ? 'text-app-success-400' : 'text-app-warning-400'"
438
+ />
439
+ <SectionLabel as="span">
440
+ {{ t('panels.stepDetail.reproduction.heading') }}
441
+ </SectionLabel>
442
+ <span
443
+ class="shrink-0 rounded-sm px-1.5 py-0.5 text-2xs font-medium"
444
+ :class="
445
+ reproductionKeys!.proven
446
+ ? 'bg-app-success-500/15 text-app-success-300'
447
+ : 'bg-app-warning-500/15 text-app-warning-300'
448
+ "
449
+ >
450
+ {{ t(reproductionKeys!.chip) }}
451
+ </span>
452
+ <span class="min-w-0 flex-1 truncate text-xs text-muted">
453
+ {{ t(reproductionKeys!.verdict) }}
454
+ </span>
455
+ <UIcon
456
+ :name="reproductionOpen ? 'i-lucide-chevron-down' : 'i-lucide-chevron-up'"
457
+ class="ms-auto h-3.5 w-3.5 shrink-0 text-dimmed"
458
+ />
459
+ </UButton>
460
+ <template #content>
461
+ <div class="max-h-72 overflow-y-auto px-5 pb-3">
462
+ <StepReproductionReport :report="reproductionReport" />
463
+ </div>
464
+ </template>
465
+ </UCollapsible>
455
466
  </section>
456
467
 
457
468
  <!-- Shared trailing section: what this step's agent declared it stored through a
@@ -464,28 +475,31 @@ const panelClass = computed(() => [
464
475
  class="shrink-0 border-t border-default bg-default/60"
465
476
  data-testid="result-window-binary-outputs"
466
477
  >
467
- <button
468
- type="button"
469
- class="flex w-full items-center gap-2 px-5 py-2 text-start hover:bg-elevated/40"
470
- :aria-expanded="binaryOutputsOpen"
471
- data-testid="result-window-binary-outputs-toggle"
472
- @click="binaryOutputsOpen = !binaryOutputsOpen"
473
- >
474
- <UIcon name="i-lucide-image" class="h-3.5 w-3.5 shrink-0 text-muted" />
475
- <SectionLabel as="span">
476
- {{ t('binaryOutput.heading') }}
477
- </SectionLabel>
478
- <span class="min-w-0 flex-1 truncate text-xs text-muted">
479
- {{ binaryOutputSummary }}
480
- </span>
481
- <UIcon
482
- :name="binaryOutputsOpen ? 'i-lucide-chevron-down' : 'i-lucide-chevron-up'"
483
- class="ms-auto h-3.5 w-3.5 shrink-0 text-dimmed"
484
- />
485
- </button>
486
- <div v-if="binaryOutputsOpen && activeStep" class="max-h-72 overflow-y-auto px-5 pb-3">
487
- <BinaryOutputReport :step="activeStep" />
488
- </div>
478
+ <UCollapsible v-model:open="binaryOutputsOpen">
479
+ <UButton
480
+ color="neutral"
481
+ variant="ghost"
482
+ class="flex w-full items-center gap-2 px-5 py-2 text-start hover:bg-elevated/40"
483
+ data-testid="result-window-binary-outputs-toggle"
484
+ >
485
+ <UIcon name="i-lucide-image" class="h-3.5 w-3.5 shrink-0 text-muted" />
486
+ <SectionLabel as="span">
487
+ {{ t('binaryOutput.heading') }}
488
+ </SectionLabel>
489
+ <span class="min-w-0 flex-1 truncate text-xs text-muted">
490
+ {{ binaryOutputSummary }}
491
+ </span>
492
+ <UIcon
493
+ :name="binaryOutputsOpen ? 'i-lucide-chevron-down' : 'i-lucide-chevron-up'"
494
+ class="ms-auto h-3.5 w-3.5 shrink-0 text-dimmed"
495
+ />
496
+ </UButton>
497
+ <template #content>
498
+ <div v-if="activeStep" class="max-h-72 overflow-y-auto px-5 pb-3">
499
+ <BinaryOutputReport :step="activeStep" />
500
+ </div>
501
+ </template>
502
+ </UCollapsible>
489
503
  </section>
490
504
  </div>
491
505
  </div>
@@ -3,6 +3,7 @@ import { computed } from 'vue'
3
3
  import type { PipelineStep, RunContainerStatus } from '~/types/execution'
4
4
  import { containerPhaseLabel } from '~/utils/pipelineRender'
5
5
  import SectionLabel from '~/components/common/SectionLabel.vue'
6
+ import IconButton from '~/components/common/IconButton.vue'
6
7
 
7
8
  // The per-run container lifecycle for a container-backed step: its status (spinning up /
8
9
  // running / errored / reclaimed), the live phase (preparing the checkout vs the agent
@@ -118,14 +119,13 @@ const { copy: copyText } = useCopyToClipboard()
118
119
  <dd class="truncate font-mono text-2xs text-toned" :title="step.container.id">
119
120
  {{ step.container.id }}
120
121
  </dd>
121
- <UButton
122
+ <IconButton
122
123
  icon="i-lucide-copy"
123
124
  color="neutral"
124
125
  variant="ghost"
125
126
  size="xs"
126
127
  class="ms-auto shrink-0"
127
- :title="t('panels.stepMeta.container.copyId')"
128
- :aria-label="t('panels.stepMeta.container.copyId')"
128
+ :label="t('panels.stepMeta.container.copyId')"
129
129
  @click="copyText(step.container.id)"
130
130
  />
131
131
  </div>
@@ -134,23 +134,23 @@ const { copy: copyText } = useCopyToClipboard()
134
134
  {{ t('panels.stepMeta.container.url') }}
135
135
  </SectionLabel>
136
136
  <dd class="truncate font-mono text-2xs text-toned">
137
- <a
138
- :href="step.container.url"
137
+ <ULink
138
+ raw
139
+ :to="step.container.url"
139
140
  target="_blank"
140
141
  rel="noopener noreferrer"
141
142
  class="hover:underline"
142
143
  >
143
144
  {{ step.container.url }}
144
- </a>
145
+ </ULink>
145
146
  </dd>
146
- <UButton
147
+ <IconButton
147
148
  icon="i-lucide-copy"
148
149
  color="neutral"
149
150
  variant="ghost"
150
151
  size="xs"
151
152
  class="ms-auto shrink-0"
152
- :title="t('panels.stepMeta.container.copyUrl')"
153
- :aria-label="t('panels.stepMeta.container.copyUrl')"
153
+ :label="t('panels.stepMeta.container.copyUrl')"
154
154
  @click="copyText(step.container.url)"
155
155
  />
156
156
  </div>
@@ -7,6 +7,7 @@ import {
7
7
  } from './StepDelegatedStatus.logic'
8
8
  import type { PipelineStep } from '~/types/execution'
9
9
  import SectionLabel from '~/components/common/SectionLabel.vue'
10
+ import IconButton from '~/components/common/IconButton.vue'
10
11
 
11
12
  // The EXTERNAL work a delegated step dispatched: which registered executor is running it, what it
12
13
  // is doing, and the link to that system's own logs, which is the affordance everything else here
@@ -106,25 +107,25 @@ const { copy: copyText } = useCopyToClipboard()
106
107
  it is: refusing to follow it is right, and hiding it would report a run that named
107
108
  no link at all. -->
108
109
  <dd class="truncate font-mono text-2xs text-toned">
109
- <a
110
+ <ULink
111
+ raw
110
112
  v-if="runHref"
111
- :href="runHref"
113
+ :to="runHref"
112
114
  target="_blank"
113
115
  rel="noopener noreferrer"
114
116
  class="hover:underline"
115
117
  >
116
118
  {{ record.url }}
117
- </a>
119
+ </ULink>
118
120
  <span v-else>{{ record.url }}</span>
119
121
  </dd>
120
- <UButton
122
+ <IconButton
121
123
  icon="i-lucide-copy"
122
124
  color="neutral"
123
125
  variant="ghost"
124
126
  size="xs"
125
127
  class="ms-auto shrink-0"
126
- :title="t('panels.stepMeta.delegated.copyUrl')"
127
- :aria-label="t('panels.stepMeta.delegated.copyUrl')"
128
+ :label="t('panels.stepMeta.delegated.copyUrl')"
128
129
  @click="copyText(record.url)"
129
130
  />
130
131
  </div>
@@ -166,15 +167,16 @@ const { copy: copyText } = useCopyToClipboard()
166
167
  <span class="truncate">{{
167
168
  attempt.outcome || t('panels.stepMeta.delegated.noOutcome')
168
169
  }}</span>
169
- <a
170
+ <ULink
171
+ raw
170
172
  v-if="externalRunHref(attempt.url)"
171
- :href="externalRunHref(attempt.url)!"
173
+ :to="externalRunHref(attempt.url)!"
172
174
  target="_blank"
173
175
  rel="noopener noreferrer"
174
176
  class="ms-auto shrink-0 font-mono hover:underline"
175
177
  >
176
178
  {{ t('panels.stepMeta.delegated.openRun') }}
177
- </a>
179
+ </ULink>
178
180
  </li>
179
181
  </ul>
180
182
  </div>
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, computed } from 'vue'
3
+ import IconButton from '~/components/common/IconButton.vue'
3
4
 
4
5
  // Shared "Restart pipeline from this step" control.
5
6
  //
@@ -57,13 +58,13 @@ async function restart() {
57
58
 
58
59
  <template>
59
60
  <template v-if="canRestart">
60
- <UButton
61
+ <IconButton
61
62
  v-if="!armed"
62
63
  icon="i-lucide-rotate-ccw"
63
64
  color="neutral"
64
65
  variant="ghost"
65
66
  size="sm"
66
- :title="t('panels.stepRestart.restartFromStep')"
67
+ :label="t('panels.stepRestart.restartFromStep')"
67
68
  @click="
68
69
  () => {
69
70
  armed = true
@@ -72,10 +72,11 @@ const groups = computed(() => {
72
72
  <SectionLabel v-if="mod.module">
73
73
  {{ mod.module.title }}
74
74
  </SectionLabel>
75
- <button
75
+ <UButton
76
+ color="neutral"
77
+ variant="ghost"
76
78
  v-for="task in mod.tasks"
77
79
  :key="task.id"
78
- type="button"
79
80
  class="flex w-full items-center gap-1.5 rounded-sm px-1 py-0.5 text-start text-xs text-default hover:bg-elevated"
80
81
  @click="ui.select(task.id)"
81
82
  >
@@ -87,7 +88,7 @@ const groups = computed(() => {
87
88
  <span class="ms-auto shrink-0 text-3xs text-dimmed">
88
89
  {{ STATUS_META[task.status].label }}
89
90
  </span>
90
- </button>
91
+ </UButton>
91
92
  </div>
92
93
  </div>
93
94
  </div>
@@ -12,6 +12,7 @@ import type {
12
12
  } from '~/types/domain'
13
13
  import FrontendBindingsResolved from '~/components/panels/inspector/FrontendBindingsResolved.vue'
14
14
  import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
15
+ import IconButton from '~/components/common/IconButton.vue'
15
16
  import { apiErrorEnvelope } from '~/composables/api/errors'
16
17
 
17
18
  // Frontend-frame (`type: 'frontend'`) configuration: how to build, serve, and mock this
@@ -556,12 +557,12 @@ onUnmounted(() => preview.stopPolling(props.block.id))
556
557
  class="flex-1"
557
558
  @update:model-value="(v: string) => setBindingSource(i, v)"
558
559
  />
559
- <UButton
560
+ <IconButton
560
561
  size="xs"
561
562
  variant="ghost"
562
563
  color="neutral"
563
564
  icon="i-lucide-x"
564
- :title="t('inspector.frontendConfig.bindings.remove')"
565
+ :label="t('inspector.frontendConfig.bindings.remove')"
565
566
  @click="removeBinding(i)"
566
567
  />
567
568
  </div>
@@ -28,27 +28,32 @@ if (props.defaultOpen) open.value = true
28
28
  <template>
29
29
  <section class="border-t border-default pt-2" data-testid="inspector-section">
30
30
  <div class="flex items-center gap-1.5">
31
- <SectionLabel
32
- as="button"
33
- type="button"
34
- class="flex min-w-0 flex-1 items-center gap-1.5 text-start hover:text-default"
31
+ <UButton
32
+ color="neutral"
33
+ variant="link"
34
+ class="group min-w-0 flex-1 p-0"
35
35
  :aria-expanded="open"
36
36
  data-testid="inspector-section-toggle"
37
37
  @click="open = !open"
38
38
  >
39
- <UIcon
40
- :name="open ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
41
- class="h-3.5 w-3.5 shrink-0 text-dimmed"
42
- />
43
- <UIcon v-if="icon" :name="icon" class="h-3.5 w-3.5 shrink-0" />
44
- <span class="truncate">{{ title }}</span>
45
- <span v-if="count" class="font-normal normal-case text-dimmed">({{ count }})</span>
46
- <UIcon
47
- v-if="warning"
48
- name="i-lucide-triangle-alert"
49
- class="h-3.5 w-3.5 shrink-0 text-app-warning-400"
50
- />
51
- </SectionLabel>
39
+ <SectionLabel
40
+ as="span"
41
+ class="flex min-w-0 flex-1 items-center gap-1.5 group-hover:text-default"
42
+ >
43
+ <UIcon
44
+ :name="open ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
45
+ class="h-3.5 w-3.5 shrink-0 text-dimmed"
46
+ />
47
+ <UIcon v-if="icon" :name="icon" class="h-3.5 w-3.5 shrink-0" />
48
+ <span class="truncate">{{ title }}</span>
49
+ <span v-if="count" class="font-normal normal-case text-dimmed">({{ count }})</span>
50
+ <UIcon
51
+ v-if="warning"
52
+ name="i-lucide-triangle-alert"
53
+ class="h-3.5 w-3.5 shrink-0 text-app-warning-400"
54
+ />
55
+ </SectionLabel>
56
+ </UButton>
52
57
  <!-- An action ("+", attach, menu) mutates the body list, so expand (never collapse)
53
58
  on interaction — otherwise the new row lands inside the collapsed body. -->
54
59
  <div v-if="$slots.actions" class="contents" @click="open = true">
@@ -2,6 +2,7 @@
2
2
  import { computed } from 'vue'
3
3
  import type { Block, ServiceConnection } from '~/types/domain'
4
4
  import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
5
+ import IconButton from '~/components/common/IconButton.vue'
5
6
 
6
7
  // Service-frame (`type: 'service'`) connections: the other services this one USES
7
8
  // (consumer→provider edges, stored on this frame — the consumer end). Each row picks a
@@ -124,12 +125,12 @@ const usedBy = computed(() =>
124
125
  (e: KeyboardEvent) => setDescription(i, (e.target as HTMLInputElement).value)
125
126
  "
126
127
  />
127
- <UButton
128
+ <IconButton
128
129
  size="xs"
129
130
  variant="ghost"
130
131
  color="neutral"
131
132
  icon="i-lucide-x"
132
- :title="t('inspector.serviceConnections.remove')"
133
+ :label="t('inspector.serviceConnections.remove')"
133
134
  data-testid="service-connection-remove"
134
135
  @click="removeConnection(i)"
135
136
  />
@@ -19,6 +19,7 @@ import type {
19
19
  import RepoTreeBrowser from '~/components/github/RepoTreeBrowser.vue'
20
20
  import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
21
21
  import ServiceSelfTests from '~/components/panels/inspector/ServiceSelfTests.vue'
22
+ import IconButton from '~/components/common/IconButton.vue'
22
23
  import { apiErrorEnvelope } from '~/composables/api/errors'
23
24
 
24
25
  // Service-level (frame) configuration: the service-owned PROVISIONING — the provision
@@ -634,13 +635,13 @@ function setSize(value: InstanceSize) {
634
635
  (e: KeyboardEvent) => setComposePath((e.target as HTMLInputElement).value)
635
636
  "
636
637
  />
637
- <UButton
638
+ <IconButton
638
639
  v-if="repoContext"
639
640
  size="xs"
640
641
  variant="soft"
641
642
  color="neutral"
642
643
  icon="i-lucide-folder-search"
643
- :title="t('inspector.testConfig.browseRepo')"
644
+ :label="t('inspector.testConfig.browseRepo')"
644
645
  @click="openBrowse('compose')"
645
646
  />
646
647
  </div>
@@ -715,13 +716,13 @@ function setSize(value: InstanceSize) {
715
716
  @blur="(e: FocusEvent) => setKubePath((e.target as HTMLInputElement).value)"
716
717
  @keydown.enter="(e: KeyboardEvent) => setKubePath((e.target as HTMLInputElement).value)"
717
718
  />
718
- <UButton
719
+ <IconButton
719
720
  v-if="repoContext && kubeSourceType === 'colocated'"
720
721
  size="xs"
721
722
  variant="soft"
722
723
  color="neutral"
723
724
  icon="i-lucide-folder-search"
724
- :title="t('inspector.testConfig.browseRepo')"
725
+ :label="t('inspector.testConfig.browseRepo')"
725
726
  @click="openBrowse('k8s')"
726
727
  />
727
728
  </div>