Object › Flatten Transparency replaced a frame of threaded text by its
outlines with Convert All Text to Outlines, and by flat regions or an
image where the frame was transparent. The thread's re-flow then added
the frame's text to the story, as it does for a frame that was deleted,
so the other frames held that text a second time while the outlines,
regions or image drew it.
A frame that Flatten Transparency outlines or flattens now leaves its
thread with the text it shows, and the other frames keep the text they
hold, as with Create Outlines and Expand. When the thread's last frame
is flattened, the text it has no room for goes after the text of the
last frame that stays. A thread left with one frame is no longer a
thread, and undo restores the thread.
Docs: docs/mcp.md, docs/target-app-parity.md.
A merge removed every piece of an open line that had a merged region on
both sides. The end of a line that stops inside a shape has the same
region on both sides, so a click or a drag on the shape, or deleting it
in erase mode, cut the line back to where it enters the shape.
A piece is now removed only when it runs between two different merged
or deleted regions. A line that crosses a shape still splits it, and
merging the two halves still removes the piece between them.
A drag tested points spaced by a step that grew with the selection (at
least 0.25 pt), and the tool's highlight tested points 2 screen pixels
apart. A region narrower than the step, such as a thin overlap, fell
between samples: it kept its piece of each source, the merge left strips
behind, or the drag failed with "no region under the pointer" while the
highlight showed the region. Zoomed out, the highlight could miss a
region the merge then took.
The drag now finds where each segment crosses a region outline and tests
the middle of every stretch between crossings, so every region it
crosses is picked. The tool's highlight and shapeBuilder.merge use the
same picking, and Shaper's scribble picks the faces it crosses the same
way. In erase mode the line pieces and edges are still found within the
tolerance, and a segment that crosses one also tests the crossing point.
Shaper's scribble found the strokes to erase at points at least 0.5 pt
apart, so with a tolerance below 1 pt it could pass over a stroke
without erasing it. It now also tests where the scribble crosses each
stroke and points tolerance / 2 apart.
`open -a` and PowerShell's `&` return before the app opens its control
port, and `vectorcraft-cli mcp` tries the port only once, so the docs
now say to wait for the VectorCraft window before starting the MCP
client. The `--connect` failure is documented as the full line the CLI
prints, `vectorcraft-cli: cannot connect to 127.0.0.1:7979: ` followed
by the system's connection error.
A paragraph split between two threaded frames took its first-line
indent again at the top of the next frame, and a list paragraph drew
its bullet or number there again. Each frame is laid out on its own,
and its first paragraph kept every paragraph attribute there.
A text object now marks a frame whose first paragraph continues from
the frame before (first_para_continues, not saved). The thread re-flow
sets the mark where each frame's share starts inside a paragraph and
measures each share with it, and a restored document reads the marks
from its saved text. Area type and type on a path set the first line of
such a paragraph as a wrapped line: no first-line indent and no bullet
or number. The paragraph after it takes the next number. Release
Selection, Remove Threading, Convert To Point Type and copies of a
frame clear the mark, so the first paragraph there has its indent and
bullet or number again. When Create Outlines, Expand or Expand
Appearance takes frames out of a thread, a frame that stays keeps the
mark only while it is threaded and the frames before it end inside a
paragraph. A frame whose mark changes lays out its first line again,
and when it is still threaded, its thread re-flows. When the outlined
frame is the last one, the text it has no room for is measured as the
frame lays it out: without the first-line indent when it continues a
paragraph, and with its numbers counted on from the frames before it.
The frame before it now takes exactly the text the last frame has no
room for. Duplicates, copies dragged in the Layers panel, the copies
that Release to Layers (Build), Release Opacity Mask and Edit Opacity
Mask make, and Draw Inside's clipping copy then have their cached
bounds computed again, as paste already does for pasted type.
Docs: docs/mcp.md, docs/target-app-parity.md.
pdf_real_text_draws_svg_glyphs_as_art_and_keeps_their_characters counted
every line containing Tj or TJ. The file ID in the trailer and the XMP is
base64 derived from the timestamp, so about one run in a hundred it spells
one of them and the test saw two extra lines (main CI, 5c1185dd). Only
lines that begin a text object count now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Shape Builder, Live Paint Bucket and Live Paint Selection keep the
selection's region map between pointer events, keyed on the document's
address and the selected ids. Every edit replaces the document, and after
an undo followed by an edit a later document can be allocated at the
address of the one the map was built from. The key then matched a map of
an earlier revision, so a drag across the art merged nothing and a Live
Paint Bucket click filled nothing.
The key now also holds the tool context's revision, which moves with every
edit, undo and selection change.
A Paintbrush sample dropped for being under a pixel from the last point
left the ring computed from the pointer, while the release keeps the last
point; the ring now follows that point (CodeRabbit).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Pencil or Paintbrush stroke released within Close Paths When Ends Are
Within of its start closes, but the cursor gave no sign of it. While the
pointer is where a release would close the stroke, the cursor is now
PencilClose (PaintbrushClose for the Paintbrush), the crosshair with a
ring, as Draw straight lines with the Pencil tool describes:
https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/draw-shapes/draw-straight-lines-with-the-pencil-tool.html
The check is the one the release makes, on the end the stroke would
take (snapped for the Pencil). A straight line (Shift or Alt held at the
press), a stroke that continues a selected path, a stroke that redraws
one and a smoothing drag show no ring.
Docs: docs/target-app-parity.md, docs/ui-parity.md.
Without a loader, check() scanned the script once, so a function using a
variable the global code assigns after it (`function g(){ f.write('x'); }
var f = new File('a');`) missed file.write, while the loader path found it.
The no-loader path now runs the same fix-point, keeping a scan that finds
nothing new as the report scan, so a script that includes nothing gets the
same report with and without a loader.
Fixes#4011
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found while testing crates/doc/src/xml_id.rs:32 (from_object_name), decoded by escaped() at :72
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Within about a minute of its window no longer being visible, macOS App
Nap lowers the app's threads to the lowest priority and coalesces its
timers. On a loaded machine, control requests then took seconds to
answer, and the 60 s timeout reply arrived up to 26 s late.
When the control channel is on (--control or VECTORCRAFT_CONTROL_PORT),
the app now holds an NSProcessInfo activity (user initiated, allowing
idle system sleep, latency critical) for as long as it runs, so App Nap
does not apply. The activity does not keep the Mac awake.
docs/control-protocol.md states this in the paragraph on the 60 s
timeout.
Move the declaration pass, the significant-token helpers and the feature
recognisers into analyze/declared.rs, sig.rs and features.rs without
changing their code, and replace the duplicated line-break and directive
dispatch of scan and declarations with LineBreaks::read.
Part of #4014
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
FeatureId and OutsideId replace the string ids the analyzer passed around.
One table each gives the id, the need and the report order, so
catalog::FEATURES and catalog::need() are gone. Feature.id and the engine
JSON are unchanged; OUTSIDE is generated from the same table.
Fixes#4013
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Work out the unescaped text and the declared names of each file once and
share them between the scans, keep included files as bytes and decode
them on demand, check the cheap tokens before unquoting in the '=' case,
and stop allocating a String per recorded chain.
Fixes#4010Fixes#4012
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/cad/src/import/text.rs:14-27 (mtext calls escapes at :14, which treats `^2` as a caret escape and drops it; the `\S` branch at :25 expects the `^`)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The server lists two names for several calls (command_list and
list_commands, command_run and run_command, doc_inspect and
inspect_document, ui_inspect and inspect_ui, ui_screenshot and
screenshot with window:true), but no description said so, and the two
listing and running pairs take different argument names. Each
description now names its pair and the argument that differs, and undo
and redo name each other.
ui_screenshot took no arguments and returned the window at its full
size, which can be several megapixels on a large display.
ui_screenshot and screenshot {window: true} now take an optional
max_side (1..4096) that scales the returned image down to that many
pixels on its longer side. The reply's text gives the returned size as
width/height and the capture's as capturedWidth/capturedHeight; a file
written to path keeps the full capture. max_side on an artboard render
is an error that points to scale. render_preview reads its max_side
through the same check.
docs/mcp.md describes the pairs and the new argument.
The planar map's `RectIndex` sorted rectangles narrow in x by their left
edge and those narrow in y by their top edge, and scanned the rest for
every lookup. A long diagonal segment is narrow in neither, so a hatch of
diagonals put every line in the scanned list and `Outlines::source_at`
(one lookup per edge) cost a scan of the hatch per edge: quadratic in the
hatch below the 65,536-segment cap, as CodeRabbit found on #3357.
`RectIndex` is now a grid of about one cell per item, each cell listing
the items touching it; a lookup reads the cells around the point. An item
is filed under a footprint of rectangles: its bounds (`new`), or, for a
segment, the bounds of its pieces about a cell long (`with_footprint`,
`segment_footprint`), so a diagonal is filed under the cells along it,
not under every cell its bounds cover. A footprint rectangle covering
more than 64 cells or two rows/columns (a face covering most of the art)
is scanned for every point as before; a thin stripe face is filed under
its column of cells. Cells hold `u32` indices.
Tests: `rect_index_reads_a_diagonal_hatch_only_near_the_point` (2,000
diagonals a tenth of a point apart: a lookup got 1,001 candidates with
the old index, now at most n/4, and still every line through the point;
a hatch of stripe faces is filed, not scanned; a collinear, degenerate
grid). `rect_index_finds_every_rectangle_holding_a_point` keeps the
bounds-only contract.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gCpjL3yUhaH3jRp7ksufh
A document opened or created between edit.undoGroup.begin and
edit.undoGroup.end opens an undo group of its own with the begun
group's name, so an atomic command_batch that starts with file.new or
document.open is one undo step in the new document. The group opens
after the command that opens the document: when that command also
edits it (a command.batch), those edits stay the command's own undo
step, and a canceled group keeps them, so the journal still replays.
Both commands also run with no document open, and a command.batch that
fails rolls back a begun group. Switching to a document that was
already open does not open a group in it.
The outlines of a run's character stroke are kept with the glyph geometry the renderer keeps for the type, in a byte-limited cache swept by frame, so type that changes in every frame (an animation) frees them within a few frames.
The installed-release section of docs/mcp.md said how to register the
server but not how to start an installed app for a live session; the
only start command in the setup steps was the cargo run from a
checkout.
It now gives the command that starts the installed app with
--control 7979 on macOS, Windows and Linux, and says that the
launchers open no control port, that the flagless server checks for
the app once when it starts, and what --connect prints when no app
is listening.
When Flat Logo finds the colors itself (no colors named), a flat bar
two pixels wide (whole pixels of one color) on a ground beside other
art was lost or traced in another color. No bar pixel has a uniform
3 x 3 neighborhood, so neither the first clustering nor the check for
unexplained colors took it. A gray bar on white beside black art was
traced as white, and a red or green bar was traced as black. Bars
three pixels wide or more were already found.
The check for unexplained colors now also takes the pixels of such a
bar: two rows or columns of one color (within about 3.5 in RGB
distance), three pixels long, with the same ground on both sides. They
count when the known colors do not explain them, and also when their
color lies less than two thirds as far from the ground's known color
as from every other known color, where the bar would otherwise be
traced as the ground. A gray bar darker than about #9B9B9B on white
beside black art stays black, #808080 included, because a black stroke
1 to 1.6 px wide on the pixel grid gives the same pixels. Named colors
are unchanged.