Commit Graph
12 Commits
Author SHA1 Message Date
Brandon ThomasandClaude Opus 5.5 f9b1b7fb15 Web: size-optimised wasm profile (27.0 → 18.1 MB); bump to 0.3.1 (#109)
The 0.3.0 web release failed packaging/web/package.sh's 24 MiB gate: the wasm grew from
22.2 MB (0.2.0) to 27.0 MB once the plugin sandbox (wasmi + wasmparser, #101) and the
collaboration crates (#103-#105) joined the browser build, which still used the desktop
`release` profile (opt-level 3, thin LTO, 4 codegen units).

- Cargo.toml: new `web-release` profile (inherits release; opt-level "s", fat LTO, one codegen
  unit, no debug info, stripped). opt-level "z" measured 16.9 MB; "s" keeps render speed.
- apps/figcraft-web/index.html: build with that profile, and pass the wasm features rustc emits
  (bulk memory etc.) to wasm-opt, which otherwise rejects the size-optimised module.
- Version 0.3.0 -> 0.3.1. docs/releasing.md: the web profile and size history.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 21:05:23 -04:00
Brandon ThomasandClaude Opus 5.5 a2bf43927f Release prep: 0.3.0 and linux-riscv64 release job (#108)
* Release prep: bump to 0.3.0; add linux-riscv64 release job

- release.yml: new linux-riscv64 job (cross-compiled on ubuntu-24.04 against ports.ubuntu.com
  riscv64 libraries, tar.gz only, CLI smoke-tested under qemu-riscv64), ported from GridCraft;
  the release job needs it so SHA256SUMS covers its tarball.
- packaging/linux/package.sh: CROSS_ARCH / CROSS_TARGET / CROSS_COMPILE / EMULATOR support.
- Version 0.2.0 -> 0.3.0 (cargo xtask version set), Cargo.lock refreshed.
- docs/releasing.md, docs/hardware-parity.md: list the riscv64 tarball.

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

* CI: tell actionlint about the org's github-ubuntu-16core runner label

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:42:22 -04:00
BFlatandClaude Opus 5.5 4d7a9187ff M9: live sessions: host from the app, invite links, live cursors and presence (#104)
* M9: real-time collaboration protocol (design)

docs/collab-protocol.md: WebSocket sessions with a server-ordered,
per-property last-writer-wins document sync, client-scoped ids,
fractional positions for children, ephemeral presence (cursors,
selections, follow), per-user undo, hosting from the app first and a
self-hosted server next, and the implementation plan. ROADMAP gains M9.

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

* M9: collaboration core: ids, change sets, server ordering, convergence

Step 1 of docs/collab-protocol.md, no networking:

- Client-scoped node ids: Document::set_client_id makes alloc_id hand out
  client << 32 | counter; single-user files allocate as before; the id
  counter is no longer part of document equality.
- crates/collab (layer 2): the synced object/property model (every saved
  Node field, plus document-level styles, comments, variables, images,
  dev snapshots, library data), Change batches, an O(changed) diff, strict
  and lenient apply, fractional sibling positions, snapshots.
- Server (ordering, validation, rejects with fixes) and Client (optimistic
  view, replay under remote batches, author-side repair of derived data)
  as pure state machines; binary (version byte + postcard, JSON values) and
  JSON message forms, fuzzed decoders.
- Engine: normalize as the recompute hook, collab.diff.
- Tests: round trips over random real commands, an N-client convergence
  fuzz test over delayed in-order links, junk batches.

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

* M9: fmt after merge

* M9: live sessions: host from the app, invite links, live cursors and presence

Step 2 of docs/collab-protocol.md, networking and presence half:

- crates/collab-net (layer 6): WebSocket transport (sync tungstenite, a
  reader and a writer thread per connection, no async runtime), the
  session host the app embeds (runs figcraft_collab::Server as the
  authority, carries its messages), invite links, presence throttling
  (~30 Hz, latest wins), limits (sockets, hello/idle timeouts, message
  size, rate, slow clients), keep-alive, clean close, reconnect with
  backoff; a browser stand-in until the web-sys client (step 4).
- engine: live session state (status, peers, presence, follow) and the
  collab.host/stop/join/leave/peers/follow/cursor commands; the network
  is reached only through the LiveLink the app installs.
- ui-egui: remote cursors (pointer drawn in code, name tag, gliding),
  selection outlines (once documents sync), avatars in the right panel
  with follow, the Share popover (start, invite link + URL with Copy,
  stop), File > Join live session.
- desktop app and MCP headless session install the link; xtask layers
  keeps networking crates out of L0-L5.
- Documents don't sync between apps yet (follow-up).

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

* M9: live sessions: document sync between apps

- The host's session runs figcraft_collab::Server seeded with the shared
  document; every app (the host's too, through the hub without a socket)
  is a collab Client. Guests open the host's file from the welcome as a
  new tab; leaving keeps it as a local copy.
- Engine: after each local action (a gesture once it ends; nothing during
  version or text previews) the session document is diffed into one
  Changes batch; server batches go through Client::receive with
  EngineRecompute and are installed with Session::apply_remote, which
  never touches the undo history. Batches arriving mid-gesture wait.
- Transport: Changes/Reject/Welcome reach the engine as Event::Sync;
  LiveLink::send carries batches in order; hellos are limited to 64 KiB,
  joined clients to the core's message size.
- Tests: engine sync against a real Server (ids in the client's space,
  one batch per gesture, remote edits skip undo, deferral, closing the
  file leaves), three apps over loopback converging (create, move,
  recolour, delete, rename) and a late joiner getting the server's state.
- Docs: collab-protocol, mcp, README, roadmap docs; the UI's
  "cursors only" note is gone, replaced by the per-person undo caveat.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 02:40:59 +07:00
BFlatandClaude Opus 5.5 4924512f4e M9: collaboration core: ids, change sets, server ordering, convergence (#103)
* M9: real-time collaboration protocol (design)

docs/collab-protocol.md: WebSocket sessions with a server-ordered,
per-property last-writer-wins document sync, client-scoped ids,
fractional positions for children, ephemeral presence (cursors,
selections, follow), per-user undo, hosting from the app first and a
self-hosted server next, and the implementation plan. ROADMAP gains M9.

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

* M9: collaboration core: ids, change sets, server ordering, convergence

Step 1 of docs/collab-protocol.md, no networking:

- Client-scoped node ids: Document::set_client_id makes alloc_id hand out
  client << 32 | counter; single-user files allocate as before; the id
  counter is no longer part of document equality.
- crates/collab (layer 2): the synced object/property model (every saved
  Node field, plus document-level styles, comments, variables, images,
  dev snapshots, library data), Change batches, an O(changed) diff, strict
  and lenient apply, fractional sibling positions, snapshots.
- Server (ordering, validation, rejects with fixes) and Client (optimistic
  view, replay under remote batches, author-side repair of derived data)
  as pure state machines; binary (version byte + postcard, JSON values) and
  JSON message forms, fuzzed decoders.
- Engine: normalize as the recompute hook, collab.diff.
- Tests: round trips over random real commands, an N-client convergence
  fuzz test over delayed in-order links, junk batches.

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

* M9: fmt after merge

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 01:03:03 +07:00
BFlatandClaude Opus 5.5 041b3f4e5d M7: WASM plugins (#101)
Sandboxed WebAssembly plugins whose API is the command registry:

- figcraft-plugin (new crate, L2): the figcraft-plugin.json manifest
  (menu commands with parameters, read/write access, command allowlist)
  and the sandbox: wasmi (pure Rust, no JIT, also in the web build) with
  fuel metering, a memory cap, no WASI and seven host functions (call,
  selection, reply_len, reply_read, log, notify, set_output).
- Engine: plugin.list / plugin.import / plugin.remove / plugin.run,
  plugins.runLast (Alt+Cmd+P; Ctrl+Alt+Shift+P on Windows/Linux) and
  plugins.manage. A run is one undo step (Session::grouped, which
  command.batch now uses too) and is rolled back when it fails. Files,
  other documents, the undo history, dialogs and plugins are off limits;
  read-only plugins can't change the document. Installed plugins live in
  the preferences; development plugins re-read their module every run.
- UI: installed plugins in the Plugins menu, a parameters form drawn
  from the manifest, Manage plugins, Plugins > Development > Import
  plugin from manifest.
- plugins/: a no_std Rust SDK and two example plugins (Tidy names,
  Swatch grid), built for wasm32 by `cargo xtask plugins` (part of
  `cargo xtask wasm`).
- Docs: docs/plugins.md (manifest, permissions, limits, ABI, SDK),
  docs/mcp.md, docs/control-protocol.md, README, ROADMAP, parity.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 00:42:17 +07:00
Brandon ThomasandClaude Opus 5.5 7acc224ca2 About: Contributors and Models tabs with full credits; bump version to 0.2.0 (from 0.1.0) for release
Ports PhotoCraft's credits view (craftrules standards/contributors.md): contributor table with
PR/commit/line stats and verified names, AI-model credits, generated contributors.json, and
docs/contributors.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:10:33 -04:00
BFlatandClaude Opus 5.5 d3dbc6f495 M3: paint: colour profiles (Display P3), image adjustment presets, style picker (#54)
* M3: colour profiles: sRGB or Display P3 per file, convert or keep values, tagged exports

- color: ColorProfile (srgb, displayP3) with conversions derived from the primaries (shared D65
  white and sRGB curve; out-of-gamut channels clip), table-driven pixel conversion, a generated
  ICC v4 matrix/TRC profile and PNG cICP code points.
- doc: Document.color_profile (serde default sRGB, written only when set); every colour value
  (paints, text runs, effects, grids, page and prototype backgrounds, overlay backdrops, styles,
  colour variables, document colours) can be mapped; set_color_profile converts or reinterprets.
- engine: file.colorProfile {profile?, convert?} (File > Color profile...; without a profile it
  opens the dialog); document.inspect reports colorProfile.
- render: images are taken as sRGB and managed into the file's space; encode_export tags PNG
  (sRGB chunk, or iCCP + cICP for Display P3) and embeds the ICC profile in Display P3 JPG/WebP;
  SVG and PDF export Display P3 files converted to sRGB. The CLI exports the same way.
- ui: Color profile dialog (keep appearance / keep values) and a Color profile row on the page
  panel; the canvas, presenter, thumbnails, swatches and colour picker show a Display P3 file's
  colours converted to the screen's sRGB, and Copy as PNG converts too.

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

* M3: image adjustment presets: built-in, saved in the file, reset

- color: ImageFilters::PRESETS (Vivid, Muted, Warm, Cool, Black & white, Fade, Reset), lookup by
  id or label, and sanitized values.
- doc: Document.image_presets (name + adjustments; written only when there are some).
- engine: image.adjust {ids?, target?, index?, preset?, filters?} sets the adjustments of image
  paints from a preset (built-in or saved) with values on top, one undo step; image.savePreset,
  image.deletePreset and image.presets.
- ui: the colour picker's image section gets a presets menu named after the preset in effect
  ("Custom" otherwise), saved presets with delete, "Save as preset...", a Reset button, and
  aligned adjustment sliders.

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

* M3: style picker: search, folders, list or swatch grid, edit, create, detach

The Fill, Stroke, Effects and Layout grid sections' Styles button opens a popover laid out like
the variable picker (crates/ui-egui/src/style_picker.rs):

- search over style names; styles grouped by the folders of their `/`-separated names, which
  collapse (searching opens them); colour styles as a list or a grid of swatches;
- click applies, a check marks the applied style; the row's edit button (or a right-click) edits
  the name, description and values: colour style paints open the colour picker on the style,
  effect and grid styles update from the selection; delete;
- `+` creates a style from the selection's current values and opens it for naming; detach;
- colour slots still list colour variables to bind;
- the applied style chip opens the picker; new icons Adjust, ViewList, ViewGrid (drawn in code);
- control channel: ui.set {stylePicker, stylePickerEdit, stylePickerView}.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 07:38:17 +07:00
BFlatandClaude Opus 5.5 632c5ffc16 M4: right-to-left and mixed-direction text (Unicode bidi) (#34)
Text runs get bidi embedding levels from the Unicode bidi algorithm
(unicode-bidi, MIT/Apache-2.0). Right-to-left runs are shaped right to
left, kept in logical order for line breaking and placed in visual order
per line (rule L2, trailing spaces at the paragraph level). Glyphs and
lines know their direction, so the caret, clicks and selection
highlights follow right-to-left runs. Text without right-to-left
characters skips the analysis.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 05:12:14 +07:00
Brandon ThomasandClaude Opus 5.5 ab5914485e Perf: persistent node map (imbl) and dirty tracking; commands now scale with what changed (node.create 0.2 ms at 20k nodes)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:49:04 -04:00
Brandon ThomasandClaude Opus 5.5 b36fd771ab M0.9: MCP server + figcraft-cli, PDF export, web app, release/CI packaging, vector edit mode, workspace lint cleanup
- crates/mcp: JSON-RPC MCP server (18 tools, resources, prompts), headless and attached backends, end-to-end agent tests
- apps/figcraft-cli: info/export/convert/render/run/commands/mcp/perf
- crates/pdf: vector PDF export via krilla with embedded text; file.exportFramesPdf
- apps/figcraft-web + packaging/{macos,windows,linux,freebsd,web} + release/ci workflows
- engine vectoredit: edit points/handles/bend/add/delete-heal; canvas overlay and edit strip

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:31:53 -04:00
Brandon ThomasandClaude Opus 5.5 b3b48281bd M0.6-M0.7: egui UI3 frontend (canvas, toolbar, layers, design/prototype/inspect panels, menus, color picker, presenter), desktop app with control channel, xtask, generated app icon
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:03:43 -04:00
Brandon ThomasandClaude Opus 5.5 f7a68c864b M0.1-M0.3: workspace, licences, geom/color/doc crates
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 12:01:57 -04:00