@cat-factory/app 0.261.2 → 0.261.4

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.
Files changed (90) hide show
  1. package/README.md +40 -0
  2. package/app/components/board/AddTaskModal.vue +10 -6
  3. package/app/components/board/AgentStopButton.vue +2 -5
  4. package/app/components/board/CreateInitiativeModal.vue +2 -7
  5. package/app/components/board/RecurringPipelineModal.vue +10 -6
  6. package/app/components/bootstrap/BootstrapModal.vue +5 -24
  7. package/app/components/brainstorm/BrainstormWindow.vue +7 -15
  8. package/app/components/clarity/ClarityReviewWindow.vue +7 -15
  9. package/app/components/documents/DocumentImportModal.vue +2 -6
  10. package/app/components/documents/DocumentSourceConnectModal.vue +3 -12
  11. package/app/components/documents/DocumentSyncState.vue +2 -7
  12. package/app/components/documents/DocumentTemplatesModal.vue +4 -19
  13. package/app/components/documents/SpawnPreviewModal.vue +3 -12
  14. package/app/components/foundational/FoundationalServiceCatalogList.vue +3 -11
  15. package/app/components/foundational/FoundationalServiceRegistry.vue +3 -11
  16. package/app/components/foundational/FoundationalServiceSources.vue +5 -13
  17. package/app/components/foundational/FoundationalSuppressions.vue +2 -6
  18. package/app/components/fragments/FragmentLibraryManager.vue +12 -20
  19. package/app/components/github/AddServiceFromRepoModal.vue +3 -12
  20. package/app/components/github/GitHubConnect.vue +4 -12
  21. package/app/components/github/GitHubPanel.vue +9 -17
  22. package/app/components/github/RepoTreeBrowser.vue +2 -7
  23. package/app/components/humanTest/HumanTestWindow.vue +2 -7
  24. package/app/components/initiative/InitiativeTrackerWindow.vue +2 -3
  25. package/app/components/layout/AccountAuditLog.vue +2 -7
  26. package/app/components/layout/AccountDeploymentSettings.vue +11 -51
  27. package/app/components/layout/AccountModelPolicySettings.vue +2 -5
  28. package/app/components/layout/AccountPlatformAlertSettings.vue +2 -5
  29. package/app/components/layout/AccountRunCredentialSettings.vue +2 -5
  30. package/app/components/layout/AccountTeamSettings.vue +9 -18
  31. package/app/components/layout/BoardSwitcher.vue +6 -14
  32. package/app/components/layout/BoardToolbar.vue +3 -10
  33. package/app/components/layout/NotificationsInbox.vue +3 -13
  34. package/app/components/layout/WorkspaceMembersSettings.vue +6 -15
  35. package/app/components/notifications/NotificationSettingsPanel.vue +3 -11
  36. package/app/components/panels/DecisionModal.vue +2 -7
  37. package/app/components/panels/inspector/RecurringScheduleSettings.vue +4 -20
  38. package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +3 -11
  39. package/app/components/panels/inspector/ServiceTestSecrets.vue +3 -11
  40. package/app/components/panels/inspector/ServiceValidationConfig.vue +4 -12
  41. package/app/components/pipeline/PipelineBuilder.vue +4 -7
  42. package/app/components/providers/ApiKeysSection.vue +4 -15
  43. package/app/components/providers/PersonalCredentialModal.vue +2 -5
  44. package/app/components/providers/PersonalSubscriptionSection.vue +3 -10
  45. package/app/components/providers/VendorCredentialsModal.vue +5 -20
  46. package/app/components/requirements/RequirementsReviewWindow.vue +11 -19
  47. package/app/components/sandbox/SandboxPanel.vue +4 -18
  48. package/app/components/settings/ApiTokensPanel.vue +4 -12
  49. package/app/components/settings/BudgetSettings.vue +2 -5
  50. package/app/components/settings/CloudflareHandlerSection.vue +3 -12
  51. package/app/components/settings/ConsensusGroupsSection.vue +3 -12
  52. package/app/components/settings/CustomManifestTypeEditor.vue +3 -13
  53. package/app/components/settings/DefaultProvisionTypeSection.vue +2 -5
  54. package/app/components/settings/InfraHandlersConfigurator.vue +7 -14
  55. package/app/components/settings/InfrastructureBackendPicker.vue +2 -7
  56. package/app/components/settings/IssueTrackerPanel.vue +4 -18
  57. package/app/components/settings/LocalContainerPoolSettings.vue +2 -6
  58. package/app/components/settings/LocalModelEndpointsPanel.vue +3 -11
  59. package/app/components/settings/ModelConfigurationPanel.vue +13 -16
  60. package/app/components/settings/ModelPresetHealthModal.vue +3 -13
  61. package/app/components/settings/ObservabilityConnectionPanel.vue +6 -14
  62. package/app/components/settings/OpenRouterCatalogPanel.vue +3 -12
  63. package/app/components/settings/PackageRegistriesPanel.vue +4 -12
  64. package/app/components/settings/ProviderConnectionTab.vue +5 -13
  65. package/app/components/settings/RiskPolicyHealthModal.vue +2 -7
  66. package/app/components/settings/RiskPolicyPanel.vue +5 -13
  67. package/app/components/settings/ServiceFragmentDefaultsPanel.vue +2 -7
  68. package/app/components/settings/SharedStacksPanel.vue +10 -20
  69. package/app/components/settings/TaskTypeSuppressionsPanel.vue +3 -12
  70. package/app/components/settings/UserSecretsSection.vue +3 -11
  71. package/app/components/settings/WorkspaceMetadataSettings.vue +2 -6
  72. package/app/components/settings/WorkspaceSettingsPanel.vue +2 -6
  73. package/app/components/skills/SkillLibraryManager.vue +5 -13
  74. package/app/components/slack/SlackPanel.vue +7 -15
  75. package/app/components/tasks/BugHuntModal.vue +2 -6
  76. package/app/components/tasks/TaskImportModal.vue +2 -6
  77. package/app/components/tasks/TaskSourceConnectModal.vue +4 -18
  78. package/app/composables/useBlockDeletion.ts +2 -6
  79. package/app/composables/useCopyToClipboard.ts +7 -1
  80. package/app/composables/usePipelineErrorToast.spec.ts +76 -16
  81. package/app/composables/usePipelineErrorToast.ts +109 -50
  82. package/app/stores/board/context.ts +9 -2
  83. package/app/stores/board/dependencies.ts +3 -13
  84. package/app/stores/board/mutations.ts +2 -7
  85. package/app/stores/board/placement.ts +12 -25
  86. package/app/stores/board/removal.ts +2 -7
  87. package/app/stores/board.spec.ts +15 -8
  88. package/app/stores/board.ts +11 -1
  89. package/app/stores/recurringPipelines.ts +5 -11
  90. package/package.json +2 -2
