Skip to content

Jixoai UI v0.4.0: effects become attachments, plus 23 net new registry items

jixoai v0.4.0 ui

jixoai-ui v0.4.0 turns the press effects into Svelte attachments ({@attach pressEffect(...)}) and lands six-engine highlighting, the icon pipeline, the prose scope, KaTeX and Mermaid, the Card structural kernel and a floating canvas dock. The registry goes from 113 items to 136. Three migration actions, none with a compat layer.

Эта запись написана на английский. Читать на китайский →

$ git log v0.3.0..v0.4.0 --oneline | wc -l
175

Jixoai UI v0.4.0 is out (2026-09-10). 175 commits — by prefix, 41 feat, 49 fix and 66 docs, with the remaining 19 chore, test and refactors; the registry is up from 113 items to 136. The headline is effects becoming attachments; the same window also lands highlighting, icons, typography, math and diagrams, surface structure, and the canvas. Three migration actions, none with a compat layer.

Effects: component props become attachments

The press effects are Svelte attachments now. shimmer, pulse, rainbow and ripple mount through {@attach pressEffect(fx)} on any element you render, and the effect props retire from press-button, icon-button and chip.

<script lang="ts">
  import PressButton, { shimmer, rainbow } from '$lib/ui/press-button/press-button.svelte';
  import { pressEffect } from '$lib/ui/press-button';
</script>

<PressButton variant="fill" {@attach pressEffect(shimmer())}>Deploy</PressButton>
<PressButton variant="outline" {@attach pressEffect(rainbow())}>Cancel</PressButton>

Svelte's attachments are first-class syntax, no experimental flag, and they are a different contract from actions: the value of {@attach expr} is the attachment itself, with the signature (element) => cleanup, and a replaced param re-mounts by identity, with no update channel and no destroy object. So the effect exports were rewritten as attachment factories, param in and attachment out, and the repo's 39 use: mounts and action-shaped internals all moved through fromAction() in the same change.

Two mechanisms are worth a look. shimmer and rainbow paint the host's own border ring, with ringW driving the ring width, and the fill rides a number | null | Canvas channel where solidFill() mints any CSS color into a guaranteed-opaque 0xRRGGBB. ripple moves to a viewBox-free svg seat, carrying the host's inherited silhouette in user units, clipped per layer and softened by feGaussianBlur.

The rainbow canvas: three neutral host buttons (upgrade, subscribe, ⌘) share one fx object, the host's own border is the flowing ring with ringW driving its width, and the fill rides the auto / null / solidFill channel

The family now has its own home at /docs/effects.html, with glass's two-layer playground moved in whole and the old route /docs/components/glass.html retired; the CLI knows the group alias npx jixoai-ui add effects.

The effects home: the family law, an effect being an attachment factory you mount rather than a component you render; five chips list the two mount channels, the action bridge and the mount-point attributes

Highlighting: six swappable engines, and automatic language detection

The highlight layer splits into a zero-dependency core plus six installable backends: microlighter, highlight.js, prismjs, shiki, sugar-high and tree-sitter. Only one engine ships by default, with all of its capability; the rest are opt-in. Previously the code-card and highlight items wrote prismjs and microlighter into their npm dependencies, so installing one component installed every engine, and this release fixes that.

Language detection is new: a LanguageDetector abstraction plus the built-in DLD (default language detector), in four layers loaded on demand. The first is a filename and extension map, followed by shebang, meta and content heuristics. lang="auto" enables detection, and markdown faces now default to auto. Inline code defaults to microlighter: 4.5KB, zero markup, plain-text copyable.

The complete code-card: the spawn.ts filename tab, the shiki · ts engine badge top right, and the powered by Shiki · theme: jixoai footer with its copy control

Icons: one pipeline, two faces, one component

The icon system gained its component face: <Icon name="check" /> with a type-safe IconName union and size and strokeWidth props, replacing 197 {@html icons.x} injection sites across the registry and the docs site.

The plugin face makes the channel the public API. @jixoai/ui-vite-plugin/icons/[lucide|md|ph|rx] are independent entries of the same package, with lucide on by default and the rest enabled by import; jixoai({ icons: { library } }) generates icon-set.gen.ts, folding custom icon names into IconName. Icons load in lazy chunks, so a consumer never pays for the whole set.

This release also adds a real-project E2E tier for icons: examples/hmos-icons is a real consumer, installed and built for real, and it caught two live defects on the spot. svgo minifies every icon's ids to the same short token (a), so two Sketch exports inlined on one page cross-resolve their use targets; scopeIconIds now prefixes every packed id with its canonical name. And the RAW safety gate rejected the Sketch-export shape of use plus xlink outright, which made channels near-useless for exactly the third-party sets they exist for; the fragment-law carve-out now passes same-document references while external references still reject.

The icons page, component face: the size ladder, one prop rendering across 12 / 16 / 24 / 32

Typography and markdown: the prose scope and the reading-content family

