feat(ui): Settings window as v4 laid it out — grouped sections, About and Appearance real, the rest named stand-ins
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
63dfd9c499
commit
326f3a145a
+1
-1
@@ -24,7 +24,7 @@ linkPipeline({ project, pipeline, onOpen: (fn) => on((what) => { if (what === "o
|
||||
const frame = build(document.getElementById("app"));
|
||||
const selection = createSelection({ project });
|
||||
selection.on(() => say("select"));
|
||||
const app = { project, pipeline, generate, api, on, say, title: frame.setZoneTitle, selection, playhead: createPlayhead() };
|
||||
const app = { project, pipeline, generate, api, on, say, title: frame.setZoneTitle, selection, playhead: createPlayhead(), theme: window.ComposerTheme };
|
||||
const settings = createSettings({ menubar: hostFor("menubar.menus") });
|
||||
const result = await loadFeatures(paths, { app });
|
||||
settings.addButton();
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
background: transparent; color: var(--text);
|
||||
font-size: var(--fs-5); text-align: left; cursor: pointer; width: 100%;
|
||||
}
|
||||
.cx-filter-gap { block-size: var(--sp-5); }
|
||||
.cx-filter-row:hover { background: var(--hover); }
|
||||
.cx-filter-row.cx-on { background: var(--accent-soft); color: var(--accent-hi); }
|
||||
.cx-filter-row.cx-on .cx-filter-label { font-weight: var(--fw-semibold); }
|
||||
|
||||
@@ -169,7 +169,10 @@ define("filterList", "md", ({ items = [], value, onChange, placeholder = "Search
|
||||
!q || `${i.label} ${i.hint || ""} ${i.keywords || ""}`.toLowerCase().includes(q));
|
||||
list.replaceChildren();
|
||||
if (!shown.length) { list.append(el("div", { cls: "cx-filter-empty", text: empty })); return; }
|
||||
let before;
|
||||
for (const item of shown) {
|
||||
if (before !== undefined && item.group !== before) list.append(el("div", { cls: "cx-filter-gap", attrs: { "aria-hidden": "true" } })); // a new group starts after a gap
|
||||
before = item.group;
|
||||
// `tone` picks one of a FIXED set of chip colours -- never a raw
|
||||
// colour from data, which is the rule `el()`'s style refusal exists to
|
||||
// hold everywhere else too. Enforced, not just claimed: an unknown
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// About FunPack: which build this is, and the machine ComfyUI runs on (which on a rental is not this browser's).
|
||||
import { composer as c } from "../../composer/composer.js";
|
||||
|
||||
const facts = (rows) => rows.filter(([, v]) => v).map(([k, v]) => c.settingsRow.default({ label: k, control: c.text.sm({ text: String(v) }) }));
|
||||
|
||||
export const about = (api) => function mount() {
|
||||
const body = c.region.stack({ gap: "sm", label: "About" });
|
||||
body.set([c.hint.default({ text: "Looking…" })]);
|
||||
Promise.all([api.gitStatus().catch(() => ({})), api.system().catch(() => null)]).then(([git, sys]) => {
|
||||
const major = String(git.version || "").split(".")[0];
|
||||
const mem = (sys && sys.memory) || {}, cpu = (sys && sys.cpu) || {}, disk = (sys && sys.disk) || {}, torch = (sys && sys.torch) || {}, gpu = ((sys && sys.gpus) || [])[0];
|
||||
body.set([
|
||||
c.header.md({ text: `FunPack${major ? ` ${major}` : ""}` }),
|
||||
c.hint.default({ text: [git.codename && `“${git.codename}”`, "Cutting Room"].filter(Boolean).join(" · ") }),
|
||||
...facts([["Version", git.version], ["Commit", git.commit && String(git.commit).slice(0, 7)], ["Branch", git.branch]]),
|
||||
...(sys ? [c.label.section({ text: "Hardware" }),
|
||||
...facts([["Chip", cpu.name], ["Memory", mem.total_gb != null && `${mem.available_gb} GB free of ${mem.total_gb} GB`],
|
||||
["Graphics", gpu ? [gpu.name, gpu.vram_gb && `${gpu.vram_gb} GB`].filter(Boolean).join(" · ") : sys.mps ? "Apple GPU (MPS)" : "CPU only"],
|
||||
["Storage", disk.total_gb != null && `${disk.free_gb} GB available of ${disk.total_gb} GB`]]),
|
||||
c.label.section({ text: "Software" }),
|
||||
...facts([["System", sys.os], ["ComfyUI", sys.comfyui], ["Python", sys.python], ["Torch", [torch.version, torch.cuda && `CUDA ${torch.cuda}`].filter(Boolean).join(" · ")], ["Host", sys.host]]),
|
||||
c.hint.default({ text: "The machine ComfyUI runs on — not this browser." })] : [c.hint.default({ text: "Machine details are not available from this server." })]),
|
||||
]);
|
||||
});
|
||||
return body;
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
// Appearance: dark, light, or follow the system.
|
||||
import { composer as c } from "../../composer/composer.js";
|
||||
|
||||
export const appearance = (theme) => function mount() {
|
||||
return c.settingsRow.default({ label: "Colour scheme", hint: "Light, dark, or follow the system.",
|
||||
control: c.segmented.sm({ label: "Colour scheme", value: theme.get(), onChange: (v) => theme.apply(v),
|
||||
options: [{ value: "dark", label: "Dark" }, { value: "light", label: "Light" }, { value: "auto", label: "Auto" }] }) });
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
// Settings sections that sit in v4's window: the real ones, and stand-ins (named, grouped, saying what is coming) for the rest.
|
||||
import { composer as c } from "../../composer/composer.js";
|
||||
import { about } from "./about.js";
|
||||
import { appearance } from "./appearance.js";
|
||||
|
||||
const later = (what) => function mount() {
|
||||
return c.emptyState.default({ icon: "◌", title: "Not built yet", hint: what });
|
||||
};
|
||||
|
||||
export default {
|
||||
id: "settings-sections",
|
||||
mount: "settings",
|
||||
needs: ["api", "theme"],
|
||||
setup({ host, app }) {
|
||||
const add = (id, title, subtitle, icon, tone, mount, group = "", keywords = "") => host.add({ id, group, title, subtitle, icon, tone, mount, keywords: `${keywords} ${title}` });
|
||||
add("about", "About FunPack", "", "◎", "accent", about(app.api), "", "version commit branch cpu memory gpu disk python torch");
|
||||
add("appearance", "Appearance", "Light, dark, or follow the system.", "◐", "neutral", appearance(app.theme), "", "theme colour light dark auto");
|
||||
add("editor", "Editor", "How the editor behaves. The open project remembers these, so they follow it to another machine.", "☰", "neutral",
|
||||
later("Upscale model, prompt autocomplete, shortcut ideas, i2v options."), "", "upscale autocomplete shortcuts ideas preferences");
|
||||
add("engine", "Engine", "Settings every installed module has volunteered, grouped by what they're for.", "⚡", "warn",
|
||||
later("Continuity, guidance, conditioning, sampling and post settings of the installed modules."), "Generation", "engine module settings");
|
||||
add("models", "Models & Pipeline", "Loaders and nodes wired into the live pipeline.", "⬡", "accent",
|
||||
later("Pick the model, VAE, CLIP and LoRA files; see the live pipeline."), "Generation", "models loaders unet vae clip lora");
|
||||
add("modules", "Modules", "Switch a module off for this project; see and re-enable ones that failed.", "☷", "neutral",
|
||||
later("Per-module on/off for this project, and the ones that failed to load."), "Generation", "modules enable disable quarantine");
|
||||
add("refinement", "Refinement & Taste", "Learned-taste state: refinement keys and the Absolute global-taste store.", "✦", "danger",
|
||||
later("Refinement keys: export, delete; the taste store."), "Learning", "refinement taste keys rating");
|
||||
add("system", "Updates & ComfyUI", "Server connection, FunPack code updates, pipeline health.", "⟳", "good",
|
||||
later("Update, switch branch, restart ComfyUI, roll back."), "System", "update git branch restart rollback");
|
||||
add("customnodes", "Custom Nodes", "Install, update and remove ComfyUI node packs.", "⧉", "neutral",
|
||||
later("Install, update and remove node packs."), "System", "custom nodes packs install");
|
||||
},
|
||||
};
|
||||
@@ -4,7 +4,7 @@ export default {
|
||||
id: "temp",
|
||||
mount: "settings",
|
||||
setup: ({ host }) => host.add({
|
||||
id: "temp", title: "Temp files", subtitle: "Where a file went when it did not land in the bin.",
|
||||
id: "temp", group: "System", title: "Temp Files", subtitle: "Where a file went when it did not land in the bin.",
|
||||
keywords: "temp files output directory", icon: "▥", mount,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -12,5 +12,6 @@ export default [
|
||||
"./features/render/index.js",
|
||||
"./features/placeholders/index.js",
|
||||
"./features/generate/warn.js",
|
||||
"./features/settings/index.js",
|
||||
"./features/temp/index.js",
|
||||
].map((path) => new URL(path, import.meta.url).href);
|
||||
|
||||
@@ -22,6 +22,8 @@ export const api = {
|
||||
pipeline: () => call("GET", "/api/pipeline"),
|
||||
editPipeline: (body) => call("POST", "/api/pipeline", body),
|
||||
modules: (traits) => call("GET", `/api/modules${traits ? `?traits=${encodeURIComponent(traits)}` : ""}`),
|
||||
system: () => call("GET", "/api/system"),
|
||||
gitStatus: () => call("GET", "/api/git/status?remote=0"),
|
||||
media: () => call("GET", "/api/media"),
|
||||
deleteMedia: (id) => call("DELETE", `/api/media/${encodeURIComponent(id)}`),
|
||||
/** Upload files as media, one request each so one refusal (too big, wrong type) does not sink the rest.
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
// The Settings window. It owns no section: features announce theirs by mounting at "settings"
|
||||
// setup({ host }) { host.add({ id, title, subtitle, keywords, icon, tone, mount({ setFooter, close }) -> handle }) }
|
||||
// setup({ host }) { host.add({ id, group?, title, subtitle, keywords, icon, tone, mount({ setFooter, close }) -> handle }) }
|
||||
// and the window lists whatever arrived. It is also the one place a "Settings" button is made.
|
||||
import { composer } from "../composer/composer.js";
|
||||
import { offer } from "./mounts.js";
|
||||
|
||||
export function createSettings({ menubar }) {
|
||||
const GROUPS = ["", "Generation", "Learning", "System"]; // the order v4 laid the window out in
|
||||
const sections = [];
|
||||
// childNodes: the feature loader checks a region's children to undo a feature that fails half-way
|
||||
const host = { childNodes: [], add: (section) => sections.push(section) };
|
||||
const host = { childNodes: [], add: (section) => { sections.push(section); sections.sort((a, b) => GROUPS.indexOf(a.group || "") - GROUPS.indexOf(b.group || "")); } };
|
||||
offer("settings", host);
|
||||
let current = null;
|
||||
|
||||
@@ -29,7 +30,7 @@ export function createSettings({ menubar }) {
|
||||
}
|
||||
|
||||
const nav = composer.filterList.md({
|
||||
items: sections.map((s) => ({ id: s.id, label: s.title, icon: s.icon, keywords: s.keywords, tone: s.tone || "neutral" })),
|
||||
items: sections.map((s) => ({ id: s.id, label: s.title, icon: s.icon, keywords: s.keywords, tone: s.tone || "neutral", group: s.group || "" })),
|
||||
value: id, onChange: show, placeholder: "Search settings",
|
||||
});
|
||||
nav.node.classList.add("cx-filter-full", "cx-settings-rail");
|
||||
|
||||
Reference in New Issue
Block a user