Commit Graph
3820 Commits
Author SHA1 Message Date
dotCooCoo 634b9c7ff8 Objects on Path: the Move All widget slides every attached object along the path
With one Objects on Path group selected whose objects have room to move,
the Selection and Objects on Path tools show a diamond below the first
object. Dragging it moves every object along the path together, keeping
the distances between them (object.objectsOnPath.options with start),
in one undo step; Escape cancels it, and the first object's distance
along the path shows next to the pointer.

On an open path the widget shows only when the distance from the first
object to the last is shorter than the path, and the objects stop at its
ends; on a closed path they go on round across the first anchor. Where
Auto or a Space value the open path is too short for sets the distance,
the drag also sets spacing to the distance as drawn.
2026-11-11 14:55:46 -08:00
dotCooCoo 3fda7ad83e Actions: Batch shows the data set File Name row only where it applies
The Batch dialog showed File Name for every destination when Play was
Data Sets, though only Destination Folder writes a file named from it.
The row now shows after Destination Folder for Data Sets with a folder
destination, and the dialog sends datasetFileNaming only then. The
chosen value stays in the dialog while another destination is picked.
action.batch and its default for an omitted datasetFileNaming are
unchanged.
2026-11-11 14:51:21 -08:00
dotCooCoo d9c38df736 MCP: link replies leave out paths outside a confined client's read roots
links.list, links.check, links.info and document.open's missingLinks,
modifiedLinks and updatedLinks rows no longer give an automation client
with read roots the path of a linked file outside those roots. path and
found are left out of the rows, and links.info also leaves out link,
location and the file size and date the document stores. A relative path
is taken from the document's folder. Each row keeps its id, file name and
status, and the document's links are not changed. Without read roots the
replies are as before.

