Replaces every native confirm() with a shared P.confirm() modal (a hung native dialog was found live to block the whole tab, including auto-refresh, and once even double-confirmed an unrelated deletion). Also fixes Edit Container silently resetting to Bridge/blanking the Static IP for any container on a custom network, and a context menu losing its anchor to a mid-read auto-refresh. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
736 lines
44 KiB
CSS
736 lines
44 KiB
CSS
/**
|
||
* styles/podman.css
|
||
*
|
||
* Design tokens and component styles for the Podman plugin page. Ported
|
||
* directly from the approved mockup (webui/mockups/prototype.html) so the
|
||
* real implementation is visually identical to what was reviewed — the
|
||
* masthead/main-nav chrome from that mockup is NOT included here, since
|
||
* Podman.page renders inside Unraid's own real chrome, not a recreation
|
||
* of it.
|
||
*
|
||
* Token strategy: custom properties on :root, redefined under
|
||
* prefers-color-scheme (OS default) and under [data-theme] (explicit
|
||
* override, e.g. from Unraid's own theme setting) — component rules below
|
||
* only ever reference the tokens, never hardcode a color, so both themes
|
||
* stay in sync automatically. See docs/ARCHITECTURE.md section 18.
|
||
*/
|
||
|
||
.podman-plugin {
|
||
--bg: #eef0f2; --surface: #ffffff; --surface-2: #f4f5f7; --surface-3: #e9ebee;
|
||
--border: #dde1e6; --text: #1c2024; --text-dim: #5b6572; --text-faint: #8a94a1;
|
||
--accent: #d8541a; --accent-strong: #b8420f; --accent-contrast: #fff8f3;
|
||
--good: #1a8f4c; --good-bg: #e4f6ea; --warn: #9a6b00; --warn-bg: #fdf1d6;
|
||
--bad: #c22b3a; --bad-bg: #fbe6e8; --bad-strong: #9c1f2c; --bad-contrast: #fff5f6; --neutral: #5b6572; --neutral-bg: #e9ebee;
|
||
--shadow: 0 1px 2px rgba(20, 22, 26, .06), 0 4px 12px rgba(20, 22, 26, .05);
|
||
--font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||
--font-mono: ui-monospace, "SF Mono", "Cascadia Code", "Roboto Mono", Consolas, "Liberation Mono", monospace;
|
||
font-family: var(--font-ui);
|
||
color: var(--text);
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
}
|
||
@media (prefers-color-scheme: dark) {
|
||
.podman-plugin {
|
||
--bg: #15171b; --surface: #1c1f24; --surface-2: #23262c; --surface-3: #2b2f36;
|
||
--border: #34383f; --text: #e7e9ec; --text-dim: #9aa1ab; --text-faint: #6b7280;
|
||
--accent: #ef7f3f; --accent-strong: #f6975f; --accent-contrast: #1a1002;
|
||
--good: #4cc785; --good-bg: #123322; --warn: #e0b23d; --warn-bg: #3a2e0d;
|
||
--bad: #ef6470; --bad-bg: #3a1519; --bad-strong: #f6838c; --bad-contrast: #2a0a0d; --neutral: #9aa1ab; --neutral-bg: #2b2f36;
|
||
--shadow: 0 1px 2px rgba(0,0,0,.3), 0 8px 24px rgba(0,0,0,.35);
|
||
}
|
||
}
|
||
:root[data-theme="dark"] .podman-plugin {
|
||
--bg: #15171b; --surface: #1c1f24; --surface-2: #23262c; --surface-3: #2b2f36;
|
||
--border: #34383f; --text: #e7e9ec; --text-dim: #9aa1ab; --text-faint: #6b7280;
|
||
--accent: #ef7f3f; --accent-strong: #f6975f; --accent-contrast: #1a1002;
|
||
--good: #4cc785; --good-bg: #123322; --warn: #e0b23d; --warn-bg: #3a2e0d;
|
||
--bad: #ef6470; --bad-bg: #3a1519; --bad-strong: #f6838c; --bad-contrast: #2a0a0d; --neutral: #9aa1ab; --neutral-bg: #2b2f36;
|
||
--shadow: 0 1px 2px rgba(0,0,0,.3), 0 8px 24px rgba(0,0,0,.35);
|
||
}
|
||
:root[data-theme="light"] .podman-plugin {
|
||
--bg: #eef0f2; --surface: #ffffff; --surface-2: #f4f5f7; --surface-3: #e9ebee;
|
||
--border: #dde1e6; --text: #1c2024; --text-dim: #5b6572; --text-faint: #8a94a1;
|
||
--accent: #d8541a; --accent-strong: #b8420f; --accent-contrast: #fff8f3;
|
||
--good: #1a8f4c; --good-bg: #e4f6ea; --warn: #9a6b00; --warn-bg: #fdf1d6;
|
||
--bad: #c22b3a; --bad-bg: #fbe6e8; --bad-strong: #9c1f2c; --bad-contrast: #fff5f6; --neutral: #5b6572; --neutral-bg: #e9ebee;
|
||
--shadow: 0 1px 2px rgba(20,22,26,.06), 0 4px 12px rgba(20,22,26,.05);
|
||
}
|
||
|
||
.podman-plugin * { box-sizing: border-box; }
|
||
.podman-plugin h1, .podman-plugin h2, .podman-plugin h3 { text-wrap: balance; margin: 0; font-weight: 600; }
|
||
.podman-plugin .tnum { font-variant-numeric: tabular-nums; }
|
||
.podman-plugin .mono { font-family: var(--font-mono); }
|
||
.podman-plugin :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
|
||
|
||
.podman-pagehead {
|
||
display: flex; align-items: flex-end; justify-content: space-between;
|
||
gap: 16px; flex-wrap: wrap; padding: 4px 0 0;
|
||
}
|
||
.podman-pagehead .titlewrap { display: flex; align-items: center; gap: 12px; }
|
||
.podman-pagehead .icon {
|
||
width: 38px; height: 38px; border-radius: 9px; background: var(--surface-3);
|
||
display: grid; place-items: center; color: var(--accent); border: 1px solid var(--border);
|
||
}
|
||
.podman-pagehead h1 { font-size: 21px; }
|
||
.podman-pagehead .meta { font-size: 12.5px; color: var(--text-dim); margin-top: 2px; }
|
||
.podman-pagehead .meta .dot-good { color: var(--good); }
|
||
.podman-pagehead .meta .dot-bad { color: var(--bad); }
|
||
|
||
/*
|
||
* margin: 0 — Unraid's own webGui theme applies a 10px top/bottom margin
|
||
* to plain <button> elements site-wide. Without resetting it, every
|
||
* .podman-btn carries an invisible 10px gap above and below its own box,
|
||
* which silently breaks flex cross-axis alignment anywhere a button sits
|
||
* next to a non-button sibling (e.g. align-items: flex-end next to a
|
||
* <select> — verified live: the button's margin, not its content, was
|
||
* what left it floating 10px above the dropdown it should line up with).
|
||
*/
|
||
.podman-btn {
|
||
appearance: none; border: 1px solid var(--border); background: var(--surface); color: var(--text);
|
||
padding: 8px 14px; border-radius: 7px; font-size: 13px; font-weight: 600; cursor: pointer;
|
||
display: inline-flex; align-items: center; gap: 6px; transition: border-color .12s, background .12s;
|
||
font-family: var(--font-ui); margin: 0;
|
||
}
|
||
.podman-btn:hover { border-color: var(--text-faint); }
|
||
/*
|
||
* !important here for the same reason as .podman-btn-ghost below: Unraid's
|
||
* own webGUI theme applies a default border/hover treatment to every
|
||
* <button> that otherwise silently wins over this rule at rest — verified
|
||
* live via a screen recording: without !important, "Create" only ever
|
||
* looked filled while under the mouse (Unraid's generic hover glow, applied
|
||
* to literally any button), never at rest, making it indistinguishable
|
||
* from Cancel except by coincidence of cursor position.
|
||
*/
|
||
.podman-btn-primary {
|
||
background: var(--accent) !important; border-color: var(--accent) !important; color: var(--accent-contrast) !important;
|
||
}
|
||
.podman-btn-primary:hover {
|
||
background: var(--accent-strong) !important; border-color: var(--accent-strong) !important;
|
||
}
|
||
.podman-btn-danger { color: var(--bad); }
|
||
.podman-btn-danger:hover { border-color: var(--bad); }
|
||
.podman-btn-icon { padding: 6px 8px; min-width: 32px; min-height: 32px; justify-content: center; font-size: 15px; line-height: 1; }
|
||
.podman-btn[disabled] { opacity: .4; cursor: not-allowed; }
|
||
/*
|
||
* Secondary action (Cancel, "+ Add row") — every button previously shared
|
||
* the same bordered/accent-colored treatment as Create, so nothing in a
|
||
* form stood out as THE primary action (found from a live screenshot AND
|
||
* a screen recording: Cancel/Create/+Add/× all read as equally weighted,
|
||
* with the exact same hover glow even). Unraid's own webGUI applies a
|
||
* site-wide default border+hover-gradient to every <button>, at higher
|
||
* effective priority than a plain single-class selector here — verified
|
||
* live: a bare `.podman-btn-ghost { border-color: transparent }` was
|
||
* silently losing to it, on both the rest AND hover state. !important is
|
||
* the only reliable way to guarantee this specific, deliberate style
|
||
* wins regardless of what Unraid's base theme does elsewhere.
|
||
*/
|
||
.podman-btn-ghost {
|
||
background: transparent !important; border-color: transparent !important;
|
||
color: var(--text-dim) !important; box-shadow: none !important;
|
||
}
|
||
.podman-btn-ghost:hover {
|
||
background: var(--surface-2) !important; border-color: transparent !important;
|
||
color: var(--text) !important; box-shadow: none !important;
|
||
}
|
||
/*
|
||
* A ghost button can still carry danger intent (Disconnect, Delete,
|
||
* template "Delete") — needs its own !important since .podman-btn-ghost's
|
||
* color/background/border would otherwise win by rule order. Solid fill
|
||
* at rest (not just a tint, and not just on hover) so it reads with the
|
||
* same weight as .podman-btn-primary, just in red instead of accent —
|
||
* a merely tinted/outlined button still read as "just another secondary
|
||
* action" per live feedback.
|
||
*/
|
||
.podman-btn-ghost.podman-btn-danger {
|
||
color: var(--bad-contrast) !important; background: var(--bad) !important; border-color: var(--bad) !important;
|
||
}
|
||
.podman-btn-ghost.podman-btn-danger:hover {
|
||
background: var(--bad-strong) !important; border-color: var(--bad-strong) !important; color: var(--bad-contrast) !important;
|
||
}
|
||
.podman-btn-ghost.podman-btn-danger[disabled] {
|
||
color: var(--text-faint) !important; background: transparent !important; border-color: var(--border) !important;
|
||
}
|
||
/* Icon-only danger buttons (row "remove" trash icons) carry an emoji
|
||
glyph, not text — .podman-btn-danger's `color` alone doesn't recolor an
|
||
emoji, so these get the same solid red fill instead, at rest not just
|
||
on hover, so "destructive" reads at a glance across a whole table. */
|
||
.podman-btn-icon.podman-btn-danger { border-color: var(--bad) !important; background: var(--bad) !important; }
|
||
.podman-btn-icon.podman-btn-danger:hover { background: var(--bad-strong) !important; border-color: var(--bad-strong) !important; }
|
||
.podman-btn-icon.podman-btn-danger[disabled] { border-color: var(--border) !important; background: transparent !important; }
|
||
|
||
.podman-subnav {
|
||
margin: 14px 0 0; padding: 0; display: flex; gap: 4px; border-bottom: 1px solid var(--border);
|
||
overflow-x: auto; list-style: none;
|
||
}
|
||
.podman-subnav button {
|
||
appearance: none; background: none; border: none; border-bottom: 2px solid transparent;
|
||
color: var(--text-dim); padding: 10px 12px; font-size: 13px; font-weight: 600; cursor: pointer;
|
||
white-space: nowrap; display: flex; align-items: center; gap: 6px; font-family: var(--font-ui);
|
||
}
|
||
.podman-subnav button:hover { color: var(--text); }
|
||
.podman-subnav button.active { color: var(--accent-strong); border-bottom-color: var(--accent); }
|
||
.podman-subnav button .n { font-size: 10.5px; padding: 1px 5px; border-radius: 8px; background: var(--surface-3); color: var(--text-dim); }
|
||
|
||
.podman-main { padding: 20px 0 48px; }
|
||
.podman-panel { display: none; }
|
||
.podman-panel.active { display: block; }
|
||
|
||
.podman-card { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow); }
|
||
.podman-card-pad { padding: 16px 18px; }
|
||
.podman-card-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px; border-bottom: 1px solid var(--border); }
|
||
.podman-card-head h2 { font-size: 14.5px; }
|
||
.podman-card-head .sub { font-size: 12px; color: var(--text-faint); font-weight: 500; }
|
||
|
||
.podman-grid { display: grid; gap: 14px; }
|
||
.podman-stat-grid { grid-template-columns: repeat(6, 1fr); }
|
||
@media (max-width: 1080px) { .podman-stat-grid { grid-template-columns: repeat(3, 1fr); } }
|
||
@media (max-width: 620px) { .podman-stat-grid { grid-template-columns: repeat(2, 1fr); } }
|
||
|
||
.podman-stat {
|
||
background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 14px 16px;
|
||
box-shadow: var(--shadow); transition: transform .12s ease, box-shadow .12s ease;
|
||
}
|
||
.podman-stat:hover { transform: translateY(-1px); box-shadow: 0 2px 4px rgba(20, 22, 26, .08), 0 8px 20px rgba(20, 22, 26, .08); }
|
||
.podman-stat .label { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); font-weight: 700; }
|
||
.podman-stat .value { font-size: 24px; font-weight: 700; margin-top: 6px; }
|
||
.podman-stat .value small { font-size: 13px; color: var(--text-dim); font-weight: 600; }
|
||
.podman-stat .tone-good { color: var(--good); }
|
||
.podman-stat .tone-warn { color: var(--warn); }
|
||
.podman-stat .tone-bad { color: var(--bad); }
|
||
.podman-usage-mini .track > span.warn { background: var(--warn); }
|
||
.podman-usage-mini .track > span.bad { background: var(--bad); }
|
||
|
||
.podman-resource-rows { margin-top: 8px; }
|
||
.podman-resource-row { display: flex; align-items: center; gap: 12px; padding: 7px 0; }
|
||
.podman-resource-row + .podman-resource-row { border-top: 1px solid var(--border); }
|
||
.podman-resource-row .k { font-size: 12.5px; color: var(--text-dim); font-weight: 600; min-width: 84px; flex: none; }
|
||
.podman-resource-row .v { font-size: 12.5px; color: var(--text-faint); font-weight: 600; white-space: nowrap; flex: none; font-variant-numeric: tabular-nums; }
|
||
|
||
.podman-chip {
|
||
display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: 100px;
|
||
font-size: 11.5px; font-weight: 700; letter-spacing: .01em; white-space: nowrap;
|
||
}
|
||
.podman-chip .d { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
|
||
.podman-chip-good { background: var(--good-bg); color: var(--good); }
|
||
.podman-chip-warn { background: var(--warn-bg); color: var(--warn); }
|
||
.podman-chip-bad { background: var(--bad-bg); color: var(--bad); }
|
||
.podman-chip-neutral { background: var(--neutral-bg); color: var(--neutral); }
|
||
|
||
.podman-plugin table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||
.podman-plugin thead th {
|
||
text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
|
||
color: var(--text-faint); font-weight: 700; padding: 10px 18px; border-bottom: 1px solid var(--border);
|
||
}
|
||
.podman-plugin tbody td { padding: 11px 18px; border-bottom: 1px solid var(--border); vertical-align: middle; }
|
||
.podman-plugin tbody tr:last-child td { border-bottom: none; }
|
||
.podman-plugin tbody tr:hover { background: var(--surface-2); }
|
||
.podman-table-wrap { overflow-x: auto; }
|
||
/*
|
||
* table-layout: auto (the default) sizes every column from the widest
|
||
* content across only the CURRENTLY VISIBLE rows — so expanding/
|
||
* collapsing a folder (which adds/removes rows) changed what counted as
|
||
* "widest" and shifted every column, including the header text, on
|
||
* every toggle (found live). Fixed widths (set on the <th>s in
|
||
* Podman.page) make column sizing depend only on those, never on row
|
||
* content, so toggling a folder can no longer move anything.
|
||
*/
|
||
#containers-table { table-layout: fixed; }
|
||
/* Only the plain-text columns truncate with an ellipsis (Image/Ports —
|
||
both are just escaped text directly in the <td>, so ellipsis renders
|
||
correctly) — NOT the Name column (its text sits inside a flex
|
||
button+icon, where overflow:hidden would hard-clip instead of
|
||
ellipsis-truncate), the folder-header row's colspan cell (its member
|
||
chips need to wrap, see .podman-folder-members), or the actions
|
||
column (icon buttons, not text, would otherwise get clipped if they
|
||
ever didn't quite fit). */
|
||
#containers-table tbody > tr:not(.podman-folder-row) > td:nth-child(3),
|
||
#containers-table tbody > tr:not(.podman-folder-row) > td:nth-child(5) {
|
||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||
}
|
||
/* .podman-name-cell is a <div> INSIDE the <td>, not the <td> itself —
|
||
display:flex directly on a table cell pulls it out of table-cell
|
||
layout entirely, so with table-layout:fixed it stops respecting the
|
||
column width the <th> assigns and throws off alignment with every
|
||
other column (found live: the whole Name column visibly drifted).
|
||
The div gives the same flex row (so a WebUI link, see
|
||
.podman-webui-link, sits on the same line as the name button instead
|
||
of wrapping below it — .podman-row-name-btn's own display:flex makes
|
||
IT block-level by default, which would otherwise push everything
|
||
after it in the cell onto its own line) without that side effect.
|
||
min-width:0 lets the button actually shrink instead of forcing the
|
||
cell wider than its fixed column width; .text (not the plain text
|
||
node it used to be) is what actually ellipsis-truncates, since
|
||
text-overflow only applies to the element whose own inline content
|
||
overflows, not a nested flex child's. */
|
||
.podman-name-cell { display: flex; align-items: center; gap: 6px; }
|
||
.podman-row-name { display: flex; align-items: center; gap: 10px; font-weight: 600; min-width: 0; }
|
||
.podman-row-name .text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||
.podman-row-name-btn {
|
||
/* !important for the same reason as .podman-btn-ghost/-primary — Unraid's
|
||
own site-wide button theme otherwise still shows its default border
|
||
at rest (only losing to plain rules on hover), so a name link one
|
||
click away from every table row still looked like a bordered button
|
||
forever, not a plain label. */
|
||
appearance: none; border: none !important; background: none !important; padding: 0; cursor: pointer;
|
||
color: var(--text); font-family: var(--font-ui); font-size: 13px; text-align: left; min-width: 0;
|
||
}
|
||
.podman-row-name-btn:hover { color: var(--accent-strong); }
|
||
.podman-row-name-btn:hover .ico { border-color: var(--accent); }
|
||
.podman-row-name .ico {
|
||
width: 26px; height: 26px; border-radius: 6px; flex: none; background: var(--surface-3);
|
||
display: grid; place-items: center; font-size: 12px; border: 1px solid var(--border); color: var(--text-dim);
|
||
overflow: hidden;
|
||
}
|
||
.podman-row-name .ico img { width: 100%; height: 100%; object-fit: cover; }
|
||
.podman-row-sub { font-size: 11.5px; color: var(--text-faint); font-weight: 500; margin-top: 1px; }
|
||
/*
|
||
* The actions <td> itself stays a plain table-cell (default display) so
|
||
* every row's column width is computed the same way by the table's layout
|
||
* algorithm — putting "display: flex" directly on the <td> used to take it
|
||
* out of that algorithm, so browsers could size/position it slightly
|
||
* differently row to row (found live: the trash-can button in Images drifted
|
||
* a few pixels between rows instead of lining up in one column). The actual
|
||
* flex/gap/alignment lives on this inner wrapper instead.
|
||
*/
|
||
.podman-actions { text-align: right; white-space: nowrap; }
|
||
.podman-actions-row { display: inline-flex; gap: 4px; justify-content: flex-end; }
|
||
|
||
/*
|
||
* Segmented toggle (Containers' All/Running/Stopped filter, Logs' Follow/
|
||
* Paused) — previously just an inline-styled wrapper <div> around plain
|
||
* <button>s with no CSS of their own at all, so every option (not just the
|
||
* active one) showed Unraid's own default button border permanently,
|
||
* all three chips looking identically "selected". !important for the same
|
||
* site-wide-theme-override reason as .podman-btn-ghost/-primary.
|
||
*/
|
||
.podman-segmented { display: flex; gap: 2px; background: var(--surface-3); border: 1px solid var(--text-faint); padding: 3px; border-radius: 8px; }
|
||
.podman-segmented button {
|
||
appearance: none; border: none !important; background: transparent !important; color: var(--text-dim) !important;
|
||
padding: 6px 12px; border-radius: 6px; font-size: 12px; font-weight: 700; cursor: pointer;
|
||
font-family: var(--font-ui); transition: background .12s, color .12s;
|
||
}
|
||
.podman-segmented button:hover { color: var(--text) !important; }
|
||
/* Filled with the accent color (not just a slightly different neutral
|
||
shade) — the previous var(--surface) vs. var(--surface-2) contrast
|
||
between active/inactive was too close in the dark theme to notice at a
|
||
glance (found live). */
|
||
.podman-segmented button.active { background: var(--accent) !important; color: var(--accent-contrast) !important; box-shadow: var(--shadow); }
|
||
|
||
.podman-usage-mini { display: flex; align-items: center; gap: 8px; min-width: 110px; }
|
||
.podman-usage-mini .track { flex: 1; height: 5px; border-radius: 3px; background: var(--surface-3); overflow: hidden; }
|
||
.podman-usage-mini .track > span { display: block; height: 100%; background: var(--accent); }
|
||
.podman-usage-mini .num { font-size: 11.5px; color: var(--text-dim); width: 34px; text-align: right; font-variant-numeric: tabular-nums; }
|
||
|
||
.podman-toolbar { display: flex; align-items: center; gap: 10px; padding: 14px 18px; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
|
||
/*
|
||
* !important throughout: Unraid's own webGui/styles/default-base.css
|
||
* targets input[type="text"] with an attribute selector (higher
|
||
* specificity than our single .podman-search class, :where() around it
|
||
* notwithstanding) forcing border-width:0 / border-bottom-width:1px /
|
||
* background:transparent — an underline-only text field, not a boxed one.
|
||
* Found live: our border/background were being silently dropped even
|
||
* though this rule appears later in the stylesheet.
|
||
*/
|
||
.podman-search {
|
||
flex: 1; min-width: 180px; max-width: 320px; font-size: 13px; color: var(--text); font-family: var(--font-ui);
|
||
background: var(--surface-3) !important; border: 1px solid var(--text-faint) !important;
|
||
border-radius: 7px !important; padding: 7px 11px !important;
|
||
}
|
||
.podman-search::placeholder { color: var(--text-faint); }
|
||
|
||
.podman-two-col { display: grid; grid-template-columns: 1.3fr 1fr; gap: 14px; align-items: start; }
|
||
@media (max-width: 920px) { .podman-two-col { grid-template-columns: 1fr; } }
|
||
|
||
.podman-activity-list { list-style: none; margin: 0; padding: 6px 0; }
|
||
.podman-activity-list li { display: flex; gap: 10px; padding: 9px 18px; border-bottom: 1px solid var(--border); font-size: 12.5px; }
|
||
.podman-activity-list li:last-child { border-bottom: none; }
|
||
.podman-activity-list .dot { width: 7px; height: 7px; border-radius: 50%; margin-top: 5px; flex: none; }
|
||
.podman-activity-list .when { color: var(--text-faint); font-size: 11px; margin-top: 1px; }
|
||
|
||
.podman-empty-note { padding: 28px 18px; text-align: center; color: var(--text-faint); font-size: 13px; }
|
||
|
||
.podman-pod-card { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; margin-bottom: 14px; background: var(--surface); box-shadow: var(--shadow); }
|
||
.podman-pod-head { display: flex; align-items: center; gap: 10px; padding: 13px 16px; background: var(--surface-2); border-bottom: 1px solid var(--border); }
|
||
.podman-pod-head .name { font-weight: 700; font-size: 13.5px; }
|
||
.podman-pod-head .infra { font-size: 11.5px; color: var(--text-faint); margin-right: auto; }
|
||
|
||
.podman-badge { display: inline-block; font-size: 10.5px; font-weight: 700; color: var(--text-dim); background: var(--surface-3); padding: 2px 8px; border-radius: 100px; margin-top: 6px; }
|
||
|
||
.podman-template-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 14px; padding: 18px; }
|
||
.podman-template-grid .podman-empty-note { grid-column: 1 / -1; }
|
||
.podman-pager { display: flex; align-items: center; justify-content: center; gap: 14px; padding: 4px 18px 18px; font-size: 12.5px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
|
||
.podman-template-card {
|
||
border: 1px solid var(--border); border-radius: 10px; padding: 14px; background: var(--surface);
|
||
display: flex; flex-direction: column; gap: 10px;
|
||
}
|
||
.podman-template-icon { width: 40px; height: 40px; border-radius: 8px; object-fit: cover; background: var(--surface-2); }
|
||
.podman-template-icon-fallback {
|
||
display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 16px;
|
||
color: var(--accent); border: 1px solid var(--border);
|
||
}
|
||
.podman-template-name { font-weight: 700; font-size: 13.5px; }
|
||
.podman-template-overview { font-size: 12px; color: var(--text-dim); line-height: 1.4; }
|
||
.podman-template-actions { display: flex; gap: 6px; margin-top: auto; padding-top: 4px; }
|
||
/*
|
||
* min-width: 0 overrides the flex-item default of min-width: auto, which
|
||
* otherwise refuses to shrink a button below its own label's intrinsic
|
||
* width — without it, "Delete" (the widest label, and uppercased by
|
||
* Unraid's own site-wide button theme) pushed past the card's right edge
|
||
* instead of actually sharing the row evenly with Use/Export (found live).
|
||
*/
|
||
.podman-template-actions .podman-btn {
|
||
flex: 1; min-width: 0; justify-content: center; padding: 6px 8px; font-size: 11.5px;
|
||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||
}
|
||
|
||
.podman-local-template-list { max-height: 220px; overflow-y: auto; border: 1px solid var(--border); border-radius: 7px; margin-top: 8px; }
|
||
.podman-local-template-item {
|
||
display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-bottom: 1px solid var(--border);
|
||
}
|
||
.podman-local-template-item:last-child { border-bottom: none; }
|
||
.podman-local-template-name { font-weight: 600; font-size: 12.5px; white-space: nowrap; }
|
||
.podman-local-template-item .podman-row-sub { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||
.podman-local-template-item .podman-btn { flex: none; padding: 5px 10px; font-size: 11.5px; }
|
||
|
||
.podman-logs-layout { display: grid; grid-template-columns: 200px 1fr; min-height: 460px; }
|
||
@media (max-width: 760px) { .podman-logs-layout { grid-template-columns: 1fr; } }
|
||
.podman-logs-side { border-right: 1px solid var(--border); }
|
||
.podman-logs-side .item { padding: 10px 14px; font-size: 12.5px; border-bottom: 1px solid var(--border); cursor: pointer; display: flex; align-items: center; gap: 8px; }
|
||
.podman-logs-side .item.active { background: var(--surface-2); font-weight: 700; box-shadow: inset 2px 0 0 var(--accent); }
|
||
.podman-log-pane {
|
||
background: #0f1114; color: #c7ccd4; font-family: var(--font-mono); font-size: 12.3px;
|
||
padding: 14px 16px; height: 400px; overflow-y: auto; line-height: 1.65;
|
||
/* Settings' service log (and the log modal) set .textContent directly
|
||
on this element rather than wrapping every line in a child .l div —
|
||
needs its own white-space here too, or real newlines in rc.podman's
|
||
output collapse into one run-together line (found live: the whole
|
||
multi-line `rc.podman status` output rendered as a single paragraph). */
|
||
white-space: pre-wrap;
|
||
}
|
||
.podman-log-pane .l { white-space: pre-wrap; word-break: break-word; }
|
||
.podman-log-pane .ts { color: #6b7280; }
|
||
.podman-log-pane .lvl-warn { color: #e0b23d; }
|
||
.podman-log-pane .lvl-error { color: #ef6470; }
|
||
|
||
.podman-term-launcher {
|
||
display: flex; align-items: flex-end; gap: 16px; flex-wrap: wrap; margin-bottom: 14px;
|
||
padding: 12px 14px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px;
|
||
}
|
||
.podman-term-launcher label { display: flex; flex-direction: column; gap: 4px; font-size: 12.5px; font-weight: 600; color: var(--text-dim); }
|
||
/*
|
||
* !important here for the same reason as .podman-search's: Unraid's own
|
||
* webGui/styles/default-base.css has `select:where(:not(.unapi *))` rules
|
||
* for background/border/padding that would otherwise still show through
|
||
* around this class's box-model properties.
|
||
*/
|
||
.podman-term-select {
|
||
min-width: 180px !important; padding: 7px 12px !important; font-size: 13px !important;
|
||
font-family: var(--font-mono) !important; color: var(--text) !important;
|
||
background: var(--surface-3) !important; border: 1px solid var(--accent) !important; border-radius: 6px !important;
|
||
}
|
||
.podman-term-frame { display: block; width: 100%; height: 480px; border: 1px solid var(--border); border-radius: 8px; background: #0f1114; }
|
||
|
||
.podman-compose-layout { display: grid; grid-template-columns: 230px 1fr; min-height: 480px; }
|
||
@media (max-width: 800px) { .podman-compose-layout { grid-template-columns: 1fr; } }
|
||
.podman-compose-side { border-right: 1px solid var(--border); }
|
||
.podman-compose-proj { padding: 12px 14px; border-bottom: 1px solid var(--border); cursor: pointer; }
|
||
.podman-compose-proj.active { background: var(--surface-2); box-shadow: inset 2px 0 0 var(--accent); }
|
||
.podman-compose-proj .path { font-size: 11px; color: var(--text-faint); margin-top: 2px; font-family: var(--font-mono); }
|
||
.podman-yaml { background: #0f1114; color: #c7ccd4; font-family: var(--font-mono); font-size: 12.4px; padding: 16px 18px; height: 420px; overflow: auto; line-height: 1.7; white-space: pre-wrap; }
|
||
/*
|
||
* !important: this is now a real <textarea>, not a read-only <pre> —
|
||
* Unraid's own webGui/styles/default-base.css targets textarea the same
|
||
* way it targets input[type="text"] (see .podman-search's comment for
|
||
* the exact rule), forcing border-width:0/border-bottom-width:1px/
|
||
* background:transparent/border-radius:0, which would otherwise make the
|
||
* whole editor look like a barely-visible underline instead of an actual
|
||
* text area.
|
||
*/
|
||
.podman-yaml-editor {
|
||
display: block; width: 100%; box-sizing: border-box; resize: vertical;
|
||
border: none !important; border-radius: 0 !important; outline: none;
|
||
}
|
||
|
||
.podman-field-row { display: grid; grid-template-columns: 220px 1fr; gap: 16px; padding: 14px 18px; border-bottom: 1px solid var(--border); align-items: start; }
|
||
.podman-field-row:last-child { border-bottom: none; }
|
||
.podman-field-row label { font-weight: 600; font-size: 13px; }
|
||
.podman-field-row .hint { font-size: 11.5px; color: var(--text-faint); margin-top: 4px; max-width: 46ch; }
|
||
.podman-field-row input[type="text"], .podman-field-row input[type="number"], .podman-field-row select {
|
||
background: var(--surface-2); border: 1px solid var(--border); border-radius: 7px; padding: 8px 10px;
|
||
font-size: 13px; color: var(--text); width: 100%; max-width: 340px; font-family: var(--font-ui);
|
||
}
|
||
.podman-danger-card { border-color: color-mix(in srgb, var(--bad) 40%, var(--border)); }
|
||
|
||
/* Settings panel: a shared save action above all cards (Storage's and
|
||
Autostart & Lifecycle's fields save together in one call — see
|
||
settings.js's save() — so one button belongs above both, not buried in
|
||
either card, and definitely not in its own row with an empty label).
|
||
Framed as its own small bar (background/border), not bare text+button
|
||
floating at the top of the page. */
|
||
.podman-settings-actions {
|
||
display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 14px;
|
||
padding: 12px 16px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px;
|
||
}
|
||
.podman-settings-actions .hint { margin: 0; max-width: 52ch; color: var(--text-dim); }
|
||
.podman-card-head .sub { margin-top: 3px; }
|
||
/* Number field + unit label (GB, seconds) — the input itself stays compact
|
||
instead of stretching to .podman-field-row's normal 340px text-field width. */
|
||
.podman-input-suffix { display: flex; align-items: center; gap: 8px; }
|
||
.podman-input-suffix input[type="number"] { max-width: 100px; width: auto; }
|
||
.podman-input-suffix span { font-size: 12px; color: var(--text-dim); }
|
||
|
||
.podman-service-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
|
||
.podman-service-row:last-child { margin-bottom: 0; }
|
||
|
||
/*
|
||
* Toggle switch — a plain checkbox reads as a leftover form control next
|
||
* to everything else in this panel getting a designed treatment; this
|
||
* hides the native checkbox (still the real, accessible input driving
|
||
* state) and draws a track+thumb off its :checked state instead. Sized in
|
||
* em off the track's own font-size so it scales if that ever changes.
|
||
*/
|
||
.podman-switch { position: relative; display: inline-flex; align-items: center; cursor: pointer; font-size: 22px; }
|
||
.podman-switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }
|
||
.podman-switch-track {
|
||
display: inline-block; width: 1.9em; height: 1.05em; border-radius: 999px;
|
||
background: var(--surface-3); border: 1px solid var(--border); transition: background .15s, border-color .15s;
|
||
}
|
||
.podman-switch-thumb {
|
||
display: block; width: 0.75em; height: 0.75em; margin: 0.13em; border-radius: 50%;
|
||
background: var(--text-faint); transition: transform .15s, background .15s;
|
||
}
|
||
.podman-switch input:checked + .podman-switch-track { background: var(--accent); border-color: var(--accent); }
|
||
.podman-switch input:checked + .podman-switch-track .podman-switch-thumb { background: var(--accent-contrast); transform: translateX(0.85em); }
|
||
.podman-switch input:focus-visible + .podman-switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }
|
||
|
||
.podman-version-chips { display: flex; flex-wrap: wrap; gap: 8px; }
|
||
.podman-version-chip {
|
||
font-size: 11.5px; font-family: var(--font-mono); background: var(--surface-3); color: var(--text-dim);
|
||
border: 1px solid var(--border); padding: 5px 11px; border-radius: 100px;
|
||
}
|
||
.podman-version-chip b { color: var(--text); font-weight: 600; margin-left: 5px; }
|
||
.podman-danger-card .podman-card-head { border-bottom-color: color-mix(in srgb, var(--bad) 30%, var(--border)); }
|
||
.podman-danger-card .podman-card-head h2 { color: var(--bad); }
|
||
|
||
.podman-badge-update { font-size: 10px; font-weight: 700; color: var(--accent-strong); background: color-mix(in srgb, var(--accent) 16%, transparent); padding: 2px 7px; border-radius: 100px; margin-left: 8px; }
|
||
|
||
.podman-webui-link {
|
||
display: inline-flex; align-items: center; justify-content: center; height: 20px; padding: 0 7px;
|
||
border-radius: 5px; color: var(--text-faint); text-decoration: none; font-size: 10px; font-weight: 700;
|
||
letter-spacing: .03em; flex: none; border: 1px solid var(--border); background: var(--surface-3);
|
||
vertical-align: middle;
|
||
}
|
||
.podman-webui-link:hover { color: var(--accent-strong); border-color: var(--accent); }
|
||
|
||
.podman-loading, .podman-error { padding: 32px 18px; text-align: center; color: var(--text-faint); font-size: 13px; }
|
||
|
||
/**
|
||
* Modal form dialog — replaces browser-native prompt()/confirm() for any
|
||
* action that needs more than a single yes/no (e.g. "New Volume" needs a
|
||
* name AND an optional host path together, which prompt() can't express
|
||
* as one coherent form). See app.js's openFormModal().
|
||
*/
|
||
.podman-modal-backdrop {
|
||
position: fixed; inset: 0; background: rgba(15, 17, 20, .55); z-index: 1000;
|
||
display: flex; align-items: center; justify-content: center; padding: 20px;
|
||
}
|
||
.podman-modal {
|
||
background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
|
||
box-shadow: var(--shadow); width: 100%; max-width: 420px; max-height: calc(100vh - 40px);
|
||
overflow-y: auto; color: var(--text); font-family: var(--font-ui);
|
||
}
|
||
.podman-modal-head { padding: 16px 20px; border-bottom: 1px solid var(--border); }
|
||
.podman-modal-head h3 { font-size: 15px; }
|
||
.podman-modal-body { padding: 16px 20px; display: grid; gap: 14px; }
|
||
.podman-modal-field label { display: block; font-weight: 600; font-size: 12.5px; margin-bottom: 6px; }
|
||
.podman-modal-field input[type="text"], .podman-modal-field input[type="url"] {
|
||
background: var(--surface-2); border: 1px solid var(--border); border-radius: 7px; padding: 8px 10px;
|
||
font-size: 13px; color: var(--text); width: 100%; font-family: var(--font-ui);
|
||
}
|
||
.podman-modal-field .hint { font-size: 11.5px; color: var(--text-faint); margin-top: 4px; }
|
||
.podman-modal-field select {
|
||
background: var(--surface-2); border: 1px solid var(--border); border-radius: 7px; padding: 8px 10px;
|
||
font-size: 13px; color: var(--text); font-family: var(--font-ui);
|
||
}
|
||
.podman-modal-checkbox label { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 12.5px; margin-bottom: 0; }
|
||
.podman-modal-error { font-size: 12.5px; color: var(--bad); background: var(--bad-bg); border-radius: 7px; padding: 8px 10px; }
|
||
.podman-modal-actions { padding: 14px 20px; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; gap: 8px; }
|
||
.podman-error { color: var(--bad); }
|
||
|
||
/* Wider variant + repeatable row groups, for forms with more than 1-2 fields (e.g. Create Container). */
|
||
.podman-modal-wide { max-width: 640px; }
|
||
.podman-row-group { display: grid; gap: 8px; margin-bottom: 8px; }
|
||
.podman-row-group-item {
|
||
display: flex; align-items: center; gap: 8px;
|
||
}
|
||
.podman-row-group-item input[type="text"] {
|
||
background: var(--surface-2); border: 1px solid var(--border); border-radius: 7px; padding: 7px 9px;
|
||
font-size: 12.5px; color: var(--text); font-family: var(--font-mono); flex: 1; min-width: 0;
|
||
}
|
||
.podman-row-group-item select {
|
||
background: var(--surface-2); border: 1px solid var(--border); border-radius: 7px; padding: 7px 9px;
|
||
font-size: 12.5px; color: var(--text); font-family: var(--font-ui);
|
||
/* flex:none alone wasn't enough — its auto flex-basis still let the
|
||
select stretch to fill the row (verified live: "TCP"/"Volume"
|
||
dropdowns spanned almost the entire row width). An explicit width
|
||
pins it to content-appropriate size regardless. */
|
||
flex: none; width: 110px;
|
||
}
|
||
.podman-row-group-item span { color: var(--text-faint); font-size: 12px; flex: none; }
|
||
/*
|
||
* Port number fields share the row with a select + remove button, unlike
|
||
* the wide source/path/key/value fields elsewhere in these row groups —
|
||
* left on flex:1 like everything else, both port inputs fought the fixed-
|
||
* width select/button for space and got squeezed down to a few pixels
|
||
* (found live: they rendered as near-invisible slivers). Fixed width,
|
||
* not flex-grown.
|
||
*/
|
||
.podman-row-group-item input.podman-input-narrow { flex: none; width: 90px; }
|
||
.podman-row-checkbox-label {
|
||
display: flex; align-items: center; gap: 4px; flex: none; font-size: 12px; color: var(--text-dim);
|
||
white-space: nowrap; cursor: pointer;
|
||
}
|
||
/* Row-remove (x) reads as a normal button like everything else at full
|
||
.podman-btn weight — muted/borderless by default, only turning
|
||
"danger" red on hover, so it registers as a quiet per-row affordance
|
||
rather than competing with the form's actual actions. */
|
||
.podman-row-group-item .podman-row-remove-btn {
|
||
background: transparent; border-color: transparent; color: var(--text-faint); flex: none;
|
||
}
|
||
.podman-row-group-item .podman-row-remove-btn:hover { background: var(--bad-bg); border-color: transparent; color: var(--bad); }
|
||
|
||
/* Anchored dropdown context menu — see app.js openContextMenu(). */
|
||
.podman-context-menu {
|
||
/*
|
||
* "fixed", not "absolute": this menu is appended to .podman-plugin, not
|
||
* document.body, and Unraid's own page wrapper around .podman-plugin
|
||
* turned out to have its own positioned ancestor — with "absolute" the
|
||
* menu was positioning itself relative to THAT ancestor's box while the
|
||
* JS math (getBoundingClientRect + scrollY/X) assumed the viewport,
|
||
* so it rendered far from the button that opened it (found live: it
|
||
* appeared well below and to the side of the anchor). "fixed" is always
|
||
* viewport-relative regardless of any ancestor, which is what the JS
|
||
* math actually assumes.
|
||
*/
|
||
position: fixed; width: 180px; background: var(--surface); border: 1px solid var(--border);
|
||
border-radius: 9px; box-shadow: var(--shadow); z-index: 1001; padding: 4px; display: grid; gap: 1px;
|
||
}
|
||
.podman-context-menu button {
|
||
appearance: none; border: none; background: none; text-align: left; padding: 8px 10px;
|
||
font-size: 12.5px; font-weight: 600; color: var(--text); border-radius: 6px; cursor: pointer;
|
||
font-family: var(--font-ui); width: 100%;
|
||
}
|
||
.podman-context-menu button:hover { background: var(--surface-2); }
|
||
.podman-context-menu button.danger { color: var(--bad); background: var(--bad-bg); }
|
||
.podman-context-menu button.danger:hover { background: var(--bad); color: var(--bad-contrast); }
|
||
.podman-context-menu button[disabled] { opacity: .4; cursor: not-allowed; }
|
||
.podman-context-menu-sep { height: 1px; background: var(--border); margin: 4px 2px; }
|
||
|
||
/* Container detail modal — see containers.js openDetailModal(). */
|
||
.podman-modal-xwide { max-width: 760px; }
|
||
.podman-detail-tabs {
|
||
display: flex; gap: 2px; padding: 0 20px; border-bottom: 1px solid var(--border); overflow-x: auto;
|
||
}
|
||
.podman-detail-tabs button {
|
||
appearance: none; background: none; border: none; border-bottom: 2px solid transparent;
|
||
color: var(--text-dim); padding: 10px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;
|
||
white-space: nowrap; font-family: var(--font-ui);
|
||
}
|
||
.podman-detail-tabs button:hover { color: var(--text); }
|
||
.podman-detail-tabs button.active { color: var(--accent-strong); border-bottom-color: var(--accent); }
|
||
.podman-detail-body { padding: 16px 20px; max-height: 50vh; overflow-y: auto; }
|
||
.podman-detail-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
|
||
.podman-detail-table td { padding: 6px 0; border-bottom: 1px solid var(--border); vertical-align: top; }
|
||
.podman-detail-table td:first-child { color: var(--text-faint); font-weight: 600; width: 160px; padding-right: 12px; }
|
||
.podman-detail-table td.mono { font-family: var(--font-mono); word-break: break-all; }
|
||
.podman-detail-json {
|
||
background: #0f1114; color: #c7ccd4; font-family: var(--font-mono); font-size: 11.8px;
|
||
padding: 14px 16px; border-radius: 8px; white-space: pre-wrap; word-break: break-all; line-height: 1.6;
|
||
}
|
||
.podman-detail-empty { color: var(--text-faint); font-size: 12.5px; padding: 8px 0; }
|
||
|
||
/*
|
||
* Toast notifications — replaces alert() for one-way feedback (saved,
|
||
* removed, failed, ...) across every panel. Confirmations stay native
|
||
* confirm() (a decision, not a notice); long-running command output
|
||
* stays in the existing log-modal pattern (a toast has to stay short to
|
||
* be readable while it's animating/auto-dismissing). z-index above the
|
||
* modal backdrop (1000) so a toast fired from within a modal's own
|
||
* action (e.g. "Save failed") is never hidden behind it.
|
||
*/
|
||
.podman-toast-container {
|
||
position: fixed; right: 20px; bottom: 20px; z-index: 1100;
|
||
display: flex; flex-direction: column-reverse; gap: 10px; pointer-events: none;
|
||
max-width: min(380px, calc(100vw - 40px));
|
||
}
|
||
.podman-toast {
|
||
pointer-events: auto; display: flex; align-items: flex-start; gap: 10px;
|
||
background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--neutral);
|
||
border-radius: 9px; box-shadow: var(--shadow); padding: 12px 14px; font-size: 13px; color: var(--text);
|
||
animation: podman-toast-in .18s ease-out;
|
||
}
|
||
.podman-toast.leaving { animation: podman-toast-out .16s ease-in forwards; }
|
||
.podman-toast-success { border-left-color: var(--good); }
|
||
.podman-toast-warn { border-left-color: var(--warn); }
|
||
.podman-toast-error { border-left-color: var(--bad); }
|
||
.podman-toast .ico { flex: none; margin-top: 1px; font-size: 15px; line-height: 1; }
|
||
.podman-toast-success .ico { color: var(--good); }
|
||
.podman-toast-warn .ico { color: var(--warn); }
|
||
.podman-toast-error .ico { color: var(--bad); }
|
||
.podman-toast .msg { flex: 1; line-height: 1.45; word-break: break-word; }
|
||
.podman-toast .close {
|
||
appearance: none; background: none; border: none; color: var(--text-faint); cursor: pointer;
|
||
font-size: 15px; line-height: 1; padding: 0; flex: none; font-family: var(--font-ui);
|
||
}
|
||
.podman-toast .close:hover { color: var(--text); }
|
||
@keyframes podman-toast-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
|
||
@keyframes podman-toast-out { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(8px); } }
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.podman-toast, .podman-toast.leaving { animation: none; }
|
||
}
|
||
|
||
/* Container folders — a group header row spanning the full table width,
|
||
sitting between normal <tr>s in the same <tbody> (see containers.js's
|
||
renderTable()) rather than a separate nested table, so column
|
||
alignment with member rows stays free. */
|
||
.podman-folder-row td { padding: 0; background: var(--surface-2); border-bottom: 1px solid var(--border); }
|
||
.podman-folder-head { display: flex; align-items: center; gap: 14px; padding: 9px 16px; flex-wrap: wrap; }
|
||
.podman-folder-toggle {
|
||
appearance: none; background: none; border: none; display: flex; align-items: center; gap: 10px;
|
||
cursor: pointer; font-family: var(--font-ui); color: var(--text); padding: 0; flex: none; text-align: left;
|
||
}
|
||
.podman-folder-toggle .chevron { color: var(--text-faint); font-size: 10px; width: 10px; flex: none; }
|
||
.podman-folder-toggle .ico {
|
||
width: 22px; height: 22px; border-radius: 6px; flex: none; background: var(--surface-3);
|
||
display: grid; place-items: center; font-size: 10.5px; border: 1px solid var(--border); color: var(--text-dim);
|
||
overflow: hidden;
|
||
}
|
||
.podman-folder-toggle .ico img { width: 100%; height: 100%; object-fit: cover; }
|
||
.podman-folder-toggle .name { font-weight: 700; font-size: 12.5px; }
|
||
.podman-folder-toggle .count {
|
||
font-size: 11px; color: var(--text-faint); font-weight: 700; background: var(--surface-3);
|
||
padding: 1px 8px; border-radius: 100px; font-variant-numeric: tabular-nums; white-space: nowrap;
|
||
}
|
||
|
||
/* The always-visible per-member preview inside a folder's header row —
|
||
see containers.js's folderMemberChipHtml(). Each chip opens that
|
||
container's detail modal directly, without needing to expand the
|
||
folder first. */
|
||
.podman-folder-members { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; flex: 1; min-width: 0; }
|
||
.podman-folder-member {
|
||
appearance: none; display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--border);
|
||
background: var(--surface); border-radius: 100px; padding: 3px 10px 3px 4px; cursor: pointer;
|
||
font-family: var(--font-ui); color: var(--text-dim); font-size: 11.5px;
|
||
}
|
||
.podman-folder-member:hover { border-color: var(--accent); color: var(--text); }
|
||
.podman-folder-member .ico {
|
||
width: 18px; height: 18px; border-radius: 5px; font-size: 9px; flex: none; background: var(--surface-3);
|
||
display: grid; place-items: center; overflow: hidden;
|
||
}
|
||
.podman-folder-member .ico img { width: 100%; height: 100%; object-fit: cover; }
|
||
.podman-folder-member .name { font-weight: 600; }
|
||
.podman-folder-member .dot { width: 6px; height: 6px; border-radius: 50%; flex: none; }
|
||
.podman-folder-member .dot.good { background: var(--good); }
|
||
.podman-folder-member .dot.bad { background: var(--bad); }
|
||
.podman-folder-member .state { color: var(--text-faint); }
|