@@ -13,6 +13,7 @@ const auth = useAuthStore()
13
13
  const workspace = useWorkspaceStore()
14
14
  const models = useModelsStore()
15
15
  const toast = useToast()
16
+ const { present } = usePipelineErrorToast()
16
17
  const { t, d } = useI18n()
17
18
  const { confirm } = useConfirm()
18
19
 
@@ -177,11 +178,7 @@ async function connect() {
177
178
  color: 'success',
178
179
  })
179
180
  } catch (e) {
180
- toast.add({
181
- title: t('personalSubscriptions.toast.connectFailed'),
182
- description: e instanceof Error ? e.message : String(e),
183
- color: 'error',
184
- })
181
+ present(e, 'personalSubscriptions.toast.connectFailed')
185
182
  } finally {
186
183
  busy.value = false
187
184
  }
@@ -203,11 +200,7 @@ async function disconnect(v: SubscriptionVendor) {
203
200
  if (workspace.workspaceId) await models.refresh(workspace.workspaceId)
204
201
  toast.add({ title: t('personalSubscriptions.toast.disconnected'), icon: 'i-lucide-check' })
205
202
  } catch (e) {
206
- toast.add({
207
- title: t('personalSubscriptions.toast.disconnectFailed'),
208
- description: e instanceof Error ? e.message : String(e),
209
- color: 'error',
210
- })
203
+ present(e, 'personalSubscriptions.toast.disconnectFailed')
211
204
  }
212
205
  }
213
206
  </script>
@@ -15,6 +15,7 @@ const ui = useUiStore()
15
15
  const workspace = useWorkspaceStore()
16
16
  const creds = useVendorCredentialsStore()
17
17
  const toast = useToast()
18
+ const { present } = usePipelineErrorToast()
18
19
  const { confirm } = useConfirm()
19
20
 
20
21
  const open = computed({
@@ -131,11 +132,7 @@ async function add() {
131
132
  color: 'success',
132
133
  })
133
134
  } catch (e) {
134
- toast.add({
135
- title: t('providers.vendorCredentials.toast.connectFailed'),
136
- description: e instanceof Error ? e.message : String(e),
137
- color: 'error',
138
- })
135
+ present(e, 'providers.vendorCredentials.toast.connectFailed')
139
136
  } finally {
140
137
  busy.value = false
141
138
  }
