@cat-factory/app 0.307.1 → 0.309.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/README.md +21 -0
  2. package/app/assets/css/main.css +22 -0
  3. package/app/assets/css/prose.css +4 -4
  4. package/app/assets/css/type.css +24 -0
  5. package/app/components/assistant/AssistantModal.vue +3 -2
  6. package/app/components/auth/UserMenu.vue +1 -1
  7. package/app/components/binaryCandidates/BinaryCandidatesWindow.vue +15 -15
  8. package/app/components/binaryOutput/BinaryOutputReport.vue +10 -14
  9. package/app/components/binaryOutput/StoredAssetView.vue +5 -5
  10. package/app/components/board/AddTaskModal.vue +16 -14
  11. package/app/components/board/AgentFailureCard.vue +9 -9
  12. package/app/components/board/AgentFailureHistory.vue +1 -1
  13. package/app/components/board/CreateInitiativeModal.vue +2 -2
  14. package/app/components/board/FailureDetail.vue +1 -1
  15. package/app/components/board/FailureHistoryList.vue +5 -5
  16. package/app/components/board/RecurringPipelineModal.vue +12 -11
  17. package/app/components/board/ReviewFrictionDialog.vue +4 -3
  18. package/app/components/board/StepExecutionHistory.vue +8 -8
  19. package/app/components/board/nodes/AgentChip.vue +1 -1
  20. package/app/components/board/nodes/BlockNode.vue +8 -9
  21. package/app/components/board/nodes/EpicNode.vue +4 -4
  22. package/app/components/board/nodes/FrameSwimlanes.vue +4 -7
  23. package/app/components/board/nodes/InitiativeCard.vue +4 -4
  24. package/app/components/board/nodes/LaneGroup.vue +3 -5
  25. package/app/components/board/nodes/TaskCard.vue +8 -8
  26. package/app/components/board/nodes/TaskLane.vue +3 -3
  27. package/app/components/board/nodes/TaskPipelineMini.vue +13 -12
  28. package/app/components/bootstrap/AdoptionReviewModal.vue +7 -6
  29. package/app/components/bootstrap/BootstrapModal.vue +11 -10
  30. package/app/components/bootstrap/BootstrapRunSteps.vue +1 -1
  31. package/app/components/brainstorm/BrainstormWindow.vue +8 -7
  32. package/app/components/bugFishing/BugFishingWindow.vue +37 -36
  33. package/app/components/clarity/ClarityReviewWindow.vue +8 -7
  34. package/app/components/common/AsyncViewError.vue +1 -1
  35. package/app/components/common/ClarificationItem.vue +6 -6
  36. package/app/components/common/DescriptorFields.vue +1 -1
  37. package/app/components/common/EmptyState.vue +1 -1
  38. package/app/components/common/InterviewGateNotice.vue +4 -4
  39. package/app/components/common/MarkdownProse.vue +8 -2
  40. package/app/components/common/SectionLabel.vue +38 -0
  41. package/app/components/consensus/ConsensusSessionWindow.vue +10 -9
  42. package/app/components/context/ContextAttachmentFields.vue +9 -8
  43. package/app/components/docs/DocInterviewWindow.vue +10 -9
  44. package/app/components/documents/ContextDocumentPicker.vue +10 -12
  45. package/app/components/documents/DocumentImportModal.vue +3 -2
  46. package/app/components/documents/DocumentSourceConnectModal.vue +2 -2
  47. package/app/components/documents/DocumentSyncState.vue +1 -1
  48. package/app/components/documents/DocumentTemplatesModal.vue +5 -4
  49. package/app/components/documents/RepoContextDocPicker.vue +3 -6
  50. package/app/components/documents/StartFromDesignModal.vue +4 -4
  51. package/app/components/documents/TaskContextDocs.vue +1 -1
  52. package/app/components/environments/EnvSetupStepper.vue +3 -3
  53. package/app/components/environments/EnvironmentStatusPanel.vue +11 -10
  54. package/app/components/environments/steps/EnvPreflightStep.vue +8 -8
  55. package/app/components/environments/steps/EnvReviewStep.vue +23 -22
  56. package/app/components/environments/steps/EnvSaveStep.vue +5 -5
  57. package/app/components/focus/BlockFocusView.vue +11 -10
  58. package/app/components/followUp/FollowUpWindow.vue +10 -10
  59. package/app/components/forkDecision/ForkDecisionWindow.vue +23 -26
  60. package/app/components/foundational/FoundationalContractSummary.vue +2 -2
  61. package/app/components/foundational/FoundationalServiceCatalogList.vue +3 -3
  62. package/app/components/foundational/FoundationalServiceRegistry.vue +3 -3
  63. package/app/components/foundational/FoundationalSuppressions.vue +2 -2
  64. package/app/components/fragments/FragmentLibraryManager.vue +3 -3
  65. package/app/components/fragments/FragmentSelector.vue +10 -11
  66. package/app/components/gates/GateFailingCheckList.vue +3 -3
  67. package/app/components/gates/GateResultView.vue +39 -38
  68. package/app/components/github/AddServiceFromRepoModal.vue +5 -4
  69. package/app/components/github/BranchProtectionPreflight.vue +5 -5
  70. package/app/components/github/GitHubConnect.vue +5 -3
  71. package/app/components/github/GitHubPanel.vue +14 -13
  72. package/app/components/github/RepoTreeBrowser.vue +1 -1
  73. package/app/components/humanTest/HumanTestWindow.vue +18 -17
  74. package/app/components/initiative/InitiativePlanNotice.vue +3 -3
  75. package/app/components/initiative/InitiativePlanReview.vue +19 -21
  76. package/app/components/initiative/InitiativePlanningWindow.vue +5 -5
  77. package/app/components/initiative/InitiativeTrackerWindow.vue +54 -53
  78. package/app/components/judge/JudgeResultView.vue +24 -25
  79. package/app/components/kaizen/KaizenPanel.vue +4 -4
  80. package/app/components/kaizen/KaizenStepStatus.vue +7 -6
  81. package/app/components/layout/AccountAuditLog.vue +1 -1
  82. package/app/components/layout/AccountDeploymentSettings.vue +10 -10
  83. package/app/components/layout/AccountFailureKindRules.vue +13 -13
  84. package/app/components/layout/AccountFoundationalSettings.vue +1 -1
  85. package/app/components/layout/AccountFragmentSettings.vue +1 -1
  86. package/app/components/layout/AccountModelPolicySettings.vue +7 -7
  87. package/app/components/layout/AccountPlatformAlertSettings.vue +8 -8
  88. package/app/components/layout/AccountRiskPolicySettings.vue +2 -2
  89. package/app/components/layout/AccountRunCredentialSettings.vue +3 -3
  90. package/app/components/layout/AccountSkillSettings.vue +1 -1
  91. package/app/components/layout/AccountTeamSettings.vue +4 -0
  92. package/app/components/layout/AiProvidersBanner.vue +1 -1
  93. package/app/components/layout/AppearanceSwitcher.vue +1 -1
  94. package/app/components/layout/BoardSwitcher.vue +3 -2
  95. package/app/components/layout/BoardToolbar.vue +6 -2
  96. package/app/components/layout/CommandBar.vue +4 -2
  97. package/app/components/layout/DefaultTestEnvBanner.vue +2 -2
  98. package/app/components/layout/IntegrationsHub.vue +4 -3
  99. package/app/components/layout/LanguageSwitcher.vue +1 -1
  100. package/app/components/layout/ModelProvidersHub.vue +6 -4
  101. package/app/components/layout/NotificationsInbox.vue +8 -7
  102. package/app/components/layout/PersonalSetupModal.vue +5 -3
  103. package/app/components/layout/RolePrompt.vue +1 -1
  104. package/app/components/layout/SideBar.vue +3 -5
  105. package/app/components/layout/SpendWarningBanner.vue +3 -3
  106. package/app/components/layout/TranslationWarningBanner.vue +1 -1
  107. package/app/components/layout/UiModeSwitcher.vue +7 -6
  108. package/app/components/layout/UiRoleSwitcher.vue +2 -2
  109. package/app/components/media/ArtifactLightbox.vue +6 -6
  110. package/app/components/media/ImageCompare.vue +16 -16
  111. package/app/components/merge/MergeEffortChips.vue +4 -6
  112. package/app/components/notifications/NotificationSettingsPanel.vue +6 -5
  113. package/app/components/observability/OutcomeFilterChips.vue +1 -1
  114. package/app/components/observability/RunFailureSummary.vue +14 -17
  115. package/app/components/observability/StepMetricsBar.vue +9 -8
  116. package/app/components/observability/StepModelActivity.vue +4 -3
  117. package/app/components/observability/ToolCallList.vue +15 -18
  118. package/app/components/outcome/OutcomeSummaryWindow.vue +43 -42
  119. package/app/components/palettes/AgentPalette.vue +7 -5
  120. package/app/components/palettes/AgentTierSelect.vue +1 -1
  121. package/app/components/palettes/PipelinePurposeSelect.vue +2 -1
  122. package/app/components/panels/AgentStepDetail.vue +27 -30
  123. package/app/components/panels/AttemptEntryHeader.vue +2 -2
  124. package/app/components/panels/DecisionModal.vue +1 -1
  125. package/app/components/panels/GenericStructuredResultView.vue +6 -5
  126. package/app/components/panels/InspectorPanel.vue +15 -13
  127. package/app/components/panels/MergerResultView.vue +10 -9
  128. package/app/components/panels/ObservabilityPanel.vue +66 -76
  129. package/app/components/panels/OperatorDashboardPanel.vue +27 -26
  130. package/app/components/panels/ReportsPanel.vue +26 -25
  131. package/app/components/panels/ReportsSpendBreakdown.vue +2 -2
  132. package/app/components/panels/ResultWindowShell.vue +19 -18
  133. package/app/components/panels/RunDetailLoadState.vue +1 -1
  134. package/app/components/panels/StepContainerStatus.vue +8 -7
  135. package/app/components/panels/StepDelegatedStatus.vue +13 -12
  136. package/app/components/panels/StepEffortReport.vue +12 -17
  137. package/app/components/panels/StepFragmentAdherence.vue +9 -10
  138. package/app/components/panels/StepMetadataCard.vue +41 -43
  139. package/app/components/panels/StepReproductionReport.vue +18 -20
  140. package/app/components/panels/StepRunMeta.vue +27 -26
  141. package/app/components/panels/StepTestReport.vue +16 -15
  142. package/app/components/panels/StepToolServers.vue +7 -8
  143. package/app/components/panels/StepValidationReport.vue +5 -5
  144. package/app/components/panels/inspector/ContainerSummary.vue +4 -4
  145. package/app/components/panels/inspector/DocReferenceRepos.vue +5 -4
  146. package/app/components/panels/inspector/EnvProbeReport.vue +16 -13
  147. package/app/components/panels/inspector/EpicChildren.vue +8 -7
  148. package/app/components/panels/inspector/FrontendBindingsResolved.vue +5 -4
  149. package/app/components/panels/inspector/FrontendConfig.vue +26 -34
  150. package/app/components/panels/inspector/InitiativeInspector.vue +3 -3
  151. package/app/components/panels/inspector/InspectorSection.vue +7 -4
  152. package/app/components/panels/inspector/RecurringScheduleSettings.vue +9 -8
  153. package/app/components/panels/inspector/ServiceConnections.vue +2 -2
  154. package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +2 -2
  155. package/app/components/panels/inspector/ServiceSelfTests.vue +9 -13
  156. package/app/components/panels/inspector/ServiceTestConfig.vue +41 -45
  157. package/app/components/panels/inspector/ServiceTestSecrets.vue +3 -3
  158. package/app/components/panels/inspector/ServiceTestingContext.vue +2 -2
  159. package/app/components/panels/inspector/ServiceValidationConfig.vue +2 -2
  160. package/app/components/panels/inspector/TaskAgentConfig.vue +2 -2
  161. package/app/components/panels/inspector/TaskAprioriBranches.vue +6 -5
  162. package/app/components/panels/inspector/TaskDependencies.vue +1 -1
  163. package/app/components/panels/inspector/TaskEstimateBadge.vue +3 -7
  164. package/app/components/panels/inspector/TaskExecution.vue +13 -12
  165. package/app/components/panels/inspector/TaskRunSettings.vue +35 -38
  166. package/app/components/panels/inspector/TaskStructure.vue +5 -4
  167. package/app/components/pipeline/AgentPromptEditor.vue +15 -14
  168. package/app/components/pipeline/BinaryOutputStepPicker.vue +42 -52
  169. package/app/components/pipeline/EstimateThresholdFields.vue +2 -2
  170. package/app/components/pipeline/GateConfigFields.vue +6 -6
  171. package/app/components/pipeline/IterationCapPrompt.vue +1 -1
  172. package/app/components/pipeline/PipelineBuilder.vue +28 -27
  173. package/app/components/pipeline/PipelineHealthModal.vue +7 -7
  174. package/app/components/pipeline/PipelinePicker.vue +3 -3
  175. package/app/components/pipeline/PipelinePreview.vue +6 -7
  176. package/app/components/pipeline/PipelineProgress.vue +23 -24
  177. package/app/components/prReview/PrReviewWindow.vue +42 -41
  178. package/app/components/providers/AiPresetMismatchDialog.vue +4 -3
  179. package/app/components/providers/AiProviderOnboardingModal.vue +2 -2
  180. package/app/components/providers/ApiKeysSection.vue +7 -6
  181. package/app/components/providers/PersonalSubscriptionSection.vue +4 -3
  182. package/app/components/providers/VendorCredentialsModal.vue +5 -4
  183. package/app/components/provisioning/ProvisioningLogsDrawer.vue +10 -9
  184. package/app/components/ralph/RalphLoopResultView.vue +25 -26
  185. package/app/components/requirements/RequirementsReviewWindow.vue +13 -12
  186. package/app/components/riskPolicy/RiskPolicyPicker.vue +5 -5
  187. package/app/components/riskPolicy/RiskPolicyPreview.vue +16 -15
  188. package/app/components/sandbox/SandboxPanel.vue +21 -20
  189. package/app/components/settings/ApiTokensPanel.vue +1 -1
  190. package/app/components/settings/BudgetSettings.vue +18 -17
  191. package/app/components/settings/CapabilityCredentialsPanel.vue +5 -5
  192. package/app/components/settings/CloudflareHandlerSection.vue +16 -16
  193. package/app/components/settings/ComposeEnvironmentSetupSection.vue +14 -12
  194. package/app/components/settings/ConnectionTestVerdict.vue +1 -1
  195. package/app/components/settings/ConsensusGroupsSection.vue +21 -20
  196. package/app/components/settings/CustomManifestTypeEditor.vue +8 -7
  197. package/app/components/settings/DefaultProvisionTypeSection.vue +5 -5
  198. package/app/components/settings/InfraHandlersConfigurator.vue +14 -15
  199. package/app/components/settings/InfrastructureBackendPicker.vue +2 -2
  200. package/app/components/settings/IssueTrackerPanel.vue +9 -15
  201. package/app/components/settings/KubernetesEngineForm.vue +9 -8
  202. package/app/components/settings/KubernetesEnvironmentForm.vue +4 -3
  203. package/app/components/settings/LocalContainerPoolSettings.vue +2 -2
  204. package/app/components/settings/LocalModelEndpointsPanel.vue +10 -9
  205. package/app/components/settings/MergeClassRulesEditor.vue +6 -5
  206. package/app/components/settings/MergeRolePolicyEditor.vue +13 -12
  207. package/app/components/settings/ModelConfigurationPanel.vue +8 -11
  208. package/app/components/settings/ModelPresetHealthModal.vue +3 -3
  209. package/app/components/settings/ObservabilityConnectionPanel.vue +1 -1
  210. package/app/components/settings/OpenRouterCatalogPanel.vue +6 -5
  211. package/app/components/settings/PackageRegistriesPanel.vue +4 -4
  212. package/app/components/settings/ProviderConnectionTab.vue +5 -4
  213. package/app/components/settings/ProviderManifestEditor.vue +9 -8
  214. package/app/components/settings/ProviderPreferenceEditor.vue +8 -7
  215. package/app/components/settings/RiskPolicyCreateForm.vue +40 -26
  216. package/app/components/settings/RiskPolicyEditorRow.vue +18 -17
  217. package/app/components/settings/RiskPolicyHealthModal.vue +3 -3
  218. package/app/components/settings/RiskPolicyPanel.vue +11 -13
  219. package/app/components/settings/ServiceFragmentDefaultsPanel.vue +5 -4
  220. package/app/components/settings/SharedStacksPanel.vue +5 -5
  221. package/app/components/settings/TaskSourceCard.vue +2 -2
  222. package/app/components/settings/TaskTypeSuppressionsPanel.vue +4 -4
  223. package/app/components/settings/ToolServerChecklist.vue +22 -22
  224. package/app/components/settings/UsageSettings.vue +13 -13
  225. package/app/components/settings/UserSecretsSection.vue +4 -3
  226. package/app/components/settings/WorkspaceMetadataSettings.vue +6 -5
  227. package/app/components/settings/WorkspaceSettingsPanel.vue +72 -58
  228. package/app/components/skills/ReviewSkillQueue.vue +8 -7
  229. package/app/components/skills/SkillLibraryManager.vue +2 -2
  230. package/app/components/slack/SlackPanel.vue +8 -7
  231. package/app/components/spec/ServiceSpecWindow.vue +15 -16
  232. package/app/components/tasks/BugHuntModal.vue +1 -1
  233. package/app/components/tasks/ContextIssuePicker.vue +4 -6
  234. package/app/components/tasks/TaskContextIssues.vue +1 -1
  235. package/app/components/tasks/TaskImportModal.vue +3 -2
  236. package/app/components/tasks/TaskSourceConnectModal.vue +5 -5
  237. package/app/components/testing/TestReportWindow.vue +54 -53
  238. package/app/components/tutorial/TutorialNudge.vue +3 -2
  239. package/app/components/tutorial/TutorialOverlay.vue +3 -2
  240. package/app/components/vcs/GitLabConnect.vue +1 -1
  241. package/app/components/visualConfirm/VisualConfirmationWindow.vue +23 -25
  242. package/app/composables/usePipelineErrorToast.ts +2 -0
  243. package/app/utils/catalog.ts +8 -0
  244. package/app/utils/theme/builtins.ts +7 -1
  245. package/i18n/locales/de.json +6 -3
  246. package/i18n/locales/en.json +6 -3
  247. package/i18n/locales/es.json +6 -3
  248. package/i18n/locales/fr.json +6 -3
  249. package/i18n/locales/he.json +6 -3
  250. package/i18n/locales/it.json +6 -3
  251. package/i18n/locales/ja.json +6 -3
  252. package/i18n/locales/pl.json +6 -3
  253. package/i18n/locales/tr.json +6 -3
  254. package/i18n/locales/uk.json +6 -3
  255. package/package.json +2 -2
