Commit Graph
12344 Commits
Author SHA1 Message Date
dotCooCoo 3348b41796 Merge main into fix/freehand-tolerance-low-zoom 2026-11-13 08:12:01 -08:00
BFlat cb061d6a2c Merge pull request #7085 from dotCooCoo/feat/learn-next-up
Learn: View more opens a tutorial's practice sample, and Next Up shows the next tutorial
2026-11-13 23:05:29 +07:00
BFlat 6326acb122 Merge pull request #7070 from dotCooCoo/feat/recolor-prominent-colors
Recolor Artwork: the Prominent Colors bar weighs the art's colors
2026-11-13 23:05:19 +07:00
BFlat 25553f21fe Merge pull request #7123 from dotCooCoo/fix/actions-insert-menu-item-ui-commands
Actions: Insert Menu Item adds Object › Align and View menu commands
2026-11-13 23:05:00 +07:00
BFlat e0e97e8ed1 Merge pull request #7153 from dotCooCoo/fix/blend-spine-binary-search
Blend: find the spine segment for an arc length with a binary search
2026-11-13 23:04:50 +07:00
BFlat be99b8895d Merge pull request #7155 from dotCooCoo/fix/hindi-message-terms
i18n: Hindi messages keep their English meaning and the catalog's terms
2026-11-13 23:04:40 +07:00
BFlat 508c1b658e Merge pull request #7096 from dotCooCoo/feat/ai-save-envelopes
PDF/.ai save: envelopes stay editable when the file is reopened
2026-11-13 23:04:30 +07:00
BFlat d26413c3fb Merge pull request #7117 from dotCooCoo/fix/smooth-tool-smooths
Smooth tool: each drag smooths the path under it, and repeated drags keep smoothing
2026-11-13 23:04:21 +07:00
BFlat de93bef121 Merge pull request #7101 from dotCooCoo/fix/eyedropper-series-opacity
Eyedropper: a pick that leaves a series mark's row without paint replaces the stored transparency
2026-11-13 23:04:12 +07:00
BFlat f525bd742e Merge pull request #7147 from dotCooCoo/fix/script-file-name
Scripts: $.fileName names the file of the code that reads it
2026-11-13 23:04:04 +07:00
BFlat dd8f8ed2bd Merge pull request #7115 from dotCooCoo/fix/settle-keeps-preview-warnings
Edit: settling an interaction keeps the warning of the preview it applies
2026-11-13 23:03:55 +07:00
BFlat ada78a07ba Merge pull request #7114 from dotCooCoo/feat/group-effect-after-members
Effects: a group's geometry effects reshape what its members draw
2026-11-13 23:03:46 +07:00
BFlat 0cc04afd57 Merge pull request #7161 from dotCooCoo/fix/script-xml-heap-limit
Scripts: E4X operations count their temporary storage against the heap limit
2026-11-13 23:03:38 +07:00
BFlat 2d91c860a7 Merge pull request #7087 from dotCooCoo/feat/every-line-composer-shaped-area
Type: the Every-line Composer sets mixed sizes in shaped and wrapped area type
2026-11-13 23:03:29 +07:00
BFlat c68b2775e4 Merge pull request #7125 from dotCooCoo/fix/plugin-install-checks-first
Plug-ins: plugin.install writes nothing to the store until the module is checked
2026-11-13 23:02:12 +07:00
BFlat f239944df0 Merge pull request #7107 from dotCooCoo/fix/measured-arclen-coincident-quad
Paths: a quadratic segment whose points coincide measures zero length instead of NaN
2026-11-13 23:02:03 +07:00
BFlat b17a62be34 Merge pull request #7109 from dotCooCoo/fix/painted-fx-brush-reach
Effects: culling a painted piece leaves room for its own brush art
2026-11-13 23:01:55 +07:00
BFlat c8bf38c6a1 Merge pull request #7092 from dotCooCoo/fix/dimension-guide-line-snap
Dimension tool: a point on a Smart Guide stays on the guide
2026-11-13 23:01:45 +07:00
BFlat 714e3a3821 Merge pull request #7135 from dotCooCoo/fix/docs-paragraph-style-options-rows
Docs: Paragraph Style Options rows name the dialog and its seven sections
2026-11-13 23:01:36 +07:00
BFlat cd1feada31 Merge pull request #7132 from dotCooCoo/fix/ui-docs-brush-libraries
Docs: `ui-parity.md` names all nine built-in brush libraries
2026-11-13 23:01:27 +07:00
BFlat 017db27090 Merge pull request #7131 from dotCooCoo/fix/flatten-grouping-sweep-meets
Flatten: grouping sweeps skip boxes that a box already holds or cannot join
2026-11-13 23:01:18 +07:00
BFlat dc5c3aa444 Merge pull request #7119 from dotCooCoo/perf/pathops-overlap-sweep
Path ops: Offset Path sweeps long, low paths along their length
2026-11-13 23:01:08 +07:00
BFlat 7372d7ee5e Merge pull request #7081 from dotCooCoo/fix/svg-rtl-text-anchor
SVG: right-to-left text with text-anchor start or end opens at its directional edge
2026-11-13 23:00:55 +07:00
BFlat 02445d5f02 Merge pull request #7130 from dotCooCoo/fix/eps-image-samples-once
EPS: an image drawn more than once writes its samples once
2026-11-13 23:00:45 +07:00
BFlat 0455c12162 Merge pull request #7127 from dotCooCoo/fix/thread-empty-last-paragraph-style
Type: a threaded frame's empty last paragraph keeps a format given to it
2026-11-13 23:00:36 +07:00
BFlat e9459df52b Merge pull request #7099 from jmverges/fix/7097-top-object-split-curve
Envelopes: a top object's side that is one split curve follows its parameter
2026-11-13 23:00:21 +07:00
BFlat 77970c6924 Merge pull request #7082 from dotCooCoo/fix/smooth-resets-anchor-selection
Smooth tool: smoothing a path deselects its direct-selected anchors
2026-11-13 22:26:18 +07:00
BFlat 45acda45fe Merge pull request #7084 from dotCooCoo/feat/message-rows-id-nl-da-bg
i18n: Dutch translates status and error messages
2026-11-13 22:26:08 +07:00
BFlat 5fcfb0ce6e Merge pull request #7086 from dotCooCoo/feat/library-charstyle-menu
Libraries: a character style's menu offers Apply, Add to Character Styles, Rename and Delete
2026-11-13 22:24:41 +07:00
BFlat 7fb0a5b51b Merge pull request #7088 from dotCooCoo/fix/panel-drags-primary-button
Panels: item drags start only with the primary button, as Layers does
2026-11-13 22:23:19 +07:00
dotCooCoo ccb14182e9 Plug-ins: plugin.install approves from the grant it checks again, and others wait for its store writes
Without a plug-in store, plugin.install read the process grant once
for its later check and again for its approval. A grant another session
recorded between the two reads approved the module; when that session
revoked it before the install, the check saw the grant it first read
and the install recorded the revoked grant again. The approval now comes
from the first read.