@@ -145,11 +142,7 @@ async function toggleEnabled(cred: { id: string }, enabled: boolean) {
145
142
  try {
146
143
  await creds.update(cred.id, { enabled })
147
144
  } catch (e) {
148
- toast.add({
149
- title: t('providers.vendorCredentials.toast.updateFailed'),
150
- description: e instanceof Error ? e.message : String(e),
151
- color: 'error',
152
- })
145
+ present(e, 'providers.vendorCredentials.toast.updateFailed')
153
146
  }
154
147
  }
155
148
 
@@ -157,11 +150,7 @@ async function toggleDefault(cred: { id: string; isDefault: boolean }) {
157
150
  try {
158
151
  await creds.update(cred.id, { isDefault: !cred.isDefault })
159
152
  } catch (e) {
160
- toast.add({
161
- title: t('providers.vendorCredentials.toast.updateFailed'),
162
- description: e instanceof Error ? e.message : String(e),
163
- color: 'error',
164
- })
153
+ present(e, 'providers.vendorCredentials.toast.updateFailed')
165
154
  }
166
155
  }
167
156
 
@@ -177,11 +166,7 @@ async function remove(cred: { id: string; label: string }) {
177
166
  try {
178
167
  await creds.remove(cred.id)
179
168
  } catch (e) {
180
- toast.add({
181
- title: t('providers.vendorCredentials.toast.removeFailed'),
182
- description: e instanceof Error ? e.message : String(e),
183
- color: 'error',
184
- })
169
+ present(e, 'providers.vendorCredentials.toast.removeFailed')
185
170
  }
186
171
  }
187
172
 
@@ -30,6 +30,7 @@ const board = useBoardStore()
30
30
  const requirements = useRequirementsStore()
31
31
  const models = useModelsStore()
32
32
  const toast = useToast()
33
+ const { present } = usePipelineErrorToast()
33
34
  const { t } = useI18n()
34
35
  const access = useWorkspaceAccess()
35
36
 
@@ -190,15 +191,6 @@ const STATUS_LABELS = computed<Record<ReviewItemStatus, string>>(() => ({
190
191
  recommend_requested: t('requirements.itemStatus.recommend_requested'),
191
192
  }))
192
193
 
193
- function notifyError(title: string, e: unknown) {
194
- toast.add({
195
- title,
196
- description: e instanceof Error ? e.message : String(e),
197
- icon: 'i-lucide-triangle-alert',
198
- color: 'error',
199
- })
200
- }
201
-
202
194
  // Answers auto-save: there is no explicit "save" button. The textarea is pre-seeded with
203
195
  // the recorded reply (see the watch below); editing and blurring persists it. Persist only
204
196
  // when the trimmed draft actually differs from what's already recorded, so blurring an
@@ -213,7 +205,7 @@ async function persistDraft(
213
205
  try {
214
206
  await requirements.reply(r, item.id, text)
215
207
  } catch (e) {
216
- notifyError(t('requirements.errors.saveAnswer'), e)
208
+ present(e, 'requirements.errors.saveAnswer')
217
209
  }
218
210
  }
219
211
 
@@ -280,7 +272,7 @@ async function setStatus(item: RequirementReviewItem, itemStatus: ReviewItemStat
280
272
  try {
281
273
  await requirements.setItemStatus(review.value, item.id, itemStatus)
282
274
  } catch (e) {
283
- notifyError(t('requirements.errors.updateFinding'), e)
275
+ present(e, 'requirements.errors.updateFinding')
284
276
  }
285
277
  }
286
278
 
@@ -535,7 +527,7 @@ async function requestRecommendations() {
535
527
  },
536
528
  )
537
529
  } catch (e) {
538
- notifyError(t('requirements.errors.requestRecommendations'), e)
530
+ present(e, 'requirements.errors.requestRecommendations')
539
531
  }
540
532
  }
541
533
 
@@ -544,7 +536,7 @@ async function acceptRecommendation(rec: RequirementRecommendation) {
544
536
  try {
545
537
  await requirements.acceptRecommendation(review.value, rec.id)
546
538
  } catch (e) {
547
- notifyError(t('requirements.errors.acceptRecommendation'), e)
539
+ present(e, 'requirements.errors.acceptRecommendation')
548
540
  }
549
541
  }
550
542
 
