From fc4cd55890b5460eb9182c9ced865a5076c03714 Mon Sep 17 00:00:00 2001 From: Eduardo Russo Date: Thu, 15 Oct 2026 13:43:55 +0100 Subject: [PATCH 1/2] fix(studio): a link clicked in the snapshot no longer navigates the frame Outside pick mode the click went through, so the sandboxed frame loaded the live site, which refuses to be framed ("refused to connect"). Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01TjR6eM2tUwU3NGnKXsmEap --- .../src/content-pane/snapshot-frame.component.test.tsx | 9 +++++++++ .../src/content-pane/snapshot-frame.component.tsx | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/apps/studio-ui/src/content-pane/snapshot-frame.component.test.tsx b/apps/studio-ui/src/content-pane/snapshot-frame.component.test.tsx index 5c05121e..8b372f89 100644 --- a/apps/studio-ui/src/content-pane/snapshot-frame.component.test.tsx +++ b/apps/studio-ui/src/content-pane/snapshot-frame.component.test.tsx @@ -35,4 +35,13 @@ describe('SnapshotFrame', () => { expect(onPickNode).not.toHaveBeenCalled() }) + + it.each([true, false])('never lets a clicked link navigate the frame to the live site (pick mode %s)', (pickMode) => { + const { container } = render( undefined} onPickNode={() => undefined} />) + const link = loadedLink(container) + + const notCancelled = fireEvent.click(link) + + expect(notCancelled).toBe(false) + }) }) diff --git a/apps/studio-ui/src/content-pane/snapshot-frame.component.tsx b/apps/studio-ui/src/content-pane/snapshot-frame.component.tsx index ecaa6e22..bf59bd4b 100644 --- a/apps/studio-ui/src/content-pane/snapshot-frame.component.tsx +++ b/apps/studio-ui/src/content-pane/snapshot-frame.component.tsx @@ -46,6 +46,9 @@ export function SnapshotFrame ({ html, pickMode, showHidden, hoveredSelector, on const handleMouseOver = (event: MouseEvent): void => { propsRef.current.onHoverNode(nodeIdOf(event.target)) } const handleMouseOut = (): void => { propsRef.current.onHoverNode(undefined) } const handleClick = (event: MouseEvent): void => { + // The snapshot is a picture of the page, not the page: a link must never navigate this frame to the live + // site (which usually refuses to be framed), whether or not a pick is under way. + if (linkOf(event.target) !== undefined) event.preventDefault() if (!propsRef.current.pickMode) return const nodeId = nodeIdOf(event.target) if (nodeId === undefined) return @@ -97,6 +100,13 @@ export function SnapshotFrame ({ html, pickMode, showHidden, hoveredSelector, on ) } +/** The link at or above `target`, if any (same cross-realm caveat as `nodeIdOf`). */ +function linkOf (target: EventTarget | null): Element | undefined { + const element = (target as Node | null)?.nodeType === Node.ELEMENT_NODE ? (target as Element).closest('a[href], area[href]') : null + + return element ?? undefined +} + /** The `data-oc-node` id of the stamped element at or above `target`. The target belongs to the iframe's own realm, so `target instanceof Element` is false for it: test the node type instead (issue #153). */ function nodeIdOf (target: EventTarget | null): string | undefined { const element = (target as Node | null)?.nodeType === Node.ELEMENT_NODE ? (target as Element).closest(`[${CSS.escape(NODE_ID_ATTRIBUTE)}]`) : null From 0872844ce0d4589cb39f652aaf6d5c38ad6e8a90 Mon Sep 17 00:00:00 2001 From: Eduardo Russo Date: Thu, 15 Oct 2026 14:24:40 +0100 Subject: [PATCH 2/2] feat(vscode): release 0.1.2 Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01TjR6eM2tUwU3NGnKXsmEap --- apps/vscode-extension/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/vscode-extension/package.json b/apps/vscode-extension/package.json index 4a78ab17..07ead00e 100644 --- a/apps/vscode-extension/package.json +++ b/apps/vscode-extension/package.json @@ -1,6 +1,6 @@ { "name": "@opencraw/vscode", - "version": "0.1.1", + "version": "0.1.2", "private": true, "description": "OpenCraw in the editor: starts the Studio server and opens a folder, recipe or document in the browser.", "type": "module",