Skip to content

Releases: vercel-labs/native

v0.5.3

Choose a tag to compare

@github-actions github-actions released this 17 Jul 21:54
57bf56b

New Features

  • <media-surface> — the dynamic texture channel: a new element compositing textures produced outside the widget tree (video decoders, camera pipelines, external renderers like mpv) into the layout like any widget, with a stable Zig-tier producer API (runtime.acquireMediaSurfaceProducer) pushing RGBA8 frames from any thread — latest-wins, damage-tracked, paced by the compositor's presented-frame clock.
  • Pushes wake an idle compositor: a push staging new bytes requests one coalesced frame through the platform's thread-safe cross-thread frame request (the automation watcher's wake path), so video keeps playing in an idle demand-driven app and a late-starting producer is adopted promptly; damage-skipped and stale-handle pushes wake nothing, and teardown disarms the binding under the same fence the wake call holds, so an orphaned producer can never wake a dead host.
  • Markup, both engines, and tooling: surface="{binding}" binds the model-owned u64 surface id in the runtime-image-id grammar (binding-only, required, media-surface-scoped with teaching errors), wired through the validator, the compiled and interpreter engines, native check's model contract, LSP hover docs, and a docs component page plus a "Media Producers" recipe.
  • Deterministic by policy: texture contents are presentation chrome — goldens, reference screenshots, and session fingerprints see only the surface's id-derived placeholder, so a session recorded with a live producer replays fingerprint-identical with no producer attached; live GPU hosts composite the real texture through the existing image upload pipeline.
  • Reserved id namespace: bit 63 of the ImageId space now belongs to media-surface textures; registerCanvasImage rejects ids with it set (error.InvalidImageId) so producer textures and registered images can never collide.
  • Cover fit stays inside the frame: a cover-fit media surface carries the image widget's rectangular clip around its texture draw, so the fit-expanded texture can never paint over siblings on hosts that only mask corner radii.
  • Adopted-texture memory is on-demand: each channel's texture buffer is one lazy frame-budget allocation from the new Runtime.Options.allocator at first adoption (freed by the new Runtime.deinit), so a runtime with no media producers carries zero media-texture bytes — an embedded pool would have put 32 MiB in every Runtime (measured on the docs wasm preview host: 169.5 MB → 137.5 MB per component tile). The allocator freezes into the runtime at init: mutating options.allocator on a live runtime never retargets ownership, so allocations and their frees always pair on one allocator.
  • Trackpad pinch reaches apps: pinch-to-zoom now flows from the macOS host (magnifyWithEvent:) through phase-explicit pinch_begin/pinch_change/pinch_end input events into a view-global app channel — Zig cores declare Options.on_pinch, TypeScript cores export pinchMsg(pinch) with PinchPhase/PinchEvent in @native-sdk/core/events; each change carries a multiplicative delta (cumulative gesture scale is the product of 1 + scale, applied memorylessly — zoom *= 1 + scale) and the pointer anchor rides view-local x/y (the pointer location during the gesture — zoom-at-cursor anchoring, not a between-the-fingers midpoint); every event names its source window and view (window_id/label in Zig, windowId/label in TypeScript), so multi-window apps tell pinches apart; a terminal Ended/Cancelled event that still measured a nonzero delta arrives as one last change before the end, so the product always matches what the OS reported. On macOS the delta is AppKit's raw per-event NSEvent.magnification, forwarded untransformed: raw magnification IS the multiplicative per-event delta — the convention every browser engine ships — so the product of 1 + scale is the zoom users already experience for the same gesture in Safari and Chrome. The one guard is a per-event floor: a single event's magnification at or below -1 (a zoom inverting through zero scale — physically impossible, only a driver glitch could report it) clamps just above -1, so every emitted factor stays positive. Windows precision-touchpad and GTK gesture sources are staged follow-ups.
  • widget-pinch automation verb: native automate widget-pinch <view-label> <scale> [x y] drives the real pinch event stream without a trackpad (<scale> is the gesture's FINAL multiplicative zoom — one change carrying scale - 1, anchor point defaulting to the view center), journaled like every synthesized input so recorded sessions replay the identical zoom. Automation protocol bumps to v7.
  • Session journal v5: gpu-surface input records gain the pinch scale field and the pinch kinds; readers refuse v4 journals loudly in both directions, per the format's skew discipline — re-record sessions with this build.

Bug Fixes

  • Registered CJK fonts render dense glyphs: glyph outline budgets are now sized from real CJK faces (Noto Sans JP/SC/TC/KR measured; 1024 points / 128 contours per glyph), so everyday dense kanji like 鬱 ink as real outlines instead of notdef blocks; the per-font registration size bound rose to 24 MiB so full CJK faces register.
  • Glyph complexity validates at registration, not render: a face whose maxp declares glyphs denser than the outline budgets — simple maxima and flattened-composite maxima (maxCompositePoints/maxCompositeContours) alike — is refused at registration with a teaching that names its numbers against the budgets (error.FontExceedsGlyphBudgets), instead of silently degrading individual glyphs to blocks at render time.
  • Font bytes are heap-allocated on demand: the registry copies each registered file into an exact-size allocation from the runtime's new Options.allocator (freed by the new Runtime.deinit), so the 24 MiB bound is validation, not a storage reservation — a runtime with no registered fonts carries zero font bytes, where a reservation-shaped pool would have embedded 192 MiB in every Runtime (measured on the docs wasm preview host: 313.5 MB → 121.5 MB per component tile).
  • EmbeddedApp.deinit completes the embed lifecycle: direct embedders end an embedded app with defer embedded.deinit() (idempotent), which returns the runtime's heap-owned registrations — without it, a host creating and destroying apps in one process leaked the registered font storage per cycle. The wrapper hosts and the C ABI's native_sdk_app_destroy route their teardown through the same deinit, one lifecycle owner.
  • Glyph raster budgets are derived from the registration gate: the vector core's glyph-fill capacities (GlyphRasterizer: 18,560 edges/crossings, flattening clamped at 16 segments per curve) are computed from the outline budgets registration admits, so a truthfully-declared budget-maximal glyph — a 1024-point zigzag contour included — rasterizes at any size instead of hitting VectorPathTooComplex and degrading to the block fallback the gate promises cannot happen; the clamp binds only above ~128-px ems and keeps the polyline within 0.2% of the em, so existing renders are byte-identical.
  • Single-line fields never hold or paint line breaks: pasting multi-line text into an input, text field, search field, or combobox now strips the line breaks at the edit seam (the HTML value-sanitization rule — lines join with nothing between them), covering clipboard paste from the shortcut and the context menu, typed and automation text_input, and IME composition, with the app's on_input hearing the same sanitized bytes the editor applied; a paste of only newlines inserts nothing.
  • Defensive render containment: a single-line value that still holds a \n (a model-set value, an old journal) now paints as one line — breaks present as spaces — under a forced content-rect clip, so text can never escape the field's rounded border on any renderer.

Contributors

v0.5.2

Choose a tag to compare

@github-actions github-actions released this 17 Jul 03:42
1e6b615

New Features

  • Anchored tooltips with hover intent: <tooltip anchor="above|below"> beside its trigger in a stack floats against the trigger and hands visibility to the runtime — it shows after the pointer has rested on the trigger for the show delay (default 600ms) and hides on leave, so sweeping a toolbar flashes nothing; after a pointer-hovered tooltip hides on leave, a shared 400ms warm window shows the next trigger's tooltip instantly (the other hide causes below — focus moving on, Escape, a press, view blur — deliberately open no warm window). Delay, warm window, and the behaviors below match shadcn/ui's defaults (Base UI). The model never hears hover, both engines lower it identically, and every transition steps on the recorded input/frame clock, so recorded hover-dwell sessions replay their show/hide frames byte-identically.
  • tooltip-delay attribute: per-tooltip show delay in milliseconds (0 shows the instant the trigger is hovered); absent follows the new tooltip_show_delay_ms/tooltip_warm_window_ms metric tokens. Registry attr code 80; static (non-anchored) tooltips keep their classic paint-when-rendered behavior.
  • Keyboard focus reveals immediately: tabbing onto a tooltip-owning trigger shows its tooltip with no dwell (keyboard navigation is deliberate, and content revealed on hover or focus must not depend on pointer timing); focus moving on or Escape hides it just as immediately, without warming the pointer's skip window.
  • Hoverable content: a shown tooltip's own bounds hold it open (WCAG 1.4.13; Base UI's hoverable default) — the pointer can cross the anchor gap into the tooltip along a bounded safe-polygon corridor, and it hides only after leaving both the trigger and the tooltip (with the usual warm window); the corridor resolves on the recorded frame clock, so replays stay byte-identical.
  • Scroll steps the machine: every scroll path (wheel, kinetic steps, native drivers, keyboard scrolling) routes its hover change through the same intent transition a pointer move takes — a trigger scrolled out from under the pointer disarms/hides, and a trigger scrolled under it arms per normal.
  • Press dismisses: ANY pointer-down — primary or secondary, including downs consumed by the context-menu gesture or a window-drag region — or Space/Enter on the focused trigger cancels a pending reveal, dismisses a shown tooltip, and closes the warm window, so an activated control never re-explains itself on the post-click hover. Keyboard activation and Escape also spend the standing focus reveal: a rebuild that replaces or rekeys the tooltip (the activation's own model update, typically) cannot resurrect it — it stays down while focus rests on the trigger, until the keyboard genuinely leaves and returns; the focus ring stays painted, and a later pointer hover re-earns the dwell normally.
  • Rebuild hygiene: a rebuild that removes, rekeys, disables, or re-parents a tooltip's trigger resets that tooltip's armed/shown/warm state and re-stamps it hidden, even when the tooltip node itself survives.
  • View blur resets: a canvas view that loses focus (to a sibling view or with the window) drops its entire tooltip conversation — armed delay, shown tooltip (keyboard- and pointer-owned), warm window — and re-stamps hidden, so no stale tooltip floats in a view the keyboard left. The window key-loss reset fires on the flag's own focused→unfocused edge, so it holds however the host announces the change: one gain event (macOS) or loss-before-gain (Windows, GTK), including a loss with no gain at all (every window inactive).

Improvements

  • Search empty state names its scope: the system monitor's no-match state now says search only sees the top 128 processes by CPU, so a miss on a quiet process reads as scope, not absence (both tiers).

Bug Fixes

  • Dark-scheme accent focus rings settle down — and stay visible: a theme_accent (or canvas.accentOverrides) now derives its dark-appearance focus ring at half the accent's saturation instead of the raw brand hue, contrast-floored at 3:1 (WCAG non-text) against the lightest dark tone controls commonly sit on (the house muted surface #262626 — rings draw outside controls, so clearing the lightest adjacent container tone clears the page background and card surface too, in both shipped packs) — desaturation alone can cost a deep accent the bar it cleared (#008000 fell from 3.9:1 to ~2.6:1 on the background; the floor lifts it back over 3:1 on background and card surface alike) — so the soundboard search field's ring no longer glares neon in dark mode; canvas.accentFocusRing exports the derivation so hand-authored token sets (the Zig soundboard's theme) state the identical ring.
  • Breaking: canvas.accentOverrides now takes the resolved ColorScheme alongside the accent — a deliberate break while the toolkit is pre-1.0, so the one function under the natural name states the scheme it layers over; pass .light to reproduce the previous output exactly.
  • Escape in a search field now reaches your core: Escape's clear (and its composition cancel) was a runtime-local editor operation — the field emptied on screen while the model kept the stale query and the list stayed filtered. Every keyboard-driven editor mutation now derives ONE edit that is applied to the retained editor AND stamped onto the dispatched event, so on-input hears Escape exactly like typing, paste, and the clear affordance — on both authoring tiers, and byte-identically under record→replay.
  • Automation and accessibility composition verbs ride the real input path: widget-action set_composition/commit_composition/cancel_composition now dispatch the same ime input events a live IME session produces (journaled, mirrored to the core), and set_selection reaches the core's selection mirror through the stamped-edit channel.
  • Accessibility actions replay without double-dispatch: a journaled assistive action (press, toggle, set_text, drag, ...) no longer also journals the key/text events its verb synthesizes — replaying the action re-derives them, so recorded AX-driven sessions replay each input exactly once instead of twice.
  • Direct verb calls journal outer-wins too: an embed host's widgetAction and automation widget_action commands now record the same single widget_accessibility_action record the platform accessibility path does (the enum gained the composition kinds), so replay re-runs the verb — focus included — instead of delivering its untargeted key/ime children to whichever field the session happened to leave focused.
  • Grids keep their declared column slots: children fewer than a grid's declared columns now keep the column-slot width and fill the leading slots, instead of stretching across the freed row — a search that narrows the soundboard album grid below its column count leaves image-forward tiles at their natural size.
  • "terminate request delivered" retires itself: the system monitor's delivery notice now clears on the next applied sample instead of sitting in the footer forever; failure notes keep sticking (both tiers).
  • System monitor footer says UTC: the sample-time stamp renders from the journaled clock in UTC, and the footer now labels it "UTC" instead of passing it off as local time — local rendering would need a journaled timezone channel to stay replay-byte-identical, so the label is the honest fix (both the Zig example and the TS port).

Contributors

v0.5.1

Choose a tag to compare

@github-actions github-actions released this 13 Jul 19:21
f7aa92a

Improvements

  • The TS scaffold's status bar earns its empty state: a fresh scaffold said "stamped: -1ms" until the first Stamp press; the template's markup now branches on {stampedMs < 0} and says "press Stamp for a timestamp" instead — teaching the if/else markup shape in the starter while it's at it.

Bug Fixes

  • Exact-fit text no longer elides under geometry pixel snapping on Windows: edge snapping rounds a frame's two edges independently, so a hug-sized text box at a fractional position could come back up to a full device pixel narrower than the label it was measured for — past the elision slack, so the TS scaffold's centered counter painted "…" instead of its digit at 100% scale. The wrap/elision budget (textWrapMaxWidth) now hands back the full snap quantum (1/scale, was 0.5/scale), and the epsilon policy is documented at the seam: painted width may exceed the snapped frame by less than 1/scale + text_elision_slack, always below the smallest real overflow (a glyph).
  • Spawn teardown crash window closed: the effects channel now joins every spawn, fetch, and file worker thread that converges before its teardown returns (previously it gave up after ~5s and abandoned them), so cancelling or quitting while a real child is still streaming can no longer leave a stale worker writing into freed memory.
  • Spawn cancel reaches the whole process tree: each spawned child runs in its own process group and cancel/teardown signals the group (POSIX), so shell-wrapped commands (sh -c "a; b") no longer leave orphaned grandchildren holding the stream open past the cancel.
  • Every worker class tears down bounded: spawn, fetch, and file workers now share one terminal guarantee — teardown returns within a budget and never frees memory a live thread can still touch. A file worker stuck in blocking I/O that nothing can converge (a write to a FIFO with no reader, a stalled network filesystem), or a spawn worker held hostage by a descendant that escaped the kill's process group (setsid daemonization, a shell's set -m background job) while holding the stdout pipe open, no longer hangs teardown: teardown interrupts the blocked syscall best-effort at half its budget and, past the full 15s, abandons the worker with one warning and a small deliberate leak. Everything an abandoned worker can still reach lives in process-lifetime storage, so the leak stays safe even when the app tears down the allocator behind the channel right after.
  • A fetch that cannot start cancellably is rejected, never run inline: when the executor cannot start the exchange as a cancelable task, the fetch now delivers one honest .rejected terminal instead of silently running an exchange that would evade cancel, the timeout, and teardown's join.
  • The npm-installed CLI carries its TypeScript toolchain: @typescript/typescript6 — and @typescript/old, the exactly-pinned alias of the real compiler its one-line entrypoint re-exports — are now regular dependencies of @native-sdk/cli, installed by npm in the same transaction as the CLI itself. The first native check|dev|build|test on a TypeScript app needs no network and no install step, and never runs npm: it works offline right after install, on read-only (system-owned) prefixes, and under NODE_ENV=production alike, because the bundled transpiler resolves the toolchain by node's own ancestor node_modules walk across every layout npm or pnpm produces. A repo checkout whose packages/core install hasn't happened yet is taught the one command (cd <sdk>/packages/core && npm ci --include=dev), and a direct zig build against an unresolvable toolchain fails with the same clean teaching and a resolved SDK path instead of a panic stack trace. TypeScript apps need Node.js 22.15+ (on the 23 line: 23.5+) on every layout — repo checkouts included, because every .ts module rides the same module.registerHooks type stripping: on a node without the hook the runner fails fast with a one-line upgrade teaching instead of node's raw ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING.
  • Drag headers lay out clear of the Windows caption buttons: on hidden-titlebar windows, a window-drag header whose app never consumed the chrome channel's trailing inset rendered right-aligned content UNDER the DWM min/max/close cluster (system-monitor-ts's status text was truncated by the caption punch-out). The runtime now detects the collision after layout and re-lays the view once with the cluster reserved (DesignTokens.window_controls, stamped like text_measure), so drag-header content stops at the cluster's edge on every app — markup and builder, Zig and TS — while headers that already pad through the chrome insets (soundboard's trailing spacer) keep a byte-identical layout. The same mechanism covers the macOS mirror (a leading traffic-light cluster). Anchored floating children of the drag header resolve against the cleared rect too, so an end-aligned floater moves out from under the buttons just like flow content; anchored children of non-drag widgets keep byte-identical placement.
  • Standard titlebars follow dark mode on Windows: windows with standard chrome now set DWMWA_USE_IMMERSIVE_DARK_MODE from the OS app color scheme — at creation (before first show, so a dark launch never flashes a light caption) and again on every appearance broadcast — so a dark-themed app no longer sits under a glaring white titlebar. Hidden-titlebar windows keep their higher-fidelity pixel-sampled caption color, and chromeless windows have no caption to tint.
  • Windows release exes are GUI-subsystem: native build (and therefore everything native package --target windows wraps) no longer produces console-subsystem binaries, so launching a packaged app never flashes a terminal window behind it. Debug exes keep the console — the dev loop's logs live there — and redirected logging (app.exe > log 2>&1) still works on GUI exes. Packaging now reads the exe's PE subsystem, warns loudly when a console-subsystem binary is wrapped (stale zig-out or hand-supplied --binary), and carries the finding in the package stats, pinned by tests over synthetic PE headers. The web-frontend scaffold's standalone build.zig (Next/Vite/React/Svelte/Vue and native init --full) emits the same release-only assignment, so scaffolded apps get the posture without the SDK build graph.

