Commit Graph
298 Commits
Author SHA1 Message Date
bflatastic 10a778930b Merge main into #1615 2026-10-30 13:50:49 +07:00
BFlat c47177498d M8: every file a command reads by path is a regular file within a cap (#1114) (#1625) 2026-10-30 13:48:26 +07:00
bflatastic 9ae8c2e377 Merge main into #1615 2026-10-30 13:20:02 +07:00
bflatastic cabbb138da Merge main into #1614 2026-10-30 13:04:48 +07:00
bflatastic f11cad01b8 Merge main into #1615 2026-10-30 13:03:09 +07:00
BFlat f1eaf3fcb4 M8: cap the people a client keeps track of and the deleted ids a session remembers (#1622)
* M8: cap the people a client keeps track of and the deleted ids a session remembers (#1121)

A hostile host or server could grow a client's presence, seen-peers and who-is-here lists without bound, and a session's deleted-id set only grew. Clients keep at most MAX_PEERS (4,096) others, leaving newcomers out with a warning; max_people is at most that; a session remembers at most 262,144 deleted ids, the lowest forgotten first.

* M8: a max_people above what clients track is lowered with a warning, not refused (#1121)

Refusing it would stop a server whose existing config says more from starting after an upgrade. Config::people() lowers max_people to MAX_PEERS for the core's limits, and the startup warnings name the configured and the used value.

* M8: the live welcome's handling in its own function (Sonar S3776)
2026-10-30 12:33:29 +07:00
Dylan M. Taylor 0b7528b76d Update prompt shows the app icon and accent color instead of a letter (#1612)
UPDATE_APP (native builds; craft-update is a native-only dependency) carries our 128 px icon and accent (#02da96, its colour in ArtCraft Launcher). The updater, the self-update
helper and craft-update's startup prompt all get them through crate::UPDATE_APP; the
prompt draws the icon, the accent and "by ArtCraft" (craft-update's default developer).

craft-update is pinned to storytold/craft-libs main at 1065653e7 (#434).
2026-10-30 01:28:14 -04:00
BFlat 7d18fa391b M8: libraries and plugins larger than the download burst can be downloaded, more slowly (#1089) (#1619)
The per-address download bucket never held more than its 16 MiB burst, so a larger library or plugin was refused for good. Bucket::take_spread serves one from a full bucket and leaves the debt to refill at the rate before the next download; the rate over time still holds.
2026-10-30 11:39:51 +07:00
87e4abfbf6 #1615: each generated-docs test gets its own temporary directory, removed when it ends
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
2026-10-30 07:11:15 +03:00
05a475dccf M0.9: generate shell completions and man pages for figcraft-cli (part of #1396)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6 Astra <noreply@openai.com>
Co-Authored-By: GPT-6.1 Terra <noreply@openai.com>
2026-10-30 06:49:05 +03:00
8da99e5374 M9: the server compares the operator key by its hash (#1130)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6 Astra <noreply@openai.com>
Co-Authored-By: GPT-6.1 Terra <noreply@openai.com>
2026-10-30 06:49:05 +03:00
BFlat fb0465f325 M8: a server file everyone left stays loaded a minute and its open time is written hourly, so idle join/leave does no disk I/O (#1074) (#1606) 2026-10-30 10:10:46 +07:00
BFlat 019058ffd5 M1: a recovery copy made during a version preview holds the file, not the version shown (#1588)
Desktop recovery copies saved DocState::doc, which is the version shown while a version (or a live text preview) is previewed, so a crash during a preview brought the old version back as the unsaved file. What a recovery copy holds is now one shared helper, branches::recovery_copy: the document itself (document_itself(), the same document the unsaved-changes check compares), its history and its branches. The desktop copies and the browser's (#1580) both use it.
2026-10-30 08:21:47 +07: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 974ab39503 M8: keep unsaved files when a panic stops the browser build (#1123) (#1580)
wasm32 can't catch a panic, so the command and frame guards do nothing in the browser and a panic ended the page with its unsaved changes. The page now follows its files with unsaved changes after every frame; its panic hook writes them to local storage as .figcraft copies (as many as fit) and says on the page that it stopped. The next page opens them again as unsaved files: file.recover also takes the copy as base64 data. The recovery-copy helpers move from the desktop app into figcraft_engine::branches so both share them.
2026-10-30 06:36:33 +07:00
BFlat be7a2ecd3c M0.9: parse figcraft-cli arguments with clap, following RFC-0012 (#1396) (#1570)
clap derive types replace the hand-written parser: typed values, usage
errors (exit 2) before any work, help from the same definitions.

Syntax changes: a repeated option is a usage error (was: last wins; the
automation roots still repeat); a value starting with `-` needs `=`; a
positional starting with `-` needs `--`; `--o` and the `version`
subcommand are gone (`--version` stays); `<sub> --help` prints that
subcommand's help; usage errors are clap's text; `--format` is checked
before the file is opened.
2026-10-30 05:41:26 +07:00
BFlat 3c1718bf86 M9: publish to a server from the browser (Share > Online and Publish to server in the web build, #917) (#1567)
The web transport implements LiveLink::publish and the hosting and sharing background requests over the page's WebSocket. Managing connections run as exchanges (exchange.rs) in the browser client's driver; the requests and what their answers mean are shared with the desktop transport (requests.rs). The Share popover offers online sharing in the browser (no local network), and Publish to server opens there. Fixes #917.
2026-10-30 04:32:48 +07:00
BFlat 704fb0b717 M4: use the fonts installed on this computer in the browser (Local Font Access, #1051) (#1568)
Where the browser lists them (window.queryLocalFonts, Chromium), the font menu offers Use fonts installed on this computer; the fonts join the font database by name and each style's bytes are read only when text uses it, then auto-sizing text refits. Rust only (js-sys/web-sys). Fixes #1051.
2026-10-30 04:14:33 +07:00
BFlat aa727500a4 M1: keep settings next to the executable in portable mode (#1320) (#1558)
The settings folder comes from craft-settings' rules: FIGCRAFT_CONFIG_DIR,
a `portable` file next to the executable (config/ beside it), then the
platform folder every earlier version used. The Windows portable zip ships
the marker, and a portable folder without preferences starts with a copy
of the platform folder's.
2026-10-30 03:35:12 +07:00
BFlat 28a0fe6d18 M9: the admin budget test allows for the bucket refilling during a slow run (#1564) 2026-10-30 03:23:11 +07:00
BFlat 05f811105c M1: start on other graphics when the window's first ones fail (#965, #1438) (#1557)
Direct3D 12 compiles with FXC and no device compiles wgpu's indirect-call
validation shaders, the two causes of "Parent device is lost" at start.
When the graphics still fail before the first frame, the app starts again
with the next of a fixed list of attempts (Windows: Vulkan, OpenGL; Linux
and FreeBSD: OpenGL; then lower requirements on the power-saving GPU), and
the last failure's message names what was tried.
2026-10-30 03:04:11 +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
BFlat f6ccef0c5d M9: a per-address budget for server admin requests, and join codes found by hash (#1075) (#1552)
* M9: a per-address budget for server admin requests, and join codes found by hash (#1075)

* M9: the server tests share one snapshot and address helper (#1075)
2026-10-30 00:20:57 +07:00
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