Files
olivv-cs-ComfyUI-FunPack/app/shell/settings_window.js
T
digital-garbage 563b1c6f02 app: Settings looks designed instead of default -- icon chips, real sidebar, centred About
A bare glyph character had no framing at all; filterList's icon now gets a
coloured chip, drawn from a fixed tone (accent/danger/warn/good/neutral) --
never a raw colour from data, the same rule el()'s style refusal already
holds everywhere else. Each Settings section picks one.

The sidebar was the filterList element's small dropdown-list shape (a
240px-capped box with its own border, floating in a large pane) rather
than a real full-height sidebar; .cx-filter-full opts a host into the
pane version without changing the compact one pipeline_window's own node
picker still uses.

Round one had a per-row hint line under the icon+label, which doesn't
exist in v4's own sidebar and broke alignment the moment a hint wrapped
(icon and label baseline-aligned against a hint floating as an independent
sibling rather than stacked under its own label). Fixed the real bug --
label+hint are a column now, not loose flex siblings -- then removed the
hint entirely to match v4: a section's subtitle belongs in its own head
once picked, not repeated under every row whether picked or not.

About is centred -- mark, name, version/branch -- rather than a plain
settings-row list, the one section built fresh rather than mounting an
existing window.
2026-09-07 01:22:35 +03:00

136 lines
5.4 KiB
JavaScript

// One home for everything that is a PREFERENCE about the app or the pipeline,
// rather than an edit to the project on screen -- replacing five separate
// menu items (Models and pipeline, Updates, Node packs, Log, Temp files) that
// nothing tied together and nothing let you search.
//
// Every section is MOUNTED here, in place, beside the same always-visible
// nav list -- picking "Node packs" does not leave this window any more than
// clicking a different System Settings pane leaves System Settings. Each of
// pipeline_window.js/updates.js/packs.js/logwindow.js/tempfiles.js already
// had its content built separately from its own modal chrome (a `body`
// region.stack, footer buttons set on the modal handle); their `mount()`
// exports are that same content, taking `setFooter`/`close` from whoever is
// hosting them instead of owning a modal directly. Their `open()` exports
// are unchanged for anyone opening one standalone.
import { composer } from "../composer/composer.js";
/**
* createSettingsWindow({ sections, gitStatus }) -> { open, close }
*
* `sections`: [{ id, title, subtitle, keywords, icon,
* mount({ setFooter, close }) -> handle }].
* `mount()` returns a composer element handle (its own `destroy()` is the
* cleanup, called when another section is picked or the window closes).
* `setFooter`/`close` act on this window's own modal chrome -- a section
* does not know or care whether it is standing alone or hosted here.
*
* `gitStatus` is injected, not imported, for the same reason every other
* shell window takes its network calls as props: testable without a server,
* and unable to reach for one that was not handed to it.
*
* `current` lives HERE, per call, not at module scope: a module-level
* singleton would let a second independent settings window (there is only
* ever one in this app today, but nothing enforced that) silently redraw
* the FIRST one's modal instead of opening its own.
*/
export function createSettingsWindow({ sections = [], gitStatus } = {}) {
const all = [aboutSection(gitStatus), ...sections];
let current = null;
function open(id) {
if (current) { current.show(id); return current; }
let activeCleanup = null;
const contentStack = composer.region.stack({ gap: "sm", fill: true });
function showSection(pick) {
const spec = all.find((s) => s.id === pick) || all[0];
if (!spec) return;
if (activeCleanup) { activeCleanup.destroy?.(); activeCleanup = null; }
// Cleared before mounting: a section that sets no footer of its own
// (About) must not go on showing the PREVIOUS section's buttons.
modal.setFooter({});
const content = spec.mount({
setFooter: (f) => modal.setFooter(f),
close: () => modal.close("done"),
});
activeCleanup = content;
contentStack.set([
composer.header.md({ text: spec.title }),
spec.subtitle ? composer.hint.default({ text: spec.subtitle }) : null,
content,
]);
nav.setValue(spec.id);
}
const nav = composer.filterList.md({
// No hint line here -- an icon and a name, the way v4's own sidebar
// reads. What a section IS goes in its own head once picked, not
// repeated under every row whether picked or not.
items: all.map((s) => ({
id: s.id, label: s.title, icon: s.icon, keywords: s.keywords,
tone: s.tone || "neutral",
})),
value: id,
onChange: showSection,
placeholder: "Search settings",
});
// A whole pane, not the compact dropdown-style list this element is
// usually a small part of.
nav.node.classList.add("cx-filter-full");
const body = composer.splitPane.h({ panes: [nav, contentStack], size: 25, min: 15, label: "Settings" });
const modal = composer.modal.generic({
title: "Settings", size: "xl", body,
onClose: () => {
if (activeCleanup) { activeCleanup.destroy?.(); activeCleanup = null; }
if (current === handle) current = null;
},
});
const handle = { node: modal.node, close: () => modal.close(), show: showSection };
current = handle;
showSection(id || (all[0] && all[0].id));
return handle;
}
function close() {
if (current) current.close();
}
return { open, close };
}
/** Version and branch -- the one thing everyone with the window open at all
* might be there to check, so it is built in rather than one more click away. */
function aboutSection(gitStatus) {
return {
id: "about", title: "About FunPack",
subtitle: "The version and branch running right now.",
keywords: "about version commit branch",
icon: "◉", tone: "neutral",
mount() {
const stack = composer.region.stack({ gap: "md" });
stack.node.classList.add("cx-about");
const rows = composer.region.stack({ gap: "none" });
const row = (label, value) => composer.settingsRow.default({
label, control: composer.text.sm({ text: value }) });
rows.set([row("Version", "…"), row("Branch", "…")]);
stack.set([
composer.text.lg({ text: "◉" }),
composer.header.lg({ text: "FunPack" }),
rows,
]);
if (gitStatus) {
gitStatus().then((s) => {
rows.set([row("Version", s.version || "unknown"), row("Branch", s.branch || "?")]);
}).catch(() => {
rows.set([composer.hint.default({ text: "Could not read version information." })]);
});
}
return stack;
},
};
}