Commit Graph
12259 Commits
Author SHA1 Message Date
dotCooCoo 283d127e2a JavaScript: characters assigned in Unicode 17 are identifier characters
vectorcraft-js and Check Script took identifier categories from
unicode-general-category 1.1.0, which carries Unicode 16.0, so letters
and marks assigned in Unicode 17.0 (U+088F at the start of a name,
U+1ACF after its first character) were refused. Both lexers now read
general categories from unicode-properties 0.1.4 (Unicode 17.0), which
Cargo.lock already holds. The version is exact because 0.1.5 adds the
Unicode-3.0 license, which is not on the license allowlist. The
Other_ID_Start, Other_ID_Continue and Pattern_Syntax lists are the same
in PropList 17.0.

The exhaustive identifier test now expects the DerivedCoreProperties
17.0 totals (145,916 ID_Start, 149,240 ID_Continue), and new tests read
U+088F and U+1ACF written out and as escapes in both lexers.
2026-11-11 13:57:54 -08:00
dotCooCoo 39683ecdd9 PDF/.ai export: fit Dynamic Text before resolving font heights
The legacy .ai writer resolved cap-height and other font height
references to em sizes before fitting, so Dynamic Text stepped and
limited the em size instead of the size the Character panel shows, and
the type could be written smaller than it is drawn. It now fits first,
and outlines come from the type's own layout.

The editing data now also warns that Dynamic Text is not written when
the type is written as outlines.
2026-11-11 13:55:47 -08:00
dotCooCoo fc767dad09 Type: Dynamic Text sets each line of area type to the frame width
Type > Dynamic Text (type.dynamicText) now also works on area type as
Block. Each line as typed (each paragraph, and each forced line break in
one) is set to the frame's width, less the inset, the column gutters and
the paragraph's indents, by changing its font size only. Lines are not
broken again. The first character's size moves in 0.1 pt steps within
0.1 to 1,296 pt, and the line's other characters keep their proportions.
The sizes are written into the characters and fitted again after every
edit to the text or the frame, in the same undo step.

- Word Gap (0 to 1,000 % of a word space, default 100) sets every
  paragraph's Desired word spacing; Line Gap (-1,296 to 1,296 pt,
  default 0) is added to each character's size for its leading. Both are
  VectorCraft's own ranges and defaults, saved in the area options only
  when they differ from the defaults.
- Area Type Options gains a Block fit (text.areaOptions fit "block",
  with wordGap and lineGap), and the Properties panel's Paragraph section
  shows a Dynamic Text checkbox with Word Gap and Line Gap rows.
- Block replaces Auto Size and Shrink Text to Fit. Off keeps the sizes.
  Threaded area type is refused, as for type on a path.
- Empty lines keep their size, a frame with no width changes nothing,
  vertical type fits each line to the frame's height, and right-to-left
  lines and list markers are fitted like other lines. A refit runs at
  most four extra layouts.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-11 13:53:20 -08:00
dotCooCoo 0bf35ac543 Docs: each progress line appears once in the shipped-features evidence list
The "Evidence: what ships today, by area" list in
docs/target-app-parity.md carried three lines twice, each copy
byte-identical: Window › Arrange › Tile, the renderer's path cache for
moved anchors, and the plug-in instruction budget at a bulk memory
instruction. One copy of each is removed. The budget line stays next to
the bulk memory fuzz entry; the other two stay at their first place.
2026-11-11 13:50:57 -08:00
dotCooCoo 8e90c833f6 Scripts: a kept engine's next run starts after the last uncaught exception is dropped
Resetting Folder.current for the next run of a named engine now drops the
last run's uncaught exception and $.error first, so memory only they held
no longer makes the reset fail with the heap limit on every later run.

A File an earlier run left open now counts as closed in a later run:
setting its encoding stores it for the next open, and reading the encoding
gives the stored one.

