@cat-factory/app 0.141.0 → 0.141.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/components/documents/RepoContextDocPicker.vue +6 -2
- package/app/components/documents/TaskContextDocs.vue +80 -34
- package/app/components/fragments/FragmentLibraryManager.vue +139 -33
- package/app/components/github/GitHubRepoSearchSelect.vue +7 -2
- package/app/components/tasks/TaskContextIssues.vue +88 -43
- package/i18n/locales/de.json +10 -7
- package/i18n/locales/en.json +10 -7
- package/i18n/locales/es.json +10 -7
- package/i18n/locales/fr.json +10 -7
- package/i18n/locales/he.json +10 -7
- package/i18n/locales/it.json +10 -7
- package/i18n/locales/ja.json +10 -7
- package/i18n/locales/pl.json +10 -7
- package/i18n/locales/tr.json +10 -7
- package/i18n/locales/uk.json +10 -7
- package/package.json +1 -1
|
@@ -51,8 +51,12 @@ const selectedRepoId = ref<number | undefined>(undefined)
|
|
|
51
51
|
const selectedRepo = ref<GitHubAvailableRepo | undefined>(undefined)
|
|
52
52
|
|
|
53
53
|
function toRepoItem(r: GitHubAvailableRepo) {
|
|
54
|
-
const
|
|
55
|
-
|
|
54
|
+
const priv = r.private ? t('github.addService.repoLabel.private') : ''
|
|
55
|
+
// Badge a repo reachable only via the user's own PAT: a context doc from it resolves through
|
|
56
|
+
// the workspace App at run time, so on hosted a personal-only repo fails the link with a clear
|
|
57
|
+
// error. The badge flags that before the pick.
|
|
58
|
+
const personal = r.personal ? t('github.addService.repoLabel.personal') : ''
|
|
59
|
+
return { label: `${r.owner}/${r.name}${priv}${personal}`, value: r.githubId }
|
|
56
60
|
}
|
|
57
61
|
const queryMatches = computed(() => (belowMinChars.value ? [] : repoResults.value.map(toRepoItem)))
|
|
58
62
|
const repoMenuItems = computed(() => {
|
|
@@ -1,54 +1,70 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { DropdownMenuItem } from '@nuxt/ui'
|
|
3
|
-
import type { Block
|
|
3
|
+
import type { Block } from '~/types/domain'
|
|
4
|
+
import ContextDocumentPicker from '~/components/documents/ContextDocumentPicker.vue'
|
|
4
5
|
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
5
6
|
|
|
6
7
|
// Documents (from any source) attached to a task as agent context, shown inside
|
|
7
|
-
// the InspectorPanel.
|
|
8
|
-
//
|
|
8
|
+
// the InspectorPanel. Attaching uses the SAME inline picker as task creation
|
|
9
|
+
// (source selector + repo→file browse + free-text search + paste-by-reference —
|
|
10
|
+
// ContextDocumentPicker), NOT the old dropdown that opened a second, page-level
|
|
11
|
+
// "Import a page…" modal on top of the inspector. Stacked page-level modals don't
|
|
12
|
+
// interact here, so that menu appeared to open something with nothing clickable.
|
|
13
|
+
// Because the block already exists, a picked item is imported-when-needed then
|
|
14
|
+
// linked immediately via useContextLinking (the add-task flow's shared
|
|
15
|
+
// orchestration), so failures surface with their real cause. Mirrors
|
|
16
|
+
// TaskContextIssues.vue; rendered only when the integration is available.
|
|
9
17
|
const props = defineProps<{ block: Block }>()
|
|
10
18
|
|
|
11
19
|
const { t } = useI18n()
|
|
12
20
|
const documents = useDocumentsStore()
|
|
13
21
|
const ui = useUiStore()
|
|
14
22
|
const toast = useToast()
|
|
23
|
+
const { linkPending, presentLinkFailures } = useContextLinking()
|
|
15
24
|
|
|
16
25
|
onMounted(() => {
|
|
17
26
|
documents.loadDocuments().catch(() => {})
|
|
18
27
|
})
|
|
19
28
|
|
|
20
29
|
const linked = computed(() => documents.docsForBlock(props.block.id))
|
|
30
|
+
// Already-linked docs, so the inline picker filters them out / never re-offers them.
|
|
31
|
+
const chosenKeys = computed(() =>
|
|
32
|
+
linked.value.map((d) =>
|
|
33
|
+
contextKey({ kind: 'document', source: d.source, externalId: d.externalId }),
|
|
34
|
+
),
|
|
35
|
+
)
|
|
21
36
|
|
|
22
|
-
|
|
37
|
+
const connected = computed(() => documents.available && documents.anyConnected)
|
|
38
|
+
// Sources the user could connect right now to unlock the picker, when none is
|
|
39
|
+
// connected yet (GitHub docs are implicitly connected via the App, so never here).
|
|
40
|
+
const connectableSources = computed(() =>
|
|
41
|
+
documents.available ? documents.sources.filter((s) => !documents.isConnected(s.source)) : [],
|
|
42
|
+
)
|
|
43
|
+
const connectMenu = computed<DropdownMenuItem[][]>(() => [
|
|
44
|
+
connectableSources.value.map((s) => ({
|
|
45
|
+
label: s.label,
|
|
46
|
+
icon: s.icon,
|
|
47
|
+
onSelect: () => ui.openDocumentConnect(s.source),
|
|
48
|
+
})),
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
const showPicker = ref(false)
|
|
52
|
+
const linking = ref(false)
|
|
53
|
+
|
|
54
|
+
// The block exists, so import-when-needed then link immediately (vs the add-task
|
|
55
|
+
// flow which stages the pick and links after create). linkPending never throws —
|
|
56
|
+
// it captures each failure with its cause for the shared presenter.
|
|
57
|
+
async function attach(item: PendingContext) {
|
|
58
|
+
if (linking.value) return
|
|
59
|
+
linking.value = true
|
|
23
60
|
try {
|
|
24
|
-
await
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
description: e instanceof Error ? e.message : String(e),
|
|
30
|
-
icon: 'i-lucide-triangle-alert',
|
|
31
|
-
color: 'error',
|
|
32
|
-
})
|
|
61
|
+
const failures = await linkPending(props.block.id, [item])
|
|
62
|
+
if (failures.length) presentLinkFailures(failures, props.block.id)
|
|
63
|
+
else toast.add({ title: t('documents.taskDocs.attached'), icon: 'i-lucide-link' })
|
|
64
|
+
} finally {
|
|
65
|
+
linking.value = false
|
|
33
66
|
}
|
|
34
67
|
}
|
|
35
|
-
|
|
36
|
-
const attachMenu = computed<DropdownMenuItem[][]>(() => {
|
|
37
|
-
const linkedKeys = new Set(linked.value.map((d) => `${d.source}:${d.externalId}`))
|
|
38
|
-
const items: DropdownMenuItem[] = documents.documents
|
|
39
|
-
.filter((d) => !linkedKeys.has(`${d.source}:${d.externalId}`))
|
|
40
|
-
.map((d) => ({
|
|
41
|
-
label: d.title,
|
|
42
|
-
icon: documents.descriptorFor(d.source)?.icon ?? 'i-lucide-file-text',
|
|
43
|
-
onSelect: () => attach(d.source, d.externalId),
|
|
44
|
-
}))
|
|
45
|
-
items.push({
|
|
46
|
-
label: t('documents.taskDocs.importPage'),
|
|
47
|
-
icon: 'i-lucide-file-down',
|
|
48
|
-
onSelect: () => ui.openDocumentImport(null),
|
|
49
|
-
})
|
|
50
|
-
return [items]
|
|
51
|
-
})
|
|
52
68
|
</script>
|
|
53
69
|
|
|
54
70
|
<template>
|
|
@@ -59,13 +75,43 @@ const attachMenu = computed<DropdownMenuItem[][]>(() => {
|
|
|
59
75
|
:count="linked.length"
|
|
60
76
|
>
|
|
61
77
|
<template #actions>
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
78
|
+
<UButton
|
|
79
|
+
v-if="connected"
|
|
80
|
+
color="neutral"
|
|
81
|
+
variant="soft"
|
|
82
|
+
size="xs"
|
|
83
|
+
:icon="showPicker ? 'i-lucide-x' : 'i-lucide-plus'"
|
|
84
|
+
@click="showPicker = !showPicker"
|
|
85
|
+
>
|
|
86
|
+
{{ showPicker ? t('common.done') : t('documents.taskDocs.attach') }}
|
|
87
|
+
</UButton>
|
|
88
|
+
<UDropdownMenu
|
|
89
|
+
v-else-if="connectableSources.length > 1"
|
|
90
|
+
:items="connectMenu"
|
|
91
|
+
:content="{ side: 'bottom', align: 'end' }"
|
|
92
|
+
>
|
|
93
|
+
<UButton color="neutral" variant="soft" size="xs" icon="i-lucide-plug">
|
|
94
|
+
{{ t('documents.taskDocs.connectSource') }}
|
|
95
|
+
</UButton>
|
|
66
96
|
</UDropdownMenu>
|
|
97
|
+
<UButton
|
|
98
|
+
v-else-if="connectableSources.length === 1"
|
|
99
|
+
color="neutral"
|
|
100
|
+
variant="soft"
|
|
101
|
+
size="xs"
|
|
102
|
+
icon="i-lucide-plug"
|
|
103
|
+
@click="ui.openDocumentConnect(connectableSources[0]!.source)"
|
|
104
|
+
>
|
|
105
|
+
{{ t('documents.taskDocs.connectSourceNamed', { source: connectableSources[0]!.label }) }}
|
|
106
|
+
</UButton>
|
|
67
107
|
</template>
|
|
68
108
|
|
|
109
|
+
<ContextDocumentPicker
|
|
110
|
+
v-if="showPicker && connected"
|
|
111
|
+
:chosen-keys="chosenKeys"
|
|
112
|
+
@pick="attach"
|
|
113
|
+
/>
|
|
114
|
+
|
|
69
115
|
<div v-if="linked.length" class="space-y-1">
|
|
70
116
|
<a
|
|
71
117
|
v-for="doc in linked"
|
|
@@ -181,61 +181,131 @@ async function removeFragment(id: string) {
|
|
|
181
181
|
// Link a Confluence/Notion page or GitHub file as a fragment that is re-resolved
|
|
182
182
|
// from the source at run time (a living source of truth, not a frozen snapshot).
|
|
183
183
|
const docDraft = ref({ source: '' as DocumentSourceKind | '', ref: '', tags: '' })
|
|
184
|
-
const docDraftValid = computed(
|
|
185
|
-
(
|
|
186
|
-
|
|
184
|
+
const docDraftValid = computed(() => {
|
|
185
|
+
if (docLinkDisabled.value || !docDraft.value.source) return false
|
|
186
|
+
// The GitHub picker validates on staged files; every other path on the free-text ref.
|
|
187
|
+
return usingDocPicker.value ? docFilePaths.value.length > 0 : !!docDraft.value.ref.trim()
|
|
188
|
+
})
|
|
187
189
|
|
|
188
190
|
// ---- GitHub file picker (documents tab) -----------------------------------
|
|
189
|
-
// For a GitHub source, let the user search a repo + browse to
|
|
190
|
-
// hand-typing a `owner/repo:path` ref.
|
|
191
|
-
//
|
|
192
|
-
//
|
|
191
|
+
// For a GitHub source, let the user search a repo + browse to one or MORE files
|
|
192
|
+
// instead of hand-typing a `owner/repo:path` ref. Files are staged into a cart that
|
|
193
|
+
// spans folders (browsing away never drops earlier picks), and every staged file is
|
|
194
|
+
// linked as its own living fragment on submit. Only offered when the App/PAT is
|
|
195
|
+
// connected; other sources (Confluence/Notion) keep the single free-text ref field.
|
|
193
196
|
const isGithubDoc = computed(() => docDraft.value.source === 'github')
|
|
194
197
|
const showGithubDocPicker = computed(() => isGithubDoc.value && githubReady.value)
|
|
195
198
|
const docRepoId = ref<number | undefined>(undefined)
|
|
196
199
|
const docRepo = ref<GitHubAvailableRepo | undefined>(undefined)
|
|
197
|
-
|
|
200
|
+
// The staged files (repo-root-relative paths) for the selected repo. Multi-select so
|
|
201
|
+
// any number of files from any nesting level can be linked in one action.
|
|
202
|
+
const docFilePaths = ref<string[]>([])
|
|
198
203
|
|
|
199
|
-
// Reset the picker (and the
|
|
204
|
+
// Reset the picker (and the manual ref) whenever the selected source changes.
|
|
200
205
|
watch(
|
|
201
206
|
() => docDraft.value.source,
|
|
202
207
|
() => {
|
|
203
208
|
docRepoId.value = undefined
|
|
204
209
|
docRepo.value = undefined
|
|
205
|
-
|
|
210
|
+
docFilePaths.value = []
|
|
206
211
|
docDraft.value.ref = ''
|
|
207
212
|
},
|
|
208
213
|
)
|
|
209
214
|
|
|
210
|
-
// A new repo selection clears the previously-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
watch([docRepo, docFilePath], () => {
|
|
217
|
-
if (docRepo.value && docFilePath.value) {
|
|
218
|
-
docDraft.value.ref = `${docRepo.value.owner}/${docRepo.value.name}:${docFilePath.value}`
|
|
219
|
-
}
|
|
215
|
+
// A new repo selection clears the previously-staged files (they were repo-scoped).
|
|
216
|
+
// When the repo is fully deselected, drop the cached repo too so no stale `docRepo`
|
|
217
|
+
// lingers behind a now-empty picker.
|
|
218
|
+
watch(docRepoId, (id) => {
|
|
219
|
+
docFilePaths.value = []
|
|
220
|
+
if (id === undefined) docRepo.value = undefined
|
|
220
221
|
})
|
|
221
222
|
|
|
222
223
|
/** This tier's existing document-backed fragments. */
|
|
223
224
|
const documentFragments = computed(() => library.fragments.filter((f) => f.documentRef))
|
|
224
225
|
|
|
226
|
+
/** Add/remove a browsed file to/from the staged cart (the tree emits `toggle`). */
|
|
227
|
+
function toggleDocFile(path: string) {
|
|
228
|
+
const clean = normalizeRepoPath(path)
|
|
229
|
+
const i = docFilePaths.value.indexOf(clean)
|
|
230
|
+
if (i >= 0) docFilePaths.value.splice(i, 1)
|
|
231
|
+
else docFilePaths.value.push(clean)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Files of the selected repo already linked as fragments — shown "added"/not re-pickable. */
|
|
235
|
+
const docAddedPaths = computed<string[]>(() => {
|
|
236
|
+
const repo = docRepo.value
|
|
237
|
+
if (!repo) return []
|
|
238
|
+
const prefix = `${repo.owner}/${repo.name}:`
|
|
239
|
+
return documentFragments.value
|
|
240
|
+
.filter(
|
|
241
|
+
(f) => f.documentRef?.source === 'github' && f.documentRef.externalId.startsWith(prefix),
|
|
242
|
+
)
|
|
243
|
+
.map((f) => f.documentRef!.externalId.slice(prefix.length))
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
/** The canonical `owner/repo:path` refs for the staged files. */
|
|
247
|
+
const stagedDocRefs = computed(() =>
|
|
248
|
+
docRepo.value
|
|
249
|
+
? docFilePaths.value.map((path) => ({
|
|
250
|
+
path,
|
|
251
|
+
ref: `${docRepo.value!.owner}/${docRepo.value!.name}:${path}`,
|
|
252
|
+
}))
|
|
253
|
+
: [],
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
/** When the rich picker drives the ref(s); otherwise the free-text field does. */
|
|
257
|
+
const usingDocPicker = computed(() => showGithubDocPicker.value)
|
|
258
|
+
|
|
225
259
|
async function linkDocumentFragment() {
|
|
226
260
|
if (!docDraftValid.value) return
|
|
227
261
|
linkingDoc.value = true
|
|
228
262
|
try {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
263
|
+
const source = docDraft.value.source as DocumentSourceKind
|
|
264
|
+
const tags = docDraft.value.tags
|
|
265
|
+
.split(',')
|
|
266
|
+
.map((t) => t.trim())
|
|
267
|
+
.filter(Boolean)
|
|
268
|
+
|
|
269
|
+
if (!usingDocPicker.value) {
|
|
270
|
+
// Free-text field: exactly one ref.
|
|
271
|
+
await library.createDocumentFragment({ source, ref: docDraft.value.ref.trim(), tags })
|
|
272
|
+
docDraft.value = { source: '', ref: '', tags: '' }
|
|
273
|
+
toast.add({ title: t('fragments.toast.documentLinked'), icon: 'i-lucide-link' })
|
|
274
|
+
return
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// The picker stages many files (one fragment each). Link them serially — serial, not
|
|
278
|
+
// parallel, so one bad ref can't be masked by a burst of concurrent failures — and drop
|
|
279
|
+
// each from the cart the moment it succeeds. Every staged file is attempted even if an
|
|
280
|
+
// earlier one fails, so a single unlinkable ref never blocks the rest: the successes are
|
|
281
|
+
// linked and removed from the cart, and only the failures stay staged (with the first
|
|
282
|
+
// error surfaced) so a retry re-attempts just those, never re-linking what already went in.
|
|
283
|
+
let linked = 0
|
|
284
|
+
let firstError: unknown
|
|
285
|
+
for (const { path, ref } of [...stagedDocRefs.value]) {
|
|
286
|
+
try {
|
|
287
|
+
await library.createDocumentFragment({ source, ref, tags })
|
|
288
|
+
const i = docFilePaths.value.indexOf(path)
|
|
289
|
+
if (i >= 0) docFilePaths.value.splice(i, 1)
|
|
290
|
+
linked++
|
|
291
|
+
} catch (e) {
|
|
292
|
+
firstError ??= e
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (linked > 0) {
|
|
296
|
+
// A vue-i18n plural message (count as the plural choice) so Slavic few/many forms
|
|
297
|
+
// render correctly, and the count==1 case reads naturally too.
|
|
298
|
+
toast.add({
|
|
299
|
+
title: t('fragments.toast.documentsLinked', { count: linked }, linked),
|
|
300
|
+
icon: 'i-lucide-link',
|
|
301
|
+
})
|
|
302
|
+
}
|
|
303
|
+
if (firstError) {
|
|
304
|
+
notifyError(t('fragments.toast.linkDocumentFailed'), firstError)
|
|
305
|
+
} else {
|
|
306
|
+
// Everything staged linked — clear the whole draft (also resets repo/source selection).
|
|
307
|
+
docDraft.value = { source: '', ref: '', tags: '' }
|
|
308
|
+
}
|
|
239
309
|
} catch (e) {
|
|
240
310
|
notifyError(t('fragments.toast.linkDocumentFailed'), e)
|
|
241
311
|
} finally {
|
|
@@ -584,7 +654,7 @@ async function unlinkSource(id: string) {
|
|
|
584
654
|
</UButton>
|
|
585
655
|
</div>
|
|
586
656
|
|
|
587
|
-
<!-- GitHub: search a repo + browse to
|
|
657
|
+
<!-- GitHub: search a repo + browse to one or more files instead of typing the ref -->
|
|
588
658
|
<template v-if="showGithubDocPicker">
|
|
589
659
|
<GitHubRepoSearchSelect v-model="docRepoId" @update:repo="docRepo = $event" />
|
|
590
660
|
<div
|
|
@@ -594,11 +664,47 @@ async function unlinkSource(id: string) {
|
|
|
594
664
|
<p class="mb-2 text-xs text-slate-400">
|
|
595
665
|
{{ t('fragments.documents.githubBrowseHint') }}
|
|
596
666
|
</p>
|
|
597
|
-
<RepoTreeBrowser
|
|
667
|
+
<RepoTreeBrowser
|
|
668
|
+
:repo-github-id="docRepoId"
|
|
669
|
+
mode="file"
|
|
670
|
+
multiple
|
|
671
|
+
:selected-paths="docFilePaths"
|
|
672
|
+
:added-paths="docAddedPaths"
|
|
673
|
+
@toggle="toggleDocFile"
|
|
674
|
+
/>
|
|
675
|
+
<div v-if="stagedDocRefs.length" class="mt-2 space-y-1">
|
|
676
|
+
<p class="text-xs font-medium text-slate-400">
|
|
677
|
+
{{ t('fragments.documents.selectedFiles', { count: stagedDocRefs.length }) }}
|
|
678
|
+
</p>
|
|
679
|
+
<div
|
|
680
|
+
v-for="staged in stagedDocRefs"
|
|
681
|
+
:key="staged.path"
|
|
682
|
+
class="flex items-center gap-1.5 rounded bg-slate-800/60 px-2 py-1 text-xs text-slate-300"
|
|
683
|
+
>
|
|
684
|
+
<UIcon
|
|
685
|
+
name="i-lucide-file-code-2"
|
|
686
|
+
class="h-3.5 w-3.5 shrink-0 text-indigo-400"
|
|
687
|
+
/>
|
|
688
|
+
<span class="truncate">{{ staged.path }}</span>
|
|
689
|
+
<UButton
|
|
690
|
+
class="ms-auto"
|
|
691
|
+
color="neutral"
|
|
692
|
+
variant="ghost"
|
|
693
|
+
size="xs"
|
|
694
|
+
icon="i-lucide-x"
|
|
695
|
+
:aria-label="t('fragments.documents.removeFile')"
|
|
696
|
+
@click="toggleDocFile(staged.path)"
|
|
697
|
+
/>
|
|
698
|
+
</div>
|
|
699
|
+
</div>
|
|
598
700
|
</div>
|
|
599
701
|
</template>
|
|
600
702
|
|
|
601
|
-
<UInput
|
|
703
|
+
<UInput
|
|
704
|
+
v-if="!usingDocPicker"
|
|
705
|
+
v-model="docDraft.ref"
|
|
706
|
+
:placeholder="t('fragments.documents.refPlaceholder')"
|
|
707
|
+
/>
|
|
602
708
|
<UInput
|
|
603
709
|
v-model="docDraft.tags"
|
|
604
710
|
:placeholder="t('fragments.documents.tagsPlaceholder')"
|
|
@@ -34,8 +34,13 @@ const belowMinChars = computed(() => repoQueryRaw.value.length < MIN_SEARCH_LEN)
|
|
|
34
34
|
const selectedRepo = ref<GitHubAvailableRepo | undefined>(undefined)
|
|
35
35
|
|
|
36
36
|
function toRepoItem(r: GitHubAvailableRepo) {
|
|
37
|
-
const
|
|
38
|
-
|
|
37
|
+
const priv = r.private ? t('github.addService.repoLabel.private') : ''
|
|
38
|
+
// A repo surfaced only through the signed-in user's PAT (the workspace App can't reach it):
|
|
39
|
+
// badge it so the user knows this pick rides their personal token. On hosted, a doc/fragment
|
|
40
|
+
// linked from it resolves through the App at run time, so a personal-only repo will fail the
|
|
41
|
+
// link with a clear error — the badge sets that expectation up front.
|
|
42
|
+
const personal = r.personal ? t('github.addService.repoLabel.personal') : ''
|
|
43
|
+
return { label: `${r.owner}/${r.name}${priv}${personal}`, value: r.githubId }
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
const repoItems = computed(() => github.availableRepos.map(toRepoItem))
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
// Inspector section for a task block: the tracker issues (Jira, …)
|
|
3
|
-
// it as agent context
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
2
|
+
// Inspector section for a task block: the tracker issues (Jira, GitHub Issues, …)
|
|
3
|
+
// attached to it as agent context. Attaching uses the SAME inline picker as task
|
|
4
|
+
// creation (source selector + in-repo search + paste-by-reference —
|
|
5
|
+
// ContextIssuePicker), NOT the old dropdown that opened a second, page-level
|
|
6
|
+
// "Import an issue…" modal on top of the inspector (stacked page-level modals
|
|
7
|
+
// don't interact here, so the menu appeared to open something with nothing
|
|
8
|
+
// clickable). Because the block already exists, a picked item is
|
|
9
|
+
// imported-when-needed then linked immediately via useContextLinking, scoped to
|
|
10
|
+
// this block's repo. Mirrors TaskContextDocs.vue; shown only when the task-source
|
|
11
|
+
// integration is available. Each linked issue shows its status so the structured
|
|
12
|
+
// nature of an issue is visible at a glance.
|
|
7
13
|
import type { DropdownMenuItem } from '@nuxt/ui'
|
|
8
|
-
import type { Block
|
|
14
|
+
import type { Block } from '~/types/domain'
|
|
15
|
+
import ContextIssuePicker from '~/components/tasks/ContextIssuePicker.vue'
|
|
9
16
|
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
10
17
|
|
|
11
18
|
const props = defineProps<{ block: Block }>()
|
|
@@ -14,43 +21,50 @@ const { t } = useI18n()
|
|
|
14
21
|
const tasks = useTasksStore()
|
|
15
22
|
const ui = useUiStore()
|
|
16
23
|
const toast = useToast()
|
|
24
|
+
const { linkPending, presentLinkFailures } = useContextLinking()
|
|
17
25
|
|
|
18
26
|
onMounted(() => {
|
|
19
27
|
tasks.loadTasks().catch(() => {})
|
|
20
28
|
})
|
|
21
29
|
|
|
22
30
|
const linked = computed(() => tasks.tasksForBlock(props.block.id))
|
|
31
|
+
// Already-linked issues, so the inline picker filters them out / never re-offers them.
|
|
32
|
+
const chosenKeys = computed(() =>
|
|
33
|
+
linked.value.map((issue) =>
|
|
34
|
+
contextKey({ kind: 'task', source: issue.source, externalId: issue.externalId }),
|
|
35
|
+
),
|
|
36
|
+
)
|
|
23
37
|
|
|
24
|
-
|
|
38
|
+
const connected = computed(() => tasks.available && tasks.anyOffered)
|
|
39
|
+
// Trackers the user could connect right now to unlock the picker, when none is offered yet.
|
|
40
|
+
const connectableSources = computed(() =>
|
|
41
|
+
tasks.available ? tasks.sources.filter((s) => !s.available) : [],
|
|
42
|
+
)
|
|
43
|
+
const connectMenu = computed<DropdownMenuItem[][]>(() => [
|
|
44
|
+
connectableSources.value.map((s) => ({
|
|
45
|
+
label: s.label,
|
|
46
|
+
icon: s.icon,
|
|
47
|
+
onSelect: () => ui.openTaskConnect(s.source),
|
|
48
|
+
})),
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
const showPicker = ref(false)
|
|
52
|
+
const linking = ref(false)
|
|
53
|
+
|
|
54
|
+
// The block exists, so import-when-needed then link immediately (vs the add-task
|
|
55
|
+
// flow which stages the pick and links after create). linkPending never throws —
|
|
56
|
+
// it captures each failure with its cause for the shared presenter.
|
|
57
|
+
async function attach(item: PendingContext) {
|
|
58
|
+
if (linking.value) return
|
|
59
|
+
linking.value = true
|
|
25
60
|
try {
|
|
26
|
-
await
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
description: e instanceof Error ? e.message : String(e),
|
|
32
|
-
icon: 'i-lucide-triangle-alert',
|
|
33
|
-
color: 'error',
|
|
34
|
-
})
|
|
61
|
+
const failures = await linkPending(props.block.id, [item])
|
|
62
|
+
if (failures.length) presentLinkFailures(failures, props.block.id)
|
|
63
|
+
else toast.add({ title: t('tasks.contextIssues.attached'), icon: 'i-lucide-link' })
|
|
64
|
+
} finally {
|
|
65
|
+
linking.value = false
|
|
35
66
|
}
|
|
36
67
|
}
|
|
37
|
-
|
|
38
|
-
const attachMenu = computed<DropdownMenuItem[][]>(() => {
|
|
39
|
-
const linkedKeys = new Set(linked.value.map((t) => `${t.source}:${t.externalId}`))
|
|
40
|
-
const items: DropdownMenuItem[] = tasks.tasks
|
|
41
|
-
.filter((t) => !linkedKeys.has(`${t.source}:${t.externalId}`))
|
|
42
|
-
.map((t) => ({
|
|
43
|
-
label: `${t.externalId} · ${t.title}`,
|
|
44
|
-
icon: tasks.descriptorFor(t.source)?.icon ?? 'i-lucide-square-check',
|
|
45
|
-
onSelect: () => attach(t.source, t.externalId),
|
|
46
|
-
}))
|
|
47
|
-
items.push({
|
|
48
|
-
label: t('tasks.contextIssues.importIssue'),
|
|
49
|
-
icon: 'i-lucide-file-down',
|
|
50
|
-
onSelect: () => ui.openTaskImport(),
|
|
51
|
-
})
|
|
52
|
-
return [items]
|
|
53
|
-
})
|
|
54
68
|
</script>
|
|
55
69
|
|
|
56
70
|
<template>
|
|
@@ -61,29 +75,60 @@ const attachMenu = computed<DropdownMenuItem[][]>(() => {
|
|
|
61
75
|
:count="linked.length"
|
|
62
76
|
>
|
|
63
77
|
<template #actions>
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
78
|
+
<UButton
|
|
79
|
+
v-if="connected"
|
|
80
|
+
color="neutral"
|
|
81
|
+
variant="soft"
|
|
82
|
+
size="xs"
|
|
83
|
+
:icon="showPicker ? 'i-lucide-x' : 'i-lucide-plus'"
|
|
84
|
+
@click="showPicker = !showPicker"
|
|
85
|
+
>
|
|
86
|
+
{{ showPicker ? t('common.done') : t('tasks.contextIssues.attach') }}
|
|
87
|
+
</UButton>
|
|
88
|
+
<UDropdownMenu
|
|
89
|
+
v-else-if="connectableSources.length > 1"
|
|
90
|
+
:items="connectMenu"
|
|
91
|
+
:content="{ side: 'bottom', align: 'end' }"
|
|
92
|
+
>
|
|
93
|
+
<UButton color="neutral" variant="soft" size="xs" icon="i-lucide-plug">
|
|
94
|
+
{{ t('tasks.contextIssues.connectSource') }}
|
|
95
|
+
</UButton>
|
|
68
96
|
</UDropdownMenu>
|
|
97
|
+
<UButton
|
|
98
|
+
v-else-if="connectableSources.length === 1"
|
|
99
|
+
color="neutral"
|
|
100
|
+
variant="soft"
|
|
101
|
+
size="xs"
|
|
102
|
+
icon="i-lucide-plug"
|
|
103
|
+
@click="ui.openTaskConnect(connectableSources[0]!.source)"
|
|
104
|
+
>
|
|
105
|
+
{{ t('tasks.contextIssues.connectSourceNamed', { source: connectableSources[0]!.label }) }}
|
|
106
|
+
</UButton>
|
|
69
107
|
</template>
|
|
70
108
|
|
|
109
|
+
<ContextIssuePicker
|
|
110
|
+
v-if="showPicker && connected"
|
|
111
|
+
:chosen-keys="chosenKeys"
|
|
112
|
+
:scope-block-id="block.id"
|
|
113
|
+
@pick="attach"
|
|
114
|
+
/>
|
|
115
|
+
|
|
71
116
|
<div v-if="linked.length" class="space-y-1">
|
|
72
117
|
<a
|
|
73
|
-
v-for="
|
|
74
|
-
:key="`${
|
|
75
|
-
:href="
|
|
118
|
+
v-for="issue in linked"
|
|
119
|
+
:key="`${issue.source}:${issue.externalId}`"
|
|
120
|
+
:href="issue.url"
|
|
76
121
|
target="_blank"
|
|
77
122
|
rel="noopener"
|
|
78
123
|
class="flex items-center gap-1.5 rounded-md border border-slate-800 bg-slate-900/60 px-2 py-1.5 text-xs text-slate-300 hover:bg-slate-800/60"
|
|
79
124
|
>
|
|
80
125
|
<UIcon
|
|
81
|
-
:name="tasks.descriptorFor(
|
|
126
|
+
:name="tasks.descriptorFor(issue.source)?.icon ?? 'i-lucide-square-check'"
|
|
82
127
|
class="h-3.5 w-3.5 shrink-0 text-indigo-400"
|
|
83
128
|
/>
|
|
84
|
-
<span class="truncate">{{
|
|
129
|
+
<span class="truncate">{{ issue.externalId }} · {{ issue.title }}</span>
|
|
85
130
|
<UBadge color="neutral" variant="soft" size="xs" class="ms-auto shrink-0">
|
|
86
|
-
{{
|
|
131
|
+
{{ issue.status }}
|
|
87
132
|
</UBadge>
|
|
88
133
|
</a>
|
|
89
134
|
</div>
|
package/i18n/locales/de.json
CHANGED
|
@@ -2992,10 +2992,10 @@
|
|
|
2992
2992
|
"heading": "Kontextdokumente",
|
|
2993
2993
|
"hint": "Dokumente, die dieser Aufgabe als Kontext angehängt sind. Ihr Inhalt wird den Agents gegeben, die an der Aufgabe arbeiten.",
|
|
2994
2994
|
"attach": "Anhängen",
|
|
2995
|
-
"
|
|
2995
|
+
"connectSource": "Quelle verbinden",
|
|
2996
|
+
"connectSourceNamed": "{source} verbinden",
|
|
2996
2997
|
"empty": "Hängen Sie eine Anforderung, ein RFC oder ein PRD an, damit Agents es beim Umsetzen dieser Aufgabe sehen.",
|
|
2997
|
-
"attached": "Dokument angehängt"
|
|
2998
|
-
"attachFailed": "Anhängen nicht möglich"
|
|
2998
|
+
"attached": "Dokument angehängt"
|
|
2999
2999
|
},
|
|
3000
3000
|
"templates": {
|
|
3001
3001
|
"title": "Dokumentvorlagen & Beispiele",
|
|
@@ -3033,9 +3033,9 @@
|
|
|
3033
3033
|
"title": "Kontext-Issues",
|
|
3034
3034
|
"hint": "Tracker-Issues, die dieser Aufgabe als Kontext angehängt sind. Ihr Inhalt wird den Agents gegeben, die an der Aufgabe arbeiten.",
|
|
3035
3035
|
"attach": "Anhängen",
|
|
3036
|
+
"connectSource": "Quelle verbinden",
|
|
3037
|
+
"connectSourceNamed": "{source} verbinden",
|
|
3036
3038
|
"attached": "Issue angehängt",
|
|
3037
|
-
"attachFailed": "Anhängen nicht möglich",
|
|
3038
|
-
"importIssue": "Ein Issue importieren…",
|
|
3039
3039
|
"emptyHint": "Hängen Sie ein Jira-Issue an, damit Agents seine Beschreibung und Kommentare beim Umsetzen dieser Aufgabe sehen."
|
|
3040
3040
|
},
|
|
3041
3041
|
"import": {
|
|
@@ -3561,7 +3561,9 @@
|
|
|
3561
3561
|
"refPlaceholder": "Seiten-ID oder URL (z. B. eine Confluence-/Notion-Seite oder GitHub-Datei-URL)",
|
|
3562
3562
|
"tagsPlaceholder": "Tags, kommagetrennt (optional)",
|
|
3563
3563
|
"link": "Als lebendes Fragment verknüpfen",
|
|
3564
|
-
"githubBrowseHint": "Durchsuchen Sie das Repo und wählen Sie
|
|
3564
|
+
"githubBrowseHint": "Durchsuchen Sie das Repo und wählen Sie eine oder mehrere zu verknüpfende Dateien.",
|
|
3565
|
+
"selectedFiles": "Ausgewählte Dateien ({count})",
|
|
3566
|
+
"removeFile": "Datei entfernen"
|
|
3565
3567
|
},
|
|
3566
3568
|
"sources": {
|
|
3567
3569
|
"metaSynced": "synchronisiert · ref {ref}",
|
|
@@ -3595,7 +3597,8 @@
|
|
|
3595
3597
|
"upToDate": "Aktuell",
|
|
3596
3598
|
"checkSourceFailed": "Quelle konnte nicht geprüft werden",
|
|
3597
3599
|
"sourceUnlinked": "Quelle-Verknüpfung aufgehoben",
|
|
3598
|
-
"unlinkSourceFailed": "Quelle-Verknüpfung konnte nicht aufgehoben werden"
|
|
3600
|
+
"unlinkSourceFailed": "Quelle-Verknüpfung konnte nicht aufgehoben werden",
|
|
3601
|
+
"documentsLinked": "{count} Dokument als lebendes Fragment verknüpft | {count} Dokumente als lebende Fragmente verknüpft"
|
|
3599
3602
|
},
|
|
3600
3603
|
"confirmRemove": {
|
|
3601
3604
|
"title": "Dieses Fragment löschen?",
|
package/i18n/locales/en.json
CHANGED
|
@@ -3355,10 +3355,10 @@
|
|
|
3355
3355
|
"heading": "Context documents",
|
|
3356
3356
|
"hint": "Documents attached to this task as context. Their content is given to the agents that work on the task.",
|
|
3357
3357
|
"attach": "Attach",
|
|
3358
|
-
"
|
|
3358
|
+
"connectSource": "Connect a source",
|
|
3359
|
+
"connectSourceNamed": "Connect {source}",
|
|
3359
3360
|
"empty": "Attach a requirement, RFC or PRD so agents see it while implementing this task.",
|
|
3360
|
-
"attached": "Document attached"
|
|
3361
|
-
"attachFailed": "Could not attach"
|
|
3361
|
+
"attached": "Document attached"
|
|
3362
3362
|
},
|
|
3363
3363
|
"templates": {
|
|
3364
3364
|
"title": "Document templates & examples",
|
|
@@ -3396,9 +3396,9 @@
|
|
|
3396
3396
|
"title": "Context issues",
|
|
3397
3397
|
"hint": "Tracker issues attached to this task as context. Their content is given to the agents that work on the task.",
|
|
3398
3398
|
"attach": "Attach",
|
|
3399
|
+
"connectSource": "Connect a source",
|
|
3400
|
+
"connectSourceNamed": "Connect {source}",
|
|
3399
3401
|
"attached": "Issue attached",
|
|
3400
|
-
"attachFailed": "Could not attach",
|
|
3401
|
-
"importIssue": "Import an issue…",
|
|
3402
3402
|
"emptyHint": "Attach a Jira issue so agents see its description and comments while implementing this task."
|
|
3403
3403
|
},
|
|
3404
3404
|
"import": {
|
|
@@ -4561,7 +4561,9 @@
|
|
|
4561
4561
|
"refPlaceholder": "Page id or URL (e.g. a Confluence/Notion page or GitHub file URL)",
|
|
4562
4562
|
"tagsPlaceholder": "Tags, comma-separated (optional)",
|
|
4563
4563
|
"link": "Link as living fragment",
|
|
4564
|
-
"githubBrowseHint": "Browse the repo and pick
|
|
4564
|
+
"githubBrowseHint": "Browse the repo and pick one or more files to link.",
|
|
4565
|
+
"selectedFiles": "Selected files ({count})",
|
|
4566
|
+
"removeFile": "Remove file"
|
|
4565
4567
|
},
|
|
4566
4568
|
"sources": {
|
|
4567
4569
|
"metaSynced": "synced · ref {ref}",
|
|
@@ -4595,7 +4597,8 @@
|
|
|
4595
4597
|
"upToDate": "Up to date",
|
|
4596
4598
|
"checkSourceFailed": "Could not check source",
|
|
4597
4599
|
"sourceUnlinked": "Source unlinked",
|
|
4598
|
-
"unlinkSourceFailed": "Could not unlink source"
|
|
4600
|
+
"unlinkSourceFailed": "Could not unlink source",
|
|
4601
|
+
"documentsLinked": "Linked {count} document as a living fragment | Linked {count} documents as living fragments"
|
|
4599
4602
|
},
|
|
4600
4603
|
"confirmRemove": {
|
|
4601
4604
|
"title": "Delete this fragment?",
|
package/i18n/locales/es.json
CHANGED
|
@@ -3259,10 +3259,10 @@
|
|
|
3259
3259
|
"heading": "Documentos de contexto",
|
|
3260
3260
|
"hint": "Documentos adjuntos a esta tarea como contexto. Su contenido se entrega a los agentes que trabajan en la tarea.",
|
|
3261
3261
|
"attach": "Adjuntar",
|
|
3262
|
-
"
|
|
3262
|
+
"connectSource": "Conectar una fuente",
|
|
3263
|
+
"connectSourceNamed": "Conectar {source}",
|
|
3263
3264
|
"empty": "Adjunta un requisito, RFC o PRD para que los agentes lo vean mientras implementan esta tarea.",
|
|
3264
|
-
"attached": "Documento adjuntado"
|
|
3265
|
-
"attachFailed": "No se pudo adjuntar"
|
|
3265
|
+
"attached": "Documento adjuntado"
|
|
3266
3266
|
},
|
|
3267
3267
|
"templates": {
|
|
3268
3268
|
"title": "Plantillas y ejemplos de documentos",
|
|
@@ -3300,9 +3300,9 @@
|
|
|
3300
3300
|
"title": "Incidencias de contexto",
|
|
3301
3301
|
"hint": "Incidencias del tracker adjuntas a esta tarea como contexto. Su contenido se entrega a los agentes que trabajan en la tarea.",
|
|
3302
3302
|
"attach": "Adjuntar",
|
|
3303
|
+
"connectSource": "Conectar una fuente",
|
|
3304
|
+
"connectSourceNamed": "Conectar {source}",
|
|
3303
3305
|
"attached": "Incidencia adjuntada",
|
|
3304
|
-
"attachFailed": "No se pudo adjuntar",
|
|
3305
|
-
"importIssue": "Importar una incidencia…",
|
|
3306
3306
|
"emptyHint": "Adjunta una incidencia de Jira para que los agentes vean su descripción y comentarios al implementar esta tarea."
|
|
3307
3307
|
},
|
|
3308
3308
|
"import": {
|
|
@@ -4385,7 +4385,9 @@
|
|
|
4385
4385
|
"refPlaceholder": "Id o URL de página (p. ej., una página de Confluence/Notion o la URL de un archivo de GitHub)",
|
|
4386
4386
|
"tagsPlaceholder": "Etiquetas, separadas por comas (opcional)",
|
|
4387
4387
|
"link": "Vincular como fragmento vivo",
|
|
4388
|
-
"githubBrowseHint": "Explora el repositorio y elige
|
|
4388
|
+
"githubBrowseHint": "Explora el repositorio y elige uno o más archivos a enlazar.",
|
|
4389
|
+
"selectedFiles": "Archivos seleccionados ({count})",
|
|
4390
|
+
"removeFile": "Quitar archivo"
|
|
4389
4391
|
},
|
|
4390
4392
|
"sources": {
|
|
4391
4393
|
"metaSynced": "sincronizado · ref {ref}",
|
|
@@ -4419,7 +4421,8 @@
|
|
|
4419
4421
|
"upToDate": "Al día",
|
|
4420
4422
|
"checkSourceFailed": "No se pudo comprobar el origen",
|
|
4421
4423
|
"sourceUnlinked": "Origen desvinculado",
|
|
4422
|
-
"unlinkSourceFailed": "No se pudo desvincular el origen"
|
|
4424
|
+
"unlinkSourceFailed": "No se pudo desvincular el origen",
|
|
4425
|
+
"documentsLinked": "{count} documento vinculado como fragmento vivo | {count} documentos vinculados como fragmentos vivos"
|
|
4423
4426
|
},
|
|
4424
4427
|
"confirmRemove": {
|
|
4425
4428
|
"title": "¿Eliminar este fragmento?",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -3259,10 +3259,10 @@
|
|
|
3259
3259
|
"heading": "Documents de contexte",
|
|
3260
3260
|
"hint": "Documents joints à cette tâche comme contexte. Leur contenu est fourni aux agents qui travaillent sur la tâche.",
|
|
3261
3261
|
"attach": "Joindre",
|
|
3262
|
-
"
|
|
3262
|
+
"connectSource": "Connecter une source",
|
|
3263
|
+
"connectSourceNamed": "Connecter {source}",
|
|
3263
3264
|
"empty": "Joignez une exigence, un RFC ou un PRD pour que les agents le voient pendant l'implémentation de cette tâche.",
|
|
3264
|
-
"attached": "Document joint"
|
|
3265
|
-
"attachFailed": "Impossible de joindre"
|
|
3265
|
+
"attached": "Document joint"
|
|
3266
3266
|
},
|
|
3267
3267
|
"templates": {
|
|
3268
3268
|
"title": "Modèles et exemples de documents",
|
|
@@ -3300,9 +3300,9 @@
|
|
|
3300
3300
|
"title": "Tickets de contexte",
|
|
3301
3301
|
"hint": "Tickets du tracker joints à cette tâche comme contexte. Leur contenu est fourni aux agents qui travaillent sur la tâche.",
|
|
3302
3302
|
"attach": "Joindre",
|
|
3303
|
+
"connectSource": "Connecter une source",
|
|
3304
|
+
"connectSourceNamed": "Connecter {source}",
|
|
3303
3305
|
"attached": "Ticket joint",
|
|
3304
|
-
"attachFailed": "Impossible de joindre",
|
|
3305
|
-
"importIssue": "Importer un ticket…",
|
|
3306
3306
|
"emptyHint": "Joignez un ticket Jira pour que les agents voient sa description et ses commentaires lors de l'implémentation de cette tâche."
|
|
3307
3307
|
},
|
|
3308
3308
|
"import": {
|
|
@@ -4385,7 +4385,9 @@
|
|
|
4385
4385
|
"refPlaceholder": "Id ou URL de page (par ex. une page Confluence/Notion ou l'URL d'un fichier GitHub)",
|
|
4386
4386
|
"tagsPlaceholder": "Étiquettes, séparées par des virgules (facultatif)",
|
|
4387
4387
|
"link": "Lier comme fragment vivant",
|
|
4388
|
-
"githubBrowseHint": "Parcourez le dépôt et choisissez
|
|
4388
|
+
"githubBrowseHint": "Parcourez le dépôt et choisissez un ou plusieurs fichiers à lier.",
|
|
4389
|
+
"selectedFiles": "Fichiers sélectionnés ({count})",
|
|
4390
|
+
"removeFile": "Retirer le fichier"
|
|
4389
4391
|
},
|
|
4390
4392
|
"sources": {
|
|
4391
4393
|
"metaSynced": "synchronisé · ref {ref}",
|
|
@@ -4419,7 +4421,8 @@
|
|
|
4419
4421
|
"upToDate": "À jour",
|
|
4420
4422
|
"checkSourceFailed": "Impossible de vérifier la source",
|
|
4421
4423
|
"sourceUnlinked": "Source dissociée",
|
|
4422
|
-
"unlinkSourceFailed": "Impossible de dissocier la source"
|
|
4424
|
+
"unlinkSourceFailed": "Impossible de dissocier la source",
|
|
4425
|
+
"documentsLinked": "{count} document lié comme fragment vivant | {count} documents liés comme fragments vivants"
|
|
4423
4426
|
},
|
|
4424
4427
|
"confirmRemove": {
|
|
4425
4428
|
"title": "Supprimer ce fragment ?",
|
package/i18n/locales/he.json
CHANGED
|
@@ -3270,10 +3270,10 @@
|
|
|
3270
3270
|
"heading": "מסמכי הקשר",
|
|
3271
3271
|
"hint": "מסמכים המצורפים למשימה זו כהקשר. תוכנם נמסר לסוכנים שעובדים על המשימה.",
|
|
3272
3272
|
"attach": "צרף",
|
|
3273
|
-
"
|
|
3273
|
+
"connectSource": "חבר מקור",
|
|
3274
|
+
"connectSourceNamed": "חבר את {source}",
|
|
3274
3275
|
"empty": "צרף דרישה, RFC או PRD כדי שהסוכנים יראו אותם בעת מימוש משימה זו.",
|
|
3275
|
-
"attached": "המסמך צורף"
|
|
3276
|
-
"attachFailed": "לא ניתן לצרף"
|
|
3276
|
+
"attached": "המסמך צורף"
|
|
3277
3277
|
},
|
|
3278
3278
|
"templates": {
|
|
3279
3279
|
"title": "תבניות ודוגמאות למסמכים",
|
|
@@ -3311,9 +3311,9 @@
|
|
|
3311
3311
|
"title": "ניושני הקשר",
|
|
3312
3312
|
"hint": "כרטיסי ה-tracker המצורפים למשימה זו כהקשר. תוכנם נמסר לסוכנים שעובדים על המשימה.",
|
|
3313
3313
|
"attach": "צרף",
|
|
3314
|
+
"connectSource": "חבר מקור",
|
|
3315
|
+
"connectSourceNamed": "חבר את {source}",
|
|
3314
3316
|
"attached": "הניושן צורף",
|
|
3315
|
-
"attachFailed": "לא ניתן היה לצרף",
|
|
3316
|
-
"importIssue": "ייבא ניושן…",
|
|
3317
3317
|
"emptyHint": "צרף ניושן Jira כדי שסוכנים יראו את התיאור וההערות שלו בעת מימוש משימה זו."
|
|
3318
3318
|
},
|
|
3319
3319
|
"import": {
|
|
@@ -4396,7 +4396,9 @@
|
|
|
4396
4396
|
"refPlaceholder": "מזהה עמוד או כתובת (למשל עמוד Confluence/Notion או כתובת קובץ GitHub)",
|
|
4397
4397
|
"tagsPlaceholder": "תגיות, מופרדות בפסיקים (אופציונלי)",
|
|
4398
4398
|
"link": "קשר כמקטע חי",
|
|
4399
|
-
"githubBrowseHint": "עיין במאגר ובחר
|
|
4399
|
+
"githubBrowseHint": "עיין במאגר ובחר קובץ אחד או יותר לקישור.",
|
|
4400
|
+
"selectedFiles": "קבצים שנבחרו ({count})",
|
|
4401
|
+
"removeFile": "הסר קובץ"
|
|
4400
4402
|
},
|
|
4401
4403
|
"sources": {
|
|
4402
4404
|
"metaSynced": "סונכרן · ref {ref}",
|
|
@@ -4430,7 +4432,8 @@
|
|
|
4430
4432
|
"upToDate": "מעודכן",
|
|
4431
4433
|
"checkSourceFailed": "לא ניתן היה לבדוק את המקור",
|
|
4432
4434
|
"sourceUnlinked": "קישור המקור בוטל",
|
|
4433
|
-
"unlinkSourceFailed": "לא ניתן היה לבטל את קישור המקור"
|
|
4435
|
+
"unlinkSourceFailed": "לא ניתן היה לבטל את קישור המקור",
|
|
4436
|
+
"documentsLinked": "{count} מסמך קושר כמקטע חי | {count} מסמכים קושרו כמקטעים חיים"
|
|
4434
4437
|
},
|
|
4435
4438
|
"confirmRemove": {
|
|
4436
4439
|
"title": "למחוק את המקטע הזה?",
|
package/i18n/locales/it.json
CHANGED
|
@@ -2992,10 +2992,10 @@
|
|
|
2992
2992
|
"heading": "Documenti di contesto",
|
|
2993
2993
|
"hint": "Documenti allegati a questa attività come contesto. Il loro contenuto viene fornito agli agenti che lavorano sull'attività.",
|
|
2994
2994
|
"attach": "Allega",
|
|
2995
|
-
"
|
|
2995
|
+
"connectSource": "Collega una fonte",
|
|
2996
|
+
"connectSourceNamed": "Collega {source}",
|
|
2996
2997
|
"empty": "Allega un requisito, un RFC o un PRD così gli agenti lo vedono durante l'implementazione di questa attività.",
|
|
2997
|
-
"attached": "Documento allegato"
|
|
2998
|
-
"attachFailed": "Impossibile allegare"
|
|
2998
|
+
"attached": "Documento allegato"
|
|
2999
2999
|
},
|
|
3000
3000
|
"templates": {
|
|
3001
3001
|
"title": "Modelli ed esempi di documenti",
|
|
@@ -3033,9 +3033,9 @@
|
|
|
3033
3033
|
"title": "Issue di contesto",
|
|
3034
3034
|
"hint": "Issue del tracker allegate a questa attività come contesto. Il loro contenuto viene fornito agli agenti che lavorano sull'attività.",
|
|
3035
3035
|
"attach": "Allega",
|
|
3036
|
+
"connectSource": "Collega una fonte",
|
|
3037
|
+
"connectSourceNamed": "Collega {source}",
|
|
3036
3038
|
"attached": "Issue allegata",
|
|
3037
|
-
"attachFailed": "Impossibile allegare",
|
|
3038
|
-
"importIssue": "Importa una issue…",
|
|
3039
3039
|
"emptyHint": "Allega una issue Jira così gli agenti vedono la sua descrizione e i commenti durante l'implementazione di questa attività."
|
|
3040
3040
|
},
|
|
3041
3041
|
"import": {
|
|
@@ -3561,7 +3561,9 @@
|
|
|
3561
3561
|
"refPlaceholder": "Id o URL della pagina (es. una pagina Confluence/Notion o l'URL di un file GitHub)",
|
|
3562
3562
|
"tagsPlaceholder": "Tag, separati da virgola (opzionale)",
|
|
3563
3563
|
"link": "Collega come frammento vivente",
|
|
3564
|
-
"githubBrowseHint": "Sfoglia il repository e scegli
|
|
3564
|
+
"githubBrowseHint": "Sfoglia il repository e scegli uno o più file da collegare.",
|
|
3565
|
+
"selectedFiles": "File selezionati ({count})",
|
|
3566
|
+
"removeFile": "Rimuovi file"
|
|
3565
3567
|
},
|
|
3566
3568
|
"sources": {
|
|
3567
3569
|
"metaSynced": "sincronizzato · ref {ref}",
|
|
@@ -3595,7 +3597,8 @@
|
|
|
3595
3597
|
"upToDate": "Aggiornato",
|
|
3596
3598
|
"checkSourceFailed": "Impossibile controllare la sorgente",
|
|
3597
3599
|
"sourceUnlinked": "Sorgente scollegata",
|
|
3598
|
-
"unlinkSourceFailed": "Impossibile scollegare la sorgente"
|
|
3600
|
+
"unlinkSourceFailed": "Impossibile scollegare la sorgente",
|
|
3601
|
+
"documentsLinked": "{count} documento collegato come frammento vivente | {count} documenti collegati come frammenti viventi"
|
|
3599
3602
|
},
|
|
3600
3603
|
"confirmRemove": {
|
|
3601
3604
|
"title": "Eliminare questo frammento?",
|
package/i18n/locales/ja.json
CHANGED
|
@@ -3271,10 +3271,10 @@
|
|
|
3271
3271
|
"heading": "コンテキストドキュメント",
|
|
3272
3272
|
"hint": "このタスクにコンテキストとして添付されたドキュメント。その内容はタスクに取り組むエージェントに渡されます。",
|
|
3273
3273
|
"attach": "添付",
|
|
3274
|
-
"
|
|
3274
|
+
"connectSource": "ソースを接続",
|
|
3275
|
+
"connectSourceNamed": "{source} を接続",
|
|
3275
3276
|
"empty": "要件、RFC、PRD を添付すると、このタスクの実装中にエージェントが参照できます。",
|
|
3276
|
-
"attached": "ドキュメントを添付しました"
|
|
3277
|
-
"attachFailed": "添付できませんでした"
|
|
3277
|
+
"attached": "ドキュメントを添付しました"
|
|
3278
3278
|
},
|
|
3279
3279
|
"templates": {
|
|
3280
3280
|
"title": "ドキュメントのテンプレートと例",
|
|
@@ -3312,9 +3312,9 @@
|
|
|
3312
3312
|
"title": "コンテキスト課題",
|
|
3313
3313
|
"hint": "このタスクにコンテキストとして添付されたトラッカーの課題。その内容はタスクに取り組むエージェントに渡されます。",
|
|
3314
3314
|
"attach": "添付",
|
|
3315
|
+
"connectSource": "ソースを接続",
|
|
3316
|
+
"connectSourceNamed": "{source} を接続",
|
|
3315
3317
|
"attached": "課題を添付しました",
|
|
3316
|
-
"attachFailed": "添付できませんでした",
|
|
3317
|
-
"importIssue": "課題をインポート…",
|
|
3318
3318
|
"emptyHint": "Jira 課題を添付すると、このタスクの実装中にエージェントがその説明とコメントを参照できます。"
|
|
3319
3319
|
},
|
|
3320
3320
|
"import": {
|
|
@@ -4397,7 +4397,9 @@
|
|
|
4397
4397
|
"refPlaceholder": "ページ id または URL (例: Confluence/Notion ページや GitHub ファイル URL)",
|
|
4398
4398
|
"tagsPlaceholder": "タグ、カンマ区切り (任意)",
|
|
4399
4399
|
"link": "リビングフラグメントとしてリンク",
|
|
4400
|
-
"githubBrowseHint": "
|
|
4400
|
+
"githubBrowseHint": "リポジトリを参照してリンクするファイルを1つ以上選択します。",
|
|
4401
|
+
"selectedFiles": "選択したファイル ({count})",
|
|
4402
|
+
"removeFile": "ファイルを削除"
|
|
4401
4403
|
},
|
|
4402
4404
|
"sources": {
|
|
4403
4405
|
"metaSynced": "同期済み · ref {ref}",
|
|
@@ -4431,7 +4433,8 @@
|
|
|
4431
4433
|
"upToDate": "最新です",
|
|
4432
4434
|
"checkSourceFailed": "ソースを確認できませんでした",
|
|
4433
4435
|
"sourceUnlinked": "ソースのリンクを解除しました",
|
|
4434
|
-
"unlinkSourceFailed": "ソースのリンクを解除できませんでした"
|
|
4436
|
+
"unlinkSourceFailed": "ソースのリンクを解除できませんでした",
|
|
4437
|
+
"documentsLinked": "{count}件のドキュメントをリビングフラグメントとしてリンクしました | {count}件のドキュメントをリビングフラグメントとしてリンクしました"
|
|
4435
4438
|
},
|
|
4436
4439
|
"confirmRemove": {
|
|
4437
4440
|
"title": "このフラグメントを削除しますか?",
|
package/i18n/locales/pl.json
CHANGED
|
@@ -3259,10 +3259,10 @@
|
|
|
3259
3259
|
"heading": "Dokumenty kontekstowe",
|
|
3260
3260
|
"hint": "Dokumenty dołączone do tego zadania jako kontekst. Ich treść trafia do agentów pracujących nad zadaniem.",
|
|
3261
3261
|
"attach": "Dołącz",
|
|
3262
|
-
"
|
|
3262
|
+
"connectSource": "Połącz źródło",
|
|
3263
|
+
"connectSourceNamed": "Połącz {source}",
|
|
3263
3264
|
"empty": "Dołącz wymaganie, dokument RFC lub PRD, aby agenci widzieli je podczas realizacji tego zadania.",
|
|
3264
|
-
"attached": "Dokument dołączony"
|
|
3265
|
-
"attachFailed": "Nie udało się dołączyć"
|
|
3265
|
+
"attached": "Dokument dołączony"
|
|
3266
3266
|
},
|
|
3267
3267
|
"templates": {
|
|
3268
3268
|
"title": "Szablony i przykłady dokumentów",
|
|
@@ -3300,9 +3300,9 @@
|
|
|
3300
3300
|
"title": "Zgłoszenia kontekstowe",
|
|
3301
3301
|
"hint": "Zgłoszenia z trackera dołączone do tego zadania jako kontekst. Ich treść trafia do agentów pracujących nad zadaniem.",
|
|
3302
3302
|
"attach": "Dołącz",
|
|
3303
|
+
"connectSource": "Połącz źródło",
|
|
3304
|
+
"connectSourceNamed": "Połącz {source}",
|
|
3303
3305
|
"attached": "Dołączono zgłoszenie",
|
|
3304
|
-
"attachFailed": "Nie można dołączyć",
|
|
3305
|
-
"importIssue": "Importuj zgłoszenie…",
|
|
3306
3306
|
"emptyHint": "Dołącz zgłoszenie Jira, aby agenci widzieli jego opis i komentarze podczas realizacji tego zadania."
|
|
3307
3307
|
},
|
|
3308
3308
|
"import": {
|
|
@@ -4385,7 +4385,9 @@
|
|
|
4385
4385
|
"refPlaceholder": "Id lub URL strony (np. strona Confluence/Notion lub URL pliku GitHub)",
|
|
4386
4386
|
"tagsPlaceholder": "Tagi, oddzielone przecinkami (opcjonalnie)",
|
|
4387
4387
|
"link": "Połącz jako żywy fragment",
|
|
4388
|
-
"githubBrowseHint": "Przeglądaj repozytorium i wybierz
|
|
4388
|
+
"githubBrowseHint": "Przeglądaj repozytorium i wybierz jeden lub więcej plików do połączenia.",
|
|
4389
|
+
"selectedFiles": "Wybrane pliki ({count})",
|
|
4390
|
+
"removeFile": "Usuń plik"
|
|
4389
4391
|
},
|
|
4390
4392
|
"sources": {
|
|
4391
4393
|
"metaSynced": "zsynchronizowano · ref {ref}",
|
|
@@ -4419,7 +4421,8 @@
|
|
|
4419
4421
|
"upToDate": "Aktualne",
|
|
4420
4422
|
"checkSourceFailed": "Nie udało się sprawdzić źródła",
|
|
4421
4423
|
"sourceUnlinked": "Odłączono źródło",
|
|
4422
|
-
"unlinkSourceFailed": "Nie udało się odłączyć źródła"
|
|
4424
|
+
"unlinkSourceFailed": "Nie udało się odłączyć źródła",
|
|
4425
|
+
"documentsLinked": "Połączono {count} dokument jako żywy fragment | Połączono {count} dokumenty jako żywe fragmenty | Połączono {count} dokumentów jako żywe fragmenty"
|
|
4423
4426
|
},
|
|
4424
4427
|
"confirmRemove": {
|
|
4425
4428
|
"title": "Usunąć ten fragment?",
|
package/i18n/locales/tr.json
CHANGED
|
@@ -3271,10 +3271,10 @@
|
|
|
3271
3271
|
"heading": "Bağlam belgeleri",
|
|
3272
3272
|
"hint": "Bu göreve bağlam olarak eklenen belgeler. İçerikleri görev üzerinde çalışan ajanlara iletilir.",
|
|
3273
3273
|
"attach": "Ekle",
|
|
3274
|
-
"
|
|
3274
|
+
"connectSource": "Bir kaynak bağla",
|
|
3275
|
+
"connectSourceNamed": "{source} bağla",
|
|
3275
3276
|
"empty": "Agentların bu görevi uygularken görebilmesi için bir gereksinim, RFC veya PRD ekle.",
|
|
3276
|
-
"attached": "Belge eklendi"
|
|
3277
|
-
"attachFailed": "Eklenemedi"
|
|
3277
|
+
"attached": "Belge eklendi"
|
|
3278
3278
|
},
|
|
3279
3279
|
"templates": {
|
|
3280
3280
|
"title": "Belge şablonları ve örnekleri",
|
|
@@ -3312,9 +3312,9 @@
|
|
|
3312
3312
|
"title": "Bağlam sorunları",
|
|
3313
3313
|
"hint": "Bu göreve bağlam olarak eklenen tracker sorunları. İçerikleri görev üzerinde çalışan ajanlara iletilir.",
|
|
3314
3314
|
"attach": "Ekle",
|
|
3315
|
+
"connectSource": "Bir kaynak bağla",
|
|
3316
|
+
"connectSourceNamed": "{source} bağla",
|
|
3315
3317
|
"attached": "Sorun eklendi",
|
|
3316
|
-
"attachFailed": "Eklenemedi",
|
|
3317
|
-
"importIssue": "Bir sorun içe aktar…",
|
|
3318
3318
|
"emptyHint": "Agentların bu görevi uygularken açıklamasını ve yorumlarını görebilmesi için bir Jira sorunu ekle."
|
|
3319
3319
|
},
|
|
3320
3320
|
"import": {
|
|
@@ -4397,7 +4397,9 @@
|
|
|
4397
4397
|
"refPlaceholder": "Sayfa kimliği veya URL (örn. bir Confluence/Notion sayfası veya GitHub dosya URL'si)",
|
|
4398
4398
|
"tagsPlaceholder": "Etiketler, virgülle ayrılmış (isteğe bağlı)",
|
|
4399
4399
|
"link": "Canlı parça olarak bağla",
|
|
4400
|
-
"githubBrowseHint": "Depoya göz atın ve bağlanacak
|
|
4400
|
+
"githubBrowseHint": "Depoya göz atın ve bağlanacak bir veya daha fazla dosya seçin.",
|
|
4401
|
+
"selectedFiles": "Seçilen dosyalar ({count})",
|
|
4402
|
+
"removeFile": "Dosyayı kaldır"
|
|
4401
4403
|
},
|
|
4402
4404
|
"sources": {
|
|
4403
4405
|
"metaSynced": "eşitlendi · ref {ref}",
|
|
@@ -4431,7 +4433,8 @@
|
|
|
4431
4433
|
"upToDate": "Güncel",
|
|
4432
4434
|
"checkSourceFailed": "Kaynak denetlenemedi",
|
|
4433
4435
|
"sourceUnlinked": "Kaynak bağlantısı kaldırıldı",
|
|
4434
|
-
"unlinkSourceFailed": "Kaynak bağlantısı kaldırılamadı"
|
|
4436
|
+
"unlinkSourceFailed": "Kaynak bağlantısı kaldırılamadı",
|
|
4437
|
+
"documentsLinked": "{count} belge canlı parça olarak bağlandı | {count} belge canlı parça olarak bağlandı"
|
|
4435
4438
|
},
|
|
4436
4439
|
"confirmRemove": {
|
|
4437
4440
|
"title": "Bu parça silinsin mi?",
|
package/i18n/locales/uk.json
CHANGED
|
@@ -3259,10 +3259,10 @@
|
|
|
3259
3259
|
"heading": "Контекстні документи",
|
|
3260
3260
|
"hint": "Документи, долучені до цього завдання як контекст. Їхній вміст передається агентам, що працюють над завданням.",
|
|
3261
3261
|
"attach": "Долучити",
|
|
3262
|
-
"
|
|
3262
|
+
"connectSource": "Підключити джерело",
|
|
3263
|
+
"connectSourceNamed": "Підключити {source}",
|
|
3263
3264
|
"empty": "Долучіть вимогу, RFC або PRD, щоб агенти бачили їх під час реалізації цього завдання.",
|
|
3264
|
-
"attached": "Документ долучено"
|
|
3265
|
-
"attachFailed": "Не вдалося долучити"
|
|
3265
|
+
"attached": "Документ долучено"
|
|
3266
3266
|
},
|
|
3267
3267
|
"templates": {
|
|
3268
3268
|
"title": "Шаблони та приклади документів",
|
|
@@ -3300,9 +3300,9 @@
|
|
|
3300
3300
|
"title": "Контекстні завдання",
|
|
3301
3301
|
"hint": "Завдання з трекера, долучені до цього завдання як контекст. Їхній вміст передається агентам, що працюють над завданням.",
|
|
3302
3302
|
"attach": "Прикріпити",
|
|
3303
|
+
"connectSource": "Підключити джерело",
|
|
3304
|
+
"connectSourceNamed": "Підключити {source}",
|
|
3303
3305
|
"attached": "Завдання прикріплено",
|
|
3304
|
-
"attachFailed": "Не вдалося прикріпити",
|
|
3305
|
-
"importIssue": "Імпортувати завдання…",
|
|
3306
3306
|
"emptyHint": "Прикріпіть завдання Jira, щоб агенти бачили його опис і коментарі під час реалізації цього завдання."
|
|
3307
3307
|
},
|
|
3308
3308
|
"import": {
|
|
@@ -4385,7 +4385,9 @@
|
|
|
4385
4385
|
"refPlaceholder": "Id або URL сторінки (напр. сторінка Confluence/Notion чи URL файлу GitHub)",
|
|
4386
4386
|
"tagsPlaceholder": "Теги, розділені комами (необов'язково)",
|
|
4387
4387
|
"link": "Прив'язати як живий фрагмент",
|
|
4388
|
-
"githubBrowseHint": "Перегляньте репозиторій і виберіть
|
|
4388
|
+
"githubBrowseHint": "Перегляньте репозиторій і виберіть один або кілька файлів для зв’язування.",
|
|
4389
|
+
"selectedFiles": "Вибрані файли ({count})",
|
|
4390
|
+
"removeFile": "Видалити файл"
|
|
4389
4391
|
},
|
|
4390
4392
|
"sources": {
|
|
4391
4393
|
"metaSynced": "синхронізовано · ref {ref}",
|
|
@@ -4419,7 +4421,8 @@
|
|
|
4419
4421
|
"upToDate": "Актуально",
|
|
4420
4422
|
"checkSourceFailed": "Не вдалося перевірити джерело",
|
|
4421
4423
|
"sourceUnlinked": "Джерело відв'язано",
|
|
4422
|
-
"unlinkSourceFailed": "Не вдалося відв'язати джерело"
|
|
4424
|
+
"unlinkSourceFailed": "Не вдалося відв'язати джерело",
|
|
4425
|
+
"documentsLinked": "Прив’язано {count} документ як живий фрагмент | Прив’язано {count} документи як живі фрагменти | Прив’язано {count} документів як живі фрагменти"
|
|
4423
4426
|
},
|
|
4424
4427
|
"confirmRemove": {
|
|
4425
4428
|
"title": "Видалити цей фрагмент?",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.141.
|
|
3
|
+
"version": "0.141.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",
|