Typography gains a prose item (the <Prose> provider) and a typography lib. All eleven knobs use absentSlot semantics: not setting one means it stays silent, the nearest upstream setter wins, and an outer <Prose size leading> can never beat an inner <Markdown typography=>. That ladder is the sovereignty contract.

The reading-content family extracts every markdown construct that had no registry part: text.svelte implements <Text mark="…"> and exports P Strong Em Del Mark Ins Sub Sup from the same module; heading renders native h1–h6 by level, carrying the em ladder migrated out of the markdown face sheet; blockquote splits its left rule into two literal slots, rule (inset shadow or border) and ruleSize (1px, 4px or 8px); Link marks external links with a suffix icon by default; List splits into the strict ol and ul cases. The markdown face itself is streaming, mapping its AST onto registry parts, with code blocks riding code-card's generation-guard async repaint.

The prose page knob showcase: size and leading scaling by inheritance on the left (ambient 14px against 17px), align justify with hyphens and wrap pretty on the right

Math and diagrams: KaTeX and Mermaid

lib katex is a thin facade over renderToString with jixoai defaults (output: 'htmlAndMathml', throwOnError: false, an errorColor bound to a token), untouched option passthrough, a registerMacros extension point, and the katex.min.css import that makes math out-of-the-box: the fonts ride the npm package, so no consumer configures fonts. The naming follows the syntax standard: LaTeX's components are math-block and math-inline, and mermaid keeps its own name.

lib mermaid-engine is a lazy singleton, code-split because the engine is about 1MB. The acceptance round added two rulings: mermaid's CROSSED pin brings its own canvas, painting the viewport from the target theme when a theme is pinned explicitly, and math-block gains a FIT variant that scales instead of scrolling, which is the print default.

Surfaces: the Card structural kernel and the action band

The structural kernel of 2026-09-09 collapses "the bottom action area" into one law: Card's head, body and foot bands plus sticker-style structure (every rule anchors on the data-jx-* attribute family, so any element that carries the stamp is structure), a scroll law owned by the body alone, and an inline ruler served by CSS. Dialog sheds its own flesh for Card's dialect, and ButtonBar retires.

Two follow-up sweeps then cleared the remaining flesh from sheet and system-dialog, and the action-band law generalised to all three of sheet, system-dialog and the canvas dock, with buttons filling their band vertically. AlertDialog is renamed SystemDialog along the way, and the registry item alert-dialog becomes system-dialog.

The card page: the structural kernel and its head / body / foot bands, with the inline action seat Cancel / Save in the foot

Canvas: a floating dock, and canvas everywhere

ComponentCanvas carried too many responsibilities, the worst of them a permanent playground lane eating 18–22rem of stage width. This release turns the playground into a floating, collapsible, draggable dock inside the canvas, mounted expanded and collapsing to an icon chip, with its controls assembled through the ItemGroup family. The canvas itself slims down to a code-card-shaped card.

The companion canvas-everywhere sweep routes every docs demo through the canvas. Previously, across 114 pages, 443 ComponentCanvas instances stood against 1580 SectionCard instances, leaving readers unable to see the code behind most of what they saw; demos now carry a source lane by default.

component-canvas: a canvas rendering a canvas, the outer LIVE stage embedding a simplified second instance, each with its own Code lane

CLI and vite-plugin

The CLI gains group aliases: npx jixoai-ui add effects expands the whole group, and npx jixoai-ui add effects/glass takes one member.

@jixoai/ui-vite-plugin 0.3.0 ships alongside: typography brings the prose scope and the parts' R2/R3, icons brings the real-project E2E tier above, and canvas brings the dock's acceptance rulings (the acrylic popover material, the grip-vertical icon, and position: absolute retired). The plugin test suite is at 398/398.

Migration (breaking changes)

Three migration actions, none with a compat layer: how effects mount, two item renames, and the glass class. Only the rename commit carries a ! marker in the repo; the other two state their breakage in their own proposals.

The first is effects, from a component prop to an attachment:

  <PressButton {@attach pressEffect(shimmer())}>Deploy</PressButton>

The second is two renames: alert-dialog becomes system-dialog, and icons splits into icon and icon-set. The lowest-friction path is to re-add items under the new names:

npx jixoai-ui add system-dialog icon icon-set

The third is glass: the hand-tuned .jx-glass class retires and blur()'s defaults take over (14px blur, 1.35 saturation, 68% fill), so an unconfigured migration is computed-equivalent. The full mounting map:

The migration table: what replaces the action directive, the builder prop and the attachments record

Other changes

  • Glass: the band gained three parallax scenery layers on seamless infinite loops; blur() subsumes every hand-tuned paint from before, and liquid() and liquid.apple() mount the kube-faithful lens.

  • Highlighting: the langDetector contract enters the core item, AUTO_LANG lands in code-card, and the detection lab enters the docs.

  • Typography: the oklch token palette goes to v2, with the secondary leading ladder, the fused variant and the shared text-modifier kernel.

  • The remaining 49 commits are fixes.

Links