Commit Graph
10704 Commits
Author SHA1 Message Date
Drew Dibble 62a01689f0 3D effects: resolve a profile only when its contours meet
The boolean that merges crossing contours cost about as much as the rest
of a plain extrusion, and it ran on every profile. Most art has no
contours that meet, so the profile is now sampled first (its point limit
checked there, before any boolean) and resolved only when two edges
cross, touch, run along each other or fold back, in one contour or
across two. Edges are swept by their left ends, within a pair budget past
which the boolean decides. Touching counts: squares sharing an edge
inflate as one puff, not two with a seam.

On a release build, Extrude of 120 separate contours takes 0.31 ms
instead of 4.18 ms; profiles that do cross cost what they did.

Tests: the meeting cases, squares sharing an edge, the fallback for a
profile the boolean refuses, Inflate's extent and area for the crossing
k, and the coincident circles' area. The hairline test now holds a
crossing pair so that its profile still goes through the boolean.

Docs: the merge is described once for all three effects, linking to a new
Limits section in the pathops README instead of repeating its limits,
and the point limit notes that a merged outline can have more points.
2026-11-09 02:24:47 +07:00
Drew Dibble e1108210f8 3D effects: keep narrow contours when resolving overlaps
try_normalize refits its result at Pathfinder's default precision and
drops every contour thinner than 0.01 pt on average as a sliver. A
hairline beside a larger shape is art, not debris, and resolved() only
fell back to the input when nothing at all was left, so the hairline lost
its cap, walls and surface.

Resolve with try_unite_all_with at a precision far below any art, which
still drops the debris a boolean leaves where curves coincide: a circle
drawn twice, exactly or 1e-7 pt off, stays one outline.
2026-11-09 02:24:47 +07:00
Drew Dibble 5bd046ef64 Docs: 3D solids merge crossing contours before building 2026-11-09 02:24:47 +07:00
Drew Dibble 9bd8b69613 3D effects: resolve overlapping contours before building a solid
Extrude & Bevel, Inflate and Rotate broke on shapes whose contours cross.
Type often draws a glyph that way: Roboto Slab's "k" has its arm and leg
as one contour that crosses both the stem and the leg's foot serif.

rings() decided each contour's role (outline, hole, or no edge at all
under nonzero) from the contour's first point only. A contour that starts
inside another was dropped or flipped into a hole, cutting part of the
glyph away. A contour that starts outside was kept whole, so its walls and
bevels ran through the overlap and the overlapping parts drew as separate
boxes.

