Palette: add browse filter bar for create file/folder, delete folder actions
CI / lint (push) Failing after 4s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 9s

This commit is contained in:
2026-06-03 10:26:07 -04:00
parent b513add15b
commit 81e53a930a
2 changed files with 87 additions and 2 deletions
+70 -2
View File
@@ -17,6 +17,7 @@ function getCurrentFile() {
// ── DOM / state ──
let _overlay = null, _input = null, _results = null, _mode = 'files', _sel = 0, _to = null, _items = [];
let _browse = { action: null, vault: null, path: '' };
let _browseFilterEl = null, _browseFilter = '', _allItems = [];
// ── Commands ──
const CMDS = [
@@ -36,6 +37,7 @@ const CMDS = [
// ── Browse: vault picker ──
function startBrowse(action) {
_browse = { action, vault: null, path: '' };
_browseFilter = ''; _allItems = [];
switchMode('browse-vault');
renderVaultPicker();
}
@@ -98,9 +100,57 @@ async function renderBrowse() {
if (_items.length === 0) _items.push({ type:'_empty', label: '📂 Dossier vide', sublabel: '' });
} catch { _items = [{ type:'_error', label: 'Erreur', sublabel: '' }]; }
// Store all items for filtering
_allItems = _items;
applyBrowseFilter();
}
// ── Browse filter ──
function applyBrowseFilter() {
const q = _browseFilter.toLowerCase().trim();
const shouldKeep = (it) => {
if (!q) return true;
const label = (it.label || '').toLowerCase();
const sublabel = (it.sublabel || '').toLowerCase();
return label.includes(q) || sublabel.includes(q);
};
// Always keep action row and parent nav
_items = _allItems.filter(it => it.type === '_action' || it.type === '_nav' && it.label.includes('..') || shouldKeep(it));
render();
}
function onBrowseFilterInput() {
_browseFilter = _browseFilterEl ? _browseFilterEl.value : '';
applyBrowseFilter();
}
function onBrowseFilterKey(e) {
if (e.key === 'Escape') {
e.preventDefault();
if (_browseFilter) {
// Clear filter
_browseFilter = '';
if (_browseFilterEl) _browseFilterEl.value = '';
applyBrowseFilter();
} else {
close();
}
} else if (e.key === 'ArrowDown') {
e.preventDefault();
const first = _results.querySelector('.cp-item');
if (first) { first.classList.add('cp-selected'); _sel = 0; first.focus ? first.focus() : _overlay.focus(); }
} else if (e.key === 'Enter') {
e.preventDefault();
if (_items.length > 0) exec(_sel);
} else if (e.key === 'Backspace' && !_browseFilter && !_browseFilterEl?.value) {
e.preventDefault();
const segs = _browse.path ? _browse.path.split('/') : [];
if (segs.length > 0) { _browse.path = segs.slice(0, -1).join('/'); renderBrowse(); }
else if (_browse.vault) { switchMode('browse-vault'); renderVaultPicker(); }
}
}
// ── Name input modal ──
function askName() {
const label = _browse.action === 'create-file' ? 'Nom du fichier' : 'Nom du dossier';
@@ -199,13 +249,20 @@ function createDOM() {
_overlay.tabIndex = -1; // needed to receive keyboard events when input is hidden (browse mode)
_overlay.innerHTML = `<div class="command-palette">
<div class="cp-header"><input type="text" class="cp-input" placeholder="Rechercher un fichier... (tapez > pour les commandes)" spellcheck="false" autocomplete="off"><div class="cp-breadcrumb" id="cp-breadcrumb"></div><span class="cp-hint">Ctrl+Shift+Espace</span></div>
<input type="text" class="cp-browse-filter" id="cp-browse-filter" placeholder="Filtrer dossiers/fichiers..." spellcheck="false" autocomplete="off" style="display:none">
<div class="cp-results"></div>
<div class="cp-footer"><span>↑↓ naviguer</span><span>↵ ouvrir/entrer</span><span>⌫ monter</span><span>Esc fermer</span></div></div>`;
<div class="cp-footer"><span id="cp-footer-hint">↑↓ naviguer</span><span>↵ ouvrir/entrer</span><span>⌫ monter</span><span>Esc fermer</span></div></div>`;
_input = _overlay.querySelector('.cp-input');
_browseFilterEl = _overlay.querySelector('#cp-browse-filter');
_results = _overlay.querySelector('.cp-results');
_overlay.addEventListener('click', (e) => { if (e.target === _overlay) close(); });
_input.addEventListener('keydown', onKeyDown);
_input.addEventListener('input', onInput);
// Browse filter events
if (_browseFilterEl) {
_browseFilterEl.addEventListener('input', onBrowseFilterInput);
_browseFilterEl.addEventListener('keydown', onBrowseFilterKey);
}
// Also listen on overlay for browse modes (input is hidden)
_overlay.addEventListener('keydown', onOverlayKeyDown);
document.body.appendChild(_overlay);
@@ -227,7 +284,10 @@ export function close() {
_overlay.classList.remove('active');
if (_to) clearTimeout(_to); _to = null;
_mode = 'files';
_browseFilter = '';
_allItems = [];
cleanupNameInput();
if (_browseFilterEl) { _browseFilterEl.style.display = 'none'; _browseFilterEl.value = ''; }
const bc = document.getElementById('cp-breadcrumb'); if (bc) bc.style.display = 'none';
const si = document.querySelector('.search-input'); if (si) si.focus();
}
@@ -239,9 +299,16 @@ function switchMode(m) {
if (m === 'browse' || m === 'browse-vault') {
_input.style.display = 'none'; _input.disabled = true;
_overlay.focus(); // overlay receives keyboard events when input is hidden
if (m === 'browse') {
// Show browse filter for directory navigation
if (_browseFilterEl) { _browseFilterEl.style.display = ''; _browseFilterEl.value = _browseFilter; _browseFilterEl.focus(); }
} else {
if (_browseFilterEl) _browseFilterEl.style.display = 'none';
}
} else {
_input.style.display = ''; _input.disabled = false;
_input.focus(); // restore focus to search input
if (_browseFilterEl) _browseFilterEl.style.display = 'none';
const bc = document.getElementById('cp-breadcrumb'); if (bc) bc.style.display = 'none';
}
}
@@ -312,8 +379,9 @@ function scroll() { const s = _results.querySelector('.cp-selected'); if (s) s.s
// ── Overlay keydown handler (for browse modes where input is hidden) ──
function onOverlayKeyDown(e) {
if (_mode !== 'browse' && _mode !== 'browse-vault') return;
// Don't handle if name input is active (it has its own keydown handler)
// Don't handle if name input or browse filter is active (they have their own handlers)
if (document.getElementById('cp-name-input')) return;
if (document.activeElement === _browseFilterEl) return;
onKeyDown(e);
}
+17
View File
@@ -6423,6 +6423,23 @@ body.popup-mode .content-area {
margin-top: 6px;
}
/* Browse filter in command palette */
.cp-browse-filter {
width: calc(100% - 24px);
margin: 8px 12px 4px;
padding: 7px 10px;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--bg-primary);
color: var(--text-primary);
font-size: 0.8rem;
font-family: inherit;
outline: none;
transition: border-color 0.15s;
}
.cp-browse-filter:focus { border-color: var(--accent); }
.cp-browse-filter::placeholder { color: var(--text-muted); }
/* ── Help Modal: Mobile responsive ── */
@media (max-width: 768px) {
.help-container {