ugcinc 4.5.43 → 4.5.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/automations/nodes/create-dm.d.ts +1 -0
- package/dist/automations/nodes/create-dm.js +2 -0
- package/dist/automations/nodes/index.d.ts +1 -0
- package/dist/render/Root.js +70 -1
- package/dist/render/compositions/DmComposition/BaseDmComposition.d.ts +4 -1
- package/dist/render/compositions/DmComposition/BaseDmComposition.js +129 -117
- package/dist/render/compositions/IMessageDmComposition/index.js +17 -1
- package/dist/render/compositions/IMessageDmComposition/types.d.ts +6 -0
- package/dist/render/compositions/IMessageDmComposition/types.js +2 -0
- package/dist/render/compositions/InstagramDmComposition/convertPublicToProps.js +2 -0
- package/dist/render/compositions/InstagramDmComposition/index.js +12 -1
- package/dist/render/compositions/InstagramDmComposition/types.d.ts +3 -0
- package/dist/render/types/instagram-dm-public.d.ts +3 -0
- package/dist/render.d.ts +4 -0
- package/dist/render.js +2 -0
- package/package.json +1 -1
|
@@ -48,6 +48,7 @@ declare const definition: import("./types").NodeDefinition<"create-dm", "generat
|
|
|
48
48
|
imessageMessageHeaderTimestampText: string;
|
|
49
49
|
imessageMessageHeaderTimeInputType: InputType;
|
|
50
50
|
instagramUserHasStory: boolean;
|
|
51
|
+
dynamicCrop: import("../../render/types/crop").DynamicCropConfig | undefined;
|
|
51
52
|
outputMode: OutputMode;
|
|
52
53
|
selectionMode: SelectionMode | null;
|
|
53
54
|
}, CreateDmNodeInputs, CreateDmNodeOutputs, false>;
|
|
@@ -42,6 +42,8 @@ const definition = (0, types_1.defineNode)({
|
|
|
42
42
|
imessageMessageHeaderTimeInputType: 'static',
|
|
43
43
|
// Instagram-specific
|
|
44
44
|
instagramUserHasStory: false,
|
|
45
|
+
// Dynamic crop - crop output to DM element bounds
|
|
46
|
+
dynamicCrop: undefined,
|
|
45
47
|
outputMode: 'per-input',
|
|
46
48
|
selectionMode: null,
|
|
47
49
|
},
|
|
@@ -110,6 +110,7 @@ export declare const nodeDefinitions: {
|
|
|
110
110
|
imessageMessageHeaderTimestampText: string;
|
|
111
111
|
imessageMessageHeaderTimeInputType: import("./types").InputType;
|
|
112
112
|
instagramUserHasStory: boolean;
|
|
113
|
+
dynamicCrop: import("../..").DynamicCropConfig | undefined;
|
|
113
114
|
outputMode: import("./types").OutputMode;
|
|
114
115
|
selectionMode: import("./types").SelectionMode | null;
|
|
115
116
|
}, import("./create-dm").CreateDmNodeInputs, import("./create-dm").CreateDmNodeOutputs, false> & {
|
package/dist/render/Root.js
CHANGED
|
@@ -9,8 +9,10 @@ const VideoEditorComposition_1 = require("./compositions/VideoEditorComposition"
|
|
|
9
9
|
const AutoCaptionComposition_1 = require("./compositions/AutoCaptionComposition");
|
|
10
10
|
const ScreenshotAnimation_1 = require("./compositions/ScreenshotAnimation");
|
|
11
11
|
const InstagramDmComposition_1 = require("./compositions/InstagramDmComposition");
|
|
12
|
+
const convertPropsToElements_1 = require("./compositions/InstagramDmComposition/convertPropsToElements");
|
|
12
13
|
const IMessageDmComposition_1 = require("./compositions/IMessageDmComposition");
|
|
13
14
|
const types_1 = require("./compositions/IMessageDmComposition/types");
|
|
15
|
+
const convertPropsToElements_2 = require("./compositions/IMessageDmComposition/convertPropsToElements");
|
|
14
16
|
const positionResolver_1 = require("./utils/positionResolver");
|
|
15
17
|
const cropBounds_1 = require("./utils/cropBounds");
|
|
16
18
|
const fonts_1 = require("./utils/fonts");
|
|
@@ -427,6 +429,8 @@ const instagramDmSchema = zod_1.z.object({
|
|
|
427
429
|
// Divider line
|
|
428
430
|
dividerLineY: zod_1.z.number().optional().default(353).describe('Divider line Y position'),
|
|
429
431
|
dividerLineColor: zod_1.z.string().optional().default('#25282d').describe('Divider line color'),
|
|
432
|
+
// Dynamic crop - crop output to element bounds
|
|
433
|
+
dynamicCrop: zod_1.z.record(zod_1.z.any()).optional().describe('Dynamic crop configuration'),
|
|
430
434
|
});
|
|
431
435
|
// Cast components to work around renderer's strict typing
|
|
432
436
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -501,8 +505,73 @@ const calculateImageMetadata = async ({ props }) => {
|
|
|
501
505
|
durationInFrames: 1,
|
|
502
506
|
};
|
|
503
507
|
};
|
|
508
|
+
/**
|
|
509
|
+
* calculateMetadata for InstagramDmComposition
|
|
510
|
+
* Computes output dimensions when dynamic crop is enabled.
|
|
511
|
+
*/
|
|
512
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
513
|
+
const calculateInstagramDmMetadata = async ({ props }) => {
|
|
514
|
+
const canvasWidth = props.width ?? 1206;
|
|
515
|
+
const canvasHeight = props.height ?? 2622;
|
|
516
|
+
const defaultResult = { width: canvasWidth, height: canvasHeight, fps: 30, durationInFrames: 90 };
|
|
517
|
+
if (!(0, cropBounds_1.isDynamicCropEnabled)(props.dynamicCrop)) {
|
|
518
|
+
return defaultResult;
|
|
519
|
+
}
|
|
520
|
+
try {
|
|
521
|
+
await (0, fonts_1.preloadFonts)();
|
|
522
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
523
|
+
}
|
|
524
|
+
catch (err) {
|
|
525
|
+
console.error('[calculateInstagramDmMetadata] Font loading failed:', err);
|
|
526
|
+
return defaultResult;
|
|
527
|
+
}
|
|
528
|
+
const { elements } = (0, convertPropsToElements_1.convertPropsToElements)(props);
|
|
529
|
+
const { elements: resolved } = (0, positionResolver_1.resolveElementPositions)(elements, {});
|
|
530
|
+
if (!resolved)
|
|
531
|
+
return defaultResult;
|
|
532
|
+
const cropBounds = (0, cropBounds_1.calculateCropBounds)(resolved, props.dynamicCrop, canvasWidth, canvasHeight);
|
|
533
|
+
return {
|
|
534
|
+
width: Math.round(cropBounds.width),
|
|
535
|
+
height: Math.round(cropBounds.height),
|
|
536
|
+
fps: 30,
|
|
537
|
+
durationInFrames: 90,
|
|
538
|
+
};
|
|
539
|
+
};
|
|
540
|
+
/**
|
|
541
|
+
* calculateMetadata for IMessageDmComposition
|
|
542
|
+
* Computes output dimensions when dynamic crop is enabled.
|
|
543
|
+
*/
|
|
544
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
545
|
+
const calculateIMessageDmMetadata = async ({ props }) => {
|
|
546
|
+
const canvasWidth = props.width ?? 1206;
|
|
547
|
+
const canvasHeight = props.height ?? 2622;
|
|
548
|
+
const defaultResult = { width: canvasWidth, height: canvasHeight, fps: 30, durationInFrames: 90 };
|
|
549
|
+
if (!(0, cropBounds_1.isDynamicCropEnabled)(props.dynamicCrop)) {
|
|
550
|
+
return defaultResult;
|
|
551
|
+
}
|
|
552
|
+
try {
|
|
553
|
+
await (0, fonts_1.preloadFonts)();
|
|
554
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
555
|
+
}
|
|
556
|
+
catch (err) {
|
|
557
|
+
console.error('[calculateIMessageDmMetadata] Font loading failed:', err);
|
|
558
|
+
return defaultResult;
|
|
559
|
+
}
|
|
560
|
+
const mergedProps = { ...IMessageDmComposition_1.defaultIMessageDmProps, ...props };
|
|
561
|
+
const { elements } = (0, convertPropsToElements_2.convertPropsToElements)(mergedProps);
|
|
562
|
+
const { elements: resolved } = (0, positionResolver_1.resolveElementPositions)(elements, {});
|
|
563
|
+
if (!resolved)
|
|
564
|
+
return defaultResult;
|
|
565
|
+
const cropBounds = (0, cropBounds_1.calculateCropBounds)(resolved, props.dynamicCrop, canvasWidth, canvasHeight);
|
|
566
|
+
return {
|
|
567
|
+
width: Math.round(cropBounds.width),
|
|
568
|
+
height: Math.round(cropBounds.height),
|
|
569
|
+
fps: 30,
|
|
570
|
+
durationInFrames: 90,
|
|
571
|
+
};
|
|
572
|
+
};
|
|
504
573
|
const RenderRoot = () => {
|
|
505
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(remotion_1.Composition, { id: "ImageEditorComposition", component: ImageComp, calculateMetadata: calculateImageMetadata, durationInFrames: 1, fps: 30, width: 1080, height: 1920, defaultProps: defaultImageProps }), (0, jsx_runtime_1.jsx)(remotion_1.Composition, { id: "VideoEditorComposition", component: VideoComp, durationInFrames: 150, fps: 30, width: 1080, height: 1920, defaultProps: defaultVideoProps }), (0, jsx_runtime_1.jsx)(remotion_1.Composition, { id: "AutoCaptionComposition", component: AutoCaptionComp, durationInFrames: 300, fps: 30, width: 1080, height: 1920, defaultProps: defaultAutoCaptionProps }), (0, jsx_runtime_1.jsx)(remotion_1.Composition, { id: "ScreenshotAnimationComposition", component: ScreenshotAnimationComp, schema: screenshotAnimationSchema, durationInFrames: 180, fps: 59, width: 1206, height: 2622, defaultProps: defaultScreenshotAnimationProps }), (0, jsx_runtime_1.jsx)(remotion_1.Composition, { id: "InstagramDmComposition", component: InstagramDmComp, schema: instagramDmSchema, durationInFrames: 90, fps: 30, width: 1206, height: 2622, defaultProps: InstagramDmComposition_1.defaultInstagramDmProps }), (0, jsx_runtime_1.jsx)(remotion_1.Composition, { id: "IMessageDmComposition", component: IMessageDmComp, schema: types_1.iMessageDmPropsSchema, durationInFrames: 90, fps: 30, width: 1206, height: 2622, defaultProps: IMessageDmComposition_1.defaultIMessageDmProps })] }));
|
|
574
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(remotion_1.Composition, { id: "ImageEditorComposition", component: ImageComp, calculateMetadata: calculateImageMetadata, durationInFrames: 1, fps: 30, width: 1080, height: 1920, defaultProps: defaultImageProps }), (0, jsx_runtime_1.jsx)(remotion_1.Composition, { id: "VideoEditorComposition", component: VideoComp, durationInFrames: 150, fps: 30, width: 1080, height: 1920, defaultProps: defaultVideoProps }), (0, jsx_runtime_1.jsx)(remotion_1.Composition, { id: "AutoCaptionComposition", component: AutoCaptionComp, durationInFrames: 300, fps: 30, width: 1080, height: 1920, defaultProps: defaultAutoCaptionProps }), (0, jsx_runtime_1.jsx)(remotion_1.Composition, { id: "ScreenshotAnimationComposition", component: ScreenshotAnimationComp, schema: screenshotAnimationSchema, durationInFrames: 180, fps: 59, width: 1206, height: 2622, defaultProps: defaultScreenshotAnimationProps }), (0, jsx_runtime_1.jsx)(remotion_1.Composition, { id: "InstagramDmComposition", component: InstagramDmComp, calculateMetadata: calculateInstagramDmMetadata, schema: instagramDmSchema, durationInFrames: 90, fps: 30, width: 1206, height: 2622, defaultProps: InstagramDmComposition_1.defaultInstagramDmProps }), (0, jsx_runtime_1.jsx)(remotion_1.Composition, { id: "IMessageDmComposition", component: IMessageDmComp, calculateMetadata: calculateIMessageDmMetadata, schema: types_1.iMessageDmPropsSchema, durationInFrames: 90, fps: 30, width: 1206, height: 2622, defaultProps: IMessageDmComposition_1.defaultIMessageDmProps })] }));
|
|
506
575
|
};
|
|
507
576
|
exports.RenderRoot = RenderRoot;
|
|
508
577
|
exports.default = exports.RenderRoot;
|
|
@@ -6,11 +6,14 @@
|
|
|
6
6
|
* font loading, and shared iOS chrome (status bar, home indicator).
|
|
7
7
|
*/
|
|
8
8
|
import React from 'react';
|
|
9
|
+
import type { CropBounds } from '../../types/crop';
|
|
9
10
|
export interface BaseDmCompositionProps {
|
|
10
11
|
/** Canvas width */
|
|
11
12
|
width: number;
|
|
12
13
|
/** Canvas height */
|
|
13
14
|
height: number;
|
|
15
|
+
/** When set, crop output to these bounds (for dynamic crop) */
|
|
16
|
+
cropBounds?: CropBounds;
|
|
14
17
|
/** Background color */
|
|
15
18
|
backgroundColor: string;
|
|
16
19
|
/** Show debug overlay with crosshair and coordinates */
|
|
@@ -108,5 +111,5 @@ export interface BaseDmCompositionProps {
|
|
|
108
111
|
/** Status bar elements color (time, cell bars, icons) - defaults to white */
|
|
109
112
|
statusBarColor?: string;
|
|
110
113
|
}
|
|
111
|
-
export declare function BaseDmComposition({ width, height, backgroundColor, showDebugOverlay, referenceImageUrl, showReferenceImage, referenceOpacity, currentProps, children, time, timeRight, timeBottom, timeFontSize, timeLetterSpacing, timeColor, noNotisTop, noNotisBottom, noNotisLeft, noNotisRight, cellLevel, cell1Top, cell1Bottom, cell1Left, cell1Right, cell2Top, cell2Bottom, cell2Left, cell2Right, cell3Top, cell3Bottom, cell3Left, cell3Right, cell4Top, cell4Bottom, cell4Left, cell4Right, wifiTop, wifiBottom, wifiLeft, wifiRight, batteryTop, batteryBottom, batteryLeft, batteryRight, homeIndicatorTop, homeIndicatorBottom, homeIndicatorLeft, homeIndicatorRight, homeIndicatorColor, statusBarColor, }: BaseDmCompositionProps): import("react/jsx-runtime").JSX.Element | null;
|
|
114
|
+
export declare function BaseDmComposition({ width, height, cropBounds, backgroundColor, showDebugOverlay, referenceImageUrl, showReferenceImage, referenceOpacity, currentProps, children, time, timeRight, timeBottom, timeFontSize, timeLetterSpacing, timeColor, noNotisTop, noNotisBottom, noNotisLeft, noNotisRight, cellLevel, cell1Top, cell1Bottom, cell1Left, cell1Right, cell2Top, cell2Bottom, cell2Left, cell2Right, cell3Top, cell3Bottom, cell3Left, cell3Right, cell4Top, cell4Bottom, cell4Left, cell4Right, wifiTop, wifiBottom, wifiLeft, wifiRight, batteryTop, batteryBottom, batteryLeft, batteryRight, homeIndicatorTop, homeIndicatorBottom, homeIndicatorLeft, homeIndicatorRight, homeIndicatorColor, statusBarColor, }: BaseDmCompositionProps): import("react/jsx-runtime").JSX.Element | null;
|
|
112
115
|
export default BaseDmComposition;
|
|
@@ -13,7 +13,7 @@ const react_1 = require("react");
|
|
|
13
13
|
const remotion_1 = require("remotion");
|
|
14
14
|
const fonts_1 = require("../../utils/fonts");
|
|
15
15
|
const DebugOverlay_1 = require("./DebugOverlay");
|
|
16
|
-
function BaseDmComposition({ width, height, backgroundColor, showDebugOverlay = false, referenceImageUrl, showReferenceImage = false, referenceOpacity = 50, currentProps, children,
|
|
16
|
+
function BaseDmComposition({ width, height, cropBounds, backgroundColor, showDebugOverlay = false, referenceImageUrl, showReferenceImage = false, referenceOpacity = 50, currentProps, children,
|
|
17
17
|
// Status bar - Time (defaults from IG DM)
|
|
18
18
|
time = '9:41', timeRight = 262, timeBottom = 125, timeFontSize = 52, timeLetterSpacing = 1.6, timeColor = '#ffffff',
|
|
19
19
|
// Status bar - No notifications
|
|
@@ -75,6 +75,11 @@ statusBarColor = '#ffffff', }) {
|
|
|
75
75
|
setReferenceVisible((prev) => !prev);
|
|
76
76
|
}, []);
|
|
77
77
|
const zoomScale = zoom / 100;
|
|
78
|
+
// Crop offset when dynamic crop is applied
|
|
79
|
+
const cropOffsetX = cropBounds?.x ?? 0;
|
|
80
|
+
const cropOffsetY = cropBounds?.y ?? 0;
|
|
81
|
+
const contentWidth = cropBounds ? cropBounds.width : width;
|
|
82
|
+
const contentHeight = cropBounds ? cropBounds.height : height;
|
|
78
83
|
// Determine if we need to invert icons for dark status bar color
|
|
79
84
|
// If statusBarColor is black or very dark, invert the white icons
|
|
80
85
|
const isStatusBarDark = statusBarColor === '#000000' || statusBarColor === '#000' || statusBarColor.toLowerCase() === 'black';
|
|
@@ -89,124 +94,131 @@ statusBarColor = '#ffffff', }) {
|
|
|
89
94
|
return ((0, jsx_runtime_1.jsxs)(remotion_1.AbsoluteFill, { style: {
|
|
90
95
|
backgroundColor,
|
|
91
96
|
overflow: 'hidden',
|
|
92
|
-
}, children: [(0, jsx_runtime_1.
|
|
97
|
+
}, children: [(0, jsx_runtime_1.jsx)("div", { style: {
|
|
93
98
|
position: 'absolute',
|
|
94
99
|
top: 0,
|
|
95
100
|
left: 0,
|
|
96
|
-
width:
|
|
97
|
-
height:
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
101
|
+
width: contentWidth,
|
|
102
|
+
height: contentHeight,
|
|
103
|
+
overflow: 'hidden',
|
|
104
|
+
}, children: (0, jsx_runtime_1.jsxs)("div", { style: {
|
|
105
|
+
position: 'absolute',
|
|
106
|
+
top: -cropOffsetY,
|
|
107
|
+
left: -cropOffsetX,
|
|
108
|
+
width: width,
|
|
109
|
+
height: height,
|
|
110
|
+
transform: `translate(${-scrollX}px, ${-scrollY}px) scale(${zoomScale})`,
|
|
111
|
+
transformOrigin: 'top left',
|
|
112
|
+
}, children: [children, (0, jsx_runtime_1.jsx)("div", { style: {
|
|
113
|
+
position: 'absolute',
|
|
114
|
+
top: timeBottom - timeFontSize,
|
|
115
|
+
left: 0,
|
|
116
|
+
width: timeRight,
|
|
117
|
+
height: timeFontSize,
|
|
118
|
+
display: 'flex',
|
|
119
|
+
alignItems: 'center',
|
|
120
|
+
justifyContent: 'flex-end',
|
|
121
|
+
fontFamily: '"SF Pro", "SF Pro Display", -apple-system, BlinkMacSystemFont, sans-serif',
|
|
122
|
+
fontWeight: 600,
|
|
123
|
+
fontSize: timeFontSize,
|
|
124
|
+
letterSpacing: timeLetterSpacing,
|
|
125
|
+
color: statusBarColor,
|
|
126
|
+
zIndex: 30,
|
|
127
|
+
pointerEvents: 'none',
|
|
128
|
+
}, children: time }), (0, jsx_runtime_1.jsx)(remotion_1.Img, { src: (0, remotion_1.staticFile)('ig-no-notis.png'), style: {
|
|
129
|
+
position: 'absolute',
|
|
130
|
+
top: noNotisTop,
|
|
131
|
+
left: noNotisLeft,
|
|
132
|
+
width: noNotisRight - noNotisLeft,
|
|
133
|
+
height: noNotisBottom - noNotisTop,
|
|
134
|
+
objectFit: 'fill',
|
|
135
|
+
pointerEvents: 'none',
|
|
136
|
+
zIndex: 50,
|
|
137
|
+
filter: iconFilter,
|
|
138
|
+
} }), (0, jsx_runtime_1.jsx)("div", { style: {
|
|
139
|
+
position: 'absolute',
|
|
140
|
+
top: cell1Top,
|
|
141
|
+
left: cell1Left,
|
|
142
|
+
width: cell1Right - cell1Left,
|
|
143
|
+
height: cell1Bottom - cell1Top,
|
|
144
|
+
backgroundColor: cellLevel >= 1 ? cellBarActiveColor : cellBarInactiveColor,
|
|
145
|
+
borderRadius: 2,
|
|
146
|
+
pointerEvents: 'none',
|
|
147
|
+
zIndex: 50,
|
|
148
|
+
} }), (0, jsx_runtime_1.jsx)("div", { style: {
|
|
149
|
+
position: 'absolute',
|
|
150
|
+
top: cell2Top,
|
|
151
|
+
left: cell2Left,
|
|
152
|
+
width: cell2Right - cell2Left,
|
|
153
|
+
height: cell2Bottom - cell2Top,
|
|
154
|
+
backgroundColor: cellLevel >= 2 ? cellBarActiveColor : cellBarInactiveColor,
|
|
155
|
+
borderRadius: 2,
|
|
156
|
+
pointerEvents: 'none',
|
|
157
|
+
zIndex: 50,
|
|
158
|
+
} }), (0, jsx_runtime_1.jsx)("div", { style: {
|
|
159
|
+
position: 'absolute',
|
|
160
|
+
top: cell3Top,
|
|
161
|
+
left: cell3Left,
|
|
162
|
+
width: cell3Right - cell3Left,
|
|
163
|
+
height: cell3Bottom - cell3Top,
|
|
164
|
+
backgroundColor: cellLevel >= 3 ? cellBarActiveColor : cellBarInactiveColor,
|
|
165
|
+
borderRadius: 2,
|
|
166
|
+
pointerEvents: 'none',
|
|
167
|
+
zIndex: 50,
|
|
168
|
+
} }), (0, jsx_runtime_1.jsx)("div", { style: {
|
|
169
|
+
position: 'absolute',
|
|
170
|
+
top: cell4Top,
|
|
171
|
+
left: cell4Left,
|
|
172
|
+
width: cell4Right - cell4Left,
|
|
173
|
+
height: cell4Bottom - cell4Top,
|
|
174
|
+
backgroundColor: cellLevel >= 4 ? cellBarActiveColor : cellBarInactiveColor,
|
|
175
|
+
borderRadius: 2,
|
|
176
|
+
pointerEvents: 'none',
|
|
177
|
+
zIndex: 50,
|
|
178
|
+
} }), (0, jsx_runtime_1.jsx)(remotion_1.Img, { src: (0, remotion_1.staticFile)('ig-wifi.png'), style: {
|
|
179
|
+
position: 'absolute',
|
|
180
|
+
top: wifiTop,
|
|
181
|
+
left: wifiLeft,
|
|
182
|
+
width: wifiRight - wifiLeft,
|
|
183
|
+
height: wifiBottom - wifiTop,
|
|
184
|
+
objectFit: 'fill',
|
|
185
|
+
pointerEvents: 'none',
|
|
186
|
+
zIndex: 50,
|
|
187
|
+
filter: iconFilter,
|
|
188
|
+
} }), (0, jsx_runtime_1.jsx)(remotion_1.Img, { src: (0, remotion_1.staticFile)('ig-battery.png'), style: {
|
|
189
|
+
position: 'absolute',
|
|
190
|
+
top: batteryTop,
|
|
191
|
+
left: batteryLeft,
|
|
192
|
+
width: batteryRight - batteryLeft,
|
|
193
|
+
height: batteryBottom - batteryTop,
|
|
194
|
+
objectFit: 'fill',
|
|
195
|
+
pointerEvents: 'none',
|
|
196
|
+
zIndex: 50,
|
|
197
|
+
filter: iconFilter,
|
|
198
|
+
} }), (0, jsx_runtime_1.jsx)("div", { style: {
|
|
199
|
+
position: 'absolute',
|
|
200
|
+
top: homeIndicatorTop,
|
|
201
|
+
left: homeIndicatorLeft,
|
|
202
|
+
width: homeIndicatorRight - homeIndicatorLeft,
|
|
203
|
+
height: homeIndicatorBottom - homeIndicatorTop,
|
|
204
|
+
backgroundColor: homeIndicatorColor,
|
|
205
|
+
borderRadius: (homeIndicatorBottom - homeIndicatorTop) / 2,
|
|
206
|
+
pointerEvents: 'none',
|
|
207
|
+
zIndex: 50,
|
|
208
|
+
} }), referenceImageUrl && showReferenceImage && (!showDebugOverlay || referenceVisible) && ((0, jsx_runtime_1.jsx)("div", { style: {
|
|
209
|
+
position: 'absolute',
|
|
210
|
+
top: 0,
|
|
211
|
+
left: 0,
|
|
212
|
+
right: 0,
|
|
213
|
+
bottom: 0,
|
|
214
|
+
zIndex: 100,
|
|
215
|
+
pointerEvents: 'none',
|
|
216
|
+
opacity: clampedReferenceOpacity / 100,
|
|
217
|
+
}, children: (0, jsx_runtime_1.jsx)(remotion_1.Img, { src: referenceImageUrl, style: {
|
|
218
|
+
width: '100%',
|
|
219
|
+
height: '100%',
|
|
220
|
+
objectFit: 'cover',
|
|
221
|
+
imageRendering: 'pixelated',
|
|
222
|
+
} }) }))] }) }), showDebugOverlay && ((0, jsx_runtime_1.jsx)(DebugOverlay_1.DebugOverlay, { width: contentWidth, height: contentHeight, zoom: zoom, scrollX: scrollX, scrollY: scrollY, referenceVisible: referenceVisible, onZoomChange: handleZoomChange, onToggleReference: handleToggleReference, currentProps: currentProps }))] }));
|
|
211
223
|
}
|
|
212
224
|
exports.default = BaseDmComposition;
|
|
@@ -15,6 +15,8 @@ const ImageEditorComposition_1 = require("../ImageEditorComposition");
|
|
|
15
15
|
const DmComposition_1 = require("../DmComposition");
|
|
16
16
|
const convertPropsToElements_1 = require("./convertPropsToElements");
|
|
17
17
|
const fonts_1 = require("../../utils/fonts");
|
|
18
|
+
const positionResolver_1 = require("../../utils/positionResolver");
|
|
19
|
+
const cropBounds_1 = require("../../utils/cropBounds");
|
|
18
20
|
/**
|
|
19
21
|
* Default composition props for registration
|
|
20
22
|
*/
|
|
@@ -286,6 +288,20 @@ const IMessageDmComposition = (props) => {
|
|
|
286
288
|
};
|
|
287
289
|
return (0, convertPropsToElements_1.convertPropsToElements)(propsWithEffectiveColors);
|
|
288
290
|
}, [mergedProps, fontsLoaded]);
|
|
291
|
+
// Resolve element positions and compute crop bounds when dynamic crop enabled
|
|
292
|
+
const cropBounds = (0, react_1.useMemo)(() => {
|
|
293
|
+
const elements = conversionResult.elements;
|
|
294
|
+
if (!elements.length || !(0, cropBounds_1.isDynamicCropEnabled)(mergedProps.dynamicCrop)) {
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
const result = (0, positionResolver_1.resolveElementPositions)(elements, {});
|
|
298
|
+
const resolved = result.elements;
|
|
299
|
+
if (!resolved)
|
|
300
|
+
return undefined;
|
|
301
|
+
const w = mergedProps.width ?? 1206;
|
|
302
|
+
const h = mergedProps.height ?? 2622;
|
|
303
|
+
return (0, cropBounds_1.calculateCropBounds)(resolved, mergedProps.dynamicCrop, w, h);
|
|
304
|
+
}, [conversionResult.elements, mergedProps.dynamicCrop, mergedProps.width, mergedProps.height]);
|
|
289
305
|
// Extract values with defaults
|
|
290
306
|
const { width = 1206, height = 2622, backgroundColor = '#000000', headerBackgroundColor = '#171717', headerBottom = 424, lightMode = false,
|
|
291
307
|
// Profile section
|
|
@@ -318,7 +334,7 @@ const IMessageDmComposition = (props) => {
|
|
|
318
334
|
const effectiveUsernameColor = lightMode ? '#000000' : usernameColor;
|
|
319
335
|
const effectiveHomeIndicatorColor = lightMode ? '#000000' : homeIndicatorColor;
|
|
320
336
|
const effectiveStatusBarColor = lightMode ? '#000000' : '#ffffff';
|
|
321
|
-
return ((0, jsx_runtime_1.jsxs)(DmComposition_1.BaseDmComposition, { width: width, height: height, backgroundColor: effectiveBackgroundColor, showDebugOverlay: showDebugOverlay, referenceImageUrl: referenceImageUrl, showReferenceImage: showReferenceImage, referenceOpacity: referenceOpacity, currentProps: { ...mergedProps },
|
|
337
|
+
return ((0, jsx_runtime_1.jsxs)(DmComposition_1.BaseDmComposition, { width: width, height: height, cropBounds: cropBounds, backgroundColor: effectiveBackgroundColor, showDebugOverlay: showDebugOverlay, referenceImageUrl: referenceImageUrl, showReferenceImage: showReferenceImage, referenceOpacity: referenceOpacity, currentProps: { ...mergedProps },
|
|
322
338
|
// Status bar props
|
|
323
339
|
time: time, timeRight: timeRight, timeBottom: timeBottom, timeFontSize: timeFontSize, timeLetterSpacing: timeLetterSpacing, noNotisTop: noNotisTop, noNotisBottom: noNotisBottom, noNotisLeft: noNotisLeft, noNotisRight: noNotisRight, cellLevel: cellLevel, cell1Top: cell1Top, cell1Bottom: cell1Bottom, cell1Left: cell1Left, cell1Right: cell1Right, cell2Top: cell2Top, cell2Bottom: cell2Bottom, cell2Left: cell2Left, cell2Right: cell2Right, cell3Top: cell3Top, cell3Bottom: cell3Bottom, cell3Left: cell3Left, cell3Right: cell3Right, cell4Top: cell4Top, cell4Bottom: cell4Bottom, cell4Left: cell4Left, cell4Right: cell4Right, wifiTop: wifiTop, wifiBottom: wifiBottom, wifiLeft: wifiLeft, wifiRight: wifiRight, batteryTop: batteryTop, batteryBottom: batteryBottom, batteryLeft: batteryLeft, batteryRight: batteryRight,
|
|
324
340
|
// Home indicator props
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Types for the iMessage DM conversation composition.
|
|
5
5
|
*/
|
|
6
6
|
import { z } from 'zod';
|
|
7
|
+
import type { DynamicCropConfig } from '../../types/crop';
|
|
7
8
|
/** Message sender type */
|
|
8
9
|
export type ImMessageSender = 'user' | 'recipient';
|
|
9
10
|
/** Message in the conversation */
|
|
@@ -194,6 +195,8 @@ export interface IMessageDmCompositionProps {
|
|
|
194
195
|
readReceiptLeft?: number;
|
|
195
196
|
readReceiptRight?: number;
|
|
196
197
|
messages?: ImMessage[];
|
|
198
|
+
/** Dynamic cropping configuration - crop output to DM element bounds */
|
|
199
|
+
dynamicCrop?: DynamicCropConfig;
|
|
197
200
|
}
|
|
198
201
|
/**
|
|
199
202
|
* Zod schema for iMessage DM composition props (for Remotion)
|
|
@@ -391,6 +394,7 @@ export declare const iMessageDmPropsSchema: z.ZodObject<{
|
|
|
391
394
|
imageUrl?: string | undefined;
|
|
392
395
|
groupWithPrevious?: boolean | undefined;
|
|
393
396
|
}>, "many">>;
|
|
397
|
+
dynamicCrop: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
394
398
|
}, "strip", z.ZodTypeAny, {
|
|
395
399
|
backgroundColor?: string | undefined;
|
|
396
400
|
lightMode?: boolean | undefined;
|
|
@@ -404,6 +408,7 @@ export declare const iMessageDmPropsSchema: z.ZodObject<{
|
|
|
404
408
|
imageUrl?: string | undefined;
|
|
405
409
|
groupWithPrevious?: boolean | undefined;
|
|
406
410
|
}[] | undefined;
|
|
411
|
+
dynamicCrop?: Record<string, any> | undefined;
|
|
407
412
|
width?: number | undefined;
|
|
408
413
|
height?: number | undefined;
|
|
409
414
|
fps?: number | undefined;
|
|
@@ -585,6 +590,7 @@ export declare const iMessageDmPropsSchema: z.ZodObject<{
|
|
|
585
590
|
imageUrl?: string | undefined;
|
|
586
591
|
groupWithPrevious?: boolean | undefined;
|
|
587
592
|
}[] | undefined;
|
|
593
|
+
dynamicCrop?: Record<string, any> | undefined;
|
|
588
594
|
width?: number | undefined;
|
|
589
595
|
height?: number | undefined;
|
|
590
596
|
fps?: number | undefined;
|
|
@@ -222,4 +222,6 @@ exports.iMessageDmPropsSchema = zod_1.z.object({
|
|
|
222
222
|
imageUrl: zod_1.z.string().optional(),
|
|
223
223
|
groupWithPrevious: zod_1.z.boolean().optional(),
|
|
224
224
|
})).optional(),
|
|
225
|
+
// Dynamic crop - crop output to element bounds
|
|
226
|
+
dynamicCrop: zod_1.z.record(zod_1.z.any()).optional(),
|
|
225
227
|
});
|
|
@@ -126,6 +126,8 @@ function convertPublicToProps(input) {
|
|
|
126
126
|
// Disable debug/reference overlays for production renders
|
|
127
127
|
showDebugOverlay: false,
|
|
128
128
|
showReferenceImage: false,
|
|
129
|
+
// Pass through dynamic crop if provided
|
|
130
|
+
dynamicCrop: input.dynamicCrop,
|
|
129
131
|
};
|
|
130
132
|
return props;
|
|
131
133
|
}
|
|
@@ -16,6 +16,8 @@ const ImageEditorComposition_1 = require("../ImageEditorComposition");
|
|
|
16
16
|
const DmComposition_1 = require("../DmComposition");
|
|
17
17
|
const theme_1 = require("./theme");
|
|
18
18
|
const convertPropsToElements_1 = require("./convertPropsToElements");
|
|
19
|
+
const positionResolver_1 = require("../../utils/positionResolver");
|
|
20
|
+
const cropBounds_1 = require("../../utils/cropBounds");
|
|
19
21
|
/**
|
|
20
22
|
* Default composition props for registration
|
|
21
23
|
*/
|
|
@@ -268,11 +270,20 @@ function InstagramDmComposition(props) {
|
|
|
268
270
|
};
|
|
269
271
|
return (0, convertPropsToElements_1.convertPropsToElements)(propsWithThemeColors);
|
|
270
272
|
}, [props, isLightMode, colors.recipientBubble, colors.headerBg, colors.divider, colors.footerBg]);
|
|
273
|
+
// Resolve element positions and compute crop bounds when dynamic crop enabled
|
|
274
|
+
const cropBounds = (0, react_1.useMemo)(() => {
|
|
275
|
+
const result = (0, positionResolver_1.resolveElementPositions)(elements, {});
|
|
276
|
+
const resolved = result.elements;
|
|
277
|
+
if (!(0, cropBounds_1.isDynamicCropEnabled)(props.dynamicCrop) || !resolved) {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
return (0, cropBounds_1.calculateCropBounds)(resolved, props.dynamicCrop, width, height);
|
|
281
|
+
}, [elements, props.dynamicCrop, width, height]);
|
|
271
282
|
// Compute effective colors based on theme
|
|
272
283
|
const effectiveHomeIndicatorColor = isLightMode ? '#000000' : homeIndicatorColor;
|
|
273
284
|
const effectiveStatusBarColor = isLightMode ? '#000000' : '#ffffff';
|
|
274
285
|
const headerIconFilter = isLightMode ? 'invert(1)' : 'none';
|
|
275
|
-
return ((0, jsx_runtime_1.jsxs)(DmComposition_1.BaseDmComposition, { width: width, height: height, backgroundColor: colors.background, showDebugOverlay: showDebugOverlay, referenceImageUrl: referenceImageUrl, showReferenceImage: showReferenceImage, referenceOpacity: referenceOpacity, currentProps: { ...props },
|
|
286
|
+
return ((0, jsx_runtime_1.jsxs)(DmComposition_1.BaseDmComposition, { width: width, height: height, cropBounds: cropBounds, backgroundColor: colors.background, showDebugOverlay: showDebugOverlay, referenceImageUrl: referenceImageUrl, showReferenceImage: showReferenceImage, referenceOpacity: referenceOpacity, currentProps: { ...props },
|
|
276
287
|
// Status bar props
|
|
277
288
|
time: time, timeRight: timeRight, timeBottom: timeBottom, timeFontSize: timeFontSize, timeLetterSpacing: timeLetterSpacing, noNotisTop: noNotisTop, noNotisBottom: noNotisBottom, noNotisLeft: noNotisLeft, noNotisRight: noNotisRight, cellLevel: cellLevel, cell1Top: cell1Top, cell1Bottom: cell1Bottom, cell1Left: cell1Left, cell1Right: cell1Right, cell2Top: cell2Top, cell2Bottom: cell2Bottom, cell2Left: cell2Left, cell2Right: cell2Right, cell3Top: cell3Top, cell3Bottom: cell3Bottom, cell3Left: cell3Left, cell3Right: cell3Right, cell4Top: cell4Top, cell4Bottom: cell4Bottom, cell4Left: cell4Left, cell4Right: cell4Right, wifiTop: wifiTop, wifiBottom: wifiBottom, wifiLeft: wifiLeft, wifiRight: wifiRight, batteryTop: batteryTop, batteryBottom: batteryBottom, batteryLeft: batteryLeft, batteryRight: batteryRight,
|
|
278
289
|
// Home indicator props
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Minimal types for the stripped-down composition.
|
|
5
5
|
*/
|
|
6
|
+
import type { DynamicCropConfig } from '../../types/crop';
|
|
6
7
|
/** Instagram theme - light or dark mode */
|
|
7
8
|
export type IgTheme = 'light' | 'dark';
|
|
8
9
|
/** Message sender type */
|
|
@@ -212,4 +213,6 @@ export interface InstagramDmCompositionProps {
|
|
|
212
213
|
dividerLineY?: number;
|
|
213
214
|
dividerLineColor?: string;
|
|
214
215
|
headerBackgroundColor?: string;
|
|
216
|
+
/** Dynamic cropping configuration - crop output to element bounds */
|
|
217
|
+
dynamicCrop?: DynamicCropConfig;
|
|
215
218
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* These are the types exposed to users via the automation node.
|
|
5
5
|
* They provide a simpler interface than the full InstagramDmCompositionProps.
|
|
6
6
|
*/
|
|
7
|
+
import type { DynamicCropConfig } from './crop';
|
|
7
8
|
/** Message sender side */
|
|
8
9
|
export type IgDmSender = 'user' | 'recipient';
|
|
9
10
|
/** Theme for the DM composition */
|
|
@@ -57,4 +58,6 @@ export interface InstagramDmPublicInput {
|
|
|
57
58
|
* Required if any message has isStoryReply=true.
|
|
58
59
|
*/
|
|
59
60
|
storyImageUrl?: string;
|
|
61
|
+
/** Dynamic cropping configuration - crop output to DM element bounds */
|
|
62
|
+
dynamicCrop?: DynamicCropConfig;
|
|
60
63
|
}
|
package/dist/render.d.ts
CHANGED
|
@@ -100,6 +100,8 @@ export interface SubmitInstagramDmRenderJobParams {
|
|
|
100
100
|
messages: CreateDmMessage[];
|
|
101
101
|
/** Image attachment URL (used for story reply) */
|
|
102
102
|
imageAttachmentUrl?: string;
|
|
103
|
+
/** Dynamic crop configuration - crop output to DM element bounds */
|
|
104
|
+
dynamicCrop?: import('./render/types/crop').DynamicCropConfig;
|
|
103
105
|
}
|
|
104
106
|
interface ImDmMessageInternal {
|
|
105
107
|
id: string;
|
|
@@ -132,6 +134,8 @@ export interface SubmitIMessageDmRenderJobParams {
|
|
|
132
134
|
unreadBadgeText?: string;
|
|
133
135
|
/** Message header timestamp text (e.g., "Today 2:15 PM") */
|
|
134
136
|
messageHeaderTimestampText?: string;
|
|
137
|
+
/** Dynamic crop configuration - crop output to DM element bounds */
|
|
138
|
+
dynamicCrop?: import('./render/types/crop').DynamicCropConfig;
|
|
135
139
|
}
|
|
136
140
|
/**
|
|
137
141
|
* Submit an image render job to the Modal renderer.
|
package/dist/render.js
CHANGED
|
@@ -314,6 +314,7 @@ async function submitInstagramDmRenderJob(params) {
|
|
|
314
314
|
theme: params.theme,
|
|
315
315
|
messages: transformedMessages,
|
|
316
316
|
storyImageUrl: hasStoryReply ? params.imageAttachmentUrl : undefined,
|
|
317
|
+
dynamicCrop: params.dynamicCrop,
|
|
317
318
|
},
|
|
318
319
|
},
|
|
319
320
|
output_type: 'image',
|
|
@@ -362,6 +363,7 @@ async function submitIMessageDmRenderJob(params) {
|
|
|
362
363
|
readReceiptText: params.readReceiptText,
|
|
363
364
|
unreadBadgeText: params.unreadBadgeText,
|
|
364
365
|
messageHeaderTimestampText: params.messageHeaderTimestampText,
|
|
366
|
+
dynamicCrop: params.dynamicCrop,
|
|
365
367
|
},
|
|
366
368
|
},
|
|
367
369
|
output_type: 'image',
|