eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

1// Emoji shortcode interpolation: ":tada:" โ†’ "๐ŸŽ‰". A curated set of the common
2// ones (not the full ~1800 โ€” smaller and less obscure for a family/kids' chat).
3// Pure + framework-free so it's unit-testable in Node. Replacement happens on
4// send, so the encrypted message stores the real emoji and renders natively
5// everywhere (bubbles, previews, notifications). Only known names convert, so
6// URLs and times like 10:30 are left alone. Easily extended โ€” just add entries.
7export const EMOJI = {
8 // smileys & emotion
9 grinning: "๐Ÿ˜€", smiley: "๐Ÿ˜ƒ", smile: "๐Ÿ˜„", grin: "๐Ÿ˜", laughing: "๐Ÿ˜†", satisfied: "๐Ÿ˜†",
10 sweat_smile: "๐Ÿ˜…", rofl: "๐Ÿคฃ", joy: "๐Ÿ˜‚", slightly_smiling_face: "๐Ÿ™‚", upside_down_face: "๐Ÿ™ƒ",
11 wink: "๐Ÿ˜‰", blush: "๐Ÿ˜Š", innocent: "๐Ÿ˜‡", heart_eyes: "๐Ÿ˜", star_struck: "๐Ÿคฉ", kissing_heart: "๐Ÿ˜˜",
12 yum: "๐Ÿ˜‹", stuck_out_tongue: "๐Ÿ˜›", stuck_out_tongue_winking_eye: "๐Ÿ˜œ", zany_face: "๐Ÿคช",
13 money_mouth_face: "๐Ÿค‘", hugs: "๐Ÿค—", thinking: "๐Ÿค”", shushing_face: "๐Ÿคซ", zipper_mouth_face: "๐Ÿค",
14 neutral_face: "๐Ÿ˜", expressionless: "๐Ÿ˜‘", no_mouth: "๐Ÿ˜ถ", smirk: "๐Ÿ˜", unamused: "๐Ÿ˜’",
15 roll_eyes: "๐Ÿ™„", grimacing: "๐Ÿ˜ฌ", lying_face: "๐Ÿคฅ", relieved: "๐Ÿ˜Œ", pensive: "๐Ÿ˜”", sleepy: "๐Ÿ˜ช",
16 sleeping: "๐Ÿ˜ด", mask: "๐Ÿ˜ท", face_with_thermometer: "๐Ÿค’", nauseated_face: "๐Ÿคข", vomiting: "๐Ÿคฎ",
17 sneezing_face: "๐Ÿคง", hot_face: "๐Ÿฅต", cold_face: "๐Ÿฅถ", woozy_face: "๐Ÿฅด", dizzy_face: "๐Ÿ˜ต",
18 exploding_head: "๐Ÿคฏ", cowboy_hat_face: "๐Ÿค ", partying_face: "๐Ÿฅณ", sunglasses: "๐Ÿ˜Ž", nerd_face: "๐Ÿค“",
19 confused: "๐Ÿ˜•", worried: "๐Ÿ˜Ÿ", frowning_face: "๐Ÿ™", open_mouth: "๐Ÿ˜ฎ", hushed: "๐Ÿ˜ฏ", astonished: "๐Ÿ˜ฒ",
20 flushed: "๐Ÿ˜ณ", pleading_face: "๐Ÿฅบ", cry: "๐Ÿ˜ข", sob: "๐Ÿ˜ญ", scream: "๐Ÿ˜ฑ", confounded: "๐Ÿ˜–",
21 persevere: "๐Ÿ˜ฃ", disappointed: "๐Ÿ˜ž", sweat: "๐Ÿ˜“", weary: "๐Ÿ˜ฉ", tired_face: "๐Ÿ˜ซ", yawning_face: "๐Ÿฅฑ",
22 triumph: "๐Ÿ˜ค", rage: "๐Ÿ˜ก", angry: "๐Ÿ˜ ", cursing_face: "๐Ÿคฌ", smiling_imp: "๐Ÿ˜ˆ", imp: "๐Ÿ‘ฟ",
23 skull: "๐Ÿ’€", poop: "๐Ÿ’ฉ", hankey: "๐Ÿ’ฉ", clown_face: "๐Ÿคก", ghost: "๐Ÿ‘ป", alien: "๐Ÿ‘ฝ", robot: "๐Ÿค–",
24 // hearts & symbols
25 heart: "โค๏ธ", orange_heart: "๐Ÿงก", yellow_heart: "๐Ÿ’›", green_heart: "๐Ÿ’š", blue_heart: "๐Ÿ’™",
26 purple_heart: "๐Ÿ’œ", black_heart: "๐Ÿ–ค", broken_heart: "๐Ÿ’”", two_hearts: "๐Ÿ’•", sparkling_heart: "๐Ÿ’–",
27 heartbeat: "๐Ÿ’“", heartpulse: "๐Ÿ’—", cupid: "๐Ÿ’˜", gift_heart: "๐Ÿ’", "100": "๐Ÿ’ฏ", anger: "๐Ÿ’ข",
28 boom: "๐Ÿ’ฅ", collision: "๐Ÿ’ฅ", sweat_drops: "๐Ÿ’ฆ", dash: "๐Ÿ’จ", zzz: "๐Ÿ’ค", fire: "๐Ÿ”ฅ", star: "โญ",
29 star2: "๐ŸŒŸ", sparkles: "โœจ", dizzy: "๐Ÿ’ซ", tada: "๐ŸŽ‰", confetti_ball: "๐ŸŽŠ", balloon: "๐ŸŽˆ",
30 gift: "๐ŸŽ", white_check_mark: "โœ…", heavy_check_mark: "โœ”๏ธ", x: "โŒ", warning: "โš ๏ธ", question: "โ“",
31 exclamation: "โ—", bell: "๐Ÿ””", crown: "๐Ÿ‘‘", gem: "๐Ÿ’Ž", lock: "๐Ÿ”’", key: "๐Ÿ”‘", bulb: "๐Ÿ’ก",
32 // hands & people
33 "+1": "๐Ÿ‘", thumbsup: "๐Ÿ‘", "-1": "๐Ÿ‘Ž", thumbsdown: "๐Ÿ‘Ž", ok_hand: "๐Ÿ‘Œ", punch: "๐Ÿ‘Š", fist: "โœŠ",
34 wave: "๐Ÿ‘‹", raised_hands: "๐Ÿ™Œ", clap: "๐Ÿ‘", pray: "๐Ÿ™", muscle: "๐Ÿ’ช", point_up: "โ˜๏ธ",
35 point_down: "๐Ÿ‘‡", point_left: "๐Ÿ‘ˆ", point_right: "๐Ÿ‘‰", v: "โœŒ๏ธ", crossed_fingers: "๐Ÿคž",
36 handshake: "๐Ÿค", writing_hand: "โœ๏ธ", eyes: "๐Ÿ‘€", brain: "๐Ÿง ", baby: "๐Ÿ‘ถ",
37 shrug: "๐Ÿคท", facepalm: "๐Ÿคฆ", strong: "๐Ÿ’ช", lol: "๐Ÿ˜‚",
38 // animals & nature
39 dog: "๐Ÿถ", cat: "๐Ÿฑ", mouse: "๐Ÿญ", fox: "๐ŸฆŠ", bear: "๐Ÿป", panda_face: "๐Ÿผ", koala: "๐Ÿจ",
40 tiger: "๐Ÿฏ", lion: "๐Ÿฆ", cow: "๐Ÿฎ", pig: "๐Ÿท", frog: "๐Ÿธ", monkey_face: "๐Ÿต", chicken: "๐Ÿ”",
41 penguin: "๐Ÿง", bird: "๐Ÿฆ", unicorn: "๐Ÿฆ„", bee: "๐Ÿ", bug: "๐Ÿ›", butterfly: "๐Ÿฆ‹", snail: "๐ŸŒ",
42 turtle: "๐Ÿข", fish: "๐ŸŸ", dolphin: "๐Ÿฌ", whale: "๐Ÿณ", paw_prints: "๐Ÿพ", sun: "โ˜€๏ธ", cloud: "โ˜๏ธ",
43 rainbow: "๐ŸŒˆ", snowflake: "โ„๏ธ", snowman: "โ›„", zap: "โšก", umbrella: "โ˜”", droplet: "๐Ÿ’ง",
44 ocean: "๐ŸŒŠ", christmas_tree: "๐ŸŽ„", earth_africa: "๐ŸŒ", moon: "๐ŸŒ™",
45 // food & drink
46 pizza: "๐Ÿ•", hamburger: "๐Ÿ”", fries: "๐ŸŸ", hotdog: "๐ŸŒญ", taco: "๐ŸŒฎ", popcorn: "๐Ÿฟ",
47 doughnut: "๐Ÿฉ", cookie: "๐Ÿช", cake: "๐Ÿฐ", birthday: "๐ŸŽ‚", icecream: "๐Ÿฆ", candy: "๐Ÿฌ",
48 lollipop: "๐Ÿญ", chocolate_bar: "๐Ÿซ", apple: "๐ŸŽ", banana: "๐ŸŒ", strawberry: "๐Ÿ“",
49 watermelon: "๐Ÿ‰", grapes: "๐Ÿ‡", avocado: "๐Ÿฅ‘", coffee: "โ˜•", tea: "๐Ÿต", beer: "๐Ÿบ",
50 wine_glass: "๐Ÿท", cocktail: "๐Ÿธ", champagne: "๐Ÿพ",
51 // activity, objects & travel
52 soccer: "โšฝ", basketball: "๐Ÿ€", football: "๐Ÿˆ", baseball: "โšพ", tennis: "๐ŸŽพ", trophy: "๐Ÿ†",
53 medal: "๐Ÿ…", dart: "๐ŸŽฏ", game_die: "๐ŸŽฒ", video_game: "๐ŸŽฎ", musical_note: "๐ŸŽต", notes: "๐ŸŽถ",
54 guitar: "๐ŸŽธ", microphone: "๐ŸŽค", headphones: "๐ŸŽง", art: "๐ŸŽจ", camera: "๐Ÿ“ท", iphone: "๐Ÿ“ฑ",
55 computer: "๐Ÿ’ป", money_with_wings: "๐Ÿ’ธ", moneybag: "๐Ÿ’ฐ", dollar: "๐Ÿ’ต", mag: "๐Ÿ”", link: "๐Ÿ”—",
56 pushpin: "๐Ÿ“Œ", calendar: "๐Ÿ“…", memo: "๐Ÿ“", pencil: "๐Ÿ“", book: "๐Ÿ“–", books: "๐Ÿ“š", email: "โœ‰๏ธ",
57 package: "๐Ÿ“ฆ", rocket: "๐Ÿš€", airplane: "โœˆ๏ธ", car: "๐Ÿš—", bike: "๐Ÿšฒ", house: "๐Ÿ ", school: "๐Ÿซ",
58 // flags & misc
59 checkered_flag: "๐Ÿ", tada_alias: "๐ŸŽ‰", jack_o_lantern: "๐ŸŽƒ", santa: "๐ŸŽ…",
60};
61
62// emojiMatches ranks shortcodes for a typed ":query" (prefix hits first, then
63// substring), returning up to `limit` { code, emoji } for the composer typeahead.
64// De-dupes aliases that resolve to the same emoji (e.g. joy/laughing) โ€” keeping
65// the first (shortest-name) match โ€” so the menu isn't full of duplicate glyphs.
66export function emojiMatches(query, limit = 8) {
67 const q = String(query || "").toLowerCase();
68 if (!q) return [];
69 const names = Object.keys(EMOJI).filter((n) => n.includes(q));
70 names.sort((a, b) => {
71 const ap = a.startsWith(q) ? 0 : 1;
72 const bp = b.startsWith(q) ? 0 : 1;
73 return ap - bp || a.length - b.length || a.localeCompare(b);
74 });
75 const out = [];
76 const seen = new Set();
77 for (const code of names) {
78 if (seen.has(EMOJI[code])) continue;
79 seen.add(EMOJI[code]);
80 out.push({ code, emoji: EMOJI[code] });
81 if (out.length >= limit) break;
82 }
83 return out;
84}
85
86const SHORTCODE_RE = /:([a-z0-9_+-]+):/gi;
87
88// emojify replaces known :shortcodes: with their emoji, leaving unknown ones
89// (and anything that isn't a :name: token) untouched.
90export function emojify(text) {
91 if (text == null) return "";
92 return String(text).replace(SHORTCODE_RE, (m, name) => EMOJI[name.toLowerCase()] || m);
93}
94