Commit Graph
68 Commits
Author SHA1 Message Date
bflatastic f4f16f1ba1 Merge main into #1913 2026-11-07 10:57:11 +07:00
SHXJ ba79fcd5d4 docs: document the i18n and view helpers touched by the shell
Clears the docstring-coverage pre-merge check for the files this PR touches.
2026-11-06 08:58:15 +08:00
SHXJ 9770f1696e M10: Traditional Chinese (Taiwan) interface draft
Add a 2,647-row zh-Hant catalog (1,975 UI strings, 641 command labels, 31 menu wordings), locale mapping for zh-TW/zh-HK/zh-MO/zh-Hant, and a Noto Sans CJK TC-first fallback. The catalog is marked draft pending native review; the web picker stays hidden because craft-fonts currently ships only the Simplified CJK face.
2026-11-03 22:56:03 +08:00
Trevor BrownandClaude Fable 5.1 90bec33322 Crosshair: the board Shapes tool places on it, and the view pans to keep it in sight
draws_shape includes BoardShape (tool.place lets the tool centre its own shape); a nudge past an edge of the view pans by the overshoot. Tests; docs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-02 07:23:33 -05:00
Trevor BrownandClaude Fable 5.1 41ee4584cb Merge upstream/main into fix/place-crosshair
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-02 06:02:48 -05:00
e65ce42000 M7: Minimize collapses the navigation bar and both sidebars, the right one back while something is selected (#1835) (#1865)
* M7: Minimize collapses the navigation bar and both sidebars, the right one back while something is selected (#1835)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* M7: a narrow window ignores Minimize, and a rename started while minimized waits without repaints

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-02 05:31:32 -05:00
Trevor BrownandClaude Fable 5.1 56ad0ba14f Placement crosshair: kept with its page and document, gone with a pointer placement, kept when the same tool is chosen again
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-02 05:08:41 -05:00
Trevor BrownandClaude Fable 5.1 367f188b34 M6: the arrows show and move a placement crosshair with a shape or frame tool chosen, and Enter places there (#1839)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-02 04:39:07 -05:00
a953a04e33 Shortcuts: Cmd/Ctrl+Shift+Alt+= / - / 0 make the interface larger, smaller and default-sized (#1846)
* Shortcuts: Cmd/Ctrl+Shift+Alt+= / - / 0 make the interface larger, smaller and default-sized

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Interface size keys: live while editing text too, and the new size draws on the next frame

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* A preference set during a frame repaints this window too, so the interface size applies on the next frame

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Control channel: a preference changed by a hidden window's command repaints every window

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-01 23:59:08 -05:00
Claude b352eb866b M10: Bosnian (bs) interface catalog
Adds a Bosnian (Bosanski) catalog in Latin script with ijekavian forms. It covers
every UI string (1,898), command label (629) and menu wording (31). It is
machine-drafted and marked as a draft until a native speaker reviews it.

Bosnian plural rule in i18n::plural: one for 1, 21, 31… (not 11), few for
2–4, 22–24… (not 12–14), other otherwise. Because one covers 21, every form
keeps {n}. Test: bosnian_tags_and_integer_plurals.

Docs: translating.md (conventions and plural rule), localization-parity.md,
README, ROADMAP, mcp.md and the pref.language param doc.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Tayw548pu8zRqLH3HjGHN
2026-10-30 09:49:32 +00:00
BFlat e7ae0f4426 M9: share links can comment: a comment access level beside edit and view (#1525) (#1576)
Someone who joins with a comment link (or its join code) sees the file live and adds, replies to, reacts to and resolves comment threads, but can't change the design: their app refuses other edits and the server accepts a comment link's batch only when every change in it touches comment threads. Sharing online makes a comment link and code beside the edit and view ones; the Share popover offers Can edit, Can comment and Can view, and the commenter's toolbar shows a Can comment badge with Move, Hand and Comment.

Backward compatible: clients announce the comments word; one that doesn't is welcomed with a comment link as a viewer and gets comment links listed as view links, a level this build doesn't know reads as view, and an older server that can't make a comment link leaves the share without one.
2026-10-30 07:29:40 +07:00
BFlat a09821f942 M1: Preferences > Scroll wheel zoom (#1390) (#1584)
With pref.scrollWheelZoom on, a mouse wheel zooms the canvas about the pointer and Cmd/Ctrl+wheel scrolls; Shift+wheel still scrolls sideways, and trackpads, pinches and touch screens are unchanged. A wheel is told from a trackpad by the device macOS reports, elsewhere by line versus pixel steps.
2026-10-30 07:12:46 +07:00
BFlat e0abd55d69 M9: one clear, persistent display name, renamed live in sessions (#1380) (#1554)
* M9: one name for comments and live sessions, shown in the Join dialog, kept in the browser, renamed live (#1380)

* M9: a reconnecting session's reset is its own helper (#1380)
2026-10-30 01:27:52 +07:00
bflatastic 9202b1032f Merge main into #1460 2026-10-29 10:38:06 +07:00
Dylan M. Taylor 23f57c8660 refactor(engine): simplify command handling for strict clippy 2026-10-28 16:00:36 +00:00
MortenHustedandClaude Opus 5.5 64e648ae7e M0.9: command docs list every parameter their commands read
Before commands can refuse names their docs don't list (#1413), the docs
have to list what the code reads. These are the top-level names the run
code (and every helper it hands the params to) reads that its doc left
out, all checked against the code; nothing changes behaviour:

- creation: node.create (characters, points, closed), frame.create,
  shape.*, text.create and vector.create (index, name, select),
  image.place (name)
- aliases the code already took: comment.* (id | link), annotation.add
  and annotation.edit (label | text), text.set (characters | text),
  text.listStyle (style | list), fill.add and stroke.add (paint | color),
  proto.interaction.add/set (durationMs | duration_ms, delayMs | delay_ms)
- options: file.export and export.update (contentsOnly,
  includeBoundingBox, svgOutlineText, svgSimplifyStroke), comment.next
  (frame, resolved), layout.suggest (direction), variable.create
  (description), vector.edit.widthPoint (side), collab.share (key),
  collab.manageFile (access, label, link, name), fill.pattern (visible,
  blend)
- docs whose shape hid a name: proto.interaction.add (a stray `}` closed
  the object before delayMs; dest was only in a trailer),
  proto.interaction.set (patch fields in a second object), collab.join
  (a top-level comma inside the link examples), pref.uiScale (commas in
  the value's description)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:06:33 +01:00
bflatastic 30ea80d541 Merge main into #1026 2026-10-26 08:41:31 +07:00
dotCooCoo d8900e1c16 M4: the Text on a path tool
Text went onto a path only with the Design panel's Put text on path
button (a text layer and a path selected together) or with text.onPath.
The Text on a path tool, in the Text flyout of the toolbar
(tool.textOnPath), shows the I-beam over a shape's or vector's path, and
a click there puts new, empty text on the path, starting at the click,
and starts typing. The text takes the path layer's place, transform,
box, constraints and effects, and its fills when one of them shows; the
path layer is removed. Ending the edit with nothing typed puts the path
back with no undo step.

The tool measures the distance on the page, so a layer scaled more one
way than the other, or shown smaller, is reached where it shows. Shapes
in front whose outline is away from the click do not hide a path behind
them, and paths that a mask or a clipping frame hides at the click are
skipped, as are locked layers and layers inside instances.

A double-click into a text layer that was just made no longer makes it
count as an existing layer, so leaving it empty records no undo step,
with this tool and with the Text tool.
2026-10-24 11:37:47 -07:00
bflatastic 3e99f489e1 Merge main into #1026
# Conflicts:
#	docs/localization-parity.md
#	docs/translating.md
2026-10-24 12:54:30 +07:00
bflatastic 2053098e8e Merge #1200: The colour picker's eyedropper applies the colour to the paint the picker edits (Reported in storytold/vectorcraft#3342) (@marcoarnulfo)
# Conflicts:
#	crates/engine/src/tools.rs
2026-10-24 09:12:42 +07:00
BFlat 8167f13a6a M12: Boards: a collaborative whiteboard file kind (#1229)
Board files (File > New board) with sticky notes, shapes with text, connectors that follow what they join (straight or elbow, arrowheads, labels), marker, highlighter and eraser, and stamps; a board toolbar and a contextual bar; the Design panel hidden on boards, Prototype and Dev Mode refused there. Boards are ordinary FigCraft files built from ordinary layers, so saving, undo, export, MCP and live sessions work unchanged. File tabs, recent files and Open files show a pen nib for design files and a note for boards (original icons drawn in code).
2026-10-24 04:18:55 +07:00
Marco Arnulfo 410c1f10e2 The colour picker's eyedropper applies the colour to the paint the picker edits (Reported in storytold/vectorcraft#3342) 2026-10-23 04:08:48 +02:00
Tống Huy Sơn e3bf9b7270 i18n: add Vietnamese (vi) interface localization 2026-10-22 11:33:34 +07:00
bflatastic 4b030f027a Merge main into #872 2026-10-20 03:20:30 +07:00
bflatastic baf93b7756 Merge #874 into #872
# Conflicts:
#	docs/mcp.md
2026-10-20 02:05:13 +07:00
bflatastic e8b7917002 Merge #874 into #818
# Conflicts:
#	crates/ui-egui/locales/es.toml
#	crates/ui-egui/locales/fr.toml
2026-10-20 01:43:53 +07:00
bflatastic 20e4028afa #818: interface sizes are relative to the system's display scale (100% is native, Auto goes; 75-200% in Preferences, pref.uiScale takes 0.75-3, "auto" reads as 1); interface_size moved above batch_rename_dialog's doc comment; tests and docs follow 2026-10-20 00:23:14 +07:00
bflatastic 9877b8e6cc #874: test that the Start on the Home screen preference persists (old settings read off) 2026-10-20 00:13:46 +07:00
justinjohn0306andClaude Opus 5.5 82a50dad1b M7: a preference to start on the Home screen (#857)
Launching opens a new empty file, which then has to be closed to get
back to Home. Preferences ▸ "Start on the Home screen"
(`pref.startOnHome {on?}`, off by default) starts the desktop app on Home
instead. Files named on the command line and recovered files still open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:53:24 +05:30
Davit MatchakhelidzeandClaude Opus 5.5 41c123657e i18n: Georgian (ka) interface language
Adds crates/ui-egui/locales/ka.toml (ქართული): every key of en.toml (1,730 UI
strings), every command label (567) and every menu wording (31), marked as a
draft. Georgian has no letter case and keeps a noun singular after a numeral,
so plural entries give the same text for one and other. System picks it for
ka, ka-GE and ka_GE.UTF-8; pref.language's help text lists ka.

Inter and egui's fonts have no Georgian, so the UI draws it in craft-fonts'
Noto Sans Georgian: it joins the built-in faces in theme::install_fonts and
the web build keeps it (crates/text/build.rs). craft-fonts-ref moves to
a71c20e3 (storytold/craft-fonts#27, the commit that adds the face) in the
release and FreeBSD workflows, and the release builds the web version with
craft-fonts (web-craft-fonts: true).

Tests: the Georgian locale tags, draft status and count-neutral plurals, and
that the built-in Georgian face draws every Georgian letter in the catalog
(skipped without craft-fonts). Docs: localization-parity, translating, mcp,
README and ROADMAP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-19 15:53:03 +04:00
leandrodslvandClaude Sonnet 5.5 2c3ea03c2e feat(ui): add the interface size preference (#540)
Preferences > Interface size and the `pref.uiScale` command make the whole
interface larger or smaller. Auto keeps the system's display scale; a fixed
size from 75 % to 300 % is absolute (100 % is one screen pixel per point on
every system). The size is held back while the window is too small for it.

Strings are added in en, es and fr; the control protocol, MCP, UI parity and
roadmap docs are updated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 14:39:57 +02:00
bflatastic 913634bec8 #385: outline modes and soft-proof stay with their file; one toggle table for the menu's check marks 2026-10-18 00:13:49 +07:00
bflatastic 4a038a5f63 Merge main into #385
# Conflicts:
#	crates/engine/src/lib.rs
2026-10-18 00:10:16 +07:00
bflatastic 07130947f9 Merge branch 'pr-517' into batch/t5 2026-10-16 01:35:45 +07:00
bflatastic 7a9e9de38c Merge #305: M1: save files automatically (#296) (@AganzeFelicite)
# Conflicts:
#	crates/ui-egui/src/idle_tests.rs
#	docs/mcp.md
2026-10-15 19:53:52 +07:00
bflatastic 5a10623414 Merge #469: i18n: Czech (cs) UI catalog (draft) (@Losminek)
# Conflicts:
#	contributors/people.toml
#	crates/engine/src/cmd/view.rs
#	docs/mcp.md
#	docs/translating.md
2026-10-15 19:34:05 +07:00
NetterSilverandClaude Haiku 5.5 7d6a3ce137 Skip divider rows in Next page and Previous page
Fixes #413

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 11:49:53 +00:00
LosminekandClaude Opus 5.5 49a2c643c6 i18n: add Czech (cs) UI catalog as a draft
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>
2026-10-15 07:01:27 +02:00
NetterSilverandClaude Haiku 5.5 3d90d8edfe Keep View toggles such as Rulers in the preferences
Fixes #379

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:33:28 +02:00
AganzeFelicite 00ac44f6ca M1: save files automatically
Preferences > Save files automatically (pref.autoSave, off by default;
hidden in the browser, which has no files to save to). With it on, each
open file that has a path is saved 3 s after it changes, never during a
gesture, a live text preview or while a version is shown. A failed save
says why in a toast and tries again a minute later.

Version history checkpoints (version.autosave, every 30 minutes) only ran
for files with unsaved changes, so a file that saves itself would stop
getting them; with auto-save on, edits made since the file was opened
count.

Closes #296
2026-10-14 16:48:56 +02:00
VanTan 7a0bd980c6 M9: add Russian interface translation 2026-10-14 09:33:58 +03:00
fb8ace5bf4 M1: full screen (#227) (#238)
View > Full screen (`view.fullscreen {on?}`, F11 on Windows and Linux,
Ctrl+Cmd+F on macOS) makes the window fill the screen, taskbar included,
and leaves it again. FigCraft's own menu item, translated in every catalog.

Tests: the key and the command toggle the window, junk parameters are
ignored; the shortcut table pins the new binding (49 pinned).

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: bflatastic <bombayapirat@gmail.com>
2026-10-14 10:31:59 +07:00
bflatastic c2a67f81e5 M11: starter templates: eight templates built in code, template.list and file.newFromTemplate, help.gettingStarted 2026-10-13 03:01:19 +07:00
bflatastic 485715f11e M1: cursors per tool and modifier, panel widths, outline options, dark pages
- Cursors: the engine names a cursor for every tool, modifier and hover
  target (rotate turned to its corner, pen states, eyedropper, comment,
  copy on Alt, not-allowed while an instance refuses the edit); the UI shows
  the system's or draws its own; guides and handles keep their cursors;
  ui.inspect reports it.
- Panels: each edge drags its panel within limits, a double-click resets,
  widths are per window and kept in the preferences (view.panelWidths); a
  small window keeps its canvas.
- Outline mode: Include hidden layers and Include object bounds
  (view.outlinesHiddenLayers, view.outlinesObjectBounds), drawn and
  clickable; light lines on a dark page; check marks in the View menu.
- A file or page made in the dark theme has a #1E1E1E page (sessions
  without an interface keep #F5F5F5); text drawn on a dark page is white.
- Tests for Enter and Escape on a selected text.
2026-10-13 00:29:41 +07:00
bflatastic 6720ea4a21 M9: share with a join code: online through the built-in server or on the local network
The Share popover is one flow: Online (the file is published to the server in
Preferences and the server names its links with short codes) or Local network
(the app hosts; the code carries its address and the session's secret), one
Start sharing button, the code large with Copy code and Copy link, who is here,
can edit / can view, and Stop sharing. Join with a code is one field in the
popover, on the home screen and in the Join dialog, for a code of either kind
or an invite link.

Protocol (within version 2, new variants last): Resolve / Resolved and Admin
code. The server keeps a stretched hash and the wrapped token, never the code,
and limits wrong codes per address (with a lockout) and for everyone. Older
servers leave the links in place of a code; older apps join by link.

collab.share, collab.join {code}, code and share in collab.peers; the server
preference defaults to the built-in server and can be reset or turned off.
2026-10-12 19:42:28 +07:00
bflatastic b7af7c7ef7 M9: open hosting mode for public figcraft-server
Config-gated (open_publish): anyone publishes without the server key and
gets an owner token (128-bit, shown once, stored hashed) that manages only
that file. Per-address limits (X-Forwarded-For only from trusted_proxy,
bounded tables), global caps, idle expiry, reports, persisted blocks,
files list --sort, check, GET /healthz on the same listener, startup
warnings. The app asks the server first, publishes without a key when it
is open, shows the owner token once, the host/terms line, the expiry date
and a report button; collab.serverInfo, collab.manageFile, collab.report
and pref.publishServer for MCP/CLI. Protocol additions stay within
version 2, gated like signals.
2026-10-12 11:40:03 +07:00
bflatastic 29e31c2054 M1: rotation origin and place-with-Enter shortcuts
The last two documented shortcuts the parity table listed as missing, and a stale toast.

- **Alt+R: rotation origin** (both platforms). `transform.rotationOrigin {on?}` shows (or hides) a
  handle at the selection's rotation origin: a ring with a cross through it, drawn in code. Drag it
  to move the origin (it snaps to the selection box's corners, edge middles and centre within the
  snap distance); double-click it to put it back at the centre; Escape hides it before it deselects,
  and Alt+R again hides it too. `transform.setRotationOrigin {x, y} | {reset: true}` does the same
  from the CLI, MCP and the control channel. The corner rotation handles, the rotation field
  (`node.set {rotation}` on the selection: each layer's point under the origin stays put, so flipped
  layers work too), Rotate 90/180 and `transform.rotate` without `cx`/`cy` turn about it. The
  origin is stored on the document state per selection, in the first layer's own coordinates, so it
  follows the layers as they move and turn; it is dropped when the selection changes and is not
  saved in the file (the public documentation doesn't say it persists; pinned in the parity row).
- **Enter places an object with the chosen tool.** With a shape or frame tool chosen, Enter on the
  canvas, or Enter again on the toolbar's chosen tool (after picking it from the toolbar with the
  keyboard), places what a click with the tool makes (100x100; lines 100 long; sections 400x300, the
  existing click sizes, now one `ToolId::click_size`) centred in the view, selected, as one undo
  step; the canvas then takes the keyboard. New `tool.place {tool?, x?, y?}`; MCP's headless `key
  Enter` does the same. The documented crosshair that the arrows move before placing is not built.
- **Keyboard box selection toast** (and its screen-reader text) now names both ways to size the box:
  "Shift+arrows or Cmd+arrows" (Ctrl on Windows and Linux). The changed key is renamed in every
  catalog with its translation updated.
- **Shortcuts panel labels:** a label too long for its column was measured in a smaller font than
  it is drawn in, so long ones (several Spanish rows) ran into their keycaps; it is now measured in
  its own font, ends with "..." before the keycaps and shows in full on hover. The new Tools row
  reads "Place a shape or frame (its tool chosen)" with an Enter keycap.
- **Parity table:** the two rows move from Missing to bound (Alt+R -> `transform.rotationOrigin`;
  Enter -> a Tools-tab gesture row, the keymap's Enter staying `select.children`); counts 129
  documented, 128 bound, 1 moved, 0 missing, 2 kept by a platform, 48 pinned.
- New command labels in Spanish (`tool.place`, `transform.rotationOrigin`,
  `transform.setRotationOrigin`), new UI strings in English and Spanish (es-419 overlays "Enter").
- Docs: ui-parity.md (selection and transform rows, shortcut counts, header, revision row),
  mcp.md (the new commands).
2026-10-12 07:53:24 +07:00
bflatastic 8d5f8a0978 M1: keyboard shortcut parity suite and bindings
- A shortcut parity table (`crates/engine/src/shortcut_parity_tests.rs`): one row per shortcut in
  the public documentation (help-centre feature articles: keyboard layouts, text properties,
  alignment, frames, components, prototypes, plugins, keyboard navigation), with the macOS and the
  Windows/Linux keys, what handles it here (keymap command, held key/gesture listed in the panel,
  vector edit tool key, or missing), and a note in our own words. Rows the documentation is silent
  on pin today's keys, so every default binding is in the table.
  - 129 documented rows: 126 bound (110 before), 1 moved off a documented conflict (Run last
    plugin on Windows: the docs also give Ctrl+Alt+P to pixel preview; the plugin keeps
    Ctrl+Alt+Shift+P), 2 missing (moving the rotation origin with Alt+R; placing an object with
    Enter after choosing a tool from the toolbar), 2 kept by a platform (Ctrl+Space with some CJK
    input methods; F6 in browsers). 48 undocumented bindings pinned.
  - Tests: every row fires its command on both platforms and sits in the right shortcuts-panel tab;
    every default binding has a row; no binding uses a chord the OS keeps (Cmd+H/M/Q/Tab/Space,
    Ctrl+Space, screenshot keys on macOS; Alt+Space/Tab/F4, Ctrl+Esc, Ctrl+Alt+Delete, Linux
    workspace and VT keys); macOS keycaps show Cmd/Option glyphs only and Windows/Linux keycaps
    Ctrl/Alt words only; vector edit tool keys; counts pinned for the docs.
- Newly bound: A (Frame tool), Shift+' (pixel grid; Cmd+' kept, the articles disagree),
  Alt+Cmd+\ / Ctrl+Alt+\ (others' cursors in a live session), Ctrl+F6 (toolbar focus on
  Windows/Linux), Shift+Cmd+U (link text; Cmd+K kept), Cmd/Ctrl+] and [ (list levels while
  editing), Cmd/Ctrl+Backspace (ungroup), Alt+Space / Ctrl+Space (keyboard box selection; F8 kept,
  Cmd/Ctrl+arrows draw the box out), Ctrl+Shift+A / Ctrl+Alt+Shift+A (suggest auto layout),
  Ctrl+Alt+Cmd+K / Ctrl+Alt+Shift+K (go to main component).
- New commands: `view.multiplayerCursors {on?}` (View menu item, menu breadth 207/207),
  `view.pan {dx, dy, big?}` (arrows pan the view by 20 screen px, 200 with Shift, when nothing is
  selected; the canvas and MCP `key` both use it), `text.listIndent` / `text.listOutdent`.
- Behaviour: Control held while dragging suspends snapping on macOS too (Cmd/Ctrl already did);
  the presenter takes Space and N for the next frame, C for comments (Shift+C kept) and F for full
  screen, after the prototype's own key triggers; the paint bucket's tooltip shows Shift+B (B still
  works).
- Keyboard layouts: shortcuts follow the character typed, named as on a US keyboard. A symbol
  another layout types with Shift (`/` is Shift+7 on German keyboards) now counts without the
  Shift (canvas, text editing and F6 navigation share one lookup). Characters egui has no key for
  already arrive as the key at that position.
- Keyboard shortcuts panel: new rows for panning with the arrows, suspending snapping, moving along
  the toolbar, and the presentation keys, in the tabs the documentation groups them in (en/es
  strings).
- Docs: ui-parity.md gets a Keyboard shortcuts section with the counts; target-app-parity.md,
  ROADMAP.md, gaps.md (gap 4 landed list), mcp.md, parity-checklist.md (regenerated).
2026-10-12 06:49:02 +07:00
bflatastic 9f13cb4ba2 M9: Simplified Chinese draft catalog
- `crates/ui-egui/locales/zh-Hans.toml`: Simplified Chinese, translated from our English catalog:
  every UI string (1,371 keys), command label (524) and menu wording (29), 1,924 entries. Concise UI
  style, full-width punctuation in Chinese text, a space between Chinese and Latin words or numbers;
  plural entries give only `other`. `[meta] status = "draft — needs native review"`; registered as
  a draft (not in `COMPLETE`), like fr/de/pt-BR/ja/ko.
- Preferences > Language shows the native name (Simplified Chinese in Chinese characters).
- Detection: zh, zh-CN, zh-SG, zh-Hans, zh-Hans-CN (and zh-Hans-HK) -> zh-Hans. Traditional Chinese
  (zh-Hant, zh-TW, zh-HK, zh-MO, zh_TW.UTF-8) has no catalog: it resolves to English, or to the next
  OS language in the list, never to Simplified (before this, the regional fallback would have picked
  zh-Hans for zh-TW).
- `plural_one` never picks "one" for zh-Hans.
- CJK UI fonts (`cjk.rs`, `theme.rs`, `lib.rs`):
  - `cjk::samples` takes the UI language's scripts: a Chinese UI puts Han first, looked up in the
    Chinese faces first (Noto Sans CJK SC, Microsoft YaHei UI / YaHei, PingFang SC, ...), even when
    the document also has kana.
  - `install_fonts`: in a Chinese UI the craft-fonts build's built-in Japanese BIZ UDPGothic goes
    after the system faces and doesn't count as drawing Han, so Han takes Simplified Chinese shapes
    instead of Japanese ones (and characters BIZ lacks no longer mix in from another face).
  - The installed CJK faces are keyed by UI language too, so switching languages lays them out
    again (before: switching from Japanese to Chinese kept the Japanese face in front).
- `pref.language` params doc and `docs/mcp.md` list zh-Hans.
- Docs: localization-parity, translating, target-app-parity, ROADMAP, gaps (gap 5), roadmap (focus
  item 4). String counts re-measured from source: 1,371 UI keys, 524 command labels, 29 menu
  wordings (1,924 entries; the docs said 1,899); fr/de/pt-BR/ja/ko drafts have 1,899 of them (25
  strings added since read English), graded 99%. Localization 36% -> 39%, 31-54 h -> 29-51 h; ready
  for real work unchanged (~53%); full parity ~267-527 h -> ~265-524 h.
2026-10-12 05:57:48 +07:00
bflatastic 08df188e1a M9: Latin American Spanish overlay
- `crates/ui-egui/locales/es-419.toml`: Latin American Spanish as an overlay on `es.toml`. It lists
  only the 87 entries (61 UI strings, 26 command labels) whose wording differs in Latin American
  usage: agregar for anadir, presionar for pulsar, configuracion for settings, Enter for the Intro
  key, control for a gamepad, resorte for a spring. Everything else reads `es.toml`, then English.
  Same tu register as `es.toml`. Marked as a draft awaiting native review (not in `COMPLETE`).
- Fallback chain: a regional catalog whose language has a catalog overlays it. `build.rs` bakes a
  `parent` index into each `Lang` (es-419 -> es; others -> English), and `i18n::chain` walks
  overlay -> base -> English for UI strings, command labels and menu wordings. Catalogs are now
  sorted by code, so the picker lists Espanol before Espanol (Latinoamerica).
- System detection: es-419, es-MX, es-AR, es-CO, es-US and the other Latin American tags (also
  `es_CO.UTF-8`, `es-Latn-MX`) resolve to es-419; es, es-ES and other regions stay es.
- Preferences > Language shows "Espanol (Latinoamerica)" (in Spanish spelling) with the draft note.
- `pref.language` params doc and `docs/mcp.md` list es-419.
- Docs: localization-parity (overlay section, language preference, Spanish row, total), translating
  (overlays, detection, wording), target-app-parity, ROADMAP, gaps (gap 5), roadmap (focus item 4).
  Localization 35% -> 36%, 32-55 h -> 31-54 h; ready for real work unchanged (~53%); full parity
  ~268-528 h -> ~267-527 h.
2026-10-12 05:57:48 +07:00