fix(studio): show ALL template inputs (bridge V2 content-elements → layers)
The studio parser required scene.layers; a template-created project's scene_data carries content-elements (scene.contents), so every scene parsed to null and the editor fell back to the default 2-layer title/subtitle scene. Now parseScene bridges contents → editable layers (Text→text, Media→image), so all of a scene's inputs appear (e.g. c1 → 6: 4 text + 2 media). Scene name/duration also read V2 fields. Remaining studio↔template epic (separate): edit→content-element→AE-render binding, real AE scene-preview thumbnails, and FIX-mode (hide add-scene). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -43,14 +43,75 @@ function parseLayer(value: unknown): Layer | null {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Bridge a V2 template/saved-project scene (content-element model, no canvas
|
||||
* geometry) into editable studio layers so every input shows in the editor.
|
||||
* Text/TextArea → text layer; Media/Image/Video/Audio → image layer. Laid out in a
|
||||
* column since AE templates carry no canvas coords. One-time on first load; after the
|
||||
* user edits + saves, the scene persists in the studio's own layer format.
|
||||
*/
|
||||
function layersFromContents(contents: unknown[]): Layer[] {
|
||||
const str = (v: unknown) => (typeof v === "string" ? v : "");
|
||||
const num = (v: unknown) => (typeof v === "number" ? v : undefined);
|
||||
const items = contents.filter(isRecord);
|
||||
items.sort(
|
||||
(a, b) =>
|
||||
(num(a.sort) ?? num(a.positionInContainer) ?? 0) -
|
||||
(num(b.sort) ?? num(b.positionInContainer) ?? 0)
|
||||
);
|
||||
return items.map((c, i) => {
|
||||
const type = str(c.type).toLowerCase();
|
||||
const isMedia = ["media", "image", "video", "audio", "voiceover"].includes(type);
|
||||
const key = str(c.key) || str(c.id) || `el${i}`;
|
||||
const value = str(c.value) || str(c.defaultValue) || str(c.default_value);
|
||||
return {
|
||||
id: `c-${key}`,
|
||||
type: isMedia ? "image" : "text",
|
||||
name: str(c.title) || key,
|
||||
x: 160,
|
||||
y: 160 + i * 150,
|
||||
width: 1600,
|
||||
height: isMedia ? 360 : 110,
|
||||
rotation: 0,
|
||||
opacity: 1,
|
||||
zIndex: i,
|
||||
props: isMedia
|
||||
? { src: value }
|
||||
: {
|
||||
text: value || (str(c.title) || key),
|
||||
fontSize: 48,
|
||||
fill: "#111827",
|
||||
fontFamily: "Inter, sans-serif",
|
||||
align: "center",
|
||||
},
|
||||
} as Layer;
|
||||
});
|
||||
}
|
||||
|
||||
function parseScene(value: unknown): Scene | null {
|
||||
if (!isRecord(value)) return null;
|
||||
if (typeof value.id !== "string" || typeof value.name !== "string") return null;
|
||||
if (!Array.isArray(value.layers)) return null;
|
||||
if (typeof value.id !== "string") return null;
|
||||
const name =
|
||||
typeof value.name === "string"
|
||||
? value.name
|
||||
: typeof value.title === "string"
|
||||
? value.title
|
||||
: typeof value.key === "string"
|
||||
? value.key
|
||||
: "Scene";
|
||||
|
||||
const layers = value.layers
|
||||
// Studio's own format carries `layers`; a freshly-copied V2 template carries
|
||||
// `contents` (content elements / inputs) instead — bridge those to layers.
|
||||
let layers: Layer[];
|
||||
if (Array.isArray(value.layers)) {
|
||||
layers = value.layers
|
||||
.map(parseLayer)
|
||||
.filter((layer): layer is Layer => layer !== null);
|
||||
} else if (Array.isArray(value.contents)) {
|
||||
layers = layersFromContents(value.contents);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
const transitionType =
|
||||
typeof value.transitionType === "string" &&
|
||||
@@ -60,9 +121,15 @@ function parseScene(value: unknown): Scene | null {
|
||||
|
||||
return {
|
||||
id: value.id,
|
||||
name: value.name,
|
||||
name,
|
||||
duration:
|
||||
typeof value.duration === "number" ? value.duration : DEFAULT_SCENE_DURATION,
|
||||
typeof value.duration === "number"
|
||||
? value.duration
|
||||
: typeof value.sceneLengthSec === "number"
|
||||
? value.sceneLengthSec
|
||||
: typeof value.defaultDurationSec === "number"
|
||||
? value.defaultDurationSec
|
||||
: DEFAULT_SCENE_DURATION,
|
||||
layers,
|
||||
transitionType,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user