feat: daily timeline advanced UI and calendar integration

This commit is contained in:
2026-06-03 04:08:13 -05:00
parent e944594e06
commit ad8622e243
34 changed files with 2088 additions and 788 deletions
+23 -13
View File
@@ -122,6 +122,7 @@ export function compileExpressToTimeline(
designMD: DesignMD,
company?: CompanyProfile,
videoDurations?: Record<string, number>,
variationId?: string,
): { elements: TimelineElement[]; layers: TimelineLayer[] } {
const fps = 30;
const elements: TimelineElement[] = [];
@@ -278,14 +279,19 @@ export function compileExpressToTimeline(
});
}
// Process fields — prefer new TemplateField[] format over legacy editableFields
const fieldsToProcess = (scene.fields && scene.fields.length > 0)
? scene.fields
: null;
const activeVariation = variationId && scene.variations ? scene.variations.find(v => v.id === variationId) : null;
if (fieldsToProcess) {
// New TemplateField[] format: process ALL natures
for (const field of fieldsToProcess) {
const position = activeVariation && activeVariation.positions[field.id]
? { ...field.position, ...activeVariation.positions[field.id] }
: field.position;
let value: string;
if (field.nature === 'static') {
@@ -346,12 +352,12 @@ export function compileExpressToTimeline(
sourceFieldId: field.id,
type: elType,
content: field.type === 'sticker' ? compiledContent : (value || ''),
x: field.position.x,
y: field.position.y,
x: position.x,
y: position.y,
startFrame: sceneStart,
endFrame: sceneEnd,
scale: 1,
rotation: field.position.rotation || 0,
rotation: position.rotation || 0,
opacity: field.style.opacity ?? 100,
blendMode: field.style.blendMode,
layerId,
@@ -372,8 +378,8 @@ export function compileExpressToTimeline(
textAlign: (field.style.textAlign as 'left' | 'center' | 'right') || (field.type === 'sticker' ? 'left' : 'center'),
} : {}),
...(field.type === 'image' || field.type === 'video' ? {
width: field.position.w,
height: field.position.h,
width: position.w,
height: position.h,
objectFit: ((field.nature === 'brand-variable' && field.brandSource === 'intro-video')
? (designMD.introVideoFit || field.style.mediaFit || 'cover')
: (field.nature === 'brand-variable' && field.brandSource === 'outro-video')
@@ -386,8 +392,8 @@ export function compileExpressToTimeline(
: undefined,
} : {}),
...(field.type === 'shape' ? {
width: field.position.w,
height: field.position.h,
width: position.w,
height: position.h,
shapeType: field.style.shapeType || 'rectangle',
color: field.style.shapeFill || designMD.primaryColor,
} : {}),
@@ -397,6 +403,10 @@ export function compileExpressToTimeline(
} else {
// Legacy ExpressField[] format
for (const field of scene.editableFields) {
const position = activeVariation && activeVariation.positions[field.id]
? { ...field.position, ...activeVariation.positions[field.id] }
: field.position;
let value = resolveBrandValue(field, fieldData[field.id] || '', designMD, company, company?.brandContent);
// For media fields, placeholder text is not a valid URL — clear it to avoid crashing Remotion
const isLegacyMedia = field.type === 'media' || field.type === 'logo';
@@ -421,8 +431,8 @@ export function compileExpressToTimeline(
sourceFieldId: field.id,
type: elType,
content: value || '',
x: field.position.x,
y: field.position.y,
x: position.x,
y: position.y,
startFrame: sceneStart,
endFrame: sceneEnd,
scale: 1,
@@ -444,11 +454,11 @@ export function compileExpressToTimeline(
fontFamily: resolveFont(field, designMD),
color: resolveColor(field, designMD),
textAlign: (field.style.textAlign as 'left' | 'center' | 'right') || 'center',
width: field.position.w,
width: position.w,
} : {}),
...(field.type === 'media' || field.type === 'logo' ? {
width: field.position.w,
height: field.position.h,
width: position.w,
height: position.h,
objectFit: 'cover' as const,
} : {}),
transitionIn: scene.transition ? { type: scene.transition.type as TransitionType, duration: scene.transition.duration } : undefined,