Three things the unified Settings window was still missing next to v4's
own, all found by looking at it next to v4 rather than in isolation:
- The sidebar is a 52px icon rail now, expanding to a full list on
hover/focus and overlaying the content instead of a splitPane's
permanently-docked two-pane shape -- v4's own sidebar, not an
approximation of it. Fixing this surfaced two real CSS bugs along the
way: filterList's own min-width:220px silently defeated the collapse
(clipping the search box to "Sear" instead of showing an icon), and
.cx-filter-list's unset overflow-x (computed to auto once overflow-y
was set) put a horizontal scrollbar under it. Centering the icon in
the collapsed row needed the label's flex-basis AND the row's own gap
to both go to zero -- an invisible label still claims its share of
both otherwise, and the icon renders off-centre.
- The modal now has a real height (v4's own 660px) rather than sizing to
whichever section is showing, so switching between a short section
(About) and a long one (Node packs) doesn't visibly resize the whole
window. That exposed a THIRD latent bug: .cx-modal-body never had
flex:1 1 auto, invisible until a modal had a real height to fill --
every prior modal shrink-wrapped its content, so head+body+foot always
summed to exactly the card's height by accident. Fixed at the shared
rule, not just for Settings.
- About now shows what v4's does: commit and dirty state, a codename
when the checkout has one, and the machine's real hardware/software
facts (chip, memory, GPU or MPS, disk, OS, ComfyUI/Python/Torch/
attention-backend versions, hostname) -- a new core/sysinfo.py
(ported from v4's own, individually guarded so a missing probe never
takes the panel down) behind a new GET /funpack/api/system, injected
into settings_window.js the same way every other network call here is.
Also: a nav row's tone (the coloured icon chip) is now validated against
its closed set the same way button.js already validates its own tone --
an unknown one used to render silently wrong instead of failing loudly.
An outside click on the Settings window closed it unconditionally --
pipeline_window.js's own standalone modal has always refused to close
that way (a group edit's draft is the only copy of it), and hosting it
inside Settings silently dropped that guarantee, with no way left for a
real user to reach the protected version (boot.js has no other caller
of pipeline_window.js any more). Settings now holds the door the same
way, for every section: an accidental backdrop click during any
half-finished edit no longer costs it.
filterList's icon `tone` claimed to be a closed set, the same rule
el()'s style refusal already holds -- but nothing checked it, so a
typo would have rendered a silent, unstyled chip instead of failing
loudly the way button.js's own tone check already does for the same
mistake. Now it throws, matching that sibling pattern.
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.
Picking Node packs, Updates, the log or Temp files used to close the
Settings window and open that feature's own separate modal, with no way
back except reopening Settings from the menu bar -- not what v4 does,
and not what "unified" is supposed to mean. Each of those five files
(plus pipeline_window.js) now exports mount({ setFooter, close }), the
same content its standalone open() already built, taking the modal
chrome's footer and close from whoever hosts it instead of owning a
modal directly. open() is unchanged for anyone opening one alone --
pipeline_window.test.js and every Playwright spec for these five prove
it, unedited, still passing.
Also fixes two findings from the adversarial pass on the earlier
deep-link version: filterList's search only matched a section's visible
label/hint, so a section findable only by a keyword (e.g. "loaders" for
"Models and pipeline") silently vanished from the filtered list; and
createSettingsWindow's "one at a time" state lived at module scope, so
a second independent instance would have redrawn the first one's modal
instead of opening its own. Both now have regression tests.
Models and pipeline, Updates, Node packs, the log, and Temp files were
five separate dropdown entries with no way to search across them. One
button now opens a single searchable window; About is drawn in place,
everything else is a deep link -- each of those five already has real,
tested modal chrome of its own, so this reuses it rather than rebuilding
it inline.
Uses the existing composer.splitPane.h for the two-pane layout rather
than a new element -- an earlier pass added one, and its CSS collided
with splitPane's own .cx-split class, silently breaking the pipeline
window's own list/parameters split (caught by pipeline.spec.js before
this landed).