From 449e849f4dcc5432eb844b2304b63f47d615a073 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 2 Jun 2026 22:46:47 +1000 Subject: [PATCH] feat(card): search spotlight --- public/style.css | 1 + public/views/cards/search.js | 29 +++++++++++++++++++++++++++++ public/views/sacred_valley.js | 3 ++- 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 public/views/cards/search.js diff --git a/public/style.css b/public/style.css index 24e6bb3..ec21587 100644 --- a/public/style.css +++ b/public/style.css @@ -210,3 +210,4 @@ ul.plain li:last-child { border-bottom: none; } .sv-bar { height: 5px; border-radius: 3px; background: #221820; overflow: hidden; margin-top: 3px; } .sv-bar > i { display: block; height: 100%; border-radius: 3px; background: linear-gradient(90deg, var(--accent-dim), var(--accent)); transition: box-shadow .35s; } .sv-card:hover .sv-bar > i { box-shadow: 0 0 9px rgba(255,79,46,.55); } +.sv-search-input{width:100%;background:#0d0d13;border:1px solid var(--border);border-radius:6px;padding:8px 10px;color:var(--text);font-family:var(--font-mono);font-size:12px} diff --git a/public/views/cards/search.js b/public/views/cards/search.js new file mode 100644 index 0000000..de15a95 --- /dev/null +++ b/public/views/cards/search.js @@ -0,0 +1,29 @@ +// public/views/cards/search.js +import { el, mount } from '../../dom.js'; +import { api } from '../../api.js'; +import { navigate } from '../../router.js'; + +const ROUTE = { page: id => '#/page/' + id, ref: id => '#/ref/' + id, source_doc: () => '#/', message: () => '#/' }; +let body, input, results, deb; +async function run(q) { + if (!q) { mount(results); return; } + try { + const hits = await api.get('/api/search?q=' + encodeURIComponent(q)); + mount(results, (hits || []).slice(0, 6).map(h => + el('div', { class: 'sv-row', style: { cursor: 'pointer' }, + onclick: () => { const r = ROUTE[h.kind]; if (r) navigate(r(h.id)); } }, + el('span', {}, h.title_or_snippet || '(untitled)'), el('span', { class: 'k' }, h.kind)) + )); + } catch { mount(results, el('span', { class: 'muted' }, 'Search failed')); } +} +export default { + id: 'search', title: 'Spotlight', size: 'l', + mount(el_) { + body = el_; + input = el('input', { class: 'sv-search-input', placeholder: 'Search the Void…', + oninput: e => { clearTimeout(deb); const q = e.target.value.trim(); deb = setTimeout(() => run(q), 250); } }); + results = el('div', { style: { marginTop: '10px' } }); + mount(body, input, results); + }, + start() {}, stop() { body = null; } +}; diff --git a/public/views/sacred_valley.js b/public/views/sacred_valley.js index 358f2aa..4d937ec 100644 --- a/public/views/sacred_valley.js +++ b/public/views/sacred_valley.js @@ -8,8 +8,9 @@ import weather from './cards/weather.js'; import hostPerf from './cards/host_perf.js'; import jobs from './cards/jobs.js'; import inbox from './cards/inbox.js'; +import search from './cards/search.js'; -const CARD_MODULES = [clock, weather, hostPerf, jobs, inbox]; // grows in later tasks +const CARD_MODULES = [clock, weather, hostPerf, jobs, inbox, search]; // grows in later tasks let active = []; // mounted cards needing stop() export async function render(main) {