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>
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>
The mesh covers the image's rectangle with its transform kept; each point takes the average colour and opacity of the picture around it. A linked image is refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A CMYK document holding RGB colours or images separates them into CMYK on export, and dark ones come out as rich blacks. The export report now says how many there are.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PDF presets are kept one file each in a PDF Presets folder, ICC profiles as copies in a Color Profiles folder, next to the preferences; the desktop app and vectorcraft-cli use them. Adds color.removeProfile.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The MCP server read one line, ran it and answered it before reading the next,
so notifications/cancelled was never seen during a call and every request
(even ping) waited behind a running export.
- vectorcraft_doc::cancel: a thread-local cancel token and progress sink that
long work polls. Checkpoints: PDF pages, PDF flattening, geometry bake,
raster-effect flattening, drawing objects, raster artboards, and before any
file is written. A cancelled command returns EngineError::Cancelled and
changes nothing.
- Server::serve reads on its own thread: notifications/cancelled drops a
queued request or stops the running one (no reply, as the MCP spec says),
engine-free requests (ping, the list methods, prompts/get) are answered at
once, a request that would wait behind a call running for 2 s is refused
with -32003 naming that call, and _meta.progressToken gets
notifications/progress (page n of m). Queue and output are bounded.
- The remote backend stops waiting for the app when the call is cancelled.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The snap ring on an anchor (or a dot on a line) and the rubber band now follow the snapped point, so both measured points visibly land on anchors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A tab's width depends on where it falls from the tab origin. Line breaking and the line width measure it from the left edge, but a centred or right aligned line is placed from its own start, so its tabs stopped elsewhere. The line is now laid out with its tabs measured from the left and then aligned as a whole.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Smooth Zig Zag ran the ridge points through a Catmull-Rom spline. Its end tangents slant across the path, and where the ridges are taller than about twice their spacing the sharp-turn floor of turn_tangent lengthens the handles, so the result read as straight diagonals with rounded tips. Each peak and valley is now a smooth point whose handles run along the path, a third of the way to the neighbouring ridge, so every span follows a half cosine through the path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A path with a Transform effect that makes copies and a gradient was still merged into one path by bake_leaf and expand_leaf, so the gradient spanned every copy. Route it through reshape / expand_outlined like the renderer does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A corner's radius was capped at half its shorter side whatever its neighbours did, so one corner of a 100 pt square rounded alone stopped at 50 pt. Each corner now rounds until its cut reaches the cuts its neighbours ask for: the whole side when the neighbour is square, halfway when both ask for more. The corner widget drag, the red limit outline and CSS border-radius export follow the same limit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Line breaking, the line's natural width (and so justification) and the composer counted a tab as its glyph's width, a space, while it is set up to its next tab stop. A paragraph starting with a tab, as in the report, filled a justified line as if the tab were one space, and the line ran past the frame. break_line and the line width now measure tabs with tab_advance, and a paragraph with a tab is broken line by line, since the composer works with fixed glyph widths.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Shift click already snapped the second point to 45-degree steps, but the dashed line from the first point followed the raw pointer, so the constraint looked absent until the click.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gradient_chip now takes the GradientPaint and lays the gradient out as it would be on an object of the chip's shape: linear along its angle, radial from the centre, freeform from its colour points. Gradient panel swatch menu and the Swatch Options dialog carry the full paint.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shared dock left its pane and tab strip backgrounds transparent because the docked card behind them is filled. Floating groups lie on the canvas, so the canvas showed through. They now paint the panel colour, which is what the card shows when docked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sRGB to Generic CMYK applies black-point compensation (black separates to the
darkest rich black) but CMYK to display did not undo it, so hex 000000 became
C67 M67 Y67 K100 and then showed as #1b1818. The display XYZ now maps the press
black to black when the colour settings use black-point compensation, in the
sRGB conversion, the ink lookup table the canvas uses, Lab of a CMYK colour and
the gamut warning.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The overflow marker was drawn by the Type tools only, so with the Selection tool a text box smaller than its text showed no sign that text was hidden. Session::overlays now adds the marker of every selected area type and type on a path that overflows, unless a Type tool is active (it draws its own). The verdict is cached per object and font generation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With no highlighted rows and art selected in two or more layers (Select All), layer.merge merges those layers; the panel menu enables it then.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Multiply squared a colour overprinting itself (stroke over same-colour fill showed a darker band). Darken keeps the stronger ink per plate/channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>