From 4fa7227b948ae4fc26d8a8c487c612e311dcb48e Mon Sep 17 00:00:00 2001 From: Spencer Brower <6729162+sbrow@users.noreply.github.com> Date: Sat, 11 Jul 2026 19:28:36 -0400 Subject: [PATCH] feat: Emoji shorthand. --- TODOS.md | 6 +- content.odin | 3 +- emoji.odin | 438 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 443 insertions(+), 4 deletions(-) create mode 100644 emoji.odin diff --git a/TODOS.md b/TODOS.md index 59db89c..d3b15c8 100644 --- a/TODOS.md +++ b/TODOS.md @@ -7,10 +7,10 @@ - Option B: Steal Tufte's sidenote/margin-note CSS (adapt for dark theme), keep TailwindCSS - Option C: Full Tufte CSS — drop TailwindCSS, no build step, semantic HTML, customize for dark theme + Roboto - Our sidenote HTML pattern already matches Tufte's exactly -- [ ] GitHub alerts (`> [!CAUTION]` → styled blockquote) — 2 posts - [ ] Block attributes on code fences (`{ #ex-1 }`) — hello-world.md -- [ ] Emoji shortcodes (`:shrug:` etc.) — 2 instances +- [x] Emoji shortcodes (`:shrug:` etc.) — 2 instances + - [ ] Backslash in shrug not visible. - [ ] include-code shortcode (`{{< include-code ... >}}`) — i-ported-fd-to-odin -- [ ] Nix build integration — main flake runs thor + tailwindcss instead of Hugo +- [x] Nix build integration — main flake runs thor + tailwindcss instead of Hugo - [ ] Copy-to-clipboard button on code blocks (was in Hugo's custom-head.html) - [ ] OpenGraph meta tags diff --git a/content.odin b/content.odin index ba7acd9..f30dd0c 100644 --- a/content.odin +++ b/content.odin @@ -173,7 +173,8 @@ load_page :: proc( if strings.has_suffix(file_path, ".html") { page.body_html = strings.clone(body) } else { - clean_body, defs := strip_definitions(body) + expanded := expand_emoji(body) + clean_body, defs := strip_definitions(expanded) html := cm.markdown_to_html_from_string(clean_body, {.Unsafe}) page.body_html = inject_alerts(inject_sidenotes(html, defs)) } diff --git a/emoji.odin b/emoji.odin new file mode 100644 index 0000000..96a4539 --- /dev/null +++ b/emoji.odin @@ -0,0 +1,438 @@ +#+feature dynamic-literals +package main + +import "core:strings" + +EMOJIS: map[string]string = { + // People + "smile" = "\xf0\x9f\x98\x84", + "laughing" = "\xf0\x9f\x98\x86", + "blush" = "\xf0\x9f\x98\x8a", + "smiley" = "\xf0\x9f\x98\x83", + "wink" = "\xf0\x9f\x98\x89", + "joy" = "\xf0\x9f\xa4\xa3", + "rofl" = "\xf0\x9f\xa4\xa3", + "relaxed" = "\xe2\x98\xba\xef\xb8\x8f", + "thinking" = "\xf0\x9f\xa4\x94", + "neutral_face" = "\xf0\x9f\x98\x90", + "expressionless" = "\xf0\x9f\x98\x91", + "no_mouth" = "\xf0\x9f\x98\xb6", + "rolling_eyes" = "\xf0\x9f\x99\x84", + "smirk" = "\xf0\x9f\x98\x8f", + "persevere" = "\xf0\x9f\x98\xa3", + "disappointed_relieved" = "\xf0\x9f\x98\xa5", + "open_mouth" = "\xf0\x9f\x98\xae", + "zipper_mouth_face" = "\xf0\x9f\xa4\x90", + "hushed" = "\xf0\x9f\x98\xaf", + "sleepy" = "\xf0\x9f\x98\xaa", + "tired_face" = "\xf0\x9f\x98\xab", + "sleeping" = "\xf0\x9f\x98\xb4", + "relieved" = "\xf0\x9f\x98\x8c", + "stuck_out_tongue" = "\xf0\x9f\x98\x9b", + "stuck_out_tongue_winking_eye" = "\xf0\x9f\x98\x9c", + "stuck_out_tongue_closed_eyes" = "\xf0\x9f\x98\x9d", + "drooling_face" = "\xf0\x9f\xa4\xa4", + "unamused" = "\xf0\x9f\x98\x92", + "sweat" = "\xf0\x9f\x98\x85", + "pensive" = "\xf0\x9f\x98\x94", + "confused" = "\xf0\x9f\x98\x95", + "upside_down_face" = "\xf0\x9f\x99\x83", + "money_mouth_face" = "\xf0\x9f\xa4\x91", + "astonished" = "\xf0\x9f\x98\xb2", + "white_frowning_face" = "\xe2\x98\xb9\xef\xb8\x8f", + "slightly_sad_face" = "\xe2\x98\xb9\xef\xb8\x8f", + "confounded" = "\xf0\x9f\x98\x96", + "disappointed" = "\xf0\x9f\x98\x9e", + "worried" = "\xf0\x9f\x98\x9f", + "triumph" = "\xf0\x9f\x98\xa4", + "cry" = "\xf0\x9f\x98\xa2", + "sob" = "\xf0\x9f\x98\xad", + "frowning" = "\xf0\x9f\x98\xa6", + "frowning_face" = "\xf0\x9f\x99\x81", + "anguished" = "\xf0\x9f\x98\xa7", + "fearful" = "\xf0\x9f\x98\xa8", + "weary" = "\xf0\x9f\x98\xa9", + "grimacing" = "\xf0\x9f\x98\xac", + "cold_sweat" = "\xf0\x9f\x98\xb0", + "scream" = "\xf0\x9f\x98\xb1", + "flushed" = "\xf0\x9f\x98\xb3", + "dizzy_face" = "\xf0\x9f\x98\xb5", + "rage" = "\xf0\x9f\x98\xa1", + "angry" = "\xf0\x9f\x98\xa0", + "innocent" = "\xf0\x9f\x98\x87", + "cowboy_hat_face" = "\xf0\x9f\xa4\xa0", + "clown_face" = "\xf0\x9f\xa4\xa1", + "mask" = "\xf0\x9f\x98\xb7", + "thermometer_face" = "\xf0\x9f\xa4\x92", + "head_bandage" = "\xf0\x9f\xa4\x95", + "nauseated_face" = "\xf0\x9f\xa4\xa2", + "sneezing_face" = "\xf0\x9f\xa4\xa7", + "smiling_imp" = "\xf0\x9f\x98\x88", + "imp" = "\xf0\x9f\x91\xbf", + "shrug" = "\xc2\xaf\\_(\xe3\x83\x84)_/\xc2\xaf", + "facepalm" = "\xf0\x9f\xa4\xa6", + "facepunch" = "\xf0\x9f\x91\x8a", + "wave" = "\xf0\x9f\x91\x8b", + "ok_hand" = "\xf0\x9f\x91\x8c", + "thumbsup" = "\xf0\x9f\x91\x8d", + "thumbsdown" = "\xf0\x9f\x91\x8e", + "clap" = "\xf0\x9f\x91\x8f", + "pray" = "\xf0\x9f\x99\x8f", + "point_up" = "\xe2\x98\x9d\xef\xb8\x8f", + "point_down" = "\xf0\x9f\x91\x87", + "point_left" = "\xf0\x9f\x91\x88", + "point_right" = "\xf0\x9f\x89\x89", + "v" = "\xe2\x9c\x8c\xef\xb8\x8f", + "raised_hands" = "\xf0\x9f\x99\x8c", + "muscle" = "\xf0\x9f\x92\xaa", + "fist" = "\xe2\x9c\x8a", + "hand" = "\xe2\x9c\x8b", + + // Hearts & symbols + "heart" = "\xe2\x9d\xa4\xef\xb8\x8f", + "orange_heart" = "\xf0\x9f\xa7\xa1", + "yellow_heart" = "\xf0\x9f\x92\x9b", + "green_heart" = "\xf0\x9f\x92\x9a", + "blue_heart" = "\xf0\x9f\x92\x99", + "purple_heart" = "\xf0\x9f\x92\x9c", + "broken_heart" = "\xf0\x9f\x92\x94", + "sparkling_heart" = "\xf0\x9f\x92\x96", + "100" = "\xf0\x9f\x92\xaf", + "anger" = "\xf0\x9f\x92\xa2", + "checkered_flag" = "\xf0\x9f\x8f\x81", + "crossed_flags" = "\xf0\x9f\x9a\xa9", + "rocket" = "\xf0\x9f\x9a\x80", + "star" = "\xe2\xad\x90", + "star2" = "\xf0\x9f\x8c\x9f", + "sparkles" = "\xe2\x9c\xa8", + "boom" = "\xf0\x9f\x92\xa5", + "exclamation" = "\xe2\x9d\x97", + "question" = "\xe2\x9d\x93", + "grey_exclamation" = "\xe2\x9d\x95", + "grey_question" = "\xe2\x9d\x94", + "zzz" = "\xf0\x9f\x92\xa4", + "warning" = "\xe2\x9a\xa0\xef\xb8\x8f", + "no_entry_sign" = "\xf0\x9f\x9a\xab", + "no_entry" = "\xe2\x9b\x94", + "white_check_mark" = "\xe2\x9c\x85", + "negative_squared_cross_mark" = "\xe2\x9d\x8e", + "x" = "\xe2\x9d\x8c", + "o" = "\xe2\xad\x95", + "heavy_plus_sign" = "\xe2\x9e\x95", + "heavy_minus_sign" = "\xe2\x9e\x96", + "heavy_division_sign" = "\xe2\x9e\x97", + "copyright" = "\xc2\xa9\xef\xb8\x8f", + "registered" = "\xc2\xae\xef\xb8\x8f", + "tm" = "\xe2\x84\xa2\xef\xb8\x8f", + + // Nature & weather + "fire" = "\xf0\x9f\x94\xa5", + "zap" = "\xe2\x9a\xa1", + "sunny" = "\xe2\x98\x80\xef\xb8\x8f", + "cloud" = "\xe2\x98\x81\xef\xb8\x8f", + "rainbow" = "\xf0\x9f\x8c\x88", + "snowflake" = "\xe2\x9d\x84\xef\xb8\x8f", + "dash" = "\xf0\x9f\x92\xa8", + "tornado" = "\xf0\x9f\x8c\xaa\xef\xb8\x8f", + "deciduous_tree" = "\xf0\x9f\x8c\xb3", + "evergreen_tree" = "\xf0\x9f\x8c\xb2", + "palm_tree" = "\xf0\x9f\x8c\xb4", + "cactus" = "\xf0\x9f\x8c\xb5", + "tulip" = "\xf0\x9f\x8c\xb7", + "rose" = "\xf0\x9f\x8c\xb9", + "sunflower" = "\xf0\x9f\x8c\xbb", + "hibiscus" = "\xf0\x9f\x8c\xba", + "earth_africa" = "\xf0\x9f\x8c\x8d", + "earth_americas" = "\xf0\x9f\x8c\x8e", + "earth_asia" = "\xf0\x9f\x8c\x8f", + "new_moon" = "\xf0\x9f\x8c\x9a", + "full_moon" = "\xf0\x9f\x8c\x95", + + // Food & drink + "coffee" = "\xe2\x98\x95", + "tea" = "\xf0\x9f\x8d\xb5", + "beer" = "\xf0\x9f\x8d\xba", + "beers" = "\xf0\x9f\x8d\xbb", + "wine_glass" = "\xf0\x9f\x8d\xb7", + "tropical_drink" = "\xf0\x9f\x8d\xb9", + "apple" = "\xf0\x9f\x8d\x8e", + "green_apple" = "\xf0\x9f\x8d\x8f", + "orange" = "\xf0\x9f\x8d\x8a", + "lemon" = "\xf0\x9f\x8d\x8b", + "banana" = "\xf0\x9f\x8d\x8c", + "watermelon" = "\xf0\x9f\x8d\x89", + "grapes" = "\xf0\x9f\x8d\x87", + "strawberry" = "\xf0\x9f\x8d\x93", + "melon" = "\xf0\x9f\x8d\x88", + "cherries" = "\xf0\x9f\x8d\x92", + "peach" = "\xf0\x9f\x8d\x91", + "pineapple" = "\xf0\x9f\x8d\x8d", + "pizza" = "\xf0\x9f\x8d\x95", + "hamburger" = "\xf0\x9f\x8d\x94", + "hotdog" = "\xf0\x9f\x8c\xad", + "taco" = "\xf0\x9f\x8c\xae", + "burrito" = "\xf0\x9f\x8c\xaf", + "cake" = "\xf0\x9f\x8d\xb0", + "cookie" = "\xf0\x9f\x8d\xaa", + "chocolate_bar" = "\xf0\x9f\x8d\xab", + "candy" = "\xf0\x9f\x8d\xac", + "popcorn" = "\xf0\x9f\x8d\xbf", + + // Animals + "dog" = "\xf0\x9f\x90\xb6", + "cat" = "\xf0\x9f\x90\xb1", + "mouse" = "\xf0\x9f\x90\xad", + "hamster" = "\xf0\x9f\x90\xb9", + "rabbit" = "\xf0\x9f\x90\xb0", + "bear" = "\xf0\x9f\x90\xbb", + "panda_face" = "\xf0\x9f\x90\xbc", + "koala" = "\xf0\x9f\x90\xa8", + "tiger" = "\xf0\x9f\x90\xaf", + "lion_face" = "\xf0\x9f\xa6\x81", + "cow" = "\xf0\x9f\x90\xae", + "pig" = "\xf0\x9f\x90\xb7", + "frog" = "\xf0\x9f\x90\xb8", + "monkey_face" = "\xf0\x9f\x90\xb5", + "see_no_evil" = "\xf0\x9f\x99\x88", + "hear_no_evil" = "\xf0\x9f\x99\x89", + "speak_no_evil" = "\xf0\x9f\x99\x8a", + "owl" = "\xf0\x9f\xa6\x89", + "bat" = "\xf0\x9f\xa6\x87", + "wolf" = "\xf0\x9f\x90\xba", + "boar" = "\xf0\x9f\x90\x97", + "horse" = "\xf0\x9f\x90\x8e", + "unicorn" = "\xf0\x9f\xa6\x84", + "honeybee" = "\xf0\x9f\x90\x9d", + "bug" = "\xf0\x9f\x90\x9b", + "snail" = "\xf0\x9f\x90\x8c", + "beetle" = "\xf0\x9f\x90\x9e", + "ant" = "\xf0\x9f\x90\x9c", + "spider" = "\xf0\x9f\x95\xb7", + "scorpion" = "\xf0\x9f\xa6\x82", + "turtle" = "\xf0\x9f\x90\xa2", + "snake" = "\xf0\x9f\x90\x8d", + "octopus" = "\xf0\x9f\x90\x99", + "shell" = "\xf0\x9f\x90\x9a", + "whale" = "\xf0\x9f\x90\xb3", + "dolphin" = "\xf0\x9f\x90\xac", + "fish" = "\xf0\x9f\x90\x9f", + "tropical_fish" = "\xf0\x9f\x90\xa0", + "blowfish" = "\xf0\x9f\x90\xa1", + "penguin" = "\xf0\x9f\x90\xa7", + "chicken" = "\xf0\x9f\x90\x94", + "bird" = "\xf0\x9f\x90\xa6", + "eagle" = "\xf0\x9f\xa6\x85", + + // Objects + "computer" = "\xf0\x9f\x92\xbb", + "iphone" = "\xf0\x9f\x93\xb1", + "keyboard" = "\xe2\x8c\xa8\xef\xb8\x8f", + "desktop_computer" = "\xf0\x9f\x96\xa5", + "printer" = "\xf0\x9f\x96\xa8", + "mouse_three_button" = "\xf0\x9f\x96\xb1", + "joystick" = "\xf0\x9f\x95\xb9", + "stash" = "\xf0\x9f\x92\xbe", + "floppy_disk" = "\xf0\x9f\x92\xbe", + "cd" = "\xf0\x9f\x92\xbf", + "dvd" = "\xf0\x9f\x93\x80", + "vhs" = "\xf0\x9f\x93\xbc", + "camera" = "\xf0\x9f\x93\xb7", + "video_camera" = "\xf0\x9f\x93\xb9", + "tv" = "\xf0\x9f\x93\xba", + "radio" = "\xf0\x9f\x93\xbb", + "pager" = "\xf0\x9f\x93\x9f", + "telephone" = "\xe2\x98\x8e\xef\xb8\x8f", + "fax" = "\xf0\x9f\x93\xa0", + "bulb" = "\xf0\x9f\x92\xa1", + "candle" = "\xf0\x9f\x95\xaf", + "bathtub" = "\xf0\x9f\x9b\x81", + "shower" = "\xf0\x9f\x9a\xbf", + "toilet" = "\xf0\x9f\x9a\xbd", + "wrench" = "\xf0\x9f\x94\xa7", + "hammer" = "\xf0\x9f\x94\xa8", + "nut_and_bolt" = "\xf0\x9f\x94\xa9", + "gear" = "\xe2\x9a\x99\xef\xb8\x8f", + "link" = "\xf0\x9f\x94\x97", + "lock" = "\xf0\x9f\x94\x92", + "unlock" = "\xf0\x9f\x94\x93", + "key" = "\xf0\x9f\x94\x91", + "bell" = "\xf0\x9f\x94\x94", + "bookmark" = "\xf0\x9f\x94\x96", + "pushpin" = "\xf0\x9f\x93\x8c", + "paperclip" = "\xf0\x9f\x93\x8e", + "memo" = "\xf0\x9f\x93\x9d", + "pencil2" = "\xe2\x9c\x8f\xef\xb8\x8f", + "black_nib" = "\xe2\x9c\x92\xef\xb8\x8f", + "pen" = "\xf0\x9f\x96\x8a", + "paintbrush" = "\xf0\x9f\x96\x8c", + "crayon" = "\xf0\x9f\x96\x8d", + "book" = "\xf0\x9f\x93\x97", + "books" = "\xf0\x9f\x93\x9a", + "ledger" = "\xf0\x9f\x93\x92", + "notebook" = "\xf0\x9f\x93\x93", + "scroll" = "\xf0\x9f\x93\x9c", + "page_with_curl" = "\xf0\x9f\x93\x84", + "newspaper" = "\xf0\x9f\x93\xb0", + "chart_with_upwards_trend" = "\xf0\x9f\x93\x88", + "chart_with_downwards_trend" = "\xf0\x9f\x93\x89", + "bar_chart" = "\xf0\x9f\x93\x8a", + "calendar" = "\xf0\x9f\x93\x85", + "date" = "\xf0\x9f\x93\x85", + "hourglass" = "\xe2\x8f\xb3", + "hourglass_flowing_sand" = "\xe2\x8f\xb3", + "clock" = "\xf0\x9f\x95\x90", + "alarm_clock" = "\xe2\x8f\xb0", + "stopwatch" = "\xe2\x8f\xb1", + "watch" = "\xe2\x8c\x9a", + "moneybag" = "\xf0\x9f\x92\xb0", + "yen" = "\xf0\x9f\x92\xb4", + "dollar" = "\xf0\x9f\x92\xb5", + "euro" = "\xf0\x9f\x92\xb6", + "pound" = "\xf0\x9f\x92\xb7", + "money_with_wings" = "\xf0\x9f\x92\xb8", + "credit_card" = "\xf0\x9f\x92\xb3", + "gem" = "\xf0\x9f\x92\x8e", + "bomb" = "\xf0\x9f\x92\xa3", + "gift" = "\xf0\x9f\x8e\x81", + "balloon" = "\xf0\x9f\x8e\x88", + "tada" = "\xf0\x9f\x8e\x89", + "confetti_ball" = "\xf0\x9f\x8e\x8a", + "package" = "\xf0\x9f\x93\xa6", + "mailbox" = "\xf0\x9f\x93\xab", + "inbox_tray" = "\xf0\x9f\x93\xa5", + "outbox_tray" = "\xf0\x9f\x93\xa4", + "email" = "\xe2\x9c\x89\xef\xb8\x8f", + "envelope" = "\xe2\x9c\x89\xef\xb8\x8f", + "incoming_envelope" = "\xf0\x9f\x93\xa8", + + // Activities + "soccer" = "\xe2\x9a\xbd", + "basketball" = "\xf0\x9f\x8f\x80", + "football" = "\xf0\x9f\x8f\x88", + "baseball" = "\xe2\x9a\xbe", + "tennis" = "\xf0\x9f\x8e\xbe", + "8ball" = "\xf0\x9f\x8e\xb1", + "bowling" = "\xf0\x9f\x8e\xb3", + "video_game" = "\xf0\x9f\x8e\xae", + "dart" = "\xf0\x9f\x8e\xaf", + "game_die" = "\xf0\x9f\x8e\xb2", + "slot_machine" = "\xf0\x9f\x8e\xb0", + "cards" = "\xf0\x9f\x83\x8f", + "black_joker" = "\xf0\x9f\x83\x9f", + "mahjong" = "\xf0\x9f\x80\x84", + "musical_note" = "\xf0\x9f\x8e\xb5", + "notes" = "\xf0\x9f\x8e\xb6", + "saxophone" = "\xf0\x9f\x8e\xb7", + "guitar" = "\xf0\x9f\x8e\xb8", + "musical_keyboard" = "\xf0\x9f\x8e\xb9", + "trumpet" = "\xf0\x9f\x8e\xba", + "violin" = "\xf0\x9f\x8e\xbb", + "drum" = "\xf0\x9f\xa5\x81", + "headphones" = "\xf0\x9f\x8e\xa7", + "microphone" = "\xf0\x9f\x8e\xa4", + "level_slider" = "\xf0\x9f\x8e\x9a", + "control_knobs" = "\xf0\x9f\x8e\x9b", + "ticket" = "\xf0\x9f\x8e\xab", + "art" = "\xf0\x9f\x8e\xa8", + "circus_tent" = "\xf0\x9f\x8e\xaa", + "theater_masks" = "\xf0\x9f\x8e\xad", + "clapper" = "\xf0\x9f\x8e\xac", + + // Travel + "car" = "\xf0\x9f\x9a\x97", + "taxi" = "\xf0\x9f\x9a\x95", + "bus" = "\xf0\x9f\x9a\x8d", + "train" = "\xf0\x9f\x9a\x86", + "metro" = "\xf0\x9f\x9a\x87", + "light_rail" = "\xf0\x9f\x9a\x88", + "tram" = "\xf0\x9f\x9a\x8a", + "bike" = "\xf0\x9f\x9a\xb2", + "motorcycle" = "\xf0\x9f\x8f\x8d", + "airplane" = "\xe2\x9c\x88\xef\xb8\x8f", + "helicopter" = "\xf0\x9f\x9a\x81", + "boat" = "\xe2\x9b\xb5", + "sailboat" = "\xe2\x9b\xb5", + "ship" = "\xf0\x9f\x9a\xa2", + "fuelpump" = "\xe2\x9b\xbd", + "construction" = "\xf0\x9f\x9a\xa7", + "house" = "\xf0\x9f\x8f\xa0", + "house_with_garden" = "\xf0\x9f\x8f\xa1", + "office" = "\xf0\x9f\x8f\xa2", + "post_office" = "\xf0\x9f\x8f\xa3", + "hospital" = "\xf0\x9f\x8f\xa5", + "bank" = "\xf0\x9f\x8f\xa6", + "hotel" = "\xf0\x9f\x8f\xa8", + "school" = "\xf0\x9f\x8f\xab", + "department_store" = "\xf0\x9f\x8f\xac", + "church" = "\xe2\x9b\xaa", + "castle" = "\xf0\x9f\x8f\xb0", + "factory" = "\xf0\x9f\x8f\xad", + "tokyo_tower" = "\xf0\x9f\x97\xbc", + "statue_of_liberty" = "\xf0\x9f\x97\xbd", + "fountain" = "\xe2\x9b\xb2", + "tent" = "\xe2\x9b\xba", + "mountain" = "\xf0\x9f\x8f\x94", + "snow_capped_mountain" = "\xf0\x9f\x8f\x94", + "beach" = "\xf0\x9f\x8f\x96", + "camping" = "\xf0\x9f\x8f\x95", + "world_map" = "\xf0\x9f\x97\xba", + "japan" = "\xf0\x9f\x97\xbe", +}; + +expand_emoji :: proc(text: string) -> string { + if len(EMOJIS) == 0 { + return text + } + + parts: [dynamic]string + defer delete(parts) + + remaining := text + + for { + colon := strings.index(remaining, ":") + if colon < 0 { + append(&parts, remaining) + break + } + + // Find the closing colon + after := remaining[colon + 1:] + end := strings.index(after, ":") + if end < 0 { + append(&parts, remaining) + break + } + + shortcode := remaining[colon + 1 : colon + 1 + end] + + // Validate: shortcode must be all lowercase letters, digits, or underscores + // and must not contain whitespace + valid := true + for c in shortcode { + if !(c >= 'a' && c <= 'z') && !(c >= '0' && c <= '9') && c != '_' && c != '+' && c != '-' { + valid = false + break + } + } + if !valid || len(shortcode) == 0 { + append(&parts, remaining[:colon + 1]) + remaining = remaining[colon + 1:] + continue + } + + emoji, found := EMOJIS[shortcode] + if !found { + append(&parts, remaining[:colon + 1]) + remaining = remaining[colon + 1:] + continue + } + + // Replace :shortcode: with emoji + append(&parts, remaining[:colon]) + append(&parts, emoji) + remaining = remaining[colon + 1 + end + 1:] + } + + return strings.join(parts[:], "") +}