@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.
- package/README.md +32 -2
- package/app/assets/css/main.css +22 -0
- package/app/assets/css/prose.css +4 -4
- package/app/assets/css/type.css +24 -0
- package/app/components/assistant/AssistantModal.vue +8 -6
- package/app/components/auth/BackendMisconfiguredScreen.vue +4 -3
- package/app/components/auth/LoginScreen.vue +40 -26
- package/app/components/auth/ResetPasswordScreen.vue +9 -4
- package/app/components/auth/UserMenu.vue +5 -4
- package/app/components/binaryCandidates/BinaryCandidatesWindow.vue +48 -37
- package/app/components/binaryOutput/BinaryOutputReport.vue +10 -14
- package/app/components/binaryOutput/StoredAssetView.vue +13 -11
- package/app/components/board/AddTaskModal.vue +30 -27
- package/app/components/board/AgentFailureCard.vue +21 -18
- package/app/components/board/AgentFailureHistory.vue +18 -9
- package/app/components/board/CreateInitiativeModal.vue +7 -6
- package/app/components/board/FailureDetail.vue +22 -12
- package/app/components/board/FailureHistoryList.vue +5 -5
- package/app/components/board/RecurringPipelineModal.vue +12 -11
- package/app/components/board/ReviewFrictionDialog.vue +8 -6
- package/app/components/board/StepExecutionHistory.vue +8 -8
- package/app/components/board/nodes/AgentChip.vue +1 -1
- package/app/components/board/nodes/BlockNode.vue +25 -24
- package/app/components/board/nodes/DecisionBadge.vue +4 -3
- package/app/components/board/nodes/EpicNode.vue +4 -4
- package/app/components/board/nodes/FrameSwimlanes.vue +8 -10
- package/app/components/board/nodes/InitiativeCard.vue +4 -4
- package/app/components/board/nodes/LaneGroup.vue +3 -5
- package/app/components/board/nodes/TaskCard.vue +20 -15
- package/app/components/board/nodes/TaskLane.vue +3 -3
- package/app/components/board/nodes/TaskPipelineMini.vue +21 -18
- package/app/components/bootstrap/AdoptionReviewModal.vue +36 -26
- package/app/components/bootstrap/BootstrapModal.vue +14 -13
- package/app/components/bootstrap/BootstrapRunSteps.vue +1 -1
- package/app/components/brainstorm/BrainstormWindow.vue +13 -10
- package/app/components/bugFishing/BugFishingWindow.vue +81 -58
- package/app/components/clarity/ClarityReviewWindow.vue +13 -10
- package/app/components/common/AsyncViewError.vue +1 -1
- package/app/components/common/ClarificationItem.vue +6 -6
- package/app/components/common/CopyButton.vue +12 -11
- package/app/components/common/DescriptorFields.vue +1 -1
- package/app/components/common/EmptyState.vue +1 -1
- package/app/components/common/IconButton.vue +34 -9
- package/app/components/common/InterviewGateNotice.vue +4 -4
- package/app/components/common/MarkdownProse.vue +8 -2
- package/app/components/common/SectionLabel.vue +38 -0
- package/app/components/consensus/ConsensusSessionWindow.vue +10 -9
- package/app/components/context/ContextAttachmentFields.vue +32 -20
- package/app/components/docs/DocInterviewWindow.vue +10 -9
- package/app/components/documents/ContextDocumentPicker.vue +22 -21
- package/app/components/documents/DocumentImportModal.vue +7 -5
- package/app/components/documents/DocumentOriginLink.vue +12 -9
- package/app/components/documents/DocumentSourceConnectModal.vue +2 -2
- package/app/components/documents/DocumentSyncState.vue +4 -4
- package/app/components/documents/DocumentTemplatesModal.vue +5 -4
- package/app/components/documents/RepoContextDocPicker.vue +7 -9
- package/app/components/documents/StartFromDesignModal.vue +4 -4
- package/app/components/documents/TaskContextDocs.vue +1 -1
- package/app/components/environments/EnvSetupStepper.vue +3 -3
- package/app/components/environments/EnvironmentStatusPanel.vue +15 -13
- package/app/components/environments/steps/EnvPreflightStep.vue +8 -8
- package/app/components/environments/steps/EnvReviewStep.vue +23 -22
- package/app/components/environments/steps/EnvSaveStep.vue +5 -5
- package/app/components/focus/BlockFocusView.vue +11 -10
- package/app/components/followUp/FollowUpWindow.vue +18 -15
- package/app/components/forkDecision/ForkDecisionWindow.vue +85 -97
- package/app/components/foundational/FoundationalContractSummary.vue +2 -2
- package/app/components/foundational/FoundationalServiceCatalogList.vue +6 -5
- package/app/components/foundational/FoundationalServiceRegistry.vue +10 -9
- package/app/components/foundational/FoundationalServiceSources.vue +7 -6
- package/app/components/foundational/FoundationalSuppressions.vue +2 -2
- package/app/components/fragments/FragmentLibraryManager.vue +8 -7
- package/app/components/fragments/FragmentSelector.vue +22 -20
- package/app/components/gates/GateFailingCheckList.vue +7 -6
- package/app/components/gates/GateResultView.vue +55 -49
- package/app/components/github/AddServiceFromRepoModal.vue +16 -11
- package/app/components/github/BranchProtectionPreflight.vue +5 -5
- package/app/components/github/GitHubConnect.vue +5 -3
- package/app/components/github/GitHubOnboarding.vue +7 -2
- package/app/components/github/GitHubPanel.vue +23 -19
- package/app/components/github/RepoTreeBrowser.vue +16 -13
- package/app/components/humanTest/HumanTestWindow.vue +29 -24
- package/app/components/initiative/InitiativePlanNotice.vue +3 -3
- package/app/components/initiative/InitiativePlanReview.vue +46 -38
- package/app/components/initiative/InitiativePlanningWindow.vue +5 -5
- package/app/components/initiative/InitiativeTrackerWindow.vue +213 -165
- package/app/components/judge/JudgeResultView.vue +28 -27
- package/app/components/kaizen/KaizenPanel.vue +70 -56
- package/app/components/kaizen/KaizenStepStatus.vue +7 -6
- package/app/components/layout/AccountAuditLog.vue +1 -1
- package/app/components/layout/AccountDeploymentSettings.vue +10 -10
- package/app/components/layout/AccountFailureKindRules.vue +13 -13
- package/app/components/layout/AccountFoundationalSettings.vue +1 -1
- package/app/components/layout/AccountFragmentSettings.vue +1 -1
- package/app/components/layout/AccountModelPolicySettings.vue +7 -7
- package/app/components/layout/AccountPlatformAlertSettings.vue +8 -8
- package/app/components/layout/AccountRiskPolicySettings.vue +2 -2
- package/app/components/layout/AccountRunCredentialSettings.vue +3 -3
- package/app/components/layout/AccountSkillSettings.vue +1 -1
- package/app/components/layout/AccountTeamSettings.vue +13 -9
- package/app/components/layout/AiProvidersBanner.vue +1 -1
- package/app/components/layout/AppearanceSwitcher.vue +5 -4
- package/app/components/layout/BoardSwitcher.vue +13 -10
- package/app/components/layout/BoardToolbar.vue +10 -5
- package/app/components/layout/CommandBar.vue +8 -5
- package/app/components/layout/DefaultTestEnvBanner.vue +6 -5
- package/app/components/layout/IntegrationsHub.vue +12 -9
- package/app/components/layout/LanguageSwitcher.vue +5 -4
- package/app/components/layout/ModelProvidersHub.vue +10 -7
- package/app/components/layout/NotificationsInbox.vue +47 -36
- package/app/components/layout/PersonalSetupModal.vue +9 -6
- package/app/components/layout/RolePrompt.vue +5 -4
- package/app/components/layout/SideBar.vue +7 -8
- package/app/components/layout/SpendWarningBanner.vue +3 -3
- package/app/components/layout/TranslationWarningBanner.vue +5 -4
- package/app/components/layout/UiModeSwitcher.vue +15 -12
- package/app/components/layout/UiRoleSwitcher.vue +10 -8
- package/app/components/layout/WorkspaceMembersSettings.vue +2 -2
- package/app/components/media/ArtifactLightbox.vue +74 -44
- package/app/components/media/ImageCompare.vue +80 -85
- package/app/components/merge/MergeEffortChips.vue +4 -6
- package/app/components/notifications/NotificationSettingsPanel.vue +6 -5
- package/app/components/observability/OutcomeFilterChips.vue +6 -5
- package/app/components/observability/RunFailureSummary.vue +22 -23
- package/app/components/observability/StepMetricsBar.vue +9 -8
- package/app/components/observability/StepModelActivity.vue +8 -5
- package/app/components/observability/ToolCallList.vue +19 -20
- package/app/components/outcome/OutcomeSummaryWindow.vue +48 -46
- package/app/components/palettes/AgentPalette.vue +21 -15
- package/app/components/palettes/AgentTierSelect.vue +1 -1
- package/app/components/palettes/PipelinePurposeSelect.vue +1 -1
- package/app/components/panels/AgentStepDetail.vue +57 -49
- package/app/components/panels/AttemptEntryHeader.vue +2 -2
- package/app/components/panels/DecisionModal.vue +1 -1
- package/app/components/panels/GenericStructuredResultView.vue +6 -5
- package/app/components/panels/InspectorPanel.vue +15 -13
- package/app/components/panels/MergerResultView.vue +10 -9
- package/app/components/panels/ObservabilityPanel.vue +211 -186
- package/app/components/panels/OperatorDashboardPanel.vue +124 -115
- package/app/components/panels/ReportsPanel.vue +87 -83
- package/app/components/panels/ReportsSpendBreakdown.vue +2 -2
- package/app/components/panels/ResultWindowShell.vue +152 -137
- package/app/components/panels/RunDetailLoadState.vue +1 -1
- package/app/components/panels/StepContainerStatus.vue +17 -16
- package/app/components/panels/StepDelegatedStatus.vue +24 -21
- package/app/components/panels/StepEffortReport.vue +12 -17
- package/app/components/panels/StepFragmentAdherence.vue +9 -10
- package/app/components/panels/StepMetadataCard.vue +41 -43
- package/app/components/panels/StepReproductionReport.vue +18 -20
- package/app/components/panels/StepRestartControl.vue +3 -2
- package/app/components/panels/StepRunMeta.vue +27 -26
- package/app/components/panels/StepTestReport.vue +16 -15
- package/app/components/panels/StepToolServers.vue +7 -8
- package/app/components/panels/StepValidationReport.vue +5 -5
- package/app/components/panels/inspector/ContainerSummary.vue +4 -4
- package/app/components/panels/inspector/DocReferenceRepos.vue +5 -4
- package/app/components/panels/inspector/EnvProbeReport.vue +16 -13
- package/app/components/panels/inspector/EpicChildren.vue +12 -10
- package/app/components/panels/inspector/FrontendBindingsResolved.vue +5 -4
- package/app/components/panels/inspector/FrontendConfig.vue +29 -36
- package/app/components/panels/inspector/InitiativeInspector.vue +3 -3
- package/app/components/panels/inspector/InspectorSection.vue +25 -17
- package/app/components/panels/inspector/RecurringScheduleSettings.vue +9 -8
- package/app/components/panels/inspector/ServiceConnections.vue +5 -4
- package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +2 -2
- package/app/components/panels/inspector/ServiceSelfTests.vue +9 -13
- package/app/components/panels/inspector/ServiceTestConfig.vue +46 -49
- package/app/components/panels/inspector/ServiceTestSecrets.vue +3 -3
- package/app/components/panels/inspector/ServiceTestingContext.vue +2 -2
- package/app/components/panels/inspector/ServiceValidationConfig.vue +2 -2
- package/app/components/panels/inspector/TaskAgentConfig.vue +2 -2
- package/app/components/panels/inspector/TaskAprioriBranches.vue +6 -5
- package/app/components/panels/inspector/TaskDependencies.vue +1 -1
- package/app/components/panels/inspector/TaskEstimateBadge.vue +3 -7
- package/app/components/panels/inspector/TaskExecution.vue +18 -16
- package/app/components/panels/inspector/TaskRunSettings.vue +45 -44
- package/app/components/panels/inspector/TaskStructure.vue +5 -4
- package/app/components/pipeline/AgentPromptEditor.vue +15 -14
- package/app/components/pipeline/BinaryOutputStepPicker.vue +42 -52
- package/app/components/pipeline/EstimateThresholdFields.vue +14 -11
- package/app/components/pipeline/GateConfigFields.vue +19 -23
- package/app/components/pipeline/IterationCapPrompt.vue +1 -1
- package/app/components/pipeline/PipelineBuilder.vue +113 -99
- package/app/components/pipeline/PipelineHealthModal.vue +7 -7
- package/app/components/pipeline/PipelinePicker.vue +11 -9
- package/app/components/pipeline/PipelinePreview.vue +6 -7
- package/app/components/pipeline/PipelineProgress.vue +49 -45
- package/app/components/prReview/PrReviewWindow.vue +87 -65
- package/app/components/providers/AiPresetMismatchDialog.vue +4 -3
- package/app/components/providers/AiProviderOnboardingModal.vue +2 -2
- package/app/components/providers/ApiKeysSection.vue +11 -9
- package/app/components/providers/PersonalSubscriptionSection.vue +4 -3
- package/app/components/providers/VendorCredentialsModal.vue +5 -4
- package/app/components/provisioning/ProvisioningLogsDrawer.vue +10 -9
- package/app/components/ralph/RalphLoopResultView.vue +29 -29
- package/app/components/requirements/RequirementsReviewWindow.vue +22 -17
- package/app/components/riskPolicy/RiskPolicyPicker.vue +13 -11
- package/app/components/riskPolicy/RiskPolicyPreview.vue +16 -15
- package/app/components/sandbox/SandboxPanel.vue +93 -73
- package/app/components/settings/ApiTokensPanel.vue +1 -1
- package/app/components/settings/BudgetSettings.vue +18 -17
- package/app/components/settings/CapabilityCredentialsPanel.vue +5 -5
- package/app/components/settings/CloudflareHandlerSection.vue +44 -41
- package/app/components/settings/ComposeEnvironmentSetupSection.vue +14 -12
- package/app/components/settings/ConnectionTestVerdict.vue +1 -1
- package/app/components/settings/ConsensusGroupsSection.vue +58 -47
- package/app/components/settings/CustomManifestTypeEditor.vue +8 -7
- package/app/components/settings/DefaultProvisionTypeSection.vue +5 -5
- package/app/components/settings/InfraHandlersConfigurator.vue +26 -24
- package/app/components/settings/InfrastructureBackendPicker.vue +18 -18
- package/app/components/settings/IssueTrackerPanel.vue +30 -21
- package/app/components/settings/KubernetesEngineForm.vue +9 -8
- package/app/components/settings/KubernetesEnvironmentForm.vue +4 -3
- package/app/components/settings/LocalContainerPoolSettings.vue +2 -2
- package/app/components/settings/LocalModelEndpointsPanel.vue +13 -11
- package/app/components/settings/MergeClassRulesEditor.vue +6 -5
- package/app/components/settings/MergeRolePolicyEditor.vue +13 -12
- package/app/components/settings/ModelConfigurationPanel.vue +17 -19
- package/app/components/settings/ModelPresetHealthModal.vue +3 -3
- package/app/components/settings/ObservabilityConnectionPanel.vue +1 -1
- package/app/components/settings/OpenRouterCatalogPanel.vue +10 -8
- package/app/components/settings/PackageRegistriesPanel.vue +4 -4
- package/app/components/settings/ProviderConnectionTab.vue +5 -4
- package/app/components/settings/ProviderManifestEditor.vue +9 -8
- package/app/components/settings/ProviderPreferenceEditor.vue +13 -13
- package/app/components/settings/RiskPolicyCreateForm.vue +40 -26
- package/app/components/settings/RiskPolicyEditorRow.vue +21 -19
- package/app/components/settings/RiskPolicyHealthModal.vue +3 -3
- package/app/components/settings/RiskPolicyPanel.vue +11 -13
- package/app/components/settings/ServiceFragmentDefaultsPanel.vue +15 -12
- package/app/components/settings/SharedStacksPanel.vue +5 -5
- package/app/components/settings/TaskSourceCard.vue +2 -2
- package/app/components/settings/TaskTypeSuppressionsPanel.vue +4 -4
- package/app/components/settings/ToolServerChecklist.vue +22 -22
- package/app/components/settings/UsageSettings.vue +13 -13
- package/app/components/settings/UserSecretsSection.vue +4 -3
- package/app/components/settings/WorkspaceMetadataSettings.vue +6 -5
- package/app/components/settings/WorkspaceSettingsPanel.vue +72 -58
- package/app/components/skills/ReviewSkillQueue.vue +12 -10
- package/app/components/skills/SkillLibraryManager.vue +9 -8
- package/app/components/slack/SlackPanel.vue +17 -12
- package/app/components/spec/ServiceSpecWindow.vue +19 -19
- package/app/components/tasks/BugHuntModal.vue +1 -1
- package/app/components/tasks/ContextIssuePicker.vue +16 -15
- package/app/components/tasks/TaskContextIssues.vue +5 -4
- package/app/components/tasks/TaskImportModal.vue +3 -2
- package/app/components/tasks/TaskSourceConnectModal.vue +5 -5
- package/app/components/testing/TestReportWindow.vue +67 -60
- package/app/components/theme/ThemeImportModal.vue +2 -2
- package/app/components/tutorial/TutorialNudge.vue +3 -2
- package/app/components/tutorial/TutorialOverlay.vue +3 -2
- package/app/components/vcs/GitLabConnect.vue +1 -1
- package/app/components/visualConfirm/VisualConfirmationWindow.vue +79 -54
- package/app/utils/theme/builtins.ts +7 -1
- package/i18n/locales/de.json +7 -3
- package/i18n/locales/en.json +7 -3
- package/i18n/locales/es.json +7 -3
- package/i18n/locales/fr.json +7 -3
- package/i18n/locales/he.json +7 -3
- package/i18n/locales/it.json +7 -3
- package/i18n/locales/ja.json +7 -3
- package/i18n/locales/pl.json +7 -3
- package/i18n/locales/tr.json +7 -3
- package/i18n/locales/uk.json +7 -3
- package/package.json +1 -1
|
@@ -28,6 +28,7 @@ import type { BugFishingFinding, BugFishingSeverity, BugFishingStepState } from
|
|
|
28
28
|
import ResultWindowShell from '~/components/panels/ResultWindowShell.vue'
|
|
29
29
|
import StepRunMeta from '~/components/panels/StepRunMeta.vue'
|
|
30
30
|
import MarkdownProse from '~/components/common/MarkdownProse.vue'
|
|
31
|
+
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
31
32
|
|
|
32
33
|
const execution = useExecutionStore()
|
|
33
34
|
const board = useBoardStore()
|
|
@@ -263,12 +264,13 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
263
264
|
data-testid="bug-fishing-phases"
|
|
264
265
|
class="w-60 shrink-0 overflow-y-auto border-r border-default px-3 py-4"
|
|
265
266
|
>
|
|
266
|
-
<p class="mb-2 px-1
|
|
267
|
+
<SectionLabel as="p" class="mb-2 px-1">
|
|
267
268
|
{{ t('bugFishing.phases.heading') }}
|
|
268
|
-
</
|
|
269
|
-
<
|
|
270
|
-
|
|
271
|
-
|
|
269
|
+
</SectionLabel>
|
|
270
|
+
<UButton
|
|
271
|
+
color="neutral"
|
|
272
|
+
variant="ghost"
|
|
273
|
+
class="mb-1 w-full rounded-md px-2 py-1.5 text-left text-xs"
|
|
272
274
|
:class="
|
|
273
275
|
selectedPassKey === null
|
|
274
276
|
? 'bg-elevated text-app-100'
|
|
@@ -277,21 +279,22 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
277
279
|
@click="selectedPassKey = null"
|
|
278
280
|
>
|
|
279
281
|
{{ t('bugFishing.phases.all', { count: findings.length }) }}
|
|
280
|
-
</
|
|
282
|
+
</UButton>
|
|
281
283
|
<!-- Grouped by TERRITORY on a partitioned codebase. A codebase small enough to fish
|
|
282
284
|
whole has one group with no label, which renders as the flat angle rail. -->
|
|
283
285
|
<div v-for="group in territoryGroups" :key="group.id ?? 'whole'" class="mb-2">
|
|
284
286
|
<p
|
|
285
287
|
v-if="group.label"
|
|
286
|
-
class="mb-1 mt-2 truncate px-1 text-
|
|
288
|
+
class="mb-1 mt-2 truncate px-1 text-3xs font-semibold uppercase tracking-wide text-app-info-400/80"
|
|
287
289
|
:title="group.label"
|
|
288
290
|
>
|
|
289
291
|
{{ group.label }}
|
|
290
292
|
</p>
|
|
291
293
|
<ul class="space-y-0.5">
|
|
292
294
|
<li v-for="phase in group.passes" :key="passKey(phase)">
|
|
293
|
-
<
|
|
294
|
-
|
|
295
|
+
<UButton
|
|
296
|
+
color="neutral"
|
|
297
|
+
variant="ghost"
|
|
295
298
|
class="flex w-full items-start gap-2 rounded-md px-2 py-1.5 text-left"
|
|
296
299
|
:class="
|
|
297
300
|
selectedPassKey === passKey(phase)
|
|
@@ -312,8 +315,8 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
312
315
|
}"
|
|
313
316
|
/>
|
|
314
317
|
<span class="min-w-0 flex-1">
|
|
315
|
-
<span class="block truncate text-
|
|
316
|
-
<span class="block text-
|
|
318
|
+
<span class="block truncate text-xs">{{ phase.title }}</span>
|
|
319
|
+
<span class="block text-3xs text-dimmed">
|
|
317
320
|
{{
|
|
318
321
|
phase.status === 'completed' || phase.status === 'failed'
|
|
319
322
|
? t('bugFishing.phases.found', { count: phaseFindingCount(phase) })
|
|
@@ -335,12 +338,12 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
335
338
|
:style="{ width: `${coverageShare(phase)}%` }"
|
|
336
339
|
/>
|
|
337
340
|
</span>
|
|
338
|
-
<span class="mt-0.5 block text-
|
|
341
|
+
<span class="mt-0.5 block text-3xs text-app-600">
|
|
339
342
|
{{ t('bugFishing.coverage.share', { percent: coverageShare(phase) }) }}
|
|
340
343
|
</span>
|
|
341
344
|
</span>
|
|
342
345
|
</span>
|
|
343
|
-
</
|
|
346
|
+
</UButton>
|
|
344
347
|
</li>
|
|
345
348
|
</ul>
|
|
346
349
|
</div>
|
|
@@ -354,18 +357,18 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
354
357
|
v-if="selectedPhase"
|
|
355
358
|
class="mb-4 rounded-lg border border-default bg-default/40 px-3 py-2.5"
|
|
356
359
|
>
|
|
357
|
-
<p class="text-
|
|
360
|
+
<p class="text-2xs text-muted">{{ selectedPhase.goal }}</p>
|
|
358
361
|
<p
|
|
359
362
|
v-if="selectedPhase.status === 'failed'"
|
|
360
363
|
data-testid="bug-fishing-phase-failed"
|
|
361
|
-
class="mt-2 text-
|
|
364
|
+
class="mt-2 text-xs text-app-warning-300"
|
|
362
365
|
>
|
|
363
366
|
{{ t('bugFishing.phases.failedNote', { reason: selectedPhase.failureReason ?? '' }) }}
|
|
364
367
|
</p>
|
|
365
368
|
<MarkdownProse
|
|
366
369
|
v-else-if="selectedPhase.summary"
|
|
367
370
|
:text="selectedPhase.summary"
|
|
368
|
-
class="mt-2 max-w-3xl text-
|
|
371
|
+
class="mt-2 max-w-3xl text-xs"
|
|
369
372
|
/>
|
|
370
373
|
</div>
|
|
371
374
|
|
|
@@ -374,7 +377,7 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
374
377
|
<div
|
|
375
378
|
v-if="fishing"
|
|
376
379
|
data-testid="bug-fishing-in-progress"
|
|
377
|
-
class="mb-4 flex items-center gap-2 rounded-lg border border-app-info-500/25 bg-app-info-500/5 px-3 py-2 text-
|
|
380
|
+
class="mb-4 flex items-center gap-2 rounded-lg border border-app-info-500/25 bg-app-info-500/5 px-3 py-2 text-xs text-app-info-200"
|
|
378
381
|
>
|
|
379
382
|
<UIcon name="i-lucide-loader-circle" class="h-4 w-4 shrink-0 animate-spin" />
|
|
380
383
|
<span>
|
|
@@ -395,7 +398,7 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
395
398
|
<div
|
|
396
399
|
v-if="plan && (unfished.length > 0 || plan.treeTruncated || plan.surveyUnavailableReason)"
|
|
397
400
|
data-testid="bug-fishing-plan"
|
|
398
|
-
class="mb-4 rounded-lg border border-app-warning-500/25 bg-app-warning-500/5 px-3 py-2 text-
|
|
401
|
+
class="mb-4 rounded-lg border border-app-warning-500/25 bg-app-warning-500/5 px-3 py-2 text-xs text-app-warning-200"
|
|
399
402
|
>
|
|
400
403
|
<p v-if="plan.surveyUnavailableReason" data-testid="bug-fishing-survey-unavailable">
|
|
401
404
|
{{ t('bugFishing.plan.surveyUnavailable', { reason: plan.surveyUnavailableReason }) }}
|
|
@@ -412,7 +415,7 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
412
415
|
})
|
|
413
416
|
}}
|
|
414
417
|
</p>
|
|
415
|
-
<ul class="mt-1 space-y-0.5 text-
|
|
418
|
+
<ul class="mt-1 space-y-0.5 text-2xs text-app-warning-200/80">
|
|
416
419
|
<li v-for="cell in unfished" :key="`${cell.territoryId}::${cell.phaseId}`">
|
|
417
420
|
{{
|
|
418
421
|
t('bugFishing.plan.unfishedCell', {
|
|
@@ -432,7 +435,7 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
432
435
|
data-testid="bug-fishing-pipeline"
|
|
433
436
|
class="mb-4 flex flex-wrap items-center gap-2 rounded-lg border border-default bg-default/40 px-3 py-2"
|
|
434
437
|
>
|
|
435
|
-
<span class="text-
|
|
438
|
+
<span class="text-2xs text-muted">{{ t('bugFishing.fixPipeline.label') }}</span>
|
|
436
439
|
<USelectMenu
|
|
437
440
|
v-model="pipelineOverride"
|
|
438
441
|
:items="pipelineOptions"
|
|
@@ -440,22 +443,19 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
440
443
|
size="xs"
|
|
441
444
|
class="min-w-52"
|
|
442
445
|
/>
|
|
443
|
-
<span class="text-
|
|
446
|
+
<span class="text-2xs text-dimmed">
|
|
444
447
|
{{ t('bugFishing.fixPipeline.hint', { pipeline: effectivePipelineName ?? '—' }) }}
|
|
445
448
|
</span>
|
|
446
449
|
</div>
|
|
447
450
|
|
|
448
451
|
<div class="mb-2 flex items-center justify-between">
|
|
449
|
-
<p class="text-
|
|
452
|
+
<p class="text-2xs text-muted">
|
|
450
453
|
{{ t('bugFishing.counts', { untriaged: untriagedCount, spawned: spawnedCount }) }}
|
|
451
454
|
</p>
|
|
452
|
-
<
|
|
453
|
-
<input v-model="showTriaged" type="checkbox" class="accent-app-info-500" />
|
|
454
|
-
{{ t('bugFishing.showTriaged') }}
|
|
455
|
-
</label>
|
|
455
|
+
<UCheckbox v-model="showTriaged" size="xs" :label="t('bugFishing.showTriaged')" />
|
|
456
456
|
</div>
|
|
457
457
|
|
|
458
|
-
<p v-if="bugFishing.error" class="mb-3 text-
|
|
458
|
+
<p v-if="bugFishing.error" class="mb-3 text-xs text-app-error-300">
|
|
459
459
|
{{ bugFishing.error }}
|
|
460
460
|
</p>
|
|
461
461
|
|
|
@@ -465,7 +465,7 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
465
465
|
<div
|
|
466
466
|
v-if="visibleFindings.length === 0"
|
|
467
467
|
data-testid="bug-fishing-empty"
|
|
468
|
-
class="rounded-lg border border-default bg-default/40 px-4 py-8 text-center text-
|
|
468
|
+
class="rounded-lg border border-default bg-default/40 px-4 py-8 text-center text-xs text-muted"
|
|
469
469
|
>
|
|
470
470
|
{{
|
|
471
471
|
findings.length === 0
|
|
@@ -484,54 +484,76 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
484
484
|
>
|
|
485
485
|
<div class="flex flex-wrap items-center gap-2">
|
|
486
486
|
<span
|
|
487
|
-
class="rounded border px-1.5 py-0.5 text-
|
|
487
|
+
class="rounded-sm border px-1.5 py-0.5 text-3xs font-semibold uppercase"
|
|
488
488
|
:class="SEVERITY_CLASS[finding.severity]"
|
|
489
489
|
>
|
|
490
490
|
{{ t(`bugFishing.severity.${finding.severity}`) }}
|
|
491
491
|
</span>
|
|
492
|
-
<span class="rounded bg-elevated px-1.5 py-0.5 text-
|
|
492
|
+
<span class="rounded-sm bg-elevated px-1.5 py-0.5 text-3xs text-toned">
|
|
493
493
|
{{ t(`bugFishing.kind.${finding.kind}`) }}
|
|
494
494
|
</span>
|
|
495
|
-
<span class="text-
|
|
495
|
+
<span class="text-3xs text-dimmed">
|
|
496
496
|
{{ t(`bugFishing.confidence.${finding.confidence}`) }}
|
|
497
497
|
</span>
|
|
498
498
|
<span
|
|
499
|
-
class="min-w-0 flex-1 text-
|
|
499
|
+
class="min-w-0 flex-1 text-sm text-app-100"
|
|
500
500
|
:class="{ 'line-through': finding.dismissed }"
|
|
501
501
|
>
|
|
502
502
|
{{ finding.title }}
|
|
503
503
|
</span>
|
|
504
504
|
</div>
|
|
505
505
|
|
|
506
|
-
<p v-if="finding.path" class="mt-1 font-mono text-
|
|
506
|
+
<p v-if="finding.path" class="mt-1 font-mono text-2xs text-dimmed">
|
|
507
507
|
{{ finding.path }}<span v-if="finding.line">:{{ finding.line }}</span>
|
|
508
508
|
</p>
|
|
509
509
|
|
|
510
|
-
<MarkdownProse :text="finding.detail" class="mt-2 max-w-3xl text-
|
|
510
|
+
<MarkdownProse :text="finding.detail" class="mt-2 max-w-3xl text-xs" />
|
|
511
511
|
|
|
512
|
-
<div v-if="finding.failureScenario" class="mt-2 text-
|
|
513
|
-
<
|
|
512
|
+
<div v-if="finding.failureScenario" class="mt-2 text-xs text-toned">
|
|
513
|
+
<SectionLabel as="span">
|
|
514
514
|
{{ t('bugFishing.finding.failureScenario') }}
|
|
515
|
-
</
|
|
515
|
+
</SectionLabel>
|
|
516
516
|
<MarkdownProse :text="finding.failureScenario" class="mt-0.5 max-w-3xl" />
|
|
517
517
|
</div>
|
|
518
518
|
|
|
519
519
|
<!-- Evidence is rendered apart from the detail for the reason the contract keeps them
|
|
520
520
|
apart: a finding that cannot point at the code it describes is speculating, and
|
|
521
521
|
that should be visible without reading the prose for it. -->
|
|
522
|
-
<
|
|
523
|
-
<
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
522
|
+
<UCollapsible v-if="finding.evidence" class="mt-2">
|
|
523
|
+
<template #default="{ open }">
|
|
524
|
+
<UButton
|
|
525
|
+
variant="link"
|
|
526
|
+
color="neutral"
|
|
527
|
+
size="xs"
|
|
528
|
+
:icon="open ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
|
|
529
|
+
:label="t('bugFishing.finding.evidence')"
|
|
530
|
+
:ui="{
|
|
531
|
+
base: 'w-full justify-start gap-1 p-0 text-2xs text-muted hover:text-default',
|
|
532
|
+
}"
|
|
533
|
+
/>
|
|
534
|
+
</template>
|
|
535
|
+
<template #content>
|
|
536
|
+
<MarkdownProse :text="finding.evidence" class="mt-1 max-w-3xl text-xs" />
|
|
537
|
+
</template>
|
|
538
|
+
</UCollapsible>
|
|
539
|
+
|
|
540
|
+
<UCollapsible v-if="finding.suggestedFix" class="mt-1">
|
|
541
|
+
<template #default="{ open }">
|
|
542
|
+
<UButton
|
|
543
|
+
variant="link"
|
|
544
|
+
color="neutral"
|
|
545
|
+
size="xs"
|
|
546
|
+
:icon="open ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
|
|
547
|
+
:label="t('bugFishing.finding.suggestedFix')"
|
|
548
|
+
:ui="{
|
|
549
|
+
base: 'w-full justify-start gap-1 p-0 text-2xs text-muted hover:text-default',
|
|
550
|
+
}"
|
|
551
|
+
/>
|
|
552
|
+
</template>
|
|
553
|
+
<template #content>
|
|
554
|
+
<MarkdownProse :text="finding.suggestedFix" class="mt-1 max-w-3xl text-xs" />
|
|
555
|
+
</template>
|
|
556
|
+
</UCollapsible>
|
|
535
557
|
|
|
536
558
|
<!-- Already marked: say what was created and let the reader follow it. The three
|
|
537
559
|
spawn states are rendered apart because they are three different facts — a task
|
|
@@ -540,7 +562,7 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
540
562
|
<div
|
|
541
563
|
v-if="finding.spawn?.status === 'spawned'"
|
|
542
564
|
data-testid="bug-fishing-finding-spawned"
|
|
543
|
-
class="mt-2 flex flex-wrap items-center gap-2 text-
|
|
565
|
+
class="mt-2 flex flex-wrap items-center gap-2 text-2xs text-app-success-300"
|
|
544
566
|
>
|
|
545
567
|
<UIcon name="i-lucide-check-circle-2" class="h-3.5 w-3.5" />
|
|
546
568
|
<span>
|
|
@@ -552,13 +574,14 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
552
574
|
})
|
|
553
575
|
}}
|
|
554
576
|
</span>
|
|
555
|
-
<
|
|
556
|
-
|
|
557
|
-
|
|
577
|
+
<UButton
|
|
578
|
+
color="neutral"
|
|
579
|
+
variant="link"
|
|
580
|
+
class="p-0 text-2xs underline hover:text-app-success-200"
|
|
558
581
|
@click="openSpawnedTask(finding.spawn.taskId)"
|
|
559
582
|
>
|
|
560
583
|
{{ t('bugFishing.finding.openTask') }}
|
|
561
|
-
</
|
|
584
|
+
</UButton>
|
|
562
585
|
</div>
|
|
563
586
|
|
|
564
587
|
<!-- A claim held by a marking still in flight. No task to link yet, and no Fix
|
|
@@ -566,7 +589,7 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
566
589
|
<div
|
|
567
590
|
v-else-if="finding.spawn?.status === 'pending'"
|
|
568
591
|
data-testid="bug-fishing-finding-spawning"
|
|
569
|
-
class="mt-2 flex flex-wrap items-center gap-2 text-
|
|
592
|
+
class="mt-2 flex flex-wrap items-center gap-2 text-2xs text-muted"
|
|
570
593
|
>
|
|
571
594
|
<UIcon name="i-lucide-loader-circle" class="h-3.5 w-3.5 animate-spin" />
|
|
572
595
|
<span>{{ t('bugFishing.finding.spawning') }}</span>
|
|
@@ -577,7 +600,7 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
577
600
|
<p
|
|
578
601
|
v-else-if="finding.spawn?.status === 'failed'"
|
|
579
602
|
data-testid="bug-fishing-finding-spawn-failed"
|
|
580
|
-
class="mt-2 max-w-3xl text-
|
|
603
|
+
class="mt-2 max-w-3xl text-2xs text-app-warning-300"
|
|
581
604
|
>
|
|
582
605
|
{{
|
|
583
606
|
t('bugFishing.finding.spawnFailed', {
|
|
@@ -623,7 +646,7 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
623
646
|
v-if="awaiting"
|
|
624
647
|
class="flex items-center justify-between gap-3 border-t border-default px-5 py-3"
|
|
625
648
|
>
|
|
626
|
-
<p class="text-
|
|
649
|
+
<p class="text-2xs text-dimmed">
|
|
627
650
|
{{
|
|
628
651
|
untriagedCount > 0
|
|
629
652
|
? t('bugFishing.footer.parkedWithUntriaged', { count: untriagedCount })
|
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
ReviewItemSeverity,
|
|
20
20
|
ReviewItemStatus,
|
|
21
21
|
} from '~/types/clarity'
|
|
22
|
+
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
22
23
|
|
|
23
24
|
const board = useBoardStore()
|
|
24
25
|
const clarity = useClarityStore()
|
|
@@ -424,9 +425,9 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
|
|
|
424
425
|
v-if="item.reply && item.status !== 'open' && item.status !== 'answered'"
|
|
425
426
|
class="mt-2 max-w-3xl rounded-md border-s-2 border-muted bg-app-950/40 px-3 py-1.5 text-sm text-toned"
|
|
426
427
|
>
|
|
427
|
-
<
|
|
428
|
+
<SectionLabel as="span">
|
|
428
429
|
{{ t('clarity.answerLabel') }}
|
|
429
|
-
</
|
|
430
|
+
</SectionLabel>
|
|
430
431
|
<p class="whitespace-pre-line">{{ item.reply }}</p>
|
|
431
432
|
</div>
|
|
432
433
|
|
|
@@ -480,7 +481,7 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
|
|
|
480
481
|
|
|
481
482
|
<!-- clarified document: the standard-format bug report -->
|
|
482
483
|
<section v-if="outline" class="mt-6 border-t border-default pt-5">
|
|
483
|
-
<div class="mb-3 flex items-center gap-1.5 text-
|
|
484
|
+
<div class="mb-3 flex items-center gap-1.5 text-2xs text-app-success-400">
|
|
484
485
|
<UIcon name="i-lucide-file-check-2" class="h-3.5 w-3.5" />
|
|
485
486
|
<span class="font-semibold uppercase tracking-wide">
|
|
486
487
|
{{ incorporated ? t('clarity.docHeading') : t('clarity.docHeadingDraft') }}
|
|
@@ -490,9 +491,11 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
|
|
|
490
491
|
`width` prop): the window is `full`-width now, and this is continuous prose that
|
|
491
492
|
would otherwise run to 200-character lines. -->
|
|
492
493
|
<div v-for="s in outline.sections" :key="s.id" class="mb-2 max-w-3xl">
|
|
493
|
-
<
|
|
494
|
+
<UButton
|
|
495
|
+
color="neutral"
|
|
496
|
+
variant="ghost"
|
|
494
497
|
v-if="s.title"
|
|
495
|
-
class="group flex w-full items-center gap-2 text-start"
|
|
498
|
+
class="group flex w-full items-center gap-2 p-0 text-start hover:bg-transparent"
|
|
496
499
|
@click="toggle(s.id)"
|
|
497
500
|
>
|
|
498
501
|
<UIcon
|
|
@@ -505,10 +508,10 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
|
|
|
505
508
|
:class="s.depth <= 1 ? 'text-base' : s.depth === 2 ? 'text-sm' : 'text-xs'"
|
|
506
509
|
v-html="s.titleHtml"
|
|
507
510
|
/>
|
|
508
|
-
</
|
|
511
|
+
</UButton>
|
|
509
512
|
<div
|
|
510
513
|
v-show="!s.title || !collapsed[s.id]"
|
|
511
|
-
class="reader-prose mt-1 ps-5.5 text-
|
|
514
|
+
class="reader-prose mt-1 ps-5.5 text-sm leading-relaxed text-toned"
|
|
512
515
|
v-html="s.bodyHtml"
|
|
513
516
|
/>
|
|
514
517
|
</div>
|
|
@@ -574,7 +577,7 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
|
|
|
574
577
|
>
|
|
575
578
|
{{ t('clarity.incorporateAnswers') }}
|
|
576
579
|
</UButton>
|
|
577
|
-
<p class="text-
|
|
580
|
+
<p class="text-2xs leading-relaxed text-dimmed">
|
|
578
581
|
<template v-if="canProceed">
|
|
579
582
|
{{ t('clarity.hint.proceed') }}
|
|
580
583
|
</template>
|
|
@@ -636,14 +639,14 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
|
|
|
636
639
|
{{ t('clarity.redoWithDirection') }}
|
|
637
640
|
</UButton>
|
|
638
641
|
</div>
|
|
639
|
-
<p class="text-
|
|
642
|
+
<p class="text-2xs leading-relaxed text-dimmed">
|
|
640
643
|
{{ t('clarity.redoHint') }}
|
|
641
644
|
</p>
|
|
642
645
|
</div>
|
|
643
646
|
|
|
644
647
|
<div
|
|
645
648
|
v-if="review && incorporated"
|
|
646
|
-
class="border-t border-default pt-4 text-
|
|
649
|
+
class="border-t border-default pt-4 text-2xs leading-relaxed text-dimmed"
|
|
647
650
|
>
|
|
648
651
|
{{ t('clarity.incorporatedFooter') }}
|
|
649
652
|
</div>
|
|
@@ -19,7 +19,7 @@ function reload() {
|
|
|
19
19
|
<UIcon name="i-lucide-unplug" class="h-4 w-4 shrink-0 text-app-warning-400" />
|
|
20
20
|
{{ t('errors.asyncView.title') }}
|
|
21
21
|
</div>
|
|
22
|
-
<p class="mt-2 text-
|
|
22
|
+
<p class="mt-2 text-xs leading-relaxed text-muted">
|
|
23
23
|
{{ t('errors.asyncView.body') }}
|
|
24
24
|
</p>
|
|
25
25
|
<UButton class="mt-4" color="primary" variant="soft" size="xs" @click="reload">
|
|
@@ -51,16 +51,16 @@ const draft = computed({
|
|
|
51
51
|
<template>
|
|
52
52
|
<div class="rounded-lg border border-default bg-app-950/40 p-3" data-testid="clarification-item">
|
|
53
53
|
<div class="flex items-start justify-between gap-2">
|
|
54
|
-
<p class="text-
|
|
54
|
+
<p class="text-sm font-medium text-default">{{ prompt }}</p>
|
|
55
55
|
<slot name="badges" />
|
|
56
56
|
</div>
|
|
57
|
-
<p v-if="detail" class="mt-1 whitespace-pre-wrap text-
|
|
57
|
+
<p v-if="detail" class="mt-1 whitespace-pre-wrap text-xs leading-relaxed text-muted">
|
|
58
58
|
{{ detail }}
|
|
59
59
|
</p>
|
|
60
60
|
|
|
61
61
|
<!-- dismissed: a "not relevant" chip + reopen -->
|
|
62
62
|
<div v-if="dismissed" class="mt-2 flex items-center justify-between gap-2">
|
|
63
|
-
<span class="inline-flex items-center gap-1 text-
|
|
63
|
+
<span class="inline-flex items-center gap-1 text-2xs text-dimmed">
|
|
64
64
|
<UIcon name="i-lucide-x" class="h-3.5 w-3.5" />{{ t('clarification.dismissed') }}
|
|
65
65
|
</span>
|
|
66
66
|
<UButton
|
|
@@ -79,7 +79,7 @@ const draft = computed({
|
|
|
79
79
|
<!-- a recommendation is being requested/generated: a working chip, no answer box -->
|
|
80
80
|
<div
|
|
81
81
|
v-else-if="requested"
|
|
82
|
-
class="mt-2 inline-flex items-center gap-1 text-
|
|
82
|
+
class="mt-2 inline-flex items-center gap-1 text-2xs text-primary"
|
|
83
83
|
data-testid="clarification-requested"
|
|
84
84
|
>
|
|
85
85
|
<UIcon name="i-lucide-loader-circle" class="h-3.5 w-3.5 animate-spin" />
|
|
@@ -131,10 +131,10 @@ const draft = computed({
|
|
|
131
131
|
class="mt-2 rounded-md border border-primary/50 bg-primary/10 p-2"
|
|
132
132
|
data-testid="clarification-recommendation"
|
|
133
133
|
>
|
|
134
|
-
<div class="mb-1 flex items-center gap-1 text-
|
|
134
|
+
<div class="mb-1 flex items-center gap-1 text-3xs uppercase tracking-wide text-primary">
|
|
135
135
|
<UIcon name="i-lucide-wand-2" class="h-3 w-3" />{{ t('clarification.suggestion') }}
|
|
136
136
|
</div>
|
|
137
|
-
<p class="whitespace-pre-wrap text-
|
|
137
|
+
<p class="whitespace-pre-wrap text-xs text-default">{{ recommendation }}</p>
|
|
138
138
|
<UButton
|
|
139
139
|
class="mt-1.5"
|
|
140
140
|
size="xs"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
// Standard icon-only "copy to clipboard" button with confirmation feedback. Routes through
|
|
3
3
|
// `useCopyToClipboard` so success/failure is always toasted (UX-38), and carries both a
|
|
4
|
-
//
|
|
5
|
-
// Used to make error/detail surfaces copyable (UX-39)
|
|
4
|
+
// tooltip and an `aria-label` so it's named for pointer users and screen readers alike.
|
|
5
|
+
// Used to make error/detail surfaces copyable (UX-39): the first thing a user does with a
|
|
6
6
|
// stack trace or failure summary is copy it.
|
|
7
7
|
const props = defineProps<{
|
|
8
8
|
/** The text to place on the clipboard. */
|
|
@@ -18,13 +18,14 @@ const label = computed(() => props.label ?? t('common.copy'))
|
|
|
18
18
|
</script>
|
|
19
19
|
|
|
20
20
|
<template>
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
<UTooltip :text="label">
|
|
22
|
+
<UButton
|
|
23
|
+
icon="i-lucide-copy"
|
|
24
|
+
color="neutral"
|
|
25
|
+
variant="ghost"
|
|
26
|
+
:size="size ?? 'xs'"
|
|
27
|
+
:aria-label="label"
|
|
28
|
+
@click.stop="copy(text)"
|
|
29
|
+
/>
|
|
30
|
+
</UTooltip>
|
|
30
31
|
</template>
|
|
@@ -112,7 +112,7 @@ function selectItems(field: DescriptorField) {
|
|
|
112
112
|
testid that would have to be slugified to be selectable. -->
|
|
113
113
|
<p
|
|
114
114
|
v-if="caption"
|
|
115
|
-
class="-mb-1 text-
|
|
115
|
+
class="-mb-1 text-2xs font-semibold uppercase tracking-wide text-muted"
|
|
116
116
|
:class="{ 'pt-2': startsGroup }"
|
|
117
117
|
:data-testid="`${testidPrefix}-section`"
|
|
118
118
|
>
|
|
@@ -25,7 +25,7 @@ withDefaults(
|
|
|
25
25
|
<p :class="compact ? 'text-xs' : 'text-sm'" class="font-medium text-muted">
|
|
26
26
|
{{ title }}
|
|
27
27
|
</p>
|
|
28
|
-
<p v-if="description" :class="compact ? 'text-
|
|
28
|
+
<p v-if="description" :class="compact ? 'text-2xs' : 'text-xs'" class="max-w-xs">
|
|
29
29
|
{{ description }}
|
|
30
30
|
</p>
|
|
31
31
|
<div v-if="$slots.default" :class="compact ? 'mt-1' : 'mt-2'">
|
|
@@ -1,19 +1,44 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
// Shared icon-only button primitive that ENFORCES the accessible-name convention (UX-62/63):
|
|
3
|
-
// every icon button must carry a `label`, applied as BOTH `
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
3
|
+
// every icon button must carry a `label`, applied as BOTH the tooltip and `aria-label` (screen
|
|
4
|
+
// readers): an unlabeled icon button is a bug this component makes unrepresentable.
|
|
5
|
+
//
|
|
6
|
+
// The hint is a UTooltip, not a `title` attribute: `title` never fires on touch, is not styled,
|
|
7
|
+
// takes about a second to appear, and is not what the rest of the app's tooltips look like.
|
|
8
|
+
// `aria-label` stays on the button itself, because the tooltip is a hover/focus affordance and
|
|
9
|
+
// the accessible NAME has to be there whether or not one ever opens.
|
|
10
|
+
//
|
|
11
|
+
// All other UButton props/listeners (icon, color, variant, size, `ui`, @click, :loading,
|
|
12
|
+
// :disabled) pass straight through via `$attrs`; `label` is declared as a prop so it strips off
|
|
13
|
+
// before reaching UButton (whose own `label` prop would otherwise render visible text).
|
|
14
|
+
//
|
|
15
|
+
// `class` and `style` land on the WRAPPER, not the button: the wrapper is the box the parent lays
|
|
16
|
+
// out, so `ms-auto`, `shrink-0` or Vue Flow's `nodrag` only work there. The button's own look is
|
|
17
|
+
// its props, and `:ui="{ base: '…' }"` for anything they do not cover.
|
|
18
|
+
import { computed, useAttrs } from 'vue'
|
|
19
|
+
|
|
10
20
|
defineProps<{
|
|
11
|
-
/** Accessible name + tooltip. Required
|
|
21
|
+
/** Accessible name + tooltip. Required: the whole point of the primitive. */
|
|
12
22
|
label: string
|
|
13
23
|
}>()
|
|
14
24
|
defineOptions({ inheritAttrs: false })
|
|
25
|
+
|
|
26
|
+
const attrs = useAttrs()
|
|
27
|
+
const buttonAttrs = computed(() => {
|
|
28
|
+
const { class: _class, style: _style, ...rest } = attrs
|
|
29
|
+
return rest
|
|
30
|
+
})
|
|
15
31
|
</script>
|
|
16
32
|
|
|
17
33
|
<template>
|
|
18
|
-
<
|
|
34
|
+
<UTooltip :text="label">
|
|
35
|
+
<!-- The trigger is a SPAN around the button, not the button. A disabled `<button>` fires no
|
|
36
|
+
pointer or focus event, so a tooltip bound straight to it never opens: the one state
|
|
37
|
+
where the hint is the only thing explaining why the control does nothing (a blocked
|
|
38
|
+
delete naming its reason) was exactly the state that lost it. The span is
|
|
39
|
+
`inline-flex` so it adds no box of its own. -->
|
|
40
|
+
<span class="inline-flex" :class="$attrs.class" :style="$attrs.style">
|
|
41
|
+
<UButton v-bind="buttonAttrs" :aria-label="label" />
|
|
42
|
+
</span>
|
|
43
|
+
</UTooltip>
|
|
19
44
|
</template>
|
|
@@ -25,15 +25,15 @@ defineProps<{
|
|
|
25
25
|
:data-testid="testid"
|
|
26
26
|
>
|
|
27
27
|
<UIcon name="i-lucide-loader-circle" class="h-5 w-5 animate-spin text-primary" />
|
|
28
|
-
<p class="text-
|
|
29
|
-
<p class="text-
|
|
28
|
+
<p class="text-sm text-default">{{ title }}</p>
|
|
29
|
+
<p class="text-xs text-muted">{{ hint }}</p>
|
|
30
30
|
</div>
|
|
31
31
|
<div
|
|
32
32
|
v-else
|
|
33
33
|
class="rounded-lg border border-app-error-900/60 bg-app-error-950/20 p-4 text-center"
|
|
34
34
|
:data-testid="testid"
|
|
35
35
|
>
|
|
36
|
-
<p class="text-
|
|
37
|
-
<p class="mt-1 text-
|
|
36
|
+
<p class="text-sm text-app-error-200">{{ title }}</p>
|
|
37
|
+
<p class="mt-1 text-xs text-muted">{{ hint }}</p>
|
|
38
38
|
</div>
|
|
39
39
|
</template>
|
|
@@ -55,7 +55,11 @@ const html = computed(() => renderMarkdown(props.text))
|
|
|
55
55
|
font-style: italic;
|
|
56
56
|
}
|
|
57
57
|
.cf-prose :deep(code) {
|
|
58
|
-
|
|
58
|
+
/* `--ui-radius` rather than `--radius-sm`: Nuxt UI declares the `--radius-*` scale in a
|
|
59
|
+
* `@theme default inline` block, so Tailwind emits those only when the COMPILED stylesheet
|
|
60
|
+
* graph references them. This scoped block is not part of that compile, and `--ui-radius` is
|
|
61
|
+
* an ordinary custom property on `:root`. `--radius-sm` is `var(--ui-radius)` (prose.css). */
|
|
62
|
+
border-radius: var(--ui-radius);
|
|
59
63
|
background: var(--ui-bg-elevated);
|
|
60
64
|
padding: 0.1rem 0.3rem;
|
|
61
65
|
font-family: var(--font-mono);
|
|
@@ -65,7 +69,9 @@ const html = computed(() => renderMarkdown(props.text))
|
|
|
65
69
|
.cf-prose :deep(pre) {
|
|
66
70
|
margin: 0.6rem 0;
|
|
67
71
|
overflow: auto;
|
|
68
|
-
|
|
72
|
+
/* `--radius-lg` is `calc(var(--ui-radius) * 2)`; see the note on `code` above for why this
|
|
73
|
+
* block cannot name the scale variable. */
|
|
74
|
+
border-radius: calc(var(--ui-radius) * 2);
|
|
69
75
|
background: var(--app-bg-canvas);
|
|
70
76
|
padding: 0.75rem 0.9rem;
|
|
71
77
|
}
|