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 {
|
function parseScene(value: unknown): Scene | null {
|
||||||
if (!isRecord(value)) return null;
|
if (!isRecord(value)) return null;
|
||||||
if (typeof value.id !== "string" || typeof value.name !== "string") return null;
|
if (typeof value.id !== "string") return null;
|
||||||
if (!Array.isArray(value.layers)) 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
|
||||||
.map(parseLayer)
|
// `contents` (content elements / inputs) instead — bridge those to layers.
|
||||||
.filter((layer): layer is Layer => layer !== null);
|
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 =
|
const transitionType =
|
||||||
typeof value.transitionType === "string" &&
|
typeof value.transitionType === "string" &&
|
||||||
@@ -60,9 +121,15 @@ function parseScene(value: unknown): Scene | null {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
id: value.id,
|
id: value.id,
|
||||||
name: value.name,
|
name,
|
||||||
duration:
|
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,
|
layers,
|
||||||
transitionType,
|
transitionType,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user