liminal.rip/slag

· Tangled

4 min read Original article ↗

9

Configure Feed

Select the types of activity you want to include in your feed.

Scheme+GPUI scriptable native rust UI framework with hot-reloading

9

Configure Feed

Select the types of activity you want to include in your feed.

9 1 1

Clone this repository

https://tangled.org/liminal.rip/slag https://tangled.org/did:plc:du52dqrtr7hu4zkidnwp732a

git@tangled.org:liminal.rip/slag git@tangled.org:did:plc:du52dqrtr7hu4zkidnwp732a

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz Download .zip

Commits 9

A #:scroll-y container can declare #:stick-bottom #t to follow its bottom
edge while content grows (chat logs). The decision happens in
ensure_scrolls, before the new layout, so the handle still holds last
frame's offsets: a container within ~4px of its bottom — or never laid
out, when both offset and max are zero — is re-pinned via gpui's
scroll_to_bottom flag, which the element consumes in its next prepaint,
after this render's layout, where the new bottom is known. A reader who
scrolled up fails the check and stays put until they return to the
bottom.

The new scroll-to-bottom! builtin (jump-to-latest buttons, snapping a log
down after a route change) rides the widget command queue as a one-shot
Command::ScrollToBottom; its drain interns the handle rather than looking
it up — handles are just shared offsets, no Window needed — so a command
aimed at a container that first appears on the very next render still
lands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

An input can now declare #:multiline #t (plus optional #:rows-max, default
8): the InputState is built in auto-grow mode from one row, with
submit_on_enter so plain Enter still dispatches on-submit while Shift-Enter
inserts the newline itself.

Programmatic set/clear rides a new reserved-slot command queue
(host::CMDS_KEY), following the nav-slot pattern: the input-set! /
input-clear! builtins only write state — widget entities are unreachable
while the engine is borrowed — and the component drains the queue where a
Window is in hand (after click handlers, and at the top of each render, which
also covers the windowless effect/subscription paths via their cx.notify()).
Applying a command pushes the value into the live InputState and syncs
input-<key> to match; commands for keys with no live entity drop harmlessly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

gpui's img element writes the image's aspect ratio into its own layout
style, which overrides size_full in taffy: the element lays out at full
width times aspect height, overflows its box, and the clip eats the
bottom of every frame narrower than the box - object-fit never runs
against the real box, so #:fit 'contain silently rendered as a top-
anchored crop. Paint the frame directly in a canvas with ObjectFit
math against the true bounds instead, and pin the decode path with a
test that runs a real quoted symbol through the Steel engine.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

README.md

A Steel (Scheme) component framework for gpui + gpui-component. Screens are .scm files that build a plain-data element tree; slag decodes it once per frame into real gpui elements. Saving a file hot-reloads in ~10 ms.

(require "slag/ui.scm")
(require-builtin hello/host)

(provide render init)

(define (init) (state-set! 'count 0))

(define (render)
  (v-flex #:gap 8 #:p 16
    (text "Hello from slag" #:size 'lg)
    (text (number->string (state-ref 'count)))
    (button 'bump #:label "Bump"
            #:on-click (lambda () (state-set! 'count (+ 1 (state-ref 'count)))))))

examples/hello is the full wiring — host module, runtime, config, component, shell — in ~60 lines of Rust.

  • slag-core — gpui-free: element model, sandboxed Steel runtime, host registry, effects, packs, fixtures. Tests run headless.
  • slag-gpui — the gpui layer: component entity, materializer, string-keyed screen shell, hot reload.

Build notes for consumers (this workspace demonstrates all three):

  • Declare gpui deps as unpinned zed git URLs so cargo unifies them with gpui-component's, and pin the rev in your lockfile (match this repo's).
  • On Windows, carry the [patch] for vendor/gpui_windows and -C link-arg=/STACK:16777216 in .cargo/config.toml.