Commit Graph
5637 Commits
Author SHA1 Message Date
dotCooCoo a98b800761 Export: live effects in symbol art apply in PDF, EPS, print and DXF
PDF, print, EPS and DXF wrote a symbol instance by placing its symbol's
art as stored, so live effects inside that art were missing: a square
moved 100 pt by a Transform effect stayed in place in the file while the
canvas drew it moved.

bake_document_placing_symbols replaces each instance of a symbol whose
art (or the art of a symbol nested in it) needs baking by that art
placed as the instance places it, then baked. The art is placed before
its effects apply, as on the canvas, so an instance that scales its art
does not scale what its effects do. A dynamic instance's own paint
changes apply before baking. Nesting stops at 32 levels and the placed
art at 16,000,000 objects per export; past them an instance stays an
instance. DXF places 9-slice cells unclipped, as its scene does. The
symbols' own art, SVG and the .ai editing data are unchanged, and HPGL
already baked each placed instance.
2026-11-13 22:31:48 -08:00
dotCooCoo 1d5c30d753 Merge main into fix/colored-light-seams 2026-11-13 22:31:14 -08:00
dotCooCoo 5a4020a6c0 Graphs: graphic styles leave a design's art in a series unlinked
graphicStyle.new linked the object it was made from, the art of a column
or marker design in a graph series included, and graphicStyle.redefine
gave the new look to every object still linked to the style. Redefining
the style from another object then restyled that art, which the graph
draws again from the design.

- graphicStyle.new makes a style from the art without linking it.
- graphicStyle.redefine leaves the art's look as it was and unlinks it,
  as an edited object is, whether the art is a linked object (as in a
  document saved before this change) or the source.

Docs: docs/mcp.md, the params text of graphicStyle.new and
graphicStyle.redefine.
2026-11-13 22:27:58 -08:00
dotCooCoo 58bc03a3e7 Gradient tool: a vector drawn on the art snaps as other drawn points do
A gradient vector drawn on the art, a click that applies one, and the
dragged start and end handles snapped through snap_draw, which gathers
the bounds and anchors of the art only. Path segments, View › Snap to
Glyph's guides and the construction guides through the vector's start
did not pull them, and a click applied the gradient from the pointer
rather than from the snapped point. They now snap through DrawSnap, as
the Pen and Line Segment tools do:

- The press gathers the targets, for a vector drawn on the art and the
  start and end handles only, and the drag keeps them. A click applies
  the gradient from the snapped press.
- A dragged end or handle leaves its start as a Line Segment's end does:
  it lands on the construction guides through the start, and with Shift
  it keeps to 45° steps from the Constrain Angle and snaps along them.
- The pointer hovering where a press would draw a vector (anywhere with
  art selected, over art with nothing selected), and not over the
  annotator, shows the guides of the point the vector would start on.

A press that snaps onto a nearby target no longer starts the drag
early: the 3 px drag threshold counts from where the pointer was
pressed, not from the snapped start.
2026-11-13 22:25:34 -08:00
dotCooCoo 380b67f1bc Command Palette: Delete Selection asks first, as the Layers panel does
The palette listed the Layers panel's Delete Selection twice: as
Window › Layers › Delete Selection, which ran layer.delete and deleted a
layer holding art without asking, and as a bare Delete Selection, which
ran ui.layerDelete and asked first.

- The palette now lists one Window › Layers › Delete Selection result,
  which runs ui.layerDelete, so a pick asks before deleting a layer or
  sublayer that holds art. Scripts and the control channel still run
  layer.delete at once.
- New Brush…, Brush Options…, Symbol Options…, Import SVG Filter… and
  Convert to Text were also listed twice under the same name, once as
  the engine command, which the palette ran with no params. Each is now
  listed once, as the UI command that opens its dialog or file chooser
  or acts on the edited paragraphs.
- A search for a wrapped engine command's id or menu path still finds
  the one result.

