prime8dev

prime8dev
git clone https://git.prime8.dev/prime8dev.git
Log | Files | Refs | README | LICENSE

commit 0f1d364b2a636d5881740ab509efdaa0261fc93b
parent b00af639b7bd3f08408367f8fe1dec0c7aac91d2
Author: Damian Myrda <damian@prime8.dev>
Date:   Wed, 21 Oct 2026 02:50:31 +0000

feat: curl prime8.dev prints the fetch block as ansi text

Diffstat:
Acmd/server/handlers/curl.go | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcmd/server/handlers/home.go | 4++++
Minternal/tui/about_ascii.go | 33++-------------------------------
Ainternal/utils/logo.go | 35+++++++++++++++++++++++++++++++++++
4 files changed, 147 insertions(+), 31 deletions(-)

diff --git a/cmd/server/handlers/curl.go b/cmd/server/handlers/curl.go @@ -0,0 +1,106 @@ +package handlers + +import ( + "fmt" + "net/http" + "strconv" + "strings" + "sync" + "time" + "unicode/utf8" + + "git.prime8.dev/moncheeta/prime8dev/internal/config" + "git.prime8.dev/moncheeta/prime8dev/internal/utils" +) + +const ( + curlLogoWidth = 32 + ansiReset = "\x1b[0m" + ansiDim = "\x1b[38;5;242m" + ansiAccent = "\x1b[38;5;179m" + ansiGreen = "\x1b[38;5;71m" + ansiBlue = "\x1b[38;5;110m" +) + +var curlLogo = sync.OnceValue(func() string { return utils.LogoASCII(curlLogoWidth, false) }) + +// isTerminalClient reports whether the request comes from a CLI http client. +func isTerminalClient(r *http.Request) bool { + ua := strings.ToLower(r.UserAgent()) + for _, p := range []string{"curl/", "wget/", "httpie/", "xh/"} { + if strings.HasPrefix(ua, p) { + return true + } + } + return false +} + +func (h *Handler) serveTerminalFetch(w http.ResponseWriter) { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + w.Header().Set("Vary", "User-Agent") + fmt.Fprint(w, terminalFetch(config.GetAbout(), config.CurrentJob(config.GetExperience()))) +} + +func terminalFetch(about config.About, job *config.Experience) string { + role := about.Profession + if job != nil { + role = job.Role + " @ " + job.Company + } + title := about.Handle + "@prime8" + + row := func(key, value string) string { return ansiAccent + key + ansiReset + ": " + value } + lines := []string{ + ansiGreen + about.Handle + ansiReset + ansiDim + "@" + ansiReset + ansiAccent + "prime8" + ansiReset, + ansiDim + strings.Repeat("-", len(title)) + ansiReset, + row("name", about.Name), + row("role", role), + row("location", about.Location), + row("uptime", utils.Uptime(config.Birthday, time.Now())), + } + for _, kv := range about.Setup { + lines = append(lines, row(kv.Key, kv.Value)) + } + langs := make([]string, len(about.Languages)) + for i, l := range about.Languages { + langs[i] = hexFG(utils.TagColor(l)) + l + ansiReset + } + lines = append(lines, + row("langs", strings.Join(langs, ", ")), + row("email", ansiBlue+"damian@prime8.dev"+ansiReset), + row("web", ansiBlue+"https://prime8.dev"+ansiReset), + "", + ansiDim+"the full thing: "+ansiReset+ansiGreen+"ssh prime8.dev"+ansiReset, + ) + + logo := strings.Split(curlLogo(), "\n") + if len(logo) == 1 && logo[0] == "" { + return "\n" + strings.Join(lines, "\n") + "\n\n" + } + + var b strings.Builder + b.WriteString("\n") + for i := 0; i < max(len(logo), len(lines)); i++ { + l := "" + if i < len(logo) { + l = logo[i] + } + b.WriteString(l + strings.Repeat(" ", max(0, curlLogoWidth-utf8.RuneCountInString(l))) + " ") + if i < len(lines) { + b.WriteString(lines[i]) + } + b.WriteString("\n") + } + b.WriteString("\n") + return b.String() +} + +func hexFG(hex string) string { + if len(hex) != 7 || hex[0] != '#' { + return "" + } + v, err := strconv.ParseUint(hex[1:], 16, 32) + if err != nil { + return "" + } + return fmt.Sprintf("\x1b[38;2;%d;%d;%dm", v>>16, v>>8&0xff, v&0xff) +} diff --git a/cmd/server/handlers/home.go b/cmd/server/handlers/home.go @@ -9,6 +9,10 @@ import ( ) func (h *Handler) HomeHandler(w http.ResponseWriter, r *http.Request) { + if isTerminalClient(r) { + h.serveTerminalFetch(w) + return + } about := config.GetAbout() experience := config.GetExperience() data := web.BaseData("About", "about", about) diff --git a/internal/tui/about_ascii.go b/internal/tui/about_ascii.go @@ -1,40 +1,11 @@ package tui -import ( - "path/filepath" - "strings" - "sync" - - aic "github.com/TheZoraiz/ascii-image-converter/aic_package" -) - -var aboutASCIIConvertMu sync.Mutex +import "git.prime8.dev/moncheeta/prime8dev/internal/utils" func (m portfolioModel) aboutASCIIFor(width int) string { return m.aboutASCIICache[width] } func loadAboutASCII(width int) string { - flags := aic.DefaultFlags() - flags.Width = width - flags.Colored = true - flags.Braille = false - flags.Dither = true - flags.Threshold = 160 - - aboutASCIIConvertMu.Lock() - defer aboutASCIIConvertMu.Unlock() - - path := filepath.Join("static", "me.jpg") - ascii, err := aic.Convert(path, flags) - if err != nil { - // aic refuses color when the server's terminal env lacks COLORTERM; - // fall back to plain ascii so the logo always renders. - flags.Colored = false - if ascii, err = aic.Convert(path, flags); err != nil { - return "" - } - } - - return strings.TrimRight(ascii, "\n") + return utils.LogoASCII(width, true) } diff --git a/internal/utils/logo.go b/internal/utils/logo.go @@ -0,0 +1,35 @@ +package utils + +import ( + "path/filepath" + "strings" + "sync" + + aic "github.com/TheZoraiz/ascii-image-converter/aic_package" +) + +var logoMu sync.Mutex + +// LogoASCII renders static/me.jpg as ascii art. aic refuses color when the +// server's env lacks COLORTERM, so it falls back to plain ascii. +func LogoASCII(width int, colored bool) string { + flags := aic.DefaultFlags() + flags.Width = width + flags.Colored = colored + flags.Dither = true + flags.Threshold = 160 + + logoMu.Lock() + defer logoMu.Unlock() + + path := filepath.Join("static", "me.jpg") + ascii, err := aic.Convert(path, flags) + if err != nil && colored { + flags.Colored = false + ascii, err = aic.Convert(path, flags) + } + if err != nil { + return "" + } + return strings.TrimRight(ascii, "\n") +}