Commit Graph
275 Commits
Author SHA1 Message Date
bflatastic d3f382e9c6 Merge main into #1499 2026-10-29 23:22:53 +07:00
BFlat 436cfa1b96 M9: the replay log is bounded in bytes by default (#1069) (#1524)
The batches a session keeps for apps that reconnect were bounded only by count on a desktop host
and a private figcraft-server (`max_log_bytes` defaulted to usize::MAX). It now defaults to
64 MiB, and figcraft-server takes it from a new `max_log_mb` setting (default 64; open to the
public, also at most a quarter of `max_file_mb` as before). An app behind the trimmed log catches
up with a snapshot, as it already did past the count limits. The protocol is unchanged.

Nine 8 MiB renames with default limits: all 75.5 MB kept before, 58.7 MB after.

Fixes #1069
2026-10-29 20:02:46 +07:00
BFlat 3d5185c2ab M0.9: web wasm back under the 24 MiB gate (#1472) (#1511)
The browser build's wasm was 25,982,540 bytes, past the gate raised for 0.12.1. It is now
23,915,213 bytes (22.8 MiB), 1.2 MiB under the restored 24 MiB gate:

- Catalog rows keep their plural forms behind a reference: Text is two words, not room for
  four strings, across ~52,000 baked-in rows (-454 KB).
- The browser build compiles with wasm SIMD (apps/figcraft-web/.cargo/config.toml, wasm-opt
  --enable-simd), so egui's glyph rasterizer leaves out its scalar SIMD fallback (-1.6 MB).
  The plugin workspace is built from the repository root and stays without SIMD.
- Revert the gate raise (cb227c97); the hosting guide names the WebAssembly SIMD requirement.
2026-10-29 16:14:40 +07:00
BFlat fe7b4bac63 Merge pull request #1143 from Guillergood/feat/penpot-import-export
M6: import and export Penpot 2.x files offline
2026-10-29 14:20:14 +07:00
BFlat e1c7879957 Merge pull request #1360 from denisg0mes/place-images-at-own-size
M7: place images at their own size, in points for 2x screenshots
2026-10-29 13:52:42 +07:00
bflatastic 0a6642747a Merge main into #1143
file.open and file.export docs: main's (reload, export options) plus the Penpot format.
2026-10-29 13:51:11 +07:00
samhyunandClaude Fable 5.1 f0996645b1 M8: the browser applies input sent over its control channel
`ui.input` over `window.figcraft` (the page's control channel, `?control`) never did anything in
the browser: the batch was queued for the next frame as on the desktop, but the browser shell
has no `raw_input_hook`, the eframe callback that hands a queued batch to the frame about to
draw (`windows::take_input`). The events stayed in their queue and the request's promise never
settled, so an agent driving the page could run commands and take screenshots but not point,
click or type.

The browser shell now has the hook the desktop app has: the batch goes into the frame's raw
input, is applied by it and is acknowledged once the frame is presented, as on the desktop.

Checked in Chrome on the release web build: `ui.input` with a key (O, R, F: the ellipse,
rectangle and frame tools) and with a pointer gesture (a drag with the rectangle tool: a
rectangle drawn) resolves and takes effect, where on `main` the same requests time out and
change nothing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-29 15:34:14 +09:00
bflatastic 1e22268602 Merge main into #1143 2026-10-29 12:44:42 +07:00
bflatastic d8a4de1bd5 Merge main into #1360 2026-10-29 11:28:49 +07:00
bflatastic f99f01d57b Merge main into #1460 2026-10-29 11:09:30 +07:00
bflatastic 9202b1032f Merge main into #1460 2026-10-29 10:38:06 +07:00
bflatastic 6abddb0f68 Merge main into #1465 2026-10-29 09:55:55 +07:00
bflatastic 9d42833008 Merge main into #1465 2026-10-29 09:18:39 +07:00
Christopher Linnett 73398cedf1 fix(app): ask before ⌘Q quits with unsaved changes (#857)
winit's default macOS app menu ends the process through AppKit's
`terminate:` at once, so ⌘Q and Quit from the menu bar never asked about
unsaved changes (the recovery copies were all that survived).

- Install craft-libs' app menu (craft-menu, same rev as the other
  craft-libs pins; macOS only). Its Quit sends the main window a close
  request, the same path as the window's close button, so
  windows::on_close_request cancels the close and the unsaved-changes
  question asks first. Failure to install keeps winit's menu and logs it.
- windows_tests::a_quit_with_unsaved_changes_waits_for_the_answer pins
  the contract the menu relies on: a close request with unsaved changes
  sends CancelClose and asks; Don't Save then closes.
- Like the close button it closes the main window only: with several
  windows open, each quit closes one (documented).
- docs/hardware-parity.md macOS row, status line and revision history.
- Cargo.lock: craft-menu, craft-quit, craft-keymap, muda and their deps;
  cargo's own minimal resolution also moves a few windows-sys/getrandom
  edges to versions already in the lock.
2026-10-29 08:11:13 +08:00
Denis Gomes 57dcff6d9a M7: #1360 review: cap the clipboard PNG's bytes before copying it
The pixel cap reads only the PNG's header, so a small image followed by a
large tail of data passed it and was copied and base64-encoded whole.
MAX_CLIPBOARD_PNG_BYTES (256 MB, the same cap as design data read from the
clipboard) is checked on the NSData length before the copy on macOS, and
again in the paste path so every Services implementation is held to it.
A PNG past the cap is treated like an unreadable one: paste falls back to
the decoded image.
2026-10-28 14:39:35 -04:00
Dylan M. Taylor 95a456f391 refactor(server): simplify storage and collaboration helpers 2026-10-28 16:00:36 +00:00
phonedu 8e6b3bf449 M12: MCP comment feedback and live change events
Comments become the task queue for an attached or headless MCP client: comment.prompt returns a bounded Markdown brief, the Comments panel gains Copy for AI, and the MCP server exposes fix_comments, watch_comments, figcraft://comments and wait_for_changes. Mutating comment.* commands append to a bounded in-memory event log; the control channel's events.poll long-polls it without the UI thread, and a successful batch commits its events only if the whole batch lands. No model, network dependency, asset or file-format change.
2026-10-28 21:42:31 +08:00
MortenHustedandClaude Opus 5.5 27c11858e1 M0.9: MCP tools and batch steps refuse names they don't take (#1413)
`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>
2026-10-28 09:06:59 +01:00
Guillermo Bueno 7ea161f33f M6: merge main and isolate offline Penpot import/export 2026-10-27 18:59:49 +01:00
Denis Gomes d94609d63f M7: #1360 review: narrower density match, decoded image when the PNG is unreadable
A density now counts only within 0.005 of a whole 2x-4x multiple of
72 dpi. That covers the rounding of pixels per metre (144 dpi is stored
as 5669 or 5670) and rejects nearby resolutions: 141, 143 and 145 dpi
were read as 2x under the old 0.05.

Paste falls back to the decoded clipboard image when the clipboard's PNG
can't be read, instead of pasting nothing.

Also the line on what the objc2 crates are for, and the ROADMAP row for
the new placing default.
2026-10-27 01:39:28 -04:00
Denis Gomes a497b99078 M7: place images at their own size, in points for 2x screenshots
A placed image was always fitted into 1000 px on its longer side, so a
screenshot of a 1440 px wide page came in at 1000 px and stopped lining
up with the design it was taken from. Figma places an image at its pixel
size and only scales down a side past 4096 px.

Placing now does the same, and reads the screen density a PNG records:
macOS screenshots carry 144 dpi (pHYs), so an 800 x 600 px capture of a
400 x 300 pt area comes in at 400 x 300, as in Figma. Only whole 2x-4x
multiples of 72 dpi count as a screen density; 300 dpi print scans and
other values keep their pixel size. The image data stays at full
resolution, so exports at 2x and up stay sharp.

On macOS, pasting reads the clipboard's public.png as is instead of the
decoded pixels arboard returns, which drop the density (and the colour
profile). Other platforms keep the decoded image.

Files dropped, Place image, paste and image.place all go through the
same path; an explicit width/height still wins.
2026-10-27 01:38:19 -04:00
aiongg 3102bffc90 refactor(server): split store.rs into store/ 2026-10-26 20:36:50 +00:00
bflatastic 2aae395310 Merge main into #840 2026-10-27 00:31:32 +07:00
bflatastic c208ae442c #840: move the CLI and MCP XML export tests into tests/it 2026-10-26 23:23:08 +07:00
bflatastic e469cc8135 Merge main into #840
# Conflicts:
#	apps/figcraft-cli/src/files.rs
#	apps/figcraft-cli/src/main.rs
#	crates/engine/src/cmd/file.rs
#	crates/engine/src/lib.rs
#	crates/ui-egui/locales/en.toml
#	crates/ui-egui/locales/es.toml
#	crates/ui-egui/src/export_panel.rs
#	crates/ui-egui/src/export_preview_tests.rs
#	docs/file-format-parity.md
#	docs/mcp.md
#	docs/roadmap.md
#	docs/ui-parity.md
2026-10-26 23:23:07 +07:00
bflatastic 1f1c8624c9 #824: window.figcraft is opt-in: installed only with a ?control query entry 2026-10-26 23:10:04 +07:00
bflatastic b87cc3ab47 #824: a request carries main's request gate (state), not the old cancelled flag 2026-10-26 23:06:38 +07:00
bflatastic b9fc8f5113 Merge main into #824 2026-10-26 22:57:20 +07:00
Pedro Monteiro 11cf6fb12a fix: fsync the files directory after creating a new files folder (#1093) 2026-10-26 02:34:04 -03:00
bflatastic 27d1089847 Merge main into #1021
# Conflicts:
#	ROADMAP.md
#	docs/target-app-parity.md
2026-10-26 10:43:34 +07:00
BFlat bcde334043 Merge pull request #1314 from denisg0mes/keep-images-past-budget
M7: don't save over a file whose images past the budget weren't loaded
2026-10-26 10:41:03 +07:00
bflatastic 7177f1c042 Merge main into #1021
# Conflicts:
#	ROADMAP.md
#	docs/roadmap.md
2026-10-26 10:29:24 +07:00
BFlat f229d91ff2 Merge pull request #1243 from SusieeTheLinuxUser/fix/1239-wayland-clipboard
UI: Route text clipboard writes through egui (#1239)
2026-10-26 10:25:41 +07:00
BFlat b2ac04bdf8 Merge pull request #1357 from storytold/land/1017
M9: make graphics-startup assertions independent of UI language (#1017)
2026-10-26 10:24:00 +07:00
bflatastic 6c4bb00c9e Merge main into #1021
# Conflicts:
#	crates/text/src/fontdb_tests.rs
2026-10-26 10:20:43 +07:00
BFlat e03ebccece Merge pull request #1209 from aryann2611/mobile-layout
M6: mobile layout (phones and portrait tablets)
2026-10-26 10:20:07 +07:00
bflatastic 42ac4a50d2 Merge main into #623 2026-10-26 09:20:06 +07:00
bflatastic 5a9a45795a Merge main into #1209 2026-10-26 09:19:57 +07:00
bflatastic 6fe1618ed4 Merge main into #1017 2026-10-26 09:19:54 +07:00
bflatastic 3bd905fbdf Merge main into #1243
# Conflicts:
#	apps/figcraft/src/services.rs
2026-10-26 09:15:53 +07:00
bflatastic 07a654959a Merge main into #1021
# Conflicts:
#	docs/file-format-parity.md
2026-10-26 09:12:39 +07:00
bflatastic 1ec74401e6 Merge main into #1314 2026-10-26 09:11:52 +07:00
bflatastic fa6c1bd3f4 Merge main into #1209 2026-10-26 08:41:46 +07:00
bflatastic 08ce40fd88 #1134: name the snapshot checksum's place SNAP_HEADER - 4, not 13 2026-10-26 08:35:22 +07:00
BFlat 57af418419 Merge pull request #1134 from F3R9/pr/server-memory-retention
M9: avoid unnecessary snapshot rewrites and duplicate buffers
2026-10-26 08:33:59 +07:00
BFlat 23b443978e Merge pull request #919 from YarilaFox/fix/windows-gnu-a11y-crash
M7: avoid Windows GNU UI Automation crash
2026-10-26 08:33:45 +07:00
BFlat 378ac56286 Merge pull request #1003 from kkilchrist/feat-994-batch-step-refs
command.batch: named step references (`as` / `$ref`) and paths in `field`
2026-10-26 08:31:53 +07:00
Denis Gomes 03f4e6e070 M7: images-past-budget guard: hard links, and script output over its input
Two gaps CodeRabbit found in the guard:

- `same_file` compared paths, plain and resolved. A second hard link to
  the file is a different path to the same data, so `convert`,
  `run --out` and Save As to the link passed the guard and truncated
  the file. It now asks the file system (same-file: device and inode on
  Unix, volume and file index on Windows). same-file was already in the
  lock through walkdir.

- `run --in X --out X` checked the tab the script left active. A script
  that ends on `file.duplicate` or `file.new` leaves a tab with no count
  of images left in X, so X was written over. The count is now taken
  when X is opened and checked against `--out` whatever tab is active;
  the active tab's own file is still checked as before.

The script check is a separate function so it can be tested without a
file past the 4 GiB budget.
2026-10-25 18:41:58 -04:00
Denis GomesandClaude Opus 5.5 c305e23ab4 M7: don't save over a file whose images past the budget weren't loaded
A native file whose images together pass IMAGE_BUDGET (4 GiB, or 20,000
images) opens without the images past it. They are intact in the file, but
saving it again wrote the archive from the images in memory, so every
save, autosave or `convert` onto itself deleted them for good. A 7.6 GiB
`.fig` converted to a 7.3 GiB `.figcraft` reopened with 1,312 of its 2,337
images: one save would have dropped the other 1,025.

Loading now tells images left out for the budget apart from damaged ones
(`Loaded::images_past_budget`). A tab opened or reverted from such a file
remembers the file, and saving over it is refused with a message that says
how many images would be lost and to save under another name; saving a copy
elsewhere works, and the original keeps its images. Auto-save skips the
tab instead of retrying and failing every minute. `figcraft-cli convert`
and `run --in X --out X` refuse to write over their input the same way.
The same file is matched by its resolved path, so `./a.figcraft` and
`a.figcraft` count as one (`file_access::same_file`).

The note for each image left out for the budget now says it stays in the
file, which can't be saved over without losing it. Damaged and missing
images keep the old wording: they can't be kept, and saving drops them as
before. The doc comment on report_lost_images said saving always drops
the images it reports, which is no longer true for budget ones, so it went.

This stops the data loss; it doesn't make such files editable in place.
Loading images on demand would, and is discussed in the issue this links.

Tests: the count and the note for both budgets; saving over the file
refused with the file unchanged, Save As then working, and saving back over
the original refused again; a snapshot encoded in the background refused
too; a tab without images left out saving as before. Checked by hand on the
7.3 GiB file with file.save, file.saveAs, convert and run --out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:31:47 -04:00
Jindřich ŠaršonandClaude Opus 5.5 6258ad2786 M6: Sketch import: merge main
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 22:23:40 +01:00