From d2da3bf6dbd26e9069fa21680c0ce641c04ffff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Thu, 21 May 2026 20:12:02 -0400 Subject: [PATCH] feat(studio): show prompt preview modal before copying Clicking "Ask agent" now opens a modal that shows the full generated prompt so the user can read it before copying. The modal has a "Copy prompt" button that turns green on success. This replaces the silent clipboard copy that gave no visibility into what was copied. --- .../src/components/sidebar/BlocksTab.tsx | 151 +++++++++++++----- 1 file changed, 108 insertions(+), 43 deletions(-) diff --git a/packages/studio/src/components/sidebar/BlocksTab.tsx b/packages/studio/src/components/sidebar/BlocksTab.tsx index e12b78a0b..a68ed3e2a 100644 --- a/packages/studio/src/components/sidebar/BlocksTab.tsx +++ b/packages/studio/src/components/sidebar/BlocksTab.tsx @@ -1,4 +1,5 @@ import { memo, useState, useCallback, useRef, useEffect } from "react"; +import { createPortal } from "react-dom"; import { useBlockCatalog } from "../../hooks/useBlockCatalog"; import { BLOCK_CATEGORIES, @@ -23,6 +24,7 @@ interface BlocksTabProps { export const BlocksTab = memo(function BlocksTab({ onAddBlock, onPreviewBlock }: BlocksTabProps) { const { loading, error, search, setSearch, category, setCategory, filteredBlocks } = useBlockCatalog(); + const [promptModal, setPromptModal] = useState<{ title: string; prompt: string } | null>(null); if (loading) { return ( @@ -118,6 +120,7 @@ export const BlocksTab = memo(function BlocksTab({ onAddBlock, onPreviewBlock }: posterUrl={block.preview?.poster} videoUrl={block.preview?.video} onPreview={onPreviewBlock} + onShowPrompt={setPromptModal} onAdd={ block.category === "vfx" || block.category === "social" || @@ -131,6 +134,15 @@ export const BlocksTab = memo(function BlocksTab({ onAddBlock, onPreviewBlock }: )} + {promptModal && + createPortal( + setPromptModal(null)} + />, + document.body, + )} ); }); @@ -290,11 +302,11 @@ function BlockCard({ posterUrl?: string; videoUrl?: string; onAdd?: () => void; + onShowPrompt?: (info: { title: string; prompt: string }) => void; onPreview?: (preview: BlockPreviewInfo | null) => void; }) { const [hovered, setHovered] = useState(false); const [adding, setAdding] = useState(false); - const [copied, setCopied] = useState(false); const hoverTimer = useRef | null>(null); const colors = getCategoryColors(category); const needsWebGL = tags?.includes("html-in-canvas") || tags?.includes("webgl"); @@ -334,7 +346,7 @@ function BlockCard({ const { activeCompPath, compositionDimensions } = useStudioContext(); - const handleCopyPrompt = useCallback( + const handleShowPrompt = useCallback( (e: React.MouseEvent) => { e.stopPropagation(); const state = usePlayerStore.getState(); @@ -352,9 +364,7 @@ function BlockCard({ compositionDimensions: compositionDimensions ?? undefined, }; const prompt = buildAgentPrompt(title, name, description, category, blockType, context); - navigator.clipboard.writeText(prompt); - setCopied(true); - setTimeout(() => setCopied(false), 1500); + onShowPrompt?.({ title, prompt }); }, [title, name, description, category, blockType, activeCompPath, compositionDimensions], ); @@ -418,46 +428,26 @@ function BlockCard({ )} @@ -491,3 +481,78 @@ function BlockCard({ ); } + +function PromptPreviewModal({ + title, + prompt, + onClose, +}: { + title: string; + prompt: string; + onClose: () => void; +}) { + const [copied, setCopied] = useState(false); + + const handleCopy = useCallback(() => { + navigator.clipboard.writeText(prompt); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }, [prompt]); + + return ( +
+
e.stopPropagation()} + > +
+
+

Ask agent

+

{title}

+
+ +
+
+

+ Copy this prompt and paste it into your AI agent (Claude Code, Cursor, etc.) +

+
+            {prompt}
+          
+
+
+ Paste into your agent after copying + +
+
+
+ ); +}