summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/style.css
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-21 13:52:11 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-21 13:52:11 +0200
commit8730281d739d9ed1d6f2d366772582eea8ba0294 (patch)
tree4df2f3c8d42dc17147bcdec23109aa9c0f1b5b35 /packages/meshbay-hub/src/meshbay_hub/static/style.css
parentae52b69b14a6997d01aad66f49fbea7b5ca2dfe6 (diff)
downloadmeshbay-8730281d739d9ed1d6f2d366772582eea8ba0294.tar.gz
feat: LAN Wi-Fi casting to Chromecast via local HTTP relay
Re-serve decrypted fMP4 video over HTTP on the LAN so a Chromecast can play the stream. The relay runs in the Electron main process — same trust boundary as downloads and MSE playback. - cast-relay.js: HTTP server with BoxAccumulator (reassembles WebRTC chunks into moof+mdat pairs), ring buffer, backpressure, finish() for clean end-of-stream, fixed port range 19550-19553 - cast-chromecast.js: mDNS discovery (bonjour-service) + CASTV2 protocol (castv2-client), connect/reload/disconnect lifecycle - Seek-aware: relay restarts on every seek, Chromecast reloads new URL; generation counter prevents stale async errors from killing active restarts; landingPlayheadRef suppresses programmatic seeking events - Device picker in video top bar with scan, device selection, copy-URL fallback, and cast status indicator - IPC bridge (main/preload/platform) for start/push/stop/finish/status/ discover/chromecastConnect/chromecastReload/chromecastDisconnect - Phase 3 design doc for DLNA/Smart TV in docs/cast-smart-tv.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/style.css')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/style.css33
1 files changed, 33 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 7ce073a..ee51a8a 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/style.css
+++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css
@@ -1060,6 +1060,39 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
}
.video-close:hover { background: rgba(255, 255, 255, 0.25); }
.video-close.dl-active { background: rgba(34, 197, 94, 0.25); pointer-events: none; }
+.video-close.cast-active { background: rgba(59, 130, 246, 0.35); }
+.video-close.cast-active:hover { background: rgba(59, 130, 246, 0.5); }
+.cast-url-input {
+ background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.15);
+ border-radius: 4px; color: #e2e8f0; font-size: 0.75em; font-family: inherit;
+ padding: 4px 8px; min-width: 0; flex: 1; max-width: 420px;
+ cursor: text; outline: none;
+}
+.cast-url-input:focus { border-color: rgba(59, 130, 246, 0.6); }
+.cast-wrapper { display: inline-block; }
+.cast-picker {
+ position: absolute; top: calc(100% + 4px); right: 0; z-index: 100;
+ background: rgba(28, 28, 32, 0.97); border: 1px solid rgba(255, 255, 255, 0.14);
+ border-radius: 8px; padding: 4px 0; min-width: 230px;
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
+}
+.cast-picker-item {
+ display: flex; align-items: center; gap: 8px; width: 100%;
+ padding: 9px 14px; background: none; border: none;
+ color: #e2e8f0; font-size: 0.82em; cursor: pointer; text-align: left;
+ font-family: inherit;
+}
+.cast-picker-item:hover { background: rgba(255, 255, 255, 0.09); }
+.cast-picker-item svg { width: 16px; height: 16px; flex-shrink: 0; }
+.cast-picker-scanning { color: #94a3b8; cursor: default; }
+.cast-picker-scanning:hover { background: none; }
+.cast-picker-empty { color: #64748b; cursor: default; font-style: italic; }
+.cast-picker-empty:hover { background: none; }
+.cast-picker-sep { height: 1px; margin: 4px 10px; background: rgba(255,255,255,0.1); }
+.cast-status-label {
+ color: #94a3b8; font-size: 0.78em; display: flex; align-items: center;
+ max-width: 420px; overflow: hidden; white-space: nowrap;
+}
.video-container {
width: 100%;