ugcinc-render 1.3.13 → 1.3.14
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/index.d.mts +23 -2
- package/dist/index.d.ts +23 -2
- package/dist/index.js +185 -55
- package/dist/index.mjs +183 -55
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -756,6 +756,8 @@ interface ImageEditorCompositionProps {
|
|
|
756
756
|
imageUrls?: Record<string, string | null>;
|
|
757
757
|
/** Text values keyed by textInputId (when using elements, for autoWidth calculation) */
|
|
758
758
|
textValues?: Record<string, string>;
|
|
759
|
+
/** Dynamic crop configuration */
|
|
760
|
+
dynamicCrop?: DynamicCropConfig;
|
|
759
761
|
}
|
|
760
762
|
/**
|
|
761
763
|
* ImageEditorComposition renders a complete image editor configuration.
|
|
@@ -790,7 +792,7 @@ interface ImageEditorCompositionProps {
|
|
|
790
792
|
* />
|
|
791
793
|
* ```
|
|
792
794
|
*/
|
|
793
|
-
declare function ImageEditorComposition({ config, sources, scale, elements, width, height, backgroundType, backgroundColor, backgroundFit, backgroundUrl, imageUrls, textValues, }: ImageEditorCompositionProps): react_jsx_runtime.JSX.Element;
|
|
795
|
+
declare function ImageEditorComposition({ config, sources, scale, elements, width, height, backgroundType, backgroundColor, backgroundFit, backgroundUrl, imageUrls, textValues, dynamicCrop, }: ImageEditorCompositionProps): react_jsx_runtime.JSX.Element;
|
|
794
796
|
|
|
795
797
|
interface VideoEditorCompositionProps {
|
|
796
798
|
/** The editor configuration to render */
|
|
@@ -1144,6 +1146,25 @@ declare function getReferenceElementX(elements: ImageEditorElement[], elementId:
|
|
|
1144
1146
|
*/
|
|
1145
1147
|
declare function getReferenceElementY(elements: ImageEditorElement[], elementId: string): ImageEditorElement | null;
|
|
1146
1148
|
|
|
1149
|
+
/**
|
|
1150
|
+
* Utility functions for calculating dynamic crop bounds
|
|
1151
|
+
*/
|
|
1152
|
+
|
|
1153
|
+
/**
|
|
1154
|
+
* Calculate dynamic crop bounds based on element positions
|
|
1155
|
+
*
|
|
1156
|
+
* @param elements - Array of resolved elements with absolute positions
|
|
1157
|
+
* @param dynamicCrop - Crop configuration
|
|
1158
|
+
* @param canvasWidth - Original canvas width
|
|
1159
|
+
* @param canvasHeight - Original canvas height
|
|
1160
|
+
* @returns CropBounds with x, y, width, height
|
|
1161
|
+
*/
|
|
1162
|
+
declare function calculateCropBounds(elements: ImageEditorElement[], dynamicCrop: DynamicCropConfig | undefined, canvasWidth: number, canvasHeight: number): CropBounds;
|
|
1163
|
+
/**
|
|
1164
|
+
* Check if dynamic crop is enabled (either vertical or horizontal)
|
|
1165
|
+
*/
|
|
1166
|
+
declare function isDynamicCropEnabled(dynamicCrop: DynamicCropConfig | undefined): boolean;
|
|
1167
|
+
|
|
1147
1168
|
/**
|
|
1148
1169
|
* Hook exports for ugcinc-render
|
|
1149
1170
|
*
|
|
@@ -1203,4 +1224,4 @@ declare function useResolvedPositions(elements: ImageEditorElement[], textValues
|
|
|
1203
1224
|
|
|
1204
1225
|
declare const RenderRoot: React.FC;
|
|
1205
1226
|
|
|
1206
|
-
export { type AudioSegment, type BaseEditorConfig, type BaseSegment, type BorderRadiusConfig, type Channel, type CropAxisConfig, type CropBoundary, type CropBounds, DIMENSION_PRESETS, type DimensionPreset, type DimensionPresetKey, type DynamicCropConfig, type EditorConfig, type EditorSegment, FONT_FAMILIES, FONT_URLS, type FitDimensions, type FitMode, type FontType, type FontWeight, type HorizontalAnchor, type HorizontalSelfAnchor, type Hyphenation, IMAGE_DEFAULTS, ImageEditorComposition, type ImageEditorCompositionProps, type ImageEditorConfig, type ImageEditorElement, type ImageEditorNodeConfig, ImageElement, type ImageElementProps, type ImageSegment, type PictureSegment, type PositionResolutionError, type PositionResolutionResult, type RelativePositionConfigX, type RelativePositionConfigY, RenderRoot, type Segment, type SegmentType, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextWrap, type TimeMode, type TimeValue, VIDEO_DEFAULTS, VISUAL_DEFAULTS, type VerticalAlignment, type VerticalAnchor, type VerticalSelfAnchor, type VideoEditorAudioSegment, type VideoEditorBaseSegment, type VideoEditorChannel, VideoEditorComposition, type VideoEditorCompositionProps, type VideoEditorConfig, type VideoEditorImageSegment, type VideoEditorNodeConfig, type VideoEditorSegment, type VideoEditorTextSegment, type VideoEditorVideoSegment, type VideoEditorVisualSegment, VideoElement, type VideoElementProps, type VideoSegment, type VisualSegment, type VisualSegmentUnion, type WordBreak, applyImageDefaults, applyTextDefaults, applyVideoDefaults, areFontsLoaded, buildFontString, calculateAutoWidthDimensions, calculateFitDimensions, calculateLineWidth, canSetAsReference, getBorderRadii, getDependentElements, getFontFamily, getReferenceElementX, getReferenceElementY, hexToRgba, parseHexColor, preloadFonts, resolveElementPositions, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
|
|
1227
|
+
export { type AudioSegment, type BaseEditorConfig, type BaseSegment, type BorderRadiusConfig, type Channel, type CropAxisConfig, type CropBoundary, type CropBounds, DIMENSION_PRESETS, type DimensionPreset, type DimensionPresetKey, type DynamicCropConfig, type EditorConfig, type EditorSegment, FONT_FAMILIES, FONT_URLS, type FitDimensions, type FitMode, type FontType, type FontWeight, type HorizontalAnchor, type HorizontalSelfAnchor, type Hyphenation, IMAGE_DEFAULTS, ImageEditorComposition, type ImageEditorCompositionProps, type ImageEditorConfig, type ImageEditorElement, type ImageEditorNodeConfig, ImageElement, type ImageElementProps, type ImageSegment, type PictureSegment, type PositionResolutionError, type PositionResolutionResult, type RelativePositionConfigX, type RelativePositionConfigY, RenderRoot, type Segment, type SegmentType, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextWrap, type TimeMode, type TimeValue, VIDEO_DEFAULTS, VISUAL_DEFAULTS, type VerticalAlignment, type VerticalAnchor, type VerticalSelfAnchor, type VideoEditorAudioSegment, type VideoEditorBaseSegment, type VideoEditorChannel, VideoEditorComposition, type VideoEditorCompositionProps, type VideoEditorConfig, type VideoEditorImageSegment, type VideoEditorNodeConfig, type VideoEditorSegment, type VideoEditorTextSegment, type VideoEditorVideoSegment, type VideoEditorVisualSegment, VideoElement, type VideoElementProps, type VideoSegment, type VisualSegment, type VisualSegmentUnion, type WordBreak, applyImageDefaults, applyTextDefaults, applyVideoDefaults, areFontsLoaded, buildFontString, calculateAutoWidthDimensions, calculateCropBounds, calculateFitDimensions, calculateLineWidth, canSetAsReference, getBorderRadii, getDependentElements, getFontFamily, getReferenceElementX, getReferenceElementY, hexToRgba, isDynamicCropEnabled, parseHexColor, preloadFonts, resolveElementPositions, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
|
package/dist/index.d.ts
CHANGED
|
@@ -756,6 +756,8 @@ interface ImageEditorCompositionProps {
|
|
|
756
756
|
imageUrls?: Record<string, string | null>;
|
|
757
757
|
/** Text values keyed by textInputId (when using elements, for autoWidth calculation) */
|
|
758
758
|
textValues?: Record<string, string>;
|
|
759
|
+
/** Dynamic crop configuration */
|
|
760
|
+
dynamicCrop?: DynamicCropConfig;
|
|
759
761
|
}
|
|
760
762
|
/**
|
|
761
763
|
* ImageEditorComposition renders a complete image editor configuration.
|
|
@@ -790,7 +792,7 @@ interface ImageEditorCompositionProps {
|
|
|
790
792
|
* />
|
|
791
793
|
* ```
|
|
792
794
|
*/
|
|
793
|
-
declare function ImageEditorComposition({ config, sources, scale, elements, width, height, backgroundType, backgroundColor, backgroundFit, backgroundUrl, imageUrls, textValues, }: ImageEditorCompositionProps): react_jsx_runtime.JSX.Element;
|
|
795
|
+
declare function ImageEditorComposition({ config, sources, scale, elements, width, height, backgroundType, backgroundColor, backgroundFit, backgroundUrl, imageUrls, textValues, dynamicCrop, }: ImageEditorCompositionProps): react_jsx_runtime.JSX.Element;
|
|
794
796
|
|
|
795
797
|
interface VideoEditorCompositionProps {
|
|
796
798
|
/** The editor configuration to render */
|
|
@@ -1144,6 +1146,25 @@ declare function getReferenceElementX(elements: ImageEditorElement[], elementId:
|
|
|
1144
1146
|
*/
|
|
1145
1147
|
declare function getReferenceElementY(elements: ImageEditorElement[], elementId: string): ImageEditorElement | null;
|
|
1146
1148
|
|
|
1149
|
+
/**
|
|
1150
|
+
* Utility functions for calculating dynamic crop bounds
|
|
1151
|
+
*/
|
|
1152
|
+
|
|
1153
|
+
/**
|
|
1154
|
+
* Calculate dynamic crop bounds based on element positions
|
|
1155
|
+
*
|
|
1156
|
+
* @param elements - Array of resolved elements with absolute positions
|
|
1157
|
+
* @param dynamicCrop - Crop configuration
|
|
1158
|
+
* @param canvasWidth - Original canvas width
|
|
1159
|
+
* @param canvasHeight - Original canvas height
|
|
1160
|
+
* @returns CropBounds with x, y, width, height
|
|
1161
|
+
*/
|
|
1162
|
+
declare function calculateCropBounds(elements: ImageEditorElement[], dynamicCrop: DynamicCropConfig | undefined, canvasWidth: number, canvasHeight: number): CropBounds;
|
|
1163
|
+
/**
|
|
1164
|
+
* Check if dynamic crop is enabled (either vertical or horizontal)
|
|
1165
|
+
*/
|
|
1166
|
+
declare function isDynamicCropEnabled(dynamicCrop: DynamicCropConfig | undefined): boolean;
|
|
1167
|
+
|
|
1147
1168
|
/**
|
|
1148
1169
|
* Hook exports for ugcinc-render
|
|
1149
1170
|
*
|
|
@@ -1203,4 +1224,4 @@ declare function useResolvedPositions(elements: ImageEditorElement[], textValues
|
|
|
1203
1224
|
|
|
1204
1225
|
declare const RenderRoot: React.FC;
|
|
1205
1226
|
|
|
1206
|
-
export { type AudioSegment, type BaseEditorConfig, type BaseSegment, type BorderRadiusConfig, type Channel, type CropAxisConfig, type CropBoundary, type CropBounds, DIMENSION_PRESETS, type DimensionPreset, type DimensionPresetKey, type DynamicCropConfig, type EditorConfig, type EditorSegment, FONT_FAMILIES, FONT_URLS, type FitDimensions, type FitMode, type FontType, type FontWeight, type HorizontalAnchor, type HorizontalSelfAnchor, type Hyphenation, IMAGE_DEFAULTS, ImageEditorComposition, type ImageEditorCompositionProps, type ImageEditorConfig, type ImageEditorElement, type ImageEditorNodeConfig, ImageElement, type ImageElementProps, type ImageSegment, type PictureSegment, type PositionResolutionError, type PositionResolutionResult, type RelativePositionConfigX, type RelativePositionConfigY, RenderRoot, type Segment, type SegmentType, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextWrap, type TimeMode, type TimeValue, VIDEO_DEFAULTS, VISUAL_DEFAULTS, type VerticalAlignment, type VerticalAnchor, type VerticalSelfAnchor, type VideoEditorAudioSegment, type VideoEditorBaseSegment, type VideoEditorChannel, VideoEditorComposition, type VideoEditorCompositionProps, type VideoEditorConfig, type VideoEditorImageSegment, type VideoEditorNodeConfig, type VideoEditorSegment, type VideoEditorTextSegment, type VideoEditorVideoSegment, type VideoEditorVisualSegment, VideoElement, type VideoElementProps, type VideoSegment, type VisualSegment, type VisualSegmentUnion, type WordBreak, applyImageDefaults, applyTextDefaults, applyVideoDefaults, areFontsLoaded, buildFontString, calculateAutoWidthDimensions, calculateFitDimensions, calculateLineWidth, canSetAsReference, getBorderRadii, getDependentElements, getFontFamily, getReferenceElementX, getReferenceElementY, hexToRgba, parseHexColor, preloadFonts, resolveElementPositions, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
|
|
1227
|
+
export { type AudioSegment, type BaseEditorConfig, type BaseSegment, type BorderRadiusConfig, type Channel, type CropAxisConfig, type CropBoundary, type CropBounds, DIMENSION_PRESETS, type DimensionPreset, type DimensionPresetKey, type DynamicCropConfig, type EditorConfig, type EditorSegment, FONT_FAMILIES, FONT_URLS, type FitDimensions, type FitMode, type FontType, type FontWeight, type HorizontalAnchor, type HorizontalSelfAnchor, type Hyphenation, IMAGE_DEFAULTS, ImageEditorComposition, type ImageEditorCompositionProps, type ImageEditorConfig, type ImageEditorElement, type ImageEditorNodeConfig, ImageElement, type ImageElementProps, type ImageSegment, type PictureSegment, type PositionResolutionError, type PositionResolutionResult, type RelativePositionConfigX, type RelativePositionConfigY, RenderRoot, type Segment, type SegmentType, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextWrap, type TimeMode, type TimeValue, VIDEO_DEFAULTS, VISUAL_DEFAULTS, type VerticalAlignment, type VerticalAnchor, type VerticalSelfAnchor, type VideoEditorAudioSegment, type VideoEditorBaseSegment, type VideoEditorChannel, VideoEditorComposition, type VideoEditorCompositionProps, type VideoEditorConfig, type VideoEditorImageSegment, type VideoEditorNodeConfig, type VideoEditorSegment, type VideoEditorTextSegment, type VideoEditorVideoSegment, type VideoEditorVisualSegment, VideoElement, type VideoElementProps, type VideoSegment, type VisualSegment, type VisualSegmentUnion, type WordBreak, applyImageDefaults, applyTextDefaults, applyVideoDefaults, areFontsLoaded, buildFontString, calculateAutoWidthDimensions, calculateCropBounds, calculateFitDimensions, calculateLineWidth, canSetAsReference, getBorderRadii, getDependentElements, getFontFamily, getReferenceElementX, getReferenceElementY, hexToRgba, isDynamicCropEnabled, parseHexColor, preloadFonts, resolveElementPositions, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ __export(index_exports, {
|
|
|
39
39
|
areFontsLoaded: () => areFontsLoaded,
|
|
40
40
|
buildFontString: () => buildFontString,
|
|
41
41
|
calculateAutoWidthDimensions: () => calculateAutoWidthDimensions,
|
|
42
|
+
calculateCropBounds: () => calculateCropBounds,
|
|
42
43
|
calculateFitDimensions: () => calculateFitDimensions,
|
|
43
44
|
calculateLineWidth: () => calculateLineWidth,
|
|
44
45
|
canSetAsReference: () => canSetAsReference,
|
|
@@ -48,6 +49,7 @@ __export(index_exports, {
|
|
|
48
49
|
getReferenceElementX: () => getReferenceElementX,
|
|
49
50
|
getReferenceElementY: () => getReferenceElementY,
|
|
50
51
|
hexToRgba: () => hexToRgba,
|
|
52
|
+
isDynamicCropEnabled: () => isDynamicCropEnabled,
|
|
51
53
|
parseHexColor: () => parseHexColor,
|
|
52
54
|
preloadFonts: () => preloadFonts,
|
|
53
55
|
resolveElementPositions: () => resolveElementPositions,
|
|
@@ -799,6 +801,113 @@ function getReferenceElementY(elements, elementId) {
|
|
|
799
801
|
return elements.find((e) => e.id === element.relativePositionY.elementId) ?? null;
|
|
800
802
|
}
|
|
801
803
|
|
|
804
|
+
// src/utils/cropBounds.ts
|
|
805
|
+
function calculateCropBounds(elements, dynamicCrop, canvasWidth, canvasHeight) {
|
|
806
|
+
if (!dynamicCrop) {
|
|
807
|
+
return { x: 0, y: 0, width: canvasWidth, height: canvasHeight };
|
|
808
|
+
}
|
|
809
|
+
const elementMap = /* @__PURE__ */ new Map();
|
|
810
|
+
for (const elem of elements) {
|
|
811
|
+
elementMap.set(elem.id, elem);
|
|
812
|
+
}
|
|
813
|
+
const resolveBoundary = (boundary) => {
|
|
814
|
+
if (!boundary) return void 0;
|
|
815
|
+
if (boundary.elementId) return boundary.elementId;
|
|
816
|
+
return void 0;
|
|
817
|
+
};
|
|
818
|
+
let cropY = 0;
|
|
819
|
+
let cropHeight = canvasHeight;
|
|
820
|
+
if (dynamicCrop.vertical?.enabled) {
|
|
821
|
+
const vCrop = dynamicCrop.vertical;
|
|
822
|
+
const paddingStart = vCrop.paddingStart ?? 0;
|
|
823
|
+
const paddingEnd = vCrop.paddingEnd ?? 0;
|
|
824
|
+
if (vCrop.mode === "all-elements") {
|
|
825
|
+
let minY = canvasHeight;
|
|
826
|
+
let maxY = 0;
|
|
827
|
+
for (const elem of elements) {
|
|
828
|
+
minY = Math.min(minY, elem.y);
|
|
829
|
+
maxY = Math.max(maxY, elem.y + elem.height);
|
|
830
|
+
}
|
|
831
|
+
if (elements.length > 0) {
|
|
832
|
+
cropY = Math.max(0, minY - paddingStart);
|
|
833
|
+
const bottomY = Math.min(canvasHeight, maxY + paddingEnd);
|
|
834
|
+
cropHeight = bottomY - cropY;
|
|
835
|
+
}
|
|
836
|
+
} else if (vCrop.mode === "between-elements") {
|
|
837
|
+
const startElementId = resolveBoundary(vCrop.startBoundary);
|
|
838
|
+
const endElementId = resolveBoundary(vCrop.endBoundary);
|
|
839
|
+
let topY = 0;
|
|
840
|
+
let bottomY = canvasHeight;
|
|
841
|
+
if (startElementId) {
|
|
842
|
+
const startElem = elementMap.get(startElementId);
|
|
843
|
+
if (startElem) {
|
|
844
|
+
topY = startElem.y;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
if (endElementId) {
|
|
848
|
+
const endElem = elementMap.get(endElementId);
|
|
849
|
+
if (endElem) {
|
|
850
|
+
bottomY = endElem.y + endElem.height;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
cropY = Math.max(0, topY - paddingStart);
|
|
854
|
+
const adjustedBottom = Math.min(canvasHeight, bottomY + paddingEnd);
|
|
855
|
+
cropHeight = adjustedBottom - cropY;
|
|
856
|
+
}
|
|
857
|
+
if (vCrop.minSize && cropHeight < vCrop.minSize) {
|
|
858
|
+
cropHeight = vCrop.minSize;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
let cropX = 0;
|
|
862
|
+
let cropWidth = canvasWidth;
|
|
863
|
+
if (dynamicCrop.horizontal?.enabled) {
|
|
864
|
+
const hCrop = dynamicCrop.horizontal;
|
|
865
|
+
const paddingStart = hCrop.paddingStart ?? 0;
|
|
866
|
+
const paddingEnd = hCrop.paddingEnd ?? 0;
|
|
867
|
+
if (hCrop.mode === "all-elements") {
|
|
868
|
+
let minX = canvasWidth;
|
|
869
|
+
let maxX = 0;
|
|
870
|
+
for (const elem of elements) {
|
|
871
|
+
minX = Math.min(minX, elem.x);
|
|
872
|
+
maxX = Math.max(maxX, elem.x + elem.width);
|
|
873
|
+
}
|
|
874
|
+
if (elements.length > 0) {
|
|
875
|
+
cropX = Math.max(0, minX - paddingStart);
|
|
876
|
+
const rightX = Math.min(canvasWidth, maxX + paddingEnd);
|
|
877
|
+
cropWidth = rightX - cropX;
|
|
878
|
+
}
|
|
879
|
+
} else if (hCrop.mode === "between-elements") {
|
|
880
|
+
const startElementId = resolveBoundary(hCrop.startBoundary);
|
|
881
|
+
const endElementId = resolveBoundary(hCrop.endBoundary);
|
|
882
|
+
let leftX = 0;
|
|
883
|
+
let rightX = canvasWidth;
|
|
884
|
+
if (startElementId) {
|
|
885
|
+
const startElem = elementMap.get(startElementId);
|
|
886
|
+
if (startElem) {
|
|
887
|
+
leftX = startElem.x;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
if (endElementId) {
|
|
891
|
+
const endElem = elementMap.get(endElementId);
|
|
892
|
+
if (endElem) {
|
|
893
|
+
rightX = endElem.x + endElem.width;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
cropX = Math.max(0, leftX - paddingStart);
|
|
897
|
+
const adjustedRight = Math.min(canvasWidth, rightX + paddingEnd);
|
|
898
|
+
cropWidth = adjustedRight - cropX;
|
|
899
|
+
}
|
|
900
|
+
if (hCrop.minSize && cropWidth < hCrop.minSize) {
|
|
901
|
+
cropWidth = hCrop.minSize;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
return { x: cropX, y: cropY, width: cropWidth, height: cropHeight };
|
|
905
|
+
}
|
|
906
|
+
function isDynamicCropEnabled(dynamicCrop) {
|
|
907
|
+
if (!dynamicCrop) return false;
|
|
908
|
+
return !!(dynamicCrop.vertical?.enabled || dynamicCrop.horizontal?.enabled);
|
|
909
|
+
}
|
|
910
|
+
|
|
802
911
|
// src/compositions/ImageEditorComposition.tsx
|
|
803
912
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
804
913
|
function getSortedSegments(config) {
|
|
@@ -879,8 +988,11 @@ function ImageEditorComposition({
|
|
|
879
988
|
backgroundFit = "cover",
|
|
880
989
|
backgroundUrl,
|
|
881
990
|
imageUrls = {},
|
|
882
|
-
textValues = {}
|
|
991
|
+
textValues = {},
|
|
992
|
+
dynamicCrop
|
|
883
993
|
}) {
|
|
994
|
+
const canvasWidth = width ?? config?.width ?? 1080;
|
|
995
|
+
const canvasHeight = height ?? config?.height ?? 1920;
|
|
884
996
|
const resolvedElements = (0, import_react3.useMemo)(() => {
|
|
885
997
|
if (!elements) return null;
|
|
886
998
|
const result = resolveElementPositions(elements, textValues);
|
|
@@ -889,6 +1001,10 @@ function ImageEditorComposition({
|
|
|
889
1001
|
}
|
|
890
1002
|
return result.elements;
|
|
891
1003
|
}, [elements, textValues]);
|
|
1004
|
+
const cropBounds = (0, import_react3.useMemo)(() => {
|
|
1005
|
+
if (!isDynamicCropEnabled(dynamicCrop) || !resolvedElements) return null;
|
|
1006
|
+
return calculateCropBounds(resolvedElements, dynamicCrop, canvasWidth, canvasHeight);
|
|
1007
|
+
}, [resolvedElements, dynamicCrop, canvasWidth, canvasHeight]);
|
|
892
1008
|
const segmentsFromElements = (0, import_react3.useMemo)(() => {
|
|
893
1009
|
if (!resolvedElements) return null;
|
|
894
1010
|
const segments = [];
|
|
@@ -905,10 +1021,10 @@ function ImageEditorComposition({
|
|
|
905
1021
|
}
|
|
906
1022
|
return segments.sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));
|
|
907
1023
|
}, [resolvedElements, imageUrls, textValues]);
|
|
908
|
-
const canvasWidth = width ?? config?.width ?? 1080;
|
|
909
|
-
const canvasHeight = height ?? config?.height ?? 1920;
|
|
910
1024
|
const bgFit = backgroundFit ?? "cover";
|
|
911
1025
|
const bgUrl = backgroundUrl ?? sources.background;
|
|
1026
|
+
const cropOffsetX = cropBounds?.x ?? 0;
|
|
1027
|
+
const cropOffsetY = cropBounds?.y ?? 0;
|
|
912
1028
|
const contentSegments = segmentsFromElements ?? (() => {
|
|
913
1029
|
if (!config) return [];
|
|
914
1030
|
const sorted = getSortedSegments(config);
|
|
@@ -929,61 +1045,73 @@ function ImageEditorComposition({
|
|
|
929
1045
|
return void 0;
|
|
930
1046
|
};
|
|
931
1047
|
const containerBgColor = backgroundType === "color" && backgroundColor ? backgroundColor : "#000000";
|
|
932
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
{
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
}
|
|
946
|
-
),
|
|
947
|
-
legacyBackgroundSegment && !segmentsFromElements && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
948
|
-
BackgroundImage,
|
|
949
|
-
{
|
|
950
|
-
segment: legacyBackgroundSegment,
|
|
951
|
-
src: getSource(legacyBackgroundSegment),
|
|
952
|
-
width: canvasWidth,
|
|
953
|
-
height: canvasHeight,
|
|
954
|
-
scale
|
|
955
|
-
}
|
|
956
|
-
),
|
|
957
|
-
contentSegments.map((segment) => {
|
|
958
|
-
if (segment.type === "text") {
|
|
959
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
960
|
-
TextElement,
|
|
1048
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_remotion2.AbsoluteFill, { style: { backgroundColor: containerBgColor }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1049
|
+
"div",
|
|
1050
|
+
{
|
|
1051
|
+
style: {
|
|
1052
|
+
position: "absolute",
|
|
1053
|
+
left: -cropOffsetX * scale,
|
|
1054
|
+
top: -cropOffsetY * scale,
|
|
1055
|
+
width: canvasWidth * scale,
|
|
1056
|
+
height: canvasHeight * scale
|
|
1057
|
+
},
|
|
1058
|
+
children: [
|
|
1059
|
+
backgroundType === "image" && bgUrl && segmentsFromElements && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1060
|
+
import_remotion2.Img,
|
|
961
1061
|
{
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
ImageElement,
|
|
1062
|
+
src: bgUrl,
|
|
1063
|
+
style: {
|
|
1064
|
+
position: "absolute",
|
|
1065
|
+
left: 0,
|
|
1066
|
+
top: 0,
|
|
1067
|
+
width: canvasWidth * scale,
|
|
1068
|
+
height: canvasHeight * scale,
|
|
1069
|
+
objectFit: bgFit
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
),
|
|
1073
|
+
legacyBackgroundSegment && !segmentsFromElements && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1074
|
+
BackgroundImage,
|
|
976
1075
|
{
|
|
977
|
-
segment,
|
|
978
|
-
src,
|
|
1076
|
+
segment: legacyBackgroundSegment,
|
|
1077
|
+
src: getSource(legacyBackgroundSegment),
|
|
1078
|
+
width: canvasWidth,
|
|
1079
|
+
height: canvasHeight,
|
|
979
1080
|
scale
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
)
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1081
|
+
}
|
|
1082
|
+
),
|
|
1083
|
+
contentSegments.map((segment) => {
|
|
1084
|
+
if (segment.type === "text") {
|
|
1085
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1086
|
+
TextElement,
|
|
1087
|
+
{
|
|
1088
|
+
segment,
|
|
1089
|
+
scale
|
|
1090
|
+
},
|
|
1091
|
+
segment.id
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
if (segment.type === "image") {
|
|
1095
|
+
const src = segment.source || getSource(segment);
|
|
1096
|
+
if (!src) {
|
|
1097
|
+
console.warn(`No source found for image segment: ${segment.id}`);
|
|
1098
|
+
return null;
|
|
1099
|
+
}
|
|
1100
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1101
|
+
ImageElement,
|
|
1102
|
+
{
|
|
1103
|
+
segment,
|
|
1104
|
+
src,
|
|
1105
|
+
scale
|
|
1106
|
+
},
|
|
1107
|
+
segment.id
|
|
1108
|
+
);
|
|
1109
|
+
}
|
|
1110
|
+
return null;
|
|
1111
|
+
})
|
|
1112
|
+
]
|
|
1113
|
+
}
|
|
1114
|
+
) });
|
|
987
1115
|
}
|
|
988
1116
|
function BackgroundImage({
|
|
989
1117
|
segment,
|
|
@@ -1489,6 +1617,7 @@ var RenderRoot = () => {
|
|
|
1489
1617
|
areFontsLoaded,
|
|
1490
1618
|
buildFontString,
|
|
1491
1619
|
calculateAutoWidthDimensions,
|
|
1620
|
+
calculateCropBounds,
|
|
1492
1621
|
calculateFitDimensions,
|
|
1493
1622
|
calculateLineWidth,
|
|
1494
1623
|
canSetAsReference,
|
|
@@ -1498,6 +1627,7 @@ var RenderRoot = () => {
|
|
|
1498
1627
|
getReferenceElementX,
|
|
1499
1628
|
getReferenceElementY,
|
|
1500
1629
|
hexToRgba,
|
|
1630
|
+
isDynamicCropEnabled,
|
|
1501
1631
|
parseHexColor,
|
|
1502
1632
|
preloadFonts,
|
|
1503
1633
|
resolveElementPositions,
|
package/dist/index.mjs
CHANGED
|
@@ -738,6 +738,113 @@ function getReferenceElementY(elements, elementId) {
|
|
|
738
738
|
return elements.find((e) => e.id === element.relativePositionY.elementId) ?? null;
|
|
739
739
|
}
|
|
740
740
|
|
|
741
|
+
// src/utils/cropBounds.ts
|
|
742
|
+
function calculateCropBounds(elements, dynamicCrop, canvasWidth, canvasHeight) {
|
|
743
|
+
if (!dynamicCrop) {
|
|
744
|
+
return { x: 0, y: 0, width: canvasWidth, height: canvasHeight };
|
|
745
|
+
}
|
|
746
|
+
const elementMap = /* @__PURE__ */ new Map();
|
|
747
|
+
for (const elem of elements) {
|
|
748
|
+
elementMap.set(elem.id, elem);
|
|
749
|
+
}
|
|
750
|
+
const resolveBoundary = (boundary) => {
|
|
751
|
+
if (!boundary) return void 0;
|
|
752
|
+
if (boundary.elementId) return boundary.elementId;
|
|
753
|
+
return void 0;
|
|
754
|
+
};
|
|
755
|
+
let cropY = 0;
|
|
756
|
+
let cropHeight = canvasHeight;
|
|
757
|
+
if (dynamicCrop.vertical?.enabled) {
|
|
758
|
+
const vCrop = dynamicCrop.vertical;
|
|
759
|
+
const paddingStart = vCrop.paddingStart ?? 0;
|
|
760
|
+
const paddingEnd = vCrop.paddingEnd ?? 0;
|
|
761
|
+
if (vCrop.mode === "all-elements") {
|
|
762
|
+
let minY = canvasHeight;
|
|
763
|
+
let maxY = 0;
|
|
764
|
+
for (const elem of elements) {
|
|
765
|
+
minY = Math.min(minY, elem.y);
|
|
766
|
+
maxY = Math.max(maxY, elem.y + elem.height);
|
|
767
|
+
}
|
|
768
|
+
if (elements.length > 0) {
|
|
769
|
+
cropY = Math.max(0, minY - paddingStart);
|
|
770
|
+
const bottomY = Math.min(canvasHeight, maxY + paddingEnd);
|
|
771
|
+
cropHeight = bottomY - cropY;
|
|
772
|
+
}
|
|
773
|
+
} else if (vCrop.mode === "between-elements") {
|
|
774
|
+
const startElementId = resolveBoundary(vCrop.startBoundary);
|
|
775
|
+
const endElementId = resolveBoundary(vCrop.endBoundary);
|
|
776
|
+
let topY = 0;
|
|
777
|
+
let bottomY = canvasHeight;
|
|
778
|
+
if (startElementId) {
|
|
779
|
+
const startElem = elementMap.get(startElementId);
|
|
780
|
+
if (startElem) {
|
|
781
|
+
topY = startElem.y;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
if (endElementId) {
|
|
785
|
+
const endElem = elementMap.get(endElementId);
|
|
786
|
+
if (endElem) {
|
|
787
|
+
bottomY = endElem.y + endElem.height;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
cropY = Math.max(0, topY - paddingStart);
|
|
791
|
+
const adjustedBottom = Math.min(canvasHeight, bottomY + paddingEnd);
|
|
792
|
+
cropHeight = adjustedBottom - cropY;
|
|
793
|
+
}
|
|
794
|
+
if (vCrop.minSize && cropHeight < vCrop.minSize) {
|
|
795
|
+
cropHeight = vCrop.minSize;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
let cropX = 0;
|
|
799
|
+
let cropWidth = canvasWidth;
|
|
800
|
+
if (dynamicCrop.horizontal?.enabled) {
|
|
801
|
+
const hCrop = dynamicCrop.horizontal;
|
|
802
|
+
const paddingStart = hCrop.paddingStart ?? 0;
|
|
803
|
+
const paddingEnd = hCrop.paddingEnd ?? 0;
|
|
804
|
+
if (hCrop.mode === "all-elements") {
|
|
805
|
+
let minX = canvasWidth;
|
|
806
|
+
let maxX = 0;
|
|
807
|
+
for (const elem of elements) {
|
|
808
|
+
minX = Math.min(minX, elem.x);
|
|
809
|
+
maxX = Math.max(maxX, elem.x + elem.width);
|
|
810
|
+
}
|
|
811
|
+
if (elements.length > 0) {
|
|
812
|
+
cropX = Math.max(0, minX - paddingStart);
|
|
813
|
+
const rightX = Math.min(canvasWidth, maxX + paddingEnd);
|
|
814
|
+
cropWidth = rightX - cropX;
|
|
815
|
+
}
|
|
816
|
+
} else if (hCrop.mode === "between-elements") {
|
|
817
|
+
const startElementId = resolveBoundary(hCrop.startBoundary);
|
|
818
|
+
const endElementId = resolveBoundary(hCrop.endBoundary);
|
|
819
|
+
let leftX = 0;
|
|
820
|
+
let rightX = canvasWidth;
|
|
821
|
+
if (startElementId) {
|
|
822
|
+
const startElem = elementMap.get(startElementId);
|
|
823
|
+
if (startElem) {
|
|
824
|
+
leftX = startElem.x;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
if (endElementId) {
|
|
828
|
+
const endElem = elementMap.get(endElementId);
|
|
829
|
+
if (endElem) {
|
|
830
|
+
rightX = endElem.x + endElem.width;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
cropX = Math.max(0, leftX - paddingStart);
|
|
834
|
+
const adjustedRight = Math.min(canvasWidth, rightX + paddingEnd);
|
|
835
|
+
cropWidth = adjustedRight - cropX;
|
|
836
|
+
}
|
|
837
|
+
if (hCrop.minSize && cropWidth < hCrop.minSize) {
|
|
838
|
+
cropWidth = hCrop.minSize;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return { x: cropX, y: cropY, width: cropWidth, height: cropHeight };
|
|
842
|
+
}
|
|
843
|
+
function isDynamicCropEnabled(dynamicCrop) {
|
|
844
|
+
if (!dynamicCrop) return false;
|
|
845
|
+
return !!(dynamicCrop.vertical?.enabled || dynamicCrop.horizontal?.enabled);
|
|
846
|
+
}
|
|
847
|
+
|
|
741
848
|
// src/compositions/ImageEditorComposition.tsx
|
|
742
849
|
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
743
850
|
function getSortedSegments(config) {
|
|
@@ -818,8 +925,11 @@ function ImageEditorComposition({
|
|
|
818
925
|
backgroundFit = "cover",
|
|
819
926
|
backgroundUrl,
|
|
820
927
|
imageUrls = {},
|
|
821
|
-
textValues = {}
|
|
928
|
+
textValues = {},
|
|
929
|
+
dynamicCrop
|
|
822
930
|
}) {
|
|
931
|
+
const canvasWidth = width ?? config?.width ?? 1080;
|
|
932
|
+
const canvasHeight = height ?? config?.height ?? 1920;
|
|
823
933
|
const resolvedElements = useMemo3(() => {
|
|
824
934
|
if (!elements) return null;
|
|
825
935
|
const result = resolveElementPositions(elements, textValues);
|
|
@@ -828,6 +938,10 @@ function ImageEditorComposition({
|
|
|
828
938
|
}
|
|
829
939
|
return result.elements;
|
|
830
940
|
}, [elements, textValues]);
|
|
941
|
+
const cropBounds = useMemo3(() => {
|
|
942
|
+
if (!isDynamicCropEnabled(dynamicCrop) || !resolvedElements) return null;
|
|
943
|
+
return calculateCropBounds(resolvedElements, dynamicCrop, canvasWidth, canvasHeight);
|
|
944
|
+
}, [resolvedElements, dynamicCrop, canvasWidth, canvasHeight]);
|
|
831
945
|
const segmentsFromElements = useMemo3(() => {
|
|
832
946
|
if (!resolvedElements) return null;
|
|
833
947
|
const segments = [];
|
|
@@ -844,10 +958,10 @@ function ImageEditorComposition({
|
|
|
844
958
|
}
|
|
845
959
|
return segments.sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));
|
|
846
960
|
}, [resolvedElements, imageUrls, textValues]);
|
|
847
|
-
const canvasWidth = width ?? config?.width ?? 1080;
|
|
848
|
-
const canvasHeight = height ?? config?.height ?? 1920;
|
|
849
961
|
const bgFit = backgroundFit ?? "cover";
|
|
850
962
|
const bgUrl = backgroundUrl ?? sources.background;
|
|
963
|
+
const cropOffsetX = cropBounds?.x ?? 0;
|
|
964
|
+
const cropOffsetY = cropBounds?.y ?? 0;
|
|
851
965
|
const contentSegments = segmentsFromElements ?? (() => {
|
|
852
966
|
if (!config) return [];
|
|
853
967
|
const sorted = getSortedSegments(config);
|
|
@@ -868,61 +982,73 @@ function ImageEditorComposition({
|
|
|
868
982
|
return void 0;
|
|
869
983
|
};
|
|
870
984
|
const containerBgColor = backgroundType === "color" && backgroundColor ? backgroundColor : "#000000";
|
|
871
|
-
return /* @__PURE__ */
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
{
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
}
|
|
885
|
-
),
|
|
886
|
-
legacyBackgroundSegment && !segmentsFromElements && /* @__PURE__ */ jsx3(
|
|
887
|
-
BackgroundImage,
|
|
888
|
-
{
|
|
889
|
-
segment: legacyBackgroundSegment,
|
|
890
|
-
src: getSource(legacyBackgroundSegment),
|
|
891
|
-
width: canvasWidth,
|
|
892
|
-
height: canvasHeight,
|
|
893
|
-
scale
|
|
894
|
-
}
|
|
895
|
-
),
|
|
896
|
-
contentSegments.map((segment) => {
|
|
897
|
-
if (segment.type === "text") {
|
|
898
|
-
return /* @__PURE__ */ jsx3(
|
|
899
|
-
TextElement,
|
|
985
|
+
return /* @__PURE__ */ jsx3(AbsoluteFill, { style: { backgroundColor: containerBgColor }, children: /* @__PURE__ */ jsxs(
|
|
986
|
+
"div",
|
|
987
|
+
{
|
|
988
|
+
style: {
|
|
989
|
+
position: "absolute",
|
|
990
|
+
left: -cropOffsetX * scale,
|
|
991
|
+
top: -cropOffsetY * scale,
|
|
992
|
+
width: canvasWidth * scale,
|
|
993
|
+
height: canvasHeight * scale
|
|
994
|
+
},
|
|
995
|
+
children: [
|
|
996
|
+
backgroundType === "image" && bgUrl && segmentsFromElements && /* @__PURE__ */ jsx3(
|
|
997
|
+
Img2,
|
|
900
998
|
{
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
ImageElement,
|
|
999
|
+
src: bgUrl,
|
|
1000
|
+
style: {
|
|
1001
|
+
position: "absolute",
|
|
1002
|
+
left: 0,
|
|
1003
|
+
top: 0,
|
|
1004
|
+
width: canvasWidth * scale,
|
|
1005
|
+
height: canvasHeight * scale,
|
|
1006
|
+
objectFit: bgFit
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
),
|
|
1010
|
+
legacyBackgroundSegment && !segmentsFromElements && /* @__PURE__ */ jsx3(
|
|
1011
|
+
BackgroundImage,
|
|
915
1012
|
{
|
|
916
|
-
segment,
|
|
917
|
-
src,
|
|
1013
|
+
segment: legacyBackgroundSegment,
|
|
1014
|
+
src: getSource(legacyBackgroundSegment),
|
|
1015
|
+
width: canvasWidth,
|
|
1016
|
+
height: canvasHeight,
|
|
918
1017
|
scale
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
)
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1018
|
+
}
|
|
1019
|
+
),
|
|
1020
|
+
contentSegments.map((segment) => {
|
|
1021
|
+
if (segment.type === "text") {
|
|
1022
|
+
return /* @__PURE__ */ jsx3(
|
|
1023
|
+
TextElement,
|
|
1024
|
+
{
|
|
1025
|
+
segment,
|
|
1026
|
+
scale
|
|
1027
|
+
},
|
|
1028
|
+
segment.id
|
|
1029
|
+
);
|
|
1030
|
+
}
|
|
1031
|
+
if (segment.type === "image") {
|
|
1032
|
+
const src = segment.source || getSource(segment);
|
|
1033
|
+
if (!src) {
|
|
1034
|
+
console.warn(`No source found for image segment: ${segment.id}`);
|
|
1035
|
+
return null;
|
|
1036
|
+
}
|
|
1037
|
+
return /* @__PURE__ */ jsx3(
|
|
1038
|
+
ImageElement,
|
|
1039
|
+
{
|
|
1040
|
+
segment,
|
|
1041
|
+
src,
|
|
1042
|
+
scale
|
|
1043
|
+
},
|
|
1044
|
+
segment.id
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
return null;
|
|
1048
|
+
})
|
|
1049
|
+
]
|
|
1050
|
+
}
|
|
1051
|
+
) });
|
|
926
1052
|
}
|
|
927
1053
|
function BackgroundImage({
|
|
928
1054
|
segment,
|
|
@@ -1427,6 +1553,7 @@ export {
|
|
|
1427
1553
|
areFontsLoaded,
|
|
1428
1554
|
buildFontString,
|
|
1429
1555
|
calculateAutoWidthDimensions,
|
|
1556
|
+
calculateCropBounds,
|
|
1430
1557
|
calculateFitDimensions,
|
|
1431
1558
|
calculateLineWidth,
|
|
1432
1559
|
canSetAsReference,
|
|
@@ -1436,6 +1563,7 @@ export {
|
|
|
1436
1563
|
getReferenceElementX,
|
|
1437
1564
|
getReferenceElementY,
|
|
1438
1565
|
hexToRgba,
|
|
1566
|
+
isDynamicCropEnabled,
|
|
1439
1567
|
parseHexColor,
|
|
1440
1568
|
preloadFonts,
|
|
1441
1569
|
resolveElementPositions,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ugcinc-render",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.14",
|
|
4
4
|
"description": "Unified rendering package for UGC Inc - shared types, components, and compositions for pixel-perfect client/server rendering",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|