[object Object]

← back to build-pages

search: fleet search ?q= deep-link — hydrate input + replaceState + fire-on-load + 1 smoke

e2faa47bc3280640e45df6d156d28702e067f901 · 2026-05-13 13:56:51 -0700 · SteveStudio2

Files touched

Diff

commit e2faa47bc3280640e45df6d156d28702e067f901
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Wed May 13 13:56:51 2026 -0700

    search: fleet search ?q= deep-link — hydrate input + replaceState + fire-on-load + 1 smoke
---
 server.js     | 10 ++++++++--
 test/smoke.js |  4 ++++
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/server.js b/server.js
index 1b4ddd3..794b070 100644
--- a/server.js
+++ b/server.js
@@ -273,7 +273,7 @@ app.get('/api/version', (_req, res) => {
 // Index — all projects. Pulls counts from the per-repo bundle cache (shared
 // with /p/:slug), so a warm index hit is one HEAD-probe per repo, not a full
 // git rev-list scan each.
-app.get('/', async (_req, res, next) => {
+app.get('/', async (req, res, next) => {
   try {
     const entries = await Promise.all(Object.entries(PROJECTS).map(async ([slug, p]) => {
       const bundle = await getProjectBundle(slug, p).catch(() => null);
@@ -313,7 +313,7 @@ app.get('/', async (_req, res, next) => {
 
       <section class="bp-section">
         <h2>Search the whole fleet</h2>
-        <input id="fleet-q" class="bp-q" type="search" placeholder="search every commit across every project…" autocomplete="off">
+        <input id="fleet-q" class="bp-q" type="search" placeholder="search every commit across every project…" autocomplete="off" value="${esc(typeof req.query.q === 'string' ? req.query.q : '')}">
         <p id="fleet-tally" class="bp-meta">type ≥ 2 characters to search</p>
         <ol id="fleet-hits" class="bp-commits"></ol>
       </section>
@@ -350,7 +350,13 @@ app.get('/', async (_req, res, next) => {
         fq.addEventListener('input', () => {
           if (pending) clearTimeout(pending);
           pending = setTimeout(doFleetSearch, 150);
+          const url = new URL(window.location.href);
+          if (fq.value.trim()) url.searchParams.set('q', fq.value);
+          else url.searchParams.delete('q');
+          history.replaceState(null, '', url);
         });
+        // Fire once on load if ?q= was server-rendered into the input.
+        if (fq.value.trim().length >= 2) doFleetSearch();
       </script>
     `));
   } catch (e) { next(e); }
diff --git a/test/smoke.js b/test/smoke.js
index a65683f..01bc5f1 100644
--- a/test/smoke.js
+++ b/test/smoke.js
@@ -83,6 +83,10 @@ function check(name, cond, hint = '') {
   check('home: ItemList JSON-LD',  /"@type":"ItemList"/.test(r.body));
   check('home: fleet search input',/id="fleet-q"/.test(r.body));
 
+  // 5b. fleet search ?q= deep-link
+  r = await fetch('/?q=whisper');
+  check('home ?q=: hydrated',      /value="whisper"/.test(r.body));
+
   // 6. butlr project page renders + has commit count
   r = await fetch('/p/butlr');
   check('butlr: 200',              r.status === 200);

← 4287f44 ui: fleet search now shows by_project breakdown — '26 hits —  ·  back to build-pages  ·  api: /api discovery doc listing all 13 endpoints + 3 smoke 929b3db →