@cat-factory/app 0.308.0 → 0.310.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 (265) hide show
  1. package/README.md +32 -2
  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 +8 -6
  6. package/app/components/auth/BackendMisconfiguredScreen.vue +4 -3
  7. package/app/components/auth/LoginScreen.vue +40 -26
  8. package/app/components/auth/ResetPasswordScreen.vue +9 -4
  9. package/app/components/auth/UserMenu.vue +5 -4
  10. package/app/components/binaryCandidates/BinaryCandidatesWindow.vue +48 -37
  11. package/app/components/binaryOutput/BinaryOutputReport.vue +10 -14
  12. package/app/components/binaryOutput/StoredAssetView.vue +13 -11
  13. package/app/components/board/AddTaskModal.vue +30 -27
  14. package/app/components/board/AgentFailureCard.vue +21 -18
  15. package/app/components/board/AgentFailureHistory.vue +18 -9
  16. package/app/components/board/CreateInitiativeModal.vue +7 -6
  17. package/app/components/board/FailureDetail.vue +22 -12
  18. package/app/components/board/FailureHistoryList.vue +5 -5
  19. package/app/components/board/RecurringPipelineModal.vue +12 -11
  20. package/app/components/board/ReviewFrictionDialog.vue +8 -6
  21. package/app/components/board/StepExecutionHistory.vue +8 -8
  22. package/app/components/board/nodes/AgentChip.vue +1 -1
  23. package/app/components/board/nodes/BlockNode.vue +25 -24
  24. package/app/components/board/nodes/DecisionBadge.vue +4 -3
  25. package/app/components/board/nodes/EpicNode.vue +4 -4
  26. package/app/components/board/nodes/FrameSwimlanes.vue +8 -10
  27. package/app/components/board/nodes/InitiativeCard.vue +4 -4
  28. package/app/components/board/nodes/LaneGroup.vue +3 -5
  29. package/app/components/board/nodes/TaskCard.vue +20 -15
  30. package/app/components/board/nodes/TaskLane.vue +3 -3
  31. package/app/components/board/nodes/TaskPipelineMini.vue +21 -18
  32. package/app/components/bootstrap/AdoptionReviewModal.vue +36 -26
  33. package/app/components/bootstrap/BootstrapModal.vue +14 -13
  34. package/app/components/bootstrap/BootstrapRunSteps.vue +1 -1
  35. package/app/components/brainstorm/BrainstormWindow.vue +13 -10
  36. package/app/components/bugFishing/BugFishingWindow.vue +81 -58
  37. package/app/components/clarity/ClarityReviewWindow.vue +13 -10
  38. package/app/components/common/AsyncViewError.vue +1 -1
  39. package/app/components/common/ClarificationItem.vue +6 -6
  40. package/app/components/common/CopyButton.vue +12 -11
  41. package/app/components/common/DescriptorFields.vue +1 -1
  42. package/app/components/common/EmptyState.vue +1 -1
  43. package/app/components/common/IconButton.vue +34 -9
  44. package/app/components/common/InterviewGateNotice.vue +4 -4
  45. package/app/components/common/MarkdownProse.vue +8 -2
  46. package/app/components/common/SectionLabel.vue +38 -0
  47. package/app/components/consensus/ConsensusSessionWindow.vue +10 -9
  48. package/app/components/context/ContextAttachmentFields.vue +32 -20
  49. package/app/components/docs/DocInterviewWindow.vue +10 -9
  50. package/app/components/documents/ContextDocumentPicker.vue +22 -21
  51. package/app/components/documents/DocumentImportModal.vue +7 -5
  52. package/app/components/documents/DocumentOriginLink.vue +12 -9
  53. package/app/components/documents/DocumentSourceConnectModal.vue +2 -2
  54. package/app/components/documents/DocumentSyncState.vue +4 -4
  55. package/app/components/documents/DocumentTemplatesModal.vue +5 -4
  56. package/app/components/documents/RepoContextDocPicker.vue +7 -9
  57. package/app/components/documents/StartFromDesignModal.vue +4 -4
  58. package/app/components/documents/TaskContextDocs.vue +1 -1
  59. package/app/components/environments/EnvSetupStepper.vue +3 -3
  60. package/app/components/environments/EnvironmentStatusPanel.vue +15 -13
  61. package/app/components/environments/steps/EnvPreflightStep.vue +8 -8
  62. package/app/components/environments/steps/EnvReviewStep.vue +23 -22
  63. package/app/components/environments/steps/EnvSaveStep.vue +5 -5
  64. package/app/components/focus/BlockFocusView.vue +11 -10
  65. package/app/components/followUp/FollowUpWindow.vue +18 -15
  66. package/app/components/forkDecision/ForkDecisionWindow.vue +85 -97
  67. package/app/components/foundational/FoundationalContractSummary.vue +2 -2
  68. package/app/components/foundational/FoundationalServiceCatalogList.vue +6 -5
  69. package/app/components/foundational/FoundationalServiceRegistry.vue +10 -9
  70. package/app/components/foundational/FoundationalServiceSources.vue +7 -6
  71. package/app/components/foundational/FoundationalSuppressions.vue +2 -2
  72. package/app/components/fragments/FragmentLibraryManager.vue +8 -7
  73. package/app/components/fragments/FragmentSelector.vue +22 -20
  74. package/app/components/gates/GateFailingCheckList.vue +7 -6
  75. package/app/components/gates/GateResultView.vue +55 -49
  76. package/app/components/github/AddServiceFromRepoModal.vue +16 -11
  77. package/app/components/github/BranchProtectionPreflight.vue +5 -5
  78. package/app/components/github/GitHubConnect.vue +5 -3
  79. package/app/components/github/GitHubOnboarding.vue +7 -2
  80. package/app/components/github/GitHubPanel.vue +23 -19
  81. package/app/components/github/RepoTreeBrowser.vue +16 -13
  82. package/app/components/humanTest/HumanTestWindow.vue +29 -24
  83. package/app/components/initiative/InitiativePlanNotice.vue +3 -3
  84. package/app/components/initiative/InitiativePlanReview.vue +46 -38
  85. package/app/components/initiative/InitiativePlanningWindow.vue +5 -5
  86. package/app/components/initiative/InitiativeTrackerWindow.vue +213 -165
  87. package/app/components/judge/JudgeResultView.vue +28 -27
  88. package/app/components/kaizen/KaizenPanel.vue +70 -56
  89. package/app/components/kaizen/KaizenStepStatus.vue +7 -6
  90. package/app/components/layout/AccountAuditLog.vue +1 -1
  91. package/app/components/layout/AccountDeploymentSettings.vue +10 -10
  92. package/app/components/layout/AccountFailureKindRules.vue +13 -13
  93. package/app/components/layout/AccountFoundationalSettings.vue +1 -1
  94. package/app/components/layout/AccountFragmentSettings.vue +1 -1
  95. package/app/components/layout/AccountModelPolicySettings.vue +7 -7
  96. package/app/components/layout/AccountPlatformAlertSettings.vue +8 -8
  97. package/app/components/layout/AccountRiskPolicySettings.vue +2 -2
  98. package/app/components/layout/AccountRunCredentialSettings.vue +3 -3
  99. package/app/components/layout/AccountSkillSettings.vue +1 -1
  100. package/app/components/layout/AccountTeamSettings.vue +13 -9
  101. package/app/components/layout/AiProvidersBanner.vue +1 -1
  102. package/app/components/layout/AppearanceSwitcher.vue +5 -4
  103. package/app/components/layout/BoardSwitcher.vue +13 -10
  104. package/app/components/layout/BoardToolbar.vue +10 -5
  105. package/app/components/layout/CommandBar.vue +8 -5
  106. package/app/components/layout/DefaultTestEnvBanner.vue +6 -5
  107. package/app/components/layout/IntegrationsHub.vue +12 -9
  108. package/app/components/layout/LanguageSwitcher.vue +5 -4
  109. package/app/components/layout/ModelProvidersHub.vue +10 -7
  110. package/app/components/layout/NotificationsInbox.vue +47 -36
  111. package/app/components/layout/PersonalSetupModal.vue +9 -6
  112. package/app/components/layout/RolePrompt.vue +5 -4
  113. package/app/components/layout/SideBar.vue +7 -8
  114. package/app/components/layout/SpendWarningBanner.vue +3 -3
  115. package/app/components/layout/TranslationWarningBanner.vue +5 -4
  116. package/app/components/layout/UiModeSwitcher.vue +15 -12
  117. package/app/components/layout/UiRoleSwitcher.vue +10 -8
  118. package/app/components/layout/WorkspaceMembersSettings.vue +2 -2
  119. package/app/components/media/ArtifactLightbox.vue +74 -44
  120. package/app/components/media/ImageCompare.vue +80 -85
  121. package/app/components/merge/MergeEffortChips.vue +4 -6
  122. package/app/components/notifications/NotificationSettingsPanel.vue +6 -5
  123. package/app/components/observability/OutcomeFilterChips.vue +6 -5
  124. package/app/components/observability/RunFailureSummary.vue +22 -23
  125. package/app/components/observability/StepMetricsBar.vue +9 -8
  126. package/app/components/observability/StepModelActivity.vue +8 -5
  127. package/app/components/observability/ToolCallList.vue +19 -20
  128. package/app/components/outcome/OutcomeSummaryWindow.vue +48 -46
  129. package/app/components/palettes/AgentPalette.vue +21 -15
  130. package/app/components/palettes/AgentTierSelect.vue +1 -1
  131. package/app/components/palettes/PipelinePurposeSelect.vue +1 -1
  132. package/app/components/panels/AgentStepDetail.vue +57 -49
  133. package/app/components/panels/AttemptEntryHeader.vue +2 -2
  134. package/app/components/panels/DecisionModal.vue +1 -1
  135. package/app/components/panels/GenericStructuredResultView.vue +6 -5
  136. package/app/components/panels/InspectorPanel.vue +15 -13
  137. package/app/components/panels/MergerResultView.vue +10 -9
  138. package/app/components/panels/ObservabilityPanel.vue +211 -186
  139. package/app/components/panels/OperatorDashboardPanel.vue +124 -115
  140. package/app/components/panels/ReportsPanel.vue +87 -83
  141. package/app/components/panels/ReportsSpendBreakdown.vue +2 -2
  142. package/app/components/panels/ResultWindowShell.vue +152 -137
  143. package/app/components/panels/RunDetailLoadState.vue +1 -1
  144. package/app/components/panels/StepContainerStatus.vue +17 -16
  145. package/app/components/panels/StepDelegatedStatus.vue +24 -21
  146. package/app/components/panels/StepEffortReport.vue +12 -17
  147. package/app/components/panels/StepFragmentAdherence.vue +9 -10
  148. package/app/components/panels/StepMetadataCard.vue +41 -43
  149. package/app/components/panels/StepReproductionReport.vue +18 -20
  150. package/app/components/panels/StepRestartControl.vue +3 -2
  151. package/app/components/panels/StepRunMeta.vue +27 -26
  152. package/app/components/panels/StepTestReport.vue +16 -15
  153. package/app/components/panels/StepToolServers.vue +7 -8
  154. package/app/components/panels/StepValidationReport.vue +5 -5
  155. package/app/components/panels/inspector/ContainerSummary.vue +4 -4
  156. package/app/components/panels/inspector/DocReferenceRepos.vue +5 -4
  157. package/app/components/panels/inspector/EnvProbeReport.vue +16 -13
  158. package/app/components/panels/inspector/EpicChildren.vue +12 -10
  159. package/app/components/panels/inspector/FrontendBindingsResolved.vue +5 -4
  160. package/app/components/panels/inspector/FrontendConfig.vue +29 -36
  161. package/app/components/panels/inspector/InitiativeInspector.vue +3 -3
  162. package/app/components/panels/inspector/InspectorSection.vue +25 -17
  163. package/app/components/panels/inspector/RecurringScheduleSettings.vue +9 -8
  164. package/app/components/panels/inspector/ServiceConnections.vue +5 -4
  165. package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +2 -2
  166. package/app/components/panels/inspector/ServiceSelfTests.vue +9 -13
  167. package/app/components/panels/inspector/ServiceTestConfig.vue +46 -49
  168. package/app/components/panels/inspector/ServiceTestSecrets.vue +3 -3
  169. package/app/components/panels/inspector/ServiceTestingContext.vue +2 -2
  170. package/app/components/panels/inspector/ServiceValidationConfig.vue +2 -2
  171. package/app/components/panels/inspector/TaskAgentConfig.vue +2 -2
  172. package/app/components/panels/inspector/TaskAprioriBranches.vue +6 -5
  173. package/app/components/panels/inspector/TaskDependencies.vue +1 -1
  174. package/app/components/panels/inspector/TaskEstimateBadge.vue +3 -7
  175. package/app/components/panels/inspector/TaskExecution.vue +18 -16
  176. package/app/components/panels/inspector/TaskRunSettings.vue +45 -44
  177. package/app/components/panels/inspector/TaskStructure.vue +5 -4
  178. package/app/components/pipeline/AgentPromptEditor.vue +15 -14
  179. package/app/components/pipeline/BinaryOutputStepPicker.vue +42 -52
  180. package/app/components/pipeline/EstimateThresholdFields.vue +14 -11
  181. package/app/components/pipeline/GateConfigFields.vue +19 -23
  182. package/app/components/pipeline/IterationCapPrompt.vue +1 -1
  183. package/app/components/pipeline/PipelineBuilder.vue +113 -99
  184. package/app/components/pipeline/PipelineHealthModal.vue +7 -7
  185. package/app/components/pipeline/PipelinePicker.vue +11 -9
  186. package/app/components/pipeline/PipelinePreview.vue +6 -7
  187. package/app/components/pipeline/PipelineProgress.vue +49 -45
  188. package/app/components/prReview/PrReviewWindow.vue +87 -65
  189. package/app/components/providers/AiPresetMismatchDialog.vue +4 -3
  190. package/app/components/providers/AiProviderOnboardingModal.vue +2 -2
  191. package/app/components/providers/ApiKeysSection.vue +11 -9
  192. package/app/components/providers/PersonalSubscriptionSection.vue +4 -3
  193. package/app/components/providers/VendorCredentialsModal.vue +5 -4
  194. package/app/components/provisioning/ProvisioningLogsDrawer.vue +10 -9
  195. package/app/components/ralph/RalphLoopResultView.vue +29 -29
  196. package/app/components/requirements/RequirementsReviewWindow.vue +22 -17
  197. package/app/components/riskPolicy/RiskPolicyPicker.vue +13 -11
  198. package/app/components/riskPolicy/RiskPolicyPreview.vue +16 -15
  199. package/app/components/sandbox/SandboxPanel.vue +93 -73
  200. package/app/components/settings/ApiTokensPanel.vue +1 -1
  201. package/app/components/settings/BudgetSettings.vue +18 -17
  202. package/app/components/settings/CapabilityCredentialsPanel.vue +5 -5
  203. package/app/components/settings/CloudflareHandlerSection.vue +44 -41
  204. package/app/components/settings/ComposeEnvironmentSetupSection.vue +14 -12
  205. package/app/components/settings/ConnectionTestVerdict.vue +1 -1
  206. package/app/components/settings/ConsensusGroupsSection.vue +58 -47
  207. package/app/components/settings/CustomManifestTypeEditor.vue +8 -7
  208. package/app/components/settings/DefaultProvisionTypeSection.vue +5 -5
  209. package/app/components/settings/InfraHandlersConfigurator.vue +26 -24
  210. package/app/components/settings/InfrastructureBackendPicker.vue +18 -18
  211. package/app/components/settings/IssueTrackerPanel.vue +30 -21
  212. package/app/components/settings/KubernetesEngineForm.vue +9 -8
  213. package/app/components/settings/KubernetesEnvironmentForm.vue +4 -3
  214. package/app/components/settings/LocalContainerPoolSettings.vue +2 -2
  215. package/app/components/settings/LocalModelEndpointsPanel.vue +13 -11
  216. package/app/components/settings/MergeClassRulesEditor.vue +6 -5
  217. package/app/components/settings/MergeRolePolicyEditor.vue +13 -12
  218. package/app/components/settings/ModelConfigurationPanel.vue +17 -19
  219. package/app/components/settings/ModelPresetHealthModal.vue +3 -3
  220. package/app/components/settings/ObservabilityConnectionPanel.vue +1 -1
  221. package/app/components/settings/OpenRouterCatalogPanel.vue +10 -8
  222. package/app/components/settings/PackageRegistriesPanel.vue +4 -4
  223. package/app/components/settings/ProviderConnectionTab.vue +5 -4
  224. package/app/components/settings/ProviderManifestEditor.vue +9 -8
  225. package/app/components/settings/ProviderPreferenceEditor.vue +13 -13
  226. package/app/components/settings/RiskPolicyCreateForm.vue +40 -26
  227. package/app/components/settings/RiskPolicyEditorRow.vue +21 -19
  228. package/app/components/settings/RiskPolicyHealthModal.vue +3 -3
  229. package/app/components/settings/RiskPolicyPanel.vue +11 -13
  230. package/app/components/settings/ServiceFragmentDefaultsPanel.vue +15 -12
  231. package/app/components/settings/SharedStacksPanel.vue +5 -5
  232. package/app/components/settings/TaskSourceCard.vue +2 -2
  233. package/app/components/settings/TaskTypeSuppressionsPanel.vue +4 -4
  234. package/app/components/settings/ToolServerChecklist.vue +22 -22
  235. package/app/components/settings/UsageSettings.vue +13 -13
  236. package/app/components/settings/UserSecretsSection.vue +4 -3
  237. package/app/components/settings/WorkspaceMetadataSettings.vue +6 -5
  238. package/app/components/settings/WorkspaceSettingsPanel.vue +72 -58
  239. package/app/components/skills/ReviewSkillQueue.vue +12 -10
  240. package/app/components/skills/SkillLibraryManager.vue +9 -8
  241. package/app/components/slack/SlackPanel.vue +17 -12
  242. package/app/components/spec/ServiceSpecWindow.vue +19 -19
  243. package/app/components/tasks/BugHuntModal.vue +1 -1
  244. package/app/components/tasks/ContextIssuePicker.vue +16 -15
  245. package/app/components/tasks/TaskContextIssues.vue +5 -4
  246. package/app/components/tasks/TaskImportModal.vue +3 -2
  247. package/app/components/tasks/TaskSourceConnectModal.vue +5 -5
  248. package/app/components/testing/TestReportWindow.vue +67 -60
  249. package/app/components/theme/ThemeImportModal.vue +2 -2
  250. package/app/components/tutorial/TutorialNudge.vue +3 -2
  251. package/app/components/tutorial/TutorialOverlay.vue +3 -2
  252. package/app/components/vcs/GitLabConnect.vue +1 -1
  253. package/app/components/visualConfirm/VisualConfirmationWindow.vue +79 -54
  254. package/app/utils/theme/builtins.ts +7 -1
  255. package/i18n/locales/de.json +7 -3
  256. package/i18n/locales/en.json +7 -3
  257. package/i18n/locales/es.json +7 -3
  258. package/i18n/locales/fr.json +7 -3
  259. package/i18n/locales/he.json +7 -3
  260. package/i18n/locales/it.json +7 -3
  261. package/i18n/locales/ja.json +7 -3
  262. package/i18n/locales/pl.json +7 -3
  263. package/i18n/locales/tr.json +7 -3
  264. package/i18n/locales/uk.json +7 -3
  265. package/package.json +1 -1