docs/mcp.md: engines stay on the thread that made them, and a
#targetengine line in a file $.evalFile runs makes that call throw a
SyntaxError when it runs.
2026-11-11 13:50:12 -08:00
dotCooCoo 2f4c7584ea Merge main into fix/menu-list-recent-confined 2026-11-11 13:48:28 -08:00
dotCooCoo e3093034ea Scripts: opening a macOS menu reads the Scripts folder again
With the macOS menu bar, File › Scripts was read only at startup and
when the window became active, so a script added while the window
stayed active did not appear. The native menu now reports a menu
opening (Event::Opened, when AppKit begins tracking), and
native_menu::run reads the folder again, as opening the in-window File
menu does. Menu updates wait until tracking ends, so the new listing
shows the next time the menu opens.

Docs: docs/mcp.md (File › Scripts), docs/target-app-parity.md.
2026-11-11 13:47:41 -08:00
dotCooCoo dc98b59444 PDF: the in-points option leaves PDF/A pages within the standard's size limit
longPagesInPoints now applies only to files without a standard. PDF/A-2 limits page
boundaries to 14,400 units (ISO 19005-2, 6.1.13), so a PDF/A file with a page longer than
200 in keeps its UserUnit whether the option is on or off. The Save PDF dialog disables the
checkbox for PDF/A as it does for PDF/X.

Follows #6643.
2026-11-11 13:45:46 -08:00
Sven Lafebre d9f961b0c1 Fix #5111: check spaced harakat with the in-memory Arabic font
The harakat test now runs on the in-memory Arabic test font in every
environment, as well as on an installed Arabic face, and checks that
the line is the space longer.
2026-11-11 22:38:30 +01:00
dotCooCoo be27042060 Guides: sublayer guides survive layer effects and the canvas check
- A layer with a Pathfinder effect, or a Transform effect that copies its
  art, keeps the ruler guides of its sublayers on the layer, hidden and
  locked as their sublayers were, instead of losing them with the art.
- Opening a file counts the ruler guides of sublayers removed beyond the
  canvas.
- Opening a file moves misplaced ruler guides above the art before the
  canvas check, so a guide saved before a clipping path beyond the canvas
  no longer makes other art the clipping path.

Follows #6632.
2026-11-11 13:32:57 -08:00
Sven Lafebre bd9fef69b8 Fix #5111: keep coloured harakat attached when the first letter is spaced
paint_only ignores kerning_before, as drawn_alike does: a space before
a line's first letter, set on the letter alone, no longer splits it from
its harakat coloured apart.
2026-11-11 22:24:49 +01:00
dotCooCoo 5ce2337715 Tests: malformed id cases check and restore isolation mode and the shown mask
malformed_ids_are_refused_by_commands_that_default_to_the_selection put back
the document, the selection and the undo history after a case that changed
them, but not isolation mode or the opacity mask view. If object.isolate
entered isolation mode while refusing a bad id, the test did not report it,
and the later transparency.editOpacityMask and transparency.viewOpacityMask
cases failed with "exit isolation mode first" instead.

Each case now also compares isolation mode and the shown mask before and
after the command, and puts them back when they changed, so a failure is
reported against the command that caused it.
2026-11-11 13:20:45 -08:00
dotCooCoo 590b579a49 Clipboard: quitting waits for the paste check so copied data reaches the clipboard manager
On X11 the last clipboard handle dropped hands what the app copied to
the clipboard manager. When the paste check's thread was still waiting
on a clipboard owner that had not answered, quitting dropped the app's
own handle first and waited 250 ms at most for the check's handle, so
the process could exit before the copy reached the clipboard manager.

- VectorcraftApp declares clipboard_probe before services, so the paste
  check stops and lets go of its handle before the app's handle drops.
- Dropping the paste check waits up to 5 s for its thread, longer than
  arboard's 4 s X11 read wait.
- New test quitting_drops_the_probes_clipboard_before_the_apps.
2026-11-11 13:17:48 -08:00
dotCooCoo 3edc1640fd Simplify: a flat closed loop keeps its outline when splitting reaches its limit
Follows #6651. When the refits of a closed subpath enclose no area,
Simplify splits its curves until straight lines between the pieces
enclose one. That split stopped at 65,536 pieces in all, and a loop
with many anchors then came back as the flat refit, with no outline.

- Only curved segments are split; line segments stay one piece each.
- When no split within the limits encloses an area, the loop stays as
  it is. With Convert to Straight Lines, each curved segment becomes
  the lines between its samples instead.
