Commit Graph
204 Commits
Author SHA1 Message Date
dotCooCoo cca9a42c6c Effects: Effect › SVG Filters applies the document's SVG filters as live effects
Squashed from #6200 onto main.
2026-11-09 00:34:23 +07:00
dotCooCoo d5e1c00470 File formats: HEIC and HEIF images open and place
File › Open and File › Place read HEIC and HEIF still images (.heic,
.heif) through heic-rs 0.1.4, the pure-Rust decoder photocraft-heif
uses, and store the pixels as PNG. Single pictures and grids of tiles,
8 to 10 bits, with or without an alpha image, are read with the
container's rotation, mirror and crop applied. A grid without a color
box of its own takes its tiles'. When neither the image nor its tiles
have an nclx box, the HEVC stream's video signal type gives the
samples' matrix and range. An RGB ICC profile, or Display P3 with the
sRGB curve named by an nclx box or, without a color box, by the
stream, is converted to sRGB. The EXIF resolution tags size the image;
without them it is 72 ppi. When the container's rotations turn the
image an odd number of quarter turns, the resolution's axes swap with
the pixels'.

Image sequences, images that aren't HEVC-coded, grids of cropped tiles
and images past the pixel allowance are refused with a message before
the pixels are decoded. The allowance counts each coded picture at the
size its sequence parameter sets give it.

The decoder is in the engine's `heif` feature, which is on by default
and listed by the desktop, CLI and web apps. A build without it still
recognizes the files and refuses them with a message.
2026-11-08 23:51:41 +07:00
dotCooCoo 1c676fc372 Plug-ins: API level 2 lets a plug-in report progress, see Cancel and write log lines
A plug-in whose manifest gives "api": 2 and lists the new `progress`
permission may import three host functions from module `env`:
vc_progress(done, total), vc_cancelled() -> i32 and
vc_log(level, ptr, len) -> i32. Installing checks every import and
refuses, by name, one the host doesn't offer, one with another type and
one the manifest doesn't allow.

- Progress is clamped and passed on once per tenth of a percent (past
  2,000 reports, once per 1,000 changes). A heavy filter's progress
  window and the export's progress window show it as a percentage bar,
  and their Cancel makes vc_cancelled return 1. A live effect's progress
  is not shown.
- A run cancelled before it returns ends as cancelled, and one that
  returns after its time budget ran out ends at that budget, so what a
  plug-in returns after it sees vc_cancelled changes nothing. This also
  applies to a plug-in of API level 1 that finishes after a Cancel or
  past its time budget.
- Log text must lie in the module's memory and be UTF-8. Lines go to the
  app's log as "<id>: <text>" with hidden characters escaped, at most
  1,024 characters a line, 200 lines and 32 KiB a run, and the host reads
  at most 1 MiB of log text a run.
- plugin.list and plugin.info give the manifest's `api`; the install
  prompt names the new permission in plain words.

Docs: docs/plugins.md, docs/mcp.md, docs/target-app-parity.md.
2026-11-08 13:21:15 +07:00
dotCooCoo 874ae85fab Plug-ins: manifests list permissions, the user grants them at install, and the host enforces the grant
A manifest may list `permissions` (doc.read, doc.write, state,
effect.render, export.write) and an `api` level. doc.read, doc.write
and state follow the capability model proposed in
storytold/photocraft#662; the other names are VectorCraft's own. An
unknown, reserved, duplicate or wrong-kind name is refused by name. A
manifest without the key gets the permissions its kind implies and
installs as before.

- Install Plug-in... and File > Open show the name, id, version, file
  SHA-256 and each permission in plain words, with control and
  bidirectional characters escaped and each string cut to 64
  characters. OK installs the module only if it still has the SHA-256
  shown; Cancel installs nothing. The heavy-budget prompt and the
  status line show the escaped name too, and OK keeps the `replace` it
  was given.