Contributors

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 13 Jul 03:47
e2627ee

New Features

  • TypeScript authoring — write app cores in TypeScript: native init now scaffolds a TypeScript app by default — src/core.ts (logic), src/app.native (view), app.zon (manifest), zero Zig to write — and the build transpiles the core to readable arena-backed Zig, so the shipped binary carries no JS engine and no GC and keeps the native dispatch path (~83ns per update). The @native-sdk/core package (the SDK cores import, plus the transpiler the CLI runs) publishes to npm with this release at the SDK's version. Zig cores remain first-class (native init --template zig-core), mixed TS-core + Zig-helper apps fall out of tree detection naturally, and a TypeScript app can eject to its emitted Zig at any time. The entries below are the pieces of this one feature (#119).
  • examples/ai-chat-ts — an AI chat client authored entirely in TypeScript + Native markup: a conversation UI over an OpenAI-compatible chat-completions endpoint as two subset modules and zero Zig — Cmd.fetch with routed {status, body} results, the JSON wire format as pure byte math (src/api.ts encodes requests and parses choices[0].message.content / error.message, refusing anything malformed), conversation history in the Model, the composer on the SDK byte-splice text engine, endpoint/model/key through the env channel (no baked endpoint, no key anywhere in the tree — a teaching state until all three arrive), a model-level in-flight guard, and honest failed states that keep the history with a Retry. The e2e battery (tests/ts-core/ai_chat_e2e_tests.zig, in zig build test-ts-core-e2e) pins the exact request bytes turn by turn and replays a recorded conversation — transport failure and retry included — byte-identically with zero network; the README states the v1 boundaries plainly (buffered responses, compile-time fetch headers) (#119).
  • Docs: "Where Packages Go" (/typescript/packages): the four first-class patterns behind "can I use npm?" — HTTP/AI APIs through Cmd.fetch (with a complete transpile-checked core sample), npm-heavy UIs as embedded web frontends, Node libraries as Cmd.spawn sidecars, and pure utilities vendored under src/ or imported from the curated @native-sdk/core/* channel — with the core's no-npm boundary stated as the thing that buys replay, headless testing, and native dispatch speed (#119).
  • Eval wave 2 — six dual-track authoring cases, one language-blind spec each: the eval harness gains "frontend": "app-dual" cases that run the SAME realistic ask on both authoring tracks — <case>@ts scaffolds a full TypeScript app (native init --template ts-core), <case>@zig the Zig app template — graded by shared checks plus per-track behavioral harnesses asserting one spec: fetch-JSON-into-a-sortable-table, debounced notes autosave (starter provided), a pomodoro timer with a completion sound, a seeded stale-cache delete bug to root-cause, a module split with byte-exact CSV export, and a shell-command system-info panel. The ts harnesses decode the Cmd/Sub wire format (evals/harness-lib/cmdview.zig); the zig harnesses ride the SDK's fake effects executor inside the workspace's own native test graph. --track ts|zig selects a lane; each track gets its current skills (ts-core+native-ui vs native-ui+zig), and pnpm metrics now reports per-track teaching-error encounters alongside first-pass compliance and the violation taxonomy (#119).
  • Transpiler: two real-app fixes the wave surfaced: byte-element stores of computed values (buf[i] = src[j]) now emit with JS's exact ToUint8 wrap instead of stopping as invalid Zig, and records referenced from a declared text-input mirror union stay by value even when the core also stores its editor state in the Model — previously that pointer promotion silently broke on-input resolution (runtime view build and native check's model contract alike) for any core keeping a TextEditState in its model (#119).
  • examples/soundboard-ts — the soundboard authored entirely in TypeScript + Native markup: the launch-bar port of the Zig soundboard as three files of truth and zero Zig — src/core.ts (the committed catalog as const tables, REAL audio through the Cmd.audioPlay stream with the engine's local-then-URL cascade, the play-next queue, Copy Title on the clipboard, a motion-gated Sub.timer playback clock, and search through the full byte-splice text engine), src/app.native (the whole view: grid, album detail, songs library, context menus, the now-playing transport), and app.zon. An end-to-end suite (zig build test-ts-core-e2e) drives the shipping core and markup through playback, auto-advance, the stale-event window, volume, clipboard, search, record→replay, and a dispatch-latency budget; the README carries an honest ledger of where the port diverges from the Zig original (#119).
  • Generated TS wiring resolves the theme and the audio cache: app.zon's .theme pack now reaches TypeScript apps (composed with the live system appearance), and the platform caches directory is resolved at launch so a core's URL audio playback caches under the conventional content-addressed path with no cachePath in the core (#119).
  • Transpiler emit-contract fixes: the global undefined VALUE now emits the optional empty (null) — it previously emitted Zig undefined, uninitialized memory — and an early-exit null guard whose narrowed value goes unread emits a plain null test instead of an unused (uncompilable) capture (#119).
  • examples/system-monitor-ts — the system monitor authored entirely in TypeScript + Native markup: the spawn-showcase port of the Zig system monitor as three files of truth and zero Zig — src/core.ts (the 2 s sampling cadence as a declarative Sub.timer, collect-mode Cmd.spawn for ps/vm_stat/meminfo, pure byte parsers over the collected stdout, the exact top-128-by-CPU selection, the confirmed SIGTERM action, and a runtime boot probe that discovers the host's sampler conventions where the Zig original switches at comptime — with the honest "no sampler for this OS" state when nothing answers), src/app.native (the whole view: chromeMsg-driven hidden-inset header, <chart> sparklines over the core's NaN-padded windows, the real table register with per-row context menus and controlled scroll, the modal confirmation), and app.zon. An end-to-end suite (zig build test-ts-core-e2e) drives the shipping core and markup through the probe cascade, the Zig example's committed real captures, the timer cadence with pause, search/sort, the kill round trip, and record→replay; the README carries an honest ledger of where the port diverges from the Zig original (#119).
  • Markup chart series bind f64 iterables: <series values="{binding}"> now accepts []const f64 model fields, decls, and fns alongside f32 — transpiled TS cores carry every number array as f64, so markup charts were unreachable from a TS model — narrowed per sample into the f32 chart pipeline in both engines, the validator, and native check's model contract (#119).
  • The everyday string methods on core bytes — familiar spellings, byte-honest semantics: TypeScript cores can now write s.toUpperCase(), s.trim().toLowerCase().includes(q), s.repeat(n), s.padStart(w), s.split(sep), s.startsWith(p), s.indexOf(t)/s.lastIndexOf(t), and s.at(i) directly on Uint8Array text. Every length/offset is a BYTE measure, search is byte-wise (includes/indexOf/lastIndexOf dispatch by argument type: bytes needle = substring search, number = the TypedArray element search), case mapping is Unicode 17 SIMPLE case mapping (locale-free, generated tables — 3.1 KiB in the binary — with invalid UTF-8 passing through unchanged), trim strips the exact JS whitespace set over UTF-8, and split returns a locally-owned Uint8Array[]. Native lowers each call onto rt kernel helpers; node runs the same methods from the same generated tables (devhost polyfill), so both runtimes produce identical bytes by construction — pinned by run-fidelity cases across Greek/Cyrillic casing, growth mappings, invalid-UTF-8 passthrough, repeat/pad edges, and split shapes, plus a machine-checked method matrix (#119).
  • The stays-out spellings teach their reason: charCodeAt/charAt/codePointAt/normalize/replace/replaceAll on bytes teach the new NS1060 (byte text speaks the byte-honest method set), the locale family teaches NS1005, and the regex-taking methods teach NS1040 — never a bare "property does not exist". trimAsciiSpaces stays for LF-preserving line parsing; .trim() is the canonical whitespace trim (#119).
  • TS cores: generics, local function values, and the complete-language tail: user-declared generic functions, interfaces, and type aliases now compile via per-call-site monomorphization from tsc's own resolved type arguments — one readable Zig fn per distinct instantiation (pick__Task, pick__f64), deduped, covering records/unions/arrays/optionals, recursion inside a generic, generics calling generics, and structural instantiation of generic types (Box<Task>Box__Task); unresolvable call sites teach the new NS1053 (#119).
  • Const-bound local function values hoist: const scale = (x: number): number => x * 3; (arrow or function expression) becomes an ordinary module-level fn when capture-free and fully annotated, usable by direct call (recursion included) or as an array-method callback (xs.map(scale)); captures, reassignment, storing/returning the value, and record-field calls teach the new NS1054 — and capturing a locally-owned array ends its ownership at the capture (NS1051 machinery) (#119).
  • The small-fry tail lands with node-byte-identical pins: for (const [i, x] of xs.entries()) (the destructured-pair loop form), ++/--/assignments in provably order-exact value positions (arr[i++], const n = ++count), ?.[i] and ?.method() optional-chain hops on supported receivers, plain `numb...
Read more

v0.4.4

Choose a tag to compare

@github-actions github-actions released this 11 Jul 18:23
ce3e42d

New Features

  • Native-only host builds (Windows): the build graph now infers web use from app.zon — a .frontend block, the "webview" capability, a .shell webview view, or the Chromium engine — and an app that declares none of them compiles its Windows host without the embedded WebView layer: no WebView2 header, no WebView2Loader.dll installed, staged, or referenced by the executable. A new .webview_layer = "auto"|"include"|"exclude" manifest field (and -Dweb-layer) overrides the inference, an exclude that contradicts a web declaration is rejected at validate, configure, and package time, and a native-only build that reaches webview creation at runtime fails fast with a teaching WebViewLayerNotBuilt error. native check and the package report print the web-layer verdict, and a CI cross-audit asserts the presence/absence of the loader reference in real cross-compiled executables (#107).
  • Native-only host builds (Linux): the WebKitGTK compile seam mirrors the Windows one — an app whose app.zon declares no web use compiles its GTK host with NATIVE_SDK_ALLOW_WEBKITGTK_STUB, so the executable neither links webkitgtk-6.0 nor references any webkit_/jsc_ symbol, building needs no WebKitGTK development package, and users need no libwebkitgtk at runtime. The web-layer auditor (tools/audit_web_layer.zig) grew a hand-rolled ELF reader (DT_NEEDED entries + dynamic symbols) that CI runs both ways: the native-only fixture must scan clean even with the dev package installed, and the Linux canvas smoke now builds on a runner without WebKitGTK at all. native package refuses to package a WebKitGTK-linking binary under a native-only decision, record→replay and automation-driven sessions are pinned on native-only apps, and the macOS GPU dashboard smoke asserts a native-only app spawns zero WebKit helper processes (#110).

Improvements

  • Zig 0.16 guidance: a new zig skill (native skills get zig) maps each pre-0.16 std idiom's compile error to the current one — std.Io file IO and writers, unmanaged ArrayList, main(std.process.Init), spawning, clocks, {t}/{f} formatting, build.zig modules — with the same content for humans as the docs' Zig 0.16 Notes page; the native-ui skill carries the short table, and a failing native build|test|dev now points at the catalog when std members come up missing (#105).
  • Lazy Linux WebView startup: GTK windows now create only GTK chrome at window creation and materialize the main WebKitWebView on first web use, so canvas-only apps do not start WebKit processes on Linux; child-WebView bridge responses no longer require a main WebView to exist (#106).

Contributors

v0.4.3

Choose a tag to compare

@github-actions github-actions released this 10 Jul 19:27
c5bb87a

Improvements

  • Linear-light edge blending: anti-aliased fringes on opaque rounded rectangles, path fills, and strokes now composite through a linear-light coverage path, removing the dark rims that sRGB blending produced on curved geometry while keeping opaque interiors, glyph coverage, and translucent overlays byte-identical (#89).

Bug Fixes

  • Single-line fields handle overflowing values: text, selection rects, composition underlines, and the caret now clip to the field's content rect, and a horizontal scroll offset keeps the caret visible — typing past the edge scrolls the value, Home scrolls back, and deleting never leaves trailing emptiness. Covers text fields, inputs, search fields, and comboboxes; values that fit render exactly as before (#90).
  • Cross-drive apps on Windows: native dev|build|test no longer fails with expected path relative to build root; found absolute path when the app and the npm-installed SDK live on different drives — the generated build graph now bridges volumes with a .native/sdk directory junction (no admin rights needed) and keeps the zon dependency relative; the junction is retargeted automatically when the SDK moves or upgrades. Where the bridge cannot apply (native eject, full-shape native init, or a filesystem that refuses junctions), the CLI explains the cross-volume constraint and both ways out instead of writing a build Zig would reject (#92).

Contributors

v0.4.2

Choose a tag to compare

@github-actions github-actions released this 10 Jul 04:49
20bc1eb

Improvements

  • Windows rendering is DPI-aware and sharper: Windows apps now declare Per-Monitor V2 DPI awareness, each window carries its own device scale, and canvases, native child views, hidden-titlebar sizing, and explicit WebView frames re-render/re-round correctly when moved across mixed-DPI monitors (#81).
  • Smoother canvas geometry: rounded-rect fills and strokes now render through continuous coverage while eligible hairline borders snap to crisp device-pixel columns, so arcs stay anti-aliased and 1px borders stay sharp under the default house and Geist packs (#81).
  • Canonical package and documentation metadata: npm package metadata, release automation, docs, templates, and examples now point at the renamed vercel-labs/native repository and native-sdk.dev; version:sync stamps repository/homepage metadata into platform packages and version:check rejects drift before publish (#78, #80).

Bug Fixes

  • Windows embedded WebView is real from a plain checkout: the WebView2 SDK header and loader are vendored under third_party/webview2/ (BSD-licensed), every build graph puts the header on the include path, and the host now refuses to compile with the WebView layer silently stubbed — previously every Windows build shipped the stub and WebView loads reported WebViewNotFound at runtime (#86).
  • WebView2 host conformance fixes: a missing lambda capture in the bridge message handler, a mingw-compatible WRL event-handler factory, an EventToken shim, and STA COM initialization on the host thread let WebView2 environment creation and bridge messaging run on Windows (#86).
  • WebView2Loader.dll ships with the app: zig build installs the architecture's loader next to the executable, zig build run resolves it during dev runs, generated frontend/package commands carry NATIVE_SDK_PATH, and native package --target windows includes the loader in the artifact (the Evergreen WebView2 runtime itself is preinstalled on current Windows) (#86).
  • Checkbox marks use the vector core: checked boxes now draw one stroked polyline with round caps and joins instead of two aliased diagonal lines, and stroke caps ride the GPU packet path so the host and reference renderer agree (#87).
  • Path geometry lifetimes are owned by the builder: chart, spinner, and checkbox path commands no longer borrow threadlocal frame scratch, so separately emitted trees cannot alias each other's path elements (#87).

Contributors

v0.4.1

Choose a tag to compare

@github-actions github-actions released this 09 Jul 12:49
bd8fb61

Bug Fixes

  • npm package assets: Ship the SDK's root assets/ directory in @native-sdk/cli so installed packages include assets/native-sdk.manifest, the default macOS icon, and entitlements needed by generated apps (#72, #75).

Contributors

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 08 Jul 23:58
b471110

New Features

  • zero-native is now the Native SDK: The toolkit, CLI, and packages are renamed end to end — the CLI binary is native, the Zig module and build helper are native_sdk (native_sdk.addApp, native_sdk.addMobileLib), the embed C ABI prefix is native_sdk_*, and the npm CLI package is @native-sdk/cli.
  • Native-rendered apps by default: native init scaffolds a native-rendered app — a declarative .native markup view plus Zig logic on the UiApp runtime (a Model, a Msg union, update, and a view) — with web frontends still available via --frontend next|vite|react|svelte|vue.
    • Native markup: HTML-inspired views with flex layout, {bindings} to model fields and functions, typed on-* message dispatch, for/if/else structure tags (multi-child for bodies, <else> empty states), and keyed identity; a deliberately closed grammar keeps logic in Zig.
    • Comptime compilation: views compile at build time into direct field access — release binaries carry no parser, and markup or binding mistakes are compile errors with line and column.
    • Hot reload: dev builds watch every .native file — imported components and fragments embedded in Zig views included — and update the running window in place, preserving model state, selection, and widget identity.
    • Expressions in bindings: arithmetic, comparisons, boolean logic, string concatenation, and a closed 17-function formatting library (fixed, thousands, date/time, pad, plural, ...), evaluated bit-identically by both markup engines; string-producing model functions bind directly through the build arena.
    • Cross-file components: <import> splices template files (transitively, with cycle and duplicate diagnostics), template args take literal defaults, <slot/> marks where use-site children land, and native eject component transfers a library composite's canonical source into your app exactly once.
    • canvas.Ui, the programmatic builder under the markup: structural widget identity, typed message handlers, flex-first layout, and per-element opacity/transform render channels for animated composition.
  • The model–view contract, checked in both directions: native check verifies every binding path, iterable, key, message tag, payload type, and expression in every .native file against the app's reflected Model/Msg surface in milliseconds — with did-you-mean suggestions and a dead-state lint for model fields and messages no view uses.
  • Markup tooling: native markup check (instant validation with positions), a language server (diagnostics, completion, hover), a TextMate grammar with editor setup, native markup dump over the canonical serialized document format, and the native-ui agent skill — the complete authoring reference, served through the skills CLI.
  • Two-way tooling: native automate provenance reports where a live widget was authored (file, byte span, template instantiation chain), and native automate edit writes minimal-diff attribute and text edits back into the markup source — validated before anything touches the file, with hot reload closing the loop.
  • Full component catalog: every built-in component is expressible in markup — tabs, tables, dialogs, drawers, sheets, selects, comboboxes, accordions, menus, badges, avatars, tooltips, inputs, and more — implemented in both engines with parity tests, alongside new composites in markup and Zig:
    • Charts (<chart> / ui.chart): line, area, bar, and band series drawn through the vector path pipeline with design-token colors, deterministic downsampling past 256 points, axis labels on a nice-step lattice, and pointer hover details.
    • Markdown (<markdown> / native_sdk.markdown): a GitHub-flavored subset — headings, inline styles, links, lists, task lists, fenced code, blockquotes, pipe tables, autolinks, and model-driven collapsibles — that degrades malformed input to text and never fails a build.
    • Disclosure trees with the full ARIA tree keymap, steppers and timeline items, input groups with focus-within rings, chat bubbles with reaction pills and thread-width caps, and a ui.nav push/pop page container with stable per-page state.
    • Resizable split panes with model-owned fractions, keyboard and assistive resize, and optional eased animation on model-driven moves.
    • Windowed virtual lists: viewport-sized widget budgets at 100,000 items, variable row extents that converge to measured truth without visible jumps, tail anchoring for chat transcripts, and on_reach_end/on_reach_start for infinite fetch and history loading.
    • Anchored floating surfaces (dropdowns, selects, popovers) that float above the tree with edge auto-flip; dismissal (Escape, click-outside, assistive dismiss) is a Msg the model owns, and focused selects get the full open/navigate/commit keymap.
    • Vector icons: an SVG stroke-icon subset parser, 50 curated built-in icons, leading or trailing icon slots on buttons, toggle chips, list and menu rows, badges, and timeline items, app-registered icons comptime-parsed from your own SVGs, model-bound icon names, and a loud missing-icon fallback.
  • Text engine:
    • Inline styled spans — weight (resolved to real faces), italic, monospace, color tokens, underline, strikethrough, size scale, per-span backgrounds, and hit-testable links — wrap as one paragraph in Zig and markup alike.
    • Honest single-line text: unwrapped text elides with a trailing ellipsis by default, an overflow policy knob keeps the deliberate hard cut available, and word wrap is an explicit opt-in — paint always agrees with measurement.
    • heading/display typography rungs on the token ladder, first-class text alignment, and fixed grid column counts.
  • Selection and clipboard: cmd/ctrl+C/X/V in editable fields through the platform clipboard, click-drag selection with copy on static text (surviving rebuilds, exposed to semantics and automation), and clipboard effects for app code.
  • Interaction model:
    • Presses fall through to the nearest pressable ancestor, so any element with a handler is a real hit target — nested pressables resolve to the deepest one, and text selection still works inside pressable rows.
    • Press-and-hold, double-click, Enter as a list row's primary action, and an app-level key fallback (Options.on_key) with pinned precedence — quiet list rows stay transparent to app-owned selection models.
    • Source-driven autofocus, observable typed scroll events (on_scroll), a built-in search-field clear affordance, and a quiet-hover style knob for content tiles.
  • Effect system: the update loop's command half — update gains an effects channel of bounded, key-addressed effects that deliver exactly one terminal Msg each and are fully testable against a deterministic fake executor:
    • fx.spawn runs subprocesses with streamed lines or whole-output collect mode (stderr tail included), raisable per-effect line bounds, and cancellation; fx.fetch runs HTTP(S) requests with an explicit failure taxonomy, timeouts, and a streaming response mode for line-oriented endpoints.
    • fx.readFile/fx.writeFile persistence, fx.startTimer/fx.cancelTimer, fx.writeClipboard/fx.readClipboard, fx.registerImageBytes for runtime images, fx.closeWindow/fx.minimizeWindow, and the init_fx boot hook so loading states are in the very first paint.
    • A facade time API (nowMs, monotonicMs) plus Clock/TestClock seams for deterministic time-dependent logic.
  • Audio, end to end on five platforms: fx.playAudio with full transport (pause, resume, stop, seek, volume), real decoded durations, position ticks, and honest completion and failure reports — AVFoundation on macOS, Media Foundation on Windows, GStreamer on Linux, and the experimental mobile hosts on iOS (AVFoundation) and Android (MediaPlayer).
    • Streaming with a verified track cache: URL sources resolve local file, then size-verified cache, then progressive stream (filling the cache in parallel for the next play), with honest buffering states and explicit failures — never a silent stall.
    • Real spectrum analysis on macOS, Windows, and Linux: 32 log-spaced bands at ~25 Hz from the app's own playback, journaled at the effect boundary so record/replay repaints identical bars; hosts that cannot analyze report the capability honestly instead of fabricating bands.
  • Images: a platform decode seam (CGImageSource, gdk-pixbuf, WIC) so the toolkit bundles no image decoders; runtime image registration renders through every path — GPU packets, software presentation, and screenshots — with pixels riding an out-of-band upload channel so image-bearing frames stay on the GPU path; avatars take a bound image with initials fallback.
  • Windowing and chrome: model-declared secondary windows (presence is visibility; a user close dispatches a Msg), enforced window minimum sizes, and present-before-show so a canvas window never appears blank.
    • Titlebar control on all three desktops: hidden_inset, a tall unified-toolbar variant, and fully chromeless styles; markup window-drag regions; and an on_chrome hook carrying the real overlay insets and control-cluster frames — with real system window controls preserved on Linux client-side decorations and Windows DWM caption buttons.
    • Native context menus, declared per widget in Zig or markup (<context-menu>): the real OS menu where one exists, an anchored canvas surface elsewhere, editable-text cut/copy/paste defaults, and full automation support for enumerating and invoking items.
    • A menu-bar status item with model-driven title and menu; canvas and WebView panes composed in one window; adoption of app-owned native views into the layout (adoptViewSurface); and native scroll drivers on macOS that give every scroll region OS momentum, rubber-band overscroll, and the system overlay scrollbar with zero app code.
  • **Experimental iOS and Android host tiers — the toolkit...
Read more

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 24 Jun 23:20
7c8df23

New Features

  • Keyboard shortcuts: Add app-level keyboard shortcuts with manifest and runtime configuration, native delivery to Zig Event.shortcut, and typed JavaScript window.zero shortcut events (#62).
  • Manifest-driven runner shortcuts: Load app.zon shortcuts automatically in generated runners, with a RunOptions.shortcuts override for apps that build shortcut lists in Zig (#62).

Improvements

  • Shortcut documentation and validation: Document the app.zon shortcut schema, portable key names, modifier behavior, backend support, and validation limits (#62).
  • Windows WebView2 child bridges: Enable bridge-enabled trusted child WebViews on Windows WebView2, bringing that backend closer to the macOS and Linux system WebView behavior (#62).

Bug Fixes

  • Shortcut matching and delivery: Fix shortcut modifier handling, shifted punctuation matching, backend event routing, and edge cases across AppKit, GTK, WebView2, and macOS CEF (#62).

Contributors