diff --git a/apps/sandbox/templates/spf-segment-loading/index.html b/apps/sandbox/templates/spf-segment-loading/index.html
index 9c3b862a..2b157a8f 100644
--- a/apps/sandbox/templates/spf-segment-loading/index.html
+++ b/apps/sandbox/templates/spf-segment-loading/index.html
@@ -168,12 +168,27 @@
#resolution-list { display: flex; flex-wrap: wrap; gap: 8px; }
.resolution-item {
- padding: 4px 10px;
+ padding: 4px 10px;
font-family: monospace; font-size: 11px;border: 1px solid #ccc; border-radius: 4px;
}
.resolution-item.resolved { color: green; border-color: green; }
.resolution-item.unresolved { color: #888; border-color: #aaa; }
+ /* ── Live / DVR ── */
+ #live-controls { margin-bottom: 10px; }
+ #live-controls button:disabled { color: #aaa; cursor: not-allowed; background: #f7f7f7; border-color: #ccc; }
+ #live-panel {
+ padding: 12px 15px; margin: 16px 0; font-size: 12px;
+ background: #f5f5f5; border: 1px solid #ddd; border-radius: 4px;
+ }
+ #live-panel h2 { margin-top: 0; font-size: 14px; }
+ #live-info { display: grid; grid-template-columns: max-content 1fr; gap: 2px 16px; }
+ #live-info .k { color: #777; }
+ #live-info .v { color: #0070f3; }
+ #live-info .edge { font-weight: bold; color: #16a34a; }
+ #live-info .warn { font-weight: bold; color: #d97706; }
+ #live-info .muted { color: #888; }
+
/* ── Logs ── */
#logs-section { margin: 16px 0; }
#logs-section h2 { margin-bottom: 6px; font-size: 14px; }
@@ -236,11 +251,24 @@
+
+
+
+
+
+
+
+
+
+
Live / DVR
+
Waiting for presentation…
+
+
Video Renditions
Waiting for presentation…
diff --git a/apps/sandbox/templates/spf-segment-loading/main.ts b/apps/sandbox/templates/spf-segment-loading/main.ts
index 85915122..9dfcfeb1 100644
--- a/apps/sandbox/templates/spf-segment-loading/main.ts
+++ b/apps/sandbox/templates/spf-segment-loading/main.ts
@@ -28,6 +28,12 @@ const mutedToggle = document.getElementById('muted-toggle') as HTMLInputElement;
const autoplayToggle = document.getElementById('autoplay-toggle') as HTMLInputElement;
const preloadSelect = document.getElementById('preload-select') as HTMLSelectElement;
const shareLink = document.getElementById('share-link') as HTMLAnchorElement;
+const liveInfoDiv = document.getElementById('live-info') as HTMLDivElement;
+const seekLiveBtn = document.getElementById('seek-live') as HTMLButtonElement;
+const seekBack10Btn = document.getElementById('seek-back-10') as HTMLButtonElement;
+const seekBack30Btn = document.getElementById('seek-back-30') as HTMLButtonElement;
+const seekWindowStartBtn = document.getElementById('seek-window-start') as HTMLButtonElement;
+const liveSeekButtons = [seekLiveBtn, seekBack10Btn, seekBack30Btn, seekWindowStartBtn];
// ── Query params ──────────────────────────────────────────────────────────────
const DEFAULT_STREAM = 'https://stream.mux.com/JX01bG8eB4uaoV3OpDuK602rBfvdSgrMObjwuUOBn4JrQ.m3u8';
@@ -142,6 +148,100 @@ function updateThroughputDisplay() {
throughputDiv.className = 'has-data';
}
+// ── Live / DVR panel ────────────────────────────────────────────────────────
+// All values are derived sandbox-side from the resolved presentation's
+// streamType plus the native