Commit Graph
827 Commits
Author SHA1 Message Date
bflatastic a6b41b2f4c Merge main into #1484 2026-10-29 09:31:53 +07:00
BFlat cff1d0bf45 Merge pull request #1487 from ChristopherLinnett/issue/1096-openurl-gesture
fix(present): prototype links open only behind the notice or a viewer's click (#1096)
2026-10-29 09:30:04 +07:00
BFlat baaafc381b Merge pull request #1485 from StrajnarFilip/claude/fix-1476-guides-reopen
M1: page ruler guides survive save and reopen (#1476)
2026-10-29 09:29:31 +07:00
Christopher Linnett 53f47706ec fix(present): prototype links open only behind the notice or a viewer's click (#1096)
An Open link action carries a URL from the document and fires from whatever
trigger the prototype sets, so an unattended presentation could open web links
and mail messages one after another: a mail link bypassed the leaving-the-
prototype notice entirely, and a skipped notice let any trigger open any
number of links.

- finish() routes every openable URL, mail included, through the notice.
- Skipping the notice ("Don't show this again") opens at once only a link the
  viewer's own click, press, drag or key fired, one per frame. A delay, the
  pointer passing over a layer (enter, leave, hover) and a timed mouse
  trigger that fires after the gesture is over still show the notice, which
  then offers no checkbox: it couldn't skip that notice anyway.
- The player tags each queued link with whether the viewer fired it
  (Presenter::fire_from); an OpenUrl outside an interaction defaults to not.
- The notice covers mail links (new locale string, mail wording).
- Engine prefs/command doc text follows the rule; behavior untouched there.
- present_links_tests: shared setup helpers for the link tests; new
  tests for the skip rule (delay, hover, delayed mouse-up, key, one link per
  click, checkbox only for clicked links); the scheme-filter test now clicks
  with the notice skipped so the filter alone keeps those URLs out.
- docs/prototyping-parity.md row, status line and revision history updated.
2026-10-29 08:11:13 +08:00
Filip StrajnarandClaude Opus 5.5 652bf35b19 M1: page ruler guides survive save and reopen (#1476)
A page kept its ruler guides in `NodeKind::Page`, and since frame guides
(#70) every layer also has its own `guides` field. Both are written as
`guides`, and on reading the layer's own field takes the key, so:

- a reopened page's guides landed in the layer field and were not drawn;
- saving that page wrote `guides` twice (the page kind's list, always
  written, then the layer's), and the file no longer opened ("duplicate
  field `guides`").

Every layer, pages included, now keeps its guides in the one layer field;
the key in the file is unchanged, so existing files open with their guides.
The field reads every `guides` list a layer has, so files already saved
with the key twice open again with both lists, in file order. This also
covers collab `Create` payloads from older peers.

Regressions: JSON round trips of page and frame guides, a page with two
lists, a bad list still refused (doc); save, reopen, save, reopen through
`.figcraft` and a document.json with the key twice (format). Checked in the
app over the control channel: guides drawn after two save/reopen cycles, and
a file with the key twice opens with all its guides.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LvG6Dyzof21BCrUsByhx2w
2026-10-28 22:47:24 +01:00
Filip StrajnarandClaude Opus 5.5 2d6638599d M3: scaled instances keep their scale when their main is rebuilt (#922)
The Scale tool (and `transform.scale`) scales a layer's children whatever
their constraints. After the edit, overrides are recorded by diffing the
changed layers, and a size or place that changed while an ancestor resized
is read as what constraints give, so it isn't an override. For a scaled
instance that left its layers' scaled size and points unrecorded: the next
rebuild of the instance (a layer added to its main, say) put a 14 px
icon's triangle back at the main's 24 px size. Strokes already kept their
scaled weight, as a separate override group.

`scale_node` now marks `size` and `position` overrides on the layers it
scales below the scaled layer when they are layers of an instance and the
value changed. Undo takes them back with the rest of the edit.

The report's own sequence (clearing effects on the component holding the
icon) no longer reproduces on main; this covers the rebuild path that
still did. Regressions: a scaled instance through value edits and a
rebuild of its main; one scaled with the tool, with undo; a scaled icon
nested in a component and in an instance of that component, through edits
of both mains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LvG6Dyzof21BCrUsByhx2w
2026-10-28 22:45:32 +01:00
Filip StrajnarandClaude Opus 5.5 62c69ba7ab M8: normalising after a command runs under the panic guard (part of #1085)
Session::execute ran a command under guard::run but normalised what it
changed afterwards, outside it: a panic in normalisation escaped execute
instead of becoming EngineError::Internal with the edit rolled back. Inside
a batch it unwound through Session::grouped, which then never restored the
undo limit it had lifted or ended the group. Normalising is now the last
step of the guarded result chain, so a panic there fails the command like
one in the command itself (and a batch step fails like any other failed
step); live text previews normalise under the guard too.

The other half of #1085, restoring grouped()'s state when its closure
panics, is left alone: #1419 rewrites that function.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LvG6E2qDnjtMzfPVuEP7iH
2026-10-28 22:11:31 +01:00
Dylan M. Taylor e98b97de2b refactor(engine): simplify session and test helpers 2026-10-28 16:00:36 +00:00
Dylan M. Taylor d87f9effe1 refactor(engine): simplify asset and code generation helpers 2026-10-28 16:00:36 +00:00
Dylan M. Taylor 5e16683963 refactor(engine): simplify canvas and board handlers 2026-10-28 16:00:36 +00:00
Dylan M. Taylor 23f57c8660 refactor(engine): simplify command handling for strict clippy 2026-10-28 16:00:36 +00:00
NetterSilverandClaude Opus 5.5 adb1b43033 Speed up raster export: faster blur and spread passes
Shadows and layer blurs dominated PNG/JPG/WebP export time (gdb stack
sampling of the example files: about 45% in fx::box_blur_h, 13% in
fx::morph). Both now produce the same bytes, faster:

- fx::blur runs its three box passes per row while the row is in cache,
  works on whole pixels instead of bounds-checked bytes, skips fully
  transparent rows, transposes in cache-friendly column blocks, and
  splits rows and transposes across rayon on desktop for pixmaps of
  64K pixels and more, only when not already on a rayon worker (a band
  of render_view stays on its thread: no nested parallelism).
- fx::morph (shadow spread on layers that aren't one outline) uses the
  van Herk / Gil-Werman running max/min: constant work per pixel instead
  of 2r + 1.

Integer arithmetic and pass order are unchanged; new tests compare both
against the previous implementations byte for byte, including sizes on
the parallel path.

An ignored timing test exports every top-level frame of examples/ as
PNG, JPG and WebP at 2x through export_file:
cargo test --release -p figcraft-engine --lib export_timings -- --ignored --nocapture

Ryzen, 16 threads, Linux; median of 7 (min):
  PNG   2195 ms (2182) ->  992 ms (989)   -55%
  JPG   2880 ms (2560) -> 1550 ms (1535)  -46%
  WebP  1964 ms (1901) -> 1022 ms (1018)  -48%
  total 7035 ms (6677) -> 3567 ms (3555)  -49%
All 80 exported files (PNG, JPG, WebP, PDF, SVG) are byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 17:43:23 +01:00
aiongg 0330a7cc85 refactor(engine): move assets/components test files to assets/components/ 2026-10-26 20:36:51 +00:00
aiongg 2576e646a1 refactor(engine): move sharing/background test files to sharing/background/ 2026-10-26 20:36:51 +00:00
aiongg 7d70e0bfe5 refactor(engine): move assets/library test files to assets/library/ 2026-10-26 20:36:51 +00:00
aiongg 9c97cb6e17 refactor(engine): move assets/library_server test files to assets/library_server/ 2026-10-26 20:36:51 +00:00
aiongg 32bd9dc35a refactor(engine): move fonts test files to fonts/ 2026-10-26 20:36:51 +00:00
aiongg 3398ec3c8c refactor(engine): move automation/file_access test files to automation/file_access/ 2026-10-26 20:36:50 +00:00
aiongg f664a82093 refactor(engine): move assets/library_swap test files to assets/library_swap/ 2026-10-26 20:36:50 +00:00
aiongg a9d93f8310 refactor(engine): split cmd/file.rs into file/ 2026-10-26 20:17:48 +00:00
aiongg 96423f5a6e refactor(engine): move cmd/file.rs zip_cap_tests to file/zip_cap_tests.rs 2026-10-26 20:17:48 +00:00
aiongg 13855cfabe refactor(engine): move cmd/file.rs staging_write_tests to file/staging_write_tests.rs 2026-10-26 20:17:48 +00:00
aiongg 47c807f209 refactor(engine): move cmd/file.rs rest_import_tests to file/rest_import_tests.rs 2026-10-26 20:17:48 +00:00
aiongg f0f1c2003d refactor(engine): move cmd/file.rs tab_order_tests to file/tab_order_tests.rs 2026-10-26 20:17:48 +00:00
aiongg 1e9e0a4e75 refactor(engine): split session.rs into session/ 2026-10-26 20:17:48 +00:00
aiongg c4bd0dd542 refactor(engine): move Session state, prefs, errors and methods out of lib.rs 2026-10-26 20:17:48 +00:00
aiongg 0b9fcb7f4e refactor(engine): split cmd/component.rs into component/ 2026-10-26 20:17:48 +00:00
aiongg cea90cf935 refactor(engine): split cmd/edit.rs into edit/ 2026-10-26 20:17:48 +00:00
aiongg 688eeccc7a refactor(engine): split boards/board.rs into board/ 2026-10-26 20:17:48 +00:00
aiongg ad4ba1ae9b refactor(engine): split dev/codegen.rs into codegen/ 2026-10-26 20:17:48 +00:00
aiongg c631450b0c refactor(engine): split sharing/live.rs into live/ 2026-10-26 20:17:48 +00:00
aiongg 0bc49705bd refactor(engine): split assets/components.rs into components/ 2026-10-26 20:17:48 +00:00
aiongg 2e7790b58b refactor(engine): split canvas/tools.rs into tools/ 2026-10-26 20:17:48 +00:00
aiongg e1b0ea2c1d refactor(engine): move batch, params, inspect, file_access, doc_urls to automation/ 2026-10-26 20:17:48 +00:00
aiongg 2cf15106e1 refactor(engine): move datafill, textfit, templates, synthetic, synthetic_ds to content/ 2026-10-26 20:17:48 +00:00
aiongg 53b04cc784 refactor(engine): move plugins, plugin_directory, widgets to extensions/ 2026-10-26 20:17:48 +00:00
aiongg fa8e917ede refactor(engine): move collab, live, online, branches, invite_links, background to sharing/ 2026-10-26 20:17:48 +00:00
aiongg 9452ec1ada refactor(engine): move annotations, code_connect, codegen, component_codegen, tailwind, devcompare, devinspect, dev_measurements, dev_resources to dev/ 2026-10-26 20:17:48 +00:00
aiongg e466f4a03a refactor(engine): move components, library, library_server, library_swap, style_vars, playground, token_roles to assets/ 2026-10-26 20:17:48 +00:00
aiongg a587d964c9 refactor(engine): move selection_colors, selection_reorder, selection_spacing, rename to selection/ 2026-10-26 20:17:47 +00:00
aiongg 094b01e501 refactor(engine): move tools, snap, distance, guides, drop, arc_handles, shape_handles, rotation_origin, al_keys, booleans, shapebuilder, textedit, textpreview, vectoredit to canvas/ 2026-10-26 20:17:47 +00:00
aiongg 5d1b826f7a refactor(engine): move board, board_table, board_templates to boards/ 2026-10-26 20:17:47 +00:00
aiongg a3179236f3 refactor(engine): move textedit.rs inline tests to crates/engine/src/textedit/tests.rs 2026-10-26 20:17:47 +00:00
aiongg 71524803d1 refactor(engine): move crates/engine/src/templates/mod.rs to crates/engine/src/templates.rs 2026-10-26 20:17:47 +00:00
aiongg d853eee820 refactor(engine): move crates/engine/src/cmd/mod.rs to crates/engine/src/cmd.rs 2026-10-26 20:17:47 +00:00
BFlat cac54fd552 Merge pull request #840 from StrajnarFilip/feat/vector-drawable-export
M6: export as Android Vector Drawable (XML)
2026-10-27 01:06:21 +07:00
BFlat 740cc2a2c6 Merge pull request #726 from ijashuzain/feat/variables-dart-export
M6: export variables as Dart constants for Flutter
2026-10-27 00:53:10 +07:00
BFlat 29c89f7d5b Merge pull request #650 from JimKnoxx/overlay-anchor-trigger
M5: overlay positions relative to the layer that opens them
2026-10-27 00:49:52 +07:00
bflatastic 2aae395310 Merge main into #840 2026-10-27 00:31:32 +07:00
bflatastic fd0f4d8b60 Merge main into #664
Main's class_name takes the node kind (#1372); Vue's slot names pass slot.
2026-10-27 00:30:42 +07:00