ui

SwiftUI-style retained UI framework bootstrap for Kit using SDL and WGPU

Files

FileDescription
.editorconfigEditor formatting configuration
.gitignoreGit ignore rules for build artifacts and generated files
.tool-versionsasdf tool versions for local development
examples/focus-scroll-smoke.kitPure smoke example for focus, scroll, and page-scroll behavior
examples/framework-loop.kitDeterministic retained UI smoke example for commands, model updates, layout, and hit testing
examples/layout-smoke.kitMinimal layout and hit-test smoke example
kit.tomlPackage manifest with metadata, dependencies, capabilities, and tasks
src/core.kitPure retained UI core: node builders, layout, hit testing, commands, focus, scroll, and runtime reducers
src/internal/types.kitPublic UI data types shared by the core and window runtime
src/main.kitSDL + WGPU retained window runtime and public package entry point
tests/ui-core.test.kitUnit tests for the pure retained UI core

Dependencies

PackageSourcePurpose
sdl../kit-sdlWindow creation, events, input state, cursors
wgpu../kit-wgpuSurface setup and retained panel drawing

This package declares the ffi capability because the public entry point imports SDL and WGPU. The pure core in src/core.kit can be imported directly for tests and non-window smoke examples.

Installation

kit add gitlab.com/kit-lang/packages/kit-ui.git

Usage

Use Kit.UI when you want the full package entry point, including the SDL/WGPU window runner:

import Kit.UI as UI

render-scene = fn(model) =>
  {
    background-top: UI.color 0.06 0.08 0.11 1.0, 
    background-bottom: UI.color 0.11 0.14 0.19 1.0, 
    root: UI.button "save-button" "save" UI.default-panel-style
  }

update-model = fn(model, clicked-id) =>
  if clicked-id == "save" then
    {status: "saved"}
  else
    model

main = fn =>
  initial-model = {status: "idle"}
  UI.run-app-window "Kit UI Demo" 800 480 initial-model render-scene update-model

main

For pure layout, hit testing, reducers, and parity-friendly tests inside this package, import the core module directly:

import "../src/core.kit" as UI

main = fn =>
  scene = {
    background-top: UI.color 0.0 0.0 0.0 1.0, 
    background-bottom: UI.color 0.1 0.1 0.1 1.0, 
    root: UI.button "save-button" "save" UI.default-panel-style
  }

  panels = UI.layout-panels scene 640 360
  println "panel-count=${show (List.length panels)}"
  println "hit=${UI.hit-test-scene scene 640 360 40.0 40.0}"

main

Core concepts:

  1. Build retained Node trees with panel, button, hstack, vstack, flex, overlay, inset, clip, and scroll-viewport-id.
  2. Use layout-panels, render-panels, hit-test-scene, and hit-test-viewport for pure layout and interaction queries.
  3. Track interaction with initial-runtime-state, reduce-state, pointer/focus/scroll input constructors, and consume-runtime-effects.
  4. Dispatch semantic actions with command, command-handler, command-updater, command-shortcut-mapper, and command button helpers.
  5. Keep scroll position in app model state with viewport-scroll-y, set-viewport-scroll-y, and the viewport update helpers.

Development

Running Examples

Run a pure smoke example with the interpreter:

kit run examples/layout-smoke.kit

Compile and execute a smoke example:

kit build examples/layout-smoke.kit -o /tmp/kit-ui-layout-smoke && /tmp/kit-ui-layout-smoke

Run interpreter/compiler parity across all examples:

kit parity --no-spinner --failures-only

Running Tests

Run the test suite:

kit test

Run the test suite with coverage:

kit test --coverage

Running kit dev

Run the standard development workflow:

kit dev

This will:

  1. Check formatting
  2. Type check sources in src/
  3. Type check examples in examples/
  4. Run tests in tests/ with coverage

Generating Documentation

Generate API documentation from doc comments:

mkdir -p docs
kit doc src/main.kit -o docs/ui.html
kit doc src/core.kit -o docs/core.html

