Spaces:
Running
Running
Add url-sync helper + wire into viewer
Browse files- src/lib/TraceViewer.svelte +8 -0
- src/lib/url-sync.js +46 -0
src/lib/TraceViewer.svelte
CHANGED
|
@@ -2,6 +2,7 @@
|
|
| 2 |
import { tick, onMount, onDestroy } from 'svelte';
|
| 3 |
import { parseJsonl, toRawUrl } from './parse.js';
|
| 4 |
import { renderMarkdown } from './markdown.js';
|
|
|
|
| 5 |
|
| 6 |
const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
| 7 |
let spinnerFrame = $state(0);
|
|
@@ -73,6 +74,7 @@
|
|
| 73 |
if (parsed.length === 0) {
|
| 74 |
error = 'No messages parsed from this file.';
|
| 75 |
} else {
|
|
|
|
| 76 |
await tick();
|
| 77 |
playback();
|
| 78 |
}
|
|
@@ -236,6 +238,12 @@
|
|
| 236 |
spinnerInterval = setInterval(() => {
|
| 237 |
spinnerFrame = (spinnerFrame + 1) % spinnerFrames.length;
|
| 238 |
}, 90);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 239 |
});
|
| 240 |
|
| 241 |
onDestroy(() => {
|
|
|
|
| 2 |
import { tick, onMount, onDestroy } from 'svelte';
|
| 3 |
import { parseJsonl, toRawUrl } from './parse.js';
|
| 4 |
import { renderMarkdown } from './markdown.js';
|
| 5 |
+
import { getParam, setParam } from './url-sync.js';
|
| 6 |
|
| 7 |
const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
| 8 |
let spinnerFrame = $state(0);
|
|
|
|
| 74 |
if (parsed.length === 0) {
|
| 75 |
error = 'No messages parsed from this file.';
|
| 76 |
} else {
|
| 77 |
+
setParam('url', url);
|
| 78 |
await tick();
|
| 79 |
playback();
|
| 80 |
}
|
|
|
|
| 238 |
spinnerInterval = setInterval(() => {
|
| 239 |
spinnerFrame = (spinnerFrame + 1) % spinnerFrames.length;
|
| 240 |
}, 90);
|
| 241 |
+
|
| 242 |
+
const shared = getParam('url');
|
| 243 |
+
if (shared) {
|
| 244 |
+
url = shared;
|
| 245 |
+
load();
|
| 246 |
+
}
|
| 247 |
});
|
| 248 |
|
| 249 |
onDestroy(() => {
|
src/lib/url-sync.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Sync shareable URL query params between this iframe and the HF Spaces
|
| 2 |
+
// parent frame (huggingface.co/spaces/<owner>/<name>?...).
|
| 3 |
+
//
|
| 4 |
+
// Pattern borrowed from lerobot-dataset-visualizer:
|
| 5 |
+
// - On mount, read the iframe's own query string (HF forwards the parent's
|
| 6 |
+
// params into the iframe's URL on first load).
|
| 7 |
+
// - On state change, update our own URL via history.replaceState AND
|
| 8 |
+
// postMessage({ queryString }) to the parent so the shareable link on
|
| 9 |
+
// huggingface.co reflects our state.
|
| 10 |
+
|
| 11 |
+
const HF_PARENT_ORIGIN = 'https://huggingface.co';
|
| 12 |
+
|
| 13 |
+
export function getParam(key) {
|
| 14 |
+
try {
|
| 15 |
+
return new URLSearchParams(window.location.search).get(key);
|
| 16 |
+
} catch {
|
| 17 |
+
return null;
|
| 18 |
+
}
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
export function setParam(key, value) {
|
| 22 |
+
try {
|
| 23 |
+
const params = new URLSearchParams(window.location.search);
|
| 24 |
+
if (value == null || value === '') params.delete(key);
|
| 25 |
+
else params.set(key, value);
|
| 26 |
+
const qs = params.toString();
|
| 27 |
+
const newUrl = `${window.location.pathname}${qs ? '?' + qs : ''}`;
|
| 28 |
+
window.history.replaceState(null, '', newUrl);
|
| 29 |
+
postToParent(params);
|
| 30 |
+
} catch {
|
| 31 |
+
// Ignore — standalone deploy or cross-origin blocked.
|
| 32 |
+
}
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
function postToParent(params) {
|
| 36 |
+
try {
|
| 37 |
+
if (window.parent && window.parent !== window) {
|
| 38 |
+
window.parent.postMessage(
|
| 39 |
+
{ queryString: params.toString() },
|
| 40 |
+
HF_PARENT_ORIGIN
|
| 41 |
+
);
|
| 42 |
+
}
|
| 43 |
+
} catch {
|
| 44 |
+
// Ignore.
|
| 45 |
+
}
|
| 46 |
+
}
|