@@ -553,7 +545,7 @@ async function rejectRecommendation(rec: RequirementRecommendation) {
553
545
  try {
554
546
  await requirements.rejectRecommendation(review.value, rec.id)
555
547
  } catch (e) {
556
- notifyError(t('requirements.errors.rejectRecommendation'), e)
548
+ present(e, 'requirements.errors.rejectRecommendation')
557
549
  }
558
550
  }
559
551
 
@@ -565,7 +557,7 @@ async function reRequestRecommendation(rec: RequirementRecommendation) {
565
557
  await requirements.reRequestRecommendation(review.value, rec.id, note)
566
558
  reRequestNotes.value = { ...reRequestNotes.value, [rec.id]: '' }
567
559
  } catch (e) {
568
- notifyError(t('requirements.errors.reRequestRecommendation'), e)
560
+ present(e, 'requirements.errors.reRequestRecommendation')
569
561
  }
570
562
  }
571
563
 
@@ -575,7 +567,7 @@ async function incorporate(feedback?: string) {
575
567
  await flushDrafts()
576
568
  await requirements.incorporate(review.value, feedback)
577
569
  } catch (e) {
578
- notifyError(t('requirements.errors.incorporate'), e)
570
+ present(e, 'requirements.errors.incorporate')
579
571
  return
580
572
  }
581
573
  redoComment.value = ''
@@ -605,7 +597,7 @@ async function reReview() {
605
597
  icon: 'i-lucide-sparkles',
606
598
  })
607
599
  } catch (e) {
608
- notifyError(t('requirements.errors.reReview'), e)
600
+ present(e, 'requirements.errors.reReview')
609
601
  }
610
602
  }
611
603
 
@@ -617,7 +609,7 @@ async function proceed() {
617
609
  await requirements.proceed(blockId.value)
618
610
  toast.add({ title: t('requirements.toast.proceeding'), icon: 'i-lucide-arrow-right' })
619
611
  } catch (e) {
620
- notifyError(t('requirements.errors.proceed'), e)
612
+ present(e, 'requirements.errors.proceed')
621
613
  } finally {
622
614
  acting.value = false
623
615
  }
@@ -637,7 +629,7 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
637
629
  toast.add({ title: t('requirements.toast.extraRoundGranted'), icon: 'i-lucide-rotate-cw' })
638
630
  }
639
631
  } catch (e) {
640
- notifyError(t('requirements.errors.resolveReview'), e)
632
+ present(e, 'requirements.errors.resolveReview')
641
633
  } finally {
642
634
  acting.value = false
643
635
  }
@@ -18,6 +18,7 @@ import type {
18
18
  const ui = useUiStore()
19
19
  const store = useSandboxStore()
20
20
  const toast = useToast()
21
+ const { present } = usePipelineErrorToast()
21
22
  const { t } = useI18n()
22
23
 
23
24
  // Exhaustive enum→label maps of literal `t(...)` keys (keeps the typed-key drift guard
@@ -167,12 +168,7 @@ async function createAndRun() {
167
168
  await store.launch(created.id)
168
169
  toast.add({ title: t('sandbox.toast.complete'), icon: 'i-lucide-check', color: 'success' })
169
170
  } catch (e) {
170
- toast.add({
171
- title: t('sandbox.toast.runFailed'),
172
- description: e instanceof Error ? e.message : String(e),
173
- icon: 'i-lucide-triangle-alert',
174
- color: 'error',
175
- })
171
+ present(e, 'sandbox.toast.runFailed')
176
172
  }
177
173
  }
178
174
 
@@ -223,12 +219,7 @@ async function saveVersion() {
223
219
  toast.add({ title: t('sandbox.toast.versionSaved'), icon: 'i-lucide-check', color: 'success' })
224
220
  editing.value = null
225
221
  } catch (e) {
226
- toast.add({
227
- title: t('sandbox.toast.saveFailed'),
228
- description: e instanceof Error ? e.message : String(e),
229
- icon: 'i-lucide-triangle-alert',
230
- color: 'error',
231
- })
222
+ present(e, 'sandbox.toast.saveFailed')
232
223
  } finally {
233
224
  savingPrompt.value = false
234
225
  }
@@ -239,12 +230,7 @@ async function archive(prompt: SandboxPromptVersion) {
239
230
  await store.archivePrompt(prompt.id)
240
231
  if (editing.value?.id === prompt.id) editing.value = null
241
232
  } catch (e) {
242
- toast.add({
243
- title: t('sandbox.toast.archiveFailed'),
244
- description: e instanceof Error ? e.message : String(e),
245
- icon: 'i-lucide-triangle-alert',
246
- color: 'error',
247
- })
233
+ present(e, 'sandbox.toast.archiveFailed')
248
234
  }
