Commit Graph
13 Commits
Author SHA1 Message Date
bflatastic c2ad9f8bbb Type: emoji go with type that effects reshape and Expand Appearance
Squashed from #3879 onto main.
2026-10-24 04:49:06 +07:00
Drew DibbleandClaude Opus 5.5 a7ee168efe perf --deep: the embedded-picture import row reports a failed import instead of timing it
One import is checked first and its document kept through the memory reading after the row, so
a failure stops the run with its error rather than being timed as an import (CodeRabbit). The
row's comment now gives the picture's real size, about 2 MB as a PNG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 01:01:27 +07:00
Drew DibbleandClaude Fable 5.1 5b919c0529 SVG import: read the text once for the prepass's markers, with the data URLs left out
The prepass decided what to look for in the tree by a dozen substring searches over the whole
SVG text (`:not(`, the event attributes, `script`, `transform:` and nine more), and a separate
pass counted the elements' depth before roxmltree. With an embedded picture the text is mostly
its data URL: a 1.35 MB card SVG (a 968 KB PNG) paid about 7 ms of searches and 1 ms of depth
scan per import, on a 64 ms render, and a 54-page PDF export 54 times that.

`depth::prescan` is the one pass: it counts the depth as `xml_too_deep` did (the DOCTYPE entity
count included) and, in the same walk, notes every `data:` attribute value, answering the text
with those values left out, their quotes kept. The prepass and `fx::blends` search that text;
the edits are still made in the source. A data URL can hold no tag, attribute name, selector or
CSS declaration, and everything else, a `style="display:none"` included, is kept, so what is
found is what was found before.

`vectorcraft-cli perf --deep` gains a tracked row, "import SVG, one embedded PNG", beside the
export row it had.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-23 01:01:27 +07:00
dotCooCoo 6928f422d3 CLI: perf --deep reports the peak working set on Windows and the peak resident memory on Linux
perf --deep printed the memory at eight points of its run, and no row
showed memory that the run took and freed between two readings. With
--paths 2000, a release build on Windows printed 430 MB as its largest
row, and the process's peak working set was 612 MB. On Windows the
memory row after rendering the photos also printed its value 3 columns
right of the others: its name is 55 characters, and the name column is
52 characters wide. The tracked row for the photos printed its value 2
columns right of the other tracked rows: its name is 54 characters, and
their name column is 52 characters wide. In the budget table the handle
and segment drag rows printed their values 1 and 2 columns right of the
other rows: their names are 41 and 42 characters, and the name column
is 40 characters wide.

Each memory row now also gives the peak so far: the peak working set
(PeakWorkingSetSize, from the same GetProcessMemoryInfo call) on
Windows, and VmHWM from /proc/self/status on Linux. On macOS the rows
give no peak. A new last row gives the memory after the SVG export and
the save → open → save at the end of the run. The name columns of the
memory rows, the tracked rows and the budget table are 56, 54 and 42
characters wide. The help says on which systems the rows give the peak.

Docs: docs/target-app-parity.md.
2026-10-16 04:04:50 +07:00
dotCooCoo 22e06db73d CLI: perf on Windows reports the cores busy and its working set, and the help lists --deep
On Windows, perf printed "load average ?": load_average runs sysctl
or reads /proc/loadavg, and Windows has neither. The warning for a
busy machine could not print, so a run over budget on a busy machine
failed, where macOS and Linux warn and exit 0. perf --deep printed no
memory rows, because rss_mb runs ps. --help did not list --deep.

