OK on the untouched field says the password is incorrect instead of
asking again; the Backstage closes once the workbook opens; app.open on
the control channel takes a password and never opens the dialog. Control
protocol docs cover file.open {password}.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 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.
- 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).
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.
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.
Grouped rows and columns now get Excel's outline band beside the headers:
a bracket per group, a -/+ button on the summary line and level buttons
in the corner. The buttons run data.hideDetail / data.showDetail and the
new data.showLevels, so they undo. Hide Detail marks the summary line
collapsed, Show Detail keeps inner groups that were collapsed on their
own, and Group / Ungroup take by: rows|cols. Show Detail and Hide Detail
are on the Data tab and in the Data menu, the group shortcuts are listed
as Alt+Shift+Right / Left, and shortcut lookup reads navigation keys by
their logical key so the numpad arrows work.
Fixes#373
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The model read topLeftCell but the grid always opened at A1 and never wrote
the current scroll back. Sheets now open scrolled to their saved first
visible cell; saves record the current one (and the active cell) without
dirtying the workbook. Frozen panes use the pane's topLeftCell.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Issue #348: copying multiple selections works when the areas line up, and explains when it can't
# Conflicts:
# crates/engine/src/tests.rs
# crates/ui-egui/tests/clipboard.rs
View: Formula Bar and Zoom 100% as engine commands (#289)
# Conflicts:
# ROADMAP.md
# crates/ui-egui/src/control.rs
# crates/ui-egui/src/lib.rs
# docs/target-app-parity.md
Copying a multi-area selection always failed in the engine, and the UI
dropped the error, so Ctrl+C did nothing and Ctrl+V had nothing to
paste. Now, as in Excel:
- Areas that cover the same rows (or the same columns) and don't
overlap are copied as one block, slid together left to right (top to
bottom). The clipboard keeps a snapshot with the block packed at the
first area's place; formulas travel as their values, and column
widths or row heights, comments, pictures and merges come along.
- Other layouts, Cut of several areas and Paste Link of a multi-area
copy are refused with "This action won't work on multiple
selections."; Copy and Cut from the keyboard, ribbon and context menus
now show that message instead of failing silently.
- The marching ants go around each copied area.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add a Czech table with all 266 interface labels (the same key set as
Korean, Russian, Simplified Chinese and Brazilian Portuguese) and
register `cs` in Language: name "Čeština", code `cs`, system tags
`cs-*` / `cs_*`, appended to the end of the switcher. The language
command help, its error text and the snapshot example usage list `cs`.
Like the other ribbon tables, it covers the ribbon, sheet tabs and
widgets; dialogs, menus and messages stay English. Formulas stay
canonical English: Czech uses the English formula locale like every
non-Spanish language.
Translated from the meaning of each English label (clean-room, not from
another product's localization), with AI assistance.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
View › Page Layout (and the status-bar button) now shows the sheet as printed
pages on a desk instead of overlaying page outlines on the grid.
- Model/XLSX: per-sheet view mode (normal / pageLayout / pageBreakPreview),
saved as <sheetView view="…"> and read back. view.* commands set it per
sheet ({sheet?}) without an undo step or dirtying a clean workbook.
- Engine: paper::PageGrid paginates the sheet from A1 with the print
pagination (print::split_bands, shared with PDF export and printing) and
maps cells to pages and back; header/footer section helpers
(split/join, &[Page] field names) shared with the PDF header code.
pageLayout.headerFooter takes {part, section, text} for one section and a
sheet; header/footer strings are capped at 255 characters.
view.ruler toggles the rulers.
- UI: Geo carries the page grid, so painting, hit-testing, selection,
editing, objects and scrolling work through the page transform; frozen
panes are ignored in this view. Pages with margins and gaps, "Click to add
data" pages, rulers in inches (English) or centimetres, headers/footers
drawn on printed pages, click a margin section to edit it in place; a
click elsewhere commits (one undo step), Escape cancels. Header & Footer
contextual tab: presets, Page Number, Number of Pages, Current Date/Time,
File Path, File Name, Sheet Name, Go to Header/Footer. View › Ruler.
- New strings in every catalog; new code-drawn icons; desk colour token.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Clicking the File tab now opens a full-window Backstage view in place of the
ribbon, formula bar, grid and sheet tabs, like Excel's. Esc or the back arrow
return to the sheet. Pages: Home (new + recent), New (blank + samples), Open
(recent + Browse), Info (location, properties, Protect Workbook), Save, Save As,
Print (page preview, scope, fit to page, Page Setup), Export (PDF, CSV, web
page), Close and Options (theme, language, formula bar, AutoSave, calculation).
Every action runs an existing command or dialog.
Adds `file.options` (reports the workbook's calculation settings and asks the
interface for the Options page) and the UI command `ui.backstage {page}`.
Keyboard shortcuts and the macOS native menu are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Audited the grid, ribbon and editor key handling against Microsoft's
published Windows and Mac shortcut lists. New keymap.rs binds the ones
whose feature already exists: Ctrl+Shift+~!@#$%^&_ number formats and
borders, Ctrl+2-5, Ctrl+Shift+: time, Ctrl+' / Ctrl+Shift+" from the cell
above, Ctrl+L, Ctrl+Shift+L, Ctrl+P, Ctrl+Alt+V, Alt+=, Alt+Down,
Alt+Page Down/Up, Ctrl+Shift+Home/End, Shift+Home, the F-key family
(Shift+F2/F3/F9/F10/F11, F5, F7, F12, Ctrl+F1/F3/F4, Alt+F1, Ctrl+Alt+F9)
and the Excel for Mac variants (Cmd+L/E alignment, Control+E Flash Fill,
Cmd+Option borders, Cmd+Shift+K/J/H/R). Number-format keys match the
physical key; AltGr text never triggers a Ctrl+Alt shortcut. Fixes the
command catalog's wrong shortcut labels and documents the audit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- view.zoom100 sets the active sheet's zoom to 100% (the ribbon's 100%
button runs it).
- view.formulaBar {on?} toggles a formulaBar preference (Prefs, saved in
prefs.json on desktop; an application option, not in the file). The UI
shows the formula bar from it; the ribbon checkbox, native menu and
ui.set {formulaBar} go through the command.
- The UI-only interceptions and their entries in engine.commands are gone,
so the CLI and MCP can run both. Parity checklist regenerated (260 / 290).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UiState gains interfaceScale (percent, 100 = Auto, 75–200), saved in
ui.json and applied as egui's zoom factor on top of the desktop scale.
New UI command view.interfaceScale {percent | "auto"} clamps hostile
values; a malformed saved value reads as 100. View tab gets an
"Interface size" picker. egui's built-in keyboard zoom is turned off so the
preference owns the zoom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spreadsheets put an HTML table next to the tab-separated text; Excel
(x:num) and LibreOffice Calc (sdval) record each cell's number there.
Paste now reads the HTML flavour (new paste_html host service, arboard on
desktop) and edit.paste takes an optional html param: a field that would
paste as text becomes the recorded number when the HTML cell shows the
same text, with an inferred format ("Rp"#,##0.00) that renders it back
identically.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Excel's cell cursor doesn't jump: press an arrow key or click a cell and the
border slides over; extend the selection and it grows. The outline of the
current area now does the same, in 0.12 s with a decelerating curve, from
wherever it is drawn (a held arrow key reads as one motion), with its fill
and fill handle. The timing is an estimate, not yet measured against Excel.
The same cells moving on screen (scrolling, zoom, a resized column), another
sheet or workbook, an outline that wasn't drawn the frame before, and moves
that start or end off screen don't glide. Only painting waits: the
selection, hit testing and the cell editor use the real cells at once.
View > Animations (under Display theme) turns it off; view.animations {on?}
and ui.set {animations?} do the same for agents. Saved with the UI
preferences, on by default; translated in the five catalogs.
Scripted pictures never catch an outline in flight: it stays on its cells
while a ui.screenshot is on its way and when egui's animations are off (the
headless harness, so the offscreen snapshot). The marching ants around a
copied range now advance by the clock instead of once per painted frame,
since a glide repaints every frame and made them lurch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The web build had no way to actually print — Ctrl+P (Cmd+P, declared on
file.print's shortcut) did nothing at all, and the ribbon's "Print…" menu
item ran file.print and silently discarded the result. file.print already
builds a PDF and returns it as base64 specifically "for the UI to hand to
the system print dialog" per its own doc comment — this wires that up.
- ui-egui: new Services::print hook (web: hand PDF bytes to the browser's
print flow directly, no download); SheetApp::print_now runs file.print,
decodes the base64 result, and calls the hook.
- ui-egui: Key::P now dispatches to print_now (was unbound); the ribbon's
"Print…" menu item calls print_now instead of discarding file.print's
result.
- gridcraft-web: implements the hook (Blob -> object URL -> hidden iframe
-> iframe.contentWindow.print() on load).
Desktop builds are unaffected (no Services::print set there; print_now
falls back to the existing alert-on-error behaviour with no UI action on
success, same as before this change).
winit reports no system theme on Linux — `ActiveEventLoop::system_theme()` is
`None` there on every desktop — so `System` in the appearance setting never saw
the desktop's choice and fell back to light. A new `desktop_theme` module asks
the XDG Desktop Portal's Settings interface for
`org.gnome.desktop.interface color-scheme` on a thread of its own, so no
D-Bus call is ever made from the frame loop, and the app follows the answer,
including a change made while it runs.
egui resolves `ThemePreference::System` from what winit reports and offers no
way to set that itself, so where a desktop answered, the app hands egui the
concrete theme instead. What is drawn is unchanged on macOS and Windows, where
winit answers and the same resolution happens a step earlier; the saved
preference is still `system`, so a file keeps meaning the same thing.
The colour scheme is asked for under its GNOME schema name first, which GNOME's
portal and XFCE's both answer, then under the older freedesktop one, which KDE
answers. A desktop that answers neither leaves the user's own choice alone.
Add static pictures inside cells with XLSX roundtrip
Rebased onto main (conflicts with #83/#98 and main resolved by maintainer); the contributor's branch does not allow maintainer edits.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge the fixed#104 branch (which carries #31 and #38) and restructure
the Brazilian Portuguese language onto its per-language layout:
- The PORTUGUESE table moves, unchanged, to crates/ui-egui/src/i18n/pt.rs
as pt::translate, like zh/ko/ru. Language::PtBr gets code() "pt"
(parse also accepts pt-BR/pt_BR via the tag reduction); it is still
saved as "pt".
- No per-language command: the View combo and the control channel switch
with app.language.set {"language": "pt"} (app.language.portuguese is
dropped); error text, engine.commands params and the snapshot example
list every code.
- pt.rs covers the keys added since (Page Layout|Orientation, the display
theme combo, and the Sheet Options View/Print checkboxes, Exibir /
Imprimir); the coverage test checks pt like zh/ko/ru.
- people.toml keeps both new contributor entries.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>