@cat-factory/app 0.309.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 +11 -2
- package/app/components/assistant/AssistantModal.vue +5 -4
- 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 +4 -3
- package/app/components/binaryCandidates/BinaryCandidatesWindow.vue +34 -23
- package/app/components/binaryOutput/StoredAssetView.vue +8 -6
- package/app/components/board/AddTaskModal.vue +16 -15
- package/app/components/board/AgentFailureCard.vue +12 -9
- package/app/components/board/AgentFailureHistory.vue +18 -9
- package/app/components/board/CreateInitiativeModal.vue +5 -4
- package/app/components/board/FailureDetail.vue +22 -12
- package/app/components/board/ReviewFrictionDialog.vue +4 -3
- package/app/components/board/nodes/BlockNode.vue +17 -15
- package/app/components/board/nodes/DecisionBadge.vue +4 -3
- package/app/components/board/nodes/FrameSwimlanes.vue +4 -3
- package/app/components/board/nodes/TaskCard.vue +12 -7
- package/app/components/board/nodes/TaskPipelineMini.vue +9 -7
- package/app/components/bootstrap/AdoptionReviewModal.vue +30 -21
- package/app/components/bootstrap/BootstrapModal.vue +3 -3
- package/app/components/brainstorm/BrainstormWindow.vue +5 -3
- package/app/components/bugFishing/BugFishingWindow.vue +49 -27
- package/app/components/clarity/ClarityReviewWindow.vue +5 -3
- package/app/components/common/CopyButton.vue +12 -11
- package/app/components/common/IconButton.vue +34 -9
- package/app/components/context/ContextAttachmentFields.vue +23 -12
- package/app/components/documents/ContextDocumentPicker.vue +12 -9
- package/app/components/documents/DocumentImportModal.vue +4 -3
- package/app/components/documents/DocumentOriginLink.vue +12 -9
- package/app/components/documents/DocumentSyncState.vue +3 -3
- package/app/components/documents/RepoContextDocPicker.vue +4 -3
- package/app/components/environments/EnvironmentStatusPanel.vue +4 -3
- package/app/components/followUp/FollowUpWindow.vue +9 -6
- package/app/components/forkDecision/ForkDecisionWindow.vue +71 -80
- package/app/components/foundational/FoundationalServiceCatalogList.vue +3 -2
- package/app/components/foundational/FoundationalServiceRegistry.vue +7 -6
- package/app/components/foundational/FoundationalServiceSources.vue +7 -6
- package/app/components/fragments/FragmentLibraryManager.vue +5 -4
- package/app/components/fragments/FragmentSelector.vue +12 -9
- package/app/components/gates/GateFailingCheckList.vue +4 -3
- package/app/components/gates/GateResultView.vue +17 -12
- package/app/components/github/AddServiceFromRepoModal.vue +11 -7
- package/app/components/github/GitHubOnboarding.vue +7 -2
- package/app/components/github/GitHubPanel.vue +9 -6
- package/app/components/github/RepoTreeBrowser.vue +15 -12
- package/app/components/humanTest/HumanTestWindow.vue +13 -9
- package/app/components/initiative/InitiativePlanReview.vue +28 -18
- package/app/components/initiative/InitiativeTrackerWindow.vue +171 -124
- package/app/components/judge/JudgeResultView.vue +5 -3
- package/app/components/kaizen/KaizenPanel.vue +67 -53
- package/app/components/layout/AccountTeamSettings.vue +9 -9
- package/app/components/layout/AppearanceSwitcher.vue +4 -3
- package/app/components/layout/BoardSwitcher.vue +10 -8
- package/app/components/layout/BoardToolbar.vue +5 -4
- package/app/components/layout/CommandBar.vue +4 -3
- package/app/components/layout/DefaultTestEnvBanner.vue +4 -3
- package/app/components/layout/IntegrationsHub.vue +8 -6
- package/app/components/layout/LanguageSwitcher.vue +4 -3
- package/app/components/layout/ModelProvidersHub.vue +4 -3
- package/app/components/layout/NotificationsInbox.vue +40 -30
- package/app/components/layout/PersonalSetupModal.vue +4 -3
- package/app/components/layout/RolePrompt.vue +4 -3
- package/app/components/layout/SideBar.vue +4 -3
- package/app/components/layout/TranslationWarningBanner.vue +4 -3
- package/app/components/layout/UiModeSwitcher.vue +8 -6
- package/app/components/layout/UiRoleSwitcher.vue +8 -6
- package/app/components/layout/WorkspaceMembersSettings.vue +2 -2
- package/app/components/media/ArtifactLightbox.vue +69 -39
- package/app/components/media/ImageCompare.vue +69 -74
- package/app/components/observability/OutcomeFilterChips.vue +5 -4
- package/app/components/observability/RunFailureSummary.vue +8 -6
- package/app/components/observability/StepModelActivity.vue +5 -3
- package/app/components/observability/ToolCallList.vue +4 -2
- package/app/components/outcome/OutcomeSummaryWindow.vue +5 -4
- package/app/components/palettes/AgentPalette.vue +18 -14
- package/app/components/panels/AgentStepDetail.vue +30 -19
- package/app/components/panels/ObservabilityPanel.vue +149 -114
- package/app/components/panels/OperatorDashboardPanel.vue +99 -91
- package/app/components/panels/ReportsPanel.vue +64 -61
- package/app/components/panels/ResultWindowShell.vue +148 -134
- package/app/components/panels/StepContainerStatus.vue +9 -9
- package/app/components/panels/StepDelegatedStatus.vue +11 -9
- package/app/components/panels/StepRestartControl.vue +3 -2
- package/app/components/panels/inspector/EpicChildren.vue +4 -3
- package/app/components/panels/inspector/FrontendConfig.vue +3 -2
- package/app/components/panels/inspector/InspectorSection.vue +22 -17
- package/app/components/panels/inspector/ServiceConnections.vue +3 -2
- package/app/components/panels/inspector/ServiceTestConfig.vue +5 -4
- package/app/components/panels/inspector/TaskExecution.vue +5 -4
- package/app/components/panels/inspector/TaskRunSettings.vue +10 -6
- package/app/components/pipeline/EstimateThresholdFields.vue +13 -10
- package/app/components/pipeline/GateConfigFields.vue +14 -18
- package/app/components/pipeline/PipelineBuilder.vue +90 -77
- package/app/components/pipeline/PipelinePicker.vue +8 -6
- package/app/components/pipeline/PipelineProgress.vue +26 -21
- package/app/components/prReview/PrReviewWindow.vue +46 -25
- package/app/components/providers/ApiKeysSection.vue +4 -3
- package/app/components/ralph/RalphLoopResultView.vue +4 -3
- package/app/components/requirements/RequirementsReviewWindow.vue +10 -6
- package/app/components/riskPolicy/RiskPolicyPicker.vue +8 -6
- package/app/components/sandbox/SandboxPanel.vue +73 -54
- package/app/components/settings/CloudflareHandlerSection.vue +33 -30
- package/app/components/settings/ConsensusGroupsSection.vue +40 -30
- package/app/components/settings/InfraHandlersConfigurator.vue +16 -13
- package/app/components/settings/InfrastructureBackendPicker.vue +17 -17
- package/app/components/settings/IssueTrackerPanel.vue +21 -6
- package/app/components/settings/LocalModelEndpointsPanel.vue +3 -2
- package/app/components/settings/ModelConfigurationPanel.vue +9 -8
- package/app/components/settings/OpenRouterCatalogPanel.vue +4 -3
- package/app/components/settings/ProviderPreferenceEditor.vue +5 -6
- package/app/components/settings/RiskPolicyEditorRow.vue +3 -2
- package/app/components/settings/ServiceFragmentDefaultsPanel.vue +10 -8
- package/app/components/skills/ReviewSkillQueue.vue +4 -3
- package/app/components/skills/SkillLibraryManager.vue +7 -6
- package/app/components/slack/SlackPanel.vue +9 -5
- package/app/components/spec/ServiceSpecWindow.vue +4 -3
- package/app/components/tasks/ContextIssuePicker.vue +12 -9
- package/app/components/tasks/TaskContextIssues.vue +4 -3
- package/app/components/testing/TestReportWindow.vue +14 -8
- package/app/components/theme/ThemeImportModal.vue +2 -2
- package/app/components/visualConfirm/VisualConfirmationWindow.vue +62 -35
- 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
|
@@ -9,6 +9,7 @@ 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'
|
|
@@ -440,27 +441,27 @@ const ITEM_ICON: Record<string, string> = {
|
|
|
440
441
|
are `board.write`, hidden for a read-only viewer, who keeps the status badge
|
|
441
442
|
(the one view-only affordance here). -->
|
|
442
443
|
<template v-if="access.canWriteBoard.value">
|
|
443
|
-
<
|
|
444
|
+
<IconButton
|
|
444
445
|
class="nodrag"
|
|
445
446
|
data-testid="frame-add-task"
|
|
446
447
|
:size="isTouch ? 'sm' : 'xs'"
|
|
447
448
|
variant="ghost"
|
|
448
449
|
color="neutral"
|
|
449
450
|
icon="i-lucide-plus"
|
|
450
|
-
:
|
|
451
|
+
:label="t('board.frame.addTaskTitle')"
|
|
451
452
|
@click.stop="addTask"
|
|
452
453
|
/>
|
|
453
|
-
<
|
|
454
|
+
<IconButton
|
|
454
455
|
v-if="tasks.anyOffered"
|
|
455
456
|
class="nodrag"
|
|
456
457
|
:size="isTouch ? 'sm' : 'xs'"
|
|
457
458
|
variant="ghost"
|
|
458
459
|
color="neutral"
|
|
459
460
|
icon="i-lucide-ticket"
|
|
460
|
-
:
|
|
461
|
+
:label="t('board.frame.createTaskFromIssueTitle')"
|
|
461
462
|
@click.stop="createTaskFromIssue"
|
|
462
463
|
/>
|
|
463
|
-
<
|
|
464
|
+
<IconButton
|
|
464
465
|
v-if="documents.connectedDesignSources.length > 0"
|
|
465
466
|
class="nodrag"
|
|
466
467
|
data-testid="frame-start-from-design"
|
|
@@ -468,7 +469,7 @@ const ITEM_ICON: Record<string, string> = {
|
|
|
468
469
|
variant="ghost"
|
|
469
470
|
color="neutral"
|
|
470
471
|
icon="i-lucide-frame"
|
|
471
|
-
:
|
|
472
|
+
:label="t('board.frame.startFromDesignTitle')"
|
|
472
473
|
@click.stop="startFromDesign"
|
|
473
474
|
/>
|
|
474
475
|
<!-- Recurring pipelines + initiatives are ADVANCED-tier authoring: both plan
|
|
@@ -479,7 +480,7 @@ const ITEM_ICON: Record<string, string> = {
|
|
|
479
480
|
badges its task card and opens its inspector panel, and an initiative is
|
|
480
481
|
still a block on the board with its own inspector — so what a basic-mode
|
|
481
482
|
user loses is the ability to author a new one, not sight of one. -->
|
|
482
|
-
<
|
|
483
|
+
<IconButton
|
|
483
484
|
v-if="uiMode.isAdvanced"
|
|
484
485
|
class="nodrag"
|
|
485
486
|
data-testid="frame-add-recurring"
|
|
@@ -487,10 +488,10 @@ const ITEM_ICON: Record<string, string> = {
|
|
|
487
488
|
variant="ghost"
|
|
488
489
|
color="neutral"
|
|
489
490
|
icon="i-lucide-repeat"
|
|
490
|
-
:
|
|
491
|
+
:label="t('board.frame.addRecurringTitle')"
|
|
491
492
|
@click.stop="addRecurring"
|
|
492
493
|
/>
|
|
493
|
-
<
|
|
494
|
+
<IconButton
|
|
494
495
|
v-if="uiMode.isAdvanced"
|
|
495
496
|
class="nodrag"
|
|
496
497
|
data-testid="frame-add-initiative"
|
|
@@ -498,7 +499,7 @@ const ITEM_ICON: Record<string, string> = {
|
|
|
498
499
|
variant="ghost"
|
|
499
500
|
color="neutral"
|
|
500
501
|
icon="i-lucide-milestone"
|
|
501
|
-
:
|
|
502
|
+
:label="t('board.frame.createInitiativeTitle')"
|
|
502
503
|
@click.stop="createInitiative"
|
|
503
504
|
/>
|
|
504
505
|
<!-- Hunting a tracker board for a bug worth adopting is TRIAGE, not intake: it
|
|
@@ -506,7 +507,7 @@ const ITEM_ICON: Record<string, string> = {
|
|
|
506
507
|
engineer/PM judgement call. So it is dropped for a narrowed role, whose three
|
|
507
508
|
routes in (a new task, a task from a named ticket, a task from a design) all
|
|
508
509
|
start from work somebody has already decided to do. -->
|
|
509
|
-
<
|
|
510
|
+
<IconButton
|
|
510
511
|
v-if="tasks.anyOffered && uiRole.fullSurface"
|
|
511
512
|
class="nodrag"
|
|
512
513
|
data-testid="frame-hunt-bugs"
|
|
@@ -514,7 +515,7 @@ const ITEM_ICON: Record<string, string> = {
|
|
|
514
515
|
variant="ghost"
|
|
515
516
|
color="neutral"
|
|
516
517
|
icon="i-lucide-radar"
|
|
517
|
-
:
|
|
518
|
+
:label="t('board.frame.huntBugsTitle')"
|
|
518
519
|
@click.stop="huntBugs"
|
|
519
520
|
/>
|
|
520
521
|
</template>
|
|
@@ -559,15 +560,16 @@ const ITEM_ICON: Record<string, string> = {
|
|
|
559
560
|
|
|
560
561
|
<FrameSwimlanes v-if="hasTasks" :frame-id="block.id" :lane-body-height="laneBodyHeight" />
|
|
561
562
|
|
|
562
|
-
<
|
|
563
|
+
<UButton
|
|
564
|
+
color="neutral"
|
|
565
|
+
variant="ghost"
|
|
563
566
|
v-if="!hasTasks && access.canWriteBoard.value"
|
|
564
|
-
type="button"
|
|
565
567
|
data-testid="frame-add-task-empty"
|
|
566
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"
|
|
567
569
|
@click.stop="addTask"
|
|
568
570
|
>
|
|
569
571
|
<UIcon name="i-lucide-plus" class="h-3.5 w-3.5" /> {{ t('board.frame.addFirstTask') }}
|
|
570
|
-
</
|
|
572
|
+
</UButton>
|
|
571
573
|
</div>
|
|
572
574
|
</div>
|
|
573
575
|
|
|
@@ -16,8 +16,9 @@ const displayLabel = computed(() => props.label ?? t('board.decisionBadge.decisi
|
|
|
16
16
|
</script>
|
|
17
17
|
|
|
18
18
|
<template>
|
|
19
|
-
<
|
|
20
|
-
|
|
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
|
-
</
|
|
30
|
+
</UButton>
|
|
30
31
|
</template>
|
|
@@ -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
|
-
<
|
|
71
|
-
|
|
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"
|
|
@@ -99,7 +100,7 @@ const withheldNote = computed(() => {
|
|
|
99
100
|
<span v-if="withheldNote" class="ms-auto truncate text-3xs text-dimmed">{{
|
|
100
101
|
withheldNote
|
|
101
102
|
}}</span>
|
|
102
|
-
</
|
|
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
|
|
@@ -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
|
|
|
@@ -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
|
-
<
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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
|
|
@@ -107,9 +107,10 @@ const ITEM_ICON: Record<string, string> = {
|
|
|
107
107
|
{{ t('board.task.buildSteps') }}
|
|
108
108
|
</SectionLabel>
|
|
109
109
|
<div v-for="(s, i) in steps" :key="i" class="rounded-sm bg-default/60 px-1.5 py-1">
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
<UButton
|
|
111
|
+
color="neutral"
|
|
112
|
+
variant="ghost"
|
|
113
|
+
class="flex w-full cursor-pointer items-center gap-1 rounded-sm p-0 text-start hover:bg-transparent"
|
|
113
114
|
:title="`${agentKindMeta(s.agentKind).label} — ${agentKindMeta(s.agentKind).description}\n${t('board.task.clickToViewStep')}`"
|
|
114
115
|
@click.stop="openStep(i)"
|
|
115
116
|
>
|
|
@@ -141,7 +142,7 @@ const ITEM_ICON: Record<string, string> = {
|
|
|
141
142
|
:class="stepSpinning(s) ? 'animate-spin' : ''"
|
|
142
143
|
:style="{ color: stepVisual(s).color }"
|
|
143
144
|
/>
|
|
144
|
-
</
|
|
145
|
+
</UButton>
|
|
145
146
|
|
|
146
147
|
<!-- PR reviewer: the precise sub-phase (slicing vs reviewing N/M slices), so the board
|
|
147
148
|
card tells the reviewer's progress apart from a bare subtask count. Self-hides once
|
|
@@ -155,20 +156,21 @@ const ITEM_ICON: Record<string, string> = {
|
|
|
155
156
|
|
|
156
157
|
<!-- pending approval gate: jump straight to the conclusions reader. Suppressed
|
|
157
158
|
while a reviewer gate is folding/re-reviewing in the background (no human needed). -->
|
|
158
|
-
<
|
|
159
|
+
<UButton
|
|
160
|
+
color="neutral"
|
|
161
|
+
variant="ghost"
|
|
159
162
|
v-if="
|
|
160
163
|
s.approval &&
|
|
161
164
|
s.approval.status === 'pending' &&
|
|
162
165
|
instance &&
|
|
163
166
|
!reviews.isBackground(s.agentKind, props.taskId)
|
|
164
167
|
"
|
|
165
|
-
type="button"
|
|
166
168
|
class="mt-1 flex w-full items-center justify-center gap-1 rounded-sm bg-app-warning-500 px-1.5 py-0.5 text-3xs font-semibold text-app-warning-50 dark:text-app-warning-950 transition hover:bg-app-warning-400"
|
|
167
169
|
@click.stop="ui.openApprovalDetail(instance.id, s.approval.id)"
|
|
168
170
|
>
|
|
169
171
|
<UIcon name="i-lucide-shield-check" class="h-2.5 w-2.5" />
|
|
170
172
|
{{ t('board.task.reviewAndApprove') }}
|
|
171
|
-
</
|
|
173
|
+
</UButton>
|
|
172
174
|
|
|
173
175
|
<!-- per-step subtask progress bar -->
|
|
174
176
|
<div
|
|
@@ -225,31 +225,40 @@ watch(open, (isOpen) => {
|
|
|
225
225
|
<p v-if="recordsDropped > 0" class="text-app-warning-300/90">
|
|
226
226
|
{{ t('bootstrap.adoption.survey.truncated', { count: recordsDropped }) }}
|
|
227
227
|
</p>
|
|
228
|
-
<
|
|
229
|
-
<
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
228
|
+
<UCollapsible>
|
|
229
|
+
<template #default="{ open }">
|
|
230
|
+
<UButton
|
|
231
|
+
variant="link"
|
|
232
|
+
color="neutral"
|
|
233
|
+
size="xs"
|
|
234
|
+
:icon="open ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
|
|
235
|
+
:label="t('bootstrap.adoption.survey.show')"
|
|
236
|
+
:ui="{ base: 'w-full justify-start gap-1 p-0 text-dimmed hover:text-toned' }"
|
|
237
|
+
/>
|
|
238
|
+
</template>
|
|
239
|
+
<template #content>
|
|
240
|
+
<ul class="mt-2 space-y-1">
|
|
241
|
+
<!-- Keyed by POSITION: the transcript is append-only and rendered in order, and
|
|
234
242
|
the same path legitimately appears twice (a body refused by the seed and then
|
|
235
243
|
served to the model, a path the model retried). Keying on the path patched
|
|
236
244
|
those two rows against each other and rendered a note beside the wrong one. -->
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
>
|
|
242
|
-
<span
|
|
243
|
-
class="shrink-0 font-mono text-3xs uppercase"
|
|
244
|
-
:class="entry.outcome === 'read' ? 'text-dimmed' : 'text-app-warning-400/80'"
|
|
245
|
+
<li
|
|
246
|
+
v-for="(entry, index) in surveyReads"
|
|
247
|
+
:key="index"
|
|
248
|
+
class="flex items-baseline gap-2"
|
|
245
249
|
>
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
250
|
+
<span
|
|
251
|
+
class="shrink-0 font-mono text-3xs uppercase"
|
|
252
|
+
:class="entry.outcome === 'read' ? 'text-dimmed' : 'text-app-warning-400/80'"
|
|
253
|
+
>
|
|
254
|
+
{{ t(`bootstrap.adoption.survey.outcome.${entry.outcome}`) }}
|
|
255
|
+
</span>
|
|
256
|
+
<span class="font-mono text-muted">{{ entry.path }}</span>
|
|
257
|
+
<span v-if="entry.note" class="text-app-600">{{ entry.note }}</span>
|
|
258
|
+
</li>
|
|
259
|
+
</ul>
|
|
260
|
+
</template>
|
|
261
|
+
</UCollapsible>
|
|
253
262
|
</div>
|
|
254
263
|
|
|
255
264
|
<!-- The reviewer's own instructions, on BOTH paths. With no suggestion to answer this is
|
|
@@ -22,6 +22,7 @@ import RepoTreeBrowser from '~/components/github/RepoTreeBrowser.vue'
|
|
|
22
22
|
import VcsConnectSurfaces from '~/components/vcs/VcsConnectSurfaces.vue'
|
|
23
23
|
import { appInstallationManageUrl, newRepoUrl, VCS_PROVIDER_LABELS } from '~/utils/vcs'
|
|
24
24
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
25
|
+
import IconButton from '~/components/common/IconButton.vue'
|
|
25
26
|
|
|
26
27
|
const ui = useUiStore()
|
|
27
28
|
const bootstrap = useBootstrapStore()
|
|
@@ -701,13 +702,12 @@ const statusLabel = computed<Record<BootstrapStatus, string>>(() => ({
|
|
|
701
702
|
:placeholder="t('bootstrap.monorepo.directory.placeholder')"
|
|
702
703
|
class="flex-1"
|
|
703
704
|
/>
|
|
704
|
-
<
|
|
705
|
+
<IconButton
|
|
705
706
|
v-if="monorepoRepoId !== undefined"
|
|
706
707
|
variant="soft"
|
|
707
708
|
color="neutral"
|
|
708
709
|
icon="i-lucide-folder-search"
|
|
709
|
-
:
|
|
710
|
-
:aria-label="t('bootstrap.monorepo.directory.browse')"
|
|
710
|
+
:label="t('bootstrap.monorepo.directory.browse')"
|
|
711
711
|
data-testid="bootstrap-directory-browse"
|
|
712
712
|
@click="toggleDirectoryBrowse()"
|
|
713
713
|
/>
|
|
@@ -465,9 +465,11 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
|
|
|
465
465
|
`width` prop): the window is `full`-width now, and this is continuous prose that
|
|
466
466
|
would otherwise run to 200-character lines. -->
|
|
467
467
|
<div v-for="s in outline.sections" :key="s.id" class="mb-2 max-w-3xl">
|
|
468
|
-
<
|
|
468
|
+
<UButton
|
|
469
|
+
color="neutral"
|
|
470
|
+
variant="ghost"
|
|
469
471
|
v-if="s.title"
|
|
470
|
-
class="group flex w-full items-center gap-2 text-start"
|
|
472
|
+
class="group flex w-full items-center gap-2 p-0 text-start hover:bg-transparent"
|
|
471
473
|
@click="toggle(s.id)"
|
|
472
474
|
>
|
|
473
475
|
<UIcon
|
|
@@ -480,7 +482,7 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
|
|
|
480
482
|
:class="s.depth <= 1 ? 'text-base' : s.depth === 2 ? 'text-sm' : 'text-xs'"
|
|
481
483
|
v-html="s.titleHtml"
|
|
482
484
|
/>
|
|
483
|
-
</
|
|
485
|
+
</UButton>
|
|
484
486
|
<div
|
|
485
487
|
v-show="!s.title || !collapsed[s.id]"
|
|
486
488
|
class="reader-prose mt-1 ps-5.5 text-sm leading-relaxed text-toned"
|
|
@@ -267,8 +267,9 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
267
267
|
<SectionLabel as="p" class="mb-2 px-1">
|
|
268
268
|
{{ t('bugFishing.phases.heading') }}
|
|
269
269
|
</SectionLabel>
|
|
270
|
-
<
|
|
271
|
-
|
|
270
|
+
<UButton
|
|
271
|
+
color="neutral"
|
|
272
|
+
variant="ghost"
|
|
272
273
|
class="mb-1 w-full rounded-md px-2 py-1.5 text-left text-xs"
|
|
273
274
|
:class="
|
|
274
275
|
selectedPassKey === null
|
|
@@ -278,7 +279,7 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
278
279
|
@click="selectedPassKey = null"
|
|
279
280
|
>
|
|
280
281
|
{{ t('bugFishing.phases.all', { count: findings.length }) }}
|
|
281
|
-
</
|
|
282
|
+
</UButton>
|
|
282
283
|
<!-- Grouped by TERRITORY on a partitioned codebase. A codebase small enough to fish
|
|
283
284
|
whole has one group with no label, which renders as the flat angle rail. -->
|
|
284
285
|
<div v-for="group in territoryGroups" :key="group.id ?? 'whole'" class="mb-2">
|
|
@@ -291,8 +292,9 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
291
292
|
</p>
|
|
292
293
|
<ul class="space-y-0.5">
|
|
293
294
|
<li v-for="phase in group.passes" :key="passKey(phase)">
|
|
294
|
-
<
|
|
295
|
-
|
|
295
|
+
<UButton
|
|
296
|
+
color="neutral"
|
|
297
|
+
variant="ghost"
|
|
296
298
|
class="flex w-full items-start gap-2 rounded-md px-2 py-1.5 text-left"
|
|
297
299
|
:class="
|
|
298
300
|
selectedPassKey === passKey(phase)
|
|
@@ -341,7 +343,7 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
341
343
|
</span>
|
|
342
344
|
</span>
|
|
343
345
|
</span>
|
|
344
|
-
</
|
|
346
|
+
</UButton>
|
|
345
347
|
</li>
|
|
346
348
|
</ul>
|
|
347
349
|
</div>
|
|
@@ -450,10 +452,7 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
450
452
|
<p class="text-2xs text-muted">
|
|
451
453
|
{{ t('bugFishing.counts', { untriaged: untriagedCount, spawned: spawnedCount }) }}
|
|
452
454
|
</p>
|
|
453
|
-
<
|
|
454
|
-
<input v-model="showTriaged" type="checkbox" class="accent-app-info-500" />
|
|
455
|
-
{{ t('bugFishing.showTriaged') }}
|
|
456
|
-
</label>
|
|
455
|
+
<UCheckbox v-model="showTriaged" size="xs" :label="t('bugFishing.showTriaged')" />
|
|
457
456
|
</div>
|
|
458
457
|
|
|
459
458
|
<p v-if="bugFishing.error" class="mb-3 text-xs text-app-error-300">
|
|
@@ -520,19 +519,41 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
520
519
|
<!-- Evidence is rendered apart from the detail for the reason the contract keeps them
|
|
521
520
|
apart: a finding that cannot point at the code it describes is speculating, and
|
|
522
521
|
that should be visible without reading the prose for it. -->
|
|
523
|
-
<
|
|
524
|
-
<
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
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>
|
|
536
557
|
|
|
537
558
|
<!-- Already marked: say what was created and let the reader follow it. The three
|
|
538
559
|
spawn states are rendered apart because they are three different facts — a task
|
|
@@ -553,13 +574,14 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
553
574
|
})
|
|
554
575
|
}}
|
|
555
576
|
</span>
|
|
556
|
-
<
|
|
557
|
-
|
|
558
|
-
|
|
577
|
+
<UButton
|
|
578
|
+
color="neutral"
|
|
579
|
+
variant="link"
|
|
580
|
+
class="p-0 text-2xs underline hover:text-app-success-200"
|
|
559
581
|
@click="openSpawnedTask(finding.spawn.taskId)"
|
|
560
582
|
>
|
|
561
583
|
{{ t('bugFishing.finding.openTask') }}
|
|
562
|
-
</
|
|
584
|
+
</UButton>
|
|
563
585
|
</div>
|
|
564
586
|
|
|
565
587
|
<!-- A claim held by a marking still in flight. No task to link yet, and no Fix
|
|
@@ -491,9 +491,11 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
|
|
|
491
491
|
`width` prop): the window is `full`-width now, and this is continuous prose that
|
|
492
492
|
would otherwise run to 200-character lines. -->
|
|
493
493
|
<div v-for="s in outline.sections" :key="s.id" class="mb-2 max-w-3xl">
|
|
494
|
-
<
|
|
494
|
+
<UButton
|
|
495
|
+
color="neutral"
|
|
496
|
+
variant="ghost"
|
|
495
497
|
v-if="s.title"
|
|
496
|
-
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"
|
|
497
499
|
@click="toggle(s.id)"
|
|
498
500
|
>
|
|
499
501
|
<UIcon
|
|
@@ -506,7 +508,7 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
|
|
|
506
508
|
:class="s.depth <= 1 ? 'text-base' : s.depth === 2 ? 'text-sm' : 'text-xs'"
|
|
507
509
|
v-html="s.titleHtml"
|
|
508
510
|
/>
|
|
509
|
-
</
|
|
511
|
+
</UButton>
|
|
510
512
|
<div
|
|
511
513
|
v-show="!s.title || !collapsed[s.id]"
|
|
512
514
|
class="reader-prose mt-1 ps-5.5 text-sm leading-relaxed text-toned"
|
|
@@ -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>
|
|
@@ -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>
|