Each frame checks the active document's names for CJK scripts, to install
faces for them. The check was keyed on the revision, which pans and zooms
bump too, so every frame of a pan read every layer name. In a 900 000-layer
file that was ~70 ms a frame: panning ran at ~14 fps whatever the canvas cost.
It now keeps the document version it read: the same version (a pan, a zoom)
is skipped, and a new one reads only the layers that changed since
(`Document::changed_ids`, sized by the edit), or every name past 10 000
changes. On that file the UI frame while panning went from ~71 ms to ~3 ms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YQxjkNSUJhTkTfY74aNeki
Adds crates/ui-egui/locales/id.toml, a draft Indonesian catalog covering
every UI string (1,577 keys), command label (557) and menu wording (31),
machine-drafted and marked "draft — needs native review".
Indonesian keeps a noun unchanged after a count, so plural entries give
only `other` and plural_one needs no change. Preferences ▸ Language lists
Bahasa Indonesia; System maps id / id_ID to it (new assertion in
i18n_tests.rs) and `id` joins the cjk can-show list. Docs updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds crates/ui-egui/locales/cs.toml with all 2165 entries (ui, commands,
menu), marked "draft — needs native review" and kept out of COMPLETE.
Plurals use one/other only; the other form is worded count-neutral so it
reads correctly for 2-4 as well. Lists cs in the pref.language help text,
adds a cs_CZ.UTF-8 detection test and updates the localization docs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On macOS without craft-fonts the default face is Helvetica Neue, whose
kerning comes out of the shaper partly as an x offset (e.g. on the "e" of
"here."), so a glyph's x + advance is not its pen end. The layout was
right (the full stop's pen sits exactly on the edge); the test assumed no
offsets. Each hanging line is now compared with the same line set flush:
every glyph must move by exactly the hanging mark's advance, and the flush
right line must end on the edge.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/arrange.rs:327-330
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Inside command.batch, `{"$step": n}` anywhere in a step's params is
replaced by the `id` in step n's result before the step runs
(`{"$step": n, "field": "..."}` picks another field). A nested tree,
such as a frame with children, now takes one batch and one undo step,
not one round-trip per level with ids copied between calls by hand.
Malformed references (forward or self, negative or fractional index,
missing or non-string field, extra keys) fail the batch with BadParams
naming the step, and the existing group machinery rolls it back. The
params walk is depth-capped. Steps are journalled with resolved params.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/edit.rs:256 (effect.set), :305 (export.update), :317 (export.remove)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The colour of new text in the app was chosen from the page alone, so
on a dark page text drawn in a white frame (a new frame's default) was
white and invisible. readable_text_fills now looks at what the text is
drawn on: the nearest layer from its parent out whose top visible fill
is a mostly opaque solid colour, else the page. The Text tool and
text.create / node.create pass the text's parent.
The two tests that asserted the page-only rule in a frame now expect
black text in a white frame; new cases cover dark, unfilled, nested,
faint and stacked fills, and the Text tool.
The web build's Services never wrote to the clipboard, so Cmd+V after
Cmd+C got the paste event with whatever text the clipboard held before
and made a text layer of it instead of pasting the copied layers.
WebServices now writes copies (text and PNG) through egui's output,
which eframe hands to the browser clipboard during the copy event.
The web build has no save dialogs, so Export… ▸ Variables JSON / CSS
custom properties returned silently. Services gains downloads() and
download(name, bytes); the browser hands the file over through a Blob
link, and the variables panel uses it where the platform saves by
downloads.
The Text tool made its text on the press and ignored the drag, so a
drag gave auto-width text like a click. The new text now stays in a
TextBox gesture while pressed: past the drag slop it becomes a box as
wide as the drag, its top-left at the drag's, with a fixed width and a
height that follows its text. A click, or a drag within the slop, still
makes auto-width text.
Found while testing crates/engine/src/cmd/edit.rs:962-977
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>