2026-11-11 13:17:39 -08:00
dotCooCoo 133e2c1356 MCP: livePaint.info reads id when group is left out
The docs said every livePaint key left out is reported as missing. livePaint.info reads `id` when `group` is left out or null, and reports a missing group only when both are left out.
2026-11-11 13:17:08 -08:00
Ahmed Gomaa f0e50437c2 Save on a read-only location offers Save As
A plain Save that the location refuses (read-only filesystem or permission denied) now opens Save As with the name filled in, instead of ending in an error dialog. The refusal travels as a typed engine error (READ_ONLY_LOCATION), so the control protocol and MCP still return an error and never open a panel. Recovery failures, explicit destinations, a busy picker and Close/Quit keep their existing flow.

Closes #6146
2026-11-11 23:16:03 +02:00
adambelis a9fd0eb7c0 Merge remote-tracking branch 'origin/main' into feat/width-smooth-profile
# Conflicts:
#	docs/target-app-parity.md
2026-11-11 22:13:44 +01:00
dotCooCoo e236ff07ce Merge main into fix/plugin-grants-delete-test-portable 2026-11-11 13:11:51 -08:00
Dylan M. Taylor 566f720868 SVG import: references follow an id's first element, one read per linked file, bounded blank lines (#5409)
* fix(svg): first element of an id, shared linked file reads, bounded blank lines

* address review: cache failed linked-image reads but give each href its own placeholder and link

* address review: charge only blank lines to the line-break budget

* address review: a collapsed line break keeps its measured leading in tiny type

* address review: blank lines use the same scaled leading tolerance
2026-11-11 16:11:34 -05:00
Sven Lafebre 7844657096 Fix #5111: keep ligatures whole and place line starts in SVG by value
- drawn_alike ignores kerning_before, so a space before the "f" of "fi"
  no longer splits the ligature.
- SVG export takes a line's starting space from kerningBefore and the em
  shaping sets the character in (vectorcraft_text::line_start_space),
  not from where the glyph is drawn, which can include a shaping offset;
  the reader layout for justified lines leaves the space out, so those
  lines start there too.
- Type on a path writes the space as the first character's dx; a space
  after a line break, which <textPath> can't write, is outlined.
