aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/style.css
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-24 10:04:46 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-24 10:04:46 +0200
commit6af05abf410bbd038ce7fa6915a659defc509071 (patch)
tree09b1c941fa446b077ff51282fa18250998528263 /packages/meshbay-hub/src/meshbay_hub/static/style.css
parentc4981454078a59f776d484f0f1828f2fc5eaad09 (diff)
downloadmeshbay-6af05abf410bbd038ce7fa6915a659defc509071.tar.gz
feat(node,hub): add Videos group app (poster grid, flat list, TMDB metadata)
Implements docs/mediacenter.md: a "Videos" group application built on the existing files index rather than a separate catalogue. On the node side, new indexer enrichment (technical probe, filename/season parsing, thumbnail generation) runs per-file once an operator has chosen a video_root for the group, plus a TMDB client for on-demand poster/metadata lookups (never client-side, thumbnails delivered over the existing chunk path). On the hub side, a new video-app.js renders a lazily-mounted poster grid or a thumbnail-only flat list, with TMDB entirely optional per group. Along the way: the global apps registry now drives Settings' default-tab picker instead of a hardcoded list, and the video_root is configured from group Settings (like uploads) rather than from Files, with the node refusing to run any TMDB/thumbnail work until one is set. Fixes several bugs found via live testing against a real library, notably a race between two effects writing the same "image ready" state that could leave a poster grid spinning forever on a same-tab revisit — see mediacenter.md §5.4 for the full account of each one.
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/style.css')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/style.css225
1 files changed, 225 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/style.css b/packages/meshbay-hub/src/meshbay_hub/static/style.css
index 2cb27d0..e1900d9 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/style.css
+++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css
@@ -1000,6 +1000,20 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
color: var(--text);
font-size: 0.95em;
}
+.settings-label input[type="password"],
+.settings-label input[type="text"],
+.settings-label select {
+ display: block;
+ width: 100%;
+ max-width: 320px;
+ margin-top: 4px;
+ padding: 6px 8px;
+ border-radius: 6px;
+ border: 1px solid var(--border);
+ background: var(--bg-base);
+ color: var(--text);
+ font-size: 0.95em;
+}
.settings-value {
font-size: 0.9em;
@@ -2399,3 +2413,214 @@ a.transfer-name {
border-radius: 8px;
font-size: 0.85em;
}
+
+/* ── Videos app (video-app.js) ──────────────────────────────────────────────── */
+
+.video-toolbar {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ margin-bottom: 14px;
+}
+.video-toolbar .tb-search { margin-left: auto; }
+
+/* Mode A — poster grid */
+
+.video-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
+ gap: 16px;
+}
+
+.video-tile-slot {
+ min-height: 260px;
+}
+
+.video-card {
+ cursor: pointer;
+ border-radius: 8px;
+ overflow: hidden;
+ background: var(--bg-raised);
+ border: 1px solid var(--border);
+ transition: border-color 0.12s, transform 0.12s;
+}
+.video-card:hover { border-color: var(--accent); transform: translateY(-2px); }
+
+.video-poster {
+ width: 100%;
+ aspect-ratio: 2 / 3;
+ object-fit: cover;
+ display: block;
+ background: var(--bg-surface);
+}
+
+.video-poster-loading {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.video-poster-slot { display: contents; }
+
+.video-thumb {
+ width: 100%;
+ aspect-ratio: 2 / 3;
+ object-fit: cover;
+ display: block;
+ background: var(--bg-surface);
+}
+.video-thumb-empty {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--text-dim);
+}
+.video-thumb-empty .icon { width: 28px; height: 28px; }
+
+.video-card-info { padding: 8px 10px; }
+.video-card-title {
+ font-size: 0.88em;
+ font-weight: 600;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.video-card-sub {
+ font-size: 0.78em;
+ color: var(--text-dim);
+ margin-top: 2px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+/* Detail modal — sits inside the existing .video-overlay */
+
+.video-detail {
+ width: min(720px, 92vw);
+ max-height: calc(100vh - 100px);
+ margin-top: 60px;
+ background: var(--bg-surface);
+ border-radius: 10px;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+}
+.video-detail .video-top-bar {
+ position: static;
+ background: var(--bg-raised);
+ border-bottom: 1px solid var(--border);
+}
+.video-detail .video-title { color: var(--text); }
+.video-detail .video-close { background: var(--bg-surface); color: var(--text); }
+.video-detail .video-close:hover { background: var(--border); }
+
+.video-detail-body {
+ padding: 16px 20px;
+ overflow-y: auto;
+}
+.video-detail-overview {
+ font-size: 0.9em;
+ color: var(--text);
+ line-height: 1.5;
+}
+.video-detail-facts {
+ font-size: 0.82em;
+ color: var(--text-dim);
+ margin-top: 6px;
+}
+.video-detail-cast {
+ font-size: 0.82em;
+ color: var(--text-secondary);
+ margin-top: 6px;
+}
+
+.video-season-list { margin-top: 14px; }
+.video-season { margin-bottom: 14px; }
+.video-season-header {
+ font-weight: 600;
+ font-size: 0.85em;
+ color: var(--text-dim);
+ margin-bottom: 6px;
+ text-transform: uppercase;
+ letter-spacing: 0.02em;
+}
+.video-episode-row {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ width: 100%;
+ padding: 8px 10px;
+ background: none;
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ color: var(--text);
+ font-size: 0.85em;
+ cursor: pointer;
+ text-align: left;
+ margin-bottom: 4px;
+}
+.video-episode-row:hover { border-color: var(--accent); color: var(--accent); }
+.video-episode-row .icon { width: 14px; height: 14px; flex-shrink: 0; }
+.video-episode-thumb-slot { width: 64px; height: 40px; flex-shrink: 0; }
+.video-episode-thumb {
+ width: 64px;
+ height: 40px;
+ object-fit: cover;
+ border-radius: 4px;
+ display: block;
+ background: var(--bg-raised);
+}
+.video-episode-thumb.video-thumb-empty { border: 1px solid var(--border); }
+.video-episode-label {
+ flex: 1;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.video-episode-meta {
+ color: var(--text-dim);
+ font-size: 0.9em;
+ flex-shrink: 0;
+}
+
+/* Mode B — flat list */
+
+.video-flat-list { display: flex; flex-direction: column; gap: 4px; }
+
+.video-flat-row {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 6px 8px;
+ border-radius: 6px;
+ cursor: pointer;
+}
+.video-flat-row:hover { background: var(--bg-raised); }
+
+.video-flat-thumb-slot { width: 64px; height: 40px; flex-shrink: 0; }
+.video-flat-thumb {
+ width: 64px;
+ height: 40px;
+ object-fit: cover;
+ border-radius: 4px;
+ display: block;
+ background: var(--bg-raised);
+}
+.video-flat-thumb.video-thumb-empty { border: 1px solid var(--border); }
+
+.video-flat-info { min-width: 0; flex: 1; }
+.video-flat-title {
+ font-size: 0.88em;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.video-flat-sub { font-size: 0.78em; color: var(--text-dim); margin-top: 1px; }
+
+.video-flat-folder { border-bottom: 1px solid var(--border); padding-bottom: 4px; margin-bottom: 4px; }
+.video-flat-chevron { transition: transform 0.12s; flex-shrink: 0; }
+.video-flat-chevron.open { transform: rotate(180deg); }
+.video-flat-season { padding-left: 24px; margin-bottom: 8px; }
+.video-flat-season .video-season-header { margin: 6px 0 4px; }