@@ -128,10 +128,10 @@ async function toggleEnabled(enabled: boolean) {
128
128
 
129
129
  <!-- Credentialless source (GitHub Issues): no form, just the on/off toggle. -->
130
130
  <template v-if="credentialless">
131
- <p class="text-[11px] text-dimmed">
131
+ <p class="text-2xs text-dimmed">
132
132
  {{ t('tasks.connect.credentialless') }}
133
133
  </p>
134
- <p v-if="!available" class="text-[11px] text-app-warning-400">
134
+ <p v-if="!available" class="text-2xs text-app-warning-400">
135
135
  {{ t('tasks.connect.installAppHint', { label: descriptor.label }) }}
136
136
  </p>
137
137
  </template>
@@ -149,7 +149,7 @@ async function toggleEnabled(enabled: boolean) {
149
149
  >
150
150
  {{ t('tasks.connect.oauthButton', { label: descriptor.label }) }}
151
151
  </UButton>
152
- <p class="text-center text-[11px] text-dimmed">
152
+ <p class="text-center text-2xs text-dimmed">
153
153
  {{ t('tasks.connect.oauthOr') }}
154
154
  </p>
155
155
  </template>
@@ -167,7 +167,7 @@ async function toggleEnabled(enabled: boolean) {
167
167
  />
168
168
  </UFormField>
169
169
  </div>
170
- <p v-else class="text-[11px] text-dimmed">
170
+ <p v-else class="text-2xs text-dimmed">
171
171
  {{
172
172
  connection?.label
173
173
  ? t('tasks.connect.connectedTo', { label: connection.label })
@@ -184,7 +184,7 @@ async function toggleEnabled(enabled: boolean) {
184
184
  <div class="font-medium text-default">
185
185
  {{ t('tasks.connect.offerToWorkspace') }}
186
186
  </div>
187
- <div class="text-[11px] text-dimmed">
187
+ <div class="text-2xs text-dimmed">
188
188
  {{ t('tasks.connect.offerHint', { label: descriptor.label }) }}
189
189
  </div>
190
190
  </div>
@@ -22,6 +22,7 @@ import EnvironmentStatusPanel from '~/components/environments/EnvironmentStatusP
22
22
  import ProvisioningLogsDrawer from '~/components/provisioning/ProvisioningLogsDrawer.vue'
23
23
  import MarkdownProse from '~/components/common/MarkdownProse.vue'
24
24
  import { runIsActive } from '~/utils/pipelineRender'
25
+ import SectionLabel from '~/components/common/SectionLabel.vue'
25
26
 
26
27
  const board = useBoardStore()
27
28
  const execution = useExecutionStore()
@@ -361,7 +362,7 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
361
362
  </UBadge>
362
363
  <span
363
364
  v-if="testState && testState.attempts > 0"
364
- class="text-[11px] text-muted"
365
+ class="text-2xs text-muted"
365
366
  :title="t('testing.fixerAttempts')"
366
367
  >
367
368
  {{ t('testing.fixCount', { attempts: testState.attempts, max: testState.maxAttempts }) }}
@@ -384,9 +385,9 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
384
385
  data-testid="tester-infrastructure"
385
386
  class="space-y-3"
386
387
  >
387
- <h3 class="text-[11px] font-semibold uppercase tracking-wide text-dimmed">
388
+ <SectionLabel as="h3">
388
389
  {{ t('testing.infrastructure') }}
389
- </h3>
390
+ </SectionLabel>
390
391
  <StepContainerStatus :step="step" :run-failed="runFailed" :run-active="runActive" />
391
392
  <EnvironmentStatusPanel
392
393
  v-if="stepEnvironment"
@@ -414,10 +415,10 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
414
415
  class="h-3.5 w-3.5 shrink-0"
415
416
  :class="infraSetup.started ? 'text-app-success-400' : 'text-app-error-400'"
416
417
  />
417
- <span class="text-[13px] font-medium text-default">
418
+ <span class="text-sm font-medium text-default">
418
419
  {{ standupHeadline }}
419
420
  </span>
420
- <span v-if="infraSetup.durationMs != null" class="ms-auto text-[11px] text-dimmed">
421
+ <span v-if="infraSetup.durationMs != null" class="ms-auto text-2xs text-dimmed">
421
422
  {{
422
423
  t('testing.standup.took', {
423
424
  seconds: n(infraSetup.durationMs / 1000, 'decimal'),
@@ -425,12 +426,12 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
425
426
  }}
426
427
  </span>
427
428
  </div>
428
- <p v-if="infraSetup.composePath" class="mt-1 font-mono text-[11px] text-dimmed">
429
+ <p v-if="infraSetup.composePath" class="mt-1 font-mono text-2xs text-dimmed">
429
430
  {{ infraSetup.composePath }}
430
431
  </p>
431
432
  <p
432
433
  v-if="infraSetup.error"
433
- class="mt-1 text-[12px] leading-snug text-app-error-300"
434
+ class="mt-1 text-xs leading-snug text-app-error-300"
434
435
  data-testid="tester-infra-setup-error"
435
436
  >
436
437
  {{ infraSetup.error }}
@@ -455,7 +456,7 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
455
456
  <pre
456
457
  v-if="showInfraSetupLogs"
457
458
  data-testid="tester-infra-setup-logs"
458
- class="mt-2 max-h-64 overflow-auto rounded bg-app-950/70 p-2 font-mono text-[11px] leading-relaxed text-toned"
459
+ class="mt-2 max-h-64 overflow-auto rounded-sm bg-app-950/70 p-2 font-mono text-2xs leading-relaxed text-toned"
459
460
  >{{ infraSetup.logs }}</pre>
460
461
  </template>
461
462
  </div>
@@ -467,7 +468,7 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
467
468
  <div
468
469
  v-if="infraReady"
469
470
  data-testid="tester-env-ready"
470
- class="flex items-center gap-2 rounded-lg border border-app-success-500/30 bg-app-success-500/10 px-3 py-2 text-[13px] text-app-success-200"
471
+ class="flex items-center gap-2 rounded-lg border border-app-success-500/30 bg-app-success-500/10 px-3 py-2 text-sm text-app-success-200"
471
472
  >
472
473
  <UIcon name="i-lucide-rocket" class="h-4 w-4 shrink-0 text-app-success-400" />
473
474
  <span>{{ t('testing.readyBanner') }}</span>
@@ -504,9 +505,9 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
504
505
  how it ended), so the otherwise-opaque fixer sub-jobs have a surface — the
505
506
  analogue of the polling gate's attempt history. -->
506
507
  <section v-if="fixerAttempts.length" data-testid="tester-fixer-attempts" class="space-y-2">
507
- <h3 class="text-[11px] font-semibold uppercase tracking-wide text-dimmed">
508
+ <SectionLabel as="h3">
508
509
  {{ t('testing.fixerAttempts') }}
509
- </h3>
510
+ </SectionLabel>
510
511
  <ol class="space-y-2">
511
512
  <li
512
513
  v-for="a in fixerAttempts"
@@ -531,20 +532,20 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
531
532
  <MarkdownProse
532
533
  v-if="a.summary"
533
534
  :text="a.summary"
534
- class="mt-1 max-w-3xl text-[12px] leading-snug text-muted"
535
+ class="mt-1 max-w-3xl text-xs leading-snug text-muted"
535
536
  />
536
537
  <div v-if="a.concerns && a.concerns.length" class="mt-1.5">
537
- <p class="text-[11px] text-dimmed">
538
+ <p class="text-2xs text-dimmed">
538
539
  {{ t('testing.fixerTimeline.addressed') }}
539
540
  </p>
540
541
  <ul class="mt-1 space-y-0.5">
541
542
  <li
542
543
  v-for="(c, ci) in a.concerns"
543
544
  :key="`fa${a.attempt}-c${ci}`"
544
- class="flex items-center gap-1.5 text-[12px] text-toned"
545
+ class="flex items-center gap-1.5 text-xs text-toned"
545
546
  >
546
547
  <span
547
- class="rounded px-1 text-[10px] uppercase"
548
+ class="rounded-sm px-1 text-3xs uppercase"
548
549
  :class="SEVERITY_META[c.severity].chip"
549
550
  >{{ SEVERITY_LABELS[c.severity] }}</span
550
551
  >
@@ -566,12 +567,12 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
566
567
  class="space-y-2"
567
568
  >
568
569
  <div class="flex items-center gap-2">
569
- <h3 class="text-[11px] font-semibold uppercase tracking-wide text-dimmed">
570
+ <SectionLabel as="h3">
570
571
  {{ t('testing.quality.heading') }}
571
- </h3>
572
+ </SectionLabel>
572
573
  <span
573
574
  v-if="quality.attempts"
574
- class="text-[11px] text-muted"
575
+ class="text-2xs text-muted"
575
576
  :title="t('testing.quality.reruns')"
576
577
  >
577
578
  {{
@@ -604,30 +605,30 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
604
605
  class="h-3.5 w-3.5 shrink-0"
605
606
  :class="vd.adequate ? 'text-app-success-400' : 'text-app-warning-300'"
606
607
  />
607
- <span class="text-[13px] font-medium text-default">
608
+ <span class="text-sm font-medium text-default">
608
609
  {{
609
610
  vd.adequate ? t('testing.quality.adequate') : t('testing.quality.inadequate')
610
611
  }}
611
612
  </span>
612
- <span v-if="vd.model" class="ms-auto font-mono text-[10px] text-dimmed">{{
613
+ <span v-if="vd.model" class="ms-auto font-mono text-3xs text-dimmed">{{
613
614
  vd.model
614
615
  }}</span>
615
- <span class="text-[11px] text-dimmed" :class="{ 'ms-auto': !vd.model }">{{
616
+ <span class="text-2xs text-dimmed" :class="{ 'ms-auto': !vd.model }">{{
616
617
  d(new Date(vd.at), 'short')
617
618
  }}</span>
618
619
  </div>
619
620
  <MarkdownProse
620
621
  v-if="vd.feedback"
621
622
  :text="vd.feedback"
622
- class="mt-1 text-[12px] leading-snug text-muted"
623
+ class="mt-1 text-xs leading-snug text-muted"
623
624
  />
624
625
  <div v-if="vd.gaps.length" class="mt-1.5">
625
- <p class="text-[11px] text-dimmed">{{ t('testing.quality.gaps') }}</p>
626
+ <p class="text-2xs text-dimmed">{{ t('testing.quality.gaps') }}</p>
626
627
  <ul class="mt-1 space-y-0.5">
627
628
  <li
628
629
  v-for="(gap, gi) in vd.gaps"
629
630
  :key="`qc${vi}-g${gi}`"
630
- class="flex items-start gap-1.5 text-[12px] text-toned"
631
+ class="flex items-start gap-1.5 text-xs text-toned"
631
632
  >
632
633
  <UIcon
633
634
  name="i-lucide-dot"
@@ -647,7 +648,7 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
647
648
  >
648
649
  <UIcon name="i-lucide-flask-conical" class="h-8 w-8 opacity-40" />
649
650
  <p class="text-sm">{{ t('testing.empty.title') }}</p>
650
- <p class="max-w-sm text-[11px] text-dimmed">
651
+ <p class="max-w-sm text-2xs text-dimmed">
651
652
  {{ t('testing.empty.hint') }}
652
653
  </p>
653
654
  </div>
@@ -659,12 +660,12 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
659
660
  <MarkdownProse
660
661
  v-if="report.summary"
661
662
  :text="report.summary"
662
- class="mb-4 max-w-3xl text-[13px] leading-relaxed text-toned"
663
+ class="mb-4 max-w-3xl text-sm leading-relaxed text-toned"
663
664
  />
664
665
 
665
- <h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
666
+ <SectionLabel as="h3" class="mb-2">
666
667
  {{ t('testing.scenariosOutcomes') }}
667
- </h3>
668
+ </SectionLabel>
668
669
  <ul class="space-y-2">
669
670
  <li
670
671
  v-for="g in groups"
@@ -685,7 +686,7 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
685
686
  :class="GROUP_STATUS_META[g.status].text"
686
687
  />
687
688
  <span
688
- class="min-w-0 flex-1 truncate text-[13px]"
689
+ class="min-w-0 flex-1 truncate text-sm"
689
690
  :class="g.other ? 'text-muted' : 'font-medium text-default'"
690
691
  >
691
692
  {{ g.title }}
@@ -702,7 +703,7 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
702
703
  )
703
704
  "
704
705
  />
705
- <span class="shrink-0 text-[11px] text-dimmed">
706
+ <span class="shrink-0 text-2xs text-dimmed">
706
707
  {{ t('testing.checkCount', { count: g.outcomes.length }, g.outcomes.length) }}
707
708
  <template v-if="g.concerns.length">
708
709
  ·
@@ -724,15 +725,15 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
724
725
  :class="STATUS_META[o.status].text"
725
726
  />
726
727
  <div class="min-w-0">
727
- <span class="text-[13px] text-default">{{ o.name }}</span>
728
+ <span class="text-sm text-default">{{ o.name }}</span>
728
729
  <MarkdownProse
729
730
  v-if="o.detail"
730
731
  :text="o.detail"
731
- class="max-w-3xl text-[12px] leading-snug text-muted"
732
+ class="max-w-3xl text-xs leading-snug text-muted"
732
733
  />
733
734
  </div>
734
735
  </div>
735
- <p v-if="!g.outcomes.length" class="py-0.5 text-[12px] italic text-dimmed">
736
+ <p v-if="!g.outcomes.length" class="py-0.5 text-xs italic text-dimmed">
736
737
  {{ t('testing.noDiscreteCheck') }}
737
738
  </p>
738
739
 
@@ -749,9 +750,9 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
749
750
  />
750
751
  <div class="min-w-0">
751
752
  <div class="flex items-center gap-1.5">
752
- <span class="text-[12px] font-medium text-default">{{ c.title }}</span>
753
+ <span class="text-xs font-medium text-default">{{ c.title }}</span>
753
754
  <span
754
- class="rounded px-1 text-[10px] uppercase"
755
+ class="rounded-sm px-1 text-3xs uppercase"
755
756
  :class="SEVERITY_META[c.severity].chip"
756
757
  >
757
758
  {{ SEVERITY_LABELS[c.severity] }}
@@ -760,7 +761,7 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
760
761
  <MarkdownProse
761
762
  v-if="c.detail"
762
763
  :text="c.detail"
763
- class="max-w-3xl text-[12px] leading-snug text-muted"
764
+ class="max-w-3xl text-xs leading-snug text-muted"
764
765
  />
765
766
  </div>
766
767
  </div>
@@ -771,7 +772,7 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
771
772
  v-for="(s, si) in g.screenshots"
772
773
  :key="`shot${si}`"
773
774
  data-testid="tester-screenshot"
774
- class="group relative h-20 w-28 shrink-0 overflow-hidden rounded border border-default bg-app-950/60 hover:border-app-600"
775
+ class="group relative h-20 w-28 shrink-0 overflow-hidden rounded-sm border border-default bg-app-950/60 hover:border-app-600"
775
776
  :title="s.view"
776
777
  @click="openShot(s.artifactId)"
777
778
  >
@@ -783,7 +784,7 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
783
784
  />
784
785
  <span
785
786
  v-else
786
- class="flex h-full w-full items-center justify-center text-[10px] text-app-600"
787
+ class="flex h-full w-full items-center justify-center text-3xs text-app-600"
787
788
  >
788
789
  {{
789
790
  blobs.statusFor(s.artifactId) === 'error'
@@ -792,7 +793,7 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
792
793
  }}
793
794
  </span>
794
795
  <span
795
- class="absolute inset-x-0 bottom-0 truncate bg-app-950/80 px-1 py-0.5 text-[9px] text-toned"
796
+ class="absolute inset-x-0 bottom-0 truncate bg-app-950/80 px-1 py-0.5 text-3xs text-toned"
796
797
  >{{ s.view }}</span
797
798
  >
798
799
  </button>
@@ -803,9 +804,9 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
803
804
 
804
805
  <!-- Standalone gallery: any captures not mapped to a scenario above -->
805
806
  <section v-if="ungroupedScreenshots.length" class="mt-5">
806
- <h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
807
+ <SectionLabel as="h3" class="mb-2">
807
808
  {{ t('testing.screenshots') }}
808
- </h3>
809
+ </SectionLabel>
809
810
  <div class="grid grid-cols-2 gap-3 sm:grid-cols-3">
810
811
  <button
811
812
  v-for="(s, si) in ungroupedScreenshots"
@@ -822,7 +823,7 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
822
823
  />
823
824
  <span
824
825
  v-else
825
- class="flex h-full w-full items-center justify-center text-[11px] text-app-600"
826
+ class="flex h-full w-full items-center justify-center text-2xs text-app-600"
826
827
  >
827
828
  {{
828
829
  blobs.statusFor(s.artifactId) === 'error'
@@ -831,7 +832,7 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
831
832
  }}
832
833
  </span>
833
834
  <span
834
- class="absolute inset-x-0 bottom-0 truncate bg-app-950/80 px-1.5 py-0.5 text-[10px] text-toned"
835
+ class="absolute inset-x-0 bottom-0 truncate bg-app-950/80 px-1.5 py-0.5 text-3xs text-toned"
835
836
  >{{ s.view }}</span
836
837
  >
837
838
  </button>
@@ -845,10 +846,10 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
845
846
  class="hidden w-60 shrink-0 flex-col gap-4 border-s border-default bg-default/50 px-4 py-4 lg:flex"
846
847
  >
847
848
  <div v-if="report">
848
- <h4 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
849
+ <SectionLabel as="h4" class="mb-2">
849
850
  {{ t('testing.verdict.heading') }}
850
- </h4>
851
- <div class="flex items-center gap-2 text-[13px]">
851
+ </SectionLabel>
852
+ <div class="flex items-center gap-2 text-sm">
852
853
  <UIcon
853
854
  :name="report.greenlight ? 'i-lucide-circle-check' : 'i-lucide-circle-x'"
854
855
  class="h-4 w-4"
@@ -861,10 +862,10 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
861
862
  </div>
862
863
 
863
864
  <div v-if="report">
864
- <h4 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
865
+ <SectionLabel as="h4" class="mb-2">
865
866
  {{ t('testing.outcomes.heading') }}
866
- </h4>
867
- <dl class="space-y-1 text-[12px]">
867
+ </SectionLabel>
868
+ <dl class="space-y-1 text-xs">
868
869
  <div class="flex items-center justify-between">
869
870
  <dt class="text-muted">{{ t('testing.outcomes.passed') }}</dt>
870
871
  <dd class="text-app-success-300">{{ counts.passed }}</dd>
@@ -892,10 +893,10 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
892
893
  </div>
893
894
 
894
895
  <div v-if="report?.environment">
895
- <h4 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
896
+ <SectionLabel as="h4" class="mb-1">
896
897
  {{ t('testing.environment') }}
897
- </h4>
898
- <p class="text-[12px] capitalize text-toned">{{ report.environment }}</p>
898
+ </SectionLabel>
899
+ <p class="text-xs capitalize text-toned">{{ report.environment }}</p>
899
900
  </div>
900
901
 
901
902
  <!-- Shared run metadata + embedded observability (model, run id, timing,
@@ -910,7 +911,7 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
910
911
  :failure-at="instance?.failure?.occurredAt"
911
912
  />
912
913
 
913
- <p class="mt-auto text-[10px] leading-relaxed text-app-600">
914
+ <p class="mt-auto text-3xs leading-relaxed text-app-600">
914
915
  {{ t('testing.footer') }}
915
916
  </p>
916
917
  </aside>
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from 'vue'
3
+ import SectionLabel from '~/components/common/SectionLabel.vue'
3
4
 
4
5
  // The contextual offer's surface: one walkthrough, offered beside the work that just made it
5
6
  // relevant. Deliberately NOT a modal — the whole point is the moment, and a modal would
@@ -75,9 +76,9 @@ function take(tourId: string) {
75
76
  class="text-primary mt-0.5 h-4 w-4 shrink-0"
76
77
  />
77
78
  <div class="min-w-0 flex-1">
78
- <p class="text-[11px] tracking-wide text-muted uppercase">
79
+ <SectionLabel as="p">
79
80
  {{ t('tutorial.nudge.label') }}
80
- </p>
81
+ </SectionLabel>
81
82
  <p class="mt-0.5 text-sm font-medium text-app-100">{{ t(offered.titleKey) }}</p>
82
83
  <p class="mt-0.5 text-xs text-muted">{{ t(offered.descriptionKey) }}</p>
83
84
  </div>
@@ -20,6 +20,7 @@ import {
20
20
  waitBudgetMs,
21
21
  } from './TutorialOverlay.logic'
22
22
  import type { TutorialAdvanceCause, TutorialDirection } from './TutorialOverlay.logic'
23
+ import SectionLabel from '~/components/common/SectionLabel.vue'
23
24
 
24
25
  // The one shared tour runtime: resolves the running tour from the `tutorialTours` slot,
25
26
  // anchors a highlight ring + tooltip to the current step's `data-testid`, and advances
@@ -628,9 +629,9 @@ onUnmounted(() => {
628
629
  class="mt-3 rounded-lg border border-muted/70 bg-elevated/40 p-2.5"
629
630
  data-testid="tutorial-next-tour"
630
631
  >
631
- <p class="text-[11px] tracking-wide text-muted uppercase">
632
+ <SectionLabel as="p">
632
633
  {{ t('tutorial.overlay.nextUp') }}
633
- </p>
634
+ </SectionLabel>
634
635
  <p class="mt-0.5 text-sm font-medium text-app-100">{{ t(nextTour.titleKey) }}</p>
635
636
  <p class="mt-0.5 text-xs text-muted">{{ t(nextTour.descriptionKey) }}</p>
636
637
  <UButton
@@ -67,7 +67,7 @@ async function connect() {
67
67
  />
68
68
  </UFormField>
69
69
 
70
- <p class="text-[11px] text-dimmed">
70
+ <p class="text-2xs text-dimmed">
71
71
  <ULink :to="tokenUrl" target="_blank" class="text-primary hover:underline">
72
72
  {{ t('vcs.connect.gitlab.createToken') }}
73
73
  </ULink>
@@ -14,6 +14,7 @@ import ImageCompare from '~/components/media/ImageCompare.vue'
14
14
  import ArtifactLightbox from '~/components/media/ArtifactLightbox.vue'
15
15
  import ResultWindowShell from '~/components/panels/ResultWindowShell.vue'
16
16
  import StepRunMeta from '~/components/panels/StepRunMeta.vue'
17
+ import SectionLabel from '~/components/common/SectionLabel.vue'
17
18
 
18
19
  const board = useBoardStore()
19
20
  const execution = useExecutionStore()
@@ -242,7 +243,7 @@ async function onFilePicked(e: Event) {
242
243
  <template v-else>
243
244
  <p
244
245
  v-if="vc.degradedReason"
245
- class="rounded-lg border border-app-warning-700/40 bg-app-warning-500/5 px-3 py-2 text-[12px] text-app-warning-300/90"
246
+ class="rounded-lg border border-app-warning-700/40 bg-app-warning-500/5 px-3 py-2 text-xs text-app-warning-300/90"
246
247
  >
247
248
  {{ vc.degradedReason }}
248
249
  </p>
@@ -251,7 +252,7 @@ async function onFilePicked(e: Event) {
251
252
  reference the reviewer is judging against is never anonymous. -->
252
253
  <section
253
254
  v-if="design"
254
- class="rounded-lg border border-default bg-default/60 px-3 py-2 text-[12px] text-toned"
255
+ class="rounded-lg border border-default bg-default/60 px-3 py-2 text-xs text-toned"
255
256
  >
256
257
  <p class="flex items-center gap-1.5">
257
258
  <UIcon name="i-lucide-figma" class="h-3.5 w-3.5 shrink-0 text-app-warning-300" />
@@ -265,10 +266,7 @@ async function onFilePicked(e: Event) {
265
266
  <!-- One line per short design, carrying both ways it can fall short: what its source
266
267
  kept, and what this gallery's shared ceiling cut from it. A design the ceiling shut
267
268
  out entirely reads as one with no frames unless it is named here. -->
268
- <ul
269
- v-if="design.gaps?.length"
270
- class="mt-1.5 space-y-1 text-[11px] text-app-warning-300/90"
271
- >
269
+ <ul v-if="design.gaps?.length" class="mt-1.5 space-y-1 text-2xs text-app-warning-300/90">
272
270
  <li v-for="gap in design.gaps" :key="`${gap.title}-${gap.reason ?? 'capped'}`">
273
271
  {{ t('visualConfirm.design.gapLine', { title: gap.title }) }}
274
272
  <template v-if="gap.reason">{{ DESIGN_GAP_LABELS[gap.reason] }}</template>
@@ -281,7 +279,7 @@ async function onFilePicked(e: Event) {
281
279
 
282
280
  <p
283
281
  v-if="working"
284
- class="flex items-center gap-2 rounded-lg border border-default bg-app-950/40 px-3 py-2 text-[12px] text-toned"
282
+ class="flex items-center gap-2 rounded-lg border border-default bg-app-950/40 px-3 py-2 text-xs text-toned"
285
283
  >
286
284
  <UIcon name="i-lucide-loader" class="h-3.5 w-3.5 animate-spin text-app-warning-300" />
287
285
  {{ phase ? PHASE_LABEL[phase] : '' }}
@@ -304,7 +302,7 @@ async function onFilePicked(e: Event) {
304
302
  <!-- Per-view note (folded into the fixer findings) -->
305
303
  <div v-if="awaitingHuman" class="px-1">
306
304
  <button
307
- class="flex items-center gap-1.5 text-[11px] text-muted hover:text-default"
305
+ class="flex items-center gap-1.5 text-2xs text-muted hover:text-default"
308
306
  @click="noteOpen[p.view] = !noteOpen[p.view]"
309
307
  >
310
308
  <UIcon
@@ -314,7 +312,7 @@ async function onFilePicked(e: Event) {
314
312
  {{ t('visualConfirm.noteIssue', { view: p.view }) }}
315
313
  <span
316
314
  v-if="perViewNotes[p.view]?.trim()"
317
- class="rounded-full bg-app-warning-500/15 px-1.5 text-[9px] text-app-warning-300"
315
+ class="rounded-full bg-app-warning-500/15 px-1.5 text-3xs text-app-warning-300"
318
316
  >{{ t('visualConfirm.noted') }}</span
319
317
  >
320
318
  </button>
@@ -323,26 +321,26 @@ async function onFilePicked(e: Event) {
323
321
  v-model="perViewNotes[p.view]"
324
322
  rows="2"
325
323
  :placeholder="t('visualConfirm.notePlaceholder', { view: p.view })"
326
- class="mt-1 w-full rounded-md border border-muted bg-app-950 px-2 py-1.5 text-[12px] text-default placeholder:text-app-600 focus:border-app-warning-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-app-warning-500/60"
324
+ class="mt-1 w-full rounded-md border border-muted bg-app-950 px-2 py-1.5 text-xs text-default placeholder:text-app-600 focus:border-app-warning-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-app-warning-500/60"
327
325
  />
328
326
  </div>
329
327
  </div>
330
328
  </section>
331
- <p v-else class="text-[12px] italic text-dimmed">
329
+ <p v-else class="text-xs italic text-dimmed">
332
330
  {{ t('visualConfirm.noScreenshots') }}
333
331
  </p>
334
332
 
335
333
  <!-- Upload a reference for any view -->
336
334
  <section class="rounded-lg border border-default bg-default/60 p-3">
337
- <h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
335
+ <SectionLabel as="h3" class="mb-2">
338
336
  {{ t('visualConfirm.upload.heading') }}
339
- </h3>
337
+ </SectionLabel>
340
338
  <div class="flex flex-wrap items-center gap-2">
341
339
  <input
342
340
  v-model="uploadView"
343
341
  list="vc-views"
344
342
  :placeholder="t('visualConfirm.upload.viewPlaceholder')"
345
- class="rounded-md border border-muted bg-app-950 px-2 py-1 text-[12px] text-default placeholder:text-app-600"
343
+ class="rounded-md border border-muted bg-app-950 px-2 py-1 text-xs text-default placeholder:text-app-600"
346
344
  />
347
345
  <datalist id="vc-views">
348
346
  <option v-for="p in pairs" :key="p.view" :value="p.view" />
@@ -352,11 +350,11 @@ async function onFilePicked(e: Event) {
352
350
  type="file"
353
351
  accept="image/png,image/jpeg"
354
352
  :disabled="busy || !uploadView.trim()"
355
- class="text-[12px] text-toned file:me-2 file:rounded file:border-0 file:bg-elevated file:px-2 file:py-1 file:text-default disabled:opacity-40"
353
+ class="text-xs text-toned file:me-2 file:rounded-sm file:border-0 file:bg-elevated file:px-2 file:py-1 file:text-default disabled:opacity-40"
356
354
  @change="onFilePicked"
357
355
  />
358
356
  </div>
359
- <p class="mt-1.5 text-[10px] text-app-600">
357
+ <p class="mt-1.5 text-3xs text-app-600">
360
358
  {{
361
359
  uploadView.trim()
362
360
  ? t('visualConfirm.upload.tipReady')
@@ -367,17 +365,17 @@ async function onFilePicked(e: Event) {
367
365
 
368
366
  <!-- Request fix -->
369
367
  <section v-if="awaitingHuman" class="rounded-lg border border-default bg-default/60 p-3">
370
- <h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
368
+ <SectionLabel as="h3" class="mb-2">
371
369
  {{ t('visualConfirm.requestFix.heading') }}
372
- </h3>
370
+ </SectionLabel>
373
371
  <textarea
374
372
  v-model="globalFindings"
375
373
  rows="3"
376
374
  :placeholder="t('visualConfirm.requestFix.placeholder')"
377
- class="w-full rounded-md border border-muted bg-app-950 px-3 py-2 text-[13px] text-default placeholder:text-app-600 focus:border-app-warning-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-app-warning-500/60"
375
+ class="w-full rounded-md border border-muted bg-app-950 px-3 py-2 text-sm text-default placeholder:text-app-600 focus:border-app-warning-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-app-warning-500/60"
378
376
  />
379
377
  <div class="mt-2 flex items-center justify-between">
380
- <span class="text-[11px] text-dimmed">
378
+ <span class="text-2xs text-dimmed">
381
379
  {{ t('visualConfirm.requestFix.foldedHint') }}
382
380
  </span>
383
381
  <UButton
@@ -399,16 +397,16 @@ async function onFilePicked(e: Event) {
399
397
  v-if="vc.rounds && vc.rounds.length"
400
398
  class="rounded-lg border border-default bg-default/60 p-3"
401
399
  >
402
- <h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
400
+ <SectionLabel as="h3" class="mb-2">
403
401
  {{ t('visualConfirm.history.heading', { count: vc.attempts }, vc.attempts) }}
404
- </h3>
402
+ </SectionLabel>
405
403
  <ol class="space-y-2">
406
- <li v-for="(r, i) in vc.rounds" :key="i" class="flex items-start gap-2 text-[12px]">
404
+ <li v-for="(r, i) in vc.rounds" :key="i" class="flex items-start gap-2 text-xs">
407
405
  <UIcon name="i-lucide-wrench" class="mt-0.5 h-3.5 w-3.5 shrink-0 text-muted" />
408
406
  <div class="min-w-0 flex-1">
409
407
  <span class="text-default">{{ t('visualConfirm.history.fixRequested') }}</span>
410
408
  <span
411
- class="ms-1.5 rounded px-1 text-[10px] uppercase"
409
+ class="ms-1.5 rounded-sm px-1 text-3xs uppercase"
412
410
  :class="
413
411
  r.outcome === 'completed'
414
412
  ? 'bg-app-success-500/15 text-app-success-300'
@@ -447,7 +445,7 @@ async function onFilePicked(e: Event) {
447
445
  <div class="flex items-center gap-2">
448
446
  <label
449
447
  v-if="awaitingHuman && needsAck"
450
- class="flex items-center gap-1.5 text-[11px] text-app-warning-300/90"
448
+ class="flex items-center gap-1.5 text-2xs text-app-warning-300/90"
451
449
  >
452
450
  <input v-model="ackDegraded" type="checkbox" class="accent-app-warning-500" />
453
451
  {{ t('visualConfirm.reviewedManually') }}
@@ -477,6 +477,8 @@ const REASON_DESCRIPTION_KEYS: Record<UnavailableReason | BootstrapReferenceReas
477
477
  delegated_executor_failed: 'errors.unavailable.description.delegated_executor_failed',
478
478
  delegated_work_branch_unprepared:
479
479
  'errors.unavailable.description.delegated_work_branch_unprepared',
480
+ attached_pr_provider_unreachable:
481
+ 'errors.unavailable.description.attached_pr_provider_unreachable',
480
482
  }
481
483
 
482
484
  /**
@@ -1048,6 +1048,14 @@ export const TASK_TYPE_META: Record<string, TaskTypeMeta> = {
1048
1048
  color: 'var(--app-hue-emerald)',
1049
1049
  labelKey: 'board.addTask.types.review',
1050
1050
  },
1051
+ // Filed by an integration against a pull request it already has open; the create form does not
1052
+ // offer it, but the card still needs a badge.
1053
+ 'resolve-conflicts': {
1054
+ taskType: 'resolve-conflicts',
1055
+ icon: 'i-lucide-git-merge',
1056
+ color: 'var(--app-hue-emerald)',
1057
+ labelKey: 'board.addTask.types.resolveConflicts',
1058
+ },
1051
1059
  ralph: {
1052
1060
  taskType: 'ralph',
1053
1061
  icon: 'i-lucide-infinity',