2026-11-11 22:09:20 +01:00
adambelis a08937661b Width profiles: near-outline sampling and review fixes (#3145) 2026-11-11 22:09:10 +01:00
dotCooCoo b426822a9c Type: Release Type on a Path keeps the size Dynamic Text drew the type at
Release Type on a Path copied the stored runs into point type with the
Dynamic Text flag still set, so the text dropped back to its stored size
and the file saved the flag on point type. The point type now gets the
drawn sizes and the flag is turned off.

The legacy .ai writer writes Dynamic Text type at its drawn sizes; it
now also warns that the setting itself is not written, as the editing
data writer does, whether the type is written as text or as outlines.

docs/target-app-parity.md lists reshaping or scaling the path among
VectorCraft's own rules, and a task bar comment marks leaving the item
out for threaded type as VectorCraft's own rule.
2026-11-11 13:06:51 -08:00
Dylan M. Taylor da8c37aa8c PDF: note fonts once per walk, place repeated rasters without converting again, check image headers before embedding 2026-11-11 21:06:08 +00:00
dotCooCoo dc206a9836 Merge main into fix/plugin-put-back-result 2026-11-11 13:04:11 -08:00
dotCooCoo fd7ff70941 Plug-ins: an update left in the store by a failed put-back turns a disabled plug-in on
When plugin.install writes a module that lists permissions, its grant
can't be written, and the old module can't be put back (or, on a first
install, the new module can't be removed), the store holds the new
module. install now takes put_back_plugin's result, so the plug-in's
disabled entries (by id and by stored file) are cleared as for any
install the store holds. The old grant, or none, still doesn't cover the
new module, so the next start skips it until it is approved.

Docs: docs/plugins.md, docs/mcp.md, docs/target-app-parity.md.
2026-11-11 13:03:38 -08:00
Dylan M. Taylor 8e8b43f045 ci: use StepSecurity's maintained actions (#6658)
Each third-party action that StepSecurity publishes a secure drop-in
replacement for now uses that replacement, pinned to a commit with its
version as a comment. Versions follow what we used: the newest
StepSecurity release in the same major, or the closest major they
publish. dtolnay/rust-toolchain refs that named a toolchain (stable,
nightly, 1.76) become the replacement's `toolchain` input.
2026-11-11 16:02:52 -05:00
Dylan M. Taylor 56142af888 Fonts: the budget test starts after the first scan 2026-11-11 20:54:53 +00:00
dotCooCoo 42ba91fb6d Plug-ins: the failed delete of grants.json is tested on every platform
A test-only switch makes removing grants.json fail on the test's thread while the file still reads. The new test replaces an approved plug-in with one that asks for no permissions while the switch is on, and checks that the store gets the approved module back and keeps the grant unchanged. The Windows test that holds the file open without delete sharing stays.
2026-11-11 12:49:41 -08:00
Dylan M. Taylor f64438122f Fonts: system_fallback visible to the module's tests 2026-11-11 20:48:51 +00:00
dotCooCoo 5c2e182388 MCP: a malformed object id is refused as such, not reported as missing
Commands that need an object id read it with `id_param`, which returns
None for a value that is not a non-negative integer, so `{"id": "5"}` or
`{"id": -1}` was refused with "missing id". They now read it with
`object_param`, which refuses such a value with "`id` must be a whole
number", naming the key. This covers `document.node`, the `path.*` anchor
and handle commands, the `text.*` range and path commands, the mesh,
envelope and slice point commands, `layer.target`, `layer.move`,
`node.move`, `select.magicWand`, and the `source`, `target`, `group` and
`path` keys of `appearance.transfer`, `appearance.copyFrom`, the
`livePaint.*` commands and `text.createInPath`.

The optional `group` of `livePaint.fill` and `parent` of `node.move` are
refused the same way when given. Before, a malformed `parent` moved the
object to the top level and a malformed `group` fell back to the
selection.
2026-11-11 12:47:49 -08:00
jmvergesandClaude Opus 5.5 543034987e Envelopes: type's strokes paint at their own weight along the bent outlines (#6644)
With Envelope Options > Distort Appearance on, VectorCraft turned the strokes
of type in an envelope into filled outlines and bent them with the art, so a
stroke got thinner where the grid squeezes the type and thicker where it
stretches it. The drawn art of users' .ai files keeps those strokes as stroked
paths at the type's own weights, and the ring they paint is as wide wherever
the grid squeezes or stretches the type. An envelope of stroked type in such a
grid therefore didn't draw as its drawn art and kept it instead of opening live.

Type's strokes (its characters' and those on its own appearance) now paint
along the bent glyph outlines at their own weight, with Distort Appearance on
or off, also in an envelope inside an envelope:

- doc: `map_node` maps the groups that hold type's glyph outlines with
  `Hooks::type_appearance` in place of `Hooks::appearance`, for type in the
  envelope's contents and for type that a live object in them (an inner
  envelope) outlined (`Warper::type_outlines`). Such a group's opacity mask
  is mapped with the ordinary hooks, so it bends as other art does. Groups
  marked as type's outlines in the contents themselves (art an earlier Expand
  made) stay plain art.
- effects: `bake_type_appearance`, `bake_appearance` with strokes kept as
  strokes: geometry effects on type's fills and strokes still bake in and bend.
- doc: `max_outset` counts type's character strokes as type's own visual
  bounds do (`char_stroke_outset`). It left them out, though they already
  painted past an envelope's grid where the glyphs reach its edge; this also
  grows the visual bounds of blends and repeats of stroked type and the reach
  of symbol art holding it.

Paths' strokes, geometry effects, opacity masks, linear gradients and pattern
fills bend as before. The canvas, hit testing, effect bounds, Expand, every
export and the .ai save take the evaluation from the same hooks. Expand of an
envelope of stroked type gives the bent outlines with the strokes on them.
This changes how VectorCraft's own envelopes of stroked type draw.

