File size: 7,205 Bytes
fe029e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import { Plus, Sparkles, UserCircle, Clapperboard } from "lucide-react";
import type { ProjectModeData } from "../../types";

interface ProjectSidebarProps {
  data: ProjectModeData;
}

export function ProjectSidebar({ data }: ProjectSidebarProps) {
  const { project, scenes, shots, selectedSceneId, phase, onSelectScene, onAddScene } = data;

  if (scenes.length === 0) {
    return <OutlineSidebar data={data} />;
  }

  return (
    <div className="flex-1 min-h-0 flex flex-col">
      <div className="px-4 py-3 border-b border-gray-800/40 flex-shrink-0">
        <p className="text-[10px] uppercase tracking-wider text-gray-500 font-medium">Description</p>
        <p className="text-xs text-gray-300 mt-1 leading-relaxed line-clamp-3">
          {project.description || <span className="italic text-gray-600">No description yet.</span>}
        </p>
      </div>

      <div className="px-4 py-2.5 flex items-center justify-between flex-shrink-0">
        <span className="text-[11px] uppercase tracking-wider text-gray-500 font-medium">Scenes</span>
        <button
          onClick={onAddScene}
          title="Add scene"
          className="inline-flex items-center gap-1 rounded-lg border border-gray-800 hover:border-gray-700 hover:bg-gray-900/60 px-2 py-1 text-[10px] text-gray-400 hover:text-gray-200 transition"
        >
          <Plus className="w-3 h-3" /> Add scene
        </button>
      </div>

      <div className="flex-1 min-h-0 overflow-y-auto px-2 pb-3 space-y-1">
        {scenes.map((scene) => {
          const sceneShots = shots.filter((s) => s.scene_id === scene.id);
          const generated = sceneShots.filter((s) => s.image_file).length;
          const active = scene.id === selectedSceneId;
          return (
            <button
              key={scene.id}
              onClick={() => onSelectScene(scene.id)}
              className={`w-full text-left rounded-xl px-3 py-2.5 transition group ${active ? "bg-rose-600/10 ring-1 ring-rose-500/30" : "hover:bg-gray-900/50"}`}
            >
              <div className="flex items-center justify-between gap-2 mb-1">
                <div className="flex items-center gap-2 min-w-0">
                  <span className={`text-[10px] font-mono ${active ? "text-rose-300" : "text-gray-500"}`}>S{scene.scene_number}</span>
                  <span className={`text-xs font-medium truncate ${active ? "text-gray-100" : "text-gray-300"}`}>
                    {scene.heading || "Untitled scene"}
                  </span>
                </div>
              </div>
              {scene.summary && (
                <p className="text-[11px] text-gray-500 leading-relaxed line-clamp-2">{scene.summary}</p>
              )}
              <div className="flex items-center gap-2 mt-2 text-[10px] text-gray-600">
                <span>{sceneShots.length} shot{sceneShots.length === 1 ? "" : "s"}</span>
                {generated > 0 && (
                  <span className={phase === "remix" ? "text-violet-400/70" : "text-emerald-400/70"}>
                    · {generated} rendered
                  </span>
                )}
              </div>
            </button>
          );
        })}
      </div>
    </div>
  );
}

function OutlineSidebar({ data }: ProjectSidebarProps) {
  const { project, onAddScene } = data;
  return (
    <div className="flex-1 min-h-0 overflow-y-auto p-4 space-y-4">
      <section className="rounded-2xl border border-rose-500/20 bg-gradient-to-b from-rose-950/15 to-gray-900/20 p-4 space-y-2.5">
        <div className="flex items-center gap-2">
          <Sparkles className="w-3.5 h-3.5 text-rose-300" />
          <span className="text-[11px] uppercase tracking-wider text-rose-200 font-semibold">Start the outline</span>
        </div>
        <p className="text-xs text-gray-300 leading-relaxed">
          Pitch your project to your agent and it'll draft scenes and shots here. No scenes yet — once they appear, this sidebar becomes a scene switcher.
        </p>
        <div className="rounded-xl border border-gray-800/60 bg-black/30 p-3 space-y-1.5">
          <p className="text-[10px] uppercase tracking-wider text-gray-500">Try saying</p>
          <p className="text-[11px] text-gray-300 italic leading-relaxed">"Make me a 30s cyberpunk teaser, my character walking through neon rain."</p>
          <p className="text-[11px] text-gray-300 italic leading-relaxed">"Put me in an Iron Man movie. Workshop, suit assembly, rooftop in the rain."</p>
        </div>
      </section>

      <section className="space-y-2.5">
        <p className="text-[10px] uppercase tracking-wider text-gray-500 font-medium">Project</p>
        <div className="rounded-xl border border-gray-800/60 bg-gray-900/30 p-3 space-y-2">
          <div>
            <p className="text-[10px] uppercase tracking-wider text-gray-600">Title</p>
            <p className="text-sm text-gray-100 mt-0.5">{project.title}</p>
          </div>
          {project.description && (
            <div>
              <p className="text-[10px] uppercase tracking-wider text-gray-600">Description</p>
              <p className="text-xs text-gray-400 mt-0.5 leading-relaxed">{project.description}</p>
            </div>
          )}
          <div className="grid grid-cols-2 gap-2 pt-1">
            <div>
              <p className="text-[10px] uppercase tracking-wider text-gray-600">Aspect</p>
              <p className="text-xs text-gray-300 mt-0.5 font-mono">{project.aspect_ratio}</p>
            </div>
            <div>
              <p className="text-[10px] uppercase tracking-wider text-gray-600">Duration</p>
              <p className="text-xs text-gray-300 mt-0.5">{project.duration_seconds ?? "—"}s</p>
            </div>
          </div>
          {project.characters.length > 0 && (
            <div className="pt-1">
              <p className="text-[10px] uppercase tracking-wider text-gray-600 mb-1.5">Cast</p>
              <div className="flex flex-wrap gap-1">
                {project.characters.map((id) => (
                  <span key={id} className="inline-flex items-center gap-1 rounded-md border border-gray-800 bg-gray-900/40 px-2 py-0.5 text-[11px] text-gray-300 font-mono">
                    <UserCircle className="w-3 h-3 text-gray-500" /> {id}
                  </span>
                ))}
              </div>
            </div>
          )}
        </div>
      </section>

      <button
        onClick={onAddScene}
        className="w-full inline-flex items-center justify-center gap-2 rounded-xl border border-gray-700 bg-gray-900/40 hover:bg-gray-900 hover:border-gray-600 px-3 py-2.5 text-xs text-gray-300 transition"
      >
        <Plus className="w-3.5 h-3.5" /> Add scene manually
      </button>

      <div className="rounded-xl border border-gray-800/40 bg-gray-900/20 p-3 flex items-start gap-2.5">
        <Clapperboard className="w-3.5 h-3.5 text-gray-500 mt-0.5 flex-shrink-0" />
        <p className="text-[11px] text-gray-500 leading-relaxed">
          Scenes hold the story beats. Shots inside scenes get rendered into images, then animated. Outline first; only generate after the structure is approved.
        </p>
      </div>
    </div>
  );
}