Files
olivv-cs-ComfyUI-FunPack/app/shell/layout.js
T
digital-garbage 78b623b2f7 app: panels sized by what they hold, and out of the way when they do not fit
The columns were a share of the viewport, which is not a measurement of
anything. A 900x900 window -- an ordinary shape -- gave the properties column
198px, in which "Loosen the starting image" wrapped over three lines and a
section heading broke mid-word; 375px put three columns side by side with
clipped headings and a preview no wider than a thumbnail. A panel is now as wide
as a settings row needs, and below 760px it stops taking space from the centre
and overlays it instead.

The element decides what is OPEN and the stylesheet decides how it is DRAWN, so
entering that mode closes both panels and only one opens at a time -- two
overlays over a narrow centre is the same problem with an extra step. What was
open is restored on the way out, and none of it touches the remembered
preference: a window being small is not the user changing their mind.

The rails paint above the overlay. Without that the panel covered the only
control that closes it, which is the exact fault this arrangement exists to
avoid, arriving by a different route the moment the panel stopped taking its own
space.

Also: the prompt was holding 38% of the height for two lines of text. The result
gets the room; the divider is still draggable.
2026-08-30 04:51:53 +03:00

99 lines
4.2 KiB
JavaScript

// The app's regions, and the mount points each one offers.
//
// A region declares what it can host. Core never learns these names, and no
// list of them exists anywhere else -- a module asks for one by name, and gets
// it or does not.
//
// The shape is the one v4 called Simple mode, built the other way round. v4's
// Simple mode was the Editor with about ten `display: none !important` rules
// over it, which is not a smaller app but the same app wearing a mask -- and it
// broke: a collapsed column was marked [hidden], and nothing can slide a
// display:none element into view, so a panel button did nothing at all and said
// nothing. So here a region that does not exist yet is ABSENT. The timeline, the
// media bin's view modes, the pipeline window and the wheel are not hidden
// below; they have not been built, and adding one is adding a region rather
// than removing a rule.
import { composer } from "../composer/composer.js";
import { offer } from "./mounts.js";
import { createTransport } from "./transport.js";
export function build(root, handlers = {}) {
// Left: what a project is made of. Empty until the media bin exists, and an
// empty state rather than an absent panel, because "there is nothing here
// yet" and "this part of the app is missing" must not look the same.
const assets = composer.panel.default({
title: "Assets",
body: composer.emptyState.default({
icon: "▤",
title: "Nothing in the bin",
hint: "Media you add to the project appears here.",
}),
});
// Centre: the result, and the prompt under it. The prompt is the one control
// that is always on screen, because it is the one always being edited.
const viewer = composer.viewer.media({ empty: "The result of the last run appears here." });
const preview = composer.panel.default({ title: "Preview", body: viewer });
const prompt = composer.panel.default({
title: "Prompt",
body: composer.hint.default({ text: "Modules that announced generation.prompt." }),
});
// The result gets the room. The prompt is a few lines and a button's worth of
// controls; at 38% of the height it was mostly empty space taken from the one
// thing on the page anybody is looking at. Draggable, so anyone writing a long
// prompt can take it back.
const centre = composer.splitPane.v({
size: 74,
label: "Preview and prompt",
panes: [preview, prompt],
});
// Right: everything about the run. Generation is open, settings folded away
// -- one is read constantly and the other rarely, and a fold says which is
// which without needing a second place to look. A settings WINDOW is a
// separate thing that has not been built; this is not a stand-in for it.
const generation = composer.panel.default({
title: "Generation",
body: composer.hint.default({ text: "Modules that announced a generation.* mount point." }),
});
const settings = composer.panel.default({
body: composer.hint.default({ text: "Modules that announced settings.general." }),
});
const properties = composer.group.default({
rows: [generation, composer.collapsible.default({ label: "Settings", body: settings })],
});
const workspace = composer.workspace.docked({
id: "main",
leftLabel: "Assets",
rightLabel: "Properties",
left: assets,
centre,
right: properties,
});
// The transport. One row, always visible, and the place a run is started and
// reported -- an inert setting is said HERE, next to Generate, not in the
// panel that would have enabled it.
const transport = createTransport(handlers);
const page = composer.frame.app({ main: workspace, footer: transport });
root.replaceChildren(page.node);
// The mount point IS the contract with modules; the region behind it can be
// rearranged freely as long as the name survives.
offer("assets.library", assets.body);
offer("generation.prompt", prompt.body);
offer("settings.general", settings.body);
offer("generation.model", generation.body);
offer("generation.latent", generation.body);
offer("generation.sampling", generation.body);
offer("generation.timing", generation.body);
offer("generation.post", generation.body);
return { workspace, assets, preview, viewer, prompt, generation, settings,
properties, transport, page };
}