Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
54 lines
2.7 KiB
JavaScript
54 lines
2.7 KiB
JavaScript
// The Settings window. It owns no section: features announce theirs by mounting at "settings"
|
|
// 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); sections.sort((a, b) => GROUPS.indexOf(a.group || "") - GROUPS.indexOf(b.group || "")); } };
|
|
offer("settings", host);
|
|
let current = null;
|
|
|
|
function open(id) {
|
|
if (current) { current.show(id); return current; }
|
|
let shown = null;
|
|
const content = composer.region.stack({ gap: "sm", fill: true });
|
|
content.node.classList.add("cx-settings-content");
|
|
|
|
function show(pick) {
|
|
const spec = sections.find((s) => s.id === pick) || sections[0];
|
|
if (!spec) return;
|
|
shown?.destroy?.();
|
|
modal.setFooter({}); // a section with no footer must not keep the last one's buttons
|
|
shown = spec.mount({ setFooter: (f) => modal.setFooter(f), close: () => modal.close("done") });
|
|
content.set([composer.header.md({ text: spec.title }),
|
|
spec.subtitle ? composer.hint.default({ text: spec.subtitle }) : null, shown]);
|
|
nav.setValue(spec.id);
|
|
}
|
|
|
|
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", group: s.group || "" })),
|
|
value: id, onChange: show, placeholder: "Search settings",
|
|
});
|
|
nav.node.classList.add("cx-filter-full", "cx-settings-rail");
|
|
const body = composer.region.stack({ gap: "none", fill: true, children: [nav, content] });
|
|
body.node.classList.add("cx-settings-body");
|
|
const modal = composer.modal.generic({
|
|
title: "Settings", size: "xl", body,
|
|
closeOnOutside: false, // a half-finished edit must not vanish on a stray backdrop click
|
|
onClose: () => { shown?.destroy?.(); current = null; },
|
|
});
|
|
modal.node.classList.add("cx-settings-modal");
|
|
current = { show, close: () => modal.close() };
|
|
show(id);
|
|
return current;
|
|
}
|
|
|
|
// Added once the other menus are in, so it sits last as in v4.
|
|
const addButton = () => menubar.append(composer.button.menu({ label: "Settings", onClick: () => open() }).node);
|
|
return { open, addButton, sections: () => [...sections] };
|
|
}
|