Add Create Container UI and Templates (Unraid XML) feature
Lets users create containers from the WebUI (image/name/ports/volumes/ env/network mode/restart policy/privileged) instead of only managing existing ones, and adds a Templates panel to save/reuse those configs as Unraid-Docker-compatible template XML, including browsing and importing the host's own existing Docker Manager templates directly. Also fixes bugs found via live testing along the way: container names with spaces/invalid characters now get a clear client- and server-side error with a suggested fix instead of podman's raw API error, the New Container modal's backdrop no longer renders transparent (was being appended outside the .podman-plugin CSS scope), and modal buttons now have real visual hierarchy (ghost/primary/danger) after Unraid's own site-wide button theme was found to override plain single-class rules. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -83,12 +83,51 @@
|
||||
font-family: var(--font-ui);
|
||||
}
|
||||
.podman-btn:hover { border-color: var(--text-faint); }
|
||||
.podman-btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-contrast); }
|
||||
.podman-btn-primary:hover { background: var(--accent-strong); border-color: var(--accent-strong); }
|
||||
/*
|
||||
* !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; }
|
||||
.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 (e.g. a template's
|
||||
"Delete") — needs its own !important since .podman-btn-ghost's color
|
||||
would otherwise win by rule order. */
|
||||
.podman-btn-ghost.podman-btn-danger { color: var(--bad) !important; }
|
||||
.podman-btn-ghost.podman-btn-danger:hover { background: var(--bad-bg) !important; color: var(--bad) !important; }
|
||||
|
||||
.podman-subnav {
|
||||
margin: 14px 0 0; padding: 0; display: flex; gap: 4px; border-bottom: 1px solid var(--border);
|
||||
@@ -183,6 +222,33 @@
|
||||
.podman-pod-head .name { font-weight: 700; font-size: 13.5px; }
|
||||
.podman-pod-head .infra { font-size: 11.5px; color: var(--text-faint); }
|
||||
|
||||
.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-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: 8px; margin-top: auto; padding-top: 4px; }
|
||||
.podman-template-actions .podman-btn { flex: 1; justify-content: center; padding: 6px 10px; font-size: 12px; }
|
||||
|
||||
.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); }
|
||||
@@ -274,9 +340,31 @@
|
||||
}
|
||||
.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;
|
||||
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; }
|
||||
/* 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 {
|
||||
|
||||
Reference in New Issue
Block a user