Tests: envelope_distort::type_strokes_paint_at_their_own_weight_in_a_squeezed_envelope,
a_stroke_on_types_appearance_paints_at_its_own_weight_in_a_squeezed_envelope,
type_strokes_paint_at_their_own_weight_in_an_envelope_inside_an_envelope,
a_paths_stroke_still_bends_in_a_squeezed_envelope,
an_opacity_mask_on_type_bends_as_other_art,
the_visual_bounds_of_an_envelope_hold_its_types_strokes,
the_visual_bounds_of_a_repeat_of_stroked_type_grow_by_half_the_stroke,
expanding_an_envelope_of_stroked_type_keeps_the_stroke_at_its_weight,
bake::tests::type_pieces_bake_their_geometry_effects_and_keep_their_strokes and
tests_native::an_envelope_of_stroked_type_whose_drawn_art_strokes_the_bent_outlines_at_the_weight_opens_live.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 20:45:33 +00:00
Sven Lafebre 6ae7051e4e Fix #5111: kerning at the caret before a line's first character moves it
At the Type tool's caret, kerning goes on the character before the caret.
At the start of the text there is none, so Alt+arrows, the Kerning field
and Reset Tracking and Kerning did nothing; at a later paragraph's start
the kerning landed on the invisible line break.

CharStyle::kerning_before (kerningBefore) is the space before a character
that starts a line by itself: the first character of the text, of a
paragraph, or after a forced line break. Shaping gives its cluster the
space and draws it that much later along the line (right to left the
space is at its right; in vertical type it moves down the column). The
caret at such a line's start sets it (edit::caret_kerning_target), in
type.step, type.resetTracking and the Character panel; text.setRangeStyle
takes it. SVG export starts the line where the character is drawn. The
Eyedropper and character styles leave it alone.
2026-11-11 21:43:31 +01:00
dotCooCoo 1348ec0003 Guides: the ruler guide export test leaves out the PDF's dates
exports_and_fit_all_are_the_same_with_ruler_guides compared PDF exports
byte for byte, but each export carries the time it was written in
/ModDate and the XMP packet. Two exports in different seconds differed,
so the test failed now and then in CI. The test now drops the dated
lines from the PDF before comparing, as undated in the envelope tests
does. SVG, PNG and PSD are still compared whole.
2026-11-11 12:35:29 -08:00
dotCooCoo 2b12ef37a2 Merge main into fix/custom-toolbar-add-tools 2026-11-11 12:30:07 -08:00
Dylan M. Taylor 21e6524f06 Text: colour layers stop drawing outlines once over budget, one frame's baseline search has a total step budget, a glyph outline keeps at most a million elements 2026-11-11 20:24:43 +00:00
Dylan M. Taylor 3c495a7b61 Fonts: the file search for an uncovered character stops at a budget between scans 2026-11-11 20:22:05 +00:00
BFlat b08e421445 Merge pull request #6651 from dotCooCoo/fix/simplify-flat-loops-keep-area
Simplify: a closed subpath whose refits enclose no area keeps its outline
2026-11-12 03:20:46 +07:00
dotCooCoo f04aa61fc1 Scripts: a return from a finally block drops the discarded error's throw site
A `return` inside a finally block discards the pending exception, but the
saved throw site for it stayed in the list of collection roots. The code it
pointed to then stayed live after the function was gone, which could stop a
script with the heap limit when that code came from `Function` or `eval`.
Returning from a frame now drops the saved sites for the slots it leaves.

Follows #6653.
2026-11-11 12:19:32 -08:00
dotCooCoo 96e20d548d Plug-ins: a light filter that reports progress runs in the background with Cancel
A light filter of plug-in API level 2 with the `progress` permission ran
on the UI thread, so its progress reports and `vc_cancelled` never
reached a progress window. It now runs in the background under its light
budget, as a heavy filter does, when it runs at once from Object ›
Plug-ins or its shortcut, or when OK runs it with Preview off. The
progress window shows its share and Cancel stops it. With Preview on, OK
keeps the previewed run. A filter that lists a `commandAllowlist` still
runs on the UI thread.