Docs: docs/target-app-parity.md (Layers panel deletion).
2026-11-13 22:23:25 -08:00
dotCooCoo 70da63f5b0 Merge main into fix/polygon-sides-refused 2026-11-13 22:23:24 -08:00
dotCooCoo a008a0d68e SVG import: a nested tspan keeps its direction and unicode-bidi
A <tspan>, <textPath> or <a> inside a <text> whose unicode-bidi is embed
now gives its characters its direction as their Character Direction. An
element with normal stays in its parent's embedding, unicode-bidi is not
inherited (inherit takes the parent's value), and the <text> keeps its
own paragraph direction. A run that SVG export writes with direction and
unicode-bidi="embed" now opens with its Character Direction again.

Character Direction holds one level of embedding and no override or
isolation, so bidi-override, isolate and isolate-override are imported
as embeddings in their direction, plaintext as an embedding in the
direction of its first strong character, and an embedding inside
another as an embedding of its own, each with an import warning.
unicode-bidi: plaintext on the <text> runs and anchors each paragraph
in the direction of its first strong character.

https://www.w3.org/TR/SVG11/text.html#RelationshipWithBi-directionality
https://www.w3.org/TR/css-writing-modes-3/#unicode-bidi
2026-11-13 22:21:42 -08:00
dotCooCoo a5a67f8c4d EPS: a test covers type taken back with its color bitmap glyph images
Type whose color bitmap glyphs fit under the page limit, but whose run
fill passes it, is left out together with the image placements of its
glyphs. The new test builds the color font in memory and checks that
the page then equals the page without that type: no reference or shared
resource is left for the glyph image, a glyph drawn once elsewhere is
written inline again, and an image drawn twice keeps its shared samples.
2026-11-13 22:18:45 -08:00
dotCooCoo 2a4185d6ca Merge main into fix/graph-series-gradient-per-mark 2026-11-13 22:15:43 -08:00
dotCooCoo bc6c48367e Merge main into fix/knockout-groups-flat-masks 2026-11-13 22:15:33 -08:00
dotCooCoo 387b3a52eb Merge main into fix/i18n-literal-brace-messages 2026-11-13 22:11:11 -08:00
dotCooCoo 3341061ca3 i18n: Simplified Chinese translates 114 status and error messages that no catalog had
untranslated_report lists 167 status and error messages that no
complete-message catalog translates. zh-hans.tsv gains 114 @msg rows for
the ones no open pull request covers: 3D and Revolve limits, image export
and encoding errors, color profiles, color books and swatch exchange
files, binary DXF and DWG import, path operations, swatch, graphic style
and action set libraries, and zip, script and regular expression errors.
Placeholders, backticked names, command ids and listed values stay as the
English writes them.

The other 53 (composite fonts, SVG filters, read roots and graph design)
are left to #7211, #7378 and #7405. docs/localization-parity.md and
docs/target-app-parity.md give the new counts: 3,554 @msg rows, and 55
messages the report lists for zh-hans, all of them translated by open
pull requests.
2026-11-13 22:11:05 -08:00
dotCooCoo 5d89b37040 Paste: graphs bring and follow the styles and swatches their labels keep
A graph keeps paints for its series and labels, and character and
paragraph style names for its labels, which each redraw gives the
marks and labels. Copy did not follow them, so a graph pasted into
another document left those swatches and styles behind, and a paste
that renamed them left the graph pointing at the old names.

Copy now collects them and paste relinks them, as it does for type.
Paste without Formatting drops the labels' style names, so it still
leaves text styles behind.
2026-11-13 22:10:18 -08:00
dotCooCoo 458d61ccf1 i18n: Danish translates status and error messages
The Danish catalog had 1,033 `@msg` rows and 47 `@msgplural` rows, and
Danish was not in COMPLETE_MESSAGES, so with the interface in Danish
script errors, plug-in manifest and permission errors, parameter
schema errors, 3D effect refusals, file-reader errors and data-set
messages showed in English.

- da.tsv gains the 602 `@msg` rows and the one `@msgplural` row that
  the German, Czech, Spanish and French catalogs have and Danish
  lacked. The `@msg` rows are one block with a header comment on
  their conventions. Backticked names, command ids, double-quoted
  values and bare true, false and null stay as the English writes
  them.
- da joins COMPLETE_MESSAGES, so the message tests that iterate the
  list check Danish too, and the untranslated report lists Danish's
  missing messages. It lists the same set for Danish as for Czech,
  Spanish and French.
- The registry comment, docs/development.md, the Danish row and
  introduction of docs/localization-parity.md, and the interface
  languages line of docs/target-app-parity.md list Danish among the
  languages that translate messages.
2026-11-13 22:09:57 -08:00
dotCooCoo 82842e4fe0 Clipboard: a Wayland read or send ends when the other app moves no data for 2 s
On the core Wayland clipboard, a read waited for the app that copied to
close its pipe, and a send wrote until the app that pastes took all of
the data. An app that left its pipe open and stopped held the one read
worker, so later pastes failed at once, or held one of the eight send
threads until it closed its end.

Reads and sends now use the pipe nonblocking and wait for it with poll.
When no byte moves for 2 s (VectorCraft's own limit, the existing text
wait) the read fails without pasting partial data and the send stops;
both close their pipe. The wait starts again after each byte that moves,
so a large transfer that keeps going is not cut short.
2026-11-13 22:09:17 -08:00
dotCooCoo 6c26233a63 Clipboard: quitting on Linux takes every window off screen before the clipboard wait
Quitting can wait up to 27 s for the clipboard handles to let go before
eframe closes the windows. winit cannot hide a native Wayland window, so
the main window stayed on screen for that wait, and the X11 windows of
floating panels and documents were not hidden at all.

- A native Wayland main window is unmapped by attaching no buffer to its
  surface. The window and its connection stay open for the Wayland
  clipboard. An X11 main window, XWayland included, is hidden as before.
- On Linux the X11 windows of floating panels and documents are recorded
  as eframe creates and drops them (viewport hooks), and quitting unmaps
  them through a second X11 connection before the wait.
- The new module apps/vectorcraft/src/quit_windows.rs replaces
  app::hide_before_teardown, with tests of which windows are hidden or
  unmapped. portal_save's Wayland surface helper is shared with it.
2026-11-13 22:02:56 -08:00
dotCooCoo d3e520b14b Effects: raster effects on a symbol instance's fill or stroke are drawn
A raster effect (Drop Shadow, Outer Glow, Gaussian Blur and the other
effects that paint pixels) on a symbol instance's topmost fill now draws
on each filled shape of the symbol's art, and one on its topmost stroke
draws on each stroked shape, after any geometry effects listed before
it. The same rules as for geometry effects apply: only a visible fill or
stroke with an opacity above 0 counts, a group's own fills and strokes
in the art take the effect, and an instance inside the art passes it on.

The canvas, Use Preview Bounds and the drawn bounds, SVG and PDF export
draw it, and Object > Expand Appearance is enabled for such an instance:
a shadow or glow becomes an image under the instance, and a blur,
feather or inner glow replaces it with the image, in one undo step.

Existing documents with a raster effect on a symbol instance's fill or
stroke draw differently: before, the effect drew nothing.
2026-11-13 21:53:15 -08:00
dotCooCoo 1494e2c230 Type: Tsume narrows the space around each character's ink
Character panel > Tsume, shown with Show East Asian Options, takes away a
percentage of the blank space on each side of a character's ink, inside
its own width, in horizontal type. The value is from 0 to 100, 0 by
default. 50 halves that space and 100 sets the ink against the
characters before and after it; the glyph keeps its shape. A character
without ink, such as a space, keeps its width, and a ligature or a
letter with its marks takes the value of its first character. The
font's pair kerning, tracking, manual kerning and Insert Aki are added
after it, and justification keeps it. Vertical type keeps its spacing.

- text.setFormat and text.setRangeStyle take `tsume`; a value that is
  not a number or is outside 0 to 100 is refused, naming the key.
  Character styles carry it, and a file holding a value outside the
  range opens with the nearer end.
- The Character panel's Tsume field has 0, 25, 50, 75 and 100 in its
  menu, and Reset Panel sets it to 0. Character Style Options shows it
  in Advanced Character Formats.
- Live SVG text places each character Tsume moves, and type on a path
  with Tsume is written as outlines.

Docs: docs/mcp.md (Tsume, Character Style Options fields),
docs/target-app-parity.md.
2026-11-13 21:50:05 -08:00
dotCooCoo b386d54264 Image Trace: centre-lines quality measures Create Strokes' split on a mixed-width body
The centre-lines class gains a mixed-width body scene: one disc of radius
24 px with arms of 2, 3 and 5 px that start inside it, Stroke 6 px, and no
other ink. A test checks that the scene is one connected shape and that
both the anti-aliased and hard input trace as one fill and three strokes
that reach all three free ends.

docs/image-trace-quality.md's centre-lines section is measured again on
current main (24 scenes, two runs identical in every column but ms). The
new scene finds 100.0% of its arms with 99.3% / 98.3% of the disc filled;
mix now traces as 6+3 paths with 99.9% / 99.8% found and 7/7 ends, where
the earlier whole-blob rows showed 3+1, 57.3% and 6/7. All other rows
are unchanged except ms.
2026-11-13 21:48:57 -08:00
dotCooCoo 1b268a725e 3D: a render with a shadow builds each solid once
With Shadows on, Extrude & Bevel, Inflate, Rotate and Revolve built
every solid twice: once for the art and again for the shadow. The build
that draws a solid now also keeps the faces its shadow is cast from and
the projection they were built with, and the shadow is cast from them.
The art and the shadow are unchanged.

- three-d: the solid builder can keep the projected facets and the
  view-space faces together (`Keep::Both`); `extrude_shadowed`,
  `plane_shadowed`, `inflate_shadowed` and `revolve_shadowed` return
  both, and `ShadowCaster::add_solid` adds the kept faces. Revolve's
  sweep draws the surface and keeps the shadow's faces in one pass; a
  shadow past its limits still gives the same error while the surface
  draws.
- effects: the solids drawn hand their faces to the shadow. A stroke's
  solid left out of the art is still built for the shadow alone, as
  before.

Tests compare each solid and its shadow with the two built apart, to
the bit, and count the solids built for a shadowed object.
2026-11-13 21:48:53 -08:00
dotCooCoo 396796ff9b Type: the Every-line Composer sets the visible lines of a mixed-size paragraph that overflows a shaped frame
A paragraph whose characters differ in line metrics, in area type that is
not a rectangle or that wraps around an object, was broken line by line
with the Single-line Composer when the frame could not hold all of it.
The lines the frame holds are now composed with the whole paragraph: a
line the frame has no room for, and every line after it, is measured as
wide as the span of the last line set in the frame, less its indents.
Those lines overflow, or go on in the next threaded frame, which composes
them again at its own widths. A paragraph whose first line has no room,
and a search that passes its limits, still fall back to Single-line.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-13 21:46:30 -08:00
dotCooCoo b39bfbed4f Export Selection: the picked format's options dialog follows the save panel
document.exportSelection run with no params or {} (a shortcut, or an
automation call) wrote the picked file straight away with each format's
defaults: a PDF used the default preset, so Save PDF's options, such as
writing long pages in points, could not be chosen. It now takes Export
As's route after the save panel: Save PDF without its Artboards
section, SVG Options without its artboards, the raster options, EPS
Options, DXF Options, Text Export Options with Selection Only on, or an
exporter plug-in's parameters. OK writes the selection alone, cropped
to it, with the chosen options. While a dialog is open the call returns
null, and OK returns what file.exportAs returns, without format or
bounds.
With a path or any option it still writes straight away.

- file.exportAs takes exportSelection: true and then writes the
  document that document.exportSelection writes
  (fileio::selection_doc), without artboard choices. The options
  dialogs and the replace-file questions pass it on.
- Nothing selected stops before the save panel.
- SVG Options opened by Export Selection leaves the remembered Use
  Artboards as it was.
- Exports through file.exportAs (Export As and the options dialogs)
  apply Appearance of Black's rich black, as document.export does.

Docs: docs/mcp.md, docs/control-protocol.md, docs/target-app-parity.md.
2026-11-13 21:44:06 -08:00
dotCooCoo 8fb2897c36 i18n: Ukrainian translates 114 messages that no catalog had a row for
The untranslated report listed 167 status and error messages that none
of the 23 complete-message catalogs translates. The Ukrainian catalog
now has an @msg row for 114 of them: the errors of the image encoders,
color profiles, swatch exchange files and color books, 3D and Revolve,
path operations, DXF import, the script engine and the parameter
checks. Placeholders, backticked names, accepted values and limits stay
as the English writes them. Open #7211, #7378 and #7405 translate the
other 53.
2026-11-13 21:31:32 -08:00
dotCooCoo 81b79ae67d Swatches: a gradient stop unlinked from its spot swatch also drops its plates
A gradient stop that loses its link to a spot swatch kept the process
plates it printed with that ink, so it carried a DeviceN mix with no
ink. Paint::map_links, which deleting a swatch, recoloring and color
adjustments go through, now clears a stop's plates whenever it leaves
the stop without a link, as GradientStop::set_color(color, None)
already does. Library gradients and library character styles linked
to a swatch the document lacks unlink their stops the same way.

Docs: docs/target-app-parity.md.
2026-11-13 21:30:14 -08:00
dotCooCoo d57cc3896f Docs: the complete-message language lists match COMPLETE_MESSAGES
The development guide and the progress doc now name all 23 languages in
COMPLETE_MESSAGES: Hindi was missing from the first, and Georgian, Hindi,
Turkish and Catalan from the second. The localization summary adds
Bosnian to the languages marked full that are not in the list.
2026-11-13 21:28:53 -08:00
dotCooCoo f4fdf2755e i18n: Brazilian Portuguese translates 114 messages that no catalog had
The untranslated report listed 167 status and error messages that no
complete-message catalog translated. pt-br.tsv now has an @msg row for
the 114 of them that no open pull request already translates: messages
of the 3D and Revolve effects, the image encoders, color profiles, swatch
exchange files, color books and DXF import. Placeholders, parameter names
and literal values stay as the English writes them.

The other 53 (composite font, SVG filter, read root and graph design
messages) are translated by open #7211, #7378 and #7405; adding them here
too would make bundled_catalogs_are_consistent fail on a duplicate row
once those merge.
2026-11-13 21:27:20 -08:00
dotCooCoo a903feb870 Dimension tool: no construction guides through a first point off the anchors
With Smart Guides on, the Dimension tool's second point snapped onto the
construction guides through the first point (Smart Guides › Construction
Guides), so a click beside a segment straight below a first point that
was not on an anchor stayed on the vertical guide instead of landing on
the segment.

The second point now has no construction guides through a first point
that is not on an anchor. After a first point picked on an anchor, the
construction guides through it still apply. The second point still snaps
to anchors, centers and alignment lines, and with Snap to Perpendicular
or Snap to Tangent on it lands where the line from the first point meets
a segment square or grazes a curve.

Docs: docs/dimensions.md, docs/target-app-parity.md.
2026-11-13 21:26:04 -08:00
dotCooCoo fb48216aa2 Image Trace: clean flat art of 10 to 16 evenly spaced levels traces as flat color
The continuous-tone check exempts a neutral or single-curve picture when
sixteen colors leave at most a fifth of the error of eight, but only
applied the exemption below the 0.1 ratio. Clean gray, sepia, single-hue
and duotone art of 10 to 16 evenly spaced levels lands above it, so it
was smoothed before tracing. The exemption now applies in both branches.

At 16 Colors, 12 sepia and 16 gray or sepia levels crossed by 1 px lines
keep every region the lines cut, and Automatic traces 16 clean sepia
levels in 9 colors instead of 4. Art in several unrelated colors is
unaffected.
2026-11-13 21:23:00 -08:00
dotCooCoo 6bd6755283 Effects: a clipping path's geometry effects shape the clip on the canvas
A clipping path with Roughen, Transform or another effect that reshapes
a path drew its own fill and stroke reshaped, but the canvas and raster
export still clipped the art by the plain path. SVG and PDF export bake
the effect into the path before writing the clip path, so the screen
and a saved SVG showed different art.

effects::clip_outline now applies the live geometry effects in the
clipping path before taking its region: the path's or compound path's
own effects, the effects of its fills and strokes (the region is then
the union of the paths they give, as an export bakes them), the effects
of a compound path's paths, of a group's visible members and of a
nested clip group's clipping path, shown or hidden, and a group's own
effects as an export evaluates them. Raster effects such as Gaussian
Blur leave the region as it is. The renderer, the engine commands that
measure a clip region (flatten, arrowheads, cutting) and the writers use
it; the writers receive the baked document, so their output for a path
with effects does not change.

The renderer measures a clip group by the region its clipping path's
effects give it, so a view or tile that shows only the moved part of
the clip still draws the clipped art, and it drops cached clip regions
that hold a plug-in effect when plug-ins change.

Two rules change in every export as well as on the canvas:

- When the clip group, or a group around it, has a geometry effect of
  its own, the clipping path's effects apply first and its gradients
  move with them. #7114 left the clipping path's effects live under the
  group's effect, so they applied after it.
- Type used as a clipping path keeps its own effects when a group's
  effect outlines it. #2073 and #2290 outlined it without them, to
  match the plain clip the canvas drew.
2026-11-13 21:18:18 -08:00
dotCooCoo d64ba43e50 3D: Inflate with no volume solves each puff only when its solid is taken
At a Volume of 0 % no shape puffs, so the eye does not depend on the
other shapes. Each shape's membrane is now solved as its solid is taken,
and a caller that stops at the object's face limit solves none of the
shapes after it. With any volume every puff is still solved first, since
the highest one places the eye, and the solids are unchanged bit for bit.

The shadow no longer builds the solid of a fully transparent fill or
stroke: its puff still places the eye, and at 0 % volume it is not
solved either.
2026-11-13 21:17:21 -08:00
dotCooCoo 2f8301671e Blend: stroke gradients and dashes follow a key path the blend turns
When a blend starts a key's path at another anchor or runs it the other
way, the steps already read that key's width profile and pen recordings
along the turned path. Its stroke gradient and dashes still followed the
key's own direction, so steps near that key showed them flipped or
shifted.

- A gradient along the path starts where the turned path starts and
  runs backwards on a reversed one; a gradient across the path swaps
  sides on a reversed one. Stops that reflect an even number of times
  are left as they are.
- Dashes run backwards on a reversed path, and exact dashes start where
  the turned path starts, when every subpath needs the same offset.
- Two gradients with different stop counts keep a step (two stops at
  one offset) when they are resampled to common stops.
2026-11-13 21:16:01 -08:00
dotCooCoo 39dc6c365b Type: Convert to Text and Remove Breaks on a selected story include its locked and hidden frames
After Select All in a frame of threaded text, Convert to Text skipped
the story's locked and hidden frames, even when `ids` named them, and
left a paragraph whose text started in one as a list. Remove Line
Breaks and Remove Paragraph Breaks also kept the breaks in those frames.
A selected story is one text, as it already is for Apply Bullets and
Apply Numbers and the Character and Paragraph panels, so both commands
now change every frame of the story, as one undo step.

- text.convertListToText converts every list paragraph of the story,
  locked and hidden frames included; `ids` naming a locked or hidden
  frame converts its paragraphs too. A call that changes nothing adds
  no undo step.
- addon.cleanup.removeBreaks removes the breaks in every frame of the
  selected story.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-13 21:11:10 -08:00
dotCooCoo df951caea4 Docs: $.fileName names the file being run in code eval or Function compiled
The script module docs, the script.run params text and the evidence line now
say that $.fileName in code that eval or Function compiled gives the file
being run, and the params text says that the empty name applies to code
compiled from a script given as text.
2026-11-13 21:09:02 -08:00
dotCooCoo 7abd11d19d Plug-ins: windows and processes take turns on the Plug-ins folder
Two windows or processes using one Plug-ins folder could interleave their
writes: an install from one could leave its module with the other's grant,
a grant written for one plug-in could drop one written for another, and
plug-in settings and cache changes could lose each other's keys or pass the
cache caps together.

Each change to the folder now runs in one turn on disabled.lock, the file
the disabled list was already locked with:

- plugin.install takes the turn before it reads the stored grants and holds
  it through the module, grant, put-back and disabled-list writes;
  plugin.remove holds it through grant removal and storage deletion.
- Grant writes, the disabled list, loading the folder, and the plug-ins'
  settings and cache changes read and write in a turn.
- When disabled.lock can't be locked, plugin.install and plugin.remove fail
  and change nothing, plugin.setEnabled keeps its change in the process, and
  loading the folder writes nothing to it.

Docs: docs/plugins.md, docs/mcp.md, the plugin.install and plugin.remove
params text, docs/target-app-parity.md.
2026-11-13 21:07:28 -08:00
dotCooCoo 2d70c6b0ae Learn: the tabs, skill and tutorial rows and Back run commands
The For you and Skills tab buttons, a skill's row, a tutorial's row and
Back changed the Learn panel's view directly, so the control channel and
MCP could not choose a tab, open a skill's tutorials or go back.

- learn.tab {tab: forYou|skills} shows the panel on that tab; the Skills
  tab keeps the skill and tutorial it had open.
- learn.skill {skill} shows the panel on the Skills tab with that skill's
  tutorials and returns the skill as learn.list lists it.
- learn.back {} goes up one level, as Back does.
- A tutorial's row runs learn.show.
- The commands are listed in both UI command catalogs, and their labels
  have a row in every catalog.
- Docs: docs/mcp.md, docs/target-app-parity.md, docs/ui-parity.md.
2026-11-13 21:06:48 -08:00
dotCooCoo cf250db2e8 Paths: Reverse Path Direction turns the stroke's width profile and pen records with the path
Reverse Path Direction reversed the anchors but left the stroke's width
profile, pressure, tilt, bearing and rotation running the old way, so a
tapered or pressure-drawn stroke changed shape. They now turn with the
path through the blend code's stroke reorientation (the width profile's
sides swap), and the stroke draws as before. A path whose subpaths do
not all turn alike keeps its stroke as it was, as blends do. Arrowheads
stay at the path's start and end.
2026-11-13 21:05:44 -08:00
dotCooCoo 3beda53fd0 i18n: French row counts match the branch after the merge of main
The French row of docs/localization-parity.md gave the counts on 92594378a. After main was merged in, fr.tsv holds 9,166 rows, 3,436 of them @msg and 48 @msgplural, and the untranslated report lists 178 missing French messages: 11 for French alone and 167 that main added for every language.
2026-11-13 21:03:26 -08:00
dotCooCoo ab0ae9f880 Type: area type insets stop at the frame's edges, not at an edge its contours share
An edge that two contours of an area type frame share lies inside the
frame, and lines already run across it. Inset Spacing still kept text
clear of it, so a frame of contours that meet along an edge left a gap
of twice the inset at the seam. The inset now applies only beside the
stretches of edges that bound the frame, counted by parity as the line
spans are. A negative text wrap offset no longer shrinks a wrap object
beside an edge two of its contours share.
2026-11-13 21:00:03 -08:00
ruchirguitar 7929c95ce9 #6613: guard web Gujarati selection and address local review 2026-11-14 10:29:25 +05:30
dotCooCoo 15bf65f014 Merge main into fix/issue-6784 2026-11-13 20:54:25 -08:00
dotCooCoo 5750843d73 Links: placed images in stored brush art get Links rows
Linked images and placed documents in the art of the stored brushes (a
scatter or art brush's art, a pattern brush's tiles) are listed in the
Links panel after the other stored art, one row per linked or placed
object in each brush tile, with the brush as owner
(`{kind: brush, name, index, tile?}`). Check Links, the missing and
modified files reported on open, the link watch, Find in Folder, Relink
(including Relink All Instances), Update, Embed, Placement Options, Link
Info and Go To act on them, and their tooltip names the brush.

