Compare commits
2
Commits
8e36ec5ca5
...
cba6a173e6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cba6a173e6 | ||
|
|
5bc3b6bbcb |
+7
-11
@@ -33,24 +33,20 @@ REQUIRED JSON STRUCTURE:
|
|||||||
"video_prompts": [
|
"video_prompts": [
|
||||||
{
|
{
|
||||||
"type": "Abstract",
|
"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",
|
"prompt": "short 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"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Cinematic",
|
"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",
|
"prompt": "short 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"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Hybrid",
|
"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",
|
"prompt": "short 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"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Keep each video_prompts[].prompt under 900 characters (excluding the Negative line).
|
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.`,
|
|
||||||
partial: `You are a music production assistant specializing in Suno AI song creation.
|
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
|
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.
|
ABAB rhyme. No references to instruments, genre, or music production.
|
||||||
- style: comma-separated, max 120 words, include tempo + BPM range, never
|
- style: comma-separated, max 120 words, include tempo + BPM range, never
|
||||||
name an artist, no "sounds like" phrases.
|
name an artist, no "sounds like" phrases.
|
||||||
- video_prompts: 5-second seamless loops, 16:9. Include the "Negative" line
|
- video_prompts: short seamless loops, 16:9. Include the "Negative" line
|
||||||
inside each prompt. Keep prompts under 900 characters. Recommended tools: {{VIDEO_TOOLS}}. Never recommend Sora.
|
inside each prompt. Keep prompts under 900 characters.
|
||||||
- youtube_description: follow the hook → description → divider → lyrics →
|
- youtube_description: follow the hook → description → divider → lyrics →
|
||||||
divider → CTA → hashtags structure.`,
|
divider → CTA → hashtags structure.`,
|
||||||
style_normal: `You are a Suno style-prompt generator.
|
style_normal: `You are a Suno style-prompt generator.
|
||||||
@@ -157,7 +153,7 @@ function getToolsString() {
|
|||||||
|
|
||||||
export function buildSystemPrompt(req) {
|
export function buildSystemPrompt(req) {
|
||||||
const toolsStr = getToolsString();
|
const toolsStr = getToolsString();
|
||||||
|
|
||||||
if (req.section === 'lyrics_partial') {
|
if (req.section === 'lyrics_partial') {
|
||||||
return activePrompts.lyrics_partial;
|
return activePrompts.lyrics_partial;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
} from "../../lib/types";
|
} from "../../lib/types";
|
||||||
import { useVideoAiLinks } from "../../lib/useVideoAiLinks";
|
import { useVideoAiLinks } from "../../lib/useVideoAiLinks";
|
||||||
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons";
|
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { useToast } from "../../lib/toast";
|
||||||
|
|
||||||
interface VideoPromptsCardProps {
|
interface VideoPromptsCardProps {
|
||||||
prompts: VideoPrompt[];
|
prompts: VideoPrompt[];
|
||||||
@@ -37,6 +38,36 @@ export function VideoPromptsCard({
|
|||||||
|
|
||||||
const ref = useAutoHeight(current?.prompt ?? "", 120);
|
const ref = useAutoHeight(current?.prompt ?? "", 120);
|
||||||
const { links } = useVideoAiLinks();
|
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 (
|
return (
|
||||||
<ResultCard
|
<ResultCard
|
||||||
@@ -103,13 +134,6 @@ export function VideoPromptsCard({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-sm italic text-fg-muted">
|
|
||||||
→ Best for:{" "}
|
|
||||||
<span className="not-italic text-fg">
|
|
||||||
{current.tool_recommendation}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="mt-1 p-3 rounded-lg bg-bg-hover/40 border border-border">
|
<div className="mt-1 p-3 rounded-lg bg-bg-hover/40 border border-border">
|
||||||
<p className="text-[11px] uppercase tracking-wider text-fg-muted mb-1">
|
<p className="text-[11px] uppercase tracking-wider text-fg-muted mb-1">
|
||||||
Negative
|
Negative
|
||||||
@@ -122,16 +146,16 @@ export function VideoPromptsCard({
|
|||||||
{links && links.length > 0 && (
|
{links && links.length > 0 && (
|
||||||
<div className="pt-2 flex flex-wrap gap-2">
|
<div className="pt-2 flex flex-wrap gap-2">
|
||||||
{links.map((link, i) => (
|
{links.map((link, i) => (
|
||||||
<a
|
<button
|
||||||
key={i}
|
key={i}
|
||||||
href={link.url}
|
type="button"
|
||||||
target="_blank"
|
onClick={() => handleToolClick(link.url)}
|
||||||
rel="noopener noreferrer"
|
|
||||||
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"
|
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}
|
{link.name}
|
||||||
<FontAwesomeIcon icon={faExternalLinkAlt} className="w-3 h-3" />
|
<FontAwesomeIcon icon={faExternalLinkAlt} className="w-3 h-3" />
|
||||||
</a>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user