- `plugin.install` needs `grant` listing every permission, and
  `plugin.inspect` reports `needsApproval`. `hash` pins the module:
  one with another SHA-256 is refused. The grant is kept with the
  module's hash in the store's `grants.json`, and only that store's
  grants count while it is attached; the same set does not ask again,
  more permissions do, and `plugin.remove` forgets it.
- The Additional Plug-ins Folder and `plugin.reload` install only
  plug-ins the attached store has granted and report the others as
  needing approval. At startup the store skips an ungranted plug-in.
- A filter run without doc.read is given no objects; without state it
  neither reads nor writes `documentData`; without doc.write an output
  that changes the document is refused and the document is unchanged.

Docs: docs/plugins.md, docs/mcp.md, docs/target-app-parity.md.
2026-11-08 13:20:32 +07:00
dotCooCoo 5a3de875fa JavaScript: identifiers follow the Unicode categories of section 7.6, and label and parameter checks are linear
is_id_start accepts the letter categories (Lu, Ll, Lt, Lm, Lo, Nl) and
is_id_part adds Mn, Mc, Nd and Pc, read with unicode-general-category.
A superscript digit (No) inside an identifier and a mark such as U+09BE
at its start are now SyntaxErrors, and identifiers with marks outside
the old list, such as the Bengali and Tamil viramas, now parse.

Labels in scope are kept in a map, and duplicate labels in a chain and
duplicate parameters of a strict function are found with a hash set, so
long chains, many labeled statements and long parameter lists are
checked in linear time.
2026-11-08 13:17:13 +07:00
dotCooCoo e592e8277b JavaScript: parser for ES5 scripts, with cargo xtask test262
New crate vectorcraft-js reads ECMAScript 5.1 source into a syntax tree
with source positions: a UTF-16 lexer (line and column tracking, automatic
semicolon insertion, the regexp and division goals), preprocessor lines
read as directive tokens, a recursive-descent parser to an arena tree, and
the ES5 early errors, strict mode included. Nesting deeper than
Options::max_depth (200 by default) gives a SyntaxError with its line and
column rather than overflowing the stack. Nothing runs scripts yet.

cargo xtask test262 downloads test262 at a pinned commit, checks its
SHA-256, unpacks it into the gitignored corpus/ folder and runs the ES5
syntax tests. A differential test in the engine compares the lexer with
scriptcheck on line counts, directives and unterminated literals.
2026-11-08 13:17:13 +07:00
dotCooCoo f416fc49ef Build: linesweeper from craft-libs' vendored copy at 481f66d
The workspace patches linesweeper to storytold/craft-libs at 481f66d, the
merge of craft-libs#633, which keeps the crates.io 0.5.0 package in
vendor/linesweeper. Fixes made to that copy reach VectorCraft when the
rev moves. Cargo.toml still asks for linesweeper 0.5.

The library is unchanged: Cargo.toml and every file under src/ in the
copy are byte-identical to the crates.io 0.5.0 package, whose sha256 is
a25df1b0c5432893f41bba817150e323703ee5f6d974f3a6808ac7fd7f046d18, the
checksum Cargo.lock carried. Cargo.lock changes only linesweeper's
source and checksum lines.

NOTICE no longer lists linesweeper among the crates fetched through
crates.io, and names the craft-libs revision it comes from.
2026-11-08 12:47:58 +07:00
dotCooCoo 81a7a68388 .ai import: legacy type in CJK fonts decodes by the font's CMap
Legacy type in a composite font such as HeiseiMin-W3-83pv-RKSJ-H was
decoded one byte to a character, so Japanese, Chinese and Korean type
opened as unrelated single-byte characters, and the font name kept the
CMap.

