@cat-factory/app 0.47.10 → 0.47.11
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.
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// pinned to a subdirectory. When the selected repo is a monorepo, the user
|
|
11
11
|
// browses its tree and picks the service's directory before adding (and may add
|
|
12
12
|
// more than one, a subset of the repo's services).
|
|
13
|
+
import { refDebounced } from '@vueuse/core'
|
|
13
14
|
import GitHubConnect from '~/components/github/GitHubConnect.vue'
|
|
14
15
|
import RepoTreeBrowser from '~/components/github/RepoTreeBrowser.vue'
|
|
15
16
|
import ServiceTestConfig from '~/components/panels/inspector/ServiceTestConfig.vue'
|
|
@@ -76,16 +77,53 @@ const repoItems = computed(() =>
|
|
|
76
77
|
}),
|
|
77
78
|
)
|
|
78
79
|
|
|
79
|
-
// The PAT (or a wide App install) can expose hundreds of repos, too many for a plain
|
|
80
|
-
// dropdown —
|
|
81
|
-
//
|
|
80
|
+
// The PAT (or a wide App install) can expose hundreds of repos, far too many for a plain
|
|
81
|
+
// dropdown — so the picker is a typeahead combobox. The user types and matching repos
|
|
82
|
+
// surface: matching is a debounced, case-insensitive substring over `owner/name` (so any
|
|
83
|
+
// part of either matches). For a LARGE list the search only kicks in once at least
|
|
84
|
+
// MIN_SEARCH_LEN characters are typed, to keep early keystrokes from listing hundreds of
|
|
85
|
+
// rows; but when the whole list is small enough to browse (<= BROWSE_ALL_MAX) the gate is
|
|
86
|
+
// dropped and every repo is offered up-front (typing then narrows), so a handful of repos
|
|
87
|
+
// stays pickable without having to type — matching the old always-open dropdown.
|
|
88
|
+
const MIN_SEARCH_LEN = 3
|
|
89
|
+
const BROWSE_ALL_MAX = 25
|
|
82
90
|
const repoSearch = ref('')
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
91
|
+
const repoSearchDebounced = refDebounced(repoSearch, 250)
|
|
92
|
+
// Trimmed (original case) for display; lowercased for matching.
|
|
93
|
+
const repoQueryRaw = computed(() => repoSearchDebounced.value.trim())
|
|
94
|
+
const repoQuery = computed(() => repoQueryRaw.value.toLowerCase())
|
|
95
|
+
|
|
96
|
+
// Small lists are browseable without typing; large lists require the min-length gate.
|
|
97
|
+
const browseAll = computed(() => repoItems.value.length <= BROWSE_ALL_MAX)
|
|
98
|
+
// True only on a large list whose query is still too short to search.
|
|
99
|
+
const belowMinChars = computed(() => !browseAll.value && repoQuery.value.length < MIN_SEARCH_LEN)
|
|
100
|
+
|
|
101
|
+
// Matches for the current query. On a small list an empty query lists everything; on a
|
|
102
|
+
// large list nothing surfaces until the query passes the min length.
|
|
103
|
+
const queryMatches = computed(() => {
|
|
104
|
+
if (belowMinChars.value) return []
|
|
105
|
+
if (!repoQuery.value) return repoItems.value
|
|
106
|
+
return repoItems.value.filter((r) => r.search.includes(repoQuery.value))
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
// Items fed to the combobox: the query matches plus the current selection kept present,
|
|
110
|
+
// so the menu can still render the selected repo's label once the (reset) search term no
|
|
111
|
+
// longer matches it.
|
|
112
|
+
const repoMenuItems = computed(() => {
|
|
113
|
+
const matches = queryMatches.value
|
|
114
|
+
if (selectedRepoId.value === undefined) return matches
|
|
115
|
+
if (matches.some((r) => r.value === selectedRepoId.value)) return matches
|
|
116
|
+
const selected = repoItems.value.find((r) => r.value === selectedRepoId.value)
|
|
117
|
+
return selected ? [selected, ...matches] : matches
|
|
87
118
|
})
|
|
88
119
|
|
|
120
|
+
// The count summary under the field is shown only when it's meaningful: there are matches
|
|
121
|
+
// to count AND the user is actually searching (or browsing a small list). After a
|
|
122
|
+
// selection resets the search term this is false, so the field doesn't claim "Showing 0"
|
|
123
|
+
// or nag "type 3 characters" right under the repo the user just picked. The zero-match and
|
|
124
|
+
// min-length messages are owned by the combobox's own empty state instead.
|
|
125
|
+
const showResultCount = computed(() => !belowMinChars.value && queryMatches.value.length > 0)
|
|
126
|
+
|
|
89
127
|
const hasRepos = computed(() => github.availableRepos.length > 0)
|
|
90
128
|
const selectedRepo = computed(() =>
|
|
91
129
|
github.availableRepos.find((r) => r.githubId === selectedRepoId.value),
|
|
@@ -121,6 +159,13 @@ function resetSelection() {
|
|
|
121
159
|
repoSearch.value = ''
|
|
122
160
|
}
|
|
123
161
|
|
|
162
|
+
// Clear the current repo selection (the combobox's trailing ✕) so the user can pick a
|
|
163
|
+
// different one — drops the selection-dependent state and resets the search term. The
|
|
164
|
+
// combobox has no built-in deselect, so the field would otherwise stay pinned to a repo.
|
|
165
|
+
function clearSelection() {
|
|
166
|
+
resetSelection()
|
|
167
|
+
}
|
|
168
|
+
|
|
124
169
|
// The App's installation settings page — where the user grants it access to a
|
|
125
170
|
// repo it can't see yet (mirrors the bootstrap modal's "grant access" link).
|
|
126
171
|
const manageInstallUrl = computed(() => {
|
|
@@ -236,34 +281,42 @@ function done() {
|
|
|
236
281
|
{{ t('github.addService.noReposAvailable') }}
|
|
237
282
|
</div>
|
|
238
283
|
<div v-else class="space-y-1.5">
|
|
239
|
-
<
|
|
240
|
-
v-model="
|
|
284
|
+
<UInputMenu
|
|
285
|
+
v-model="selectedRepoId"
|
|
286
|
+
v-model:search-term="repoSearch"
|
|
287
|
+
:items="repoMenuItems"
|
|
288
|
+
:ignore-filter="true"
|
|
289
|
+
value-key="value"
|
|
290
|
+
:loading="github.loadingAvailable"
|
|
241
291
|
icon="i-lucide-search"
|
|
242
|
-
:placeholder="t('github.addService.
|
|
292
|
+
:placeholder="t('github.addService.searchPlaceholder')"
|
|
243
293
|
class="w-full"
|
|
244
|
-
:ui="{ trailing: 'pe-1' }"
|
|
245
294
|
>
|
|
246
|
-
<template v-if="
|
|
295
|
+
<template v-if="selectedRepoId !== undefined" #trailing>
|
|
247
296
|
<UButton
|
|
248
297
|
color="neutral"
|
|
249
298
|
variant="link"
|
|
250
299
|
size="sm"
|
|
251
300
|
icon="i-lucide-x"
|
|
252
|
-
:aria-label="t('github.addService.
|
|
253
|
-
@click="
|
|
301
|
+
:aria-label="t('github.addService.clearSelection')"
|
|
302
|
+
@click.stop="clearSelection"
|
|
254
303
|
/>
|
|
255
304
|
</template>
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
305
|
+
<template #empty>
|
|
306
|
+
<span v-if="belowMinChars">
|
|
307
|
+
{{
|
|
308
|
+
t('github.addService.searchMinChars', { min: MIN_SEARCH_LEN }, MIN_SEARCH_LEN)
|
|
309
|
+
}}
|
|
310
|
+
</span>
|
|
311
|
+
<span v-else>{{
|
|
312
|
+
t('github.addService.noMatches', { query: repoQueryRaw })
|
|
313
|
+
}}</span>
|
|
314
|
+
</template>
|
|
315
|
+
</UInputMenu>
|
|
316
|
+
<p v-if="showResultCount" class="text-xs text-slate-500">
|
|
264
317
|
{{
|
|
265
318
|
t('github.addService.showingCount', {
|
|
266
|
-
shown:
|
|
319
|
+
shown: queryMatches.length,
|
|
267
320
|
total: repoItems.length,
|
|
268
321
|
})
|
|
269
322
|
}}
|
package/i18n/locales/en.json
CHANGED
|
@@ -1936,9 +1936,13 @@
|
|
|
1936
1936
|
"repository": "Repository",
|
|
1937
1937
|
"repositoryHint": "Repositories the GitHub App can access. Don't see yours? Grant the App access below, then refresh.",
|
|
1938
1938
|
"noReposAvailable": "No repositories available yet. Grant the App access to one below, then refresh.",
|
|
1939
|
-
"
|
|
1940
|
-
"
|
|
1941
|
-
"
|
|
1939
|
+
"searchPlaceholder": "Search repositories by owner or name…",
|
|
1940
|
+
"searchMinChars": "Type at least {min} character to search. | Type at least {min} characters to search.",
|
|
1941
|
+
"@searchMinChars": {
|
|
1942
|
+
"description": "Shown when a large repo list needs a typed query before searching. Resolved via t(key, { min }, min) so {min} also drives the plural choice; min is always 2 or more. Provide ALL plural forms your language needs (English has 2; Polish/Ukrainian need 3 - one/few/many - and rely on the custom pluralRules wired in i18n.config.ts)."
|
|
1943
|
+
},
|
|
1944
|
+
"noMatches": "No repositories found for {query}.",
|
|
1945
|
+
"clearSelection": "Clear selection",
|
|
1942
1946
|
"showingCount": "Showing {shown} of {total} repositories.",
|
|
1943
1947
|
"repoLabel": {
|
|
1944
1948
|
"private": " (private)",
|
package/i18n/locales/es.json
CHANGED
|
@@ -1885,9 +1885,10 @@
|
|
|
1885
1885
|
"repository": "Repositorio",
|
|
1886
1886
|
"repositoryHint": "Repositorios a los que la GitHub App puede acceder. ¿No ves el tuyo? Concede acceso a la App abajo y luego actualiza.",
|
|
1887
1887
|
"noReposAvailable": "Aún no hay repositorios disponibles. Concede acceso a la App a uno abajo y luego actualiza.",
|
|
1888
|
-
"
|
|
1889
|
-
"
|
|
1890
|
-
"
|
|
1888
|
+
"searchPlaceholder": "Busca repositorios por propietario o nombre…",
|
|
1889
|
+
"searchMinChars": "Escribe al menos {min} carácter para buscar. | Escribe al menos {min} caracteres para buscar.",
|
|
1890
|
+
"noMatches": "No se encontraron repositorios para {query}.",
|
|
1891
|
+
"clearSelection": "Borrar selección",
|
|
1891
1892
|
"showingCount": "Mostrando {shown} de {total} repositorios.",
|
|
1892
1893
|
"repoLabel": {
|
|
1893
1894
|
"private": " (privado)",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -1885,9 +1885,10 @@
|
|
|
1885
1885
|
"repository": "Dépôt",
|
|
1886
1886
|
"repositoryHint": "Dépôts auxquels la GitHub App peut accéder. Vous ne voyez pas le vôtre ? Accordez l'accès à l'App ci-dessous, puis actualisez.",
|
|
1887
1887
|
"noReposAvailable": "Aucun dépôt disponible pour le moment. Accordez l'accès de l'App à l'un d'eux ci-dessous, puis actualisez.",
|
|
1888
|
-
"
|
|
1889
|
-
"
|
|
1890
|
-
"
|
|
1888
|
+
"searchPlaceholder": "Rechercher des dépôts par propriétaire ou nom…",
|
|
1889
|
+
"searchMinChars": "Saisissez au moins {min} caractère pour rechercher. | Saisissez au moins {min} caractères pour rechercher.",
|
|
1890
|
+
"noMatches": "Aucun dépôt trouvé pour {query}.",
|
|
1891
|
+
"clearSelection": "Effacer la sélection",
|
|
1891
1892
|
"showingCount": "Affichage de {shown} dépôts sur {total}.",
|
|
1892
1893
|
"repoLabel": {
|
|
1893
1894
|
"private": " (privé)",
|
package/i18n/locales/pl.json
CHANGED
|
@@ -1885,9 +1885,10 @@
|
|
|
1885
1885
|
"repository": "Repozytorium",
|
|
1886
1886
|
"repositoryHint": "Repozytoria, do których aplikacja GitHub ma dostęp. Nie widzisz swojego? Przyznaj aplikacji dostęp poniżej, a następnie odśwież.",
|
|
1887
1887
|
"noReposAvailable": "Brak dostępnych repozytoriów. Przyznaj aplikacji dostęp do jednego poniżej, a następnie odśwież.",
|
|
1888
|
-
"
|
|
1889
|
-
"
|
|
1890
|
-
"
|
|
1888
|
+
"searchPlaceholder": "Szukaj repozytoriów według właściciela lub nazwy…",
|
|
1889
|
+
"searchMinChars": "Wpisz co najmniej {min} znak, aby wyszukać. | Wpisz co najmniej {min} znaki, aby wyszukać. | Wpisz co najmniej {min} znaków, aby wyszukać.",
|
|
1890
|
+
"noMatches": "Nie znaleziono repozytoriów dla {query}.",
|
|
1891
|
+
"clearSelection": "Wyczyść wybór",
|
|
1891
1892
|
"showingCount": "Wyświetlanie {shown} z {total} repozytoriów.",
|
|
1892
1893
|
"repoLabel": {
|
|
1893
1894
|
"private": " (prywatne)",
|
package/i18n/locales/uk.json
CHANGED
|
@@ -1885,9 +1885,10 @@
|
|
|
1885
1885
|
"repository": "Репозиторій",
|
|
1886
1886
|
"repositoryHint": "Репозиторії, до яких застосунок GitHub має доступ. Не бачите свого? Надайте застосунку доступ нижче, а потім оновіть.",
|
|
1887
1887
|
"noReposAvailable": "Поки що немає доступних репозиторіїв. Надайте застосунку доступ до одного нижче, а потім оновіть.",
|
|
1888
|
-
"
|
|
1889
|
-
"
|
|
1890
|
-
"
|
|
1888
|
+
"searchPlaceholder": "Шукайте репозиторії за власником або назвою…",
|
|
1889
|
+
"searchMinChars": "Введіть щонайменше {min} символ для пошуку. | Введіть щонайменше {min} символи для пошуку. | Введіть щонайменше {min} символів для пошуку.",
|
|
1890
|
+
"noMatches": "Не знайдено репозиторіїв для {query}.",
|
|
1891
|
+
"clearSelection": "Очистити вибір",
|
|
1891
1892
|
"showingCount": "Показано {shown} із {total} репозиторіїв.",
|
|
1892
1893
|
"repoLabel": {
|
|
1893
1894
|
"private": " (приватний)",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.47.
|
|
3
|
+
"version": "0.47.11",
|
|
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",
|