- New Action::Control routes a keytip to its ribbon control (popup/dropdown
open, checkbox toggle, button handler) instead of an engine command with
empty params. Fixes ALT+H FF/FS/N/H/H error boxes.
- Ribbon consumes a one-shot ui.keytip_invoke: font/size/number combos
converted to the popup pattern (ComboBox has no open_memory), fill/font
color menus force-open via SetOpenCommand, 100% zoom and Formula Bar run
their handlers.
- keytips.json: FF/FS/N/H/H -> control kind; View/Formulas/Data dialog
openers (WQ zoom, HFM format cells, HO*, MV, AWG, sparkline types) and
control WVF; MSP/HVP re-targeted; dropped 15 unreachable Home sub-items
and non-Excel HWS shrink-to-fit.
- View tab: badges on workbook views, gridlines/headings, formula bar.
- Generated suite: 399 tests incl control-kind arm; direct tests 18.
cargo xtask keytips now emits tests/keytips_generated.rs alongside the
table: one end-to-end test per sequence, asserting exactly what its
action does today. The generator probes the live engine catalog at
generation time (blank workbook, empty params), so each live command
asserts journaled / ran clean / surfaced its missing-params error;
pending asserts silence; dialogs assert the dialog opens; tabs assert
the ribbon switch; group levels assert the prefix stays armed.
- pending-but-landed targets get live assertions and a generation
warning (HWS home.shrinkToFit and MAA formulas.removeArrows had
landed since population - flipped to live)
- unreachable-sequence validation: any seq with a terminal prefix
entry is rejected (HCP dropped - HC Copy fires first)
- ENV_SENSITIVE list keeps the checked-in file byte-stable across
machines (review.spelling uses the system dictionary)
- sample_sequences_work_with_text_before_key covers IME text-first
event order for one representative of each action kind
- generated_test_count_matches_the_table pins the table size
518 ui-egui tests green; all 8 CI gates pass.
- Action::Pending now consults the engine catalog at fire time: silent
no-op while the command is missing, runs it once it exists - no table
edit needed when a future feature lands.
- Stripped stray backticks from 7 pending target ids so catalog lookup
can ever match them.
- gridcraft-engine: doc(hidden) register_test_command hook so UI tests
can prove the go-live path without touching production code.
- Tests: pending_sequences_are_silent_no_ops (no dialog/toast/journal),
pending_sequences_go_live_when_the_command_lands (fake command fires
via its reserved sequence), every_sequence_terminates_without_panicking
(all 413 sequences pressed end-to-end).
- HP -> home.percent (was wrongly numberFormat); HN stays the Number dropdown.
- H3 -> home.underline fires directly; H3U/H3D dropped as unreachable.
- HAT/HAM/HAB -> Top/Middle/Bottom Align (were unbound).
- NCM/NZT/NZS -> modern Comment/Text Box/Symbol (NC2/NZTX/NU kept as
legacy aliases); MI1 -> Financial first item.
- Badges on the three dropdowns (font FF, font size FS, number format N)
and the vertical alignment buttons; Print Titles paints PI.
The badge painter only covered five sites, so pressing Alt+H showed
nothing past the tab letter. Now every control with a catalog command id
paints its badge automatically:
- control_badge(prefix, id): the rest of a control's unique sequence
(HFC -> "FC" on Font Color), or the common group letter when many
variants land on one control (HBA..HBW -> "B" on Borders).
- anchor_badge(prefix, targets): menu anchors resolve to the dominant
group-level candidate (Pictures shows "P", not SmartArt's "M").
- ribbon.rs: 90+ control sites now paint through control_keytip/
anchor_keytip (mechanical sweep + hand-wired anchors); split buttons
paint at their arrow rect.
- Offscreen snapshot verified: at prefix H the Home ribbon paints
V/X/C/FP/1/2/3U/FC/H/FG/FK/AL/AC/AR/5/6/WW/MC/K/0/9/L/T/J/I/D/O.
- Tests: 29 lib + 14 keytips, all 8 CI gates green.
Population of keytips.json from the collected Excel Alt-sequence set:
- 408 curated entries: 10 tabs, 354 command binds (64 reserved pending,
290 live catalog commands), 2 dialog openers, 42 group levels.
- Bind-all policy: sequences whose command does not exist yet bind to the
canonical future id as Action::Pending -- badge shows, invocation is a
silent no-op until the command lands (ids from docs/parity-checklist).
- Group levels: 46 command entries that are strict prefixes of deeper
sequences were demoted to menu openers so the trie never fires early
(HV must not paste; it waits for the variant letter).
- Fire-immediately restores: HC Copy, HAN Accounting, FA Save As, FO Open,
FI Info. Unreachable legacy aliases (HCC, HANM, FA1, FAY3, FOC, FOO, FIP)
dropped: the parent fires first.
- ES stays a terminal dialog opener as in Excel: the A/F/T/V variants are
dialog mnemonics, not trie sequences.
- Escape pops one trie level and returns to the armed root at a single
letter; keytips.json is the single modifiable source; xtask keytips now
emits through rustfmt so --check is byte-stable.
- Tests: 99 pass (27 lib + 14 keytips + suite), all 8 CI gates green.
Move the hardcoded keytip sequence list into keytip_table.rs as the single
source of truth for both the matcher and the ribbon badge painter.
- Generalize Action::Home into Action::Tab(&'static str); the legacy
Edit/Format prefix special-case in lib.rs is absorbed into the table.
- Badge helpers: tab_sequence (tabs), group_extension (menu anchors),
item_letter (menu items), control_badges (command controls).
- Add tab letters for Insert (N), Page Layout (P), Formulas (M), Data (A),
Review (R), View (W); Draw/Automate stay unbadged until contextual
letters land.
- Tests: 5 table unit tests + tab_letters_switch_to_their_ribbon_tabs
integration test. All 7 xtask ci gates green.
The sequence table is no longer hand-maintained Rust: keytips.json is the
single human-edited source (one line per sequence, grouped by tab, pending
reserves sequences for future commands), and `cargo xtask keytips`
regenerates keytip_table/generated.rs deterministically. `cargo xtask ci`
now fails if the generated table is stale.
- Action::Pending: reserved sequences show their badge and invoke silently
until the command exists (bind-all policy).
- Intermediate group levels are implicit: the matcher waits whenever a
pressed letter starts a deeper sequence; only terminal entries are
listed (two-letter units like Draw's "JI" work with no extra rows).
- First populated sequence beyond the legacy set: W,V,G toggles gridlines,
proven end-to-end by view_gridlines_sequence_runs_end_to_end.
Excel shows some access keys as one multi-letter unit (Draw's "JI") that
fires only once complete. The matcher now follows the trie shape:
- A pressed letter that matches a complete sequence fires immediately.
- If it only starts deeper sequences, the matcher waits for more input
(implicit group level, no table entry required).
- A tab with deeper sequences stays as an active level so group badges
show; a terminal unit clears the prefix.
- tab_sequence() returns multi-letter tab units as one badge.
Draw: Alt+JI switches to the Draw tab; deeper Draw letters are left
blank for future contributors (GridCraft's Draw differs from Excel's).
Move the hardcoded keytip sequence list into keytip_table.rs as the single
source of truth for both the matcher and the ribbon badge painter.
- Generalize Action::Home into Action::Tab(&'static str); the legacy
Edit/Format prefix special-case in lib.rs is absorbed into the table.
- Badge helpers: tab_sequence (tabs), group_extension (menu anchors),
item_letter (menu items), control_badges (command controls).
- Add tab letters for Insert (N), Page Layout (P), Formulas (M), Data (A),
Review (R), View (W); Draw/Automate stay unbadged until contextual
letters land.
- Tests: 5 table unit tests + tab_letters_switch_to_their_ribbon_tabs
integration test. All 7 xtask ci gates green.
A save without a recent user gesture (e.g. over the control channel) is refused with SecurityError and would otherwise fail silently.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
XLSX headerFooter kept only oddHeader/oddFooter: a workbook whose header is on a
different first page (differentFirst + firstHeader) or on even pages showed no
header, and saving dropped it. The model now keeps the first/even headers and
footers and the differentFirst / differentOddEven / scaleWithDoc / alignWithMargins
flags; XLSX reads and writes them; the PDF and the Page Layout view use the right
pair per page; in-place editing edits the page's own pair, and the Header & Footer
tab has Different First Page and Different Odd & Even Pages. pageLayout.headerFooter
takes the variants and a page: odd|even|first for one-section edits.
Insert › Header & Footer now switches to Page Layout view and edits the first
page's centre header, as in Excel, instead of opening the codes dialog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On macOS the UI font (SF) has no Hebrew and no Hebrew fallback face was
found, while cells in Arial drew it: the formula bar showed boxes. The
interface families now carry the cell face ahead of the script fallbacks,
and the macOS Hebrew candidates include ArialHB.ttc / SFHebrew.ttf /
LucidaGrande.ttc.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Text boxes ignored a:rPr (size, bold, italic, typeface, fill, outline),
a:pPr algn, a:bodyPr anchor and spPr/a:ln/a:noFill. They are now read into
Shape::format, drawn on screen and in PDF, and written back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>