249
235
  }
250
236
  </script>
@@ -58,6 +58,7 @@ function minterLabel(key: PublicApiKey): string | null {
58
58
  : key.createdByUserId
59
59
  }
60
60
  const toast = useToast()
61
+ const { present } = usePipelineErrorToast()
61
62
  const { confirmAction, toastDone } = useConfirmAction()
62
63
 
63
64
  const open = computed({
@@ -74,15 +75,6 @@ const busy = ref(false)
74
75
  // re-fetchable, so it lives only in this transient ref (not the store).
75
76
  const newSecret = ref<string | null>(null)
76
77
 
77
- function notifyError(title: string, e: unknown) {
78
- toast.add({
79
- title,
80
- description: e instanceof Error ? e.message : String(e),
81
- icon: 'i-lucide-triangle-alert',
82
- color: 'error',
83
- })
84
- }
85
-
86
78
  watch(
87
79
  open,
88
80
  async (isOpen) => {
@@ -94,7 +86,7 @@ watch(
94
86
  try {
95
87
  await store.ensureLoaded()
96
88
  } catch (e) {
97
- notifyError(t('settings.apiTokens.toast.loadFailed'), e)
89
+ present(e, 'settings.apiTokens.toast.loadFailed')
98
90
  }
99
91
  },
100
92
  { immediate: true },
@@ -115,7 +107,7 @@ async function createToken() {
115
107
  color: 'success',
116
108
  })
117
109
  } catch (e) {
118
- notifyError(t('settings.apiTokens.toast.createFailed'), e)
110
+ present(e, 'settings.apiTokens.toast.createFailed')
119
111
  } finally {
120
112
  busy.value = false
121
113
  }
@@ -132,7 +124,7 @@ async function revokeToken(key: PublicApiKey) {
132
124
  await store.revoke(key.id)
133
125
  toastDone('revoke', key.label)
134
126
  } catch (e) {
135
- notifyError(t('settings.apiTokens.toast.revokeFailed'), e)
127
+ present(e, 'settings.apiTokens.toast.revokeFailed')
136
128
  } finally {
137
129
  busy.value = false
138
130
  }
@@ -8,6 +8,7 @@ import { computed, reactive, ref, watch, type Ref } from 'vue'
8
8
 
9
9
  const { t, n } = useI18n()
10
10
  const toast = useToast()
11
+ const { present } = usePipelineErrorToast()
11
12
 
12
13
  const settingsStore = useWorkspaceSettingsStore()
13
14
  const userSettingsStore = useUserSettingsStore()
@@ -37,11 +38,7 @@ async function runSave(saving: Ref<boolean>, save: () => Promise<unknown>) {
37
38
  // ignore — the budget is persisted; the meter will catch up on the next snapshot.
38
39
  }
39
40
  } catch (e) {
40
- toast.add({
41
- title: t('settings.workspaceSettings.toast.budgetSaveFailed'),
42
- description: e instanceof Error ? e.message : String(e),
43
- color: 'error',
44
- })
41
+ present(e, 'settings.workspaceSettings.toast.budgetSaveFailed')
45
42
  } finally {
46
43
  saving.value = false
47
44
  }
@@ -19,6 +19,7 @@ type CloudflareConfig = CloudflareHandlerConfig['cloudflare']
19
19
  const { t } = useI18n()
20
20
  const infra = useInfraConfigStore()
21
21
  const toast = useToast()
22
+ const { present } = usePipelineErrorToast()
22
23
  const { confirmAction } = useConfirmAction()
23
24
 
24
25
  const busy = ref(false)
@@ -106,12 +107,7 @@ async function save() {
106
107
  color: 'success',
107
108
  })
108
109
  } catch (e) {
109
- toast.add({
110
- title: t('settings.infrastructure.handler.saveFailed'),
111
- description: e instanceof Error ? e.message : String(e),
112
- icon: 'i-lucide-triangle-alert',
113
- color: 'error',
114
- })
110
+ present(e, 'settings.infrastructure.handler.saveFailed')
115
111
  } finally {
116
112
  busy.value = false
117
113
  }
