Commit Graph
1560 Commits
Author SHA1 Message Date
Dylan M. Taylor d744379e60 fix(engine): refuse document URLs with shell syntax or a malformed authority
webbrowser on WSL hands an address to cmd.exe /c start, escaping only &
and ^, so a document link holding | (or < > " `) ran what followed it
with the viewer's authority; and an address its parser can't read is
reopened as a file path. Characters never valid in a URL unencoded are now
refused, and an http(s) authority must be made of host characters.
2026-11-03 15:46:13 +00:00
Dylan M. Taylor 02db900cc8 fix(engine): keep plugins out of branches, saved versions and library links
A read-only plugin could archive, merge, create or update a branch: branch
state lives beside the document, so neither the command's history flag nor
the document Arc check saw the write. Any plugin could read a saved version
through dev.compare's version parameter although version.* is denied, and
library.list handed plugins each library's path, which for a server library
is the share link with its token.
2026-11-03 15:46:13 +00:00
Dylan M. Taylor c673996fe7 fix(engine): install a directory plugin only with the permissions that were shown
The confirmation showed the directory entry's access and commands, but the
downloaded bundle's manifest was registered as is, so a directory could
show a read-only plugin and install one that edits the document. A manifest
asking for more than the entry showed is refused before it is saved.
2026-11-03 15:46:01 +00:00
Dylan M. Taylor 411b16931b fix(geom): keep arc subdivision bounded whatever the radius
kurbo turns an arc into cubics by radius divided by tolerance, so the fixed
0.01 let an ellipse, polygon corner, rounded rectangle, per-side stroke
corner or SVG arc with a radius like 1e300 ask for more cubics than memory
holds before any size check saw the geometry. Arcs now use a tolerance
that scales with the radius, and SVG path data from documents, commands and
imports is refused before parsing when a number passes 1e9.
2026-11-03 15:45:48 +00:00
Dylan M. Taylor ac184c1ca8 fix(engine): refuse linked files when publishing a plugin folder
The assets of a plugin folder were read through whatever links they
pointed at, so a folder handed to the publisher could put any file the
publisher can read into the bundle the directory then serves to everyone.
A linked asset, or a linked assets folder, is refused by name before
anything is sent.
2026-11-03 15:28:54 +00: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 bb7d568d49 M2: baseline alignment puts a layer without text on the line with its bottom (#1825)
In the baseline branch of the flow layout only an item with a text baseline got one; any other
layer stayed at the top of the line. A layer without a text baseline now uses its bottom edge, as
the help center's figure shows ("the bottoms of the icon and the word home are aligned on the red
line"): an icon next to a text sits on the text's baseline, a layer reaching below it moves the
text down, and a line with no text lines the layers' bottoms up (CSS does the same).

Tests: alignment_rules (three cases), baseline_alignment_horizontal, engine
baseline_alignment_puts_an_icons_bottom_on_the_text_baseline. Docs: auto-layout-parity row,
status line and revision history.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-03 09:13:19 -05:00
ljkx 692c4545ba M4: weight steps read Univers numbers (45 Light, 57 Condensed) by weight within their width (#1831) 2026-11-03 11:41:15 +01:00
Hamza 58af1a6237 M4, #1747: a text layer's name follows the characters typed into it 2026-11-03 12:41:29 +05:00
Trevor BrownandClaude Opus 5.5 564dbcfe52 Merge upstream/main into fix/scrub-speed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 01:39:16 -05:00
Trevor BrownandClaude Opus 5.5 bc140f51cd Merge upstream/main into fix/text-mixed-fill-section
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 01:38:49 -05:00
Trevor BrownandClaude Opus 5.5 1496b8028c Merge upstream/main into fix/place-crosshair
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 01:38:48 -05:00
Trevor BrownandClaude Opus 5.5 a22d8b1f9e Merge upstream/main into feat/region-fills
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 01:12:17 -05:00
Trevor BrownandClaude Opus 5.5 b936b95fd9 Stack on #1857: merge fix/text-mixed-fill-section
Both redirect the Fill section's reads and writes: the selected regions of a vector come first (#1878), else a text's characters' shared fills (#1857), in the Fill section, the colour picker, the canvas handles, fill.copy, fill.set and fill.move.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 01:12:08 -05:00
Trevor BrownandClaude Opus 5.5 4671402df5 Stack on #1854 and #1869: merge fix/control-arrows-pan
Pick select changes the same ui_key and key_event paths as the crosshair and the pan; stacked on them as the maintainers asked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 00:55:16 -05:00
Trevor BrownandClaude Opus 5.5 66a98b108b Merge upstream/main into fix/padding-all-sides
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 00:47:59 -05:00
Trevor BrownandClaude Opus 5.5 e884e3373a Merge upstream/main into fix/control-arrows-pan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 00:46:51 -05:00
0e86f3597a M4: wrap style on a text layer: Auto, Balance and Pretty decide where its lines break (#1827) (#1871)
* M4: wrap style on a text layer: Auto, Balance and Pretty decide where its lines break (#1827)

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

* Docs: no table pipe inside the wrap-style row's code span

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

* Wrap styles: no new emergency breaks, hanging punctuation in Pretty's width, wrap style as an override

Balance and Pretty reject a re-break with more mid-word breaks than the greedy lines; Pretty narrows a line by its text less its hanging marks; wrapStyle joins the textStyle override group; docs wording. Tests.

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-03 00:13:48 -05:00
a579ba2d3c M8: a click with the Bend tool curves a sharp point, its handles along the line between its neighbours (#1875) (#1877)
* M8: a click with the Bend tool curves a sharp point, its handles along the line between its neighbours (#1875)

A release without movement on a sharp point between two segments runs ops::auto_smooth instead of cancelling; a curved point still sharpens, a drag still sets the handles by hand, an endpoint is left alone. Tests; docs.

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

* Vector edit hit test: the nearer of a handle's end and a point wins

A short path's handles end within the point's hit tolerance; a click on the point is on the point, one on the handle's end still takes the handle. Test short_handles_leave_their_point_clickable.

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

* Vector edit hit test: the nearer of a handle's end and a point is judged on the page

Candidates stay within the tolerance as before; ranking by page distance keeps a non-uniform scale from favouring the wrong one. Test a_handle_and_a_point_are_compared_on_the_page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Vector edit hit test: a handle wins a tie with a point, as before

A handle snapped onto another point stays selectable there. Test a_handle_on_another_point_is_still_the_handles (fails with the strict comparison).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-03 00:13:38 -05:00
ad20c56874 Reject invalid dashes in node.set instead of dropping entries (#1899)
* Reject invalid dashes in node.set instead of dropping entries

Found while testing crates/engine/src/cmd/props.rs:580

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Reject boolean dash entries in node.set (review)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-03 00:12:49 -05:00
7d9d2bda50 Reject fractional index in layout.setTrack (#1892)
Found while testing crates/engine/src/cmd/layout.rs:46-48 (set_track)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-03 00:12:39 -05:00
033e943dc7 Reject fractional index in proto.interaction.remove and set (#1874)
* Reject fractional index in proto.interaction.remove and set

Found while testing crates/engine/src/cmd/proto.rs:37 (proto.interaction.remove) and :412 (set)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Name the upper bound in the interaction index error (review)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-03 00:12:29 -05:00
Dylan M. Taylor 72ec49f92f fix(engine): bound the keys an effect object is probed for
Unknown effect keys are found by reparsing the whole object once per key,
so an effect with many unknown keys cost their number squared in cloning
and parsing before it was refused. More than 32 keys is refused outright.
2026-11-03 04:31:08 +00:00
ljkx dd03165d79 M4: weight steps read number weights after a width too (#1831)
An off-list "Condensed 600" read as 400, since a number word counted as
a weight only at the start of the name: weightUp took Condensed 500 and
weightDown found nothing. A number from 100 to 1000 now counts in any
position, as W numbers do. Smaller numbers (a width grade, an optical
size: "Condensed 2", "Caption 12") are part of the width rather than a
weight, so optical sizes no longer step into each other.
2026-11-03 05:27:23 +01:00
Dylan M. Taylor 970a3e6745 fix(engine): check the opened file, not the looked-up path, before reading it
read_capped checked the path's metadata (type and size) and then opened
the path again, so an entry swapped between the two steps was read without
those checks. The opened handle's metadata is checked as well; the path
check stays, so a pipe is still refused before it is opened.
2026-11-03 04:22:36 +00:00
Dylan M. Taylor 8864068cd5 fix(engine): compare online servers' paths with their case
same_server lowercased the whole address, so files on /Team/ and /team/
(which a proxy can route to different servers) counted as one server and
the online refresh sent both files' owner tokens to whichever answered.
The scheme and host still compare without case; the path keeps its.
2026-11-03 04:21:45 +00:00
ljkx 45c89c5a73 M4: weight steps reach a weight family's italics and read W weights after a width (#1831)
A family installed as one family per weight ("Foo Bold" with Regular and
Italic faces) now lists Bold and Bold Italic, the names resolve draws them
by, with the faces' own weights when they are read. A W-numbered weight
counts after other words too ("Condensed W7 Italic" is 700).
2026-11-03 04:38:16 +01:00
ljkx 2c9226c021 M4: weight steps from an off-list weight compare the faces' own weights (#1831)
next_style compared a custom wght value or a style the family lacks with
the weights the style names say, so Segoe UI Variable's Semilight (350)
counted as 300 and a wght of 325 stepped up to Regular. The family's
styles now come with the weights the style menu sorts them by
(figcraft_text::family_style_weights), and "W7 Italic" reads as 700.
2026-11-03 03:44:58 +01:00
NetterSilverandClaude Haiku 5.5 41ec33eeec Reject negative and fractional indexes in node.reorder
Found while testing crates/engine/src/cmd/edit.rs:329

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 01:41:31 +01:00
ljkx dc82e6cac9 M4: weight steps follow the family's order and keep widths and oblique slants (#1831)
Review follow-up. The step now finds the chars' style in the family's list
(the style menu's order, lightest first) and takes its neighbour, so a style
whose name gives no distinct weight (Semilight) is reachable; a style the
family lacks or a custom wght value still goes by weight, ties going to the
nearer one in that order. "Extra", "Ultra", "Semi" and "Demi" are weight
words only before a weight ("Extra Bold"), so "Extra Condensed" keeps its
width; Oblique names count as slanted for the current style too; W3-style
names are weights; and style_weight reads Hairline as 100, not 400. The
installed-font test picks a family the step actually moves.
2026-11-03 00:49:00 +01:00
ljkx 6365e4339b M4: Increase and decrease font weight step through the styles the family has (#1831)
text.weightUp/weightDown stepped the CSS weight by 100 and stored a canonical
name ("Medium", "Semi Bold", ...) whether the family had that face or not, so
the first press on Arial Regular changed nothing visible and the stored style
named a face no font provides. They now move to the nearest heavier or lighter
style of the family with the same slant and width (Narrow stays Narrow), and
stay put at either end. A family that isn't installed keeps the old 100 steps.
2026-11-02 20:32:37 +01:00
Trevor BrownandClaude Opus 5.5 aa2fed07e2 Region fills: fill.pattern stays on the layer; the canvas handles and the picker's image paths edit the region's paint
fill.pattern sets the layer's fill as its spec says. The gradient and crop handles read the paint from the stack fill.set writes (the selected regions' while there are any); for a region's paint, Upload from computer and image presets write the paint like any edit, and a drop on the picker goes to the canvas (image.replace and image.adjust edit layer paints). Tests in regions_tests, canvas/handles/region_tests and colorpicker image tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 09:49:10 -05:00
Trevor BrownandClaude Opus 5.5 d2e7c49e57 Region fills: each region once, no override for a no-op, Copy fill reads the region, layer-only controls off region rows
Named regions are deduplicated; an inheriting region stays inheriting when an edit leaves its stack as it was, and its own stack is only replaced once the edit succeeds; fill.copy reads the selected regions' paint; the Fill section leaves the fill style, a variable bound to the layer's fill and the image crop off region rows. Tests in regions_tests and region_fill_rows_tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 09:14:55 -05:00
Trevor BrownandClaude Fable 5.1 da4a798323 M1: pick select: Tab while the keyboard selection box shows walks the layer tree with a pink outline, Space selects (#1836)
Tab or the chord again switches the box to a pick cursor; Tab/Shift+Tab move between sibling layers, Enter/Shift+Enter into and out of a layer, Space selects, Escape or a canvas press ends it; ui.key takes the same keys. Gesture and parity rows, catalog, test, docs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-02 08:13:54 -05:00
Trevor BrownandClaude Fable 5.1 2e392e99b1 M8: a fill per region: the Fill section and the fill commands edit the regions selected in vector edit mode (#1875)
fill.set/add/remove/move/paste on the edited layer go to the selected regions' own fill stacks (or those named by regions), the layer's fills untouched; the Fill section and the colour picker list them. Test regions_tests::the_fill_commands_edit_the_selected_regions; docs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-02 07:55:27 -05: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 0a9ecbe596 Merge fix: 135 documented shortcut rows, 134 bound
Both sides of the merge added a documented row: the counts the parity test and the docs table quote move with them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-02 07:09:01 -05:00
Trevor BrownandClaude Fable 5.1 695ca07528 M3: text styles carry paragraph spacing and indent, the list style and the OpenType features (#1829)
ParagraphStyle beside TextStyle in StyleKind::Text: style.create takes it from the layer or a value, style.apply sets it (the list on the paragraphs touched for a range), style.update moves it, editing it unlinks, .fig files keep it with the style; tests in styles_behaviour_tests and fig_export_assets_tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-02 06:43:02 -05:00
Trevor BrownandClaude Fable 5.1 e04e86f38d Merge upstream/main into fix/text-mixed-fill-section
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-02 06:02:49 -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
Trevor BrownandClaude Fable 5.1 b846d586a8 Merge upstream/main into fix/padding-all-sides
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-02 06:02:48 -05:00
Trevor BrownandClaude Fable 5.1 e87b46b4e3 Merge upstream/main into fix/scrub-speed
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-02 06:02:47 -05:00
Trevor BrownandClaude Fable 5.1 3d704ce4c0 M1: scrubbing a field faster or slower by the pointer's height, with a notice of the speed (#1837)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-02 05:32:33 -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
1f0acd2bbd M4: Alt+Enter while editing text opens the link at the caret (text.openLink) (#1860)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-02 05:31:21 -05:00
f9d507f7b0 M2: a child resized to the full available space of its auto layout frame becomes Fill container (#1859)
* M2: a child resized to the full available space of its auto layout frame becomes Fill container

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

* Auto layout: a child becomes Fill for the full space only across the flow or alone along it

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

* Auto layout: the full available space is what layout leaves (padding and an included inside stroke), judged after a new path's size

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

* M2: a rotated child resized to the frame's room stays Fixed (review)

CodeRabbit on #1859: a rotated layer's local size isn't the room layout
gives it, so Fill would squash its geometry.

Co-Authored-By: Claude Opus 5.5 <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:11 -05:00
9f09b7163c M2: a double-click on an auto layout edge sets Hug contents, with Alt Fill container (#1838) (#1867)
* M2: a double-click on an auto layout edge sets Hug contents, with Alt Fill container (#1838)

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

* Auto layout edges: a text child fills with Alt too, and still enters on a plain double-click

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:00 -05:00
c44cfc94af M4: superscript and subscript are synthesised where the font has no glyphs for them (#1828) (#1868)
* M4: superscript and subscript are synthesised where the font has no glyphs for them (#1828)

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

* Text: a zero OS/2 script offset counts as unset, and the native-glyph check sees the case-mapped characters

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:30:50 -05:00
e9eb7ce7f4 M4: max lines on auto-width text keep it auto width, hugging that many lines (#1864)
* M4: max lines on auto-width text keep it auto width, hugging that many lines

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

* M4: an auto-width text with max lines hugs the ellipsis on its last line, and the editing test expects auto width

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:29:59 -05:00
3358961033 M4: Backspace or Delete at a list item's start keeps its indentation level (#1862)
* M4: Backspace or Delete at a list item's start keeps its indentation level

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

* Lists: the level is kept by Backspace and Delete alone, and the tests read the text's own glyph

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:29:33 -05:00