Skip to content

Commit 8729f7f

Browse files
thomasahleclaude
andcommitted
Track Surfer focused item via get_state polling
Expose get_state() on the iframe window (patched via setup-surfer.sh). The integration.js shim polls it every 100ms (after waves load) and posts a 'focused-item-changed' message to the parent when WaveData.focused_item changes. WaveformViewer updates _focusedVisibleIdx on this event so MoveCursorToTransition always navigates the signal the user last clicked, not just the auto-selected one on load. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 11207ef commit 8729f7f

2 files changed

Lines changed: 44 additions & 0 deletions

File tree

scripts/setup-surfer.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ function patchIndex(path) {
7474
if (!names.includes('index_of_name')) {
7575
names.push('index_of_name');
7676
}
77+
if (!names.includes('get_state')) {
78+
names.push('get_state');
79+
}
7780
return `import { ${names.join(', ')} } from './surfer.js';`;
7881
});
7982
@@ -92,6 +95,13 @@ function patchIndex(path) {
9295
}
9396
}
9497
98+
if (!src.includes('window.get_state = get_state;')) {
99+
const anchor = 'window.waves_loaded = waves_loaded;';
100+
if (src.includes(anchor)) {
101+
src = src.replace(anchor, `${anchor}\n window.get_state = get_state;`);
102+
}
103+
}
104+
95105
fs.writeFileSync(path, src);
96106
}
97107
@@ -103,6 +113,8 @@ function register_message_listener() {
103113
const pending = [];
104114
let wavesLoadedNotified = false;
105115
let engineReadyNotified = false;
116+
let lastFocusedItemIdx = undefined;
117+
let focusedItemPolling = false;
106118
107119
function notifyParent(type, payload = {}) {
108120
if (window.parent && window.parent !== window) {
@@ -147,6 +159,33 @@ function register_message_listener() {
147159
}
148160
}
149161
162+
// Poll Surfer's RON state to detect focused-item changes.
163+
// WaveData.focused_item: Option<VisibleItemIndex> is included in get_state() output.
164+
// When the user clicks a different signal row, we notify the parent so it can
165+
// update which variable the transition toolbar buttons navigate.
166+
async function notifyIfFocusedItemChanged() {
167+
if (!wavesLoadedNotified) return;
168+
if (typeof window.get_state !== 'function') return;
169+
if (focusedItemPolling) return;
170+
focusedItemPolling = true;
171+
try {
172+
const state = await window.get_state();
173+
if (state) {
174+
const m = state.match(/focused_item:\\s*Some\\s*\\(\\s*VisibleItemIndex\\s*\\(\\s*(\\d+)\\s*\\)\\s*\\)/);
175+
const newIdx = m ? parseInt(m[1], 10) : null;
176+
if (newIdx !== lastFocusedItemIdx) {
177+
lastFocusedItemIdx = newIdx;
178+
if (newIdx !== null) {
179+
notifyParent('focused-item-changed', { index: newIdx });
180+
}
181+
}
182+
}
183+
} catch {
184+
// Ignore transient errors while Surfer initialises.
185+
}
186+
focusedItemPolling = false;
187+
}
188+
150189
function injectSurferMessage(message) {
151190
queueOrInjectMessage(JSON.stringify(message));
152191
}
@@ -157,6 +196,7 @@ function register_message_listener() {
157196
switch (decoded.command) {
158197
case 'LoadUrl': {
159198
wavesLoadedNotified = false;
199+
lastFocusedItemIdx = undefined;
160200
injectSurferMessage({ LoadWaveformFileFromUrl: [decoded.url, 'Clear'] });
161201
break;
162202
}
@@ -180,6 +220,7 @@ function register_message_listener() {
180220
setInterval(() => {
181221
flushPendingMessages();
182222
notifyIfWavesLoaded();
223+
notifyIfFocusedItemChanged();
183224
}, 100);
184225
}
185226
`;

src/lib/components/WaveformViewer.svelte

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,9 @@
417417
signalsReady = true;
418418
waveReadySource = 'surfer';
419419
}
420+
if (data.type === 'focused-item-changed' && typeof data.index === 'number') {
421+
_focusedVisibleIdx = data.index;
422+
}
420423
}
421424
422425
onMount(() => {

0 commit comments

Comments
 (0)