ugcinc-render 1.5.11 → 1.5.13
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 +85 -2
- package/dist/index.mjs +81 -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,36 @@ 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: "1.2em",
|
|
673
|
+
width: "1.2em",
|
|
674
|
+
verticalAlign: "-0.25em",
|
|
675
|
+
display: "inline-block",
|
|
676
|
+
margin: "0 0.05em"
|
|
677
|
+
},
|
|
678
|
+
draggable: false
|
|
679
|
+
},
|
|
680
|
+
i
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.default.Fragment, { children: segment2.content }, i);
|
|
684
|
+
});
|
|
685
|
+
};
|
|
607
686
|
if (autoWidth) {
|
|
608
687
|
const textContent = calculatedLines.map((line, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [
|
|
609
|
-
line,
|
|
688
|
+
renderTextWithEmojis(line),
|
|
610
689
|
index < calculatedLines.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {})
|
|
611
690
|
] }, index));
|
|
612
691
|
if (backgroundColor) {
|
|
@@ -625,7 +704,7 @@ function TextElement({ segment, scale = 1 }) {
|
|
|
625
704
|
}
|
|
626
705
|
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
706
|
}
|
|
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 }) }) });
|
|
707
|
+
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
708
|
}
|
|
630
709
|
|
|
631
710
|
// src/components/ImageElement.tsx
|
|
@@ -1945,9 +2024,11 @@ var RenderRoot = () => {
|
|
|
1945
2024
|
calculateTimelineContentEnd,
|
|
1946
2025
|
canSetAsReference,
|
|
1947
2026
|
defaultOffset,
|
|
2027
|
+
emojiToUnified,
|
|
1948
2028
|
formatTime,
|
|
1949
2029
|
generateOverlayId,
|
|
1950
2030
|
generateSegmentId,
|
|
2031
|
+
getAppleEmojiUrl,
|
|
1951
2032
|
getBaseSegments,
|
|
1952
2033
|
getBorderRadii,
|
|
1953
2034
|
getDependentElements,
|
|
@@ -1956,6 +2037,7 @@ var RenderRoot = () => {
|
|
|
1956
2037
|
getReferenceElementX,
|
|
1957
2038
|
getReferenceElementY,
|
|
1958
2039
|
getSegmentTimelinePosition,
|
|
2040
|
+
hasEmoji,
|
|
1959
2041
|
hexToRgba,
|
|
1960
2042
|
isDynamicCropEnabled,
|
|
1961
2043
|
isSegmentVisibleAtTime,
|
|
@@ -1963,6 +2045,7 @@ var RenderRoot = () => {
|
|
|
1963
2045
|
parseTime,
|
|
1964
2046
|
preloadFonts,
|
|
1965
2047
|
resolveElementPositions,
|
|
2048
|
+
splitTextAndEmojis,
|
|
1966
2049
|
useFontsLoaded,
|
|
1967
2050
|
useImageLoader,
|
|
1968
2051
|
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,36 @@ 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: "1.2em",
|
|
585
|
+
width: "1.2em",
|
|
586
|
+
verticalAlign: "-0.25em",
|
|
587
|
+
display: "inline-block",
|
|
588
|
+
margin: "0 0.05em"
|
|
589
|
+
},
|
|
590
|
+
draggable: false
|
|
591
|
+
},
|
|
592
|
+
i
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
return /* @__PURE__ */ jsx(React.Fragment, { children: segment2.content }, i);
|
|
596
|
+
});
|
|
597
|
+
};
|
|
523
598
|
if (autoWidth) {
|
|
524
599
|
const textContent = calculatedLines.map((line, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
525
|
-
line,
|
|
600
|
+
renderTextWithEmojis(line),
|
|
526
601
|
index < calculatedLines.length - 1 && /* @__PURE__ */ jsx("br", {})
|
|
527
602
|
] }, index));
|
|
528
603
|
if (backgroundColor) {
|
|
@@ -541,7 +616,7 @@ function TextElement({ segment, scale = 1 }) {
|
|
|
541
616
|
}
|
|
542
617
|
return /* @__PURE__ */ jsx("div", { style: positioningContainerStyle, children: /* @__PURE__ */ jsx("div", { style: { width: calculatedWidth, maxWidth: width }, children: /* @__PURE__ */ jsx("div", { style: textStyle, children: textContent }) }) });
|
|
543
618
|
}
|
|
544
|
-
return /* @__PURE__ */ jsx("div", { style: positioningContainerStyle, children: /* @__PURE__ */ jsx("div", { style: backgroundBoxStyle, children: /* @__PURE__ */ jsx("div", { style: textStyle, children: segment.text }) }) });
|
|
619
|
+
return /* @__PURE__ */ jsx("div", { style: positioningContainerStyle, children: /* @__PURE__ */ jsx("div", { style: backgroundBoxStyle, children: /* @__PURE__ */ jsx("div", { style: textStyle, children: renderTextWithEmojis(segment.text) }) }) });
|
|
545
620
|
}
|
|
546
621
|
|
|
547
622
|
// src/components/ImageElement.tsx
|
|
@@ -1860,9 +1935,11 @@ export {
|
|
|
1860
1935
|
calculateTimelineContentEnd,
|
|
1861
1936
|
canSetAsReference,
|
|
1862
1937
|
defaultOffset,
|
|
1938
|
+
emojiToUnified,
|
|
1863
1939
|
formatTime,
|
|
1864
1940
|
generateOverlayId,
|
|
1865
1941
|
generateSegmentId,
|
|
1942
|
+
getAppleEmojiUrl,
|
|
1866
1943
|
getBaseSegments,
|
|
1867
1944
|
getBorderRadii,
|
|
1868
1945
|
getDependentElements,
|
|
@@ -1871,6 +1948,7 @@ export {
|
|
|
1871
1948
|
getReferenceElementX,
|
|
1872
1949
|
getReferenceElementY,
|
|
1873
1950
|
getSegmentTimelinePosition,
|
|
1951
|
+
hasEmoji,
|
|
1874
1952
|
hexToRgba,
|
|
1875
1953
|
isDynamicCropEnabled,
|
|
1876
1954
|
isSegmentVisibleAtTime,
|
|
@@ -1878,6 +1956,7 @@ export {
|
|
|
1878
1956
|
parseTime,
|
|
1879
1957
|
preloadFonts,
|
|
1880
1958
|
resolveElementPositions,
|
|
1959
|
+
splitTextAndEmojis,
|
|
1881
1960
|
useFontsLoaded,
|
|
1882
1961
|
useImageLoader,
|
|
1883
1962
|
useImagePreloader,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ugcinc-render",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.13",
|
|
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",
|