@@ -139,12 +135,7 @@ async function remove() {
139
135
  await infra.unregisterHandler('cloudflare')
140
136
  toast.add({ title: t('settings.infrastructure.handler.removed'), icon: 'i-lucide-check' })
141
137
  } catch (e) {
142
- toast.add({
143
- title: t('settings.infrastructure.handler.saveFailed'),
144
- description: e instanceof Error ? e.message : String(e),
145
- icon: 'i-lucide-triangle-alert',
146
- color: 'error',
147
- })
138
+ present(e, 'settings.infrastructure.handler.saveFailed')
148
139
  } finally {
149
140
  busy.value = false
150
141
  }
@@ -18,7 +18,7 @@ const groups = useConsensusGroupsStore()
18
18
  const models = useModelsStore()
19
19
  const creds = useVendorCredentialsStore()
20
20
  const uiMode = useUiModeStore()
21
- const toast = useToast()
21
+ const { present } = usePipelineErrorToast()
22
22
  const { confirm } = useConfirm()
23
23
 
24
24
  /**
@@ -184,7 +184,7 @@ async function save() {
184
184
  else await groups.create(body)
185
185
  editor.value = null
186
186
  } catch (err) {
187
- fail(t('settings.consensusGroups.toast.saveFailed'), err)
187
+ present(err, 'settings.consensusGroups.toast.saveFailed')
188
188
  } finally {
189
189
  busy.value = false
190
190
  }
@@ -203,20 +203,11 @@ async function remove(group: ConsensusGroup) {
203
203
  try {
204
204
  await groups.remove(group.id)
205
205
  } catch (err) {
206
- fail(t('settings.consensusGroups.toast.deleteFailed'), err)
206
+ present(err, 'settings.consensusGroups.toast.deleteFailed')
207
207
  } finally {
208
208
  busy.value = false
209
209
  }
210
210
  }
211
-
212
- function fail(title: string, e: unknown) {
213
- toast.add({
214
- title,
215
- description: e instanceof Error ? e.message : String(e),
216
- icon: 'i-lucide-triangle-alert',
217
- color: 'error',
218
- })
219
- }
220
211
  </script>
221
212
 
222
213
  <template>
@@ -9,7 +9,7 @@ import type { CustomManifestType } from '@cat-factory/contracts'
9
9
 
10
10
  const { t } = useI18n()
11
11
  const infra = useInfraConfigStore()
12
- const toast = useToast()
12
+ const { present } = usePipelineErrorToast()
13
13
  const { confirmAction, toastDone } = useConfirmAction()
14
14
 
15
15
  // A draft for the add/edit form. `manifestId` is locked on edit (it's the PK).
@@ -68,12 +68,7 @@ async function save() {
68
68
  })
69
69
  startAdd()
70
70
  } catch (e) {
71
- toast.add({
72
- title: t('settings.infrastructure.customType.saveFailed'),
73
- description: e instanceof Error ? e.message : String(e),
74
- icon: 'i-lucide-triangle-alert',
75
- color: 'error',
76
- })
71
+ present(e, 'settings.infrastructure.customType.saveFailed')
77
72
  } finally {
78
73
  busy.value = false
79
74
  }
@@ -87,12 +82,7 @@ async function remove(type: CustomManifestType) {
87
82
  if (editing.value && draft.manifestId === type.manifestId) startAdd()
88
83
  toastDone('remove', type.label)
89
84
  } catch (e) {
90
- toast.add({
91
- title: t('settings.infrastructure.customType.removeFailed'),
92
- description: e instanceof Error ? e.message : String(e),
93
- icon: 'i-lucide-triangle-alert',
94
- color: 'error',
95
- })
85
+ present(e, 'settings.infrastructure.customType.removeFailed')
96
86
  } finally {
97
87
  busy.value = false
98
88
  }
@@ -25,6 +25,7 @@ const { t } = useI18n()
25
25
  const infra = useInfraConfigStore()
26
26
  const settingsStore = useWorkspaceSettingsStore()
27
27
  const toast = useToast()
28
+ const { present } = usePipelineErrorToast()
28
29
 
29
30
  onMounted(() => {
30
31
  void infra.ensureLoaded()
@@ -91,11 +92,7 @@ async function save() {
91
92
  })
92
93
  toast.add({ title: t('settings.defaultProvision.saved'), color: 'success' })
93
94
  } catch (e) {
94
- toast.add({
95
- title: t('settings.defaultProvision.saveFailed'),
96
- description: e instanceof Error ? e.message : undefined,
97
- color: 'error',
98
- })
95
+ present(e, 'settings.defaultProvision.saveFailed')
99
96
  } finally {
100
97
  saving.value = false
101
98
  }
@@ -36,6 +36,7 @@ const infra = useInfraConfigStore()
36
36
  const auth = useAuthStore()
37
37
  const ui = useUiStore()
38
38
  const toast = useToast()
39
+ const { present } = usePipelineErrorToast()
39
40
  const { confirmAction } = useConfirmAction()
40
41
 
41
42
  const isLocal = computed(() => auth.localMode?.enabled === true)
@@ -221,7 +222,7 @@ async function saveKube(payload: { config: KubeHandlerConfig; secrets: Record<st
221
222
  })
222
223
  toastSaved()
223
224
  } catch (e) {
224
- notifyError(e)
225
+ present(e, 'settings.infrastructure.handler.saveFailed')
225
226
  } finally {
226
227
  busy.value = false
227
228
  }
@@ -234,7 +235,7 @@ async function removeKube() {
234
235
  await infra.unregisterHandler('kubernetes')
235
236
  toastRemoved()
236
237
  } catch (e) {
237
- notifyError(e)
238
+ present(e, 'settings.infrastructure.handler.saveFailed')
238
239
  } finally {
239
240
  busy.value = false
240
241
  }
@@ -258,7 +259,7 @@ async function saveKubeOverride(payload: {
258
259
  })
259
260
  toastSaved()
260
261
  } catch (e) {
261
- notifyError(e)
262
+ present(e, 'settings.infrastructure.handler.saveFailed')
262
263
  } finally {
263
264
  busy.value = false
264
265
  }
@@ -272,7 +273,7 @@ async function removeKubeOverride() {
272
273
  await infra.removeUserHandler('kubernetes')
273
274
  toastRemoved()
274
275
  } catch (e) {
275
- notifyError(e)
276
+ present(e, 'settings.infrastructure.handler.saveFailed')
276
277
  } finally {
277
278
  busy.value = false
278
279
  }
@@ -380,7 +381,7 @@ async function saveCustom(payload: {
380
381
  })
381
382
  toastSaved()
382
383
  } catch (e) {
383
- notifyError(e)
384
+ present(e, 'settings.infrastructure.handler.saveFailed')
384
385
  } finally {
385
386
  busy.value = false
386
387
  }
@@ -394,7 +395,7 @@ async function removeCustom() {
394
395
  await infra.unregisterHandler('custom', selectedCustomId.value)
395
396
  toastRemoved()
396
397
  } catch (e) {
397
- notifyError(e)
398
+ present(e, 'settings.infrastructure.handler.saveFailed')
398
399
  } finally {
399
400
  busy.value = false
400
401
  }
@@ -410,14 +411,6 @@ function toastSaved() {
410
411
  function toastRemoved() {
411
412
  toast.add({ title: t('settings.infrastructure.handler.removed'), icon: 'i-lucide-check' })
412
413
  }
413
- function notifyError(e: unknown) {
414
- toast.add({
415
- title: t('settings.infrastructure.handler.saveFailed'),
416
- description: e instanceof Error ? e.message : String(e),
417
- icon: 'i-lucide-triangle-alert',
418
- color: 'error',
419
- })
420
- }
421
414
  </script>
422
415
 
423
416
  <template>
@@ -27,7 +27,7 @@ const { t } = useI18n()
27
27
  const auth = useAuthStore()
28
28
  const settings = useWorkspaceSettingsStore()
29
29
  const providerConnections = useProviderConnectionsStore()
30
- const toast = useToast()
30
+ const { present } = usePipelineErrorToast()
31
31
 
32
32
  type BackendKind = ExecutionBackendKind | TestEnvBackendKind
33
33
  // A radio item: the built-in facade runtime, one per registered backend kind (built-in +
@@ -223,12 +223,7 @@ async function setDelegate(value: boolean) {
223
223
  // Only reachable on the execution axis (`writable` is false for testEnv now).
224
224
  await settings.update({ delegateAgentsToRunnerPool: value })
225
225
  } catch (e) {
226
- toast.add({
227
- title: t('settings.infrastructure.updateFailed'),
228
- description: e instanceof Error ? e.message : String(e),
229
- icon: 'i-lucide-triangle-alert',
230
- color: 'error',
231
- })
226
+ present(e, 'settings.infrastructure.updateFailed')
232
227
  } finally {
233
228
  saving.value = false
234
229
  }
@@ -23,6 +23,7 @@ const tracker = useTrackerStore()
23
23
  const tasks = useTasksStore()
24
24
  const ui = useUiStore()
25
25
  const toast = useToast()
26
+ const { present } = usePipelineErrorToast()
26
27
 
27
28
  // --- filing tracker + writeback (one Save, persisted on tracker settings) -----
28
29
  const trackerKind = ref<TrackerKind | null>(null)
@@ -119,12 +120,7 @@ async function save() {
119
120
  color: 'success',
120
121
  })
121
122
  } catch (e) {
122
- toast.add({
123
- title: t('settings.issueTracker.toast.saveFailed'),
124
- description: e instanceof Error ? e.message : String(e),
125
- icon: 'i-lucide-triangle-alert',
126
- color: 'error',
127
- })
123
+ present(e, 'settings.issueTracker.toast.saveFailed')
128
124
  } finally {
129
125
  saving.value = false
130
126
  }
@@ -150,12 +146,7 @@ async function toggleSource(source: TaskSourceKind, enabled: boolean) {
150
146
  try {
151
147
  await tasks.setEnabled(source, enabled)
152
148
  } catch (e) {
153
- toast.add({
154
- title: t('settings.issueTracker.toast.updateFailed'),
155
- description: e instanceof Error ? e.message : String(e),
156
- icon: 'i-lucide-triangle-alert',
157
- color: 'error',
158
- })
149
+ present(e, 'settings.issueTracker.toast.updateFailed')
159
150
  } finally {
160
151
  togglingSource.value = null
161
152
  }
@@ -189,12 +180,7 @@ async function checkSetup(source: TaskSourceKind) {
189
180
  try {
190
181
  await tasks.checkSetup(source)
191
182
  } catch (e) {
192
- toast.add({
193
- title: t('settings.issueTracker.toast.checkFailed'),
194
- description: e instanceof Error ? e.message : String(e),
195
- icon: 'i-lucide-triangle-alert',
196
- color: 'error',
197
- })
183
+ present(e, 'settings.issueTracker.toast.checkFailed')
198
184
  }
199
185
  }
200
186
  </script>
@@ -14,6 +14,7 @@ import { reactive, ref, watch } from 'vue'
14
14
  const { t } = useI18n()
15
15
  const store = useLocalSettingsStore()
16
16
  const toast = useToast()
17
+ const { present } = usePipelineErrorToast()
17
18
 
18
19
  const saving = ref(false)
19
20
 
@@ -65,12 +66,7 @@ async function save() {
65
66
  color: 'success',
66
67
  })
67
68
  } catch (e) {
68
- toast.add({
69
- title: t('settings.localMode.toast.saveFailed'),
70
- description: e instanceof Error ? e.message : String(e),
71
- icon: 'i-lucide-triangle-alert',
72
- color: 'error',
73
- })
69
+ present(e, 'settings.localMode.toast.saveFailed')
74
70
  } finally {
75
71
  saving.value = false
76
72
  }
@@ -19,6 +19,7 @@ const { t } = useI18n()
19
19
  const ui = useUiStore()
20
20
  const store = useLocalModelsStore()
21
21
  const toast = useToast()
22
+ const { present } = usePipelineErrorToast()
22
23
  const { confirm } = useConfirm()
23
24
 
24
25
  const open = computed({
@@ -96,15 +97,6 @@ watch(provider, (p) => {
96
97
  tested.value = false
97
98
  })
98
99
 
99
- function notifyError(title: string, e: unknown) {
100
- toast.add({
101
- title,
102
- description: e instanceof Error ? e.message : String(e),
103
- icon: 'i-lucide-triangle-alert',
104
- color: 'error',
105
- })
106
- }
107
-
108
100
  async function test() {
109
101
  if (!baseUrl.value.trim()) return
110
102
  testing.value = true
@@ -166,7 +158,7 @@ async function save() {
166
158
  color: 'success',
167
159
  })
168
160
  } catch (e) {
169
- notifyError(t('settings.localModelEndpoints.toast.saveFailed'), e)
161
+ present(e, 'settings.localModelEndpoints.toast.saveFailed')
170
162
  } finally {
171
163
  busy.value = false
172
164
  }
@@ -195,7 +187,7 @@ async function remove(p: LocalRunner) {
195
187
  }
196
188
  toast.add({ title: t('settings.localModelEndpoints.toast.removed'), icon: 'i-lucide-check' })
197
189
  } catch (e) {
198
- notifyError(t('settings.localModelEndpoints.toast.removeFailed'), e)
190
+ present(e, 'settings.localModelEndpoints.toast.removeFailed')
199
191
  } finally {
200
192
  busy.value = false
201
193
  }