- vectorcraft-doc: `Document::mounted_art` holds art kept outside the
  document's trees while a command looks it up by id; the stored-art
  lookups, link visitors and `art_roots` include it, and
  `number_mounted_art` gives its objects ids of their own.
- vectorcraft-brush: `stored_art::StoredArt` mounts the art of the
  brushes that hold images or placed documents and writes back only the
  entries whose art changed. Entries that do not read as a brush are left
  as stored. `brush::store` keeps ids of their own in that art, so a row
  keeps its id between commands.
- vectorcraft-engine: the links commands, opening a document, saving a
  placed document's source, output of linked files and placed documents,
  color masks after Update or Relink, and Package relinking go through
  the mounted art.

Brush art rows follow the general Links rule as VectorCraft's own
application of it.
2026-11-13 20:51:54 -08:00
dotCooCoo edee68e3d1 EPS import: Ryumin-Light-H and GothicBBB-Medium-V read as Japanese type without their CIDFont
A name ending in the row-and-cell CMap H or V split only when the file
defined the CIDFont or a face of that name was installed, so
/Ryumin-Light-H findfont in a file that only names it read its strings
byte by byte as a plain font: <467c> came in as "F|" instead of 日.
Ryumin-Light and GothicBBB-Medium, the Japanese CIDFonts a printer
carries (Adobe Technical Note #5014, appendix A.2), now split at H or V
as well. Latin names such as Foo-Bold-H still stay whole.
2026-11-13 20:48:18 -08:00
dotCooCoo 5b24c0a55e Actions: Batch errors name the step by its place in the commands sent
action.batch drops Open steps before it plays each file or data set, and
returned the per-file error of command.batch unchanged, so `batch step N`
counted from 0 within the filtered steps. A failed step is now named
``action step N (`id`) failed: reason``, N being its place in the
`commands` the caller sent, from 1, with skipped Open steps counted.
The batch's own open, data set choice and destination steps keep their
unnumbered errors, and command.batch messages are unchanged.

A prefs.import step, which command.batch refuses when it reaches that
step, rolling the batch back, is now refused before any file opens
instead of failing each file or data set the batch plays.

Docs: action.batch params text, docs/mcp.md, docs/target-app-parity.md.
2026-11-13 20:47:50 -08:00
dotCooCoo cf7a72e21b Type: the Single-line Composer measures a line's room across its tallest character
In area type of a shape other than a rectangle, or with text wrap, the
Single-line Composer placed each line at the metrics of its first
character and broke it in the room there. When a later character on the
line was taller, the baseline moved down by the difference without
measuring the room again, so the line could reach into a narrower part
of the frame or into a wrap object.

- A line that turns out taller than it was placed is placed again at
  metrics that hold both and broken again in the room there.
- When that leaves the taller character for the next line, the line is
  set at its own metrics where the room there is no narrower, and takes
  the words before that character that fit there. Otherwise it stays
  where it was placed.

Tests: crates/text/src/tests/combos.rs (a stepped frame and text wrap;
the two mixed-size tests now expect the Single-line Composer to break
as the Every-line Composer does there).

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-13 20:46:39 -08:00
datatimp 2610d97cd4 Layers: a plain click on another row lets go of the selected guide (#7340) 2026-11-13 23:44:33 -05:00
dotCooCoo 0263b4af13 Merge main into fix/smooth-keeps-closed-start 2026-11-13 20:38:29 -08:00
dotCooCoo 702b863306 .ai import: reads Kinsoku, Kinsoku Shori Type, Burasagari and Bunri-Kinshi
Paragraphs in a modern .ai text document now keep their East Asian line
breaking settings: the Kinsoku set (None, Hard or Soft), Kinsoku Shori
Type, Burasagari and Bunri-Kinshi. Each comes from the paragraph, else
its paragraph style or a style that one is based on, else the document's
defaults, as the other paragraph settings do. A value that cannot be read
takes the paragraph style's value.

A paragraph whose Kinsoku set is a custom set takes its paragraph
style's set, and the import warns that custom Kinsoku sets were left out.
.ai export does not write these settings.
2026-11-13 20:36:17 -08:00
dotCooCoo 3ea1524618 Image Trace: Flat Logo traces a gray between black and white as one shape
With a gray among the colors, an anti-aliased edge pixel of black on
white is also a mix of black and gray, or of gray and white, and the
unmixing took the mix with the smallest error, which often held the
gray. A black disc and a gray square on white traced the gray as the
square plus 8 to 24 slivers around the disc, with the colors found or
named exactly.

When another mix of two classes also reproduces an edge pixel, the pixel
now takes it if that mix's classes lie nearer in the flat areas around
the pixel and the pixel has a neighbor on each side of the left-out
class, along the line between the other two. Pixels within a few levels
of a class outside a flat area are checked the same way, and they now
count toward the number of pixel values that decides when a busy image
is averaged down further. A flat area that stands for no class, as with
colors named some levels off, counts as holding the classes of its mix,
and so do the pixels of exactly its color joined to it. Whether a
hard-edged image is softened is still judged from each pixel's best mix.

The slivers are removed around sharp edges and edges blurred by up to
about half a pixel; an edge blurred by a pixel or more, or pixel noise
of a level or more, can still leave some. Gray strokes two and three
pixels wide beside black stay gray, and so does a one-pixel gray line
between black and white of at least 13 joined pixels. These trace as
black and white: a shorter one-pixel line, or one that falls between
pixels or is blurred; a soft-edged gray stroke about two pixels wide
touching black; and most or all of a soft-edged stroke about three
pixels wide touching black, 12 pixels long or less.

Docs: docs/image-trace-adjustments.md, docs/target-app-parity.md.
2026-11-13 20:32:14 -08:00
ruchirguitar f965fd6f4a Merge branch 'upstream/main' into feat/layers-row-context-menu 2026-11-14 09:58:09 +05:30
dotCooCoo f9b1c2504f Smooth tool: measure twice the reach along a curve that leaves without a handle
Near an anchor whose handle does not reach out, the curve starts more slowly than its control polygon, so the finely cut stretch could end before twice the reach and the turn there was measured along a long chord. The stretches past it are now cut as finely until the pieces reach three times the reach. docs/mcp.md says the one-eighth cap applies to a closed path the drag reaches all the way round.
2026-11-13 20:28:08 -08:00