PageSourceSearch

https://jayreddinjr-jr-odysseus-hf.hf.space/static/js/emojiShortcodes.js

js jayreddinjr-jr-odysseus-hf.hf.space collected 2026-10-04 16:59:42 UTC 13,425 bytes, 458 lines download raw bytes

1// static/js/emojiShortcodes.js
2//
3// Emoji shortcode → Unicode conversion (issue #345).
4//
5// Chat models frequently emit GitHub/Slack-style `:shortcode:` text — e.g.
6// `:blush:`, `:fire:`, `:microphone:` — instead of the actual emoji character.
7// Nothing in the render pipeline used to translate these, so they showed up as
8// literal `:blush:` text in the chat bubble.
9//
10// This module turns the common shortcode set into the real Unicode emoji. The
11// chat renderer (markdown.js → svgifyEmoji) runs this BEFORE its existing
12// Unicode-emoji → monochrome-SVG pass, so a converted `:blush:` renders as the
13// same theme-tinted single-color line icon as any other emoji (project rule:
14// never colorful emoji), not as a colored system glyph.
15//
16// Pure and browser-free on purpose: no DOM, no imports, so it can be unit
17// tested with plain `node` (see tests/test_emoji_shortcodes_js.py).
18
19// Canonical map of common shortcode → Unicode emoji. Names follow the GitHub
20// convention (lowercase, underscore-separated). A handful of well-known aliases
21// (`+1`, `thumbsup`, `grinning_face`, …) point at the same glyph so the most
22// frequent model spellings all resolve.
23export const EMOJI_SHORTCODES = {
24  // ── Smileys & emotion ──
25  grinning: '😀', grinning_face: '😀',
26  smiley: '😃', smiley_face: '😃',
27  smile: '😄',
28  grin: '😁',
29  laughing: '😆', satisfied: '😆',
30  sweat_smile: '😅',
31  rofl: '🤣', rolling_on_the_floor_laughing: '🤣',
32  joy: '😂',
33  slightly_smiling_face: '🙂', slight_smile: '🙂',
34  upside_down_face: '🙃', upside_down: '🙃',
35  wink: '😉', winking_face: '😉',
36  blush: '😊', smiling_face_with_smiling_eyes: '😊',
37  innocent: '😇',
38  smiling_face_with_three_hearts: '🥰',
39  heart_eyes: '😍', heart_eyes_face: '😍',
40  star_struck: '🤩',
41  kissing_heart: '😘',
42  kissing: '😗',
43  kissing_closed_eyes: '😚',
44  kissing_smiling_eyes: '😙',
45  yum: '😋',
46  stuck_out_tongue: '😛',
47  stuck_out_tongue_winking_eye: '😜',
48  zany_face: '🤪',
49  stuck_out_tongue_closed_eyes: '😝',
50  money_mouth_face: '🤑',
51  hugs: '🤗', hugging_face: '🤗',
52  hand_over_mouth: '🤭',
53  shushing_face: '🤫',
54  thinking: '🤔', thinking_face: '🤔',
55  zipper_mouth_face: '🤐',
56  raised_eyebrow: '🤨',
57  neutral_face: '😐',
58  expressionless: '😑',
59  no_mouth: '😶',
60  smirk: '😏', smirk_face: '😏',
61  unamused: '😒',
62  roll_eyes: '🙄', face_with_rolling_eyes: '🙄',
63  grimacing: '😬',
64  lying_face: '🤥',
65  relieved: '😌',
66  pensive: '😔',
67  sleepy: '😪',
68  drooling_face: '🤤',
69  sleeping: '😴',
70  mask: '😷',
71  face_with_thermometer: '🤒',
72  face_with_head_bandage: '🤕',
73  nauseated_face: '🤢',
74  vomiting_face: '🤮',
75  sneezing_face: '🤧',
76  hot_face: '🥵',
77  cold_face: '🥶',
78  woozy_face: '🥴',
79  dizzy_face: '😵',
80  exploding_head: '🤯',
81  cowboy_hat_face: '🤠',
82  partying_face: '🥳',
83  sunglasses: '😎',
84  nerd_face: '🤓',
85  monocle_face: '🧐',
86  confused: '😕',
87  worried: '😟',
88  slightly_frowning_face: '🙁',
89  frowning_face: '☹️',
90  open_mouth: '😮',
91  hushed: '😯',
92  astonished: '😲',
93  flushed: '😳',
94  pleading_face: '🥺',
95  frowning: '😦',
96  anguished: '😧',
97  fearful: '😨',
98  cold_sweat: '😰',
99  disappointed_relieved: '😥',
100  cry: '😢',
101  sob: '😭',
102  scream: '😱',
103  confounded: '😖',
104  persevere: '😣',
105  disappointed: '😞',
106  sweat: '😓',
107  weary: '😩',
108  tired_face: '😫',
109  yawning_face: '🥱',
110  triumph: '😤',
111  rage: '😡', pout: '😡', pouting_face: '😡',
112  angry: '😠',
113  cursing_face: '🤬',
114  smiling_imp: '😈',
115  imp: '👿',
116  skull: '💀',
117  skull_and_crossbones: '☠️',
118  hankey: '💩', poop: '💩', shit: '💩',
119  clown_face: '🤡',
120  japanese_ogre: '👹',
121  japanese_goblin: '👺',
122  ghost: '👻',
123  alien: '👽',
124  space_invader: '👾',
125  robot: '🤖', robot_face: '🤖',
126  // ── Cats ──
127  smiley_cat: '😺',
128  smile_cat: '😸',
129  joy_cat: '😹',
130  heart_eyes_cat: '😻',
131  smirk_cat: '😼',
132  kissing_cat: '😽',
133  scream_cat: '🙀',
134  crying_cat_face: '😿',
135  pouting_cat: '😾',
136  see_no_evil: '🙈',
137  hear_no_evil: '🙉',
138  speak_no_evil: '🙊',
139  // ── Hands & body ──
140  wave: '👋', wave_hand: '👋',
141  raised_back_of_hand: '🤚',
142  raised_hand_with_fingers_splayed: '🖐️',
143  hand: '✋', raised_hand: '✋',
144  vulcan_salute: '🖖',
145  ok_hand: '👌',
146  pinched_fingers: '🤌',
147  pinching_hand: '🤏',
148  v: '✌️', victory_hand: '✌️',
149  crossed_fingers: '🤞',
150  love_you_gesture: '🤟',
151  metal: '🤘',
152  call_me_hand: '🤙',
153  point_left: '👈',
154  point_right: '👉',
155  point_up_2: '👆',
156  middle_finger: '🖕', fu: '🖕',
157  point_down: '👇',
158  point_up: '☝️',
159  '+1': '👍', thumbsup: '👍', thumbup: '👍', thumbs_up: '👍',
160  '-1': '👎', thumbsdown: '👎', thumbdown: '👎', thumbs_down: '👎',
161  fist_raised: '✊', fist: '✊',
162  fist_oncoming: '👊', facepunch: '👊', punch: '👊',
163  fist_left: '🤛',
164  fist_right: '🤜',
165  clap: '👏', clapping_hands: '👏',
166  raised_hands: '🙌',
167  open_hands: '👐',
168  palms_up_together: '🤲',
169  handshake: '🤝',
170  pray: '🙏', folded_hands: '🙏',
171  writing_hand: '✍️',
172  nail_care: '💅',
173  selfie: '🤳',
174  muscle: '💪', flexed_biceps: '💪',
175  // ── Hearts & symbols of feeling ──
176  heart: '❤️', red_heart: '❤️',
177  orange_heart: '🧡',
178  yellow_heart: '💛',
179  green_heart: '💚',
180  blue_heart: '💙',
181  purple_heart: '💜',
182  black_heart: '🖤',
183  white_heart: '🤍',
184  brown_heart: '🤎',
185  broken_heart: '💔',
186  heart_on_fire: '❤️‍🔥',
187  two_hearts: '💕',
188  revolving_hearts: '💞',
189  heartbeat: '💓',
190  heartpulse: '💗',
191  sparkling_heart: '💖',
192  cupid: '💘',
193  gift_heart: '💝',
194  heart_decoration: '💟',
195  heavy_heart_exclamation: '❣️',
196  // ── Celebration & misc objects ──
197  fire: '🔥', flame: '🔥',
198  '100': '💯', hundred: '💯',
199  sparkles: '✨',
200  star: '⭐',
201  star2: '🌟', glowing_star: '🌟',
202  dizzy: '💫',
203  boom: '💥', collision: '💥',
204  anger: '💢',
205  sweat_drops: '💦',
206  dash: '💨',
207  zzz: '💤',
208  tada: '🎉', party_popper: '🎉',
209  confetti_ball: '🎊',
210  balloon: '🎈',
211  gift: '🎁',
212  trophy: '🏆',
213  '1st_place_medal': '🥇',
214  '2nd_place_medal': '🥈',
215  '3rd_place_medal': '🥉',
216  medal_sports: '🏅',
217  zap: '⚡', lightning: '⚡',
218  bulb: '💡', light_bulb: '💡',
219  key: '🔑',
220  lock: '🔒',
221  unlock: '🔓',
222  bell: '🔔',
223  no_bell: '🔕',
224  loudspeaker: '📢',
225  mega: '📣', megaphone: '📣',
226  speech_balloon: '💬',
227  thought_balloon: '💭',
228  white_check_mark: '✅',
229  heavy_check_mark: '✔️', check_mark: '✔️',
230  ballot_box_with_check: '☑️',
231  x: '❌', cross_mark: '❌',
232  negative_squared_cross_mark: '❎',
233  question: '❓',
234  grey_question: '❔',
235  exclamation: '❗', heavy_exclamation_mark: '❗',
236  grey_exclamation: '❕',
237  warning: '⚠️',
238  no_entry: '⛔',
239  no_entry_sign: '🚫',
240  red_circle: '🔴',
241  green_circle: '🟢',
242  large_blue_circle: '🔵',
243  yellow_circle: '🟡',
244  white_circle: '⚪',
245  black_circle: '⚫',
246  orange_circle: '🟠',
247  purple_circle: '🟣',
248  brown_circle: '🟤',
249  // ── Tech, work, study ──
250  rocket: '🚀',
251  eyes: '👀',
252  eye: '👁️',
253  brain: '🧠',
254  books: '📚',
255  book: '📖', open_book: '📖',
256  memo: '📝', pencil: '📝',
257  pencil2: '✏️',
258  page_facing_up: '📄',
259  paperclip: '📎',
260  pushpin: '📌',
261  round_pushpin: '📍',
262  link: '🔗',
263  bar_chart: '📊',
264  chart_with_upwards_trend: '📈',
265  chart_with_downwards_trend: '📉',
266  mag: '🔍',
267  mag_right: '🔎',
268  globe_with_meridians: '🌐',
269  earth_africa: '🌍',
270  earth_americas: '🌎',
271  earth_asia: '🌏',
272  alarm_clock: '⏰',
273  hourglass_flowing_sand: '⏳',
274  hourglass: '⌛',
275  microphone: '🎤', mic: '🎤',
276  musical_note: '🎵',
277  notes: '🎶', musical_notes: '🎶',
278  headphones: '🎧',
279  camera: '📷',
280  camera_flash: '📸',
281  clapper: '🎬',
282  tv: '📺',
283  computer: '💻', laptop: '💻',
284  desktop_computer: '🖥️',
285  iphone: '📱', mobile_phone: '📱',
286  telephone: '☎️',
287  wrench: '🔧',
288  hammer: '🔨',
289  gear: '⚙️',
290  nut_and_bolt: '🔩',
291  magnet: '🧲',
292  test_tube: '🧪',
293  microscope: '🔬',
294  dart: '🎯', bullseye: '🎯',
295  game_die: '🎲',
296  jigsaw: '🧩',
297  // ── Food & drink ──
298  pizza: '🍕',
299  hamburger: '🍔',
300  fries: '🍟',
301  taco: '🌮',
302  sushi: '🍣',
303  doughnut: '🍩', donut: '🍩',
304  coffee: '☕',
305  beer: '🍺',
306  wine_glass: '🍷',
307  // ── Animals & nature ──
308  dog: '🐶',
309  cat: '🐱',
310  mouse: '🐭',
311  hamster: '🐹',
312  rabbit: '🐰',
313  fox_face: '🦊',
314  bear: '🐻',
315  panda_face: '🐼',
316  koala: '🐨',
317  tiger: '🐯',
318  lion: '🦁',
319  cow: '🐮',
320  pig: '🐷',
321  frog: '🐸',
322  monkey_face: '🐵',
323  chicken: '🐔',
324  penguin: '🐧',
325  bird: '🐦',
326  eagle: '🦅',
327  duck: '🦆',
328  owl: '🦉',
329  wolf: '🐺',
330  horse: '🐴',
331  unicorn: '🦄',
332  bee: '🐝', honeybee: '🐝',
333  bug: '🐛',
334  butterfly: '🦋',
335  snail: '🐌',
336  lady_beetle: '🐞',
337  snake: '🐍',
338  turtle: '🐢',
339  octopus: '🐙',
340  crab: '🦀',
341  tropical_fish: '🐠',
342  whale: '🐳',
343  shark: '🦈',
344  cherry_blossom: '🌸',
345  rose: '🌹',
346  sunflower: '🌻',
347  hibiscus: '🌺',
348  tulip: '🌷',
349  seedling: '🌱',
350  evergreen_tree: '🌲',
351  deciduous_tree: '🌳',
352  four_leaf_clover: '🍀',
353  apple: '🍎',
354  green_apple: '🍏',
355  pear: '🍐',
356  tangerine: '🍊',
357  lemon: '🍋',
358  banana: '🍌',
359  watermelon: '🍉',
360  grapes: '🍇',
361  strawberry: '🍓',
362  blueberries: '🫐',
363  peach: '🍑',
364  rainbow: '🌈',
365  sunny: '☀️', sun: '☀️',
366  partly_sunny: '⛅',
367  cloud: '☁️',
368  snowflake: '❄️',
369  ocean: '🌊',
370  // ── Arrows & signs ──
371  arrow_right: '➡️',
372  arrow_left: '⬅️',
373  arrow_up: '⬆️',
374  arrow_down: '⬇️',
375  arrow_upper_right: '↗️',
376  arrow_lower_right: '↘️',
377  arrow_lower_left: '↙️',
378  arrow_upper_left: '↖️',
379  leftwards_arrow_with_hook: '↩️',
380  arrow_right_hook: '↪️',
381  arrows_counterclockwise: '🔄',
382  arrows_clockwise: '🔃',
383  heavy_plus_sign: '➕',
384  heavy_minus_sign: '➖',
385  heavy_division_sign: '➗',
386  heavy_multiplication_x: '✖️',
387  infinity: '♾️',
388  copyright: '©️',
389  registered: '®️',
390  tm: '™️',
391  recycle: '♻️',
392  checkered_flag: '🏁',
393  triangular_flag_on_post: '🚩',
394  white_flag: '🏳️',
395  black_flag: '🏴',
396  // ── People & wearables ──
397  baby: '👶',
398  boy: '👦',
399  girl: '👧',
400  man: '👨',
401  woman: '👩',
402  older_man: '👴',
403  older_woman: '👵',
404  crown: '👑',
405  gem: '💎',
406  graduation_cap: '🎓', mortar_board: '🎓',
407};
408
409// `:name:` where name is letters/digits/`_`/`+`/`-`. Length ≥1 so `:+1:` and
410// `:-1:` match. Global + case-insensitive for replace; a separate non-global
411// literal is used for the cheap presence check so there's no shared lastIndex
412// state to reset.
413const SHORTCODE_RE = /:([a-z0-9_+-]{1,40}):/gi;
414
415/**
416 * Cheap test for whether `text` could contain any emoji shortcode at all.
417 * Lets callers skip the replace pass entirely on the common no-shortcode path.
418 */
419export function hasEmojiShortcode(text) {
420  return !!text && text.indexOf(':') !== -1 && /:[a-z0-9_+-]{1,40}:/i.test(text);
421}
422
423// A shortcode must stand on its own — flanked by whitespace, punctuation, a
424// string edge, or markup, never glued to an ASCII word character. Without this
425// guard, real `:name:` shortcodes that happen to sit inside a longer run of
426// digits/letters get converted by mistake and mangle perfectly literal text:
427//   "1:100:2"  → the `:100:` would become 💯  ("1💯2")
428//   "host:fire:port", URL authorities, `key:value:` pairs, etc.
429// Chat models always emit shortcodes delimited by spaces/punctuation (":fire:",
430// "**:microphone:**", "nice :tada:!"), so requiring a boundary keeps every real
431// shortcode working while leaving embedded colon runs untouched. `_` counts as a
432// word char too (identifier-like), but `+`/`-` do not, so "C++ :fire:" still works.
433const _WORDISH = /[A-Za-z0-9_]/;
434function _boundedOnBothSides(str, start, end) {
435  const before = start > 0 ? str[start - 1] : '';
436  const after = end < str.length ? str[end] : '';
437  return !_WORDISH.test(before) && !_WORDISH.test(after);
438}
439
440/**
441 * Replace every known `:shortcode:` in `text` with its Unicode emoji. Unknown
442 * shortcodes (`:definitely_not_emoji:`), colon runs that don't form a shortcode
443 * (`10:30:45`, `16:9`), and known shortcodes embedded mid-token (`1:100:2`) are
444 * all left exactly as-is.
445 */
446export function replaceEmojiShortcodes(text) {
447  if (!text || text.indexOf(':') === -1) return text;
448  return text.replace(SHORTCODE_RE, (whole, name, offset, str) => {
449    const key = name.toLowerCase();
450    if (!Object.prototype.hasOwnProperty.call(EMOJI_SHORTCODES, key)) return whole;
451    // Only convert when the `:shortcode:` is a standalone token, not glued to a
452    // surrounding word/number (which would mean it's literal text, not an emoji).
453    if (!_boundedOnBothSides(str, offset, offset + whole.length)) return whole;
454    return EMOJI_SHORTCODES[key];
455  });
456}
457
458export default { EMOJI_SHORTCODES, replaceEmojiShortcodes, hasEmojiShortcode };

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.