diff --git a/docs/superpowers/plans/2026-05-17-console-command-autocomplete.md b/docs/superpowers/plans/2026-05-17-console-command-autocomplete.md new file mode 100644 index 0000000..b50c9c5 --- /dev/null +++ b/docs/superpowers/plans/2026-05-17-console-command-autocomplete.md @@ -0,0 +1,725 @@ +# Console Command Autocomplete Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add command/cvar autocomplete to the runtime console input on `server_detail.html`, sharing the editor's ranking algorithm via a pure-JS module compiled to a tiny additional bundle, with a vanilla dropdown that does not collide with the existing ArrowUp/Down history recall. + +**Architecture:** Extract the editor's inlined ranking logic into a pure ES module `editor-src/vocab-rank.js`. The editor imports it directly; for the console, a second esbuild entry point bundles it into a small `static/vendor/vocab-rank.bundle.js` that exposes `window.__rankVocab`. A new `static/js/console-autocomplete.js` builds a vanilla dropdown (positioned absolutely under the console input), lazy-fetches `srccfg-vocab.json` on first focus, hides the dropdown once the user types past the first token, and binds Tab/Shift+Tab/Esc only — leaving ArrowUp/Down/Enter untouched for `console-history.js`. + +**Tech Stack:** Vanilla JS (no framework), esbuild (IIFE bundles), CodeMirror 6 (editor-side only — console is plain ``), HTMX (existing — for form submission and dynamic page-fragment swap), CSS variables defined in `tokens.css`/`editor.css`. Tests use Node's built-in `node:test` runner (no extra deps). + +**Reference Spec:** `docs/superpowers/specs/2026-05-17-console-command-autocomplete-design.md` + +--- + +## File Structure + +**New files:** +- `l4d2web/scripts/editor-src/vocab-rank.js` — pure ranking module (ES, exports `rankVocab`) +- `l4d2web/scripts/editor-src/vocab-rank-entry.js` — IIFE entry that assigns `rankVocab` to `window.__rankVocab` +- `l4d2web/scripts/editor-src/vocab-rank.test.js` — Node `node:test` unit tests for the ranker +- `l4d2web/l4d2web/static/js/console-autocomplete.js` — vanilla dropdown, lazy fetch, key handling +- `l4d2web/l4d2web/static/css/console-autocomplete.css` — dropdown styling using existing CSS tokens + +**Modified files:** +- `l4d2web/scripts/editor-src/autocomplete.js` — replace inlined `rank()` + scoring with `import { rankVocab } from "./vocab-rank.js"` +- `l4d2web/scripts/editor-src/package.json` — add `build:vocab-rank` script; chain into `build` +- `l4d2web/l4d2web/templates/base.html` — add ` +``` + +- [ ] **Step 2: Add the new tags directly after it** + +Add immediately after the `console-history.js` script tag: + +```html + + +``` + +And add to the `` section (alongside other `` tags — search for existing ones in `base.html`): + +```html + +``` + +- [ ] **Step 3: Sanity-check the template renders without syntax errors** + +```bash +cd l4d2web && python -c "from l4d2web.app import create_app; create_app(); print('ok')" +``` + +Expected: prints `ok` (Flask app boots; templates are valid Jinja). + +- [ ] **Step 4: Commit** + +```bash +git add l4d2web/l4d2web/templates/base.html +git commit -m "feat(console): wire up autocomplete bundle + stylesheet in base.html" +``` + +--- + +## Task 7: End-to-end smoke test + +**Goal:** Verify the full feature works in the browser against the dev server. + +- [ ] **Step 1: Start the dev server** + +```bash +cd l4d2web && python ../scripts/dev-server.py +``` + +Expected: server starts on `http://localhost:5000` (or whatever the script reports). `LEFT4ME_ROOT` is auto-set to `.tmp/dev-server` and seeded with demo content per memory. + +- [ ] **Step 2: Run through the smoke-test checklist in a browser** + +Open a server-detail page (one of the demo servers seeded by the dev server). Then verify each: + +1. **Vocab fetch is lazy.** Open DevTools → Network → filter `srccfg-vocab`. Reload page. **Expected:** no request yet. +2. **Click into the console input.** **Expected:** one `srccfg-vocab.json` request fires. +3. **Type `sv_`.** **Expected:** dropdown appears showing cvars starting with `sv_`. Top row highlighted. +4. **Press Tab.** **Expected:** first token replaced with the highlighted suggestion (e.g. `sv_cheats`). Dropdown updates with matches for the new query. +5. **Press Shift+Tab.** **Expected:** highlight moves up; or wraps to bottom if at top. +6. **Press Esc.** **Expected:** dropdown closes. Input value unchanged. +7. **Type a space then `god`.** **Expected:** dropdown stays hidden (we're past the first token). +8. **Press ArrowUp.** **Expected:** history recall works — input is replaced with a previously submitted command. No interference from autocomplete. +9. **Clear the input. Type `sv_che`.** Verify `sv_cheats` is highlighted in the dropdown. **Press Enter.** **Expected:** the server console receives `sv_che` (the typed text), not `sv_cheats`. Confirm in the console transcript. +10. **Refocus the input.** **Expected:** no second `srccfg-vocab.json` request (cached in module-scope promise). +11. **Click on a dropdown row with the mouse.** **Expected:** that row's command is inserted into the input. +12. **Editor regression check.** Navigate to a `.cfg` file in the editor (files view). Type `sv_`. **Expected:** editor's autocomplete still works exactly as before. + +If all 12 pass, the feature is complete. + +- [ ] **Step 3: Stop dev server (Ctrl+C) and confirm final commit state** + +```bash +git log --oneline -10 +git status +``` + +Expected: 6 new commits ahead of the pre-feature state; working tree clean. + +--- + +## Verification Summary + +- **Unit tests:** `cd l4d2web/scripts/editor-src && node --test vocab-rank.test.js` — 10 passing tests for the ranker. +- **Manual editor regression:** Editor autocomplete still works on `.cfg` files. +- **Manual console smoke test:** 12-point checklist in Task 7 Step 2. +- **No new runtime JS dependencies** added (vocab-rank.test.js uses only `node:test` + `node:assert/strict`, which are built into Node ≥ 18). + +## What's Explicitly Out of Scope + +- Argument value completion (player names, map names) — would require runtime data, not in `srccfg-vocab.json`. +- Fuzzy / typo-tolerant matching. +- Replacing CodeMirror's editor dropdown with a custom widget. +- Cross-browser e2e automation (no Playwright/Cypress in the codebase; not adding one as part of this work).