feat: add repository filters to selection dialog
This commit is contained in:
@@ -305,6 +305,8 @@ SELECTION_DIALOG_TEXTS = {
|
||||
"clear_all": "Clear all",
|
||||
"quick_select": "Filter by type",
|
||||
"all_types": "All",
|
||||
"repo_filter": "Filter by repository",
|
||||
"all_repos": "All repositories",
|
||||
"search_label": "Search",
|
||||
"search_placeholder": "Search title, description, file path...",
|
||||
"no_results": "No plugins match the current filter.",
|
||||
@@ -321,6 +323,8 @@ SELECTION_DIALOG_TEXTS = {
|
||||
"clear_all": "清空",
|
||||
"quick_select": "按类型筛选",
|
||||
"all_types": "全部",
|
||||
"repo_filter": "按仓库筛选",
|
||||
"all_repos": "全部仓库",
|
||||
"search_label": "搜索",
|
||||
"search_placeholder": "搜索标题、描述、文件路径...",
|
||||
"no_results": "当前筛选条件下没有匹配的插件。",
|
||||
@@ -337,6 +341,8 @@ SELECTION_DIALOG_TEXTS = {
|
||||
"clear_all": "清空",
|
||||
"quick_select": "按類型篩選",
|
||||
"all_types": "全部",
|
||||
"repo_filter": "按倉庫篩選",
|
||||
"all_repos": "全部倉庫",
|
||||
"search_label": "搜尋",
|
||||
"search_placeholder": "搜尋標題、描述、檔案路徑...",
|
||||
"no_results": "目前篩選條件下沒有相符的外掛。",
|
||||
@@ -353,6 +359,8 @@ SELECTION_DIALOG_TEXTS = {
|
||||
"clear_all": "清空",
|
||||
"quick_select": "按類型篩選",
|
||||
"all_types": "全部",
|
||||
"repo_filter": "按倉庫篩選",
|
||||
"all_repos": "全部倉庫",
|
||||
"search_label": "搜尋",
|
||||
"search_placeholder": "搜尋標題、描述、檔案路徑...",
|
||||
"no_results": "目前篩選條件下沒有符合的外掛。",
|
||||
@@ -369,6 +377,8 @@ SELECTION_DIALOG_TEXTS = {
|
||||
"clear_all": "선택 해제",
|
||||
"quick_select": "유형별 필터",
|
||||
"all_types": "전체",
|
||||
"repo_filter": "저장소별 필터",
|
||||
"all_repos": "전체 저장소",
|
||||
"search_label": "검색",
|
||||
"search_placeholder": "제목, 설명, 파일 경로 검색...",
|
||||
"no_results": "현재 필터와 일치하는 플러그인이 없습니다.",
|
||||
@@ -385,6 +395,8 @@ SELECTION_DIALOG_TEXTS = {
|
||||
"clear_all": "クリア",
|
||||
"quick_select": "タイプで絞り込み",
|
||||
"all_types": "すべて",
|
||||
"repo_filter": "リポジトリで絞り込み",
|
||||
"all_repos": "すべてのリポジトリ",
|
||||
"search_label": "検索",
|
||||
"search_placeholder": "タイトル、説明、ファイルパスを検索...",
|
||||
"no_results": "現在の条件に一致するプラグインはありません。",
|
||||
@@ -401,6 +413,8 @@ SELECTION_DIALOG_TEXTS = {
|
||||
"clear_all": "Tout effacer",
|
||||
"quick_select": "Filtrer par type",
|
||||
"all_types": "Tous",
|
||||
"repo_filter": "Filtrer par dépôt",
|
||||
"all_repos": "Tous les dépôts",
|
||||
"search_label": "Rechercher",
|
||||
"search_placeholder": "Rechercher par titre, description, fichier...",
|
||||
"no_results": "Aucun plugin ne correspond au filtre actuel.",
|
||||
@@ -417,6 +431,8 @@ SELECTION_DIALOG_TEXTS = {
|
||||
"clear_all": "Auswahl löschen",
|
||||
"quick_select": "Nach Typ filtern",
|
||||
"all_types": "Alle",
|
||||
"repo_filter": "Nach Repository filtern",
|
||||
"all_repos": "Alle Repositories",
|
||||
"search_label": "Suchen",
|
||||
"search_placeholder": "Titel, Beschreibung, Dateipfad durchsuchen...",
|
||||
"no_results": "Keine Plugins entsprechen dem aktuellen Filter.",
|
||||
@@ -433,6 +449,8 @@ SELECTION_DIALOG_TEXTS = {
|
||||
"clear_all": "Limpiar",
|
||||
"quick_select": "Filtrar por tipo",
|
||||
"all_types": "Todos",
|
||||
"repo_filter": "Filtrar por repositorio",
|
||||
"all_repos": "Todos los repositorios",
|
||||
"search_label": "Buscar",
|
||||
"search_placeholder": "Buscar por titulo, descripcion o archivo...",
|
||||
"no_results": "Ningun plugin coincide con el filtro actual.",
|
||||
@@ -449,6 +467,8 @@ SELECTION_DIALOG_TEXTS = {
|
||||
"clear_all": "Cancella",
|
||||
"quick_select": "Filtra per tipo",
|
||||
"all_types": "Tutti",
|
||||
"repo_filter": "Filtra per repository",
|
||||
"all_repos": "Tutti i repository",
|
||||
"search_label": "Cerca",
|
||||
"search_placeholder": "Cerca per titolo, descrizione o file...",
|
||||
"no_results": "Nessun plugin corrisponde al filtro attuale.",
|
||||
@@ -465,6 +485,8 @@ SELECTION_DIALOG_TEXTS = {
|
||||
"clear_all": "Bỏ chọn",
|
||||
"quick_select": "Lọc theo loại",
|
||||
"all_types": "Tất cả",
|
||||
"repo_filter": "Lọc theo kho",
|
||||
"all_repos": "Tất cả kho",
|
||||
"search_label": "Tìm kiếm",
|
||||
"search_placeholder": "Tìm theo tiêu đề, mô tả, đường dẫn tệp...",
|
||||
"no_results": "Không có plugin nào khớp với bộ lọc hiện tại.",
|
||||
@@ -1006,6 +1028,7 @@ def _build_selection_dialog_js(
|
||||
" }",
|
||||
" const selected = new Set(options.map((item) => item.id));",
|
||||
" let activeFilter = '';",
|
||||
" let activeRepoFilter = '';",
|
||||
" let searchTerm = '';",
|
||||
" const escapeHtml = (value) => String(value ?? '').replace(/[&<>\"']/g, (char) => ({",
|
||||
" '&': '&',",
|
||||
@@ -1056,6 +1079,10 @@ def _build_selection_dialog_js(
|
||||
" <div style=\"font-size:12px;font-weight:700;color:#475569;text-transform:uppercase;letter-spacing:0.04em\">${escapeHtml(ui.quick_select)}</div>",
|
||||
" <div id=\"batch-install-plugin-selector-types\" style=\"display:flex;gap:8px;flex-wrap:wrap\"></div>",
|
||||
" </div>",
|
||||
" <div id=\"batch-install-plugin-selector-repo-row\" style=\"display:flex;gap:10px;align-items:center;flex-wrap:wrap\">",
|
||||
" <div style=\"font-size:12px;font-weight:700;color:#475569;text-transform:uppercase;letter-spacing:0.04em\">${escapeHtml(ui.repo_filter)}</div>",
|
||||
" <div id=\"batch-install-plugin-selector-repos\" style=\"display:flex;gap:8px;flex-wrap:wrap\"></div>",
|
||||
" </div>",
|
||||
" <div style=\"display:grid;gap:6px\">",
|
||||
" <div style=\"font-size:12px;font-weight:700;color:#475569;text-transform:uppercase;letter-spacing:0.04em\">${escapeHtml(ui.search_label)}</div>",
|
||||
" <input id=\"batch-install-plugin-selector-search\" type=\"text\" placeholder=\"${escapeHtml(ui.search_placeholder)}\" style=\"width:100%;padding:10px 12px;border:1px solid #cbd5e1;border-radius:12px;background:#fff;color:#0f172a;font-size:14px;outline:none;box-sizing:border-box\" />",
|
||||
@@ -1076,6 +1103,8 @@ def _build_selection_dialog_js(
|
||||
" const countEl = overlay.querySelector('#batch-install-plugin-selector-count');",
|
||||
" const hintEl = overlay.querySelector('#batch-install-plugin-selector-hint');",
|
||||
" const typesEl = overlay.querySelector('#batch-install-plugin-selector-types');",
|
||||
" const repoRowEl = overlay.querySelector('#batch-install-plugin-selector-repo-row');",
|
||||
" const reposEl = overlay.querySelector('#batch-install-plugin-selector-repos');",
|
||||
" const searchInput = overlay.querySelector('#batch-install-plugin-selector-search');",
|
||||
" const submitBtn = overlay.querySelector('#batch-install-plugin-selector-submit');",
|
||||
" const cancelBtn = overlay.querySelector('#batch-install-plugin-selector-cancel');",
|
||||
@@ -1088,18 +1117,37 @@ def _build_selection_dialog_js(
|
||||
" groups[item.type].push(item);",
|
||||
" return groups;",
|
||||
" }, {});",
|
||||
" const repoMap = options.reduce((groups, item) => {",
|
||||
" if (!groups[item.repo]) {",
|
||||
" groups[item.repo] = [];",
|
||||
" }",
|
||||
" groups[item.repo].push(item);",
|
||||
" return groups;",
|
||||
" }, {});",
|
||||
" const typeEntries = Object.entries(typeMap);",
|
||||
" const repoEntries = Object.entries(repoMap);",
|
||||
" const matchesSearch = (item) => {",
|
||||
" const haystack = [item.title, item.description, item.file_path, item.type, item.repo].join(' ').toLowerCase();",
|
||||
" return !searchTerm || haystack.includes(searchTerm);",
|
||||
" };",
|
||||
" const getVisibleOptions = () => options.filter((item) => {",
|
||||
" const matchesType = !activeFilter || item.type === activeFilter;",
|
||||
" const haystack = [item.title, item.description, item.file_path, item.type, item.repo].join(' ').toLowerCase();",
|
||||
" const matchesSearch = !searchTerm || haystack.includes(searchTerm);",
|
||||
" return matchesType && matchesSearch;",
|
||||
" const matchesRepo = !activeRepoFilter || item.repo === activeRepoFilter;",
|
||||
" return matchesType && matchesRepo && matchesSearch(item);",
|
||||
" });",
|
||||
" const syncSelectionToVisible = () => {",
|
||||
" selected.clear();",
|
||||
" getVisibleOptions().forEach((item) => selected.add(item.id));",
|
||||
" };",
|
||||
" const formatMultilineText = (value) => escapeHtml(value).replace(/\\n+/g, '<br />');",
|
||||
" hintEl.textContent = ui.hint || '';",
|
||||
" hintEl.style.display = ui.hint ? 'block' : 'none';",
|
||||
" const renderTypeButtons = () => {",
|
||||
" const filterEntries = [['', options], ...typeEntries];",
|
||||
" const scopedOptions = options.filter((item) => {",
|
||||
" const matchesRepo = !activeRepoFilter || item.repo === activeRepoFilter;",
|
||||
" return matchesRepo && matchesSearch(item);",
|
||||
" });",
|
||||
" const filterEntries = [['', scopedOptions], ...typeEntries.map(([type]) => [type, scopedOptions.filter((item) => item.type === type)])];",
|
||||
" typesEl.innerHTML = filterEntries.map(([type, items]) => {",
|
||||
" const isActive = activeFilter === type;",
|
||||
" const background = isActive ? '#0f172a' : '#ffffff';",
|
||||
@@ -1117,8 +1165,42 @@ def _build_selection_dialog_js(
|
||||
" button.addEventListener('click', () => {",
|
||||
" const pluginType = button.getAttribute('data-plugin-type') || '';",
|
||||
" activeFilter = activeFilter === pluginType ? '' : pluginType;",
|
||||
" selected.clear();",
|
||||
" getVisibleOptions().forEach((item) => selected.add(item.id));",
|
||||
" syncSelectionToVisible();",
|
||||
" renderList();",
|
||||
" });",
|
||||
" });",
|
||||
" };",
|
||||
" const renderRepoButtons = () => {",
|
||||
" if (repoEntries.length <= 1) {",
|
||||
" repoRowEl.style.display = 'none';",
|
||||
" reposEl.innerHTML = '';",
|
||||
" activeRepoFilter = '';",
|
||||
" return;",
|
||||
" }",
|
||||
" repoRowEl.style.display = 'flex';",
|
||||
" const scopedOptions = options.filter((item) => {",
|
||||
" const matchesType = !activeFilter || item.type === activeFilter;",
|
||||
" return matchesType && matchesSearch(item);",
|
||||
" });",
|
||||
" const filterEntries = [['', scopedOptions], ...repoEntries.map(([repoName]) => [repoName, scopedOptions.filter((item) => item.repo === repoName)])];",
|
||||
" reposEl.innerHTML = filterEntries.map(([repoName, items]) => {",
|
||||
" const isActive = activeRepoFilter === repoName;",
|
||||
" const background = isActive ? '#1d4ed8' : '#ffffff';",
|
||||
" const color = isActive ? '#ffffff' : '#1d4ed8';",
|
||||
" const border = isActive ? '#1d4ed8' : '#bfdbfe';",
|
||||
" const label = repoName || ui.all_repos;",
|
||||
" return `",
|
||||
" <button type=\"button\" data-plugin-repo=\"${escapeHtml(repoName)}\" style=\"padding:7px 12px;border:1px solid ${border};border-radius:999px;background:${background};color:${color};font-size:12px;font-weight:700;cursor:pointer;display:inline-flex;gap:8px;align-items:center\">",
|
||||
" <span>${escapeHtml(label)}</span>",
|
||||
" <span style=\"display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:${isActive ? 'rgba(255,255,255,0.16)' : '#dbeafe'};color:${isActive ? '#ffffff' : '#1e3a8a'}\">${items.length}</span>",
|
||||
" </button>",
|
||||
" `;",
|
||||
" }).join('');",
|
||||
" reposEl.querySelectorAll('button[data-plugin-repo]').forEach((button) => {",
|
||||
" button.addEventListener('click', () => {",
|
||||
" const repoName = button.getAttribute('data-plugin-repo') || '';",
|
||||
" activeRepoFilter = activeRepoFilter === repoName ? '' : repoName;",
|
||||
" syncSelectionToVisible();",
|
||||
" renderList();",
|
||||
" });",
|
||||
" });",
|
||||
@@ -1129,6 +1211,7 @@ def _build_selection_dialog_js(
|
||||
" submitBtn.style.opacity = selected.size === 0 ? '0.45' : '1';",
|
||||
" submitBtn.style.cursor = selected.size === 0 ? 'not-allowed' : 'pointer';",
|
||||
" renderTypeButtons();",
|
||||
" renderRepoButtons();",
|
||||
" };",
|
||||
" const renderOptionCard = (item) => {",
|
||||
" const checked = selected.has(item.id) ? 'checked' : '';",
|
||||
@@ -1144,9 +1227,10 @@ def _build_selection_dialog_js(
|
||||
" <div style=\"min-width:0;display:grid;gap:6px\">",
|
||||
" <div style=\"display:flex;gap:10px;align-items:center;flex-wrap:wrap\">",
|
||||
" <span style=\"display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;background:#f1f5f9;color:#334155;font-size:12px;font-weight:700;text-transform:uppercase\">${escapeHtml(item.type)}</span>",
|
||||
" <span style=\"display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;background:#eff6ff;color:#1d4ed8;font-size:12px;font-weight:700\">${escapeHtml(item.repo)}</span>",
|
||||
" <span style=\"font-size:15px;font-weight:700;color:#0f172a;word-break:break-word\">${escapeHtml(item.title)}</span>",
|
||||
" </div>",
|
||||
" <div style=\"font-size:12px;color:#475569;word-break:break-word\">${escapeHtml(ui.repo_label)}: ${escapeHtml(item.repo)} · ${escapeHtml(ui.version_label)}: ${escapeHtml(item.version)} · ${escapeHtml(ui.file_label)}: ${escapeHtml(item.file_path)}</div>",
|
||||
" <div style=\"font-size:12px;color:#475569;word-break:break-word\">${escapeHtml(ui.version_label)}: ${escapeHtml(item.version)} · ${escapeHtml(ui.file_label)}: ${escapeHtml(item.file_path)}</div>",
|
||||
" ${description}",
|
||||
" </div>",
|
||||
" </label>",
|
||||
@@ -1169,7 +1253,7 @@ def _build_selection_dialog_js(
|
||||
" listEl.innerHTML = Object.entries(groups).map(([repoName, items]) => `",
|
||||
" <section style=\"display:grid;gap:10px\">",
|
||||
" <div style=\"display:flex;justify-content:space-between;gap:12px;align-items:center;flex-wrap:wrap;padding:0 2px\">",
|
||||
" <div style=\"font-size:13px;font-weight:700;color:#0f172a;word-break:break-word\">${escapeHtml(repoName)}</div>",
|
||||
" <div style=\"display:inline-flex;align-items:center;gap:8px;padding:6px 12px;border-radius:999px;background:#eff6ff;color:#1d4ed8;font-size:12px;font-weight:700;word-break:break-word\">${escapeHtml(repoName)}</div>",
|
||||
" <div style=\"display:inline-flex;align-items:center;gap:8px;padding:4px 10px;border-radius:999px;background:#f8fafc;color:#475569;font-size:12px;font-weight:600\">${items.length}</div>",
|
||||
" </div>",
|
||||
" <div style=\"display:grid;gap:12px\">${items.map((item) => renderOptionCard(item)).join('')}</div>",
|
||||
@@ -1219,8 +1303,7 @@ def _build_selection_dialog_js(
|
||||
" });",
|
||||
" searchInput.addEventListener('input', () => {",
|
||||
" searchTerm = searchInput.value.trim().toLowerCase();",
|
||||
" selected.clear();",
|
||||
" getVisibleOptions().forEach((item) => selected.add(item.id));",
|
||||
" syncSelectionToVisible();",
|
||||
" renderList();",
|
||||
" });",
|
||||
" cancelBtn.addEventListener('click', () => finish(false));",
|
||||
@@ -1279,6 +1362,8 @@ async def _request_plugin_selection(
|
||||
"clear_all": _selection_t(lang, "clear_all"),
|
||||
"quick_select": _selection_t(lang, "quick_select"),
|
||||
"all_types": _selection_t(lang, "all_types"),
|
||||
"repo_filter": _selection_t(lang, "repo_filter"),
|
||||
"all_repos": _selection_t(lang, "all_repos"),
|
||||
"search_label": _selection_t(lang, "search_label"),
|
||||
"search_placeholder": _selection_t(lang, "search_placeholder"),
|
||||
"no_results": _selection_t(lang, "no_results"),
|
||||
|
||||
Reference in New Issue
Block a user