Repository navigation
Releases: monfa-red/lini
Release list
Lini 1.1.0
Lini can now compile source it did not write. A rendering service like Kroki pipes a stranger's diagram into lini - and serves the SVG back, so this release adds a flag that keeps that source away from the server's disk, and closes the two ways it could reach the reader's browser through the output.
cargo install liniIt is a minor bump for the new flag, and for two inputs that used to compile and now error (below). Nothing else in SPEC.md moved, and every sample renders byte-identical to 1.0.6.
--no-fs
An |image| with a local path is read from disk and embedded. That is right for your own file and wrong for a server: the source could name any file the server can read and get it back inside the SVG, and even the error answered whether a path existed — no such file and permission denied are not the same reply.
lini - --no-fs refuses every local path before it is joined or opened, so a file that exists and one that does not give the same error. URLs and data: URIs work as before, and so do assets a host hands over as bytes, which is how the browser build has always worked. --theme takes the built-ins and the light/dark pairs only, so a theme name a service forwards cannot be a path either. In the library it is Options::no_fs. See the CLI.
A string can no longer end the <style> element
Rule values and variables were written into the <style> body as they were. A font named "A & B" made the SVG malformed XML, and a string holding </style><script>…</script><style> closed the element and put a script in the document, one that runs when the SVG is opened on its own.
The body is escaped like any other character data now. Engine CSS never contains <, > or &, so no existing output changes. Besides the snapshots, I compiled every string in every sample with injection payloads in it, live and --static: 7,024 outputs, all well-formed, none with markup of its own. 1.0.6 fails that same run 8 times.
An href cannot run code
href: "javascript:alert(1)" made a node into a link that runs script in whatever page shows the figure. A javascript:, vbscript: or data: URL is now an error at its declaration, read the way a browser's URL parser reads it, so case, padding and a tab inside the scheme don't hide one. Pages, mailto:, fragments, relative paths and app links like obsidian:// are untouched. See the property ledger.
Downstream
lini-wasm@1.1.0 carries both fixes to every JavaScript host; its API did not move, and a browser has no filesystem for --no-fs to switch off. mdbook-lini@0.5.5 follows.
Lini 1.0.6
A figure now keeps the font its boxes were measured with, even on a page whose stylesheet says otherwise. That is the whole release: one rule moves, and text stops walking out of its box when a Lini SVG is inlined somewhere with opinions about text.
cargo install liniNothing in the language changed. SPEC.md is the contract, and 1.x stays compatible with it.
The font is stated on the glyphs, not inherited by them
A box is sized at compile time from the metrics of the family the compiler picked. The stylesheet stated that family once, on the root <svg>, and let it inherit down to every <text>. Inheritance is the weakest thing in the CSS cascade: a bare text { font-family: … } in the host page — specificity 0,0,1, the feeblest selector that exists — replaces it, and from then on the boxes are drawn for one face and filled with another.
That is not a theoretical loss. A 19-character label measured at 118px paints at 171px in a monospace face and clears its 155px box by 20px on each side. It surfaced in obsidian-lini-view, where note styling reaches into inlined SVG, but any host that styles text, svg text, or a * inside its content area could do it.
Worth being precise about the cause, because the obvious diagnosis is wrong: it is not the declared fallbacks. Measured in a browser, every one of them fits — the declared stack at 118.2px, system-ui at 120.5px, Helvetica Neue at 116.1px, against 130.7px of content width. They are all proportional and within a couple of percent of each other. Only a face from outside the stack, arriving via a host rule, is wide enough to overflow.
So the family now rides its own selector list — the root and the glyph elements:
.lini-scope-abc123, .lini-scope-abc123 text { font-family: … }One declaration, stated once, with nothing left to inherit away. The root rule keeps font-size, font-weight and color, which no host stylesheet was stealing.
An authored font still wins
A rule that matches <text> directly also outranks an ancestor's inline font-family — so |box| { font-family: "Courier New" } would have lost its face while keeping the width it was measured at, the same bug inverted. A text leaf now diffs that property against its effective attributes rather than its own, so an authored family inlines onto the leaf and beats the rule, while a leaf at the default carries nothing. The rule stays the only place the default is written.
If you want a different default face — a monospace one, say — set font-family in the source, where the compiler measures with it. What cannot work is changing the font after the widths are committed.
Also in this release
- The wordmark follows the reader's theme.
assets/logo/lini.svgwas compiled with--static, which bakes the palette to literal hex and stamps an opaque white rect behind the mark — so on a dark README it read as a pasted screenshot. It is built like every other asset now: transparent, with its colours onvar(). - The editor extensions are a search away. Both are published: VS Code and Zed. Search Lini in the extensions pane rather than copying a folder.
- The community's integrations have their own shelf in the README, starting with obsidian-lini-view — a
```linifence that draws in the note itself, live in the editor. - Issue forms, so a bug report arrives with the source that drew the figure in it.
Downstream
lini-wasm@1.0.6 carries the fix to every JavaScript host — that is the one an Obsidian, Docusaurus or Astro site wants. remark-lini-lang@0.1.1, astro-lini@0.2.1 and mdbook-lini@0.5.4 follow it. No API moved; the floors move so the fix is not left to a caret to maybe pick up.
Lini 1.0.5
The compiler can be handed its assets rather than left to read them. That is one new field on Options, and it is what lets a Lini figure carry a local image on a host with no disk — the browser playground, a bundler plugin, a Markdown pipeline compiling a page. The binary is unaffected: the CLI reads from disk exactly as before.
cargo install liniThe npm surface is breaking. compile, compile_html, compile_static and diagnostics each take the asset table alongside the source, so lini-wasm@1.0.5 is not a drop-in for 1.0.4. Pass {} if a document names no local file.
Options::assets — the bytes, keyed by the src: as written
An |image| src: naming a local file could only ever be read from disk, which made it unreachable from the one host that has no disk. In WebAssembly the read failed with "operation not supported on this platform", so the playground answered a sheet with an R010 and a blank pane — and a reader concludes the feature is broken, not that the platform is.
The table is keyed by the src: string exactly as authored, because that is the only thing a host knows about the file. It is consulted before any read and joins the existing embedding path rather than opening a second one: the same id-isolation, the same classification, the same determinism from the bytes. An asset that is neither supplied nor readable is still the error at the src: span.
The proof is a test that could not previously exist
tests/wasm.rs compiles every sample through the browser artifact and demands byte-equality with the binary, and it had to skip every image-bearing sample — the exact samples worth comparing. That filter is gone: the binary resolves assets/logo.svg against samples/ and reads it, the driver passes the same file through the asset table, and drawing_sheet's two outputs now match byte for byte. One embedding path, reached two ways, and a corpus check that fails if the last |image| sample ever leaves.
Two figures redrawn
- The sheeted screw's hex socket turns. The end view drew the socket flat-top, so the dimension between
:leftand:rightspanned tip to tip — 7 is the across-corners of a 6 mm hex, not a key size.rotate: 90stands it point-up and the anchors turn with the geometry, so the callout is now 6, the DIN 912 M8 key. The side view's hidden socket follows to the across-corners width, and the projection construction lines — which left one card at a bbox midpoint, mid-shank, in open air — are gone:align: originalready reads the views as aligned, and a finished ISO sheet carries no construction chrome. - The ER hero wires two of its cards field to field. SPEC 8 has promised field-level wiring since the beginning and 1.0.4 made it draw, but the showroom never showed it.
user.pk -o< order.user_fk:leftnow leaves one card at itsidrow and lands on the other atuser_id. Worth knowing: a cell anchor places the endpoint along a wall but does not pick the wall, so a field-level wire usually wants an explicit:sideto read as one.
Every showcase PNG under assets/ was re-rendered from the SVG beside it; most had drifted months behind.
Markdown, anywhere remark runs
remark-lini draws the same ```lini fences in Docusaurus, Next and MDX, Gatsby, or a bare unified() pipeline — on npm as remark-lini-lang. astro-lini 0.2.0 is now the Astro wrapper over it rather than the only door, and both ride the asset table above.
The language is unchanged. SPEC.md is the contract, and 1.x stays compatible with it.
Lini 1.0.4
Charts get their type scale and their daylight sorted out. Every chart redraws — text is a step larger and stands further off the things it labels — so a figure you rendered on 1.0.2 will not be byte-identical.
cargo install liniThis release carries 1.0.3's changes too; that version was tagged in the tree but never published.
Charts read a step larger
- The type grew a step. Body text 11 → 12, the title 14 → 15, and the plot's gutter to its title and legend 10 → 15.
LABEL_SIZEandAXIS_TITLE_SIZEwere two names for one number and are now oneTEXT_SIZE— a chart is two type steps, the title and everything under it. - The scale is stated once, not ninety times. Every string a chart drew inlined its own
font-size;chart_hero.svgcarried about ninety of them. Three rules state it now, and the inline-font builder is deleted, so there is no way left to inline a font on a chart leaf. A leaf still carries its own colour — that is the class-diff law, and the only thing left in astyle=. - One
clearancereplaces five hand-tuned numbers. A chart's chrome text stood off the thing it labels by five constants no author could reach. It is one cascadingclearancenow, default 6 — the same scene config a link routes at, read by the one layout that draws no links. Widening it shrinks the plot rather than pushing a label out of the box.
Four fixes the larger type made visible
- A log axis thins to its room.
decade_ticksemitted 1-2-5 per decade whatever the span — sixteen labels at a 10px pitch on a 170px plot. It now takes the densest rung of the decade ladder whose ticks stand a text row apart. The linear side has aimed at a tick count since it was written; log finally has the twin. - A
|mark|'s label no longer sits on its line. The two branches disagreed — a mark on a vertical axis put its name beside the line, one on a horizontal axis centred it on the line, so the rule ran through the glyphs. Both stand clear now, and near the plot's far edge the label takes the other flank rather than spilling. - A bubble's label fits the disc, not a diameter. A diameter is only available on the exact centre line, and a label has height — so text whose corners were already outside the rim passed the test. The fit is the chord of the disc inset by
clearanceat the text's own half-height. - A radial's categories stand off the rim edge to edge, so a long name to the side clears by the same daylight as a short one on top.
The cost, stated in SPEC 14.6
A scene that widens clearance for its wiring widens its charts' text too. That is what one cascading number buys.
The language is otherwise unchanged. SPEC.md is the contract, and 1.x stays compatible with it.
Lini 1.0.2
A packaging release. The language and the compiler's output are unchanged from 1.0.1.
cargo install liniSince 1.0.1
- Lini runs in Node, Bun, Deno and any bundler.
cargo xtask wasmnow builds a publishable npm package — two wasm-bindgen targets behind oneexportsmap, with the TypeScript declarations wasm-bindgen generates. Published separately aslini-wasm. lini-wasmexports the token palette.highlight_css()had no binding, so a host that highlighted Lini in the browser had no colours to do it with — the class names are the compiler's and change with it.- astro-lini —
```linifences in an Astro site, compiled to inline SVG at build time. The same fence vocabulary mdbook-lini uses. - The README is a front door rather than a second manual: the figures, the palette, and links to lini.rs for the tour and reference.
RELEASING.mddocuments how the pieces ship together, and the one cargo[patch]failure that can quietly build a book against the wrong compiler.
Lini 1.0.1
A patch release: three fixes to what the compiler draws, and a spec that says what it always meant.
cargo install liniFixed
- A
|band|and a|mark|can be placed on a time axis. SPEC 14.5 said a band'srange:is "the same interval shape the axis itself reads", but only the axis read date literals — so a dated chart could carry neither a shaded span nor a reference line. Both take dates now, and the axis's ownrange:/ticks:read through the same one place, so the three cannot drift apart again. A number on a dated domain, or a date on a numeric one, is the mixed-domain error either way round. - A wire off a table cell leaves the card at that cell's row. Giving an
|entity|cell an id and linking the path — the field-level relationship SPEC 8 specifies — reportedno legal route: every side entry or channel is closed. The orthogonal router now opens the row's own edge. - Drawings breathe. A leader's stand-off was measured on its elbow rather than on its ink, so a note leaving upward or downward hung its text back across the exit. The drawing scope's
clearancegoes 5 → 18 — the old default drafted so tight that every sample overrode it — withnote-offset14 → 20,note-landing8 → 12, and the text's daylight off the landing named asnote-text-gap5. Figures with dimensions and callouts are laid out more loosely than in 1.0.0.
Also
- Every fence SPEC.md writes bare is Lini, and says so — the reference chapters on the site are syntax-highlighted throughout.
- The floor-plan examples in
SPEC.md,SKILL.mdandsamples/are redrawn: dimensions read clear spans face to face, no wall crosses an opening, and every fixture clears what it sits beside. ## Quickstartis## At a glance, so the reference chapter is00-at-a-glance.html.
The language is unchanged. SPEC.md is the contract, and 1.x stays compatible with it.
Lini 1.0.0
From mindmap to blueprint. Lini 1.0 — one small language for every kind of figure: diagrams, mindmaps, tables and ER schemas, sequences, charts, engineering drawings, floor plans and circuit schematics, from plain text to clean, themeable SVG.
The site is lini.rs: the tour, the reference, the gallery and a playground that runs this compiler in the browser. Agents get SKILL.md.
cargo install liniSince 1.0.0-rc.4
- A
|page|lifts its content clear of the seated title block, and only when a view would run under it. - The schematic sheet wash settles at a paler, cooler cream —
hsl(46 62% 95%)in light mode. - The no-font build is warning-free again.
- The README, the VS Code and Zed extensions name lini.rs as home and carry the family's tagline.
The language is SPEC.md — complete, and the contract this release commits to. 1.x stays compatible with it.