plugin.install published the plug-in before it wrote the module and its
grant to the Plug-ins folder. A plugin.remove in another session in
between removed the plug-in, its file and its grant, and the install then
wrote both back, so the plug-in loaded with its grant at the next launch.
A session attaching the folder in between loaded the old version, which
kept running while the folder held the new one. plugin.remove, loads of
the stored plug-ins and loads of a plug-ins folder now wait until an
install has written the folder, and an install waits for them.

Docs: docs/plugins.md, docs/target-app-parity.md.
2026-11-13 06:56:24 -08:00
dotCooCoo b1c684e912 Scripts: E4X operations count their temporary storage against the heap limit
Assigning to a named child counts its list of the matching children, and
assigning through an empty XMLList counts the chain of targets it climbs,
before either changes the tree. copy(), toXMLString(), normalize(), the
`..` selector and the depth check on insertion hold the stack they keep
for each open level of the tree. Inserting an XMLList, and assigning one
to an item of an XMLList, hold their copy of the list's items.

copy() reads attributes in place, and toXMLString() reads attributes and
namespace declarations in place, instead of copying the lists. copy()
charges the declarations it copies before it copies them. Deleting by
name or assigning an attribute compacts the remaining links in place
instead of building a second list.

toXMLString() holds the set of prefixes a namespace lookup passes, and
picks a new nsN prefix in one pass over the declarations in scope
instead of rescanning them for each candidate.

The parser holds its open elements and their names, the text it gathers,
each tag's attribute values, the sets that check declarations and
attribute names, and its one copy of an element's namespaces, and it
charges the strings of the namespace declarations it makes. The
allowance it holds from the source's length covers the copy a value
takes as it becomes a string.

