From 7afd8846caaa7092dfc88c2097f52a246bc44530 Mon Sep 17 00:00:00 2001 From: Guilhem Lemouel Date: Thu, 23 Apr 2026 12:59:49 +0200 Subject: [PATCH] fix(frontend): prefer WebSocket for flow round-trip when wmill dev is connected updateFlow used isInIframe priority, which routed Claude Code's iframe preview through postMessage (no listener) and silently dropped flow edits. Flip the priority: when the wmill dev WebSocket is open, use it (covers standalone tabs and Claude Code's preview); fall back to postMessage only when no WS is connected (the VS Code extension's iframe URL has no `local=true`, so it never opens one). Also stop assigning lastSent before a channel actually accepted the message, so a CONNECTING WS doesn't silently swallow the first change. Co-Authored-By: Claude Opus 4.7 (1M context) --- frontend/src/lib/components/Dev.svelte | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/frontend/src/lib/components/Dev.svelte b/frontend/src/lib/components/Dev.svelte index 4791f5aa2e..7f18984fee 100644 --- a/frontend/src/lib/components/Dev.svelte +++ b/frontend/src/lib/components/Dev.svelte @@ -596,16 +596,23 @@ if (lockChanges) { return } - if (!deepEqual(flow, lastSent)) { - lastSent = $state.snapshot(flow) - if (isInIframe) { - // VS Code extension: round-trip via postMessage - window?.parent.postMessage({ type: 'flow', flow: lastSent, uriPath: lastUriPath }, '*') - } else if (socket && socket.readyState === WebSocket.OPEN) { - // CLI dev mode: round-trip via WebSocket - socket.send(JSON.stringify({ type: 'flow', flow: lastSent, uriPath: lastUriPath })) - } + if (deepEqual(flow, lastSent)) { + return } + const snapshot = $state.snapshot(flow) + // Prefer the WebSocket whenever a `wmill dev` session is connected — this covers + // both standalone browser tabs and Claude Code's iframe preview. The VS Code + // extension never opens this socket (its iframe URL omits `local=true`), so it + // falls through to the postMessage path it has always used. + if (socket && socket.readyState === WebSocket.OPEN) { + socket.send(JSON.stringify({ type: 'flow', flow: snapshot, uriPath: lastUriPath })) + lastSent = snapshot + } else if (isInIframe) { + window?.parent.postMessage({ type: 'flow', flow: snapshot, uriPath: lastUriPath }, '*') + lastSent = snapshot + } + // Else: no channel available yet (WS still connecting, not in an iframe). + // Don't mark `lastSent` so the next change will retry instead of being silently swallowed. } let reload = $state(0)