facett-menu/.nornir/assets/menu-flow.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 820 340" font-family="sans-serif" font-size="13">
<rect width="820" height="340" fill="#0f1720"/>
<text x="20" y="30" fill="#e6edf3" font-size="16" font-weight="bold">facett-menu — one Command set, three surfaces, one Effect</text>
<!-- command registry -->
<rect x="20" y="60" width="220" height="90" rx="8" fill="#15202b" stroke="#2d3f50"/>
<text x="34" y="84" fill="#f0c674" font-weight="bold">Command[] (data)</text>
<text x="34" y="106" fill="#c6d3df" font-size="11">id · label · group · keywords</text>
<text x="34" y="124" fill="#c6d3df" font-size="11">shortcut? · icon? · enabled</text>
<text x="34" y="142" fill="#9aa7b3" font-size="11">host builds these at runtime</text>
<!-- MenuView Elm box -->
<rect x="300" y="52" width="300" height="240" rx="8" fill="#15202b" stroke="#2d3f50"/>
<text x="316" y="76" fill="#9fd3f5" font-weight="bold">MenuView (facett Elm → Facet)</text>
<rect x="316" y="90" width="268" height="26" rx="4" fill="#1b2b3a" stroke="#33506b"/>
<text x="326" y="107" fill="#c6d3df" font-size="11">MenuState: commands · palette_open · query · cursor</text>
<rect x="316" y="124" width="268" height="24" rx="4" fill="#1b2b3a" stroke="#33506b"/>
<text x="326" y="140" fill="#c6d3df" font-size="11">update_json(Msg) · state_json() → hits[]</text>
<text x="316" y="172" fill="#7fd08c" font-size="12" font-weight="bold">▶ view (pure) — three surfaces:</text>
<text x="330" y="192" fill="#c6d3df" font-size="11">• menu_bar — groups → dropdowns</text>
<text x="330" y="210" fill="#c6d3df" font-size="11">• context_menu — right-click, grouped</text>
<text x="330" y="228" fill="#c6d3df" font-size="11">• CommandPalette — Ctrl-K, fuzzy rank()</text>
<text x="316" y="256" fill="#9aa7b3" font-size="11">Msg: OpenPalette · SetQuery · MoveCursor</text>
<text x="316" y="274" fill="#9aa7b3" font-size="11"> · SelectHighlighted · Select(id)</text>
<!-- input arrow -->
<line x1="240" y1="105" x2="300" y2="105" stroke="#6fd3f5" stroke-width="2" marker-end="url(#a)"/>
<!-- effect out -->
<rect x="640" y="70" width="160" height="70" rx="8" fill="#15202b" stroke="#2d3f50"/>
<text x="654" y="94" fill="#7fd08c" font-weight="bold">host</text>
<text x="654" y="114" fill="#c6d3df" font-size="11">runs the command</text>
<text x="654" y="132" fill="#9aa7b3" font-size="11">by id (route/action)</text>
<line x1="600" y1="150" x2="640" y2="120" stroke="#7fd08c" stroke-width="2" marker-end="url(#a)"/>
<text x="606" y="164" fill="#7fd08c" font-size="11">Effect::Selected(id)</text>
<!-- robot seam -->
<rect x="640" y="200" width="160" height="92" rx="8" fill="#15202b" stroke="#2d3f50"/>
<text x="654" y="224" fill="#f0c674" font-weight="bold">robot (in code)</text>
<text x="654" y="244" fill="#c6d3df" font-size="11">open → type filter</text>
<text x="654" y="262" fill="#c6d3df" font-size="11">state_json → hits[]</text>
<text x="654" y="280" fill="#c6d3df" font-size="11">select → Effect</text>
<line x1="600" y1="240" x2="640" y2="240" stroke="#6fd3f5" stroke-width="2" marker-end="url(#a)"/>
<text x="20" y="322" fill="#586570" font-size="11">Nothing is pointer-only: every command is reachable by right-click, by typing in the palette, AND by id from a headless test.</text>
<defs><marker id="a" markerWidth="9" markerHeight="9" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 Z" fill="#8fb0c8"/></marker></defs>
</svg>