Files
olivv-cs-ComfyUI-FunPack/app/composer/elements/overlay.css
T
digital-garbage 739a720adc app, core: Settings matches v4 -- retractable rail, fixed frame, real About
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.
2026-09-07 13:47:18 +03:00

173 lines
8.3 KiB
CSS

/* Every overlay lives on the portal root, which is fixed and inset:0 with
* pointer-events:none; each layer turns its own events back on. */
.cx-modal-layer, .cx-slideover-layer, .cx-blocking {
position: fixed; inset: 0; display: flex; pointer-events: auto;
}
.cx-modal-layer { align-items: flex-start; justify-content: center; padding: 7vh var(--sp-6); }
.cx-backdrop { position: absolute; inset: 0; background: var(--backdrop); }
.cx-modal {
position: relative; width: 100%;
display: flex; flex-direction: column; max-height: 86vh;
background: var(--elev-3); color: var(--text);
border: var(--bw) solid var(--line); border-radius: var(--r-5);
box-shadow: var(--shadow-3);
animation: cx-pop var(--dur-2) var(--ease-standard);
}
/* A stacked modal gets a deeper shadow and nothing else.
*
* It used to paint its own backdrop as `0 0 0 100vmax` of shadow spread, which
* was redundant -- every modal, stacked or not, already mounts a real
* .cx-backdrop element that covers the layer -- so the area under a stacked
* modal was dimmed twice. It was also the most expensive declaration in this
* sheet by a distance: a shadow spread the size of the viewport diagonal, on an
* element inside a fixed layer, with an entry animation running. That is a
* standing invitation to a stale compositor layer, and one showed up as the
* whole app painted twice, side by side, until a window resize forced a
* repaint. */
.cx-modal-stacked {
box-shadow: 0 20px 48px color-mix(in srgb, var(--shadow-ink) 90%, transparent);
}
@keyframes cx-pop { from { opacity: 0; transform: translateY(-6px) scale(.99); } }
/* A modal that hosts SWITCHABLE content -- Settings, so far -- rather than
one fixed question: sizing to whichever section is showing means the frame
itself grows and shrinks on every click, which nothing in a real System
Settings window does. A real height, not a max, holds the frame still;
.cx-modal-body's own scroll takes the rest. */
.cx-settings-modal { height: min(660px, 90vh); } /* v4's own window size, verbatim */
.cx-modal-head {
display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-5);
padding: var(--sp-6); border-bottom: var(--bw) solid var(--line);
}
.cx-modal-titles { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cx-modal-title { font-family: var(--font-display); font-size: var(--fs-2); font-weight: var(--fw-semibold); }
/* A column, so a body that wants to fill the card can. Without it the body
sized to its content and the card scrolled as a whole -- which put the group
editor's Save bar below the fold instead of holding the bottom of the
window. */
.cx-modal-body {
padding: var(--sp-6); overflow-y: auto;
display: flex; flex-direction: column; min-block-size: 0;
/* Latent until a modal had a real (not max-) height: a shrink-wrapped card
always summed head+body+foot exactly, so the foot landed at the bottom by
accident. .cx-settings-modal is a fixed height, and without this the body
stopped short of it, leaving the foot floating above true bottom with a
gap of bare card showing under it. */
flex: 1 1 auto;
}
.cx-modal-foot {
display: flex; align-items: center; justify-content: space-between; gap: var(--sp-6);
padding: var(--sp-5) var(--sp-6); border-top: var(--bw) solid var(--line);
background: var(--elev-2);
}
.cx-modal-foot-buttons { display: flex; gap: var(--sp-3); margin-left: auto; }
.cx-choice-list { display: flex; flex-direction: column; gap: var(--sp-1); }
.cx-choice-row {
display: flex; flex-direction: column; gap: 2px; text-align: left;
padding: var(--sp-4) var(--sp-5);
border: var(--bw) solid var(--line); border-radius: var(--r-3);
background: var(--elev-2); color: var(--text); cursor: pointer;
}
.cx-choice-row:hover { border-color: var(--accent); background: var(--accent-soft); }
.cx-choice-label { font-size: var(--fs-4); }
.cx-popover {
position: fixed; pointer-events: auto;
background: var(--elev-3); color: var(--text);
border: var(--bw) solid var(--line); border-radius: var(--r-4);
box-shadow: var(--shadow-3);
padding: var(--sp-2);
/* Both axes bounded, not just one. The height was capped from the start and
the width was not, so a long unbroken string -- a file path, a prompt, a
model filename -- made the popover wider than the screen and no amount of
repositioning could bring it back: anchor.js can move a box, it cannot
shrink one. Measured at 1436px inside a 1280px viewport. */
max-height: 60vh; overflow-y: auto;
max-inline-size: min(92vw, 480px);
overflow-wrap: anywhere;
animation: cx-pop var(--dur-1) var(--ease-standard);
}
.cx-popover-menu { min-width: 180px; }
.cx-menu { display: flex; flex-direction: column; gap: 1px; }
.cx-menu-item {
display: flex; align-items: center; gap: var(--sp-3); width: 100%;
padding: var(--sp-3) var(--sp-4);
border: 0; border-radius: var(--r-2); background: transparent;
color: var(--text); font-size: var(--fs-5); text-align: left; cursor: pointer;
}
.cx-menu-item:hover:not(:disabled), .cx-menu-item.cx-on { background: var(--hover); }
.cx-menu-item:disabled { opacity: var(--disabled-opacity); cursor: default; }
.cx-menu-item.cx-danger { color: var(--danger); }
.cx-menu-label { flex: 1; }
.cx-menu-hint { font-size: var(--fs-7); color: var(--faint); }
.cx-menu-icon { width: 16px; text-align: center; color: var(--muted); }
.cx-menu-sep { height: 1px; background: var(--line); margin: var(--sp-2) 0; }
.cx-tooltip {
position: fixed; pointer-events: none;
background: var(--elev-3); color: var(--text);
border: var(--bw) solid var(--line); border-radius: var(--r-2);
padding: var(--sp-2) var(--sp-3);
font-size: var(--fs-6); box-shadow: var(--shadow-2);
max-width: 260px;
}
.cx-autocomplete { min-width: 220px; padding: var(--sp-1); }
.cx-split-btn { display: inline-flex; }
.cx-split-btn .cx-btn:first-child { border-radius: var(--ctl-r) 0 0 var(--ctl-r); }
.cx-split-arrow { border-radius: 0 var(--ctl-r) var(--ctl-r) 0; margin-left: calc(var(--bw) * -1); padding: 0 var(--sp-3); }
.cx-window {
position: fixed; pointer-events: auto;
display: flex; flex-direction: column;
background: var(--elev-3); color: var(--text);
border: var(--bw) solid var(--line); border-radius: var(--r-4);
box-shadow: var(--shadow-3); overflow: hidden;
}
.cx-window-bar {
display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
padding: var(--sp-2) var(--sp-3) var(--sp-2) var(--sp-5);
background: var(--elev-2); border-bottom: var(--bw) solid var(--line);
cursor: grab; user-select: none; flex: none;
}
.cx-window-bar:active { cursor: grabbing; }
.cx-window-titles { display: flex; align-items: baseline; gap: var(--sp-3); min-width: 0; }
.cx-window-title { font-size: var(--fs-5); font-weight: var(--fw-semibold); }
.cx-window-controls { display: flex; gap: var(--sp-1); }
.cx-window-body { flex: 1; overflow: auto; padding: var(--sp-5); }
.cx-rolled .cx-window-body, .cx-rolled .cx-window-grip { display: none; }
.cx-window-grip {
position: absolute; right: 0; bottom: 0; width: 16px; height: 16px;
cursor: nwse-resize;
background: linear-gradient(135deg, transparent 50%, var(--line) 50%, var(--line) 60%, transparent 60%);
}
.cx-blocking { align-items: center; justify-content: center; background: var(--backdrop); }
.cx-blocking-card {
display: flex; flex-direction: column; align-items: center; gap: var(--sp-5);
padding: var(--sp-9) var(--sp-10);
background: var(--elev-3); border: var(--bw) solid var(--line);
border-radius: var(--r-5); box-shadow: var(--shadow-3); text-align: center;
}
.cx-slideover-layer { align-items: stretch; }
.cx-slideover {
position: relative; margin-left: auto;
width: min(420px, 92vw); display: flex; flex-direction: column;
background: var(--elev-1); border-left: var(--bw) solid var(--line);
box-shadow: var(--shadow-3);
animation: cx-slide-in var(--dur-2) var(--ease-standard);
}
.cx-slideover-left { margin-left: 0; margin-right: auto; border-left: 0; border-right: var(--bw) solid var(--line); }
@keyframes cx-slide-in { from { transform: translateX(12px); opacity: 0; } }
.cx-slideover-head {
display: flex; align-items: center; justify-content: space-between;
padding: var(--sp-5) var(--sp-6); border-bottom: var(--bw) solid var(--line);
}
.cx-slideover-body { flex: 1; overflow-y: auto; padding: var(--sp-6); }