ugcinc-render 1.5.11 → 1.5.12
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 +29 -1
- package/dist/index.d.ts +29 -1
- package/dist/index.js +84 -2
- package/dist/index.mjs +80 -2
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1290,6 +1290,34 @@ declare function generateSegmentId(): string;
|
|
|
1290
1290
|
*/
|
|
1291
1291
|
declare function generateOverlayId(): string;
|
|
1292
1292
|
|
|
1293
|
+
/**
|
|
1294
|
+
* Emoji utilities for rendering Apple-style emojis consistently
|
|
1295
|
+
* across all platforms using CDN-hosted emoji images.
|
|
1296
|
+
*/
|
|
1297
|
+
/**
|
|
1298
|
+
* Convert an emoji character to its unified unicode codepoint format
|
|
1299
|
+
* Example: "😀" -> "1f600", "👨👩👧" -> "1f468-200d-1f469-200d-1f467"
|
|
1300
|
+
*/
|
|
1301
|
+
declare function emojiToUnified(emoji: string): string;
|
|
1302
|
+
/**
|
|
1303
|
+
* Get the Apple emoji CDN URL for a given emoji character
|
|
1304
|
+
*/
|
|
1305
|
+
declare function getAppleEmojiUrl(emoji: string): string;
|
|
1306
|
+
/**
|
|
1307
|
+
* Check if a string contains any emoji characters
|
|
1308
|
+
*/
|
|
1309
|
+
declare function hasEmoji(text: string): boolean;
|
|
1310
|
+
/**
|
|
1311
|
+
* Split text into segments of text and emojis
|
|
1312
|
+
* Returns an array of segments with type 'text' or 'emoji'
|
|
1313
|
+
*/
|
|
1314
|
+
interface TextSegmentPart {
|
|
1315
|
+
type: 'text' | 'emoji';
|
|
1316
|
+
content: string;
|
|
1317
|
+
imageUrl?: string;
|
|
1318
|
+
}
|
|
1319
|
+
declare function splitTextAndEmojis(text: string): TextSegmentPart[];
|
|
1320
|
+
|
|
1293
1321
|
/**
|
|
1294
1322
|
* Hook exports for ugcinc-render
|
|
1295
1323
|
*
|
|
@@ -1349,4 +1377,4 @@ declare function useResolvedPositions(elements: ImageEditorElement[], textValues
|
|
|
1349
1377
|
|
|
1350
1378
|
declare const RenderRoot: React.FC;
|
|
1351
1379
|
|
|
1352
|
-
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 SegmentTimelinePosition, 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, calculateEstimatedDuration, calculateFitDimensions, calculateLineWidth, calculateTimelineContentEnd, canSetAsReference, defaultOffset, formatTime, generateOverlayId, generateSegmentId, getBaseSegments, getBorderRadii, getDependentElements, getFontFamily, getOverlays, getReferenceElementX, getReferenceElementY, getSegmentTimelinePosition, hexToRgba, isDynamicCropEnabled, isSegmentVisibleAtTime, parseHexColor, parseTime, preloadFonts, resolveElementPositions, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
|
|
1380
|
+
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 SegmentTimelinePosition, type SegmentType, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextSegmentPart, 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, calculateEstimatedDuration, calculateFitDimensions, calculateLineWidth, calculateTimelineContentEnd, canSetAsReference, defaultOffset, emojiToUnified, formatTime, generateOverlayId, generateSegmentId, getAppleEmojiUrl, getBaseSegments, getBorderRadii, getDependentElements, getFontFamily, getOverlays, getReferenceElementX, getReferenceElementY, getSegmentTimelinePosition, hasEmoji, hexToRgba, isDynamicCropEnabled, isSegmentVisibleAtTime, parseHexColor, parseTime, preloadFonts, resolveElementPositions, splitTextAndEmojis, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
|
package/dist/index.d.ts
CHANGED
|
@@ -1290,6 +1290,34 @@ declare function generateSegmentId(): string;
|
|
|
1290
1290
|
*/
|
|
1291
1291
|
declare function generateOverlayId(): string;
|
|
1292
1292
|
|
|
1293
|
+
/**
|
|
1294
|
+
* Emoji utilities for rendering Apple-style emojis consistently
|
|
1295
|
+
* across all platforms using CDN-hosted emoji images.
|
|
1296
|
+
*/
|
|
1297
|
+
/**
|
|
1298
|
+
* Convert an emoji character to its unified unicode codepoint format
|
|
1299
|
+
* Example: "😀" -> "1f600", "👨👩👧" -> "1f468-200d-1f469-200d-1f467"
|
|
1300
|
+
*/
|
|
1301
|
+
declare function emojiToUnified(emoji: string): string;
|
|
1302
|
+
/**
|
|
1303
|
+
* Get the Apple emoji CDN URL for a given emoji character
|
|
1304
|
+
*/
|
|
1305
|
+
declare function getAppleEmojiUrl(emoji: string): string;
|
|
1306
|
+
/**
|
|
1307
|
+
* Check if a string contains any emoji characters
|
|
1308
|
+
*/
|
|
1309
|
+
declare function hasEmoji(text: string): boolean;
|
|
1310
|
+
/**
|
|
1311
|
+
* Split text into segments of text and emojis
|
|
1312
|
+
* Returns an array of segments with type 'text' or 'emoji'
|
|
1313
|
+
*/
|
|
1314
|
+
interface TextSegmentPart {
|
|
1315
|
+
type: 'text' | 'emoji';
|
|
1316
|
+
content: string;
|
|
1317
|
+
imageUrl?: string;
|
|
1318
|
+
}
|
|
1319
|
+
declare function splitTextAndEmojis(text: string): TextSegmentPart[];
|
|
1320
|
+
|
|
1293
1321
|
/**
|
|
1294
1322
|
* Hook exports for ugcinc-render
|
|
1295
1323
|
*
|
|
@@ -1349,4 +1377,4 @@ declare function useResolvedPositions(elements: ImageEditorElement[], textValues
|
|
|
1349
1377
|
|
|
1350
1378
|
declare const RenderRoot: React.FC;
|
|
1351
1379
|
|
|
1352
|
-
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 SegmentTimelinePosition, 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, calculateEstimatedDuration, calculateFitDimensions, calculateLineWidth, calculateTimelineContentEnd, canSetAsReference, defaultOffset, formatTime, generateOverlayId, generateSegmentId, getBaseSegments, getBorderRadii, getDependentElements, getFontFamily, getOverlays, getReferenceElementX, getReferenceElementY, getSegmentTimelinePosition, hexToRgba, isDynamicCropEnabled, isSegmentVisibleAtTime, parseHexColor, parseTime, preloadFonts, resolveElementPositions, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
|
|
1380
|
+
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 SegmentTimelinePosition, type SegmentType, type StaticSegment, TEXT_DEFAULTS, type TextAlignment, type TextDirection, TextElement, type TextElementProps, type TextOverflow, type TextSegment, type TextSegmentPart, 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, calculateEstimatedDuration, calculateFitDimensions, calculateLineWidth, calculateTimelineContentEnd, canSetAsReference, defaultOffset, emojiToUnified, formatTime, generateOverlayId, generateSegmentId, getAppleEmojiUrl, getBaseSegments, getBorderRadii, getDependentElements, getFontFamily, getOverlays, getReferenceElementX, getReferenceElementY, getSegmentTimelinePosition, hasEmoji, hexToRgba, isDynamicCropEnabled, isSegmentVisibleAtTime, parseHexColor, parseTime, preloadFonts, resolveElementPositions, splitTextAndEmojis, useFontsLoaded, useImageLoader, useImagePreloader, useResolvedPositions, wrapText };
|
package/dist/index.js
CHANGED
|
@@ -56,9 +56,11 @@ __export(index_exports, {
|
|
|
56
56
|
calculateTimelineContentEnd: () => calculateTimelineContentEnd,
|
|
57
57
|
canSetAsReference: () => canSetAsReference,
|
|
58
58
|
defaultOffset: () => defaultOffset,
|
|
59
|
+
emojiToUnified: () => emojiToUnified,
|
|
59
60
|
formatTime: () => formatTime,
|
|
60
61
|
generateOverlayId: () => generateOverlayId,
|
|
61
62
|
generateSegmentId: () => generateSegmentId,
|
|
63
|
+
getAppleEmojiUrl: () => getAppleEmojiUrl,
|
|
62
64
|
getBaseSegments: () => getBaseSegments,
|
|
63
65
|
getBorderRadii: () => getBorderRadii,
|
|
64
66
|
getDependentElements: () => getDependentElements,
|
|
@@ -67,6 +69,7 @@ __export(index_exports, {
|
|
|
67
69
|
getReferenceElementX: () => getReferenceElementX,
|
|
68
70
|
getReferenceElementY: () => getReferenceElementY,
|
|
69
71
|
getSegmentTimelinePosition: () => getSegmentTimelinePosition,
|
|
72
|
+
hasEmoji: () => hasEmoji,
|
|
70
73
|
hexToRgba: () => hexToRgba,
|
|
71
74
|
isDynamicCropEnabled: () => isDynamicCropEnabled,
|
|
72
75
|
isSegmentVisibleAtTime: () => isSegmentVisibleAtTime,
|
|
@@ -74,6 +77,7 @@ __export(index_exports, {
|
|
|
74
77
|
parseTime: () => parseTime,
|
|
75
78
|
preloadFonts: () => preloadFonts,
|
|
76
79
|
resolveElementPositions: () => resolveElementPositions,
|
|
80
|
+
splitTextAndEmojis: () => splitTextAndEmojis,
|
|
77
81
|
useFontsLoaded: () => useFontsLoaded,
|
|
78
82
|
useImageLoader: () => useImageLoader,
|
|
79
83
|
useImagePreloader: () => useImagePreloader,
|
|
@@ -302,6 +306,54 @@ function hexToRgba(hex, opacity = 100) {
|
|
|
302
306
|
return `rgba(${r}, ${g}, ${b}, ${opacity / 100})`;
|
|
303
307
|
}
|
|
304
308
|
|
|
309
|
+
// src/utils/emoji.ts
|
|
310
|
+
var APPLE_EMOJI_CDN_BASE = "https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/";
|
|
311
|
+
function emojiToUnified(emoji) {
|
|
312
|
+
const codePoints = [];
|
|
313
|
+
for (const codePoint of emoji) {
|
|
314
|
+
const hex = codePoint.codePointAt(0)?.toString(16);
|
|
315
|
+
if (hex) {
|
|
316
|
+
codePoints.push(hex);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return codePoints.join("-");
|
|
320
|
+
}
|
|
321
|
+
function getAppleEmojiUrl(emoji) {
|
|
322
|
+
const unified = emojiToUnified(emoji);
|
|
323
|
+
return `${APPLE_EMOJI_CDN_BASE}${unified}.png`;
|
|
324
|
+
}
|
|
325
|
+
var EMOJI_REGEX = /(?:\p{Emoji_Presentation}|\p{Emoji}\uFE0F)(?:\p{Emoji_Modifier}|\uFE0F|\u200D(?:\p{Emoji_Presentation}|\p{Emoji}\uFE0F)(?:\p{Emoji_Modifier})?)*|\p{Regional_Indicator}{2}/gu;
|
|
326
|
+
function hasEmoji(text) {
|
|
327
|
+
return EMOJI_REGEX.test(text);
|
|
328
|
+
}
|
|
329
|
+
function splitTextAndEmojis(text) {
|
|
330
|
+
const segments = [];
|
|
331
|
+
let lastIndex = 0;
|
|
332
|
+
EMOJI_REGEX.lastIndex = 0;
|
|
333
|
+
let match;
|
|
334
|
+
while ((match = EMOJI_REGEX.exec(text)) !== null) {
|
|
335
|
+
if (match.index > lastIndex) {
|
|
336
|
+
segments.push({
|
|
337
|
+
type: "text",
|
|
338
|
+
content: text.slice(lastIndex, match.index)
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
segments.push({
|
|
342
|
+
type: "emoji",
|
|
343
|
+
content: match[0],
|
|
344
|
+
imageUrl: getAppleEmojiUrl(match[0])
|
|
345
|
+
});
|
|
346
|
+
lastIndex = match.index + match[0].length;
|
|
347
|
+
}
|
|
348
|
+
if (lastIndex < text.length) {
|
|
349
|
+
segments.push({
|
|
350
|
+
type: "text",
|
|
351
|
+
content: text.slice(lastIndex)
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
return segments;
|
|
355
|
+
}
|
|
356
|
+
|
|
305
357
|
// src/components/TextElement.tsx
|
|
306
358
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
307
359
|
function calculateAutoWidthAndLines({
|
|
@@ -604,9 +656,35 @@ function TextElement({ segment, scale = 1 }) {
|
|
|
604
656
|
autoWidth,
|
|
605
657
|
verticalAlign
|
|
606
658
|
]);
|
|
659
|
+
const renderTextWithEmojis = (text) => {
|
|
660
|
+
if (!hasEmoji(text)) {
|
|
661
|
+
return text;
|
|
662
|
+
}
|
|
663
|
+
const segments = splitTextAndEmojis(text);
|
|
664
|
+
return segments.map((segment2, i) => {
|
|
665
|
+
if (segment2.type === "emoji" && segment2.imageUrl) {
|
|
666
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
667
|
+
"img",
|
|
668
|
+
{
|
|
669
|
+
src: segment2.imageUrl,
|
|
670
|
+
alt: segment2.content,
|
|
671
|
+
style: {
|
|
672
|
+
height: "1em",
|
|
673
|
+
width: "1em",
|
|
674
|
+
verticalAlign: "-0.1em",
|
|
675
|
+
display: "inline"
|
|
676
|
+
},
|
|
677
|
+
draggable: false
|
|
678
|
+
},
|
|
679
|
+
i
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.default.Fragment, { children: segment2.content }, i);
|
|
683
|
+
});
|
|
684
|
+
};
|
|
607
685
|
if (autoWidth) {
|
|
608
686
|
const textContent = calculatedLines.map((line, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [
|
|
609
|
-
line,
|
|
687
|
+
renderTextWithEmojis(line),
|
|
610
688
|
index < calculatedLines.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {})
|
|
611
689
|
] }, index));
|
|
612
690
|
if (backgroundColor) {
|
|
@@ -625,7 +703,7 @@ function TextElement({ segment, scale = 1 }) {
|
|
|
625
703
|
}
|
|
626
704
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: positioningContainerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: calculatedWidth, maxWidth: width }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: textStyle, children: textContent }) }) });
|
|
627
705
|
}
|
|
628
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: positioningContainerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: backgroundBoxStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: textStyle, children: segment.text }) }) });
|
|
706
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: positioningContainerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: backgroundBoxStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: textStyle, children: renderTextWithEmojis(segment.text) }) }) });
|
|
629
707
|
}
|
|
630
708
|
|
|
631
709
|
// src/components/ImageElement.tsx
|
|
@@ -1945,9 +2023,11 @@ var RenderRoot = () => {
|
|
|
1945
2023
|
calculateTimelineContentEnd,
|
|
1946
2024
|
canSetAsReference,
|
|
1947
2025
|
defaultOffset,
|
|
2026
|
+
emojiToUnified,
|
|
1948
2027
|
formatTime,
|
|
1949
2028
|
generateOverlayId,
|
|
1950
2029
|
generateSegmentId,
|
|
2030
|
+
getAppleEmojiUrl,
|
|
1951
2031
|
getBaseSegments,
|
|
1952
2032
|
getBorderRadii,
|
|
1953
2033
|
getDependentElements,
|
|
@@ -1956,6 +2036,7 @@ var RenderRoot = () => {
|
|
|
1956
2036
|
getReferenceElementX,
|
|
1957
2037
|
getReferenceElementY,
|
|
1958
2038
|
getSegmentTimelinePosition,
|
|
2039
|
+
hasEmoji,
|
|
1959
2040
|
hexToRgba,
|
|
1960
2041
|
isDynamicCropEnabled,
|
|
1961
2042
|
isSegmentVisibleAtTime,
|
|
@@ -1963,6 +2044,7 @@ var RenderRoot = () => {
|
|
|
1963
2044
|
parseTime,
|
|
1964
2045
|
preloadFonts,
|
|
1965
2046
|
resolveElementPositions,
|
|
2047
|
+
splitTextAndEmojis,
|
|
1966
2048
|
useFontsLoaded,
|
|
1967
2049
|
useImageLoader,
|
|
1968
2050
|
useImagePreloader,
|
package/dist/index.mjs
CHANGED
|
@@ -218,6 +218,54 @@ function hexToRgba(hex, opacity = 100) {
|
|
|
218
218
|
return `rgba(${r}, ${g}, ${b}, ${opacity / 100})`;
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
// src/utils/emoji.ts
|
|
222
|
+
var APPLE_EMOJI_CDN_BASE = "https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/";
|
|
223
|
+
function emojiToUnified(emoji) {
|
|
224
|
+
const codePoints = [];
|
|
225
|
+
for (const codePoint of emoji) {
|
|
226
|
+
const hex = codePoint.codePointAt(0)?.toString(16);
|
|
227
|
+
if (hex) {
|
|
228
|
+
codePoints.push(hex);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return codePoints.join("-");
|
|
232
|
+
}
|
|
233
|
+
function getAppleEmojiUrl(emoji) {
|
|
234
|
+
const unified = emojiToUnified(emoji);
|
|
235
|
+
return `${APPLE_EMOJI_CDN_BASE}${unified}.png`;
|
|
236
|
+
}
|
|
237
|
+
var EMOJI_REGEX = /(?:\p{Emoji_Presentation}|\p{Emoji}\uFE0F)(?:\p{Emoji_Modifier}|\uFE0F|\u200D(?:\p{Emoji_Presentation}|\p{Emoji}\uFE0F)(?:\p{Emoji_Modifier})?)*|\p{Regional_Indicator}{2}/gu;
|
|
238
|
+
function hasEmoji(text) {
|
|
239
|
+
return EMOJI_REGEX.test(text);
|
|
240
|
+
}
|
|
241
|
+
function splitTextAndEmojis(text) {
|
|
242
|
+
const segments = [];
|
|
243
|
+
let lastIndex = 0;
|
|
244
|
+
EMOJI_REGEX.lastIndex = 0;
|
|
245
|
+
let match;
|
|
246
|
+
while ((match = EMOJI_REGEX.exec(text)) !== null) {
|
|
247
|
+
if (match.index > lastIndex) {
|
|
248
|
+
segments.push({
|
|
249
|
+
type: "text",
|
|
250
|
+
content: text.slice(lastIndex, match.index)
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
segments.push({
|
|
254
|
+
type: "emoji",
|
|
255
|
+
content: match[0],
|
|
256
|
+
imageUrl: getAppleEmojiUrl(match[0])
|
|
257
|
+
});
|
|
258
|
+
lastIndex = match.index + match[0].length;
|
|
259
|
+
}
|
|
260
|
+
if (lastIndex < text.length) {
|
|
261
|
+
segments.push({
|
|
262
|
+
type: "text",
|
|
263
|
+
content: text.slice(lastIndex)
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return segments;
|
|
267
|
+
}
|
|
268
|
+
|
|
221
269
|
// src/components/TextElement.tsx
|
|
222
270
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
223
271
|
function calculateAutoWidthAndLines({
|
|
@@ -520,9 +568,35 @@ function TextElement({ segment, scale = 1 }) {
|
|
|
520
568
|
autoWidth,
|
|
521
569
|
verticalAlign
|
|
522
570
|
]);
|
|
571
|
+
const renderTextWithEmojis = (text) => {
|
|
572
|
+
if (!hasEmoji(text)) {
|
|
573
|
+
return text;
|
|
574
|
+
}
|
|
575
|
+
const segments = splitTextAndEmojis(text);
|
|
576
|
+
return segments.map((segment2, i) => {
|
|
577
|
+
if (segment2.type === "emoji" && segment2.imageUrl) {
|
|
578
|
+
return /* @__PURE__ */ jsx(
|
|
579
|
+
"img",
|
|
580
|
+
{
|
|
581
|
+
src: segment2.imageUrl,
|
|
582
|
+
alt: segment2.content,
|
|
583
|
+
style: {
|
|
584
|
+
height: "1em",
|
|
585
|
+
width: "1em",
|
|
586
|
+
verticalAlign: "-0.1em",
|
|
587
|
+
display: "inline"
|
|
588
|
+
},
|
|
589
|
+
draggable: false
|
|
590
|
+
},
|
|
591
|
+
i
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
return /* @__PURE__ */ jsx(React.Fragment, { children: segment2.content }, i);
|
|
595
|
+
});
|
|
596
|
+
};
|
|
523
597
|
if (autoWidth) {
|
|
524
598
|
const textContent = calculatedLines.map((line, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
525
|
-
line,
|
|
599
|
+
renderTextWithEmojis(line),
|
|
526
600
|
index < calculatedLines.length - 1 && /* @__PURE__ */ jsx("br", {})
|
|
527
601
|
] }, index));
|
|
528
602
|
if (backgroundColor) {
|
|
@@ -541,7 +615,7 @@ function TextElement({ segment, scale = 1 }) {
|
|
|
541
615
|
}
|
|
542
616
|
return /* @__PURE__ */ jsx("div", { style: positioningContainerStyle, children: /* @__PURE__ */ jsx("div", { style: { width: calculatedWidth, maxWidth: width }, children: /* @__PURE__ */ jsx("div", { style: textStyle, children: textContent }) }) });
|
|
543
617
|
}
|
|
544
|
-
return /* @__PURE__ */ jsx("div", { style: positioningContainerStyle, children: /* @__PURE__ */ jsx("div", { style: backgroundBoxStyle, children: /* @__PURE__ */ jsx("div", { style: textStyle, children: segment.text }) }) });
|
|
618
|
+
return /* @__PURE__ */ jsx("div", { style: positioningContainerStyle, children: /* @__PURE__ */ jsx("div", { style: backgroundBoxStyle, children: /* @__PURE__ */ jsx("div", { style: textStyle, children: renderTextWithEmojis(segment.text) }) }) });
|
|
545
619
|
}
|
|
546
620
|
|
|
547
621
|
// src/components/ImageElement.tsx
|
|
@@ -1860,9 +1934,11 @@ export {
|
|
|
1860
1934
|
calculateTimelineContentEnd,
|
|
1861
1935
|
canSetAsReference,
|
|
1862
1936
|
defaultOffset,
|
|
1937
|
+
emojiToUnified,
|
|
1863
1938
|
formatTime,
|
|
1864
1939
|
generateOverlayId,
|
|
1865
1940
|
generateSegmentId,
|
|
1941
|
+
getAppleEmojiUrl,
|
|
1866
1942
|
getBaseSegments,
|
|
1867
1943
|
getBorderRadii,
|
|
1868
1944
|
getDependentElements,
|
|
@@ -1871,6 +1947,7 @@ export {
|
|
|
1871
1947
|
getReferenceElementX,
|
|
1872
1948
|
getReferenceElementY,
|
|
1873
1949
|
getSegmentTimelinePosition,
|
|
1950
|
+
hasEmoji,
|
|
1874
1951
|
hexToRgba,
|
|
1875
1952
|
isDynamicCropEnabled,
|
|
1876
1953
|
isSegmentVisibleAtTime,
|
|
@@ -1878,6 +1955,7 @@ export {
|
|
|
1878
1955
|
parseTime,
|
|
1879
1956
|
preloadFonts,
|
|
1880
1957
|
resolveElementPositions,
|
|
1958
|
+
splitTextAndEmojis,
|
|
1881
1959
|
useFontsLoaded,
|
|
1882
1960
|
useImageLoader,
|
|
1883
1961
|
useImagePreloader,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ugcinc-render",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.12",
|
|
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",
|