Releases: VidGuiCode/assemblash
Release list
Assemblash 1.8.0
Assemblash 1.8.0
Clipping and cropping. A layer can mask itself to a rectangle or an ellipse, an
image layer can show one rectangle of its source, and any layer can mirror
horizontally or vertically. This is how a circle avatar or a rounded screenshot
is made, and how a crop becomes a rectangle rather than a fit mode.
schemaVersion stays 1. The Operation union does not grow: all four values
are ordinary UpdateLayer fields.
Added
clip. A layer masks itself to its own box:{"shape":"rect", "cornerRadius":12}or{"shape":"ellipse"}. A radius larger than the box
makes a stadium, the same way a shape rectangle's radius does.crop. An image layer shows one rectangle of its source, in source
pixels:{"x":0,"y":0,"width":400,"height":400}. It composes withfit.
Values outside the source clamp to it, and a rectangle that misses the source
is refused.flipHorizontalandflipVertical. A mirror about the centre of the
box, composed with the rotation. Mirroring text mirrors its glyphs.- Every surface carries them. The CLI takes
set --clip-rect,
--clip-radius,--clip-ellipse,--no-clip,--crop X,Y,W,H,--no-crop,
--flip-hand--flip-v. MCPupdate_layertakesclip,clearClip,
crop,clearCrop,flipHorizontalandflipVertical. The interface has
one inspector row per feature. HTTP needs nothing:POST /operations
deserialises the union. - An imported image records its pixel size. A crop is a rectangle in the
source's own pixels, so the header of a PNG, JPEG, GIF or WebP is read at
import. A format this build cannot measure is imported with an unknown size,
and a crop of it is refused by name rather than guessed at.
Fixed
- A clip and a shadow on one layer now keep the shadow. The mask is applied
inside the effect stack, because SVG applies aclip-pathafter afilter
and would cut the shadow off at the boundary. - A rotated clipped layer is cut to the box its own coordinates name. The mask
carries the inverse of the layer's rotation, so it does not turn with the
content.
Older builds
Builds 1.0 through 1.7 open a 1.8.0 document, keep clip, crop,
flipHorizontal and flipVertical exactly as written, and render the layer
unclipped, uncropped and unflipped. That is a silent degrade, named here rather
than discovered later.
A layer with no clip, no crop and no flip renders exactly as it did in 1.7.1.
Every determinism golden from 1.7.0 is byte-identical.
Assemblash 1.7.1
Assemblash 1.7.1
Bugfix release. font install now delivers a bold face. fontWeight: 700
then works for install-only users. This release contains only the defect
registered against 1.7.0. No new capability. No schema change. No operation
change. No new surface. No dependency bump.
Fixed
- A store built only through
font installheld no bold face. Every
fontWeight: 700layer then failed with a typedMissingFont. The
bundled manifest shipped Noto Sans, Noto Serif and Noto Sans Mono only as
variable fonts. The font database sees only the 400 default face of a
variable font. The manifest now also registers a bold (700) face for each
of the threedefault-pack families. Each new entry pins sha256 to a
commit of the notofonts distribution. An install of a family now installs
every face registered under its name.font install --listand
GET /api/fonts/cataloguestill answer once per family. The byte totals
now include the bold faces.
Rendering does not change. The engine, the gate goldens, and the typed
refusal for a truly absent face stay the same.
v1.7.0 — typographic control and the canonical capability surface
v1.7.0 — typographic control and the canonical capability surface
Bold headlines, tracked captions, outlined display text, and text that sits
where the box says it should — plus one capability listing that every
surface reads, so what the engine renders is exactly what it advertises.
schemaVersion stays 1 and the Operation union is unchanged. The
oldest build that opens a 1.7.0 document is 1.0: builds 1.0 through 1.6
degrade silently through the Extras capture map, rendering the regular
face with no spacing, stroke or vertical alignment.
Added
fontWeight(100–900) andfontStyleon text layers, resolved by
exact face: a weight the font store has no face for is refused with a
typedMissingFontnaming family, weight and style — never substituted.
Set them throughassemblash set/add-text(--weight,--font-style),
MCPadd_text_layer/update_layer, the HTTP create/update payloads,
presets, and the interface.letterSpacing(px) on text layers, part of measurement itself —
wrapping,/text-layout, the overflow warnings and the export all agree.strokeon text layers, painted centred on the glyph outline with
paint-order="stroke". A layer withcolor: nulland a stroke is hollow
display text.verticalAlign(top|middle|bottom, defaulttop), so
existing documents render pixel-identically.- The canonical capability listing.
assemblash styles --json, HTTP
GET /api/capabilities, and the new MCPlist_capabilitiestool serve
one identical listing of every blend mode and effect this build renders,
with examples and notes. Check it before building. - Four new cross-target determinism gates for the new paint paths
(text-weight,text-stroke,text-spacing,text-valign); every
pre-existing golden is byte-identical.
Upgrade: 1.7.0 is a drop-in replacement for 1.6.x. Documents written by
1.7.0 open unchanged in 1.6.1 and in every build back to 1.0 (new fields are
preserved, just not rendered, by older builds).
v1.6.1 — styles tells the truth
v1.6.1 — styles tells the truth
A defect sweep on 1.6.0. No schema change (schemaVersion stays 1), no
operation-shape change, no new surface.
Fixed
assemblash stylesnow listsdropShadow. 1.6.0 rendered the effect
but the CLI's discovery listing still named only the five pre-1.6 effects,
so an agent readingstylesconcluded drop shadows were unsupported. The
listing is now derived from theEffectenum in core instead of a literal
array in the CLI, a new effect variant fails to compile until it is given
its line, and the regression test asserts every variant by an exhaustive
match. ThedropShadowentry documents its four fields (dx,dy,
blur,color), notes thatdx/dyat 0 is a glow, and that an
#rrggbbaaalpha sets flood opacity.- The
add-text --textandset --texthelp no longer claim`\n`
is a line break. A backslash-n typed at the shell is stored as the two
characters and renders as such; only--text-fileproduces a real line
break. The help now says the text is taken verbatim and points at
--text-filefor a line break. Behaviour is unchanged.
Upgrade: 1.6.1 is a drop-in replacement for 1.6.0. Documents written by
1.6.1 open unchanged in 1.6.0 and in every build back to 1.0.
v1.6.0 — shapes and shadows
No 1.5.1 was released: no defect was outstanding after 1.5.0.
A rectangle, an ellipse and a line stop being things you draw elsewhere and import: they are layers the document draws itself, with fill, stroke and corner radius as ordinary properties. A drop shadow joins the effect stack, and a glow is that shadow with no offset.
Added
- Shape layers, a fifth layer kind:
rect(withcornerRadius),ellipseandline. The transform box is the geometry, somove,resizeandrotatemean what they already mean for every other layer. A line is its box:widthis the length,rotationthe angle. fillandstroke({ color, width }), either absent for no paint. Strokes are painted inward on a rect and an ellipse, so the box stays the visual box; a line's stroke is centred. Widths below 1 draw as hairlines and may spill up to half a pixel.- A
dropShadoweffect withdx,dy,blurandcolor; an#rrggbbaaalpha is its opacity. Zero offset is a glow. - Presets carry
fillandstroke, and a colour slot may target a shape's fill as well as a text layer's colour. - CLI
add-rect,add-ellipse,add-line, and--fill,--stroke,--stroke-width,--corner-radiusonset(noneclears a paint). - MCP
add_shape_layer, taking the server from 44 tools to 45;update_layergainsfill,stroke,cornerRadius,clearFillandclearStroke; layer summaries report a shape's geometry. - Interface: a Shapes row in the Add panel, a Shape inspector (Fill, Stroke, Stroke width, Corner radius), and
dropShadowin the effects editor. - Two new reference documents in the render gate,
shapesandshadow, checked byte for byte on all six release targets. - A new README hero, built with this release as the editable example project
examples/readme-hero.
Changed
- Asset uploads accept up to 64 MiB (previously the framework's 2 MB default). A body over the limit on either upload route now answers with the JSON error envelope: status 413, code
payloadTooLarge.
Fixed
- A font family literally named
catalogueorinstallcan now be removed;DELETEsits on both fixed paths.
Compatibility
schemaVersionstays 1; the operation union is additive.- 1.0 through 1.5 refuse a document that contains a shape layer — the whole document — with
unknown variant "shape", expected one of "text", "image", "group", "svg". Nothing needs migrating: open the project with 1.6.0 or newer. AdropShadoweffect is preserved by every 1.3.0-and-newer build and only its render is refused. - Rounded corners and ellipses are emitted as cubic curves rather than SVG arcs, keeping the rasteriser's arc conversion and the platform maths library off the path to a pixel; shadows get an explicit filter region.
Downloads below are built by CI for Windows, Linux and macOS on x86_64 and aarch64; SHA256SUMS covers the archives.
v1.5.0 — the no-terminal promise, kept
Manage fonts from the editor instead of a terminal: list the installed families, import TTF, OTF, TTC, OTC, WOFF and WOFF2 files, remove a family after a confirmation, and, when the store is empty, install the bundled default pack (Noto Sans, Noto Serif, Noto Sans Mono) with one button that says what it will download. The same store is reachable over HTTP under the usual access-token rule; the install route is the one route that reaches the network, only on that request, and a pack install is atomic.
A server started by the double-click launch (or serve --reclaim-stale-locks, also on mcp) reclaims a project lock by itself only when the lock names this machine and the process it names is provably gone. Every reclaim is reported in the server log, in the editor, and as a lockReclaimed export warning. A lock from another machine, or from an older build that recorded no machine, still needs a person.
An imported SVG asset whose text no loaded font can draw is now refused with a typed error naming the asset, instead of exporting blank and calling it a success. A document that used to export blank but successfully now refuses: name a loaded family in the asset or outline the text before importing it. No 1.4.1 was released: no defect was outstanding after 1.4.0.
The document schema remains version 1 and every existing operation shape is unchanged. 1.4.0 and earlier open every project 1.5.0 writes; the only new on-disk data is the optional host in a lock file, which older builds ignore.
Downloads cover Windows, Linux and macOS on x86_64 and ARM64. Archives include license notices; SHA256SUMS lists checksums. Debian packages and standalone Windows executables are also provided.
v1.4.0 — Canvas editing
Resize an existing canvas, change its background, and place content using nine anchors. Layers retain their sizes. Use the Canvas editor panel, assemblash canvas set, HTTP updateCanvas, or MCP update_canvas.
Every change uses the existing validated operation layer: protected content is respected, MCP supports dry runs and expected versions, and one undo restores dimensions, positions and background together. Unknown operation and HTTP request fields now produce explicit errors; actor.detail remains accepted as an alias for name.
The document schema remains version 1 and existing operation shapes are unchanged. After a project records updateCanvas, use 1.4.0 or newer. The 1.3.1 binary refuses both show and history because it does not recognize the new journal operation, including after undo. Preserve the journal and keep the newer binary.
Downloads cover Windows, Linux and macOS on x86_64 and ARM64. Archives include license notices; SHA256SUMS lists checksums. Debian packages and standalone Windows executables are also provided.
v1.3.1 — Reliable release checks
This patch makes the test and release checks more reliable without changing the document schema, operation API or editor features.
- MCP integration tests build the executable they exercise and use Cargo's reported artifact path.
- HTTP test clients stop at a complete response instead of waiting for the socket to close.
- Browser test startup, commands and cleanup have bounded waits; CI and release jobs have time limits.
- Cross-transport smoke tests run on all six targets before release and against checksum-verified downloaded archives after publication.
schemaVersion remains 1. Existing documents retain the same rendering pixels; exported metadata records renderer version 1.3.1.
v1.3.0 — Every capability reachable from every surface
Nothing new that the engine can draw; everything it could already draw becomes
reachable from every interface. A layer property only the HTTP API could set is
now a CLI flag. A query only the CLI could answer is now an HTTP route and an
MCP tool. An export now says what it could not do well instead of staying
quiet. And an operation carrying a property it does not define is refused,
where it used to be accepted and silently discarded.
Added
assemblash set, one command for every updatable layer property:
--name,--x,--y,--width,--height,--rotation,--opacity,
--visible,--locked,--blend,--effects,--effects-file,--text,
--text-file,--font,--size,--color,--align,--line-height,
--fit,--assetand--allow-locked. However many flags an invocation
carries, it is oneupdateoperation: journalled once, undone once. Position
and size flags are merged into the layer's current transform, so--xalone
moves without resizing, and--name ""removes a name.assemblash styleis
unchanged and now calls the same builder, so the two cannot drift.--line-heightonassemblash add-text, andlineHeighton the MCP
update_layertool — the last updatable property those two surfaces could
not set.- Export warnings. An export now reports what it could not do well:
wordBrokenMidWord(a single word too wide for its box, split to fit),
textOverflowsBox(laid-out text taller than the box holding it) and
svgAssetTextWithoutFont(an imported SVG asset drawing text in a family no
loaded font provides). Each carriescode,messageand, where one applies,
layerId.POST /api/projects/{id}/exportand the MCPexport_document
result carry awarningsarray; the CLI prints one line per warning on
stderr, or the whole array as JSON on stdout with the new--warnings-json
flag onrenderandexport. A warning is advisory: it changes no pixel and
no exit status. GET /api/projects/{id}/overlaps, returning{"pairs": …}in the same
order asassemblash overlaps, narrowable with a repeated or
comma-separated?layers=. A layer id that is not in the document is
refused, not ignored.- Four MCP tools —
create_project,add_svg_layer,render_documentand
find_overlaps— taking the server from 39 tools to 43.render_document
returns the same SVG the HTTP preview route serves;find_overlapsreturns
the same pairs as the CLI and the new route;add_svg_layerdraws an asset
that was already imported, because a layer can only ever draw a sanitized
one. - Positional output paths on
assemblash renderandassemblash export,
beside the existing--out, and both now print the written path and its
sha256:digest as one tab-separated line on stdout — the same digest form
variantsalready prints — so two interfaces can be compared without
hashing the files. - Positional input files on
assemblash add-svgandassemblash add-image, beside the existing--file, matchingassemblash font add. assemblash preset define --properties-file, so a preset's JSON can come
from a file rather than from a shell that may rewrite it on the way through.- An SVG choice in the reference editor's export dialog. Choosing SVG
downloads the engine's vector render and disables the resolution row, since a
scale means nothing for a vector; nothing is written into the project. - Reference-editor inspector controls for image and SVG
fit, and for
reordering the effect stack, where order is part of what the stack means. The
font fields gained a suggestion list of the families the server reports, with
free text still accepted so a document naming a family this machine lacks
stays editable. An uploaded image or SVG now arrives at its own recorded
dimensions, scaled down to fit the canvas, instead of always 300×200.
Changed
- The reference editor's export dialog no longer carries its bare SVG download
link. The link could not carry an access token, so it failed on any server
started with one, and the new format choice is the working version of the
same affordance. - CI's
uijob now runsnpm run check, which typechecks, builds and runs the
interface's unit tests and browser journeys in one step. Those tests existed
but had never been invoked in CI. The check that the committed build output
is the build output still runs after it, unchanged.
Fixed
-
An operation carrying a property it does not define used to succeed.
{"op":"update","id":"layer_…","letterSpacing":4}returned200 OK, bumped
the document version, reported the layer as changed, and journalled an update
carrying no properties at all — a false success that told a client its edit
had landed when nothing had happened.createbehaved the same way. Both now
return422with codeoperationRefused, naming the property; the document
version does not move and no journal entry is written. The known keys are
derived from the same definitions the published operation schema is generated
from, and thecreatecheck uses the payload's owntype, so a text
property on an image create is refused as precisely as a misspelt one. Three
spellings that predate 0.6.0 —font_family,font_sizeandline_height
on a textcreate— are still accepted, so journals written before then
still replay.This affects only a newer client talking to this server. It is a refusal of
what is sent, never of what is stored: no document, journal or existing
client behaviour changes meaning. MCP is unaffected — every MCP tool
builds a typed operation in Rust, so a property the operation does not define
could never reach the operation layer from there.
Compatibility and safety
schemaVersionstays 1.- The existing
Operationunion is unchanged, and no operation changed shape.
setand all four new MCP tools compile to variants that already existed.
schema/document.schema.json,schema/operation.schema.jsonand both
generated TypeScript declaration files are unchanged. - A document written by 1.2.x is unchanged by this release, and every 1.x
build — back to 1.0.0 — reads a document produced with this release's
capabilities exactly as it read one before it. No document field was added
and none changed shape. warningsis a new field on an export response, not on a document, so it is
additive for any client that ignores unknown response fields.- The published operation schema does not state whether extra keys are allowed
oncreateandupdate. The refusal lives in the operation layer, so a
client author should read this behaviour rather than the schema's silence. - Export warnings are not displayed in the reference editor in this release.
They are reported by the CLI, the HTTP API and the MCP server. svgAssetTextWithoutFontmakes a silent failure loud; it does not fix it. An
imported SVG asset drawing text in a family the loaded fonts do not provide
still renders that text as nothing. The check readsfont-familyattributes
only — not astyle="…"attribute and not a<style>block.- Every renderer gate golden is unmoved: this release adds no paint path.
- Every existing CLI flag and subcommand, HTTP route and MCP tool still works.
--out,--fileandassemblash styleare all still accepted.
v1.2.1 — Nothing quietly dropped
A bugfix release on 1.2.0. Five defects found by auditing the released build
against what it claims to do, four of them cases where something was quietly
discarded or quietly duplicated rather than reported. Nothing new was added,
and this is the 1.2 release to install.
Fixed
- A font that names its family in more than one language arrived as more than
one family.assemblash font addwrote one index record per name-table
language record, so a font naming itself in English and Japanese filled
font listwith families nobody had installed. One file's face is now one
record, under the name the English record gives, or the first record when
there is none. - The same font bytes under a different filename were added twice. A stored
record compared equal only when itssourceandlicensematched too, so
re-adding a renamed copy — or the same file again with--licensefilled in
— appended a second row describing a file already in the store. Records are
now matched on what they identify: family, style, weight, file, hash and face
index. The first import's source and licence are kept. - SVG import destroyed the accessibility metadata an author had written.
role,aria-label,aria-labelledbyandaria-describedbywere not on the
import allowlist and were stripped without appearing in the removal report.
They are now kept. None of them references anything outside the document, so
nothing about the import threat model changes. prune_unused_assetstreated ansvglayer's asset as an orphan and
would have deleted a file the document was still drawing. It now counts every
layer kind that names an asset, and does so exhaustively, so a layer kind
added later cannot be forgotten here silently.- The published schema described
blendModewrongly. Its description still
read "Reserved (v0.5): onlynormalis rendered today", fourteen releases
after fourteen modes started rendering, and that sentence was in
schema/document.schema.jsonandschema/document.d.tsas well as the Rust.
It now says what the renderer does, including whycolor-dodgeand
color-burnare deliberately absent and what happens to a mode this build
does not draw.
Changed
- Removed
drawInspectorLegacyfrom the reference interface. It was superseded
and called from nowhere; the committed bundle is smaller by its size. crates/assemblash-renderer/tests/fonts/TwoFamilyNames-Subset.ttf, a Noto
Sans subset carrying two Unicode family-name records, is committed as a test
fixture for the first of the font-store fixes. Like the other fixtures there
it is under the SIL Open Font License 1.1; it is renamed rather than keeping
the original family name, because it is a modified version.subset.py
rebuilds it from the committed Latin subset.
Compatibility and safety
schemaVersionstays 1.- The existing
Operationunion is unchanged, and no operation changed shape. - No CLI flag or subcommand, HTTP route, MCP tool or interface control was
added, changed or removed. - A document written by 1.2.0 is unchanged by this release: it loads, saves,
renders and exports exactly as it did, byte for byte. The only change to a
published artefact is a corrected description string in the generated schema
and TypeScript declarations, which no document depends on. - Every renderer gate golden is unmoved. No dependency was bumped.
- Documents whose SVG assets were imported by an earlier build are unaffected;
the accessibility fix applies to imports made from now on, and re-importing
the original file recovers what an earlier import dropped.
Install
Download the archive, the bare executable, or the .deb for your target from
the assets below, or install from source with Rust 1.92 or newer:
cargo install --git https://github.com/VidGuiCode/assemblash --tag v1.2.1 assemblash-cliSHA256SUMS in the assets lists the checksum of every file attached here.
See the changelog
for the complete release notes.