aboutsummaryrefslogtreecommitdiff
path: root/todo.org
diff options
context:
space:
mode:
Diffstat (limited to 'todo.org')
-rw-r--r--todo.org23
1 files changed, 22 insertions, 1 deletions
diff --git a/todo.org b/todo.org
index bd56197..1af8443 100644
--- a/todo.org
+++ b/todo.org
@@ -173,7 +173,28 @@ Review (subagent, since I wrote it all) cleared the widget itself — buffer log
Third vacuous check in one day, and the shape is consistent: checks written in the builder's own vocabulary inherit its assumptions and confirm what the code does rather than what it should do. The one that mattered was found by someone reading the render arithmetic cold.
-Next: item 3, the index typewriter.
+**** 2026-07-16 Thu @ 14:42 -0500 Keyboard contract recorded, R57 built against it
+Craig asked whether keyboard input is the widget's job or its container's, and picked "record the contract first, then build against it" — it's a decision about all 110 cards, not one.
+
+The kit already held the answer in two halves. =GW.slideRule= takes arrow keys scoped to its *own focusable element* (the browser arbitrates focus, which is why it never fights the gallery's global Escape handler at line 1157) — that's the web-correct pattern. But Emacs can't do that at all: the SVG region is an image and never sees a keypress, so the mode's keymap must own delivery and call =press=. Same split as the tick contract, which the README already states: the page owns the ambient resource (the clock there, focus here), the builder exposes a handle.
+
+Contract written into [[file:docs/prototypes/README.org][the widget-library README]] beside the tick contract: *the target owns focus and delivery, the builder declares what it accepts* via a =KEYS= table. Deliberately a table and not a function over a DOM event — the Emacs port installs it into a keymap and never sees a keydown, so a function would force it to re-derive the widget's intent and the two bindings would drift. Five rules, each one a bug otherwise shipped: never listen on =document=; spend =preventDefault= only where there's a default worth killing (Space scrolls, Backspace navigates back); let Tab and Escape bubble (Tab is how the page stays navigable, Escape belongs to the audit stepper); =press= filters rather than trusts (it appends whatever it's handed, so a stray F1 would land as text); click and key both route through =press= so they can't drift. A widget with no =KEYS= table takes no keys, which is most of them — the kit is click-first and that's what makes it port.
+
+R57 built against it: =tabindex=, focus on click, element-scoped keydown, gold focus ring (an unlit focus state means typing vanishes into a card you thought was live). Five checks written first and confirmed red, including the contract's own rules — unfocused keys ignored (the no-document-listener rule, tested behaviourally), Space and Backspace claimed, Tab/Escape/F1 let through, unmapped keys dropped. Audited the kit: no document-level key listener anywhere in widgets.js, so the contract holds retroactively.
+
+Spec sheet reworded. "Click-only, so it ports to Emacs unchanged" stopped being true, and the honest version is the opposite: keys are the *more* native idiom in Emacs, so the card ports better, not worse.
+
+Review (subagent) found two High defects in the keyboard work, both fixed:
+- *The contract's own rule was unmet by the commit that recorded it.* The README says "press filters, it does not trust", and the filter went into the keydown handler instead — leaving press wide open for the one caller the table exists to serve, since the Emacs port installs KEYS into a keymap and calls press directly with no handler in the stack. Now gated on =GW.abcKeypad.ACTIONS= (the plate's whole vocabulary), which is a superset of the KEYS values because CLR is a real key no keystroke reaches.
+- *The focus ring never appeared on the path anyone uses.* =:focus-visible= doesn't match a mouse-driven focus on a non-text element in Chrome, and clicking the plate IS how it takes focus, so the ring showed only when tabbed to. Now =:focus=.
+Also: two more weak checks. One claimed click and key can't drift while only reading a string (now enters the same sequence both ways and compares buffer + readout); one asserted Tab/Escape/F1 aren't swallowed, which couldn't fail because those keys return before the preventDefault (now also checks 'A' and Enter, which are mapped and reach the same code path).
+
+*** 2026-07-16 Thu @ 15:20 -0500 Screen families on the keypad window, and a probe that was lying
+Craig: offer the display in every colour, including the vfd marquee cyan. The window now takes the =--scr-*= vars with the shipped colours as fallbacks, so the default is pixel-identical until a chip is clicked (=--gold-hi= IS the amber family's =--scr-hi=, which is what makes that exact rather than close). Both the glass and the ink recolour — a screen that changes its text and keeps its backlight isn't a screen. R57 offers all six families where its siblings each carry five: a passphrase window has no reason to prefer one phosphor.
+
+Found a real defect while adding the checks, and a nasty one: *probe-fams.mjs had no try/finally*, so a failing check orphaned its headless browser. The next run then connected to the survivor on the same port and reported results from a STALE page — old widgets.js, chips already clicked. It cost a confusing debug loop: R10's default ink read as green and R57's chips read as absent, neither of which was true of the actual page. A probe that answers from the previous run is worse than one that crashes. Wrapped the checks in try/finally like its two siblings already had, and swept nine orphaned chromes (the pattern is bracketed on the debug port, so it cannot touch Craig's daily browser, which has none).
+
+Craig called the ABC entry keypad done at 110 cards.
Banked from the survey, not cards yet: CDU/MCDU scratchpad + line-select keys (a staged-commit *flow*, not a new selector), joystick scroll-and-fire alphabet, multi-tap/T9 letter cycling (the most practical small-panel password entry), trackball gesture (Atari Quantum), keypunch program drum (IBM 029), Enigma lampboard (a *feedback* idiom — steal the 26-lamp grid as a readout, not as entry), Teletype Model 15 tape perforator.