Translate 2508 interface entries from English, keep count-neutral plurals, and gate the web catalog on Arabic font readiness. Record AI assistance and pending native review.
AI-assisted-by: OpenAI Codex
`file.open` on a path already open in a tab switched to it and returned
its index, even when another process had changed the file since, so the
caller kept looking at the old copy with nothing saying so (#997). The
tab's disk stamp from #1330 (`DocState::on_disk`) now tells:
- the result says `stale: true` when the file on disk differs from what
the tab read or last saved;
- `reload: true` reads the file again into the tab, as Revert to saved
does (refused while the tab has unsaved changes or is in a live
session), and the result says `reloaded: true`.
MCP's `open_file` takes `reload` and passes it on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`run_command {"command": "inspect.code", "args": {...}}` ran the command
without its params, on the selection. MCP tool arguments are now checked
against each tool's input schema (and the objects it describes inside,
such as a batch's steps; open objects like `params` take anything), and
a step key other than `command`, `params` and `as` is refused in
command.batch, figcraft-cli scripts and the MCP batch tool.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A misspelt parameter name was ignored and the command reported success,
answering as if the parameter hadn't been given (`comment.list
{"filterr": "open"}` listed resolved threads). `Session::execute` now
refuses names the command's params doc doesn't list, naming them and
what the command takes, before the command runs, so every path into a
command (CLI, batches, control channel, MCP, plugins, the UI) gets the
check.
The names come from the existing docs, parsed once with the registry:
each top-level item's first word and the words joined to it by `|`, `/`
or `+`, bracketed text and what follows `:`, `=` or `→` being
description, alternative groups (`{x, y} | {points}`) included. `ids`
and `id` are always taken (`params::targets` reads either); node.set,
whose doc is its props list, keeps its own check.
The junk sweep runs each junk object as given and cut down to the names
a command takes, so the junk values still reach the parsers. Tests that
passed names their command never read (comment.resolve's author,
collab.host's link, node.get's depth) pass only what it takes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`apply_order` gave `layout` the rank of every other key, so the props
map's alphabetical order decided: `gap` applied before an object
`layout`, which then replaced the whole auto layout (the gap went back
to its default), and `padding` applied after `layout: null` and turned
auto layout back on.
`layout`/`layoutMode` now apply right after the geometry keys and
before the keys that tune them, and auto layout keys beside a `layout`
of null or "none" are refused, naming them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
node.create and node.set take {"$var": variable id or name} for fill and stroke: an exact id
first, then an unambiguous name or Collection/name. The binding goes through the same code as
variable.bind, is applied after literal values and style links, replaces other bindings of that
paint's colour and clears its style link, all in one undo step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6 Astra <noreply@openai.com>
Session::grouped saved only the undo stack's length, so a failed group could drop the entries it
added but not bring back the ones an edit.undo inside it popped. It now keeps each document's undo
entries on entry, as it already did for redo, and restores them when the group fails.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6 Astra <noreply@openai.com>
node.find {root?, name?, type?, text?, component?, limit?} returns {ids, total} for the strict
descendants of root (or the current page) that match every filter given: name, text and main
component name as case-insensitive substrings, type as an exact type name. It changes neither the
selection, the page nor the undo history. Part of #996; fields/outline are left for later.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6 Astra <noreply@openai.com>
The attached backend read each reply from the app's control port with read_line, which has no
limit. It now reads up to 512 MiB before the newline (an app window capture is at most 256 MiB as
base64) and stops at the first byte past that. An oversized or non-UTF-8 reply drops the
connection and is reported like a lost reply; the next call reconnects.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6 Astra <noreply@openai.com>
Brings #1210 onto main as it stands after #1019 and #1209, and keeps both:
- #1019's flyout caret keeps its 16 pt target, 2 pt clear of its tool (#988).
- #1209's narrow-window handling stays as it landed: the pill is never wider
than the canvas less a margin and its buttons scroll inside it under a drag
or the wheel, the help button lifts above it when they'd overlap, and the
pill still clips to itself rather than floating over the panels.
The design itself, as #1210 proposed it:
- One grid for every state of the pill: 32 pt cells, 8 pt padding, 8 pt between
top-level items (the divider included), a 1 pt divider as tall as the content,
16 pt carets, 14 pt mode marks, and a 32 pt mode strip whose current mode is a
plain inset with no outline.
- The Resources button goes; the left rail's Assets tab, Shift+I, Alt+2 and
View > Panels > Assets remain the ways into the Assets panel (and the locale
key, its test and the parity notes go with it).
- The vector edit and shape builder pills move onto the same grid, so a pill
never changes its spacing as the tool changes.
main_width is now the same walk the pill draws, and a test walks it for the
design and board toolbars: the last item ends exactly PAD from the right edge.
That caught the board pill asking for 8 pt more than it drew.
main split `codegen.rs` into `dev/codegen.rs` (the shell: `CodeLang`,
`code_for`, `Ctx`, the shared helpers) and one module per native target
(`css`, `html`, `swift`, `compose`, `arkui`), and added Vue and ArkUI from
other PRs. Flutter arrives as the tenth target, in its own module:
`dev/codegen/flutter.rs`, wired from `codegen.rs` (`CodeLang::Flutter`, `dart`
as an alias, rem left to the web targets), with `flutter_definition` /
`flutter_type` / `flutter_default` beside main's `arkui_definition`.
Every language list in the tree is a union of the two sides now: the
`inspect.code` and `inspect.componentCode` specs, `codeConnect` (parse, import
line, expression, template, path checks, messages), the playground CSS/HTML
assets, the 21 UI catalogues (main added eight after this branch: bn, fi, it,
ka, nl, pl, th, vi, each with Flutter in its own conjunction) and the docs.
Flutter is an extra code target; nothing main gained is dropped.
The reviewer's absolute-child bug (#1382-1403: a child that ignores its auto
layout parent fell through to `Place::Flow`) is fixed in `flutter_children`:
flow and absolute children are split, the absolute ones go in a `Stack` around
the Row/Column at the parent's own box (so their offsets stay the parent's and
the padding moves onto the line instead of insetting them), the line fills that
Stack (`Positioned.fill`, so `Expanded` still has a bounded width), and only
flow children now decide `CrossAxisAlignment.stretch` and `Expanded` — the
absolute child no longer joins the line's spacing or loses its offsets.
`flutter_lifts_a_child_that_ignores_its_auto_layout_parent_out_of_the_line`
covers both kinds of child in one auto-layout frame: a 50×20 flow child and a
30×30 absolute one asking for `fill` on both axes, in a parent with 4 pt
padding. It checks the offset, that the absolute child is out of the Row, that
no `Expanded`/stretch comes from it, and that the padding insets the line only.
ROADMAP and dev-mode-parity keep main's header (2026-10-26) and take this
branch's history row in date order (2026-10-16).
`cargo test`: engine 1391 pass, ui-egui 814 + 8 + 1 pass; fmt and clippy clean.
The one engine failure, `board_tables_tests::a_double_click…`, fails the same
way on a pristine `origin/main` here: this sandbox has no `craft-fonts`
checkout, so its text doesn't wrap and the row can't grow.
Retain regional paint stacks and coordinate frames through simplify, offset, flatten, bucket normalization and Shape Builder. Preserve legacy fill semantics and synchronize importer warning catalogs. Verified with cargo xtask ci on Windows and Ubuntu/WSL, native UI screenshots, and CLI round-trip/export checks.