fix(studio): add blocks at current playhead, extend root duration

Blocks and components now start at the current playhead position
instead of being appended after all existing content. If the new
element extends beyond the root composition's data-duration, the
root is automatically extended to fit.
This commit is contained in:
Miguel Ángel
2026-05-21 18:28:17 -04:00
parent 6f9b655fff
commit 67701df4e9
2 changed files with 29 additions and 13 deletions
+3
View File
@@ -193,6 +193,7 @@ export function StudioApp() {
blockName,
activeCompPath,
previewIframe: previewIframeRef.current,
currentTime: usePlayerStore.getState().currentTime,
timelineElements,
readProjectFile: fileManager.readProjectFile,
writeProjectFile: fileManager.writeProjectFile,
@@ -237,6 +238,7 @@ export function StudioApp() {
activeCompPath,
placement,
previewIframe: previewIframeRef.current,
currentTime: usePlayerStore.getState().currentTime,
timelineElements,
readProjectFile: fileManager.readProjectFile,
writeProjectFile: fileManager.writeProjectFile,
@@ -268,6 +270,7 @@ export function StudioApp() {
activeCompPath,
visualPosition: position,
previewIframe: previewIframeRef.current,
currentTime: usePlayerStore.getState().currentTime,
timelineElements,
readProjectFile: fileManager.readProjectFile,
writeProjectFile: fileManager.writeProjectFile,
+26 -13
View File
@@ -31,6 +31,7 @@ interface AddBlockOptions {
placement?: { start: number; track: number };
visualPosition?: { left: number; top: number };
previewIframe?: HTMLIFrameElement | null;
currentTime?: number;
timelineElements: TimelineElement[];
readProjectFile: (path: string) => Promise<string>;
writeProjectFile: (path: string, content: string) => Promise<void>;
@@ -120,20 +121,18 @@ export async function addBlockToProject(
const isBlock = block.type === "hyperframes:block";
const hostDims = resolveTimelineAssetInitialGeometry(originalContent);
const currentTime = opts.currentTime ?? 0;
const start = placement
? Number(formatTimelineAttributeNumber(placement.start))
: isBlock
? relevantElements.reduce(
(max, te) => Math.max(max, (te.start ?? 0) + (te.duration ?? 0)),
0,
)
: 0;
const duration = isBlock
? (block as { duration: number }).duration
: relevantElements.reduce(
(max, te) => Math.max(max, (te.start ?? 0) + (te.duration ?? 0)),
10,
);
: Number(formatTimelineAttributeNumber(currentTime));
const blockDuration =
"duration" in block ? (block as { duration: number }).duration : undefined;
const duration =
blockDuration ??
relevantElements.reduce(
(max, te) => Math.max(max, (te.start ?? 0) + (te.duration ?? 0)),
10,
);
const track =
placement?.track ??
(isBlock
@@ -163,7 +162,21 @@ export async function addBlockToProject(
`></div>`,
].join("\n");
const patchedContent = insertTimelineAssetIntoSource(originalContent, subCompHtml);
let patchedContent = insertTimelineAssetIntoSource(originalContent, subCompHtml);
const newEnd = start + duration;
const rootDurMatch = patchedContent.match(
/(<[^>]*data-composition-id="[^"]*"[^>]*data-duration=")([^"]*)(")/,
);
if (rootDurMatch) {
const rootDur = parseFloat(rootDurMatch[2]!);
if (newEnd > rootDur) {
patchedContent = patchedContent.replace(
rootDurMatch[0],
`${rootDurMatch[1]}${formatTimelineAttributeNumber(newEnd)}${rootDurMatch[3]}`,
);
}
}
await saveProjectFilesWithHistory({
projectId,