streaming-ui-script · settings app ← Back to docs
Live demo · rich patterns + two-way binding

A full settings & preferences screen, driven by two-way bound primitives

Tabs across the top, a PageHeader with breadcrumbs, a usage progress bar, switches and toggle groups for preferences, keyboard shortcut chips, and a slide-in Sheet for confirming the dangerous "delete workspace" action. Every control binds straight to a $variable — no @Js needed.

Live preview

Flip the switches, change the theme, hit "Save changes" — the save progress bar animates and a banner confirms. "Delete workspace" opens a confirmation sheet.

$tab = "general"
$theme = "light"
$accent = "indigo"
$density = "comfortable"
$notifications = true
$autosave = true
$weeklyDigest = true
$mentionEmails = false
$shareUsage = true
$language = "en"
$deleting = false
$saveStatus = "idle"

usage = Query("workspace_usage", {}, {storageUsed: 0, storageMax: 100, seatsUsed: 0, seatsMax: 0, planLabel: "", renews: ""})
saveMutation = Mutation("save_settings", {tab: $tab, theme: $theme, density: $density, language: $language})
deleteMutation = Mutation("delete_workspace", {})

savedBanner = $saveStatus == "saved" ? Banner("Saved", "Your preferences are up to date.", null, "circle-check", "success") : null
savingBanner = $saveStatus == "saving" ? Banner("Saving…", "Hang tight while we sync your preferences.", null, "⏳", "info") : null

header = PageHeader(
  "Settings",
  "Personalise your workspace",
  Breadcrumb([BreadcrumbItem("Workspace", "#"), BreadcrumbItem("Settings")]),
  [
    Button("Cancel",       Action([@ToAssistant("Discard pending changes")]), "ghost"),
    Button("Save changes", Action([@Set($saveStatus, "saving"), @Run(saveMutation), @Set($saveStatus, "saved")]), "primary")
  ],
  Tag(usage.planLabel, null, "sm", "primary")
)

usageCard = Card([
  CardHeader("Workspace usage", "Renews " + usage.renews),
  MetricGrid([
    StatCard("Storage", "" + usage.storageUsed + " / " + usage.storageMax + " GB", "flat", null, "database"),
    StatCard("Seats",   "" + usage.seatsUsed + " / " + usage.seatsMax,             "up",   "+2 this month", "users"),
    StatCard("Plan",    usage.planLabel,                                            "flat", null, "id-card")
  ], 3),
  Progress(usage.storageUsed, usage.storageMax, "Storage used", "primary", false, true)
])

generalCard = Card([
  CardHeader("General"),
  FormControl("Language", Select("language", [
    SelectItem("en", "English"),
    SelectItem("fr", "Français"),
    SelectItem("de", "Deutsch"),
    SelectItem("ja", "日本語")
  ], null, null, $language)),
  Separator("horizontal", true),
  Switch("notifications", "Enable desktop notifications", $notifications, "We'll ping you when a build finishes or someone @mentions you."),
  Switch("autosave",      "Autosave every 30 seconds",     $autosave),
  Switch("shareUsage",    "Share anonymous usage data",    $shareUsage, "Helps us prioritise the right components and themes.")
])

notificationsCard = Card([
  CardHeader("Notifications"),
  Switch("weeklyDigest",  "Weekly digest email",      $weeklyDigest),
  Switch("mentionEmails", "Email me on @mentions",    $mentionEmails, "Beyond just an in-app notification."),
  Separator("horizontal", true),
  TextContent("Keyboard shortcut to mark all as read:", "small", "muted"),
  Kbd(["⌘", "Shift", "R"])
])

appearanceCard = Card([
  CardHeader("Appearance"),
  FormControl("Theme", ToggleGroup("theme", [
    {value: "light",  label: "Light",  icon: "sun"},
    {value: "dark",   label: "Dark",   icon: "moon"},
    {value: "neon",   label: "Neon",   icon: "wand-magic-sparkles"},
    {value: "pastel", label: "Pastel", icon: "ribbon"}
  ], $theme)),
  FormControl("Accent", ToggleGroup("accent", [
    {value: "indigo",  label: "Indigo"},
    {value: "emerald", label: "Emerald"},
    {value: "rose",    label: "Rose"},
    {value: "amber",   label: "Amber"}
  ], $accent)),
  FormControl("Density", ToggleGroup("density", [
    {value: "compact",     label: "Compact"},
    {value: "comfortable", label: "Comfortable"},
    {value: "spacious",    label: "Spacious"}
  ], $density))
])

shortcutsCard = Card([
  CardHeader("Keyboard shortcuts"),
  List([
    ListItem("Open command palette",     null, "⌘ K"),
    ListItem("Quick search",             null, "⌘ /"),
    ListItem("Toggle theme",             null, "⌘ Shift T"),
    ListItem("Create new doc",           null, "⌘ N"),
    ListItem("Mark all notifs as read",  null, "⌘ Shift R")
  ])
])

dangerCard = Card([
  CardHeader("Danger zone", "Permanent actions — proceed with care"),
  TextContent("Deleting the workspace removes every project, file, member, and history record. This action cannot be undone.", "small", "muted"),
  Buttons([Button("Delete workspace", Action([@Set($deleting, true)]), "danger")])
], "outlined")

tabs = Tabs([
  TabItem("general",       "General",       [generalCard, usageCard]),
  TabItem("appearance",    "Appearance",    [appearanceCard]),
  TabItem("notifications", "Notifications", [notificationsCard]),
  TabItem("shortcuts",     "Shortcuts",     [shortcutsCard]),
  TabItem("danger",        "Danger zone",   [dangerCard])
], $tab)

confirmSheet = Sheet(
  "Delete workspace?",
  $deleting,
  [
    Callout("danger", "This cannot be undone", "Every project, file, and member will be lost."),
    TextContent("Type DELETE in the box below to confirm.", "small", "muted"),
    FormControl("Confirmation", Input("confirm", "DELETE", "text"))
  ],
  "right",
  [
    Button("Cancel", Action([@Set($deleting, false)]), "ghost"),
    Button("Permanently delete", Action([@Run(deleteMutation), @Set($deleting, false), @ToAssistant("Workspace deleted")]), "danger")
  ]
)

root = Stack([header, savingBanner, savedBanner, tabs, confirmSheet], "column", "l")

The mutation tools are trivial

Most of the UI binds straight to $variables. The only thing the host has to do is mirror "Save" into a backend. Try flipping a switch, then hitting "Save changes" — the saving banner appears, then resolves into a "Saved" banner.

el.setTools({
  workspace_usage: () => ({
    storageUsed: 42, storageMax: 100,
    seatsUsed: 12, seatsMax: 25,
    planLabel: "Pro", renews: "Mar 14",
  }),
  save_settings: async ({ tab, theme, density, language }) => {
    await sleep(600);
    console.log("[settings] save", { tab, theme, density, language });
    return { ok: true };
  },
  delete_workspace: async () => {
    await sleep(400);
    console.warn("[settings] delete_workspace would fire here");
    return { ok: true };
  },
});