From 5bc3b6bbcbcef679bb6f5206397b895318bb7eca Mon Sep 17 00:00:00 2001 From: orfelorfel23 Date: Thu, 4 Jun 2026 12:49:01 +0200 Subject: [PATCH] feat: improve video AI tool links UX and copy prompt on click --- server/prompts.mjs | 18 ++++----- src/components/cards/VideoPromptsCard.tsx | 48 +++++++++++++++++------ 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/server/prompts.mjs b/server/prompts.mjs index 18113fc..e3f9d6a 100644 --- a/server/prompts.mjs +++ b/server/prompts.mjs @@ -33,24 +33,20 @@ REQUIRED JSON STRUCTURE: "video_prompts": [ { "type": "Abstract", - "prompt": "5-second seamless loop, 16:9 aspect ratio. Abstract visuals: particles, fluid colors, geometric shapes, motion graphics. Specify: scene, camera angle, camera movement (slow pan/zoom/static), color palette (2-3 colors only), light source, motion quality. Must loop seamlessly (first frame = last frame). If using reverse playback, only use elements that make physical sense in reverse (e.g. pulsing light, expanding rings — NOT falling rain or rising smoke). Mood must match the song. Negative: text, watermark, logo, flash, abrupt cuts, camera shake, faces, hands, distortion, blur", - "tool_recommendation": "Tool name(s) + one-sentence reason why they suit this prompt" + "prompt": "5-second seamless loop, 16:9 aspect ratio. Abstract visuals: particles, fluid colors, geometric shapes, motion graphics. Specify: scene, camera angle, camera movement (slow pan/zoom/static), color palette (2-3 colors only), light source, motion quality. Must loop seamlessly (first frame = last frame). If using reverse playback, only use elements that make physical sense in reverse (e.g. pulsing light, expanding rings — NOT falling rain or rising smoke). Mood must match the song. Negative: text, watermark, logo, flash, abrupt cuts, camera shake, faces, hands, distortion, blur" }, { "type": "Cinematic", - "prompt": "5-second seamless loop, 16:9 aspect ratio. Real-world environment: landscape, weather, architecture, nature. Specify: scene, camera angle, camera movement, color palette (2-3 colors), light source, motion quality. Loopable (first frame = last frame). If elements move (rain, wind, water, fire), they must move in a physically correct direction — do NOT suggest rain or smoke as reversible loops. Negative: text, watermark, logo, flash, abrupt cuts, camera shake, faces, hands, distortion, blur", - "tool_recommendation": "Tool name(s) + one-sentence reason" + "prompt": "5-second seamless loop, 16:9 aspect ratio. Real-world environment: landscape, weather, architecture, nature. Specify: scene, camera angle, camera movement, color palette (2-3 colors), light source, motion quality. Loopable (first frame = last frame). If elements move (rain, wind, water, fire), they must move in a physically correct direction — do NOT suggest rain or smoke as reversible loops. Negative: text, watermark, logo, flash, abrupt cuts, camera shake, faces, hands, distortion, blur" }, { "type": "Hybrid", - "prompt": "5-second seamless loop, 16:9 aspect ratio. Blend of real and abstract: e.g. real environment with overlaid light effects, particle overlays on landscape, or a semi-abstract architectural scene. Loopable. Negative: text, watermark, logo, flash, abrupt cuts, camera shake, faces, hands, distortion, blur", - "tool_recommendation": "Tool name(s) + one-sentence reason" + "prompt": "5-second seamless loop, 16:9 aspect ratio. Blend of real and abstract: e.g. real environment with overlaid light effects, particle overlays on landscape, or a semi-abstract architectural scene. Loopable. Negative: text, watermark, logo, flash, abrupt cuts, camera shake, faces, hands, distortion, blur" } ] } -Keep each video_prompts[].prompt under 900 characters (excluding the Negative line). -Recommended video tools (choose the best fit per prompt): {{VIDEO_TOOLS}}. Never recommend Sora.`, +Keep each video_prompts[].prompt under 900 characters (excluding the Negative line).`, partial: `You are a music production assistant specializing in Suno AI song creation. The user has an existing song and wants to regenerate ONE section. Output ONLY @@ -78,8 +74,8 @@ QUALITY CONSTRAINTS: ABAB rhyme. No references to instruments, genre, or music production. - style: comma-separated, max 120 words, include tempo + BPM range, never name an artist, no "sounds like" phrases. -- video_prompts: 5-second seamless loops, 16:9. Include the "Negative" line - inside each prompt. Keep prompts under 900 characters. Recommended tools: {{VIDEO_TOOLS}}. Never recommend Sora. +- video_prompts: short seamless loops, 16:9. Include the "Negative" line + inside each prompt. Keep prompts under 900 characters. - youtube_description: follow the hook → description → divider → lyrics → divider → CTA → hashtags structure.`, style_normal: `You are a Suno style-prompt generator. @@ -157,7 +153,7 @@ function getToolsString() { export function buildSystemPrompt(req) { const toolsStr = getToolsString(); - + if (req.section === 'lyrics_partial') { return activePrompts.lyrics_partial; } diff --git a/src/components/cards/VideoPromptsCard.tsx b/src/components/cards/VideoPromptsCard.tsx index db7fe0e..dca3987 100644 --- a/src/components/cards/VideoPromptsCard.tsx +++ b/src/components/cards/VideoPromptsCard.tsx @@ -11,6 +11,7 @@ import { } from "../../lib/types"; import { useVideoAiLinks } from "../../lib/useVideoAiLinks"; import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons"; +import { useToast } from "../../lib/toast"; interface VideoPromptsCardProps { prompts: VideoPrompt[]; @@ -37,6 +38,36 @@ export function VideoPromptsCard({ const ref = useAutoHeight(current?.prompt ?? "", 120); const { links } = useVideoAiLinks(); + const toast = useToast(); + + const handleToolClick = async (url: string) => { + if (!current) return; + + // Copy the prompt to clipboard (with negative prompt if applicable) + const textToCopy = `Prompt: ${current.prompt}\n\nNegative: ${NEGATIVE_VIDEO_PROMPT}`; + try { + await navigator.clipboard.writeText(textToCopy); + toast.success("Prompt copied to clipboard!"); + } catch { + // Fallback + const ta = document.createElement("textarea"); + ta.value = textToCopy; + ta.style.position = "fixed"; + ta.style.opacity = "0"; + document.body.appendChild(ta); + ta.select(); + try { + document.execCommand("copy"); + toast.success("Prompt copied to clipboard!"); + } catch { + toast.error("Failed to copy prompt"); + } + document.body.removeChild(ta); + } + + // Open the tool in a new tab + window.open(url, "_blank", "noopener,noreferrer"); + }; return ( -

- → Best for:{" "} - - {current.tool_recommendation} - -

-

Negative @@ -122,16 +146,16 @@ export function VideoPromptsCard({ {links && links.length > 0 && (

{links.map((link, i) => ( - handleToolClick(link.url)} className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-[11px] font-bold uppercase tracking-wide bg-accent-primary/10 text-accent-primary hover:bg-accent-primary hover:text-white transition-all duration-150 border border-accent-primary/20 hover:border-accent-primary" + title={`Copy prompt and open ${link.name}`} > {link.name} - + ))}
)}