SPRINKLE(1) — Persistent .shtml UI Panels
SYNOPSIS
sprinkle list
sprinkle open <name>
sprinkle close <name>
sprinkle refresh
sprinkle send <name> <json>
sprinkle chat <html>
DESCRIPTION
Sprinkles are persistent UI panels built from .shtml files. They live in /shared/sprinkles/<name>/<name>.shtml on the VFS. Sprinkles are created by scoops, outlive the scoops that created them, and are managed via the sprinkle shell command. They appear as tabs in the sidebar (standalone) or as additional panels (extension mode).
Sprinkles are discovered automatically by scanning the VFS. The priority root is /shared/sprinkles/, then the entire VFS. The sprinkle name is the basename without .shtml. Titles are extracted from data-sprinkle-title attributes, then <title> tags, falling back to the basename.
Add data-sprinkle-autoopen to any element in the .shtml to auto-open the sprinkle on first run.
RULES
-
Rule 1: One scoop per sprinkle, named identically
A sprinkle named "dashboard" must be owned by a scoop named "dashboard". Create the scoop with scoop_scoop("dashboard"), then feed_scoop to build and open the sprinkle. The scoop name and sprinkle name MUST match.
-
Rule 2: Cone NEVER touches sprinkle files or commands
The cone must NOT edit .shtml files, run sprinkle open/close/send, or handle sprinkle work directly. ALL sprinkle work goes through scoops via feed_scoop. The cone's only role is to create the scoop, feed it instructions, and forward lick events.
LICK EVENTS
Button clicks and user interactions inside a sprinkle trigger lick events. Sprinkle licks always route to the cone first. The cone must NEVER handle a lick itself — always forward it to the owning scoop via feed_scoop.
Lick flow:
User clicks button in sprinkle
→ slicc.lick({action: "refresh", data: {...}})
→ lick event routed to cone
→ cone calls feed_scoop("dashboard", "Lick event on YOUR sprinkle...")
→ scoop processes the action
→ scoop pushes updates via: sprinkle send dashboard '{"key":"value"}'
PANEL SPRINKLES vs INLINE SPRINKLES
Panel sprinkles are persistent .shtml files on the VFS, opened via the sprinkle command. They have the full bridge API (lick, onUpdate, readFile, setState, getState, open, close) and survive across sessions.
Inline sprinkles are ```shtml code blocks in chat messages. They are rendered as sandboxed iframes after streaming completes. They have a minimal bridge (lick-only, no state persistence, no readFile). Inline sprinkle licks route to the cone as sprinkle lick events with sprinkleName "inline".
SPRINKLE CHAT
sprinkle chat renders a blocking inline HTML card in the chat. It waits for the user to click a button and returns the action as JSON. Use data-action attributes on buttons for callbacks.
sprinkle chat '<div class="sprinkle-action-card">
<div class="sprinkle-action-card__header">Confirm?</div>
<div class="sprinkle-action-card__actions">
<button class="sprinkle-btn" data-action="cancel">Cancel</button>
<button class="sprinkle-btn sprinkle-btn--primary" data-action="ok">OK</button>
</div>
</div>'
Returns JSON like {"action":"ok","data":{}} on stdout. Can also receive HTML via stdin: echo "<div>...</div>" | sprinkle chat
BRIDGE API
Inside .shtml files, the bridge is available as both "slicc" and "bridge" in script tags and onclick attributes.
-
slicc.lick({action, data}) or slicc.lick("action")
Send a lick event to the cone. The cone routes it to the owning scoop.
-
slicc.on("update", function(data) {...})
Listen for data pushed via sprinkle send. This is how the scoop communicates back to the UI.
-
slicc.off("update", callback)
Remove an update listener.
-
slicc.readFile(path)
Read a file from the VFS. Returns a Promise. Panel sprinkles only.
-
slicc.setState(data)
Persist sprinkle state (survives panel close/reopen). Panel sprinkles only.
-
slicc.getState()
Read persisted state. Returns null if none saved. Panel sprinkles only.
-
slicc.open(path)
Open a VFS file in a browser tab via the preview service worker.
-
slicc.close()
Close this sprinkle panel.
-
slicc.name
The sprinkle's name (read-only).
STYLE GUIDE
Use the built-in .sprinkle-* CSS classes. Do NOT write custom CSS. Theme variables and component rules are auto-injected into sprinkle iframes.
- sprinkle-card — Card with shadow, hover elevates
- sprinkle-stat-card — Stat card with .value + .label children
- sprinkle-action-card — Compact card for inline interactions. Children: __header, __body, __actions
- sprinkle-btn — Button. Variants: --primary, --secondary, --negative, --quiet
- sprinkle-btn-group — Horizontal button group
- sprinkle-badge — Bold solid-fill badge. Variants: --positive, --negative, --notice, --informative
- sprinkle-chip — Small interactive tag
- sprinkle-status-light — Colored dot with label. Variants: --positive, --negative, --notice
- sprinkle-table — Table with bold headers, row hover, dividers
- sprinkle-progress-bar — Bar with .fill child. Has __header, __track sub-elements
- sprinkle-meter — Like progress-bar but for measured values
- sprinkle-list-item — List row with __icon, __content (__title/__subtitle), __end
- sprinkle-kv-list — Key-value list with .key and .value spans
- sprinkle-text-field — Styled text input
- sprinkle-tabs — Tab bar with __tab and __panel children
- sprinkle-toolbar — Horizontal bar with __start, __center, __end slots
- sprinkle-grid — CSS grid container
- sprinkle-stack — Vertical flex stack
- sprinkle-row — Horizontal flex row
- sprinkle-divider — Horizontal rule
- sprinkle-dialog — Modal dialog with __backdrop, __content, __header, __footer
- sprinkle-collapsible — Expandable section with __header, __chevron, __body
- sprinkle-empty-state — Centered placeholder text
- sprinkle-sidebar — Side nav with __nav, __nav-item, __main
- sprinkle-canvas — Responsive container for SVG/canvas (--16x9, --4x3, --1x1)
- sprinkle-heading — Section heading (--m for medium, --s for small)
- sprinkle-body — Body text
- sprinkle-detail — Small detail text
- sprinkle-label — Bold uppercase label
SUBCOMMANDS
-
sprinkle list
List all discovered .shtml sprinkles. Shows name, open status, title, and VFS path.
-
sprinkle open <name>
Open a sprinkle by name. Adds it as a tab/panel in the UI. Triggers a refresh if the sprinkle is not yet discovered.
-
sprinkle close <name>
Close an open sprinkle. Removes it from the UI.
-
sprinkle refresh
Re-scan the VFS for .shtml files. Run after writing a new sprinkle to make it discoverable.
-
sprinkle send <name> <json>
Push JSON data to an open sprinkle. The data arrives in the sprinkle's slicc.on("update") handler. Single-quote the JSON to avoid shell escaping issues.
-
sprinkle chat <html>
Show a blocking inline HTML card in chat. Waits for the user to click a button with a data-action attribute. Returns the action as JSON on stdout. Also accepts HTML via stdin pipe.
EXAMPLES
# Create a sprinkle via scoop (cone does this)
scoop_scoop("dashboard")
feed_scoop("dashboard", "You own the sprinkle 'dashboard'.
1. Read /workspace/skills/sprinkles/style-guide.md
2. Write /shared/sprinkles/dashboard/dashboard.shtml
3. Run: sprinkle open dashboard
4. Stay ready for lick events — do NOT finish.")
# Push data to a sprinkle (scoop does this)
sprinkle send dashboard '{"scores":[95,87,72],"status":"ready"}'
# Forward a lick event (cone does this)
feed_scoop("dashboard", "Lick event on YOUR sprinkle 'dashboard':
Action: 'refresh'
Data: {\"id\": 42}
Process the action and push updates via sprinkle send.
Stay ready for more events.")
# Button with lick in .shtml
<button class="sprinkle-btn sprinkle-btn--primary"
onclick="slicc.lick({action: 'refresh', data: {id: 42}})">
Refresh
</button>
# Listen for updates in .shtml
<script>
slicc.on('update', function(data) {
if (data.scores) renderScores(data.scores);
});
</script>
# Quick confirmation via sprinkle chat
sprinkle chat '<div class="sprinkle-action-card">
<div class="sprinkle-action-card__header">Deploy to production?</div>
<div class="sprinkle-action-card__actions">
<button class="sprinkle-btn" data-action="cancel">Cancel</button>
<button class="sprinkle-btn sprinkle-btn--primary" data-action="deploy">Deploy</button>
</div>
</div>'
# Also opens sprinkles
open /shared/sprinkles/dashboard/dashboard.shtml
SEE ALSO
man scoops — Scoop management (scoop_scoop, feed_scoop, drop_scoop). man licks — External event system (webhooks, cron, sprinkle licks). /workspace/skills/sprinkles/style-guide.md — Full CSS component reference. /workspace/skills/sprinkles/SKILL.md — Sprinkle creation skill for scoops.