Fix filtered command palette action selection #61
10
frontend/commands.js
Normal file
10
frontend/commands.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
(function (root, factory) {
|
||||||
|
const filterCommands = factory();
|
||||||
|
if (typeof module === 'object' && module.exports) module.exports = filterCommands;
|
||||||
|
if (root) root.filterCommands = filterCommands;
|
||||||
|
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
|
||||||
|
return function filterCommands(commands, filter) {
|
||||||
|
const term = String(filter || '').toLowerCase();
|
||||||
|
return commands.filter(command => command.name.toLowerCase().includes(term));
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
@ -160,6 +160,7 @@ textarea { resize: vertical; min-height: 120px; }
|
||||||
<div class="footer">Creative AI-imbued UI • stackchain-dashboard</div>
|
<div class="footer">Creative AI-imbued UI • stackchain-dashboard</div>
|
||||||
|
|
||||||
<script src="/static/markdown.js"></script>
|
<script src="/static/markdown.js"></script>
|
||||||
|
<script src="/static/commands.js"></script>
|
||||||
<script>
|
<script>
|
||||||
(function(){
|
(function(){
|
||||||
const qs = (s, el=document) => el.querySelector(s);
|
const qs = (s, el=document) => el.querySelector(s);
|
||||||
|
|
@ -340,11 +341,10 @@ textarea { resize: vertical; min-height: 120px; }
|
||||||
];
|
];
|
||||||
function renderCommands(filter) {
|
function renderCommands(filter) {
|
||||||
const el = qs('#cmd-results');
|
const el = qs('#cmd-results');
|
||||||
const term = String(filter||'').toLowerCase();
|
const items = filterCommands(commands, filter);
|
||||||
const items = commands.filter(c => c.name.toLowerCase().includes(term));
|
|
||||||
el.innerHTML = items.map((c, idx) => '<div class="cmd-item" data-idx="' + idx + '">' + escapeHtml(c.name) + '</div>').join('');
|
el.innerHTML = items.map((c, idx) => '<div class="cmd-item" data-idx="' + idx + '">' + escapeHtml(c.name) + '</div>').join('');
|
||||||
el.querySelectorAll('.cmd-item').forEach((item) => {
|
el.querySelectorAll('.cmd-item').forEach((item) => {
|
||||||
item.addEventListener('click', () => { commands[Number(item.dataset.idx)].run(); qs('#cmd-palette').classList.remove('open'); qs('#cmd-input').value=''; });
|
item.addEventListener('click', () => { items[Number(item.dataset.idx)].run(); qs('#cmd-palette').classList.remove('open'); qs('#cmd-input').value=''; });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
qs('#open-palette').addEventListener('click', () => { qs('#cmd-palette').classList.add('open'); qs('#cmd-input').focus(); renderCommands(''); });
|
qs('#open-palette').addEventListener('click', () => { qs('#cmd-palette').classList.add('open'); qs('#cmd-input').focus(); renderCommands(''); });
|
||||||
|
|
|
||||||
23
tests/test_command_palette.py
Normal file
23
tests/test_command_palette.py
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
COMMANDS = Path(__file__).parents[1] / "frontend" / "commands.js"
|
||||||
|
|
||||||
|
|
||||||
|
def test_filtered_command_runs_the_matching_action():
|
||||||
|
script = f"""
|
||||||
|
const filterCommands = require({json.dumps(str(COMMANDS))});
|
||||||
|
let action = '';
|
||||||
|
const commands = [
|
||||||
|
{{ name: 'Open whiteboard', run: () => {{ action = 'whiteboard'; }} }},
|
||||||
|
{{ name: 'Refresh now', run: () => {{ action = 'refresh'; }} }},
|
||||||
|
];
|
||||||
|
const matches = filterCommands(commands, 'refresh');
|
||||||
|
if (matches.length !== 1) throw new Error(`expected one match, got ${{matches.length}}`);
|
||||||
|
matches[0].run();
|
||||||
|
if (action !== 'refresh') throw new Error(`expected refresh, got ${{action}}`);
|
||||||
|
"""
|
||||||
|
|
||||||
|
subprocess.run(["node", "-e", script], check=True, capture_output=True, text=True)
|
||||||
Loading…
Reference in New Issue
Block a user