VectorCraft had no lists. A bullet could only be typed as a character,
and the tab after it did not stop at the left indent. The reference app
turns paragraphs into a bulleted list from the Paragraph panel, the
context menu and Type › Bullets and Numbering › Apply Bullets.
text.setFormat takes list ("none" or "bullet") and bullet ("none",
"dot", "dash", "circle" or "square"). The layout draws the bullet before
the first line of each list paragraph that has text, in the font, size
and color of its first character, and starts the text at the next tab
stop, with the left indent counted as a stop. Wrapped lines start at
the left indent, and type on a path draws the bullet too. In a
horizontal right-to-left list paragraph the indents are mirrored: the
bullet hangs at the right, and the wrapped lines start at the right
where the first line's text starts after the bullet. The bullet is not
a character of the text. A paragraph whose left and first-line indents
are both 0 gets 18 pt and -18 pt when it joins a list. Return continues
the list, paragraph styles carry it, Reset Panel turns it off, and the
native file saves it only when it is set.
The Paragraph panel has a Bullets button with a list of the five
presets, and Type › Bullets and Numbering › Apply Bullets and the
context menu run type.applyBullets. Live SVG text writes the bullet's
character, and SVG writes type on a path with bullets as outlines. DXF
writes the bullet as a TEXT entity of its own, and legacy .ai export
warns that bulleted lists are left out. The caret, selections, Touch
Type and Show Hidden Characters skip the bullet.
Live SVG text of horizontally scaled type now writes each tab in a
tspan of its own. Before, the characters before a tab shared its tspan,
and its textLength made a viewer stretch them across the tab's gap.
Docs: docs/mcp.md, docs/target-app-parity.md.
Review follow-up (CodeRabbit): each thumbnail hashed every symbol's and pattern's art before its
texture cache lookup, so a panel frame cost visible tiles × (symbols + patterns) even when every
thumbnail was cached. The Symbols panel works the key out once per frame, a symbol library panel
once per library it shows, and the dragged chip once; thumbnails take it as given.
Review follow-up (CodeRabbit): the library panel test only checked the drag payload. The new
canvas test drops PanelDrag::LibrarySymbol over art and checks that the symbol joins the
document's Symbols, an instance is placed with its centre at the drop point in document
coordinates and at its natural size, as one Add to Symbols undo step that undo takes back.
Review follow-up (CodeRabbit): thumbnails rendered the symbol alone in an empty document, so a
symbol nesting another one, or painting with a pattern or showing an image, lost that art in the
Symbols panel and in symbol library panels. The preview now renders an instance alone on a copy of
the document (or library) the symbol comes from, which keeps its resources but none of its art,
and the thumbnail cache also follows the art of the document's symbols and patterns.
Window › Symbol Libraries and the Symbols panel's Symbol Libraries Menu open each symbol library
in a library panel of its own, on the shared library panel code the swatch and graphic style
libraries use (https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/create-or-import-symbol-libraries.html,
.../symbols-panel-options.html, .../create-and-place-symbols.html).
- Basic Shapes: one original library generated in code (circle, squares, triangle, diamond,
hexagon, star, burst, arrow); no other app's libraries.
- A click adds a library symbol to the document's Symbols as one undo step, with its Symbol
Options and the resources it uses (nested symbols, patterns, swatches, brushes, graphic styles,
images), and makes it the current symbol; one already there isn't added again, a clash of
names numbers the library's. Shift/Cmd-clicks select for Add to Symbols. Dragging a library
symbol onto the canvas adds it and places an instance there, in the same step.
- Save Symbols… (library menu, Window menu) and Save Symbol Library… (Symbols panel menu) write
the document's symbols as a .vectorcraft document holding only them, into the user library
folder (User Defined, window.userSymbolLibrary1-10) or a file; Other Library… reads the symbols
of any document VectorCraft opens, and a file it can't read is an error.
Commands: symbol.libraries, symbol.library, symbol.addFromLibrary (place, x, y),
symbol.saveLibrary, symbol.loadLibrary; UI: window.symbolLibrary (.other), ui.saveSymbolLibrary
(dialog saveSymbolLibrary). i18n rows in every complete catalog; docs updated.
The hover's context now includes the active tool (PR review follow-up):
switching to the Selection tool with Alt held and the pointer still
showed no Distance Guides until the pointer moved, since the new tool
had never hovered. A tool switch re-hovers at the pointer whatever the
tool; the rest of the context (canvas, view, document and revision)
re-hovers tools that ask (Tool::wants_rehover).
canvas::tests::distance_guides_follow_what_lies_under_a_still_pointer
adds a tool switch (fails without the rule), a modal dialog with its
backdrop over the pointer, and another document opened under it.
One rule for the hover's context (PR review follow-ups): the target is
recomputed where the pointer is, or cleared, whenever what lies under it
can change while Alt is held.
- The tool hovers only where the pointer is on the canvas itself
(`on_canvas`): under a floating panel, a menu, a popup or a dialog
its hover ends (Tool::hover_ended), as when the pointer leaves the
canvas or a pan, zoom or view-rotation press starts.
- The canvas keeps the hover's context: its rect, the view's centre,
zoom and rotation, and the active document's id and revision (which
moves with every edit, undo and selection change). When it changes
under a still pointer, a tool that asks (Tool::wants_rehover; the
Selection tool after an Alt hover) is hovered again at the pointer,
so wheel, gesture, key or command scrolling and zooming, tab
switches, edits and undos re-measure.
- Coming back onto the canvas re-hovers the tool.
canvas::tests::distance_guides_follow_what_lies_under_a_still_pointer:
a floating window over the pointer (fails without the on_canvas filter),
a 150 pt scroll that leaves empty artboard under it (fails without the
re-hover), a square made under it. select::tests::distance_guides_need_
alt_a_selection_and_smart_guides checks wants_rehover.
An Alt+Space pan press went to Drag::Pan and never reached the tool, so the Distance Guides stayed through the pan until the pointer left the canvas (PR review follow-up). One rule now covers all three cases: the tool's hover ends (Tool::hover_ended, renamed from pointer_left; Session::tool_hover_ended) when the pointer leaves the canvas with no press the tool holds, and when a press starts any drag that doesn't reach the tool (pan, middle-button pan, zoom box or scrub, view rotation, touch gesture). canvas::tests::distance_guides_go_when_the_hover_ends asserts they go at an Alt+Space press and stay gone panning out over a panel.
The pointer-left check skipped every drag, so Alt+Space panning out over a panel kept the measurements (PR review follow-up). Only a press the tool holds (Drag::Tool, Drag::Art) keeps its state now; pan, zoom box, zoom scrub, view rotation, gesture and presentation drags never reach the tool. canvas::tests::distance_guides_go_when_the_pointer_leaves_the_canvas pans out with Alt+Space; it fails with the old drag.is_none() check.
Leaving the canvas (for a panel, or out of the window) sent the tool no
hover event, so the Selection tool kept its Alt-hover target and went
on drawing the measurements (PR review finding).
- Tool::pointer_left: the pointer left the canvas with no button held;
the Selection tool drops its Distance Guides target and its Smart
Guides hover label. Session::tool_pointer_left reaches it.
- The canvas remembers whether the pointer was over it last frame: on
leaving with no drag it calls tool_pointer_left; on coming back it
re-hovers the tool even after a single jump egui doesn't count as
moving, so the guides show again.
Tests: canvas::tests::distance_guides_go_when_the_pointer_leaves_the_
canvas (fails before: the label stayed over a panel with Alt held),
select::tests::distance_guides_need_alt_a_selection_and_smart_guides
(pointer_left clears them).
Distance Guides, from the public "Use Distance Guides for accurate
placement" and "Smart Guides options" (Invoke Distance Guides) pages:
with Smart Guides on, something selected and the Selection tool,
holding Alt/Option over another object shows the horizontal and
vertical distances between the selection and it, and over an empty
part of an artboard the selection's distances from the artboard's left,
right, top and bottom edges.
- guides::distance_guides measures the selection's bounds to a box: on
an axis on which they lie apart, the gap between their facing edges;
otherwise the offsets of both pairs of matching edges. Each line runs
across the middle of the overlap on the other axis (else the
selection's middle), with a dashed line out to the target's edge when
it passes beside it. An artboard is always measured edge to edge.
Labels are in the General unit, in the smart guide colour.
- The Selection tool keeps what its Alt hover points at and draws the
guides while idle; letting Alt go (the canvas re-hovers on a modifier
change), a press or moving off clears them.
- Overlay::Distance: the line with end ticks and its length in a pill;
the canvas draws it.
- Preferences › Smart Guides › Invoke Distance Guides (`distanceGuides`,
on by default), with i18n rows; View › Smart Guides gates it too.
- Hover only, as the pages describe: nothing while moving or nudging.
Tests: select::tests::alt_over_* (gap and edges beside and below,
artboard edges in pt and mm), distance_guides_need_alt_a_selection_and_
smart_guides, tests_prefs::invoke_distance_guides_measures_the_selection_
to_the_object_under_alt, canvas::tests::alt_over_another_object_draws_
the_distance_guide_label (frame test of the drawn label and line).
The Pathfinder panel menu's Trap… (object.pathfinder.trap) and Effect › Pathfinder › Trap… (pathfinder.trap, live on groups) share one geometry (vectorcraft_pathops::trap_band, vectorcraft_effects::trap): Thickness, Height/Width, Tint Reduction, Traps with Process Color and Reverse Traps, plus the effect's Precision and Remove Redundant Points. The panel's Trap is one undo step and groups the traps above the topmost selected object. Behaviour from https://helpx.adobe.com/illustrator/using/trapping.html and https://helpx.adobe.com/illustrator/using/pathfinder-panel-overview.html.
Review follow-up (CodeRabbit): -80 pt at 0° drew the line leftwards but
kept 80 pt at 0°, so the next click drew it rightwards. Both now use
80 pt at 180°.
Step 3 of the Rectangular Grid tool and Arc tool help pages
(https://helpx.adobe.com/illustrator/using/tool-techniques/rectangular-grid-tool.html,
https://helpx.adobe.com/illustrator/using/tool-techniques/arc-tool.html)
and the Tools page (https://helpx.adobe.com/illustrator/using/tools.html)
say a double-click on the tool opens its Tool Options. For these four tools
the double-click (tool.options, and Enter once #2891 lands) said the tool
had no options (#2691).
The Tool Options are the tool's click dialog in dialogs/shapes.rs, opened
without a place to draw (shapes::open_options; a `__toolOptions` field
marks it), as the review of #2979 asked: no copied form code. Its OK runs
tool.setOption and draws nothing; Cancel changes nothing. The grids' dialogs
are titled Rectangular Grid / Polar Grid Tool Options there. The options
are what the next drag draws with and what a click's dialog opens at:
- Line Segment: Length and Angle (now kept by the tool and used by the
click dialog) and Fill Line (new: shape.line {fill: true} gives the line
the current fill; the click dialog has it too);
- Arc: lengths, Reference Point, Type, Base Along, Slope, Fill Arc;
- the grids: dividers, skews and switches, plus the Default Size and
Reference Point, now kept by the tool (width, height, reference) for the
click dialog; a drag leaves them out of its command.
The grids' Reference Point is drawn only as the 9-point locator (it also
showed as a plain number field).
New strings (Rectangular Grid Tool Options, Polar Grid Tool Options, Fill
Line) have rows in all 24 catalogs.
Verification:
toolbar::tests::double_clicking_the_line_arc_and_grid_tools_opens_their_tool_options
double-clicks each tool's toolbar button: the dialog and its title, Cancel
leaves the next drag as it was, OK draws nothing, the next drag uses the
option and a click's dialog opens at it (a grid at its Default Size);
line_segment_options_and_fill_line, grid_default_size_is_an_option_the_drag_leaves_out,
fill_line_gives_a_new_line_the_current_fill. Grid dialog tests now expect
the stored Default Size. Live: double-clicking the Rectangular Grid tool in
the release build opens Rectangular Grid Tool Options; OK with 2 and 3
dividers, then a drag, draws a 3 × 4 grid.
On Linux under X11, and under XWayland, the X server lists a pen's
eraser end as a pointer device of its own, and winit 0.30 hands it to
the window as a mouse. Turning the pen over and dragging the eraser
end drew with the active tool, for example a Paintbrush stroke.
The tablet reader now marks a pen device whose name or XInput type
contains "eraser", and Services::stylus carries the pen's end with
its pressure. Once a frame while no press is held and the pointer is
in the window, the canvas selects the Eraser when the eraser end
moves, and it selects the tool used before when the tip moves again.
The return goes through Session::switch_tool, so a perspective grid
hidden meanwhile stays hidden. A tool chosen while the eraser end
stands in stays when the tip comes back. The feed starts afresh when
the pen turns over, so a press of one end does not take the pressure
the other end last touched with. Native Wayland is unchanged.
The switch follows PhotoCraft's sync_eraser_tool
(storytold/photocraft#255, and storytold/photocraft#5043 for the
cleared tool).
Docs: docs/development.md, docs/gaps.md, docs/hardware-parity.md,
docs/target-app-parity.md.
The list view gives colours and graphics a row each with the item's name (a colour's hex value too, where it fits); items drag and drop by row there. Graphic thumbnails are shrunk on the CPU to the size they are drawn at, as egui-wgpu doesn't mipmap textures and thin strokes broke up in small tiles.
Two Polish rows added this week:
- "Plug-ins & Scratch Disks" read "Wtyczki & Dyski robocze": every other "&"
in the catalog is "i", and Polish doesn't capitalize the second noun.
- "Geometric Guides" read "Prowadnice geometryczne"; Guides are "linie
pomocnicze" in the rest of the catalog (Guides, Smart Guides …), as in #2870.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DukHNvUY45rjRnvtz91x33
- Packaged … in {folder} {note}: keep the space before {note}, as the
English does, and use "Paketoitiin" in the status line like the @msg row
- Colored Pencil: Värikynä (Väriliitu is a crayon)
- Underpainting: Pohjamaalaus
- Select artwork to apply effects: use "kuvitus" like the rest of the catalog
- Arc Segment Tool Options: Kanta akselilla, X-/Y-akselin pituus
- Plug-ins & Scratch Disks: Laajennukset ja työlevyt
- Add the Finnish row for "PDF security settings are not an object" (#3836)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n tests: report missing translations instead of failing
The scans for untranslated menu strings, tl! literals, undo labels, menu labels and status
messages print what a complete language lacks. A missing translation shows the English
text until a translator adds the row, so it no longer fails the gate.
* i18n: one untranslated report in xtask ci; drop one-language wording tests
`i18n::tests::untranslated_report` lists, per language, the menu strings, tl! literals, undo
step names and templates, menu labels, render-site labels, status and error messages,
PostScript error meanings and default swatch names its catalog lacks; strings every language
lacks are listed once. It never fails on a gap, only on a scan that finds too few strings.
`cargo xtask ci` runs it in an `i18n-report` step, and `cargo xtask i18n-report` runs it alone.
The five scan tests, the render-site and swatch coverage tests and untranslated_messages.txt
fold into the report. Tests that pinned one language's wording are gone; where they also
checked code (lookups, plural rules, locale tags, menu and History step names, engine
parameter errors), that check stays and compares with the catalog lookup.
As Illustrator's help describes (Filter fonts by classification, Font Technology › Variable), the font menu gets a Font Technology row whose Variable choice lists only the families with variation axes, together with the script, kind, favourites and search filters, and stays set from one opening to the next. A family is variable when any of its faces, loaded or scanned, has an `fvar` table, so a family installed as static files beside a variable one counts.
Spec: https://helpx.adobe.com/illustrator/desktop/design-with-text/fonts-and-scripts/find-apply-and-adjust-variable-fonts.html
The Simplify help page says a path can be simplified whole or "a specific
path region with the Direct Selection tool"
(https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/manually-simplify-paths.html).
Simplify refitted every selected path whole, whatever part of it was
direct-selected.
object.path.simplify now looks at each path's direct selection: the
segments picked as segments, or else every segment with both anchors
selected. Each run of consecutive selected segments is refitted on its own
(vectorcraft_pathops::simplify_part) between its two end anchors, which stay
where they are with their handles on the side away from the run; every
other anchor and segment stays exactly as it was. A closed path's run may
cross its start point (the path then starts at the run's end anchor),
separate runs on one path simplify independently in one undo step, and the
runs stay direct-selected (as segments when segments were picked). A path
with only lone anchors selected still simplifies whole, as before, and
wholePaths: true simplifies partly selected paths whole. The slider, its
preview, Auto-Simplify and the Simplify dialog all run the command, so
they act on the part too. simplify_with now shares its run fitting with
the part refit (same results).
Verification: crates/engine/src/tests_simplify_part.rs (unselected anchors
and segments equal bit for bit, run end anchors fixed, the run within the
tolerance, two runs equal to each simplified alone in one undo step, a
closed run across the start point, picked segments, wholePaths, slider
preview and Auto-Simplify, a run that can't be simpler records no step)
and simplify_slider_and_dialog_simplify_only_the_direct_selected_part_of_a_path
(the slider's preview, Enter and the dialog's OK). Live: the release build's
slider at level 20 on a 91-anchor wave with anchors 30–60 selected refits
only that stretch.
A file opened or placed off the UI thread waited while the active tool
reported busy. The Dimension tool also reports busy between its clicks,
so after one click a ready file stayed queued with its spinner until
the dimension was finished or canceled. Files also waited between the
clicks of the Blend tool.
The session now records whether the pointer button is held. A file
waits for a busy tool only while the button is held. A file that
opens between a tool's clicks resets the tool, as a switch of
documents does; a file placed in the active document leaves the
tool's clicks in place. Files still wait while a dialog is open, a
drag is under way or a preview is open, and a tool's preview between
its clicks counts as one (the Flare tool after its center drag, the
Dimension tool's line once the pointer moves after its second click).
#3836 added the message without @msg rows, so
complete_languages_translate_every_message fails on main (cs first, then
every language in COMPLETE_MESSAGES). Each row follows the catalog's own
"PDF settings are not an object" row and its word for Security.
On Linux under X11, and under XWayland, winit 0.30 hands a drawing
tablet's pen to the window as a mouse without its pressure. Every pen
stroke reached the tools at full pressure: a Calligraphic brush with
Size on Pressure drew one width, and the Liquify tools' Use Pressure
Pen applied full intensity.
When the window is on X11, the desktop app now reads XInput2 raw
events on a second X connection (x11rb's RustConnection, without
unsafe). A device with an "Abs Pressure" valuator is a pen, unless
its XInput type is TOUCHPAD or TOUCHSCREEN. Its pressure, scaled from
the valuator's range to 0..1, goes into Services::stylus, and another
pointer moving clears it. The canvas uses that pressure when a frame
has no touch force. A press takes the highest pressure of the pen's
latest touch since the canvas last read the feed, and waits up to
5 ms for the touch of a hovering pen. The moves and the release of a
held press keep the pressure the pen last touched with when its lift
has already arrived. Native Wayland is unchanged.
The valuator mapping and the reader come from PhotoCraft's tablet
crate (storytold/photocraft#255), with the pressure axis only.
Docs: README.md, docs/development.md, docs/hardware-parity.md,
docs/target-app-parity.md.
The Artboard Options help pages describe two display options
(https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/modify-display-settings-of-artboards.html,
.../set-video-display-options-for-artboards.html): a mark at the artboard's
center, and lines from the middle of each side through the center. The
document already had show_center_mark and show_cross_hairs on Artboard, but
nothing set or drew them.
artboard.setProps (each `artboards` entry too) and artboard.new take
showCenterMark and showCrossHairs as true/false, in the same single undo
step as the other fields; a locked artboard can still change them.
Artboard Options has both checkboxes, filled in from the artboard whether
it opens from the panel or a double-click with the Artboard tool, and its
New Artboard form passes them to artboard.new. The canvas draws them in the
new theme token artboard_mark, 1 point wide on screen at any zoom: a cross
12 points across at the center whose arms follow the artboard's sides, and
the two cross hairs. They are screen-only like the edges; exports and print
never read the fields. inspect_document reports them when on. Saved files
are unchanged: the two keys were already always written, so a document that
never shows them saves byte for byte as before, and a file without the keys
opens with both off. Video safe areas, video rulers and the pixel aspect
ratio are not part of this change.
Verification: artboard_display_marks_are_set_and_cleared_in_one_undo_step,
artboard_display_marks_round_trip_through_the_native_format,
artboard_display_marks_stay_out_of_exports (SVG and PDF identical with the
marks on and off), an_artboard_shows_its_center_mark_and_cross_hairs
(positions, screen-constant size under zoom, rotated view) and
artboard_options_show_center_mark_and_cross_hairs. cargo xtask ci: all 10
steps passed.
The Simplify help page says the slider starts at an auto-simplified value
(https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/manually-simplify-paths.html).
The slider opened at a fixed level 50 (1 pt), whatever the size of the
selection, so the first preview was the same tolerance for a 30 pt icon as
for a 1000 pt drawing.
The level Auto-Simplify picks for the selection is now one engine function,
vectorcraft_engine::auto_simplify_level (the existing 1/400-of-the-longer-
side rule in vectorcraft_pathops), which object.path.simplify {auto: true}
and the slider's opening fields both use. Object › Path › Simplify (and
ui.menuDialog, the Properties and contextual task bar buttons, which go
through the same menu dialog) opens the slider there, and the canvas
previews that result; Auto-Simplify still moves the slider back to it.
The retained Simplify dialog path is unchanged.
Verification: simplify_slider_opens_at_the_auto_simplify_level_of_the_selection
opens the slider on 1140, 285 and 28.5 pt waves (levels 35, 55, 89) and
checks the preview is exactly object.path.simplify {auto: true}'s result;
without the change it fails with level 50 != 35, and
simplify_opens_a_slider_whose_drag_previews_fewer_or_more_points fails with
{"level": 50} != {"level": 55}. cargo xtask ci: all 10 steps passed.
Export for Screens and the Asset Export panel's Export encoded and wrote
every file on the UI thread, so the window stopped responding until the
last file was written. For 50 artboards in PNG at 1x, 2x and 3x, PDF and
SVG (250 files, 47 MiB), the dialog's OK took a median of 43.8 s in a
release build on Windows.
With Enable Background Export on (Preferences › File Handling), OK now
checks the settings, remembers them and takes a snapshot of the
document, and the dialog closes. The thread that writes background
saves and exports then encodes the files and writes them into the
folder, or one ZIP in it, and the export progress dialog's Cancel stops
it before the next file. The same OK takes under 0.001 s, and the files
have the same names and bytes. With the preference off, without a
folder, on the web and over MCP and scripts (document.exportForScreens
and assets.export), the files are made at once with the same reply as
before.
Both commands report one progress step per file, so the progress dialog
shows "File 3 of 250" and MCP progress notifications count the files in
place of the encoders' artboards and pages.
Docs: docs/target-app-parity.md, docs/control-protocol.md, docs/mcp.md.
A filter plug-in gets 50 M instructions and a minute for each run.
Filters that order a plotter's paths, pack shapes or run a simulation
need minutes, and the app shouldn't freeze while they compute.
A filter's manifest may now ask for "budget": "heavy" (Budget): each run
gets HEAVY_FUEL (1 G instructions) and HEAVY_WALL_TIME_MS (300 s). Only a
filter may ask. plugin.install refuses such a filter without allowHeavy:
true, and plugin.inspect returns the entry a module would have without
installing it. Install Plug-in… and File › Open ask the user and install
on OK; the plug-in store keeps the answer, and folder loads skip heavy
filters with a note, since they ask nobody.
The app runs a heavy filter on a thread of its own (plugin_runs), not in
Background Save's queue, with the status bar and a progress window
(Cancel, Continue in Background) that it shares with Background Export
(export_progress::window). FilterRun splits plugin.run: prepared on the
session, run on any thread, its output handed in, and the next
plugin.run with the same params applies that output instead of running
the plug-in again, so the edit stays one guarded, journaled command. A
result whose document or plug-in changed meanwhile is dropped, and the
status bar says to run it again. A cancelled plug-in run is now
EngineError::Cancelled.
What a filter keeps with the document (#3720) goes with the run: it is
given _context.documentData when the run is prepared, and the
documentData of its output is applied with its objects, in the same undo
step, when the run is handed in (FilterRun::finish).
Tests: plugins/tests/abi.rs (100 M loop steps out of a light budget,
within a heavy one; folders skip heavy filters), manifest.rs, engine
tests_plugins.rs (consent, inspect, the store, hand-in, a handed-in
run's document data, cancel) and
ui-egui tests_plugin_runs.rs (installing asks, no Preview, a background
run as one undo step, a stale result kept out, the progress window).
CRAFT_UI_HEAVY_FILTER_WASM=<folder> writes a test filter to try in the
app. Its strings and messages have rows in each complete catalog
(COMPLETE_MESSAGES).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HhvnnnD5fTFupK8WWq35un
each_paragraph_run skipped the empty paragraph after a closing line break, so a
selected object whose last paragraph's Auto Leading differed showed one Leading
instead of a mixed (blank) field. With no range, that paragraph now counts with
the style of the run before it. Covered by
leading_field_counts_the_final_empty_paragraph.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gCpjL3yUhaH3jRp7ksufh