diff options
| author | Kyren223 <contact@kyren.codes> | 2026-10-08 19:44:46 +0300 |
|---|---|---|
| committer | Kyren223 <contact@kyren.codes> | 2026-10-08 19:44:46 +0300 |
| commit | 9b03d2790b8b8db1b5a6755b6130f37e5f7f2455 (patch) | |
| tree | a84f6e9cfe2b8aaf087d55830c938443f6be683e /website.templ | |
| parent | 6e70fe447ec9504757fbdc152b5b3697c9af8def (diff) | |
WIP
Diffstat (limited to 'website.templ')
| -rw-r--r-- | website.templ | 352 |
1 files changed, 342 insertions, 10 deletions
diff --git a/website.templ b/website.templ index face9f9..1f4f636 100644 --- a/website.templ +++ b/website.templ @@ -19,18 +19,97 @@ templ HeadComponent(title string, description string) { <meta property="og:image" content="https://kyren.codes/image.png"/> <meta property="og:image:alt" content="a cyan sphere with a dark background"/> <meta property="og:site_name" content="Kyren's Website"/> + <script src="/public/copy.js" defer></script> <script src="/public/theme.js" defer></script> + <script src="/public/scrollbar.js" defer></script> + <script src="/public/my_uptime.js" defer></script> <link rel="stylesheet" href="/public/style.css"/> { children... } </head> } +templ BlogItem(header string, blog Blog, extra string) { + @TerminalBorder(header, blog.Date.Format("January 2, 2006"), extra) { + <span class="cursor-pointer group block"> + <a href={ templ.SafeURL("/blogs/" + blog.Slug) } class="no-underline hover:no-underline"> + <h1 class="text-[130%] font-bold text-(--accent) group-hover:underline"> + { blog.Title } + </h1> + <p class="text-(--secondary)">{ blog.Desc }</p> + </a> + </span> + } +} + +templ Link(href string, newTab bool) { + <a + href={ templ.SafeURL(href) } + if newTab { + target="_blank" + rel="noopener noreferrer" + } + > + { children... } + </a> +} + +templ TerminalStack(joined bool, extra string) { + <div class={ "flex flex-col min-h-0", templ.KV("[&>*+*]:mt-[-0.1rem]", joined), templ.KV("gap-4", !joined), extra }> + { children... } + </div> +} + +templ TerminalBorder(header string, footer string, extra string) { + <div class={ "relative flex px-3.5 py-(--ypad) pt-[calc(var(--ypad)+0.0625rem)] [--ypad:var(--border-py,0.875rem)] border-[0.1rem] border-solid border-[color:var(--alt)]", extra }> + <span class="absolute px-[0.625rem] text-[1rem] left-[0.625rem] top-[-0.75rem] bg-[var(--background)] text-[color:var(--alt)]">{ header }</span> + <span class="absolute px-[0.625rem] text-[1rem] left-[0.625rem] bottom-[-0.75rem] bg-[var(--background)] text-[color:var(--alt)]">{ footer }</span> + { children... } + </div> +} + +templ CopyIcon() { + <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy preview-icon"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg> +} + +templ TerminalBorderWithCopy(header string, footer string, extra string, text string, href string, showLabel bool) { + @TerminalBorder(header, footer, extra) { + <div data-copy-scope class="group/copy flex w-full items-center justify-between gap-4"> + if href != "" { + <a href={ templ.SafeURL(href) } class="wrap-anywhere"> + { children... } + </a> + } else { + <span data-copy={ text } class="cursor-pointer wrap-anywhere"> + { children... } + </span> + } + <span class="relative shrink-0"> + if showLabel { + <span class="hidden group-data-copied/copy:block absolute right-full top-1/2 -translate-y-1/2 mr-2 px-[0.4rem] whitespace-nowrap bg-(--background) text-(--primary)">copied</span> + } + <button type="button" data-copy={ text } aria-label="copy" class="block p-[0.4rem] rounded-[0.4rem] cursor-pointer transition-colors hover:bg-(--alt)/25 group-data-copied/copy:bg-(--primary)/25!"> + @CopyIcon() + </button> + </span> + </div> + } +} + +templ CopyPre(text string, preClass string) { + <button type="button" data-copy={ text } class="group relative shrink-0 cursor-pointer text-left rounded-[0.4rem] p-[0.4rem] -m-[0.4rem] transition-colors hover:bg-(--alt)/25 data-copied:bg-(--primary)/25! data-copied:animate-[bump_0.25s]"> + <pre class={ preClass }> + { children... } + </pre> + <span class="hidden group-data-copied:block absolute right-[0.6rem] top-[0.6rem] px-[0.4rem] bg-(--background) text-(--primary) text-[0.8rem]">copied</span> + </button> +} + templ NavLink(href string, label string) { <a href={ templ.SafeURL(href) } class="text-(--primary) hover:text-(--accent)">{ label }</a> } -templ NavComponent(path string) { - <nav class="flex justify-between gap-10 p-4 mb-[1.5625rem] border-2 border-(--alt) text-[1.375rem]"> +templ Nav(path string) { + <nav class="flex justify-between gap-10 p-4 mb-6 border-[0.125rem] border-(--alt) text-[1.375rem]"> <span>{ path }</span> <span class="flex gap-[1ch]"> @NavLink("/", "home") @@ -49,27 +128,109 @@ templ TerminalLayout(style Style, path string) { <link rel="stylesheet" href="/public/styles/terminal.css"/> } <body> - <main class="w-[75rem] h-[40rem] p-8 border-[0.2rem] border-(--text) overflow-hidden text-[1.125rem]"> - @NavComponent(path) + <main class="flex flex-col w-[75rem] h-[40rem] p-8 border-[0.2rem] border-(--text) overflow-hidden text-[1.125rem]"> + @Nav(path) { children... } </main> </body> </html> } -templ HomePage(style Style) { +const fingerprint = `+--[ED25519 256]--+ +| +. .. +.B| +| + o..o . BB| +| ..o oo.o =+E| +| ... oo. + o.| +| . S . = | +| . . + o | +| o o o . | +| oo .o+ + | +| ++ooo+= | ++----[SHA256]-----+` +const sshKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO7P9K9D5RkBk+JCRRS6AtHuTAc6cRpXfRfRMg/Kyren" + +templ HomePage(style Style, latestBlog *Blog) { @TerminalLayout(style, "/home/kyren/") { - <div id="content" class="w-0 h-0 invisible sm:w-full sm:h-full sm:visible"> - <h1>Home</h1> - <p>Go server running with Templ templates and Air hot-reloading.</p> + <div class={ "grid grid-cols-[29%_1fr] gap-x-8 flex-1 min-h-0", templ.KV("gap-y-4", !false) }> + <div class="flex flex-col min-h-0 [--border-py:0.75rem]"> + @TerminalStack(true, "") { + @TerminalBorder("Profile", "", "[--border-py:0.875rem]") { + <div class="flex items-center gap-4 h-38"> + <img src="/public/favicon.svg" alt="picture" class="h-full object-cover"/> + @CopyPre(sshKey, "shrink-0 text-[0.675rem] leading-[normal]") { + { fingerprint } + } + </div> + } + @TerminalBorderWithCopy("Email", "", "", "contact@kyren.codes", "mailto:contact@kyren.codes", false) { + contact@kyren.codes + } + @TerminalBorder("Git Repos (main)", "", "") { + @Link("https://git.kyren.codes", true) { + git.kyren.codes + } + } + @TerminalBorder("GitHub (secondary)", "", "") { + @Link("https://github.com/kyren223", true) { + github.com/Kyren223 + } + } + @TerminalBorder("Discord", "", "") { + @Link("https://discord.com/users/481884415090229258", true) { + Kyren223 + } + } + } + </div> + <div class="flex flex-col gap-10 min-h-0 select-text"> + @TerminalBorder("Status", "", "") { + <div class="flex gap-[1ch] text-[1rem]"> + <span>Working on</span> + <span> + @Link("/projects/krypton", false) { + Krypton + } + </span> + <span>|</span> + <span>Learning Langdev & Compiler Design</span> + <span>|</span> + <span>Available for hire</span> + </div> + } + @TerminalBorder("About Me", "", "") { + Hi! I'm Kyren, a softwarea engineer and game developer. + I love working with low level languages and learning things deeply. + I am also a terminal nerd and Linux enthusiast who uses NVIM btw. + And when I am not coding, I am most likely playing video games, + watching Anime, or solving Rubik's cubes fast. + } + @BlogItem("Latest Blog Post", *latestBlog, "") + </div> + @TerminalBorderWithCopy("SSH Key", "", footerClass(false), sshKey, "", true) { + { sshKey } + } </div> } } +func footerClass(connected bool) string { + style := "col-span-2 select-text [--border-py:0.5rem]" + if connected { + style += " mb-[0.30rem]" + } + return style +} + templ SkillsPage(style Style) { @TerminalLayout(style, "/home/kyren/skills.md") { - <h1 class="text-3xl font-bold">About</h1> - <p>Single-binary web app using Go's standard library net/http.</p> + @TerminalStack(false, "") { + @TerminalBorder("Setup", "", "") { + <span> + $ fastfetch + @Fastfetch(fetchRows) + </span> + } + } } } @@ -86,3 +247,174 @@ templ ProjectsPage(style Style, projects []Project) { </div> } } + +templ NotFoundPage(style Style, path string) { + @TerminalLayout(style, "404") { + @TerminalStack(true, "") { + @TerminalBorder("Shell", "", "") { + <span> + <p>$ cd ~{ path }</p> + <p class="pt-3">"{ path[1:] }": No such file or directory (os error 2) </p> + </span> + } + @TerminalBorder("", "", "") { + <div class="gap-[1ch]"> + <span>{ ">" }</span> + <span class="text-(--secondary)"> + @Link("/", false) { + { "cd $HOME" } + } + </span> + </div> + } + } + } +} + +templ ScrollBox(extra string) { + <div data-scrollbox class={ "flex flex-1 min-h-0", extra }> + <div data-scroll-area class="flex-1 min-w-0 overflow-y-auto border-[0.1rem] border-solid border-(--alt) p-[1rem] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"> + { children... } + </div> + <div data-scroll-track class="relative w-8 -ml-[0.1rem] border-[0.1rem] border-solid border-(--alt)"> + <div data-scroll-thumb class="absolute inset-x-[0.3rem] bg-(--secondary) cursor-grab touch-none"></div> + </div> + </div> +} + +templ BlogsPage(style Style, blogs []Blog) { + @TerminalLayout(style, "blogs") { + @TerminalBorder("Shell", "", "mb-[1rem]") { + $ ls -lh ~/blogs + } + @ScrollBox("") { + @TerminalStack(false, "mt-[0.5rem]") { + for i := 0; i < 1; i++ { + for _, blog := range blogs { + @BlogItem("", blog, "mb-[1rem]") + } + } + } + } + } +} + +type FetchRow struct { + Icon templ.Component + Color string // tailwind text color class, used for the icon and label + Label string + Value string +} + +templ IconMonitor(color string) { + @Icon(color) { + <rect width="20" height="14" x="2" y="3" rx="2"></rect> + <line x1="8" x2="16" y1="21" y2="21"></line> + <line x1="12" x2="12" y1="17" y2="21"></line> + } +} + +templ IconTerminal(color string) { + @Icon(color) { + <polyline points="4 17 10 11 4 5"></polyline> + <line x1="12" x2="20" y1="19" y2="19"></line> + } +} + +templ IconCode(color string) { + @Icon(color) { + <polyline points="16 18 22 12 16 6"></polyline> + <polyline points="8 6 2 12 8 18"></polyline> + } +} + +templ IconCpu(color string) { + @Icon(color) { + <rect width="16" height="16" x="4" y="4" rx="2"></rect> + <rect width="6" height="6" x="9" y="9" rx="1"></rect> + <path d="M15 2v2M15 20v2M2 15h2M2 9h2M20 15h2M20 9h2M9 2v2M9 20v2"></path> + } +} + +templ IconScriptLang(color string) { + @Icon(color) { + <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-file-terminal preview-icon"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"></path><path d="M14 2v5a1 1 0 0 0 1 1h5"></path><path d="m8 16 2-2-2-2"></path><path d="M12 18h4"></path></svg> + } +} + +templ IconEditor(color string) { + @Icon(color) { + <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-pen preview-icon"><path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z"></path></svg> + } +} + +templ IconDebugger(color string) { + @Icon(color) { + <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bug-off preview-icon"><path d="M12 20v-8"></path><path d="M12.656 7H14a4 4 0 0 1 4 4v1.344"></path><path d="M14.12 3.88 16 2"></path><path d="M17.123 17.123A6 6 0 0 1 6 14v-3a4 4 0 0 1 1.72-3.287"></path><path d="m2 2 20 20"></path><path d="M21 5a4 4 0 0 1-3.55 3.97"></path><path d="M22 13h-3.344"></path><path d="M3 21a4 4 0 0 1 3.81-4"></path><path d="M3 5a4 4 0 0 0 3.55 3.97"></path><path d="M6 13H2"></path><path d="m8 2 1.88 1.88"></path><path d="M9.712 4.06A3 3 0 0 1 15 6v1.13"></path></svg> + } +} + +templ IconKeyboard(color string) { + @Icon(color) { + <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-keyboard preview-icon"><path d="M10 8h.01"></path><path d="M12 12h.01"></path><path d="M14 8h.01"></path><path d="M16 12h.01"></path><path d="M18 8h.01"></path><path d="M6 8h.01"></path><path d="M7 16h10"></path><path d="M8 12h.01"></path><rect width="20" height="16" x="2" y="4" rx="2"></rect></svg> + } +} + +templ IconUptime(color string) { + @Icon(color) { + <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-clock preview-icon"><circle cx="12" cy="12" r="10"></circle><path d="M12 6v6l4 2"></path></svg> + } +} + +func row(icon func(color string) templ.Component, color, label, value string) FetchRow { + return FetchRow{Icon: icon(color), Color: color, Label: label, Value: value} +} + +var fetchRows = []FetchRow{ + // row(IconTerminal, "text-(--secondary)", "Terminal", "Ghostty & Zsh"), + row(IconCode, "text-(--accent)", "Main Language", "C"), + row(IconScriptLang, "text-(--accent)", "Scripting Langs", "Go and Python"), + row(IconDebugger, "text-[#F16265]", "Debugger", "Raddebugger"), + row(IconMonitor, "text-[#54D7A9]", "Operating System", "Fedora KDE Linux"), + row(IconEditor, "text-[#54D7A9]", "Text Editor", "Neovim"), + row(IconUptime, "text-[#D8C44B]", "Uptime", GetMyUptime()), + // row(IconCpu, "text-(--extra)", "Hardware", "..."), +} + +func branch(i, n int) string { + switch { + case n == 1: + return "─" + case i == 0: + return "┌" + case i == n-1: + return "└" + default: + return "├" + } +} + +templ Fastfetch(rows []FetchRow) { + <div + style=" + display: grid; + grid-template-columns: auto auto auto 1fr; + align-items: center; + column-gap: 1ch; + line-height: normal; + " + > + for i, row := range rows { + <span class="text-(--alt)">{ branch(i, len(rows)) }</span> + @row.Icon + <span class={ row.Color, "font-bold" }>{ row.Label }</span> + <span id={ row.Label }>{ row.Value }</span> + } + </div> +} + +templ Icon(color string) { + <svg width="1.1em" height="1.1em" class={ "shrink-0", color } viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> + { children... } + </svg> +} |
