@cat-factory/app 0.54.1 → 0.54.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.
|
@@ -52,115 +52,109 @@ function openApprovalFor(approvalId: string) {
|
|
|
52
52
|
</script>
|
|
53
53
|
|
|
54
54
|
<template>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
<UIcon name="
|
|
55
|
+
<!-- The focus-fade Transition is owned by the parent mount (pages/index.vue), so the
|
|
56
|
+
leave animation survives this component being unmounted by its v-if gate. -->
|
|
57
|
+
<div
|
|
58
|
+
v-if="block && statusMeta && typeMeta"
|
|
59
|
+
class="absolute inset-0 z-30 flex flex-col bg-slate-950/95 backdrop-blur"
|
|
60
|
+
>
|
|
61
|
+
<!-- header / breadcrumb -->
|
|
62
|
+
<header class="flex items-center gap-3 border-b border-slate-800 px-6 py-4">
|
|
63
|
+
<UButton icon="i-lucide-arrow-left" color="neutral" variant="ghost" size="sm" @click="close">
|
|
64
|
+
{{ t('focus.board') }}
|
|
65
|
+
</UButton>
|
|
66
|
+
<UIcon name="i-lucide-chevron-right" class="h-4 w-4 text-slate-600" />
|
|
67
|
+
<div
|
|
68
|
+
class="flex h-9 w-9 items-center justify-center rounded-lg"
|
|
69
|
+
:style="{ backgroundColor: typeMeta.accent + '22' }"
|
|
70
|
+
>
|
|
71
|
+
<UIcon :name="typeMeta.icon" class="h-5 w-5" :style="{ color: typeMeta.accent }" />
|
|
72
|
+
</div>
|
|
73
|
+
<div>
|
|
74
|
+
<h1 class="text-lg font-semibold text-white">{{ block.title }}</h1>
|
|
75
|
+
<div class="text-xs text-slate-500">
|
|
76
|
+
{{ t('focus.typeSubtitle', { type: typeMeta.label }) }}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
<UBadge :color="statusMeta.chip as any" variant="subtle" class="ml-2">
|
|
80
|
+
{{ statusMeta.label }}
|
|
81
|
+
</UBadge>
|
|
82
|
+
<div class="ml-auto flex items-center gap-2">
|
|
83
|
+
<UDropdownMenu :items="runMenu">
|
|
84
|
+
<UButton
|
|
85
|
+
color="primary"
|
|
86
|
+
variant="soft"
|
|
87
|
+
size="sm"
|
|
88
|
+
icon="i-lucide-play"
|
|
89
|
+
trailing-icon="i-lucide-chevron-down"
|
|
90
|
+
>
|
|
91
|
+
{{ instance ? t('focus.rerunPipeline') : t('focus.runPipeline') }}
|
|
92
|
+
</UButton>
|
|
93
|
+
</UDropdownMenu>
|
|
94
|
+
<UButton icon="i-lucide-x" color="neutral" variant="ghost" @click="close" />
|
|
95
|
+
</div>
|
|
96
|
+
</header>
|
|
97
|
+
|
|
98
|
+
<div class="grid flex-1 grid-cols-[1fr_300px] gap-6 overflow-hidden p-6">
|
|
99
|
+
<!-- main: pipeline flow -->
|
|
100
|
+
<section
|
|
101
|
+
class="flex flex-col overflow-auto rounded-2xl border border-slate-800 bg-slate-900/60 p-6"
|
|
102
|
+
>
|
|
103
|
+
<div class="mb-4 flex items-center gap-2">
|
|
104
|
+
<UIcon name="i-lucide-workflow" class="h-4 w-4 text-slate-500" />
|
|
105
|
+
<h2 class="text-sm font-semibold uppercase tracking-wide text-slate-400">
|
|
106
|
+
{{ instance ? instance.pipelineName : t('focus.noPipelineRunning') }}
|
|
107
|
+
</h2>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<PipelineProgress
|
|
111
|
+
v-if="instance"
|
|
112
|
+
:instance="instance"
|
|
113
|
+
@open-decision="openDecisionFor"
|
|
114
|
+
@open-approval="openApprovalFor"
|
|
115
|
+
/>
|
|
116
|
+
|
|
72
117
|
<div
|
|
73
|
-
|
|
74
|
-
|
|
118
|
+
v-else
|
|
119
|
+
class="flex flex-1 items-center justify-center rounded-xl border border-dashed border-slate-700 text-sm text-slate-500"
|
|
75
120
|
>
|
|
76
|
-
|
|
121
|
+
{{ t('focus.emptyPipelineHint') }}
|
|
77
122
|
</div>
|
|
123
|
+
</section>
|
|
124
|
+
|
|
125
|
+
<!-- side: details -->
|
|
126
|
+
<aside
|
|
127
|
+
class="space-y-4 overflow-auto rounded-2xl border border-slate-800 bg-slate-900/60 p-5"
|
|
128
|
+
>
|
|
78
129
|
<div>
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
{{ t('focus.typeSubtitle', { type: typeMeta.label }) }}
|
|
130
|
+
<div class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
131
|
+
{{ t('focus.description') }}
|
|
82
132
|
</div>
|
|
133
|
+
<p class="text-sm text-slate-300">{{ block.description }}</p>
|
|
83
134
|
</div>
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<div class="ml-auto flex items-center gap-2">
|
|
88
|
-
<UDropdownMenu :items="runMenu">
|
|
89
|
-
<UButton
|
|
90
|
-
color="primary"
|
|
91
|
-
variant="soft"
|
|
92
|
-
size="sm"
|
|
93
|
-
icon="i-lucide-play"
|
|
94
|
-
trailing-icon="i-lucide-chevron-down"
|
|
95
|
-
>
|
|
96
|
-
{{ instance ? t('focus.rerunPipeline') : t('focus.runPipeline') }}
|
|
97
|
-
</UButton>
|
|
98
|
-
</UDropdownMenu>
|
|
99
|
-
<UButton icon="i-lucide-x" color="neutral" variant="ghost" @click="close" />
|
|
100
|
-
</div>
|
|
101
|
-
</header>
|
|
102
|
-
|
|
103
|
-
<div class="grid flex-1 grid-cols-[1fr_300px] gap-6 overflow-hidden p-6">
|
|
104
|
-
<!-- main: pipeline flow -->
|
|
105
|
-
<section
|
|
106
|
-
class="flex flex-col overflow-auto rounded-2xl border border-slate-800 bg-slate-900/60 p-6"
|
|
107
|
-
>
|
|
108
|
-
<div class="mb-4 flex items-center gap-2">
|
|
109
|
-
<UIcon name="i-lucide-workflow" class="h-4 w-4 text-slate-500" />
|
|
110
|
-
<h2 class="text-sm font-semibold uppercase tracking-wide text-slate-400">
|
|
111
|
-
{{ instance ? instance.pipelineName : t('focus.noPipelineRunning') }}
|
|
112
|
-
</h2>
|
|
135
|
+
<div v-if="instance">
|
|
136
|
+
<div class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
137
|
+
{{ t('focus.overallProgress') }}
|
|
113
138
|
</div>
|
|
114
|
-
|
|
115
|
-
<
|
|
116
|
-
|
|
117
|
-
:instance="instance"
|
|
118
|
-
@open-decision="openDecisionFor"
|
|
119
|
-
@open-approval="openApprovalFor"
|
|
120
|
-
/>
|
|
121
|
-
|
|
122
|
-
<div
|
|
123
|
-
v-else
|
|
124
|
-
class="flex flex-1 items-center justify-center rounded-xl border border-dashed border-slate-700 text-sm text-slate-500"
|
|
125
|
-
>
|
|
126
|
-
{{ t('focus.emptyPipelineHint') }}
|
|
139
|
+
<UProgress :model-value="Math.round(block.progress * 100)" />
|
|
140
|
+
<div class="mt-1 text-[11px] text-slate-400">
|
|
141
|
+
{{ n(block.progress, 'percent') }}
|
|
127
142
|
</div>
|
|
128
|
-
</
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
class="space-y-4 overflow-auto rounded-2xl border border-slate-800 bg-slate-900/60 p-5"
|
|
133
|
-
>
|
|
134
|
-
<div>
|
|
135
|
-
<div class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
136
|
-
{{ t('focus.description') }}
|
|
137
|
-
</div>
|
|
138
|
-
<p class="text-sm text-slate-300">{{ block.description }}</p>
|
|
139
|
-
</div>
|
|
140
|
-
<div v-if="instance">
|
|
141
|
-
<div class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
142
|
-
{{ t('focus.overallProgress') }}
|
|
143
|
-
</div>
|
|
144
|
-
<UProgress :model-value="Math.round(block.progress * 100)" />
|
|
145
|
-
<div class="mt-1 text-[11px] text-slate-400">
|
|
146
|
-
{{ n(block.progress, 'percent') }}
|
|
147
|
-
</div>
|
|
143
|
+
</div>
|
|
144
|
+
<div>
|
|
145
|
+
<div class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
146
|
+
{{ t('focus.dependencies') }}
|
|
148
147
|
</div>
|
|
149
|
-
<div>
|
|
150
|
-
<
|
|
151
|
-
{{
|
|
152
|
-
</
|
|
153
|
-
<div v-if="deps.length" class="flex flex-wrap gap-1">
|
|
154
|
-
<UBadge v-for="d in deps" :key="d.id" color="neutral" variant="subtle" size="sm">
|
|
155
|
-
{{ d.title }}
|
|
156
|
-
</UBadge>
|
|
157
|
-
</div>
|
|
158
|
-
<div v-else class="text-[11px] text-slate-500">{{ t('focus.noDependencies') }}</div>
|
|
148
|
+
<div v-if="deps.length" class="flex flex-wrap gap-1">
|
|
149
|
+
<UBadge v-for="d in deps" :key="d.id" color="neutral" variant="subtle" size="sm">
|
|
150
|
+
{{ d.title }}
|
|
151
|
+
</UBadge>
|
|
159
152
|
</div>
|
|
160
|
-
|
|
161
|
-
|
|
153
|
+
<div v-else class="text-[11px] text-slate-500">{{ t('focus.noDependencies') }}</div>
|
|
154
|
+
</div>
|
|
155
|
+
</aside>
|
|
162
156
|
</div>
|
|
163
|
-
</
|
|
157
|
+
</div>
|
|
164
158
|
</template>
|
|
165
159
|
|
|
166
160
|
<style scoped>
|
package/app/pages/index.vue
CHANGED
|
@@ -14,11 +14,7 @@ import InspectorPanel from '~/components/panels/InspectorPanel.vue'
|
|
|
14
14
|
import DecisionModal from '~/components/panels/DecisionModal.vue'
|
|
15
15
|
import AgentStepDetail from '~/components/panels/AgentStepDetail.vue'
|
|
16
16
|
import StepResultViewHost from '~/components/panels/StepResultViewHost.vue'
|
|
17
|
-
import BlockFocusView from '~/components/focus/BlockFocusView.vue'
|
|
18
|
-
import TaskSourceConnectModal from '~/components/tasks/TaskSourceConnectModal.vue'
|
|
19
|
-
import TaskImportModal from '~/components/tasks/TaskImportModal.vue'
|
|
20
17
|
import AddTaskModal from '~/components/board/AddTaskModal.vue'
|
|
21
|
-
import RecurringPipelineModal from '~/components/board/RecurringPipelineModal.vue'
|
|
22
18
|
import GitHubOnboarding from '~/components/github/GitHubOnboarding.vue'
|
|
23
19
|
import CommandBar from '~/components/layout/CommandBar.vue'
|
|
24
20
|
import PersonalCredentialModal from '~/components/providers/PersonalCredentialModal.vue'
|
|
@@ -30,6 +26,17 @@ const ObservabilityPanel = defineAsyncComponent(
|
|
|
30
26
|
() => import('~/components/panels/ObservabilityPanel.vue'),
|
|
31
27
|
)
|
|
32
28
|
const KaizenPanel = defineAsyncComponent(() => import('~/components/kaizen/KaizenPanel.vue'))
|
|
29
|
+
// Occasional, externally store-gated surfaces — deferred to their own chunks like the
|
|
30
|
+
// sibling document modals above. Each mounts only while its ui open-flag is set, so it
|
|
31
|
+
// loads on first open instead of bloating the initial bundle.
|
|
32
|
+
const BlockFocusView = defineAsyncComponent(() => import('~/components/focus/BlockFocusView.vue'))
|
|
33
|
+
const TaskSourceConnectModal = defineAsyncComponent(
|
|
34
|
+
() => import('~/components/tasks/TaskSourceConnectModal.vue'),
|
|
35
|
+
)
|
|
36
|
+
const TaskImportModal = defineAsyncComponent(() => import('~/components/tasks/TaskImportModal.vue'))
|
|
37
|
+
const RecurringPipelineModal = defineAsyncComponent(
|
|
38
|
+
() => import('~/components/board/RecurringPipelineModal.vue'),
|
|
39
|
+
)
|
|
33
40
|
const DocumentSourceConnectModal = defineAsyncComponent(
|
|
34
41
|
() => import('~/components/documents/DocumentSourceConnectModal.vue'),
|
|
35
42
|
)
|
|
@@ -248,7 +255,12 @@ watch(
|
|
|
248
255
|
<BoardToolbar />
|
|
249
256
|
<SpendWarningBanner />
|
|
250
257
|
<InspectorPanel />
|
|
251
|
-
|
|
258
|
+
<!-- Code-split focus view. The fade lives here (not inside the component) so the
|
|
259
|
+
leave animation still plays when `focusBlockId` clears and the v-if unmounts
|
|
260
|
+
the chunk — an inner Transition would be torn down before it could run. -->
|
|
261
|
+
<Transition name="focus-fade">
|
|
262
|
+
<BlockFocusView v-if="ui.focusBlockId" />
|
|
263
|
+
</Transition>
|
|
252
264
|
</main>
|
|
253
265
|
|
|
254
266
|
<!-- Always-mounted, fast-path surfaces. -->
|
|
@@ -256,15 +268,15 @@ watch(
|
|
|
256
268
|
<DecisionModal />
|
|
257
269
|
<AgentStepDetail />
|
|
258
270
|
<StepResultViewHost />
|
|
259
|
-
<TaskSourceConnectModal />
|
|
260
|
-
<TaskImportModal />
|
|
261
271
|
<AddTaskModal />
|
|
262
|
-
<RecurringPipelineModal />
|
|
263
272
|
<CommandBar />
|
|
264
273
|
<PersonalCredentialModal />
|
|
265
274
|
|
|
266
275
|
<!-- Lazy panels: mounted only while their ui open-flag is set, so each loads on
|
|
267
276
|
first open (its own chunk) rather than bloating the initial bundle. -->
|
|
277
|
+
<TaskSourceConnectModal v-if="ui.taskConnect" />
|
|
278
|
+
<TaskImportModal v-if="ui.taskImport" />
|
|
279
|
+
<RecurringPipelineModal v-if="ui.addRecurringFrameId" />
|
|
268
280
|
<ObservabilityPanel v-if="ui.observabilityInstanceId" />
|
|
269
281
|
<KaizenPanel v-if="ui.kaizenScreenOpen" />
|
|
270
282
|
<DocumentSourceConnectModal v-if="ui.documentConnect" />
|
package/app/stores/workspace.ts
CHANGED
|
@@ -113,11 +113,16 @@ export const useWorkspaceStore = defineStore(
|
|
|
113
113
|
ready.value = false
|
|
114
114
|
error.value = null
|
|
115
115
|
try {
|
|
116
|
-
// Accounts
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
// Accounts (an auth concept — empty in dev, which leaves boards unscoped) and the
|
|
117
|
+
// workspace list are independent, so fetch them concurrently. resolveActiveBoard
|
|
118
|
+
// needs both, so it still runs after.
|
|
119
|
+
const [, workspaceList] = await Promise.all([
|
|
120
|
+
useAccountsStore()
|
|
121
|
+
.load()
|
|
122
|
+
.catch(() => {}),
|
|
123
|
+
api.listWorkspaces(),
|
|
124
|
+
])
|
|
125
|
+
workspaces.value = workspaceList
|
|
121
126
|
await resolveActiveBoard()
|
|
122
127
|
ready.value = true
|
|
123
128
|
} catch (e) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.54.
|
|
3
|
+
"version": "0.54.2",
|
|
4
4
|
"description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|