Note: Kit sources with doc comments (##) generate HTML documentation.

Cleaning Build Artifacts

Remove generated files, caches, vendored package copies, and build artifacts:

kit task clean

Note: Defined in kit.toml. This also removes local artifacts such as parity result files, coverage.info, kit-lock.json, and kit-packages/.

Capability Notes

Window-running APIs in src/main.kit use SDL and WGPU through FFI. If you run under explicit capability restrictions, allow FFI:

kit run my-ui-app.kit --allow=ffi

Pure core examples import src/core.kit and do not need the SDL/WGPU runtime.

Local Installation

To install this package locally for development:

kit install

This installs the package to ~/.kit/packages/@kit/ui/, making it available for import as Kit.UI in other projects.

License

This package is released under the MIT License. See the license field in kit.toml.

Exported Functions & Types

run-window

Run the first framework-loop demo window on the current macOS SDL + WGPU path.

String -> Int -> Int -> Scene -> Unit

run-app-window

Run a retained app window with caller-owned model state.

String -> Int -> Int -> a -> (a -> Scene) -> (a -> String -> a) -> Unit

run-command-app-window

Run a retained app window that dispatches click actions through command handlers.

String -> Int -> Int -> a -> (a -> Scene) -> [b] -> Unit

run-command-app-window-with-key-command

Run a retained app window that maps key presses to command action ids.

String -> Int -> Int -> a -> (a -> Scene) -> [b] -> (Int -> Int -> String) -> Unit

run-command-app-window-with-shortcuts

Run a retained app window that dispatches command handlers and keyboard shortcuts from command metadata.

String -> Int -> Int -> a -> (a -> Scene) -> [b] -> Unit

run-scroll-app-window

Run a retained app window with caller-owned wheel-driven model updates.

String -> Int -> Int -> a -> (a -> Scene) -> (a -> String -> a) -> (a -> Scene -> String -> Int -> Int -> a) -> Unit

run-scroll-command-app-window-with-shortcuts

Run a retained command app window that dispatches handlers, shortcuts, and wheel-driven model updates.

String -> Int -> Int -> a -> (a -> Scene) -> [b] -> (a -> Scene -> String -> Int -> Int -> a) -> Unit

run-scroll-command-app-window-with-shortcuts-and-focus-update

Run a retained command app window that also syncs model state after focus navigation events.

String -> Int -> Int -> a -> (a -> Scene) -> [b] -> (a -> Scene -> String -> Int -> Int -> a) -> (a -> Scene -> RuntimeState -> Int -> Int -> a) -> Unit

color

Construct a normalized RGBA color.

Float -> Float -> Float -> Float -> Color

viewport-scroll-y

Return the stored vertical scroll offset for a viewport id, defaulting to zero.

List ViewportOffset -> String -> Int

set-viewport-scroll-y

Return viewport offsets with the given viewport's vertical scroll replaced.

List ViewportOffset -> String -> Int -> List ViewportOffset

update-hovered-viewport-scroll

Apply a wheel delta to the hovered viewport using a caller-provided max-scroll resolver.

List ViewportOffset -> String -> Int -> Int -> (String -> Int) -> List ViewportOffset

update-hovered-viewport-scroll-in-scene

Apply a wheel delta to the hovered viewport using measured overflow from the current scene.

List ViewportOffset -> Scene -> Int -> Int -> String -> Int -> Int -> List ViewportOffset

update-targeted-viewport-page-scroll-in-scene

Apply a page-sized scroll to the hovered or focused viewport using measured scene geometry.

List ViewportOffset -> Scene -> Int -> Int -> RuntimeState -> Int -> List ViewportOffset

reveal-panel-in-scene

Update viewport offsets so the given panel is visible inside its containing viewport.

List ViewportOffset -> Scene -> Int -> Int -> String -> List ViewportOffset

reveal-focused-panel-in-scene

Update viewport offsets so the currently focused panel is visible inside its containing viewport.

List ViewportOffset -> Scene -> Int -> Int -> RuntimeState -> List ViewportOffset

default-panel-style

Default panel style for the bootstrap scene.

PanelStyle

panel

Construct a retained panel node.

String -> PanelStyle -> Node

button

Construct a retained button node with a semantic action id.

String -> String -> PanelStyle -> Node

disabled-button

Construct a retained button node that does not participate in input.

String -> String -> PanelStyle -> Node

command

Construct an enabled semantic command.

String -> Command

disabled-command

Construct a disabled semantic command.

String -> Command

with-command-enabled

Return a command with the requested enabled state.

Command -> Bool -> Command

with-command-shortcut

Return a command with the requested keyboard shortcut.

Command -> Int -> Int -> Command

command-id

Return the semantic action id for a command.

Command -> String

is-command-clicked?

Return true when the clicked action matches the command.

String -> Command -> Bool

command-handler

Construct a command handler record.

Command -> (a -> a) -> {command: Command, update: (a -> a)}

update-model-for-command

Apply the first matching command handler for a clicked action.

a -> String -> List {command: Command, update: (a -> a)} -> a

command-updater

Construct a reusable update-model function from command handlers.

List {command: Command, update: (a -> a)} -> (a -> String -> a)

command-shortcut-mapper

Construct a key-command mapper from command handlers with shortcut metadata.

List {command: Command, update: (a -> a)} -> (Int -> Int -> String)

command-button

Construct a retained command button with explicit enabled state.

String -> String -> Bool -> PanelStyle -> Node

command-button-for

Construct a retained button from a semantic command value.

String -> Command -> PanelStyle -> Node

toggle-command-button

Construct a command button that switches style based on a toggle state.

String -> Command -> Bool -> PanelStyle -> PanelStyle -> Node

vstack

Construct a vertical stack node.

Float -> List Node -> Node

hstack

Construct a horizontal stack node.

Float -> List Node -> Node

flex

Construct a weighted child that receives proportional stack space.

Float -> Node -> Node

spacer

Construct a weighted empty stack child that only occupies space.

Float -> Node

offset

Construct a translated child wrapper.

Float -> Float -> Node -> Node

clip

Construct a clipped viewport wrapper.

Node -> Node

scroll-viewport-id

Construct an identified clipped viewport with a content offset.

String -> Float -> Float -> Node -> Node

scroll-viewport

Construct a clipped viewport with a content offset.

Float -> Float -> Node -> Node

overlay

Construct an overlay node.

List Node -> Node

inset

Construct an inset node.

Float -> Float -> Float -> Float -> Node -> Node

initial-runtime-state

Construct the initial runtime state for a retained scene.

RuntimeState

clean-runtime-state

Clear the dirty flag after a rendered frame while preserving one-shot outputs.

RuntimeState -> RuntimeState

clear-runtime-clicked-id

Clear the clicked id while preserving the rest of the runtime state.

RuntimeState -> RuntimeState

settle-runtime-state

Clear one-shot outputs after the caller has consumed them.

RuntimeState -> RuntimeState

consume-runtime-effects

Consume runtime outputs and return the settled state for the next frame.

RuntimeState -> RuntimeEffects

layout-panels

Flatten a retained scene into draw-order panel frames.

Scene -> Int -> Int -> List LaidOutPanel

measure-panel-scroll-overflow

Measure the vertical overflow for the clipped region containing the given panel.

Scene -> Int -> Int -> String -> Float

measure-viewport-scroll-overflow

Measure the vertical overflow for an identified viewport.

Scene -> Int -> Int -> String -> Float

measure-viewport-visible-height

Measure the visible clipped height for an identified viewport.

Scene -> Int -> Int -> String -> Float

viewport-page-scroll-units

Return an approximate page-scroll wheel unit count for a viewport based on its visible height.

Scene -> Int -> Int -> String -> Int -> Int

page-scroll-wheel-input

Build a wheel input that scrolls roughly one viewport page for the active hovered or focused viewport.

Scene -> RuntimeState -> Int -> Int -> Int -> Int -> RuntimeInput

render-panels

Resolve layout plus interaction state into draw-ready panels.

Scene -> RuntimeState -> Int -> Int -> List RenderPanel

panel-fill-color

Resolve the fill color for a laid out panel.

LaidOutPanel -> RuntimeState -> Color

hit-test-scene

Return the topmost panel id under the given scene-space point, or "" if none.

Scene -> Int -> Int -> Float -> Float -> String

hit-test-viewport

Return the topmost identified viewport under the given scene-space point, or "" if none.

Scene -> Int -> Int -> Float -> Float -> String

pointer-move-state

Reduce a pointer-move event into updated runtime state.

Scene -> Int -> Int -> RuntimeState -> Float -> Float -> RuntimeState

pointer-down-state

Reduce a primary pointer-down event into updated runtime state.

Scene -> Int -> Int -> RuntimeState -> Float -> Float -> RuntimeState

pointer-up-state

Reduce a primary pointer-up event into updated runtime state.

Scene -> Int -> Int -> RuntimeState -> Float -> Float -> RuntimeState

pointer-move-input

Construct a pointer-move runtime input.

Int -> Int -> Float -> Float -> RuntimeInput

pointer-down-input

Construct a pointer-down runtime input.

Int -> Int -> Float -> Float -> RuntimeInput

pointer-up-input

Construct a pointer-up runtime input.

Int -> Int -> Float -> Float -> RuntimeInput

focus-next-input

Construct a focus-next runtime input.

Int -> Int -> RuntimeInput

focus-prev-input

Construct a focus-previous runtime input.

Int -> Int -> RuntimeInput

focus-left-input

Construct a focus-left runtime input.

Int -> Int -> RuntimeInput

focus-right-input

Construct a focus-right runtime input.

Int -> Int -> RuntimeInput

focus-up-input

Construct a focus-up runtime input.

Int -> Int -> RuntimeInput

focus-down-input

Construct a focus-down runtime input.

Int -> Int -> RuntimeInput

activate-focus-input

Construct a focused-activation runtime input.

RuntimeInput

scroll-wheel-input

Construct a scroll-wheel runtime input.

Int -> Int -> RuntimeInput

resize-input

Construct a resize runtime input.

Int -> Int -> RuntimeInput

reduce-state

Reduce a pure runtime input into updated runtime state.

Scene -> RuntimeState -> RuntimeInput -> RuntimeState

advance-app-state

Advance model and runtime state after a rendered frame has produced effects.

(a -> Scene) -> (a -> String -> a) -> a -> RuntimeState -> Int -> Int -> {model: a, scene: Scene, state: RuntimeState, clicked-id: String, has-hot-target?: Bool}

advance-scroll-app-state

Advance model and runtime state after click and wheel effects.

(a -> Scene) -> (a -> String -> a) -> (a -> Scene -> String -> Int -> Int -> a) -> a -> RuntimeState -> Int -> Int -> {model: a, scene: Scene, state: RuntimeState, clicked-id: String, wheel-x: Int, wheel-y: Int, has-hot-target?: Bool}

sync-focus-frame

Apply a model sync after focus changes and rerender once if focus changed.

(a -> Scene) -> (a -> Scene -> RuntimeState -> Int -> Int -> a) -> String -> {model: a, scene: Scene, state: RuntimeState, has-hot-target?: Bool} -> Int -> Int -> {model: a, scene: Scene, state: RuntimeState, has-hot-target?: Bool}

advance-focus-input-frame

Apply a focus-related runtime input and then sync focused content into view if focus changed.

(a -> Scene) -> (a -> Scene -> RuntimeState -> Int -> Int -> a) -> a -> RuntimeState -> RuntimeInput -> Int -> Int -> {model: a, scene: Scene, state: RuntimeState, has-hot-target?: Bool}