lab / gesture-parser  ·  notation defined in chord-fn-row

Gesture parser

Type on the pad; it parses your real key edges into the CLX gesture notation, live.

Click here, then press keys — Esc to disarm
nothing held
Notation
press something…
 

Rollover check — the M3 test

The proposed chord needs three non-modifier keys down at once (capslock, space, a digit), and five with Ctrl+Shift. Membrane keyboards often ghost past three. Hold them all and watch the counter.

0
held now
0
max simultaneous
0
max non-modifier
0
edges seen

Target: ctrl+shift+capslock+space+5

Hold all five at once. If “max simultaneous” reaches 5 and every key shows in the pad, your keyboard has the rollover this design needs.

Edge log

t (ms)edgekeyevent.codeheld after
no events yet

Pattern log — last 10

A pattern closes after 3 s of no key activity, so consecutive gestures accumulate into one line until you pause. Holding a key keeps the pattern open — the countdown only starts once everything is back up.

How the parse works

Three passes, no timers anywhere — the structure comes from edge order alone, which is the same thing the engine has to reason about.

PassWhat it does
1. treeEach key-down opens a node inside the currently-innermost open node; each key-up closes it. Containment falls out of the edge order, so a key pressed while another is held becomes its child.
2. collapseA node holding exactly one child collapses into a group when their two up-edges are adjacent in the log — nothing happened between them. That is precisely the rule that ] needs the whole group released; a partial release leaves the nesting intact instead. A child that has a body is demonstrably a hold — something was struck while it was down — so holding a and b and striking e gives (a->b)[ e ] for any keys at all, with no key table and no clock. Only the childless case is ambiguous: c↓ s↓ s↑ c↑ and space↓ 3↓ 3↑ space↑ are the same shape, so there the key roles break the tie.
2b. connectGroup members are joined with +. Whether your fingers happen to nest or cross on release is not something anyone controls, so that difference is noise in captured input — the same intended gesture would otherwise render two ways. Nothing is lost, because the trace line still records the exact edges: observation lives in the trace, the binding lives in the spec. Flip the connector button to -> to see the order as actually pressed; crossings still show + there, since a crossing is real evidence of symmetry.
3. renderEmpty body and closed → X.  · empty and still held → X[  · non-empty → X[ body ], or X[ body while the hold is still open. Body keys print bare, because inside a hold a bare name already means a tap.

Strict nesting turns pass 2 off, so you can see the raw press order the engine sees — useful for reproducing T1, where capslock[ alt[ space[ 1 ] ] ] fails to form a chord because prior_key is alt when Space arrives.

What this can and cannot see

It reads the browser's key events, not the hardware