Comparing XML values, converting them to strings, assigning an XMLList
to an attribute, and reading a name, a qualifier, a namespace URI or
embedded text from a string built by joining strings flatten it with
flat_charged. An attribute name read from a string reserves its copy,
and an element name shares the string.

Docs: docs/target-app-parity.md.
2026-11-13 06:54:23 -08:00
dotCooCoo 666084fb81 Blend: find the spine segment for an arc length with a binary search
Spine::at_length walked the first subpath's segments from the start
until one ended at or past the arc length, so placing every blend step,
spread key, blend widget and Objects on Path attachment cost one
comparison per earlier segment. It now searches the anchors' arc
lengths, which never decrease, and picks the same segment: the first
whose end is at or past the length, so an exact anchor length and
repeated lengths from zero-length segments keep their segment. The
point and tangent evaluation is unchanged.

Tests compare the search with the old walk on open and closed spines,
one segment, curves, zero-length segments, every anchor, both ends,
out-of-range and non-finite lengths, and check that a 4,096-segment
spine needs at most 13 comparisons.
2026-11-13 06:46:06 -08:00
dotCooCoo 5d5b8aa53b i18n: Hindi messages keep their English meaning and the catalog's terms
193 Hindi rows outside the plug-in messages said something other than
their English: "a number of points" read as "many digits", swatches as
clocks, crop as a harvest, resolution as a resolve, and saving used
words the glossary replaces. Others left English prose in place,
dropped a clause ("to keep them all", "what follows it") or ran the
sentence backwards.

- Rows use सेव, स्वॉच, क्रॉप, रिज़ॉल्यूशन, पाथ and टाइप (for a text
  object) as docs/localization-hi.md sets them.
- Values a command reads (enum values, preset names, OpenType tags)
  stay as the English writes them.
- A test checks those terms and the point unit in every Hindi row, and
  another pins rows whose quantity or clause was lost.
- docs/localization-hi.md, docs/localization-parity.md and
  docs/target-app-parity.md carry the new counts and a wording note.
2026-11-13 06:43:32 -08:00
jmvergesandClaude Opus 5.5 e11155c692 Envelopes: a top object's split goes by the longer of handles or bends
The previous commit took the spans from how far each piece bends away
from the split whenever both handles there were within ONE_CURVE of the
anchor. A curve that barely moves at the split but doesn't bend there
(a straight one, say) has short handles and no bend, so that gave 0/0
and the side fell back to the polygon shares. Now the spans come from
whichever pair is the longer: the handles, or the bends (which go with
the squares of the spans). The check of every piece against the
rebuilt curve is unchanged.

Test: a_split_curve_keeps_the_top_object_patch_where_it_barely_moves_and_runs_straight.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 14:42:07 +00:00
dotCooCoo 75c74557bb Scripts: $.fileName names the file of the code that reads it
$.fileName gave the file being run, so a function defined in a file
that #include inserted, in a file $.evalFile ran, or in an earlier run
of a named engine reported the calling file instead of its own.

- vectorcraft_js: HostItem::SiteGetter gives a getter the CallSite
  (source tag, line, column) of the code that reads it.
- $.fileName maps that place through the run's source maps, or the
  maps a named engine kept, to the file the code is in. It is empty in
  code from a script given as source and in kept code whose map was
  dropped. Code that eval or Function compiled still gets the file
  being run.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-13 06:19:23 -08:00
dotCooCoo 60daa85f5b Actions: a View menu step in a recorded batch counts in action set files
Save Actions… writes version 6 when a View menu step sits inside a
`command.batch` step, since playback runs that step on its own, and Load
Actions… refuses such a nested step whose params are not an object.
2026-11-13 06:15:31 -08:00
FredApps e8c7421a3d CI: shared gate for the Affinity corpus; drop release's git-sources copy (#6683)
affinity-corpus.yml ran a local job; it now calls craft-common-actions
gate.yml with the same toolchain (1.95), environment and commands, and a
cache of its own. release.yml's git-sources copy is removed: the shared
release.yml runs the same check before any build.
2026-11-13 15:01:22 +01:00
dotCooCoo 4cf010c130 Flatten: grouping sweeps skip boxes that a box already holds or cannot join
The sweep filtered by transparency kept every open box in one tree, so
a box without transparency met every open box without transparency it
overlapped, though such pairs never join. Both sweeps closed an open
box early only when a later box held it, so in a nest of boxes that
share their left and top edges and get smaller to the right and
bottom, each box met every earlier box around it. One see-through
object inside 512 nested opaque objects took 130,817 meets in the
unfiltered sweep and 131,328 in the filtered one.

