@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
@@ -64,6 +64,12 @@ const canChat = computed(() => awaiting.value && !chatBudgetSpent.value && !fork
64
64
 
65
65
  // The human's selection: a proposed fork id, or the sentinel 'custom' for the free-text path.
66
66
  const selected = ref<string | null>(null)
67
+ // The proposed forks and "my own approach" are one choice, so they are one item list. `fork` is
68
+ // null on the custom entry, which is what the label and description slots branch on.
69
+ const forkItems = computed(() => [
70
+ ...forks.value.map((fork) => ({ value: fork.id, label: fork.title, fork })),
71
+ { value: 'custom', label: t('forkDecision.custom.title'), fork: null as ForkOption | null },
72
+ ])
67
73
  const customText = ref('')
68
74
  const note = ref('')
69
75
  const chatInput = ref('')
@@ -166,7 +172,7 @@ const { requestClose } = useUnsavedGuard({
166
172
  >
167
173
  <UIcon name="i-lucide-loader-circle" class="h-8 w-8 animate-spin opacity-60" />
168
174
  <p class="text-sm">{{ t('forkDecision.proposing.title') }}</p>
169
- <p class="max-w-sm text-[11px] text-dimmed">
175
+ <p class="max-w-sm text-2xs text-dimmed">
170
176
  {{ t('forkDecision.proposing.hint') }}
171
177
  </p>
172
178
  </div>
@@ -176,10 +182,10 @@ const { requestClose } = useUnsavedGuard({
176
182
  v-else-if="status === 'single_path'"
177
183
  class="rounded-xl border border-default bg-default/60 px-4 py-3 text-toned"
178
184
  >
179
- <p class="text-[13px] font-medium text-app-100">
185
+ <p class="text-sm font-medium text-app-100">
180
186
  {{ t('forkDecision.singlePath.title') }}
181
187
  </p>
182
- <p v-if="state?.singlePathReason" class="mt-1 text-[12px]">
188
+ <p v-if="state?.singlePathReason" class="mt-1 text-xs">
183
189
  {{ state.singlePathReason }}
184
190
  </p>
185
191
  </div>
@@ -189,16 +195,16 @@ const { requestClose } = useUnsavedGuard({
189
195
  v-else-if="status === 'chosen'"
190
196
  class="rounded-xl border border-app-secondary-500/40 bg-default/60 px-4 py-3 text-toned"
191
197
  >
192
- <p class="text-[13px] font-medium text-app-secondary-200">
198
+ <p class="text-sm font-medium text-app-secondary-200">
193
199
  {{ t('forkDecision.chosen.title') }}
194
200
  </p>
195
- <p v-if="state?.chosen?.custom" class="mt-1 whitespace-pre-wrap text-[12px]">
201
+ <p v-if="state?.chosen?.custom" class="mt-1 whitespace-pre-wrap text-xs">
196
202
  {{ state.chosen.custom }}
197
203
  </p>
198
- <p v-else-if="state?.chosen?.forkId" class="mt-1 text-[12px]">
204
+ <p v-else-if="state?.chosen?.forkId" class="mt-1 text-xs">
199
205
  {{ forks.find((f) => f.id === state?.chosen?.forkId)?.title }}
200
206
  </p>
201
- <p v-if="state?.chosen?.note" class="mt-1 text-[11px] text-muted">
207
+ <p v-if="state?.chosen?.note" class="mt-1 text-2xs text-muted">
202
208
  {{ t('forkDecision.chosen.note', { note: state.chosen.note }) }}
203
209
  </p>
204
210
  </div>
@@ -207,110 +213,90 @@ const { requestClose } = useUnsavedGuard({
207
213
  <div v-else-if="interactive" class="space-y-3">
208
214
  <p
209
215
  v-if="forkDecision.error"
210
- class="rounded-md bg-app-error-500/10 px-3 py-2 text-[12px] text-app-error-300"
216
+ class="rounded-md bg-app-error-500/10 px-3 py-2 text-xs text-app-error-300"
211
217
  >
212
218
  {{ forkDecision.error }}
213
219
  </p>
214
220
 
215
- <p
216
- v-if="state?.seamSummary"
217
- class="rounded-md bg-elevated/50 px-3 py-2 text-[12px] text-toned"
218
- >
221
+ <p v-if="state?.seamSummary" class="rounded-md bg-elevated/50 px-3 py-2 text-xs text-toned">
219
222
  <span class="text-dimmed">{{ t('forkDecision.seam') }}</span>
220
223
  {{ state.seamSummary }}
221
224
  </p>
222
225
 
223
- <!-- Proposed fork cards -->
224
- <article
225
- v-for="fork in forks"
226
- :key="fork.id"
227
- data-testid="fork-option-card"
228
- class="cursor-pointer rounded-xl border px-4 py-3 transition"
229
- :class="
230
- selected === fork.id
231
- ? 'border-app-secondary-500/70 bg-app-secondary-500/5'
232
- : 'border-default bg-default/60 hover:border-muted'
233
- "
234
- @click="selected = fork.id"
226
+ <!-- The proposed forks and "my own approach" are ONE choice, so they are one radio
227
+ group rather than a set of cards each holding their own radio. `variant="card"`
228
+ is the shape these already had; the rich body rides the description slot. -->
229
+ <URadioGroup
230
+ :model-value="selected ?? undefined"
231
+ color="secondary"
232
+ variant="card"
233
+ :items="forkItems"
234
+ data-testid="fork-options"
235
+ :ui="{ item: 'px-4 py-3' }"
236
+ @update:model-value="selected = String($event)"
235
237
  >
236
- <div class="flex items-start gap-2">
237
- <input
238
- type="radio"
239
- class="mt-1 accent-app-secondary-500"
240
- :checked="selected === fork.id"
241
- @change="selected = fork.id"
242
- />
243
- <div class="min-w-0 flex-1">
244
- <div class="flex items-center gap-2">
245
- <h3 class="min-w-0 flex-1 text-[13px] font-medium text-app-100">
246
- {{ fork.title }}
247
- </h3>
248
- <UBadge v-if="fork.recommended" color="primary" variant="subtle" size="sm">
249
- {{ t('forkDecision.recommended') }}
250
- </UBadge>
251
- </div>
252
- <p v-if="fork.summary" class="mt-0.5 text-[12px] text-muted">
253
- {{ fork.summary }}
254
- </p>
255
- <p class="mt-1.5 whitespace-pre-wrap text-[12px] text-toned">
256
- {{ fork.approach }}
257
- </p>
258
- <ul v-if="fork.tradeoffs.length" class="mt-1.5 space-y-0.5">
259
- <li
260
- v-for="(tr, i) in fork.tradeoffs"
238
+ <template #label="{ item }">
239
+ <span class="flex items-center gap-2">
240
+ <span class="min-w-0 flex-1 text-sm font-medium text-app-100">{{ item.label }}</span>
241
+ <UBadge v-if="item.fork?.recommended" color="primary" variant="subtle" size="sm">
242
+ {{ t('forkDecision.recommended') }}
243
+ </UBadge>
244
+ </span>
245
+ </template>
246
+ <template #description="{ item }">
247
+ <template v-if="item.fork">
248
+ <span v-if="item.fork.summary" class="block text-xs text-muted">
249
+ {{ item.fork.summary }}
250
+ </span>
251
+ <span class="mt-1.5 block whitespace-pre-wrap text-xs text-toned">
252
+ {{ item.fork.approach }}
253
+ </span>
254
+ <span v-if="item.fork.tradeoffs.length" class="mt-1.5 block space-y-0.5">
255
+ <span
256
+ v-for="(tr, i) in item.fork.tradeoffs"
261
257
  :key="i"
262
- class="flex gap-1.5 text-[11px] text-muted"
258
+ class="flex gap-1.5 text-2xs text-muted"
263
259
  >
264
260
  <span class="text-app-600">•</span>{{ tr }}
265
- </li>
266
- </ul>
267
- <p v-if="fork.riskNotes" class="mt-1.5 text-[11px] text-app-warning-300/90">
261
+ </span>
262
+ </span>
263
+ <span
264
+ v-if="item.fork.riskNotes"
265
+ class="mt-1.5 block text-2xs text-app-warning-300/90"
266
+ >
268
267
  <span class="text-app-warning-500/70">{{ t('forkDecision.riskNotes') }}</span>
269
- {{ fork.riskNotes }}
270
- </p>
271
- </div>
272
- </div>
273
- </article>
274
-
275
- <!-- Custom approach -->
276
- <article
277
- class="rounded-xl border px-4 py-3 transition"
278
- :class="
279
- selected === 'custom'
280
- ? 'border-app-secondary-500/70 bg-app-secondary-500/5'
281
- : 'border-default bg-default/60'
282
- "
283
- >
284
- <label class="flex cursor-pointer items-center gap-2" @click="selected = 'custom'">
285
- <input type="radio" class="accent-app-secondary-500" :checked="selected === 'custom'" />
286
- <span class="text-[13px] font-medium text-app-100">{{
287
- t('forkDecision.custom.title')
288
- }}</span>
289
- </label>
290
- <textarea
291
- v-model="customText"
292
- data-testid="fork-custom-input"
293
- rows="3"
294
- :placeholder="t('forkDecision.custom.placeholder')"
295
- class="mt-2 w-full resize-y rounded-md border border-muted bg-app-950/60 px-2.5 py-1.5 text-[12px] text-app-100 placeholder:text-app-600 focus:border-app-secondary-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-app-secondary-500/60"
296
- @focus="selected = 'custom'"
297
- />
298
- </article>
268
+ {{ item.fork.riskNotes }}
269
+ </span>
270
+ </template>
271
+ <UTextarea
272
+ v-else
273
+ v-model="customText"
274
+ data-testid="fork-custom-input"
275
+ :rows="3"
276
+ size="xs"
277
+ class="mt-2 w-full"
278
+ :ui="{ base: 'resize-y' }"
279
+ :placeholder="t('forkDecision.custom.placeholder')"
280
+ @focus="selected = 'custom'"
281
+ />
282
+ </template>
283
+ </URadioGroup>
299
284
 
300
285
  <!-- Optional steering note -->
301
286
  <div>
302
- <label class="mb-1 block text-[11px] text-muted">{{ t('forkDecision.noteLabel') }}</label>
303
- <input
304
- v-model="note"
305
- type="text"
306
- :placeholder="t('forkDecision.notePlaceholder')"
307
- class="w-full rounded-md border border-muted bg-app-950/60 px-2.5 py-1.5 text-[12px] text-app-100 placeholder:text-app-600 focus:border-app-secondary-500 focus:outline-none"
308
- />
287
+ <UFormField size="xs" :label="t('forkDecision.noteLabel')">
288
+ <UInput
289
+ v-model="note"
290
+ size="xs"
291
+ class="w-full"
292
+ :placeholder="t('forkDecision.notePlaceholder')"
293
+ />
294
+ </UFormField>
309
295
  </div>
310
296
 
311
297
  <!-- Grounded chat: ask about the forks before deciding. -->
312
298
  <section class="rounded-xl border border-default bg-default/40 px-4 py-3">
313
- <p class="text-[11px] font-medium text-muted">
299
+ <p class="text-2xs font-medium text-muted">
314
300
  {{ t('forkDecision.chat.title') }}
315
301
  </p>
316
302
  <div v-if="chat.length || answering" class="mt-2 max-h-64 space-y-2 overflow-y-auto pr-1">
@@ -322,7 +308,7 @@ const { requestClose } = useUnsavedGuard({
322
308
  :class="msg.role === 'human' ? 'justify-end' : 'justify-start'"
323
309
  >
324
310
  <p
325
- class="max-w-[85%] whitespace-pre-wrap rounded-lg px-3 py-1.5 text-[12px]"
311
+ class="max-w-[85%] whitespace-pre-wrap rounded-lg px-3 py-1.5 text-xs"
326
312
  :class="
327
313
  msg.role === 'human'
328
314
  ? 'bg-app-secondary-500/15 text-app-secondary-100'
@@ -334,29 +320,31 @@ const { requestClose } = useUnsavedGuard({
334
320
  </div>
335
321
  <div v-if="answering" class="flex justify-start">
336
322
  <p
337
- class="flex items-center gap-1.5 rounded-lg bg-elevated/70 px-3 py-1.5 text-[12px] text-muted"
323
+ class="flex items-center gap-1.5 rounded-lg bg-elevated/70 px-3 py-1.5 text-xs text-muted"
338
324
  >
339
325
  <UIcon name="i-lucide-loader-circle" class="h-3.5 w-3.5 animate-spin" />
340
326
  {{ t('forkDecision.chat.thinking') }}
341
327
  </p>
342
328
  </div>
343
329
  </div>
344
- <p v-else class="mt-1 text-[11px] text-dimmed">
330
+ <p v-else class="mt-1 text-2xs text-dimmed">
345
331
  {{ t('forkDecision.chat.hint') }}
346
332
  </p>
347
333
  <div class="mt-2 flex items-end gap-2">
348
- <textarea
334
+ <UTextarea
349
335
  v-model="chatInput"
350
336
  data-testid="fork-chat-input"
351
- rows="2"
337
+ :rows="2"
352
338
  :disabled="!canChat"
353
339
  :placeholder="
354
340
  chatBudgetSpent
355
341
  ? t('forkDecision.chat.budgetSpent')
356
342
  : t('forkDecision.chat.placeholder')
357
343
  "
358
- class="min-h-0 flex-1 resize-y rounded-md border border-muted bg-app-950/60 px-2.5 py-1.5 text-[12px] text-app-100 placeholder:text-app-600 focus:border-app-secondary-500 focus:outline-none disabled:opacity-50"
359
344
  @keydown.enter.exact.prevent="onSend"
345
+ size="xs"
346
+ class="min-h-0 flex-1"
347
+ :ui="{ base: 'resize-y' }"
360
348
  />
361
349
  <UButton
362
350
  data-testid="fork-chat-send"
@@ -25,7 +25,7 @@ const { t, n } = useI18n()
25
25
 
26
26
  <template>
27
27
  <div v-if="contracts.length" class="mt-1 flex flex-col gap-1">
28
- <div v-for="c in contracts" :key="c.contractId" class="text-[11px] text-dimmed">
28
+ <div v-for="c in contracts" :key="c.contractId" class="text-2xs text-dimmed">
29
29
  <span class="text-muted">{{ c.title }}</span>
30
30
  <span class="ms-1">({{ formatLabel[c.format] }}, {{ n(c.size) }})</span>
31
31
  <span v-if="c.operations.length" class="ms-1 font-mono text-dimmed">
@@ -42,7 +42,7 @@ const { t, n } = useI18n()
42
42
  </span>
43
43
  </div>
44
44
  </div>
45
- <p v-else class="mt-1 text-[11px] text-app-warning-500/80">
45
+ <p v-else class="mt-1 text-2xs text-app-warning-500/80">
46
46
  {{ t('foundational.contracts.none') }}
47
47
  </p>
48
48
  </template>
@@ -19,6 +19,7 @@ import { useBusyRows } from '~/composables/useBusyRows'
19
19
  import type { ApiContractFormat, FoundationalServiceTier } from '~/types/domain'
20
20
  import { useFoundationalServicesStore } from '~/stores/foundationalServices'
21
21
  import FoundationalContractSummary from '~/components/foundational/FoundationalContractSummary.vue'
22
+ import IconButton from '~/components/common/IconButton.vue'
22
23
 
23
24
  const catalog = useFoundationalServicesStore()
24
25
  const toast = useToast()
@@ -92,7 +93,7 @@ async function suppress(serviceId: string) {
92
93
  <div class="min-w-0 flex-1">
93
94
  <p class="truncate text-sm font-medium text-app-100">
94
95
  {{ s.name }}
95
- <code class="ms-1 text-[11px] text-dimmed">{{ s.id }}</code>
96
+ <code class="ms-1 text-2xs text-dimmed">{{ s.id }}</code>
96
97
  </p>
97
98
  <p class="text-xs text-muted">{{ s.summary }}</p>
98
99
  <div v-if="s.capabilities.length" class="mt-1 flex flex-wrap gap-1">
@@ -124,11 +125,11 @@ async function suppress(serviceId: string) {
124
125
  :key="doc.contractId"
125
126
  class="rounded-md border border-default bg-app-950/60 p-2"
126
127
  >
127
- <p class="mb-1 text-[11px] text-muted">
128
+ <p class="mb-1 text-2xs text-muted">
128
129
  {{ doc.title }}
129
130
  <span v-if="doc.path" class="ms-1 font-mono text-app-600">{{ doc.path }}</span>
130
131
  </p>
131
- <pre class="max-h-64 overflow-auto text-[11px] text-toned">{{ doc.body }}</pre>
132
+ <pre class="max-h-64 overflow-auto text-2xs text-toned">{{ doc.body }}</pre>
132
133
  </div>
133
134
  </div>
134
135
  </div>
@@ -139,13 +140,13 @@ async function suppress(serviceId: string) {
139
140
  <!-- Only an INHERITED entry can be suppressed — an account service or one the
140
141
  deployment registered in code; the board's own row is managed in the registry tab,
141
142
  where deleting it is the honest action. -->
142
- <UButton
143
+ <IconButton
143
144
  v-if="s.tier !== 'workspace'"
144
145
  icon="i-lucide-eye-off"
145
146
  size="xs"
146
147
  variant="ghost"
147
148
  :loading="rowBusy(`suppress:${s.id}`)"
148
- :title="t('foundational.catalog.suppress')"
149
+ :label="t('foundational.catalog.suppress')"
149
150
  :data-testid="`foundational-suppress-${s.id}`"
150
151
  @click="suppress(s.id)"
151
152
  />
@@ -20,6 +20,7 @@ import {
20
20
  useFoundationalServicesStore,
21
21
  } from '~/stores/foundationalServices'
22
22
  import FoundationalContractSummary from '~/components/foundational/FoundationalContractSummary.vue'
23
+ import IconButton from '~/components/common/IconButton.vue'
23
24
 
24
25
  const props = defineProps<{ kind: FoundationalServiceOwnerKind; ownerId: string }>()
25
26
 
@@ -189,7 +190,7 @@ async function remove(service: FoundationalService) {
189
190
  <div class="min-w-0 flex-1">
190
191
  <p class="truncate text-sm font-medium text-app-100">
191
192
  {{ s.name }}
192
- <code class="ms-1 text-[11px] text-dimmed">{{ s.id }}</code>
193
+ <code class="ms-1 text-2xs text-dimmed">{{ s.id }}</code>
193
194
  </p>
194
195
  <p class="text-xs text-muted">{{ s.summary }}</p>
195
196
  <div v-if="s.capabilities.length" class="mt-1 flex flex-wrap gap-1">
@@ -198,25 +199,25 @@ async function remove(service: FoundationalService) {
198
199
  </UBadge>
199
200
  </div>
200
201
  <FoundationalContractSummary :contracts="s.contracts" :format-label="formatLabel" />
201
- <p v-if="s.sourceId" class="mt-1 text-[11px] text-dimmed">
202
+ <p v-if="s.sourceId" class="mt-1 text-2xs text-dimmed">
202
203
  {{ t('foundational.registry.fromSource', { path: s.sourcePath ?? '' }) }}
203
204
  </p>
204
205
  </div>
205
206
  <div class="flex shrink-0 gap-1">
206
- <UButton
207
+ <IconButton
207
208
  v-if="!s.sourceId"
208
209
  icon="i-lucide-pencil"
209
210
  size="xs"
210
211
  variant="ghost"
211
- :title="t('foundational.registry.edit')"
212
+ :label="t('foundational.registry.edit')"
212
213
  @click="openEdit(s)"
213
214
  />
214
- <UButton
215
+ <IconButton
215
216
  icon="i-lucide-trash-2"
216
217
  size="xs"
217
218
  color="error"
218
219
  variant="ghost"
219
- :title="t('foundational.registry.delete')"
220
+ :label="t('foundational.registry.delete')"
220
221
  @click="remove(s)"
221
222
  />
222
223
  </div>
@@ -274,7 +275,7 @@ async function remove(service: FoundationalService) {
274
275
  <p class="text-xs font-medium text-toned">
275
276
  {{ t('foundational.registry.contractsTitle') }}
276
277
  </p>
277
- <p class="mb-2 text-[11px] text-dimmed">
278
+ <p class="mb-2 text-2xs text-dimmed">
278
279
  {{
279
280
  draft.contractsTouched
280
281
  ? t('foundational.registry.contractsReplace')
@@ -289,12 +290,12 @@ async function remove(service: FoundationalService) {
289
290
  class="flex-1"
290
291
  />
291
292
  <USelect v-model="c.format" :items="formatItems" class="w-56" />
292
- <UButton
293
+ <IconButton
293
294
  icon="i-lucide-x"
294
295
  size="xs"
295
296
  color="error"
296
297
  variant="ghost"
297
- :title="t('foundational.registry.removeContract')"
298
+ :label="t('foundational.registry.removeContract')"
298
299
  @click="removeContract(i)"
299
300
  />
300
301
  </div>
@@ -27,6 +27,7 @@ import {
27
27
  } from '~/stores/foundationalServices'
28
28
  import GitHubRepoSearchSelect from '~/components/github/GitHubRepoSearchSelect.vue'
29
29
  import RepoTreeBrowser from '~/components/github/RepoTreeBrowser.vue'
30
+ import IconButton from '~/components/common/IconButton.vue'
30
31
 
31
32
  const props = defineProps<{ kind: FoundationalServiceOwnerKind; ownerId: string }>()
32
33
 
@@ -295,29 +296,29 @@ async function unlink(id: string) {
295
296
  {{ t('foundational.sources.changes') }}
296
297
  </UBadge>
297
298
  <div class="ms-auto flex gap-1">
298
- <UButton
299
+ <IconButton
299
300
  icon="i-lucide-search-check"
300
301
  size="xs"
301
302
  variant="ghost"
302
303
  :loading="rowBusy(`check:${s.id}`)"
303
- :title="t('foundational.sources.check')"
304
+ :label="t('foundational.sources.check')"
304
305
  @click="check(s.id)"
305
306
  />
306
- <UButton
307
+ <IconButton
307
308
  icon="i-lucide-refresh-cw"
308
309
  size="xs"
309
310
  variant="ghost"
310
311
  :loading="rowBusy(`sync:${s.id}`)"
311
- :title="t('foundational.sources.sync')"
312
+ :label="t('foundational.sources.sync')"
312
313
  @click="sync(s.id)"
313
314
  />
314
- <UButton
315
+ <IconButton
315
316
  icon="i-lucide-unplug"
316
317
  size="xs"
317
318
  color="error"
318
319
  variant="ghost"
319
320
  :loading="rowBusy(`unlink:${s.id}`)"
320
- :title="t('foundational.sources.unlink')"
321
+ :label="t('foundational.sources.unlink')"
321
322
  @click="unlink(s.id)"
322
323
  />
323
324
  </div>
@@ -73,10 +73,10 @@ async function restore(serviceId: string) {
73
73
  <div class="min-w-0 flex-1">
74
74
  <p class="truncate text-sm text-toned">
75
75
  {{ s.name || s.id }}
76
- <code v-if="s.name" class="ms-1 text-[11px] text-dimmed">{{ s.id }}</code>
76
+ <code v-if="s.name" class="ms-1 text-2xs text-dimmed">{{ s.id }}</code>
77
77
  </p>
78
78
  <p v-if="s.summary" class="text-xs text-dimmed">{{ s.summary }}</p>
79
- <p v-if="!s.inherited" class="text-[11px] text-dimmed">
79
+ <p v-if="!s.inherited" class="text-2xs text-dimmed">
80
80
  {{ t('foundational.suppressions.shadowsNothing') }}
81
81
  </p>
82
82
  </div>
@@ -19,6 +19,7 @@ import { showOverrideField } from '~/utils/uiMode'
19
19
  import GitHubRepoSearchSelect from '~/components/github/GitHubRepoSearchSelect.vue'
20
20
  import RepoTreeBrowser from '~/components/github/RepoTreeBrowser.vue'
21
21
  import GitHubDocUrlImport from '~/components/fragments/GitHubDocUrlImport.vue'
22
+ import IconButton from '~/components/common/IconButton.vue'
22
23
 
23
24
  const props = withDefaults(
24
25
  defineProps<{
@@ -654,7 +655,7 @@ async function unlinkSource(id: string) {
654
655
  >
655
656
  {{ t('fragments.catalog.live', { source: f.documentRef.source }) }}
656
657
  </UBadge>
657
- <span class="ms-auto font-mono text-[11px] text-dimmed">{{ f.id }}</span>
658
+ <span class="ms-auto font-mono text-2xs text-dimmed">{{ f.id }}</span>
658
659
  </div>
659
660
  <p class="mt-1 text-sm text-muted">{{ f.summary }}</p>
660
661
  <div v-if="f.tags?.length" class="mt-1 flex flex-wrap gap-1">
@@ -750,12 +751,12 @@ async function unlinkSource(id: string) {
750
751
  <div class="ms-auto flex gap-1">
751
752
  <!-- Editing a repo-SOURCED fragment locally would be overwritten on the next sync,
752
753
  so only hand-authored fragments are editable here. -->
753
- <UButton
754
+ <IconButton
754
755
  v-if="!f.source"
755
756
  icon="i-lucide-pencil"
756
757
  size="xs"
757
758
  variant="ghost"
758
- :title="t('common.edit')"
759
+ :label="t('common.edit')"
759
760
  @click="startEdit(f)"
760
761
  />
761
762
  <UButton
@@ -859,19 +860,19 @@ async function unlinkSource(id: string) {
859
860
  </UBadge>
860
861
  </div>
861
862
  <p class="text-sm text-muted">{{ f.summary }}</p>
862
- <p v-if="f.resolvedAt" class="text-[11px] text-dimmed">
863
+ <p v-if="f.resolvedAt" class="text-2xs text-dimmed">
863
864
  {{
864
865
  t('fragments.documents.lastResolved', { date: d(new Date(f.resolvedAt), 'long') })
865
866
  }}
866
867
  </p>
867
868
  </div>
868
869
  <div class="ms-auto flex gap-1">
869
- <UButton
870
+ <IconButton
870
871
  icon="i-lucide-refresh-cw"
871
872
  size="xs"
872
873
  variant="ghost"
873
874
  :loading="rowBusy(`refresh:${f.id}`)"
874
- :title="t('fragments.documents.refreshTitle')"
875
+ :label="t('fragments.documents.refreshTitle')"
875
876
  @click="refreshFragment(f.id)"
876
877
  />
877
878
  <UButton
@@ -942,7 +943,7 @@ async function unlinkSource(id: string) {
942
943
  <div
943
944
  v-for="staged in stagedDocRefs"
944
945
  :key="staged.path"
945
- class="flex items-center gap-1.5 rounded bg-elevated/60 px-2 py-1 text-xs text-toned"
946
+ class="flex items-center gap-1.5 rounded-sm bg-elevated/60 px-2 py-1 text-xs text-toned"
946
947
  >
947
948
  <UIcon name="i-lucide-file-code-2" class="h-3.5 w-3.5 shrink-0 text-primary" />
948
949
  <span class="truncate">{{ staged.path }}</span>
@@ -10,6 +10,7 @@
10
10
  // (labelled by their raw id) so they stay visible and removable.
11
11
  import type { PromptFragment } from '~/types/domain'
12
12
  import { buildFragmentCategoryGroups } from '~/utils/fragmentPicker'
13
+ import SectionLabel from '~/components/common/SectionLabel.vue'
13
14
 
14
15
  const props = withDefaults(
15
16
  defineProps<{
@@ -76,9 +77,9 @@ function manageAccount() {
76
77
  <template>
77
78
  <div>
78
79
  <div class="mb-1 flex items-center justify-between gap-2">
79
- <span v-if="label" class="text-[11px] font-semibold uppercase tracking-wide text-muted">
80
+ <SectionLabel v-if="label" as="span">
80
81
  {{ label }}
81
- </span>
82
+ </SectionLabel>
82
83
  <span v-else />
83
84
  <UPopover v-model:open="open" :content="{ align: 'end' }">
84
85
  <UButton
@@ -98,16 +99,15 @@ function manageAccount() {
98
99
  <div class="min-h-0 flex-1 overflow-y-auto p-1">
99
100
  <template v-if="categoryGroups.length">
100
101
  <div v-for="group in categoryGroups" :key="group.category">
101
- <p
102
- class="px-2 pb-0.5 pt-1.5 text-[11px] font-semibold uppercase tracking-wide text-dimmed"
103
- >
102
+ <SectionLabel as="p" class="px-2 pb-0.5 pt-1.5">
104
103
  {{ group.category }}
105
- </p>
106
- <button
104
+ </SectionLabel>
105
+ <UButton
106
+ color="neutral"
107
+ variant="ghost"
107
108
  v-for="f in group.fragments"
108
109
  :key="f.id"
109
- type="button"
110
- class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-start text-sm hover:bg-elevated/60"
110
+ class="flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-start text-sm hover:bg-elevated/60"
111
111
  :class="selectedSet.has(f.id) ? 'text-app-100' : 'text-toned'"
112
112
  :title="f.summary"
113
113
  :data-testid="`fragment-option-${f.id}`"
@@ -120,31 +120,33 @@ function manageAccount() {
120
120
  :class="selectedSet.has(f.id) ? 'text-primary' : 'text-dimmed'"
121
121
  />
122
122
  <span class="flex-1 truncate">{{ f.title }}</span>
123
- </button>
123
+ </UButton>
124
124
  </div>
125
125
  </template>
126
- <p v-else class="px-2 py-3 text-[12px] text-dimmed">
126
+ <p v-else class="px-2 py-3 text-xs text-dimmed">
127
127
  {{ t('inspector.fragments.pickerEmpty') }}
128
128
  </p>
129
129
 
130
130
  <div class="mt-1 border-t border-default pt-1">
131
- <button
132
- type="button"
133
- class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-start text-sm text-toned hover:bg-elevated/60"
131
+ <UButton
132
+ color="neutral"
133
+ variant="ghost"
134
+ class="flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-start text-sm text-toned hover:bg-elevated/60"
134
135
  @click="manageBoard"
135
136
  >
136
137
  <UIcon name="i-lucide-book-marked" class="h-4 w-4 shrink-0 text-muted" />
137
138
  <span class="flex-1 truncate">{{ t('inspector.fragments.manageBoard') }}</span>
138
- </button>
139
- <button
139
+ </UButton>
140
+ <UButton
141
+ color="neutral"
142
+ variant="ghost"
140
143
  v-if="accounts.enabled"
141
- type="button"
142
- class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-start text-sm text-toned hover:bg-elevated/60"
144
+ class="flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-start text-sm text-toned hover:bg-elevated/60"
143
145
  @click="manageAccount"
144
146
  >
145
147
  <UIcon name="i-lucide-users" class="h-4 w-4 shrink-0 text-muted" />
146
148
  <span class="flex-1 truncate">{{ t('inspector.fragments.manageAccount') }}</span>
147
- </button>
149
+ </UButton>
148
150
  </div>
149
151
  </div>
150
152
 
@@ -178,7 +180,7 @@ function manageAccount() {
178
180
  {{ f.title }}<UIcon name="i-lucide-x" class="ms-0.5 h-3 w-3" />
179
181
  </UBadge>
180
182
  </div>
181
- <div v-else-if="emptyText" class="text-[11px] text-dimmed">
183
+ <div v-else-if="emptyText" class="text-2xs text-dimmed">
182
184
  {{ emptyText }}
183
185
  </div>
184
186
  </div>