@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
@@ -6,6 +6,7 @@
6
6
  // flag); the hard `blocked` tier does not. Driven by `ui.reviewFrictionContext`, mounted in
7
7
  // pages/index.vue. See backend/docs/review-debt-friction.md.
8
8
  import { computed } from 'vue'
9
+ import SectionLabel from '~/components/common/SectionLabel.vue'
9
10
 
10
11
  const { t } = useI18n()
11
12
  const ui = useUiStore()
@@ -84,13 +85,14 @@ function createAnyway() {
84
85
  <p class="text-sm text-toned">{{ body }}</p>
85
86
 
86
87
  <div class="rounded-lg border border-muted bg-default/50 p-2">
87
- <p class="mb-1.5 px-1 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
88
+ <SectionLabel as="p" class="mb-1.5 px-1">
88
89
  {{ t('errors.reviewFriction.waitingHeading') }}
89
- </p>
90
+ </SectionLabel>
90
91
  <ul class="space-y-1">
91
92
  <li v-for="item in ctx.debt" :key="item.blockId">
92
- <button
93
- type="button"
93
+ <UButton
94
+ color="neutral"
95
+ variant="ghost"
94
96
  class="flex w-full items-center justify-between gap-3 rounded-md px-2 py-1.5 text-left text-sm hover:bg-elevated/60 disabled:opacity-50"
95
97
  :disabled="pending"
96
98
  @click="goToBlock(item.blockId)"
@@ -98,10 +100,10 @@ function createAnyway() {
98
100
  <span class="truncate text-default">
99
101
  {{ item.title || t('errors.reviewFriction.untitled') }}
100
102
  </span>
101
- <span class="shrink-0 text-[12px] text-dimmed">
103
+ <span class="shrink-0 text-xs text-dimmed">
102
104
  {{ t('errors.reviewFriction.waiting', { minutes: item.waitingMinutes }) }}
103
105
  </span>
104
- </button>
106
+ </UButton>
105
107
  </li>
106
108
  </ul>
107
109
  </div>
@@ -55,7 +55,7 @@ const entries = computed<Entry[]>(() =>
55
55
  >
56
56
  <!-- a superseded SUCCESSFUL attempt: its output, collapsible + copyable -->
57
57
  <template v-if="entry.kind === 'success'">
58
- <div class="flex items-center gap-1.5 text-[10px] text-dimmed">
58
+ <div class="flex items-center gap-1.5 text-3xs text-dimmed">
59
59
  <UIcon name="i-lucide-check-circle-2" class="h-3 w-3 shrink-0 text-app-success-400/70" />
60
60
  <time>{{ d(new Date(entry.occurredAt), 'long') }}</time>
61
61
  <span class="text-app-success-400/80">{{ t('panels.stepDetail.attemptSucceeded') }}</span>
@@ -63,31 +63,31 @@ const entries = computed<Entry[]>(() =>
63
63
  <div class="relative mt-1">
64
64
  <CopyButton :text="entry.output.output" class="absolute end-1 top-1 z-10" />
65
65
  <pre
66
- class="max-h-40 overflow-auto whitespace-pre-wrap rounded bg-app-950/80 p-1.5 pe-9 text-[10px] leading-snug text-toned"
66
+ class="max-h-40 overflow-auto whitespace-pre-wrap rounded-sm bg-app-950/80 p-1.5 pe-9 text-3xs leading-snug text-toned"
67
67
  >{{ entry.output.output }}</pre>
68
68
  </div>
69
- <p v-if="entry.output.truncated" class="mt-1 text-[10px] text-dimmed">
69
+ <p v-if="entry.output.truncated" class="mt-1 text-3xs text-dimmed">
70
70
  {{ t('panels.stepDetail.outputTruncated') }}
71
71
  </p>
72
72
  </template>
73
73
 
74
74
  <!-- a FAILED attempt: mirrors FailureHistoryList's entry markup -->
75
75
  <template v-else>
76
- <div class="flex items-center gap-1.5 text-[10px] text-dimmed">
76
+ <div class="flex items-center gap-1.5 text-3xs text-dimmed">
77
77
  <UIcon name="i-lucide-alert-triangle" class="h-3 w-3 shrink-0 text-app-error-400/70" />
78
78
  <time>{{ d(new Date(entry.occurredAt), 'long') }}</time>
79
79
  </div>
80
- <p class="mt-1 text-[11px] leading-snug text-toned" :title="entry.failure.message">
80
+ <p class="mt-1 text-2xs leading-snug text-toned" :title="entry.failure.message">
81
81
  {{ entry.failure.message }}
82
82
  </p>
83
- <p v-if="entry.failure.hint" class="mt-1 text-[10px] leading-snug text-dimmed">
83
+ <p v-if="entry.failure.hint" class="mt-1 text-3xs leading-snug text-dimmed">
84
84
  {{ entry.failure.hint }}
85
85
  </p>
86
86
  <FailureDetail
87
87
  :detail="entry.failure.detail"
88
88
  :message="entry.failure.message"
89
- summary-class="text-[10px] text-dimmed hover:text-toned"
90
- pre-class="bg-app-950/80 text-[10px] text-muted"
89
+ summary-class="text-3xs text-dimmed hover:text-toned"
90
+ pre-class="bg-app-950/80 text-3xs text-muted"
91
91
  />
92
92
  </template>
93
93
  </li>
@@ -52,7 +52,7 @@ const dim = computed(() => (props.size === 'sm' ? 'h-7 w-7' : 'h-9 w-9'))
52
52
  />
53
53
  </span>
54
54
  </div>
55
- <span v-if="size !== 'sm'" class="text-[10px] leading-none text-toned">
55
+ <span v-if="size !== 'sm'" class="text-3xs leading-none text-toned">
56
56
  {{ archetype.label }}
57
57
  </span>
58
58
  </div>
@@ -9,11 +9,11 @@ import AgentFailureCard from '~/components/board/AgentFailureCard.vue'
9
9
  import AgentStopButton from '~/components/board/AgentStopButton.vue'
10
10
  import AdoptionReviewModal from '~/components/bootstrap/AdoptionReviewModal.vue'
11
11
  import BootstrapRunSteps from '~/components/bootstrap/BootstrapRunSteps.vue'
12
+ import IconButton from '~/components/common/IconButton.vue'
12
13
  import { useBlockDrag } from '~/composables/useBlockDrag'
13
14
  import { useFrameStacking } from '~/composables/useFrameStacking'
14
15
  import { useViewport } from '~/composables/useViewport'
15
16
  import { laneBodyHeightIn } from '~/utils/laneGeometry'
16
-
17
17
  // Vue Flow passes the node's `id` and `data` as props to custom node components.
18
18
  // Only frames are rendered as board nodes; their tasks live inside the card.
19
19
  const props = defineProps<{ id: string }>()
@@ -249,10 +249,10 @@ const ITEM_ICON: Record<string, string> = {
249
249
  }}</span>
250
250
  </div>
251
251
  <div class="px-3 py-3">
252
- <p class="text-[11px] leading-snug text-muted">
252
+ <p class="text-2xs leading-snug text-muted">
253
253
  {{ t('board.frame.accessDenied.hint') }}
254
254
  </p>
255
- <code class="mt-2 block truncate font-mono text-[11px] text-dimmed">{{ block.id }}</code>
255
+ <code class="mt-2 block truncate font-mono text-2xs text-dimmed">{{ block.id }}</code>
256
256
  </div>
257
257
  </div>
258
258
 
@@ -316,9 +316,9 @@ const ITEM_ICON: Record<string, string> = {
316
316
  }}
317
317
  </span>
318
318
  </div>
319
- <div class="mt-1.5 h-1 w-full overflow-hidden rounded bg-app-warning-900/40">
319
+ <div class="mt-1.5 h-1 w-full overflow-hidden rounded-sm bg-app-warning-900/40">
320
320
  <div
321
- class="h-full rounded bg-app-warning-400 transition-all"
321
+ class="h-full rounded-sm bg-app-warning-400 transition-all"
322
322
  :style="{ width: bootstrapPct + '%' }"
323
323
  />
324
324
  </div>
@@ -327,7 +327,7 @@ const ITEM_ICON: Record<string, string> = {
327
327
  <li
328
328
  v-for="(item, i) in bootstrapItems"
329
329
  :key="i"
330
- class="flex items-start gap-1.5 text-[11px]"
330
+ class="flex items-start gap-1.5 text-2xs"
331
331
  :class="
332
332
  item.status === 'completed'
333
333
  ? 'text-app-warning-200/60 line-through'
@@ -430,7 +430,7 @@ const ITEM_ICON: Record<string, string> = {
430
430
  {{ t('board.frame.shared') }}
431
431
  </UBadge>
432
432
  </div>
433
- <div class="text-[11px] text-muted">{{ typeMeta!.label }}</div>
433
+ <div class="text-2xs text-muted">{{ typeMeta!.label }}</div>
434
434
  </div>
435
435
  </div>
436
436
  <div class="flex items-center gap-1">
@@ -441,27 +441,27 @@ const ITEM_ICON: Record<string, string> = {
441
441
  are `board.write`, hidden for a read-only viewer, who keeps the status badge
442
442
  (the one view-only affordance here). -->
443
443
  <template v-if="access.canWriteBoard.value">
444
- <UButton
444
+ <IconButton
445
445
  class="nodrag"
446
446
  data-testid="frame-add-task"
447
447
  :size="isTouch ? 'sm' : 'xs'"
448
448
  variant="ghost"
449
449
  color="neutral"
450
450
  icon="i-lucide-plus"
451
- :title="t('board.frame.addTaskTitle')"
451
+ :label="t('board.frame.addTaskTitle')"
452
452
  @click.stop="addTask"
453
453
  />
454
- <UButton
454
+ <IconButton
455
455
  v-if="tasks.anyOffered"
456
456
  class="nodrag"
457
457
  :size="isTouch ? 'sm' : 'xs'"
458
458
  variant="ghost"
459
459
  color="neutral"
460
460
  icon="i-lucide-ticket"
461
- :title="t('board.frame.createTaskFromIssueTitle')"
461
+ :label="t('board.frame.createTaskFromIssueTitle')"
462
462
  @click.stop="createTaskFromIssue"
463
463
  />
464
- <UButton
464
+ <IconButton
465
465
  v-if="documents.connectedDesignSources.length > 0"
466
466
  class="nodrag"
467
467
  data-testid="frame-start-from-design"
@@ -469,7 +469,7 @@ const ITEM_ICON: Record<string, string> = {
469
469
  variant="ghost"
470
470
  color="neutral"
471
471
  icon="i-lucide-frame"
472
- :title="t('board.frame.startFromDesignTitle')"
472
+ :label="t('board.frame.startFromDesignTitle')"
473
473
  @click.stop="startFromDesign"
474
474
  />
475
475
  <!-- Recurring pipelines + initiatives are ADVANCED-tier authoring: both plan
@@ -480,7 +480,7 @@ const ITEM_ICON: Record<string, string> = {
480
480
  badges its task card and opens its inspector panel, and an initiative is
481
481
  still a block on the board with its own inspector — so what a basic-mode
482
482
  user loses is the ability to author a new one, not sight of one. -->
483
- <UButton
483
+ <IconButton
484
484
  v-if="uiMode.isAdvanced"
485
485
  class="nodrag"
486
486
  data-testid="frame-add-recurring"
@@ -488,10 +488,10 @@ const ITEM_ICON: Record<string, string> = {
488
488
  variant="ghost"
489
489
  color="neutral"
490
490
  icon="i-lucide-repeat"
491
- :title="t('board.frame.addRecurringTitle')"
491
+ :label="t('board.frame.addRecurringTitle')"
492
492
  @click.stop="addRecurring"
493
493
  />
494
- <UButton
494
+ <IconButton
495
495
  v-if="uiMode.isAdvanced"
496
496
  class="nodrag"
497
497
  data-testid="frame-add-initiative"
@@ -499,7 +499,7 @@ const ITEM_ICON: Record<string, string> = {
499
499
  variant="ghost"
500
500
  color="neutral"
501
501
  icon="i-lucide-milestone"
502
- :title="t('board.frame.createInitiativeTitle')"
502
+ :label="t('board.frame.createInitiativeTitle')"
503
503
  @click.stop="createInitiative"
504
504
  />
505
505
  <!-- Hunting a tracker board for a bug worth adopting is TRIAGE, not intake: it
@@ -507,7 +507,7 @@ const ITEM_ICON: Record<string, string> = {
507
507
  engineer/PM judgement call. So it is dropped for a narrowed role, whose three
508
508
  routes in (a new task, a task from a named ticket, a task from a design) all
509
509
  start from work somebody has already decided to do. -->
510
- <UButton
510
+ <IconButton
511
511
  v-if="tasks.anyOffered && uiRole.fullSurface"
512
512
  class="nodrag"
513
513
  data-testid="frame-hunt-bugs"
@@ -515,7 +515,7 @@ const ITEM_ICON: Record<string, string> = {
515
515
  variant="ghost"
516
516
  color="neutral"
517
517
  icon="i-lucide-radar"
518
- :title="t('board.frame.huntBugsTitle')"
518
+ :label="t('board.frame.huntBugsTitle')"
519
519
  @click.stop="huntBugs"
520
520
  />
521
521
  </template>
@@ -530,7 +530,7 @@ const ITEM_ICON: Record<string, string> = {
530
530
  stays is what the canvas can't show at a glance. -->
531
531
  <div
532
532
  v-if="modules.length || prTasks"
533
- class="flex items-center gap-2 text-[10px] uppercase tracking-wide text-dimmed"
533
+ class="flex items-center gap-2 text-3xs uppercase tracking-wide text-dimmed"
534
534
  >
535
535
  <span v-if="modules.length">{{
536
536
  t('board.frame.moduleCount', { count: modules.length }, modules.length)
@@ -560,15 +560,16 @@ const ITEM_ICON: Record<string, string> = {
560
560
 
561
561
  <FrameSwimlanes v-if="hasTasks" :frame-id="block.id" :lane-body-height="laneBodyHeight" />
562
562
 
563
- <button
563
+ <UButton
564
+ color="neutral"
565
+ variant="ghost"
564
566
  v-if="!hasTasks && access.canWriteBoard.value"
565
- type="button"
566
567
  data-testid="frame-add-task-empty"
567
- class="absolute inset-4 flex items-center justify-center gap-1 rounded-lg border border-dashed border-muted text-[11px] text-dimmed hover:border-app-500 hover:text-toned"
568
+ class="absolute inset-4 flex items-center justify-center gap-1 rounded-lg border border-dashed border-muted text-2xs text-dimmed hover:border-app-500 hover:text-toned"
568
569
  @click.stop="addTask"
569
570
  >
570
571
  <UIcon name="i-lucide-plus" class="h-3.5 w-3.5" /> {{ t('board.frame.addFirstTask') }}
571
- </button>
572
+ </UButton>
572
573
  </div>
573
574
  </div>
574
575
 
@@ -16,8 +16,9 @@ const displayLabel = computed(() => props.label ?? t('board.decisionBadge.decisi
16
16
  </script>
17
17
 
18
18
  <template>
19
- <button
20
- type="button"
19
+ <UButton
20
+ color="neutral"
21
+ variant="ghost"
21
22
  class="board-pulse flex items-center gap-1 rounded-full bg-app-warning-500 px-2 py-0.5 text-xs font-semibold text-app-warning-50 dark:text-app-warning-950 shadow-lg transition hover:bg-app-warning-400"
22
23
  @click.stop="$emit('open')"
23
24
  >
@@ -26,5 +27,5 @@ const displayLabel = computed(() => props.label ?? t('board.decisionBadge.decisi
26
27
  <span v-if="count && count > 1" class="rounded-full bg-app-warning-950/30 px-1">
27
28
  {{ count }}
28
29
  </span>
29
- </button>
30
+ </UButton>
30
31
  </template>
@@ -36,10 +36,10 @@ const selected = computed(() => ui.selectedBlockId === props.id)
36
36
  >
37
37
  <div class="flex items-center gap-1.5">
38
38
  <UIcon name="i-lucide-layers" class="h-3.5 w-3.5 shrink-0 text-app-secondary-400" />
39
- <span class="text-[10px] font-semibold uppercase tracking-wide text-app-secondary-300">{{
39
+ <span class="text-3xs font-semibold uppercase tracking-wide text-app-secondary-300">{{
40
40
  t('board.epic.label')
41
41
  }}</span>
42
- <span class="ms-auto text-[10px] text-muted">{{ done }}/{{ total }}</span>
42
+ <span class="ms-auto text-3xs text-muted">{{ done }}/{{ total }}</span>
43
43
  </div>
44
44
  <div class="mt-1 truncate text-sm font-medium text-app-100" :title="block.title">
45
45
  {{ block.title }}
@@ -50,10 +50,10 @@ const selected = computed(() => ui.selectedBlockId === props.id)
50
50
  :style="{ width: total ? `${Math.round((done / total) * 100)}%` : '0%' }"
51
51
  />
52
52
  </div>
53
- <div v-if="active" class="mt-1 text-[10px] text-muted">
53
+ <div v-if="active" class="mt-1 text-3xs text-muted">
54
54
  {{ t('board.epic.activeCount', { count: active }) }}
55
55
  </div>
56
- <div v-else-if="total === 0" class="mt-1 text-[10px] text-dimmed">
56
+ <div v-else-if="total === 0" class="mt-1 text-3xs text-dimmed">
57
57
  {{ t('board.epic.noTasksYet') }}
58
58
  </div>
59
59
  </div>
@@ -67,8 +67,9 @@ const withheldNote = computed(() => {
67
67
  is the one lane whose emptiness says nothing a reader needs (a new service has merged
68
68
  nothing, which its three live lanes already show). -->
69
69
  <div v-if="doneLane && doneLane.total > 0" class="rounded-lg bg-default/40">
70
- <button
71
- type="button"
70
+ <UButton
71
+ color="neutral"
72
+ variant="ghost"
72
73
  class="nodrag flex w-full items-center gap-1.5 rounded-lg px-2 py-1.5 text-left hover:bg-elevated/40"
73
74
  data-testid="done-lane-toggle"
74
75
  :aria-expanded="!laneView.doneLaneCollapsed"
@@ -83,12 +84,12 @@ const withheldNote = computed(() => {
83
84
  class="h-3.5 w-3.5 shrink-0"
84
85
  :style="{ color: LANE_META.done.color }"
85
86
  />
86
- <span class="text-[11px] font-semibold text-default">{{ t(LANE_META.done.labelKey) }}</span>
87
+ <span class="text-2xs font-semibold text-default">{{ t(LANE_META.done.labelKey) }}</span>
87
88
  <!-- The TOTAL, not what the caps admitted: "this service has finished 312 tasks" is the
88
89
  fact the lane carries, and counting only the visible cards would understate it by
89
90
  two orders of magnitude. -->
90
91
  <span
91
- class="shrink-0 rounded px-1 text-[10px] font-semibold tabular-nums"
92
+ class="shrink-0 rounded-sm px-1 text-3xs font-semibold tabular-nums"
92
93
  :style="{
93
94
  backgroundColor: tint(LANE_META.done.color),
94
95
  color: LANE_META.done.color,
@@ -96,10 +97,10 @@ const withheldNote = computed(() => {
96
97
  data-testid="lane-count-done"
97
98
  >{{ doneLane.total }}</span
98
99
  >
99
- <span v-if="withheldNote" class="ms-auto truncate text-[10px] text-dimmed">{{
100
+ <span v-if="withheldNote" class="ms-auto truncate text-3xs text-dimmed">{{
100
101
  withheldNote
101
102
  }}</span>
102
- </button>
103
+ </UButton>
103
104
 
104
105
  <!-- The archive opens as a WIDE GRID: each group takes the strip's full width and wraps its
105
106
  own cards across it, and the groups stack. Wrapping the GROUPS instead read correctly
@@ -116,10 +117,7 @@ const withheldNote = computed(() => {
116
117
  >
117
118
  <!-- A zero cap is a real setting ("count them, show none"), so the strip explains why it
118
119
  has nothing in it rather than looking broken. -->
119
- <p
120
- v-if="doneSelection.shown.length === 0"
121
- class="px-1 text-[10px] leading-snug text-app-600"
122
- >
120
+ <p v-if="doneSelection.shown.length === 0" class="px-1 text-3xs leading-snug text-app-600">
123
121
  {{ t('board.lanes.done.allWithheld') }}
124
122
  </p>
125
123
  <LaneGroup
@@ -82,17 +82,17 @@ function select() {
82
82
  {{ statusLabel }}
83
83
  </UBadge>
84
84
  </div>
85
- <div class="mt-1 text-[10px] uppercase tracking-wide text-primary/70">
85
+ <div class="mt-1 text-3xs uppercase tracking-wide text-primary/70">
86
86
  {{ t('initiative.card.kind') }}
87
87
  </div>
88
88
  <div v-if="progress" class="mt-2 space-y-1">
89
- <div class="h-1.5 overflow-hidden rounded bg-elevated">
89
+ <div class="h-1.5 overflow-hidden rounded-sm bg-elevated">
90
90
  <div
91
- class="h-full rounded bg-primary"
91
+ class="h-full rounded-sm bg-primary"
92
92
  :style="{ width: `${Math.round((progress.settled / progress.total) * 100)}%` }"
93
93
  />
94
94
  </div>
95
- <div class="text-[10px] text-muted">
95
+ <div class="text-3xs text-muted">
96
96
  {{ t('initiative.card.progress', { done: progress.settled, total: progress.total }) }}
97
97
  </div>
98
98
  </div>
@@ -4,6 +4,7 @@ import { MODULE_META, taskTypeMeta } from '~/utils/catalog'
4
4
  import { LANE_GEOMETRY } from '~/utils/laneGeometry'
5
5
  import type { LaneGroup, LaneGroupKey } from '~/utils/laneSort'
6
6
  import { LANE_REASON_LABEL_KEYS, type LaneReason } from '~/utils/swimlanes'
7
+ import SectionLabel from '~/components/common/SectionLabel.vue'
7
8
 
8
9
  /**
9
10
  * One labelled run of cards inside a lane.
@@ -73,14 +74,11 @@ const icon = computed(() => {
73
74
  <div :data-drop-zone="dropZone ?? undefined" class="space-y-1.5">
74
75
  <!-- `none` grouping renders no header: one unlabelled group IS the flat lane, and a header
75
76
  saying "all of them" would be a row of chrome carrying no information. -->
76
- <div
77
- v-if="groupKey !== 'none'"
78
- class="flex items-center gap-1 px-0.5 text-[10px] uppercase tracking-wide text-dimmed"
79
- >
77
+ <SectionLabel v-if="groupKey !== 'none'" class="flex items-center gap-1 px-0.5">
80
78
  <UIcon v-if="icon" :name="icon" class="h-3 w-3 shrink-0" />
81
79
  <span class="truncate" :title="label">{{ label }}</span>
82
80
  <span class="ms-auto shrink-0 tabular-nums">{{ group.entries.length }}</span>
83
- </div>
81
+ </SectionLabel>
84
82
  <div :class="layout === 'grid' ? 'flex flex-wrap items-start gap-2' : 'space-y-1.5'">
85
83
  <LaneTask
86
84
  v-for="entry in group.entries"
@@ -5,6 +5,7 @@ import { STATUS_META, MODULE_META, taskTypeMeta } from '~/utils/catalog'
5
5
  import { composeRunOutcome, hasOutcomeToShow } from '~/utils/runOutcome'
6
6
  import AgentFailureCard from '~/components/board/AgentFailureCard.vue'
7
7
  import TaskPipelineMini from './TaskPipelineMini.vue'
8
+ import IconButton from '~/components/common/IconButton.vue'
8
9
 
9
10
  const props = defineProps<{ taskId: string }>()
10
11
 
@@ -327,7 +328,7 @@ function selectTask() {
327
328
  hover. Renders a built-in OR a deployment-registered custom type via `taskTypeMeta`. -->
328
329
  <span
329
330
  v-if="typeBadge"
330
- class="inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded"
331
+ class="inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-sm"
331
332
  :style="{ color: typeBadge.color, backgroundColor: tint(typeBadge.color) }"
332
333
  :title="typeBadge.label"
333
334
  :data-task-type-badge="task.taskType"
@@ -346,7 +347,7 @@ function selectTask() {
346
347
  "
347
348
  />
348
349
  <span
349
- class="ms-auto truncate text-[9px] uppercase tracking-wide"
350
+ class="ms-auto truncate text-3xs uppercase tracking-wide"
350
351
  :class="
351
352
  runFailed
352
353
  ? 'text-app-error-400'
@@ -360,15 +361,19 @@ function selectTask() {
360
361
  {{ statusText }}
361
362
  </span>
362
363
  <!-- drag-to-connect handle: drag onto another task to make it depend on this one -->
363
- <button
364
- type="button"
365
- class="nodrag shrink-0 cursor-crosshair touch-none rounded-full p-0.5 text-dimmed hover:bg-elevated hover:text-app-warning-400 pointer-coarse:p-2.5"
366
- :title="t('board.task.dragToConnect')"
364
+ <IconButton
365
+ color="neutral"
366
+ variant="ghost"
367
+ icon="i-lucide-spline"
368
+ class="nodrag shrink-0"
369
+ :label="t('board.task.dragToConnect')"
370
+ :ui="{
371
+ base: 'cursor-crosshair touch-none rounded-full p-0.5 text-dimmed hover:bg-elevated hover:text-app-warning-400 pointer-coarse:p-2.5',
372
+ leadingIcon: 'h-3 w-3 pointer-coarse:h-5 pointer-coarse:w-5',
373
+ }"
367
374
  @pointerdown.stop="startConnect(task.id, $event)"
368
375
  @click.stop
369
- >
370
- <UIcon name="i-lucide-spline" class="h-3 w-3 pointer-coarse:h-5 pointer-coarse:w-5" />
371
- </button>
376
+ />
372
377
  </div>
373
378
 
374
379
  <!-- title gets a full-width row so long titles wrap to two lines rather than
@@ -379,7 +384,7 @@ function selectTask() {
379
384
  title is the one always-rendered part of the body that no control can occupy. -->
380
385
  <div
381
386
  data-testid="task-title"
382
- class="mt-1 line-clamp-2 break-words text-[11px] font-semibold leading-snug text-app-100"
387
+ class="mt-1 line-clamp-2 break-words text-2xs font-semibold leading-snug text-app-100"
383
388
  :title="task.title"
384
389
  >
385
390
  {{ task.title }}
@@ -415,7 +420,7 @@ function selectTask() {
415
420
  <span
416
421
  v-for="d in deps"
417
422
  :key="d.id"
418
- class="inline-flex items-center gap-0.5 rounded bg-elevated/80 px-1 py-0.5 text-[9px]"
423
+ class="inline-flex items-center gap-0.5 rounded-sm bg-elevated/80 px-1 py-0.5 text-3xs"
419
424
  :class="d.status === 'done' ? 'text-muted' : 'text-app-warning-300'"
420
425
  :title="depLabel(d)"
421
426
  >
@@ -431,7 +436,7 @@ function selectTask() {
431
436
  <div class="nodrag mt-2 flex flex-wrap items-center gap-1">
432
437
  <!-- a reviewer gate folding/re-reviewing in the background: a working indicator,
433
438
  NOT a gate — the human is back on the board and summoned only if input is needed -->
434
- <span v-if="reviewStage" class="inline-flex items-center gap-1 text-[9px] text-primary">
439
+ <span v-if="reviewStage" class="inline-flex items-center gap-1 text-3xs text-primary">
435
440
  <UIcon name="i-lucide-loader-circle" class="h-3 w-3 animate-spin" />
436
441
  {{ reviewStageLabel }}
437
442
  </span>
@@ -481,7 +486,7 @@ function selectTask() {
481
486
  </UButton>
482
487
  <span
483
488
  v-if="runnable && defaultPipeline"
484
- class="inline-flex items-center gap-0.5 text-[9px] text-dimmed"
489
+ class="inline-flex items-center gap-0.5 text-3xs text-dimmed"
485
490
  >
486
491
  <UIcon name="i-lucide-workflow" class="h-2.5 w-2.5" />{{ defaultPipeline.name }}
487
492
  </span>
@@ -539,7 +544,7 @@ function selectTask() {
539
544
  <!-- A merged task is the one people come back to READ, so its result stays openable
540
545
  rather than collapsing to a tick the moment it lands. -->
541
546
  <template v-else-if="task.status === 'done'">
542
- <span class="inline-flex items-center gap-1 text-[9px] text-app-success-400">
547
+ <span class="inline-flex items-center gap-1 text-3xs text-app-success-400">
543
548
  <UIcon name="i-lucide-check-check" class="h-3 w-3" /> {{ t('board.task.implemented') }}
544
549
  </span>
545
550
  <UButton
@@ -565,7 +570,7 @@ function selectTask() {
565
570
  class="mt-2 flex flex-wrap items-center gap-1 border-t border-default pt-2"
566
571
  >
567
572
  <span
568
- class="inline-flex items-center gap-1 rounded bg-app-secondary-500/15 px-1.5 py-0.5 text-[9px] text-app-secondary-200"
573
+ class="inline-flex items-center gap-1 rounded-sm bg-app-secondary-500/15 px-1.5 py-0.5 text-3xs text-app-secondary-200"
569
574
  :title="t('board.task.module', { name: task.moduleName })"
570
575
  >
571
576
  <UIcon :name="MODULE_META.icon" class="h-3 w-3" :style="{ color: MODULE_META.color }" />
@@ -46,9 +46,9 @@ const isEmpty = computed(() => props.rendered.groups.every((g) => g.entries.leng
46
46
  :style="{ borderColor: tint(meta.color, 20) }"
47
47
  >
48
48
  <UIcon :name="meta.icon" class="h-3.5 w-3.5 shrink-0" :style="{ color: meta.color }" />
49
- <span class="truncate text-[11px] font-semibold text-default">{{ t(meta.labelKey) }}</span>
49
+ <span class="truncate text-2xs font-semibold text-default">{{ t(meta.labelKey) }}</span>
50
50
  <span
51
- class="ms-auto shrink-0 rounded px-1 text-[10px] font-semibold tabular-nums"
51
+ class="ms-auto shrink-0 rounded-sm px-1 text-3xs font-semibold tabular-nums"
52
52
  :style="{ backgroundColor: tint(meta.color), color: meta.color }"
53
53
  :data-testid="`lane-count-${rendered.lane}`"
54
54
  >{{ rendered.total }}</span
@@ -65,7 +65,7 @@ const isEmpty = computed(() => props.rendered.groups.every((g) => g.entries.leng
65
65
  <!-- An empty lane SAYS it is empty. Left blank, "nothing needs you" and "the lane failed
66
66
  to render" look identical, and the first is worth stating: it is the answer a reader
67
67
  scanning the needs-you column is hoping for. -->
68
- <p v-if="isEmpty" class="px-1 pt-2 text-[10px] leading-snug text-app-600">
68
+ <p v-if="isEmpty" class="px-1 pt-2 text-3xs leading-snug text-app-600">
69
69
  {{ t(meta.emptyKey) }}
70
70
  </p>
71
71
  <LaneGroup