diff --git a/frontend/src/common/markdown-it/plugins/markdown-it-emojis/index.ts b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/index.ts new file mode 100644 index 0000000..ca69b59 --- /dev/null +++ b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/index.ts @@ -0,0 +1,4 @@ +export { default as bare } from './lib/bare'; +export { default as light } from './lib/light'; +export { default as full } from './lib/full'; + diff --git a/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/bare.ts b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/bare.ts new file mode 100644 index 0000000..2d7669f --- /dev/null +++ b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/bare.ts @@ -0,0 +1,26 @@ +import MarkdownIt from 'markdown-it'; +import emoji_html from './render'; +import emoji_replace from './replace'; +import normalize_opts, { EmojiOptions } from './normalize_opts'; + +/** + * Bare emoji 插件(不包含预定义的 emoji 数据) + */ +export default function emoji_plugin(md: MarkdownIt, options?: Partial): void { + const defaults: EmojiOptions = { + defs: {}, + shortcuts: {}, + enabled: [] + }; + + const opts = normalize_opts(md.utils.assign({}, defaults, options || {}) as EmojiOptions); + + md.renderer.rules.emoji = emoji_html; + + md.core.ruler.after( + 'linkify', + 'emoji', + emoji_replace(md, opts.defs, opts.shortcuts, opts.scanRE, opts.replaceRE) + ); +} + diff --git a/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/data/full.ts b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/data/full.ts new file mode 100644 index 0000000..6e6a097 --- /dev/null +++ b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/data/full.ts @@ -0,0 +1,1910 @@ +// Generated, don't edit +import { EmojiDefs } from '../normalize_opts'; + +const emojies: EmojiDefs = { + "100": "💯", + "1234": "🔢", + "grinning": "😀", + "smiley": "😃", + "smile": "😄", + "grin": "😁", + "laughing": "😆", + "satisfied": "😆", + "sweat_smile": "😅", + "rofl": "🤣", + "joy": "😂", + "slightly_smiling_face": "🙂", + "upside_down_face": "🙃", + "melting_face": "🫠", + "wink": "😉", + "blush": "😊", + "innocent": "😇", + "smiling_face_with_three_hearts": "🥰", + "heart_eyes": "😍", + "star_struck": "🤩", + "kissing_heart": "😘", + "kissing": "😗", + "relaxed": "☺️", + "kissing_closed_eyes": "😚", + "kissing_smiling_eyes": "😙", + "smiling_face_with_tear": "🥲", + "yum": "😋", + "stuck_out_tongue": "😛", + "stuck_out_tongue_winking_eye": "😜", + "zany_face": "🤪", + "stuck_out_tongue_closed_eyes": "😝", + "money_mouth_face": "🤑", + "hugs": "🤗", + "hand_over_mouth": "🤭", + "face_with_open_eyes_and_hand_over_mouth": "🫢", + "face_with_peeking_eye": "🫣", + "shushing_face": "🤫", + "thinking": "🤔", + "saluting_face": "🫡", + "zipper_mouth_face": "🤐", + "raised_eyebrow": "🤨", + "neutral_face": "😐", + "expressionless": "😑", + "no_mouth": "😶", + "dotted_line_face": "🫥", + "face_in_clouds": "😶‍🌫️", + "smirk": "😏", + "unamused": "😒", + "roll_eyes": "🙄", + "grimacing": "😬", + "face_exhaling": "😮‍💨", + "lying_face": "🤥", + "shaking_face": "🫨", + "relieved": "😌", + "pensive": "😔", + "sleepy": "😪", + "drooling_face": "🤤", + "sleeping": "😴", + "mask": "😷", + "face_with_thermometer": "🤒", + "face_with_head_bandage": "🤕", + "nauseated_face": "🤢", + "vomiting_face": "🤮", + "sneezing_face": "🤧", + "hot_face": "🥵", + "cold_face": "🥶", + "woozy_face": "🥴", + "dizzy_face": "😵", + "face_with_spiral_eyes": "😵‍💫", + "exploding_head": "🤯", + "cowboy_hat_face": "🤠", + "partying_face": "🥳", + "disguised_face": "🥸", + "sunglasses": "😎", + "nerd_face": "🤓", + "monocle_face": "🧐", + "confused": "😕", + "face_with_diagonal_mouth": "🫤", + "worried": "😟", + "slightly_frowning_face": "🙁", + "frowning_face": "☹️", + "open_mouth": "😮", + "hushed": "😯", + "astonished": "😲", + "flushed": "😳", + "pleading_face": "🥺", + "face_holding_back_tears": "🥹", + "frowning": "😦", + "anguished": "😧", + "fearful": "😨", + "cold_sweat": "😰", + "disappointed_relieved": "😥", + "cry": "😢", + "sob": "😭", + "scream": "😱", + "confounded": "😖", + "persevere": "😣", + "disappointed": "😞", + "sweat": "😓", + "weary": "😩", + "tired_face": "😫", + "yawning_face": "🥱", + "triumph": "😤", + "rage": "😡", + "pout": "😡", + "angry": "😠", + "cursing_face": "🤬", + "smiling_imp": "😈", + "imp": "👿", + "skull": "💀", + "skull_and_crossbones": "☠️", + "hankey": "💩", + "poop": "💩", + "shit": "💩", + "clown_face": "🤡", + "japanese_ogre": "👹", + "japanese_goblin": "👺", + "ghost": "👻", + "alien": "👽", + "space_invader": "👾", + "robot": "🤖", + "smiley_cat": "😺", + "smile_cat": "😸", + "joy_cat": "😹", + "heart_eyes_cat": "😻", + "smirk_cat": "😼", + "kissing_cat": "😽", + "scream_cat": "🙀", + "crying_cat_face": "😿", + "pouting_cat": "😾", + "see_no_evil": "🙈", + "hear_no_evil": "🙉", + "speak_no_evil": "🙊", + "love_letter": "💌", + "cupid": "💘", + "gift_heart": "💝", + "sparkling_heart": "💖", + "heartpulse": "💗", + "heartbeat": "💓", + "revolving_hearts": "💞", + "two_hearts": "💕", + "heart_decoration": "💟", + "heavy_heart_exclamation": "❣️", + "broken_heart": "💔", + "heart_on_fire": "❤️‍🔥", + "mending_heart": "❤️‍🩹", + "heart": "❤️", + "pink_heart": "🩷", + "orange_heart": "🧡", + "yellow_heart": "💛", + "green_heart": "💚", + "blue_heart": "💙", + "light_blue_heart": "🩵", + "purple_heart": "💜", + "brown_heart": "🤎", + "black_heart": "🖤", + "grey_heart": "🩶", + "white_heart": "🤍", + "kiss": "💋", + "anger": "💢", + "boom": "💥", + "collision": "💥", + "dizzy": "💫", + "sweat_drops": "💦", + "dash": "💨", + "hole": "🕳️", + "speech_balloon": "💬", + "eye_speech_bubble": "👁️‍🗨️", + "left_speech_bubble": "🗨️", + "right_anger_bubble": "🗯️", + "thought_balloon": "💭", + "zzz": "💤", + "wave": "👋", + "raised_back_of_hand": "🤚", + "raised_hand_with_fingers_splayed": "🖐️", + "hand": "✋", + "raised_hand": "✋", + "vulcan_salute": "🖖", + "rightwards_hand": "🫱", + "leftwards_hand": "🫲", + "palm_down_hand": "🫳", + "palm_up_hand": "🫴", + "leftwards_pushing_hand": "🫷", + "rightwards_pushing_hand": "🫸", + "ok_hand": "👌", + "pinched_fingers": "🤌", + "pinching_hand": "🤏", + "v": "✌️", + "crossed_fingers": "🤞", + "hand_with_index_finger_and_thumb_crossed": "🫰", + "love_you_gesture": "🤟", + "metal": "🤘", + "call_me_hand": "🤙", + "point_left": "👈", + "point_right": "👉", + "point_up_2": "👆", + "middle_finger": "🖕", + "fu": "🖕", + "point_down": "👇", + "point_up": "☝️", + "index_pointing_at_the_viewer": "🫵", + "+1": "👍", + "thumbsup": "👍", + "-1": "👎", + "thumbsdown": "👎", + "fist_raised": "✊", + "fist": "✊", + "fist_oncoming": "👊", + "facepunch": "👊", + "punch": "👊", + "fist_left": "🤛", + "fist_right": "🤜", + "clap": "👏", + "raised_hands": "🙌", + "heart_hands": "🫶", + "open_hands": "👐", + "palms_up_together": "🤲", + "handshake": "🤝", + "pray": "🙏", + "writing_hand": "✍️", + "nail_care": "💅", + "selfie": "🤳", + "muscle": "💪", + "mechanical_arm": "🦾", + "mechanical_leg": "🦿", + "leg": "🦵", + "foot": "🦶", + "ear": "👂", + "ear_with_hearing_aid": "🦻", + "nose": "👃", + "brain": "🧠", + "anatomical_heart": "🫀", + "lungs": "🫁", + "tooth": "🦷", + "bone": "🦴", + "eyes": "👀", + "eye": "👁️", + "tongue": "👅", + "lips": "👄", + "biting_lip": "🫦", + "baby": "👶", + "child": "🧒", + "boy": "👦", + "girl": "👧", + "adult": "🧑", + "blond_haired_person": "👱", + "man": "👨", + "bearded_person": "🧔", + "man_beard": "🧔‍♂️", + "woman_beard": "🧔‍♀️", + "red_haired_man": "👨‍🦰", + "curly_haired_man": "👨‍🦱", + "white_haired_man": "👨‍🦳", + "bald_man": "👨‍🦲", + "woman": "👩", + "red_haired_woman": "👩‍🦰", + "person_red_hair": "🧑‍🦰", + "curly_haired_woman": "👩‍🦱", + "person_curly_hair": "🧑‍🦱", + "white_haired_woman": "👩‍🦳", + "person_white_hair": "🧑‍🦳", + "bald_woman": "👩‍🦲", + "person_bald": "🧑‍🦲", + "blond_haired_woman": "👱‍♀️", + "blonde_woman": "👱‍♀️", + "blond_haired_man": "👱‍♂️", + "older_adult": "🧓", + "older_man": "👴", + "older_woman": "👵", + "frowning_person": "🙍", + "frowning_man": "🙍‍♂️", + "frowning_woman": "🙍‍♀️", + "pouting_face": "🙎", + "pouting_man": "🙎‍♂️", + "pouting_woman": "🙎‍♀️", + "no_good": "🙅", + "no_good_man": "🙅‍♂️", + "ng_man": "🙅‍♂️", + "no_good_woman": "🙅‍♀️", + "ng_woman": "🙅‍♀️", + "ok_person": "🙆", + "ok_man": "🙆‍♂️", + "ok_woman": "🙆‍♀️", + "tipping_hand_person": "💁", + "information_desk_person": "💁", + "tipping_hand_man": "💁‍♂️", + "sassy_man": "💁‍♂️", + "tipping_hand_woman": "💁‍♀️", + "sassy_woman": "💁‍♀️", + "raising_hand": "🙋", + "raising_hand_man": "🙋‍♂️", + "raising_hand_woman": "🙋‍♀️", + "deaf_person": "🧏", + "deaf_man": "🧏‍♂️", + "deaf_woman": "🧏‍♀️", + "bow": "🙇", + "bowing_man": "🙇‍♂️", + "bowing_woman": "🙇‍♀️", + "facepalm": "🤦", + "man_facepalming": "🤦‍♂️", + "woman_facepalming": "🤦‍♀️", + "shrug": "🤷", + "man_shrugging": "🤷‍♂️", + "woman_shrugging": "🤷‍♀️", + "health_worker": "🧑‍⚕️", + "man_health_worker": "👨‍⚕️", + "woman_health_worker": "👩‍⚕️", + "student": "🧑‍🎓", + "man_student": "👨‍🎓", + "woman_student": "👩‍🎓", + "teacher": "🧑‍🏫", + "man_teacher": "👨‍🏫", + "woman_teacher": "👩‍🏫", + "judge": "🧑‍⚖️", + "man_judge": "👨‍⚖️", + "woman_judge": "👩‍⚖️", + "farmer": "🧑‍🌾", + "man_farmer": "👨‍🌾", + "woman_farmer": "👩‍🌾", + "cook": "🧑‍🍳", + "man_cook": "👨‍🍳", + "woman_cook": "👩‍🍳", + "mechanic": "🧑‍🔧", + "man_mechanic": "👨‍🔧", + "woman_mechanic": "👩‍🔧", + "factory_worker": "🧑‍🏭", + "man_factory_worker": "👨‍🏭", + "woman_factory_worker": "👩‍🏭", + "office_worker": "🧑‍💼", + "man_office_worker": "👨‍💼", + "woman_office_worker": "👩‍💼", + "scientist": "🧑‍🔬", + "man_scientist": "👨‍🔬", + "woman_scientist": "👩‍🔬", + "technologist": "🧑‍💻", + "man_technologist": "👨‍💻", + "woman_technologist": "👩‍💻", + "singer": "🧑‍🎤", + "man_singer": "👨‍🎤", + "woman_singer": "👩‍🎤", + "artist": "🧑‍🎨", + "man_artist": "👨‍🎨", + "woman_artist": "👩‍🎨", + "pilot": "🧑‍✈️", + "man_pilot": "👨‍✈️", + "woman_pilot": "👩‍✈️", + "astronaut": "🧑‍🚀", + "man_astronaut": "👨‍🚀", + "woman_astronaut": "👩‍🚀", + "firefighter": "🧑‍🚒", + "man_firefighter": "👨‍🚒", + "woman_firefighter": "👩‍🚒", + "police_officer": "👮", + "cop": "👮", + "policeman": "👮‍♂️", + "policewoman": "👮‍♀️", + "detective": "🕵️", + "male_detective": "🕵️‍♂️", + "female_detective": "🕵️‍♀️", + "guard": "💂", + "guardsman": "💂‍♂️", + "guardswoman": "💂‍♀️", + "ninja": "🥷", + "construction_worker": "👷", + "construction_worker_man": "👷‍♂️", + "construction_worker_woman": "👷‍♀️", + "person_with_crown": "🫅", + "prince": "🤴", + "princess": "👸", + "person_with_turban": "👳", + "man_with_turban": "👳‍♂️", + "woman_with_turban": "👳‍♀️", + "man_with_gua_pi_mao": "👲", + "woman_with_headscarf": "🧕", + "person_in_tuxedo": "🤵", + "man_in_tuxedo": "🤵‍♂️", + "woman_in_tuxedo": "🤵‍♀️", + "person_with_veil": "👰", + "man_with_veil": "👰‍♂️", + "woman_with_veil": "👰‍♀️", + "bride_with_veil": "👰‍♀️", + "pregnant_woman": "🤰", + "pregnant_man": "🫃", + "pregnant_person": "🫄", + "breast_feeding": "🤱", + "woman_feeding_baby": "👩‍🍼", + "man_feeding_baby": "👨‍🍼", + "person_feeding_baby": "🧑‍🍼", + "angel": "👼", + "santa": "🎅", + "mrs_claus": "🤶", + "mx_claus": "🧑‍🎄", + "superhero": "🦸", + "superhero_man": "🦸‍♂️", + "superhero_woman": "🦸‍♀️", + "supervillain": "🦹", + "supervillain_man": "🦹‍♂️", + "supervillain_woman": "🦹‍♀️", + "mage": "🧙", + "mage_man": "🧙‍♂️", + "mage_woman": "🧙‍♀️", + "fairy": "🧚", + "fairy_man": "🧚‍♂️", + "fairy_woman": "🧚‍♀️", + "vampire": "🧛", + "vampire_man": "🧛‍♂️", + "vampire_woman": "🧛‍♀️", + "merperson": "🧜", + "merman": "🧜‍♂️", + "mermaid": "🧜‍♀️", + "elf": "🧝", + "elf_man": "🧝‍♂️", + "elf_woman": "🧝‍♀️", + "genie": "🧞", + "genie_man": "🧞‍♂️", + "genie_woman": "🧞‍♀️", + "zombie": "🧟", + "zombie_man": "🧟‍♂️", + "zombie_woman": "🧟‍♀️", + "troll": "🧌", + "massage": "💆", + "massage_man": "💆‍♂️", + "massage_woman": "💆‍♀️", + "haircut": "💇", + "haircut_man": "💇‍♂️", + "haircut_woman": "💇‍♀️", + "walking": "🚶", + "walking_man": "🚶‍♂️", + "walking_woman": "🚶‍♀️", + "standing_person": "🧍", + "standing_man": "🧍‍♂️", + "standing_woman": "🧍‍♀️", + "kneeling_person": "🧎", + "kneeling_man": "🧎‍♂️", + "kneeling_woman": "🧎‍♀️", + "person_with_probing_cane": "🧑‍🦯", + "man_with_probing_cane": "👨‍🦯", + "woman_with_probing_cane": "👩‍🦯", + "person_in_motorized_wheelchair": "🧑‍🦼", + "man_in_motorized_wheelchair": "👨‍🦼", + "woman_in_motorized_wheelchair": "👩‍🦼", + "person_in_manual_wheelchair": "🧑‍🦽", + "man_in_manual_wheelchair": "👨‍🦽", + "woman_in_manual_wheelchair": "👩‍🦽", + "runner": "🏃", + "running": "🏃", + "running_man": "🏃‍♂️", + "running_woman": "🏃‍♀️", + "woman_dancing": "💃", + "dancer": "💃", + "man_dancing": "🕺", + "business_suit_levitating": "🕴️", + "dancers": "👯", + "dancing_men": "👯‍♂️", + "dancing_women": "👯‍♀️", + "sauna_person": "🧖", + "sauna_man": "🧖‍♂️", + "sauna_woman": "🧖‍♀️", + "climbing": "🧗", + "climbing_man": "🧗‍♂️", + "climbing_woman": "🧗‍♀️", + "person_fencing": "🤺", + "horse_racing": "🏇", + "skier": "⛷️", + "snowboarder": "🏂", + "golfing": "🏌️", + "golfing_man": "🏌️‍♂️", + "golfing_woman": "🏌️‍♀️", + "surfer": "🏄", + "surfing_man": "🏄‍♂️", + "surfing_woman": "🏄‍♀️", + "rowboat": "🚣", + "rowing_man": "🚣‍♂️", + "rowing_woman": "🚣‍♀️", + "swimmer": "🏊", + "swimming_man": "🏊‍♂️", + "swimming_woman": "🏊‍♀️", + "bouncing_ball_person": "⛹️", + "bouncing_ball_man": "⛹️‍♂️", + "basketball_man": "⛹️‍♂️", + "bouncing_ball_woman": "⛹️‍♀️", + "basketball_woman": "⛹️‍♀️", + "weight_lifting": "🏋️", + "weight_lifting_man": "🏋️‍♂️", + "weight_lifting_woman": "🏋️‍♀️", + "bicyclist": "🚴", + "biking_man": "🚴‍♂️", + "biking_woman": "🚴‍♀️", + "mountain_bicyclist": "🚵", + "mountain_biking_man": "🚵‍♂️", + "mountain_biking_woman": "🚵‍♀️", + "cartwheeling": "🤸", + "man_cartwheeling": "🤸‍♂️", + "woman_cartwheeling": "🤸‍♀️", + "wrestling": "🤼", + "men_wrestling": "🤼‍♂️", + "women_wrestling": "🤼‍♀️", + "water_polo": "🤽", + "man_playing_water_polo": "🤽‍♂️", + "woman_playing_water_polo": "🤽‍♀️", + "handball_person": "🤾", + "man_playing_handball": "🤾‍♂️", + "woman_playing_handball": "🤾‍♀️", + "juggling_person": "🤹", + "man_juggling": "🤹‍♂️", + "woman_juggling": "🤹‍♀️", + "lotus_position": "🧘", + "lotus_position_man": "🧘‍♂️", + "lotus_position_woman": "🧘‍♀️", + "bath": "🛀", + "sleeping_bed": "🛌", + "people_holding_hands": "🧑‍🤝‍🧑", + "two_women_holding_hands": "👭", + "couple": "👫", + "two_men_holding_hands": "👬", + "couplekiss": "💏", + "couplekiss_man_woman": "👩‍❤️‍💋‍👨", + "couplekiss_man_man": "👨‍❤️‍💋‍👨", + "couplekiss_woman_woman": "👩‍❤️‍💋‍👩", + "couple_with_heart": "💑", + "couple_with_heart_woman_man": "👩‍❤️‍👨", + "couple_with_heart_man_man": "👨‍❤️‍👨", + "couple_with_heart_woman_woman": "👩‍❤️‍👩", + "family": "👪", + "family_man_woman_boy": "👨‍👩‍👦", + "family_man_woman_girl": "👨‍👩‍👧", + "family_man_woman_girl_boy": "👨‍👩‍👧‍👦", + "family_man_woman_boy_boy": "👨‍👩‍👦‍👦", + "family_man_woman_girl_girl": "👨‍👩‍👧‍👧", + "family_man_man_boy": "👨‍👨‍👦", + "family_man_man_girl": "👨‍👨‍👧", + "family_man_man_girl_boy": "👨‍👨‍👧‍👦", + "family_man_man_boy_boy": "👨‍👨‍👦‍👦", + "family_man_man_girl_girl": "👨‍👨‍👧‍👧", + "family_woman_woman_boy": "👩‍👩‍👦", + "family_woman_woman_girl": "👩‍👩‍👧", + "family_woman_woman_girl_boy": "👩‍👩‍👧‍👦", + "family_woman_woman_boy_boy": "👩‍👩‍👦‍👦", + "family_woman_woman_girl_girl": "👩‍👩‍👧‍👧", + "family_man_boy": "👨‍👦", + "family_man_boy_boy": "👨‍👦‍👦", + "family_man_girl": "👨‍👧", + "family_man_girl_boy": "👨‍👧‍👦", + "family_man_girl_girl": "👨‍👧‍👧", + "family_woman_boy": "👩‍👦", + "family_woman_boy_boy": "👩‍👦‍👦", + "family_woman_girl": "👩‍👧", + "family_woman_girl_boy": "👩‍👧‍👦", + "family_woman_girl_girl": "👩‍👧‍👧", + "speaking_head": "🗣️", + "bust_in_silhouette": "👤", + "busts_in_silhouette": "👥", + "people_hugging": "🫂", + "footprints": "👣", + "monkey_face": "🐵", + "monkey": "🐒", + "gorilla": "🦍", + "orangutan": "🦧", + "dog": "🐶", + "dog2": "🐕", + "guide_dog": "🦮", + "service_dog": "🐕‍🦺", + "poodle": "🐩", + "wolf": "🐺", + "fox_face": "🦊", + "raccoon": "🦝", + "cat": "🐱", + "cat2": "🐈", + "black_cat": "🐈‍⬛", + "lion": "🦁", + "tiger": "🐯", + "tiger2": "🐅", + "leopard": "🐆", + "horse": "🐴", + "moose": "🫎", + "donkey": "🫏", + "racehorse": "🐎", + "unicorn": "🦄", + "zebra": "🦓", + "deer": "🦌", + "bison": "🦬", + "cow": "🐮", + "ox": "🐂", + "water_buffalo": "🐃", + "cow2": "🐄", + "pig": "🐷", + "pig2": "🐖", + "boar": "🐗", + "pig_nose": "🐽", + "ram": "🐏", + "sheep": "🐑", + "goat": "🐐", + "dromedary_camel": "🐪", + "camel": "🐫", + "llama": "🦙", + "giraffe": "🦒", + "elephant": "🐘", + "mammoth": "🦣", + "rhinoceros": "🦏", + "hippopotamus": "🦛", + "mouse": "🐭", + "mouse2": "🐁", + "rat": "🐀", + "hamster": "🐹", + "rabbit": "🐰", + "rabbit2": "🐇", + "chipmunk": "🐿️", + "beaver": "🦫", + "hedgehog": "🦔", + "bat": "🦇", + "bear": "🐻", + "polar_bear": "🐻‍❄️", + "koala": "🐨", + "panda_face": "🐼", + "sloth": "🦥", + "otter": "🦦", + "skunk": "🦨", + "kangaroo": "🦘", + "badger": "🦡", + "feet": "🐾", + "paw_prints": "🐾", + "turkey": "🦃", + "chicken": "🐔", + "rooster": "🐓", + "hatching_chick": "🐣", + "baby_chick": "🐤", + "hatched_chick": "🐥", + "bird": "🐦", + "penguin": "🐧", + "dove": "🕊️", + "eagle": "🦅", + "duck": "🦆", + "swan": "🦢", + "owl": "🦉", + "dodo": "🦤", + "feather": "🪶", + "flamingo": "🦩", + "peacock": "🦚", + "parrot": "🦜", + "wing": "🪽", + "black_bird": "🐦‍⬛", + "goose": "🪿", + "frog": "🐸", + "crocodile": "🐊", + "turtle": "🐢", + "lizard": "🦎", + "snake": "🐍", + "dragon_face": "🐲", + "dragon": "🐉", + "sauropod": "🦕", + "t-rex": "🦖", + "whale": "🐳", + "whale2": "🐋", + "dolphin": "🐬", + "flipper": "🐬", + "seal": "🦭", + "fish": "🐟", + "tropical_fish": "🐠", + "blowfish": "🐡", + "shark": "🦈", + "octopus": "🐙", + "shell": "🐚", + "coral": "🪸", + "jellyfish": "🪼", + "snail": "🐌", + "butterfly": "🦋", + "bug": "🐛", + "ant": "🐜", + "bee": "🐝", + "honeybee": "🐝", + "beetle": "🪲", + "lady_beetle": "🐞", + "cricket": "🦗", + "cockroach": "🪳", + "spider": "🕷️", + "spider_web": "🕸️", + "scorpion": "🦂", + "mosquito": "🦟", + "fly": "🪰", + "worm": "🪱", + "microbe": "🦠", + "bouquet": "💐", + "cherry_blossom": "🌸", + "white_flower": "💮", + "lotus": "🪷", + "rosette": "🏵️", + "rose": "🌹", + "wilted_flower": "🥀", + "hibiscus": "🌺", + "sunflower": "🌻", + "blossom": "🌼", + "tulip": "🌷", + "hyacinth": "🪻", + "seedling": "🌱", + "potted_plant": "🪴", + "evergreen_tree": "🌲", + "deciduous_tree": "🌳", + "palm_tree": "🌴", + "cactus": "🌵", + "ear_of_rice": "🌾", + "herb": "🌿", + "shamrock": "☘️", + "four_leaf_clover": "🍀", + "maple_leaf": "🍁", + "fallen_leaf": "🍂", + "leaves": "🍃", + "empty_nest": "🪹", + "nest_with_eggs": "🪺", + "mushroom": "🍄", + "grapes": "🍇", + "melon": "🍈", + "watermelon": "🍉", + "tangerine": "🍊", + "orange": "🍊", + "mandarin": "🍊", + "lemon": "🍋", + "banana": "🍌", + "pineapple": "🍍", + "mango": "🥭", + "apple": "🍎", + "green_apple": "🍏", + "pear": "🍐", + "peach": "🍑", + "cherries": "🍒", + "strawberry": "🍓", + "blueberries": "🫐", + "kiwi_fruit": "🥝", + "tomato": "🍅", + "olive": "🫒", + "coconut": "🥥", + "avocado": "🥑", + "eggplant": "🍆", + "potato": "🥔", + "carrot": "🥕", + "corn": "🌽", + "hot_pepper": "🌶️", + "bell_pepper": "🫑", + "cucumber": "🥒", + "leafy_green": "🥬", + "broccoli": "🥦", + "garlic": "🧄", + "onion": "🧅", + "peanuts": "🥜", + "beans": "🫘", + "chestnut": "🌰", + "ginger_root": "🫚", + "pea_pod": "🫛", + "bread": "🍞", + "croissant": "🥐", + "baguette_bread": "🥖", + "flatbread": "🫓", + "pretzel": "🥨", + "bagel": "🥯", + "pancakes": "🥞", + "waffle": "🧇", + "cheese": "🧀", + "meat_on_bone": "🍖", + "poultry_leg": "🍗", + "cut_of_meat": "🥩", + "bacon": "🥓", + "hamburger": "🍔", + "fries": "🍟", + "pizza": "🍕", + "hotdog": "🌭", + "sandwich": "🥪", + "taco": "🌮", + "burrito": "🌯", + "tamale": "🫔", + "stuffed_flatbread": "🥙", + "falafel": "🧆", + "egg": "🥚", + "fried_egg": "🍳", + "shallow_pan_of_food": "🥘", + "stew": "🍲", + "fondue": "🫕", + "bowl_with_spoon": "🥣", + "green_salad": "🥗", + "popcorn": "🍿", + "butter": "🧈", + "salt": "🧂", + "canned_food": "🥫", + "bento": "🍱", + "rice_cracker": "🍘", + "rice_ball": "🍙", + "rice": "🍚", + "curry": "🍛", + "ramen": "🍜", + "spaghetti": "🍝", + "sweet_potato": "🍠", + "oden": "🍢", + "sushi": "🍣", + "fried_shrimp": "🍤", + "fish_cake": "🍥", + "moon_cake": "🥮", + "dango": "🍡", + "dumpling": "🥟", + "fortune_cookie": "🥠", + "takeout_box": "🥡", + "crab": "🦀", + "lobster": "🦞", + "shrimp": "🦐", + "squid": "🦑", + "oyster": "🦪", + "icecream": "🍦", + "shaved_ice": "🍧", + "ice_cream": "🍨", + "doughnut": "🍩", + "cookie": "🍪", + "birthday": "🎂", + "cake": "🍰", + "cupcake": "🧁", + "pie": "🥧", + "chocolate_bar": "🍫", + "candy": "🍬", + "lollipop": "🍭", + "custard": "🍮", + "honey_pot": "🍯", + "baby_bottle": "🍼", + "milk_glass": "🥛", + "coffee": "☕", + "teapot": "🫖", + "tea": "🍵", + "sake": "🍶", + "champagne": "🍾", + "wine_glass": "🍷", + "cocktail": "🍸", + "tropical_drink": "🍹", + "beer": "🍺", + "beers": "🍻", + "clinking_glasses": "🥂", + "tumbler_glass": "🥃", + "pouring_liquid": "🫗", + "cup_with_straw": "🥤", + "bubble_tea": "🧋", + "beverage_box": "🧃", + "mate": "🧉", + "ice_cube": "🧊", + "chopsticks": "🥢", + "plate_with_cutlery": "🍽️", + "fork_and_knife": "🍴", + "spoon": "🥄", + "hocho": "🔪", + "knife": "🔪", + "jar": "🫙", + "amphora": "🏺", + "earth_africa": "🌍", + "earth_americas": "🌎", + "earth_asia": "🌏", + "globe_with_meridians": "🌐", + "world_map": "🗺️", + "japan": "🗾", + "compass": "🧭", + "mountain_snow": "🏔️", + "mountain": "⛰️", + "volcano": "🌋", + "mount_fuji": "🗻", + "camping": "🏕️", + "beach_umbrella": "🏖️", + "desert": "🏜️", + "desert_island": "🏝️", + "national_park": "🏞️", + "stadium": "🏟️", + "classical_building": "🏛️", + "building_construction": "🏗️", + "bricks": "🧱", + "rock": "🪨", + "wood": "🪵", + "hut": "🛖", + "houses": "🏘️", + "derelict_house": "🏚️", + "house": "🏠", + "house_with_garden": "🏡", + "office": "🏢", + "post_office": "🏣", + "european_post_office": "🏤", + "hospital": "🏥", + "bank": "🏦", + "hotel": "🏨", + "love_hotel": "🏩", + "convenience_store": "🏪", + "school": "🏫", + "department_store": "🏬", + "factory": "🏭", + "japanese_castle": "🏯", + "european_castle": "🏰", + "wedding": "💒", + "tokyo_tower": "🗼", + "statue_of_liberty": "🗽", + "church": "⛪", + "mosque": "🕌", + "hindu_temple": "🛕", + "synagogue": "🕍", + "shinto_shrine": "⛩️", + "kaaba": "🕋", + "fountain": "⛲", + "tent": "⛺", + "foggy": "🌁", + "night_with_stars": "🌃", + "cityscape": "🏙️", + "sunrise_over_mountains": "🌄", + "sunrise": "🌅", + "city_sunset": "🌆", + "city_sunrise": "🌇", + "bridge_at_night": "🌉", + "hotsprings": "♨️", + "carousel_horse": "🎠", + "playground_slide": "🛝", + "ferris_wheel": "🎡", + "roller_coaster": "🎢", + "barber": "💈", + "circus_tent": "🎪", + "steam_locomotive": "🚂", + "railway_car": "🚃", + "bullettrain_side": "🚄", + "bullettrain_front": "🚅", + "train2": "🚆", + "metro": "🚇", + "light_rail": "🚈", + "station": "🚉", + "tram": "🚊", + "monorail": "🚝", + "mountain_railway": "🚞", + "train": "🚋", + "bus": "🚌", + "oncoming_bus": "🚍", + "trolleybus": "🚎", + "minibus": "🚐", + "ambulance": "🚑", + "fire_engine": "🚒", + "police_car": "🚓", + "oncoming_police_car": "🚔", + "taxi": "🚕", + "oncoming_taxi": "🚖", + "car": "🚗", + "red_car": "🚗", + "oncoming_automobile": "🚘", + "blue_car": "🚙", + "pickup_truck": "🛻", + "truck": "🚚", + "articulated_lorry": "🚛", + "tractor": "🚜", + "racing_car": "🏎️", + "motorcycle": "🏍️", + "motor_scooter": "🛵", + "manual_wheelchair": "🦽", + "motorized_wheelchair": "🦼", + "auto_rickshaw": "🛺", + "bike": "🚲", + "kick_scooter": "🛴", + "skateboard": "🛹", + "roller_skate": "🛼", + "busstop": "🚏", + "motorway": "🛣️", + "railway_track": "🛤️", + "oil_drum": "🛢️", + "fuelpump": "⛽", + "wheel": "🛞", + "rotating_light": "🚨", + "traffic_light": "🚥", + "vertical_traffic_light": "🚦", + "stop_sign": "🛑", + "construction": "🚧", + "anchor": "⚓", + "ring_buoy": "🛟", + "boat": "⛵", + "sailboat": "⛵", + "canoe": "🛶", + "speedboat": "🚤", + "passenger_ship": "🛳️", + "ferry": "⛴️", + "motor_boat": "🛥️", + "ship": "🚢", + "airplane": "✈️", + "small_airplane": "🛩️", + "flight_departure": "🛫", + "flight_arrival": "🛬", + "parachute": "🪂", + "seat": "💺", + "helicopter": "🚁", + "suspension_railway": "🚟", + "mountain_cableway": "🚠", + "aerial_tramway": "🚡", + "artificial_satellite": "🛰️", + "rocket": "🚀", + "flying_saucer": "🛸", + "bellhop_bell": "🛎️", + "luggage": "🧳", + "hourglass": "⌛", + "hourglass_flowing_sand": "⏳", + "watch": "⌚", + "alarm_clock": "⏰", + "stopwatch": "⏱️", + "timer_clock": "⏲️", + "mantelpiece_clock": "🕰️", + "clock12": "🕛", + "clock1230": "🕧", + "clock1": "🕐", + "clock130": "🕜", + "clock2": "🕑", + "clock230": "🕝", + "clock3": "🕒", + "clock330": "🕞", + "clock4": "🕓", + "clock430": "🕟", + "clock5": "🕔", + "clock530": "🕠", + "clock6": "🕕", + "clock630": "🕡", + "clock7": "🕖", + "clock730": "🕢", + "clock8": "🕗", + "clock830": "🕣", + "clock9": "🕘", + "clock930": "🕤", + "clock10": "🕙", + "clock1030": "🕥", + "clock11": "🕚", + "clock1130": "🕦", + "new_moon": "🌑", + "waxing_crescent_moon": "🌒", + "first_quarter_moon": "🌓", + "moon": "🌔", + "waxing_gibbous_moon": "🌔", + "full_moon": "🌕", + "waning_gibbous_moon": "🌖", + "last_quarter_moon": "🌗", + "waning_crescent_moon": "🌘", + "crescent_moon": "🌙", + "new_moon_with_face": "🌚", + "first_quarter_moon_with_face": "🌛", + "last_quarter_moon_with_face": "🌜", + "thermometer": "🌡️", + "sunny": "☀️", + "full_moon_with_face": "🌝", + "sun_with_face": "🌞", + "ringed_planet": "🪐", + "star": "⭐", + "star2": "🌟", + "stars": "🌠", + "milky_way": "🌌", + "cloud": "☁️", + "partly_sunny": "⛅", + "cloud_with_lightning_and_rain": "⛈️", + "sun_behind_small_cloud": "🌤️", + "sun_behind_large_cloud": "🌥️", + "sun_behind_rain_cloud": "🌦️", + "cloud_with_rain": "🌧️", + "cloud_with_snow": "🌨️", + "cloud_with_lightning": "🌩️", + "tornado": "🌪️", + "fog": "🌫️", + "wind_face": "🌬️", + "cyclone": "🌀", + "rainbow": "🌈", + "closed_umbrella": "🌂", + "open_umbrella": "☂️", + "umbrella": "☔", + "parasol_on_ground": "⛱️", + "zap": "⚡", + "snowflake": "❄️", + "snowman_with_snow": "☃️", + "snowman": "⛄", + "comet": "☄️", + "fire": "🔥", + "droplet": "💧", + "ocean": "🌊", + "jack_o_lantern": "🎃", + "christmas_tree": "🎄", + "fireworks": "🎆", + "sparkler": "🎇", + "firecracker": "🧨", + "sparkles": "✨", + "balloon": "🎈", + "tada": "🎉", + "confetti_ball": "🎊", + "tanabata_tree": "🎋", + "bamboo": "🎍", + "dolls": "🎎", + "flags": "🎏", + "wind_chime": "🎐", + "rice_scene": "🎑", + "red_envelope": "🧧", + "ribbon": "🎀", + "gift": "🎁", + "reminder_ribbon": "🎗️", + "tickets": "🎟️", + "ticket": "🎫", + "medal_military": "🎖️", + "trophy": "🏆", + "medal_sports": "🏅", + "1st_place_medal": "🥇", + "2nd_place_medal": "🥈", + "3rd_place_medal": "🥉", + "soccer": "⚽", + "baseball": "⚾", + "softball": "🥎", + "basketball": "🏀", + "volleyball": "🏐", + "football": "🏈", + "rugby_football": "🏉", + "tennis": "🎾", + "flying_disc": "🥏", + "bowling": "🎳", + "cricket_game": "🏏", + "field_hockey": "🏑", + "ice_hockey": "🏒", + "lacrosse": "🥍", + "ping_pong": "🏓", + "badminton": "🏸", + "boxing_glove": "🥊", + "martial_arts_uniform": "🥋", + "goal_net": "🥅", + "golf": "⛳", + "ice_skate": "⛸️", + "fishing_pole_and_fish": "🎣", + "diving_mask": "🤿", + "running_shirt_with_sash": "🎽", + "ski": "🎿", + "sled": "🛷", + "curling_stone": "🥌", + "dart": "🎯", + "yo_yo": "🪀", + "kite": "🪁", + "gun": "🔫", + "8ball": "🎱", + "crystal_ball": "🔮", + "magic_wand": "🪄", + "video_game": "🎮", + "joystick": "🕹️", + "slot_machine": "🎰", + "game_die": "🎲", + "jigsaw": "🧩", + "teddy_bear": "🧸", + "pinata": "🪅", + "mirror_ball": "🪩", + "nesting_dolls": "🪆", + "spades": "♠️", + "hearts": "♥️", + "diamonds": "♦️", + "clubs": "♣️", + "chess_pawn": "♟️", + "black_joker": "🃏", + "mahjong": "🀄", + "flower_playing_cards": "🎴", + "performing_arts": "🎭", + "framed_picture": "🖼️", + "art": "🎨", + "thread": "🧵", + "sewing_needle": "🪡", + "yarn": "🧶", + "knot": "🪢", + "eyeglasses": "👓", + "dark_sunglasses": "🕶️", + "goggles": "🥽", + "lab_coat": "🥼", + "safety_vest": "🦺", + "necktie": "👔", + "shirt": "👕", + "tshirt": "👕", + "jeans": "👖", + "scarf": "🧣", + "gloves": "🧤", + "coat": "🧥", + "socks": "🧦", + "dress": "👗", + "kimono": "👘", + "sari": "🥻", + "one_piece_swimsuit": "🩱", + "swim_brief": "🩲", + "shorts": "🩳", + "bikini": "👙", + "womans_clothes": "👚", + "folding_hand_fan": "🪭", + "purse": "👛", + "handbag": "👜", + "pouch": "👝", + "shopping": "🛍️", + "school_satchel": "🎒", + "thong_sandal": "🩴", + "mans_shoe": "👞", + "shoe": "👞", + "athletic_shoe": "👟", + "hiking_boot": "🥾", + "flat_shoe": "🥿", + "high_heel": "👠", + "sandal": "👡", + "ballet_shoes": "🩰", + "boot": "👢", + "hair_pick": "🪮", + "crown": "👑", + "womans_hat": "👒", + "tophat": "🎩", + "mortar_board": "🎓", + "billed_cap": "🧢", + "military_helmet": "🪖", + "rescue_worker_helmet": "⛑️", + "prayer_beads": "📿", + "lipstick": "💄", + "ring": "💍", + "gem": "💎", + "mute": "🔇", + "speaker": "🔈", + "sound": "🔉", + "loud_sound": "🔊", + "loudspeaker": "📢", + "mega": "📣", + "postal_horn": "📯", + "bell": "🔔", + "no_bell": "🔕", + "musical_score": "🎼", + "musical_note": "🎵", + "notes": "🎶", + "studio_microphone": "🎙️", + "level_slider": "🎚️", + "control_knobs": "🎛️", + "microphone": "🎤", + "headphones": "🎧", + "radio": "📻", + "saxophone": "🎷", + "accordion": "🪗", + "guitar": "🎸", + "musical_keyboard": "🎹", + "trumpet": "🎺", + "violin": "🎻", + "banjo": "🪕", + "drum": "🥁", + "long_drum": "🪘", + "maracas": "🪇", + "flute": "🪈", + "iphone": "📱", + "calling": "📲", + "phone": "☎️", + "telephone": "☎️", + "telephone_receiver": "📞", + "pager": "📟", + "fax": "📠", + "battery": "🔋", + "low_battery": "🪫", + "electric_plug": "🔌", + "computer": "💻", + "desktop_computer": "🖥️", + "printer": "🖨️", + "keyboard": "⌨️", + "computer_mouse": "🖱️", + "trackball": "🖲️", + "minidisc": "💽", + "floppy_disk": "💾", + "cd": "💿", + "dvd": "📀", + "abacus": "🧮", + "movie_camera": "🎥", + "film_strip": "🎞️", + "film_projector": "📽️", + "clapper": "🎬", + "tv": "📺", + "camera": "📷", + "camera_flash": "📸", + "video_camera": "📹", + "vhs": "📼", + "mag": "🔍", + "mag_right": "🔎", + "candle": "🕯️", + "bulb": "💡", + "flashlight": "🔦", + "izakaya_lantern": "🏮", + "lantern": "🏮", + "diya_lamp": "🪔", + "notebook_with_decorative_cover": "📔", + "closed_book": "📕", + "book": "📖", + "open_book": "📖", + "green_book": "📗", + "blue_book": "📘", + "orange_book": "📙", + "books": "📚", + "notebook": "📓", + "ledger": "📒", + "page_with_curl": "📃", + "scroll": "📜", + "page_facing_up": "📄", + "newspaper": "📰", + "newspaper_roll": "🗞️", + "bookmark_tabs": "📑", + "bookmark": "🔖", + "label": "🏷️", + "moneybag": "💰", + "coin": "🪙", + "yen": "💴", + "dollar": "💵", + "euro": "💶", + "pound": "💷", + "money_with_wings": "💸", + "credit_card": "💳", + "receipt": "🧾", + "chart": "💹", + "envelope": "✉️", + "email": "📧", + "e-mail": "📧", + "incoming_envelope": "📨", + "envelope_with_arrow": "📩", + "outbox_tray": "📤", + "inbox_tray": "📥", + "package": "📦", + "mailbox": "📫", + "mailbox_closed": "📪", + "mailbox_with_mail": "📬", + "mailbox_with_no_mail": "📭", + "postbox": "📮", + "ballot_box": "🗳️", + "pencil2": "✏️", + "black_nib": "✒️", + "fountain_pen": "🖋️", + "pen": "🖊️", + "paintbrush": "🖌️", + "crayon": "🖍️", + "memo": "📝", + "pencil": "📝", + "briefcase": "💼", + "file_folder": "📁", + "open_file_folder": "📂", + "card_index_dividers": "🗂️", + "date": "📅", + "calendar": "📆", + "spiral_notepad": "🗒️", + "spiral_calendar": "🗓️", + "card_index": "📇", + "chart_with_upwards_trend": "📈", + "chart_with_downwards_trend": "📉", + "bar_chart": "📊", + "clipboard": "📋", + "pushpin": "📌", + "round_pushpin": "📍", + "paperclip": "📎", + "paperclips": "🖇️", + "straight_ruler": "📏", + "triangular_ruler": "📐", + "scissors": "✂️", + "card_file_box": "🗃️", + "file_cabinet": "🗄️", + "wastebasket": "🗑️", + "lock": "🔒", + "unlock": "🔓", + "lock_with_ink_pen": "🔏", + "closed_lock_with_key": "🔐", + "key": "🔑", + "old_key": "🗝️", + "hammer": "🔨", + "axe": "🪓", + "pick": "⛏️", + "hammer_and_pick": "⚒️", + "hammer_and_wrench": "🛠️", + "dagger": "🗡️", + "crossed_swords": "⚔️", + "bomb": "💣", + "boomerang": "🪃", + "bow_and_arrow": "🏹", + "shield": "🛡️", + "carpentry_saw": "🪚", + "wrench": "🔧", + "screwdriver": "🪛", + "nut_and_bolt": "🔩", + "gear": "⚙️", + "clamp": "🗜️", + "balance_scale": "⚖️", + "probing_cane": "🦯", + "link": "🔗", + "chains": "⛓️", + "hook": "🪝", + "toolbox": "🧰", + "magnet": "🧲", + "ladder": "🪜", + "alembic": "⚗️", + "test_tube": "🧪", + "petri_dish": "🧫", + "dna": "🧬", + "microscope": "🔬", + "telescope": "🔭", + "satellite": "📡", + "syringe": "💉", + "drop_of_blood": "🩸", + "pill": "💊", + "adhesive_bandage": "🩹", + "crutch": "🩼", + "stethoscope": "🩺", + "x_ray": "🩻", + "door": "🚪", + "elevator": "🛗", + "mirror": "🪞", + "window": "🪟", + "bed": "🛏️", + "couch_and_lamp": "🛋️", + "chair": "🪑", + "toilet": "🚽", + "plunger": "🪠", + "shower": "🚿", + "bathtub": "🛁", + "mouse_trap": "🪤", + "razor": "🪒", + "lotion_bottle": "🧴", + "safety_pin": "🧷", + "broom": "🧹", + "basket": "🧺", + "roll_of_paper": "🧻", + "bucket": "🪣", + "soap": "🧼", + "bubbles": "🫧", + "toothbrush": "🪥", + "sponge": "🧽", + "fire_extinguisher": "🧯", + "shopping_cart": "🛒", + "smoking": "🚬", + "coffin": "⚰️", + "headstone": "🪦", + "funeral_urn": "⚱️", + "nazar_amulet": "🧿", + "hamsa": "🪬", + "moyai": "🗿", + "placard": "🪧", + "identification_card": "🪪", + "atm": "🏧", + "put_litter_in_its_place": "🚮", + "potable_water": "🚰", + "wheelchair": "♿", + "mens": "🚹", + "womens": "🚺", + "restroom": "🚻", + "baby_symbol": "🚼", + "wc": "🚾", + "passport_control": "🛂", + "customs": "🛃", + "baggage_claim": "🛄", + "left_luggage": "🛅", + "warning": "⚠️", + "children_crossing": "🚸", + "no_entry": "⛔", + "no_entry_sign": "🚫", + "no_bicycles": "🚳", + "no_smoking": "🚭", + "do_not_litter": "🚯", + "non-potable_water": "🚱", + "no_pedestrians": "🚷", + "no_mobile_phones": "📵", + "underage": "🔞", + "radioactive": "☢️", + "biohazard": "☣️", + "arrow_up": "⬆️", + "arrow_upper_right": "↗️", + "arrow_right": "➡️", + "arrow_lower_right": "↘️", + "arrow_down": "⬇️", + "arrow_lower_left": "↙️", + "arrow_left": "⬅️", + "arrow_upper_left": "↖️", + "arrow_up_down": "↕️", + "left_right_arrow": "↔️", + "leftwards_arrow_with_hook": "↩️", + "arrow_right_hook": "↪️", + "arrow_heading_up": "⤴️", + "arrow_heading_down": "⤵️", + "arrows_clockwise": "🔃", + "arrows_counterclockwise": "🔄", + "back": "🔙", + "end": "🔚", + "on": "🔛", + "soon": "🔜", + "top": "🔝", + "place_of_worship": "🛐", + "atom_symbol": "⚛️", + "om": "🕉️", + "star_of_david": "✡️", + "wheel_of_dharma": "☸️", + "yin_yang": "☯️", + "latin_cross": "✝️", + "orthodox_cross": "☦️", + "star_and_crescent": "☪️", + "peace_symbol": "☮️", + "menorah": "🕎", + "six_pointed_star": "🔯", + "khanda": "🪯", + "aries": "♈", + "taurus": "♉", + "gemini": "♊", + "cancer": "♋", + "leo": "♌", + "virgo": "♍", + "libra": "♎", + "scorpius": "♏", + "sagittarius": "♐", + "capricorn": "♑", + "aquarius": "♒", + "pisces": "♓", + "ophiuchus": "⛎", + "twisted_rightwards_arrows": "🔀", + "repeat": "🔁", + "repeat_one": "🔂", + "arrow_forward": "▶️", + "fast_forward": "⏩", + "next_track_button": "⏭️", + "play_or_pause_button": "⏯️", + "arrow_backward": "◀️", + "rewind": "⏪", + "previous_track_button": "⏮️", + "arrow_up_small": "🔼", + "arrow_double_up": "⏫", + "arrow_down_small": "🔽", + "arrow_double_down": "⏬", + "pause_button": "⏸️", + "stop_button": "⏹️", + "record_button": "⏺️", + "eject_button": "⏏️", + "cinema": "🎦", + "low_brightness": "🔅", + "high_brightness": "🔆", + "signal_strength": "📶", + "wireless": "🛜", + "vibration_mode": "📳", + "mobile_phone_off": "📴", + "female_sign": "♀️", + "male_sign": "♂️", + "transgender_symbol": "⚧️", + "heavy_multiplication_x": "✖️", + "heavy_plus_sign": "➕", + "heavy_minus_sign": "➖", + "heavy_division_sign": "➗", + "heavy_equals_sign": "🟰", + "infinity": "♾️", + "bangbang": "‼️", + "interrobang": "⁉️", + "question": "❓", + "grey_question": "❔", + "grey_exclamation": "❕", + "exclamation": "❗", + "heavy_exclamation_mark": "❗", + "wavy_dash": "〰️", + "currency_exchange": "💱", + "heavy_dollar_sign": "💲", + "medical_symbol": "⚕️", + "recycle": "♻️", + "fleur_de_lis": "⚜️", + "trident": "🔱", + "name_badge": "📛", + "beginner": "🔰", + "o": "⭕", + "white_check_mark": "✅", + "ballot_box_with_check": "☑️", + "heavy_check_mark": "✔️", + "x": "❌", + "negative_squared_cross_mark": "❎", + "curly_loop": "➰", + "loop": "➿", + "part_alternation_mark": "〽️", + "eight_spoked_asterisk": "✳️", + "eight_pointed_black_star": "✴️", + "sparkle": "❇️", + "copyright": "©️", + "registered": "®️", + "tm": "™️", + "hash": "#️⃣", + "asterisk": "*️⃣", + "zero": "0️⃣", + "one": "1️⃣", + "two": "2️⃣", + "three": "3️⃣", + "four": "4️⃣", + "five": "5️⃣", + "six": "6️⃣", + "seven": "7️⃣", + "eight": "8️⃣", + "nine": "9️⃣", + "keycap_ten": "🔟", + "capital_abcd": "🔠", + "abcd": "🔡", + "symbols": "🔣", + "abc": "🔤", + "a": "🅰️", + "ab": "🆎", + "b": "🅱️", + "cl": "🆑", + "cool": "🆒", + "free": "🆓", + "information_source": "ℹ️", + "id": "🆔", + "m": "Ⓜ️", + "new": "🆕", + "ng": "🆖", + "o2": "🅾️", + "ok": "🆗", + "parking": "🅿️", + "sos": "🆘", + "up": "🆙", + "vs": "🆚", + "koko": "🈁", + "sa": "🈂️", + "ideograph_advantage": "🉐", + "accept": "🉑", + "congratulations": "㊗️", + "secret": "㊙️", + "u6e80": "🈵", + "red_circle": "🔴", + "orange_circle": "🟠", + "yellow_circle": "🟡", + "green_circle": "🟢", + "large_blue_circle": "🔵", + "purple_circle": "🟣", + "brown_circle": "🟤", + "black_circle": "⚫", + "white_circle": "⚪", + "red_square": "🟥", + "orange_square": "🟧", + "yellow_square": "🟨", + "green_square": "🟩", + "blue_square": "🟦", + "purple_square": "🟪", + "brown_square": "🟫", + "black_large_square": "⬛", + "white_large_square": "⬜", + "black_medium_square": "◼️", + "white_medium_square": "◻️", + "black_medium_small_square": "◾", + "white_medium_small_square": "◽", + "black_small_square": "▪️", + "white_small_square": "▫️", + "large_orange_diamond": "🔶", + "large_blue_diamond": "🔷", + "small_orange_diamond": "🔸", + "small_blue_diamond": "🔹", + "small_red_triangle": "🔺", + "small_red_triangle_down": "🔻", + "diamond_shape_with_a_dot_inside": "💠", + "radio_button": "🔘", + "white_square_button": "🔳", + "black_square_button": "🔲", + "checkered_flag": "🏁", + "triangular_flag_on_post": "🚩", + "crossed_flags": "🎌", + "black_flag": "🏴", + "white_flag": "🏳️", + "rainbow_flag": "🏳️‍🌈", + "transgender_flag": "🏳️‍⚧️", + "pirate_flag": "🏴‍☠️", + "ascension_island": "🇦🇨", + "andorra": "🇦🇩", + "united_arab_emirates": "🇦🇪", + "afghanistan": "🇦🇫", + "antigua_barbuda": "🇦🇬", + "anguilla": "🇦🇮", + "albania": "🇦🇱", + "armenia": "🇦🇲", + "angola": "🇦🇴", + "antarctica": "🇦🇶", + "argentina": "🇦🇷", + "american_samoa": "🇦🇸", + "austria": "🇦🇹", + "australia": "🇦🇺", + "aruba": "🇦🇼", + "aland_islands": "🇦🇽", + "azerbaijan": "🇦🇿", + "bosnia_herzegovina": "🇧🇦", + "barbados": "🇧🇧", + "bangladesh": "🇧🇩", + "belgium": "🇧🇪", + "burkina_faso": "🇧🇫", + "bulgaria": "🇧🇬", + "bahrain": "🇧🇭", + "burundi": "🇧🇮", + "benin": "🇧🇯", + "st_barthelemy": "🇧🇱", + "bermuda": "🇧🇲", + "brunei": "🇧🇳", + "bolivia": "🇧🇴", + "caribbean_netherlands": "🇧🇶", + "brazil": "🇧🇷", + "bahamas": "🇧🇸", + "bhutan": "🇧🇹", + "bouvet_island": "🇧🇻", + "botswana": "🇧🇼", + "belarus": "🇧🇾", + "belize": "🇧🇿", + "canada": "🇨🇦", + "cocos_islands": "🇨🇨", + "congo_kinshasa": "🇨🇩", + "central_african_republic": "🇨🇫", + "congo_brazzaville": "🇨🇬", + "switzerland": "🇨🇭", + "cote_divoire": "🇨🇮", + "cook_islands": "🇨🇰", + "chile": "🇨🇱", + "cameroon": "🇨🇲", + "cn": "🇨🇳", + "colombia": "🇨🇴", + "clipperton_island": "🇨🇵", + "costa_rica": "🇨🇷", + "cuba": "🇨🇺", + "cape_verde": "🇨🇻", + "curacao": "🇨🇼", + "christmas_island": "🇨🇽", + "cyprus": "🇨🇾", + "czech_republic": "🇨🇿", + "de": "🇩🇪", + "diego_garcia": "🇩🇬", + "djibouti": "🇩🇯", + "denmark": "🇩🇰", + "dominica": "🇩🇲", + "dominican_republic": "🇩🇴", + "algeria": "🇩🇿", + "ceuta_melilla": "🇪🇦", + "ecuador": "🇪🇨", + "estonia": "🇪🇪", + "egypt": "🇪🇬", + "western_sahara": "🇪🇭", + "eritrea": "🇪🇷", + "es": "🇪🇸", + "ethiopia": "🇪🇹", + "eu": "🇪🇺", + "european_union": "🇪🇺", + "finland": "🇫🇮", + "fiji": "🇫🇯", + "falkland_islands": "🇫🇰", + "micronesia": "🇫🇲", + "faroe_islands": "🇫🇴", + "fr": "🇫🇷", + "gabon": "🇬🇦", + "gb": "🇬🇧", + "uk": "🇬🇧", + "grenada": "🇬🇩", + "georgia": "🇬🇪", + "french_guiana": "🇬🇫", + "guernsey": "🇬🇬", + "ghana": "🇬🇭", + "gibraltar": "🇬🇮", + "greenland": "🇬🇱", + "gambia": "🇬🇲", + "guinea": "🇬🇳", + "guadeloupe": "🇬🇵", + "equatorial_guinea": "🇬🇶", + "greece": "🇬🇷", + "south_georgia_south_sandwich_islands": "🇬🇸", + "guatemala": "🇬🇹", + "guam": "🇬🇺", + "guinea_bissau": "🇬🇼", + "guyana": "🇬🇾", + "hong_kong": "🇭🇰", + "heard_mcdonald_islands": "🇭🇲", + "honduras": "🇭🇳", + "croatia": "🇭🇷", + "haiti": "🇭🇹", + "hungary": "🇭🇺", + "canary_islands": "🇮🇨", + "indonesia": "🇮🇩", + "ireland": "🇮🇪", + "israel": "🇮🇱", + "isle_of_man": "🇮🇲", + "india": "🇮🇳", + "british_indian_ocean_territory": "🇮🇴", + "iraq": "🇮🇶", + "iran": "🇮🇷", + "iceland": "🇮🇸", + "it": "🇮🇹", + "jersey": "🇯🇪", + "jamaica": "🇯🇲", + "jordan": "🇯🇴", + "jp": "🇯🇵", + "kenya": "🇰🇪", + "kyrgyzstan": "🇰🇬", + "cambodia": "🇰🇭", + "kiribati": "🇰🇮", + "comoros": "🇰🇲", + "st_kitts_nevis": "🇰🇳", + "north_korea": "🇰🇵", + "kr": "🇰🇷", + "kuwait": "🇰🇼", + "cayman_islands": "🇰🇾", + "kazakhstan": "🇰🇿", + "laos": "🇱🇦", + "lebanon": "🇱🇧", + "st_lucia": "🇱🇨", + "liechtenstein": "🇱🇮", + "sri_lanka": "🇱🇰", + "liberia": "🇱🇷", + "lesotho": "🇱🇸", + "lithuania": "🇱🇹", + "luxembourg": "🇱🇺", + "latvia": "🇱🇻", + "libya": "🇱🇾", + "morocco": "🇲🇦", + "monaco": "🇲🇨", + "moldova": "🇲🇩", + "montenegro": "🇲🇪", + "st_martin": "🇲🇫", + "madagascar": "🇲🇬", + "marshall_islands": "🇲🇭", + "macedonia": "🇲🇰", + "mali": "🇲🇱", + "myanmar": "🇲🇲", + "mongolia": "🇲🇳", + "macau": "🇲🇴", + "northern_mariana_islands": "🇲🇵", + "martinique": "🇲🇶", + "mauritania": "🇲🇷", + "montserrat": "🇲🇸", + "malta": "🇲🇹", + "mauritius": "🇲🇺", + "maldives": "🇲🇻", + "malawi": "🇲🇼", + "mexico": "🇲🇽", + "malaysia": "🇲🇾", + "mozambique": "🇲🇿", + "namibia": "🇳🇦", + "new_caledonia": "🇳🇨", + "niger": "🇳🇪", + "norfolk_island": "🇳🇫", + "nigeria": "🇳🇬", + "nicaragua": "🇳🇮", + "netherlands": "🇳🇱", + "norway": "🇳🇴", + "nepal": "🇳🇵", + "nauru": "🇳🇷", + "niue": "🇳🇺", + "new_zealand": "🇳🇿", + "oman": "🇴🇲", + "panama": "🇵🇦", + "peru": "🇵🇪", + "french_polynesia": "🇵🇫", + "papua_new_guinea": "🇵🇬", + "philippines": "🇵🇭", + "pakistan": "🇵🇰", + "poland": "🇵🇱", + "st_pierre_miquelon": "🇵🇲", + "pitcairn_islands": "🇵🇳", + "puerto_rico": "🇵🇷", + "palestinian_territories": "🇵🇸", + "portugal": "🇵🇹", + "palau": "🇵🇼", + "paraguay": "🇵🇾", + "qatar": "🇶🇦", + "reunion": "🇷🇪", + "romania": "🇷🇴", + "serbia": "🇷🇸", + "ru": "🇷🇺", + "rwanda": "🇷🇼", + "saudi_arabia": "🇸🇦", + "solomon_islands": "🇸🇧", + "seychelles": "🇸🇨", + "sudan": "🇸🇩", + "sweden": "🇸🇪", + "singapore": "🇸🇬", + "st_helena": "🇸🇭", + "slovenia": "🇸🇮", + "svalbard_jan_mayen": "🇸🇯", + "slovakia": "🇸🇰", + "sierra_leone": "🇸🇱", + "san_marino": "🇸🇲", + "senegal": "🇸🇳", + "somalia": "🇸🇴", + "suriname": "🇸🇷", + "south_sudan": "🇸🇸", + "sao_tome_principe": "🇸🇹", + "el_salvador": "🇸🇻", + "sint_maarten": "🇸🇽", + "syria": "🇸🇾", + "swaziland": "🇸🇿", + "tristan_da_cunha": "🇹🇦", + "turks_caicos_islands": "🇹🇨", + "chad": "🇹🇩", + "french_southern_territories": "🇹🇫", + "togo": "🇹🇬", + "thailand": "🇹🇭", + "tajikistan": "🇹🇯", + "tokelau": "🇹🇰", + "timor_leste": "🇹🇱", + "turkmenistan": "🇹🇲", + "tunisia": "🇹🇳", + "tonga": "🇹🇴", + "tr": "🇹🇷", + "trinidad_tobago": "🇹🇹", + "tuvalu": "🇹🇻", + "taiwan": "🇹🇼", + "tanzania": "🇹🇿", + "ukraine": "🇺🇦", + "uganda": "🇺🇬", + "us_outlying_islands": "🇺🇲", + "united_nations": "🇺🇳", + "us": "🇺🇸", + "uruguay": "🇺🇾", + "uzbekistan": "🇺🇿", + "vatican_city": "🇻🇦", + "st_vincent_grenadines": "🇻🇨", + "venezuela": "🇻🇪", + "british_virgin_islands": "🇻🇬", + "us_virgin_islands": "🇻🇮", + "vietnam": "🇻🇳", + "vanuatu": "🇻🇺", + "wallis_futuna": "🇼🇫", + "samoa": "🇼🇸", + "kosovo": "🇽🇰", + "yemen": "🇾🇪", + "mayotte": "🇾🇹", + "south_africa": "🇿🇦", + "zambia": "🇿🇲", + "zimbabwe": "🇿🇼", + "england": "🏴󠁧󠁢󠁥󠁮󠁧󠁿", + "scotland": "🏴󠁧󠁢󠁳󠁣󠁴󠁿", + "wales": "🏴󠁧󠁢󠁷󠁬󠁳󠁿" +}; + +export default emojies; diff --git a/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/data/light.ts b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/data/light.ts new file mode 100644 index 0000000..acaed2f --- /dev/null +++ b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/data/light.ts @@ -0,0 +1,158 @@ +// Generated, don't edit +import { EmojiDefs } from '../normalize_opts'; + +const emojies: EmojiDefs = { + "grinning": "😀", + "smiley": "😃", + "smile": "😄", + "grin": "😁", + "laughing": "😆", + "satisfied": "😆", + "sweat_smile": "😅", + "joy": "😂", + "wink": "😉", + "blush": "😊", + "innocent": "😇", + "heart_eyes": "😍", + "kissing_heart": "😘", + "kissing": "😗", + "kissing_closed_eyes": "😚", + "kissing_smiling_eyes": "😙", + "yum": "😋", + "stuck_out_tongue": "😛", + "stuck_out_tongue_winking_eye": "😜", + "stuck_out_tongue_closed_eyes": "😝", + "neutral_face": "😐", + "expressionless": "😑", + "no_mouth": "😶", + "smirk": "😏", + "unamused": "😒", + "relieved": "😌", + "pensive": "😔", + "sleepy": "😪", + "sleeping": "😴", + "mask": "😷", + "dizzy_face": "😵", + "sunglasses": "😎", + "confused": "😕", + "worried": "😟", + "open_mouth": "😮", + "hushed": "😯", + "astonished": "😲", + "flushed": "😳", + "frowning": "😦", + "anguished": "😧", + "fearful": "😨", + "cold_sweat": "😰", + "disappointed_relieved": "😥", + "cry": "😢", + "sob": "😭", + "scream": "😱", + "confounded": "😖", + "persevere": "😣", + "disappointed": "😞", + "sweat": "😓", + "weary": "😩", + "tired_face": "😫", + "rage": "😡", + "pout": "😡", + "angry": "😠", + "smiling_imp": "😈", + "smiley_cat": "😺", + "smile_cat": "😸", + "joy_cat": "😹", + "heart_eyes_cat": "😻", + "smirk_cat": "😼", + "kissing_cat": "😽", + "scream_cat": "🙀", + "crying_cat_face": "😿", + "pouting_cat": "😾", + "heart": "❤️", + "hand": "✋", + "raised_hand": "✋", + "v": "✌️", + "point_up": "☝️", + "fist_raised": "✊", + "fist": "✊", + "monkey_face": "🐵", + "cat": "🐱", + "cow": "🐮", + "mouse": "🐭", + "coffee": "☕", + "hotsprings": "♨️", + "anchor": "⚓", + "airplane": "✈️", + "hourglass": "⌛", + "watch": "⌚", + "sunny": "☀️", + "star": "⭐", + "cloud": "☁️", + "umbrella": "☔", + "zap": "⚡", + "snowflake": "❄️", + "sparkles": "✨", + "black_joker": "🃏", + "mahjong": "🀄", + "phone": "☎️", + "telephone": "☎️", + "envelope": "✉️", + "pencil2": "✏️", + "black_nib": "✒️", + "scissors": "✂️", + "wheelchair": "♿", + "warning": "⚠️", + "aries": "♈", + "taurus": "♉", + "gemini": "♊", + "cancer": "♋", + "leo": "♌", + "virgo": "♍", + "libra": "♎", + "scorpius": "♏", + "sagittarius": "♐", + "capricorn": "♑", + "aquarius": "♒", + "pisces": "♓", + "heavy_multiplication_x": "✖️", + "heavy_plus_sign": "➕", + "heavy_minus_sign": "➖", + "heavy_division_sign": "➗", + "bangbang": "‼️", + "interrobang": "⁉️", + "question": "❓", + "grey_question": "❔", + "grey_exclamation": "❕", + "exclamation": "❗", + "heavy_exclamation_mark": "❗", + "wavy_dash": "〰️", + "recycle": "♻️", + "white_check_mark": "✅", + "ballot_box_with_check": "☑️", + "heavy_check_mark": "✔️", + "x": "❌", + "negative_squared_cross_mark": "❎", + "curly_loop": "➰", + "loop": "➿", + "part_alternation_mark": "〽️", + "eight_spoked_asterisk": "✳️", + "eight_pointed_black_star": "✴️", + "sparkle": "❇️", + "copyright": "©️", + "registered": "®️", + "tm": "™️", + "information_source": "ℹ️", + "m": "Ⓜ️", + "black_circle": "⚫", + "white_circle": "⚪", + "black_large_square": "⬛", + "white_large_square": "⬜", + "black_medium_square": "◼️", + "white_medium_square": "◻️", + "black_medium_small_square": "◾", + "white_medium_small_square": "◽", + "black_small_square": "▪️", + "white_small_square": "▫️" +}; + +export default emojies; + diff --git a/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/data/shortcuts.ts b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/data/shortcuts.ts new file mode 100644 index 0000000..418c6c0 --- /dev/null +++ b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/data/shortcuts.ts @@ -0,0 +1,45 @@ +// Emoticons -> Emoji mapping. +// +// (!) Some patterns skipped, to avoid collisions +// without increase matcher complicity. Than can change in future. +// +// Places to look for more emoticons info: +// +// - http://en.wikipedia.org/wiki/List_of_emoticons#Western +// - https://github.com/wooorm/emoticon/blob/master/Support.md +// - http://factoryjoe.com/projects/emoticons/ +// + +import { EmojiShortcuts } from '../normalize_opts'; + +const shortcuts: EmojiShortcuts = { + angry: ['>:(', '>:-('], + blush: [':")', ':-")'], + broken_heart: ['): void { + const defaults: EmojiOptions = { + defs: emojies_defs, + shortcuts: emojies_shortcuts, + enabled: [] + }; + + const opts = md.utils.assign({}, defaults, options || {}) as EmojiOptions; + + bare_emoji_plugin(md, opts); +} + diff --git a/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/light.ts b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/light.ts new file mode 100644 index 0000000..74f8321 --- /dev/null +++ b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/light.ts @@ -0,0 +1,21 @@ +import MarkdownIt from 'markdown-it'; +import emojies_defs from './data/light'; +import emojies_shortcuts from './data/shortcuts'; +import bare_emoji_plugin from './bare'; +import { EmojiOptions } from './normalize_opts'; + +/** + * Light emoji 插件(包含常用的 emoji 数据) + */ +export default function emoji_plugin(md: MarkdownIt, options?: Partial): void { + const defaults: EmojiOptions = { + defs: emojies_defs, + shortcuts: emojies_shortcuts, + enabled: [] + }; + + const opts = md.utils.assign({}, defaults, options || {}) as EmojiOptions; + + bare_emoji_plugin(md, opts); +} + diff --git a/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/normalize_opts.ts b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/normalize_opts.ts new file mode 100644 index 0000000..53eb426 --- /dev/null +++ b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/normalize_opts.ts @@ -0,0 +1,95 @@ +/** + * Emoji 定义类型 + */ +export interface EmojiDefs { + [key: string]: string; +} + +/** + * Emoji 快捷方式类型 + */ +export interface EmojiShortcuts { + [key: string]: string | string[]; +} + +/** + * 输入选项接口 + */ +export interface EmojiOptions { + defs: EmojiDefs; + shortcuts: EmojiShortcuts; + enabled: string[]; +} + +/** + * 标准化后的选项接口 + */ +export interface NormalizedEmojiOptions { + defs: EmojiDefs; + shortcuts: { [key: string]: string }; + scanRE: RegExp; + replaceRE: RegExp; +} + +/** + * 转义正则表达式特殊字符 + */ +function quoteRE(str: string): string { + return str.replace(/[.?*+^$[\]\\(){}|-]/g, '\\$&'); +} + +/** + * 将输入选项转换为更可用的格式并编译搜索正则表达式 + */ +export default function normalize_opts(options: EmojiOptions): NormalizedEmojiOptions { + let emojies = options.defs; + + // Filter emojies by whitelist, if needed + if (options.enabled.length) { + emojies = Object.keys(emojies).reduce((acc: EmojiDefs, key: string) => { + if (options.enabled.indexOf(key) >= 0) acc[key] = emojies[key]; + return acc; + }, {}); + } + + // Flatten shortcuts to simple object: { alias: emoji_name } + const shortcuts = Object.keys(options.shortcuts).reduce((acc: { [key: string]: string }, key: string) => { + // Skip aliases for filtered emojies, to reduce regexp + if (!emojies[key]) return acc; + + if (Array.isArray(options.shortcuts[key])) { + (options.shortcuts[key] as string[]).forEach((alias: string) => { acc[alias] = key; }); + return acc; + } + + acc[options.shortcuts[key] as string] = key; + return acc; + }, {}); + + const keys = Object.keys(emojies); + let names: string; + + // If no definitions are given, return empty regex to avoid replacements with 'undefined'. + if (keys.length === 0) { + names = '^$'; + } else { + // Compile regexp + names = keys + .map((name: string) => { return `:${name}:`; }) + .concat(Object.keys(shortcuts)) + .sort() + .reverse() + .map((name: string) => { return quoteRE(name); }) + .join('|'); + } + const scanRE = RegExp(names); + const replaceRE = RegExp(names, 'g'); + + return { + defs: emojies, + shortcuts, + scanRE, + replaceRE + }; +} + diff --git a/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/render.ts b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/render.ts new file mode 100644 index 0000000..db40f94 --- /dev/null +++ b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/render.ts @@ -0,0 +1,9 @@ +import { Token } from 'markdown-it'; + +/** + * Emoji 渲染函数 + */ +export default function emoji_html(tokens: Token[], idx: number): string { + return tokens[idx].content; +} + diff --git a/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/replace.ts b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/replace.ts new file mode 100644 index 0000000..556fb54 --- /dev/null +++ b/frontend/src/common/markdown-it/plugins/markdown-it-emojis/lib/replace.ts @@ -0,0 +1,97 @@ +import MarkdownIt, { StateCore, Token } from 'markdown-it'; +import { EmojiDefs } from './normalize_opts'; + +/** + * Emoji 和快捷方式替换逻辑 + * + * 注意:理论上,在内联链中解析 :smile: 并只留下快捷方式会更快。 + * 但是,谁在乎呢... + */ +export default function create_rule( + md: MarkdownIt, + emojies: EmojiDefs, + shortcuts: { [key: string]: string }, + scanRE: RegExp, + replaceRE: RegExp +) { + const arrayReplaceAt = md.utils.arrayReplaceAt; + const ucm = md.utils.lib.ucmicro; + const has = md.utils.has; + const ZPCc = new RegExp([ucm.Z.source, ucm.P.source, ucm.Cc.source].join('|')); + + function splitTextToken(text: string, level: number, TokenConstructor: any): Token[] { + let last_pos = 0; + const nodes: Token[] = []; + + text.replace(replaceRE, function (match: string, offset: number, src: string): string { + let emoji_name: string; + // Validate emoji name + if (has(shortcuts, match)) { + // replace shortcut with full name + emoji_name = shortcuts[match]; + + // Don't allow letters before any shortcut (as in no ":/" in http://) + if (offset > 0 && !ZPCc.test(src[offset - 1])) return ''; + + // Don't allow letters after any shortcut + if (offset + match.length < src.length && !ZPCc.test(src[offset + match.length])) { + return ''; + } + } else { + emoji_name = match.slice(1, -1); + } + + // Add new tokens to pending list + if (offset > last_pos) { + const token = new TokenConstructor('text', '', 0); + token.content = text.slice(last_pos, offset); + nodes.push(token); + } + + const token = new TokenConstructor('emoji', '', 0); + token.markup = emoji_name; + token.content = emojies[emoji_name]; + nodes.push(token); + + last_pos = offset + match.length; + return ''; + }); + + if (last_pos < text.length) { + const token = new TokenConstructor('text', '', 0); + token.content = text.slice(last_pos); + nodes.push(token); + } + + return nodes; + } + + return function emoji_replace(state: StateCore): void { + let token: Token; + const blockTokens = state.tokens; + let autolinkLevel = 0; + + for (let j = 0, l = blockTokens.length; j < l; j++) { + if (blockTokens[j].type !== 'inline') { continue; } + let tokens = blockTokens[j].children!; + + // We scan from the end, to keep position when new tags added. + // Use reversed logic in links start/end match + for (let i = tokens.length - 1; i >= 0; i--) { + token = tokens[i]; + + if (token.type === 'link_open' || token.type === 'link_close') { + if (token.info === 'auto') { autolinkLevel -= token.nesting; } + } + + if (token.type === 'text' && autolinkLevel === 0 && scanRE.test(token.content)) { + // replace current node + blockTokens[j].children = tokens = arrayReplaceAt( + tokens, i, splitTextToken(token.content, token.level, state.Token) + ); + } + } + } + }; +} +