@cat-factory/app 0.121.0 → 0.121.2
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/app/composables/useUpsertList.spec.ts +13 -0
- package/app/stores/accounts.ts +9 -9
- package/app/stores/agentRuns.spec.ts +54 -1
- package/app/stores/agentRuns.ts +10 -19
- package/app/stores/bootstrap.ts +9 -5
- package/app/stores/github.ts +15 -8
- package/app/stores/pipelines.ts +12 -10
- package/app/stores/releaseHealth.ts +8 -5
- package/app/stores/sharedStacks.ts +3 -8
- package/app/stores/ui/modals.ts +810 -0
- package/app/stores/ui/navigation.ts +63 -0
- package/app/stores/ui/resultViews.ts +246 -0
- package/app/stores/ui.spec.ts +30 -0
- package/app/stores/ui.ts +19 -1072
- package/package.json +1 -1
|
@@ -0,0 +1,810 @@
|
|
|
1
|
+
import { ref } from 'vue'
|
|
2
|
+
import type { DocumentSourceKind, InfraSetupArea, TaskSourceKind } from '~/types/domain'
|
|
3
|
+
import type { PendingContext } from '~/composables/useContextLinking'
|
|
4
|
+
|
|
5
|
+
/** Values used to seed the add-task form when it is opened from another surface. */
|
|
6
|
+
export interface AddTaskPrefill {
|
|
7
|
+
title?: string
|
|
8
|
+
description?: string
|
|
9
|
+
/** Context items staged on the new task (e.g. the source issue), linked once created. */
|
|
10
|
+
context?: PendingContext[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Non-secret `local-k3s` connection values captured from the `cat-factory k3s` CLI deep-link
|
|
15
|
+
* (`?infraSetup=local-k3s&…`). Mirrors the params `buildK3sSetupUrl` emits (the CLI-side
|
|
16
|
+
* `k3s-handler.ts`); the ServiceAccount token is intentionally absent — the user pastes it.
|
|
17
|
+
*/
|
|
18
|
+
export interface K3sSetupPrefill {
|
|
19
|
+
label: string
|
|
20
|
+
apiServerUrl: string
|
|
21
|
+
namespaceTemplate: string
|
|
22
|
+
hostTemplate: string
|
|
23
|
+
// Absent when the link omitted the param, so the form keeps its engine default rather than
|
|
24
|
+
// forcing verification back on (which would break a self-signed local cluster).
|
|
25
|
+
insecureSkipTlsVerify?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The modal / panel slice of the UI store: every open-close flag for the dozens of modals,
|
|
30
|
+
* panels and hubs (document + task import, bootstrap, integrations, workspace/account settings,
|
|
31
|
+
* infrastructure, vendor credentials, the startup health advisories, the AI-onboarding surfaces,
|
|
32
|
+
* …), their deep-link params, and the hub came-from markers. Split out of the navigation +
|
|
33
|
+
* result-view state per refactoring candidate #4 so the god-object's modal churn is contained to
|
|
34
|
+
* one place. Composed into {@link useUiStore} with the same public names, so consumers are
|
|
35
|
+
* unchanged.
|
|
36
|
+
*/
|
|
37
|
+
export function createUiModals() {
|
|
38
|
+
const builderOpen = ref(false)
|
|
39
|
+
// Pipeline-health startup advisory: lists invalid pipelines (delete / reseed) + built-ins
|
|
40
|
+
// with a newer catalog version (reseed). `pipelineHealthSeen` gates auto-open to once per
|
|
41
|
+
// session so it does not re-pop on every snapshot re-hydration.
|
|
42
|
+
const pipelineHealthOpen = ref(false)
|
|
43
|
+
const pipelineHealthSeen = ref(false)
|
|
44
|
+
// Merge-preset health startup advisory: lists built-ins with a newer catalog version (reseed)
|
|
45
|
+
// and new built-in presets the workspace can add. `riskPolicyHealthSeen` gates auto-open to
|
|
46
|
+
// once per session so it does not re-pop on every snapshot re-hydration (mirrors pipelines).
|
|
47
|
+
const riskPolicyHealthOpen = ref(false)
|
|
48
|
+
const riskPolicyHealthSeen = ref(false)
|
|
49
|
+
// Model-preset health startup advisory: lists built-ins with a newer catalog version (reseed)
|
|
50
|
+
// and new built-in presets the workspace can add. `modelPresetHealthSeen` gates auto-open to
|
|
51
|
+
// once per session so it does not re-pop on every snapshot re-hydration (mirrors pipelines).
|
|
52
|
+
const modelPresetHealthOpen = ref(false)
|
|
53
|
+
const modelPresetHealthSeen = ref(false)
|
|
54
|
+
const decisionContext = ref<{ instanceId: string; decisionId: string } | null>(null)
|
|
55
|
+
|
|
56
|
+
// Document-source integration modals, keyed by source. `documentImport` and
|
|
57
|
+
// `spawnPreview` carry an optional target frame, so structure spawned from a
|
|
58
|
+
// frame's inspector lands inside that frame rather than creating new top-level
|
|
59
|
+
// frames. `documentConnect` carries the source whose connect form to show;
|
|
60
|
+
// `documentImport`'s source may be null to let the modal pick a connected one.
|
|
61
|
+
const documentConnect = ref<{ source: DocumentSourceKind } | null>(null)
|
|
62
|
+
const documentImport = ref<{
|
|
63
|
+
source: DocumentSourceKind | null
|
|
64
|
+
targetFrameId: string | null
|
|
65
|
+
} | null>(null)
|
|
66
|
+
// The workspace+DocKind template / exemplar management modal (WS1). A single boolean —
|
|
67
|
+
// it manages every kind's links in one place.
|
|
68
|
+
const documentTemplates = ref(false)
|
|
69
|
+
const spawnPreview = ref<{
|
|
70
|
+
source: DocumentSourceKind
|
|
71
|
+
externalId: string
|
|
72
|
+
targetFrameId: string | null
|
|
73
|
+
} | null>(null)
|
|
74
|
+
|
|
75
|
+
// Task-source integration modals, keyed by source. `taskConnect` carries the
|
|
76
|
+
// source whose connect form to show; `taskImport`'s source may be null to let
|
|
77
|
+
// the modal pick a connected one (there is no spawn target — issues are linked
|
|
78
|
+
// to a block for context, not expanded into structure).
|
|
79
|
+
const taskConnect = ref<{ source: TaskSourceKind } | null>(null)
|
|
80
|
+
// `containerId` (a service frame) scopes the modal: it preselects that frame as
|
|
81
|
+
// the create-in target AND scopes the issue search to the frame's linked repo.
|
|
82
|
+
// Null → the unscoped "import an issue" surface (workspace-wide search).
|
|
83
|
+
const taskImport = ref<{ source: TaskSourceKind | null; containerId: string | null } | null>(null)
|
|
84
|
+
|
|
85
|
+
// Add-task modal: the container (service frame or module) a new task is being
|
|
86
|
+
// added to, or null when closed. The user types the title + description; nothing
|
|
87
|
+
// is launched until they explicitly start the created task.
|
|
88
|
+
const addTaskContainerId = ref<string | null>(null)
|
|
89
|
+
// Optional values to seed the add-task form with when it is opened from another
|
|
90
|
+
// surface (e.g. "create task from issue" prefills the title + stages the issue as
|
|
91
|
+
// linked context). The user still confirms pipeline / preset before adding.
|
|
92
|
+
const addTaskPrefill = ref<AddTaskPrefill | null>(null)
|
|
93
|
+
|
|
94
|
+
// Add-recurring-pipeline modal: the service frame a new recurring pipeline is
|
|
95
|
+
// being added to, or null when closed (mirrors the add-task flow — a button on
|
|
96
|
+
// the frame opens it, scoped to that frame).
|
|
97
|
+
const addRecurringFrameId = ref<string | null>(null)
|
|
98
|
+
|
|
99
|
+
// Create-initiative modal: the service frame a new initiative is being created
|
|
100
|
+
// under, or null when closed (mirrors the add-task flow).
|
|
101
|
+
const createInitiativeFrameId = ref<string | null>(null)
|
|
102
|
+
|
|
103
|
+
// Repo-bootstrap modal (manage reference architectures + launch a bootstrap).
|
|
104
|
+
const bootstrapOpen = ref(false)
|
|
105
|
+
|
|
106
|
+
// "Add a service from an existing GitHub repo" modal (no bootstrap run).
|
|
107
|
+
const addServiceOpen = ref(false)
|
|
108
|
+
|
|
109
|
+
// GitHub integration panel (connection management + repo/PR/issue browsing).
|
|
110
|
+
const githubOpen = ref(false)
|
|
111
|
+
|
|
112
|
+
// Slack integration panel (connect the account's Slack + per-workspace routing).
|
|
113
|
+
const slackOpen = ref(false)
|
|
114
|
+
|
|
115
|
+
// Prompt-fragment library panel (manage the board's best-practice catalog +
|
|
116
|
+
// linked guideline repos; ADR 0006).
|
|
117
|
+
const fragmentLibraryOpen = ref(false)
|
|
118
|
+
|
|
119
|
+
// Command bar (⌘K) — searchable launcher for every navbar action.
|
|
120
|
+
const commandBarOpen = ref(false)
|
|
121
|
+
|
|
122
|
+
// Keyboard-shortcuts cheatsheet (?) — a modal listing every global shortcut.
|
|
123
|
+
const shortcutsHelpOpen = ref(false)
|
|
124
|
+
|
|
125
|
+
// Mobile navigation drawer: on compact (< lg) viewports the SideBar is an
|
|
126
|
+
// off-canvas drawer toggled by a hamburger; on lg+ it is a static aside and this
|
|
127
|
+
// flag is ignored. Closed on any nav action so the board is revealed immediately.
|
|
128
|
+
const mobileNavOpen = ref(false)
|
|
129
|
+
|
|
130
|
+
// Integrations hub: a single modal listing every external system the workspace
|
|
131
|
+
// can enable/link (GitHub, Slack, document + task sources, Datadog, LLM vendors,
|
|
132
|
+
// local runners, OpenRouter). Replaces the per-integration navbar buttons; each
|
|
133
|
+
// row inside it opens that integration's own panel via the handlers below.
|
|
134
|
+
const integrationsOpen = ref(false)
|
|
135
|
+
// True while an integration's own panel is showing AND it was reached from the hub
|
|
136
|
+
// (not the command bar, sidebar, a banner or an inspector link). Drives the "Back to
|
|
137
|
+
// Integrations" control those panels render: it only offers a return path when there
|
|
138
|
+
// is one. Every direct `open*` below resets it; `openFromIntegrations` sets it.
|
|
139
|
+
const cameFromIntegrations = ref(false)
|
|
140
|
+
|
|
141
|
+
// Personal "My setup" hub: a user-scoped sibling of the Integrations hub, listing the
|
|
142
|
+
// signed-in user's own connections (GitHub PAT, local runners, personal subscriptions)
|
|
143
|
+
// separated out of the workspace-scoped Integrations hub. `cameFromPersonal` is the
|
|
144
|
+
// symmetric came-from marker, so a panel reached from here renders a "Back to My setup"
|
|
145
|
+
// control instead of "Back to Integrations".
|
|
146
|
+
const personalSetupOpen = ref(false)
|
|
147
|
+
const cameFromPersonal = ref(false)
|
|
148
|
+
|
|
149
|
+
// Workspace-settings modal: a single tabbed window gathering the workspace-wide
|
|
150
|
+
// config (workspace / merge thresholds / issue writeback / service best practices).
|
|
151
|
+
// `workspaceSettingsTab` lets other surfaces deep-link straight to a tab.
|
|
152
|
+
const workspaceSettingsOpen = ref(false)
|
|
153
|
+
const workspaceSettingsTab = ref('workspace')
|
|
154
|
+
// Account-settings modal: a single tabbed window for the per-account configuration —
|
|
155
|
+
// the team panel (members + roles + invitations + email sender + account API keys,
|
|
156
|
+
// `AccountTeamSettings`) and the account-tier prompt-fragment library. Account-scoped
|
|
157
|
+
// (distinct from workspace settings). `accountSettingsTab` lets other surfaces deep-link
|
|
158
|
+
// straight to a tab.
|
|
159
|
+
const accountSettingsOpen = ref(false)
|
|
160
|
+
const accountSettingsTab = ref('team')
|
|
161
|
+
// A one-shot deep-link anchor: when a surface opens account settings AND wants to land on a
|
|
162
|
+
// specific section within the (long) tab body, it sets this to that section's id. The owning
|
|
163
|
+
// panel scrolls the matching element into view once, then calls `clearAccountSettingsScrollTarget`
|
|
164
|
+
// so a later plain open doesn't re-scroll. Null when no section was requested.
|
|
165
|
+
const accountSettingsScrollTarget = ref<string | null>(null)
|
|
166
|
+
// Observability integration: the post-release-health connection panel (Datadog
|
|
167
|
+
// today, pluggable). NB: distinct from `observabilityInstanceId`, which is the
|
|
168
|
+
// LLM per-call observability panel (see the result-views slice).
|
|
169
|
+
const observabilityConnectionOpen = ref(false)
|
|
170
|
+
// Private package registries: the workspace's npm/GitHub-Packages entries agent
|
|
171
|
+
// containers install with. Opened from the Integrations hub.
|
|
172
|
+
const packageRegistriesOpen = ref(false)
|
|
173
|
+
// API access tokens: the workspace's inbound public-API keys external systems present to
|
|
174
|
+
// the `/api/v1` surface. Opened from the Integrations hub.
|
|
175
|
+
const apiTokensOpen = ref(false)
|
|
176
|
+
// The single tabbed Infrastructure window — a TOP-LEVEL navbar destination (no longer
|
|
177
|
+
// reached via the Integrations hub). Two topical tabs: "Agent containers" (the execution
|
|
178
|
+
// backend + self-hosted runner pool, plus the local-mode warm pool/checkout) and "Test
|
|
179
|
+
// environments" (the ephemeral-environment provider). `infrastructureOpen` is the modal
|
|
180
|
+
// flag; `infrastructureTab` selects the tab. `openInfrastructure()` is the navbar entry;
|
|
181
|
+
// `openProviderConnection(kind)` remains for deep-links (a banner's "Configure…" button).
|
|
182
|
+
const infrastructureOpen = ref(false)
|
|
183
|
+
const infrastructureTab = ref<'environment' | 'runner-pool'>('runner-pool')
|
|
184
|
+
// Non-secret prefill captured from the `cat-factory k3s` CLI deep-link (see
|
|
185
|
+
// `consumeK3sSetupDeepLink`). When set, the Test-environments tab's kube engine form seeds the
|
|
186
|
+
// `local-k3s` connection from it; the ServiceAccount token is deliberately NOT in the link (a
|
|
187
|
+
// secret in a URL leaks into history/logs), so the user still pastes it before Test → Save.
|
|
188
|
+
const k3sSetupPrefill = ref<K3sSetupPrefill | null>(null)
|
|
189
|
+
// Environment setup wizard (shared-stacks slice 7): the guided detect → review → preflight →
|
|
190
|
+
// trial → save flow for a service frame's `docker-compose` provisioning. `environmentWizardOpen`
|
|
191
|
+
// is the modal flag; `environmentWizardFrameId` preselects the service frame the flow targets
|
|
192
|
+
// (set when launched from a frame's inspector nudge; null ⇒ the wizard's pick step chooses one).
|
|
193
|
+
const environmentWizardOpen = ref(false)
|
|
194
|
+
const environmentWizardFrameId = ref<string | null>(null)
|
|
195
|
+
const modelConfigOpen = ref(false)
|
|
196
|
+
// LLM-vendor subscription credentials (the token pool powering the Claude Code
|
|
197
|
+
// / Codex harnesses). `vendorCredentialsTab` lets a caller deep-link to one tab —
|
|
198
|
+
// the user-scoped "My subscriptions" entry opens straight onto the `personal` tab.
|
|
199
|
+
const vendorCredentialsOpen = ref(false)
|
|
200
|
+
const vendorCredentialsTab = ref('pool')
|
|
201
|
+
// Per-user settings panel: the signed-in user's own-machine local model runners.
|
|
202
|
+
const localModelsOpen = ref(false)
|
|
203
|
+
// The Sandbox (parallel prompt/model testing) surface — an opt-in, on-demand window.
|
|
204
|
+
const sandboxOpen = ref(false)
|
|
205
|
+
const userSecretsOpen = ref(false)
|
|
206
|
+
// Per-workspace settings panel: the OpenRouter dynamic catalog (browse/enable gateway models).
|
|
207
|
+
const openRouterOpen = ref(false)
|
|
208
|
+
|
|
209
|
+
// AI-onboarding surfaces (driven by `useAiReadiness`). `aiProviderSetupOpen` is the
|
|
210
|
+
// "no usable AI source" dialog; `aiPresetMismatchOpen` is the "default preset points at
|
|
211
|
+
// unavailable models" dialog. The `*Dismissed` flags are per-session: they suppress the
|
|
212
|
+
// auto-open (and let the banner be dismissed) without permanently hiding the prompt — it
|
|
213
|
+
// re-evaluates on the next load. Both clear themselves once the underlying gap is closed.
|
|
214
|
+
const aiProviderSetupOpen = ref(false)
|
|
215
|
+
const aiPresetMismatchOpen = ref(false)
|
|
216
|
+
const aiSetupDismissed = ref(false)
|
|
217
|
+
const aiPresetDismissed = ref(false)
|
|
218
|
+
|
|
219
|
+
// Infra-setup banner: per-SESSION dismissals, one flag per area, cleared on workspace switch
|
|
220
|
+
// exactly like the AI-onboarding flags (a dismissal in one workspace must not suppress the
|
|
221
|
+
// independent prompt for another). The PERMANENT "don't notify me again" dismissal is per-USER
|
|
222
|
+
// and persists in localStorage from the banner component; this only covers "hide for now".
|
|
223
|
+
const infraSetupSessionDismissed = ref<InfraSetupArea[]>([])
|
|
224
|
+
function dismissInfraSetupForSession(area: InfraSetupArea) {
|
|
225
|
+
if (!infraSetupSessionDismissed.value.includes(area))
|
|
226
|
+
infraSetupSessionDismissed.value = [...infraSetupSessionDismissed.value, area]
|
|
227
|
+
}
|
|
228
|
+
function resetInfraSetupDismissals() {
|
|
229
|
+
infraSetupSessionDismissed.value = []
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function openBuilder() {
|
|
233
|
+
builderOpen.value = true
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Auto-open the pipeline-health advisory once per session (no-op after it's been shown). */
|
|
237
|
+
function maybeOpenPipelineHealth() {
|
|
238
|
+
if (pipelineHealthSeen.value) return
|
|
239
|
+
pipelineHealthSeen.value = true
|
|
240
|
+
pipelineHealthOpen.value = true
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function openPipelineHealth() {
|
|
244
|
+
pipelineHealthSeen.value = true
|
|
245
|
+
pipelineHealthOpen.value = true
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function closePipelineHealth() {
|
|
249
|
+
pipelineHealthOpen.value = false
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** Auto-open the merge-preset health advisory once per session (no-op after it's been shown). */
|
|
253
|
+
function maybeOpenRiskPolicyHealth() {
|
|
254
|
+
if (riskPolicyHealthSeen.value) return
|
|
255
|
+
riskPolicyHealthSeen.value = true
|
|
256
|
+
riskPolicyHealthOpen.value = true
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function openRiskPolicyHealth() {
|
|
260
|
+
riskPolicyHealthSeen.value = true
|
|
261
|
+
riskPolicyHealthOpen.value = true
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function closeRiskPolicyHealth() {
|
|
265
|
+
riskPolicyHealthOpen.value = false
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** Auto-open the model-preset health advisory once per session (no-op after it's been shown). */
|
|
269
|
+
function maybeOpenModelPresetHealth() {
|
|
270
|
+
if (modelPresetHealthSeen.value) return
|
|
271
|
+
modelPresetHealthSeen.value = true
|
|
272
|
+
modelPresetHealthOpen.value = true
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function openModelPresetHealth() {
|
|
276
|
+
modelPresetHealthSeen.value = true
|
|
277
|
+
modelPresetHealthOpen.value = true
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function closeModelPresetHealth() {
|
|
281
|
+
modelPresetHealthOpen.value = false
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function openDecision(instanceId: string, decisionId: string) {
|
|
285
|
+
decisionContext.value = { instanceId, decisionId }
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function closeDecision() {
|
|
289
|
+
decisionContext.value = null
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function openDocumentConnect(source: DocumentSourceKind) {
|
|
293
|
+
resetHubReturn()
|
|
294
|
+
documentConnect.value = { source }
|
|
295
|
+
}
|
|
296
|
+
function closeDocumentConnect() {
|
|
297
|
+
documentConnect.value = null
|
|
298
|
+
}
|
|
299
|
+
function openDocumentImport(
|
|
300
|
+
targetFrameId: string | null = null,
|
|
301
|
+
source: DocumentSourceKind | null = null,
|
|
302
|
+
) {
|
|
303
|
+
resetHubReturn()
|
|
304
|
+
documentImport.value = { source, targetFrameId }
|
|
305
|
+
}
|
|
306
|
+
function closeDocumentImport() {
|
|
307
|
+
documentImport.value = null
|
|
308
|
+
}
|
|
309
|
+
function openDocumentTemplates() {
|
|
310
|
+
resetHubReturn()
|
|
311
|
+
documentTemplates.value = true
|
|
312
|
+
}
|
|
313
|
+
function closeDocumentTemplates() {
|
|
314
|
+
documentTemplates.value = false
|
|
315
|
+
}
|
|
316
|
+
function openSpawnPreview(
|
|
317
|
+
source: DocumentSourceKind,
|
|
318
|
+
externalId: string,
|
|
319
|
+
targetFrameId: string | null = null,
|
|
320
|
+
) {
|
|
321
|
+
spawnPreview.value = { source, externalId, targetFrameId }
|
|
322
|
+
}
|
|
323
|
+
function closeSpawnPreview() {
|
|
324
|
+
spawnPreview.value = null
|
|
325
|
+
}
|
|
326
|
+
function openTaskConnect(source: TaskSourceKind) {
|
|
327
|
+
resetHubReturn()
|
|
328
|
+
taskConnect.value = { source }
|
|
329
|
+
}
|
|
330
|
+
function closeTaskConnect() {
|
|
331
|
+
taskConnect.value = null
|
|
332
|
+
}
|
|
333
|
+
function openTaskImport(source: TaskSourceKind | null = null, containerId: string | null = null) {
|
|
334
|
+
resetHubReturn()
|
|
335
|
+
taskImport.value = { source, containerId }
|
|
336
|
+
}
|
|
337
|
+
function closeTaskImport() {
|
|
338
|
+
taskImport.value = null
|
|
339
|
+
}
|
|
340
|
+
function openAddTask(containerId: string, prefill: AddTaskPrefill | null = null) {
|
|
341
|
+
addTaskPrefill.value = prefill
|
|
342
|
+
addTaskContainerId.value = containerId
|
|
343
|
+
}
|
|
344
|
+
function closeAddTask() {
|
|
345
|
+
addTaskContainerId.value = null
|
|
346
|
+
addTaskPrefill.value = null
|
|
347
|
+
}
|
|
348
|
+
function openAddRecurring(frameId: string) {
|
|
349
|
+
addRecurringFrameId.value = frameId
|
|
350
|
+
}
|
|
351
|
+
function closeAddRecurring() {
|
|
352
|
+
addRecurringFrameId.value = null
|
|
353
|
+
}
|
|
354
|
+
function openCreateInitiative(frameId: string) {
|
|
355
|
+
createInitiativeFrameId.value = frameId
|
|
356
|
+
}
|
|
357
|
+
function closeCreateInitiative() {
|
|
358
|
+
createInitiativeFrameId.value = null
|
|
359
|
+
}
|
|
360
|
+
function openBootstrap() {
|
|
361
|
+
bootstrapOpen.value = true
|
|
362
|
+
}
|
|
363
|
+
function closeBootstrap() {
|
|
364
|
+
bootstrapOpen.value = false
|
|
365
|
+
}
|
|
366
|
+
function openAddService() {
|
|
367
|
+
addServiceOpen.value = true
|
|
368
|
+
}
|
|
369
|
+
function closeAddService() {
|
|
370
|
+
addServiceOpen.value = false
|
|
371
|
+
}
|
|
372
|
+
function openGitHub() {
|
|
373
|
+
resetHubReturn()
|
|
374
|
+
githubOpen.value = true
|
|
375
|
+
}
|
|
376
|
+
function closeGitHub() {
|
|
377
|
+
githubOpen.value = false
|
|
378
|
+
}
|
|
379
|
+
function openSlack() {
|
|
380
|
+
resetHubReturn()
|
|
381
|
+
slackOpen.value = true
|
|
382
|
+
}
|
|
383
|
+
function closeSlack() {
|
|
384
|
+
slackOpen.value = false
|
|
385
|
+
}
|
|
386
|
+
function openFragmentLibrary() {
|
|
387
|
+
fragmentLibraryOpen.value = true
|
|
388
|
+
}
|
|
389
|
+
function closeFragmentLibrary() {
|
|
390
|
+
fragmentLibraryOpen.value = false
|
|
391
|
+
}
|
|
392
|
+
function openCommandBar() {
|
|
393
|
+
commandBarOpen.value = true
|
|
394
|
+
}
|
|
395
|
+
function closeCommandBar() {
|
|
396
|
+
commandBarOpen.value = false
|
|
397
|
+
}
|
|
398
|
+
function toggleCommandBar() {
|
|
399
|
+
commandBarOpen.value = !commandBarOpen.value
|
|
400
|
+
}
|
|
401
|
+
function openShortcutsHelp() {
|
|
402
|
+
shortcutsHelpOpen.value = true
|
|
403
|
+
}
|
|
404
|
+
function closeShortcutsHelp() {
|
|
405
|
+
shortcutsHelpOpen.value = false
|
|
406
|
+
}
|
|
407
|
+
function toggleShortcutsHelp() {
|
|
408
|
+
shortcutsHelpOpen.value = !shortcutsHelpOpen.value
|
|
409
|
+
}
|
|
410
|
+
function openMobileNav() {
|
|
411
|
+
mobileNavOpen.value = true
|
|
412
|
+
}
|
|
413
|
+
function closeMobileNav() {
|
|
414
|
+
mobileNavOpen.value = false
|
|
415
|
+
}
|
|
416
|
+
function toggleMobileNav() {
|
|
417
|
+
mobileNavOpen.value = !mobileNavOpen.value
|
|
418
|
+
}
|
|
419
|
+
// Clear BOTH hub came-from markers. Every direct `open*` below calls this so that a
|
|
420
|
+
// panel opened outside the hubs never grows a dead Back control, and so switching from
|
|
421
|
+
// one hub's panel to the other's clears the stale marker.
|
|
422
|
+
function resetHubReturn() {
|
|
423
|
+
cameFromIntegrations.value = false
|
|
424
|
+
cameFromPersonal.value = false
|
|
425
|
+
}
|
|
426
|
+
function openIntegrations() {
|
|
427
|
+
// Reaching the hub itself (fresh, or via a panel's Back control) clears the
|
|
428
|
+
// came-from markers — we're at the hub, not inside a hub-spawned panel.
|
|
429
|
+
resetHubReturn()
|
|
430
|
+
integrationsOpen.value = true
|
|
431
|
+
}
|
|
432
|
+
function closeIntegrations() {
|
|
433
|
+
integrationsOpen.value = false
|
|
434
|
+
}
|
|
435
|
+
function openPersonalSetup() {
|
|
436
|
+
resetHubReturn()
|
|
437
|
+
personalSetupOpen.value = true
|
|
438
|
+
}
|
|
439
|
+
function closePersonalSetup() {
|
|
440
|
+
personalSetupOpen.value = false
|
|
441
|
+
}
|
|
442
|
+
// Open a user-scoped panel FROM the My-setup hub: run its open handler (which resets the
|
|
443
|
+
// markers), then mark that we came from My setup and dismiss it, so the panel's
|
|
444
|
+
// IntegrationBackTitle returns here rather than to the workspace Integrations hub.
|
|
445
|
+
function openFromPersonal(open: () => void) {
|
|
446
|
+
open()
|
|
447
|
+
cameFromPersonal.value = true
|
|
448
|
+
personalSetupOpen.value = false
|
|
449
|
+
}
|
|
450
|
+
// Open an integration's own panel FROM the hub: run its open handler (which resets
|
|
451
|
+
// `cameFromIntegrations`), then mark that we came from the hub and dismiss it. The
|
|
452
|
+
// panel reads `cameFromIntegrations` to show its Back control.
|
|
453
|
+
function openFromIntegrations(open: () => void) {
|
|
454
|
+
open()
|
|
455
|
+
cameFromIntegrations.value = true
|
|
456
|
+
integrationsOpen.value = false
|
|
457
|
+
}
|
|
458
|
+
function openWorkspaceSettings(tab = 'workspace') {
|
|
459
|
+
resetHubReturn()
|
|
460
|
+
workspaceSettingsTab.value = tab
|
|
461
|
+
workspaceSettingsOpen.value = true
|
|
462
|
+
}
|
|
463
|
+
function closeWorkspaceSettings() {
|
|
464
|
+
workspaceSettingsOpen.value = false
|
|
465
|
+
}
|
|
466
|
+
function setWorkspaceSettingsTab(tab: string) {
|
|
467
|
+
workspaceSettingsTab.value = tab
|
|
468
|
+
}
|
|
469
|
+
function openAccountSettings(tab = 'team') {
|
|
470
|
+
resetHubReturn()
|
|
471
|
+
accountSettingsTab.value = tab
|
|
472
|
+
accountSettingsOpen.value = true
|
|
473
|
+
}
|
|
474
|
+
// Deep-link to the content (binary-artifact) storage configuration, which lives near the
|
|
475
|
+
// bottom of the account settings' team tab (`AccountDeploymentSettings`). Used by the
|
|
476
|
+
// pipeline-start error prompt when a storage-reliant agent (the UI Tester) has no storage
|
|
477
|
+
// configured. Sets a scroll anchor so the panel brings the storage section into view rather
|
|
478
|
+
// than dropping the user at the top of the long team tab to hunt for it.
|
|
479
|
+
function openContentStorageSettings() {
|
|
480
|
+
accountSettingsScrollTarget.value = 'content-storage'
|
|
481
|
+
openAccountSettings('team')
|
|
482
|
+
}
|
|
483
|
+
function clearAccountSettingsScrollTarget() {
|
|
484
|
+
accountSettingsScrollTarget.value = null
|
|
485
|
+
}
|
|
486
|
+
function closeAccountSettings() {
|
|
487
|
+
accountSettingsOpen.value = false
|
|
488
|
+
accountSettingsScrollTarget.value = null
|
|
489
|
+
}
|
|
490
|
+
function setAccountSettingsTab(tab: string) {
|
|
491
|
+
accountSettingsTab.value = tab
|
|
492
|
+
}
|
|
493
|
+
function openObservabilityConnection() {
|
|
494
|
+
resetHubReturn()
|
|
495
|
+
observabilityConnectionOpen.value = true
|
|
496
|
+
}
|
|
497
|
+
function closeObservabilityConnection() {
|
|
498
|
+
observabilityConnectionOpen.value = false
|
|
499
|
+
}
|
|
500
|
+
function openPackageRegistries() {
|
|
501
|
+
resetHubReturn()
|
|
502
|
+
packageRegistriesOpen.value = true
|
|
503
|
+
}
|
|
504
|
+
function closePackageRegistries() {
|
|
505
|
+
packageRegistriesOpen.value = false
|
|
506
|
+
}
|
|
507
|
+
function openApiTokens() {
|
|
508
|
+
resetHubReturn()
|
|
509
|
+
apiTokensOpen.value = true
|
|
510
|
+
}
|
|
511
|
+
function closeApiTokens() {
|
|
512
|
+
apiTokensOpen.value = false
|
|
513
|
+
}
|
|
514
|
+
// Top-level navbar entry into the Infrastructure window. No hub-return marker (it isn't
|
|
515
|
+
// reached from the Integrations hub), so the window shows no "Back to Integrations" control.
|
|
516
|
+
function openInfrastructure(tab: 'environment' | 'runner-pool' = 'runner-pool') {
|
|
517
|
+
resetHubReturn()
|
|
518
|
+
infrastructureTab.value = tab
|
|
519
|
+
infrastructureOpen.value = true
|
|
520
|
+
}
|
|
521
|
+
function openProviderConnection(kind: 'environment' | 'runner-pool') {
|
|
522
|
+
resetHubReturn()
|
|
523
|
+
infrastructureTab.value = kind
|
|
524
|
+
infrastructureOpen.value = true
|
|
525
|
+
}
|
|
526
|
+
function closeProviderConnection() {
|
|
527
|
+
infrastructureOpen.value = false
|
|
528
|
+
// Drop any consumed CLI prefill so re-opening the window normally doesn't re-seed the form.
|
|
529
|
+
k3sSetupPrefill.value = null
|
|
530
|
+
}
|
|
531
|
+
// Capture a `cat-factory k3s` deep-link (`?infraSetup=local-k3s&…`) on app load: stash the
|
|
532
|
+
// non-secret connection values, open the Infrastructure window on the Test-environments tab so
|
|
533
|
+
// the kube engine form seeds from them, then strip the params from the URL (mirrors the
|
|
534
|
+
// `?invite=` handling in the auth store) so a reload doesn't re-trigger and the link isn't left
|
|
535
|
+
// in history. No-op when the query param is absent.
|
|
536
|
+
function consumeK3sSetupDeepLink() {
|
|
537
|
+
if (typeof window === 'undefined') return
|
|
538
|
+
const params = new URLSearchParams(window.location.search)
|
|
539
|
+
if (params.get('infraSetup') !== 'local-k3s') return
|
|
540
|
+
k3sSetupPrefill.value = {
|
|
541
|
+
label: params.get('label') ?? 'Local k3s',
|
|
542
|
+
apiServerUrl: params.get('apiServerUrl') ?? '',
|
|
543
|
+
namespaceTemplate: params.get('namespaceTemplate') ?? '',
|
|
544
|
+
hostTemplate: params.get('hostTemplate') ?? '',
|
|
545
|
+
// Only carry the flag the link actually set — a missing param leaves the form's engine
|
|
546
|
+
// default (skip-TLS on for a local self-signed cluster) untouched.
|
|
547
|
+
insecureSkipTlsVerify: params.has('insecureSkipTlsVerify')
|
|
548
|
+
? params.get('insecureSkipTlsVerify') === '1'
|
|
549
|
+
: undefined,
|
|
550
|
+
}
|
|
551
|
+
resetHubReturn()
|
|
552
|
+
infrastructureTab.value = 'environment'
|
|
553
|
+
infrastructureOpen.value = true
|
|
554
|
+
for (const key of [
|
|
555
|
+
'infraSetup',
|
|
556
|
+
'label',
|
|
557
|
+
'apiServerUrl',
|
|
558
|
+
'namespaceTemplate',
|
|
559
|
+
'hostTemplate',
|
|
560
|
+
'insecureSkipTlsVerify',
|
|
561
|
+
]) {
|
|
562
|
+
params.delete(key)
|
|
563
|
+
}
|
|
564
|
+
const qs = params.toString()
|
|
565
|
+
history.replaceState(null, '', window.location.pathname + (qs ? `?${qs}` : ''))
|
|
566
|
+
}
|
|
567
|
+
// Launch the environment setup wizard, optionally preselecting the service frame it targets
|
|
568
|
+
// (the inspector nudge passes the frame; the navbar entry opens it with the pick step active).
|
|
569
|
+
function openEnvironmentSetup(frameId: string | null = null) {
|
|
570
|
+
resetHubReturn()
|
|
571
|
+
environmentWizardFrameId.value = frameId
|
|
572
|
+
environmentWizardOpen.value = true
|
|
573
|
+
}
|
|
574
|
+
function closeEnvironmentSetup() {
|
|
575
|
+
environmentWizardOpen.value = false
|
|
576
|
+
environmentWizardFrameId.value = null
|
|
577
|
+
}
|
|
578
|
+
function openModelConfig() {
|
|
579
|
+
modelConfigOpen.value = true
|
|
580
|
+
}
|
|
581
|
+
function closeModelConfig() {
|
|
582
|
+
modelConfigOpen.value = false
|
|
583
|
+
}
|
|
584
|
+
function openVendorCredentials(tab = 'pool') {
|
|
585
|
+
resetHubReturn()
|
|
586
|
+
vendorCredentialsTab.value = tab
|
|
587
|
+
vendorCredentialsOpen.value = true
|
|
588
|
+
}
|
|
589
|
+
function setVendorCredentialsTab(tab: string) {
|
|
590
|
+
vendorCredentialsTab.value = tab
|
|
591
|
+
}
|
|
592
|
+
function closeVendorCredentials() {
|
|
593
|
+
vendorCredentialsOpen.value = false
|
|
594
|
+
}
|
|
595
|
+
function openLocalModels() {
|
|
596
|
+
resetHubReturn()
|
|
597
|
+
localModelsOpen.value = true
|
|
598
|
+
}
|
|
599
|
+
function closeLocalModels() {
|
|
600
|
+
localModelsOpen.value = false
|
|
601
|
+
}
|
|
602
|
+
function openSandbox() {
|
|
603
|
+
sandboxOpen.value = true
|
|
604
|
+
}
|
|
605
|
+
function closeSandbox() {
|
|
606
|
+
sandboxOpen.value = false
|
|
607
|
+
}
|
|
608
|
+
function openUserSecrets() {
|
|
609
|
+
resetHubReturn()
|
|
610
|
+
userSecretsOpen.value = true
|
|
611
|
+
}
|
|
612
|
+
function closeUserSecrets() {
|
|
613
|
+
userSecretsOpen.value = false
|
|
614
|
+
}
|
|
615
|
+
function openOpenRouter() {
|
|
616
|
+
resetHubReturn()
|
|
617
|
+
openRouterOpen.value = true
|
|
618
|
+
}
|
|
619
|
+
function closeOpenRouter() {
|
|
620
|
+
openRouterOpen.value = false
|
|
621
|
+
}
|
|
622
|
+
function openAiProviderSetup() {
|
|
623
|
+
aiProviderSetupOpen.value = true
|
|
624
|
+
}
|
|
625
|
+
function closeAiProviderSetup() {
|
|
626
|
+
aiProviderSetupOpen.value = false
|
|
627
|
+
}
|
|
628
|
+
function openAiPresetMismatch() {
|
|
629
|
+
aiPresetMismatchOpen.value = true
|
|
630
|
+
}
|
|
631
|
+
function closeAiPresetMismatch() {
|
|
632
|
+
aiPresetMismatchOpen.value = false
|
|
633
|
+
}
|
|
634
|
+
// Banner dismissal is distinct from closing the dialog: closing the dialog leaves the
|
|
635
|
+
// banner so the user can reopen it; dismissing the banner hides the whole prompt for
|
|
636
|
+
// the session (it re-evaluates on the next load).
|
|
637
|
+
function dismissAiSetup() {
|
|
638
|
+
aiProviderSetupOpen.value = false
|
|
639
|
+
aiSetupDismissed.value = true
|
|
640
|
+
}
|
|
641
|
+
function dismissAiPresetMismatch() {
|
|
642
|
+
aiPresetMismatchOpen.value = false
|
|
643
|
+
aiPresetDismissed.value = true
|
|
644
|
+
}
|
|
645
|
+
// Clear the per-session AI-onboarding state (open dialogs + dismissed flags). Called on
|
|
646
|
+
// workspace switch: dismissals are per-session-per-workspace, so a prompt dismissed in one
|
|
647
|
+
// workspace must not suppress the (independent) prompt for another workspace that also
|
|
648
|
+
// lacks a usable AI source / has a broken default preset.
|
|
649
|
+
function resetAiOnboarding() {
|
|
650
|
+
aiProviderSetupOpen.value = false
|
|
651
|
+
aiPresetMismatchOpen.value = false
|
|
652
|
+
aiSetupDismissed.value = false
|
|
653
|
+
aiPresetDismissed.value = false
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
return {
|
|
657
|
+
builderOpen,
|
|
658
|
+
pipelineHealthOpen,
|
|
659
|
+
pipelineHealthSeen,
|
|
660
|
+
riskPolicyHealthOpen,
|
|
661
|
+
riskPolicyHealthSeen,
|
|
662
|
+
modelPresetHealthOpen,
|
|
663
|
+
modelPresetHealthSeen,
|
|
664
|
+
decisionContext,
|
|
665
|
+
documentConnect,
|
|
666
|
+
documentImport,
|
|
667
|
+
documentTemplates,
|
|
668
|
+
spawnPreview,
|
|
669
|
+
taskConnect,
|
|
670
|
+
taskImport,
|
|
671
|
+
addTaskContainerId,
|
|
672
|
+
addTaskPrefill,
|
|
673
|
+
addRecurringFrameId,
|
|
674
|
+
createInitiativeFrameId,
|
|
675
|
+
bootstrapOpen,
|
|
676
|
+
addServiceOpen,
|
|
677
|
+
githubOpen,
|
|
678
|
+
slackOpen,
|
|
679
|
+
fragmentLibraryOpen,
|
|
680
|
+
commandBarOpen,
|
|
681
|
+
shortcutsHelpOpen,
|
|
682
|
+
mobileNavOpen,
|
|
683
|
+
integrationsOpen,
|
|
684
|
+
cameFromIntegrations,
|
|
685
|
+
personalSetupOpen,
|
|
686
|
+
cameFromPersonal,
|
|
687
|
+
workspaceSettingsOpen,
|
|
688
|
+
workspaceSettingsTab,
|
|
689
|
+
accountSettingsOpen,
|
|
690
|
+
accountSettingsTab,
|
|
691
|
+
accountSettingsScrollTarget,
|
|
692
|
+
observabilityConnectionOpen,
|
|
693
|
+
packageRegistriesOpen,
|
|
694
|
+
apiTokensOpen,
|
|
695
|
+
infrastructureOpen,
|
|
696
|
+
infrastructureTab,
|
|
697
|
+
openInfrastructure,
|
|
698
|
+
modelConfigOpen,
|
|
699
|
+
vendorCredentialsOpen,
|
|
700
|
+
vendorCredentialsTab,
|
|
701
|
+
localModelsOpen,
|
|
702
|
+
sandboxOpen,
|
|
703
|
+
userSecretsOpen,
|
|
704
|
+
openRouterOpen,
|
|
705
|
+
aiProviderSetupOpen,
|
|
706
|
+
aiPresetMismatchOpen,
|
|
707
|
+
aiSetupDismissed,
|
|
708
|
+
aiPresetDismissed,
|
|
709
|
+
infraSetupSessionDismissed,
|
|
710
|
+
dismissInfraSetupForSession,
|
|
711
|
+
resetInfraSetupDismissals,
|
|
712
|
+
k3sSetupPrefill,
|
|
713
|
+
consumeK3sSetupDeepLink,
|
|
714
|
+
environmentWizardOpen,
|
|
715
|
+
environmentWizardFrameId,
|
|
716
|
+
openBuilder,
|
|
717
|
+
maybeOpenPipelineHealth,
|
|
718
|
+
openPipelineHealth,
|
|
719
|
+
closePipelineHealth,
|
|
720
|
+
maybeOpenRiskPolicyHealth,
|
|
721
|
+
openRiskPolicyHealth,
|
|
722
|
+
closeRiskPolicyHealth,
|
|
723
|
+
maybeOpenModelPresetHealth,
|
|
724
|
+
openModelPresetHealth,
|
|
725
|
+
closeModelPresetHealth,
|
|
726
|
+
openDecision,
|
|
727
|
+
closeDecision,
|
|
728
|
+
openDocumentConnect,
|
|
729
|
+
closeDocumentConnect,
|
|
730
|
+
openDocumentImport,
|
|
731
|
+
closeDocumentImport,
|
|
732
|
+
openDocumentTemplates,
|
|
733
|
+
closeDocumentTemplates,
|
|
734
|
+
openSpawnPreview,
|
|
735
|
+
closeSpawnPreview,
|
|
736
|
+
openTaskConnect,
|
|
737
|
+
closeTaskConnect,
|
|
738
|
+
openTaskImport,
|
|
739
|
+
closeTaskImport,
|
|
740
|
+
openAddTask,
|
|
741
|
+
closeAddTask,
|
|
742
|
+
openAddRecurring,
|
|
743
|
+
closeAddRecurring,
|
|
744
|
+
openCreateInitiative,
|
|
745
|
+
closeCreateInitiative,
|
|
746
|
+
openBootstrap,
|
|
747
|
+
closeBootstrap,
|
|
748
|
+
openAddService,
|
|
749
|
+
closeAddService,
|
|
750
|
+
openGitHub,
|
|
751
|
+
closeGitHub,
|
|
752
|
+
openSlack,
|
|
753
|
+
closeSlack,
|
|
754
|
+
openFragmentLibrary,
|
|
755
|
+
closeFragmentLibrary,
|
|
756
|
+
openCommandBar,
|
|
757
|
+
closeCommandBar,
|
|
758
|
+
toggleCommandBar,
|
|
759
|
+
openShortcutsHelp,
|
|
760
|
+
closeShortcutsHelp,
|
|
761
|
+
toggleShortcutsHelp,
|
|
762
|
+
openMobileNav,
|
|
763
|
+
closeMobileNav,
|
|
764
|
+
toggleMobileNav,
|
|
765
|
+
openIntegrations,
|
|
766
|
+
closeIntegrations,
|
|
767
|
+
openFromIntegrations,
|
|
768
|
+
openPersonalSetup,
|
|
769
|
+
closePersonalSetup,
|
|
770
|
+
openFromPersonal,
|
|
771
|
+
openWorkspaceSettings,
|
|
772
|
+
closeWorkspaceSettings,
|
|
773
|
+
setWorkspaceSettingsTab,
|
|
774
|
+
openAccountSettings,
|
|
775
|
+
openContentStorageSettings,
|
|
776
|
+
clearAccountSettingsScrollTarget,
|
|
777
|
+
closeAccountSettings,
|
|
778
|
+
setAccountSettingsTab,
|
|
779
|
+
openObservabilityConnection,
|
|
780
|
+
closeObservabilityConnection,
|
|
781
|
+
openPackageRegistries,
|
|
782
|
+
closePackageRegistries,
|
|
783
|
+
openApiTokens,
|
|
784
|
+
closeApiTokens,
|
|
785
|
+
openProviderConnection,
|
|
786
|
+
closeProviderConnection,
|
|
787
|
+
openEnvironmentSetup,
|
|
788
|
+
closeEnvironmentSetup,
|
|
789
|
+
openModelConfig,
|
|
790
|
+
closeModelConfig,
|
|
791
|
+
openVendorCredentials,
|
|
792
|
+
setVendorCredentialsTab,
|
|
793
|
+
closeVendorCredentials,
|
|
794
|
+
openLocalModels,
|
|
795
|
+
closeLocalModels,
|
|
796
|
+
openSandbox,
|
|
797
|
+
closeSandbox,
|
|
798
|
+
openUserSecrets,
|
|
799
|
+
closeUserSecrets,
|
|
800
|
+
openOpenRouter,
|
|
801
|
+
closeOpenRouter,
|
|
802
|
+
openAiProviderSetup,
|
|
803
|
+
closeAiProviderSetup,
|
|
804
|
+
openAiPresetMismatch,
|
|
805
|
+
closeAiPresetMismatch,
|
|
806
|
+
dismissAiSetup,
|
|
807
|
+
dismissAiPresetMismatch,
|
|
808
|
+
resetAiOnboarding,
|
|
809
|
+
}
|
|
810
|
+
}
|