diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/style.css')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/style.css | 277 |
1 files changed, 277 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 b332f6f..9a4fb5a 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/style.css +++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css @@ -1125,6 +1125,20 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } margin: 0; } +/* Photos app's root add/remove list (docs/photos.md §2.2) — a set, unlike + the Videos/Music single-value picker above. */ +.settings-root-list { list-style: none; margin: 4px 0 8px; padding: 0; } +.settings-root-list-item { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 4px 0; + font-size: 0.88em; +} +.settings-root-list-item .link-btn { padding: 2px; } +.settings-root-list-item .icon { width: 14px; height: 14px; } + .settings-select { padding: 6px 10px; border: 1px solid var(--border); @@ -3098,3 +3112,266 @@ a.transfer-name { .music-player-seek { order: 4; flex-basis: 100%; } .music-player-volume { display: none; } } + +/* ── Photos app (photos-app.js, docs/photos.md) ───────────────────────────── */ + +.photo-toolbar { + display: flex; + align-items: center; + gap: 6px; + margin-bottom: 14px; +} +.photo-toolbar .tb-search { margin-left: auto; } + +/* Open-album title bar: name + year on the left, two big monochrome + icon-only actions (back, zip) on the right — same row, same size as a + group tab's own icon (.tab-icon, 22px) so they read as "app-level" + controls rather than small inline buttons. */ +.photo-album-bar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-bottom: 16px; +} +.photo-album-heading { + display: flex; + align-items: center; + gap: 6px; + min-width: 0; +} +.photo-album-heading-text { + display: flex; + align-items: baseline; + gap: 10px; + min-width: 0; +} +.photo-album-heading-title { + font-weight: 600; + font-size: 1.15em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.photo-album-heading-year { font-size: 0.85em; color: var(--text-dim); flex-shrink: 0; } + +.photo-icon-btn { + background: none; + border: none; + cursor: pointer; + color: var(--text-secondary); + padding: 7px; + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; +} +.photo-icon-btn:hover { color: var(--text); background: var(--bg-surface); } +.photo-icon-btn-icon { width: 22px; height: 22px; } + +/* .tb-btn's own chevron is drawn pointing down; rotated here to read as + "back" (left) without a second icon in icon.js. */ +.photo-back-icon { transform: rotate(90deg); } + +/* Landing view — one card per album (a directory containing images) */ + +.photo-album-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + gap: 16px; + margin-bottom: 24px; +} +.photo-album-tile-slot { min-height: 190px; } + +.photo-album-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; +} +.photo-album-card:hover { border-color: var(--accent); transform: translateY(-2px); } + +.photo-album-cover { + width: 100%; + aspect-ratio: 1 / 1; + object-fit: cover; + display: block; + background: var(--bg-surface); +} +/* MediaThumb (video-app.js) always adds its own .video-thumb-empty class + for the placeholder state, whatever `cls` is passed — that global rule + already centers the icon; only the square sizing above is Photos-specific. */ +.photo-album-cover.video-thumb-empty .icon, +.photo-tile-thumb.video-thumb-empty .icon { width: 24px; height: 24px; } + +.photo-album-info { padding: 8px 10px; } +.photo-album-title { + font-size: 0.86em; + font-weight: 600; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.photo-album-sub { font-size: 0.75em; color: var(--text-dim); margin-top: 2px; } + +/* Open-album view — a grid of the album's own photos */ + +.photo-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); + gap: 8px; +} +.photo-tile-slot { min-height: 110px; } +.photo-tile { + cursor: pointer; + border-radius: 6px; + overflow: hidden; + background: var(--bg-raised); + border: 1px solid var(--border); +} +.photo-tile:hover { border-color: var(--accent); } +.photo-tile-thumb { + width: 100%; + aspect-ratio: 1 / 1; + object-fit: cover; + display: block; + background: var(--bg-surface); +} + +/* Lightbox — sits inside the shared .video-overlay/.video-top-bar/.video-title/ + .video-close, same as music-app.js's detail modal reuses them. */ + +/* Scoped to .photo-lightbox specifically — .video-top-bar itself stays + transparent everywhere else that reuses it (VideoPlayer, the video/music + detail modals), where nothing zooms underneath it. */ +.photo-lightbox .video-top-bar { + background: linear-gradient(to bottom, rgba(0, 0, 0, 0.8), transparent); + padding-bottom: 24px; +} + +.photo-lightbox-body { + /* Bounded strictly between the top bar and the info strip, rather than + the full-viewport height it used to take — a zoomed-in image, larger + than the window, painted straight through that space (nothing ever + reserved it) and sat visually behind the zoom controls, which had only + a faint translucent highlight of their own to read against a bright, + busy photo. Positioning the image area below the bar's own real, + opaque strip (below) is what actually keeps them apart, not z-index — + the buttons were never behind the image in stacking order, just hard + to see in front of it. */ + position: absolute; + top: 60px; + bottom: 44px; + left: 0; + right: 0; + display: flex; + align-items: center; + justify-content: center; + gap: 4px; + padding: 0 8px; +} +.photo-nav { + background: rgba(255, 255, 255, 0.1); + border: none; + color: #e2e8f0; + width: 40px; + height: 40px; + border-radius: 50%; + cursor: pointer; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; +} +.photo-nav:hover { background: rgba(255, 255, 255, 0.22); } +.photo-nav:disabled { opacity: 0.3; cursor: default; } +.photo-nav-icon { width: 20px; height: 20px; } +.photo-nav-prev-icon { transform: rotate(90deg); } +.photo-nav-next-icon { transform: rotate(-90deg); } + +.photo-lightbox-image-slot { + flex: 1; + min-width: 0; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + /* A zoomed-in image is routinely larger than the viewport — this is what + makes it pannable instead of just clipped. Harmless when the image + fits (fit-to-window, or zoomed below 100%): no scrollbar appears. */ + overflow: auto; +} +/* flex centering (align-items/justify-content: center) plus overflow:auto + is a well-known trap: the browser centers overflowing content by + shifting it, but the "leading" half of that overflow — here, the top of + a zoomed-in photo — sits outside the range the scrollport actually + exposes, so it can never be scrolled into view at all. Reported live as + "unusable" (the top of the photo was gone, with no way to reach it). + Switching to top/left alignment for the zoomed state fixes exactly that: + the top-left corner is always where the image starts, and scrolling + down/right reaches the rest — the same convention most image viewers + use once you're past fit-to-window anyway. */ +.photo-lightbox-image-slot.zoomed { + align-items: flex-start; + justify-content: flex-start; +} +.photo-lightbox-image { + /* Relative to the now-properly-bounded slot (.photo-lightbox-body's + top/bottom insets), not a guessed viewport fraction — correct at any + window size without re-tuning a magic number. */ + max-width: 100%; + max-height: 100%; + object-fit: contain; +} +/* An explicit pixel size (photos-app.js's imgStyle) replaces the + fit-to-window constraints above — object-fit has nothing left to do + once both dimensions are set directly. */ +.photo-lightbox-image.zoomed { + max-width: none; + max-height: none; + object-fit: initial; + display: block; +} + +/* Zoom controls sit in the dark lightbox chrome (.video-top-bar), not the + app's light Settings/toolbar chrome — .photo-icon-btn's own colors + (meant for the light chrome elsewhere in Photos) are overridden here to + match .video-close's existing treatment instead. */ +.photo-zoom-controls { display: flex; align-items: center; gap: 2px; } +.photo-zoom-controls .photo-icon-btn { + color: #e2e8f0; + padding: 5px; + border-radius: 4px; +} +.photo-zoom-controls .photo-icon-btn:hover { background: rgba(255, 255, 255, 0.15); } +.photo-zoom-controls .photo-icon-btn.active { background: rgba(255, 255, 255, 0.18); } +.photo-zoom-controls .photo-icon-btn:disabled { opacity: 0.35; cursor: default; } +.photo-zoom-controls .photo-icon-btn:disabled:hover { background: none; } +.photo-zoom-controls .photo-icon-btn-icon { width: 18px; height: 18px; } +.photo-icon-btn-text { font-size: 0.72em; font-weight: 600; padding: 5px 8px; } +.photo-zoom-percent { + font-size: 0.75em; + color: #94a3b8; + min-width: 38px; + text-align: center; + flex-shrink: 0; +} + +.photo-lightbox-info { + position: absolute; + bottom: 0; + left: 0; + right: 0; + display: flex; + align-items: center; + justify-content: center; + gap: 14px; + flex-wrap: wrap; + padding: 10px 20px; + color: #cbd5e1; + font-size: 0.78em; + background: linear-gradient(to top, rgba(0, 0, 0, 0.55), transparent); +} +.photo-lightbox-count { color: #94a3b8; } |