On Windows, perf now reads GetSystemTimes twice, one second apart,
and its first line gives the cores busy on average over that second
where macOS and Linux give the load average. It warns above three
quarters of the cores, as on macOS and Linux: with all 32 logical
processors of a test machine kept busy, the line read "cores busy
32.0" and a run with three budgets over exited 0. The --deep memory
rows give the working set (GetProcessMemoryInfo). Both calls use
winsafe 0.0.29, which Cargo.lock already lists for the desktop app;
the CLI depends on it on Windows with the kernel and psapi features.
The help lists --deep and says that a run over budget on a busy
machine exits 0. perf's report on macOS and Linux does not change.
2026-10-13 05:02:25 +07:00
arhammusheer 5beac4f122 #1244: smaller objects: Node 352 -> 152 bytes, NodeKind 200 -> 80, and a cheaper undo estimate
Rarely set fields move into one optional boxed NodeExtras, and large NodeKind payloads (images,
live shapes, blends, envelopes) are boxed. Every object, and every undo step that copies it,
shrinks: a 50k-path move keeps ~24.5 MB per step instead of ~40 MB. Files stay byte-identical
(Node keeps its flat JSON through a borrowed Serialize and a from-wire Deserialize, no flatten),
and loading is slightly faster. Undo and redo reuse their step's size estimate instead of
walking the document again (8.3 -> 5.6 ms for a 50k-path move).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmMmc1J8XeNLxhRihWv9cc

# Conflicts:
#	crates/engine/src/cmd/graph.rs
#	crates/svg/src/export.rs
2026-10-12 11:02:32 +07:00
arhammusheerandClaude Opus 5.5 454f1737a3 #1244: perf --deep measures four embedded photos drawn by three renderers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmMmc1J8XeNLxhRihWv9cc
2026-10-12 11:02:17 +07:00
arhammusheerandClaude Opus 5.5 7652439b40 #1172: Pathfinder Outline 19x, SVG export 1.5x and open 1.2x faster, same output
At 50,000 paths unless noted, against main:

- Pathfinder Outline (300 paths): 145 → 7.5 ms. Each segment was tested against every junction
  of the arrangement, and each chain against every chain kept so far. Now a segment tests only
  the junctions inside its control box (junctions are sorted by x; a point within reach of a
  curve is within reach of that box), with the same nearest-point test. Coincident chains are
  found through a grid on their midpoints, taking the first match in the same order a scan does.
- export SVG: 267 → 176 ms. Path data is written straight into one string (`push_num`, the
  same digits and trimming as `fmt_num`) instead of a string per number, point and segment.