Find in Folder now matches a search's rows by their ids, comparing paths
only when both rows have one, so it still shows the files found for a
row whose path was left out.
2026-11-11 14:47:53 -08:00
dotCooCoo 7bd559cb1d Docs: PDF/A files at PDF 1.4 write long pages in points
A PDF/A file at PDF 1.4 has no UserUnit, so a page longer than 14,400 pt is written in points with or without longPagesInPoints. The param text, docs/mcp.md and the exporter comment now say that only PDF/A files at PDF 1.5 and later keep the UserUnit.
2026-11-11 14:47:18 -08:00
dotCooCoo 5bd1bbf536 Scripts: File and Folder exists checks the kind, encode keeps slashes, every call is counted
- `File.encode` and `Folder.encode` leave `/` unescaped, as the File
  object reference lists it among the characters kept
  (https://extendscript.docsforadobe.dev/file-system-access/file-object/).
- `exists` is true for a `File` only when the path names something
  other than a folder, and for a `Folder` only when it names a folder.
- Fields of the records the app returns are read as own properties, so
  a property a script puts on `Object.prototype` does not turn a
  successful `open` into a failure.
- `File.encode`, `File.decode`, `File.isEncodingAvailable` and the
  `Folder` locations count as file calls and charge the host-call limit.
- On the web the locations are `null` and `File.tempName` names a file
  at the root, without asking the platform for a temporary folder.
2026-11-11 14:46:43 -08:00
dotCooCoo 2da0dcbba7 PDF: a PDF/A-2b file at PDF 1.4 writes long pages as PDF 1.6 with a UserUnit
PDF/A-2 limits page boundaries to 14,400 units (ISO 19005-2, 6.1.13), and
PDF 1.4 has no UserUnit. A PDF/A-2b export at PDF 1.4 with a page longer
than 14,400 pt was written in points, with a MediaBox over 14,400 units,
whether or not longPagesInPoints was on.

Such a file is now written as PDF 1.6 with a UserUnit and a warning that
names the version, as PDF 1.5 files already are. PDF/A-2 files may declare
PDF 1.0 to 1.7. Pages past 32,767 pt that aren't whole numbers of points
are written too, since the refusal applies only to PDF 1.3 and 1.4 files.
Files whose pages are all 14,400 pt or shorter stay PDF 1.4.
2026-11-11 14:43:24 -08:00
dotCooCoo aa27045ef4 Type: Character Style Options sets Tate-chu-yoko Up/Down and Left/Right
Advanced Character Formats in Character Style Options and New Character
Style gains Tate-chu-yoko Up/Down and Left/Right fields, in points from
-1296 to 1296, which set the style's tcyUpDown and tcyLeftRight as the
Character panel menu's Tate-chu-yoko Settings sets them on text. An empty
field leaves the attribute unset, an explicit 0 stays distinct from
unset, a value outside the range is an error, and the section's Reset
Panel clears them with its other attributes. Text that uses the style
takes the values and keeps its local overrides.
2026-11-11 14:40:53 -08:00
dotCooCoo f3cff033ca Edit: a command that ends a drag keeps its unapplied preview
With Real-time Drawing off, a tool stores its latest preview and applies
it only when the drag is released. A command that ends the drag first
(Undo, Redo, a filter that calls commands, a plug-in command that calls
commands, and the other callers of settle_interaction) committed the
interaction without that preview, so the drag was dropped: Undo then
took back the step before it, and a calling filter was given the
objects where they were before the drag.

settle_interaction now applies such a preview first, as the tool's own
commit does, and cancels the drag when the preview fails. The re-prepare
of a calling filter's input after settling is now reached, and tests
cover it at both plugin.run and plugin.<plug-in>.<command>.
2026-11-11 14:15:22 -08:00
dotCooCoo e63e341a95 Docs: the Check Script escape example shows the escape
The example of an escape that does not start a name read `0abc`, the character, where the four-digit escape of 0 followed by abc was meant.
2026-11-11 14:03:16 -08:00
dotCooCoo 0923d862a1 PSD import: File › Open converts a PSD file's layers to objects
Opening a .psd or .psb used to give one merged image. document.open now
reads the layer records and builds the document from them. The PSD
import options page
(https://helpx.adobe.com/illustrator/desktop/add-and-import-files/import-from-other-apps/photoshop-import-options.html)
says the option that converts layers to objects keeps as much of the
layer structure as it can, merges the layers of a clipping mask into one
layer and discards hidden layers that require merging, and that Import
Hidden Layers brings hidden layers in. document.open does the same, with
hiddenLayers: true for the hidden layers; layers: false keeps the merged
image.

VectorCraft's own rules:

- Each layer becomes a layer holding its pixels as an image, with its
  name, visibility, opacity and blend mode. A group becomes a layer of
  sublayers; a Pass Through group is not isolated.
- Clipped layers show where the layer below has pixels. That layer's
  fill opacity fades only its own pixels. Layers clipped to an
  adjustment layer, or to a fill that stores no pixels and isn't drawn,
  show over the whole canvas.
- A layer mask becomes the layer's opacity mask, its default color and
  invert flag applied; a disabled mask is kept, switched off.
- Fill opacity multiplies the layer's opacity.
- Layers and groups in Dissolve, Linear Burn, Linear Dodge, Vivid Light,
  Linear Light, Pin Light, Hard Mix, Subtract, Divide, Darker Color and
  Lighter Color are merged with the layers below, whether or not they
  hold transparent pixels; Color Dodge, Color Burn and Difference keep
  their mode. A merged Dissolve layer's pixels are opaque or
  transparent, in a pattern of each layer's own.
- A solid color fill layer that stores no pixels and has no vector mask
  comes in as its color (RGB, CMYK, Grayscale, Lab or HSB).
- A layer name stored only as a Pascal string, without its Unicode
  name, is read as Mac Roman.
- Adjustment layers, fill layers that aren't drawn, layer effects that
  are switched on, knockouts and vector masks are not applied; the
  warnings count them.
- A merge whose canvases need more memory than allowed leaves the layer
  alone, in the Normal mode or unclipped, with a warning.

This changes what opening a layered PSD does. Batch Trace Folder passes
layers: false, so a PSD is still traced as its merged image. A file
whose layers can't be read, or whose layers and merges need more memory
than allowed, opens as the merged image with a warning.

Lengths, counts, nesting, decoded pixels and the canvases merges make
are bounded. Zipped 32-bit data with prediction is now read, in layers
and in the merged image. import_fuzz gains a case that mutates the PSD
and PSB samples and opens them with their layers. The PSD reader's new
error messages have rows in every catalog.

Docs: docs/mcp.md, docs/target-app-parity.md, docs/file-format-parity.md,
README.
2026-11-11 13:59:32 -08:00
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 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 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
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 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
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
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 3a7c46975c Merge main into feat/script-target-engine 2026-11-11 12:16:55 -08: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
BFlat e0779c6266 Merge pull request #6646 from dotCooCoo/fix/plugin-fuzz-fuel-slice
Plug-ins: the fuzz test reads the runtime's fuel slice instead of a copy
2026-11-12 02:28:28 +07:00
dotCooCoo f1c17603bb Scripts: #targetengine runs a script in a named engine that keeps its globals
A script whose first lines name an engine with `#targetengine name` (or
`//@targetengine name`) runs in that engine, and a later script that names
it sees the globals and functions earlier runs left, until the engine is
dropped or the session ends. A script without the line, and
`#targetengine main`, still get a new engine for each run.

- A session keeps at most 8 named engines, each with a 64 MiB heap. A run
  that goes past the heap ends with the heap limit, and the engine stays
  usable. Names are 1 to 64 bytes. The line comes once, in the script
  itself, before its code and its #include lines.
- An uncaught error, a limit or script.stop puts the documents back and
  keeps the globals the run set. An internal error drops the engine. A run
  stopped at a dialog or left paused ends where it was before the engine
  is kept. An error in a function an earlier run defined is reported
  without a line and column.
- `vc`, `$`, `alert`, `confirm` and `prompt` are made once for each engine
  and act for the run that calls them, including copies an earlier run kept
  in a global. `vc.args` gives a read-only copy of the calling run's
  `args`, and what a run sets on `$` lasts for that run only.
- `script.engines` lists the named engines with their heap, and
  `script.engine.drop {name}` drops one. Scripts cannot run either.
- The engines stay on the thread that made them. A session used on
  another thread starts there with no named engines, and the old ones are
  dropped on their own thread.
- The web build has no named engines: a run that names one other than
  `main` ends with an error before any of it runs.
- `Realm::end` ends a run where it is, `Realm::error_source` counts from
  the realm's last script, and `HostItem::FrozenGetter` gives a frozen
  value each time it is read.
- The script checker lists `#targetengine` as a supported directive.

Docs: docs/mcp.md, docs/target-app-parity.md, docs/gaps.md.
2026-11-11 11:17:42 -08:00
BFlat 541f8beae7 Merge pull request #6643 from dotCooCoo/feat/pdf-large-pages-in-points
PDF: an option writes pages longer than 200 in at full size in points
2026-11-12 02:05:54 +07:00
dotCooCoo e4272da7a9 Merge main into fix/plugin-approval-ok-delay 2026-11-11 11:03:34 -08:00
dotCooCoo 6723d09328 Plug-ins: the approval dialog takes OK only once its question was shown for half a second
The plug-in approval dialog's OK stays disabled until the question has
been on screen in the focused window for 0.5 s. A click, Enter or Space
before then, a press begun before then, and a direct confirm install
nothing and leave the dialog open. The wait starts over for every new
question, even one that reads the same, and when the window gets the
focus back, so a question swapped in just before a click lands is not
approved by that click. Cancel and Escape work at once.
2026-11-11 11:02:36 -08:00
BFlat c70d320d64 Merge pull request #6639 from dotCooCoo/fix/issue-6622
Part of #6622: Help Bar hints and tool names show in full on hover when clipped
2026-11-12 02:01:07 +07:00
BFlat d0bb662aa2 Merge pull request #6641 from dotCooCoo/fix/scriptcheck-identifier-chars
Script checker: names follow Unicode ID_Start and ID_Continue
2026-11-12 01:52:02 +07:00
dotCooCoo 1f835d94c4 Merge main into fix/script-throw-site-through-finally 2026-11-11 10:44:55 -08:00
dotCooCoo 8d094fc811 Scripts: an uncaught error that passes through a finally block is reported where it was thrown
An error a finally block passed on unchanged was reported at the end of
that block, in script.run's report and in the Script Console, because the
block's end raised the error again at a new site. The interpreter now keeps
the site of the exception each finally block runs for, keyed by the stack
slot that holds it, and the block's end throws the exception on from that
site. This holds through nested finally blocks, called functions, resumed
runs and marked host errors. An error the finally block throws is reported
at that throw, and one its return, break or continue discards is not
reported.
2026-11-11 10:44:16 -08:00
BFlat 6a4ef12091 Merge pull request #6603 from Messhias/feat/g5-brush-lib-energy-beams
G5: built-in Energy Beams brush library
2026-11-12 01:43:54 +07:00
dotCooCoo 2606b70604 Type: Dynamic Text sizes type on a path to fill its brackets
Type › Dynamic Text (`type.dynamicText`, also on the Contextual Task Bar
for type on a path) scales the text by one factor so it fills the span
between its start and end brackets, and the layout fits it again after
each edit to the text, the path or a bracket. The first character's
size moves in 0.1 pt steps within 0.1 to 1,296 pt. Turning it off, or
threading the type, keeps the sizes it was drawn at.

- The model saves `dynamicText` only when on.
- SVG, the legacy .ai writer and the .ai editing data write the drawn
  sizes through `vectorcraft_text::drawn`; the editing data warns that
  the setting is not written.
- Shrink Text to Fit and Dynamic Text step the size the Character panel
  shows when a size measures the cap height, x-height or ICF box.

Docs: docs/mcp.md, docs/type-parity.md, docs/target-app-parity.md.
Part of #4134.
2026-11-11 10:42:49 -08:00
BFlat 1e76791ae1 Merge pull request #6615 from dotCooCoo/feat/script-file-folder
Scripts: File and Folder read files in the script's own folder
2026-11-12 01:35:45 +07:00
dotCooCoo 3fba3a440c Merge main into fix/plugin-fuzz-fuel-slice 2026-11-11 10:32:47 -08:00
BFlat f83d916d8a Merge pull request #6631 from Hassan1305/docs-6340-rotation-direction
Commands: every angle and rotation param says which way it turns (#6340)
2026-11-12 01:32:17 +07:00
BFlat 210e9ad7d0 Merge pull request #6632 from dotCooCoo/feat/ruler-guides-as-objects
Guides: ruler guides are document objects on their layers
2026-11-12 01:32:07 +07:00
dotCooCoo ae5bb7b26e Plug-ins: the fuzz test reads the runtime's fuel slice instead of a copy
vectorcraft_plugins exports FUEL_SLICE and BYTES_COPIED_PER_FUEL (hidden
from the docs), and the bulk memory fuzz cases use them for the passes
around one slice, the lengths around one unit of fuel and the fuel
estimate, in place of the copied 20,000,000 and 64.
2026-11-11 10:29:22 -08:00
dotCooCoo 3074560b07 PDF: an option writes pages longer than 200 in at full size in points
The Save PDF dialog's General section gets "Write pages longer than 200 in
at full size in points" (longPagesInPoints in document.exportPdf,
document.export, presets and document.pdfSettings), off by default. When it
is on, a page with a side longer than 14,400 pt is written with its media
box in points and no /UserUnit at every PDF version, and a PDF 1.5 file
stays PDF 1.5. The export's warning for such a page then says it is written
in points. With the option off, and for pages of 14,400 pt or less, the
file is the same as before.

Docs: docs/mcp.md, docs/target-app-parity.md. The new label has a row in
every catalog that lists the dialog's other options.
2026-11-11 10:17:24 -08:00
Fabio William Conceição 72c7cb5a73 G5: built-in Energy Beams brush library
Six brush libraries; MCP docs list Energy Beams with the other generated brush libraries.
2026-11-11 18:07:54 +00:00
dotCooCoo 4825113906 Part of #6622: Help Bar hints and tool names show in full on hover when clipped
The status message already ends in an ellipsis and shows its full text
on hover when it is clipped. The Help Bar's hint and the tool name in
the status bar ran past the window edge instead; the Dimension tool's
hint is wider than an 800-point window. Both now end in an ellipsis and
show their full text in a tooltip on hover. Screen readers receive the
full text, and text that fits shows no tooltip.

In a narrow status bar the tool name now ends in an ellipsis before it
takes the status message's room. Before, the tool name could leave the
message almost no width.

The tooltips of the clipped hint, tool name and message wrap to fit
inside the window. egui's tooltips are 500 points wide, which is wider
than an 800-point window at UI Scaling 1.6 or more.

The hint and tool name tests fail without the change. The status
message test covers its existing ellipsis and tooltip.
2026-11-11 09:58:49 -08:00
dotCooCoo 5ac7d97d85 Guides: ruler guides are document objects on their layers
Ruler guides were a list kept beside the layers. Each guide is now an
object of its layer or sublayer, always directly in it and above the
layer's art. These behave as on main:

- Guides draw on screen only. Exports (SVG, PDF, PNG, PSD), Fit All,
  Select All, the art bounds, clicks and marquees, and a layer's
  Pathfinder and Transform effects leave them out. The Layers panel
  lists them as guide rows of their layer, the highest guide.list
  index first.
- Deleting a layer deletes its guides, and merging layers keeps their
  guides in the merged layer.
- The guide commands number guides as guide.list does, in the order
  they were made.

New: each guide takes an id from the document's object ids. An object
command given a guide's id fails with an error, and node.move cannot
put a guide into a group or out of its layers. A guide made while an
opacity mask, a symbol or a pattern is edited moves to the top
visible, unlocked layer when editing ends.

File format: native files still save the guides in the document's
guides list, in every file version, so older builds read them. Each
entry may now also carry the guide's id, name, locked and hidden
flags; older builds ignore those fields. Opening a file puts each
saved guide on its layer (on the top visible, unlocked layer when the
file names none or a layer it does not have), and each saved guide
counts against the object limit for opening a file. A ruler guide node
that a file holds below a layer's art moves up to the layer's guides,
and one anywhere else (in a group or another object, beside the
layers, or in a symbol) is removed. Ruler guides read from an .ai file
become guide objects on the layer they were on, numbered in the order
the file lists them.
2026-11-11 09:52:28 -08:00
BFlat ad70adf96e Merge pull request #6624 from Messhias/feat/g5-symbol-lib-skill-icons
G5: built-in Skill Icons symbol library
2026-11-12 00:50:41 +07:00
BFlat 25433ede65 Merge pull request #6619 from jmverges/fix/6610-aligned-lines-trailing-tracking
Type: aligned lines leave the tracking after their last character out
2026-11-12 00:39:30 +07:00