@cat-factory/app 0.311.1 → 0.311.3
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 +70 -5
- package/app/app.config.ts +6 -18
- 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/bootstrap/AdoptionReviewModal.vue +4 -7
- 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/AsyncViewError.vue +1 -1
- 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 +15 -9
- 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 +3 -11
- 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 +1 -1
- 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/ResultWindowShell.vue +1 -1
- 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/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/useNavContributions.ts +4 -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/pages/index.vue +3 -2
- 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/utils/bootstrapSteps.ts +11 -2
- package/app/utils/pipelineRender.ts +21 -8
- package/i18n/locales/de.json +12 -30
- package/i18n/locales/en.json +12 -30
- package/i18n/locales/es.json +12 -30
- package/i18n/locales/fr.json +12 -30
- package/i18n/locales/he.json +12 -30
- package/i18n/locales/it.json +12 -30
- package/i18n/locales/ja.json +12 -30
- package/i18n/locales/pl.json +12 -30
- package/i18n/locales/tr.json +12 -30
- package/i18n/locales/uk.json +12 -30
- package/package.json +1 -1
|
@@ -32,6 +32,7 @@ import RunFailureSummary from '~/components/observability/RunFailureSummary.vue'
|
|
|
32
32
|
import ToolCallList from '~/components/observability/ToolCallList.vue'
|
|
33
33
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
34
34
|
import IconButton from '~/components/common/IconButton.vue'
|
|
35
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
35
36
|
import { FULL_BLEED_TAKEOVER_CLASS } from '~/utils/safeArea'
|
|
36
37
|
|
|
37
38
|
/** No run selected: the same empty, NOT-truncated trajectory the store answers with. */
|
|
@@ -844,13 +845,9 @@ function exportJson() {
|
|
|
844
845
|
</section>
|
|
845
846
|
|
|
846
847
|
<!-- states -->
|
|
847
|
-
<
|
|
848
|
-
v-
|
|
849
|
-
|
|
850
|
-
>
|
|
851
|
-
<UIcon name="i-lucide-loader-circle" class="h-4 w-4 animate-spin" />
|
|
852
|
-
{{ t('observability.loadingActivity') }}
|
|
853
|
-
</p>
|
|
848
|
+
<div v-if="loading && !calls.length" class="space-y-2">
|
|
849
|
+
<USkeleton v-for="n in 3" :key="n" class="h-10 w-full rounded-xl" />
|
|
850
|
+
</div>
|
|
854
851
|
<div
|
|
855
852
|
v-else-if="error"
|
|
856
853
|
class="flex flex-col items-center gap-3 rounded-lg border border-dashed border-app-error-900/60 py-6 text-center text-sm text-app-error-400"
|
|
@@ -867,12 +864,11 @@ function exportJson() {
|
|
|
867
864
|
{{ t('common.retry') }}
|
|
868
865
|
</UButton>
|
|
869
866
|
</div>
|
|
870
|
-
<
|
|
867
|
+
<EmptyState
|
|
871
868
|
v-else-if="!calls.length"
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
</p>
|
|
869
|
+
icon="i-lucide-activity"
|
|
870
|
+
:title="t('observability.noCalls')"
|
|
871
|
+
/>
|
|
876
872
|
|
|
877
873
|
<!-- per-call list, narrowable by outcome -->
|
|
878
874
|
<template v-else>
|
|
@@ -886,12 +882,11 @@ function exportJson() {
|
|
|
886
882
|
<!-- Narrowed to nothing reads differently from recorded nothing, and on this
|
|
887
883
|
surface it is the good news: the operator asked for the failures and there
|
|
888
884
|
are none. -->
|
|
889
|
-
<
|
|
885
|
+
<EmptyState
|
|
890
886
|
v-if="!visibleCalls.length"
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
</p>
|
|
887
|
+
icon="i-lucide-list-filter"
|
|
888
|
+
:title="t('observability.noCallsMatching')"
|
|
889
|
+
/>
|
|
895
890
|
|
|
896
891
|
<ul v-else class="space-y-2">
|
|
897
892
|
<li
|
|
@@ -1092,13 +1087,9 @@ function exportJson() {
|
|
|
1092
1087
|
|
|
1093
1088
|
<!-- Provided context: the complete context each container agent was given. -->
|
|
1094
1089
|
<div v-else-if="view === 'context'" class="mx-auto max-w-4xl space-y-5">
|
|
1095
|
-
<
|
|
1096
|
-
v-
|
|
1097
|
-
|
|
1098
|
-
>
|
|
1099
|
-
<UIcon name="i-lucide-loader-circle" class="h-4 w-4 animate-spin" />
|
|
1100
|
-
{{ t('observability.loadingContext') }}
|
|
1101
|
-
</p>
|
|
1090
|
+
<div v-if="contextLoading && !contextSnapshots.length" class="space-y-2">
|
|
1091
|
+
<USkeleton v-for="n in 3" :key="n" class="h-10 w-full rounded-xl" />
|
|
1092
|
+
</div>
|
|
1102
1093
|
<div
|
|
1103
1094
|
v-else-if="contextError && !contextSnapshots.length"
|
|
1104
1095
|
class="flex flex-col items-center gap-3 rounded-lg border border-dashed border-app-error-900/60 py-8 text-center text-sm text-app-error-400"
|
|
@@ -1115,12 +1106,11 @@ function exportJson() {
|
|
|
1115
1106
|
{{ t('common.retry') }}
|
|
1116
1107
|
</UButton>
|
|
1117
1108
|
</div>
|
|
1118
|
-
<
|
|
1109
|
+
<EmptyState
|
|
1119
1110
|
v-else-if="!contextSnapshots.length"
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
</p>
|
|
1111
|
+
icon="i-lucide-file-text"
|
|
1112
|
+
:title="t('observability.noContext')"
|
|
1113
|
+
/>
|
|
1124
1114
|
|
|
1125
1115
|
<ul v-else class="space-y-2">
|
|
1126
1116
|
<li
|
|
@@ -1251,19 +1241,14 @@ function exportJson() {
|
|
|
1251
1241
|
</span>
|
|
1252
1242
|
</section>
|
|
1253
1243
|
|
|
1254
|
-
<
|
|
1255
|
-
v-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
<UIcon name="i-lucide-loader-circle" class="h-4 w-4 animate-spin" />
|
|
1259
|
-
{{ t('observability.loadingSearch') }}
|
|
1260
|
-
</p>
|
|
1261
|
-
<p
|
|
1244
|
+
<div v-if="searchLoading && !searchQueries.length" class="space-y-2">
|
|
1245
|
+
<USkeleton v-for="n in 3" :key="n" class="h-10 w-full rounded-xl" />
|
|
1246
|
+
</div>
|
|
1247
|
+
<EmptyState
|
|
1262
1248
|
v-else-if="!searchQueries.length"
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
</p>
|
|
1249
|
+
icon="i-lucide-search"
|
|
1250
|
+
:title="t('observability.noSearch')"
|
|
1251
|
+
/>
|
|
1267
1252
|
|
|
1268
1253
|
<div v-else>
|
|
1269
1254
|
<SectionLabel class="mb-2">
|
|
@@ -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">
|
|
@@ -257,7 +257,7 @@ const chipClass = computed(() =>
|
|
|
257
257
|
|
|
258
258
|
const backdropClass = computed(() => [
|
|
259
259
|
WINDOWED_OVERLAY_CLASS,
|
|
260
|
-
'z-50 justify-center bg-
|
|
260
|
+
'z-50 justify-center bg-elevated/75 backdrop-blur-sm',
|
|
261
261
|
props.variant === 'centered' ? 'items-center' : 'items-stretch',
|
|
262
262
|
])
|
|
263
263
|
// The centered cap is the smaller of 90dvh and the backdrop's padded box: with a large inset (a
|
|
@@ -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 {
|