@cat-factory/app 0.311.2 → 0.312.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 +92 -4
- package/app/app.config.ts +7 -0
- package/app/components/assistant/AssistantModal.vue +2 -5
- package/app/components/auth/AuthGate.vue +2 -1
- package/app/components/binaryCandidates/BinaryCandidatesWindow.vue +14 -15
- package/app/components/binaryOutput/StoredAssetView.vue +1 -3
- package/app/components/board/AddTaskModal.vue +3 -10
- package/app/components/board/AgentFailureCard.vue +4 -5
- package/app/components/board/AgentStopButton.vue +5 -7
- package/app/components/board/BoardCanvas.vue +31 -30
- package/app/components/board/CreateInitiativeModal.vue +2 -4
- package/app/components/board/RecurringPipelineModal.vue +2 -7
- package/app/components/board/nodes/AgentChip.vue +3 -2
- package/app/components/board/nodes/BlockNode.vue +14 -11
- package/app/components/board/nodes/InitiativeCard.vue +6 -3
- package/app/components/board/nodes/TaskCard.vue +45 -212
- package/app/components/bootstrap/AdoptionReviewModal.vue +2 -5
- package/app/components/bootstrap/BootstrapModal.vue +6 -15
- package/app/components/bootstrap/BootstrapRunSteps.vue +3 -1
- package/app/components/brainstorm/BrainstormWindow.vue +32 -26
- package/app/components/bugFishing/BugFishingWindow.vue +15 -13
- package/app/components/clarity/ClarityReviewWindow.vue +31 -26
- package/app/components/common/ClarificationItem.vue +3 -1
- package/app/components/common/EmptyState.vue +41 -19
- package/app/components/common/InterviewGateNotice.vue +3 -1
- package/app/components/common/Spinner.vue +28 -0
- package/app/components/consensus/ConsensusSessionWindow.vue +5 -5
- package/app/components/docs/DocInterviewWindow.vue +6 -6
- package/app/components/documents/ContextDocumentPicker.vue +2 -2
- package/app/components/documents/DocumentImportModal.vue +9 -8
- package/app/components/documents/DocumentSourceConnectModal.vue +10 -10
- package/app/components/documents/DocumentTemplatesModal.vue +13 -7
- package/app/components/documents/RepoContextDocPicker.vue +2 -2
- package/app/components/documents/SpawnPreviewModal.vue +4 -6
- package/app/components/documents/StartFromDesignModal.vue +2 -1
- package/app/components/documents/TaskContextDocs.vue +4 -5
- package/app/components/environments/EnvironmentStatusPanel.vue +11 -8
- package/app/components/environments/steps/EnvPickStep.vue +7 -3
- package/app/components/environments/steps/EnvPreflightStep.vue +6 -5
- package/app/components/focus/BlockFocusView.vue +6 -5
- package/app/components/followUp/FollowUpWindow.vue +7 -9
- package/app/components/forkDecision/ForkDecisionWindow.vue +9 -8
- package/app/components/foundational/FoundationalServiceCatalogList.vue +9 -5
- package/app/components/foundational/FoundationalServiceRegistry.vue +11 -7
- package/app/components/foundational/FoundationalServiceSources.vue +20 -22
- package/app/components/foundational/FoundationalSuppressions.vue +2 -2
- package/app/components/foundational/ServiceCatalogConnection.vue +15 -15
- package/app/components/fragments/FragmentLibraryManager.vue +36 -34
- package/app/components/fragments/FragmentSelector.vue +7 -3
- package/app/components/gates/GateResultView.vue +6 -9
- package/app/components/github/AddServiceFromRepoModal.vue +6 -10
- package/app/components/github/GitHubConnect.vue +10 -17
- package/app/components/github/GitHubPanel.vue +44 -47
- package/app/components/github/RepoTreeBrowser.vue +9 -5
- package/app/components/guidedReview/GuidedReviewModal.vue +16 -6
- package/app/components/guidedReview/GuidedReviewOverview.vue +2 -1
- package/app/components/guidedReview/GuidedReviewPicker.vue +10 -7
- package/app/components/guidedReview/GuidedReviewThread.vue +9 -4
- package/app/components/humanTest/HumanTestWindow.vue +2 -1
- package/app/components/initiative/InitiativePlanningWindow.vue +6 -6
- package/app/components/initiative/InitiativeTrackerWindow.vue +11 -11
- package/app/components/judge/JudgeResultView.vue +2 -7
- package/app/components/kaizen/KaizenPanel.vue +20 -5
- package/app/components/kaizen/KaizenStepStatus.vue +2 -1
- package/app/components/layout/AccountAuditLog.vue +11 -3
- package/app/components/layout/AccountDeploymentSettings.vue +17 -62
- package/app/components/layout/AccountFailureKindRules.vue +6 -5
- package/app/components/layout/AccountModelPolicySettings.vue +2 -6
- package/app/components/layout/AccountPlatformAlertSettings.vue +5 -13
- package/app/components/layout/AccountRiskPolicySettings.vue +13 -14
- package/app/components/layout/AccountRunCredentialSettings.vue +2 -2
- package/app/components/layout/AccountTeamSettings.vue +12 -9
- package/app/components/layout/BoardSwitcher.vue +2 -2
- package/app/components/layout/BoardToolbar.vue +27 -37
- package/app/components/layout/BoardTopOverlays.vue +8 -2
- package/app/components/layout/CommandBar.vue +7 -3
- package/app/components/layout/ConnectionStatusBanner.vue +2 -1
- package/app/components/layout/IntegrationsHub.vue +7 -3
- package/app/components/layout/ModelProvidersHub.vue +7 -3
- package/app/components/layout/NotificationsInbox.vue +3 -11
- package/app/components/layout/SideBar.vue +5 -0
- package/app/components/layout/WorkViewSwitcher.vue +64 -0
- package/app/components/layout/WorkspaceMembersSettings.vue +12 -5
- package/app/components/media/ArtifactLightbox.vue +4 -3
- package/app/components/notifications/NotificationSettingsPanel.vue +4 -11
- package/app/components/observability/ToolCallList.vue +12 -17
- package/app/components/panels/AgentStepDetail.vue +5 -5
- package/app/components/panels/GenericStructuredResultView.vue +6 -9
- package/app/components/panels/InspectorPanel.vue +2 -1
- package/app/components/panels/MergerResultView.vue +5 -6
- package/app/components/panels/ObservabilityPanel.vue +26 -41
- package/app/components/panels/OperatorDashboardPanel.vue +29 -17
- package/app/components/panels/ReportsPanel.vue +17 -11
- package/app/components/panels/ReportsSpendBreakdown.vue +7 -3
- package/app/components/panels/RunDetailLoadState.vue +4 -3
- package/app/components/panels/StepContainerStatus.vue +4 -3
- package/app/components/panels/StepDelegatedStatus.vue +3 -2
- package/app/components/panels/StepMetadataCard.vue +6 -8
- package/app/components/panels/StepRunMeta.vue +2 -5
- package/app/components/panels/inspector/ContainerSummary.vue +7 -1
- package/app/components/panels/inspector/EpicChildren.vue +7 -3
- package/app/components/panels/inspector/FrontendConfig.vue +7 -3
- package/app/components/panels/inspector/InitiativeInspector.vue +5 -2
- package/app/components/panels/inspector/ServiceConnections.vue +7 -3
- package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +2 -6
- package/app/components/panels/inspector/ServiceTestSecrets.vue +2 -6
- package/app/components/panels/inspector/ServiceTestingContext.vue +2 -6
- package/app/components/panels/inspector/ServiceValidationConfig.vue +38 -39
- package/app/components/panels/inspector/TaskExecution.vue +5 -6
- package/app/components/panels/inspector/TaskRunSettings.vue +7 -3
- package/app/components/panels/inspector/TaskStructure.vue +0 -1
- package/app/components/pipeline/AgentPromptEditor.vue +25 -18
- package/app/components/pipeline/PipelineBuilder.vue +14 -24
- package/app/components/pipeline/PipelineProgress.vue +16 -17
- package/app/components/prReview/PrReviewPhaseBadge.vue +3 -2
- package/app/components/prReview/PrReviewWindow.vue +16 -20
- package/app/components/providers/ApiKeysSection.vue +2 -6
- package/app/components/providers/PersonalCredentialModal.vue +2 -6
- package/app/components/providers/PersonalSubscriptionSection.vue +3 -7
- package/app/components/providers/VendorCredentialsModal.vue +2 -6
- package/app/components/provisioning/ProvisioningLogsDrawer.vue +10 -6
- package/app/components/queue/QueueCard.vue +269 -0
- package/app/components/queue/QueueView.vue +192 -0
- package/app/components/ralph/RalphLoopResultView.vue +2 -7
- package/app/components/requirements/RequirementsReviewWindow.vue +44 -39
- package/app/components/sandbox/SandboxPanel.vue +37 -25
- package/app/components/settings/AccountSettingsPanel.vue +6 -3
- package/app/components/settings/ApiTokensPanel.vue +2 -6
- package/app/components/settings/BudgetSettings.vue +2 -2
- package/app/components/settings/CapabilityCredentialsPanel.vue +2 -6
- package/app/components/settings/CloudflareHandlerSection.vue +3 -7
- package/app/components/settings/ConsensusGroupsSection.vue +7 -3
- package/app/components/settings/CustomManifestTypeEditor.vue +7 -3
- package/app/components/settings/DefaultProvisionTypeSection.vue +2 -2
- package/app/components/settings/InfraHandlersConfigurator.vue +8 -11
- package/app/components/settings/IssueTrackerPanel.vue +9 -9
- package/app/components/settings/LocalContainerPoolSettings.vue +2 -6
- package/app/components/settings/LocalModelEndpointsPanel.vue +5 -7
- package/app/components/settings/McpAuthorizeScreen.vue +8 -3
- package/app/components/settings/McpOAuthCallbackScreen.vue +2 -1
- package/app/components/settings/ModelConfigurationPanel.vue +45 -17
- package/app/components/settings/ObservabilityConnectionPanel.vue +4 -15
- package/app/components/settings/OpenRouterCatalogPanel.vue +8 -24
- package/app/components/settings/PackageRegistriesPanel.vue +2 -6
- package/app/components/settings/ProviderConnectionTab.vue +3 -7
- package/app/components/settings/RiskPolicyPanel.vue +4 -16
- package/app/components/settings/SharedStacksPanel.vue +8 -18
- package/app/components/settings/TaskTypeSuppressionsPanel.vue +10 -6
- package/app/components/settings/UsageSettings.vue +11 -2
- package/app/components/settings/UserSecretsSection.vue +5 -7
- package/app/components/settings/WorkspaceMetadataSettings.vue +9 -9
- package/app/components/settings/WorkspaceSettingsPanel.vue +3 -11
- package/app/components/skills/ReviewSkillQueue.vue +7 -3
- package/app/components/skills/SkillLibraryManager.vue +20 -18
- package/app/components/slack/SlackPanel.vue +5 -8
- package/app/components/spec/ServiceSpecWindow.vue +32 -29
- package/app/components/tasks/BugHuntModal.vue +23 -19
- package/app/components/tasks/ContextIssuePicker.vue +2 -2
- package/app/components/tasks/TaskContextIssues.vue +4 -5
- package/app/components/tasks/TaskImportModal.vue +10 -8
- package/app/components/tasks/TaskSourceConnectModal.vue +11 -11
- package/app/components/testing/TestReportWindow.vue +6 -12
- package/app/components/tutorial/TutorialCatalogue.vue +7 -3
- package/app/components/tutorial/TutorialOverlay.vue +2 -1
- package/app/components/tutorial/TutorialPrompt.vue +7 -3
- package/app/components/vcs/GitLabConnect.vue +2 -6
- package/app/components/visualConfirm/VisualConfirmationWindow.vue +9 -4
- package/app/composables/toastTitleKeys.spec.ts +143 -0
- package/app/composables/useActionToast.spec.ts +71 -0
- package/app/composables/useActionToast.ts +101 -0
- package/app/composables/useBlockDeletion.ts +3 -6
- package/app/composables/useConfirmAction.ts +5 -3
- package/app/composables/useContextLinking.spec.ts +3 -0
- package/app/composables/useContextLinking.ts +11 -16
- package/app/composables/useCopyToClipboard.ts +4 -3
- package/app/composables/useFrameLanes.ts +6 -82
- package/app/composables/useNavContributions.ts +6 -5
- package/app/composables/usePipelineErrorToast.spec.ts +24 -0
- package/app/composables/usePipelineErrorToast.ts +26 -2
- package/app/composables/usePipelineLibraryActions.ts +8 -9
- package/app/composables/useTaskActions.ts +254 -0
- package/app/composables/useTaskLaneClassifier.ts +88 -0
- package/app/modular/nav-contributions.spec.ts +4 -0
- package/app/modular/nav-contributions.ts +31 -0
- package/app/modular/tutorial-tours.ts +10 -0
- package/app/pages/index.vue +42 -8
- package/app/stores/board/context.ts +4 -3
- package/app/stores/board/placement.ts +8 -20
- package/app/stores/board/removal.ts +4 -13
- package/app/stores/board.spec.ts +10 -1
- package/app/stores/board.ts +3 -2
- package/app/stores/workView.ts +51 -0
- package/app/stores/workspaceQueue.ts +148 -0
- package/app/utils/bootstrapSteps.ts +11 -2
- package/app/utils/pipelineRender.ts +21 -8
- package/app/utils/queueSections.spec.ts +111 -0
- package/app/utils/queueSections.ts +117 -0
- package/app/utils/workView.spec.ts +16 -0
- package/app/utils/workView.ts +25 -0
- package/i18n/locales/de.json +73 -32
- package/i18n/locales/en.json +73 -32
- package/i18n/locales/es.json +73 -32
- package/i18n/locales/fr.json +73 -32
- package/i18n/locales/he.json +73 -32
- package/i18n/locales/it.json +73 -32
- package/i18n/locales/ja.json +73 -32
- package/i18n/locales/pl.json +73 -32
- package/i18n/locales/tr.json +73 -32
- package/i18n/locales/uk.json +73 -32
- package/package.json +1 -1
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
} from './ServiceSpecWindow.logic'
|
|
24
24
|
import type { BadgeColor } from '~/utils/badge'
|
|
25
25
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
26
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
26
27
|
|
|
27
28
|
const { t } = useI18n()
|
|
28
29
|
const board = useBoardStore()
|
|
@@ -225,12 +226,14 @@ function kindLabel(item: RequirementItem): string {
|
|
|
225
226
|
</template>
|
|
226
227
|
|
|
227
228
|
<!-- loading -->
|
|
228
|
-
<div
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
<
|
|
233
|
-
|
|
229
|
+
<div v-if="loading && !view" class="flex min-h-0 flex-1">
|
|
230
|
+
<div class="w-64 shrink-0 space-y-2 border-e border-default px-3 py-4">
|
|
231
|
+
<USkeleton v-for="n in 6" :key="n" class="h-6 w-full" />
|
|
232
|
+
</div>
|
|
233
|
+
<div class="flex-1 space-y-3 px-6 py-4">
|
|
234
|
+
<USkeleton class="h-6 w-1/3" />
|
|
235
|
+
<USkeleton v-for="n in 4" :key="n" class="h-4 w-full" />
|
|
236
|
+
</div>
|
|
234
237
|
</div>
|
|
235
238
|
|
|
236
239
|
<!-- error -->
|
|
@@ -253,18 +256,13 @@ function kindLabel(item: RequirementItem): string {
|
|
|
253
256
|
</div>
|
|
254
257
|
|
|
255
258
|
<!-- empty: no spec on the repo's default branch yet -->
|
|
256
|
-
<
|
|
259
|
+
<EmptyState
|
|
257
260
|
v-else-if="!present"
|
|
258
|
-
class="flex
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
<p class="mx-auto mt-1 max-w-md text-xs text-dimmed">
|
|
264
|
-
{{ t('spec.empty.description') }}
|
|
265
|
-
</p>
|
|
266
|
-
</div>
|
|
267
|
-
</div>
|
|
261
|
+
class="flex-1"
|
|
262
|
+
icon="i-lucide-scroll-text"
|
|
263
|
+
:title="t('spec.empty.title')"
|
|
264
|
+
:description="t('spec.empty.description')"
|
|
265
|
+
/>
|
|
268
266
|
|
|
269
267
|
<!-- spec body: navigable tree + detail -->
|
|
270
268
|
<div v-else class="flex min-h-0 flex-1">
|
|
@@ -368,19 +366,22 @@ function kindLabel(item: RequirementItem): string {
|
|
|
368
366
|
v-if="selectedFeature"
|
|
369
367
|
class="mt-4 overflow-x-auto rounded-lg border border-default bg-app-950/60 p-4 text-xs leading-relaxed text-default"
|
|
370
368
|
><code>{{ selectedFeature.content }}</code></pre>
|
|
371
|
-
<
|
|
369
|
+
<EmptyState
|
|
372
370
|
v-else
|
|
373
|
-
class="mt-4
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
371
|
+
class="mt-4"
|
|
372
|
+
icon="i-lucide-file-code"
|
|
373
|
+
:title="t('spec.noGherkinForGroup')"
|
|
374
|
+
/>
|
|
377
375
|
</template>
|
|
378
376
|
|
|
379
377
|
<!-- STRUCTURED view: requirements + acceptance + domain rules -->
|
|
380
378
|
<template v-else>
|
|
381
|
-
<
|
|
382
|
-
|
|
383
|
-
|
|
379
|
+
<EmptyState
|
|
380
|
+
v-if="reqCount(selectedGroup) === 0"
|
|
381
|
+
class="mt-4"
|
|
382
|
+
icon="i-lucide-list-checks"
|
|
383
|
+
:title="t('spec.noRequirements')"
|
|
384
|
+
/>
|
|
384
385
|
<!-- per-group implementation-state rollup + the filter over the two halves -->
|
|
385
386
|
<div
|
|
386
387
|
v-else
|
|
@@ -421,12 +422,14 @@ function kindLabel(item: RequirementItem): string {
|
|
|
421
422
|
rendering a blank pane that reads like "no requirements". The filter is sticky
|
|
422
423
|
across groups, so the reader may have set it several groups ago — offer the way
|
|
423
424
|
back here rather than making them find the toggle again. -->
|
|
424
|
-
<
|
|
425
|
+
<EmptyState
|
|
425
426
|
v-if="reqCount(selectedGroup) > 0 && visibleRequirements.length === 0"
|
|
426
|
-
class="mt-4
|
|
427
|
+
class="mt-4"
|
|
428
|
+
compact
|
|
429
|
+
icon="i-lucide-filter-x"
|
|
430
|
+
:title="t('spec.state.noneMatchFilter')"
|
|
427
431
|
data-testid="spec-state-filter-empty"
|
|
428
432
|
>
|
|
429
|
-
{{ t('spec.state.noneMatchFilter') }}
|
|
430
433
|
<UButton
|
|
431
434
|
variant="link"
|
|
432
435
|
size="xs"
|
|
@@ -440,7 +443,7 @@ function kindLabel(item: RequirementItem): string {
|
|
|
440
443
|
>
|
|
441
444
|
{{ t('spec.state.showAll') }}
|
|
442
445
|
</UButton>
|
|
443
|
-
</
|
|
446
|
+
</EmptyState>
|
|
444
447
|
<ul class="mt-4 space-y-4">
|
|
445
448
|
<li
|
|
446
449
|
v-for="req in visibleRequirements"
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
sourceMenuItems,
|
|
44
44
|
} from '~/utils/sourcePicker'
|
|
45
45
|
import IntegrationBackTitle from '~/components/layout/IntegrationBackTitle.vue'
|
|
46
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
46
47
|
import { appliesIntakePredicate } from '~/utils/intakePredicates'
|
|
47
48
|
import { boardFromService as isBoardFromService, huntRequest } from './BugHuntModal.logic'
|
|
48
49
|
|
|
@@ -52,8 +53,8 @@ const tasks = useTasksStore()
|
|
|
52
53
|
const hunt = useBugHuntStore()
|
|
53
54
|
const board = useBoardStore()
|
|
54
55
|
const github = useGitHubStore()
|
|
55
|
-
const
|
|
56
|
-
const { present } = usePipelineErrorToast()
|
|
56
|
+
const actionToast = useActionToast()
|
|
57
|
+
const { present, presentReported } = usePipelineErrorToast()
|
|
57
58
|
|
|
58
59
|
const open = computed({
|
|
59
60
|
get: () => ui.bugHunt !== null,
|
|
@@ -195,8 +196,12 @@ const REFUSAL_KEYS: Partial<Record<TaskSourceReadReason, string>> = {
|
|
|
195
196
|
board_from_service: 'bugHunt.refusal.boardFromService',
|
|
196
197
|
missing_board: 'bugHunt.refusal.missingBoard',
|
|
197
198
|
}
|
|
199
|
+
/** The translated key for a refusal reason this modal recognises, or undefined. */
|
|
200
|
+
function refusalKey(reason: string | null): string | undefined {
|
|
201
|
+
return reason ? REFUSAL_KEYS[reason as TaskSourceReadReason] : undefined
|
|
202
|
+
}
|
|
198
203
|
function refusalText(reason: string | null, fallback: string | null): string | null {
|
|
199
|
-
const key = reason
|
|
204
|
+
const key = refusalKey(reason)
|
|
200
205
|
return key ? t(key) : fallback
|
|
201
206
|
}
|
|
202
207
|
|
|
@@ -278,12 +283,11 @@ async function runHunt() {
|
|
|
278
283
|
// `repo_not_linked` is worded by the panel itself beside the scope it invalidates.
|
|
279
284
|
const attempt = await hunt.hunt(source.value, input)
|
|
280
285
|
if (attempt === 'failed' && !huntNeedsRepo.value) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
})
|
|
286
|
+
// A recognised refusal reads in translated copy; anything else is the backend's prose, which
|
|
287
|
+
// is detail behind the disclosure rather than the headline.
|
|
288
|
+
const reasonKey = refusalKey(hunt.huntErrorReason)
|
|
289
|
+
if (reasonKey) actionToast.error('bugHunt.huntFailed', { description: t(reasonKey) })
|
|
290
|
+
else presentReported(hunt.huntError, 'bugHunt.huntFailed')
|
|
287
291
|
}
|
|
288
292
|
}
|
|
289
293
|
|
|
@@ -294,10 +298,9 @@ async function adopt(candidate: BugHuntCandidate) {
|
|
|
294
298
|
if (!blockId) return // the user cancelled the personal-credential prompt
|
|
295
299
|
ui.closeBugHunt()
|
|
296
300
|
ui.select(blockId)
|
|
297
|
-
|
|
298
|
-
|
|
301
|
+
actionToast.success('bugHunt.adopted', {
|
|
302
|
+
params: { id: candidate.externalId },
|
|
299
303
|
description: t('bugHunt.adoptedRunning'),
|
|
300
|
-
icon: 'i-lucide-bug-play',
|
|
301
304
|
})
|
|
302
305
|
} catch (e) {
|
|
303
306
|
present(e, 'bugHunt.adoptFailed')
|
|
@@ -338,9 +341,7 @@ const STATUS_KEYS: Record<BugHuntAnalysisStatus, string> = {
|
|
|
338
341
|
</template>
|
|
339
342
|
<template #body>
|
|
340
343
|
<!-- No tracker offered (none connected/installed, or all disabled). -->
|
|
341
|
-
<
|
|
342
|
-
<UIcon name="i-lucide-plug" class="mx-auto h-8 w-8 text-dimmed" />
|
|
343
|
-
<p class="text-sm text-muted">{{ t('bugHunt.connectFirst') }}</p>
|
|
344
|
+
<EmptyState v-if="!tasks.anyOffered" icon="i-lucide-plug" :title="t('bugHunt.connectFirst')">
|
|
344
345
|
<div class="flex flex-wrap justify-center gap-2">
|
|
345
346
|
<UButton
|
|
346
347
|
v-for="choice in addableSources"
|
|
@@ -352,7 +353,7 @@ const STATUS_KEYS: Record<BugHuntAnalysisStatus, string> = {
|
|
|
352
353
|
{{ ADD_LABEL[choice.action](choice.label) }}
|
|
353
354
|
</UButton>
|
|
354
355
|
</div>
|
|
355
|
-
</
|
|
356
|
+
</EmptyState>
|
|
356
357
|
|
|
357
358
|
<!-- No service frame yet → nowhere for an adopted bug to land. -->
|
|
358
359
|
<p v-else-if="!containerItems.length" class="text-center text-xs text-dimmed">
|
|
@@ -576,9 +577,12 @@ const STATUS_KEYS: Record<BugHuntAnalysisStatus, string> = {
|
|
|
576
577
|
</div>
|
|
577
578
|
</div>
|
|
578
579
|
|
|
579
|
-
<
|
|
580
|
-
|
|
581
|
-
|
|
580
|
+
<EmptyState
|
|
581
|
+
v-if="!hunt.candidates.length"
|
|
582
|
+
compact
|
|
583
|
+
icon="i-lucide-bug-off"
|
|
584
|
+
:title="t('bugHunt.noCandidates')"
|
|
585
|
+
/>
|
|
582
586
|
</div>
|
|
583
587
|
</div>
|
|
584
588
|
</template>
|
|
@@ -321,8 +321,8 @@ onMounted(() => {
|
|
|
321
321
|
|
|
322
322
|
<UInput
|
|
323
323
|
v-model="query"
|
|
324
|
-
|
|
325
|
-
:
|
|
324
|
+
icon="i-lucide-search"
|
|
325
|
+
:loading="searching"
|
|
326
326
|
size="sm"
|
|
327
327
|
class="w-full"
|
|
328
328
|
:placeholder="
|
|
@@ -18,13 +18,14 @@ import type { DropdownMenuItem } from '@nuxt/ui'
|
|
|
18
18
|
import type { Block } from '~/types/domain'
|
|
19
19
|
import ContextIssuePicker from '~/components/tasks/ContextIssuePicker.vue'
|
|
20
20
|
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
21
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
21
22
|
|
|
22
23
|
const props = defineProps<{ block: Block }>()
|
|
23
24
|
|
|
24
25
|
const { t } = useI18n()
|
|
25
26
|
const tasks = useTasksStore()
|
|
26
27
|
const ui = useUiStore()
|
|
27
|
-
const
|
|
28
|
+
const actionToast = useActionToast()
|
|
28
29
|
const { linkPending, presentLinkFailures } = useContextLinking()
|
|
29
30
|
|
|
30
31
|
onMounted(() => {
|
|
@@ -78,7 +79,7 @@ async function attach(item: PendingContext) {
|
|
|
78
79
|
try {
|
|
79
80
|
const failures = await linkPending(props.block.id, [item])
|
|
80
81
|
if (failures.length) presentLinkFailures(failures, props.block.id)
|
|
81
|
-
else
|
|
82
|
+
else actionToast.success('tasks.contextIssues.attached')
|
|
82
83
|
} finally {
|
|
83
84
|
linking.value = false
|
|
84
85
|
}
|
|
@@ -151,8 +152,6 @@ async function attach(item: PendingContext) {
|
|
|
151
152
|
</UBadge>
|
|
152
153
|
</ULink>
|
|
153
154
|
</div>
|
|
154
|
-
<
|
|
155
|
-
{{ emptyHint }}
|
|
156
|
-
</p>
|
|
155
|
+
<EmptyState v-else compact icon="i-lucide-ticket" :title="emptyHint" />
|
|
157
156
|
</InspectorSection>
|
|
158
157
|
</template>
|
|
@@ -18,11 +18,12 @@ import { type AddSourceLabels, addChoicesOf, buildSourceChoices } from '~/utils/
|
|
|
18
18
|
import ContextIssuePicker from '~/components/tasks/ContextIssuePicker.vue'
|
|
19
19
|
import IntegrationBackTitle from '~/components/layout/IntegrationBackTitle.vue'
|
|
20
20
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
21
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
21
22
|
|
|
22
23
|
const { t } = useI18n()
|
|
23
24
|
const ui = useUiStore()
|
|
24
25
|
const tasks = useTasksStore()
|
|
25
|
-
const
|
|
26
|
+
const actionToast = useActionToast()
|
|
26
27
|
const { present } = usePipelineErrorToast()
|
|
27
28
|
|
|
28
29
|
const open = computed({
|
|
@@ -106,10 +107,9 @@ async function doSpawnEpic() {
|
|
|
106
107
|
ref_.value = ''
|
|
107
108
|
ui.closeTaskImport()
|
|
108
109
|
ui.select(epic.id)
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
actionToast.success('tasks.import.epicSpawned', {
|
|
111
|
+
params: { title: epic.title },
|
|
111
112
|
description: t('tasks.import.epicChildren', { count: spawned.length }, spawned.length),
|
|
112
|
-
icon: 'i-lucide-layers',
|
|
113
113
|
})
|
|
114
114
|
} catch (e) {
|
|
115
115
|
present(e, 'tasks.import.epicFailed')
|
|
@@ -126,9 +126,11 @@ async function doSpawnEpic() {
|
|
|
126
126
|
</template>
|
|
127
127
|
<template #body>
|
|
128
128
|
<!-- Empty state: no source offered (none connected/installed, or all disabled) -->
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
<EmptyState
|
|
130
|
+
v-if="!tasks.anyOffered"
|
|
131
|
+
icon="i-lucide-plug"
|
|
132
|
+
:title="t('tasks.import.connectFirst')"
|
|
133
|
+
>
|
|
132
134
|
<div class="flex justify-center gap-2">
|
|
133
135
|
<UButton
|
|
134
136
|
v-for="choice in addableSources"
|
|
@@ -140,7 +142,7 @@ async function doSpawnEpic() {
|
|
|
140
142
|
{{ ADD_LABEL[choice.action](choice.label) }}
|
|
141
143
|
</UButton>
|
|
142
144
|
</div>
|
|
143
|
-
</
|
|
145
|
+
</EmptyState>
|
|
144
146
|
|
|
145
147
|
<!-- No service frame yet → nowhere to create a task. -->
|
|
146
148
|
<p v-else-if="!containerItems.length" class="text-center text-xs text-dimmed">
|
|
@@ -16,7 +16,7 @@ import IntegrationBackTitle from '~/components/layout/IntegrationBackTitle.vue'
|
|
|
16
16
|
const { t } = useI18n()
|
|
17
17
|
const ui = useUiStore()
|
|
18
18
|
const tasks = useTasksStore()
|
|
19
|
-
const
|
|
19
|
+
const actionToast = useActionToast()
|
|
20
20
|
const { present } = usePipelineErrorToast()
|
|
21
21
|
const { confirmAction } = useConfirmAction()
|
|
22
22
|
|
|
@@ -63,10 +63,8 @@ async function submit() {
|
|
|
63
63
|
saving.value = true
|
|
64
64
|
try {
|
|
65
65
|
await tasks.connect(source.value, credentials)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
icon: 'i-lucide-check',
|
|
69
|
-
color: 'success',
|
|
66
|
+
actionToast.success('tasks.connect.connectedToast', {
|
|
67
|
+
params: { label: descriptor.value!.label },
|
|
70
68
|
})
|
|
71
69
|
// Re-probe so `available`/`enabled` reflect the new connection.
|
|
72
70
|
await tasks.probe()
|
|
@@ -93,12 +91,14 @@ async function disconnect() {
|
|
|
93
91
|
if (!source.value) return
|
|
94
92
|
const label = descriptor.value?.label ?? t('tasks.connect.sourceFallback')
|
|
95
93
|
if (!(await confirmAction('disconnect', label))) return
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
try {
|
|
95
|
+
await tasks.disconnect(source.value)
|
|
96
|
+
await tasks.probe()
|
|
97
|
+
} catch (e) {
|
|
98
|
+
present(e, 'tasks.connect.disconnectFailed')
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
actionToast.success('tasks.connect.disconnectedToast', { params: { label } })
|
|
102
102
|
ui.closeTaskConnect()
|
|
103
103
|
}
|
|
104
104
|
|
|
@@ -789,15 +789,12 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
|
|
|
789
789
|
class="h-full w-full object-cover object-top"
|
|
790
790
|
/>
|
|
791
791
|
<span
|
|
792
|
-
v-else
|
|
792
|
+
v-else-if="blobs.statusFor(s.artifactId) === 'error'"
|
|
793
793
|
class="flex h-full w-full items-center justify-center text-3xs text-app-600"
|
|
794
794
|
>
|
|
795
|
-
{{
|
|
796
|
-
blobs.statusFor(s.artifactId) === 'error'
|
|
797
|
-
? t('testing.shot.failed')
|
|
798
|
-
: t('testing.shot.loading')
|
|
799
|
-
}}
|
|
795
|
+
{{ t('testing.shot.failed') }}
|
|
800
796
|
</span>
|
|
797
|
+
<USkeleton v-else class="h-full w-full rounded-none" />
|
|
801
798
|
<span
|
|
802
799
|
class="absolute inset-x-0 bottom-0 truncate bg-app-950/80 px-1 py-0.5 text-3xs text-toned"
|
|
803
800
|
>{{ s.view }}</span
|
|
@@ -830,15 +827,12 @@ const GROUP_STATUS_META: Record<ScenarioGroup['status'], { icon: string; text: s
|
|
|
830
827
|
class="h-full w-full object-cover object-top"
|
|
831
828
|
/>
|
|
832
829
|
<span
|
|
833
|
-
v-else
|
|
830
|
+
v-else-if="blobs.statusFor(s.artifactId) === 'error'"
|
|
834
831
|
class="flex h-full w-full items-center justify-center text-2xs text-app-600"
|
|
835
832
|
>
|
|
836
|
-
{{
|
|
837
|
-
blobs.statusFor(s.artifactId) === 'error'
|
|
838
|
-
? t('testing.shot.failedToLoad')
|
|
839
|
-
: t('testing.shot.loading')
|
|
840
|
-
}}
|
|
833
|
+
{{ t('testing.shot.failedToLoad') }}
|
|
841
834
|
</span>
|
|
835
|
+
<USkeleton v-else class="h-full w-full rounded-none" />
|
|
842
836
|
<span
|
|
843
837
|
class="absolute inset-x-0 bottom-0 truncate bg-app-950/80 px-1.5 py-0.5 text-3xs text-toned"
|
|
844
838
|
>{{ s.view }}</span
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { buildCatalogueRows, summarizeProgress } from './TutorialCatalogue.logic'
|
|
14
14
|
import type { TutorialCatalogueRow } from './TutorialCatalogue.logic'
|
|
15
15
|
import { TUTORIAL_ACTION_KEYS, TUTORIAL_STATUS_KEYS } from '~/utils/tutorial'
|
|
16
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
16
17
|
|
|
17
18
|
const { t } = useI18n()
|
|
18
19
|
const tutorial = useTutorialStore()
|
|
@@ -129,9 +130,12 @@ const statusColor = (row: TutorialCatalogueRow) =>
|
|
|
129
130
|
</ul>
|
|
130
131
|
<!-- No tours at all is a real state (a deployment may register none of its own and
|
|
131
132
|
strip the built-ins), and it is not the same as one whose tours are all blocked. -->
|
|
132
|
-
<
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
<EmptyState
|
|
134
|
+
v-if="rows.length === 0"
|
|
135
|
+
compact
|
|
136
|
+
icon="i-lucide-graduation-cap"
|
|
137
|
+
:title="t('tutorial.catalogue.empty')"
|
|
138
|
+
/>
|
|
135
139
|
</div>
|
|
136
140
|
</template>
|
|
137
141
|
<template #footer>
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from './TutorialOverlay.logic'
|
|
22
22
|
import type { TutorialAdvanceCause, TutorialDirection } from './TutorialOverlay.logic'
|
|
23
23
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
24
|
+
import Spinner from '~/components/common/Spinner.vue'
|
|
24
25
|
|
|
25
26
|
// The one shared tour runtime: resolves the running tour from the `tutorialTours` slot,
|
|
26
27
|
// anchors a highlight ring + tooltip to the current step's `data-testid`, and advances
|
|
@@ -593,7 +594,7 @@ onUnmounted(() => {
|
|
|
593
594
|
class="mt-2 flex items-center gap-1.5 text-xs text-muted"
|
|
594
595
|
data-testid="tutorial-searching"
|
|
595
596
|
>
|
|
596
|
-
<
|
|
597
|
+
<Spinner class="h-3.5 w-3.5" />
|
|
597
598
|
{{ t('tutorial.overlay.searching') }}
|
|
598
599
|
</p>
|
|
599
600
|
<p
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
// The decision semantics live in the store: starting a tour or "No thanks" is SAVED (the
|
|
15
15
|
// prompt never auto-opens again), while closing without answering defers to next launch.
|
|
16
16
|
import { TUTORIAL_ACTION_KEYS } from '~/utils/tutorial'
|
|
17
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
17
18
|
|
|
18
19
|
const { t } = useI18n()
|
|
19
20
|
const tutorial = useTutorialStore()
|
|
@@ -81,9 +82,12 @@ const undecided = computed(() => tutorial.decision === null)
|
|
|
81
82
|
alone, where `board-basics` requires nothing at all — but a consumer's own slot
|
|
82
83
|
filter can produce it, and "no tours exist" would be the wrong thing to say then.)
|
|
83
84
|
The footer's browse button is the way on, so it stays. -->
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
<EmptyState
|
|
86
|
+
v-if="offered.length === 0"
|
|
87
|
+
compact
|
|
88
|
+
icon="i-lucide-graduation-cap"
|
|
89
|
+
:title="t('tutorial.prompt.empty')"
|
|
90
|
+
/>
|
|
87
91
|
</div>
|
|
88
92
|
</template>
|
|
89
93
|
<template #footer>
|
|
@@ -15,7 +15,7 @@ import { vcsTokenCreateUrl } from '~/utils/vcs'
|
|
|
15
15
|
|
|
16
16
|
const { t } = useI18n()
|
|
17
17
|
const github = useGitHubStore()
|
|
18
|
-
const
|
|
18
|
+
const actionToast = useActionToast()
|
|
19
19
|
|
|
20
20
|
const pat = ref('')
|
|
21
21
|
const connecting = ref(false)
|
|
@@ -37,11 +37,7 @@ async function connect() {
|
|
|
37
37
|
try {
|
|
38
38
|
await github.connectGitLab(token)
|
|
39
39
|
pat.value = ''
|
|
40
|
-
toast.
|
|
41
|
-
title: t('vcs.connect.gitlab.toast.connected'),
|
|
42
|
-
icon: 'i-lucide-check',
|
|
43
|
-
color: 'success',
|
|
44
|
-
})
|
|
40
|
+
actionToast.success('vcs.connect.gitlab.toast.connected')
|
|
45
41
|
} catch (e) {
|
|
46
42
|
error.value =
|
|
47
43
|
apiErrorEnvelope(e)?.message ??
|
|
@@ -15,6 +15,8 @@ import ArtifactLightbox from '~/components/media/ArtifactLightbox.vue'
|
|
|
15
15
|
import ResultWindowShell from '~/components/panels/ResultWindowShell.vue'
|
|
16
16
|
import StepRunMeta from '~/components/panels/StepRunMeta.vue'
|
|
17
17
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
18
|
+
import Spinner from '~/components/common/Spinner.vue'
|
|
19
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
18
20
|
|
|
19
21
|
const board = useBoardStore()
|
|
20
22
|
const execution = useExecutionStore()
|
|
@@ -283,7 +285,7 @@ watch(pendingUpload, async (file) => {
|
|
|
283
285
|
v-if="working"
|
|
284
286
|
class="flex items-center gap-2 rounded-lg border border-default bg-app-950/40 px-3 py-2 text-xs text-toned"
|
|
285
287
|
>
|
|
286
|
-
<
|
|
288
|
+
<Spinner class="h-3.5 w-3.5 text-app-warning-300" />
|
|
287
289
|
{{ phase ? PHASE_LABEL[phase] : '' }}
|
|
288
290
|
</p>
|
|
289
291
|
|
|
@@ -331,9 +333,12 @@ watch(pendingUpload, async (file) => {
|
|
|
331
333
|
</div>
|
|
332
334
|
</div>
|
|
333
335
|
</section>
|
|
334
|
-
<
|
|
335
|
-
|
|
336
|
-
|
|
336
|
+
<EmptyState
|
|
337
|
+
v-else
|
|
338
|
+
compact
|
|
339
|
+
icon="i-lucide-image-off"
|
|
340
|
+
:title="t('visualConfirm.noScreenshots')"
|
|
341
|
+
/>
|
|
337
342
|
|
|
338
343
|
<!-- Upload a reference for any view -->
|
|
339
344
|
<section class="rounded-lg border border-default bg-default/60 p-3">
|