Commit Graph
4997 Commits
Author SHA1 Message Date
dotCooCoo 6da6b4440f Shape Builder: a split stroke keeps its path's direction and gradients
With In Merge Mode, Clicking Stroke Splits the Path on, the run a click
split off could run against its path. A click on a rectangle's top side
gave a side from (100, 0) to (0, 0) on a rectangle that runs from
(0, 0) to (100, 0) there, so arrowheads and other settings that follow
a stroke's direction moved to the other end. The run now goes the way
the path goes.

The two paths of a split, and what an Alt-click or a merge leaves of a
path, had a gradient fitted to the path's bounds fitted again to their
own bounds. They now keep it where it was on the path.

A run that left 0.05 pt or less of a path still split it, into the
run and a speck. Such a path now does not split, and the tool shows
no scissors over it.

Docs: docs/mcp.md (Shape Builder Tool Options).
2026-10-23 04:14:38 +07:00
Taha-Mahmoodi 087e38aafc Render: a dashed brush stroke keeps the layered path for object opacity
Follow-up to #3697 (CodeRabbit): a brush stroke is drawn as separate
pieces (draw_brush), so folding object opacity into its colour isn't exact.
A non-dashed brush stroke already counted as one paint before #3697; to
keep that change's scope, a dashed stroke counts as one paint only when it
has no brush. dashed_stroke_opacity_folds_into_its_colour checks that a
dashed brush stroke isn't folded; it fails without the change.
2026-10-23 04:14:37 +07:00
BFlat 0395729b31 Merge pull request #3706 from devius/ka-colorimetric
i18n(ka): Relative Colorimetric is ფარდობითი კოლორიმეტრიული
2026-10-23 04:03:54 +07:00
1e45fe99f7 i18n(ka): Relative Colorimetric is ფარდობითი კოლორიმეტრიული
The adjective from კოლორიმეტრია is კოლორიმეტრიული; ფარდობითი matches PhotoCraft
(storytold/photocraft#5845, from a review by supersfera-online).

Co-authored-by: supersfera <223132313+supersfera-online@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-23 04:01:58 +07:00
BFlat 8b03e81634 Merge pull request #3703 from Taha-Mahmoodi/docs/effect-gallery-shipped
Docs: the Effect Gallery is shipped; thumbnails and an in-dialog preview remain (G2 1–2 h)
2026-10-23 03:48:43 +07:00
Taha-Mahmoodi 9a28b1f049 Docs: the 2026-10-05 depth row points to the current raster effects score
Review follow-up (CodeRabbit): the kept 2026-10-05 assessment still read
"raster effects (~20%)" with no pointer, unlike the other rows of that
table this PR annotated. It now notes the 57/57 filters and the Effect
Gallery (#2883) and points to the area table, leaving the historical
number as it was.
2026-10-23 03:48:07 +07:00
Taha-Mahmoodi edca5e9eb0 Docs: the Effect Gallery is shipped; thumbnails and an in-dialog preview area remain (G2 1–2 h, raster effects 85%) 2026-10-23 03:48:07 +07:00
BFlat b3177919a9 Merge pull request #3702 from Taha-Mahmoodi/fix/3233-dimension-cmyk
Fix #3233: in a CMYK document dimension lines and labels are CMYK, black on the K plate only
2026-10-23 03:47:44 +07:00
BFlat 50be70a4fe Merge pull request #3704 from storytold/land/3698
Regression tests for ten fixes merged without one
2026-10-23 03:38:06 +07:00
Taha-Mahmoodi 866bf61116 Fix #3233: in a CMYK document dimension lines and labels are CMYK, black on the K plate only 2026-10-23 03:35:07 +07:00
skylar taylor-barrick 2e142583b3 #2278: test that the Properties flip buttons keep the reference app's names
The Properties panel's flip buttons had been renamed Flip Horizontal and Flip Vertical, which are the Transform panel menu's names. The test lays out the Transform section for a selected rectangle and reads the screen-reader names of its buttons: Flip Along Horizontal Axis and Flip Along Vertical Axis must be there and the old names must not. With the old labels the first is missing.
2026-10-23 03:33:30 +07:00
skylar taylor-barrick 8abb1d1c71 #1807: test that a zstd block larger than its frame's window is refused
ruzstd 0.9.0 let a block decompress to more than its frame's window allows (RUSTSEC-2026-0344). The test swaps a hand-built Zstandard frame with a 1 KiB window and one 100,000-byte RLE block into an Affinity container entry and checks that reading the entry fails. With ruzstd pinned back to 0.9.0 the block is decompressed and the read succeeds.
2026-10-23 03:33:30 +07:00
skylar taylor-barrick 02f5bc8666 #3533: test that a popover stays open through its anchor click and a panel drag
The Fill/Stroke chip's popover closed on the click that finished a drag between its proxies. The test opens a popover and checks the three outcomes: a click elsewhere closes it, a click on its own anchor that does not toggle it keeps it open, and a click elsewhere during a panel drag keeps it open. Without the fix both of the last two close it.
2026-10-23 03:33:30 +07:00
skylar taylor-barrick ae6cf1a729 #2083: test that panel menu rows light up under the pointer
The rows of the panels' menus did not light up under the pointer as the menu bar's items do, because they were drawn with no frame at all. The test hovers a menu_item_name row and checks that a filled rectangle appears behind it, and that none is there at rest. With frame(false) the hovered row stays unfilled.
2026-10-23 03:33:30 +07:00
skylar taylor-barrick a65cae367f #1506: test that sliders show their rail filled up to the handle
The theme's widget fill matched the dialog background, so a slider in a dialog showed only its handle. The test draws widgets::slider in all four brightnesses and checks that a rail in the buttons' border colour and an accent fill from the rail's start up to the handle are painted. With a plain egui slider neither is drawn, only two rectangles in the panel's button fill.
2026-10-23 03:33:30 +07:00
skylar taylor-barrick f39c9544eb #1490: test that selected text in value fields is in the strong text colour
Selected text in a value field was drawn in the accent on a selection tinted with the same accent, hard to read in every brightness. The tests focus a panel number field and a dialog text field, select their text and read the colours of the glyphs drawn, in all four brightnesses: all must be the strong text colour. Without strong_selected_text the glyphs come out in the accent.
2026-10-23 03:33:30 +07:00
skylar taylor-barrick b5a99542eb #916: test that a spinner field reads as one box in three parts
A number field with a spinner and preset chevron drew as three separate boxes, each joint with two borders. The test lays out a spin field and checks that its three boxes are the row's height and that neighbours overlap by one point so they share a border. Without the overlap (item spacing 0) the boxes touch edge to edge instead.
2026-10-23 03:33:30 +07:00
skylar taylor-barrick 888af39881 #815: test that the toolbar's scroll bar stays a slim line
The toolbar's and the dock icon column's scroll bar widened to 10 points while the pointer was on it, covering the group labels and the tool icons' right edge. The test lays out a scrolling strip, rests the pointer on its bar and measures the widest filled rectangle drawn: at most 4 points. Without the slim scroll style it measures 10 points.
2026-10-23 03:33:30 +07:00
skylar taylor-barrick 3f74e38fea #871: test that the Artboard tool names its active artboard once
With the Artboard tool the active artboard's name was drawn twice: by the canvas, as for every artboard, and by the tool, a little higher. The test draws a frame with two artboards and checks that each name is painted once and that the active artboard's sits where the other's does. It fails without the canvas skipping the tool-labelled artboard (two labels for the active one) and without the tool's label position (8 points to the right and 11 higher than the other name).
2026-10-23 03:33:30 +07:00
skylar taylor-barrick 548fad4b59 #480: test that the Add and Delete Anchor Point tool icons are the Pen's icon with a badge
The Add and Delete Anchor Point tools' icons were drawn apart from the Pen tool's, so the three didn't read as one family. The test checks that both tools' icons contain every shape of the Pen tool's icon plus a badge of their own, and that plus and minus differ. With the tool mapping pointing back at the old dc-pen-add and dc-pen-delete icons it fails: the Add tool's icon lacks the Pen's outline.
2026-10-23 03:33:30 +07:00
BFlat 625cba6f0a Merge pull request #3697 from Taha-Mahmoodi/fix/2788-dashed-opacity
Fix #2788: a dashed stroke with object opacity folds the opacity into its colour instead of compositing a canvas-wide layer
2026-10-23 03:24:06 +07:00
BFlat 4e8064cfea Merge pull request #3696 from switch87/fix/3692-paint-compound-member
Paint: a fill or stroke given to a compound path's member paints the compound path (#3692)
2026-10-23 03:23:48 +07:00
BFlat 1be08ffbd4 Merge pull request #2537 from vangel76/feat/2530-tangent-perpendicular-snap
Snapping: tangent and perpendicular snapping, View > Snapping (#2530)
2026-10-23 03:23:30 +07:00
Taha-Mahmoodi 32072c77fa Fix #2788: a dashed stroke with object opacity folds the opacity into its colour instead of compositing a canvas-wide layer 2026-10-23 03:22:55 +07:00
Gert Pellin 368fa3def2 Paint: a fill or stroke given to a compound path's member paints the compound path (#3692) 2026-10-23 03:22:54 +07:00
bflatastic 8bf7ffe39d Snapping: tangent and perpendicular snapping, View > Snapping (#2530)
Squashed from #2537 onto main.
2026-10-23 03:22:54 +07:00
BFlat ffeb37f481 Merge pull request #3695 from odgrim/fix/3578-effect-anchor-budget
Effects: stacked geometry effects keep to an anchor and line budget (#3578)
2026-10-23 03:09:07 +07:00
Brian TorresandClaude Opus 5.5 54c8a7f2bd Effects: stacked geometry effects keep to an anchor and line budget (#3578)
A chain of geometry effects makes at most 2 million anchors of a path (of a
type, symbol or image art's paths together) and about 8 million lines for the
renderer to draw at 1 px/pt, or what the path costs when that is more.
Roughen spaces its points further apart, Zig Zag makes fewer ridges a
segment, Free Distort, Twist and the warps split segments into longer pieces
(map_nonlinear_within) and Transform makes fewer copies where they would pass
it; a result drawn with too many lines is built again with fewer points, and
an effect that still doesn't fit is left out.

Three Roughens of a rectangle in a 25 KB file needed gigabytes of geometry,
and 2 million anchors of them flattened into 141 million lines (7 GB, past
vello's 2^27 lines a path); the file now opens in 0.4 s at about 450 MB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 02:44:52 +07:00
BFlat 1673b3b747 Merge pull request #2942 from nrq6993-a11y/stream/vc-feat3-g12-real-time-drawing
Honor Preferences › Performance › Real-time Drawing and Editing during tool drags
2026-10-23 02:44:15 +07:00
Brandon ThomasandClaude Opus 5.5 705eda5dc8 Release 0.15.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.15.0
2026-10-22 15:37:10 -04:00
BFlat 74c9226c3f Merge pull request #3690 from Silvertree2010/fix/h1206-unit-parse-rejects-an-explicit-l
Accept an explicit leading plus sign in Unit::parse
2026-10-23 02:31:38 +07:00
bflatastic c1f96cc6f8 Honor Preferences › Performance › Real-time Drawing and Editing during tool drags
Squashed from #2942 onto main.
2026-10-23 02:22:09 +07:00
BFlat 4e27fccdda Merge pull request #3659 from nrq6993-a11y/stream/vc-issue-3632-linux-stylus-buttons
Map pen barrel buttons before egui-winit drops them
2026-10-23 02:20:04 +07:00
BFlat f44dd739b2 Merge pull request #3688 from addibble/perf/svg-import-css-data-urls
SVG import: leave out a data URL inside url(…) too, and jump over each one
2026-10-23 02:18:29 +07:00
NetterSilverandClaude Haiku 5.5 56d34d4ced Accept an explicit leading plus sign in Unit::parse
Found while testing crates/doc/src/lib.rs:379 (number_unit, reached from parse_measure at :374)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 02:10:57 +07:00
BFlat 4b7969a05c Merge pull request #3685 from ham9zah/fix/ai-closed-path-type-range
Import: type on a closed path keeps its wrapped end bracket
2026-10-23 02:07:07 +07:00
BFlat f7dde42990 Merge pull request #3634 from makesomethingshit/fix/3607-save-for-web-active-artboard
Save for Web: initialize from the active artboard
2026-10-23 02:06:49 +07:00
BFlat ecbcf92f3a Merge pull request #3665 from dotCooCoo/feat/shape-builder-stroke-split
Shape Builder: in merge mode, a click on a stroke splits the path there
2026-10-23 02:06:32 +07:00
BFlat 44e1c3af62 Merge pull request #3684 from Taha-Mahmoodi/feat/gradient-perceptual
Gradients: Method › Perceptual mixes a gradient's colours in OKLab; SVG and PDF write it as an image
2026-10-23 02:06:14 +07:00
Cursor Agentandnrq6993-a11y 06790288e1 Linux stylus: drop generic Other(10|11) right-click remap
Review: X11 codes 10/11 are not stylus-specific; Wacom X11 already maps barrel to middle/right.

Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
2026-10-23 02:04:47 +07:00
Cursor Agentandnrq6993-a11y b37522bbb3 fix(linux): map pen barrel buttons before egui-winit drops them
## Summary

On Linux (Wayland and X11), tablet stylus side buttons arrive as winit
MouseButton::Other (BTN_STYLUS and neighbours). egui-winit ignores those
codes, so barrel presses never reached the canvas. Run the desktop app
through a thin winit ApplicationHandler wrapper on Linux that remaps
barrel buttons to right and middle mouse buttons before eframe handles
the event.

## Tests

cargo test -p vectorcraft linux_stylus
cargo clippy -p vectorcraft --all-targets -- -D warnings

Closes storytold/vectorcraft#3632

Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
2026-10-23 02:04:47 +07:00
Drew DibbleandClaude Opus 5.5 2d85603bcc SVG import: leave out the data URL of a url(…) in any attribute value too
A follow-up to #3677. A data URL in CSS, `style="fill:url(data:…)"` or a paint's
`url(…)`, stayed in the text the prepass searches, since only a value beginning with `data:`
was left out (CodeRabbit's review of #3677). Each `url(` in an element's quoted value whose URL
begins with `data:` is now left out up to the byte that closes it, its quote or `)`, so what
follows it, `display:none` say, stays. A `url(` that does not close inside its value is kept
whole: where the end is unclear, less is left out, never more.

The scan now jumps over a data URL with one search for the byte that ends it, instead of
stepping through it a byte at a time. The markers' capacity is a saturating sum, so no range can
panic it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 02:04:46 +07:00
ham9zah 1abc5d4066 Import: type on a closed path keeps its wrapped end bracket
Round a closed path the editing data's text document writes the end of
the type's range unwrapped, past the last segment (the start plus the
run), or wrapped the other way. The importer clamped it to the path's
end, so the type kept only the stretch from its start bracket to the
path's end and overflowed. Both brackets are now wrapped onto the loop
(non-finite values fall back to the start); an open path is clamped as
before.

The object's own path that then replaces the frame is written open,
ending on its start: such a path is now closed where its ends meet, so
the span is measured round the loop.

Hand-written tests: an unwrapped, a reversed and a negative range on a
closed circle, a range inside it, an exact full turn that fits a short
line, an open path still clamped, and a path ending on its start closed.
2026-10-23 01:53:47 +07:00
makesomethingshit 6a394c3a6f #3607: follow the active artboard with every tool 2026-10-23 01:53:47 +07:00
makesomethingshit 4d9749f4c7 #3607: open Save for Web on the active artboard 2026-10-23 01:53:35 +07:00
bflatastic efe2429618 Shape Builder: in merge mode, a click on a stroke splits the path there
Squashed from #3665 onto main.
2026-10-23 01:53:34 +07:00
Taha-Mahmoodi 8924b24e94 Gradients: Expand to a gradient mesh keeps a Perceptual gradient's colour curve
Review follow-up (CodeRabbit): a linear gradient expands to a mesh with a
column at each stop, and the mesh mixes linearly between columns, so a
Perceptual blue -> yellow came out as the grey RGB mix in the middle.
`breaks` now also takes a column at each stop the Perceptual expansion adds
(Gradient::expanded), so the mesh follows the OKLab curve; Classic is
unchanged. the_mesh_option_makes_a_mesh_that_paints_the_gradient gains a
Perceptual case; without the change 1812 pixels differ from the gradient.
2026-10-23 01:53:30 +07:00
Taha-Mahmoodi 6fd1f566e1 Gradients: the Perceptual test describes the method in our own words
Review follow-up (CodeRabbit): the test's doc comment quoted a phrase from
the public help page. AGENTS.md allows feature names and a cited URL, not
copied wording; the comment now describes the behaviour itself and keeps
the URL.
2026-10-23 01:53:30 +07:00
Taha-Mahmoodi 6beb68f934 Gradients: the Gradient panel's Method menu and paint.editGradient's method choose Classic or Perceptual
Illustrator 30.0's Method choice in the Gradient panel
(https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-gradients/control-perceptual-interpolation-in-gradients.html).

- paint.editGradient takes `method: classic|perceptual` (one undo step);
  paint.setFill/setStroke take it in `gradient`, and gradient params (swatches,
  copied paints) carry it when Perceptual. Anything else is refused without
  editing. One edit can set `method` and `dither` together.
- The Gradient panel shows a Method menu (Classic, Perceptual) for linear and
  radial gradients, dimmed without a gradient; the Dither box stays under the
  stops.
- i18n: "Perceptual" where a complete catalog lacked it, and the `method`
  error in all 24 catalogs.
- Docs: G13 (estimate 14–24 h → 13–23 h, after Dither's 15–25 h → 14–24 h),
  target-app-parity (row and evidence), ui-parity, mcp.md, plugins.md.
2026-10-23 01:53:30 +07:00
Taha-Mahmoodi 2bad646821 Gradients: a Perceptual gradient mixes its colours in OKLab, and SVG and PDF write it as an image
Illustrator 30.0's gradient Method, from the public documentation
(https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-gradients/control-perceptual-interpolation-in-gradients.html):
Classic or Perceptual; perceptual interpolation matches colour transitions to
how the eye perceives differences between colours, and perceptual gradients
are rasterised when saved as PDF or SVG.

- Gradient gains `method` (Classic | Perceptual; serde default Classic,
  written only when Perceptual, so older files open as Classic and Classic
  files are written as before). The colour crate's OKLab helpers open up for it.
- Perceptual mixes neighbouring stop colours in OKLab (sample, and
  sample_keep_model for the stroke meshes, Expand and blends); opacity stays
  linear. Renderers that mix linearly get stops on the perceptual curve,
  placed by halving each half segment where a straight line strays more than
  0.4 of a colour level (a nearly straight segment needs none). Classic
  gradients expand exactly as before.
- draws_as_pixels includes Perceptual gradients, so SVG and PDF write a
  Perceptual fill or stroke as an image at the document's raster effects
  resolution, clipped to what it paints. A gradient along or across a stroke
  stays slices (PDF no longer draws it as a within-stroke image).
- With Dither: a dithered Perceptual fill is the perceptual mix with dither's
  noise (the canvas's dithered grids are keyed by method too); SVG and PDF
  write it as an image with the noise, and a dithered Perceptual stroke as an
  image without it, as the canvas draws it (GradientPaint::pixels, SVG's
  raster_paint and PDF's area are told whether they paint a fill).
2026-10-23 01:53:29 +07:00