crates/engine/src/cmd/insert.rs: the object.move params line keeps both the `control` kind (this branch) and the size-cap note (w/objects).
crates/engine/src/tests/objects.rs: both sides' new tests kept, the copied-sheet controls test first, then the two oversized-object tests.
crates/ui-egui/tests/it/chart_data_table.rs: w/objects' own fix (21a27a76, `selected_object = Some(ObjRef::new(ObjKind::Chart, id))`) replaces the equivalent select_object_id line of dcefd523.
docs/control-protocol.md: this branch's `control` kind paragraph, followed by w/objects' sentences on the object.move and chart.set size cap.
crates/model/src/sheet.rs: git merged both sides' `data_table: None` lines into the push_object Chart literal (dcefd523 and 21a27a76 made the same change at different places); the w/objects line is kept, the dcefd523 one dropped.
`object.move` clamped `width` and `height` to 10,000 points, the most it
gives a new size, while the XLSX reader accepts anchors far past that.
A resize of a picture a file made 12,000 wide passed 11,990 through the
clamp and jumped to 10,000; `chart.set`, whose Format Chart pane sends
the unchanged dimension back with the edited one, clamped the same way.
Each dimension is now capped at 10,000 or the object's current size,
whichever is larger (`size_cap`): such an object keeps its size when it
is moved, shrinks by what a resize asks and never grows further, and a
hostile size is still capped. The params doc of `object.move` and the
control protocol notes say so.
Tests: an_oversized_imported_object_keeps_its_size_when_moved_and_shrinks_only_by_a_resize
makes a picture 12,000 by 300, writes and reads it back as a file, then
moves it without a size (12,000 stays), asks 12,010 (stays 12,000),
11,990 (11,990, no jump) and 50,000 by 50,000 (11,990 by 10,000).
chart_set_keeps_a_chart_size_a_file_made_larger_than_the_limit does the
same through `chart.set`. Both fail without the fix (width 10,000).
ROADMAP.md: both headers combined (form controls first, then main's entries); the form controls progress-log and revision rows re-dated 2026-11-01 on top of main's rows; the branch's duplicate floating-objects rows dropped in favour of main's.
docs/file-format-parity.md: header combined the same way; the form controls revision row on top of main's rows.
docs/target-app-parity.md: header combined; the form controls revision row (catalog 276 / 292 -> 280 / 296) on top of main's rows; the duplicate chart-resizing row dropped in favour of main's.
docs/ui-parity.md: header combined, its catalog count set to 280 / 296; the form controls revision row on top of main's rows; the duplicate floating-objects row dropped.
crates/ui-egui/tests/it/keytips_generated.rs: regenerated with cargo xtask keytips (370 sequences).
crates/model/src/sheet.rs: the exhaustive push_object test helper and the two-of-every-kind list gained the Control kind, which main's new test module did not know.
The Insert tab gains Button, Slider and Spin Button beside Cell
Controls › Checkbox, which is unchanged. Each arms placement: a drag on
the grid draws the control at that size (a tall drag makes a vertical
slider), a click places it at its default size, and Escape disarms. Key
tips (N B U, N S L, N S B) place one at the active cell. A new button
opens Assign Action, as Excel opens Assign Macro.
form_controls.rs draws the controls in the sheet tokens, so they look
the same in both themes: buttons with hover and pressed states, sliders
with a track, a filled part and a thumb (and the value beside the thumb
while it is dragged), spin buttons with two arrows, stacked or side by
side. A plain click works a control: dragging the thumb, clicking the
track (a page), clicking or holding an arrow (it repeats after 0.4 s),
pressing a button (it runs on release over it). A drag or a hold is one
undo step. The control last worked takes the arrows, Page Up and Page
Down, Home and End, and Space or Enter for a button. Ctrl or Cmd+click
or a right-click selects a control to move or resize it; a right-click
opens the object menu, which for a control adds Edit Text and Assign
Action (buttons) and Format Control before Delete.
Double-clicking a selected control opens its editor. On a right-to-left
sheet a control moves as a whole and works where it is drawn.
Assign Action runs a macro (from the list or typed, on the selection or
a fixed range, with an optional output cell) or a command (searched
among those a macro may run, with JSON parameters). Format Control edits
the value, limits, incremental and page change, cell link, orientation,
name and the locked and print flags, checking the same rules as the
engine. A button whose action needs approval, or whose macro would write
over data, asks in a prompt whose buttons press it again with the
answer.
New theme tokens (thumb, hover, pressed, fill, focus ring) are in the
contrast test; Button, Slider and Spin icons are drawn in code; the new
labels are translated in every interface language. The catalog gains
Developer › Controls (280 / 296).
The sheet tracked the selected object as a bare id in `selected_chart`, so
only charts and text boxes drew a selection frame, only the bottom-right
corner resized, and the kind of the selected object had to be guessed from
the id. The selection is now `SheetApp::selected_object: Option<ObjRef>`,
with `selected_chart()` kept for the Chart Design tab and the Format Chart
pane.
- Model: `ObjKind` (chart, image, shape, slicer) and `ObjRef { kind, id }`;
`Sheet` helpers find, move and remove an object by `ObjRef` and walk every
object's anchor in a fixed order. `next_object_id` saturates instead of
overflowing on an id at `u32::MAX` read from a file.
- Commands: `object.delete`, `object.move` and `object.setAnchorMode` accept
the four kinds through one parameter check, refuse an unknown kind or id
instead of succeeding silently, and clamp hostile offsets. `object.delete`
replies with the deleted object. Arrange and the Selection Pane include
slicers, which stack above the other objects.
- Sheet: the selected object of any kind draws a frame with eight handles
over every object. Each handle resizes only its own edges, which never
cross; the body moves the object, and the first drag frame already applies
the pointer's movement. The pointer shows the handle's resize cursor or a
move cursor, with the diagonals swapped on a right-to-left sheet, where the
frame moves with its object.
- Keys: Escape drops the object selection; Delete and Backspace keep
deleting the selected object (#213). A selection whose object disappears
(undo, redo, delete, sheet switch) is cleared.
- Double-click: `open_object` routes by kind (a text box edits in place, a
chart opens Format Chart).
- Control protocol: `ui.inspect` reports `selectedObject {kind, id}` and
keeps `selectedChart`; `ui.object.select` selects any object and replies
with its rect. The snapshot example takes `--object <id>`.
Tests: engine tests for every kind by kind and id, the Selection Pane and
hostile parameters; UI tests for selecting each kind (a slicer over a chart
takes the click), all eight handles on four kinds, cursors, Delete,
Backspace and Escape, undo and redo, double-click routing, and resizing on a
right-to-left sheet. Existing tests use the new selection API.
authenticate() set the read timeout on the writing half of the socket while it read the
token through a try_clone; on Windows the clone has its own timeout, so a peer that never
sent a line held its thread forever (and a per-read timeout let a byte-at-a-time peer
stretch it elsewhere). The first line now has one total deadline, set on the handle being
read and shortened before each read. At most 16 connections may wait to authenticate (more
are closed on accept, as #2174 suggested), and accept errors back off instead of spinning.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* E37: migrate GridCraft to egui 0.37
Pin the reviewed fork and shared dependencies, adapt input and rendering to the
new frame lifecycle, and preserve bounded capture handling and font notices.
Add representative accessibility checks and meaningful control labels.
Validation: full repository CI passed all ten stages, with 2,249 tests passed,
zero failed and one existing timing test ignored. Native workflows and paired
performance qualification remain documented separately.
* egui037: record final published pins and local qualification
* M0.37: fix RTL clipping and preserve screenshot budgets
* test: share migrated GridCraft UI harness setup
* egui037: document migrated UI and regression contracts
* egui-037: align popup test fonts and avoid redundant tooltip probes
Session keeps a window list (stable ids, per-window selection, active sheet
and per-sheet selections swapped into the workbook without dirtying it)
in place of the index-based hidden list and the copying New Window.
Titles read Name:1 / Name:2 for a workbook with several windows.
File > Close closes one window of several; window.close, window.list and
window ids for window.activate, view.switchWindows, view.hideWindow and
view.unhideWindow. Control channel and MCP tools take window/doc ids;
scroll positions and cell drafts belong to their window.
app.language.set's engine.commands docs and its unknown-language error
were written by hand and had fallen behind: the docs missed es, fr and
sr, the error missed de, fr and sr. Both are now built from
Language::ALL (Language::code_list), and tests check every language is
listed in each.
Keep the token check on the first line and the new bounded-line request parsing;
tests authenticate before sending their requests.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A line that is not a JSON object with a string method, including an HTTP
request line, bytes that are not UTF-8 or a line over 64 MiB, gets one
error reply and the connection closes before anything after it is read.
Request lines are read with a bound instead of buffering them whole.
OK runs a dialog's command with build_params(&d), which reshapes the
field values for several dialogs (the rule wrapper of cfQuick, the
delimiters list of Text to Columns, the columns of Remove Duplicates and
Subtotal, Data Validation's unused fields, Zoom presets, Series, Define
Name). ui.dialog.confirm passed the raw field values instead, so a
Highlight Cells rule confirmed over the control channel added nothing
and Text to Columns ignored the ticked delimiter.
Make build_params pub(crate) and use it from ui.dialog.confirm, say so
in the control protocol doc, and add tests driving the Greater Than and
Text to Columns dialogs over the control channel.
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>
Protect Sheet and Unprotect Sheet were journaled with their `password`
param, so the password showed in plain text in the journal dialog and
`engine.journal`, and a recorded script (Automate > Record Actions)
saved it in the workbook's hidden `_sc_scripts` name.
Session::execute now journals the params without secret ones
(`password`, or a name ending in `Password`). A replayed entry runs
without that param: Protect Sheet protects with the same options but no
password, and the matching Unprotect Sheet still succeeds.
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>
- 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>
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>
- UiState::default() is English: tests, the kittest harness and the
snapshot example no longer depend on the host's locale. The desktop app
alone starts from the system language, when ui.json names no supported
language (first run, an older file, an unknown value).
- An unknown or malformed saved language reads as English instead of
failing the whole UiState and resetting every other preference.
- app.language.set takes {"language": "ja"} (with `code` as an alias, the
name the PR description used); the View combo now uses it via
Language::code(). The app.language.* commands are listed by
engine.commands with the other UI commands and documented in
docs/control-protocol.md.
- Home's text "Orientation" (方向) and Page Layout's print "Orientation"
(印刷の向き) get separate keys through a `context|label` key form.
- Japanese: Hide Rows → 行を表示しない, Trace Dependents → 参照先のトレース;
translate the display-theme combo merged in from main.
- system_theme test: the View tab now has two combo boxes; pick the theme one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ui-egui: title bar with QAT, full ribbon (Home/Insert/Draw/Page Layout/Formulas/Data/
Review/View/Automate + Table/Chart Design), formula bar with Name Box, virtualised grid
(frozen panes, overflow, borders, CF data bars/scales/icons, tables, sparklines, filter
buttons, charts/pictures/shapes), in-cell editor with point mode, reference colouring,
autocomplete and argument hints, sheet tabs, status bar, dialogs, control channel
- apps/sheetcraft: desktop app (control server, file dialogs, prefs, drag & drop)
- chart: toolkit-free chart layout/rendering for 25 chart kinds + sparklines + rasteriser
- mcp + sheetcraft-cli: MCP server (headless or bridged) and CLI (info/convert/eval/cat/run/mcp)
- xtask (ci/layers/assets/wasm/version/parity), release workflows, packaging, placeholder icon
- automatic row heights
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>