From 3e9794a23a8a5683f7d1eaa0c531c91ac47131a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Thu, 21 May 2026 18:46:59 -0400 Subject: [PATCH] feat(studio): add dual-action buttons to catalog cards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each catalog card now shows two hover buttons: - "Add" — inserts the block/component into the composition at the current playhead position (existing behavior, now with + icon) - "Agent prompt" — copies a contextual prompt to clipboard tailored to the block's category (captions, transitions, VFX, etc.) so the user can paste it into their AI agent for guided customization --- .../src/components/sidebar/BlocksTab.tsx | 100 ++++++++++++++++-- 1 file changed, 92 insertions(+), 8 deletions(-) diff --git a/packages/studio/src/components/sidebar/BlocksTab.tsx b/packages/studio/src/components/sidebar/BlocksTab.tsx index 51e4c7622..14cce1eff 100644 --- a/packages/studio/src/components/sidebar/BlocksTab.tsx +++ b/packages/studio/src/components/sidebar/BlocksTab.tsx @@ -113,6 +113,8 @@ export const BlocksTab = memo(function BlocksTab({ onAddBlock, onPreviewBlock }: key={block.name} name={block.name} title={block.title} + description={block.description} + blockType={block.type} duration={dur} category={block.category} tags={block.tags} @@ -160,9 +162,47 @@ function CategoryPill({ ); } +function buildAgentPrompt( + title: string, + name: string, + description: string, + category: BlockCategory, + blockType: string, +): string { + const isComponent = blockType === "hyperframes:component"; + const kind = isComponent ? "component" : "block"; + + const categoryHints: Record = { + captions: + "This is a caption style. Add it to the composition, then customize the transcript text, fonts, colors, and timing to match the voiceover or audio.", + vfx: "This is a VFX effect. Add it as an overlay and adjust shader parameters, colors, and intensity to match the scene.", + transitions: + "This is a transition. Place it between two scenes on the timeline and adjust the duration and direction.", + effects: + "This is a visual effect overlay. Layer it on top of existing content and tweak colors, opacity, and animation timing.", + social: + "This is a social media template. Customize the text, handle, avatar, and metrics to match the content.", + data: "This is a data visualization. Update the data values, labels, colors, and animation stagger to tell the right story.", + scenes: + "This is a full scene. Customize the text, images, layout, and animation timing to fit the narrative.", + }; + + const hint = categoryHints[category] ?? `Customize this ${kind} to fit the composition.`; + + return [ + `Add the "${title}" ${kind} (registry: ${name}) to my composition using /hyperframes.`, + "", + `${description}`, + "", + hint, + ].join("\n"); +} + function BlockCard({ name, title, + description, + blockType, duration, category, tags, @@ -174,6 +214,8 @@ function BlockCard({ }: { name: string; title: string; + description: string; + blockType: string; duration?: number; category: BlockCategory; tags?: string[]; @@ -185,6 +227,7 @@ function BlockCard({ }) { 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"); @@ -222,6 +265,17 @@ function BlockCard({ [onAdd, adding], ); + const handleCopyPrompt = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + const prompt = buildAgentPrompt(title, name, description, category, blockType); + navigator.clipboard.writeText(prompt); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }, + [title, name, description, category, blockType], + ); + const handleDragStart = useCallback( (e: React.DragEvent) => { e.dataTransfer.effectAllowed = "copy"; @@ -267,14 +321,44 @@ function BlockCard({ )} - {/* Add button overlay */} - + {/* Action buttons overlay */} +
+ + +
{/* Badges */}