| 1 | /** A `:shortcode:` emoji the editor can complete, matched by its code or its keywords. */ |
| 2 | export interface Emoji { |
| 3 | code: string; |
| 4 | char: string; |
| 5 | keywords?: readonly string[]; |
| 6 | } |
| 7 | |
| 8 | // A curated set skewed toward dev-chat use rather than the full Unicode table, so the whole |
| 9 | // autocomplete stack stays a static in-bundle lookup with no dependency. |
| 10 | const EMOJI: readonly Emoji[] = [ |
| 11 | { code: "smile", char: "๐", keywords: ["happy", "joy"] }, |
| 12 | { code: "smiley", char: "๐", keywords: ["happy"] }, |
| 13 | { code: "grin", char: "๐" }, |
| 14 | { code: "laughing", char: "๐", keywords: ["lol", "haha"] }, |
| 15 | { code: "rofl", char: "๐คฃ", keywords: ["lmao"] }, |
| 16 | { code: "joy", char: "๐", keywords: ["tears", "cry", "laugh"] }, |
| 17 | { code: "sweat_smile", char: "๐
", keywords: ["phew"] }, |
| 18 | { code: "wink", char: "๐" }, |
| 19 | { code: "blush", char: "๐", keywords: ["happy"] }, |
| 20 | { code: "slight_smile", char: "๐" }, |
| 21 | { code: "upside_down", char: "๐" }, |
| 22 | { code: "yum", char: "๐", keywords: ["tasty"] }, |
| 23 | { code: "sunglasses", char: "๐", keywords: ["cool"] }, |
| 24 | { code: "heart_eyes", char: "๐", keywords: ["love"] }, |
| 25 | { code: "star_struck", char: "๐คฉ", keywords: ["amazed"] }, |
| 26 | { code: "thinking", char: "๐ค", keywords: ["hmm"] }, |
| 27 | { code: "smirk", char: "๐" }, |
| 28 | { code: "neutral", char: "๐", keywords: ["meh"] }, |
| 29 | { code: "expressionless", char: "๐" }, |
| 30 | { code: "unamused", char: "๐" }, |
| 31 | { code: "roll_eyes", char: "๐" }, |
| 32 | { code: "grimacing", char: "๐ฌ", keywords: ["yikes"] }, |
| 33 | { code: "relieved", char: "๐" }, |
| 34 | { code: "pensive", char: "๐" }, |
| 35 | { code: "sleepy", char: "๐ช", keywords: ["tired"] }, |
| 36 | { code: "sleeping", char: "๐ด", keywords: ["zzz"] }, |
| 37 | { code: "mask", char: "๐ท", keywords: ["sick"] }, |
| 38 | { code: "dizzy_face", char: "๐ต" }, |
| 39 | { code: "exploding_head", char: "๐คฏ", keywords: ["mind", "blown"] }, |
| 40 | { code: "cowboy", char: "๐ค " }, |
| 41 | { code: "sob", char: "๐ญ", keywords: ["cry", "crying"] }, |
| 42 | { code: "cry", char: "๐ข", keywords: ["sad", "tear"] }, |
| 43 | { code: "disappointed", char: "๐", keywords: ["sad"] }, |
| 44 | { code: "worried", char: "๐" }, |
| 45 | { code: "confused", char: "๐" }, |
| 46 | { code: "frowning", char: "๐" }, |
| 47 | { code: "persevere", char: "๐ฃ" }, |
| 48 | { code: "confounded", char: "๐" }, |
| 49 | { code: "tired_face", char: "๐ซ" }, |
| 50 | { code: "weary", char: "๐ฉ" }, |
| 51 | { code: "pleading", char: "๐ฅบ", keywords: ["puppy"] }, |
| 52 | { code: "triumph", char: "๐ค" }, |
| 53 | { code: "angry", char: "๐ ", keywords: ["mad"] }, |
| 54 | { code: "rage", char: "๐ก", keywords: ["mad", "furious"] }, |
| 55 | { code: "cursing", char: "๐คฌ", keywords: ["swear"] }, |
| 56 | { code: "scream", char: "๐ฑ", keywords: ["shock", "fear"] }, |
| 57 | { code: "fearful", char: "๐จ" }, |
| 58 | { code: "cold_sweat", char: "๐ฐ" }, |
| 59 | { code: "flushed", char: "๐ณ", keywords: ["blush", "embarrassed"] }, |
| 60 | { code: "hot", char: "๐ฅต", keywords: ["heat"] }, |
| 61 | { code: "cold", char: "๐ฅถ", keywords: ["freezing"] }, |
| 62 | { code: "woozy", char: "๐ฅด" }, |
| 63 | { code: "nauseated", char: "๐คข", keywords: ["sick"] }, |
| 64 | { code: "vomiting", char: "๐คฎ", keywords: ["puke"] }, |
| 65 | { code: "sneezing", char: "๐คง" }, |
| 66 | { code: "shush", char: "๐คซ", keywords: ["quiet"] }, |
| 67 | { code: "zipper_mouth", char: "๐ค" }, |
| 68 | { code: "raised_eyebrow", char: "๐คจ", keywords: ["skeptical", "suspicious"] }, |
| 69 | { code: "monocle", char: "๐ง", keywords: ["inspect"] }, |
| 70 | { code: "nerd", char: "๐ค", keywords: ["geek"] }, |
| 71 | { code: "shrug", char: "๐คท", keywords: ["dunno", "idk"] }, |
| 72 | { code: "facepalm", char: "๐คฆ", keywords: ["ugh"] }, |
| 73 | { code: "wave", char: "๐", keywords: ["hi", "hello", "bye"] }, |
| 74 | { code: "thumbsup", char: "๐", keywords: ["+1", "yes", "approve", "lgtm"] }, |
| 75 | { code: "thumbsdown", char: "๐", keywords: ["-1", "no"] }, |
| 76 | { code: "ok_hand", char: "๐" }, |
| 77 | { code: "pinch", char: "๐ค", keywords: ["small", "tiny"] }, |
| 78 | { code: "v", char: "โ๏ธ", keywords: ["peace"] }, |
| 79 | { code: "crossed_fingers", char: "๐ค", keywords: ["luck", "hope"] }, |
| 80 | { code: "point_up", char: "โ๏ธ" }, |
| 81 | { code: "point_down", char: "๐" }, |
| 82 | { code: "point_left", char: "๐" }, |
| 83 | { code: "point_right", char: "๐" }, |
| 84 | { code: "raised_hands", char: "๐", keywords: ["praise", "yay"] }, |
| 85 | { code: "clap", char: "๐", keywords: ["applause"] }, |
| 86 | { code: "pray", char: "๐", keywords: ["please", "thanks", "thank you"] }, |
| 87 | { code: "handshake", char: "๐ค", keywords: ["deal"] }, |
| 88 | { code: "muscle", char: "๐ช", keywords: ["strong", "flex"] }, |
| 89 | { code: "fist", char: "โ" }, |
| 90 | { code: "punch", char: "๐", keywords: ["bump"] }, |
| 91 | { code: "eyes", char: "๐", keywords: ["look", "watching"] }, |
| 92 | { code: "brain", char: "๐ง " }, |
| 93 | { code: "heart", char: "โค๏ธ", keywords: ["love"] }, |
| 94 | { code: "orange_heart", char: "๐งก" }, |
| 95 | { code: "yellow_heart", char: "๐" }, |
| 96 | { code: "green_heart", char: "๐" }, |
| 97 | { code: "blue_heart", char: "๐" }, |
| 98 | { code: "purple_heart", char: "๐" }, |
| 99 | { code: "black_heart", char: "๐ค" }, |
| 100 | { code: "broken_heart", char: "๐" }, |
| 101 | { code: "sparkling_heart", char: "๐" }, |
| 102 | { code: "fire", char: "๐ฅ", keywords: ["lit", "hot", "flame"] }, |
| 103 | { code: "sparkles", char: "โจ", keywords: ["shiny", "clean"] }, |
| 104 | { code: "star", char: "โญ", keywords: ["favorite"] }, |
| 105 | { code: "star2", char: "๐", keywords: ["glowing"] }, |
| 106 | { code: "zap", char: "โก", keywords: ["lightning", "fast"] }, |
| 107 | { code: "boom", char: "๐ฅ", keywords: ["explosion", "collision"] }, |
| 108 | { code: "collision", char: "๐ข" }, |
| 109 | { code: "dizzy", char: "๐ซ" }, |
| 110 | { code: "sweat_drops", char: "๐ฆ" }, |
| 111 | { code: "dash", char: "๐จ", keywords: ["fast", "wind"] }, |
| 112 | { code: "hundred", char: "๐ฏ", keywords: ["100", "perfect"] }, |
| 113 | { code: "tada", char: "๐", keywords: ["party", "celebrate", "yay"] }, |
| 114 | { code: "confetti", char: "๐", keywords: ["party"] }, |
| 115 | { code: "balloon", char: "๐" }, |
| 116 | { code: "gift", char: "๐", keywords: ["present"] }, |
| 117 | { code: "trophy", char: "๐", keywords: ["win", "award"] }, |
| 118 | { code: "medal", char: "๐
" }, |
| 119 | { code: "rocket", char: "๐", keywords: ["ship", "launch", "deploy"] }, |
| 120 | { code: "airplane", char: "โ๏ธ" }, |
| 121 | { code: "bulb", char: "๐ก", keywords: ["idea"] }, |
| 122 | { code: "wrench", char: "๐ง", keywords: ["fix", "tool"] }, |
| 123 | { code: "hammer", char: "๐จ", keywords: ["build"] }, |
| 124 | { code: "hammer_and_wrench", char: "๐ ๏ธ", keywords: ["tools"] }, |
| 125 | { code: "gear", char: "โ๏ธ", keywords: ["settings", "config"] }, |
| 126 | { code: "nut_and_bolt", char: "๐ฉ" }, |
| 127 | { code: "lock", char: "๐", keywords: ["secure", "private"] }, |
| 128 | { code: "unlock", char: "๐" }, |
| 129 | { code: "key", char: "๐" }, |
| 130 | { code: "mag", char: "๐", keywords: ["search", "find", "zoom"] }, |
| 131 | { code: "link", char: "๐" }, |
| 132 | { code: "paperclip", char: "๐" }, |
| 133 | { code: "pushpin", char: "๐", keywords: ["pin"] }, |
| 134 | { code: "memo", char: "๐", keywords: ["note", "write", "edit"] }, |
| 135 | { code: "clipboard", char: "๐", keywords: ["copy", "paste"] }, |
| 136 | { code: "books", char: "๐", keywords: ["docs", "read"] }, |
| 137 | { code: "book", char: "๐" }, |
| 138 | { code: "package", char: "๐ฆ", keywords: ["box", "npm", "module"] }, |
| 139 | { code: "computer", char: "๐ป", keywords: ["laptop", "code"] }, |
| 140 | { code: "keyboard", char: "โจ๏ธ" }, |
| 141 | { code: "floppy_disk", char: "๐พ", keywords: ["save"] }, |
| 142 | { code: "battery", char: "๐" }, |
| 143 | { code: "electric_plug", char: "๐" }, |
| 144 | { code: "hourglass", char: "โณ", keywords: ["wait", "loading", "time"] }, |
| 145 | { code: "watch", char: "โ", keywords: ["time"] }, |
| 146 | { code: "alarm_clock", char: "โฐ", keywords: ["time", "reminder"] }, |
| 147 | { code: "calendar", char: "๐
", keywords: ["date", "schedule"] }, |
| 148 | { code: "chart", char: "๐", keywords: ["graph", "up", "growth"] }, |
| 149 | { code: "chart_down", char: "๐", keywords: ["graph", "down"] }, |
| 150 | { code: "bar_chart", char: "๐", keywords: ["graph", "stats"] }, |
| 151 | { code: "bug", char: "๐", keywords: ["issue", "defect"] }, |
| 152 | { code: "skull", char: "๐", keywords: ["dead", "cooked"] }, |
| 153 | { code: "ghost", char: "๐ป", keywords: ["boo"] }, |
| 154 | { code: "alien", char: "๐ฝ" }, |
| 155 | { code: "robot", char: "๐ค", keywords: ["bot", "ai"] }, |
| 156 | { code: "poop", char: "๐ฉ", keywords: ["crap"] }, |
| 157 | { code: "clown", char: "๐คก" }, |
| 158 | { code: "check", char: "โ
", keywords: ["done", "yes", "pass", "white_check_mark"] }, |
| 159 | { code: "heavy_check", char: "โ๏ธ", keywords: ["done"] }, |
| 160 | { code: "x", char: "โ", keywords: ["no", "fail", "cross"] }, |
| 161 | { code: "warning", char: "โ ๏ธ", keywords: ["caution", "alert"] }, |
| 162 | { code: "no_entry", char: "โ", keywords: ["stop", "blocked"] }, |
| 163 | { code: "question", char: "โ", keywords: ["help"] }, |
| 164 | { code: "exclamation", char: "โ", keywords: ["important"] }, |
| 165 | { code: "recycle", char: "โป๏ธ", keywords: ["refactor"] }, |
| 166 | { code: "arrows_clockwise", char: "๐", keywords: ["refresh", "sync", "retry"] }, |
| 167 | { code: "back", char: "๐" }, |
| 168 | { code: "arrow_up", char: "โฌ๏ธ" }, |
| 169 | { code: "arrow_down", char: "โฌ๏ธ" }, |
| 170 | { code: "arrow_right", char: "โก๏ธ" }, |
| 171 | { code: "arrow_left", char: "โฌ
๏ธ" }, |
| 172 | { code: "hash", char: "#๏ธโฃ" }, |
| 173 | { code: "red_circle", char: "๐ด", keywords: ["record", "live"] }, |
| 174 | { code: "green_circle", char: "๐ข", keywords: ["online", "ok"] }, |
| 175 | { code: "yellow_circle", char: "๐ก" }, |
| 176 | { code: "coffee", char: "โ", keywords: ["cafe"] }, |
| 177 | { code: "beer", char: "๐บ", keywords: ["drink", "cheers"] }, |
| 178 | { code: "pizza", char: "๐" }, |
| 179 | { code: "cake", char: "๐", keywords: ["birthday"] }, |
| 180 | { code: "hamburger", char: "๐" }, |
| 181 | { code: "sun", char: "โ๏ธ", keywords: ["sunny"] }, |
| 182 | { code: "moon", char: "๐", keywords: ["night"] }, |
| 183 | { code: "cloud", char: "โ๏ธ" }, |
| 184 | { code: "rainbow", char: "๐" }, |
| 185 | { code: "snowflake", char: "โ๏ธ", keywords: ["cold", "snow"] }, |
| 186 | { code: "cat", char: "๐ฑ" }, |
| 187 | { code: "dog", char: "๐ถ" }, |
| 188 | { code: "unicorn", char: "๐ฆ" }, |
| 189 | { code: "snake", char: "๐", keywords: ["python"] }, |
| 190 | { code: "penguin", char: "๐ง", keywords: ["linux"] }, |
| 191 | { code: "four_leaf_clover", char: "๐", keywords: ["luck", "clover"] }, |
| 192 | ]; |
| 193 | |
| 194 | /** |
| 195 | * Emoji matching `query` (the text after `:`), code-prefix matches first, then code substrings, then |
| 196 | * keyword hits. An empty query returns the leading slice so the menu can open on a bare `:`. |
| 197 | */ |
| 198 | export function searchEmoji(query: string, limit = 8): Emoji[] { |
| 199 | const q = query.toLowerCase(); |
| 200 | if (q === "") return EMOJI.slice(0, limit); |
| 201 | const prefix: Emoji[] = []; |
| 202 | const infix: Emoji[] = []; |
| 203 | const keyword: Emoji[] = []; |
| 204 | for (const emoji of EMOJI) { |
| 205 | if (emoji.code.startsWith(q)) prefix.push(emoji); |
| 206 | else if (emoji.code.includes(q)) infix.push(emoji); |
| 207 | else if (emoji.keywords?.some((word) => word.includes(q))) keyword.push(emoji); |
| 208 | if (prefix.length >= limit) break; |
| 209 | } |
| 210 | return [...prefix, ...infix, ...keyword].slice(0, limit); |
| 211 | } |