@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
|
@@ -7,6 +7,7 @@ import { formatMs } from '~/utils/observability'
|
|
|
7
7
|
import { FAILURE_KIND_KEYS, isAgentFailureKind } from '~/utils/failureKinds'
|
|
8
8
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
9
9
|
import IconButton from '~/components/common/IconButton.vue'
|
|
10
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
10
11
|
import { FULL_BLEED_TAKEOVER_CLASS } from '~/utils/safeArea'
|
|
11
12
|
|
|
12
13
|
// Deployment-level (platform-operator) observability dashboard: the aggregate health of the
|
|
@@ -166,10 +167,11 @@ watch(
|
|
|
166
167
|
color="neutral"
|
|
167
168
|
variant="ghost"
|
|
168
169
|
icon="i-lucide-refresh-cw"
|
|
170
|
+
:loading="loading"
|
|
169
171
|
:label="t('platformObservability.refresh')"
|
|
170
172
|
:ui="{
|
|
171
173
|
base: 'rounded-lg border border-default p-1.5 text-muted transition hover:text-default',
|
|
172
|
-
leadingIcon:
|
|
174
|
+
leadingIcon: 'h-4 w-4',
|
|
173
175
|
}"
|
|
174
176
|
data-testid="operator-refresh"
|
|
175
177
|
@click="refresh"
|
|
@@ -205,8 +207,9 @@ watch(
|
|
|
205
207
|
</UButton>
|
|
206
208
|
</div>
|
|
207
209
|
|
|
208
|
-
<div v-else-if="loading && !view" class="
|
|
209
|
-
|
|
210
|
+
<div v-else-if="loading && !view" class="mx-auto flex max-w-5xl flex-col gap-6">
|
|
211
|
+
<USkeleton class="h-20 w-full rounded-lg" />
|
|
212
|
+
<USkeleton v-for="n in 2" :key="n" class="h-36 w-full rounded-lg" />
|
|
210
213
|
</div>
|
|
211
214
|
|
|
212
215
|
<div v-else-if="view" class="mx-auto flex max-w-5xl flex-col gap-6">
|
|
@@ -299,9 +302,12 @@ watch(
|
|
|
299
302
|
{{ t('platformObservability.trend.title') }}
|
|
300
303
|
</SectionLabel>
|
|
301
304
|
<div class="rounded-lg border border-default bg-default/40 p-4">
|
|
302
|
-
<
|
|
303
|
-
|
|
304
|
-
|
|
305
|
+
<EmptyState
|
|
306
|
+
v-if="view.outcomes.total === 0"
|
|
307
|
+
compact
|
|
308
|
+
icon="i-lucide-chart-column"
|
|
309
|
+
:title="t('platformObservability.trend.empty')"
|
|
310
|
+
/>
|
|
305
311
|
<div v-else class="flex h-28 items-end gap-0.5" data-testid="operator-trend">
|
|
306
312
|
<div
|
|
307
313
|
v-for="p in view.trend.points"
|
|
@@ -349,9 +355,12 @@ watch(
|
|
|
349
355
|
{{ t('platformObservability.gates.title') }}
|
|
350
356
|
</SectionLabel>
|
|
351
357
|
<div class="overflow-x-auto rounded-lg border border-default bg-default/40 p-4">
|
|
352
|
-
<
|
|
353
|
-
|
|
354
|
-
|
|
358
|
+
<EmptyState
|
|
359
|
+
v-if="!view.gates.length"
|
|
360
|
+
compact
|
|
361
|
+
icon="i-lucide-shield-check"
|
|
362
|
+
:title="t('platformObservability.gates.empty')"
|
|
363
|
+
/>
|
|
355
364
|
<UTable
|
|
356
365
|
v-else
|
|
357
366
|
:data="view.gates"
|
|
@@ -412,9 +421,12 @@ watch(
|
|
|
412
421
|
{{ t('platformObservability.failures.title') }}
|
|
413
422
|
</SectionLabel>
|
|
414
423
|
<div class="rounded-lg border border-default bg-default/40 p-4">
|
|
415
|
-
<
|
|
416
|
-
|
|
417
|
-
|
|
424
|
+
<EmptyState
|
|
425
|
+
v-if="!view.failures.length"
|
|
426
|
+
compact
|
|
427
|
+
icon="i-lucide-circle-x"
|
|
428
|
+
:title="t('platformObservability.failures.empty')"
|
|
429
|
+
/>
|
|
418
430
|
<ul v-else class="flex flex-col gap-2" data-testid="operator-failures">
|
|
419
431
|
<li v-for="f in view.failures" :key="f.kind" class="text-xs">
|
|
420
432
|
<div class="mb-0.5 flex items-center justify-between">
|
|
@@ -475,12 +487,12 @@ watch(
|
|
|
475
487
|
{{ t('platformObservability.durations.title') }}
|
|
476
488
|
</SectionLabel>
|
|
477
489
|
<div class="rounded-lg border border-default bg-default/40 p-3 text-sm">
|
|
478
|
-
<
|
|
490
|
+
<EmptyState
|
|
479
491
|
v-if="view.durations.count === 0"
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
492
|
+
compact
|
|
493
|
+
icon="i-lucide-timer"
|
|
494
|
+
:title="t('platformObservability.durations.empty')"
|
|
495
|
+
/>
|
|
484
496
|
<dl
|
|
485
497
|
v-else
|
|
486
498
|
class="grid grid-cols-3 gap-y-3 text-center"
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
import ReportsSpendBreakdown from '~/components/panels/ReportsSpendBreakdown.vue'
|
|
22
22
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
23
23
|
import IconButton from '~/components/common/IconButton.vue'
|
|
24
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
24
25
|
import { FULL_BLEED_TAKEOVER_CLASS } from '~/utils/safeArea'
|
|
25
26
|
|
|
26
27
|
// Reports: cross-cutting usage analytics for the active account — where the spend and the
|
|
@@ -245,10 +246,11 @@ watch(
|
|
|
245
246
|
color="neutral"
|
|
246
247
|
variant="ghost"
|
|
247
248
|
icon="i-lucide-refresh-cw"
|
|
249
|
+
:loading="loading"
|
|
248
250
|
:label="t('reports.refresh')"
|
|
249
251
|
:ui="{
|
|
250
252
|
base: 'rounded-lg border border-default p-1.5 text-muted transition hover:text-default',
|
|
251
|
-
leadingIcon:
|
|
253
|
+
leadingIcon: 'h-4 w-4',
|
|
252
254
|
}"
|
|
253
255
|
@click="refresh"
|
|
254
256
|
/>
|
|
@@ -283,8 +285,9 @@ watch(
|
|
|
283
285
|
</UButton>
|
|
284
286
|
</div>
|
|
285
287
|
|
|
286
|
-
<div v-else-if="loading && !view" class="
|
|
287
|
-
|
|
288
|
+
<div v-else-if="loading && !view" class="mx-auto flex max-w-5xl flex-col gap-6">
|
|
289
|
+
<USkeleton class="h-20 w-full rounded-lg" />
|
|
290
|
+
<USkeleton v-for="n in 2" :key="n" class="h-36 w-full rounded-lg" />
|
|
288
291
|
</div>
|
|
289
292
|
|
|
290
293
|
<div v-else-if="view" class="mx-auto flex max-w-5xl flex-col gap-6">
|
|
@@ -374,9 +377,12 @@ watch(
|
|
|
374
377
|
{{ t('reports.trend.title') }}
|
|
375
378
|
</SectionLabel>
|
|
376
379
|
<div class="rounded-lg border border-default bg-default/40 p-4">
|
|
377
|
-
<
|
|
378
|
-
|
|
379
|
-
|
|
380
|
+
<EmptyState
|
|
381
|
+
v-if="!hasSpend"
|
|
382
|
+
compact
|
|
383
|
+
icon="i-lucide-chart-column"
|
|
384
|
+
:title="t('reports.trend.empty')"
|
|
385
|
+
/>
|
|
380
386
|
<div v-else class="flex h-28 items-end gap-0.5" data-testid="reports-trend">
|
|
381
387
|
<div
|
|
382
388
|
v-for="point in view.trend.points"
|
|
@@ -505,12 +511,12 @@ watch(
|
|
|
505
511
|
{{ t('reports.activity.heading') }}
|
|
506
512
|
</h3>
|
|
507
513
|
<div class="rounded-lg border border-default bg-default/40 p-4">
|
|
508
|
-
<
|
|
514
|
+
<EmptyState
|
|
509
515
|
v-if="!activityByDimension.length"
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
516
|
+
compact
|
|
517
|
+
icon="i-lucide-activity"
|
|
518
|
+
:title="t('reports.activity.empty')"
|
|
519
|
+
/>
|
|
514
520
|
<template v-else>
|
|
515
521
|
<ul class="flex flex-col gap-3" data-testid="reports-activity">
|
|
516
522
|
<li
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { computed } from 'vue'
|
|
3
3
|
import type { ReportSpendCap, ReportSpendRow } from '~/types/execution'
|
|
4
4
|
import { maxOf, segmentPct, spendMagnitude } from './ReportsPanel.logic'
|
|
5
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
5
6
|
|
|
6
7
|
// One ranked spend breakdown: a horizontal bar per slice, split into the metered
|
|
7
8
|
// (`violet-500`, real money) and subscription (`amber-600`, illustrative equivalent-API
|
|
@@ -34,9 +35,12 @@ const max = computed(() => maxOf(props.rows, spendMagnitude))
|
|
|
34
35
|
|
|
35
36
|
<template>
|
|
36
37
|
<div class="rounded-lg border border-default bg-default/40 p-4">
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
<EmptyState
|
|
39
|
+
v-if="!rows.length"
|
|
40
|
+
compact
|
|
41
|
+
icon="i-lucide-wallet"
|
|
42
|
+
:title="t('reports.spend.empty')"
|
|
43
|
+
/>
|
|
40
44
|
<ul v-else class="flex flex-col gap-2.5" :data-testid="testId">
|
|
41
45
|
<li v-for="row in rows" :key="row.key" class="text-xs" data-testid="reports-spend-row">
|
|
42
46
|
<div class="mb-1 flex items-baseline justify-between gap-2">
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* event delivered and every run already fetched once.
|
|
13
13
|
*/
|
|
14
14
|
import { computed } from 'vue'
|
|
15
|
+
import Spinner from '~/components/common/Spinner.vue'
|
|
15
16
|
|
|
16
17
|
const props = defineProps<{ instanceId: string | null }>()
|
|
17
18
|
|
|
@@ -29,10 +30,10 @@ const error = computed(() => execution.fullError(props.instanceId))
|
|
|
29
30
|
:class="error ? 'text-app-error-300' : 'text-muted'"
|
|
30
31
|
data-testid="run-detail-load-state"
|
|
31
32
|
>
|
|
32
|
-
<
|
|
33
|
-
:name="error ? 'i-lucide-triangle-alert' :
|
|
33
|
+
<Spinner
|
|
34
|
+
:name="error ? 'i-lucide-triangle-alert' : undefined"
|
|
35
|
+
:spinning="!error"
|
|
34
36
|
class="h-3.5 w-3.5 shrink-0"
|
|
35
|
-
:class="error ? '' : 'animate-spin'"
|
|
36
37
|
/>
|
|
37
38
|
<span>{{
|
|
38
39
|
error ? t('panels.runDetail.loadFailed', { reason: error }) : t('panels.runDetail.loading')
|
|
@@ -4,6 +4,7 @@ import type { PipelineStep, RunContainerStatus } from '~/types/execution'
|
|
|
4
4
|
import { containerPhaseLabel } from '~/utils/pipelineRender'
|
|
5
5
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
6
6
|
import IconButton from '~/components/common/IconButton.vue'
|
|
7
|
+
import Spinner from '~/components/common/Spinner.vue'
|
|
7
8
|
|
|
8
9
|
// The per-run container lifecycle for a container-backed step: its status (spinning up /
|
|
9
10
|
// running / errored / reclaimed), the live phase (preparing the checkout vs the agent
|
|
@@ -74,7 +75,7 @@ const CONTAINER_STATUS_META: Record<
|
|
|
74
75
|
|
|
75
76
|
// Whether the status icon animates: only a cold-boot genuinely in flight, i.e. one whose run is
|
|
76
77
|
// still being driven. A `starting` container on a parked or terminated run keeps its label and
|
|
77
|
-
// freezes, the same way a mid-flight subtask item does (`
|
|
78
|
+
// freezes, the same way a mid-flight subtask item does (`subtaskIconSpins`).
|
|
78
79
|
const spinIcon = computed(
|
|
79
80
|
() =>
|
|
80
81
|
props.runActive && !!containerStatus.value && CONTAINER_STATUS_META[containerStatus.value].spin,
|
|
@@ -100,10 +101,10 @@ const { copy: copyText } = useCopyToClipboard()
|
|
|
100
101
|
:class="CONTAINER_STATUS_META[containerStatus].cls"
|
|
101
102
|
>
|
|
102
103
|
<div class="flex items-center gap-2">
|
|
103
|
-
<
|
|
104
|
+
<Spinner
|
|
104
105
|
:name="CONTAINER_STATUS_META[containerStatus].icon"
|
|
106
|
+
:spinning="spinIcon"
|
|
105
107
|
class="h-4 w-4 shrink-0"
|
|
106
|
-
:class="spinIcon ? 'animate-spin' : ''"
|
|
107
108
|
/>
|
|
108
109
|
<span class="font-medium">{{ t(CONTAINER_STATUS_KEYS[containerStatus]) }}</span>
|
|
109
110
|
<template v-if="phaseLabel && containerStatus === 'up'">
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import type { PipelineStep } from '~/types/execution'
|
|
9
9
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
10
10
|
import IconButton from '~/components/common/IconButton.vue'
|
|
11
|
+
import Spinner from '~/components/common/Spinner.vue'
|
|
11
12
|
|
|
12
13
|
// The EXTERNAL work a delegated step dispatched: which registered executor is running it, what it
|
|
13
14
|
// is doing, and the link to that system's own logs, which is the affordance everything else here
|
|
@@ -81,10 +82,10 @@ const { copy: copyText } = useCopyToClipboard()
|
|
|
81
82
|
<div v-if="record" data-testid="step-delegated-status">
|
|
82
83
|
<div class="rounded-lg border px-3 py-2 text-xs" :class="statusView.meta.cls">
|
|
83
84
|
<div class="flex items-center gap-2">
|
|
84
|
-
<
|
|
85
|
+
<Spinner
|
|
85
86
|
:name="statusView.meta.icon"
|
|
87
|
+
:spinning="statusView.meta.spin"
|
|
86
88
|
class="h-4 w-4 shrink-0"
|
|
87
|
-
:class="statusView.meta.spin ? 'animate-spin' : ''"
|
|
88
89
|
/>
|
|
89
90
|
<!-- A status this build does not know is NAMED as unrecognised, carrying the stored value,
|
|
90
91
|
rather than guessed onto a current one or silently dropped. -->
|
|
@@ -7,13 +7,14 @@ import {
|
|
|
7
7
|
} from '@cat-factory/contracts'
|
|
8
8
|
import type { AgentState, PipelineStep, CompanionVerdict, StepApproval } from '~/types/execution'
|
|
9
9
|
import type { BadgeColor } from '~/utils/badge'
|
|
10
|
-
import {
|
|
10
|
+
import { subtaskIconSpins, subtaskIconTone } from '~/utils/pipelineRender'
|
|
11
11
|
import StepModelActivity from '~/components/observability/StepModelActivity.vue'
|
|
12
12
|
import StepContainerStatus from '~/components/panels/StepContainerStatus.vue'
|
|
13
13
|
import StepDelegatedStatus from '~/components/panels/StepDelegatedStatus.vue'
|
|
14
14
|
import CopyButton from '~/components/common/CopyButton.vue'
|
|
15
15
|
import MarkdownProse from '~/components/common/MarkdownProse.vue'
|
|
16
16
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
17
|
+
import Spinner from '~/components/common/Spinner.vue'
|
|
17
18
|
|
|
18
19
|
// The step's metadata card body: state/timing/model/run id, the container cold-boot
|
|
19
20
|
// phase, the live subtask breakdown, the LLM observability rollup, the applied
|
|
@@ -204,11 +205,7 @@ async function copyRunId() {
|
|
|
204
205
|
{{ t('panels.stepMeta.duration') }}
|
|
205
206
|
</SectionLabel>
|
|
206
207
|
<dd class="mt-0.5 flex items-center gap-1.5 tabular-nums text-default">
|
|
207
|
-
<
|
|
208
|
-
v-if="isRunning"
|
|
209
|
-
name="i-lucide-loader-circle"
|
|
210
|
-
class="h-3 w-3 animate-spin text-primary"
|
|
211
|
-
/>
|
|
208
|
+
<Spinner v-if="isRunning" class="h-3 w-3 text-primary" />
|
|
212
209
|
<span v-if="durationLabel">{{ durationLabel }}</span>
|
|
213
210
|
<span v-else class="text-dimmed">—</span>
|
|
214
211
|
<span v-if="isRunning" class="text-2xs text-dimmed">{{
|
|
@@ -314,10 +311,11 @@ async function copyRunId() {
|
|
|
314
311
|
: 'text-muted'
|
|
315
312
|
"
|
|
316
313
|
>
|
|
317
|
-
<
|
|
314
|
+
<Spinner
|
|
318
315
|
:name="ITEM_ICON[item.status]"
|
|
316
|
+
:spinning="subtaskIconSpins(item.status, runFailed)"
|
|
319
317
|
class="mt-px h-3 w-3 shrink-0"
|
|
320
|
-
:class="
|
|
318
|
+
:class="subtaskIconTone(item.status)"
|
|
321
319
|
/>
|
|
322
320
|
<span>{{ item.label }}</span>
|
|
323
321
|
</li>
|
|
@@ -4,6 +4,7 @@ import type { PipelineStep } from '~/types/execution'
|
|
|
4
4
|
import { useStepTimer } from '~/composables/useStepTimer'
|
|
5
5
|
import StepModelActivity from '~/components/observability/StepModelActivity.vue'
|
|
6
6
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
7
|
+
import Spinner from '~/components/common/Spinner.vue'
|
|
7
8
|
|
|
8
9
|
// Shared run-metadata + observability block for the step-backed result windows
|
|
9
10
|
// (the CI/conflicts gate, the tester report). It carries the facts every step has in
|
|
@@ -71,11 +72,7 @@ async function copyRunId() {
|
|
|
71
72
|
{{ t('panels.stepMeta.duration') }}
|
|
72
73
|
</SectionLabel>
|
|
73
74
|
<p class="flex items-center gap-1.5 text-xs tabular-nums text-toned">
|
|
74
|
-
<
|
|
75
|
-
v-if="isRunning"
|
|
76
|
-
name="i-lucide-loader-circle"
|
|
77
|
-
class="h-3 w-3 animate-spin text-primary"
|
|
78
|
-
/>
|
|
75
|
+
<Spinner v-if="isRunning" class="h-3 w-3 text-primary" />
|
|
79
76
|
{{ durationLabel }}
|
|
80
77
|
<span v-if="isRunning" class="text-2xs text-dimmed">{{
|
|
81
78
|
t('panels.stepMeta.elapsed')
|
|
@@ -3,6 +3,7 @@ import type { Block } from '~/types/domain'
|
|
|
3
3
|
import { STATUS_META } from '~/utils/catalog'
|
|
4
4
|
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
5
5
|
import ReviewDebtBadge from '~/components/board/ReviewDebtBadge.vue'
|
|
6
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
6
7
|
|
|
7
8
|
const props = defineProps<{ block: Block }>()
|
|
8
9
|
|
|
@@ -79,7 +80,12 @@ function addTask() {
|
|
|
79
80
|
<span class="ms-auto text-3xs text-dimmed">{{ STATUS_META[task.status].label }}</span>
|
|
80
81
|
</li>
|
|
81
82
|
</ul>
|
|
82
|
-
<
|
|
83
|
+
<EmptyState
|
|
84
|
+
v-else
|
|
85
|
+
compact
|
|
86
|
+
icon="i-lucide-list-todo"
|
|
87
|
+
:title="t('inspector.container.noTasks')"
|
|
88
|
+
/>
|
|
83
89
|
<p v-if="isFrame" class="text-2xs text-dimmed">
|
|
84
90
|
{{ t('inspector.container.servicesHint') }}
|
|
85
91
|
</p>
|
|
@@ -4,6 +4,7 @@ import type { Block } from '~/types/domain'
|
|
|
4
4
|
import { STATUS_META } from '~/utils/catalog'
|
|
5
5
|
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
6
6
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
7
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
7
8
|
|
|
8
9
|
// The epic inspector body: the full tree of member tasks (which may live under different
|
|
9
10
|
// services/modules), grouped service → module → task. Each task row selects it. Membership
|
|
@@ -58,9 +59,12 @@ const groups = computed(() => {
|
|
|
58
59
|
}}</span>
|
|
59
60
|
</template>
|
|
60
61
|
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
<EmptyState
|
|
63
|
+
v-if="members.length === 0"
|
|
64
|
+
compact
|
|
65
|
+
icon="i-lucide-list-tree"
|
|
66
|
+
:title="t('inspector.epicChildren.empty')"
|
|
67
|
+
/>
|
|
64
68
|
|
|
65
69
|
<div v-else class="space-y-2">
|
|
66
70
|
<div v-for="(group, gi) in groups" :key="gi" class="rounded-md border border-muted/60 p-2">
|
|
@@ -14,6 +14,7 @@ import FrontendBindingsResolved from '~/components/panels/inspector/FrontendBind
|
|
|
14
14
|
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
15
15
|
import IconButton from '~/components/common/IconButton.vue'
|
|
16
16
|
import { apiErrorEnvelope } from '~/composables/api/errors'
|
|
17
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
17
18
|
|
|
18
19
|
// Frontend-frame (`type: 'frontend'`) configuration: how to build, serve, and mock this
|
|
19
20
|
// frontend for a self-contained UI test (+ an optional browsable preview on local/node),
|
|
@@ -565,9 +566,12 @@ onUnmounted(() => preview.stopPolling(props.block.id))
|
|
|
565
566
|
/>
|
|
566
567
|
</div>
|
|
567
568
|
</div>
|
|
568
|
-
<
|
|
569
|
-
|
|
570
|
-
|
|
569
|
+
<EmptyState
|
|
570
|
+
v-else
|
|
571
|
+
compact
|
|
572
|
+
icon="i-lucide-link"
|
|
573
|
+
:title="t('inspector.frontendConfig.bindings.empty')"
|
|
574
|
+
/>
|
|
571
575
|
|
|
572
576
|
<!-- How the bindings resolve RIGHT NOW: each env var → a bound service's live ephemeral
|
|
573
577
|
URL, or WireMock — plus the duplicate-env-var warning. The same view a UI-test run
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
INITIATIVE_STATUS_LABEL_KEYS,
|
|
13
13
|
initiativeProgress,
|
|
14
14
|
} from '~/utils/initiative'
|
|
15
|
+
import Spinner from '~/components/common/Spinner.vue'
|
|
15
16
|
|
|
16
17
|
const props = defineProps<{ block: Block }>()
|
|
17
18
|
|
|
@@ -93,10 +94,12 @@ function control(action: 'pause' | 'resume' | 'cancel') {
|
|
|
93
94
|
color="neutral"
|
|
94
95
|
variant="soft"
|
|
95
96
|
size="sm"
|
|
96
|
-
icon="i-lucide-loader-circle"
|
|
97
|
-
:ui="{ leadingIcon: 'animate-spin' }"
|
|
98
97
|
@click="openPlanning"
|
|
99
98
|
>
|
|
99
|
+
<!-- Not `:loading`: that disables the button, and this one has to stay clickable. -->
|
|
100
|
+
<template #leading>
|
|
101
|
+
<Spinner class="size-4" />
|
|
102
|
+
</template>
|
|
100
103
|
{{ t('initiative.inspector.planningInProgress') }}
|
|
101
104
|
</UButton>
|
|
102
105
|
<UButton
|
|
@@ -3,6 +3,7 @@ import { computed } from 'vue'
|
|
|
3
3
|
import type { Block, ServiceConnection } from '~/types/domain'
|
|
4
4
|
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
5
5
|
import IconButton from '~/components/common/IconButton.vue'
|
|
6
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
6
7
|
|
|
7
8
|
// Service-frame (`type: 'service'`) connections: the other services this one USES
|
|
8
9
|
// (consumer→provider edges, stored on this frame — the consumer end). Each row picks a
|
|
@@ -136,9 +137,12 @@ const usedBy = computed(() =>
|
|
|
136
137
|
/>
|
|
137
138
|
</div>
|
|
138
139
|
</div>
|
|
139
|
-
<
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
<EmptyState
|
|
141
|
+
v-else
|
|
142
|
+
compact
|
|
143
|
+
icon="i-lucide-plug"
|
|
144
|
+
:title="t('inspector.serviceConnections.empty')"
|
|
145
|
+
/>
|
|
142
146
|
|
|
143
147
|
<div v-if="usedBy.length" class="space-y-1" data-testid="service-connections-used-by">
|
|
144
148
|
<span class="text-2xs text-muted">{{ t('inspector.serviceConnections.usedBy') }}</span>
|
|
@@ -13,7 +13,7 @@ const props = defineProps<{ block: Block }>()
|
|
|
13
13
|
const store = useReleaseHealthStore()
|
|
14
14
|
const ui = useUiStore()
|
|
15
15
|
const uiMode = useUiModeStore()
|
|
16
|
-
const
|
|
16
|
+
const actionToast = useActionToast()
|
|
17
17
|
const { present } = usePipelineErrorToast()
|
|
18
18
|
const { t } = useI18n()
|
|
19
19
|
const { confirmAction, toastDone } = useConfirmAction()
|
|
@@ -61,11 +61,7 @@ async function save() {
|
|
|
61
61
|
sloIds: parseIds(draft.sloIds),
|
|
62
62
|
envTag: draft.envTag.trim() || null,
|
|
63
63
|
})
|
|
64
|
-
|
|
65
|
-
title: t('inspector.releaseHealth.savedToast'),
|
|
66
|
-
icon: 'i-lucide-check',
|
|
67
|
-
color: 'success',
|
|
68
|
-
})
|
|
64
|
+
actionToast.success('inspector.releaseHealth.savedToast')
|
|
69
65
|
} catch (e) {
|
|
70
66
|
present(e, 'inspector.releaseHealth.saveFailed')
|
|
71
67
|
} finally {
|
|
@@ -18,7 +18,7 @@ import { uid } from '~/utils/catalog'
|
|
|
18
18
|
const props = defineProps<{ block: Block }>()
|
|
19
19
|
|
|
20
20
|
const store = useTestSecretsStore()
|
|
21
|
-
const
|
|
21
|
+
const actionToast = useActionToast()
|
|
22
22
|
const { present } = usePipelineErrorToast()
|
|
23
23
|
const { t } = useI18n()
|
|
24
24
|
const { confirmAction, toastDone } = useConfirmAction()
|
|
@@ -111,11 +111,7 @@ async function save() {
|
|
|
111
111
|
value: r.value,
|
|
112
112
|
})),
|
|
113
113
|
})
|
|
114
|
-
|
|
115
|
-
title: t('inspector.testSecrets.savedToast'),
|
|
116
|
-
icon: 'i-lucide-check',
|
|
117
|
-
color: 'success',
|
|
118
|
-
})
|
|
114
|
+
actionToast.success('inspector.testSecrets.savedToast')
|
|
119
115
|
} catch (e) {
|
|
120
116
|
present(e, 'inspector.testSecrets.saveFailed')
|
|
121
117
|
} finally {
|
|
@@ -21,7 +21,7 @@ const props = defineProps<{ block: Block }>()
|
|
|
21
21
|
|
|
22
22
|
const board = useBoardStore()
|
|
23
23
|
const uiMode = useUiModeStore()
|
|
24
|
-
const
|
|
24
|
+
const actionToast = useActionToast()
|
|
25
25
|
const { t } = useI18n()
|
|
26
26
|
|
|
27
27
|
const busy = ref(false)
|
|
@@ -64,11 +64,7 @@ async function save() {
|
|
|
64
64
|
// needs saying here; announcing it unconditionally would claim a save the rollback undid.
|
|
65
65
|
const persisted = await board.updateBlock(props.block.id, { testingContext: outgoing.value })
|
|
66
66
|
if (persisted) {
|
|
67
|
-
|
|
68
|
-
title: t('inspector.testingContext.savedToast'),
|
|
69
|
-
icon: 'i-lucide-check',
|
|
70
|
-
color: 'success',
|
|
71
|
-
})
|
|
67
|
+
actionToast.success('inspector.testingContext.savedToast')
|
|
72
68
|
}
|
|
73
69
|
} finally {
|
|
74
70
|
busy.value = false
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type ValidationEcosystem,
|
|
11
11
|
} from '~/types/validationChecks'
|
|
12
12
|
import { mergeDetectedChecks } from '~/utils/validationDetection'
|
|
13
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
13
14
|
|
|
14
15
|
// Per-service (frame) PRE-PR VALIDATION CHECKS: the shell commands the executor-harness runs
|
|
15
16
|
// against the checkout after the coder settles and BEFORE the PR opens. A failing command's
|
|
@@ -19,7 +20,7 @@ import { mergeDetectedChecks } from '~/utils/validationDetection'
|
|
|
19
20
|
const props = defineProps<{ block: Block }>()
|
|
20
21
|
|
|
21
22
|
const store = useValidationChecksStore()
|
|
22
|
-
const
|
|
23
|
+
const actionToast = useActionToast()
|
|
23
24
|
const { present } = usePipelineErrorToast()
|
|
24
25
|
const { t, te } = useI18n()
|
|
25
26
|
const { confirmAction, toastDone } = useConfirmAction()
|
|
@@ -104,11 +105,11 @@ async function detect() {
|
|
|
104
105
|
if (result.status !== 'ok') {
|
|
105
106
|
// The backend distinguishes "no repo linked" from "the repo could not be read"; say
|
|
106
107
|
// which, because they send the operator to different places.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
actionToast.error(
|
|
109
|
+
result.status === 'repo_unavailable'
|
|
110
|
+
? 'inspector.validationChecks.detect.repo_unavailable'
|
|
111
|
+
: 'inspector.validationChecks.detect.failed',
|
|
112
|
+
)
|
|
112
113
|
return
|
|
113
114
|
}
|
|
114
115
|
const merged = mergeDetectedChecks(rows.value, result.checks, VALIDATION_MAX_CHECKS)
|
|
@@ -120,39 +121,37 @@ async function detect() {
|
|
|
120
121
|
const filledInstall = suggestedInstall !== '' && dependencyInstall.value.trim() === ''
|
|
121
122
|
if (filledInstall) dependencyInstall.value = suggestedInstall
|
|
122
123
|
if (merged.added === 0 && !filledInstall) {
|
|
123
|
-
|
|
124
|
-
title: t('inspector.validationChecks.detect.nothingNew'),
|
|
124
|
+
actionToast.info('inspector.validationChecks.detect.nothingNew', {
|
|
125
125
|
description:
|
|
126
126
|
result.checks.length > 0
|
|
127
127
|
? t('inspector.validationChecks.detect.alreadyPresent')
|
|
128
128
|
: t('inspector.validationChecks.detect.unrecognised'),
|
|
129
|
-
icon: 'i-lucide-info',
|
|
130
|
-
color: 'neutral',
|
|
131
129
|
})
|
|
132
130
|
return
|
|
133
131
|
}
|
|
134
132
|
const names = result.ecosystems.map(ecosystemLabel).join(', ')
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
? t('inspector.validationChecks.detect.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
133
|
+
// An install-only detection fills nothing but the install field, and reporting it as
|
|
134
|
+
// "0 checks added" would read as a failed press on the one repo shape prepopulation is
|
|
135
|
+
// most for (dependencies to install, nothing declared to verify).
|
|
136
|
+
actionToast.success(
|
|
137
|
+
merged.added === 0
|
|
138
|
+
? 'inspector.validationChecks.detect.installOnly'
|
|
139
|
+
: 'inspector.validationChecks.detect.added',
|
|
140
|
+
{
|
|
141
|
+
params: { count: merged.added },
|
|
142
|
+
plural: merged.added === 0 ? undefined : merged.added,
|
|
143
|
+
// Name what was recognised AND what was left out: a cap that silently swallowed a
|
|
144
|
+
// suggestion reads as "that is everything your repo has".
|
|
145
|
+
description: [
|
|
146
|
+
names ? t('inspector.validationChecks.detect.found', { ecosystems: names }) : '',
|
|
147
|
+
merged.dropped > 0 || result.truncated
|
|
148
|
+
? t('inspector.validationChecks.detect.capped', { max: VALIDATION_MAX_CHECKS })
|
|
149
|
+
: '',
|
|
150
|
+
]
|
|
151
|
+
.filter(Boolean)
|
|
152
|
+
.join(' '),
|
|
153
|
+
},
|
|
154
|
+
)
|
|
156
155
|
} catch (e) {
|
|
157
156
|
present(e, 'inspector.validationChecks.detect.failed')
|
|
158
157
|
} finally {
|
|
@@ -169,11 +168,7 @@ async function save() {
|
|
|
169
168
|
maxAttempts.value,
|
|
170
169
|
dependencyInstall.value.trim() || undefined,
|
|
171
170
|
)
|
|
172
|
-
|
|
173
|
-
title: t('inspector.validationChecks.savedToast'),
|
|
174
|
-
icon: 'i-lucide-check',
|
|
175
|
-
color: 'success',
|
|
176
|
-
})
|
|
171
|
+
actionToast.success('inspector.validationChecks.savedToast')
|
|
177
172
|
} catch (e) {
|
|
178
173
|
present(e, 'inspector.validationChecks.saveFailed')
|
|
179
174
|
} finally {
|
|
@@ -273,9 +268,13 @@ async function clear() {
|
|
|
273
268
|
/>
|
|
274
269
|
</div>
|
|
275
270
|
|
|
276
|
-
<
|
|
277
|
-
|
|
278
|
-
|
|
271
|
+
<EmptyState
|
|
272
|
+
v-if="rows.length === 0"
|
|
273
|
+
compact
|
|
274
|
+
icon="i-lucide-list-checks"
|
|
275
|
+
:title="t('inspector.validationChecks.empty')"
|
|
276
|
+
data-testid="validation-empty"
|
|
277
|
+
/>
|
|
279
278
|
|
|
280
279
|
<div class="flex items-end justify-between gap-2">
|
|
281
280
|
<UFormField
|