- open .vectorcraft: 239 → 195 ms. The JSON was parsed once only to find where it ends:
  - A file with no NUL has no data after it.
  - Otherwise the marker (which holds the first NUL, as JSON can't) is taken when it directly
    follows the JSON's last byte, as the writer puts it.
  - Anything else takes the old parse.

`perf --deep` prints output hashes of an SVG export and a save → open → save round trip; main
and this branch print the same ones. pathfinder_pinned (Outline included) passes, and so do the
pathops, format and svg suites (345 tests).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmMmc1J8XeNLxhRihWv9cc
2026-10-11 23:26:53 +07:00
arhammusheerandClaude Opus 5.5 4b260c364d #1172: edit a large selection in time linear in the document, not quadratic
Every lookup by id walked the whole tree, and the edit path did one per selected id, so editing
a selection of n objects cost O(n²). Measured with the new `perf --deep` rows at 25,000 paths:

- Select All: 458 → 3.5 ms
- nudge the selection: 3,517 → 34 ms
- drag it with the Selection tool, per pointer move: 3,413 → 35 ms
- undo / redo: 407 → 3.1 ms

- vectorcraft_doc: Document::index_paths (every id's index path, in one walk; the first in tree
  order for a duplicated id, as index_path), node_at, node_mut_at (copy-on-write, as node_mut)
  and ancestry_at.
- apply_transform, roots_of, transform_patterns, doc_sane, Selection::prune and
  Document::paint_order look their ids up with one walk instead of one each.
- Selection::set dedups with a hash set instead of Vec::contains per id.
- vectorcraft-cli perf --deep: tracked rows for editing a whole selection (select, nudge, drag,
  undo, redo), live effects, every Pathfinder operation, typing in a long story, and memory.

tests_lookups checks the batched lookups against the one-id ones on nested trees with missing and
duplicated ids; storing the last duplicate instead of the first fails it. The doc and engine
suites (2,000 tests) pass unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmMmc1J8XeNLxhRihWv9cc
2026-10-11 23:26:53 +07:00
bflatasticandClaude Opus 5.5 aefd961bbd Fix #494: Pen + Cmd edits the path being drawn; Pen + Alt reshapes segments; handle drags fixed and fast
Reported: with the Pen there was no way to edit curves in place as Ctrl does in Illustrator on
Windows (Cmd on the Mac), segments couldn't be grabbed, and the curve-editing tools felt slow and
sometimes didn't take a handle.

- Cmd/Ctrl held at a press with any tool but a selection tool now borrows the selection tool in the
  engine (Session::pointer), not in the canvas: the selection tool chosen last, or Direct Selection
  for the Pen, Curvature and anchor tools until one is chosen. The tool lent to isn't deactivated,
  so the Pen goes on drawing its path after the release while that path stays selected; it no
  longer draws on another open path selected meanwhile. Agents reach it with pointer_gesture
  mods.cmd. With Cmd held the pointer is the borrowed tool's. Choosing a tool or switching
  documents mid-gesture gives the tool back first.
- Pen + Alt over a segment of a selected path reshapes it, as the Anchor Point tool does.
- The handles shown are the handles that drag (one helper for Direct Selection, the Anchor Point
  tool and Pen + Alt): Direct Selection now drags the handles of a path selected as a whole (as
  the Pen leaves it, so Cmd + drag of a handle while drawing works), the Anchor Point tool no
  longer grabs handles that Show handles when multiple anchors are selected hides, the nearest
  handle wins, and a handle shorter than the tolerance leaves its anchor to be picked. The anchor
  tools pick within Selection & Anchor Display > Tolerance instead of a fixed 4 px.
- path.reshapeSegment keeps a smooth anchor at either end smooth (its other handle turns with the
  moved one) instead of kinking it.
- A dragged handle gathered its smart guide targets from the whole document on every pointer move
  (21 ms per move on 20,000 paths): HandleSnap now gathers them once per drag (1 ms).
  vectorcraft-cli perf measures handle and segment drags on a 5,000-anchor path.
- catalog::is_selection_tool and catalog::edits_anchors replace the UI's own lists.
- Tests in tools, engine and MCP (headless pointer_gesture); docs/mcp.md, control-protocol.md,
  ROADMAP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:00:13 +07:00
japbcoelhoandClaude Opus 5.5 32f73e9cce CLI: a closed stdout ends vectorcraft-cli quietly
`vectorcraft-cli commands | head -1` panicked and exited with status 101:

  thread 'main' panicked at library/std/src/io/stdio.rs:1168:9:
  failed printing to stdout: Broken pipe (os error 32)

println! and print! panic when stdout is closed. Output now goes through
outln! and out!, which end the program with status 0 on a broken pipe (the
reader has what it wanted) and report any other write error on stderr with
status 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 23:09:22 -03:00
Brandon ThomasandClaude Opus 5.5 3e323bfa2a Robustness: VectorCraft never crashes — no panics in shipped code
Shipped code no longer unwraps, expects or panics: every one of the 124
unwrap()/expect()/panic!/unreachable! sites outside tests now handles the
failure (Result/Option, ?, let-else, defaults) and reports errors to the user.

- Lints: workspace clippy denies unwrap_used, expect_used, panic, todo,
  unimplemented and unreachable; clippy.toml allows them in tests, and
  integration tests, examples and testkit opt out explicitly.
- Safety net (vectorcraft_engine::guard): a panic in a top-level command rolls
  the document back and returns EngineError::Internal; a panicking tool is
  reset; the MCP server answers with an internal error and keeps serving; the
  UI frame and each control request are guarded.
- APIs that hid failures now return them: Rendered::to_png/to_jpeg/to_webp
  return Result (JPEG/WebP errors used to write empty files),
  FontDb::face returns Option, Region::top returns Option.
- Fuzzing: engine/tests/import_fuzz.rs feeds garbage, hostile and mutated SVG
  and PDF through import → render → export (30k cases locally, no panics).
- Docs: AGENTS.md/CLAUDE.md rule, docs/development.md "Robustness" section,
  README pointer and a PR template checklist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 06:28:59 +07:00
Brandon ThomasandClaude Opus 5.5 4878f1c5dd Rename DrawCraft → VectorCraft: crates (vectorcraft-*), apps, binaries, types, env vars, docs, examples (.vectorcraft); old .drawcraft files and preferences still load. Fix: text bounds exact after opening a file; selection no longer marks a document modified (dirty = content differs from the saved document). Screenshots and renders regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:30:09 -04:00