@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
@@ -24,6 +24,7 @@ import IntegrationBackTitle from '~/components/layout/IntegrationBackTitle.vue'
24
24
  import type { AppSlots } from '~/modular/slots'
25
25
  import { usePipelinesStore } from '~/stores/pipelines'
26
26
  import { pipelineAllowedForTaskType } from '~/utils/pipeline'
27
+ import SectionLabel from '~/components/common/SectionLabel.vue'
27
28
 
28
29
  const { t, te } = useI18n()
29
30
  const ui = useUiStore()
@@ -360,7 +361,7 @@ async function save() {
360
361
  <h3 class="text-sm font-semibold text-default">
361
362
  {{ t('settings.workspaceSettings.waiting.heading') }}
362
363
  </h3>
363
- <p class="text-[11px] text-muted">
364
+ <p class="text-2xs text-muted">
364
365
  <i18n-t keypath="settings.workspaceSettings.waiting.body" tag="span" scope="global">
365
366
  <template #overdue>
366
367
  <span class="text-app-error-400">{{
@@ -369,11 +370,12 @@ async function save() {
369
370
  </template>
370
371
  </i18n-t>
371
372
  </p>
372
- <label class="block w-48">
373
- <span class="mb-1 block text-[10px] uppercase tracking-wide text-dimmed">
373
+ <label class="block">
374
+ <SectionLabel as="span" class="mb-1 block">
374
375
  {{ t('settings.workspaceSettings.waiting.escalateAfter') }}
375
- </span>
376
+ </SectionLabel>
376
377
  <UInput
378
+ class="w-48"
377
379
  v-model.number="draft.waitingEscalationMinutes"
378
380
  type="number"
379
381
  :min="1"
@@ -387,34 +389,40 @@ async function save() {
387
389
  <h3 class="text-sm font-semibold text-default">
388
390
  {{ t('settings.workspaceSettings.taskLimit.heading') }}
389
391
  </h3>
390
- <p class="text-[11px] text-muted">
392
+ <p class="text-2xs text-muted">
391
393
  {{ t('settings.workspaceSettings.taskLimit.body') }}
392
394
  </p>
393
- <label class="block w-64">
394
- <span class="mb-1 block text-[10px] uppercase tracking-wide text-dimmed">{{
395
+ <label class="block">
396
+ <SectionLabel as="span" class="mb-1 block">{{
395
397
  t('settings.workspaceSettings.taskLimit.mode')
396
- }}</span>
398
+ }}</SectionLabel>
397
399
  <USelect
398
400
  v-model="draft.taskLimitMode"
399
401
  :items="MODES"
400
402
  value-key="value"
401
403
  size="sm"
402
- class="w-full"
404
+ class="w-64"
403
405
  />
404
406
  </label>
405
407
 
406
- <label v-if="draft.taskLimitMode === 'shared'" class="block w-48">
407
- <span class="mb-1 block text-[10px] uppercase tracking-wide text-dimmed">
408
+ <label v-if="draft.taskLimitMode === 'shared'" class="block">
409
+ <SectionLabel as="span" class="mb-1 block">
408
410
  {{ t('settings.workspaceSettings.taskLimit.maxRunning') }}
409
- </span>
410
- <UInput v-model.number="draft.taskLimitShared" type="number" :min="1" size="sm" />
411
+ </SectionLabel>
412
+ <UInput
413
+ class="w-48"
414
+ v-model.number="draft.taskLimitShared"
415
+ type="number"
416
+ :min="1"
417
+ size="sm"
418
+ />
411
419
  </label>
412
420
 
413
421
  <div v-else-if="draft.taskLimitMode === 'per_type'" class="grid grid-cols-2 gap-3">
414
422
  <label v-for="taskType in TASK_TYPES" :key="taskType" class="block">
415
- <span class="mb-1 block text-[10px] uppercase tracking-wide text-dimmed">
423
+ <SectionLabel as="span" class="mb-1 block">
416
424
  {{ maxTaskTypeLabel(taskType) }}
417
- </span>
425
+ </SectionLabel>
418
426
  <UInput
419
427
  v-model.number="draft.perType[taskType]"
420
428
  type="number"
@@ -431,19 +439,19 @@ async function save() {
431
439
  <h3 class="text-sm font-semibold text-default">
432
440
  {{ t('settings.workspaceSettings.inputGate.heading') }}
433
441
  </h3>
434
- <p class="text-[11px] text-muted">
442
+ <p class="text-2xs text-muted">
435
443
  {{ t('settings.workspaceSettings.inputGate.body') }}
436
444
  </p>
437
- <label class="block w-64">
438
- <span class="mb-1 block text-[10px] uppercase tracking-wide text-dimmed">{{
445
+ <label class="block">
446
+ <SectionLabel as="span" class="mb-1 block">{{
439
447
  t('settings.workspaceSettings.inputGate.mode')
440
- }}</span>
448
+ }}</SectionLabel>
441
449
  <USelect
442
450
  v-model="draft.inputGateMode"
443
451
  :items="INPUT_GATE_MODES"
444
452
  value-key="value"
445
453
  size="sm"
446
- class="w-full"
454
+ class="w-64"
447
455
  data-testid="input-gate-mode"
448
456
  />
449
457
  </label>
@@ -454,27 +462,28 @@ async function save() {
454
462
  <h3 class="text-sm font-semibold text-default">
455
463
  {{ t('settings.workspaceSettings.reviewFriction.heading') }}
456
464
  </h3>
457
- <p class="text-[11px] text-muted">
465
+ <p class="text-2xs text-muted">
458
466
  {{ t('settings.workspaceSettings.reviewFriction.body') }}
459
467
  </p>
460
- <label class="block w-64">
461
- <span class="mb-1 block text-[10px] uppercase tracking-wide text-dimmed">{{
468
+ <label class="block">
469
+ <SectionLabel as="span" class="mb-1 block">{{
462
470
  t('settings.workspaceSettings.reviewFriction.mode')
463
- }}</span>
471
+ }}</SectionLabel>
464
472
  <USelect
465
473
  v-model="draft.reviewFrictionMode"
466
474
  :items="REVIEW_FRICTION_MODES"
467
475
  value-key="value"
468
476
  size="sm"
469
- class="w-full"
477
+ class="w-64"
470
478
  />
471
479
  </label>
472
480
 
473
- <label v-if="draft.reviewFrictionMode !== 'off'" class="block w-48">
474
- <span class="mb-1 block text-[10px] uppercase tracking-wide text-dimmed">
481
+ <label v-if="draft.reviewFrictionMode !== 'off'" class="block">
482
+ <SectionLabel as="span" class="mb-1 block">
475
483
  {{ t('settings.workspaceSettings.reviewFriction.warnCount') }}
476
- </span>
484
+ </SectionLabel>
477
485
  <UInput
486
+ class="w-48"
478
487
  v-model.number="draft.reviewFrictionWarnCount"
479
488
  type="number"
480
489
  :min="1"
@@ -483,20 +492,21 @@ async function save() {
483
492
  </label>
484
493
 
485
494
  <div v-if="draft.reviewFrictionMode === 'enforce'" class="space-y-2">
486
- <p class="text-[11px] text-muted">
495
+ <p class="text-2xs text-muted">
487
496
  {{ t('settings.workspaceSettings.reviewFriction.enforceHint') }}
488
497
  </p>
489
498
  <label class="flex items-center gap-2">
490
499
  <USwitch v-model="draft.reviewFrictionBlockCountEnabled" size="sm" />
491
- <span class="text-[13px] text-toned">{{
500
+ <span class="text-sm text-toned">{{
492
501
  t('settings.workspaceSettings.reviewFriction.blockCountToggle')
493
502
  }}</span>
494
503
  </label>
495
- <label v-if="draft.reviewFrictionBlockCountEnabled" class="block w-48">
496
- <span class="mb-1 block text-[10px] uppercase tracking-wide text-dimmed">
504
+ <label v-if="draft.reviewFrictionBlockCountEnabled" class="block">
505
+ <SectionLabel as="span" class="mb-1 block">
497
506
  {{ t('settings.workspaceSettings.reviewFriction.blockCount') }}
498
- </span>
507
+ </SectionLabel>
499
508
  <UInput
509
+ class="w-48"
500
510
  v-model.number="draft.reviewFrictionBlockCount"
501
511
  type="number"
502
512
  :min="1"
@@ -505,15 +515,16 @@ async function save() {
505
515
  </label>
506
516
  <label class="flex items-center gap-2">
507
517
  <USwitch v-model="draft.reviewFrictionBlockStuckEnabled" size="sm" />
508
- <span class="text-[13px] text-toned">{{
518
+ <span class="text-sm text-toned">{{
509
519
  t('settings.workspaceSettings.reviewFriction.blockStuckToggle')
510
520
  }}</span>
511
521
  </label>
512
- <label v-if="draft.reviewFrictionBlockStuckEnabled" class="block w-48">
513
- <span class="mb-1 block text-[10px] uppercase tracking-wide text-dimmed">
522
+ <label v-if="draft.reviewFrictionBlockStuckEnabled" class="block">
523
+ <SectionLabel as="span" class="mb-1 block">
514
524
  {{ t('settings.workspaceSettings.reviewFriction.blockStuckMinutes') }}
515
- </span>
525
+ </SectionLabel>
516
526
  <UInput
527
+ class="w-48"
517
528
  v-model.number="draft.reviewFrictionBlockStuckMinutes"
518
529
  type="number"
519
530
  :min="1"
@@ -528,7 +539,7 @@ async function save() {
528
539
  <h3 class="text-sm font-semibold text-default">
529
540
  {{ t('settings.workspaceSettings.observability.heading') }}
530
541
  </h3>
531
- <p class="text-[11px] text-muted">
542
+ <p class="text-2xs text-muted">
532
543
  {{ t('settings.workspaceSettings.observability.body') }}
533
544
  </p>
534
545
  <label class="flex items-center gap-2">
@@ -544,7 +555,7 @@ async function save() {
544
555
  <h3 class="text-sm font-semibold text-default">
545
556
  {{ t('settings.workspaceSettings.prReport.heading') }}
546
557
  </h3>
547
- <p class="text-[11px] text-muted">
558
+ <p class="text-2xs text-muted">
548
559
  {{ t('settings.workspaceSettings.prReport.body') }}
549
560
  </p>
550
561
  <label class="flex items-center gap-2">
@@ -560,14 +571,15 @@ async function save() {
560
571
  <h3 class="text-sm font-semibold text-default">
561
572
  {{ t('settings.workspaceSettings.retention.heading') }}
562
573
  </h3>
563
- <p class="text-[11px] text-muted">
574
+ <p class="text-2xs text-muted">
564
575
  {{ t('settings.workspaceSettings.retention.body') }}
565
576
  </p>
566
- <label class="block w-48">
567
- <span class="mb-1 block text-[10px] uppercase tracking-wide text-dimmed">
577
+ <label class="block">
578
+ <SectionLabel as="span" class="mb-1 block">
568
579
  {{ t('settings.workspaceSettings.retention.days') }}
569
- </span>
580
+ </SectionLabel>
570
581
  <UInput
582
+ class="w-48"
571
583
  v-model.number="draft.artifactRetentionDays"
572
584
  type="number"
573
585
  :min="1"
@@ -582,14 +594,15 @@ async function save() {
582
594
  <h3 class="text-sm font-semibold text-default">
583
595
  {{ t('settings.workspaceSettings.doneLane.heading') }}
584
596
  </h3>
585
- <p class="text-[11px] text-muted">
597
+ <p class="text-2xs text-muted">
586
598
  {{ t('settings.workspaceSettings.doneLane.body') }}
587
599
  </p>
588
- <label class="block w-48">
589
- <span class="mb-1 block text-[10px] uppercase tracking-wide text-dimmed">
600
+ <label class="block">
601
+ <SectionLabel as="span" class="mb-1 block">
590
602
  {{ t('settings.workspaceSettings.doneLane.maxItems') }}
591
- </span>
603
+ </SectionLabel>
592
604
  <UInput
605
+ class="w-48"
593
606
  v-model.number="draft.doneLaneMaxItems"
594
607
  type="number"
595
608
  :min="0"
@@ -598,20 +611,21 @@ async function save() {
598
611
  data-testid="done-lane-max-items"
599
612
  />
600
613
  </label>
601
- <p v-if="draft.doneLaneMaxItems === 0" class="text-[11px] text-dimmed">
614
+ <p v-if="draft.doneLaneMaxItems === 0" class="text-2xs text-dimmed">
602
615
  {{ t('settings.workspaceSettings.doneLane.zeroHint') }}
603
616
  </p>
604
617
  <label class="flex items-center gap-2">
605
618
  <UCheckbox v-model="draft.doneLaneRetentionEnabled" size="sm" />
606
- <span class="text-[11px] text-toned">{{
619
+ <span class="text-2xs text-toned">{{
607
620
  t('settings.workspaceSettings.doneLane.ageToggle')
608
621
  }}</span>
609
622
  </label>
610
- <label v-if="draft.doneLaneRetentionEnabled" class="block w-48">
611
- <span class="mb-1 block text-[10px] uppercase tracking-wide text-dimmed">
623
+ <label v-if="draft.doneLaneRetentionEnabled" class="block">
624
+ <SectionLabel as="span" class="mb-1 block">
612
625
  {{ t('settings.workspaceSettings.doneLane.days') }}
613
- </span>
626
+ </SectionLabel>
614
627
  <UInput
628
+ class="w-48"
615
629
  v-model.number="draft.doneLaneRetentionDays"
616
630
  type="number"
617
631
  :min="1"
@@ -620,7 +634,7 @@ async function save() {
620
634
  data-testid="done-lane-retention-days"
621
635
  />
622
636
  </label>
623
- <p class="text-[11px] text-dimmed">
637
+ <p class="text-2xs text-dimmed">
624
638
  {{ t('settings.workspaceSettings.doneLane.hidesOnlyHint') }}
625
639
  </p>
626
640
  </section>
@@ -630,7 +644,7 @@ async function save() {
630
644
  <h3 class="text-sm font-semibold text-default">
631
645
  {{ t('settings.workspaceSettings.runCredential.heading') }}
632
646
  </h3>
633
- <p class="text-[11px] text-muted">
647
+ <p class="text-2xs text-muted">
634
648
  {{ t('settings.workspaceSettings.runCredential.body') }}
635
649
  </p>
636
650
  <label class="flex items-center gap-2">
@@ -643,14 +657,14 @@ async function save() {
643
657
  t('settings.workspaceSettings.runCredential.toggle')
644
658
  }}</span>
645
659
  </label>
646
- <p v-if="!draft.allowInitiatorPat" class="text-[11px] text-app-warning-300">
660
+ <p v-if="!draft.allowInitiatorPat" class="text-2xs text-app-warning-300">
647
661
  {{ t('settings.workspaceSettings.runCredential.offHint') }}
648
662
  </p>
649
663
  <!-- Stated rather than read: the account floor lives behind an ACCOUNT-admin
650
664
  endpoint, which a workspace admin may not hold, so probing it here would 403
651
665
  for exactly the people this note is for. A static line sets the expectation
652
666
  without pretending to report a value we cannot see. -->
653
- <p v-if="draft.allowInitiatorPat" class="text-[11px] text-dimmed">
667
+ <p v-if="draft.allowInitiatorPat" class="text-2xs text-dimmed">
654
668
  {{ t('settings.workspaceSettings.runCredential.accountFloorNote') }}
655
669
  </p>
656
670
  </section>
@@ -662,7 +676,7 @@ async function save() {
662
676
  <h3 class="text-sm font-semibold text-default">
663
677
  {{ t('settings.workspaceSettings.bugFishing.heading') }}
664
678
  </h3>
665
- <p class="text-[11px] text-muted">
679
+ <p class="text-2xs text-muted">
666
680
  {{ t('settings.workspaceSettings.bugFishing.body') }}
667
681
  </p>
668
682
  <USelectMenu
@@ -680,7 +694,7 @@ async function save() {
680
694
  <h3 class="text-sm font-semibold text-default">
681
695
  {{ t('settings.workspaceSettings.kaizen.heading') }}
682
696
  </h3>
683
- <p class="text-[11px] text-muted">
697
+ <p class="text-2xs text-muted">
684
698
  {{ t('settings.workspaceSettings.kaizen.body') }}
685
699
  </p>
686
700
  <label class="flex items-center gap-2">
@@ -17,6 +17,7 @@
17
17
  import { computed, ref } from 'vue'
18
18
  import { MAX_REVIEW_SKILLS } from '@cat-factory/contracts'
19
19
  import { useSkillsStore } from '~/stores/skills'
20
+ import SectionLabel from '~/components/common/SectionLabel.vue'
20
21
 
21
22
  const props = defineProps<{
22
23
  /** The queued skill ids, in the order the reviewer applies them (`v-model`). */
@@ -63,9 +64,9 @@ function toggle(id: string) {
63
64
  <template>
64
65
  <div>
65
66
  <div class="mb-1 flex items-center justify-between gap-2">
66
- <span class="text-[11px] font-semibold uppercase tracking-wide text-muted">
67
+ <SectionLabel as="span">
67
68
  {{ t('skills.reviewQueue.label') }}
68
- </span>
69
+ </SectionLabel>
69
70
  <UPopover v-model:open="open" :content="{ align: 'end' }">
70
71
  <UButton
71
72
  size="xs"
@@ -83,11 +84,12 @@ function toggle(id: string) {
83
84
  >
84
85
  <div class="min-h-0 flex-1 overflow-y-auto p-1">
85
86
  <template v-if="offered.length">
86
- <button
87
+ <UButton
88
+ color="neutral"
89
+ variant="ghost"
87
90
  v-for="s in offered"
88
91
  :key="s.id"
89
- type="button"
90
- class="flex w-full items-start gap-2 rounded px-2 py-1.5 text-start text-sm hover:bg-elevated/60 disabled:cursor-not-allowed disabled:opacity-40"
92
+ class="flex w-full items-start gap-2 rounded-sm px-2 py-1.5 text-start text-sm hover:bg-elevated/60 disabled:cursor-not-allowed disabled:opacity-40"
91
93
  :class="selectedSet.has(s.id) ? 'text-app-100' : 'text-toned'"
92
94
  :disabled="atCap && !selectedSet.has(s.id)"
93
95
  :aria-pressed="selectedSet.has(s.id)"
@@ -101,20 +103,20 @@ function toggle(id: string) {
101
103
  />
102
104
  <span class="min-w-0 flex-1">
103
105
  <span class="block truncate">{{ s.name }}</span>
104
- <span class="block truncate text-[11px] text-dimmed">
106
+ <span class="block truncate text-2xs text-dimmed">
105
107
  {{ s.description }}
106
108
  </span>
107
109
  </span>
108
- </button>
110
+ </UButton>
109
111
  </template>
110
- <p v-else class="px-2 py-3 text-[12px] text-dimmed">
112
+ <p v-else class="px-2 py-3 text-xs text-dimmed">
111
113
  {{ t('skills.reviewQueue.pickerEmpty') }}
112
114
  </p>
113
115
  </div>
114
116
 
115
117
  <p
116
118
  v-if="atCap"
117
- class="border-t border-default px-2 py-1.5 text-[11px] text-app-warning-400"
119
+ class="border-t border-default px-2 py-1.5 text-2xs text-app-warning-400"
118
120
  >
119
121
  {{ t('skills.reviewQueue.capped', { max: MAX_REVIEW_SKILLS }) }}
120
122
  </p>
@@ -149,7 +151,7 @@ function toggle(id: string) {
149
151
  {{ s.name }}<UIcon name="i-lucide-x" class="ms-0.5 h-3 w-3" />
150
152
  </UBadge>
151
153
  </div>
152
- <p v-else class="text-[11px] text-dimmed">
154
+ <p v-else class="text-2xs text-dimmed">
153
155
  {{ t('skills.reviewQueue.hint') }}
154
156
  </p>
155
157
  </div>
@@ -12,6 +12,7 @@ import { useSkillLibrary } from '~/stores/skillLibrary'
12
12
  import { SKILL_GROUP_LABEL_KEYS } from '~/utils/skills'
13
13
  import GitHubRepoSearchSelect from '~/components/github/GitHubRepoSearchSelect.vue'
14
14
  import RepoTreeBrowser from '~/components/github/RepoTreeBrowser.vue'
15
+ import IconButton from '~/components/common/IconButton.vue'
15
16
 
16
17
  const props = defineProps<{ accountId: string }>()
17
18
 
@@ -181,10 +182,10 @@ async function unlinkSource(id: string) {
181
182
  <!-- The manifest declared a group this build does not know (a typo, or a member
182
183
  retired since the sync). It is filed under Other, and saying which value was
183
184
  declared is what lets the author fix their frontmatter. -->
184
- <p v-if="s.declaredGroup" class="mt-1 text-[11px] text-app-warning-400">
185
+ <p v-if="s.declaredGroup" class="mt-1 text-2xs text-app-warning-400">
185
186
  {{ t('skills.catalog.groupUnknown', { group: s.declaredGroup }) }}
186
187
  </p>
187
- <p class="mt-1 flex flex-wrap gap-x-3 text-[11px] text-dimmed">
188
+ <p class="mt-1 flex flex-wrap gap-x-3 text-2xs text-dimmed">
188
189
  <span v-if="s.resources.length">
189
190
  {{ t('skills.catalog.resources', { count: s.resources.length }) }}
190
191
  </span>
@@ -234,29 +235,29 @@ async function unlinkSource(id: string) {
234
235
  {{ t('skills.sources.changes') }}
235
236
  </UBadge>
236
237
  <div class="ms-auto flex gap-1">
237
- <UButton
238
+ <IconButton
238
239
  icon="i-lucide-search-check"
239
240
  size="xs"
240
241
  variant="ghost"
241
242
  :loading="rowBusy(`check:${s.id}`)"
242
- :title="t('skills.sources.check')"
243
+ :label="t('skills.sources.check')"
243
244
  @click="checkSource(s.id)"
244
245
  />
245
- <UButton
246
+ <IconButton
246
247
  icon="i-lucide-refresh-cw"
247
248
  size="xs"
248
249
  variant="ghost"
249
250
  :loading="rowBusy(`sync:${s.id}`)"
250
- :title="t('skills.sources.sync')"
251
+ :label="t('skills.sources.sync')"
251
252
  @click="syncSource(s.id)"
252
253
  />
253
- <UButton
254
+ <IconButton
254
255
  icon="i-lucide-unplug"
255
256
  size="xs"
256
257
  color="error"
257
258
  variant="ghost"
258
259
  :loading="rowBusy(`unlink:${s.id}`)"
259
- :title="t('skills.sources.unlink')"
260
+ :label="t('skills.sources.unlink')"
260
261
  @click="unlinkSource(s.id)"
261
262
  />
262
263
  </div>
@@ -16,6 +16,7 @@ import {
16
16
  } from '~/utils/slackMemberMapping'
17
17
  import IntegrationBackTitle from '~/components/layout/IntegrationBackTitle.vue'
18
18
  import SecretInput from '~/components/common/SecretInput.vue'
19
+ import SectionLabel from '~/components/common/SectionLabel.vue'
19
20
 
20
21
  const ui = useUiStore()
21
22
  const slack = useSlackStore()
@@ -55,6 +56,8 @@ const ROLE_OPTIONS = computed<{ label: string; value: SlackMemberRole }[]>(() =>
55
56
  ])
56
57
 
57
58
  // Local editable copies, synced from the store on load.
59
+ const channelSuggestions = computed(() => slack.channels.map((ch) => `#${ch.name}`))
60
+
58
61
  const routes = reactive<Record<NotificationType, SlackRoute>>({
59
62
  merge_review: { enabled: false, channel: '' },
60
63
  pipeline_complete: { enabled: false, channel: '' },
@@ -224,9 +227,9 @@ async function saveMapping() {
224
227
  {{ t('slack.connect.addToSlack') }}
225
228
  </UButton>
226
229
  <div class="space-y-1">
227
- <span class="block text-[10px] uppercase tracking-wide text-dimmed">
230
+ <SectionLabel as="span" class="block">
228
231
  {{ t('slack.connect.orPasteToken') }}
229
- </span>
232
+ </SectionLabel>
230
233
  <div class="flex gap-2">
231
234
  <SecretInput v-model="tokenInput" size="sm" class="flex-1" placeholder="xoxb-…" />
232
235
  <UButton
@@ -267,9 +270,9 @@ async function saveMapping() {
267
270
 
268
271
  <!-- routing -->
269
272
  <div class="space-y-3">
270
- <p class="text-[11px] font-semibold uppercase tracking-wide text-muted">
273
+ <SectionLabel as="p">
271
274
  {{ t('slack.routing.heading') }}
272
- </p>
275
+ </SectionLabel>
273
276
  <div
274
277
  v-for="row in ROUTABLE"
275
278
  :key="row.type"
@@ -277,18 +280,20 @@ async function saveMapping() {
277
280
  >
278
281
  <USwitch v-model="routes[row.type]!.enabled" size="sm" />
279
282
  <span class="w-32 text-sm text-toned">{{ row.label }}</span>
280
- <UInput
283
+ <!-- The workspace's channels are suggestions, not a closed list: a route may name
284
+ a channel the bot cannot enumerate yet. `mode="autocomplete"` is what makes the
285
+ typed name the value: the DEFAULT combobox mode writes its model only when
286
+ something is SELECTED, so a channel typed and then saved would be dropped. -->
287
+ <UInputMenu
281
288
  v-model="routes[row.type]!.channel"
289
+ mode="autocomplete"
290
+ :items="channelSuggestions"
282
291
  size="sm"
283
292
  class="flex-1"
284
293
  :placeholder="t('slack.routing.channelPlaceholder')"
285
294
  :disabled="!routes[row.type]!.enabled"
286
- list="slack-channels"
287
295
  />
288
296
  </div>
289
- <datalist id="slack-channels">
290
- <option v-for="ch in slack.channels" :key="ch.id" :value="`#${ch.name}`" />
291
- </datalist>
292
297
 
293
298
  <label class="flex items-center gap-2">
294
299
  <USwitch v-model="mentionsEnabled" size="sm" />
@@ -311,10 +316,10 @@ async function saveMapping() {
311
316
 
312
317
  <!-- member mapping -->
313
318
  <div v-if="mentionsEnabled" class="space-y-2">
314
- <p class="text-[11px] font-semibold uppercase tracking-wide text-muted">
319
+ <SectionLabel as="p">
315
320
  {{ t('slack.members.heading') }}
316
- </p>
317
- <p class="text-[11px] leading-snug text-dimmed">
321
+ </SectionLabel>
322
+ <p class="text-2xs leading-snug text-dimmed">
318
323
  <i18n-t keypath="slack.members.hint" tag="span">
319
324
  <template #product>
320
325
  <span class="font-medium text-muted">{{ t('slack.members.productLabel') }}</span>
@@ -22,6 +22,7 @@ import {
22
22
  type RequirementStateFilter,
23
23
  } from './ServiceSpecWindow.logic'
24
24
  import type { BadgeColor } from '~/utils/badge'
25
+ import SectionLabel from '~/components/common/SectionLabel.vue'
25
26
 
26
27
  const { t } = useI18n()
27
28
  const board = useBoardStore()
@@ -285,14 +286,15 @@ function kindLabel(item: RequirementItem): string {
285
286
  {{ t('spec.overview') }}
286
287
  </UButton>
287
288
  <div v-for="(mod, mi) in modules" :key="mi" class="mb-3">
288
- <div class="px-2 pb-1 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
289
+ <SectionLabel class="px-2 pb-1">
289
290
  {{ mod.name }}
290
- </div>
291
+ </SectionLabel>
291
292
  <ul class="space-y-0.5">
292
293
  <li v-for="(group, gi) in mod.groups ?? []" :key="gi">
293
- <button
294
- type="button"
295
- class="flex w-full items-center justify-between gap-2 rounded-md px-2 py-1.5 text-start text-[13px] transition"
294
+ <UButton
295
+ color="neutral"
296
+ variant="ghost"
297
+ class="flex w-full items-center justify-between gap-2 rounded-md px-2 py-1.5 text-start text-sm transition"
296
298
  :class="
297
299
  selected?.m === mi && selected?.g === gi
298
300
  ? 'bg-primary/15 text-primary'
@@ -301,12 +303,12 @@ function kindLabel(item: RequirementItem): string {
301
303
  @click="selectGroup(mi, gi)"
302
304
  >
303
305
  <span class="truncate">{{ group.name }}</span>
304
- <span class="shrink-0 text-[10px] text-dimmed">{{ reqCount(group) }}</span>
305
- </button>
306
+ <span class="shrink-0 text-3xs text-dimmed">{{ reqCount(group) }}</span>
307
+ </UButton>
306
308
  </li>
307
309
  <li
308
310
  v-if="(mod.groups?.length ?? 0) === 0"
309
- class="px-2 py-1 text-[11px] italic text-app-600"
311
+ class="px-2 py-1 text-2xs italic text-app-600"
310
312
  >
311
313
  {{ t('spec.noFeatureGroups') }}
312
314
  </li>
@@ -352,9 +354,9 @@ function kindLabel(item: RequirementItem): string {
352
354
 
353
355
  <!-- selected feature group -->
354
356
  <template v-else-if="selectedGroup">
355
- <div class="mb-1 text-[11px] uppercase tracking-wide text-dimmed">
357
+ <SectionLabel class="mb-1">
356
358
  {{ selectedModule?.name }}
357
- </div>
359
+ </SectionLabel>
358
360
  <h2 class="text-lg font-semibold text-highlighted">{{ selectedGroup.name }}</h2>
359
361
  <p v-if="selectedGroup.summary" class="mt-1 max-w-3xl text-sm text-muted">
360
362
  {{ selectedGroup.summary }}
@@ -364,7 +366,7 @@ function kindLabel(item: RequirementItem): string {
364
366
  <template v-if="mode === 'gherkin'">
365
367
  <pre
366
368
  v-if="selectedFeature"
367
- class="mt-4 overflow-x-auto rounded-lg border border-default bg-app-950/60 p-4 text-[12.5px] leading-relaxed text-default"
369
+ class="mt-4 overflow-x-auto rounded-lg border border-default bg-app-950/60 p-4 text-xs leading-relaxed text-default"
368
370
  ><code>{{ selectedFeature.content }}</code></pre>
369
371
  <div
370
372
  v-else
@@ -385,7 +387,7 @@ function kindLabel(item: RequirementItem): string {
385
387
  class="mt-4 flex flex-wrap items-center justify-between gap-2"
386
388
  data-testid="spec-state-filter"
387
389
  >
388
- <div class="flex items-center gap-1.5 text-[11px] text-dimmed">
390
+ <div class="flex items-center gap-1.5 text-2xs text-dimmed">
389
391
  <UIcon
390
392
  :name="STATE_META.established.icon"
391
393
  class="h-3.5 w-3.5 text-app-success-400"
@@ -466,7 +468,7 @@ function kindLabel(item: RequirementItem): string {
466
468
  <UBadge color="neutral" variant="subtle" size="sm">{{ kindLabel(req) }}</UBadge>
467
469
  </div>
468
470
  </div>
469
- <p class="mt-1.5 whitespace-pre-line text-[13px] leading-relaxed text-toned">
471
+ <p class="mt-1.5 whitespace-pre-line text-sm leading-relaxed text-toned">
470
472
  {{ req.statement }}
471
473
  </p>
472
474
  <!-- acceptance criteria (Given/When/Then) -->
@@ -474,7 +476,7 @@ function kindLabel(item: RequirementItem): string {
474
476
  <div
475
477
  v-for="ac in req.acceptance ?? []"
476
478
  :key="ac.id"
477
- class="rounded-md border border-default bg-app-950/50 px-3 py-2 text-[12.5px] leading-relaxed"
479
+ class="rounded-md border border-default bg-app-950/50 px-3 py-2 text-xs leading-relaxed"
478
480
  >
479
481
  <p class="text-toned">
480
482
  <span class="font-semibold text-app-success-400">{{
@@ -501,17 +503,15 @@ function kindLabel(item: RequirementItem): string {
501
503
 
502
504
  <!-- domain rules / invariants scoped to this group -->
503
505
  <div v-if="(selectedGroup.rules?.length ?? 0) > 0" class="mt-6">
504
- <div
505
- class="mb-2 flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted"
506
- >
506
+ <SectionLabel class="mb-2 flex items-center gap-1.5">
507
507
  <UIcon name="i-lucide-shield-check" class="h-3.5 w-3.5" />
508
508
  {{ t('spec.domainRules') }}
509
- </div>
509
+ </SectionLabel>
510
510
  <ul class="max-w-3xl space-y-1.5">
511
511
  <li
512
512
  v-for="rule in selectedGroup.rules ?? []"
513
513
  :key="rule.id"
514
- class="rounded-md border border-default bg-default/60 px-3 py-2 text-[13px] text-toned"
514
+ class="rounded-md border border-default bg-default/60 px-3 py-2 text-sm text-toned"
515
515
  >
516
516
  {{ rule.rule }}
517
517
  <span v-if="rule.rationale" class="text-dimmed">{{