@cat-factory/app 0.141.1 → 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.
@@ -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 suffix = r.private ? t('github.addService.repoLabel.private') : ''
55
- return { label: `${r.owner}/${r.name}${suffix}`, value: r.githubId }
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(() => {
@@ -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
- () => !docLinkDisabled.value && docDraft.value.source && docDraft.value.ref.trim(),
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 the file instead of
190
- // hand-typing a `owner/repo:path` ref. Picking a file fills `docDraft.ref` (still
191
- // editable, so pasting a URL/shorthand keeps working). Only offered when the App is
192
- // connected; other sources (Confluence/Notion) keep the free-text ref field.
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
- const docFilePath = ref<string | undefined>(undefined)
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 derived ref) whenever the selected source changes.
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
- docFilePath.value = undefined
210
+ docFilePaths.value = []
206
211
  docDraft.value.ref = ''
207
212
  },
208
213
  )
209
214
 
210
- // A new repo selection clears the previously-browsed file.
211
- watch(docRepoId, () => {
212
- docFilePath.value = undefined
213
- })
214
-
215
- // Derive the canonical `owner/repo:path` ref the GitHub docs provider expects.
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
- await library.createDocumentFragment({
230
- source: docDraft.value.source as DocumentSourceKind,
231
- ref: docDraft.value.ref.trim(),
232
- tags: docDraft.value.tags
233
- .split(',')
234
- .map((t) => t.trim())
235
- .filter(Boolean),
236
- })
237
- docDraft.value = { source: '', ref: '', tags: '' }
238
- toast.add({ title: t('fragments.toast.documentLinked'), icon: 'i-lucide-link' })
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 the file instead of typing the ref -->
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 v-model="docFilePath" :repo-github-id="docRepoId" mode="file" />
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 v-model="docDraft.ref" :placeholder="t('fragments.documents.refPlaceholder')" />
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 suffix = r.private ? t('github.addService.repoLabel.private') : ''
38
- return { label: `${r.owner}/${r.name}${suffix}`, value: r.githubId }
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))
@@ -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 die zu verknüpfende Datei."
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?",
@@ -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 the file to link."
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?",
@@ -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 el archivo a enlazar."
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?",
@@ -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 le fichier à lier."
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 ?",
@@ -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": "למחוק את המקטע הזה?",
@@ -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 il file da collegare."
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?",
@@ -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": "このフラグメントを削除しますか?",
@@ -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 plik do połączenia."
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?",
@@ -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 dosyayı seçin."
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?",
@@ -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.1",
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",