Storyboard Feature
Deploy / deploy (push) Failing after 1m53s

This commit is contained in:
twotalesanimation
2026-07-22 12:38:26 +02:00
parent 77a3161f0c
commit 3ae0a6e32f
3 changed files with 503 additions and 57 deletions
+46 -52
View File
@@ -1,6 +1,6 @@
"use client";
import { useState, useMemo, useCallback, useEffect } from "react";
import { useState, useMemo, useCallback } from "react";
import { useQuery } from "@tanstack/react-query";
import {
LayoutGrid,
@@ -100,7 +100,7 @@ function ShotCard({ shot, opts }: { shot: Shot; opts: DisplayOptions }) {
className="w-full h-full object-cover"
/>
) : (
<div className="flex items-center justify-center w-full h-full">
<div className="flex items-center justify-center w-full h-full sb-no-thumb">
<Film className="h-6 w-6 text-zinc-700 print:text-zinc-400" />
</div>
)}
@@ -182,7 +182,7 @@ function StandardLayout({
{group.label && (
<div className="sb-avoid-break mb-4 print:mb-3">
<div className="flex items-center gap-3">
<div className="flex-1 h-px bg-zinc-700 print:bg-zinc-300" />
<div className="flex-1 h-px bg-zinc-700 print:bg-zinc-300 sb-group-header-line" />
<span className="text-xs font-bold uppercase tracking-widest text-zinc-400 print:text-zinc-600 px-2">
{group.label}
</span>
@@ -239,7 +239,7 @@ function FullscreenLayout({
)}
>
{/* Full-bleed image */}
<div className="relative flex-1 bg-zinc-900 print:bg-black overflow-hidden">
<div className="relative flex-1 bg-zinc-900 print:bg-black overflow-hidden sb-fs-image-area">
{shot.thumbnailUrl ? (
// eslint-disable-next-line @next/next/no-img-element
<img
@@ -269,7 +269,7 @@ function FullscreenLayout({
</div>
{/* Info bar */}
<div className="bg-zinc-900 print:bg-zinc-950 border-t border-zinc-800 print:border-zinc-700 px-8 py-5 print:py-4 shrink-0">
<div className="bg-zinc-900 print:bg-zinc-950 border-t border-zinc-800 print:border-zinc-700 px-8 py-5 print:py-4 shrink-0 sb-fs-infobar">
<div className="flex items-start gap-8">
<div className="space-y-1 min-w-0 flex-1">
<div className="flex items-center gap-3 flex-wrap">
@@ -527,43 +527,33 @@ export function StoryboardGenerator({ projects }: Props) {
});
}, []);
// ── Print styles ────────────────────────────────────────────────────────────
const printStyles = useMemo(() => {
const isFullscreen = layout === "fullscreen";
return `
@media print {
.no-print { display: none !important; }
.print-area { display: block !important; }
body { background: ${isFullscreen ? "#000" : "#fff"} !important; margin: 0; }
@page {
size: ${isFullscreen ? "A4 landscape" : "A4 portrait"};
margin: ${isFullscreen ? "0" : "12mm 12mm 10mm 12mm"};
}
.sb-page-break { break-after: page; page-break-after: always; }
.sb-avoid-break { break-inside: avoid; page-break-inside: avoid; }
.sb-force-break { break-before: page; page-break-before: always; }
.print\\:bg-white { background: #fff !important; }
.print\\:bg-black { background: #000 !important; }
.print\\:text-black { color: #000 !important; }
.print\\:text-zinc-700 { color: #3f3f46 !important; }
.print\\:border-zinc-300 { border-color: #d4d4d8 !important; }
.print\\:h-screen { height: 100vh !important; }
.print\\:mb-0 { margin-bottom: 0 !important; }
.print\\:mt-0 { margin-top: 0 !important; }
.print\\:space-y-6 > * + * { margin-top: 1.5rem !important; }
.print\\:gap-2 { gap: 0.5rem !important; }
.print\\:py-4 { padding-top: 1rem !important; padding-bottom: 1rem !important; }
}
`;
}, [layout]);
// ── PDF URL (opens in new tab → auto-triggers browser print dialog) ─────────────────────
const pdfUrl = useMemo(() => {
const params = new URLSearchParams({
projectId,
layout,
columns: String(columns),
groupBy,
desc: includeDescription ? "1" : "0",
notes: includeNotes ? "1" : "0",
frameRange: includeFrameRange ? "1" : "0",
status: includeStatus ? "1" : "0",
version: includeVersion ? "1" : "0",
pageBreaks: pageBreakBetweenGroups ? "1" : "0",
onlyThumbs: showOnlyWithThumbnails ? "1" : "0",
});
if (selectedEpisodes.size > 0) params.set("episodes", [...selectedEpisodes].join(","));
if (selectedGroups.size > 0) params.set("groups", [...selectedGroups].join(","));
return `/api/storyboard/pdf?${params}`;
}, [
projectId, layout, columns, groupBy,
includeDescription, includeNotes, includeFrameRange, includeStatus, includeVersion,
pageBreakBetweenGroups, showOnlyWithThumbnails, selectedEpisodes, selectedGroups,
]);
// ─── Render ──────────────────────────────────────────────────────────────────
return (
<>
{/* Print styles injected dynamically */}
<style dangerouslySetInnerHTML={{ __html: printStyles }} />
<div className="flex h-full overflow-hidden bg-zinc-950">
<div className="flex h-full overflow-hidden bg-zinc-950">
{/* ── Controls sidebar ── */}
<div className="no-print w-64 shrink-0 border-r border-zinc-800 overflow-y-auto flex flex-col bg-zinc-900">
{/* Header */}
@@ -740,19 +730,24 @@ export function StoryboardGenerator({ projects }: Props) {
{/* Generate button */}
<div className="shrink-0 p-4 border-t border-zinc-800 space-y-2">
{layout === "fullscreen" && (
{layout === "fullscreen" && (
<p className="text-[10px] text-zinc-600 text-center leading-tight">
Tip: choose Landscape in the print dialog for best results
Tip: choose Landscape in the print dialog
</p>
)}
<Button
className="w-full bg-amber-600 hover:bg-amber-500 text-white font-semibold"
onClick={() => window.print()}
disabled={filteredShots.length === 0}
>
<Printer className="h-4 w-4 mr-2" />
Generate PDF
</Button>
{filteredShots.length > 0 ? (
<a href={pdfUrl} target="_blank" rel="noopener noreferrer" className="block">
<Button className="w-full bg-amber-600 hover:bg-amber-500 text-white font-semibold">
<Printer className="h-4 w-4 mr-2" />
Generate PDF
</Button>
</a>
) : (
<Button className="w-full bg-amber-600/40 text-white/40 font-semibold" disabled>
<Printer className="h-4 w-4 mr-2" />
Generate PDF
</Button>
)}
</div>
</div>
@@ -770,7 +765,7 @@ export function StoryboardGenerator({ projects }: Props) {
</div>
{/* Storyboard content */}
<div className="print-area p-6 print:p-0">
<div className={cn("print-area p-6 print:p-0", layout === "standard" ? "sb-standard-print" : "sb-fullscreen-print")}>
{isLoading ? (
<div className="flex items-center justify-center py-24 no-print">
<Loader2 className="h-8 w-8 animate-spin text-zinc-600" />
@@ -801,9 +796,8 @@ export function StoryboardGenerator({ projects }: Props) {
projectName={project?.name ?? ""}
/>
)}
</div>
</div>
</div>
</>
</div>
);
}