The filtered sweep now keeps open see-through boxes and open opaque
boxes in separate trees. An opaque box looks only in the see-through
tree; a see-through box looks in both.

In both sweeps, a box inside an open box it met, ending no further
right, is not opened when that box joins everything it overlaps (any
box without the filter, a see-through box with it). Whatever later
overlaps the inner box overlaps the outer one.

The nest above now takes 512 meets in each sweep, and a nest of 512
boxes alone takes 511 in the unfiltered sweep where it took 130,816.
The groups are unchanged.
2026-11-13 05:42:03 -08:00
dotCooCoo e97113c5c9 Docs: ui-parity.md names all nine built-in brush libraries
The Brush libraries row named four libraries. It now names the nine in
BUILTIN order (crates/engine/src/cmd/brushlib.rs), as the Window ›
Brush Libraries menu shows them, and says nine built-in libraries.
2026-11-13 05:40:21 -08:00
dotCooCoo 38bcbb817f Docs: Paragraph Style Options rows name the dialog and its seven sections
A double-click on a paragraph style's row and the panel menu both open
Paragraph Style Options (ui.paraStyleOptions, paraStyle.edit) with its
seven sections. The type-parity and ui-parity rows still called it the
rename dialog and named ui.paramDialog with paraStyle.rename; they now
name the dialog, its sections and its tests.