Tf now finds a predefined CMap at the end of the font name, unless a TZ
gives the font encoding pairs, and the font is the name without it.
Strings in that font are read by the CMap's code space, mapped to CIDs
with the predefined CMaps craft-pdf carries, then to Unicode by the
character collection's CID-to-Unicode CMap. Codes the CMap doesn't map
are dropped. A -V CMap is read with its -H form's codes. Codes in the
Unicode CMaps (UCS2, UTF8, UTF16, UTF32) are read as Unicode; those
helpers follow mockydevs' #6067. A 0x0D byte ends a paragraph and a 0x09
byte is a tab in these fonts too.

The predefined CMaps craft-pdf doesn't carry (RKSJ, 78-RKSJ, 78ms-RKSJ,
78-EUC, 78, Add, Ext, NWP, GB, CNS1, KSC, B5 and the Hong Kong B5 sets
other than HKscs-B5) are read with the nearest
carried CMap, which can give a different character for some codes.
GBT, GBT-EUC, GBTpc-EUC, CNS2 and KSC-Johab type still decodes byte by
byte, with the CMap removed from the font name.

Docs: docs/file-format-parity.md.
2026-11-08 12:47:52 +07:00
Helen Kwok f9bf685cbd vectorcraft-web: window.vectorcraft takes its request functions from craft-web-api
request, execute, commands and screenshot move onto the shared crate (storytold/craft-libs#413): a timeout on every call, the screenshot's PNG also in data, and docInspect and uiInspect beside them. A call the page gave up on gives up its request if the app has not started it, so the app skips it. openFile, captureDownloads, files, readFile and inspect stay VectorCraft's own, on the same object.
2026-11-07 09:18:00 +07:00
Kushagra Kumar 316f0d6213 SVG import: clip path on clipPath children clips child silhouettes before union (#5841)
Squashed from #5855 onto main.
2026-11-06 07:23:31 +07:00
dotCooCoo b8155698b5 Remove temporary test files after failures 2026-11-05 23:25:05 +07:00
BFlat 15670ca9f3 Merge pull request #4968 from dotCooCoo/feat/ai-editing-data 2026-11-04 11:41:38 +07:00
Don Omondi 4f60ee47e8 Fix #2920: an open path in a PDF-compatible .ai opens as drawn
Squashed from #4616 onto main.
2026-11-04 10:25:55 +07:00
dotCooCoo d2c5ea127a Merge commit 'refs/check/4968-main' into feat/ai-editing-data 2026-11-03 13:05:54 -08:00
dotCooCoo b751dce8e3 SVG import preserves selector matches while retaining metadata 2026-11-03 21:09:57 +07:00
dependabot[bot] 0fa627c6df chore(deps): bump wat from 1.262.0 to 1.263.0
Bumps [wat](https://github.com/bytecodealliance/wasm-tools) from 1.262.0 to 1.263.0.
- [Release notes](https://github.com/bytecodealliance/wasm-tools/releases)
- [Commits](https://github.com/bytecodealliance/wasm-tools/compare/v1.262.0...v1.263.0)

---
updated-dependencies:
- dependency-name: wat
  dependency-version: 1.263.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-03 20:07:58 +07:00
dotCooCoo 6228446e9e Fix #4559: shortcuts can use the ISO key next to left Shift
Squashed from #5276 onto main.
2026-11-03 16:03:38 +07:00
dotCooCoo f8095edf0d Merge commit 'refs/check/4968-main' into feat/ai-editing-data
# Conflicts:
#	crates/eps/src/lib.rs
#	crates/pdf/src/lib.rs
#	docs/file-format-parity.md
2026-11-02 18:47:39 -08:00
Brandon ThomasandClaude Opus 5.5 11862f6119 Release 0.21.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 19:27:35 -05:00
dotCooCoo b30f7ad34c Fix #3118: PDF export writes CMYK inks, gray and spot tints as the document holds them
Squashed from #5080 onto main.
2026-11-02 15:22:38 +07:00
NetterSilverandClaude Sonnet 5.5 88d8a7839e fix: validate parse_date calendar dates with chrono
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-02 14:36:06 +07:00
dotCooCoo 4e409af54a Merge main into feat/ai-editing-data 2026-11-01 21:29:58 -08:00
.CooCoo 890c27ccb2 Native format: take the .vectorcraft envelope from craft-vectorcraft-format (#5146)
* Native format: the .vectorcraft envelope comes from craft-vectorcraft-format

vectorcraft-format reads and writes the file's envelope through the
craft-vectorcraft-format crate in craft-libs: Head and Image, the version
and size constants, the format and version check, Tail for the data after
the JSON, split_tail and entry, and base64. The constants, is_compressed,
json_part, base64_encode and base64_decode are re-exported from
vectorcraft_format, so callers do not change. load_file reads the members
it uses with Image entries and skips the preview and PDF data. The
document, its migrations, the version 1 writer, gzip writing, sniff and
the limits on decompression stay here.

A preview or pdf member of the wrong shape now reads as missing on its
own, so pdf_content still returns a file's PDF when its preview is
damaged.

A new test saves a document in every version, compressed and pretty,
with a preview, profiles and a PDF, reads it back, saves it again and
compares the bytes; it also reads each uncompressed file as the crate's
File. Another reads the PDF of a file whose preview is damaged.

* Native format: sniff from craft-vectorcraft-format, pinned at #408's head

- craft-vectorcraft-format moves to craft-libs 0fd7e75fe6, the head of
  storytold/craft-libs#408.
- `sniff` is the crate's: a file is native when its top-level `format`
  member, within the first 256 bytes after decompression, is
  `vectorcraft` or `drawcraft`, read with JSON escapes decoded. The word
  elsewhere in the head no longer counts. All 192 corpus files and
  their resaved copies get the same answer as before.
- Envelope errors other than the format and the version keep the
  crate's message instead of reading as a compressed file that unpacks
  to too much.
- A test checks that a repeated `generator`, `preview` or `pdf` opens
  and any other repeated member is refused, by `load_file` and by the
  crate's `Contents` alike.

* Native format: read through the crate's Contents and Entries

Pins craft-vectorcraft-format at 420a27f18f, the current head of
craft-libs#408, and replaces VectorCraft's own copies with the crate's:

- load_file reads the JSON through Contents::read, which also refuses
  another format or a newer version. Opened is removed. Messages are
  those of main: serde names the struct File, as main's reader did.
- In-memory data after the JSON (uncompressed files, and compressed
  files unpacked whole) is read through the crate's Entries.
  entries_within keeps its streaming reader for compressed files, whose
  data is unpacked entry by entry and is never one slice in memory.
- The streaming reader finds the marker through the crate's marker_at.
- MAX_BLOB is the crate's MAX_ENTRY (1 GiB).

save_to still writes the envelope member by member: it streams the
document into the output and looks for the images it uses as it
writes, and File would need the images before the document. A new test
checks that its output, compact, pretty and compressed, for versions 1
to 4, equals serde_json::to_vec or to_vec_pretty of the crate's File
holding the same members followed by the crate's Tail, so a change to
either side fails.

* Native format: pin craft-vectorcraft-format at craft-libs main

craft-libs#408 merged as 14d5dbb50c; the crate is unchanged from
420a27f18f. A test checks that the crate's sniff takes every layout
VectorCraft has written: the version 1 and 2 files from before the
rename, compact and pretty, and each version save_with writes.
2026-11-01 23:30:54 -05:00
dotCooCoo 0823672c82 Merge main into feat/ai-editing-data 2026-11-01 15:57:03 -08:00
Brandon ThomasandClaude Opus 5.5 5628de96f8 Release 0.20.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 13:59:17 -05:00
dotCooCoo e7cb16b66d Merge main into feat/ai-editing-data 2026-10-31 20:20:39 -07:00
dotCooCoo 5fdccdf602 Place: Word 97-2003 .doc files place as area type with their formatting
File > Place reads a Word 97-2003 document (a compound file named .doc,
or one that holds a WordDocument stream) and sets its main text as area
type, as it does for .rtf and .docx files. The text comes from the
piece table, in 16-bit Unicode or compressed 8-bit pieces. Formatting
is resolved from the style sheet's default font, the paragraph style,
the character style and the direct character and paragraph properties,
and keeps the same properties the .docx reader maps.

Table cells are separated by tabs and each row is a paragraph, and a
field places as its result. Hidden and deleted text, pictures, headers,
footers, notes, comments and text boxes are left out, and the reply's
warnings name each kind of content left out or changed once. Word 6.0
and Word 95 documents, encrypted documents and documents last saved
with fast save are refused, and so are structures that lie outside
their streams.

The compound file is read with the cfb crate (MIT), which adds one
package to Cargo.lock.
2026-11-01 07:16:29 +07:00
dotCooCoo 23005d4816 Merge main into feat/ai-editing-data 2026-10-31 11:41:40 -07:00
dotCooCoo 777ad6765c File formats: JPEG 2000 images open and place
File › Open and File › Place read JP2 files and raw JPEG 2000
codestreams (.jpf, .jpx, .jp2, .j2k, .j2c, .jpc) through hayro-jpeg2000,
and store the pixels as PNG. Gray, RGB, CMYK and ICC-tagged images, with
or without alpha, are converted to RGBA. Several codestreams, JPX
composition or layers, other color spaces, inconsistent or oversize
palettes, inconsistent channel definitions, oversize tile grids and
images past the pixel allowance are refused with a message before the
pixels are decoded.

The capture and display resolution boxes size the image: the box that
comes last sets each axis, and an axis without a usable value uses
72 ppi.

IccProfile::to_srgb_into converts a pixel with the cached transform and
no heap allocation.
2026-11-01 01:22:05 +07:00
dotCooCoo 22610af58c Merge main into feat/ai-editing-data 2026-10-31 10:51:17 -07:00
dotCooCoo 034a327f5b Eyedropper: bound the work of one color sample
A sample now shares one work limit, counted in objects looked at, across
hit tests, pattern tiles and the tile or symbol art it places for hit
testing, and gives no color when the limit runs out. Objects that give
no color are skipped by id instead of cloning the document for each
retry, each nesting level reuses one scratch document that holds only
the symbols, and patterns and images are looked up in the sampled
document. The sample runs on a deep stack and follows art inside art at
most 16 levels deep.

A symbol instance is now sampled as it draws: its art is stained by the
instance's fill and placed by the instance's transform without moving
the patterns in it or scaling its strokes. Between the shapes of its art
the sample shows what lies below instead of the stain, and a pattern
tile holding an instance is sampled wherever the symbol's art reaches.
2026-11-01 00:36:32 +07:00
dotCooCoo bb6cbfb2b4 Images: SVG and EPS export write colorized grayscale and 1-bit images in their fill color
SVG export ignored an image's colorize paint and wrote the image's own
black, white and gray pixels. EPS export flattened every colorized image
into a process-color raster, so a spot color lost its plate. Both now
write the image in the fill color, with the coverage the canvas and PDF
export use (darkness times alpha), so white and transparent pixels take
no color.

- SVG writes a coverage PNG as a mask over a rectangle in the color,
  shared between repeated images while each keeps its own color. Linked
  mode writes the coverage PNG as a file, and SVG Tiny 1.2 writes a PNG
  in the color. A colorized image whose pixels are missing, or whose
  grayscale pixels can't be decoded, is left out with a warning.
- EPS writes fully covered or uncovered pixels as an imagemask in the
  color's process or Separation ink (Registration on every plate), and a
  grayscale image as ink samples scaled by coverage. Level 3 masks its
  uncovered pixels out and Level 2 writes them white; gray midtones are
  written over white, with the existing warnings.
- EPS flattening no longer counts a solid colorize paint on a grayscale
  or 1-bit image as transparency by itself. Opacity, blending modes,
  opacity masks, effects and overlapping transparent art still flatten
  it, and so does a colorize paint the writer can't draw.

The svg crate's tests turn on resvg's raster-images feature to render
the embedded PNGs.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-01 00:35:27 +07:00
Don Omondi 1679318034 Type: lam-alef highlight follow-up: optional features, all marks, exact ligature test (#394)
Highlight Alternate Glyphs no longer drops a lam-alef cluster's glyphs
outright: the ligature is clear when required shaping, or shaping with
liga left on (older fonts), gives it, so an optional feature such as
salt that changes it is highlighted again. is_lam_alef takes any
combining mark by Unicode general category (Arabic Extended-A/B/C
included) instead of a partial list. The test font maps the fatha to a
mark and skips marks in its ligature, so the mark-spanning test checks
the exact ligature glyph and cluster; it gains a salt lookup and test.

Also a one-line equivalent nonminimal_bool repair in pathops'
pathtools that strict Clippy needs on main.
2026-10-31 19:43:17 +07:00
dotCooCoo 4e785b40ce Part of #4516: Windows renders start workers only for the cores the process may use
The renderer sized its worker pool from available_parallelism, which on
Windows counts every processor in the machine, also when the process's
affinity mask allows fewer. A process allowed one core started 8 workers,
and the renderer's flush spun on that core while the workers got no time,
so renders crawled or stalled.

The desktop app and vectorcraft-cli now read the affinity mask at startup
(src/windows_cores.rs, shared) and pass the count to the renderer with
vectorcraft_render::set_usable_cores. With one usable core the renderer
renders on the calling thread.
2026-10-31 18:44:55 +07:00
aiongg 91e97c92dd updates: show check results with craft-update's CheckResultDialog 2026-10-31 04:42:43 +00:00
Brandon ThomasandClaude Opus 5.5 bcebabe3f8 Release 0.19.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 23:26:38 -04:00
dependabot[bot] 414aad10bd chore(deps): bump the cargo-patch group across 2 directories with 4 updates
Bumps the cargo-patch group with 3 updates in the / directory: [rustix](https://github.com/bytecodealliance/rustix), [clipboard-win](https://github.com/DoumanAsh/clipboard-win) and [smallvec](https://github.com/servo/rust-smallvec).
Bumps the cargo-patch group with 1 update in the /crates/plugins/example directory: [serde_json](https://github.com/serde-rs/json).


Updates `rustix` from 1.1.5 to 1.1.6
- [Release notes](https://github.com/bytecodealliance/rustix/releases)
- [Changelog](https://github.com/bytecodealliance/rustix/blob/main/CHANGES.md)
- [Commits](https://github.com/bytecodealliance/rustix/compare/v1.1.5...v1.1.6)

Updates `clipboard-win` from 5.4.1 to 5.4.3
- [Commits](https://github.com/DoumanAsh/clipboard-win/commits)

Updates `smallvec` from 1.16.3 to 1.16.4
- [Release notes](https://github.com/servo/rust-smallvec/releases)
- [Commits](https://github.com/servo/rust-smallvec/compare/v1.16.3...v1.16.4)

Updates `serde_json` from 1.0.151 to 1.0.154
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.151...v1.0.154)

Updates `serde_json` from 1.0.151 to 1.0.154
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.151...v1.0.154)

---
updated-dependencies:
- dependency-name: clipboard-win
  dependency-version: 5.4.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-patch
- dependency-name: rustix
  dependency-version: 1.1.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-patch
- dependency-name: serde_json
  dependency-version: 1.0.154
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-patch
- dependency-name: serde_json
  dependency-version: 1.0.154
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-patch
- dependency-name: smallvec
  dependency-version: 1.16.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 02:23:04 +07:00
wjrichardsandClaude Opus 5.5 dd934dfd4e Linux clipboard: copy text through the core Wayland protocol
Where arboard can't open (GNOME has no data-control protocol, and the Flatpak on a
Wayland session has no X11), Copy and Paste use the compositor's core data-device
protocol on the window's own connection through smithay-clipboard, already in the
build through egui-winit: text only, a copy's text or its SVG markup. A bitmap-only
copy reports that this clipboard carries text only and leaves it as it was.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 17:12:17 +07:00
Dylan M. Taylor 45be6f47bf UPD.1: honor app pretend versions in update checks (#4677) 2026-10-30 03:07:22 -04:00
Dylan M. Taylor a24d811c7b Update prompt shows the app icon and accent color instead of a letter (#4671)
UPDATE_APP (native builds; craft-update is a native-only dependency) carries our 128 px icon and accent (#e8573f, 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).
Clippy fixes for Rust 1.99 in untouched code (same logic):
- crates/svg/src/import/fx.rs
- crates/tools/src/text/tool.rs
2026-10-30 01:27:35 -04:00
Dylan M. Taylor 39bb862420 deps: bump tar, zip, roxmltree, rustls-webpki and symphonia to their latest versions (#4662)
- `roxmltree`: 0.20.0, 0.21.1 → 0.20.0, 0.21.2
- `zip`: 9.1.0 → 9.1.3

Lockfile only.
2026-10-29 22:32:34 -04:00
dotCooCoo f55cbf313c Fix #2249: macOS converts the canvas from sRGB to the display's profile
The window's CAMetalLayer had no color space: wgpu 30.0.1 sets it to
nil for an sRGB surface each time it configures the surface, and Core
Animation does not color-match a layer without one. On a wide-gamut
display #FF0000 showed as the display's own red, (255, 0, 0) in a
capture where Preview shows the same sRGB red as (234, 51, 35).

A paint callback that draws nothing runs after egui configures the
surface and before the frame is presented. It sets sRGB on each
window's Metal layer that has no color space and leaves any other color
space as it is. wgpu's own fix (gfx-rs/wgpu#10286) is in no release yet.

Fixes #3161.
2026-10-29 14:36:37 +07:00
NetterSilverandClaude Opus 5.5 4c4113096c Seed PDF encryption keys from the OS random source
Fixes #3605

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 08:44:36 +07:00
dotCooCoo f33617142c Save As .ai: carry editing data that other apps open as layers and paths
A PDF-compatible .ai now also carries, on its first page's PieceInfo, an
editing copy of the art in the form VectorCraft's own .ai reader reads:
AIPrivateData streams of 64 KiB holding the program compressed with
Zstandard after %AI24_ZStandard_Data, an AIMetaData stream of its header
comments, and the ContainerVersion, CreatorVersion, NumBlock,
RoundtripStreamType and RoundtripVersion entries.

The program holds the header comments, the color swatches and color
groups, every artboard, layers and sublayers (name, order, visibility,
lock, color, printing, dimming), groups, paths, fills and strokes (width,
caps, joins, miter limit, dashes, overprint, fill rule), process, spot and
global colors, guides, hidden and locked objects and object names. Type,
gradients, patterns, images, symbols, live objects, brushes, clipping
groups and compound paths are written as plain filled and stroked paths,
each with a warning; transparency and effects other than geometry effects
are left out.

The .ai option editingData (on by default, Include Editing Data for Other
Apps in Save As) turns it off. The legacy version 7 writer is unchanged;
the path, guide and line style operators both writers write move to
eps/src/ai_ops.rs. LoadOptions::native off opens a file from its editing
copy or pages instead of the VectorCraft document it carries.
2026-10-28 16:55:41 -07:00
Dylan M. Taylor 50bb4acf4e Update Cargo.lock: eframe no longer needs glow without the windows7 feature 2026-10-28 18:35:07 -04:00
Brandon ThomasandClaude Opus 5.5 68b68a30ac Release 0.18.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 12:09:18 -04:00
aiongg 13d837363d Dropdowns: menus grow back after a search with no matches (#4337)
* chore(deps): pin craft-libs to 62bb0e1d (shared combo box)

* fix(ui-egui): dropdown menus grow back after their contents shrink

Every combo box is craft_ui::ComboBox, whose menu follows its contents'
height while it is open (egui#5225); clippy.toml bans egui::ComboBox.

The font family menu is a craft_ui::combo::Menu under its field, with the
search field and filters in its header and the family rows scrolling under
them, still laid out only in view. The menu keeps to the room on its side
of the field, so the list's minimum height and the popup's room are gone.

* fix(ui-egui): the shortcut import's target picker grows back after a search

Its search field, "No equivalent" and the row's choices are the header of
a craft_ui::combo::Menu, and Keyboard Shortcuts' entries scroll under
them. That was the last menu on the popup path that capped itself at the
room left, so that path and combo's `searchable` flag are gone.

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

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

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

* chore(xtask): pdf-deps expects craft-libs 92d392cf
2026-10-27 10:36:58 -04:00
dotCooCoo 6ed4608d82 Images: an image past the 2 GiB decode allowance draws at reduced size instead of a crossed box
A PNG, JPEG, GIF or WebP image that takes more than the decode
allowance as RGBA (2 GiB, 512 MiB in the web app), such as a 24000 ×
24000 JPEG, was placed with the warning "too large to draw" and then
drew as a crossed gray box on the canvas and was left out of raster
exports. A 16-bit PNG whose decoded samples were past the allowance
was shown the same way, with the same warning, although its RGBA fits.

The renderer now decodes such an image at the largest size, keeping its
proportions, that fits the allowance as RGBA. Each pixel of the result
is the mean of the image's pixels it covers. A PNG that isn't
interlaced is read a row at a time; other images are decoded whole when
their decoded samples fit the allowance. A 16-bit PNG that isn't
interlaced and whose RGBA fits is read a row at a time at its own size.
An image of more pixels than the allowance has bytes still draws as the
crossed box, and so does one whose decoded samples are past the
allowance, other than a PNG that isn't interlaced. The renderer
decodes an image at reduced size only while the result and the memory
reading it takes (the image decoded whole, or the PNG reader's row
buffers) fit beside the images being painted; otherwise the image is
not decoded on that render (on the canvas it draws as the crossed box)
and is tried again on the next render. The document keeps the whole
file for saving and export. A placed document's saved preview past the
allowance is decoded the same way, at the size it is drawn at or at the
reduced size when that is smaller.

Open, Place and the Links panel say "an image of W × H pixels is shown
at reduced size" for an image drawn smaller. They say nothing for an
image drawn at its own size, and they say "too large to draw" for one
that is not drawn at reduced size.

Docs: docs/mcp.md, docs/target-app-parity.md (the crossed box's line
now names an image over the allowance that can't be drawn at reduced
size).
2026-10-26 21:41:42 +07:00
dotCooCoo 16205440e2 Render: the decoded-image cache sizes from installed memory on Windows, macOS and FreeBSD
The renderers share decoded images within a budget of an eighth of the
installed memory, at most 1 GiB. The installed memory was read only on
Linux, so on Windows, macOS and FreeBSD the budget was always 1 GiB,
also on machines with less than 8 GiB.

The installed memory is now read with GlobalMemoryStatusEx on Windows
(through winsafe, which vectorcraft-cli already uses), sysctl
hw.memsize on macOS and sysctl hw.physmem on FreeBSD. The budget rule
is unchanged: an eighth of the installed memory, at most 1 GiB, 1 GiB
where the memory can't be read, and 256 MiB in the web build.
2026-10-26 20:18:33 +07:00
skylar taylor-barrick 4ddd0332cf chore(deps): refresh muda, webbrowser and weezl (#4189)
* chore(deps): bump muda to 0.21.2 and webbrowser to 1.2.5

* chore(deps): bump weezl to 0.2 in render and pdf
2026-10-25 19:31:19 -04:00