@cat-factory/app 0.45.2 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/components/layout/BoardToolbar.vue +9 -7
- package/app/components/layout/NotificationsInbox.vue +1 -1
- package/app/components/layout/SideBar.vue +255 -170
- package/app/components/panels/InspectorPanel.vue +6 -3
- package/app/composables/useViewport.ts +19 -0
- package/app/pages/index.vue +14 -0
- package/app/stores/ui.ts +18 -0
- package/i18n/i18n.config.ts +36 -0
- package/i18n/locales/en.json +28 -1
- package/i18n/locales/es.json +62 -0
- package/i18n/locales/fr.json +62 -0
- package/i18n/locales/pl.json +62 -0
- package/i18n/locales/uk.json +62 -0
- package/nuxt.config.ts +7 -1
- package/package.json +1 -1
|
@@ -81,7 +81,7 @@ const decisionItems = computed(() =>
|
|
|
81
81
|
|
|
82
82
|
<template>
|
|
83
83
|
<div
|
|
84
|
-
class="absolute left-1/2 top-
|
|
84
|
+
class="absolute left-1/2 top-3 z-20 flex max-w-[calc(100vw-1rem)] -translate-x-1/2 items-center gap-1 overflow-x-auto rounded-full border border-slate-700 bg-slate-900/90 px-2 py-1.5 shadow-xl backdrop-blur"
|
|
85
85
|
>
|
|
86
86
|
<!-- zoom controls -->
|
|
87
87
|
<UButton
|
|
@@ -91,7 +91,8 @@ const decisionItems = computed(() =>
|
|
|
91
91
|
size="sm"
|
|
92
92
|
@click="zoomOut()"
|
|
93
93
|
/>
|
|
94
|
-
|
|
94
|
+
<!-- The zoom %/LOD readout is the first thing to drop on narrow viewports. -->
|
|
95
|
+
<div class="hidden w-20 text-center text-xs tabular-nums text-slate-300 sm:block">
|
|
95
96
|
{{ zoomPct }}%
|
|
96
97
|
<div class="text-[9px] uppercase tracking-wide text-slate-500">{{ lodLabel }}</div>
|
|
97
98
|
</div>
|
|
@@ -115,16 +116,17 @@ const decisionItems = computed(() =>
|
|
|
115
116
|
icon="i-lucide-circle-help"
|
|
116
117
|
data-testid="decision-badge"
|
|
117
118
|
>
|
|
118
|
-
{{ execution.pendingDecisionCount
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
{{ execution.pendingDecisionCount
|
|
120
|
+
}}<span class="hidden sm:inline"
|
|
121
|
+
> {{ $t('board.toolbar.decisionWord', execution.pendingDecisionCount) }}</span
|
|
122
|
+
>
|
|
121
123
|
</UButton>
|
|
122
124
|
</UDropdownMenu>
|
|
123
125
|
|
|
124
126
|
<!-- in-org sharing: add an existing org service to this board -->
|
|
125
127
|
<UDropdownMenu v-if="mountableItems.length" :items="mountableItems">
|
|
126
128
|
<UButton color="neutral" variant="ghost" size="sm" icon="i-lucide-plus-circle">
|
|
127
|
-
|
|
129
|
+
<span class="hidden sm:inline">{{ $t('board.toolbar.addService') }}</span>
|
|
128
130
|
</UButton>
|
|
129
131
|
</UDropdownMenu>
|
|
130
132
|
|
|
@@ -140,7 +142,7 @@ const decisionItems = computed(() =>
|
|
|
140
142
|
icon="i-lucide-wallet"
|
|
141
143
|
:title="spend?.exceeded ? 'Spend limit reached — runs paused' : 'Token spend this month'"
|
|
142
144
|
>
|
|
143
|
-
{{ spendLabel }}
|
|
145
|
+
<span class="hidden sm:inline">{{ spendLabel }}</span>
|
|
144
146
|
</UButton>
|
|
145
147
|
</div>
|
|
146
148
|
</template>
|
|
@@ -174,7 +174,7 @@ function revealDecision(n: Notification) {
|
|
|
174
174
|
</UButton>
|
|
175
175
|
|
|
176
176
|
<template #content>
|
|
177
|
-
<div class="max-h-[28rem] w-
|
|
177
|
+
<div class="max-h-[28rem] w-[min(24rem,92vw)] overflow-y-auto p-2">
|
|
178
178
|
<div class="px-2 py-1 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
179
179
|
Needs your attention
|
|
180
180
|
</div>
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
// actions, repository management, integration management, the workspace-wide
|
|
6
6
|
// context-fragment library, and workspace configuration (merge thresholds +
|
|
7
7
|
// default models).
|
|
8
|
+
import { useEventListener, useScrollLock } from '@vueuse/core'
|
|
8
9
|
import BoardSwitcher from '~/components/layout/BoardSwitcher.vue'
|
|
9
10
|
import UserMenu from '~/components/auth/UserMenu.vue'
|
|
11
|
+
import { useViewport } from '~/composables/useViewport'
|
|
10
12
|
|
|
11
13
|
const documents = useDocumentsStore()
|
|
12
14
|
const tasks = useTasksStore()
|
|
@@ -17,6 +19,60 @@ const workspace = useWorkspaceStore()
|
|
|
17
19
|
const accounts = useAccountsStore()
|
|
18
20
|
const ui = useUiStore()
|
|
19
21
|
|
|
22
|
+
// `isCompact` (< lg) is the breakpoint at which the navbar is an off-canvas drawer;
|
|
23
|
+
// above it the aside is static and the drawer flag is inert.
|
|
24
|
+
const { isCompact } = useViewport()
|
|
25
|
+
|
|
26
|
+
// The off-canvas drawer is a modal surface on compact viewports, so give it the
|
|
27
|
+
// expected affordances:
|
|
28
|
+
// • Escape closes it (keyboard parity with the backdrop tap),
|
|
29
|
+
// • body scroll is locked while it's open (defensive — the shell root is already
|
|
30
|
+
// `overflow-hidden`, so this just guards any future scrollable ancestor),
|
|
31
|
+
// • crossing back to lg+ clears the flag so it can't linger as stale open state,
|
|
32
|
+
// • focus moves into the drawer on open, and back to the hamburger on close,
|
|
33
|
+
// • when closed (off-screen) on compact the whole aside is `inert`, so its nav
|
|
34
|
+
// controls aren't reachable by keyboard / assistive tech behind the board.
|
|
35
|
+
const aside = ref<HTMLElement>()
|
|
36
|
+
const drawerOpen = computed(() => isCompact.value && ui.mobileNavOpen)
|
|
37
|
+
|
|
38
|
+
const bodyLocked = useScrollLock(import.meta.client ? document.body : null)
|
|
39
|
+
watchEffect(() => {
|
|
40
|
+
bodyLocked.value = drawerOpen.value
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
useEventListener('keydown', (e: KeyboardEvent) => {
|
|
44
|
+
if (e.key === 'Escape' && ui.mobileNavOpen) ui.closeMobileNav()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
watch(isCompact, (compact) => {
|
|
48
|
+
if (!compact) ui.closeMobileNav()
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// Closing via a nav action immediately opens a board-covering panel/modal that claims
|
|
52
|
+
// focus itself, so don't yank focus back to the hamburger in that case.
|
|
53
|
+
let suppressFocusRestore = false
|
|
54
|
+
watch(drawerOpen, (open) => {
|
|
55
|
+
if (open) {
|
|
56
|
+
suppressFocusRestore = false
|
|
57
|
+
void nextTick(() => aside.value?.focus())
|
|
58
|
+
} else if (isCompact.value && !suppressFocusRestore) {
|
|
59
|
+
void nextTick(() =>
|
|
60
|
+
document.querySelector<HTMLElement>('[data-testid="mobile-nav-toggle"]')?.focus(),
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
// On compact (< lg) viewports the navbar is an off-canvas drawer. Activating any
|
|
66
|
+
// nav control reveals a board-covering panel/modal, so close the drawer on the way
|
|
67
|
+
// out — otherwise it lingers in front of (or behind) whatever just opened. Scoped to
|
|
68
|
+
// the action sections (not the BoardSwitcher / UserMenu dropdowns at the ends).
|
|
69
|
+
function onNavAction(e: MouseEvent) {
|
|
70
|
+
if ((e.target as HTMLElement).closest('button, a')) {
|
|
71
|
+
suppressFocusRestore = true
|
|
72
|
+
ui.closeMobileNav()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
20
76
|
// Resolve whether the document-source / task-source / GitHub integrations are
|
|
21
77
|
// enabled on the backend, so each section is hidden entirely when it is off
|
|
22
78
|
// (mirrors how auth gates its UI). A 503 from a probe flips its `available` to
|
|
@@ -36,190 +92,219 @@ watch(
|
|
|
36
92
|
</script>
|
|
37
93
|
|
|
38
94
|
<template>
|
|
95
|
+
<!-- On < lg the navbar slides in over the board; this backdrop dims the board and
|
|
96
|
+
closes the drawer on tap. Hidden on lg+ where the navbar is a static aside. -->
|
|
97
|
+
<Transition
|
|
98
|
+
enter-active-class="transition-opacity duration-200"
|
|
99
|
+
leave-active-class="transition-opacity duration-200"
|
|
100
|
+
enter-from-class="opacity-0"
|
|
101
|
+
leave-to-class="opacity-0"
|
|
102
|
+
>
|
|
103
|
+
<div
|
|
104
|
+
v-if="ui.mobileNavOpen"
|
|
105
|
+
class="fixed inset-0 z-30 bg-slate-950/60 backdrop-blur-sm lg:hidden"
|
|
106
|
+
data-testid="sidebar-backdrop"
|
|
107
|
+
role="button"
|
|
108
|
+
tabindex="-1"
|
|
109
|
+
:aria-label="$t('common.close')"
|
|
110
|
+
@click="ui.closeMobileNav()"
|
|
111
|
+
/>
|
|
112
|
+
</Transition>
|
|
113
|
+
|
|
39
114
|
<aside
|
|
40
|
-
|
|
115
|
+
ref="aside"
|
|
116
|
+
data-testid="sidebar"
|
|
117
|
+
tabindex="-1"
|
|
118
|
+
:role="drawerOpen ? 'dialog' : undefined"
|
|
119
|
+
:aria-modal="drawerOpen ? 'true' : undefined"
|
|
120
|
+
:aria-label="isCompact ? $t('nav.menu') : undefined"
|
|
121
|
+
:inert="isCompact && !ui.mobileNavOpen"
|
|
122
|
+
class="fixed inset-y-0 left-0 z-40 flex h-full w-64 shrink-0 flex-col gap-4 overflow-y-auto border-r border-slate-800 bg-slate-900/95 p-3 backdrop-blur transition-transform duration-200 focus:outline-none lg:static lg:z-auto lg:translate-x-0 lg:bg-slate-900/80"
|
|
123
|
+
:class="ui.mobileNavOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'"
|
|
41
124
|
>
|
|
42
125
|
<BoardSwitcher />
|
|
43
126
|
|
|
44
|
-
|
|
127
|
+
<div class="contents" @click="onNavAction">
|
|
128
|
+
<!-- Command bar launcher (⌘K) — the primary way to create blocks / pipelines
|
|
45
129
|
and reach every action below. -->
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
130
|
+
<button
|
|
131
|
+
type="button"
|
|
132
|
+
class="flex items-center gap-2 rounded-lg border border-slate-700 bg-slate-800/60 px-2.5 py-2 text-left text-sm text-slate-400 transition hover:border-slate-500 hover:bg-slate-800"
|
|
133
|
+
@click="ui.openCommandBar()"
|
|
134
|
+
>
|
|
135
|
+
<UIcon name="i-lucide-search" class="h-4 w-4 shrink-0" />
|
|
136
|
+
<span class="flex-1 truncate">{{ $t('nav.commandBar') }}</span>
|
|
137
|
+
<UKbd value="⌘K" />
|
|
138
|
+
</button>
|
|
139
|
+
|
|
140
|
+
<section>
|
|
141
|
+
<h2 class="mb-2 px-1 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
142
|
+
{{ $t('nav.create') }}
|
|
143
|
+
</h2>
|
|
144
|
+
<div class="space-y-1.5">
|
|
145
|
+
<UButton
|
|
146
|
+
block
|
|
147
|
+
color="primary"
|
|
148
|
+
variant="soft"
|
|
149
|
+
size="sm"
|
|
150
|
+
icon="i-lucide-workflow"
|
|
151
|
+
class="justify-start"
|
|
152
|
+
@click="ui.openBuilder()"
|
|
153
|
+
>
|
|
154
|
+
{{ $t('nav.buildPipeline') }}
|
|
155
|
+
</UButton>
|
|
156
|
+
</div>
|
|
157
|
+
</section>
|
|
158
|
+
|
|
159
|
+
<USeparator />
|
|
160
|
+
|
|
161
|
+
<section>
|
|
162
|
+
<h2 class="mb-2 px-1 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
163
|
+
{{ $t('nav.repositories') }}
|
|
164
|
+
</h2>
|
|
165
|
+
<div class="space-y-1.5">
|
|
166
|
+
<UButton
|
|
167
|
+
v-if="github.available"
|
|
168
|
+
block
|
|
169
|
+
color="primary"
|
|
170
|
+
variant="soft"
|
|
171
|
+
size="sm"
|
|
172
|
+
icon="i-lucide-folder-git-2"
|
|
173
|
+
class="justify-start"
|
|
174
|
+
@click="ui.openAddService()"
|
|
175
|
+
>
|
|
176
|
+
{{ $t('nav.addFromRepo') }}
|
|
177
|
+
</UButton>
|
|
178
|
+
<UButton
|
|
179
|
+
block
|
|
180
|
+
color="primary"
|
|
181
|
+
variant="soft"
|
|
182
|
+
size="sm"
|
|
183
|
+
icon="i-lucide-git-branch-plus"
|
|
184
|
+
class="justify-start"
|
|
185
|
+
@click="ui.openBootstrap()"
|
|
186
|
+
>
|
|
187
|
+
{{ $t('nav.bootstrapRepo') }}
|
|
188
|
+
</UButton>
|
|
189
|
+
</div>
|
|
190
|
+
</section>
|
|
191
|
+
|
|
192
|
+
<USeparator />
|
|
193
|
+
|
|
194
|
+
<section>
|
|
195
|
+
<h2 class="mb-2 px-1 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
196
|
+
{{ $t('nav.integrations') }}
|
|
197
|
+
</h2>
|
|
198
|
+
<div class="space-y-1.5">
|
|
199
|
+
<!-- Every external system the workspace can enable/link now lives behind
|
|
116
200
|
this single button — the hub modal lists them grouped (source control,
|
|
117
201
|
communication, documents, trackers, observability, model providers). -->
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
202
|
+
<UButton
|
|
203
|
+
block
|
|
204
|
+
color="primary"
|
|
205
|
+
variant="soft"
|
|
206
|
+
size="sm"
|
|
207
|
+
icon="i-lucide-blocks"
|
|
208
|
+
class="justify-start"
|
|
209
|
+
@click="ui.openIntegrations()"
|
|
210
|
+
>
|
|
211
|
+
{{ $t('nav.integrations') }}
|
|
212
|
+
</UButton>
|
|
213
|
+
<!-- The Sandbox: try prompt versions/models against graded fixtures, off to the
|
|
130
214
|
side of the board. Opens the on-demand testing window. -->
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
215
|
+
<UButton
|
|
216
|
+
block
|
|
217
|
+
color="primary"
|
|
218
|
+
variant="soft"
|
|
219
|
+
size="sm"
|
|
220
|
+
icon="i-lucide-flask-conical"
|
|
221
|
+
class="justify-start"
|
|
222
|
+
@click="ui.openSandbox()"
|
|
223
|
+
>
|
|
224
|
+
{{ $t('nav.sandbox') }}
|
|
225
|
+
</UButton>
|
|
226
|
+
<!-- The Kaizen screen: grading history + verified prompt/agent/model combos. -->
|
|
227
|
+
<UButton
|
|
228
|
+
block
|
|
229
|
+
color="primary"
|
|
230
|
+
variant="soft"
|
|
231
|
+
size="sm"
|
|
232
|
+
icon="i-lucide-sparkles"
|
|
233
|
+
class="justify-start"
|
|
234
|
+
@click="ui.openKaizen()"
|
|
235
|
+
>
|
|
236
|
+
{{ $t('nav.kaizen') }}
|
|
237
|
+
</UButton>
|
|
238
|
+
</div>
|
|
239
|
+
</section>
|
|
240
|
+
|
|
241
|
+
<template v-if="library.available">
|
|
242
|
+
<USeparator />
|
|
243
|
+
<section>
|
|
244
|
+
<h2 class="mb-2 px-1 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
245
|
+
{{ $t('nav.workspaceContext') }}
|
|
246
|
+
</h2>
|
|
247
|
+
<UButton
|
|
248
|
+
block
|
|
249
|
+
color="primary"
|
|
250
|
+
variant="soft"
|
|
251
|
+
size="sm"
|
|
252
|
+
icon="i-lucide-book-marked"
|
|
253
|
+
class="justify-start"
|
|
254
|
+
@click="ui.openFragmentLibrary()"
|
|
255
|
+
>
|
|
256
|
+
{{ $t('nav.contextFragments') }}
|
|
257
|
+
</UButton>
|
|
258
|
+
</section>
|
|
259
|
+
</template>
|
|
260
|
+
|
|
158
261
|
<USeparator />
|
|
159
262
|
<section>
|
|
160
263
|
<h2 class="mb-2 px-1 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
161
|
-
|
|
264
|
+
{{ $t('nav.configuration') }}
|
|
162
265
|
</h2>
|
|
163
|
-
<
|
|
164
|
-
|
|
165
|
-
color="primary"
|
|
166
|
-
variant="soft"
|
|
167
|
-
size="sm"
|
|
168
|
-
icon="i-lucide-book-marked"
|
|
169
|
-
class="justify-start"
|
|
170
|
-
@click="ui.openFragmentLibrary()"
|
|
171
|
-
>
|
|
172
|
-
Context fragments
|
|
173
|
-
</UButton>
|
|
174
|
-
</section>
|
|
175
|
-
</template>
|
|
176
|
-
|
|
177
|
-
<USeparator />
|
|
178
|
-
<section>
|
|
179
|
-
<h2 class="mb-2 px-1 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
180
|
-
Configuration
|
|
181
|
-
</h2>
|
|
182
|
-
<div class="space-y-1.5">
|
|
183
|
-
<!-- Merge thresholds, issue writeback and default service best practices are
|
|
266
|
+
<div class="space-y-1.5">
|
|
267
|
+
<!-- Merge thresholds, issue writeback and default service best practices are
|
|
184
268
|
now tabs inside Workspace settings. -->
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
269
|
+
<UButton
|
|
270
|
+
block
|
|
271
|
+
color="primary"
|
|
272
|
+
variant="soft"
|
|
273
|
+
size="sm"
|
|
274
|
+
icon="i-lucide-sliders-horizontal"
|
|
275
|
+
class="justify-start"
|
|
276
|
+
@click="ui.openWorkspaceSettings()"
|
|
277
|
+
>
|
|
278
|
+
{{ $t('nav.workspaceSettings') }}
|
|
279
|
+
</UButton>
|
|
280
|
+
<UButton
|
|
281
|
+
block
|
|
282
|
+
color="primary"
|
|
283
|
+
variant="soft"
|
|
284
|
+
size="sm"
|
|
285
|
+
icon="i-lucide-cpu"
|
|
286
|
+
class="justify-start"
|
|
287
|
+
@click="ui.openModelConfig()"
|
|
288
|
+
>
|
|
289
|
+
{{ $t('nav.modelConfiguration') }}
|
|
290
|
+
</UButton>
|
|
291
|
+
<!-- Account & team: members + roles, invitations, email sender, account API keys.
|
|
208
292
|
Shown once accounts (auth) are enabled. -->
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
293
|
+
<UButton
|
|
294
|
+
v-if="accounts.enabled"
|
|
295
|
+
block
|
|
296
|
+
color="primary"
|
|
297
|
+
variant="soft"
|
|
298
|
+
size="sm"
|
|
299
|
+
icon="i-lucide-users"
|
|
300
|
+
class="justify-start"
|
|
301
|
+
@click="ui.openAccountSettings()"
|
|
302
|
+
>
|
|
303
|
+
{{ $t('nav.accountSettings') }}
|
|
304
|
+
</UButton>
|
|
305
|
+
</div>
|
|
306
|
+
</section>
|
|
307
|
+
</div>
|
|
223
308
|
|
|
224
309
|
<UserMenu class="mt-auto" />
|
|
225
310
|
</aside>
|
|
@@ -204,13 +204,16 @@ const showOriginalDescription = ref(false)
|
|
|
204
204
|
<template>
|
|
205
205
|
<div
|
|
206
206
|
v-if="block && statusMeta && typeMeta"
|
|
207
|
-
|
|
207
|
+
data-testid="inspector-panel"
|
|
208
|
+
class="fixed inset-x-0 bottom-0 z-20 overflow-hidden rounded-t-2xl border border-slate-700 bg-slate-900/95 shadow-2xl backdrop-blur lg:absolute lg:inset-x-auto lg:bottom-auto lg:right-4 lg:top-4 lg:w-80 lg:rounded-2xl"
|
|
208
209
|
>
|
|
209
210
|
<div class="h-1.5 w-full" :style="{ backgroundColor: statusMeta.color }" />
|
|
210
211
|
<!-- A tall task (execution steps + scenarios + docs) can overflow the
|
|
211
212
|
viewport; cap the body height and let it scroll so the lower controls
|
|
212
|
-
(Run / Focus / Delete) stay reachable. The status bar above stays put.
|
|
213
|
-
|
|
213
|
+
(Run / Focus / Delete) stay reachable. The status bar above stays put.
|
|
214
|
+
On compact viewports the panel is a bottom sheet capped to the visible
|
|
215
|
+
height (dvh excludes mobile browser chrome). -->
|
|
216
|
+
<div class="max-h-[80dvh] space-y-4 overflow-y-auto p-4 lg:max-h-[calc(100vh-5rem)]">
|
|
214
217
|
<!-- header -->
|
|
215
218
|
<div class="flex items-start justify-between gap-2">
|
|
216
219
|
<div class="flex items-center gap-2">
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useBreakpoints, breakpointsTailwind, useMediaQuery } from '@vueuse/core'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Single source of truth for responsive / input-modality decisions across the SPA.
|
|
5
|
+
*
|
|
6
|
+
* `isCompact` is the app-wide "mobile/compact" flag: true below Tailwind's `lg`
|
|
7
|
+
* (1024px), matching the breakpoint the already-responsive surfaces use
|
|
8
|
+
* (`AgentStepDetail`, the review windows) so the whole shell stays consistent.
|
|
9
|
+
*
|
|
10
|
+
* `isTouch` reports a coarse pointer (phones/tablets) so a component can enlarge
|
|
11
|
+
* hit targets without affecting precise-pointer (mouse) desktops — orthogonal to
|
|
12
|
+
* width, since a small window on a desktop is compact but not touch.
|
|
13
|
+
*/
|
|
14
|
+
export function useViewport() {
|
|
15
|
+
const breakpoints = useBreakpoints(breakpointsTailwind)
|
|
16
|
+
const isCompact = breakpoints.smaller('lg')
|
|
17
|
+
const isTouch = useMediaQuery('(pointer: coarse)')
|
|
18
|
+
return { isCompact, isTouch }
|
|
19
|
+
}
|
package/app/pages/index.vue
CHANGED
|
@@ -231,6 +231,20 @@ watch(
|
|
|
231
231
|
<SideBar />
|
|
232
232
|
<main class="relative min-w-0 flex-1">
|
|
233
233
|
<BoardCanvas />
|
|
234
|
+
<!-- Compact-viewport navbar trigger: the SideBar is an off-canvas drawer below
|
|
235
|
+
lg, so surface a hamburger to open it. Hidden on lg+ (static sidebar). -->
|
|
236
|
+
<!-- z-30 keeps the trigger above the centered BoardToolbar (z-20), whose
|
|
237
|
+
max-width can otherwise reach this corner on the narrowest viewports. -->
|
|
238
|
+
<UButton
|
|
239
|
+
class="absolute left-3 top-3 z-30 lg:hidden"
|
|
240
|
+
icon="i-lucide-menu"
|
|
241
|
+
color="neutral"
|
|
242
|
+
variant="soft"
|
|
243
|
+
size="sm"
|
|
244
|
+
:aria-label="ui.mobileNavOpen ? $t('nav.closeMenu') : $t('nav.openMenu')"
|
|
245
|
+
data-testid="mobile-nav-toggle"
|
|
246
|
+
@click="ui.toggleMobileNav()"
|
|
247
|
+
/>
|
|
234
248
|
<BoardToolbar />
|
|
235
249
|
<SpendWarningBanner />
|
|
236
250
|
<InspectorPanel />
|
package/app/stores/ui.ts
CHANGED
|
@@ -85,6 +85,11 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
85
85
|
// Command bar (⌘K) — searchable launcher for every navbar action.
|
|
86
86
|
const commandBarOpen = ref(false)
|
|
87
87
|
|
|
88
|
+
// Mobile navigation drawer: on compact (< lg) viewports the SideBar is an
|
|
89
|
+
// off-canvas drawer toggled by a hamburger; on lg+ it is a static aside and this
|
|
90
|
+
// flag is ignored. Closed on any nav action so the board is revealed immediately.
|
|
91
|
+
const mobileNavOpen = ref(false)
|
|
92
|
+
|
|
88
93
|
// Integrations hub: a single modal listing every external system the workspace
|
|
89
94
|
// can enable/link (GitHub, Slack, document + task sources, Datadog, LLM vendors,
|
|
90
95
|
// local runners, OpenRouter). Replaces the per-integration navbar buttons; each
|
|
@@ -393,6 +398,15 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
393
398
|
function toggleCommandBar() {
|
|
394
399
|
commandBarOpen.value = !commandBarOpen.value
|
|
395
400
|
}
|
|
401
|
+
function openMobileNav() {
|
|
402
|
+
mobileNavOpen.value = true
|
|
403
|
+
}
|
|
404
|
+
function closeMobileNav() {
|
|
405
|
+
mobileNavOpen.value = false
|
|
406
|
+
}
|
|
407
|
+
function toggleMobileNav() {
|
|
408
|
+
mobileNavOpen.value = !mobileNavOpen.value
|
|
409
|
+
}
|
|
396
410
|
// Clear BOTH hub came-from markers. Every direct `open*` below calls this so that a
|
|
397
411
|
// panel opened outside the hubs never grows a dead Back control, and so switching from
|
|
398
412
|
// one hub's panel to the other's clears the stale marker.
|
|
@@ -638,6 +652,7 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
638
652
|
slackOpen,
|
|
639
653
|
fragmentLibraryOpen,
|
|
640
654
|
commandBarOpen,
|
|
655
|
+
mobileNavOpen,
|
|
641
656
|
integrationsOpen,
|
|
642
657
|
cameFromIntegrations,
|
|
643
658
|
personalSetupOpen,
|
|
@@ -707,6 +722,9 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
707
722
|
openCommandBar,
|
|
708
723
|
closeCommandBar,
|
|
709
724
|
toggleCommandBar,
|
|
725
|
+
openMobileNav,
|
|
726
|
+
closeMobileNav,
|
|
727
|
+
toggleMobileNav,
|
|
710
728
|
openIntegrations,
|
|
711
729
|
closeIntegrations,
|
|
712
730
|
openFromIntegrations,
|
package/i18n/i18n.config.ts
CHANGED
|
@@ -19,11 +19,47 @@ export default defineI18nConfig(() => ({
|
|
|
19
19
|
currency: { style: 'currency', currency: 'USD', currencyDisplay: 'narrowSymbol' },
|
|
20
20
|
percent: { style: 'percent', maximumFractionDigits: 1 },
|
|
21
21
|
},
|
|
22
|
+
es: {
|
|
23
|
+
decimal: { style: 'decimal' },
|
|
24
|
+
currency: { style: 'currency', currency: 'USD', currencyDisplay: 'narrowSymbol' },
|
|
25
|
+
percent: { style: 'percent', maximumFractionDigits: 1 },
|
|
26
|
+
},
|
|
27
|
+
pl: {
|
|
28
|
+
decimal: { style: 'decimal' },
|
|
29
|
+
currency: { style: 'currency', currency: 'USD', currencyDisplay: 'narrowSymbol' },
|
|
30
|
+
percent: { style: 'percent', maximumFractionDigits: 1 },
|
|
31
|
+
},
|
|
32
|
+
uk: {
|
|
33
|
+
decimal: { style: 'decimal' },
|
|
34
|
+
currency: { style: 'currency', currency: 'USD', currencyDisplay: 'narrowSymbol' },
|
|
35
|
+
percent: { style: 'percent', maximumFractionDigits: 1 },
|
|
36
|
+
},
|
|
37
|
+
fr: {
|
|
38
|
+
decimal: { style: 'decimal' },
|
|
39
|
+
currency: { style: 'currency', currency: 'USD', currencyDisplay: 'narrowSymbol' },
|
|
40
|
+
percent: { style: 'percent', maximumFractionDigits: 1 },
|
|
41
|
+
},
|
|
22
42
|
},
|
|
23
43
|
datetimeFormats: {
|
|
24
44
|
en: {
|
|
25
45
|
short: { dateStyle: 'medium' },
|
|
26
46
|
long: { dateStyle: 'long', timeStyle: 'short' },
|
|
27
47
|
},
|
|
48
|
+
es: {
|
|
49
|
+
short: { dateStyle: 'medium' },
|
|
50
|
+
long: { dateStyle: 'long', timeStyle: 'short' },
|
|
51
|
+
},
|
|
52
|
+
pl: {
|
|
53
|
+
short: { dateStyle: 'medium' },
|
|
54
|
+
long: { dateStyle: 'long', timeStyle: 'short' },
|
|
55
|
+
},
|
|
56
|
+
uk: {
|
|
57
|
+
short: { dateStyle: 'medium' },
|
|
58
|
+
long: { dateStyle: 'long', timeStyle: 'short' },
|
|
59
|
+
},
|
|
60
|
+
fr: {
|
|
61
|
+
short: { dateStyle: 'medium' },
|
|
62
|
+
long: { dateStyle: 'long', timeStyle: 'short' },
|
|
63
|
+
},
|
|
28
64
|
},
|
|
29
65
|
}))
|
package/i18n/locales/en.json
CHANGED
|
@@ -3,7 +3,34 @@
|
|
|
3
3
|
"save": "Save",
|
|
4
4
|
"cancel": "Cancel",
|
|
5
5
|
"retry": "Retry",
|
|
6
|
-
"actionFailed": "Action failed"
|
|
6
|
+
"actionFailed": "Action failed",
|
|
7
|
+
"close": "Close"
|
|
8
|
+
},
|
|
9
|
+
"nav": {
|
|
10
|
+
"menu": "Navigation menu",
|
|
11
|
+
"openMenu": "Open menu",
|
|
12
|
+
"closeMenu": "Close menu",
|
|
13
|
+
"commandBar": "Search or run a command…",
|
|
14
|
+
"create": "Create",
|
|
15
|
+
"buildPipeline": "Build a pipeline",
|
|
16
|
+
"repositories": "Repositories",
|
|
17
|
+
"addFromRepo": "Add from existing repo",
|
|
18
|
+
"bootstrapRepo": "Bootstrap repo",
|
|
19
|
+
"integrations": "Integrations",
|
|
20
|
+
"sandbox": "Sandbox",
|
|
21
|
+
"kaizen": "Kaizen",
|
|
22
|
+
"workspaceContext": "Workspace context",
|
|
23
|
+
"contextFragments": "Context fragments",
|
|
24
|
+
"configuration": "Configuration",
|
|
25
|
+
"workspaceSettings": "Workspace settings",
|
|
26
|
+
"modelConfiguration": "Model Configuration",
|
|
27
|
+
"accountSettings": "Account settings"
|
|
28
|
+
},
|
|
29
|
+
"board": {
|
|
30
|
+
"toolbar": {
|
|
31
|
+
"addService": "Add service",
|
|
32
|
+
"decisionWord": "decision | decisions"
|
|
33
|
+
}
|
|
7
34
|
},
|
|
8
35
|
"errors": {
|
|
9
36
|
"action": {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"common": {
|
|
3
|
+
"save": "Guardar",
|
|
4
|
+
"cancel": "Cancelar",
|
|
5
|
+
"retry": "Reintentar",
|
|
6
|
+
"actionFailed": "La acción falló",
|
|
7
|
+
"close": "Cerrar"
|
|
8
|
+
},
|
|
9
|
+
"nav": {
|
|
10
|
+
"menu": "Menú de navegación",
|
|
11
|
+
"openMenu": "Abrir menú",
|
|
12
|
+
"closeMenu": "Cerrar menú",
|
|
13
|
+
"commandBar": "Buscar o ejecutar un comando…",
|
|
14
|
+
"create": "Crear",
|
|
15
|
+
"buildPipeline": "Crear una canalización",
|
|
16
|
+
"repositories": "Repositorios",
|
|
17
|
+
"addFromRepo": "Añadir desde un repositorio existente",
|
|
18
|
+
"bootstrapRepo": "Inicializar repositorio",
|
|
19
|
+
"integrations": "Integraciones",
|
|
20
|
+
"sandbox": "Entorno de pruebas",
|
|
21
|
+
"kaizen": "Kaizen",
|
|
22
|
+
"workspaceContext": "Contexto del espacio de trabajo",
|
|
23
|
+
"contextFragments": "Fragmentos de contexto",
|
|
24
|
+
"configuration": "Configuración",
|
|
25
|
+
"workspaceSettings": "Ajustes del espacio de trabajo",
|
|
26
|
+
"modelConfiguration": "Configuración del modelo",
|
|
27
|
+
"accountSettings": "Ajustes de la cuenta"
|
|
28
|
+
},
|
|
29
|
+
"board": {
|
|
30
|
+
"toolbar": {
|
|
31
|
+
"addService": "Añadir servicio",
|
|
32
|
+
"decisionWord": "decisión | decisiones"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"errors": {
|
|
36
|
+
"action": {
|
|
37
|
+
"retryFailed": "El reintento falló",
|
|
38
|
+
"startFailed": "No se pudo iniciar",
|
|
39
|
+
"mergeFailed": "No se pudo fusionar",
|
|
40
|
+
"restartFailed": "No se pudo reiniciar"
|
|
41
|
+
},
|
|
42
|
+
"conflict": {
|
|
43
|
+
"title": {
|
|
44
|
+
"dependencies_unmet": "Bloqueado por dependencias",
|
|
45
|
+
"task_limit_reached": "Límite de concurrencia alcanzado",
|
|
46
|
+
"tester_infra_unsupported": "Infraestructura de pruebas no configurada",
|
|
47
|
+
"agent_backend_unconfigured": "Backend del agente no configurado",
|
|
48
|
+
"run_not_retryable": "La ejecución no se puede reintentar",
|
|
49
|
+
"no_pr_to_merge": "No hay PR para fusionar",
|
|
50
|
+
"github_not_connected": "GitHub no conectado",
|
|
51
|
+
"bootstrap_not_retryable": "La inicialización no se puede reintentar",
|
|
52
|
+
"bootstrap_reference_missing": "La arquitectura de referencia ha desaparecido"
|
|
53
|
+
},
|
|
54
|
+
"fallbackMessage": "Esta acción entra en conflicto con el estado actual.",
|
|
55
|
+
"providersUnconfigured": {
|
|
56
|
+
"title": "No hay proveedor de IA para este modelo",
|
|
57
|
+
"body": "No hay ningún proveedor configurado para {models}. Añade una clave de proveedor, conecta una suscripción o activa Cloudflare AI para ejecutarlo.",
|
|
58
|
+
"action": "Configurar IA"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"common": {
|
|
3
|
+
"save": "Enregistrer",
|
|
4
|
+
"cancel": "Annuler",
|
|
5
|
+
"retry": "Réessayer",
|
|
6
|
+
"actionFailed": "Échec de l’action",
|
|
7
|
+
"close": "Fermer"
|
|
8
|
+
},
|
|
9
|
+
"nav": {
|
|
10
|
+
"menu": "Menu de navigation",
|
|
11
|
+
"openMenu": "Ouvrir le menu",
|
|
12
|
+
"closeMenu": "Fermer le menu",
|
|
13
|
+
"commandBar": "Rechercher ou exécuter une commande…",
|
|
14
|
+
"create": "Créer",
|
|
15
|
+
"buildPipeline": "Créer un pipeline",
|
|
16
|
+
"repositories": "Dépôts",
|
|
17
|
+
"addFromRepo": "Ajouter depuis un dépôt existant",
|
|
18
|
+
"bootstrapRepo": "Initialiser un dépôt",
|
|
19
|
+
"integrations": "Intégrations",
|
|
20
|
+
"sandbox": "Bac à sable",
|
|
21
|
+
"kaizen": "Kaizen",
|
|
22
|
+
"workspaceContext": "Contexte de l’espace de travail",
|
|
23
|
+
"contextFragments": "Fragments de contexte",
|
|
24
|
+
"configuration": "Configuration",
|
|
25
|
+
"workspaceSettings": "Paramètres de l’espace de travail",
|
|
26
|
+
"modelConfiguration": "Configuration du modèle",
|
|
27
|
+
"accountSettings": "Paramètres du compte"
|
|
28
|
+
},
|
|
29
|
+
"board": {
|
|
30
|
+
"toolbar": {
|
|
31
|
+
"addService": "Ajouter un service",
|
|
32
|
+
"decisionWord": "décision | décisions"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"errors": {
|
|
36
|
+
"action": {
|
|
37
|
+
"retryFailed": "Échec de la nouvelle tentative",
|
|
38
|
+
"startFailed": "Échec du démarrage",
|
|
39
|
+
"mergeFailed": "Échec de la fusion",
|
|
40
|
+
"restartFailed": "Échec du redémarrage"
|
|
41
|
+
},
|
|
42
|
+
"conflict": {
|
|
43
|
+
"title": {
|
|
44
|
+
"dependencies_unmet": "Bloqué par des dépendances",
|
|
45
|
+
"task_limit_reached": "Limite de concurrence atteinte",
|
|
46
|
+
"tester_infra_unsupported": "Infrastructure de test non configurée",
|
|
47
|
+
"agent_backend_unconfigured": "Backend de l’agent non configuré",
|
|
48
|
+
"run_not_retryable": "L’exécution ne peut pas être relancée",
|
|
49
|
+
"no_pr_to_merge": "Aucune PR à fusionner",
|
|
50
|
+
"github_not_connected": "GitHub non connecté",
|
|
51
|
+
"bootstrap_not_retryable": "L’initialisation ne peut pas être relancée",
|
|
52
|
+
"bootstrap_reference_missing": "L’architecture de référence a disparu"
|
|
53
|
+
},
|
|
54
|
+
"fallbackMessage": "Cette action est en conflit avec l’état actuel.",
|
|
55
|
+
"providersUnconfigured": {
|
|
56
|
+
"title": "Aucun fournisseur d’IA pour ce modèle",
|
|
57
|
+
"body": "Aucun fournisseur n’est configuré pour {models}. Ajoutez une clé de fournisseur, connectez un abonnement ou activez Cloudflare AI pour l’exécuter.",
|
|
58
|
+
"action": "Configurer l’IA"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"common": {
|
|
3
|
+
"save": "Zapisz",
|
|
4
|
+
"cancel": "Anuluj",
|
|
5
|
+
"retry": "Ponów",
|
|
6
|
+
"actionFailed": "Akcja nie powiodła się",
|
|
7
|
+
"close": "Zamknij"
|
|
8
|
+
},
|
|
9
|
+
"nav": {
|
|
10
|
+
"menu": "Menu nawigacji",
|
|
11
|
+
"openMenu": "Otwórz menu",
|
|
12
|
+
"closeMenu": "Zamknij menu",
|
|
13
|
+
"commandBar": "Wyszukaj lub uruchom polecenie…",
|
|
14
|
+
"create": "Utwórz",
|
|
15
|
+
"buildPipeline": "Zbuduj potok",
|
|
16
|
+
"repositories": "Repozytoria",
|
|
17
|
+
"addFromRepo": "Dodaj z istniejącego repozytorium",
|
|
18
|
+
"bootstrapRepo": "Zainicjuj repozytorium",
|
|
19
|
+
"integrations": "Integracje",
|
|
20
|
+
"sandbox": "Piaskownica",
|
|
21
|
+
"kaizen": "Kaizen",
|
|
22
|
+
"workspaceContext": "Kontekst obszaru roboczego",
|
|
23
|
+
"contextFragments": "Fragmenty kontekstu",
|
|
24
|
+
"configuration": "Konfiguracja",
|
|
25
|
+
"workspaceSettings": "Ustawienia obszaru roboczego",
|
|
26
|
+
"modelConfiguration": "Konfiguracja modelu",
|
|
27
|
+
"accountSettings": "Ustawienia konta"
|
|
28
|
+
},
|
|
29
|
+
"board": {
|
|
30
|
+
"toolbar": {
|
|
31
|
+
"addService": "Dodaj usługę",
|
|
32
|
+
"decisionWord": "decyzja | decyzje | decyzji"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"errors": {
|
|
36
|
+
"action": {
|
|
37
|
+
"retryFailed": "Ponowienie nie powiodło się",
|
|
38
|
+
"startFailed": "Nie udało się uruchomić",
|
|
39
|
+
"mergeFailed": "Nie udało się scalić",
|
|
40
|
+
"restartFailed": "Nie udało się ponownie uruchomić"
|
|
41
|
+
},
|
|
42
|
+
"conflict": {
|
|
43
|
+
"title": {
|
|
44
|
+
"dependencies_unmet": "Zablokowane przez zależności",
|
|
45
|
+
"task_limit_reached": "Osiągnięto limit współbieżności",
|
|
46
|
+
"tester_infra_unsupported": "Infrastruktura testowa nie jest skonfigurowana",
|
|
47
|
+
"agent_backend_unconfigured": "Backend agenta nie jest skonfigurowany",
|
|
48
|
+
"run_not_retryable": "Uruchomienia nie można ponowić",
|
|
49
|
+
"no_pr_to_merge": "Brak PR do scalenia",
|
|
50
|
+
"github_not_connected": "GitHub nie jest połączony",
|
|
51
|
+
"bootstrap_not_retryable": "Inicjalizacji nie można ponowić",
|
|
52
|
+
"bootstrap_reference_missing": "Architektura referencyjna zniknęła"
|
|
53
|
+
},
|
|
54
|
+
"fallbackMessage": "Ta akcja jest sprzeczna z bieżącym stanem.",
|
|
55
|
+
"providersUnconfigured": {
|
|
56
|
+
"title": "Brak dostawcy AI dla tego modelu",
|
|
57
|
+
"body": "Dla {models} nie skonfigurowano żadnego dostawcy. Dodaj klucz dostawcy, połącz subskrypcję lub włącz Cloudflare AI, aby go uruchomić.",
|
|
58
|
+
"action": "Skonfiguruj AI"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"common": {
|
|
3
|
+
"save": "Зберегти",
|
|
4
|
+
"cancel": "Скасувати",
|
|
5
|
+
"retry": "Повторити",
|
|
6
|
+
"actionFailed": "Не вдалося виконати дію",
|
|
7
|
+
"close": "Закрити"
|
|
8
|
+
},
|
|
9
|
+
"nav": {
|
|
10
|
+
"menu": "Меню навігації",
|
|
11
|
+
"openMenu": "Відкрити меню",
|
|
12
|
+
"closeMenu": "Закрити меню",
|
|
13
|
+
"commandBar": "Знайти або виконати команду…",
|
|
14
|
+
"create": "Створити",
|
|
15
|
+
"buildPipeline": "Створити конвеєр",
|
|
16
|
+
"repositories": "Репозиторії",
|
|
17
|
+
"addFromRepo": "Додати з наявного репозиторію",
|
|
18
|
+
"bootstrapRepo": "Ініціалізувати репозиторій",
|
|
19
|
+
"integrations": "Інтеграції",
|
|
20
|
+
"sandbox": "Пісочниця",
|
|
21
|
+
"kaizen": "Kaizen",
|
|
22
|
+
"workspaceContext": "Контекст робочого простору",
|
|
23
|
+
"contextFragments": "Фрагменти контексту",
|
|
24
|
+
"configuration": "Конфігурація",
|
|
25
|
+
"workspaceSettings": "Налаштування робочого простору",
|
|
26
|
+
"modelConfiguration": "Конфігурація моделі",
|
|
27
|
+
"accountSettings": "Налаштування облікового запису"
|
|
28
|
+
},
|
|
29
|
+
"board": {
|
|
30
|
+
"toolbar": {
|
|
31
|
+
"addService": "Додати сервіс",
|
|
32
|
+
"decisionWord": "рішення | рішення | рішень"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"errors": {
|
|
36
|
+
"action": {
|
|
37
|
+
"retryFailed": "Не вдалося повторити",
|
|
38
|
+
"startFailed": "Не вдалося запустити",
|
|
39
|
+
"mergeFailed": "Не вдалося злити",
|
|
40
|
+
"restartFailed": "Не вдалося перезапустити"
|
|
41
|
+
},
|
|
42
|
+
"conflict": {
|
|
43
|
+
"title": {
|
|
44
|
+
"dependencies_unmet": "Заблоковано залежностями",
|
|
45
|
+
"task_limit_reached": "Досягнуто ліміту одночасності",
|
|
46
|
+
"tester_infra_unsupported": "Тестову інфраструктуру не налаштовано",
|
|
47
|
+
"agent_backend_unconfigured": "Бекенд агента не налаштовано",
|
|
48
|
+
"run_not_retryable": "Запуск неможливо повторити",
|
|
49
|
+
"no_pr_to_merge": "Немає PR для злиття",
|
|
50
|
+
"github_not_connected": "GitHub не підключено",
|
|
51
|
+
"bootstrap_not_retryable": "Ініціалізацію неможливо повторити",
|
|
52
|
+
"bootstrap_reference_missing": "Еталонна архітектура зникла"
|
|
53
|
+
},
|
|
54
|
+
"fallbackMessage": "Ця дія суперечить поточному стану.",
|
|
55
|
+
"providersUnconfigured": {
|
|
56
|
+
"title": "Немає постачальника ШІ для цієї моделі",
|
|
57
|
+
"body": "Для {models} не налаштовано жодного постачальника. Додайте ключ постачальника, підключіть підписку або увімкніть Cloudflare AI, щоб запустити її.",
|
|
58
|
+
"action": "Налаштувати ШІ"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
package/nuxt.config.ts
CHANGED
|
@@ -46,7 +46,13 @@ export default defineNuxtConfig({
|
|
|
46
46
|
// Pure SPA (`ssr: false`): a single in-app locale, no URL-prefix routing.
|
|
47
47
|
strategy: 'no_prefix',
|
|
48
48
|
defaultLocale: 'en',
|
|
49
|
-
locales: [
|
|
49
|
+
locales: [
|
|
50
|
+
{ code: 'en', language: 'en-US', file: 'en.json', name: 'English' },
|
|
51
|
+
{ code: 'es', language: 'es-ES', file: 'es.json', name: 'Español' },
|
|
52
|
+
{ code: 'pl', language: 'pl-PL', file: 'pl.json', name: 'Polski' },
|
|
53
|
+
{ code: 'uk', language: 'uk-UA', file: 'uk.json', name: 'Українська' },
|
|
54
|
+
{ code: 'fr', language: 'fr-FR', file: 'fr.json', name: 'Français' },
|
|
55
|
+
],
|
|
50
56
|
vueI18n: 'i18n.config.ts',
|
|
51
57
|
experimental: {
|
|
52
58
|
// Generate types from the `en` messages so an unknown `$t`/`t` key is a `nuxt
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.0",
|
|
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",
|