The target-app-parity entry said each section has a Reset Panel. Reset
Panel is unavailable in General, and the entry now states that.
2026-11-13 05:38:52 -08:00
dotCooCoo 19ab993479 EPS: an image drawn more than once writes its samples once
An image placed several times in an EPS file (the pieces of an envelope
or a Warp effect, copies of one image, a symbol's instances) wrote its
whole ASCII85 data at every placement: 200 copies for one envelope at
the default fidelity, enough for a large photo to pass the 256 MB page
limit and lose pieces.

The samples of such an image are now written once in the setup as
VCim<i>, and each placement reads them from there:
- Level 3: a ReusableStreamDecode stream, rewound with resetfile and
  read through a new FlateDecode filter at each placement.
- Level 2: an array of strings of at most 49,152 compressed bytes, fed
  to RunLengthDecode by a procedure.

An image placed once is written where it is drawn, byte for byte as
before. The page limit counts a repeated image's samples once, and a
colorized image is reused per ink.
2026-11-13 05:38:06 -08:00
dotCooCoo 53e8391e41 Recolor Artwork: the Prominent Colors bar keeps the Edit tab's height
- With art, the bar is 10 pt tall and the Edit tab leaves out the gaps
  above the wheel and above the sliders. The dialog is as tall on the
  Edit tab as on the Assign tab, and all of it but the color chips fits
  a 1024 by 576 screen.
- recolor.weigh counts a use for the first row that is not excluded
  when an excluded row lists the same color first.
2026-11-13 05:34:30 -08:00
dotCooCoo 130f914868 Type: a threaded frame's empty last paragraph keeps a format given to it
A paragraph format given to the empty paragraph after a frame's last
paragraph break was lost when the story flowed again: the paragraph is
shared with the next frame's first paragraph, and the next frame's old
style replaced it, in that frame and in the frames after it.

- The thread reflow also notes which frames' last paragraph changed
  style, and story_paras_restyled keeps that style against the next
  frame's unedited first paragraph, across unedited empty frames too.
  When the same edit formats the next frame's first paragraph, that
  format applies, as before.
- story_paras keeps its behavior for its other callers.
- Tests cover area type and type on a path on either side, a next frame
  holding text, three frames, undo and redo, a format that changes
  nothing, and numbered list counts.
2026-11-13 05:27:02 -08:00
dotCooCoo 6f3ffeb393 Plug-ins: plugin.install writes nothing to the store until the module is checked
plugin.install wrote the module and its grant to the Plug-ins folder,
attached the plug-in's settings and cache and turned a disabled plug-in
on before the registry's last approval check. When another session
revoked the grant, or recorded one for other bytes, after the command
recorded its own grant and before that check, the install was refused
but left the new module and grant in the folder for the next launch.
A plug-in another session installed under the same id between the
command's checks and the install took no part in the decision, so the
install replaced it without `replace: true`.

- registry::install_checked records the grant (or forgets it) and
  installs the plug-in as one step, after a caller check that sees the
  plug-in installed under the id and its grant; a refusal changes
  nothing.
- plugin.install commits through it before any store write, refusing
  with the existing messages when another plug-in now needs `replace`
  or the grant its approval came from changed. The store writes, the
  put-back on a failed write and the disabled-list update now run after
  the commit; their logic is unchanged.
- The grant call-site audit also counts registry::install_checked.

Docs: docs/plugins.md, docs/target-app-parity.md.
2026-11-13 05:25:16 -08:00
dotCooCoo 73a9c22361 Actions: Insert Menu Item adds Object › Align and View menu commands
Insert Menu Item… now lists the Object › Align items and the View menu
commands the app runs itself (Outline, Pixel Preview, Trim View,
Presentation Mode, Screen Mode, zoom, fit, Rotate View, and the show,
hide and snap items).

- An Align step holds the params its click sends at insertion, so a
  later Align To choice does not change it. A step that aligns to the
  key object stops the playback when the selection has no key.
- A View menu step plays in its place at every speed and adds no undo
  step. At Accelerated the steps on each side of it are one undo step
  each. Playing it while recording records it.
- Batch… refuses an action with an included View menu step.
- An action set file with a View menu step is version 6.
- The steps before a stop now play as saved steps, as other playback
  does, so a key that an older version recorded no longer fails there.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-13 05:17:40 -08:00
dotCooCoo 3aa7ea2f78 Recolor Artwork: a weighed color recolors with the row it moved to
- recolor.weigh drops another row's entry for a color that moved, so
  recolor.apply recolors it with the row it moved to and the shares
  describe the recolor. A color that the first color of another row
  with a new color also lists stays where it is.
- Linked swatch colors are grouped through a map, so a row with many
  unlinked colors no longer compares each one with every group.
- The Prominent Colors bar reads the shares again when a row gains or
  loses a new color, since that changes which row takes a shared color.
2026-11-13 04:58:39 -08:00
dotCooCoo 6b55d7ca3e Eyedropper: a whole pick that removes a series mark's painted row replaces the stored transparency
A whole pick from a source with no fill row, or no stroke row, removes
that row from the target. When the target is a graph series mark whose
row had paint, the pick now replaces the row opacity and blend mode the
graph keeps for that kind of row in the series. A row that had no paint
before the pick still leaves the kept values as they are, and a pick of
some attributes from such a source is unchanged.
2026-11-13 04:50:59 -08:00
dotCooCoo ba2a6fc1f3 Type: a further span beside a wrap object is measured across its line's height
Text wrap can split a line into spans that share a baseline. The spans
after the first were measured across the height of the line set in the
first one, so a taller line set in a later span could run into a wrap
object the shorter line stayed clear of. Each further span is now
measured again across the height of the line that goes in it. The line
gets the part that is free, and a span with no free part, or with a part
narrowed to less than the line's first word, is left empty. The
Every-line Composer and the line loop both place lines through
`Pen::place`, so they agree.

The Every-line Composer also keeps a line start whose line found no room
while a longer line from it would be taller. A taller line steps down
the frame by its own leading and can find room the shorter line's steps
missed.

Tests: a_further_span_is_measured_across_the_height_of_its_line,
composition_tries_a_longer_line_after_one_that_cannot_be_set.
2026-11-13 04:48:43 -08:00
dotCooCoo 349034dda2 Smooth tool: each drag smooths the path under it, and repeated drags keep smoothing
Part of #2224.

A Smooth tool drag only refitted the anchors in its reach within
Fidelity, so a wobble wider than Fidelity stayed, and a second drag over
the same place changed nothing. Repeated refits also drifted away from
the path, and a drag over a whole closed path left a corner at its first
anchor.

- path.smoothRegion now smooths the stretch in reach along its length
  with a Gaussian whose spread is a third of the reach, then refits it
  within Fidelity. The run's outer end points and handles stay fixed.
- Each refit stays within twice the distance the smoothing moved the
  path (at least a tenth of Fidelity), so repeated drags do not drift
  (SimplifyOptions::smooth_moved_only).
- A whole closed path is smoothed across its start as a loop, so a
  bump at the first anchor is smoothed like any other and no corner is
  left. Object > Path > Smooth smooths a closed path across its start
  the same way.
2026-11-13 04:44:12 -08:00