Compare commits

..

1 Commits

Author SHA1 Message Date
Spencer Brower 0dc6cc117f feat: Added robots.txt 2026-07-11 10:45:22 -04:00
4 changed files with 6 additions and 552 deletions
+4 -3
View File
@@ -3,14 +3,15 @@
- Look at the source for 3 template languages that support mustache syntax,
and see how they implement the tempaltes, then pick the best one. One of them
should be Hugo / go.
- [ ] robots.txt?
- [ ] Evaluate Tufte CSS — borrow sidenote CSS or replace TailwindCSS entirely
- 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
- [x] Emoji shortcodes (`:shrug:` etc.) — 2 instances
- [ ] Backslash in shrug not visible.
- [ ] Emoji shortcodes (`:shrug:` etc.) — 2 instances
- [ ] include-code shortcode (`{{< include-code ... >}}`) — i-ported-fd-to-odin
- [x] Nix build integration — main flake runs thor + tailwindcss instead of Hugo
- [ ] 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
-108
View File
@@ -1,108 +0,0 @@
#+feature dynamic-literals
package main
import "core:fmt"
import "core:strings"
ALERT_STYLES: map[string]string = {
"note" = "border-l-blue-500 bg-blue-950/30 text-slate-300",
"tip" = "border-l-green-500 bg-green-950/30 text-slate-300",
"important" = "border-l-purple-500 bg-purple-950/30 text-slate-300",
"warning" = "border-l-yellow-500 bg-yellow-950/30 text-slate-300",
"caution" = "border-l-red-500 bg-red-950/30 text-slate-300",
}
ALERT_EMOJIS: map[string]string = {
"note" = "\xe2\x84\xb9\xef\xb8\x8f",
"tip" = "\xf0\x9f\x92\xa1",
"important" = "\xe2\x9d\x97",
"warning" = "\xe2\x9a\xa0\xef\xb8\x8f",
"caution" = "\xe2\x9d\x97",
}
inject_alerts :: proc(html: string) -> string {
parts: [dynamic]string
defer delete(parts)
remaining := html
for {
bq_start := strings.index(remaining, "<blockquote>")
if bq_start < 0 {
append(&parts, remaining)
break
}
bq_close := strings.index(remaining, "</blockquote>")
if bq_close < 0 {
append(&parts, remaining)
break
}
bq_end := bq_close + len("</blockquote>")
bq := remaining[bq_start:bq_end]
// Append text before this blockquote
append(&parts, remaining[:bq_start])
// Transform if it's an alert, otherwise keep as-is
append(&parts, transform_alert(bq))
remaining = remaining[bq_end:]
}
return strings.join(parts[:], "")
}
transform_alert :: proc(bq: string) -> string {
// Skip <blockquote> tag and whitespace
pos := len("<blockquote>")
for pos < len(bq) && (bq[pos] == '\n' || bq[pos] == '\r' || bq[pos] == ' ' || bq[pos] == '\t') {
pos += 1
}
// Check for <p>[!
if pos + 4 >= len(bq) || bq[pos] != '<' || bq[pos + 1] != 'p' || bq[pos + 2] != '>' ||
bq[pos + 3] != '[' || bq[pos + 4] != '!' {
return bq
}
// Extract alert type until ]
close := strings.index(bq[pos + 5:], "]")
if close < 0 {
return bq
}
type_raw := bq[pos + 5 : pos + 5 + close]
type_lower := strings.to_lower(type_raw)
style, has_style := ALERT_STYLES[type_lower]
emoji, has_emoji := ALERT_EMOJIS[type_lower]
if !has_style || !has_emoji {
return bq
}
// Position after ]
after_type := pos + 5 + close + 1
// Skip optional + or -
content_start := after_type
if content_start < len(bq) && (bq[content_start] == '+' || bq[content_start] == '-') {
content_start += 1
}
// Skip space after marker
if content_start < len(bq) && bq[content_start] == ' ' {
content_start += 1
}
// Rebuild: styled blockquote + bold title paragraph + rest
rest := bq[content_start:]
return fmt.aprintf(
`<blockquote class="alert %s rounded-r py-2">
<p class="font-bold mb-1">%s %s`,
style,
emoji,
rest,
)
}
+2 -3
View File
@@ -173,10 +173,9 @@ load_page :: proc(
if strings.has_suffix(file_path, ".html") {
page.body_html = strings.clone(body)
} else {
expanded := expand_emoji(body)
clean_body, defs := strip_definitions(expanded)
clean_body, defs := strip_definitions(body)
html := cm.markdown_to_html_from_string(clean_body, {.Unsafe})
page.body_html = inject_alerts(inject_sidenotes(html, defs))
page.body_html = inject_sidenotes(html, defs)
}
switch page_type {
-438
View File
@@ -1,438 +0,0 @@
#+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[:], "")
}