Files
olivv-cs-ComfyUI-FunPack/app/shell/constructor.js
T
digital-garbageandClaude Opus 5 9dcd94c156 app, core: a project of scenes, and the timeline that draws it
The app could run a pipeline and had nowhere to keep what it was making. A
project is an ordered list of scenes, one JSON file each, and it is the only
part of the app that has to survive a reload.

A scene owns its prompt text and the result it produced, and nothing else. v4's
Scene grew three dozen fields; each belongs to the feature that needs it. Per
scene length and fps are deliberately absent — a regenerate uses the project's
values, so a scene cropped on the timeline comes back whole.

The prompt box edits the current scene rather than holding its own copy, so
what is on screen and what a run uses cannot disagree.

app: the region toggles are named buttons in the timeline bar

v4's arrangement. The kit's fallback is one glyph on a rail at each edge of the
window, which is right for a kit that cannot know what its panels are called and
wrong here, where they have names and a bar to put them in.

A pressed toggle was styled only inside a button group, so a standalone one
carried aria-pressed and looked identical either way.

gallery.strip is selectable like its siblings: a filmstrip is the one shape
whose whole job is saying which of these you are on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 12:03:19 +03:00

56 lines
2.2 KiB
JavaScript

// The Constructor: where a run is written, in a window of its own.
//
// v4 called this the Composer. The name is taken here -- `app/composer` is the
// element kit -- and two things called Composer in one codebase is one thing
// nobody can search for.
//
// The prompt is not a panel on the main window. It is the longest thing a
// person writes, it is edited in bursts and read rarely, and giving it a
// permanent third of the centre column cost the timeline the room it needs. So
// it opens, is written in, and closes.
//
// The HOST is built once, at startup, and outlives every opening. Two reasons,
// and both were faults before this file existed: a mount point has to exist
// when modules mount, which is long before anyone opens a window; and a window
// that rebuilt its contents on each open would throw away whatever was typed
// and not saved.
import { composer } from "../composer/composer.js";
export function createConstructor({ title = "Constructor", onChange } = {}) {
const empty = composer.emptyState.default({
icon: "✎",
title: "Nothing to write yet",
hint: "The pipeline decides which of its inputs are written here.",
});
// The host is a plain region: it is handed to the modal as a body and handed
// back when the modal closes, which is why the modal never owns it.
const host = composer.region.stack({ gap: "sm", label: "Constructor", fill: true,
children: [empty] });
let window_ = null;
function open() {
if (window_) return window_; // one window onto one prompt
window_ = composer.modal.generic({
title,
subtitle: "What this run is made of.",
size: "lg",
body: host,
actions: [composer.button.md({ label: "Done", tone: "primary",
onClick: () => window_ && window_.close("done") })],
onClose: () => { window_ = null; if (onChange) onChange(); },
});
if (onChange) onChange();
return window_;
}
return {
host, empty, open,
/** Open, and mounted. Tests and the shell both ask. */
get isOpen() { return Boolean(window_); },
close() { if (window_) window_.close("close"); },
};
}