From fb70b3917315f6ecd09bb88cad2394465c563aa1 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Thu, 5 Mar 2026 15:41:51 -0700 Subject: [PATCH] Open embed route to all origins, add embed snippet UI, enable LTspice frame-ancestors * for /embed/* routes so any site can iframe notebooks. Remove postMessage origin allowlist (theme toggle is cosmetic-only). Add EmbedDialog popover with copy-paste iframe snippet and theme picker. Enable ltspice in the engine dropdown now that the backend supports it. --- frontend/src/components/embed/EmbedViewer.tsx | 8 -- .../notebook/toolbar/EmbedDialog.tsx | 97 +++++++++++++++++++ .../notebook/toolbar/NotebookToolbar.tsx | 8 +- frontend/src/middleware.ts | 4 +- 4 files changed, 104 insertions(+), 13 deletions(-) create mode 100644 frontend/src/components/notebook/toolbar/EmbedDialog.tsx diff --git a/frontend/src/components/embed/EmbedViewer.tsx b/frontend/src/components/embed/EmbedViewer.tsx index 26249a3..cf1339e 100644 --- a/frontend/src/components/embed/EmbedViewer.tsx +++ b/frontend/src/components/embed/EmbedViewer.tsx @@ -4,12 +4,6 @@ import * as api from '../../lib/api'; import { EmbedCell } from './EmbedCell'; import { Loader2 } from 'lucide-react'; -// Origins allowed to send postMessage theme changes to the embed. -// Must stay in sync with CSP_FRAME_ANCESTORS / CORS_EXTRA_ORIGINS. -const ALLOWED_MESSAGE_ORIGINS = new Set([ - 'https://forrest.warehack.ing', -]); - interface EmbedViewerProps { notebookId: string; initialTheme: string; @@ -33,8 +27,6 @@ export default function EmbedViewer({ notebookId, initialTheme }: EmbedViewerPro // Listen for postMessage theme changes from parent iframe host useEffect(() => { function handleMessage(event: MessageEvent) { - if (!ALLOWED_MESSAGE_ORIGINS.has(event.origin)) return; - if (event.data?.type === 'spicebook-theme') { const incoming = event.data.theme; if (incoming === 'dark' || incoming === 'light') { diff --git a/frontend/src/components/notebook/toolbar/EmbedDialog.tsx b/frontend/src/components/notebook/toolbar/EmbedDialog.tsx new file mode 100644 index 0000000..aa0f5b4 --- /dev/null +++ b/frontend/src/components/notebook/toolbar/EmbedDialog.tsx @@ -0,0 +1,97 @@ +import { useState, useRef } from 'react'; +import { Code, Check, Copy } from 'lucide-react'; +import { Popover, PopoverTrigger, PopoverContent } from '../../ui/popover'; +import { Button } from '../../ui/Button'; +import { useNotebookStore } from '../../../lib/notebook-store'; + +export function EmbedDialog() { + const { notebookId } = useNotebookStore(); + const [theme, setTheme] = useState<'dark' | 'light'>('dark'); + const [copied, setCopied] = useState(false); + const timerRef = useRef>(undefined); + + if (!notebookId) return null; + + const origin = typeof window !== 'undefined' ? window.location.origin : ''; + + const snippet = ``; + + function handleCopy() { + navigator.clipboard.writeText(snippet).then(() => { + setCopied(true); + clearTimeout(timerRef.current); + timerRef.current = setTimeout(() => setCopied(false), 2000); + }); + } + + return ( + + + + + + +
+

+ Embed this notebook on any page +

+ + {/* Theme toggle */} +
+ + +
+ + {/* Snippet */} +