Docs: docs/plugins.md, docs/mcp.md, docs/target-app-parity.md.
2026-11-11 12:18:18 -08:00
dotCooCoo 9ed4670985 Merge main into fix/custom-toolbar-add-tools 2026-11-11 12:16:57 -08:00
dotCooCoo 3a7c46975c Merge main into feat/script-target-engine 2026-11-11 12:16:55 -08:00
BFlat 6e873b806c Merge pull request #6657 from dotCooCoo/fix/js-random-seed-test
Scripts: test that the OS random value seeds Math.random
2026-11-12 03:16:14 +07:00
dotCooCoo 7fbac74885 Merge main into feat/script-target-engine 2026-11-11 12:09:30 -08:00
dotCooCoo 825620eb6c Clipboard: Linux and BSD reads wait 2 s at most for the app that copied
arboard's Wayland data-control read waits for the app that owns the
clipboard to send its data with no time limit, and its X11 read waits up
to 4 s for each format it tries. These reads ran on the UI thread: a
Copy or Cut checking what the clipboard held, a paste, and the paste
check once its background thread is gone. A clipboard owner that never
sent its data froze the window.

On Linux and the BSDs, arboard's reads now run on a worker thread with a
clipboard handle of its own. Text and file lists wait 2 s at most and a
bitmap 10 s; the core-protocol fallback's text reads already wait 2 s.
While a read is unanswered, later reads fail at once. Writes stay on the
UI thread's handle.

The core-protocol fallback's worker is now generic and built on every
platform. On X11, closing the clipboard within 25 s of a read that got
no answer waits for that read to end, so the app's own handle is the
last one and hands the copy to the clipboard manager. On data-control
and the core protocol, closing does not wait for an unanswered read.
2026-11-11 12:06:33 -08:00
dotCooCoo 3d7803289c Toolbars: a + button on a new toolbar adds and removes tools from a menu
A toolbar made with New Toolbar was filled only by dragging tools from
its All Tools drawer, which the ••• button opens. It now also has a +
button (Add Tool…) above the ••• button. Its menu lists every tool under
the drawer's headings; a click adds the tool at the end of that toolbar
(toolbar.addTool with toolbar), and the tools already in it are dimmed.
Under Remove Tool the menu lists the toolbar's tools, and a click takes
one out (toolbar.removeTool). The button takes the keyboard focus, and
the menu scrolls inside the window like the other long menus.

The drawer and its drags work as before. Add Tool… and Remove Tool have
rows in every catalog.
2026-11-11 11:57:53 -08:00
BFlat dd6b00c228 Merge pull request #6653 from dotCooCoo/fix/script-throw-site-through-finally
Scripts: an uncaught error that passes through a finally block is reported where it was thrown
2026-11-12 02:53:22 +07:00
BFlat f9ff1caf54 Merge pull request #6648 from dotCooCoo/feat/dynamic-text-on-path
Type: Dynamic Text sizes type on a path to fill its brackets
2026-11-12 02:45:39 +07:00
dotCooCoo 96b0e42bc6 Merge main into fix/i18n-plugin-limit-messages 2026-11-11 11:42:06 -08:00
dotCooCoo 8102e9e6e8 i18n: plug-in limit messages are translated in every complete-message catalog
The instruction, time and memory budgets and the output size a plug-in
run can exceed had @msg rows only in hi and sv. Every other complete-message
catalog now has them, next to the other plug-in budget rows, and every
complete-message catalog has a row for the error of a plug-in removal that
leaves the plug-in's grant in the plug-in store.

The message scan now reads a message that an error or a status takes from a
&str constant (Error::Limit(TIME_BUDGET.into()),
crate::runtime::SELECTION_TOO_LARGE), so the untranslated-strings report
lists such messages. Tests: message_scan_reads_messages_held_in_constants
and plugin_limit_errors_are_translated.
2026-11-11 11:41:02 -08:00
ruchirguitar 4f295e5fee UI: keep numeric field caret on short pointer drags (#1326)
A pointer movement under the click tolerance was still treated as a tap,
so the focus click selected the whole numeric value. Any movement from the
press now keeps the caret; only a tap without movement selects all.
Regression test: a_sub_threshold_movement_released_as_a_click_keeps_its_caret.
2026-11-12 01:06:28 +05:30