@cat-factory/app 0.47.4 → 0.47.6
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/documents/ContextDocumentPicker.vue +17 -10
- package/app/components/documents/DocumentImportModal.vue +26 -13
- package/app/components/documents/DocumentSourceConnectModal.vue +15 -11
- package/app/components/documents/SpawnPreviewModal.vue +27 -11
- package/app/components/documents/TaskContextDocs.vue +9 -6
- package/app/components/github/AddServiceFromRepoModal.vue +40 -29
- package/app/components/github/GitHubConnect.vue +30 -19
- package/app/components/github/GitHubOnboarding.vue +5 -6
- package/app/components/github/GitHubPanel.vue +101 -63
- package/app/components/github/RepoTreeBrowser.vue +13 -6
- package/app/components/slack/SlackPanel.vue +68 -43
- package/app/components/tasks/ContextIssuePicker.vue +21 -14
- package/app/components/tasks/TaskContextIssues.vue +9 -6
- package/app/components/tasks/TaskImportModal.vue +38 -26
- package/app/components/tasks/TaskSourceConnectModal.vue +21 -16
- package/i18n/locales/en.json +333 -0
- package/i18n/locales/es.json +330 -0
- package/i18n/locales/fr.json +330 -0
- package/i18n/locales/pl.json +330 -0
- package/i18n/locales/uk.json +330 -0
- package/package.json +2 -2
|
@@ -10,6 +10,7 @@ import type { TaskSearchResult, TaskSourceKind } from '~/types/domain'
|
|
|
10
10
|
import type { AddTaskPrefill } from '~/stores/ui'
|
|
11
11
|
import IntegrationBackTitle from '~/components/layout/IntegrationBackTitle.vue'
|
|
12
12
|
|
|
13
|
+
const { t } = useI18n()
|
|
13
14
|
const ui = useUiStore()
|
|
14
15
|
const tasks = useTasksStore()
|
|
15
16
|
const board = useBoardStore()
|
|
@@ -30,7 +31,7 @@ const importing = ref(false)
|
|
|
30
31
|
// When opened from a service frame the modal is the "create a task from an issue"
|
|
31
32
|
// surface; opened standalone it's the general tracker-issue browser/importer.
|
|
32
33
|
const title = computed(() =>
|
|
33
|
-
ui.taskImport?.containerId ? '
|
|
34
|
+
ui.taskImport?.containerId ? t('tasks.import.titleCreate') : t('tasks.import.titleBrowse'),
|
|
34
35
|
)
|
|
35
36
|
|
|
36
37
|
const sourceItems = computed(() =>
|
|
@@ -152,10 +153,13 @@ async function doImport() {
|
|
|
152
153
|
try {
|
|
153
154
|
const task = await tasks.importTask(source.value, value)
|
|
154
155
|
ref_.value = ''
|
|
155
|
-
toast.add({
|
|
156
|
+
toast.add({
|
|
157
|
+
title: t('tasks.import.imported', { title: task.title }),
|
|
158
|
+
icon: 'i-lucide-file-down',
|
|
159
|
+
})
|
|
156
160
|
} catch (e) {
|
|
157
161
|
toast.add({
|
|
158
|
-
title: '
|
|
162
|
+
title: t('tasks.import.importFailed'),
|
|
159
163
|
description: e instanceof Error ? e.message : String(e),
|
|
160
164
|
icon: 'i-lucide-triangle-alert',
|
|
161
165
|
color: 'error',
|
|
@@ -178,13 +182,13 @@ async function doSpawnEpic() {
|
|
|
178
182
|
ui.closeTaskImport()
|
|
179
183
|
ui.select(epic.id)
|
|
180
184
|
toast.add({
|
|
181
|
-
title:
|
|
182
|
-
description:
|
|
185
|
+
title: t('tasks.import.epicSpawned', { title: epic.title }),
|
|
186
|
+
description: t('tasks.import.epicChildren', { count: spawned.length }, spawned.length),
|
|
183
187
|
icon: 'i-lucide-layers',
|
|
184
188
|
})
|
|
185
189
|
} catch (e) {
|
|
186
190
|
toast.add({
|
|
187
|
-
title: '
|
|
191
|
+
title: t('tasks.import.epicFailed'),
|
|
188
192
|
description: e instanceof Error ? e.message : String(e),
|
|
189
193
|
icon: 'i-lucide-triangle-alert',
|
|
190
194
|
color: 'error',
|
|
@@ -204,7 +208,7 @@ async function doSpawnEpic() {
|
|
|
204
208
|
<!-- Empty state: no source offered (none connected/installed, or all disabled) -->
|
|
205
209
|
<div v-if="!tasks.anyOffered" class="space-y-3 text-center">
|
|
206
210
|
<UIcon name="i-lucide-plug" class="mx-auto h-8 w-8 text-slate-500" />
|
|
207
|
-
<p class="text-sm text-slate-400">
|
|
211
|
+
<p class="text-sm text-slate-400">{{ t('tasks.import.connectFirst') }}</p>
|
|
208
212
|
<div class="flex justify-center gap-2">
|
|
209
213
|
<UButton
|
|
210
214
|
v-for="s in tasks.sources"
|
|
@@ -214,19 +218,23 @@ async function doSpawnEpic() {
|
|
|
214
218
|
:icon="s.icon"
|
|
215
219
|
@click="ui.openTaskConnect(s.source)"
|
|
216
220
|
>
|
|
217
|
-
{{
|
|
221
|
+
{{
|
|
222
|
+
s.available
|
|
223
|
+
? t('tasks.import.enableSource', { label: s.label })
|
|
224
|
+
: t('tasks.import.connectSource', { label: s.label })
|
|
225
|
+
}}
|
|
218
226
|
</UButton>
|
|
219
227
|
</div>
|
|
220
228
|
</div>
|
|
221
229
|
|
|
222
230
|
<!-- Main form -->
|
|
223
231
|
<div v-else class="space-y-4">
|
|
224
|
-
<UFormField v-if="sourceItems.length > 1" label="
|
|
232
|
+
<UFormField v-if="sourceItems.length > 1" :label="t('tasks.import.source')">
|
|
225
233
|
<USelect v-model="source" :items="sourceItems" class="w-full" />
|
|
226
234
|
</UFormField>
|
|
227
235
|
|
|
228
236
|
<div class="flex items-end gap-2">
|
|
229
|
-
<UFormField :label="descriptor?.refLabel ?? '
|
|
237
|
+
<UFormField :label="descriptor?.refLabel ?? t('tasks.import.refLabel')" class="flex-1">
|
|
230
238
|
<UInput
|
|
231
239
|
v-model="ref_"
|
|
232
240
|
:placeholder="descriptor?.refPlaceholder"
|
|
@@ -241,7 +249,7 @@ async function doSpawnEpic() {
|
|
|
241
249
|
:disabled="!ref_.trim()"
|
|
242
250
|
@click="doImport"
|
|
243
251
|
>
|
|
244
|
-
|
|
252
|
+
{{ t('tasks.import.import') }}
|
|
245
253
|
</UButton>
|
|
246
254
|
<UButton
|
|
247
255
|
color="primary"
|
|
@@ -251,12 +259,12 @@ async function doSpawnEpic() {
|
|
|
251
259
|
:disabled="!ref_.trim() || !containerId"
|
|
252
260
|
:title="
|
|
253
261
|
containerId
|
|
254
|
-
? '
|
|
255
|
-
: '
|
|
262
|
+
? t('tasks.import.asEpicTitleReady')
|
|
263
|
+
: t('tasks.import.asEpicTitleNeedsContainer')
|
|
256
264
|
"
|
|
257
265
|
@click="doSpawnEpic"
|
|
258
266
|
>
|
|
259
|
-
|
|
267
|
+
{{ t('tasks.import.asEpic') }}
|
|
260
268
|
</UButton>
|
|
261
269
|
</div>
|
|
262
270
|
|
|
@@ -264,25 +272,25 @@ async function doSpawnEpic() {
|
|
|
264
272
|
"Create tasks in" selector below covers the search-results case). -->
|
|
265
273
|
<UFormField
|
|
266
274
|
v-if="containerItems.length && !freshHits.length"
|
|
267
|
-
label="
|
|
275
|
+
:label="t('tasks.import.epicChildrenContainer')"
|
|
268
276
|
class="w-72"
|
|
269
277
|
>
|
|
270
278
|
<USelect
|
|
271
279
|
v-model="containerId"
|
|
272
280
|
:items="containerItems"
|
|
273
|
-
placeholder="
|
|
281
|
+
:placeholder="t('tasks.import.pickContainer')"
|
|
274
282
|
class="w-full"
|
|
275
283
|
/>
|
|
276
284
|
</UFormField>
|
|
277
285
|
|
|
278
286
|
<!-- Browse: search the tracker by title so an issue can be turned into a
|
|
279
287
|
task without knowing its key. -->
|
|
280
|
-
<UFormField v-if="searchable" label="
|
|
288
|
+
<UFormField v-if="searchable" :label="t('tasks.import.searchIssues')">
|
|
281
289
|
<UInput
|
|
282
290
|
v-model="searchQuery"
|
|
283
291
|
:icon="searching ? 'i-lucide-loader-circle' : 'i-lucide-search'"
|
|
284
292
|
:ui="{ leadingIcon: searching ? 'animate-spin' : '' }"
|
|
285
|
-
placeholder="
|
|
293
|
+
:placeholder="t('tasks.import.searchPlaceholder')"
|
|
286
294
|
class="w-full"
|
|
287
295
|
/>
|
|
288
296
|
</UFormField>
|
|
@@ -290,13 +298,13 @@ async function doSpawnEpic() {
|
|
|
290
298
|
<!-- Shared target container for every "Create task" action below. -->
|
|
291
299
|
<UFormField
|
|
292
300
|
v-if="containerItems.length && freshHits.length"
|
|
293
|
-
label="
|
|
301
|
+
:label="t('tasks.import.createTasksIn')"
|
|
294
302
|
class="w-72"
|
|
295
303
|
>
|
|
296
304
|
<USelect
|
|
297
305
|
v-model="containerId"
|
|
298
306
|
:items="containerItems"
|
|
299
|
-
placeholder="
|
|
307
|
+
:placeholder="t('tasks.import.pickContainer')"
|
|
300
308
|
class="w-full"
|
|
301
309
|
/>
|
|
302
310
|
</UFormField>
|
|
@@ -304,17 +312,17 @@ async function doSpawnEpic() {
|
|
|
304
312
|
v-else-if="!containerItems.length && freshHits.length"
|
|
305
313
|
class="text-[11px] text-slate-500"
|
|
306
314
|
>
|
|
307
|
-
|
|
315
|
+
{{ t('tasks.import.needFrameFirst') }}
|
|
308
316
|
</p>
|
|
309
317
|
|
|
310
318
|
<!-- Search results (not yet imported): click a hit to create a task from it
|
|
311
319
|
(opens the prefilled add-task form); the icon button views it on GitHub. -->
|
|
312
320
|
<div v-if="searchError" class="text-[11px] text-amber-400">
|
|
313
|
-
|
|
321
|
+
{{ t('tasks.import.searchFailed', { error: searchError }) }}
|
|
314
322
|
</div>
|
|
315
323
|
<div v-if="freshHits.length" class="space-y-2">
|
|
316
324
|
<h3 class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
317
|
-
|
|
325
|
+
{{ t('tasks.import.searchResults') }}
|
|
318
326
|
</h3>
|
|
319
327
|
<div
|
|
320
328
|
v-for="hit in freshHits"
|
|
@@ -325,7 +333,11 @@ async function doSpawnEpic() {
|
|
|
325
333
|
type="button"
|
|
326
334
|
class="min-w-0 flex-1 text-left disabled:cursor-not-allowed disabled:opacity-60"
|
|
327
335
|
:disabled="!containerId"
|
|
328
|
-
:title="
|
|
336
|
+
:title="
|
|
337
|
+
containerId
|
|
338
|
+
? t('tasks.import.createFromIssue')
|
|
339
|
+
: t('tasks.import.pickContainerFirst')
|
|
340
|
+
"
|
|
329
341
|
@click="selectIssue(hit, true)"
|
|
330
342
|
>
|
|
331
343
|
<span class="block truncate text-sm font-medium text-white">
|
|
@@ -347,7 +359,7 @@ async function doSpawnEpic() {
|
|
|
347
359
|
:to="hit.url"
|
|
348
360
|
target="_blank"
|
|
349
361
|
rel="noopener"
|
|
350
|
-
:aria-label="
|
|
362
|
+
:aria-label="t('tasks.import.viewOnGitHub', { id: hit.externalId })"
|
|
351
363
|
/>
|
|
352
364
|
</div>
|
|
353
365
|
</div>
|
|
@@ -357,7 +369,7 @@ async function doSpawnEpic() {
|
|
|
357
369
|
v-if="!freshHits.length && !searchQuery.trim()"
|
|
358
370
|
class="text-center text-xs text-slate-500"
|
|
359
371
|
>
|
|
360
|
-
|
|
372
|
+
{{ t('tasks.import.emptyHint') }}
|
|
361
373
|
</p>
|
|
362
374
|
</div>
|
|
363
375
|
</template>
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// before that.
|
|
14
14
|
import IntegrationBackTitle from '~/components/layout/IntegrationBackTitle.vue'
|
|
15
15
|
|
|
16
|
+
const { t } = useI18n()
|
|
16
17
|
const ui = useUiStore()
|
|
17
18
|
const tasks = useTasksStore()
|
|
18
19
|
const toast = useToast()
|
|
@@ -58,7 +59,7 @@ async function submit() {
|
|
|
58
59
|
try {
|
|
59
60
|
await tasks.connect(source.value, credentials)
|
|
60
61
|
toast.add({
|
|
61
|
-
title:
|
|
62
|
+
title: t('tasks.connect.connectedToast', { label: descriptor.value!.label }),
|
|
62
63
|
icon: 'i-lucide-check',
|
|
63
64
|
color: 'success',
|
|
64
65
|
})
|
|
@@ -66,7 +67,7 @@ async function submit() {
|
|
|
66
67
|
await tasks.probe()
|
|
67
68
|
} catch (e) {
|
|
68
69
|
toast.add({
|
|
69
|
-
title: '
|
|
70
|
+
title: t('tasks.connect.connectFailed'),
|
|
70
71
|
description: e instanceof Error ? e.message : String(e),
|
|
71
72
|
icon: 'i-lucide-triangle-alert',
|
|
72
73
|
color: 'error',
|
|
@@ -81,7 +82,9 @@ async function disconnect() {
|
|
|
81
82
|
await tasks.disconnect(source.value)
|
|
82
83
|
await tasks.probe()
|
|
83
84
|
toast.add({
|
|
84
|
-
title:
|
|
85
|
+
title: t('tasks.connect.disconnectedToast', {
|
|
86
|
+
label: descriptor.value?.label ?? t('tasks.connect.sourceFallback'),
|
|
87
|
+
}),
|
|
85
88
|
icon: 'i-lucide-unplug',
|
|
86
89
|
})
|
|
87
90
|
ui.closeTaskConnect()
|
|
@@ -94,7 +97,7 @@ async function toggleEnabled(enabled: boolean) {
|
|
|
94
97
|
await tasks.setEnabled(source.value, enabled)
|
|
95
98
|
} catch (e) {
|
|
96
99
|
toast.add({
|
|
97
|
-
title: '
|
|
100
|
+
title: t('tasks.connect.updateFailed'),
|
|
98
101
|
description: e instanceof Error ? e.message : String(e),
|
|
99
102
|
icon: 'i-lucide-triangle-alert',
|
|
100
103
|
color: 'error',
|
|
@@ -106,25 +109,23 @@ async function toggleEnabled(enabled: boolean) {
|
|
|
106
109
|
</script>
|
|
107
110
|
|
|
108
111
|
<template>
|
|
109
|
-
<UModal v-model:open="open" :title="descriptor?.label ?? '
|
|
112
|
+
<UModal v-model:open="open" :title="descriptor?.label ?? t('tasks.connect.title')">
|
|
110
113
|
<template #title>
|
|
111
|
-
<IntegrationBackTitle :title="descriptor?.label ?? '
|
|
114
|
+
<IntegrationBackTitle :title="descriptor?.label ?? t('tasks.connect.title')" @back="back" />
|
|
112
115
|
</template>
|
|
113
116
|
<template #body>
|
|
114
117
|
<div v-if="descriptor" class="space-y-4">
|
|
115
118
|
<p class="text-sm text-slate-400">
|
|
116
|
-
{{ descriptor.label }}
|
|
119
|
+
{{ t('tasks.connect.intro', { label: descriptor.label }) }}
|
|
117
120
|
</p>
|
|
118
121
|
|
|
119
122
|
<!-- Credentialless source (GitHub Issues): no form, just the on/off toggle. -->
|
|
120
123
|
<template v-if="credentialless">
|
|
121
124
|
<p class="text-[11px] text-slate-500">
|
|
122
|
-
|
|
123
|
-
credentials to enter.
|
|
125
|
+
{{ t('tasks.connect.credentialless') }}
|
|
124
126
|
</p>
|
|
125
127
|
<p v-if="!available" class="text-[11px] text-amber-400">
|
|
126
|
-
|
|
127
|
-
{{ descriptor.label }}.
|
|
128
|
+
{{ t('tasks.connect.installAppHint', { label: descriptor.label }) }}
|
|
128
129
|
</p>
|
|
129
130
|
</template>
|
|
130
131
|
|
|
@@ -145,7 +146,11 @@ async function toggleEnabled(enabled: boolean) {
|
|
|
145
146
|
</UFormField>
|
|
146
147
|
</div>
|
|
147
148
|
<p v-else class="text-[11px] text-slate-500">
|
|
148
|
-
|
|
149
|
+
{{
|
|
150
|
+
connection?.label
|
|
151
|
+
? t('tasks.connect.connectedTo', { label: connection.label })
|
|
152
|
+
: t('tasks.connect.connected')
|
|
153
|
+
}}
|
|
149
154
|
</p>
|
|
150
155
|
|
|
151
156
|
<!-- The per-workspace on/off toggle, available once the source is usable. -->
|
|
@@ -154,9 +159,9 @@ async function toggleEnabled(enabled: boolean) {
|
|
|
154
159
|
class="flex items-center justify-between gap-2 rounded-md border border-slate-800 px-3 py-2"
|
|
155
160
|
>
|
|
156
161
|
<div class="text-sm">
|
|
157
|
-
<div class="font-medium text-slate-200">
|
|
162
|
+
<div class="font-medium text-slate-200">{{ t('tasks.connect.offerToWorkspace') }}</div>
|
|
158
163
|
<div class="text-[11px] text-slate-500">
|
|
159
|
-
|
|
164
|
+
{{ t('tasks.connect.offerHint', { label: descriptor.label }) }}
|
|
160
165
|
</div>
|
|
161
166
|
</div>
|
|
162
167
|
<USwitch
|
|
@@ -174,7 +179,7 @@ async function toggleEnabled(enabled: boolean) {
|
|
|
174
179
|
icon="i-lucide-unplug"
|
|
175
180
|
@click="disconnect"
|
|
176
181
|
>
|
|
177
|
-
|
|
182
|
+
{{ t('tasks.connect.disconnect') }}
|
|
178
183
|
</UButton>
|
|
179
184
|
<div v-else />
|
|
180
185
|
<UButton
|
|
@@ -185,7 +190,7 @@ async function toggleEnabled(enabled: boolean) {
|
|
|
185
190
|
:disabled="!canSubmit"
|
|
186
191
|
@click="submit"
|
|
187
192
|
>
|
|
188
|
-
{{ connected ? '
|
|
193
|
+
{{ connected ? t('tasks.connect.updateConnection') : t('tasks.connect.connect') }}
|
|
189
194
|
</UButton>
|
|
190
195
|
</div>
|
|
191
196
|
</div>
|
package/i18n/locales/en.json
CHANGED
|
@@ -1834,5 +1834,338 @@
|
|
|
1834
1834
|
}
|
|
1835
1835
|
}
|
|
1836
1836
|
}
|
|
1837
|
+
},
|
|
1838
|
+
"github": {
|
|
1839
|
+
"onboarding": {
|
|
1840
|
+
"title": "Connect cat-factory to GitHub",
|
|
1841
|
+
"intro": "cat-factory works by opening pull requests on your repositories. Install the GitHub App on your account or organization to continue, and you can grant it every repository or pick a subset.",
|
|
1842
|
+
"signedInAs": "Signed in as {login}",
|
|
1843
|
+
"signOut": "Sign out"
|
|
1844
|
+
},
|
|
1845
|
+
"connect": {
|
|
1846
|
+
"yourInstallations": "Your installations",
|
|
1847
|
+
"refresh": "Refresh",
|
|
1848
|
+
"lookingForInstallations": "Looking for installations…",
|
|
1849
|
+
"noInstallations": "No installations found for the App yet. Install it below, or connect by ID.",
|
|
1850
|
+
"installationMeta": "{targetType} · installation {id}",
|
|
1851
|
+
"linked": "linked",
|
|
1852
|
+
"linkedTitle": "Already linked to this workspace",
|
|
1853
|
+
"inUse": "in use",
|
|
1854
|
+
"inUseTitle": "Already connected to another workspace",
|
|
1855
|
+
"connect": "Connect",
|
|
1856
|
+
"or": "or",
|
|
1857
|
+
"installApp": "Install GitHub App",
|
|
1858
|
+
"orConnectById": "or connect by ID",
|
|
1859
|
+
"installationId": "Installation ID",
|
|
1860
|
+
"toast": {
|
|
1861
|
+
"connected": "GitHub connected"
|
|
1862
|
+
},
|
|
1863
|
+
"errors": {
|
|
1864
|
+
"listInstallations": "Could not list GitHub installations",
|
|
1865
|
+
"startInstall": "Could not start the GitHub App install",
|
|
1866
|
+
"connect": "Could not connect"
|
|
1867
|
+
}
|
|
1868
|
+
},
|
|
1869
|
+
"panel": {
|
|
1870
|
+
"connectIntro": "Connect a GitHub App installation to back board blocks with repositories, browse pull requests and issues, and let agents push branches and open PRs.",
|
|
1871
|
+
"installationMeta": "{targetType} · installation {id}",
|
|
1872
|
+
"resync": "Resync",
|
|
1873
|
+
"backfill": "Backfill",
|
|
1874
|
+
"disconnect": "Disconnect",
|
|
1875
|
+
"loading": "Loading…",
|
|
1876
|
+
"tabs": {
|
|
1877
|
+
"repos": "Repositories",
|
|
1878
|
+
"pulls": "Pull requests",
|
|
1879
|
+
"issues": "Issues"
|
|
1880
|
+
},
|
|
1881
|
+
"linkedToBoard": "Linked to this board",
|
|
1882
|
+
"manageRepos": "Manage repos",
|
|
1883
|
+
"manageHint": "Pick the repositories this board should track. The GitHub connection is shared across the account, and each board links its own repos.",
|
|
1884
|
+
"loadingRepos": "Loading repositories…",
|
|
1885
|
+
"noAvailableRepos": "The installation can't access any repositories yet.",
|
|
1886
|
+
"private": "private",
|
|
1887
|
+
"saveSelection": "Save selection",
|
|
1888
|
+
"noLinkedRepos": "No repositories linked yet. Use Manage repos to pick which repositories this board tracks.",
|
|
1889
|
+
"open": "Open",
|
|
1890
|
+
"protected": "protected",
|
|
1891
|
+
"newBranch": "New branch",
|
|
1892
|
+
"fromSha": "From SHA",
|
|
1893
|
+
"commitShaPlaceholder": "commit sha",
|
|
1894
|
+
"createBranch": "Create branch",
|
|
1895
|
+
"openPr": "Open PR",
|
|
1896
|
+
"repository": "Repository",
|
|
1897
|
+
"chooseRepository": "Choose a repository",
|
|
1898
|
+
"prTitle": "Title",
|
|
1899
|
+
"headBranch": "Head branch",
|
|
1900
|
+
"baseBranch": "Base branch",
|
|
1901
|
+
"openPullRequest": "Open pull request",
|
|
1902
|
+
"noPulls": "No pull requests synced.",
|
|
1903
|
+
"mergePr": "Merge pull request",
|
|
1904
|
+
"noIssues": "No issues synced.",
|
|
1905
|
+
"prState": {
|
|
1906
|
+
"open": "open",
|
|
1907
|
+
"closed": "closed",
|
|
1908
|
+
"merged": "merged"
|
|
1909
|
+
},
|
|
1910
|
+
"issueState": {
|
|
1911
|
+
"open": "open",
|
|
1912
|
+
"closed": "closed"
|
|
1913
|
+
},
|
|
1914
|
+
"toast": {
|
|
1915
|
+
"disconnected": "GitHub disconnected",
|
|
1916
|
+
"resync": "Resync {status}",
|
|
1917
|
+
"reposUpdated": "Linked repositories updated",
|
|
1918
|
+
"branchCreated": "Branch {name} created",
|
|
1919
|
+
"prOpened": "Pull request opened",
|
|
1920
|
+
"prMerged": "PR #{number} merged"
|
|
1921
|
+
},
|
|
1922
|
+
"errors": {
|
|
1923
|
+
"disconnect": "Could not disconnect",
|
|
1924
|
+
"resync": "Could not resync",
|
|
1925
|
+
"loadRepos": "Could not load repositories",
|
|
1926
|
+
"updateRepos": "Could not update repositories",
|
|
1927
|
+
"loadBranches": "Could not load branches",
|
|
1928
|
+
"createBranch": "Could not create branch",
|
|
1929
|
+
"openPr": "Could not open pull request",
|
|
1930
|
+
"merge": "Could not merge"
|
|
1931
|
+
}
|
|
1932
|
+
},
|
|
1933
|
+
"addService": {
|
|
1934
|
+
"title": "Add a service from a repository",
|
|
1935
|
+
"intro": "Pick an existing GitHub repository to add as a board service. No bootstrapping: the repo is linked to a new service frame as-is, and tasks you run on it target that repo.",
|
|
1936
|
+
"connectFirst": "Connect this workspace to GitHub first. Link an installation the App is already on, or install it.",
|
|
1937
|
+
"repository": "Repository",
|
|
1938
|
+
"repositoryHint": "Repositories the GitHub App can access. Don't see yours? Grant the App access below, then refresh.",
|
|
1939
|
+
"noReposAvailable": "No repositories available yet. Grant the App access to one below, then refresh.",
|
|
1940
|
+
"filterPlaceholder": "Filter by owner/name…",
|
|
1941
|
+
"clearFilter": "Clear filter",
|
|
1942
|
+
"chooseRepository": "Choose a repository",
|
|
1943
|
+
"showingCount": "Showing {shown} of {total} repositories.",
|
|
1944
|
+
"repoLabel": {
|
|
1945
|
+
"private": " (private)",
|
|
1946
|
+
"monorepo": " · monorepo",
|
|
1947
|
+
"onBoard": " · already on board"
|
|
1948
|
+
},
|
|
1949
|
+
"monorepoLabel": "This is a monorepo (hosts more than one service)",
|
|
1950
|
+
"monorepoDescription": "Add several services from one repo, each pinned to a subdirectory.",
|
|
1951
|
+
"monorepoBrowseHint": "Browse the repository and pick the directory of the service you want to add. Agents working on this service will run within that subdirectory.",
|
|
1952
|
+
"serviceDirectory": "Service directory:",
|
|
1953
|
+
"noDirectorySelected": "No directory selected yet.",
|
|
1954
|
+
"addedConfigure": "{title} added, configure it",
|
|
1955
|
+
"grantAccess": "Grant the App access to a repo",
|
|
1956
|
+
"grantAccessTitle": "Open the App's installation settings to grant it access to a repository",
|
|
1957
|
+
"refreshList": "Refresh list",
|
|
1958
|
+
"done": "Done",
|
|
1959
|
+
"add": "Add service",
|
|
1960
|
+
"addAnother": "Add another service",
|
|
1961
|
+
"toast": {
|
|
1962
|
+
"addedTitle": "Service added",
|
|
1963
|
+
"addedDescription": "{title} is on the board, configure it below.",
|
|
1964
|
+
"addFailedTitle": "Could not add service"
|
|
1965
|
+
}
|
|
1966
|
+
},
|
|
1967
|
+
"repoTree": {
|
|
1968
|
+
"root": "root",
|
|
1969
|
+
"loading": "Loading…",
|
|
1970
|
+
"noSubdirectories": "No subdirectories here.",
|
|
1971
|
+
"empty": "Nothing here.",
|
|
1972
|
+
"select": "Select",
|
|
1973
|
+
"selected": "Selected",
|
|
1974
|
+
"useThisFolder": "Use this folder",
|
|
1975
|
+
"errors": {
|
|
1976
|
+
"listDirectory": "Could not list directory"
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
},
|
|
1980
|
+
"slack": {
|
|
1981
|
+
"panel": {
|
|
1982
|
+
"title": "Slack notifications",
|
|
1983
|
+
"intro": "Post board notifications (merge reviews, pipeline completions, CI failures) to Slack. The connection is shared across the account; routing is per board."
|
|
1984
|
+
},
|
|
1985
|
+
"connect": {
|
|
1986
|
+
"addToSlack": "Add to Slack",
|
|
1987
|
+
"orPasteToken": "...or paste a bot token (xoxb-...)",
|
|
1988
|
+
"connect": "Connect"
|
|
1989
|
+
},
|
|
1990
|
+
"connected": {
|
|
1991
|
+
"label": "Connected to {team}",
|
|
1992
|
+
"disconnect": "Disconnect"
|
|
1993
|
+
},
|
|
1994
|
+
"routing": {
|
|
1995
|
+
"heading": "Routing",
|
|
1996
|
+
"channelPlaceholder": "#channel or channel id",
|
|
1997
|
+
"mentionMembers": "{'@'}-mention mapped account members",
|
|
1998
|
+
"save": "Save routing"
|
|
1999
|
+
},
|
|
2000
|
+
"members": {
|
|
2001
|
+
"heading": "Member map (user id to Slack member id)",
|
|
2002
|
+
"hint": "{product} people are mentioned on requirement-review findings; everyone else only when they created the task.",
|
|
2003
|
+
"productLabel": "Product",
|
|
2004
|
+
"userIdPlaceholder": "User id (usr_...)",
|
|
2005
|
+
"slackIdPlaceholder": "Slack member id (U...)",
|
|
2006
|
+
"add": "Add member",
|
|
2007
|
+
"save": "Save map"
|
|
2008
|
+
},
|
|
2009
|
+
"routable": {
|
|
2010
|
+
"merge_review": "Merge review",
|
|
2011
|
+
"pipeline_complete": "Pipeline complete",
|
|
2012
|
+
"ci_failed": "CI failed",
|
|
2013
|
+
"test_failed": "Tests failed",
|
|
2014
|
+
"requirement_review": "Requirement review",
|
|
2015
|
+
"clarity_review": "Clarity review",
|
|
2016
|
+
"release_regression": "Release regression",
|
|
2017
|
+
"human_test_ready": "Ready for human testing",
|
|
2018
|
+
"visual_confirmation_ready": "Ready for visual confirmation"
|
|
2019
|
+
},
|
|
2020
|
+
"role": {
|
|
2021
|
+
"engineering": "Engineering",
|
|
2022
|
+
"product": "Product"
|
|
2023
|
+
},
|
|
2024
|
+
"toast": {
|
|
2025
|
+
"connected": "Slack connected",
|
|
2026
|
+
"routingSaved": "Routing saved",
|
|
2027
|
+
"mapSaved": "Member map saved"
|
|
2028
|
+
},
|
|
2029
|
+
"error": {
|
|
2030
|
+
"loadSettings": "Could not load Slack settings",
|
|
2031
|
+
"startOAuth": "Could not start Slack OAuth",
|
|
2032
|
+
"connect": "Could not connect Slack",
|
|
2033
|
+
"disconnect": "Could not disconnect Slack",
|
|
2034
|
+
"saveRouting": "Could not save routing",
|
|
2035
|
+
"saveMap": "Could not save member map"
|
|
2036
|
+
}
|
|
2037
|
+
},
|
|
2038
|
+
"documents": {
|
|
2039
|
+
"picker": {
|
|
2040
|
+
"searchPlaceholder": "Search pages or paste a URL/ID…",
|
|
2041
|
+
"refPlaceholder": "Paste a page URL or ID…",
|
|
2042
|
+
"searchFailed": "Search failed: {error}",
|
|
2043
|
+
"importedBadge": "imported",
|
|
2044
|
+
"attachByReference": "Attach {ref} by reference",
|
|
2045
|
+
"noMatches": "No matching pages.",
|
|
2046
|
+
"emptySearchable": "Search by title, or pick an imported document.",
|
|
2047
|
+
"emptyRefOnly": "Paste a page URL or ID to attach it."
|
|
2048
|
+
},
|
|
2049
|
+
"import": {
|
|
2050
|
+
"title": "Import from a document source",
|
|
2051
|
+
"connectFirst": "Connect a document source first.",
|
|
2052
|
+
"connectSource": "Connect {source}",
|
|
2053
|
+
"spawningInto": "Spawning into {frame}",
|
|
2054
|
+
"sourceLabel": "Source",
|
|
2055
|
+
"refLabel": "Page URL or ID",
|
|
2056
|
+
"importButton": "Import",
|
|
2057
|
+
"importedHeading": "Imported documents",
|
|
2058
|
+
"previewSpawn": "Preview & spawn",
|
|
2059
|
+
"noneImported": "No documents imported yet.",
|
|
2060
|
+
"imported": "Imported \"{title}\"",
|
|
2061
|
+
"importFailed": "Import failed"
|
|
2062
|
+
},
|
|
2063
|
+
"connect": {
|
|
2064
|
+
"title": "Connect source",
|
|
2065
|
+
"sourceFallback": "Source",
|
|
2066
|
+
"intro": "Connect {source} to import requirements, RFCs and PRDs, then spawn board structure or attach them to tasks as agent context.",
|
|
2067
|
+
"personalNote": "Personal connection — this credential authenticates as you and is stored only for your account, never shared with the rest of the workspace.",
|
|
2068
|
+
"disconnect": "Disconnect",
|
|
2069
|
+
"connect": "Connect",
|
|
2070
|
+
"update": "Update connection",
|
|
2071
|
+
"connected": "{source} connected",
|
|
2072
|
+
"disconnected": "{source} disconnected",
|
|
2073
|
+
"connectFailed": "Could not connect"
|
|
2074
|
+
},
|
|
2075
|
+
"spawn": {
|
|
2076
|
+
"title": "Preview structure",
|
|
2077
|
+
"plannerLlm": "AI-generated",
|
|
2078
|
+
"plannerHeadings": "From headings",
|
|
2079
|
+
"intoFrame": "into {frame}",
|
|
2080
|
+
"asTopLevel": "as new top-level frames",
|
|
2081
|
+
"buildingPlan": "Building plan…",
|
|
2082
|
+
"spawnOntoBoard": "Spawn onto board",
|
|
2083
|
+
"planFailed": "Could not build a plan",
|
|
2084
|
+
"spawned": "Structure spawned",
|
|
2085
|
+
"spawnFailed": "Spawn failed",
|
|
2086
|
+
"summary": "{frames} · {modules} · {tasks}",
|
|
2087
|
+
"frameCount": "{count} frame | {count} frames",
|
|
2088
|
+
"moduleCount": "{count} module | {count} modules",
|
|
2089
|
+
"taskCount": "{count} task | {count} tasks"
|
|
2090
|
+
},
|
|
2091
|
+
"taskDocs": {
|
|
2092
|
+
"heading": "Context documents",
|
|
2093
|
+
"attach": "Attach",
|
|
2094
|
+
"importPage": "Import a page…",
|
|
2095
|
+
"empty": "Attach a requirement, RFC or PRD so agents see it while implementing this task.",
|
|
2096
|
+
"attached": "Document attached",
|
|
2097
|
+
"attachFailed": "Could not attach"
|
|
2098
|
+
}
|
|
2099
|
+
},
|
|
2100
|
+
"tasks": {
|
|
2101
|
+
"picker": {
|
|
2102
|
+
"searchPlaceholder": "Search issues or paste a URL/key…",
|
|
2103
|
+
"refPlaceholder": "Paste an issue URL or key…",
|
|
2104
|
+
"searchFailed": "Search failed: {error}",
|
|
2105
|
+
"imported": "imported",
|
|
2106
|
+
"attachByReference": "Attach {ref} by reference",
|
|
2107
|
+
"noMatches": "No matching issues.",
|
|
2108
|
+
"emptySearchable": "Search by title, or pick an imported issue.",
|
|
2109
|
+
"emptyRefOnly": "Paste an issue URL or key to attach it."
|
|
2110
|
+
},
|
|
2111
|
+
"contextIssues": {
|
|
2112
|
+
"title": "Context issues",
|
|
2113
|
+
"attach": "Attach",
|
|
2114
|
+
"attached": "Issue attached",
|
|
2115
|
+
"attachFailed": "Could not attach",
|
|
2116
|
+
"importIssue": "Import an issue…",
|
|
2117
|
+
"emptyHint": "Attach a Jira issue so agents see its description and comments while implementing this task."
|
|
2118
|
+
},
|
|
2119
|
+
"import": {
|
|
2120
|
+
"titleCreate": "Create task from issue",
|
|
2121
|
+
"titleBrowse": "Tracker issues",
|
|
2122
|
+
"connectFirst": "Connect or enable a task source first.",
|
|
2123
|
+
"enableSource": "Enable {label}",
|
|
2124
|
+
"connectSource": "Connect {label}",
|
|
2125
|
+
"source": "Source",
|
|
2126
|
+
"refLabel": "Issue key or URL",
|
|
2127
|
+
"import": "Import",
|
|
2128
|
+
"asEpic": "As epic",
|
|
2129
|
+
"asEpicTitleReady": "Spawn this epic + its children as a linked task group",
|
|
2130
|
+
"asEpicTitleNeedsContainer": "Pick a container for the child tasks first",
|
|
2131
|
+
"epicChildrenContainer": "Epic children container",
|
|
2132
|
+
"pickContainer": "Pick a frame or module",
|
|
2133
|
+
"searchIssues": "Search issues",
|
|
2134
|
+
"searchPlaceholder": "Search by title, paste an issue URL, or type an issue number…",
|
|
2135
|
+
"createTasksIn": "Create tasks in",
|
|
2136
|
+
"needFrameFirst": "Add a service frame to the board first to create tasks from issues.",
|
|
2137
|
+
"searchFailed": "Search failed: {error}",
|
|
2138
|
+
"searchResults": "Search results",
|
|
2139
|
+
"createFromIssue": "Create a task from this issue",
|
|
2140
|
+
"pickContainerFirst": "Pick a container first",
|
|
2141
|
+
"viewOnGitHub": "View {id} on GitHub",
|
|
2142
|
+
"emptyHint": "Search above, or paste an issue URL/key to create a task from it.",
|
|
2143
|
+
"imported": "Imported \"{title}\"",
|
|
2144
|
+
"importFailed": "Import failed",
|
|
2145
|
+
"epicSpawned": "Spawned epic \"{title}\"",
|
|
2146
|
+
"epicChildren": "{count} child task created | {count} child tasks created",
|
|
2147
|
+
"@epicChildren": {
|
|
2148
|
+
"description": "Count-based: how many child tasks were created when spawning an epic (count is always >= 1). Provide ALL plural forms your language needs (English has 2; Polish/Ukrainian need 3 - one/few/many - via the custom pluralRules in i18n.config.ts)."
|
|
2149
|
+
},
|
|
2150
|
+
"epicFailed": "Could not spawn epic"
|
|
2151
|
+
},
|
|
2152
|
+
"connect": {
|
|
2153
|
+
"title": "Task source",
|
|
2154
|
+
"sourceFallback": "Source",
|
|
2155
|
+
"connectedToast": "{label} connected",
|
|
2156
|
+
"connectFailed": "Could not connect",
|
|
2157
|
+
"disconnectedToast": "{label} disconnected",
|
|
2158
|
+
"updateFailed": "Could not update",
|
|
2159
|
+
"intro": "{label} lets you import issues and attach them to tasks as agent context.",
|
|
2160
|
+
"credentialless": "This source uses the GitHub App already installed on your workspace, so there are no credentials to enter.",
|
|
2161
|
+
"installAppHint": "Install the workspace's GitHub App (connect GitHub repos) to offer {label}.",
|
|
2162
|
+
"connected": "Connected.",
|
|
2163
|
+
"connectedTo": "Connected to {label}.",
|
|
2164
|
+
"offerToWorkspace": "Offer to this workspace",
|
|
2165
|
+
"offerHint": "When off, {label} is hidden from import and linking.",
|
|
2166
|
+
"disconnect": "Disconnect",
|
|
2167
|
+
"updateConnection": "Update connection",
|
|
2168
|
+
"connect": "Connect"
|
|
2169
|
+
}
|
|
1837
2170
|
}
|
|
1838
2171
|
}
|