@@ -193,7 +193,7 @@ watch(
193
193
  </div>
194
194
 
195
195
  <!-- listing -->
196
- <div class="max-h-56 overflow-auto rounded border border-default">
196
+ <div class="max-h-56 overflow-auto rounded-sm border border-default">
197
197
  <div v-if="loading" class="p-3 text-sm text-muted">
198
198
  {{ t('github.repoTree.loading') }}
199
199
  </div>
@@ -206,14 +206,15 @@ watch(
206
206
  :key="entry.path"
207
207
  class="flex items-center justify-between gap-2 px-3 py-1.5"
208
208
  >
209
- <button
210
- type="button"
211
- class="flex items-center gap-2 truncate text-sm text-default hover:text-primary"
209
+ <UButton
210
+ color="primary"
211
+ variant="ghost"
212
+ class="flex items-center gap-2 truncate p-0 text-sm text-default hover:bg-transparent hover:text-primary"
212
213
  @click="browseTo(entry.path)"
213
214
  >
214
215
  <UIcon name="i-lucide-folder" class="h-4 w-4 shrink-0 text-app-warning-400" />
215
216
  <span class="truncate">{{ entry.name }}</span>
216
- </button>
217
+ </UButton>
217
218
  <span
218
219
  v-if="clashes(entry)"
219
220
  class="flex shrink-0 items-center gap-1 text-xs text-app-warning-400"
@@ -256,9 +257,10 @@ watch(
256
257
  data-testid="repo-tree-select-all"
257
258
  @update:model-value="toggleAllFiles"
258
259
  />
259
- <button
260
- type="button"
261
- class="text-xs text-muted hover:text-primary"
260
+ <UButton
261
+ color="primary"
262
+ variant="ghost"
263
+ class="p-0 text-xs text-muted hover:bg-transparent hover:text-primary"
262
264
  @click="toggleAllFiles"
263
265
  >
264
266
  {{
@@ -268,7 +270,7 @@ watch(
268
270
  selectableFiles.length,
269
271
  )
270
272
  }}
271
- </button>
273
+ </UButton>
272
274
  </li>
273
275
  <li
274
276
  v-for="entry in fileEntries"
@@ -282,16 +284,17 @@ watch(
282
284
  :aria-label="entry.name"
283
285
  @update:model-value="pick(entry.path)"
284
286
  />
285
- <button
286
- type="button"
287
- class="flex items-center gap-2 truncate text-sm hover:text-primary"
287
+ <UButton
288
+ color="primary"
289
+ variant="ghost"
290
+ class="flex items-center gap-2 truncate p-0 text-sm hover:bg-transparent hover:text-primary"
288
291
  :class="isPicked(entry.path) ? 'text-primary' : 'text-toned'"
289
292
  :disabled="isAdded(entry.path)"
290
293
  @click="pick(entry.path)"
291
294
  >
292
295
  <UIcon name="i-lucide-file" class="h-4 w-4 shrink-0 text-muted" />
293
296
  <span class="truncate">{{ entry.name }}</span>
294
- </button>
297
+ </UButton>
295
298
  </div>
296
299
  <span
297
300
  v-if="isAdded(entry.path)"
@@ -15,6 +15,7 @@ import type {
15
15
  type HumanTestRoundOutcome = NonNullable<HumanTestRound['outcome']>
16
16
  import StepRunMeta from '~/components/panels/StepRunMeta.vue'
17
17
  import ResultWindowShell from '~/components/panels/ResultWindowShell.vue'
18
+ import SectionLabel from '~/components/common/SectionLabel.vue'
18
19
 
19
20
  const board = useBoardStore()
20
21
  const execution = useExecutionStore()
@@ -168,11 +169,11 @@ const canDestroy = computed(
168
169
  <template v-else>
169
170
  <!-- Environment -->
170
171
  <section class="rounded-lg border border-default bg-default/60 p-4">
171
- <h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
172
+ <SectionLabel as="h3" class="mb-2">
172
173
  {{ t('humanTest.environment.heading') }}
173
- </h3>
174
+ </SectionLabel>
174
175
  <div v-if="env" class="space-y-2">
175
- <div class="flex items-center gap-2 text-[13px]">
176
+ <div class="flex items-center gap-2 text-sm">
176
177
  <UIcon
177
178
  name="i-lucide-circle-dot"
178
179
  class="h-3.5 w-3.5"
@@ -182,27 +183,28 @@ const canDestroy = computed(
182
183
  t(ENV_STATUS_LABEL[env.status])
183
184
  }}</span>
184
185
  </div>
185
- <a
186
+ <ULink
187
+ raw
186
188
  v-if="env.url"
187
- :href="env.url"
189
+ :to="env.url"
188
190
  target="_blank"
189
191
  rel="noopener"
190
- class="inline-flex items-center gap-1.5 break-all text-[13px] text-app-info-300 hover:underline"
192
+ class="inline-flex items-center gap-1.5 break-all text-sm text-app-info-300 hover:underline"
191
193
  >
192
194
  <UIcon name="i-lucide-external-link" class="h-3.5 w-3.5 shrink-0" />
193
195
  {{ env.url }}
194
- </a>
195
- <p v-else class="text-[12px] italic text-dimmed">
196
+ </ULink>
197
+ <p v-else class="text-xs italic text-dimmed">
196
198
  {{ t('humanTest.environment.noUrl') }}
197
199
  </p>
198
- <p v-if="env.expiresAt" class="text-[11px] text-dimmed">
200
+ <p v-if="env.expiresAt" class="text-2xs text-dimmed">
199
201
  {{ t('humanTest.environment.expires', { date: d(new Date(env.expiresAt), 'long') }) }}
200
202
  </p>
201
203
  </div>
202
- <p v-else class="text-[12px] text-app-warning-300/90">
204
+ <p v-else class="text-xs text-app-warning-300/90">
203
205
  {{ ht.degradedReason ?? t('humanTest.environment.none') }}
204
206
  </p>
205
- <p v-if="env && ht.degradedReason" class="mt-2 text-[12px] text-app-warning-300/90">
207
+ <p v-if="env && ht.degradedReason" class="mt-2 text-xs text-app-warning-300/90">
206
208
  {{ ht.degradedReason }}
207
209
  </p>
208
210
 
@@ -249,7 +251,7 @@ const canDestroy = computed(
249
251
  <!-- Working state -->
250
252
  <p
251
253
  v-if="working"
252
- class="flex items-center gap-2 rounded-lg border border-default bg-app-950/40 px-3 py-2 text-[12px] text-toned"
254
+ class="flex items-center gap-2 rounded-lg border border-default bg-app-950/40 px-3 py-2 text-xs text-toned"
253
255
  >
254
256
  <UIcon name="i-lucide-loader" class="h-3.5 w-3.5 animate-spin text-app-warning-300" />
255
257
  {{ phase ? t(PHASE_LABEL[phase]) : '' }}
@@ -258,22 +260,25 @@ const canDestroy = computed(
258
260
  <!-- Findings / fix -->
259
261
  <section v-if="awaitingHuman" class="rounded-lg border border-default bg-default/60 p-4">
260
262
  <div class="flex items-center justify-between">
261
- <h3 class="text-[11px] font-semibold uppercase tracking-wide text-dimmed">
263
+ <SectionLabel as="h3">
262
264
  {{ t('humanTest.fix.heading') }}
263
- </h3>
264
- <button
265
- class="text-[12px] text-muted hover:text-default"
265
+ </SectionLabel>
266
+ <UButton
267
+ color="neutral"
268
+ variant="ghost"
269
+ class="p-0 text-xs text-muted hover:bg-transparent hover:text-default"
266
270
  @click="showFindings = !showFindings"
267
271
  >
268
272
  {{ showFindings ? t('humanTest.fix.cancel') : t('humanTest.fix.requestFix') }}
269
- </button>
273
+ </UButton>
270
274
  </div>
271
275
  <div v-if="showFindings" class="mt-2 space-y-2">
272
- <textarea
276
+ <UTextarea
273
277
  v-model="findings"
274
- rows="4"
278
+ :rows="4"
275
279
  :placeholder="t('humanTest.fix.placeholder')"
276
- 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"
280
+ size="sm"
281
+ class="w-full"
277
282
  />
278
283
  <UButton
279
284
  size="sm"
@@ -294,11 +299,11 @@ const canDestroy = computed(
294
299
  v-if="ht.rounds && ht.rounds.length"
295
300
  class="rounded-lg border border-default bg-default/60 p-4"
296
301
  >
297
- <h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
302
+ <SectionLabel as="h3" class="mb-2">
298
303
  {{ t('humanTest.history.heading', { count: ht.attempts }, ht.attempts) }}
299
- </h3>
304
+ </SectionLabel>
300
305
  <ol class="space-y-2">
301
- <li v-for="(r, i) in ht.rounds" :key="i" class="flex items-start gap-2 text-[12px]">
306
+ <li v-for="(r, i) in ht.rounds" :key="i" class="flex items-start gap-2 text-xs">
302
307
  <UIcon
303
308
  :name="r.kind === 'fix' ? 'i-lucide-wrench' : 'i-lucide-git-merge'"
304
309
  class="mt-0.5 h-3.5 w-3.5 shrink-0 text-muted"
@@ -310,7 +315,7 @@ const canDestroy = computed(
310
315
  : t('humanTest.history.pulledMain')
311
316
  }}</span>
312
317
  <span
313
- class="ms-1.5 rounded px-1 text-[10px] uppercase"
318
+ class="ms-1.5 rounded-sm px-1 text-3xs uppercase"
314
319
  :class="
315
320
  r.outcome === 'completed'
316
321
  ? 'bg-app-success-500/15 text-app-success-300'
@@ -45,13 +45,13 @@ const { t } = useI18n()
45
45
  <header class="flex items-start gap-2.5">
46
46
  <UIcon name="i-lucide-clipboard-check" class="mt-0.5 h-4 w-4 shrink-0 text-app-warning-300" />
47
47
  <div class="min-w-0 flex-1">
48
- <h3 class="text-[13px] font-semibold text-app-warning-200">
48
+ <h3 class="text-sm font-semibold text-app-warning-200">
49
49
  {{ t('initiative.planReview.title') }}
50
50
  </h3>
51
- <p class="mt-0.5 text-[12px] leading-relaxed text-app-warning-100/80">
51
+ <p class="mt-0.5 text-xs leading-relaxed text-app-warning-100/80">
52
52
  {{ t('initiative.planReview.body') }}
53
53
  </p>
54
- <p class="mt-2 text-[12px] leading-relaxed text-app-warning-100/70">
54
+ <p class="mt-2 text-xs leading-relaxed text-app-warning-100/70">
55
55
  {{ t('initiative.planReview.noDocument') }}
56
56
  <template v-if="hasSections">
57
57
  {{ t('initiative.planReview.noDocumentSections') }}
@@ -30,6 +30,8 @@ import type { StepApproval } from '~/types/execution'
30
30
  import { useStepProse } from '~/composables/useStepProse'
31
31
  import { useProseComments } from '~/composables/useProseComments'
32
32
  import InitiativePlanDecision from '~/components/initiative/InitiativePlanDecision.vue'
33
+ import SectionLabel from '~/components/common/SectionLabel.vue'
34
+ import IconButton from '~/components/common/IconButton.vue'
33
35
 
34
36
  const props = defineProps<{
35
37
  /** The parked gate under review. */
@@ -150,34 +152,31 @@ async function copyPlan() {
150
152
  class="hidden w-52 shrink-0 flex-col border-e border-default bg-default/60 lg:flex"
151
153
  >
152
154
  <div class="flex items-center gap-0.5 border-b border-default px-3 py-2">
153
- <span
154
- v-if="outline.hasToc"
155
- class="min-w-0 flex-1 truncate text-[11px] font-semibold uppercase tracking-wide text-dimmed"
156
- >
155
+ <SectionLabel v-if="outline.hasToc" as="span" class="min-w-0 flex-1 truncate">
157
156
  {{ t('panels.stepDetail.contents') }}
158
- </span>
157
+ </SectionLabel>
159
158
  <span v-else class="flex-1" />
160
159
  <!-- Collapse-all tracks the outline: with no headings the only section is the untitled
161
160
  preamble, which renders no toggle of its own, so collapsing it would hide the whole
162
161
  plan with nothing on screen to bring it back. Copying it does not — that is about the
163
162
  document, which exists either way. -->
164
- <UButton
163
+ <IconButton
165
164
  v-if="outline.hasToc"
166
165
  :icon="allCollapsed ? 'i-lucide-unfold-vertical' : 'i-lucide-fold-vertical'"
167
166
  color="neutral"
168
167
  variant="ghost"
169
168
  size="xs"
170
- :title="
169
+ :label="
171
170
  allCollapsed ? t('panels.stepDetail.expandAll') : t('panels.stepDetail.collapseAll')
172
171
  "
173
172
  @click="setAll(!allCollapsed)"
174
173
  />
175
- <UButton
174
+ <IconButton
176
175
  icon="i-lucide-copy"
177
176
  color="neutral"
178
177
  variant="ghost"
179
178
  size="xs"
180
- :title="t('panels.stepDetail.copyRawOutput')"
179
+ :label="t('panels.stepDetail.copyRawOutput')"
181
180
  @click="copyPlan"
182
181
  />
183
182
  </div>
@@ -187,10 +186,12 @@ async function copyPlan() {
187
186
  :aria-label="t('panels.stepDetail.contents')"
188
187
  class="flex-1 space-y-0.5 overflow-y-auto px-2 py-2"
189
188
  >
190
- <button
189
+ <UButton
190
+ color="neutral"
191
+ variant="ghost"
191
192
  v-for="s in tocSections"
192
193
  :key="s.id"
193
- class="block w-full truncate rounded-md px-2 py-1 text-start text-[12px] transition"
194
+ class="block w-full truncate rounded-md px-2 py-1 text-start text-xs transition"
194
195
  :class="
195
196
  activeId === s.id
196
197
  ? 'bg-app-warning-500/15 font-medium text-app-warning-100'
@@ -201,7 +202,7 @@ async function copyPlan() {
201
202
  @click="goTo(s.id)"
202
203
  >
203
204
  {{ s.title }}
204
- </button>
205
+ </UButton>
205
206
  </nav>
206
207
  <!-- Run details (the model, the run id, the token telemetry). Filled by the host, which
207
208
  already resolves the bundle through `useResultViewRunMeta`; it keeps its home in a
@@ -214,21 +215,22 @@ async function copyPlan() {
214
215
  class="flex flex-col"
215
216
  :class="outline.hasToc ? 'max-h-[45%] shrink-0 border-t border-default' : 'min-h-0 flex-1'"
216
217
  >
217
- <button
218
- type="button"
218
+ <UButton
219
+ color="neutral"
220
+ variant="ghost"
219
221
  data-testid="initiative-plan-run-meta-toggle"
220
222
  class="flex shrink-0 items-center gap-1.5 px-3 py-2 text-start transition hover:bg-elevated/40"
221
223
  :aria-expanded="runDetailsOpen"
222
224
  @click="runDetailsOpen = !runDetailsOpen"
223
225
  >
224
- <span class="flex-1 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
226
+ <SectionLabel as="span" class="flex-1">
225
227
  {{ t('panels.stepDetail.details') }}
226
- </span>
228
+ </SectionLabel>
227
229
  <UIcon
228
230
  :name="runDetailsOpen ? 'i-lucide-chevron-down' : 'i-lucide-chevron-up'"
229
231
  class="h-3.5 w-3.5 shrink-0 text-dimmed"
230
232
  />
231
- </button>
233
+ </UButton>
232
234
  <div
233
235
  v-if="runDetailsOpen"
234
236
  data-testid="initiative-plan-run-meta"
@@ -251,7 +253,7 @@ async function copyPlan() {
251
253
  `5xl` shell), so a cap was dead markup and the comment here said so. On a window that now
252
254
  spans the viewport it is the only thing between the plan and 200-character lines, and it
253
255
  is the step reader's own measure (`AgentStepDetail`, `mx-auto max-w-3xl` over the same
254
- 13px `.reader-prose`) rather than a second opinion about how wide prose should be. The
256
+ `text-sm` `.reader-prose`) rather than a second opinion about how wide prose should be. The
255
257
  leftover width is the document's margins; the LAYOUT is what the extra space bought —
256
258
  outline and rail no longer competing with the plan for one 5xl card. -->
257
259
  <div class="mx-auto w-full max-w-3xl">
@@ -262,9 +264,11 @@ async function copyPlan() {
262
264
  :ref="(el) => (sectionEls[s.id] = el as HTMLElement | null)"
263
265
  class="scroll-mt-2"
264
266
  >
265
- <button
267
+ <UButton
268
+ color="neutral"
269
+ variant="ghost"
266
270
  v-if="s.depth > 0"
267
- class="group flex w-full items-center gap-1.5 rounded py-0.5 text-start transition hover:text-highlighted"
271
+ class="group flex w-full items-center gap-1.5 rounded-sm px-0 py-0.5 text-start transition hover:bg-transparent hover:text-highlighted"
268
272
  :aria-expanded="!collapsed[s.id]"
269
273
  @click="toggle(s.id)"
270
274
  >
@@ -275,17 +279,17 @@ async function copyPlan() {
275
279
  />
276
280
  <span
277
281
  class="font-semibold text-app-100"
278
- :class="s.depth <= 1 ? 'text-base' : s.depth === 2 ? 'text-sm' : 'text-[13px]'"
282
+ :class="s.depth <= 1 ? 'text-base' : s.depth === 2 ? 'text-sm' : 'text-xs'"
279
283
  v-html="s.titleHtml"
280
284
  />
281
- </button>
285
+ </UButton>
282
286
  <!-- `review-mode` carries the click-to-comment affordance, so it tracks the same RBAC
283
287
  gate the composer does — a viewer gets the document, not hover targets that lead
284
288
  nowhere. -->
285
289
  <!-- eslint-disable-next-line vue/no-v-html -->
286
290
  <div
287
291
  v-show="!collapsed[s.id]"
288
- class="reader-prose mt-0.5 text-[13px] leading-relaxed text-toned"
292
+ class="reader-prose mt-0.5 text-sm leading-relaxed text-toned"
289
293
  :class="[s.depth > 0 ? 'ps-5' : '', canExecute ? 'review-mode' : '']"
290
294
  @click="onProseClick"
291
295
  v-html="s.bodyHtml"
@@ -305,12 +309,12 @@ async function copyPlan() {
305
309
  <!-- A HEADING, not a styled div: this rail is what the window is now for, so the surface
306
310
  that asks the human for a decision has to be reachable as one. -->
307
311
  <h3
308
- class="flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-app-warning-400"
312
+ class="flex items-center gap-1.5 text-2xs font-semibold uppercase tracking-wide text-app-warning-400"
309
313
  >
310
314
  <UIcon name="i-lucide-clipboard-check" class="h-3.5 w-3.5 shrink-0" />
311
315
  {{ t('initiative.planReview.title') }}
312
316
  </h3>
313
- <p class="mt-1 text-[12px] leading-relaxed text-muted">
317
+ <p class="mt-1 text-xs leading-relaxed text-muted">
314
318
  {{ t('initiative.planReview.body') }}
315
319
  </p>
316
320
  </div>
@@ -322,11 +326,11 @@ async function copyPlan() {
322
326
  data-testid="initiative-plan-composer"
323
327
  class="rounded-lg border border-primary/40 bg-primary/5 p-2.5"
324
328
  >
325
- <div class="mb-1 text-[10px] uppercase tracking-wide text-primary">
329
+ <div class="mb-1 text-3xs uppercase tracking-wide text-primary">
326
330
  {{ t('panels.stepDetail.commentingOn') }}
327
331
  </div>
328
332
  <pre
329
- class="mb-2 max-h-20 overflow-auto whitespace-pre-wrap rounded bg-app-950/60 p-1.5 text-[11px] text-toned"
333
+ class="mb-2 max-h-20 overflow-auto whitespace-pre-wrap rounded-sm bg-app-950/60 p-1.5 text-2xs text-toned"
330
334
  >{{ draftTarget.quotedSource }}</pre>
331
335
  <UTextarea
332
336
  v-model="draftBody"
@@ -361,28 +365,32 @@ async function copyPlan() {
361
365
  class="rounded-lg border border-default bg-default/50 p-2.5"
362
366
  >
363
367
  <div class="mb-1 flex items-start justify-between gap-2">
364
- <div class="text-[10px] uppercase tracking-wide text-dimmed">
368
+ <SectionLabel>
365
369
  {{ t('panels.stepDetail.commentN', { number: idx + 1 }) }}
366
- </div>
367
- <button
368
- class="text-dimmed transition hover:text-app-error-400"
369
- :title="t('panels.stepDetail.removeComment')"
370
+ </SectionLabel>
371
+ <IconButton
372
+ color="neutral"
373
+ variant="ghost"
374
+ icon="i-lucide-x"
375
+ :label="t('panels.stepDetail.removeComment')"
376
+ :ui="{
377
+ base: 'p-0 text-dimmed transition hover:bg-transparent hover:text-app-error-400',
378
+ leadingIcon: 'h-3.5 w-3.5',
379
+ }"
370
380
  @click="removeComment(idx)"
371
- >
372
- <UIcon name="i-lucide-x" class="h-3.5 w-3.5" />
373
- </button>
381
+ />
374
382
  </div>
375
383
  <pre
376
- class="mb-1 max-h-16 overflow-auto whitespace-pre-wrap rounded bg-app-950/50 p-1.5 text-[10px] text-muted"
384
+ class="mb-1 max-h-16 overflow-auto whitespace-pre-wrap rounded-sm bg-app-950/50 p-1.5 text-3xs text-muted"
377
385
  >{{ c.quotedSource }}</pre>
378
- <p class="text-[12px] text-default">{{ c.body }}</p>
386
+ <p class="text-xs text-default">{{ c.body }}</p>
379
387
  </div>
380
388
 
381
389
  <!-- Only worth saying where clicking a block does something (the RBAC to act on it) and
382
390
  where nothing has been said yet. -->
383
391
  <p
384
392
  v-if="canExecute && !draftTarget && !planComments.length"
385
- class="text-[11px] leading-relaxed text-muted"
393
+ class="text-2xs leading-relaxed text-muted"
386
394
  >
387
395
  {{ t('initiative.planReview.commentHint') }}
388
396
  </p>
@@ -221,7 +221,7 @@ async function onDiscard() {
221
221
  </div>
222
222
 
223
223
  <template v-else>
224
- <p class="mb-4 text-[13px] leading-relaxed text-toned">
224
+ <p class="mb-4 text-sm leading-relaxed text-toned">
225
225
  {{ t('initiative.planning.intro') }}
226
226
  </p>
227
227
 
@@ -259,7 +259,7 @@ async function onDiscard() {
259
259
  converged, which means the planner already has what it needs). -->
260
260
  <div
261
261
  v-else-if="phase === 'idle' && questions.length === 0"
262
- class="rounded-lg border border-default bg-app-950/40 p-4 text-center text-[13px] text-muted"
262
+ class="rounded-lg border border-default bg-app-950/40 p-4 text-center text-sm text-muted"
263
263
  data-testid="initiative-planning-idle"
264
264
  >
265
265
  {{ t('initiative.planning.idle') }}
@@ -268,7 +268,7 @@ async function onDiscard() {
268
268
  <!-- Converged / no pending questions -->
269
269
  <div
270
270
  v-else-if="phase === 'converged' || questions.length === 0"
271
- class="rounded-lg border border-default bg-app-950/40 p-4 text-center text-[13px] text-muted"
271
+ class="rounded-lg border border-default bg-app-950/40 p-4 text-center text-sm text-muted"
272
272
  data-testid="initiative-planning-converged"
273
273
  >
274
274
  {{ t('initiative.planning.converged') }}
@@ -301,7 +301,7 @@ async function onDiscard() {
301
301
  flush could only drop. -->
302
302
  <p
303
303
  v-if="!addressable(q)"
304
- class="mt-1 text-[11px] text-app-warning-300"
304
+ class="mt-1 text-2xs text-app-warning-300"
305
305
  data-testid="initiative-planning-unanswerable"
306
306
  >
307
307
  {{ t('initiative.planning.unanswerable') }}
@@ -360,7 +360,7 @@ async function onDiscard() {
360
360
  v-if="phase === 'awaiting' && questions.length > 0"
361
361
  class="ms-auto flex items-center gap-2"
362
362
  >
363
- <p class="text-[11px] text-dimmed">
363
+ <p class="text-2xs text-dimmed">
364
364
  <span
365
365
  v-if="unanswered > 0"
366
366
  class="text-app-warning-400/90"