The profile now goes through vectorcraft_pathops::try_normalize first, so
rings() only ever sees simple contours that do not cross. A path the
boolean refuses is used as it was.
2026-11-09 02:24:47 +07:00
BFlat 90d5692d80 Merge pull request #6225 from slafebre/fix/6135-ai-open-without-import-dialog
Fix #6135: opening a .ai with editing data skips the Import PDF dialog
2026-11-09 03:21:34 +08:00
BFlat f2768165e6 Merge pull request #6214 from el-j/fix/ai-import-registration-style-6205
Fix #6205: support Registration and named swatches in AI art styles
2026-11-09 03:21:24 +08:00
BFlat 93944d19e9 Merge pull request #6213 from Silvertree2010/fix/h11183-pattern-library-import-treats-a
Don't treat a pattern named "Name +N" as a numbered copy on import
2026-11-09 03:21:08 +08:00
BFlat 875b04b5d8 Merge pull request #6197 from Silvertree2010/fix/h10042-windows-gpu-pci-id-parser-accept
Reject a plus sign in Windows GPU PCI id hex digits
2026-11-09 03:20:54 +08:00
BFlat 4283dfd68b Merge pull request #6196 from KaboomFox/fix/opening-fit-settles
Canvas: a document opens at the zoom of the settled window
2026-11-09 03:20:38 +08:00
BFlat 54d3c58c01 Merge pull request #6180 from SBaksa/export-screens-dots
Export for Screens: an artboard name keeps its dots (fixes #6143)
2026-11-09 03:20:21 +08:00
BFlat 18f772ef06 Merge pull request #6175 from alosslessdev/fix/delete-after-layers-rows-5870
Layers: Delete clears the selection after the last row is deleted (#5870)
2026-11-09 03:20:04 +08:00
BFlat 600ef57710 Merge pull request #6171 from sadkotheguest/fix/char-style-options
Text styles: double-click opens Character/Paragraph Style Options, and the dialog scrolls in a fixed width
2026-11-09 03:19:46 +08:00
BFlat f3fe271fcb Merge pull request #6167 from Silvertree2010/fix/h9748-next-asset-name-counts-an-asset
Ignore non-digit suffixes in next_asset_name
2026-11-09 03:19:29 +08:00
BFlat 498ec0ebcc Merge pull request #6157 from thisisgomaa/fix/prefs-input-labels
Preferences: inputs expose their labels to accessibility tools
2026-11-09 03:19:14 +08:00
BFlat 02d7b6821b Merge pull request #6156 from thisisgomaa/fix/ui-tests-release-safe
UI tests: gradient and rotation controls are found in release builds
2026-11-09 03:18:59 +08:00
BFlat 82878b0e1d Merge pull request #6132 from slafebre/fix/6123-lent-direct-selection-copy
Pen: Copy and Cut take the anchors a Cmd-lent Direct Selection picked (#6123)
2026-11-09 03:18:43 +08:00
BFlat 914d094b10 Merge pull request #6128 from Silvertree2010/fix/h8678-svg-import-keeps-css-comments-in
Strip CSS comments from style attributes and rule declarations
2026-11-09 03:18:28 +08:00
BFlat 26f734e825 Merge pull request #6106 from slafebre/fix/5188-corner-widget-shift-click
Fix #5188: a Shift-click on a corner widget of a whole path picks its corner
2026-11-09 03:18:13 +08:00
BFlat 95cf8a7dd8 Merge pull request #6105 from mockydevs/fix/alt-codes-numlock-off
Alt + keypad codes: with NumLock off the keypad keys count as digits and don't move the caret
2026-11-09 03:17:58 +08:00
BFlat 027db8cb20 Merge pull request #6096 from Silvertree2010/fix/h7987-psd-export-writes-a-lone-utf-16
Cut PSD Unicode layer names on a character boundary
2026-11-09 03:17:43 +08:00
BFlat 52605ed8ab Merge pull request #6093 from slafebre/fix/5960-gradient-along-crossings
Fix #5960: a narrow part of a variable-width stroke paints over a wide part only where they overlap
2026-11-09 03:17:26 +08:00
BFlat 2693ef631e Merge pull request #6062 from devius/ka-update-2026-11-07
i18n(ka): Georgian for 1 new string
2026-11-09 03:17:10 +08:00
BFlat 9d4a203da1 Merge pull request #6021 from magyarb/fix/ai-mask-art-inherits-paint
.ai import: opacity mask art without its own paint takes the paint before its dictionary
2026-11-09 03:16:55 +08:00
dotCooCoo aee3a76e2a Merge main into fix/fitted-gradient-turns-with-transform 2026-11-08 11:16:42 -08:00
BFlat b0092f8ad9 Merge pull request #6010 from mockydevs/feat/ai-import-stroke-offset
EPS/.ai editing data: art-style strokes aligned inside or outside keep their alignment
2026-11-09 03:16:39 +08:00
dotCooCoo b4ba0a9474 Merge main into fix/plugin-grant-module-hash 2026-11-08 11:16:26 -08:00
BFlat 93c26e8bc0 Merge pull request #5578 from ruchirguitar/feat/5529-select-font-scope
Select Text by Font: limit it to artboards or layers (#5529)
2026-11-09 03:16:23 +08:00
BFlat ebfc6588a9 Merge pull request #5491 from storytold/fix/sec-affinity-shared-budget
Affinity import: one shared work and name budget across repeated links
2026-11-09 03:16:05 +08:00
BFlat c7593a25a7 Merge pull request #5459 from VladimirCeric/typed-curly-quotes
Typographer's Quotes: a lone typed ” opens like a straight quote
2026-11-09 03:15:50 +08:00
BFlat f85cea2363 Merge pull request #5423 from Silvertree2010/fix/5363-eps-import-of-a-large-valid-file
Scale the EPS operation budget with the program's length
2026-11-09 03:15:35 +08:00
dotCooCoo 2f72a998b9 Plug-ins: a painted fill or stroke effect keeps a placed gradient on its moved shape
A Transform or Free Distort before a painted effect on a fill or stroke moved the shape but left the placed gradient at its old position, both on pieces that keep the fill or stroke and on the geometry drawn after a refused result. The inherited fill or stroke now takes the same gradient move as the Transform branch.
2026-11-08 11:05:27 -08:00
dotCooCoo cb3108a9b4 Plug-ins: a failed store write keeps the stored grant
A module that lists no permissions now removes the grant from grants.json only after it has replaced the stored module. When the write fails, the old module stays stored and keeps the grant it needs at the next launch.
2026-11-08 11:05:25 -08:00
dotCooCoo a4a516e82f Plug-ins: only a person can approve a plug-in's permissions and heavy budget
`plugin.install` accepted `grant` and `allowHeavy` from any client, so
the program installing a plug-in could answer its own permission
prompt.

The desktop app now shows an approval dialog that names the module,
its hash and what it asks for. Confirming it gives the engine a
one-use approval tied to that module id, hash and permission list. A
control or MCP request and a frame of synthetic input cannot confirm
the dialog, and Enter does not confirm it.

Without such an approval, `grant` and `allowHeavy` are refused. The
new `--allow-plugin-grants` flag on `mcp` and `run` lets a person who
starts a headless session accept them from requests. The flag cannot
be combined with `--connect`. A module already approved in the desktop
app still installs through its stored grant.
2026-11-08 11:03:38 -08:00
Sven Lafebre b34de477a8 #6135: test opening a .ai from readable editing data
The direct-open test only had the editing data keys, so it opened from
the PDF part. It now has two artboards and a layer that exist only in
readable editing data. The keys-only case stays as its own test.
2026-11-09 02:03:10 +07:00
Sven Lafebre 057978f61a Fix #6135: the importPdf row states when the dialog opens 2026-11-09 02:03:10 +07:00
Sven Lafebre 1e7e1d33e9 Fix #6135: opening a .ai with editing data skips the Import PDF dialog
The editing data of a .ai or .ait holds every artboard, so a multi-page
file has no pages to pick. Opening one now goes straight to it, as a
file carrying its VectorCraft document already does. The dialog stays
for PDFs, a .ai that is only a PDF, a password, and Place.
2026-11-09 02:03:10 +07:00
el-j d499fb25da Fix #6205: support Registration and named swatches in AI art styles
Resolve missing Registration (Xs, XS, Xz, XZ) and named color (Xx, XX)
operators in AI ArtStyle filter definitions so objects styled with
Registration swatches correctly resolve their paint instead of falling
back to the previously set graphics state color.

Also exclude Registration paints from color-model inference when read via
Xx/XX, and correctly extract tint (penultimate operand) for name-free Xz.
2026-11-09 02:03:09 +07:00
NetterSilverandClaude Haiku 5.5 b1fd06090c Don't treat a pattern named "Name +N" as a numbered copy on import
Found while testing crates/engine/src/cmd/patternlib.rs:99 (existing_pattern)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 02:03:09 +07:00
NetterSilverandClaude Haiku 5.5 02c555cad7 Reject a plus sign in Windows GPU PCI id hex digits
Found while testing apps/vectorcraft/src/gpu.rs:313 (pci_ids)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 02:03:09 +07:00
bflatastic ca7605fe20 Canvas: record the opening fit without a branch in the canvas frame 2026-11-09 02:03:09 +07:00
bflatastic 2d945f92a1 Canvas: the opening fit's conditions in a helper, keeping the canvas frame under the complexity limit 2026-11-09 02:03:09 +07:00
SBaksa d329dd05e1 Export for Screens: a leading dot in an artboard name is a dash
A name like .draft would otherwise write a hidden file on Unix-like systems.
2026-11-09 02:03:08 +07:00
Francisco Herrera a3ed60650e Layers: row keys follow the click and release after the last row (#5870) 2026-11-09 02:03:08 +07:00
Louise Fox 1b85e7fee2 Canvas: a document opens at the zoom of the settled window
A document without a saved view was fitted once, to the canvas of the first frame that shows it, and the window's layout can still move the canvas's edges over the next frames. Two starts of the app then opened one file at zooms a hair apart (4.9812 and 4.9883 in a CI probe of v0.21.0, the canvas and the view's centre alike).

The opening fit now follows the canvas until the view is moved or the user presses a button or a key, types, scrolls or zooms (CanvasCache::opening_fit). After that a canvas that changes keeps the view, as before.
2026-11-09 02:03:08 +07:00
SBaksa 0b3ea7c1d2 Export for Screens: an artboard name keeps its dots (#6143)
The artboard and asset file stems came from unique_file_names, which turned
every character except letters, digits, - and _ into a dash, so the dot of
example.artboard became a dash. Export for Screens now uses a variant that keeps
dots (a name of only dots takes the Artboard-N fallback); safe_file_name still
drops trailing periods and guards device names.
Test: an_artboard_name_keeps_its_dots.
2026-11-09 02:03:08 +07:00
Louise Fox f794f4665c Canvas: a test of the opening fit when the first frame's canvas is narrower than the settled one
A document without a saved view is fitted to the canvas of the first frame that shows it. The test lays that frame's canvas out 2 points narrower than the next, and expects the zoom the settled canvas gives. It fails until the opening fit follows the canvas.
2026-11-09 02:03:08 +07:00
Alex d8d3f43642 Text styles: double-click opens Character Style Options in a scrolling window
Two problems in the Character Styles panel and its options dialog.

Opening the options took the panel menu's item, which is easy to miss; a double-click on a style's
row did nothing beyond applying the style a second time. A double-click on a user character style's
row now opens its Character Style Options (`ui.charStyleOptions`), as the menu item does. A single
click still applies the style, the second click of the double-click does not apply it again (the
Normal style's included), and with Shift, Cmd/Ctrl or Alt held the double-click only follows the
click rules. The built-in Normal style opens no dialog. Double-clicking a paragraph style's row similarly opens
its options, which are the rename dialog the Paragraph Styles panel menu opens (`ui.paramDialog`
running `paraStyle.rename`): paragraph styles have no full options window yet.

The dialog then grew with its content: the General section lists every attribute of the style, so
the window covered the screen and its buttons could sit below it. The content now scrolls both ways
inside a window at most 420 points tall (less on a short screen) and never wider than the points
left beside the section list, with one scroll offset per section. The dialog keeps a fixed width of
720 points, enough for the widest section's controls, in every section. The content's height is the
space the footer leaves, measured from the footer drawn last frame, so a large interface scale's
taller footer is reserved too and OK and Cancel stay visible. The section list stays outside the
scroll area, so scrolling a long section never moves the navigation; the section is read after the
list handles a click, so a newly picked section gets its own scroll offset in the same frame.

Tests:
`panels::text_styles::tests::double_clicking_a_character_style_row_opens_its_options` selects text,
double-clicks a character style's row and checks the dialog opens with the style's name, one apply
ran from the first click and the second click applied nothing; a paragraph style's row opens none.
`double_clicking_the_normal_style_opens_no_options` checks the Normal style opens none and is
applied once.
`dialogs::char_style_options::tests::the_sections_scroll_in_a_window_shorter_than_the_general_list`
draws the dialog on a 900 by 560 point screen and checks OK and Cancel stay on it, the dialog keeps
its 720-point width in two sections, and the General list is taller than its scroll area.
`every_section_and_the_buttons_fit_the_smallest_window` checks the app's minimum 800 by 500 point
window in all five sections: every section label, OK and Cancel are on the screen.
`a_scrolled_section_keeps_its_offset_after_switching_away_and_back` scrolls General, clicks Basic
Character Formats and then General again, and checks General''s content returns to the same place:
the section is read after the list handles the click, so no other section stores its offset.
`every_section_and_the_buttons_fit_the_smallest_window_at_twice_the_scale` checks the same in the
400 by 250 points the minimum window holds at 2x UI scale, before and after a wheel scrolls the
content.
Docs: type-parity (a panel row and the revision history), ui-parity (a panel row and the revision
history), target-app-parity (two evidence bullets and the revision history) and roadmap (the
revision history), with the status lines updated.
2026-11-09 02:03:07 +07:00
bflatastic 8199f30c74 Export for Screens: the asset-name test sets main's 3D format field 2026-11-09 02:03:07 +07:00
Francisco Herrera 5b98ed3b7d Layers: Delete clears the selection after the last row is deleted (#5870) 2026-11-09 02:03:07 +07:00
Ahmed Gomaa 23c5f6ff7c Preferences: colour wells and sliders take their row's label
`label_inputs_by` skips controls that already have a name, so colour wells stayed "Color" and sliders kept their internal text. Name them from the visible row label, on both halves of a slider across focus changes, and keep action button names.
2026-11-09 02:03:06 +07:00