From fcd73169e0c660a5b304e05c8388bfe3aa591c74 Mon Sep 17 00:00:00 2001 From: William Mantly Date: Fri, 17 Jul 2026 19:28:07 -0400 Subject: [PATCH] Add header help icon and in-app docs search - A ? icon in the top-right header deep-links to the doc most relevant to the current page (client-side path mapping, same pattern already used for top-nav active-link highlighting -- no server-side "current section" local exists to key off of instead). Falls back to the docs index. - GET /docs/search does a plain line-substring search over the existing allowlisted doc set. No new dependency, stays usable with no internet access. Bumps to v1.1.10. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01KDEx8ghuZR61pqPXc6da9C --- CHANGELOG.md | 11 ++++++++-- nodejs/package-lock.json | 4 ++-- nodejs/package.json | 2 +- nodejs/routes/docs.js | 24 +++++++++++++++++++++ nodejs/views/docs_index.ejs | 43 ++++++++++++++++++++++++++++++++++++- nodejs/views/top.ejs | 21 ++++++++++++++++++ 6 files changed, 99 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ffbe74..7e1e15b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,13 @@ correspond to git tags (`vX.Y.Z`) and `nodejs/package.json`'s `version`. ## [Unreleased] -## [1.1.9] - 2026-07-17 +## [1.1.10] - 2026-07-17 + +### Added +- A help icon (❓) in the top-right header now deep-links to the doc most relevant to the current page (falls back to the docs index elsewhere). +- The in-app docs viewer (`/docs`) is now searchable — a simple line-substring search over the same local doc set, no new dependency, still works with no internet access. + +Bumps to v1.1.10. ### Added - The host list now shows who created each host, and when. @@ -71,7 +77,8 @@ First tagged release. Establishes the `vX.Y.Z` tag convention that the in-app up - Standalone backup script (`ops/backup.sh`) for deployments not using theta-env's orchestrator — snapshots Redis and `./config`, with retention. - Admin-only in-app banner that checks GitHub releases every 24h and surfaces available updates. -[Unreleased]: https://github.com/theta42/proxy/compare/v1.1.9...HEAD +[Unreleased]: https://github.com/theta42/proxy/compare/v1.1.10...HEAD +[1.1.10]: https://github.com/theta42/proxy/compare/v1.1.9...v1.1.10 [1.1.9]: https://github.com/theta42/proxy/compare/v1.1.8...v1.1.9 [1.1.8]: https://github.com/theta42/proxy/compare/v1.1.7...v1.1.8 [1.1.7]: https://github.com/theta42/proxy/compare/v1.1.6...v1.1.7 diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index eb91aa7..fbaf745 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -1,12 +1,12 @@ { "name": "proxy-api", - "version": "1.1.9", + "version": "1.1.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "proxy-api", - "version": "1.1.9", + "version": "1.1.10", "license": "MIT", "dependencies": { "@fortawesome/fontawesome-free": "^7.3.0", diff --git a/nodejs/package.json b/nodejs/package.json index ed91936..fc5ada8 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -1,6 +1,6 @@ { "name": "proxy-api", - "version": "1.1.9", + "version": "1.1.10", "private": true, "author": [ { diff --git a/nodejs/routes/docs.js b/nodejs/routes/docs.js index fec29ad..d50eadf 100644 --- a/nodejs/routes/docs.js +++ b/nodejs/routes/docs.js @@ -60,6 +60,30 @@ router.get('/', function(req, res) { res.render('docs_index', {...values, docs: docList}); }); +// Plain, dependency-free line-substring search over the same allowlisted +// doc set -- no separate index to build/maintain, no new dependency, and it +// keeps working with no internet access (same reasoning as the rest of this +// route). Must be registered before the /:slug catch-all below, or "search" +// would be treated as a (nonexistent) doc slug and 404. +router.get('/search', function(req, res) { + const q = (req.query.q || '').trim(); + if (!q) return res.json({results: []}); + const qLower = q.toLowerCase(); + + const results = []; + for (const [slug, doc] of Object.entries(DOCS)) { + try { + const content = fs.readFileSync(doc.file, 'utf8'); + const matchLine = content.split('\n').find(line => line.toLowerCase().includes(qLower)); + if (matchLine) { + results.push({slug, title: doc.title, snippet: matchLine.trim().slice(0, 200)}); + } + } catch (error) { /* unreadable doc file -- skip it */ } + } + + res.json({results}); +}); + router.get('/:slug', function(req, res, next) { const doc = DOCS[req.params.slug]; if (!doc) return next({status: 404, message: 'Doc not found'}); diff --git a/nodejs/views/docs_index.ejs b/nodejs/views/docs_index.ejs index 0dca9cf..ed7c420 100644 --- a/nodejs/views/docs_index.ejs +++ b/nodejs/views/docs_index.ejs @@ -11,7 +11,12 @@ A local copy of this project's documentation, readable from the running app -- no internet access required.

-