tags.go (3698B)
1 package utils 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 ) 8 9 // Tag is one colored label. Web and TUI both read this table, so a color 10 // is changed here and nowhere else. 11 type Tag struct { 12 Class string 13 BG, FG string 14 Aliases []string // lowercase labels that map to this tag 15 } 16 17 const ( 18 dark = "#0d0d0d" 19 light = "#ffffff" 20 ) 21 22 // Brand colors where a brand exists; otherwise picked to stay distinct 23 // from their neighbors in the same row. 24 var Tags = []Tag{ 25 // languages 26 {"lang-zig", "#f7a41d", dark, []string{"zig"}}, 27 {"lang-rust", "#dea584", dark, []string{"rust"}}, 28 {"lang-c", "#a8b9cc", dark, []string{"c"}}, 29 {"lang-go", "#00add8", dark, []string{"go", "golang"}}, 30 {"lang-ts", "#3178c6", light, []string{"typescript", "ts"}}, 31 {"lang-python", "#ffd43b", dark, []string{"python"}}, 32 33 // tools 34 {"tool-crux", "#2e5c9a", light, []string{"crux linux", "crux"}}, 35 {"tool-helix", "#7f5ad6", light, []string{"helix"}}, 36 {"tool-fish", "#2aa198", dark, []string{"fish"}}, 37 {"tool-claude", "#d97757", dark, []string{"claude code", "claude"}}, 38 39 // interests 40 {"interest-osdev", "#b4469b", light, []string{"osdev"}}, 41 {"interest-firmware", "#2e8b57", light, []string{"firmware"}}, 42 {"interest-tui", "#ffb000", dark, []string{"tui/cli tooling", "tui"}}, 43 {"interest-unix", "#4c78a8", light, []string{"unix philosophy", "unix"}}, 44 {"interest-webdev", "#2d7d9a", light, []string{"web development", "webdev"}}, 45 46 // hobbies 47 {"hobby-audi", "#f2c400", dark, []string{"old-school audis"}}, 48 {"hobby-rally", "#d7263d", light, []string{"rally"}}, 49 {"hobby-soccer", "#2e9d5b", light, []string{"pickup soccer"}}, 50 51 // project and experience tech 52 {"tech-cuda", "#76b900", dark, []string{"cuda"}}, 53 {"tech-docker", "#2496ed", light, []string{"docker"}}, 54 {"tech-kafka", "#e8e8e8", dark, []string{"kafka"}}, 55 {"tech-rn", "#61dafb", dark, []string{"react native"}}, 56 {"tech-supabase", "#3ecf8e", dark, []string{"supabase"}}, 57 {"tech-rest", "#8b95a5", dark, []string{"rest"}}, 58 {"tech-temporal", "#7c6cf2", light, []string{"temporal"}}, 59 {"tech-pytorch", "#ee4c2c", light, []string{"pytorch"}}, 60 {"tech-postgres", "#336791", light, []string{"postgresql", "postgres"}}, 61 {"tech-astro", "#ff5d01", light, []string{"astro"}}, 62 {"project-portfolio", "#b07a2f", dark, []string{"portfolio"}}, 63 } 64 65 var tagByLabel = func() map[string]*Tag { 66 m := make(map[string]*Tag) 67 for i := range Tags { 68 for _, a := range Tags[i].Aliases { 69 m[a] = &Tags[i] 70 } 71 } 72 return m 73 }() 74 75 // LookupTag finds a tag by label, case-insensitively. 76 func LookupTag(label string) (Tag, bool) { 77 t, ok := tagByLabel[strings.ToLower(strings.TrimSpace(label))] 78 if !ok { 79 return Tag{}, false 80 } 81 return *t, true 82 } 83 84 // TagClass returns the CSS class for a label, or "" for the default style. 85 func TagClass(label string) string { 86 t, _ := LookupTag(label) 87 return t.Class 88 } 89 90 // TagColor returns the background color for a label, or "" if untagged. 91 func TagColor(label string) string { 92 t, _ := LookupTag(label) 93 return t.BG 94 } 95 96 // TagCSS renders one rule per tag for the web stylesheet. 97 func TagCSS() string { 98 rules := make([]string, 0, len(Tags)) 99 for _, t := range Tags { 100 rules = append(rules, fmt.Sprintf(".tag.%s{background:%s;color:%s}", t.Class, t.BG, t.FG)) 101 } 102 sort.Strings(rules) 103 return strings.Join(rules, "\n") 104 } 105 106 var techAbbreviations = map[string]string{ 107 "typescript": "TS", 108 } 109 110 // TechMeta is the short "rust, cuda" line shown in the project list. 111 func TechMeta(tech []string) string { 112 parts := make([]string, 0, 2) 113 for _, item := range tech { 114 if len(parts) == 2 { 115 break 116 } 117 if short, ok := techAbbreviations[strings.ToLower(item)]; ok { 118 item = short 119 } 120 parts = append(parts, item) 121 } 122 return strings.Join(parts, ", ") 123 }