Skip to content

Commit 17e9a02

Browse files
committed
process zero width
1 parent 1d9ea6f commit 17e9a02

File tree

1 file changed

+86
-3
lines changed

1 file changed

+86
-3
lines changed

app/containers/markdown/index.tsx

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,63 @@ const SKIN_TONE_MODIFIERS = [
4242
'\u{1F3FF}' // Dark Skin Tone
4343
];
4444

45+
const ZERO_WIDTH_JOINER = '\u200D';
46+
4547
const isSkinToneModifier = (unicode: string): boolean => SKIN_TONE_MODIFIERS.includes(unicode);
4648

49+
const startsWithEmojiModifier = (text: string): boolean => {
50+
if (!text) return false;
51+
52+
for (const tone of SKIN_TONE_MODIFIERS) {
53+
if (text.startsWith(tone)) return true;
54+
}
55+
56+
return text.startsWith(ZERO_WIDTH_JOINER);
57+
};
58+
59+
const extractEmojiModifiers = (text: string): { modifiers: string; remaining: string } => {
60+
let modifiers = '';
61+
let remaining = text;
62+
63+
while (remaining.length > 0) {
64+
let matched = false;
65+
66+
for (const tone of SKIN_TONE_MODIFIERS) {
67+
if (remaining.startsWith(tone)) {
68+
modifiers += tone;
69+
remaining = remaining.slice(tone.length);
70+
matched = true;
71+
break;
72+
}
73+
}
74+
75+
if (!matched) {
76+
if (remaining.startsWith(ZERO_WIDTH_JOINER)) {
77+
modifiers += ZERO_WIDTH_JOINER;
78+
remaining = remaining.slice(ZERO_WIDTH_JOINER.length);
79+
matched = true;
80+
}
81+
}
82+
83+
if (!matched) {
84+
const char = remaining.charAt(0);
85+
const codePoint = char.codePointAt(0);
86+
if (codePoint && (
87+
(codePoint >= 0x2600 && codePoint <= 0x27BF) || // Misc symbols
88+
(codePoint >= 0x1F300 && codePoint <= 0x1F9FF) || // Emoji ranges
89+
(codePoint >= 0xFE00 && codePoint <= 0xFE0F) // Variation selectors
90+
)) {
91+
modifiers += char;
92+
remaining = remaining.slice(char.length);
93+
} else {
94+
break;
95+
}
96+
}
97+
}
98+
99+
return { modifiers, remaining };
100+
};
101+
47102
const combineEmojisWithSkinTones = (items: any[]): any[] => {
48103
if (!Array.isArray(items) || items.length === 0) return items;
49104

@@ -53,6 +108,33 @@ const combineEmojisWithSkinTones = (items: any[]): any[] => {
53108
while (i < items.length) {
54109
const current = items[i];
55110

111+
if (
112+
current?.type === 'EMOJI' &&
113+
i + 1 < items.length &&
114+
items[i + 1]?.type === 'PLAIN_TEXT'
115+
) {
116+
const nextText = items[i + 1].value;
117+
118+
if (startsWithEmojiModifier(nextText)) {
119+
const { modifiers, remaining } = extractEmojiModifiers(nextText);
120+
121+
combined.push({
122+
...current,
123+
unicode: current.unicode + modifiers
124+
});
125+
126+
if (remaining) {
127+
combined.push({
128+
type: 'PLAIN_TEXT',
129+
value: remaining
130+
});
131+
}
132+
133+
i += 2;
134+
continue;
135+
}
136+
}
137+
56138
if (
57139
current?.type === 'EMOJI' &&
58140
i + 1 < items.length &&
@@ -64,10 +146,11 @@ const combineEmojisWithSkinTones = (items: any[]): any[] => {
64146
unicode: current.unicode + items[i + 1].unicode
65147
});
66148
i += 2;
67-
} else {
68-
combined.push(current);
69-
i += 1;
149+
continue;
70150
}
151+
152+
combined.push(current);
153+
i += 1;
71154
}
72155

73156
return combined;

0 commit comments

Comments
 (0)