Files
Claude 3adebb731f Rename FunPack to FunUI (5.0.3 Classy Chopper): /fun endpoint, wink logo, load-time migration of old names
Old project/workflow names and browser settings migrate on load; /funpack redirects to /fun.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016acRss69eH1235eRdFTZHY
2026-10-25 13:11:03 +00:00

401 lines
20 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// One demo per element: the props the catalogue renders it with, and the props
// the rule tests attack it with.
//
// It lives in one place because two things need it and they must not drift: the
// catalogue shows what an element looks like, and rule_content_is_data feeds
// every string in here a payload to prove it comes back as text. A registered
// element with no entry here fails the suite -- that is how coverage keeps up
// with the registry instead of lagging behind it.
// A demo is either a plain props object, or a function given `composer` -- the
// containers take other elements' handles, and those have to be built.
export const DEMOS = {
"header.display": { text: "Cutting Room" },
"header.xl": { text: "Scene 3 — rooftop, dusk" },
"header.lg": { text: "Sampling" },
"header.md": { text: "Second pass" },
"header.sm": { text: "Guidance" },
"text.lg": { text: "The chain sampler runs each scene in order, carrying the last frame forward." },
"text.md": { text: "The chain sampler runs each scene in order, carrying the last frame forward." },
"text.sm": { text: "The chain sampler runs each scene in order, carrying the last frame forward." },
"text.xs": { text: "The chain sampler runs each scene in order, carrying the last frame forward." },
"hint.default": { text: "Locks frame timing to the audio stream." },
"label.field": { text: "Lock strength", for: "demo-field" },
"label.section": { text: "Sampling" },
"code.inline": { text: "bong_tangent" },
"code.block": { text: "steps: 7\nshift: 3.0\nscheduler: bong_tangent", label: "Pass settings" },
"button.xl": { label: "Generate", tone: "primary" },
"button.lg": { label: "Save project" },
"button.md": { label: "Cancel" },
"button.sm": { label: "Reset" },
"button.menu": { label: "File", tone: "ghost" },
"iconButton.md": { icon: "✕", label: "Close" },
"iconButton.sm": { icon: "✎", label: "Rename" },
"iconButton.micro": { icon: "✕", label: "Remove" },
"input.md": { value: "rooftop, dusk", placeholder: "Scene prompt", label: "Prompt" },
"input.sm": { value: "121", label: "Frames" },
"search.md": { placeholder: "Search shortcuts", label: "Search" },
"number.md": { value: 3, min: 1, max: 10, step: 0.5, precision: 1, unit: "x", label: "Shift" },
"stepper.md": { value: 7, min: 1, max: 40, step: 1, label: "Steps" },
"textarea.md": { value: "A slow push in on the rooftop as the light goes.", rows: 3, label: "Scene prompt" },
"filterList.md": {
value: "bong",
items: [
{ id: "bong", label: "bong_tangent", hint: "validated" },
{ id: "beta", label: "beta57", hint: "" },
{ id: "karras", label: "karras", hint: "2.3 era" },
],
},
"checkbox.default": { checked: true, label: "Enabled" },
"checkboxRow.default": { label: "Sync to audio clock", hint: "Locks frame timing to the audio stream.", checked: true },
"checklist.default": {
label: "Passes",
values: ["high"],
items: [
{ value: "high", label: "High noise" },
{ value: "low", label: "Low noise" },
{ value: "detail", label: "Detail tail" },
],
},
"color.swatch": { label: "Overlay colour", value: "#f3a93c" },
"radioGroup.default": {
label: "Alignment",
value: "beat",
options: [
{ value: "beat", label: "Beat grid", hint: "Follows the tempo" },
{ value: "onset", label: "Onset", hint: "Follows transients" },
{ value: "flat", label: "Flat" },
],
},
"select.md": {
label: "Scheduler", value: "bong_tangent",
options: [
{ value: "bong_tangent", label: "bong_tangent" },
{ value: "beta57", label: "beta57" },
{ value: "karras", label: "karras" },
],
},
"select.sm": {
label: "Sort", value: "recent",
options: [{ value: "recent", label: "Most recent" }, { value: "name", label: "Name" }],
},
"segmented.md": {
label: "Mode", value: "i2v",
options: [{ value: "t2v", label: "Text" }, { value: "i2v", label: "Image" }, { value: "v2v", label: "Video" }],
},
"segmented.sm": {
label: "Columns", value: "2",
options: [{ value: "0", label: "Auto" }, { value: "2", label: "2" }, { value: "3", label: "3" }],
},
"toggle.default": { label: "Second pass", hint: "Runs both schedules in full.", checked: true },
"slider.md": { value: 0.65, min: 0, max: 1, step: 0.05, label: "Lock strength" },
"slider.sm": { value: 0.3, min: 0, max: 1, step: 0.05, label: "Blend" },
"slider.readout": { value: 0.65, min: 0, max: 1, step: 0.05, unit: "x", label: "Lock strength" },
"slider.macro": {
value: 0.4, min: 0, max: 1, step: 0.05, label: "Variability",
presets: [{ label: "Chill", value: 0.2 }, { label: "Spicy", value: 0.5 }, { label: "Chaos", value: 0.9 }],
},
"range.md": { from: 0.3, to: 0.7, min: 0, max: 1, step: 0.05, label: "Sigma window" },
"buttonGroup.md": {
label: "Alignment",
value: "beat",
items: [
{ value: "beat", label: "Beat grid" },
{ value: "onset", label: "Onset" },
{ value: "flat", label: "Flat" },
],
},
// --- layout: these compose other elements, so their demos are functions ---
"field.default": (c) => ({
label: "Lock strength", hint: "Locks frame timing to the audio stream.",
control: c.slider.readout({ value: 0.65, min: 0, max: 1, step: 0.05, unit: "x" }),
}),
"field.row": (c) => ({
fields: [
c.field.default({ label: "Width", control: c.number.md({ value: 1216, step: 32 }) }),
c.field.default({ label: "Height", control: c.number.md({ value: 704, step: 32 }) }),
],
}),
"settingsRow.default": (c) => ({
label: "Second pass", hint: "Runs both schedules in full.",
control: c.toggle.default({ checked: true }),
}),
"group.default": (c) => ({
label: "Sampling",
rows: [
c.settingsRow.default({ label: "Steps", control: c.stepper.md({ value: 7, min: 1, max: 40 }) }),
c.settingsRow.default({ label: "Shift", hint: "Structure versus fine detail.",
control: c.number.md({ value: 3, min: 1, max: 10, step: 0.5, precision: 1 }) }),
c.settingsRow.default({ label: "Reset engine", danger: true,
control: c.button.md({ label: "Reset", tone: "danger" }) }),
],
}),
"brand.default": { name: "FunUI" },
"card.default": (c) => ({ children: [c.field.default({ label: "Prompt", control: c.input.md({ value: "A slow push in on the rooftop" }) }), c.hint.default({ text: "Related fields share one card." })] }),
"grid.pair": (c) => ({ children: [c.tile.action({ title: "Update", sub: "Pull the newest version" }), c.tile.action({ title: "Restart", sub: "Reload the server" })] }),
"grid.cards": (c) => ({ children: [c.tile.node({ title: "Load checkpoint", sub: "model.safetensors" }), c.tile.node({ title: "Sampler", sub: "Needs a choice", state: "needs" }), c.tile.add({ title: "+ Add a node", sub: "to Sampling" })] }),
"tile.action": { title: "Update", sub: "Pull the newest version from origin" },
"tile.node": { title: "Load checkpoint", sub: "model.safetensors", state: "ready" },
"tile.add": { title: "+ Add a node", sub: "to Loaders" },
"progress.inline": { label: "Generating" },
"floating.corner": (c) => ({ anchor: c.textarea.md({ label: "Story", rows: 4 }), label: "💡", title: "Shortcut ideas" }),
"viewer.cover": { label: "Close preview" },
"stage.overlay": { label: "Overlays" },
"hero.default": { title: "FunUI 5", hint: "“Classy Chopper” · Cutting Room" },
"panel.default": (c) => ({
title: "Scene 3",
actions: [c.iconButton.sm({ icon: "✎", label: "Rename" }), c.iconButton.sm({ icon: "✕", label: "Remove" })],
body: c.text.sm({ text: "A slow push in on the rooftop as the light goes." }),
}),
"panel.zone": (c) => ({
title: "Assets",
actions: [c.iconButton.sm({ icon: "▦", label: "Grid" })],
body: c.text.sm({ text: "A region of the app: square, edge to edge, divided by a hairline." }),
}),
"region.stack": (c) => ({
label: "Pipeline",
children: [
c.text.sm({ text: "Contents are replaced by whoever holds the handle." }),
c.button.md({ label: "Redraw" }),
],
}),
"toolbar.default": (c) => ({
label: "Timeline",
items: [
c.button.sm({ label: "Split" }), c.button.sm({ label: "Duplicate" }),
c.button.sm({ label: "Delete", tone: "danger" }),
],
}),
"actionBar.sticky": (c) => ({
note: "Nothing is saved until you press Save.",
actions: [c.button.lg({ label: "Cancel" }), c.button.lg({ label: "Save", tone: "primary" })],
}),
"list.rows": (c) => ({
reorder: true,
addLabel: "Add LoRA",
onAdd: () => {},
onRemove: () => {},
items: [
{ label: "detail_v3.safetensors",
control: c.number.md({ value: 0.8, min: -2, max: 2, step: 0.05, precision: 2, label: "Weight" }) },
{ label: "motion_lift.safetensors",
control: c.number.md({ value: 0.4, min: -2, max: 2, step: 0.05, precision: 2, label: "Weight" }) },
],
}),
"tabs.underline": { value: "compose", tabs: [
{ value: "compose", label: "Compose" }, { value: "shortcuts", label: "Shortcuts" }, { value: "files", label: "Files" },
] },
"tabs.dock": { value: "assets", tabs: [
{ value: "assets", label: "Assets" }, { value: "preview", label: "Preview" }, { value: "props", label: "Properties" },
] },
"splitPane.h": (c) => ({
size: 40, label: "Assets and preview",
panes: [
c.panel.default({ title: "Assets", body: c.hint.default({ text: "Media bin" }) }),
c.panel.default({ title: "Preview", body: c.hint.default({ text: "Program monitor" }) }),
],
}),
"viewer.media": { src: "", empty: "Nothing generated yet" },
"frame.app": (c) => ({
header: c.toolbar.default({ label: "App", items: [c.button.sm({ label: "Models and pipeline" })] }),
main: c.panel.default({ title: "Workspace", body: c.hint.default({ text: "Everything that fills the window" }) }),
footer: c.actionBar.sticky({ note: "Ready", actions: [c.button.lg({ label: "Generate", tone: "primary" })] }),
}),
"workspace.docked": (c) => ({
id: "demo",
leftLabel: "Assets", rightLabel: "Properties",
left: c.panel.default({ title: "Assets", body: c.hint.default({ text: "Media bin" }) }),
centre: c.panel.default({ title: "Preview", body: c.hint.default({ text: "Program monitor" }) }),
right: c.panel.default({ title: "Properties", body: c.hint.default({ text: "Scene settings" }) }),
}),
"splitPane.v": (c) => ({
size: 60, label: "Preview and timeline",
panes: [
c.panel.default({ title: "Preview", body: c.hint.default({ text: "Program monitor" }) }),
c.panel.default({ title: "Timeline", body: c.hint.default({ text: "Scenes" }) }),
],
}),
"collapsible.default": (c) => ({
label: "Advanced", hint: "rarely needed",
body: c.checkboxRow.default({ label: "Bypass this node", hint: "Passes inputs straight through." }),
}),
"sidebar.rail": { value: "models", items: [
{ value: "general", icon: "◐", label: "General" },
{ value: "models", icon: "▦", label: "Models" },
{ value: "engine", icon: "⚙", label: "Engine" },
] },
// --- status ---
"chip.neutral": { label: "experimental" },
"chip.good": { label: "validated", dot: true },
"chip.warn": { label: "needs audio", dot: true },
"chip.danger": { label: "missing model", dot: true },
"chip.info": { label: "flow-match", dot: true },
"dot.default": { tone: "good", label: "Ready" },
"banner.info": { text: "This project was made with an older engine." },
"banner.warn": { text: "No audio stream: the audio clock will not run." },
"banner.danger": { text: "The diffusion model could not be loaded." },
"inlineError.default": { text: "Steps must be between 1 and 40." },
"progress.bar": { value: 62, max: 100, label: "Rendering" },
"progress.indeterminate": { label: "Preparing" },
"spinner.md": { label: "Working" },
"spinner.sm": { label: "Working" },
"emptyState.default": { icon: "▦", title: "No media yet", hint: "Drop files here, or import from the menu." },
"dropzone.default": { label: "Drop video or images", hint: "mp4, png, jpg" },
"skeleton.line": { count: 3, label: "Loading scenes" },
"skeleton.block": { count: 2, label: "Loading panels" },
"skeleton.grid": { count: 6, label: "Loading media" },
"toast.info": { text: "Project saved.", duration: 0 },
"toast.good": { text: "Render finished.", duration: 0 },
"toast.warn": { text: "Audio track is silent.", duration: 0 },
"toast.danger": { text: "Generation failed.", duration: 0 },
// --- overlays: these mount themselves into the portal ---
"modal.generic": (c) => ({
title: "Export settings", subtitle: "Written into the picture's metadata.",
body: c.checkboxRow.default({ label: "Include prompt", hint: "Anyone with the file can read it." }),
actions: [c.button.lg({ label: "Cancel" }), c.button.lg({ label: "Export", tone: "primary" })],
}),
"modal.stacked": (c) => ({
title: "Pick a model", size: "sm",
body: c.filterList.md({ items: [{ id: "a", label: "ltx-2.5-distilled" }, { id: "b", label: "minimax-h3" }] }),
}),
"modal.dialogue": { title: "Delete scene 3?", message: "This removes the scene and its render. It cannot be undone.",
tone: "danger", confirmLabel: "Delete", cancelLabel: "Keep" },
"modal.prompt": { title: "New project", label: "Name", value: "Untitled", confirmLabel: "Create" },
"modal.choice": { title: "Start from", subtitle: "You can change this later.", items: [
{ id: "blank", label: "Empty project", hint: "Nothing on the timeline" },
{ id: "image", label: "From an image", hint: "Adds it as the first anchor" },
] },
"popover.anchored": (c) => ({
anchor: c.button.md({ label: "Anchor" }),
body: c.hint.default({ text: "Anchored, flipped and clamped by the kit." }),
}),
"menu.dropdown": (c) => ({
anchor: c.button.md({ label: "Scene ▾" }),
items: [
{ id: "split", label: "Split at playhead", hint: "S" },
{ id: "dup", label: "Duplicate" },
{ separator: true },
{ id: "del", label: "Remove", danger: true },
],
}),
"menu.context": { x: 200, y: 200, items: [
{ id: "rename", label: "Rename" }, { id: "export", label: "Export" },
] },
"tooltip.default": (c) => ({ anchor: c.iconButton.md({ icon: "✎", label: "Rename" }),
text: "Rename this scene", trigger: false }),
"autocomplete.default": (c) => ({
input: c.input.md({ value: "", placeholder: "Type a shortcut" }),
source: () => [{ label: "rooftop", hint: "location" }, { label: "rain", hint: "weather" }],
}),
"splitButton.md": { label: "Generate", items: [
{ id: "all", label: "All scenes" }, { id: "one", label: "This scene only" },
] },
"floating.window": (c) => ({
id: "demo-window", title: "Composer", subtitle: "prompt",
width: 320, height: 200, x: 40, y: 40,
body: c.textarea.md({ value: "A slow push in on the rooftop.", rows: 3, label: "Prompt" }),
}),
"overlay.blocking": { message: "Restarting ComfyUI…" },
"slideOver.md": (c) => ({ side: "right", title: "Properties",
body: c.hint.default({ text: "A panel that slides in over the page." }) }),
// --- gallery + wheel ---
"gallery.adaptive": { id: "demo-gallery", cols: 3, items: [
{ id: "1", label: "rooftop_dusk_01.mp4", badge: "video", duration: "0:04" },
{ id: "2", label: "anchor_frame.png", badge: "image" },
{ id: "3", label: "rooftop_dusk_02.mp4", badge: "video", duration: "0:05" },
{ id: "4", label: "reference_face.png", badge: "ref" },
{ id: "5", label: "score.wav", badge: "audio", duration: "1:12" },
{ id: "6", label: "rooftop_dusk_03.mp4", badge: "video", duration: "0:04" },
] },
"gallery.icons": { selection: ["2"], items: [
{ id: "1", label: "rooftop_dusk_01.mp4", icon: "▶" },
{ id: "2", label: "anchor_frame.png" },
{ id: "3", label: "rooftop_dusk_02.mp4", icon: "▶" },
{ id: "4", label: "reference_face.png" },
] },
"gallery.list": { selection: ["1"], items: [
{ id: "1", label: "rooftop_dusk_01.mp4", icon: "▶", hint: "video" },
{ id: "2", label: "anchor_frame.png", hint: "image" },
{ id: "3", label: "a_very_long_generated_filename_00042_.png", hint: "image" },
] },
"gallery.rows": { selection: ["1"], items: [
{ id: "1", label: "export-size-test", hint: "3▦" },
{ id: "2", label: "Race Test 2", hint: "1▦" },
] },
"gallery.cards": { value: "image", items: [
{ id: "blank", icon: "▦", label: "Empty project", hint: "Nothing on the timeline" },
{ id: "image", icon: "◐", label: "From an image", hint: "Adds it as the first anchor" },
{ id: "import", icon: "🎞", label: "Import a workflow", hint: "Reads an existing graph" },
] },
"gallery.strip": { label: "Segments", items: [
{ id: "1", icon: "▦", label: "Scene 1" }, { id: "2", icon: "▦", label: "Scene 2" },
{ id: "3", icon: "▦", label: "Scene 3" }, { id: "4", icon: "▦", label: "Scene 4" },
] },
"ruler.default": { label: "Scenes", total: 240, marks: [
{ at: 0, label: "1", hint: "0" }, { at: 0.4, label: "2", hint: "96" },
{ at: 0.75, label: "3", hint: "180" },
] },
"timeline.stage": { pxPerSecond: 60, lanes: [
{ id: "video", label: "Video", kind: "video", reorder: true, clips: [
{ id: "a", start: 0, dur: 3, head: ["▶", "00:03"], title: "01", body: "empty scene", selected: true, trim: true },
{ id: "b", start: 3, dur: 2, head: ["▦", "00:02"], title: "02", trim: true },
] },
{ id: "audio", label: "Audio", kind: "audio", clips: [{ id: "s", start: 0, dur: 3, title: "S1" }] },
] },
"track.default": { label: "Timeline", pxPerSecond: 40, playhead: 6.5, selection: ["2"], items: [
{ id: "1", icon: "▦", label: "Scene 1", badge: "1", start: 0, duration: 4 },
{ id: "2", icon: "▦", label: "Scene 2", badge: "2", start: 4, duration: 6, rating: "liked" },
{ id: "3", icon: "▦", label: "Scene 3", badge: "3", start: 10, duration: 3, excluded: true },
] },
"chart.line": { label: "Loss", xLabel: "step", yLabel: "loss", height: "md", series: [
{ name: "loss", faint: true, values: [0.9, 0.55, 0.7, 0.4, 0.5, 0.3, 0.35, 0.22, 0.3, 0.18, 0.2, 0.15] },
{ name: "smoothed", tone: "good", values: [0.9, 0.72, 0.68, 0.58, 0.52, 0.44, 0.38, 0.33, 0.3, 0.26, 0.23, 0.2] },
], empty: "No training yet" },
"tags.editor": { value: ["rooftop", "dusk", "city lights"], suggestions: ["rooftop", "rain", "dusk", "dawn", "neon sign"], placeholder: "Add a tag", label: "Tags" },
"captioned.cell": { title: "street_01.png", caption: "rooftop, dusk, city lights", badge: "Liked", tone: "good", onChange: () => {}, onSelect: () => {}, onOpen: () => {} },
"captioned.side": { title: "street_02.mp4", kind: "video", caption: "rain, neon sign", badge: "Clip", onChange: () => {}, onOpen: () => {} },
"tags.cloud": { items: [{ tag: "rooftop", count: 42 }, { tag: "dusk", count: 17 }, { tag: "rain", count: 9 }, { tag: "neon sign", count: 3 }], selected: ["dusk"], label: "Tag counts", empty: "No tags yet", onPick: () => {}, onRemove: () => {} },
"wheel.half": { edge: "right", items: [
{ icon: "✂", label: "Split" }, { icon: "⧉", label: "Duplicate" },
{ icon: "★", label: "Rate" }, { icon: "◐", label: "Anchor" },
] },
"wheel.picker": { items: [
{ icon: "✂", label: "Split" }, { icon: "⧉", label: "Duplicate" },
{ icon: "★", label: "Rate" }, { icon: "⇄", label: "Reverse" },
{ icon: "◐", label: "Anchor" }, { icon: "✕", label: "Remove" },
] },
};
/** Extra variants worth seeing in the catalogue but not worth a second demo. */
export const VARIANTS = {
"button.xl": [
{ label: "Generate", tone: "primary" },
{ label: "Neutral" },
{ label: "Delete", tone: "danger" },
{ label: "Ghost", tone: "ghost" },
{ label: "Disabled", disabled: true },
{ label: "Working", tone: "primary", busy: true },
],
"button.md": [
{ label: "Cancel" },
{ label: "Primary", tone: "primary" },
{ label: "Danger", tone: "danger" },
{ label: "Ghost", tone: "ghost" },
],
};