Commit Graph
2340 Commits
Author SHA1 Message Date
BFlat 84e0ea5449 Merge pull request #1393 from kler/swedish-ui-2026-10-26
i18n(sv): translate 47 new strings
2026-10-29 09:35:37 +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
BFlat 95fd3c2444 Merge pull request #1482 from StrajnarFilip/claude/fix-1452-stroke-weight
M1: the stroke weight keeps room for its value beside the variable button (#1452)
2026-10-29 09:29:26 +07:00
BFlat 712074ae2d Merge pull request #1481 from StrajnarFilip/claude/fix-1085-normalize-guard
M8: normalising after a command runs under the panic guard (part of #1085)
2026-10-29 09:29:21 +07:00
BFlat f8355c0680 Merge pull request #1480 from StrajnarFilip/claude/fix-1078-linear-repair
M8: repair and library manifests check membership by set lookups (#1078)
2026-10-29 09:28:53 +07:00
bflatastic 1ed9eb6b8c Merge main into #1393 2026-10-29 09:20:18 +07:00
Christopher Linnett 7660877845 fix(present): nothing opens or releases behind an open link notice (#1096)
- finish(): a viewer-fired link opens directly (notice skipped) only while
  no notice shows; any other link that fires while one shows is dropped.
- finish(): raising a notice ends the press under way, so its release
  doesn't fire the prototype's mouse-up actions (a link, a navigation)
  behind the notice, which takes no input while it shows.
- The mail notice says the link opens the mail app to write a message
  (opening a mailto: link doesn't send anything).
- Tests: nothing_opens_while_a_notice_shows_with_the_notice_skipped,
  a_press_that_raised_a_notice_doesnt_release_behind_it.
- docs/prototyping-parity.md row and revision history.
2026-10-29 08:50:00 +08: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 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
Filip StrajnarandClaude Opus 5.5 8d7c094224 M1: the stroke weight keeps room for its value beside the variable button (#1452)
The stroke's position dropdown took half the row and the weight field what
was left after the per-side and Advanced buttons: about 40 pt at the
panel's default width, so the variable button sat on the weight's icon and
hid its value, and the row ran 3 pt past the panel's edge. The position
and the weight now share what the buttons leave: the panel's two columns
for layers without per-side strokes, and about 66 pt each with them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LvG6E2qDnjtMzfPVuEP7iH
2026-10-28 22:10:28 +01:00
Filip StrajnarandClaude Opus 5.5 d5ce10f0be M8: repair and library manifests check membership by set lookups (#1078)
Repair re-listed unlisted pages with a scan of the page list per page, and
reading a published library kept manifest styles, collections and variables
with a scan of the snapshot's lists per entry: a file with many pages or a
long manifest made both quadratic. Both now look ids up in a set built once.
validate() already indexed its parent/child edges and page list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LvG6E2qDnjtMzfPVuEP7iH
2026-10-28 22:10:28 +01:00
Dylan M. TaylorandClaude 2af5e232d5 Honor CARGO_BUILD_TARGET in FreeBSD packaging for aarch64 release builds (#1475)
Resolve host-tuple to the host triple, pass --target to cargo build when
CARGO_BUILD_TARGET is set, and skip the --version smoke run for cross
builds (they cannot run on the x86_64 VM); cross builds get an ELF check
instead. Archive names carry the arch from the target triple.

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-28 15:48:52 -04:00
Brandon ThomasandClaude Opus 5.5 ef9721c5b9 Release 0.12.1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.12.1
2026-10-28 13:37:05 -04:00
Brandon ThomasandClaude Opus 5.5 cb227c9715 Web: raise the wasm size gate to 25 MiB - 512 KiB
The 0.12.0 release's Web job failed at 25,319,327 bytes against the 24 MiB (25,165,824-byte) gate. opt-level z, fat LTO and wasm-opt -Oz are already in place and extra wasm-opt passes save only ~20 KB, so the gate moves to 25,690,112 bytes, still under Cloudflare's 25 MiB per-file cap. The wasm grew ~2 MB in three days (egui 0.37, new features); bringing it back down is follow-up work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 13:36:52 -04:00
Dylan M. Taylor 32e8fd0b29 Grant network access so update checks can reach GitHub (#1471) 2026-10-28 13:20:45 -04:00
Dylan M. Taylor fa1c6cba42 Merge pull request #1403 from dylanmtaylor/sonar/enforce-clippy-standards
Enforce Rust coding standards via Clippy
2026-10-28 12:50:16 -04:00
Brandon ThomasandClaude Opus 5.5 08204fd7c2 Release 0.12.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 12:18:40 -04:00
Dylan M. Taylor 94cf3f3fd5 test(text): derive wrap threshold from matching glyph metrics 2026-10-28 16:00:36 +00:00
Dylan M. Taylor 763c3a8dc4 refactor(svg): split node rendering and text setup 2026-10-28 16:00:36 +00:00
Dylan M. Taylor 8df2e4f8dc refactor(render): pass context through export helpers 2026-10-28 16:00:36 +00:00
Dylan M. Taylor 95a456f391 refactor(server): simplify storage and collaboration helpers 2026-10-28 16:00:36 +00:00
Dylan M. Taylor ffb4fc7e3e refactor(ui): simplify app and presentation handlers 2026-10-28 16:00:36 +00:00
Dylan M. Taylor 252d909427 refactor(ui): simplify widgets and dialogs 2026-10-28 16:00:36 +00:00
Dylan M. Taylor 927ceb0638 refactor(ui): simplify panels and menus 2026-10-28 16:00:36 +00: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
Dylan M. Taylor bafda992a7 docs: clarify crate lint placement 2026-10-28 16:00:36 +00:00
Dylan M. Taylor 4268bd859f refactor(fig): split mapping and lint test helpers 2026-10-28 16:00:36 +00:00
Dylan M. Taylor 6ebcf6b511 refactor(text): split shaping and layout finishing 2026-10-28 16:00:36 +00:00
Dylan M. Taylor c618e402fa refactor(format): replace wildcard imports 2026-10-28 16:00:36 +00:00
Dylan M. Taylor 99afdd99ab docs: document workspace lint inheritance 2026-10-28 16:00:36 +00:00
Dylan M. Taylor 8591d44909 refactor(doc): declare node property dependencies 2026-10-28 16:00:36 +00:00
Dylan M. Taylor 10bb163626 fix(xtask): apply workspace lint policy 2026-10-28 16:00:35 +00:00
Dylan M. Taylor e1a223bb1f refactor(layout): split flow layout phases 2026-10-28 16:00:35 +00:00
Dylan M. Taylor c420613efa chore(clippy): enforce strict workspace lint policy 2026-10-28 16:00:35 +00:00
aiongg 90054a89c4 Merge pull request #1399 from Jeremy8776/feat/install-scope-policy
Windows installation policy: FigCraft
2026-10-27 17:09:27 -04:00
aiongg 2ed7aee28d ci: use storytold/craft-common-actions at the merged installer-policy commit 2026-10-27 20:51:59 +00:00
Jeremy 738b218837 fix(installer): check the exact architecture-specific machine folder
The pre-signing check matched any folder named Program Files, so it rejected
a valid MSI when Windows resolves Program Files elsewhere, accepted the 64-bit
root for an x86 package and accepted a different app name. Build the expected
folder from the package architecture's resolved root and the policy name.
2026-10-27 19:08:54 +00:00
aiongg 8b591f703d Merge pull request #1437 from Silvertree2010/perf/export-speed
Speed up raster export: faster blur and spread passes
2026-10-27 13:40:56 -04: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
Jeremy 2893518c84 fix(installer): resolve the wizard scope and folder correctly
Real msiexec runs (per-user and all-users, silent and wizard) found two defects
that the metadata fixtures cannot see:

- "Only for me" published ALLUSERS=2, so FindRelatedProducts treated the run as
  per-machine and skipped the existing per-user copy. An interactive upgrade
  left two registered products and uninstall left files behind. The page now
  leaves ALLUSERS unset for a per-user choice.
- Windows Installer resolves Program Files once, for the context it started in,
  so choosing "Everyone" in the wizard installed into the user profile with a
  machine-wide registration. The wizard now sets the new-install folder itself
  after the choice (Program Files, Program Files (x86) for 32-bit, or the
  per-user Programs folder). Silent installs are unchanged.

check-install-policy.ps1 now fails if either regresses.
2026-10-27 15:32:22 +00:00
aiongg bdb7e84606 fix(ui): dropdown menus grow back after a search with no matches (#1429)
* feat(ui-egui): font family menu keeps its search field and grows back after a filter clears

Repin craft-libs to the shared dropdown (craft-ui), move the font family menu to
craft_ui::combo::Menu with the search in a fixed header, and use craft_ui::ComboBox
for the credits sort. clippy bans egui::ComboBox.

* chore(deps): craft-libs 60d60d48 (dropdown fixes)

* chore(deps): craft-libs 4d7b82f7 (dropdown fixes)

* chore(deps): craft-libs 92d392cf (#387 merged)

* fix(ui-egui): craft-ui on every target (wasm uses the shared ComboBox too)
2026-10-27 10:28:10 -04:00
Jeremy 8d1460c3c0 fix(installer): harden install-policy checks and packaging-lint workflow 2026-10-27 12:01:18 +00:00
Jeremy 1288e40a53 fix(installer): address scope and ARM64 review feedback 2026-10-27 12:01:18 +00:00
Jeremy d17d3f4860 feat(installer): default to a per-user ArtCraft folder 2026-10-27 12:01:18 +00:00
aiongg 43af145a12 Merge pull request #1394 from aiongg/layout
refactor: shared module layout (craft-contrib#20)
2026-10-26 21:30:55 -04:00