Files
flatrender/src/lib/scene-browser-data.ts
T

624 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** A layer definition without an `id` — fresh ids are assigned when the template is applied. */
export interface SceneTemplateLayer {
type: "text" | "image" | "video" | "shape" | "draw";
name?: string;
visible?: boolean;
x: number;
y: number;
width: number;
height: number;
rotation: number;
opacity: number;
zIndex: number;
props: Record<string, unknown>;
}
export const SCENE_BROWSER_CATEGORIES = [
{ id: "all", label: "All Scenes" },
{ id: "characters", label: "Characters" },
{ id: "business", label: "Business" },
{ id: "technology", label: "Technology" },
{ id: "nature", label: "Nature" },
{ id: "abstract", label: "Abstract" },
{ id: "sports", label: "Sports" },
{ id: "food", label: "Food" },
] as const;
export type SceneBrowserCategoryId =
(typeof SCENE_BROWSER_CATEGORIES)[number]["id"];
export type SceneBrowserContentCategory = Exclude<
SceneBrowserCategoryId,
"all"
>;
export type SceneBrowserMediaFilter = "all" | "video" | "photo";
export interface BrowserSceneItem {
id: string;
name: string;
category: SceneBrowserContentCategory;
mediaType: "video" | "photo";
characterCount: number;
durationLabel: string;
gradientFrom: string;
gradientTo: string;
/** Pre-built layers that populate the scene when selected. */
templateLayers: SceneTemplateLayer[];
}
// ---------------------------------------------------------------------------
// Layout helpers — canvas is 1280 × 720
// ---------------------------------------------------------------------------
/** Two-column layout: solid colour left, image placeholder right. */
function splitLayout(
bg: string,
titleColor = "#FFFFFF",
subtitleColor = "#94a3b8"
): SceneTemplateLayer[] {
return [
{
type: "shape",
x: 0,
y: 0,
width: 1280,
height: 720,
rotation: 0,
opacity: 1,
zIndex: 0,
props: { shape: "rect", fill: bg, stroke: bg, strokeWidth: 0, cornerRadius: 0 },
},
{
type: "image",
x: 660,
y: 60,
width: 540,
height: 600,
rotation: 0,
opacity: 1,
zIndex: 1,
props: { src: "", cornerRadius: 12 },
},
{
type: "text",
x: 80,
y: 230,
width: 530,
height: 120,
rotation: 0,
opacity: 1,
zIndex: 2,
props: {
text: "Your Main Title",
fontSize: 60,
fill: titleColor,
fontFamily: "Inter, sans-serif",
align: "left",
letterSpacing: 0,
lineHeight: 1.2,
animation: "none",
},
},
{
type: "text",
x: 80,
y: 380,
width: 530,
height: 80,
rotation: 0,
opacity: 1,
zIndex: 3,
props: {
text: "Your Subtitle Here",
fontSize: 36,
fill: subtitleColor,
fontFamily: "Inter, sans-serif",
align: "left",
letterSpacing: 0,
lineHeight: 1.2,
animation: "none",
},
},
];
}
/** Centred title + subtitle, no image placeholder. */
function centeredLayout(
bg: string,
titleColor = "#FFFFFF",
subtitleColor = "#94a3b8"
): SceneTemplateLayer[] {
return [
{
type: "shape",
x: 0,
y: 0,
width: 1280,
height: 720,
rotation: 0,
opacity: 1,
zIndex: 0,
props: { shape: "rect", fill: bg, stroke: bg, strokeWidth: 0, cornerRadius: 0 },
},
{
type: "text",
x: 80,
y: 265,
width: 1120,
height: 135,
rotation: 0,
opacity: 1,
zIndex: 1,
props: {
text: "Your Main Title",
fontSize: 72,
fill: titleColor,
fontFamily: "Inter, sans-serif",
align: "center",
letterSpacing: 0,
lineHeight: 1.2,
animation: "none",
},
},
{
type: "text",
x: 80,
y: 430,
width: 1120,
height: 80,
rotation: 0,
opacity: 1,
zIndex: 2,
props: {
text: "Your Subtitle Here",
fontSize: 40,
fill: subtitleColor,
fontFamily: "Inter, sans-serif",
align: "center",
letterSpacing: 0,
lineHeight: 1.2,
animation: "none",
},
},
];
}
/** Full-bleed image background with a dark overlay + centred text on top. */
function overlayLayout(
bg: string,
titleColor = "#FFFFFF",
subtitleColor = "#e2e8f0"
): SceneTemplateLayer[] {
return [
{
type: "shape",
x: 0,
y: 0,
width: 1280,
height: 720,
rotation: 0,
opacity: 1,
zIndex: 0,
props: { shape: "rect", fill: bg, stroke: bg, strokeWidth: 0, cornerRadius: 0 },
},
{
type: "image",
x: 0,
y: 0,
width: 1280,
height: 720,
rotation: 0,
opacity: 1,
zIndex: 1,
props: { src: "", cornerRadius: 0 },
},
{
type: "shape",
x: 0,
y: 0,
width: 1280,
height: 720,
rotation: 0,
opacity: 0.55,
zIndex: 2,
props: { shape: "rect", fill: "#000000", stroke: "#000000", strokeWidth: 0, cornerRadius: 0 },
},
{
type: "text",
x: 80,
y: 265,
width: 1120,
height: 135,
rotation: 0,
opacity: 1,
zIndex: 3,
props: {
text: "Your Main Title",
fontSize: 72,
fill: titleColor,
fontFamily: "Inter, sans-serif",
align: "center",
letterSpacing: 0,
lineHeight: 1.2,
animation: "none",
},
},
{
type: "text",
x: 80,
y: 430,
width: 1120,
height: 80,
rotation: 0,
opacity: 1,
zIndex: 4,
props: {
text: "Your Subtitle Here",
fontSize: 40,
fill: subtitleColor,
fontFamily: "Inter, sans-serif",
align: "center",
letterSpacing: 0,
lineHeight: 1.2,
animation: "none",
},
},
];
}
// ---------------------------------------------------------------------------
// Scene catalog
// ---------------------------------------------------------------------------
export const BROWSER_SCENES: BrowserSceneItem[] = [
// ── Characters ────────────────────────────────────────────────────────────
{
id: "man-waving",
name: "Man waving hello",
category: "characters",
mediaType: "video",
characterCount: 1,
durationLabel: "3-10 sec.",
gradientFrom: "from-sky-200",
gradientTo: "to-blue-300",
templateLayers: splitLayout("#0c1a3d"),
},
{
id: "woman-presenting",
name: "Woman presenting",
category: "characters",
mediaType: "video",
characterCount: 1,
durationLabel: "3-10 sec.",
gradientFrom: "from-violet-200",
gradientTo: "to-purple-300",
templateLayers: splitLayout("#1a0a2e"),
},
{
id: "friendly-greeting",
name: "Friendly office greeting",
category: "characters",
mediaType: "photo",
characterCount: 2,
durationLabel: "3-10 sec.",
gradientFrom: "from-rose-200",
gradientTo: "to-pink-300",
templateLayers: splitLayout("#2d0a14"),
},
{
id: "customer-support",
name: "Customer support agent",
category: "characters",
mediaType: "video",
characterCount: 1,
durationLabel: "3-10 sec.",
gradientFrom: "from-cyan-200",
gradientTo: "to-teal-300",
templateLayers: splitLayout("#071a1a"),
},
// ── Business ──────────────────────────────────────────────────────────────
{
id: "team-meeting",
name: "Team meeting",
category: "business",
mediaType: "video",
characterCount: 4,
durationLabel: "3-10 sec.",
gradientFrom: "from-blue-200",
gradientTo: "to-indigo-300",
templateLayers: splitLayout("#0a1a3d"),
},
{
id: "handshake-deal",
name: "Handshake closing deal",
category: "business",
mediaType: "photo",
characterCount: 2,
durationLabel: "3-10 sec.",
gradientFrom: "from-slate-200",
gradientTo: "to-gray-300",
templateLayers: splitLayout("#0f172a"),
},
{
id: "startup-pitch",
name: "Startup pitch deck",
category: "business",
mediaType: "video",
characterCount: 1,
durationLabel: "3-10 sec.",
gradientFrom: "from-indigo-200",
gradientTo: "to-violet-300",
templateLayers: centeredLayout("#0f0f2e"),
},
{
id: "office-collaboration",
name: "Office collaboration",
category: "business",
mediaType: "video",
characterCount: 3,
durationLabel: "3-10 sec.",
gradientFrom: "from-blue-200",
gradientTo: "to-sky-300",
templateLayers: splitLayout("#0a1428"),
},
// ── Technology ────────────────────────────────────────────────────────────
{
id: "city-skyline",
name: "City skyline",
category: "technology",
mediaType: "video",
characterCount: 0,
durationLabel: "3-10 sec.",
gradientFrom: "from-indigo-200",
gradientTo: "to-blue-400",
templateLayers: overlayLayout("#0a0f2e"),
},
{
id: "tech-network",
name: "Tech network",
category: "technology",
mediaType: "video",
characterCount: 0,
durationLabel: "3-10 sec.",
gradientFrom: "from-cyan-200",
gradientTo: "to-indigo-300",
templateLayers: centeredLayout("#071a1a"),
},
{
id: "coding-desk",
name: "Developer at desk",
category: "technology",
mediaType: "photo",
characterCount: 1,
durationLabel: "3-10 sec.",
gradientFrom: "from-emerald-200",
gradientTo: "to-teal-300",
templateLayers: splitLayout("#071c14"),
},
{
id: "data-visualization",
name: "Data visualization",
category: "technology",
mediaType: "video",
characterCount: 0,
durationLabel: "3-10 sec.",
gradientFrom: "from-violet-200",
gradientTo: "to-fuchsia-300",
templateLayers: centeredLayout("#1a0a2e"),
},
// ── Nature ────────────────────────────────────────────────────────────────
{
id: "forest-path",
name: "Forest morning path",
category: "nature",
mediaType: "video",
characterCount: 0,
durationLabel: "3-10 sec.",
gradientFrom: "from-green-200",
gradientTo: "to-emerald-300",
templateLayers: overlayLayout("#071c0f"),
},
{
id: "ocean-sunset",
name: "Ocean sunset",
category: "nature",
mediaType: "video",
characterCount: 0,
durationLabel: "3-10 sec.",
gradientFrom: "from-amber-200",
gradientTo: "to-orange-300",
templateLayers: overlayLayout("#1c0f07"),
},
{
id: "mountain-aerial",
name: "Mountain aerial",
category: "nature",
mediaType: "photo",
characterCount: 0,
durationLabel: "3-10 sec.",
gradientFrom: "from-sky-200",
gradientTo: "to-blue-300",
templateLayers: overlayLayout("#0c1a2e"),
},
{
id: "wildlife-meadow",
name: "Wildlife meadow",
category: "nature",
mediaType: "video",
characterCount: 0,
durationLabel: "3-10 sec.",
gradientFrom: "from-lime-200",
gradientTo: "to-green-300",
templateLayers: overlayLayout("#0a1c07"),
},
// ── Abstract ──────────────────────────────────────────────────────────────
{
id: "abstract-waves",
name: "Abstract waves",
category: "abstract",
mediaType: "video",
characterCount: 0,
durationLabel: "3-10 sec.",
gradientFrom: "from-fuchsia-200",
gradientTo: "to-purple-300",
templateLayers: centeredLayout("#1a0a2e"),
},
{
id: "gradient-flow",
name: "Gradient flow",
category: "abstract",
mediaType: "video",
characterCount: 0,
durationLabel: "3-10 sec.",
gradientFrom: "from-pink-200",
gradientTo: "to-rose-300",
templateLayers: centeredLayout("#1c0a14"),
},
{
id: "geometric-shapes",
name: "Geometric shapes",
category: "abstract",
mediaType: "photo",
characterCount: 0,
durationLabel: "3-10 sec.",
gradientFrom: "from-indigo-200",
gradientTo: "to-violet-300",
templateLayers: centeredLayout("#0f0a2e"),
},
{
id: "particle-burst",
name: "Particle burst",
category: "abstract",
mediaType: "video",
characterCount: 0,
durationLabel: "3-10 sec.",
gradientFrom: "from-blue-200",
gradientTo: "to-cyan-300",
templateLayers: centeredLayout("#071628"),
},
// ── Sports ────────────────────────────────────────────────────────────────
{
id: "sports-celebration",
name: "Sports celebration",
category: "sports",
mediaType: "video",
characterCount: 3,
durationLabel: "3-10 sec.",
gradientFrom: "from-orange-200",
gradientTo: "to-red-300",
templateLayers: splitLayout("#1c0f07"),
},
{
id: "soccer-goal",
name: "Soccer goal moment",
category: "sports",
mediaType: "video",
characterCount: 2,
durationLabel: "3-10 sec.",
gradientFrom: "from-green-200",
gradientTo: "to-lime-300",
templateLayers: splitLayout("#0a1c07"),
},
{
id: "gym-workout",
name: "Gym workout",
category: "sports",
mediaType: "photo",
characterCount: 1,
durationLabel: "3-10 sec.",
gradientFrom: "from-amber-200",
gradientTo: "to-yellow-300",
templateLayers: splitLayout("#1c1007"),
},
{
id: "running-track",
name: "Running on track",
category: "sports",
mediaType: "video",
characterCount: 1,
durationLabel: "3-10 sec.",
gradientFrom: "from-sky-200",
gradientTo: "to-indigo-300",
templateLayers: splitLayout("#0a1428"),
},
// ── Food ──────────────────────────────────────────────────────────────────
{
id: "food-preparation",
name: "Food preparation",
category: "food",
mediaType: "video",
characterCount: 1,
durationLabel: "3-10 sec.",
gradientFrom: "from-amber-200",
gradientTo: "to-orange-300",
templateLayers: splitLayout("#1c0f07"),
},
{
id: "restaurant-plating",
name: "Restaurant plating",
category: "food",
mediaType: "photo",
characterCount: 0,
durationLabel: "3-10 sec.",
gradientFrom: "from-rose-200",
gradientTo: "to-red-300",
templateLayers: overlayLayout("#1c0a0f"),
},
{
id: "coffee-pour",
name: "Coffee pour slow-mo",
category: "food",
mediaType: "video",
characterCount: 0,
durationLabel: "3-10 sec.",
gradientFrom: "from-yellow-200",
gradientTo: "to-amber-300",
templateLayers: overlayLayout("#0f0a07"),
},
{
id: "fresh-ingredients",
name: "Fresh ingredients",
category: "food",
mediaType: "photo",
characterCount: 0,
durationLabel: "3-10 sec.",
gradientFrom: "from-lime-200",
gradientTo: "to-green-300",
templateLayers: overlayLayout("#0a1c07"),
},
];
export function filterBrowserScenes(
scenes: BrowserSceneItem[],
options: {
categoryId: SceneBrowserCategoryId;
mediaFilter: SceneBrowserMediaFilter;
search: string;
}
): BrowserSceneItem[] {
const query = options.search.trim().toLowerCase();
return scenes.filter((scene) => {
if (options.categoryId !== "all" && scene.category !== options.categoryId) {
return false;
}
if (
options.mediaFilter !== "all" &&
scene.mediaType !== options.mediaFilter
) {
return false;
}
if (query && !scene.name.toLowerCase().includes(query)) {
return false;
}
return true;
});
}