custard — web code forge over Soft Serve
62366fb34f1ee8d458104784f3f4ba5f0bef9429
humdrum-tiv <45084903+humdrum-tiv@users.noreply.github.com> · 2026-06-17 19:50
custard — web code forge over Soft Serve A read-only, public-facing web UI over a self-hosted Soft Serve git server: repo browser, syntax-highlighted blobs, commit log + per-file diffs, branches and tags, and a GitHub-style issues view backed by in-repo Backlog.md tasks. Themed on the shared app-kit token system with a Bubble Tea / Charm aesthetic (7 themes incl. e-ink). Pure Go (go-git, templ, chroma, goldmark). Repo visibility honors Soft Serve's private/hidden flags — only public repos are served. Deployed behind Caddy at https://git.kortum.world. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
29 files changed
.gitignore +0 −17
@@ -1,17 +0,0 @@
-/custard
-/repos/
-*.out
-
-# Proprietary fonts — present on disk for build-time go:embed, never committed.
-web/static/fonts/
-
-# Agent / assistant instruction files — kept local, never published (this repo is public).
-CLAUDE.md
-AGENTS.md
-GEMINI.md
-.cursor/
-.cursorrules
-.windsurfrules
-.aider*
-.claude/
-.github/copilot-instructions.md
PLAN.md +0 −110
@@ -1,110 +0,0 @@
-# custard — build plan
-
-A custom, public-facing web code forge over my self-hosted **Soft Serve** git server.
-Own design — not a skinned cgit/gitweb. Reads bare repos directly; renders my own
-templates. Aesthetic = **Bubble Tea / Charm terminal vibe** + **_shared-app-kit** tokens
-(Flexoki / Uchu / Humdrum themes, Awke / Untitled Sans / Name Mono fonts).
-
-> Soft Serve is the git host (SSH push/TUI, read-only HTTP clone, webhooks). custard is a
-> separate read-only Go web app pointed at the same bare repos on disk. It never writes git.
-
-## Decisions (locked)
-
-- **Stack:** pure Go (1.26). `go-git/v5` reads bare repos directly off disk — no shelling to
- `git`. `templ` for typed templates, `chroma` for syntax highlight, `goldmark` for markdown,
- `gopkg.in/yaml.v3` for backlog frontmatter. Router = stdlib `net/http` (Go 1.22+
- method+pattern mux) — no web framework.
-- **Render:** dynamic server. Each request reads the bare repo live → template → HTML. Always
- fresh; leaves room for private-repo auth later. **Caddy** in front for auto-TLS.
-- **Hosting:** all-on-droplet. `Caddy → custard` (reads `./repos/*.git`); Caddy reverse-proxies
- `/git/<repo>.git/*` → Soft Serve HTTP (`:23232`) for clone, and serves `/dl/*` tarballs.
-- **Tokens are plain CSS custom properties** → port `tokens.css` + fonts straight in; no
- Tailwind/Next needed. Theme via `data-theme` on `<html>` + cookie (app-kit pattern), default
- `flexoki`.
-- **jj:** Soft Serve is git-only; jj repos colocate a `.git`, so go-git reads them unchanged.
- Optionally show a "jj" badge when `.jj/` is present. No special handling required.
-
-## Layout
-
-```
-custard/
-├─ cmd/custard/main.go flags/env, http.Server, graceful shutdown
-├─ internal/
-│ ├─ config/ REPOS_PATH, SOFT_SERVE_HTTP, LISTEN_ADDR, BASE_URL
-│ ├─ gitread/ go-git wrappers: list, refs, tree, blob, log, commit, archive
-│ ├─ backlog/ parse backlog/tasks/*.md → Task{frontmatter + md body}
-│ ├─ render/ chroma highlight, goldmark md, byte/lang detection
-│ └─ server/ handlers, routes, middleware, error pages
-├─ web/
-│ ├─ templates/*.templ layout, list, repo, tree, blob, log, commit, refs, issues, issue
-│ └─ static/
-│ ├─ tokens.css ported from _shared-app-kit (Next bits stripped)
-│ ├─ custard.css app chrome — terminal/Charm look on the token system
-│ ├─ theme.js data-theme cookie toggle (7 modes)
-│ └─ fonts/ Awke, Untitled Sans, Name Mono (.woff2)
-├─ Caddyfile
-├─ deploy/ systemd unit + release.sh
-├─ go.mod PLAN.md CLAUDE.md
-```
-
-## URL scheme (cgit-inspired)
-
-| Route | View |
-|---|---|
-| `/` | repo list (public, non-hidden) — name, description, last commit |
-| `/r/<repo>` | repo home: README render + branch/tag/commit summary |
-| `/r/<repo>/tree/<ref>/<path>` | directory browse |
-| `/r/<repo>/blob/<ref>/<path>` | file view — chroma highlight, or goldmark for `.md` |
-| `/r/<repo>/raw/<ref>/<path>` | raw bytes |
-| `/r/<repo>/log/<ref>` | commit log (paged) |
-| `/r/<repo>/commit/<sha>` | commit diff |
-| `/r/<repo>/refs` | branches + tags |
-| `/r/<repo>/issues` | **backlog tasks, GitHub-issues style** — filter by status/label |
-| `/r/<repo>/issues/<id>` | single task: rendered body + acceptance criteria |
-| `/dl/<repo>/<file>.tar.gz` | release tarball *(phase 4)* |
-| `/git/<repo>.git/*` | clone — Caddy reverse-proxy → Soft Serve HTTP *(deploy)* |
-
-## Backlog → issues view (the GitHub-issues surface)
-
-Issues already travel in-repo at `backlog/tasks/*.md` (YAML frontmatter + Markdown body), so
-**no separate data source** — go-git reads them from the chosen ref like any other file.
-
-- `internal/backlog` reads `backlog/tasks/`, splits frontmatter (`id, title, status, labels,
- assignee, dependencies, created_date, updated_date, ordinal`) from the md body, parses with
- `yaml.v3`, renders body via goldmark.
-- Issues list: group/filter by `status` (To Do / In Progress / Done), color chips from
- `labels` (feature/bug/…) mapped onto theme color tokens. Sort by `ordinal`.
-- Issue detail: title, status badge, labels, dates, dependencies, then rendered body
- (description + acceptance criteria checklist).
-- Read-only (a public forge view). No create/edit — that stays in the `backlog` CLI + git push.
-- Repos with no `backlog/tasks/` dir simply hide the Issues tab.
-- **Nice loop:** custard's own backlog renders inside custard once deployed.
-
-## Phases
-
-1. **Read layer + core views** — go-git wrappers + handlers for list, repo, tree, blob, log,
- commit, refs. Unstyled HTML. Point at a local clone of `./repos/*.git` for dev.
-2. **Issues view** — `internal/backlog` parser + `/issues` list and detail.
-3. **Styling pass** — port `tokens.css` + fonts, build `custard.css` (terminal/Charm chrome),
- theme switcher (7 modes, cookie, no-flash SSR), polish all views.
-4. **Homebrew tap + release pipeline** — `git archive` tag → `name-X.Y.Z.tar.gz` in `/dl` +
- sha256; `homebrew-tap` repo in Soft Serve; GoReleaser emits formula, ~10-line script bumps
- `url`/`sha256`/`version` and pushes the tap. Source-build formula (no GitHub, no bottles).
-5. **Deploy** — Caddyfile (auto-TLS), systemd unit, reverse-proxy `/git/*` → Soft Serve
- `:23232`, serve `/dl`. Run on droplet next to Soft Serve's data dir.
-
-## Dev commands (target)
-
-```bash
-go install github.com/a-h/templ/cmd/templ@latest # one-time: templ generator
-templ generate # .templ → _templ.go
-go run ./cmd/custard --repos ./repos --addr :8080 # local dev
-go build -o custard ./cmd/custard # release binary
-```
-
-## Open / later
-
-- Private repos: dynamic server can gate later (read Soft Serve auth model); public-only first.
-- Search across blobs/commits — defer.
-- Atom feeds per repo (cgit has them) — cheap add later.
-- Self-hosting = I own uptime/TLS; keep `/dl` + Caddy reliable so `brew install` never breaks.
README.md +0 −30
@@ -1,30 +0,0 @@
-# custard
-
-A custom, public-facing **web code forge** over a self-hosted
-[Soft Serve](https://github.com/charmbracelet/soft-serve) git server. custard reads the
-same bare repos Soft Serve hosts — directly off disk via go-git — and renders its own
-templates. It never writes git; pushing and admin stay in Soft Serve over SSH.
-
-The look is the [Bubble Tea / Charm](https://charm.sh) terminal aesthetic layered on the
-_shared-app-kit token system (Flexoki / Uchu / Humdrum themes, Awke / Untitled Sans /
-Name Mono type).
-
-## Features
-
-- Repo list, tree, blob (syntax-highlighted), raw, commit log, commit diffs, branches & tags
-- **Issues** — renders each repo's in-repo `backlog/tasks/*.md` ([Backlog.md](https://backlog.md))
- GitHub-style, grouped by the repo's own configured statuses; first label = type badge
-- 7 themes via a cookie-backed switcher (flexoki / uchu / humdrum, light + dark, plus e-ink),
- flash-free server-side render
-- Syntax + diff highlighting (chroma), Markdown (goldmark), all colored through theme tokens
-
-## Develop
-
-```bash
-go install github.com/a-h/templ/cmd/templ@latest # one-time
-templ generate # after editing *.templ
-go run ./cmd/custard --repos ./repos --addr :8080
-go test ./...
-```
-
-See `PLAN.md` for the phased build plan.
backlog/config.yml +0 −17
@@ -1,17 +0,0 @@
-project_name: "custard"
-default_status: "🟦 Backlog"
-statuses: ["🟦 Backlog", "🟢 In progress", "🚧 Paused", "🏁 Done"]
-labels: []
-date_format: yyyy-mm-dd
-max_column_width: 20
-default_editor: "micro"
-auto_open_browser: true
-default_port: 6420
-remote_operations: false
-auto_commit: false
-filesystem_only: false
-zero_padded_ids: 3
-bypass_git_hooks: false
-check_active_branches: true
-active_branch_days: 30
-task_prefix: "TASK"
- → Phase-1-go-git-read-layer-core-repo-views.md +0 −27
@@ -1,29 +0,0 @@
----
-id: TASK-001
-title: 'Phase 1: go-git read layer + core repo views'
-status: "\U0001F3C1 Done"
-assignee: []
-created_date: '2026-06-17 23:44'
-updated_date: '2026-06-18 00:24'
-labels:
- - feature
-dependencies: []
-priority: high
-ordinal: 1000
----
-
-## Description
-
-<!-- SECTION:DESCRIPTION:BEGIN -->
-Build internal/gitread (the only package touching go-git) plus internal/server handlers and templ templates for the core read views. Dynamic server: each request reads bare repos live off REPOS_PATH. Point at a local ./repos/*.git for dev. Unstyled HTML is fine here; styling is phase 3.
-<!-- SECTION:DESCRIPTION:END -->
-
-## Acceptance Criteria
-<!-- AC:BEGIN -->
-- [x] #1 go.mod initialized, deps added (go-git/v5, templ, chroma, goldmark, yaml.v3)
-- [x] #2 cmd/custard/main.go: flags/env (--repos, --addr), http.Server with graceful shutdown
-- [x] #3 internal/config resolves REPOS_PATH, SOFT_SERVE_HTTP, LISTEN_ADDR, BASE_URL
-- [x] #4 internal/gitread wraps go-git: list repos, refs, tree-at-ref, blob bytes, commit, log page
-- [x] #5 Routes + templ views: / (repo list), /r/<repo>, tree, blob, raw, log, commit, refs
-- [x] #6 stdlib net/http method+pattern mux; handlers never open repos directly
-<!-- AC:END -->
- → Phase-2-backlog-issues-view-GitHub-style.md +0 −26
@@ -1,28 +0,0 @@
----
-id: TASK-002
-title: 'Phase 2: backlog issues view (GitHub-style)'
-status: "\U0001F3C1 Done"
-assignee: []
-created_date: '2026-06-17 23:44'
-updated_date: '2026-06-18 00:24'
-labels:
- - feature
-dependencies: []
-priority: high
-ordinal: 2000
----
-
-## Description
-
-<!-- SECTION:DESCRIPTION:BEGIN -->
-Render the repo's own backlog/tasks/*.md as a read-only GitHub-issues-style surface. go-git reads the files in-repo at the chosen ref; no separate store. Editing stays in backlog CLI + push.
-<!-- SECTION:DESCRIPTION:END -->
-
-## Acceptance Criteria
-<!-- AC:BEGIN -->
-- [x] #1 internal/backlog reads backlog/tasks/ via gitread, splits YAML frontmatter from md body (yaml.v3)
-- [x] #2 /r/<repo>/issues: list grouped/filtered by status, label chips mapped to theme colors, sorted by ordinal
-- [x] #3 /r/<repo>/issues/<id>: title, status badge, labels, dates, dependencies, goldmark-rendered body + AC
-- [x] #4 Repos with no backlog/tasks/ dir hide the Issues tab
-- [x] #5 Read-only: no create/edit in the web UI
-<!-- AC:END -->
- → Phase-3-styling-pass-Charm-vibe-app-kit-tokens.md +0 −31
@@ -1,33 +0,0 @@
----
-id: TASK-003
-title: 'Phase 3: styling pass (Charm vibe + app-kit tokens)'
-status: "\U0001F3C1 Done"
-assignee: []
-created_date: '2026-06-17 23:44'
-updated_date: '2026-06-18 01:30'
-labels:
- - feature
-dependencies: []
-priority: medium
-ordinal: 3000
----
-
-## Description
-
-<!-- SECTION:DESCRIPTION:BEGIN -->
-Port the app-kit token system and fonts, build custard.css with the Bubble Tea/Charm terminal look, and add the theme switcher. Apply across all views from phases 1-2.
-<!-- SECTION:DESCRIPTION:END -->
-
-## Acceptance Criteria
-<!-- AC:BEGIN -->
-- [x] #1 web/static/tokens.css ported from _shared-app-kit (Tailwind/Next wiring stripped)
-- [x] #2 Fonts in web/static/fonts: Awke, Untitled Sans, Name Mono (.woff2)
-- [x] #3 custard.css: terminal/Charm chrome built entirely on tokens; no hard-coded colors
-- [x] #4 Theme switcher: 7 data-theme modes, cookie persistence, server reads cookie for no-flash SSR; default flexoki
-- [x] #5 All views (list/repo/tree/blob/log/commit/refs/issues) styled and legible incl eink mode
-- [x] #6 Commit diffs syntax-highlighted (chroma diff lexer); per-file split + add/del line coloring
-- [x] #7 Issues + issue detail fully themed: status columns, type badge, label chips on tokens
-- [x] #8 Markdown (READMEs, issue bodies) styled via tokens; evaluate Glamour/Glow CSS as reference
-- [x] #9 Chroma highlight theme wired to data-theme (light/dark/eink) instead of fixed github style
-- [x] #10 Full token-level code highlighting styled via tokens: keyword/name/variable/function/string/number/comment/operator/builtin/punctuation mapped to color families (covers shell commands+vars), light/dark/eink
-<!-- AC:END -->
- → Phase-4-Homebrew-tap-release-pipeline.md +0 −25
@@ -1,27 +0,0 @@
----
-id: TASK-004
-title: 'Phase 4: Homebrew tap + release pipeline'
-status: "\U0001F7E6 Backlog"
-assignee: []
-created_date: '2026-06-17 23:44'
-updated_date: '2026-06-18 00:24'
-labels:
- - feature
-dependencies: []
-priority: low
-ordinal: 4000
----
-
-## Description
-
-<!-- SECTION:DESCRIPTION:BEGIN -->
-Self-hosted Homebrew distribution with no GitHub. git archive a tag to a source tarball + sha256 served at /dl, push a generated formula to a homebrew-tap repo in Soft Serve.
-<!-- SECTION:DESCRIPTION:END -->
-
-## Acceptance Criteria
-<!-- AC:BEGIN -->
-- [ ] #1 Release script: git archive <tag> -> name-X.Y.Z.tar.gz in /dl, compute sha256
-- [ ] #2 homebrew-tap repo in Soft Serve; source-build formula (depends_on go => build, no bottles)
-- [ ] #3 GoReleaser emits formula; script bumps url/sha256/version and pushes the tap repo
-- [ ] #4 brew tap <you>/tap https://<you>/git/homebrew-tap.git then brew install works end to end
-<!-- AC:END -->
- → Phase-5-deploy-on-droplet-Caddy-systemd.md +0 −24
@@ -1,26 +0,0 @@
----
-id: TASK-005
-title: 'Phase 5: deploy on droplet (Caddy + systemd)'
-status: "\U0001F7E6 Backlog"
-assignee: []
-created_date: '2026-06-17 23:44'
-updated_date: '2026-06-18 00:24'
-labels:
- - feature
-dependencies: []
-priority: low
-ordinal: 5000
----
-
-## Description
-
-<!-- SECTION:DESCRIPTION:BEGIN -->
-Run custard on the droplet next to Soft Serve's data dir. Caddy fronts it for auto-TLS and proxies clone + tarball paths.
-<!-- SECTION:DESCRIPTION:END -->
-
-## Acceptance Criteria
-<!-- AC:BEGIN -->
-- [ ] #1 Caddyfile: auto-TLS; reverse-proxy /git/<repo>.git/* -> Soft Serve HTTP :23232; serve /dl/*
-- [ ] #2 systemd unit for custard with REPOS_PATH pointed at Soft Serve repos dir
-- [ ] #3 Public read of all non-hidden repos verified live; git clone via /git/* works
-<!-- AC:END -->
cmd/custard/main.go +0 −49
@@ -1,49 +0,0 @@
-// Command custard is a read-only web code forge over bare git repositories.
-package main
-
-import (
- "context"
- "errors"
- "log"
- "net/http"
- "os"
- "os/signal"
- "syscall"
- "time"
-
- "git.kortum.world/custard/internal/config"
- "git.kortum.world/custard/internal/server"
-)
-
-func main() {
- cfg := config.Load()
- srv, err := server.New(cfg)
- if err != nil {
- log.Fatalf("init: %v", err)
- }
-
- httpSrv := &http.Server{
- Addr: cfg.ListenAddr,
- Handler: srv.Handler(),
- ReadHeaderTimeout: 10 * time.Second,
- }
-
- // Serve until an interrupt, then drain in flight requests.
- go func() {
- log.Printf("custard listening on %s (repos: %s)", cfg.ListenAddr, cfg.ReposPath)
- if err := httpSrv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
- log.Fatalf("server error: %v", err)
- }
- }()
-
- stop := make(chan os.Signal, 1)
- signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
- <-stop
-
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
- if err := httpSrv.Shutdown(ctx); err != nil {
- log.Printf("shutdown error: %v", err)
- }
- log.Println("custard stopped")
-}
deploy/Caddyfile +0 −20
@@ -1,20 +0,0 @@
-# custard — Caddy reverse proxy with automatic TLS.
-# Web UI on 443; /git/* proxied to Soft Serve's HTTP backend for read-only clone.
-git.kortum.world {
- encode gzip zstd
-
- # Read-only HTTPS git clone → Soft Serve HTTP (handle_path strips /git):
- # git clone https://git.kortum.world/git/<repo>.git
- handle_path /git/* {
- reverse_proxy 127.0.0.1:23232
- }
-
- # Release tarballs (populated by the phase-4 Homebrew pipeline).
- handle_path /dl/* {
- root * /var/lib/custard/dl
- file_server browse
- }
-
- # Everything else → the custard web app.
- reverse_proxy 127.0.0.1:8080
-}
deploy/custard.service +0 −28
@@ -1,28 +0,0 @@
-[Unit]
-Description=custard — web code forge over Soft Serve
-After=network-online.target soft-serve.service
-Wants=network-online.target
-
-[Service]
-# Runs as the soft-serve user so it can read the bare repos (read-only).
-User=soft-serve
-Group=soft-serve
-ExecStart=/usr/local/bin/custard \
- --repos /var/lib/soft-serve/repos \
- --addr 127.0.0.1:8080 \
- --base-url https://git.kortum.world \
- --soft-serve-http https://git.kortum.world/git \
- --soft-serve-db /var/lib/soft-serve/soft-serve.db
-Restart=on-failure
-RestartSec=2
-
-# Hardening. ProtectSystem=full (not strict) keeps /var writable so SQLite can
-# manage the db's -wal/-shm sidecars; the repos tree stays read-only.
-NoNewPrivileges=true
-ProtectSystem=full
-ProtectHome=true
-PrivateTmp=true
-ReadOnlyPaths=/var/lib/soft-serve/repos
-
-[Install]
-WantedBy=multi-user.target
deploy/deploy.sh +0 −54
@@ -1,54 +0,0 @@
-#!/usr/bin/env bash
-#
-# deploy.sh — build custard for linux/amd64 and deploy it to the droplet behind
-# Caddy + systemd, next to Soft Serve. Idempotent; safe to re-run for updates.
-#
-# Usage: deploy/deploy.sh <user@host> (or set CUSTARD_DEPLOY_REMOTE)
-#
-set -euo pipefail
-
-REMOTE="${1:-${CUSTARD_DEPLOY_REMOTE:-}}"
-if [ -z "$REMOTE" ]; then
- echo "usage: deploy/deploy.sh <user@host> (or set CUSTARD_DEPLOY_REMOTE)" >&2
- exit 1
-fi
-REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
-cd "$REPO_ROOT"
-
-echo "==> generating templates + building linux/amd64 binary"
-templ generate
-GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /tmp/custard-linux ./cmd/custard
-
-echo "==> uploading binary + unit + Caddyfile to $REMOTE"
-scp /tmp/custard-linux "$REMOTE:/usr/local/bin/custard.new"
-scp deploy/custard.service "$REMOTE:/etc/systemd/system/custard.service"
-scp deploy/Caddyfile "$REMOTE:/etc/caddy/Caddyfile.custard"
-
-echo "==> installing/configuring on remote"
-ssh "$REMOTE" 'bash -seu' <<'REMOTE_EOF'
-# Install Caddy if missing (official apt repo).
-if ! command -v caddy >/dev/null 2>&1; then
- apt-get install -y debian-keyring debian-archive-keyring apt-transport-https curl
- curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
- curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' > /etc/apt/sources.list.d/caddy-stable.list
- apt-get update && apt-get install -y caddy
-fi
-
-# Use our Caddyfile.
-mv /etc/caddy/Caddyfile.custard /etc/caddy/Caddyfile
-
-# Swap binary atomically + restart.
-mv /usr/local/bin/custard.new /usr/local/bin/custard
-chmod +x /usr/local/bin/custard
-
-systemctl daemon-reload
-systemctl enable --now custard
-systemctl restart custard
-systemctl reload caddy || systemctl restart caddy
-
-sleep 1
-echo "--- custard ---"; systemctl --no-pager --lines=5 status custard | tail -6
-echo "--- local probe ---"; curl -s -o /dev/null -w 'custard 127.0.0.1:8080 -> %{http_code}\n' http://127.0.0.1:8080/ || true
-REMOTE_EOF
-
-echo "==> done. https://git.kortum.world"
go.mod +0 −45
@@ -1,45 +0,0 @@
-module git.kortum.world/custard
-
-go 1.26.4
-
-require (
- github.com/a-h/templ v0.3.1020
- github.com/alecthomas/chroma/v2 v2.27.0
- github.com/go-git/go-git/v5 v5.19.1
- github.com/yuin/goldmark v1.8.2
- github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
- gopkg.in/yaml.v3 v3.0.1
- modernc.org/sqlite v1.52.0
-)
-
-require (
- dario.cat/mergo v1.0.0 // indirect
- github.com/Microsoft/go-winio v0.6.2 // indirect
- github.com/ProtonMail/go-crypto v1.1.6 // indirect
- github.com/cloudflare/circl v1.6.3 // indirect
- github.com/cyphar/filepath-securejoin v0.6.1 // indirect
- github.com/dlclark/regexp2/v2 v2.2.1 // indirect
- github.com/dustin/go-humanize v1.0.1 // indirect
- github.com/emirpasic/gods v1.18.1 // indirect
- github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
- github.com/go-git/go-billy/v5 v5.9.0 // indirect
- github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
- github.com/google/uuid v1.6.0 // indirect
- github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
- github.com/kevinburke/ssh_config v1.2.0 // indirect
- github.com/klauspost/cpuid/v2 v2.3.0 // indirect
- github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/ncruces/go-strftime v1.0.0 // indirect
- github.com/pjbgf/sha1cd v0.6.0 // indirect
- github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
- github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
- github.com/skeema/knownhosts v1.3.1 // indirect
- github.com/xanzy/ssh-agent v0.3.3 // indirect
- golang.org/x/crypto v0.50.0 // indirect
- golang.org/x/net v0.53.0 // indirect
- golang.org/x/sys v0.43.0 // indirect
- gopkg.in/warnings.v0 v0.1.2 // indirect
- modernc.org/libc v1.72.3 // indirect
- modernc.org/mathutil v1.7.1 // indirect
- modernc.org/memory v1.11.0 // indirect
-)
go.sum +0 −176
@@ -1,176 +0,0 @@
-dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
-dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
-github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
-github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
-github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
-github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
-github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
-github.com/a-h/templ v0.3.1020 h1:ypAT/L5ySWEnZ6Zft/5yfoWXYYkhFNvEFOeeqecg4tw=
-github.com/a-h/templ v0.3.1020/go.mod h1:A2DlK61v+K+NRoGnhmYbNYVmtYHcFO5/AisMvBdDxTM=
-github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
-github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
-github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
-github.com/alecthomas/chroma/v2 v2.27.0 h1:FodwmyOBgJULFYmDqibcp9pvfDLWdtPRh9v/r5BXYZs=
-github.com/alecthomas/chroma/v2 v2.27.0/go.mod h1:NjJ3ciIgrqBNeIkWZ4e46nseoLDslxU1LmfCoL+wcY8=
-github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
-github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
-github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
-github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
-github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
-github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
-github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
-github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
-github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
-github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
-github.com/dlclark/regexp2/v2 v2.2.1 h1:mf4KkFUj0gJuarK8P+LgiS+Lit7m9N1yAwEfPbee7R0=
-github.com/dlclark/regexp2/v2 v2.2.1/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU=
-github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
-github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
-github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
-github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
-github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
-github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
-github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
-github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
-github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
-github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
-github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA=
-github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw=
-github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
-github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
-github.com/go-git/go-git/v5 v5.19.1 h1:nX27AnaU43/K5bKktKwgBmR9lawoYVe1Ckg0rgzzN00=
-github.com/go-git/go-git/v5 v5.19.1/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ=
-github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
-github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
-github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
-github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
-github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
-github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
-github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
-github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
-github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
-github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
-github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
-github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
-github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
-github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
-github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
-github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
-github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
-github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
-github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
-github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
-github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
-github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
-github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
-github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
-github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
-github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
-github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
-github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
-github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
-github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
-github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
-github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
-github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
-github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
-github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE=
-github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
-github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
-github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
-golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
-golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
-golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
-golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80=
-golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
-golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
-golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
-golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
-golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
-golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
-golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
-golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
-golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
-golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
-gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-modernc.org/cc/v4 v4.28.2 h1:3tQ0lf2ADtoby2EtSP+J7IE2SHwEJdP8ioR59wx7XpY=
-modernc.org/cc/v4 v4.28.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
-modernc.org/ccgo/v4 v4.34.0 h1:yRLPFZieg532OT4rp4JFNIVcquwalMX26G95WQDqwCQ=
-modernc.org/ccgo/v4 v4.34.0/go.mod h1:AS5WYMyBakQ+fhsHhtP8mWB82KTGPkNNJDGfGQCe0/A=
-modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
-modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
-modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
-modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
-modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=
-modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
-modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
-modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
-modernc.org/libc v1.72.3 h1:ZnDF4tXn4NBXFutMMQC4vtbTFSXhhKzR73fv0beZEAU=
-modernc.org/libc v1.72.3/go.mod h1:dn0dZNnnn1clLyvRxLxYExxiKRZIRENOfqQ8XEeg4Qs=
-modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
-modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
-modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
-modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
-modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
-modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
-modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
-modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
-modernc.org/sqlite v1.52.0 h1:p4dhYh2tXZCiyaqHwRVJDjIGKWyXayiQpThxgDzJaxo=
-modernc.org/sqlite v1.52.0/go.mod h1:tcNzv5p84E0skkmJn038y+hWJbLQXQqEnQfeh5r2JLM=
-modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
-modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
-modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
-modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
internal/backlog/backlog.go +0 −196
@@ -1,196 +0,0 @@
-// Package backlog reads a repository's in-repo Backlog.md tasks
-// (backlog/tasks/*.md) into Task values for the GitHub-issues-style views. The
-// task files are the only data source — there is no separate issue store, and
-// these views are read-only (create/edit happens through the backlog CLI).
-package backlog
-
-import (
- "sort"
- "strings"
-
- "git.kortum.world/custard/internal/gitread"
- "gopkg.in/yaml.v3"
-)
-
-// Dir is the in-repo path Backlog.md stores tasks under.
-const Dir = "backlog/tasks"
-
-// configPath is the in-repo Backlog.md config file.
-const configPath = "backlog/config.yml"
-
-// DefaultStatuses is the fallback status order when a repo has no config.yml.
-var DefaultStatuses = []string{"To Do", "In Progress", "Done"}
-
-// Config is the subset of backlog/config.yml that custard cares about: the
-// status set (which drives issue column order) and the default status.
-type Config struct {
- Statuses []string `yaml:"statuses"`
- DefaultStatus string `yaml:"default_status"`
-}
-
-// ReadConfig parses the repo's backlog/config.yml at ref. It always returns a
-// usable Config: missing or unparseable config falls back to DefaultStatuses.
-func ReadConfig(s *gitread.Store, repo, ref string) Config {
- cfg := Config{Statuses: DefaultStatuses, DefaultStatus: DefaultStatuses[0]}
- data, err := s.Blob(repo, ref, configPath)
- if err != nil {
- return cfg
- }
- var parsed Config
- if err := yaml.Unmarshal(data, &parsed); err != nil {
- return cfg
- }
- if len(parsed.Statuses) > 0 {
- cfg.Statuses = parsed.Statuses
- }
- if parsed.DefaultStatus != "" {
- cfg.DefaultStatus = parsed.DefaultStatus
- }
- return cfg
-}
-
-// Task is one backlog task: parsed frontmatter plus the Markdown body.
-type Task struct {
- ID string `yaml:"id"`
- Title string `yaml:"title"`
- Status string `yaml:"status"`
- Priority string `yaml:"priority"`
- Labels []string `yaml:"labels"`
- Assignee []string `yaml:"assignee"`
- Deps []string `yaml:"dependencies"`
- Ordinal int `yaml:"ordinal"`
- Created string `yaml:"created_date"`
- Updated string `yaml:"updated_date"`
-
- Body string `yaml:"-"` // Markdown after the frontmatter
- Slug string `yaml:"-"` // source filename without extension
-}
-
-// Key is the lowercased task id used in URLs (e.g. "task-001").
-func (t Task) Key() string { return strings.ToLower(t.ID) }
-
-// Types is the canonical label vocabulary for a task's primary type.
-var Types = []string{"feature", "bug", "chore", "docs", "refactor"}
-
-// Type is the task's primary type: its first label, lowercased ("" if none).
-func (t Task) Type() string {
- if len(t.Labels) == 0 {
- return ""
- }
- return strings.ToLower(t.Labels[0])
-}
-
-// OtherLabels are the labels after the primary type.
-func (t Task) OtherLabels() []string {
- if len(t.Labels) <= 1 {
- return nil
- }
- return t.Labels[1:]
-}
-
-// Has reports whether the repo carries any backlog tasks at ref.
-func Has(s *gitread.Store, repo, ref string) bool {
- entries, err := s.Tree(repo, ref, Dir)
- if err != nil {
- return false
- }
- for _, e := range entries {
- if !e.IsDir && strings.HasSuffix(e.Name, ".md") {
- return true
- }
- }
- return false
-}
-
-// List reads and parses every task at ref, sorted by ordinal then id.
-func List(s *gitread.Store, repo, ref string) ([]Task, error) {
- entries, err := s.Tree(repo, ref, Dir)
- if err != nil {
- return nil, err
- }
- var tasks []Task
- for _, e := range entries {
- if e.IsDir || !strings.HasSuffix(e.Name, ".md") {
- continue
- }
- data, err := s.Blob(repo, ref, e.Path)
- if err != nil {
- continue
- }
- t, err := ParseTask(strings.TrimSuffix(e.Name, ".md"), data)
- if err != nil {
- continue
- }
- tasks = append(tasks, t)
- }
- sort.Slice(tasks, func(i, j int) bool {
- if tasks[i].Ordinal != tasks[j].Ordinal {
- return tasks[i].Ordinal < tasks[j].Ordinal
- }
- return tasks[i].ID < tasks[j].ID
- })
- return tasks, nil
-}
-
-// Get returns the task whose Key matches id (case-insensitive), or ok=false.
-func Get(s *gitread.Store, repo, ref, id string) (Task, bool) {
- tasks, err := List(s, repo, ref)
- if err != nil {
- return Task{}, false
- }
- id = strings.ToLower(id)
- for _, t := range tasks {
- if t.Key() == id {
- return t, true
- }
- }
- return Task{}, false
-}
-
-// ParseTask splits YAML frontmatter from the Markdown body and unmarshals it.
-// The id falls back to the slug when frontmatter omits it.
-func ParseTask(slug string, raw []byte) (Task, error) {
- fm, body := splitFrontmatter(raw)
- var t Task
- if len(fm) > 0 {
- if err := yaml.Unmarshal(fm, &t); err != nil {
- return Task{}, err
- }
- }
- t.Slug = slug
- t.Body = strings.TrimSpace(string(body))
- if t.ID == "" {
- t.ID = slug
- }
- if t.Title == "" {
- t.Title = slug
- }
- if t.Status == "" {
- t.Status = "To Do"
- }
- return t, nil
-}
-
-// splitFrontmatter separates a leading `---`-delimited YAML block from the rest.
-// If no frontmatter is present, fm is nil and body is the whole input.
-func splitFrontmatter(raw []byte) (fm, body []byte) {
- s := string(raw)
- if !strings.HasPrefix(s, "---") {
- return nil, raw
- }
- nl := strings.IndexByte(s, '\n')
- if nl < 0 {
- return nil, raw
- }
- rest := s[nl+1:]
- end := strings.Index(rest, "\n---")
- if end < 0 {
- return nil, raw
- }
- fm = []byte(rest[:end])
- after := rest[end+1:] // begins at the closing "---"
- if nl2 := strings.IndexByte(after, '\n'); nl2 >= 0 {
- body = []byte(after[nl2+1:])
- }
- return fm, body
-}
internal/backlog/backlog_test.go +0 −63
@@ -1,64 +0,0 @@
-package backlog
-
-import "testing"
-
-func TestParse(t *testing.T) {
- raw := []byte(`---
-id: TASK-007
-title: Wire the issues view
-status: In Progress
-priority: high
-labels:
- - feature
- - ui
-dependencies:
- - TASK-001
-ordinal: 7000
-created_date: '2026-06-17 16:08'
-updated_date: '2026-06-17 16:21'
----
-Render backlog tasks as issues.
-
-## Acceptance Criteria
-- [ ] list view
-- [x] detail view
-`)
- got, err := ParseTask("task-007 - wire-the-issues-view", raw)
- if err != nil {
- t.Fatalf("ParseTask: %v", err)
- }
- if got.ID != "TASK-007" {
- t.Errorf("ID = %q, want TASK-007", got.ID)
- }
- if got.Key() != "task-007" {
- t.Errorf("Key = %q, want task-007", got.Key())
- }
- if got.Status != "In Progress" {
- t.Errorf("Status = %q", got.Status)
- }
- if got.Ordinal != 7000 {
- t.Errorf("Ordinal = %d, want 7000", got.Ordinal)
- }
- if len(got.Labels) != 2 || got.Labels[0] != "feature" {
- t.Errorf("Labels = %v", got.Labels)
- }
- if len(got.Deps) != 1 || got.Deps[0] != "TASK-001" {
- t.Errorf("Deps = %v", got.Deps)
- }
- if got.Body == "" || got.Body[0] == '-' {
- t.Errorf("Body not separated from frontmatter: %q", got.Body)
- }
-}
-
-func TestParseNoFrontmatter(t *testing.T) {
- got, err := ParseTask("loose-note", []byte("just a body, no frontmatter"))
- if err != nil {
- t.Fatalf("ParseTask: %v", err)
- }
- if got.ID != "loose-note" || got.Title != "loose-note" {
- t.Errorf("fallbacks not applied: id=%q title=%q", got.ID, got.Title)
- }
- if got.Status != "To Do" {
- t.Errorf("default status = %q, want To Do", got.Status)
- }
-}
internal/config/config.go +0 −35
@@ -1,35 +0,0 @@
-// Package config resolves runtime configuration from flags and environment.
-// Flags win over env; env wins over defaults.
-package config
-
-import (
- "flag"
- "os"
-)
-
-type Config struct {
- ReposPath string // directory holding bare *.git repos
- ListenAddr string // host:port to listen on
- BaseURL string // public base URL (for absolute links, feeds)
- SoftServeHTTP string // Soft Serve HTTP clone base, reverse-proxied at /git in prod
- SoftServeDB string // path to soft-serve.db; when set, only public repos are served
-}
-
-// Load parses flags (with env fallbacks) and returns the config.
-func Load() Config {
- var c Config
- flag.StringVar(&c.ReposPath, "repos", env("REPOS_PATH", "./repos"), "directory of bare *.git repositories")
- flag.StringVar(&c.ListenAddr, "addr", env("LISTEN_ADDR", ":8080"), "listen address")
- flag.StringVar(&c.BaseURL, "base-url", env("BASE_URL", "http://localhost:8080"), "public base URL")
- flag.StringVar(&c.SoftServeHTTP, "soft-serve-http", env("SOFT_SERVE_HTTP", "http://git.kortum.world:23232"), "Soft Serve HTTP clone base")
- flag.StringVar(&c.SoftServeDB, "soft-serve-db", env("SOFT_SERVE_DB", ""), "path to soft-serve.db; when set, only public (non-private, non-hidden) repos are served")
- flag.Parse()
- return c
-}
-
-func env(key, def string) string {
- if v := os.Getenv(key); v != "" {
- return v
- }
- return def
-}
internal/gitread/gitread.go +0 −455
@@ -1,455 +0,0 @@
-// Package gitread is the only package that touches go-git. It reads bare
-// repositories off disk and returns plain domain values; HTTP handlers ask it
-// for data and never open repositories themselves.
-package gitread
-
-import (
- "database/sql"
- "errors"
- "fmt"
- "io/fs"
- "os"
- "path/filepath"
- "sort"
- "strings"
- "time"
-
- "github.com/go-git/go-git/v5"
- "github.com/go-git/go-git/v5/plumbing"
- "github.com/go-git/go-git/v5/plumbing/object"
- _ "modernc.org/sqlite"
-)
-
-// ErrNotFound is returned when a repo, ref, or path does not exist.
-var ErrNotFound = errors.New("not found")
-
-// Store reads bare repositories from a root directory. When db is non-nil
-// (Soft Serve's database), only public repos are visible — private and hidden
-// repos are treated as if they do not exist.
-type Store struct {
- root string
- db *sql.DB
-}
-
-func New(root string) *Store { return &Store{root: root} }
-
-// WithDB attaches Soft Serve's sqlite database for visibility + metadata. The
-// db is opened read-concurrent (Soft Serve owns writes); only SELECTs are run.
-// A failure to open is returned so the caller can refuse to serve rather than
-// silently exposing private repos.
-func (s *Store) WithDB(path string) error {
- db, err := sql.Open("sqlite", "file:"+path+"?_pragma=busy_timeout(5000)&_pragma=foreign_keys(0)")
- if err != nil {
- return err
- }
- if err := db.Ping(); err != nil {
- return err
- }
- s.db = db
- return nil
-}
-
-// publicMeta returns description/project for a public repo, or ok=false if the
-// repo is private, hidden, or unknown. Always ok=true when no db is attached.
-func (s *Store) publicMeta(name string) (desc, project string, ok bool) {
- if s.db == nil {
- return "", "", true
- }
- row := s.db.QueryRow(
- `SELECT coalesce(description,''), coalesce(project_name,'')
- FROM repos WHERE name = ? AND private = 0 AND hidden = 0`, name)
- if err := row.Scan(&desc, &project); err != nil {
- return "", "", false
- }
- return desc, project, true
-}
-
-// visible reports whether a repo may be served (public, or no db gating).
-func (s *Store) visible(name string) bool {
- _, _, ok := s.publicMeta(name)
- return ok
-}
-
-// Repo is a single repository summary for the list view.
-type Repo struct {
- Name string
- Description string
- Last *Commit
-}
-
-// Commit is commit metadata. Subject is the first line of Message.
-type Commit struct {
- Hash string
- Short string
- Author string
- Email string
- When time.Time
- Message string
- Subject string
-}
-
-// Ref is a named branch or tag.
-type Ref struct {
- Name string
- Hash string
-}
-
-// Refs groups a repository's branches and tags.
-type Refs struct {
- Branches []Ref
- Tags []Ref
-}
-
-// Entry is a single tree entry (file or directory).
-type Entry struct {
- Name string
- Path string
- IsDir bool
- Mode string
- Size int64
-}
-
-// CommitDetail is a commit plus its diff against its first parent.
-type CommitDetail struct {
- Commit Commit
- Parents []string
- Diff string
-}
-
-// List returns the servable repos, sorted by name. With a db attached it lists
-// only public repos (using db descriptions); otherwise every bare repo on disk.
-func (s *Store) List() ([]Repo, error) {
- if s.db != nil {
- return s.listFromDB()
- }
- entries, err := os.ReadDir(s.root)
- if err != nil {
- return nil, err
- }
- var repos []Repo
- for _, e := range entries {
- if !e.IsDir() || !strings.HasSuffix(e.Name(), ".git") {
- continue
- }
- name := strings.TrimSuffix(e.Name(), ".git")
- repos = append(repos, s.summary(name, s.description(e.Name())))
- }
- sort.Slice(repos, func(i, j int) bool { return repos[i].Name < repos[j].Name })
- return repos, nil
-}
-
-// listFromDB lists public repos recorded in Soft Serve's db that also exist on
-// disk, preferring the db description (falling back to the git description file).
-func (s *Store) listFromDB() ([]Repo, error) {
- rows, err := s.db.Query(
- `SELECT name, coalesce(description,'') FROM repos
- WHERE private = 0 AND hidden = 0 ORDER BY name`)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
- var repos []Repo
- for rows.Next() {
- var name, desc string
- if err := rows.Scan(&name, &desc); err != nil {
- return nil, err
- }
- if info, err := os.Stat(s.dir(name)); err != nil || !info.IsDir() {
- continue // recorded but no bare repo on disk
- }
- if desc == "" {
- desc = s.description(name + ".git")
- }
- repos = append(repos, s.summary(name, desc))
- }
- return repos, rows.Err()
-}
-
-// summary builds a list entry with the repo's HEAD commit.
-func (s *Store) summary(name, desc string) Repo {
- r := Repo{Name: name, Description: desc}
- if repo, err := s.open(name); err == nil {
- if c, err := headCommit(repo); err == nil {
- r.Last = c
- }
- }
- return r
-}
-
-// Exists reports whether a repo is present and visible (public, when db-gated).
-func (s *Store) Exists(name string) bool {
- if !s.visible(name) {
- return false
- }
- info, err := os.Stat(s.dir(name))
- return err == nil && info.IsDir()
-}
-
-// Refs returns the branches and tags of a repo.
-func (s *Store) Refs(name string) (*Refs, error) {
- repo, err := s.open(name)
- if err != nil {
- return nil, err
- }
- out := &Refs{}
- bs, err := repo.Branches()
- if err == nil {
- _ = bs.ForEach(func(r *plumbing.Reference) error {
- out.Branches = append(out.Branches, Ref{Name: r.Name().Short(), Hash: r.Hash().String()})
- return nil
- })
- }
- ts, err := repo.Tags()
- if err == nil {
- _ = ts.ForEach(func(r *plumbing.Reference) error {
- out.Tags = append(out.Tags, Ref{Name: r.Name().Short(), Hash: r.Hash().String()})
- return nil
- })
- }
- sort.Slice(out.Branches, func(i, j int) bool { return out.Branches[i].Name < out.Branches[j].Name })
- sort.Slice(out.Tags, func(i, j int) bool { return out.Tags[i].Name < out.Tags[j].Name })
- return out, nil
-}
-
-// DefaultBranch returns the short name of HEAD's target branch.
-func (s *Store) DefaultBranch(name string) (string, error) {
- repo, err := s.open(name)
- if err != nil {
- return "", err
- }
- head, err := repo.Head()
- if err != nil {
- return "", ErrNotFound
- }
- return head.Name().Short(), nil
-}
-
-// Tree lists the entries at path under ref. An empty path is the root.
-func (s *Store) Tree(name, ref, path string) ([]Entry, error) {
- repo, err := s.open(name)
- if err != nil {
- return nil, err
- }
- tree, err := s.treeAt(repo, ref)
- if err != nil {
- return nil, err
- }
- path = strings.Trim(path, "/")
- if path != "" {
- tree, err = tree.Tree(path)
- if err != nil {
- return nil, ErrNotFound
- }
- }
- var out []Entry
- for _, e := range tree.Entries {
- isDir := e.Mode == 0o040000
- entry := Entry{
- Name: e.Name,
- Path: strings.TrimPrefix(path+"/"+e.Name, "/"),
- IsDir: isDir,
- Mode: e.Mode.String(),
- }
- if !isDir {
- if blob, err := repo.BlobObject(e.Hash); err == nil {
- entry.Size = blob.Size
- }
- }
- out = append(out, entry)
- }
- sort.Slice(out, func(i, j int) bool {
- if out[i].IsDir != out[j].IsDir {
- return out[i].IsDir // directories first
- }
- return out[i].Name < out[j].Name
- })
- return out, nil
-}
-
-// Blob returns the raw bytes of the file at path under ref.
-func (s *Store) Blob(name, ref, path string) ([]byte, error) {
- repo, err := s.open(name)
- if err != nil {
- return nil, err
- }
- tree, err := s.treeAt(repo, ref)
- if err != nil {
- return nil, err
- }
- f, err := tree.File(strings.Trim(path, "/"))
- if err != nil {
- return nil, ErrNotFound
- }
- r, err := f.Blob.Reader()
- if err != nil {
- return nil, err
- }
- defer r.Close()
- buf := make([]byte, f.Size)
- if _, err := readFull(r, buf); err != nil {
- return nil, err
- }
- return buf, nil
-}
-
-// Log returns up to limit commits reachable from ref, newest first.
-func (s *Store) Log(name, ref string, limit int) ([]Commit, error) {
- repo, err := s.open(name)
- if err != nil {
- return nil, err
- }
- h, err := s.resolve(repo, ref)
- if err != nil {
- return nil, err
- }
- iter, err := repo.Log(&git.LogOptions{From: *h})
- if err != nil {
- return nil, err
- }
- defer iter.Close()
- var out []Commit
- for len(out) < limit {
- c, err := iter.Next()
- if err != nil {
- break
- }
- out = append(out, toCommit(c))
- }
- return out, nil
-}
-
-// Commit returns a single commit with its diff against the first parent.
-func (s *Store) Commit(name, rev string) (*CommitDetail, error) {
- repo, err := s.open(name)
- if err != nil {
- return nil, err
- }
- h, err := s.resolve(repo, rev)
- if err != nil {
- return nil, err
- }
- c, err := repo.CommitObject(*h)
- if err != nil {
- return nil, ErrNotFound
- }
- d := &CommitDetail{Commit: toCommit(c)}
- for _, p := range c.ParentHashes {
- d.Parents = append(d.Parents, p.String())
- }
- if c.NumParents() > 0 {
- parent, err := c.Parent(0)
- if err == nil {
- if patch, err := parent.Patch(c); err == nil {
- d.Diff = patch.String()
- }
- }
- } else {
- if patch, err := c.Patch(nil); err == nil {
- d.Diff = patch.String()
- }
- }
- return d, nil
-}
-
-// --- internals ---
-
-func (s *Store) dir(name string) string {
- return filepath.Join(s.root, name+".git")
-}
-
-func (s *Store) open(name string) (*git.Repository, error) {
- if !s.visible(name) {
- return nil, ErrNotFound // private/hidden/unknown — never serve
- }
- repo, err := git.PlainOpen(s.dir(name))
- if errors.Is(err, git.ErrRepositoryNotExists) || errors.Is(err, fs.ErrNotExist) {
- return nil, ErrNotFound
- }
- return repo, err
-}
-
-// description reads the git `description` file, ignoring the default placeholder.
-func (s *Store) description(dirName string) string {
- b, err := os.ReadFile(filepath.Join(s.root, dirName, "description"))
- if err != nil {
- return ""
- }
- d := strings.TrimSpace(string(b))
- if strings.HasPrefix(d, "Unnamed repository") {
- return ""
- }
- return d
-}
-
-// resolve turns a ref name, short hash, or HEAD into a commit hash.
-func (s *Store) resolve(repo *git.Repository, ref string) (*plumbing.Hash, error) {
- if ref == "" {
- ref = "HEAD"
- }
- h, err := repo.ResolveRevision(plumbing.Revision(ref))
- if err != nil {
- return nil, ErrNotFound
- }
- return h, nil
-}
-
-func (s *Store) treeAt(repo *git.Repository, ref string) (*object.Tree, error) {
- h, err := s.resolve(repo, ref)
- if err != nil {
- return nil, err
- }
- c, err := repo.CommitObject(*h)
- if err != nil {
- return nil, ErrNotFound
- }
- return c.Tree()
-}
-
-func headCommit(repo *git.Repository) (*Commit, error) {
- head, err := repo.Head()
- if err != nil {
- return nil, err
- }
- c, err := repo.CommitObject(head.Hash())
- if err != nil {
- return nil, err
- }
- out := toCommit(c)
- return &out, nil
-}
-
-func toCommit(c *object.Commit) Commit {
- msg := strings.TrimRight(c.Message, "\n")
- subject := msg
- if i := strings.IndexByte(subject, '\n'); i >= 0 {
- subject = subject[:i]
- }
- return Commit{
- Hash: c.Hash.String(),
- Short: c.Hash.String()[:8],
- Author: c.Author.Name,
- Email: c.Author.Email,
- When: c.Author.When,
- Message: msg,
- Subject: subject,
- }
-}
-
-// readFull fills buf, tolerating short reads, and erroring on a size mismatch.
-func readFull(r interface{ Read([]byte) (int, error) }, buf []byte) (int, error) {
- total := 0
- for total < len(buf) {
- n, err := r.Read(buf[total:])
- total += n
- if err != nil {
- if total == len(buf) {
- return total, nil
- }
- return total, err
- }
- }
- if total != len(buf) {
- return total, fmt.Errorf("short read: got %d want %d", total, len(buf))
- }
- return total, nil
-}
internal/render/render.go +0 −269
@@ -1,269 +0,0 @@
-// Package render turns raw repository bytes into HTML: syntax-highlighted code
-// via chroma and rendered Markdown via goldmark. It holds no git knowledge.
-package render
-
-import (
- "bytes"
- "html/template"
- "path"
- "strings"
- "unicode/utf8"
-
- "github.com/alecthomas/chroma/v2"
- "github.com/alecthomas/chroma/v2/formatters/html"
- "github.com/alecthomas/chroma/v2/lexers"
- "github.com/alecthomas/chroma/v2/styles"
- "github.com/yuin/goldmark"
- highlighting "github.com/yuin/goldmark-highlighting/v2"
- "github.com/yuin/goldmark/extension"
- "gopkg.in/yaml.v3"
-)
-
-// FMPair is one ordered key/value from a Markdown file's YAML frontmatter.
-type FMPair struct {
- Key string
- Value string
-}
-
-// SplitFrontmatter separates a leading `---`-delimited YAML block from the body.
-// No frontmatter → fm is nil and body is the whole input.
-func SplitFrontmatter(raw []byte) (fm, body []byte) {
- s := string(raw)
- if !strings.HasPrefix(s, "---") {
- return nil, raw
- }
- nl := strings.IndexByte(s, '\n')
- if nl < 0 {
- return nil, raw
- }
- rest := s[nl+1:]
- end := strings.Index(rest, "\n---")
- if end < 0 {
- return nil, raw
- }
- fm = []byte(rest[:end])
- after := rest[end+1:] // starts at closing "---"
- if nl2 := strings.IndexByte(after, '\n'); nl2 >= 0 {
- body = []byte(after[nl2+1:])
- }
- return fm, body
-}
-
-// ParseFrontmatter parses a YAML frontmatter block into ordered key/value pairs
-// for display. Sequence values are comma-joined; nested maps are flattened to a
-// compact YAML string. Returns nil on empty or invalid input.
-func ParseFrontmatter(fm []byte) []FMPair {
- if len(bytes.TrimSpace(fm)) == 0 {
- return nil
- }
- var doc yaml.Node
- if err := yaml.Unmarshal(fm, &doc); err != nil || len(doc.Content) == 0 {
- return nil
- }
- root := doc.Content[0]
- if root.Kind != yaml.MappingNode {
- return nil
- }
- var pairs []FMPair
- for i := 0; i+1 < len(root.Content); i += 2 {
- pairs = append(pairs, FMPair{
- Key: root.Content[i].Value,
- Value: nodeString(root.Content[i+1]),
- })
- }
- return pairs
-}
-
-func nodeString(n *yaml.Node) string {
- switch n.Kind {
- case yaml.ScalarNode:
- return n.Value
- case yaml.SequenceNode:
- parts := make([]string, 0, len(n.Content))
- for _, c := range n.Content {
- parts = append(parts, nodeString(c))
- }
- return strings.Join(parts, ", ")
- default:
- var b bytes.Buffer
- enc := yaml.NewEncoder(&b)
- _ = enc.Encode(n)
- _ = enc.Close()
- return strings.TrimSpace(b.String())
- }
-}
-
-// md renders GFM and highlights fenced code blocks with chroma classes, so
-// code in READMEs/issue bodies is colored by the same theme tokens as blobs.
-var md = goldmark.New(goldmark.WithExtensions(
- extension.GFM,
- highlighting.NewHighlighting(
- highlighting.WithFormatOptions(html.WithClasses(true)),
- ),
-))
-
-// chroma formatters emit classes (not inline styles) so themes drive colors.
-// formatter is for source files (with a line-number gutter); diffFormatter omits
-// line numbers, which would fight a patch's own +/- columns.
-var formatter = html.New(html.WithClasses(true), html.WithLineNumbers(true), html.LineNumbersInTable(true))
-var diffFormatter = html.New(html.WithClasses(true))
-
-// classStyle is irrelevant to class-based output but required by the API.
-var classStyle = styles.Get("github")
-
-// Markdown renders GitHub-flavored Markdown to HTML.
-func Markdown(src []byte) (template.HTML, error) {
- var buf bytes.Buffer
- if err := md.Convert(src, &buf); err != nil {
- return "", err
- }
- return template.HTML(buf.String()), nil // #nosec G203 -- trusted repo content, rendered by goldmark
-}
-
-// Highlight returns syntax-highlighted HTML for code, choosing a lexer by
-// filename then content. Binary content yields ok=false so callers can show a
-// "binary file" notice instead.
-func Highlight(filename string, code []byte) (out template.HTML, ok bool) {
- if isBinary(code) {
- return "", false
- }
- lexer := lexers.Match(filename)
- if lexer == nil {
- lexer = lexers.Analyse(string(code))
- }
- if lexer == nil {
- lexer = lexers.Fallback
- }
- lexer = chroma.Coalesce(lexer)
-
- iterator, err := lexer.Tokenise(nil, string(code))
- if err != nil {
- return "", false
- }
- var buf bytes.Buffer
- if err := formatter.Format(&buf, classStyle, iterator); err != nil {
- return "", false
- }
- return template.HTML(buf.String()), true // #nosec G203 -- escaped by chroma formatter
-}
-
-// FileDiff is one file's slice of a commit diff: its name, highlighted hunks,
-// and add/delete line counts.
-type FileDiff struct {
- Name string
- HTML template.HTML
- Added int
- Deleted int
- Binary bool
-}
-
-// SplitDiff breaks a unified diff into per-file sections. Each section's git
-// metadata (diff --git / index / ---/+++ lines) is dropped — the filename is
-// surfaced separately — and the hunks are highlighted on their own.
-func SplitDiff(diff string) []FileDiff {
- if strings.TrimSpace(diff) == "" {
- return nil
- }
- lines := strings.Split(diff, "\n")
- var files []FileDiff
- var cur []string
- flush := func() {
- if len(cur) == 0 {
- return
- }
- files = append(files, buildFileDiff(cur))
- cur = nil
- }
- for _, ln := range lines {
- if strings.HasPrefix(ln, "diff --git ") {
- flush()
- }
- cur = append(cur, ln)
- }
- flush()
- return files
-}
-
-func buildFileDiff(block []string) FileDiff {
- fd := FileDiff{Name: diffName(block)}
- hunk := -1
- for i, ln := range block {
- switch {
- case strings.HasPrefix(ln, "@@") && hunk < 0:
- hunk = i
- case strings.HasPrefix(ln, "Binary files "), strings.HasPrefix(ln, "GIT binary patch"):
- fd.Binary = true
- case strings.HasPrefix(ln, "+") && !strings.HasPrefix(ln, "+++"):
- fd.Added++
- case strings.HasPrefix(ln, "-") && !strings.HasPrefix(ln, "---"):
- fd.Deleted++
- }
- }
- if hunk >= 0 {
- fd.HTML = HighlightDiff(strings.Join(block[hunk:], "\n"))
- }
- return fd
-}
-
-// diffName extracts a display name from a file block's git header, formatting
-// renames as "old → new".
-func diffName(block []string) string {
- for _, ln := range block {
- if strings.HasPrefix(ln, "diff --git ") {
- fields := strings.Fields(ln)
- if len(fields) >= 4 {
- a := strings.TrimPrefix(fields[len(fields)-2], "a/")
- b := strings.TrimPrefix(fields[len(fields)-1], "b/")
- if a != b {
- return a + " → " + b
- }
- return b
- }
- }
- }
- for _, ln := range block {
- if strings.HasPrefix(ln, "+++ b/") {
- return strings.TrimPrefix(ln, "+++ b/")
- }
- }
- return "diff"
-}
-
-// HighlightDiff renders a unified diff with chroma's diff lexer (class-based:
-// .gi inserts, .gd deletes, .gu/.gh hunk headers). Falls back to escaped text.
-func HighlightDiff(diff string) template.HTML {
- lexer := lexers.Get("diff")
- if lexer == nil {
- return template.HTML("<pre class=\"chroma\">" + template.HTMLEscapeString(diff) + "</pre>") // #nosec G203 -- escaped
- }
- iterator, err := lexer.Tokenise(nil, diff)
- if err != nil {
- return template.HTML("<pre class=\"chroma\">" + template.HTMLEscapeString(diff) + "</pre>") // #nosec G203 -- escaped
- }
- var buf bytes.Buffer
- if err := diffFormatter.Format(&buf, classStyle, iterator); err != nil {
- return template.HTML("<pre class=\"chroma\">" + template.HTMLEscapeString(diff) + "</pre>") // #nosec G203 -- escaped
- }
- return template.HTML(buf.String()) // #nosec G203 -- escaped by chroma formatter
-}
-
-// IsMarkdown reports whether a filename should be rendered as Markdown.
-func IsMarkdown(name string) bool {
- switch strings.ToLower(path.Ext(name)) {
- case ".md", ".markdown", ".mdown":
- return true
- }
- return false
-}
-
-// isBinary uses a NUL-byte heuristic over the first chunk, like git.
-func isBinary(b []byte) bool {
- const sniff = 8000
- if len(b) > sniff {
- b = b[:sniff]
- }
- if bytes.IndexByte(b, 0) >= 0 {
- return true
- }
- return !utf8.Valid(b)
-}
internal/server/server.go +0 −371
@@ -1,371 +0,0 @@
-// Package server wires HTTP routes to gitread reads, render helpers, and templ
-// views. Handlers never open repositories directly — they go through gitread.
-package server
-
-import (
- "fmt"
- "html/template"
- "io/fs"
- "net/http"
- "strings"
-
- "git.kortum.world/custard/internal/backlog"
- "git.kortum.world/custard/internal/config"
- "git.kortum.world/custard/internal/gitread"
- "git.kortum.world/custard/internal/render"
- "git.kortum.world/custard/web"
- "git.kortum.world/custard/web/templates"
- "github.com/a-h/templ"
-)
-
-const logLimit = 100
-
-// Server holds dependencies shared across handlers.
-type Server struct {
- cfg config.Config
- store *gitread.Store
-}
-
-// New builds the server. When cfg.SoftServeDB is set it attaches Soft Serve's
-// database for repo-visibility gating; a failure to open is fatal (returned)
-// rather than silently falling back to exposing every repo.
-func New(cfg config.Config) (*Server, error) {
- store := gitread.New(cfg.ReposPath)
- if cfg.SoftServeDB != "" {
- if err := store.WithDB(cfg.SoftServeDB); err != nil {
- return nil, fmt.Errorf("open soft-serve db %q: %w", cfg.SoftServeDB, err)
- }
- }
- return &Server{cfg: cfg, store: store}, nil
-}
-
-// Handler builds the route table. Tree/blob/raw register twice so the ref-only
-// form (no path) and the path form both match the Go 1.22 pattern mux.
-func (s *Server) Handler() http.Handler {
- mux := http.NewServeMux()
- mux.HandleFunc("GET /{$}", s.handleIndex)
- mux.HandleFunc("GET /r/{repo}", s.handleRepo)
- mux.HandleFunc("GET /r/{repo}/files", s.handleFiles)
- mux.HandleFunc("GET /r/{repo}/readme", s.handleReadme)
- mux.HandleFunc("GET /r/{repo}/refs", s.handleRefs)
- mux.HandleFunc("GET /r/{repo}/log/{ref}", s.handleLog)
- mux.HandleFunc("GET /r/{repo}/commit/{sha}", s.handleCommit)
- mux.HandleFunc("GET /r/{repo}/tree/{ref}", s.handleTree)
- mux.HandleFunc("GET /r/{repo}/tree/{ref}/{path...}", s.handleTree)
- mux.HandleFunc("GET /r/{repo}/blob/{ref}/{path...}", s.handleBlob)
- mux.HandleFunc("GET /r/{repo}/raw/{ref}/{path...}", s.handleRaw)
- mux.HandleFunc("GET /r/{repo}/issues", s.handleIssues)
- mux.HandleFunc("GET /r/{repo}/issues/{id}", s.handleIssue)
-
- staticFS, _ := fs.Sub(web.Static, "static")
- mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServerFS(staticFS)))
- return mux
-}
-
-func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
- repos, err := s.store.List()
- if err != nil {
- s.fail(w, r, http.StatusInternalServerError, "could not list repositories")
- return
- }
- page := templates.IndexPage{Meta: templates.Meta{Theme: readTheme(r)}, Repos: repos}
- s.render(w, r, templates.Index(page))
-}
-
-// handleRepo is the repo landing: a repo with a README opens to it; otherwise
-// it shows the file tree.
-func (s *Server) handleRepo(w http.ResponseWriter, r *http.Request) {
- name := r.PathValue("repo")
- if !s.store.Exists(name) {
- s.notFound(w, r)
- return
- }
- branch, err := s.store.DefaultBranch(name)
- if err != nil {
- // Empty repository: show a stub page rather than failing.
- s.render(w, r, templates.Repo(templates.RepoPage{Meta: templates.Meta{Repo: name, Tab: "code", Theme: readTheme(r)}}))
- return
- }
- if md, ok := s.readme(name, branch); ok {
- page := templates.ReadmePage{Meta: s.meta(r, name, branch, "readme"), Readme: md}
- s.render(w, r, templates.Readme(page))
- return
- }
- s.renderFiles(w, r, name, branch)
-}
-
-// handleFiles is the file-tree (code) view, the destination of the code tab.
-func (s *Server) handleFiles(w http.ResponseWriter, r *http.Request) {
- name := r.PathValue("repo")
- branch, err := s.store.DefaultBranch(name)
- if err != nil {
- s.notFound(w, r)
- return
- }
- s.renderFiles(w, r, name, branch)
-}
-
-func (s *Server) renderFiles(w http.ResponseWriter, r *http.Request, name, branch string) {
- refs, _ := s.store.Refs(name)
- entries, _ := s.store.Tree(name, branch, "")
- page := templates.RepoPage{
- Meta: s.meta(r, name, branch, "code"),
- DefaultBranch: branch,
- Entries: entries,
- Last: s.lastCommit(name, branch),
- }
- if refs != nil {
- page.Branches = len(refs.Branches)
- page.Tags = len(refs.Tags)
- }
- s.render(w, r, templates.Repo(page))
-}
-
-func (s *Server) handleReadme(w http.ResponseWriter, r *http.Request) {
- name := r.PathValue("repo")
- branch, err := s.store.DefaultBranch(name)
- if err != nil {
- s.notFound(w, r)
- return
- }
- md, ok := s.readme(name, branch)
- if !ok {
- s.notFound(w, r)
- return
- }
- page := templates.ReadmePage{Meta: s.meta(r, name, branch, "readme"), Readme: md}
- s.render(w, r, templates.Readme(page))
-}
-
-func (s *Server) handleTree(w http.ResponseWriter, r *http.Request) {
- name, ref, path := r.PathValue("repo"), r.PathValue("ref"), r.PathValue("path")
- entries, err := s.store.Tree(name, ref, path)
- if err != nil {
- s.notFound(w, r)
- return
- }
- page := templates.TreePage{
- Meta: s.meta(r, name, ref, "code"),
- Path: strings.Trim(path, "/"),
- Crumbs: templates.BuildCrumbs(path),
- Entries: entries,
- }
- s.render(w, r, templates.Tree(page))
-}
-
-func (s *Server) handleBlob(w http.ResponseWriter, r *http.Request) {
- name, ref, path := r.PathValue("repo"), r.PathValue("ref"), r.PathValue("path")
- data, err := s.store.Blob(name, ref, path)
- if err != nil {
- s.notFound(w, r)
- return
- }
- page := templates.BlobPage{
- Meta: s.meta(r, name, ref, "code"),
- Path: strings.Trim(path, "/"),
- Crumbs: templates.BuildCrumbs(path),
- Size: int64(len(data)),
- }
- switch {
- case render.IsMarkdown(path):
- fm, body := render.SplitFrontmatter(data)
- md, err := render.Markdown(body)
- if err == nil {
- page.IsMarkdown = true
- page.Markdown = md
- page.Frontmatter = render.ParseFrontmatter(fm)
- break
- }
- fallthrough
- default:
- code, ok := render.Highlight(path, data)
- if ok {
- page.Code = code
- } else {
- page.IsBinary = true
- }
- }
- s.render(w, r, templates.Blob(page))
-}
-
-func (s *Server) handleRaw(w http.ResponseWriter, r *http.Request) {
- name, ref, path := r.PathValue("repo"), r.PathValue("ref"), r.PathValue("path")
- data, err := s.store.Blob(name, ref, path)
- if err != nil {
- s.notFound(w, r)
- return
- }
- w.Header().Set("Content-Type", "text/plain; charset=utf-8")
- w.Header().Set("X-Content-Type-Options", "nosniff")
- _, _ = w.Write(data)
-}
-
-func (s *Server) handleLog(w http.ResponseWriter, r *http.Request) {
- name, ref := r.PathValue("repo"), r.PathValue("ref")
- commits, err := s.store.Log(name, ref, logLimit)
- if err != nil {
- s.notFound(w, r)
- return
- }
- page := templates.LogPage{Meta: s.meta(r, name, ref, "log"), Commits: commits}
- s.render(w, r, templates.Log(page))
-}
-
-func (s *Server) handleCommit(w http.ResponseWriter, r *http.Request) {
- name, sha := r.PathValue("repo"), r.PathValue("sha")
- detail, err := s.store.Commit(name, sha)
- if err != nil {
- s.notFound(w, r)
- return
- }
- page := templates.CommitPage{
- Meta: s.meta(r, name, "", "log"),
- Detail: detail,
- Files: render.SplitDiff(detail.Diff),
- }
- s.render(w, r, templates.Commit(page))
-}
-
-func (s *Server) handleRefs(w http.ResponseWriter, r *http.Request) {
- name := r.PathValue("repo")
- refs, err := s.store.Refs(name)
- if err != nil {
- s.notFound(w, r)
- return
- }
- page := templates.RefsPage{Meta: s.meta(r, name, "", "refs"), Refs: refs}
- s.render(w, r, templates.Refs(page))
-}
-
-func (s *Server) handleIssues(w http.ResponseWriter, r *http.Request) {
- name := r.PathValue("repo")
- ref, err := s.store.DefaultBranch(name)
- if err != nil {
- s.notFound(w, r)
- return
- }
- tasks, err := backlog.List(s.store, name, ref)
- if err != nil {
- s.notFound(w, r)
- return
- }
- cfg := backlog.ReadConfig(s.store, name, ref)
- page := templates.IssuesPage{
- Meta: s.meta(r, name, ref, "issues"),
- Groups: templates.GroupByStatus(tasks, cfg.Statuses),
- Total: len(tasks),
- }
- s.render(w, r, templates.Issues(page))
-}
-
-func (s *Server) handleIssue(w http.ResponseWriter, r *http.Request) {
- name, id := r.PathValue("repo"), r.PathValue("id")
- ref, err := s.store.DefaultBranch(name)
- if err != nil {
- s.notFound(w, r)
- return
- }
- task, ok := backlog.Get(s.store, name, ref, id)
- if !ok {
- s.notFound(w, r)
- return
- }
- page := templates.IssuePage{Meta: s.meta(r, name, ref, "issues"), Task: task}
- if task.Body != "" {
- if body, err := render.Markdown([]byte(task.Body)); err == nil {
- page.Body = body
- }
- }
- s.render(w, r, templates.Issue(page))
-}
-
-// --- helpers ---
-
-func (s *Server) lastCommit(name, ref string) *gitread.Commit {
- commits, err := s.store.Log(name, ref, 1)
- if err != nil || len(commits) == 0 {
- return nil
- }
- return &commits[0]
-}
-
-// readme finds and renders a root README, trying common filenames.
-func (s *Server) readme(name, ref string) (template.HTML, bool) {
- for _, fn := range readmeFiles {
- data, err := s.store.Blob(name, ref, fn)
- if err != nil {
- continue
- }
- html, err := render.Markdown(data)
- if err != nil {
- continue
- }
- return html, true
- }
- return "", false
-}
-
-// meta builds shared page chrome: active tab, issues-tab visibility, theme,
-// and (in a repo context) the read-only HTTP clone URL for the footer.
-func (s *Server) meta(r *http.Request, name, ref, tab string) templates.Meta {
- m := templates.Meta{Repo: name, Ref: ref, Tab: tab, HasIssues: s.hasIssues(name, ref), HasReadme: s.hasReadme(name, ref), Theme: readTheme(r)}
- if name != "" && s.cfg.SoftServeHTTP != "" {
- m.CloneURL = strings.TrimRight(s.cfg.SoftServeHTTP, "/") + "/" + name + ".git"
- }
- return m
-}
-
-// readTheme resolves the data-theme from the "theme" cookie, defaulting safely.
-func readTheme(r *http.Request) string {
- if c, err := r.Cookie("theme"); err == nil {
- return templates.ValidTheme(c.Value)
- }
- return templates.DefaultTheme
-}
-
-// hasIssues reports whether the repo carries backlog tasks (drives the tab).
-// An empty ref falls back to the default branch.
-func (s *Server) hasIssues(name, ref string) bool {
- if ref == "" {
- b, err := s.store.DefaultBranch(name)
- if err != nil {
- return false
- }
- ref = b
- }
- return backlog.Has(s.store, name, ref)
-}
-
-// readmeFiles are the root README names tried, in order.
-var readmeFiles = []string{"README.md", "readme.md", "README.markdown", "README"}
-
-// hasReadme reports whether the repo has a root README (drives the tab).
-func (s *Server) hasReadme(name, ref string) bool {
- if ref == "" {
- b, err := s.store.DefaultBranch(name)
- if err != nil {
- return false
- }
- ref = b
- }
- for _, fn := range readmeFiles {
- if _, err := s.store.Blob(name, ref, fn); err == nil {
- return true
- }
- }
- return false
-}
-
-func (s *Server) render(w http.ResponseWriter, r *http.Request, c templ.Component) {
- w.Header().Set("Content-Type", "text/html; charset=utf-8")
- _ = c.Render(r.Context(), w)
-}
-
-func (s *Server) notFound(w http.ResponseWriter, r *http.Request) {
- s.fail(w, r, http.StatusNotFound, "not found")
-}
-
-func (s *Server) fail(w http.ResponseWriter, r *http.Request, code int, msg string) {
- w.WriteHeader(code)
- _ = templates.Error(templates.Meta{Theme: readTheme(r)}, code, msg).Render(r.Context(), w)
-}
scripts/migrate-backlog-statuses.sh +0 −58
@@ -1,58 +0,0 @@
-#!/usr/bin/env bash
-#
-# migrate-backlog-statuses.sh <repo-path> [--dry-run]
-#
-# Migrate a Backlog.md repo to the unified status vocabulary:
-# 🟦 Backlog · 🟢 In progress · 🚧 Paused · 🏁 Done
-#
-# Updates backlog/config.yml (statuses + default_status) and rewrites each task's
-# status via the backlog CLI using the mapping below. Idempotent: tasks already
-# on the new vocab are left alone.
-#
-# This is the pilot rollout helper — run it per repo for the later full rollout.
-# Claude project docs (Humdrum/Claude/Projects/*.md) are migrated separately:
-# - frontmatter `status:` → one of the four (judgement: 🟡→🟢, 🔴→🚧, etc.)
-# - any ```base``` filters referencing "Done" → "🏁 Done"
-#
-set -euo pipefail
-
-repo="${1:?usage: migrate-backlog-statuses.sh <repo-path> [--dry-run]}"
-dry=""; [ "${2:-}" = "--dry-run" ] && dry=1
-cd "$repo"
-[ -f backlog/config.yml ] || { echo "no backlog/config.yml in $repo — skipping"; exit 0; }
-
-NEW_STATUSES='["🟦 Backlog", "🟢 In progress", "🚧 Paused", "🏁 Done"]'
-
-map_status() {
- case "$1" in
- "To Do"|"Todo"|"Backlog") echo "🟦 Backlog" ;;
- "In Progress"|"In progress"|"Doing") echo "🟢 In progress" ;;
- "Paused"|"Blocked"|"On Hold"|"Hold") echo "🚧 Paused" ;;
- "Done"|"Shipped"|"Complete"|"Completed") echo "🏁 Done" ;;
- *) echo "" ;; # already new vocab, or unknown — leave it
- esac
-}
-
-echo "== $repo =="
-
-# config: statuses array (CLI refuses arrays, so edit the file) + default_status
-if [ -z "$dry" ]; then
- tmp=$(mktemp)
- awk -v repl="statuses: $NEW_STATUSES" '/^statuses:/{print repl; next} {print}' backlog/config.yml > "$tmp" && mv "$tmp" backlog/config.yml
- backlog config set defaultStatus "🟦 Backlog" >/dev/null 2>&1 || true
-fi
-echo " config: statuses + default_status → new vocab"
-
-# tasks: map current status → new
-shopt -s nullglob
-for f in backlog/tasks/*.md; do
- id=$(awk -F': ' '/^id:/{print $2; exit}' "$f")
- cur=$(awk '/^status:/{sub(/^status:[ ]*/,""); gsub(/^["'\'']|["'\'']$/,""); print; exit}' "$f")
- new=$(map_status "$cur")
- [ -z "$new" ] && continue
- [ "$new" = "$cur" ] && continue
- echo " $id: $cur → $new"
- [ -z "$dry" ] && backlog task edit "$id" -s "$new" --plain >/dev/null 2>&1
-done
-
-echo " done"
web/embed.go +0 −8
@@ -1,8 +0,0 @@
-// Package web embeds custard's static assets (CSS, JS, fonts) so the compiled
-// binary is self-contained and needs no files on disk at the deploy target.
-package web
-
-import "embed"
-
-//go:embed static
-var Static embed.FS
web/static/custard.css +0 −318
@@ -1,318 +0,0 @@
-/*
- * custard.css — terminal / Charm-flavored chrome built entirely on the
- * _shared-app-kit token system (tokens.css). No hard-coded colors: every value
- * reads a theme token, so all 7 data-theme modes (incl. eink) work for free.
- */
-
-* { box-sizing: border-box; }
-
-body {
- margin: 0;
- padding: 0 var(--space-4) var(--space-16);
- max-width: 1100px;
- margin-inline: auto;
- font-family: var(--font-body);
- font-size: var(--text-base);
- line-height: var(--leading-normal);
- background: var(--bg);
- color: var(--text);
-}
-
-a { color: var(--accent); text-decoration: none; }
-a:hover { text-decoration: underline; }
-code, pre, kbd, samp { font-family: var(--font-mono); font-size: var(--text-sm); }
-.muted { color: var(--text-muted); }
-.empty { color: var(--text-faint); font-style: italic; padding: var(--space-2) 0; }
-.num { text-align: right; color: var(--text-muted); }
-h1 { font-size: var(--text-2xl); margin: var(--space-4) 0 var(--space-3); }
-h2 { font-size: var(--text-lg); }
-
-/* ─── header ───────────────────────────────────────────────────────── */
-.site-head {
- display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
- padding: var(--space-3) 0;
- border-bottom: 1px solid var(--border);
- margin-bottom: var(--space-4);
-}
-.site-head-left { display: flex; align-items: baseline; gap: var(--space-2); min-width: 0; }
-.brand {
- font-family: var(--font-mono); font-weight: 700; font-size: var(--text-lg);
- color: var(--accent); letter-spacing: -0.02em;
-}
-.brand:hover { text-decoration: none; }
-.repo-name { font-family: var(--font-mono); color: var(--text); }
-.sep { color: var(--text-faint); }
-.theme-select {
- font-family: var(--font-mono); font-size: var(--text-xs);
- color: var(--text); background: var(--bg-card);
- border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
- padding: var(--space-1) var(--space-2);
-}
-
-/* ─── repo tabs ────────────────────────────────────────────────────── */
-.repo-tabs {
- display: flex; gap: var(--space-1);
- font-family: var(--font-mono); font-size: var(--text-sm);
- border-bottom: 1px solid var(--border);
- margin-bottom: var(--space-4);
-}
-.repo-tabs a {
- color: var(--text-muted); padding: var(--space-2) var(--space-3);
- border-bottom: 2px solid transparent; margin-bottom: -1px;
-}
-.repo-tabs a:hover { color: var(--text); text-decoration: none; background: var(--bg-hover); }
-.repo-tabs a.active { color: var(--text); border-bottom-color: var(--accent); }
-
-/* ─── repo list (index) ────────────────────────────────────────────── */
-.repo-list { list-style: none; padding: 0; margin: 0; border: 1px solid var(--border); border-radius: var(--radius); }
-.repo-list li {
- padding: var(--space-3) var(--space-4);
- border-bottom: 1px solid var(--border);
- display: grid; grid-template-columns: minmax(0,1fr); gap: var(--space-1);
-}
-.repo-list li:last-child { border-bottom: 0; }
-.repo-list li:hover { background: var(--bg-hover); }
-.repo-link { font-family: var(--font-mono); font-weight: 500; font-size: var(--text-base); }
-.repo-desc { color: var(--text-muted); }
-.repo-meta { color: var(--text-faint); font-size: var(--text-sm); }
-
-/* ─── repo home ────────────────────────────────────────────────────── */
-.repo-summary {
- display: flex; flex-wrap: wrap; gap: var(--space-4); align-items: center;
- font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-muted);
- padding: var(--space-3); border: 1px solid var(--border); border-radius: var(--radius);
- margin-bottom: var(--space-4);
-}
-.repo-summary code { color: var(--text); }
-.last-commit { font-size: var(--text-sm); color: var(--text); margin: var(--space-3) 0; }
-
-/* ─── tree & breadcrumbs ───────────────────────────────────────────── */
-.crumbs { font-family: var(--font-mono); font-size: var(--text-sm); margin: var(--space-3) 0; }
-.crumbs .sep { margin: 0 var(--space-1); }
-table.tree { width: 100%; border-collapse: collapse; font-family: var(--font-mono); font-size: var(--text-sm); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
-table.tree td { padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--border); }
-table.tree tr:last-child td { border-bottom: 0; }
-table.tree tr:hover td { background: var(--bg-hover); }
-
-/* ─── blob bar ─────────────────────────────────────────────────────── */
-.blob-bar {
- display: flex; gap: var(--space-3); align-items: center; justify-content: space-between;
- font-family: var(--font-mono); font-size: var(--text-sm);
- padding: var(--space-2) var(--space-3);
- background: var(--bg-card); border: 1px solid var(--border); border-bottom: 0;
- border-radius: var(--radius) var(--radius) 0 0;
-}
-
-/* ─── log ──────────────────────────────────────────────────────────── */
-.log { list-style: none; padding: 0; margin: 0; }
-.log li {
- display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-3);
- padding: var(--space-2) 0; border-bottom: 1px solid var(--border); font-size: var(--text-sm);
-}
-.commit-id {
- font-family: var(--font-mono); color: var(--accent);
- background: var(--bg-active); padding: 0 var(--space-2); border-radius: var(--radius-sm);
- flex: none;
-}
-.commit-title { color: var(--text); }
-.last-commit { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-3); font-size: var(--text-sm); margin: var(--space-3) 0; }
-
-.refs { list-style: none; padding: 0; }
-.refs li { display: flex; gap: var(--space-3); padding: var(--space-2) 0; border-bottom: 1px solid var(--border); font-family: var(--font-mono); font-size: var(--text-sm); }
-
-/* ─── commit ───────────────────────────────────────────────────────── */
-.commit-subject { font-family: var(--font-mono); }
-.commit-body { background: var(--bg-card); padding: var(--space-3); border-radius: var(--radius-sm); white-space: pre-wrap; }
-
-/* ─── per-file diffs (commit view) ─────────────────────────────────── */
-.diff-summary { font-family: var(--font-mono); font-size: var(--text-sm); margin: var(--space-4) 0 var(--space-2); }
-.filediff { margin: var(--space-3) 0; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
-.filediff-head {
- display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
- padding: var(--space-2) var(--space-3);
- background: var(--bg-card); border-bottom: 1px solid var(--border);
- font-family: var(--font-mono); font-size: var(--text-sm);
-}
-.filediff-name { color: var(--accent); font-weight: 600; word-break: break-all; }
-.diffstat { flex: none; font-family: var(--font-mono); font-size: var(--text-xs); }
-.diffstat .add { color: var(--green-600); }
-.diffstat .del { color: var(--red-600); }
-[data-theme$="-dark"] .diffstat .add { color: var(--green-300); }
-[data-theme$="-dark"] .diffstat .del { color: var(--red-300); }
-.filediff .code.diff { border: 0; border-radius: 0; }
-.filediff .empty { padding: var(--space-3); margin: 0; }
-
-/* ─── code blocks (chroma) ─────────────────────────────────────────── */
-.code {
- border: 1px solid var(--border); border-radius: 0 0 var(--radius) var(--radius);
- overflow-x: auto; background: var(--bg-card);
-}
-.code.diff { border-radius: var(--radius); }
-.chroma { margin: 0; padding: var(--space-3); background: transparent; font-size: var(--text-sm); line-height: var(--leading-normal); }
-.chroma .lntable { border-collapse: collapse; width: 100%; }
-.chroma .lntd { vertical-align: top; padding: 0; }
-.chroma .lnt, .chroma .ln { color: var(--text-faint); padding-right: var(--space-3); user-select: none; }
-
-/* token colors — all resolve from theme color families */
-.chroma .c, .chroma .ch, .chroma .cm, .chroma .c1, .chroma .cs, .chroma .cp, .chroma .cpf { color: var(--text-faint); font-style: italic; }
-.chroma .k, .chroma .kc, .chroma .kd, .chroma .kn, .chroma .kp, .chroma .kr, .chroma .kt { color: var(--purple); }
-.chroma .o, .chroma .ow { color: var(--pink); }
-.chroma .p, .chroma .pi { color: var(--text-muted); }
-.chroma .s, .chroma .sa, .chroma .sb, .chroma .sc, .chroma .dl, .chroma .sd, .chroma .s2, .chroma .se, .chroma .sh, .chroma .si, .chroma .sx, .chroma .sr, .chroma .s1, .chroma .ss { color: var(--green); }
-.chroma .m, .chroma .mb, .chroma .mf, .chroma .mh, .chroma .mi, .chroma .il, .chroma .mo { color: var(--orange); }
-.chroma .nf, .chroma .fm { color: var(--blue); }
-.chroma .nb, .chroma .bp { color: var(--cyan); }
-.chroma .nc, .chroma .nn, .chroma .no { color: var(--yellow); }
-.chroma .na, .chroma .nv, .chroma .vc, .chroma .vg, .chroma .vi { color: var(--cyan); }
-.chroma .nt { color: var(--red); }
-.chroma .nd, .chroma .ni, .chroma .ne, .chroma .nl, .chroma .py, .chroma .nx, .chroma .n { color: var(--text); }
-.chroma .err { color: var(--red); }
-
-/* diff (chroma diff lexer) */
-.chroma .gi { color: var(--green-600); background: color-mix(in oklab, var(--green) 16%, var(--bg)); display: block; }
-.chroma .gd { color: var(--red-600); background: color-mix(in oklab, var(--red) 16%, var(--bg)); display: block; }
-.chroma .gu, .chroma .gh { color: var(--blue); font-weight: 700; display: block; }
-.chroma .gp { color: var(--text-faint); }
-
-/* ─── markdown (README, issue bodies) ──────────────────────────────── */
-.markdown { line-height: var(--leading-relax); }
-.markdown.readme, .markdown.issue-body {
- border: 1px solid var(--border); border-radius: var(--radius);
- padding: var(--space-6); margin-top: var(--space-4); background: var(--bg-card);
-}
-.markdown h1, .markdown h2, .markdown h3, .markdown h4, .markdown h5, .markdown h6 { margin: var(--space-6) 0 var(--space-3); }
-/* h1 — Awke bold inside a solid pink block (Soft Serve title style) */
-.markdown h1 {
- font-family: var(--font-display); font-weight: 700;
- display: inline-block;
- background: var(--pink); color: var(--bg);
- padding: var(--space-1) var(--space-4);
- border-radius: var(--radius-sm);
-}
-/* h2 — Awke regular; rest walk the palette in the body face */
-.markdown h2 { font-family: var(--font-display); font-weight: 400; color: var(--blue); border-bottom: 1px solid var(--blue-200); padding-bottom: var(--space-2); }
-.markdown h3 { font-family: var(--font-body); color: var(--cyan); }
-.markdown h4 { font-family: var(--font-body); color: var(--purple); }
-.markdown h5, .markdown h6 { font-family: var(--font-body); color: var(--green); }
-.markdown p, .markdown ul, .markdown ol { margin: var(--space-3) 0; }
-.markdown a { color: var(--accent); text-decoration: underline; text-decoration-color: var(--accent-subtle-border); }
-.markdown strong { color: var(--orange); font-weight: 700; }
-.markdown em { color: var(--cyan); font-style: italic; }
-.markdown del { color: var(--text-faint); }
-.markdown code { color: var(--green-600); background: var(--bg-active); padding: 0.1em 0.35em; border-radius: var(--radius-sm); }
-[data-theme$="-dark"] .markdown code { color: var(--green-300); }
-.markdown pre { background: var(--bg); border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: var(--radius); padding: var(--space-3); overflow-x: auto; }
-.markdown pre code { color: inherit; background: transparent; padding: 0; }
-.markdown pre.chroma, .markdown .chroma pre { background: transparent; border: 0; padding: 0; }
-.markdown blockquote { margin: var(--space-3) 0; padding: var(--space-1) var(--space-4); border-left: 3px solid var(--purple); color: var(--text-muted); background: var(--accent-subtle); border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }
-.markdown hr { border: 0; border-top: 1px solid var(--border-strong); margin: var(--space-6) 0; }
-.markdown ul li::marker { color: var(--accent); }
-.markdown ol li::marker { color: var(--accent); }
-.markdown table { border-collapse: collapse; }
-.markdown th, .markdown td { border: 1px solid var(--border); padding: var(--space-2) var(--space-3); }
-.markdown th { background: var(--bg-active); color: var(--text-accent); }
-.markdown a code { color: inherit; }
-
-/* ─── issues ───────────────────────────────────────────────────────── */
-.issue-group { margin: var(--space-5) 0; }
-.issue-group h2.status { display: flex; align-items: baseline; gap: var(--space-2); }
-.issue-list { list-style: none; padding: 0; margin: var(--space-2) 0; border: 1px solid var(--border); border-radius: var(--radius); }
-.issue-list li { display: flex; align-items: baseline; gap: var(--space-2); padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--border); }
-.issue-list li:last-child { border-bottom: 0; }
-.issue-list li:hover { background: var(--bg-hover); }
-.issue-link { display: flex; align-items: baseline; gap: var(--space-2); min-width: 0; }
-.issue-id { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-faint); }
-.issue-title { color: var(--text); }
-.issue-meta { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: baseline; margin: var(--space-3) 0; }
-.issue-dates { font-size: var(--text-sm); }
-.issue-detail-title { font-family: var(--font-mono); }
-
-/* chips, badges, status — color via families, shape via tokens */
-.chip, .badge {
- font-family: var(--font-mono); font-size: var(--text-xs); line-height: 1.6;
- padding: 0 var(--space-2); border-radius: var(--radius-sm);
- border: 1px solid currentColor;
-}
-.badge { font-weight: 700; text-transform: lowercase; }
-.status {
- font-family: var(--font-mono); font-size: var(--text-sm);
- padding-left: var(--space-2); border-left: 3px solid currentColor;
-}
-.chip--red, .badge--red { color: var(--red-600); }
-.chip--orange, .badge--orange { color: var(--orange-600); }
-.chip--yellow, .badge--yellow { color: var(--yellow-600); }
-.chip--green, .badge--green { color: var(--green-600); }
-.chip--cyan, .badge--cyan { color: var(--cyan-600); }
-.chip--blue, .badge--blue { color: var(--blue-600); }
-.chip--purple, .badge--purple { color: var(--purple-600); }
-.chip--pink, .badge--pink { color: var(--pink-600); }
-.chip--accent, .badge--accent { color: var(--accent); }
-
-.status--backlog { color: var(--blue); }
-.status--in-progress { color: var(--green); }
-.status--paused { color: var(--orange); }
-.status--done { color: var(--text-muted); }
-.status--other { color: var(--text-muted); }
-
-/* dark themes: the -600 levels go too dark on a dark surface — use the lighter
- -300 level for chips/badges so tags stay legible (e.g. humdrum-dark feature). */
-[data-theme$="-dark"] .chip--red, [data-theme$="-dark"] .badge--red { color: var(--red-300); }
-[data-theme$="-dark"] .chip--orange, [data-theme$="-dark"] .badge--orange { color: var(--orange-300); }
-[data-theme$="-dark"] .chip--yellow, [data-theme$="-dark"] .badge--yellow { color: var(--yellow-300); }
-[data-theme$="-dark"] .chip--green, [data-theme$="-dark"] .badge--green { color: var(--green-300); }
-[data-theme$="-dark"] .chip--cyan, [data-theme$="-dark"] .badge--cyan { color: var(--cyan-300); }
-[data-theme$="-dark"] .chip--blue, [data-theme$="-dark"] .badge--blue { color: var(--blue-300); }
-[data-theme$="-dark"] .chip--purple, [data-theme$="-dark"] .badge--purple { color: var(--purple-300); }
-[data-theme$="-dark"] .chip--pink, [data-theme$="-dark"] .badge--pink { color: var(--pink-300); }
-
-/* priority pills */
-.prio {
- font-family: var(--font-mono); font-size: var(--text-xs); line-height: 1.6;
- padding: 0 var(--space-2); border-radius: var(--radius-sm);
- border: 1px solid currentColor;
-}
-.prio--high { color: var(--red-600); }
-.prio--medium { color: var(--orange-600); }
-.prio--low { color: var(--blue-600); }
-[data-theme$="-dark"] .prio--high { color: var(--red-300); }
-[data-theme$="-dark"] .prio--medium { color: var(--orange-300); }
-[data-theme$="-dark"] .prio--low { color: var(--blue-300); }
-
-/* paths & filenames — Charm-flavored: dirs accent+bold, files plain, ext muted */
-.tree-dir { color: var(--blue); font-weight: 600; }
-.tree-file { color: var(--text); }
-.tree-file:hover, .tree-dir:hover { color: var(--accent); }
-.crumbs a { color: var(--blue); }
-.crumbs a:last-child { color: var(--accent); font-weight: 600; }
-
-/* "words in blocks" — filled pills for refs/branches */
-.ref-pill {
- font-family: var(--font-mono); font-size: var(--text-xs);
- background: var(--accent-subtle); color: var(--text-accent);
- border: 1px solid var(--accent-subtle-border); border-radius: var(--radius-sm);
- padding: 0 var(--space-2);
-}
-.section-label { font-family: var(--font-mono); color: var(--accent); font-size: var(--text-base); margin: var(--space-6) 0 var(--space-2); }
-.readme-section { margin-top: var(--space-6); }
-
-/* frontmatter block (markdown files opened in code view) */
-.frontmatter {
- margin: 0; border: 1px solid var(--border); border-radius: var(--radius);
- background: var(--bg-card); padding: var(--space-2) var(--space-3); font-family: var(--font-mono); font-size: var(--text-sm);
-}
-.fm-row { display: grid; grid-template-columns: minmax(8rem, max-content) 1fr; gap: var(--space-3); padding: var(--space-1) 0; border-bottom: 1px solid var(--border); }
-.fm-row:last-child { border-bottom: 0; }
-.fm-row dt { color: var(--purple); }
-.fm-row dd { margin: 0; color: var(--text); }
-
-/* ─── footer ───────────────────────────────────────────────────────── */
-.site-foot {
- margin-top: var(--space-12);
- display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); align-items: baseline;
- padding: var(--space-3) var(--space-4);
- background: var(--bg-card); border: 1px solid var(--border-strong); border-radius: var(--radius);
- color: var(--text-muted); font-family: var(--font-mono); font-size: var(--text-xs);
-}
-.foot-brand { color: var(--accent); font-weight: 700; }
-.foot-item { color: var(--text-muted); }
-.foot-clone code { color: var(--text); background: var(--bg-active); padding: 0 var(--space-2); border-radius: var(--radius-sm); }
web/static/theme.js +0 −42
@@ -1,42 +0,0 @@
-// theme.js — live side of the theme switcher. The picker chooses a palette
-// FAMILY (flexoki/uchu/humdrum/eink); light vs dark follows the OS. An inline
-// <head> script already resolved the first paint; this handles switching and
-// reacting when the OS color scheme flips.
-(function () {
- "use strict";
- var FAMILIES = ["flexoki", "uchu", "humdrum", "eink"];
-
- function family() {
- var m = document.cookie.match(/(?:^|; )theme=([^;]+)/);
- var f = m ? decodeURIComponent(m[1]) : "flexoki";
- return FAMILIES.indexOf(f) < 0 ? "flexoki" : f;
- }
-
- function systemDark() {
- return window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
- }
-
- function resolve(f) {
- return f === "eink" ? "eink" : (systemDark() ? f + "-dark" : f);
- }
-
- function apply(f) {
- document.documentElement.setAttribute("data-theme", resolve(f));
- document.cookie = "theme=" + f + "; Path=/; Max-Age=31536000; SameSite=Lax";
- try { localStorage.setItem("theme", f); } catch (e) {}
- }
-
- document.addEventListener("DOMContentLoaded", function () {
- var sel = document.getElementById("theme-select");
- if (sel) sel.addEventListener("change", function () { apply(sel.value); });
- });
-
- if (window.matchMedia) {
- var mq = window.matchMedia("(prefers-color-scheme: dark)");
- var onChange = function () {
- document.documentElement.setAttribute("data-theme", resolve(family()));
- };
- if (mq.addEventListener) mq.addEventListener("change", onChange);
- else if (mq.addListener) mq.addListener(onChange);
- }
-})();
web/static/tokens.css +0 −638
@@ -1,638 +0,0 @@
-/*
- * tokens.css — single source-of-truth for theme + type tokens.
- * Imported by scaffold/app/globals.css and STYLE_GUIDE.html.
- *
- * Themes (7 modes via data-theme):
- * flexoki | flexoki-dark | uchu | uchu-dark | humdrum | humdrum-dark | eink
- *
- * Color levels (per family — red, orange, yellow, green, cyan, blue, purple, pink):
- * --{color}-200 — lightest accent, hairline borders, soft fills
- * --{color}-300 — soft accent, hover backgrounds
- * --{color}-400 — DEFAULT (also exposed as --{color} alias)
- * --{color}-500 — pressed/active
- * --{color}-600 — strong text on light, prominent fills
- *
- * Sources:
- * Flexoki — github.com/kepano/flexoki (kepano)
- * Uchu — github.com/Passw/NeverCease-uchu, uchu.style
- * Humdrum — custom; blue base #0f80ea per spec, OKLCH-derived levels
- */
-
-/* ─── Font face declarations ───────────────────────────────────────── */
-
-/* Awke — display. Three weights. */
-@font-face {
- font-family: "Awke";
- src: url("/static/fonts/Awke-Thin.woff2") format("woff2");
- font-weight: 100;
- font-style: normal;
- font-display: swap;
-}
-@font-face {
- font-family: "Awke";
- src: url("/static/fonts/Awke-Regular.woff2") format("woff2");
- font-weight: 400;
- font-style: normal;
- font-display: swap;
-}
-@font-face {
- font-family: "Awke";
- src: url("/static/fonts/Awke-Bold.woff2") format("woff2");
- font-weight: 700;
- font-style: normal;
- font-display: swap;
-}
-
-/* Untitled Sans — body. Light/Regular/Medium/Bold/Black + italics. */
-@font-face {
- font-family: "Untitled Sans";
- src: url("/static/fonts/untitled-sans-light.woff2") format("woff2");
- font-weight: 300; font-style: normal; font-display: swap;
-}
-@font-face {
- font-family: "Untitled Sans";
- src: url("/static/fonts/untitled-sans-light-italic.woff2") format("woff2");
- font-weight: 300; font-style: italic; font-display: swap;
-}
-@font-face {
- font-family: "Untitled Sans";
- src: url("/static/fonts/untitled-sans-regular.woff2") format("woff2");
- font-weight: 400; font-style: normal; font-display: swap;
-}
-@font-face {
- font-family: "Untitled Sans";
- src: url("/static/fonts/untitled-sans-regular-italic.woff2") format("woff2");
- font-weight: 400; font-style: italic; font-display: swap;
-}
-@font-face {
- font-family: "Untitled Sans";
- src: url("/static/fonts/untitled-sans-medium.woff2") format("woff2");
- font-weight: 500; font-style: normal; font-display: swap;
-}
-@font-face {
- font-family: "Untitled Sans";
- src: url("/static/fonts/untitled-sans-medium-italic.woff2") format("woff2");
- font-weight: 500; font-style: italic; font-display: swap;
-}
-@font-face {
- font-family: "Untitled Sans";
- src: url("/static/fonts/untitled-sans-bold.woff2") format("woff2");
- font-weight: 700; font-style: normal; font-display: swap;
-}
-@font-face {
- font-family: "Untitled Sans";
- src: url("/static/fonts/untitled-sans-bold-italic.woff2") format("woff2");
- font-weight: 700; font-style: italic; font-display: swap;
-}
-@font-face {
- font-family: "Untitled Sans";
- src: url("/static/fonts/untitled-sans-black.woff2") format("woff2");
- font-weight: 900; font-style: normal; font-display: swap;
-}
-@font-face {
- font-family: "Untitled Sans";
- src: url("/static/fonts/untitled-sans-black-italic.woff2") format("woff2");
- font-weight: 900; font-style: italic; font-display: swap;
-}
-
-/* Name Mono — variable font preferred, static fallbacks. */
-@font-face {
- font-family: "Name Mono";
- src: url("/static/fonts/ATNameMonoVariable-Regular.woff2") format("woff2-variations"),
- url("/static/fonts/ATNameMono-Regular.woff2") format("woff2");
- font-weight: 100 900; font-style: normal; font-display: swap;
-}
-@font-face {
- font-family: "Name Mono";
- src: url("/static/fonts/ATNameMonoVariable-RegularItalic.woff2") format("woff2-variations"),
- url("/static/fonts/ATNameMono-Italic.woff2") format("woff2");
- font-weight: 100 900; font-style: italic; font-display: swap;
-}
-
-/* ─── Global tokens (shared across all themes) ─────────────────────── */
-
-:root {
- --font-display: "Awke", "Untitled Sans", sans-serif;
- --font-body: "Untitled Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
- --font-mono: "Name Mono", ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
-
- --text-xs: 0.75rem;
- --text-sm: 0.875rem;
- --text-base: 1rem;
- --text-lg: 1.125rem;
- --text-xl: 1.25rem;
- --text-2xl: 1.5rem;
- --text-3xl: 1.875rem;
- --text-4xl: 2.25rem;
- --text-5xl: 3rem;
- --leading-tight: 1.2;
- --leading-normal: 1.5;
- --leading-relax: 1.7;
-
- --space-1: 0.25rem;
- --space-2: 0.5rem;
- --space-3: 0.75rem;
- --space-4: 1rem;
- --space-5: 1.25rem;
- --space-6: 1.5rem;
- --space-8: 2rem;
- --space-10: 2.5rem;
- --space-12: 3rem;
- --space-16: 4rem;
-
- --radius-sm: 4px;
- --radius: 8px;
- --radius-lg: 12px;
- --radius-xl: 16px;
-
- --tap-min: 44px;
-
- /*
- * Derived semantic tokens — resolve per-theme via the active --accent + --bg.
- * color-mix evaluates at use-site, so the same :root declaration works for
- * every theme without repetition.
- */
- --accent-subtle: color-mix(in oklab, var(--accent) 12%, var(--bg));
- --accent-subtle-border: var(--accent);
- --focus-ring: var(--accent);
-}
-
-/* ═══════════════════════════════════════════════════════════════════
- FLEXOKI — kepano canonical (github.com/kepano/flexoki)
- ═══════════════════════════════════════════════════════════════════ */
-
-:root,
-[data-theme="flexoki"] {
- /* Surfaces */
- --bg: #FFFCF0; /* paper */
- --bg-card: #F2F0E5; /* 50 */
- --bg-elevated: #F2F0E5;
- --bg-hover: rgba(0, 0, 0, 0.04);
- --bg-active: rgba(0, 0, 0, 0.08);
- --border: #E6E4D9; /* 100 */
- --border-strong: #CECDC3; /* 200 */
- --text: #100F0F; /* black */
- --text-muted: #6F6E69; /* 600 */
- --text-faint: #B7B5AC; /* 300 */
- --text-accent: #100F0F;
- --text-on-accent:#FFFCF0;
- --selection: #DDF1E4; /* cyan-50 */
- --accent: #24837B; /* cyan-600 */
-
- /* Red */
- --red-200: #F89A8A; --red-300: #E8705F; --red-400: #D14D41; --red-500: #C03E35; --red-600: #AF3029;
- --red: var(--red-400);
- /* Orange */
- --orange-200: #F9AE77; --orange-300: #EC8B49; --orange-400: #DA702C; --orange-500: #CB6120; --orange-600: #BC5215;
- --orange: var(--orange-400);
- /* Yellow */
- --yellow-200: #ECCB60; --yellow-300: #DFB431; --yellow-400: #D0A215; --yellow-500: #BE9207; --yellow-600: #AD8301;
- --yellow: var(--yellow-400);
- /* Green */
- --green-200: #BEC97E; --green-300: #A0AF54; --green-400: #879A39; --green-500: #768D21; --green-600: #66800B;
- --green: var(--green-400);
- /* Cyan */
- --cyan-200: #87D3C3; --cyan-300: #5ABDAC; --cyan-400: #3AA99F; --cyan-500: #2F968D; --cyan-600: #24837B;
- --cyan: var(--cyan-400);
- /* Blue */
- --blue-200: #92BFDB; --blue-300: #66A0C8; --blue-400: #4385BE; --blue-500: #3171B2; --blue-600: #205EA6;
- --blue: var(--blue-400);
- /* Purple */
- --purple-200: #C4B9E0; --purple-300: #A699D0; --purple-400: #8B7EC8; --purple-500: #735EB5; --purple-600: #5E409D;
- --purple: var(--purple-400);
- /* Pink (Flexoki magenta) */
- --pink-200: #F4A4C2; --pink-300: #E47DA8; --pink-400: #CE5D97; --pink-500: #B74583; --pink-600: #A02F6F;
- --pink: var(--pink-400);
-}
-
-[data-theme="flexoki-dark"] {
- --bg: #100F0F; /* black */
- --bg-card: #1C1B1A; /* 950 */
- --bg-elevated: #282726; /* 900 */
- --bg-hover: rgba(255, 255, 255, 0.05);
- --bg-active: rgba(255, 255, 255, 0.1);
- --border: #282726; /* 900 */
- --border-strong: #403E3C; /* 800 */
- --text: #CECDC3; /* 200 */
- --text-muted: #878580; /* 500 */
- --text-faint: #575653; /* 700 */
- --text-accent: #CECDC3;
- --text-on-accent:#100F0F;
- --selection: #122F2C; /* cyan-900 */
- --accent: #3AA99F; /* cyan-400 */
-
- --red-200: #F89A8A; --red-300: #E8705F; --red-400: #D14D41; --red-500: #C03E35; --red-600: #AF3029;
- --red: var(--red-400);
- --orange-200: #F9AE77; --orange-300: #EC8B49; --orange-400: #DA702C; --orange-500: #CB6120; --orange-600: #BC5215;
- --orange: var(--orange-400);
- --yellow-200: #ECCB60; --yellow-300: #DFB431; --yellow-400: #D0A215; --yellow-500: #BE9207; --yellow-600: #AD8301;
- --yellow: var(--yellow-400);
- --green-200: #BEC97E; --green-300: #A0AF54; --green-400: #879A39; --green-500: #768D21; --green-600: #66800B;
- --green: var(--green-400);
- --cyan-200: #87D3C3; --cyan-300: #5ABDAC; --cyan-400: #3AA99F; --cyan-500: #2F968D; --cyan-600: #24837B;
- --cyan: var(--cyan-400);
- --blue-200: #92BFDB; --blue-300: #66A0C8; --blue-400: #4385BE; --blue-500: #3171B2; --blue-600: #205EA6;
- --blue: var(--blue-400);
- --purple-200: #C4B9E0; --purple-300: #A699D0; --purple-400: #8B7EC8; --purple-500: #735EB5; --purple-600: #5E409D;
- --purple: var(--purple-400);
- --pink-200: #F4A4C2; --pink-300: #E47DA8; --pink-400: #CE5D97; --pink-500: #B74583; --pink-600: #A02F6F;
- --pink: var(--pink-400);
-}
-
-/* ═══════════════════════════════════════════════════════════════════
- UCHU — canonical OKLCH (github.com/Passw/NeverCease-uchu, uchu.style)
- Levels 2-6 of canonical 1-9 scale → mapped to 200-600.
- ═══════════════════════════════════════════════════════════════════ */
-
-[data-theme="uchu"] {
- --bg: oklch(99.4% 0 0); /* yang */
- --bg-card: oklch(95.57% 0.003 286.35); /* gray-1 */
- --bg-elevated: oklch(99.4% 0 0);
- --bg-hover: rgba(0, 0, 0, 0.035);
- --bg-active: rgba(0, 0, 0, 0.07);
- --border: oklch(91.87% 0.003 264.54); /* yin-1 */
- --border-strong: oklch(84.61% 0.004 286.31); /* yin-2 */
- --text: oklch(14.38% 0.007 256.88); /* yin */
- --text-muted: oklch(43.87% 0.005 271.3); /* yin-7 */
- --text-faint: oklch(69.17% 0.004 247.88); /* yin-4 */
- --text-accent: oklch(14.38% 0.007 256.88);
- --text-on-accent:oklch(99.4% 0 0);
- --selection: oklch(89.1% 0.046 305.24); /* purple-1 */
- --accent: oklch(58.47% 0.181 302.06); /* purple-4 */
-
- --red-200: oklch(78.78% 0.109 4.54);
- --red-300: oklch(69.86% 0.162 7.82);
- --red-400: oklch(62.73% 0.209 12.37);
- --red-500: oklch(58.63% 0.231 19.6);
- --red-600: oklch(54.41% 0.214 19.06);
- --red: var(--red-400);
-
- --orange-200: oklch(88.37% 0.0726 55.8);
- --orange-300: oklch(83.56% 0.1075 56.49);
- --orange-400: oklch(78.75% 0.1416 54.33);
- --orange-500: oklch(74.61% 0.171 51.56);
- --orange-600: oklch(69.33% 0.157 52.18);
- --orange: var(--orange-400);
-
- --yellow-200: oklch(95% 0.07 92.39);
- --yellow-300: oklch(92.76% 0.098 92.58);
- --yellow-400: oklch(90.92% 0.125 92.56);
- --yellow-500: oklch(89% 0.146 91.5);
- --yellow-600: oklch(82.39% 0.133 91.5);
- --yellow: var(--yellow-400);
-
- --green-200: oklch(88.77% 0.096 147.71);
- --green-300: oklch(83.74% 0.139 146.57);
- --green-400: oklch(79.33% 0.179 145.62);
- --green-500: oklch(75.23% 0.209 144.64);
- --green-600: oklch(70.03% 0.194 144.71);
- --green: var(--green-400);
-
- /* Uchu has no canonical "cyan" — derive from blue+green hue middle. */
- --cyan-200: oklch(85% 0.07 200);
- --cyan-300: oklch(78% 0.10 200);
- --cyan-400: oklch(70% 0.13 200);
- --cyan-500: oklch(63% 0.14 200);
- --cyan-600: oklch(55% 0.13 200);
- --cyan: var(--cyan-400);
-
- --blue-200: oklch(80.17% 0.091 258.88);
- --blue-300: oklch(70.94% 0.136 258.06);
- --blue-400: oklch(62.39% 0.181 258.33);
- --blue-500: oklch(54.87% 0.222 260.33);
- --blue-600: oklch(51.15% 0.204 260.17);
- --blue: var(--blue-400);
-
- --purple-200: oklch(78.68% 0.091 305);
- --purple-300: oklch(68.5% 0.136 303.78);
- --purple-400: oklch(58.47% 0.181 302.06);
- --purple-500: oklch(49.39% 0.215 298.31);
- --purple-600: oklch(46.11% 0.198 298.4);
- --purple: var(--purple-400);
-
- --pink-200: oklch(92.14% 0.046 352.31);
- --pink-300: oklch(88.9% 0.066 354.39);
- --pink-400: oklch(85.43% 0.09 354.1);
- --pink-500: oklch(82.23% 0.112 355.33);
- --pink-600: oklch(76.37% 0.101 355.37);
- --pink: var(--pink-400);
-}
-
-[data-theme="uchu-dark"] {
- --bg: oklch(14.38% 0.007 256.88); /* yin */
- --bg-card: oklch(25.11% 0.006 258.36); /* yin-9 */
- --bg-elevated: oklch(35.02% 0.005 236.66); /* yin-8 */
- --bg-hover: rgba(255, 255, 255, 0.05);
- --bg-active: rgba(255, 255, 255, 0.1);
- --border: oklch(35.02% 0.005 236.66); /* yin-8 */
- --border-strong: oklch(43.87% 0.005 271.3); /* yin-7 */
- --text: oklch(91.87% 0.003 264.54); /* yin-1 */
- --text-muted: oklch(69.17% 0.004 247.88); /* yin-4 */
- --text-faint: oklch(52.79% 0.005 271.32); /* yin-6 */
- --text-accent: oklch(91.87% 0.003 264.54);
- --text-on-accent:oklch(14.38% 0.007 256.88);
- --selection: oklch(36.01% 0.145 298.35); /* purple-9 */
- --accent: oklch(68.5% 0.136 303.78); /* purple-3 */
-
- --red-200: oklch(78.78% 0.109 4.54);
- --red-300: oklch(69.86% 0.162 7.82);
- --red-400: oklch(62.73% 0.209 12.37);
- --red-500: oklch(58.63% 0.231 19.6);
- --red-600: oklch(54.41% 0.214 19.06);
- --red: var(--red-400);
- --orange-200: oklch(88.37% 0.0726 55.8);
- --orange-300: oklch(83.56% 0.1075 56.49);
- --orange-400: oklch(78.75% 0.1416 54.33);
- --orange-500: oklch(74.61% 0.171 51.56);
- --orange-600: oklch(69.33% 0.157 52.18);
- --orange: var(--orange-400);
- --yellow-200: oklch(95% 0.07 92.39);
- --yellow-300: oklch(92.76% 0.098 92.58);
- --yellow-400: oklch(90.92% 0.125 92.56);
- --yellow-500: oklch(89% 0.146 91.5);
- --yellow-600: oklch(82.39% 0.133 91.5);
- --yellow: var(--yellow-400);
- --green-200: oklch(88.77% 0.096 147.71);
- --green-300: oklch(83.74% 0.139 146.57);
- --green-400: oklch(79.33% 0.179 145.62);
- --green-500: oklch(75.23% 0.209 144.64);
- --green-600: oklch(70.03% 0.194 144.71);
- --green: var(--green-400);
- --cyan-200: oklch(85% 0.07 200);
- --cyan-300: oklch(78% 0.10 200);
- --cyan-400: oklch(70% 0.13 200);
- --cyan-500: oklch(63% 0.14 200);
- --cyan-600: oklch(55% 0.13 200);
- --cyan: var(--cyan-400);
- --blue-200: oklch(80.17% 0.091 258.88);
- --blue-300: oklch(70.94% 0.136 258.06);
- --blue-400: oklch(62.39% 0.181 258.33);
- --blue-500: oklch(54.87% 0.222 260.33);
- --blue-600: oklch(51.15% 0.204 260.17);
- --blue: var(--blue-400);
- --purple-200: oklch(78.68% 0.091 305);
- --purple-300: oklch(68.5% 0.136 303.78);
- --purple-400: oklch(58.47% 0.181 302.06);
- --purple-500: oklch(49.39% 0.215 298.31);
- --purple-600: oklch(46.11% 0.198 298.4);
- --purple: var(--purple-400);
- --pink-200: oklch(92.14% 0.046 352.31);
- --pink-300: oklch(88.9% 0.066 354.39);
- --pink-400: oklch(85.43% 0.09 354.1);
- --pink-500: oklch(82.23% 0.112 355.33);
- --pink-600: oklch(76.37% 0.101 355.37);
- --pink: var(--pink-400);
-}
-
-/* ═══════════════════════════════════════════════════════════════════
- HUMDRUM — warm neutrals, vivid blue accent (#0f80ea ≈ oklch 60% 0.21 246).
- All accent families derived in OKLCH around 400 = base.
- ═══════════════════════════════════════════════════════════════════ */
-
-[data-theme="humdrum"] {
- --bg: #F5F3EE;
- --bg-card: #FFFFFF;
- --bg-elevated: #FFFFFF;
- --bg-hover: rgba(0, 0, 0, 0.035);
- --bg-active: rgba(0, 0, 0, 0.07);
- --border: #E2DFD7;
- --border-strong: #C3BFB3;
- --text: #2A2825;
- --text-muted: #6D6A63;
- --text-faint: #ADA99F;
- --text-accent: #2A2825;
- --text-on-accent:#FFFFFF;
- --selection: oklch(0.85 0.07 253); /* blue-200 tint */
- --accent: #0F80EA; /* blue-400, kept as hex */
-
- /*
- * Color levels — every family anchored at oklch(0.60 0.18 H).
- * Blue 400 = oklch(0.60 0.18 253) ≈ #0F80EA (actual conversion).
- * All other families share L=0.60 C=0.18 at 400, only hue changes.
- *
- * Step pattern (L, C multiplier):
- * 200: L 0.82, C × 0.45 (= 0.081)
- * 300: L 0.72, C × 0.75 (= 0.135)
- * 400: L 0.60, C × 1.00 (= 0.180) base
- * 500: L 0.53, C × 0.95 (= 0.171)
- * 600: L 0.45, C × 0.85 (= 0.153)
- */
-
- /* Blue (primary) — base = #0F80EA */
- --blue-200: oklch(0.82 0.081 253);
- --blue-300: oklch(0.72 0.135 253);
- --blue-400: oklch(0.60 0.180 253);
- --blue-500: oklch(0.53 0.171 253);
- --blue-600: oklch(0.45 0.153 253);
- --blue: var(--blue-400);
-
- --red-200: oklch(0.82 0.081 22);
- --red-300: oklch(0.72 0.135 22);
- --red-400: oklch(0.60 0.180 22);
- --red-500: oklch(0.53 0.171 22);
- --red-600: oklch(0.45 0.153 22);
- --red: var(--red-400);
-
- --orange-200: oklch(0.82 0.081 50);
- --orange-300: oklch(0.72 0.135 50);
- --orange-400: oklch(0.60 0.180 50);
- --orange-500: oklch(0.53 0.171 50);
- --orange-600: oklch(0.45 0.153 50);
- --orange: var(--orange-400);
-
- --yellow-200: oklch(0.82 0.081 85);
- --yellow-300: oklch(0.72 0.135 85);
- --yellow-400: oklch(0.60 0.180 85);
- --yellow-500: oklch(0.53 0.171 85);
- --yellow-600: oklch(0.45 0.153 85);
- --yellow: var(--yellow-400);
-
- --green-200: oklch(0.82 0.081 140);
- --green-300: oklch(0.72 0.135 140);
- --green-400: oklch(0.60 0.180 140);
- --green-500: oklch(0.53 0.171 140);
- --green-600: oklch(0.45 0.153 140);
- --green: var(--green-400);
-
- --cyan-200: oklch(0.82 0.081 205);
- --cyan-300: oklch(0.72 0.135 205);
- --cyan-400: oklch(0.60 0.180 205);
- --cyan-500: oklch(0.53 0.171 205);
- --cyan-600: oklch(0.45 0.153 205);
- --cyan: var(--cyan-400);
-
- --purple-200: oklch(0.82 0.081 295);
- --purple-300: oklch(0.72 0.135 295);
- --purple-400: oklch(0.60 0.180 295);
- --purple-500: oklch(0.53 0.171 295);
- --purple-600: oklch(0.45 0.153 295);
- --purple: var(--purple-400);
-
- --pink-200: oklch(0.82 0.081 350);
- --pink-300: oklch(0.72 0.135 350);
- --pink-400: oklch(0.60 0.180 350);
- --pink-500: oklch(0.53 0.171 350);
- --pink-600: oklch(0.45 0.153 350);
- --pink: var(--pink-400);
-}
-
-[data-theme="humdrum-dark"] {
- --bg: #1F1D1A;
- --bg-card: #282622;
- --bg-elevated: #32302C;
- --bg-hover: rgba(255, 255, 255, 0.04);
- --bg-active: rgba(255, 255, 255, 0.08);
- --border: #32302C;
- --border-strong: #4A4740;
- --text: #E8E5DD;
- --text-muted: #A8A49B;
- --text-faint: #6D6A63;
- --text-accent: #E8E5DD;
- --text-on-accent:#FFFFFF;
- --selection: oklch(0.35 0.13 253); /* dark blue tint */
- --accent: #0F80EA; /* same as light, kept hex */
-
- --blue-200: oklch(0.82 0.081 253);
- --blue-300: oklch(0.72 0.135 253);
- --blue-400: oklch(0.60 0.180 253);
- --blue-500: oklch(0.53 0.171 253);
- --blue-600: oklch(0.45 0.153 253);
- --blue: var(--blue-400);
-
- --red-200: oklch(0.82 0.081 22);
- --red-300: oklch(0.72 0.135 22);
- --red-400: oklch(0.60 0.180 22);
- --red-500: oklch(0.53 0.171 22);
- --red-600: oklch(0.45 0.153 22);
- --red: var(--red-400);
-
- --orange-200: oklch(0.82 0.081 50);
- --orange-300: oklch(0.72 0.135 50);
- --orange-400: oklch(0.60 0.180 50);
- --orange-500: oklch(0.53 0.171 50);
- --orange-600: oklch(0.45 0.153 50);
- --orange: var(--orange-400);
-
- --yellow-200: oklch(0.82 0.081 85);
- --yellow-300: oklch(0.72 0.135 85);
- --yellow-400: oklch(0.60 0.180 85);
- --yellow-500: oklch(0.53 0.171 85);
- --yellow-600: oklch(0.45 0.153 85);
- --yellow: var(--yellow-400);
-
- --green-200: oklch(0.82 0.081 140);
- --green-300: oklch(0.72 0.135 140);
- --green-400: oklch(0.60 0.180 140);
- --green-500: oklch(0.53 0.171 140);
- --green-600: oklch(0.45 0.153 140);
- --green: var(--green-400);
-
- --cyan-200: oklch(0.82 0.081 205);
- --cyan-300: oklch(0.72 0.135 205);
- --cyan-400: oklch(0.60 0.180 205);
- --cyan-500: oklch(0.53 0.171 205);
- --cyan-600: oklch(0.45 0.153 205);
- --cyan: var(--cyan-400);
-
- --purple-200: oklch(0.82 0.081 295);
- --purple-300: oklch(0.72 0.135 295);
- --purple-400: oklch(0.60 0.180 295);
- --purple-500: oklch(0.53 0.171 295);
- --purple-600: oklch(0.45 0.153 295);
- --purple: var(--purple-400);
-
- --pink-200: oklch(0.82 0.081 350);
- --pink-300: oklch(0.72 0.135 350);
- --pink-400: oklch(0.60 0.180 350);
- --pink-500: oklch(0.53 0.171 350);
- --pink-600: oklch(0.45 0.153 350);
- --pink: var(--pink-400);
-}
-
-/* ═══════════════════════════════════════════════════════════════════
- E-INK — pure monochrome, no motion. All accents collapse to black.
- ═══════════════════════════════════════════════════════════════════ */
-
-[data-theme="eink"] {
- --bg: #FFFFFF;
- --bg-card: #FFFFFF;
- --bg-elevated: #FFFFFF;
- --bg-hover: #000000;
- --bg-active: #000000;
- --border: #000000;
- --border-strong: #000000;
- --text: #000000;
- --text-muted: #000000;
- --text-faint: #000000;
- --text-accent: #000000;
- --text-on-accent:#FFFFFF;
- --selection: #000000;
- --accent: #000000;
-
- --red-200: #000; --red-300: #000; --red-400: #000; --red-500: #000; --red-600: #000;
- --red: #000;
- --orange-200: #000; --orange-300: #000; --orange-400: #000; --orange-500: #000; --orange-600: #000;
- --orange: #000;
- --yellow-200: #000; --yellow-300: #000; --yellow-400: #000; --yellow-500: #000; --yellow-600: #000;
- --yellow: #000;
- --green-200: #000; --green-300: #000; --green-400: #000; --green-500: #000; --green-600: #000;
- --green: #000;
- --cyan-200: #000; --cyan-300: #000; --cyan-400: #000; --cyan-500: #000; --cyan-600: #000;
- --cyan: #000;
- --blue-200: #000; --blue-300: #000; --blue-400: #000; --blue-500: #000; --blue-600: #000;
- --blue: #000;
- --purple-200: #000; --purple-300: #000; --purple-400: #000; --purple-500: #000; --purple-600: #000;
- --purple: #000;
- --pink-200: #000; --pink-300: #000; --pink-400: #000; --pink-500: #000; --pink-600: #000;
- --pink: #000;
-}
-
-[data-theme="eink"],
-[data-theme="eink"] * {
- transition: none !important;
- animation: none !important;
- box-shadow: none !important;
- font-weight: 600;
-}
-
-[data-theme="eink"] strong,
-[data-theme="eink"] b,
-[data-theme="eink"] h1,
-[data-theme="eink"] h2,
-[data-theme="eink"] h3 {
- font-weight: 800;
-}
-
-[data-theme="eink"] button,
-[data-theme="eink"] .card,
-[data-theme="eink"] .nav,
-[data-theme="eink"] .btn,
-[data-theme="eink"] .dialog {
- border: 2px solid #000 !important;
- border-radius: 0 !important;
-}
-
-[data-theme="eink"] .btn-primary {
- background: #000 !important;
- color: #fff !important;
-}
-
-[data-theme="eink"] .highlight {
- background: transparent !important;
- border-bottom: 4px solid #000 !important;
- border-radius: 0 !important;
-}
-
-/* ─── Base body wiring ─────────────────────────────────────────────── */
-
-html, body {
- background: var(--bg);
- color: var(--text);
- font-family: var(--font-body);
- font-size: var(--text-base);
- line-height: var(--leading-normal);
-}
-
-h1, h2, h3 { font-family: var(--font-display); line-height: var(--leading-tight); }
-code, pre, kbd, samp { font-family: var(--font-mono); }
-::selection { background: var(--selection); }
web/templates/templates.templ +0 −459
@@ -1,459 +0,0 @@
-package templates
-
-import (
- "fmt"
- "strconv"
- "strings"
-)
-
-// Layout is the shared page shell. Styling is intentionally minimal in phase 1;
-// the design pass (tokens + Charm chrome) lands in phase 3.
-templ Layout(m Meta) {
- <!DOCTYPE html>
- <html lang="en" data-theme={ themeOr(m.Theme) }>
- <head>
- <meta charset="utf-8"/>
- <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
- <title>{ pageTitle(m) }</title>
- @templ.Raw(themeResolveScript)
- <link rel="stylesheet" href="/static/tokens.css"/>
- <link rel="stylesheet" href="/static/custard.css"/>
- <script src="/static/theme.js" defer></script>
- </head>
- <body>
- <header class="site-head">
- <div class="site-head-left">
- <a href="/" class="brand">▍ humdrum codex</a>
- if m.Repo != "" {
- <span class="sep">/</span>
- <a href={ templ.SafeURL("/r/" + m.Repo) } class="repo-name">{ m.Repo }</a>
- }
- </div>
- @themePicker(m)
- </header>
- if m.Repo != "" {
- @repoTabs(m)
- }
- <main class="content">
- { children... }
- </main>
- <footer class="site-foot">
- <span class="foot-brand">▍ served by custard</span>
- if m.Repo != "" {
- <span class="foot-item">{ m.Repo }</span>
- if m.Ref != "" {
- <span class="foot-item ref-pill">{ m.Ref }</span>
- }
- if m.CloneURL != "" {
- <span class="foot-item foot-clone">clone <code>git clone { m.CloneURL }</code></span>
- }
- }
- </footer>
- </body>
- </html>
-}
-
-templ themePicker(m Meta) {
- <select id="theme-select" aria-label="Theme" class="theme-select">
- for _, t := range Families {
- if t == themeOr(m.Theme) {
- <option value={ t } selected>{ themeLabel(t) }</option>
- } else {
- <option value={ t }>{ themeLabel(t) }</option>
- }
- }
- </select>
-}
-
-// themeResolveScript runs synchronously in <head> before paint: it reads the
-// family cookie and the OS color scheme, then sets the concrete data-theme
-// (family or family-dark; eink is fixed). This is the flash-free path for the
-// system-driven light/dark axis the server can't see.
-const themeResolveScript = `<script>(function(){try{` +
- `var m=document.cookie.match(/(?:^|; )theme=([^;]+)/);` +
- `var f=m?decodeURIComponent(m[1]):'flexoki';` +
- `if(['flexoki','uchu','humdrum','eink'].indexOf(f)<0)f='flexoki';` +
- `var d=window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches;` +
- `document.documentElement.setAttribute('data-theme',f==='eink'?'eink':(d?f+'-dark':f));` +
- `}catch(e){}})();</script>`
-
-templ repoTabs(m Meta) {
- <nav class="repo-tabs">
- if m.HasReadme {
- <a href={ templ.SafeURL("/r/" + m.Repo + "/readme") } class={ templ.KV("active", m.Tab == "readme") }>readme</a>
- }
- <a href={ templ.SafeURL("/r/" + m.Repo + "/files") } class={ templ.KV("active", m.Tab == "code") }>code</a>
- <a href={ templ.SafeURL("/r/" + m.Repo + "/log/" + refOr(m.Ref)) } class={ templ.KV("active", m.Tab == "log") }>log</a>
- <a href={ templ.SafeURL("/r/" + m.Repo + "/refs") } class={ templ.KV("active", m.Tab == "refs") }>refs</a>
- if m.HasIssues {
- <a href={ templ.SafeURL("/r/" + m.Repo + "/issues") } class={ templ.KV("active", m.Tab == "issues") }>issues</a>
- }
- </nav>
-}
-
-templ Index(p IndexPage) {
- @Layout(p.Meta) {
- <h1>Repositories</h1>
- if len(p.Repos) == 0 {
- <p class="empty">No repositories found.</p>
- }
- <ul class="repo-list">
- for _, r := range p.Repos {
- <li>
- <a class="repo-link" href={ templ.SafeURL("/r/" + r.Name) }>{ r.Name }</a>
- if r.Description != "" {
- <span class="repo-desc">{ r.Description }</span>
- }
- if r.Last != nil {
- <span class="repo-meta">{ r.Last.Subject } · { FmtTime(r.Last.When) }</span>
- }
- </li>
- }
- </ul>
- }
-}
-
-templ Repo(p RepoPage) {
- @Layout(p.Meta) {
- <div class="repo-summary">
- <span class="ref-pill">{ p.DefaultBranch }</span>
- <span>{ strconv.Itoa(p.Branches) } branches</span>
- <span>{ strconv.Itoa(p.Tags) } tags</span>
- </div>
- if p.Last != nil {
- <p class="last-commit">
- <a href={ templ.SafeURL("/r/" + p.Meta.Repo + "/commit/" + p.Last.Hash) } class="commit-id">{ p.Last.Short }</a>
- <span class="commit-title">{ p.Last.Subject }</span>
- <span class="muted">— { p.Last.Author }, { FmtTime(p.Last.When) }</span>
- </p>
- }
- if len(p.Entries) > 0 {
- <table class="tree">
- <tbody>
- for _, e := range p.Entries {
- <tr>
- <td>
- if e.IsDir {
- <a class="tree-dir" href={ templ.SafeURL("/r/" + p.Meta.Repo + "/tree/" + p.DefaultBranch + "/" + e.Path) }>{ e.Name }/</a>
- } else {
- <a class="tree-file" href={ templ.SafeURL("/r/" + p.Meta.Repo + "/blob/" + p.DefaultBranch + "/" + e.Path) }>{ e.Name }</a>
- }
- </td>
- <td class="num">
- if !e.IsDir {
- { HumanSize(e.Size) }
- }
- </td>
- </tr>
- }
- </tbody>
- </table>
- }
- }
-}
-
-templ Readme(p ReadmePage) {
- @Layout(p.Meta) {
- <article class="markdown readme">
- @templ.Raw(p.Readme)
- </article>
- }
-}
-
-templ Tree(p TreePage) {
- @Layout(p.Meta) {
- @crumbs(p.Meta, p.Crumbs)
- <table class="tree">
- <tbody>
- if p.Path != "" {
- <tr><td colspan="2"><a href={ templ.SafeURL(parentTreeURL(p.Meta, p.Path)) }>..</a></td></tr>
- }
- for _, e := range p.Entries {
- <tr>
- <td>
- if e.IsDir {
- <a class="tree-dir" href={ templ.SafeURL("/r/" + p.Meta.Repo + "/tree/" + p.Meta.Ref + "/" + e.Path) }>{ e.Name }/</a>
- } else {
- <a class="tree-file" href={ templ.SafeURL("/r/" + p.Meta.Repo + "/blob/" + p.Meta.Ref + "/" + e.Path) }>{ e.Name }</a>
- }
- </td>
- <td class="num">
- if !e.IsDir {
- { HumanSize(e.Size) }
- }
- </td>
- </tr>
- }
- </tbody>
- </table>
- }
-}
-
-templ Blob(p BlobPage) {
- @Layout(p.Meta) {
- @crumbs(p.Meta, p.Crumbs)
- <div class="blob-bar">
- <span class="muted">{ HumanSize(p.Size) }</span>
- <a href={ templ.SafeURL("/r/" + p.Meta.Repo + "/raw/" + p.Meta.Ref + "/" + p.Path) }>raw</a>
- </div>
- if p.IsBinary {
- <p class="empty">Binary file not shown.</p>
- } else if p.IsMarkdown {
- if len(p.Frontmatter) > 0 {
- <dl class="frontmatter">
- for _, kv := range p.Frontmatter {
- <div class="fm-row">
- <dt>{ kv.Key }</dt>
- <dd>{ kv.Value }</dd>
- </div>
- }
- </dl>
- }
- <article class="markdown">
- @templ.Raw(p.Markdown)
- </article>
- } else {
- <div class="code">
- @templ.Raw(p.Code)
- </div>
- }
- }
-}
-
-templ Log(p LogPage) {
- @Layout(p.Meta) {
- <h1>Commits</h1>
- <ul class="log">
- for _, c := range p.Commits {
- <li>
- <a class="commit-id" href={ templ.SafeURL("/r/" + p.Meta.Repo + "/commit/" + c.Hash) }>{ c.Short }</a>
- <span class="commit-title">{ c.Subject }</span>
- <span class="muted">— { c.Author }, { FmtTime(c.When) }</span>
- </li>
- }
- </ul>
- }
-}
-
-templ Commit(p CommitPage) {
- @Layout(p.Meta) {
- <h1 class="commit-subject">{ p.Detail.Commit.Subject }</h1>
- <p class="muted">
- <code>{ p.Detail.Commit.Hash }</code><br/>
- { p.Detail.Commit.Author } <{ p.Detail.Commit.Email }> · { FmtTime(p.Detail.Commit.When) }
- </p>
- for _, parent := range p.Detail.Parents {
- <p class="muted">parent <a href={ templ.SafeURL("/r/" + p.Meta.Repo + "/commit/" + parent) }><code>{ ShortHash(parent) }</code></a></p>
- }
- if p.Detail.Commit.Message != p.Detail.Commit.Subject {
- <pre class="commit-body">{ p.Detail.Commit.Message }</pre>
- }
- if len(p.Files) == 0 {
- <p class="empty">No changes.</p>
- }
- <p class="muted diff-summary">{ strconv.Itoa(len(p.Files)) } files changed</p>
- for _, f := range p.Files {
- <div class="filediff">
- <div class="filediff-head">
- <span class="filediff-name">{ f.Name }</span>
- <span class="diffstat">
- <span class="add">+{ strconv.Itoa(f.Added) }</span>
- <span class="del">−{ strconv.Itoa(f.Deleted) }</span>
- </span>
- </div>
- if f.Binary {
- <p class="empty">Binary file.</p>
- } else if f.HTML != "" {
- <div class="code diff">
- @templ.Raw(f.HTML)
- </div>
- } else {
- <p class="empty">No textual changes.</p>
- }
- </div>
- }
- }
-}
-
-templ Refs(p RefsPage) {
- @Layout(p.Meta) {
- <h1>Branches</h1>
- <ul class="refs">
- for _, b := range p.Refs.Branches {
- <li>
- <a href={ templ.SafeURL("/r/" + p.Meta.Repo + "/tree/" + b.Name + "/") }>{ b.Name }</a>
- <span class="muted"><code>{ ShortHash(b.Hash) }</code></span>
- </li>
- }
- </ul>
- <h1>Tags</h1>
- if len(p.Refs.Tags) == 0 {
- <p class="empty">No tags.</p>
- }
- <ul class="refs">
- for _, t := range p.Refs.Tags {
- <li>
- <a href={ templ.SafeURL("/r/" + p.Meta.Repo + "/log/" + t.Name) }>{ t.Name }</a>
- <span class="muted"><code>{ ShortHash(t.Hash) }</code></span>
- </li>
- }
- </ul>
- }
-}
-
-templ Issues(p IssuesPage) {
- @Layout(p.Meta) {
- <h1>Issues <span class="muted">({ strconv.Itoa(p.Total) })</span></h1>
- if p.Total == 0 {
- <p class="empty">No issues.</p>
- }
- for _, g := range p.Groups {
- <section class="issue-group">
- <h2 class={ "status", "status--" + StatusKind(g.Status) }>{ g.Status } <span class="muted">{ strconv.Itoa(len(g.Tasks)) }</span></h2>
- <ul class="issue-list">
- for _, t := range g.Tasks {
- <li>
- if t.Type() != "" {
- <span class={ "badge", "badge--" + LabelColor(t.Type()) }>{ t.Type() }</span>
- }
- <a class="issue-link" href={ templ.SafeURL("/r/" + p.Meta.Repo + "/issues/" + t.Key()) }>
- <span class="issue-id">{ t.ID }</span>
- <span class="issue-title">{ t.Title }</span>
- </a>
- if PriorityClass(t.Priority) != "" {
- <span class={ "prio", "prio--" + PriorityClass(t.Priority) }>{ t.Priority }</span>
- }
- for _, l := range t.OtherLabels() {
- <span class={ "chip", "chip--" + LabelColor(l) }>{ l }</span>
- }
- </li>
- }
- </ul>
- </section>
- }
- }
-}
-
-templ Issue(p IssuePage) {
- @Layout(p.Meta) {
- <p class="muted"><a href={ templ.SafeURL("/r/" + p.Meta.Repo + "/issues") }>← issues</a></p>
- <h1 class="issue-detail-title">
- <span class="issue-id">{ p.Task.ID }</span>
- { p.Task.Title }
- </h1>
- <div class="issue-meta">
- <span class={ "status", "status--" + StatusKind(p.Task.Status) }>{ p.Task.Status }</span>
- if p.Task.Type() != "" {
- <span class={ "badge", "badge--" + LabelColor(p.Task.Type()) }>{ p.Task.Type() }</span>
- }
- if PriorityClass(p.Task.Priority) != "" {
- <span class={ "prio", "prio--" + PriorityClass(p.Task.Priority) }>priority: { p.Task.Priority }</span>
- } else if p.Task.Priority != "" {
- <span class="muted">priority: { p.Task.Priority }</span>
- }
- for _, l := range p.Task.OtherLabels() {
- <span class={ "chip", "chip--" + LabelColor(l) }>{ l }</span>
- }
- </div>
- if p.Task.Created != "" || p.Task.Updated != "" {
- <p class="muted issue-dates">
- if p.Task.Created != "" {
- <span>created { p.Task.Created }</span>
- }
- if p.Task.Updated != "" {
- <span>· updated { p.Task.Updated }</span>
- }
- </p>
- }
- if len(p.Task.Deps) > 0 {
- <p class="muted">depends on: { strings.Join(p.Task.Deps, ", ") }</p>
- }
- if p.Body != "" {
- <article class="markdown issue-body">
- @templ.Raw(p.Body)
- </article>
- }
- }
-}
-
-templ Error(m Meta, code int, msg string) {
- @Layout(m) {
- <h1>{ strconv.Itoa(code) }</h1>
- <p>{ msg }</p>
- <p><a href="/">← back to repositories</a></p>
- }
-}
-
-templ crumbs(m Meta, cs []Crumb) {
- <nav class="crumbs">
- <a href={ templ.SafeURL("/r/" + m.Repo + "/tree/" + m.Ref + "/") }>{ m.Repo }</a>
- for _, c := range cs {
- <span class="sep">/</span>
- <a href={ templ.SafeURL("/r/" + m.Repo + "/tree/" + m.Ref + "/" + c.Path) }>{ c.Name }</a>
- }
- </nav>
-}
-
-// pageTitle builds the <title> from page meta.
-func pageTitle(m Meta) string {
- if m.Title != "" {
- return m.Title
- }
- if m.Repo != "" {
- return m.Repo + " · humdrum codex"
- }
- return "humdrum codex"
-}
-
-func refOr(ref string) string {
- if ref == "" {
- return "HEAD"
- }
- return ref
-}
-
-func themeOr(t string) string {
- if t == "" {
- return DefaultTheme
- }
- return t
-}
-
-// themeLabel renders a human-friendly name for the theme picker.
-func themeLabel(t string) string {
- switch t {
- case "flexoki":
- return "Flexoki"
- case "flexoki-dark":
- return "Flexoki Dark"
- case "uchu":
- return "Uchu"
- case "uchu-dark":
- return "Uchu Dark"
- case "humdrum":
- return "Humdrum"
- case "humdrum-dark":
- return "Humdrum Dark"
- case "eink":
- return "E-ink"
- }
- return t
-}
-
-// parentTreeURL returns the tree URL one directory up from path.
-func parentTreeURL(m Meta, path string) string {
- parent := ""
- if i := lastSlash(path); i >= 0 {
- parent = path[:i]
- }
- return fmt.Sprintf("/r/%s/tree/%s/%s", m.Repo, m.Ref, parent)
-}
-
-func lastSlash(s string) int {
- for i := len(s) - 1; i >= 0; i-- {
- if s[i] == '/' {
- return i
- }
- }
- return -1
-}
web/templates/templates_templ.go +0 −2709
@@ -1,2709 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.3.1020
-package templates
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import templruntime "github.com/a-h/templ/runtime"
-
-import (
- "fmt"
- "strconv"
- "strings"
-)
-
-// Layout is the shared page shell. Styling is intentionally minimal in phase 1;
-// the design pass (tokens + Charm chrome) lands in phase 3.
-func Layout(m Meta) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\" data-theme=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var2 string
- templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue(themeOr(m.Theme))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 13, Col: 46}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\"><title>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var3 string
- templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(pageTitle(m))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 17, Col: 24}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</title>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templ.Raw(themeResolveScript).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<link rel=\"stylesheet\" href=\"/static/tokens.css\"><link rel=\"stylesheet\" href=\"/static/custard.css\"><script src=\"/static/theme.js\" defer></script></head><body><header class=\"site-head\"><div class=\"site-head-left\"><a href=\"/\" class=\"brand\">▍ humdrum codex</a> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if m.Repo != "" {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<span class=\"sep\">/</span> <a href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var4 templ.SafeURL
- templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + m.Repo))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 29, Col: 45}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\" class=\"repo-name\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var5 string
- templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(m.Repo)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 29, Col: 74}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</a>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</div>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = themePicker(m).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</header>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if m.Repo != "" {
- templ_7745c5c3_Err = repoTabs(m).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<main class=\"content\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</main><footer class=\"site-foot\"><span class=\"foot-brand\">▍ served by custard</span> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if m.Repo != "" {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<span class=\"foot-item\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var6 string
- templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(m.Repo)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 43, Col: 37}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</span> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if m.Ref != "" {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<span class=\"foot-item ref-pill\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var7 string
- templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(m.Ref)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 45, Col: 46}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</span>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if m.CloneURL != "" {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<span class=\"foot-item foot-clone\">clone <code>git clone ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var8 string
- templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(m.CloneURL)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 48, Col: 75}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</code></span>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</footer></body></html>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-func themePicker(m Meta) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var9 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var9 == nil {
- templ_7745c5c3_Var9 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<select id=\"theme-select\" aria-label=\"Theme\" class=\"theme-select\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, t := range Families {
- if t == themeOr(m.Theme) {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<option value=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var10 string
- templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.ResolveAttributeValue(t)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 60, Col: 21}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" selected>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var11 string
- templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(themeLabel(t))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 60, Col: 48}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</option>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- } else {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<option value=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var12 string
- templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.ResolveAttributeValue(t)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 62, Col: 21}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var12)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var13 string
- templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(themeLabel(t))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 62, Col: 39}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</option>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</select>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-// themeResolveScript runs synchronously in <head> before paint: it reads the
-// family cookie and the OS color scheme, then sets the concrete data-theme
-// (family or family-dark; eink is fixed). This is the flash-free path for the
-// system-driven light/dark axis the server can't see.
-const themeResolveScript = `<script>(function(){try{` +
- `var m=document.cookie.match(/(?:^|; )theme=([^;]+)/);` +
- `var f=m?decodeURIComponent(m[1]):'flexoki';` +
- `if(['flexoki','uchu','humdrum','eink'].indexOf(f)<0)f='flexoki';` +
- `var d=window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches;` +
- `document.documentElement.setAttribute('data-theme',f==='eink'?'eink':(d?f+'-dark':f));` +
- `}catch(e){}})();</script>`
-
-func repoTabs(m Meta) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var14 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var14 == nil {
- templ_7745c5c3_Var14 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<nav class=\"repo-tabs\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if m.HasReadme {
- var templ_7745c5c3_Var15 = []any{templ.KV("active", m.Tab == "readme")}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var15...)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<a href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var16 templ.SafeURL
- templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + m.Repo + "/readme"))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 83, Col: 54}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\" class=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var17 string
- templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var15).String())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 1, Col: 0}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var17)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\">readme</a> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- var templ_7745c5c3_Var18 = []any{templ.KV("active", m.Tab == "code")}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var18...)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<a href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var19 templ.SafeURL
- templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + m.Repo + "/files"))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 85, Col: 52}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\" class=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var20 string
- templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var18).String())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 1, Col: 0}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var20)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "\">code</a> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var21 = []any{templ.KV("active", m.Tab == "log")}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var21...)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<a href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var22 templ.SafeURL
- templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + m.Repo + "/log/" + refOr(m.Ref)))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 86, Col: 66}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "\" class=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var23 string
- templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var21).String())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 1, Col: 0}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var23)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "\">log</a> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var24 = []any{templ.KV("active", m.Tab == "refs")}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var24...)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<a href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var25 templ.SafeURL
- templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + m.Repo + "/refs"))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 87, Col: 51}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "\" class=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var26 string
- templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var24).String())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 1, Col: 0}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var26)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "\">refs</a> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if m.HasIssues {
- var templ_7745c5c3_Var27 = []any{templ.KV("active", m.Tab == "issues")}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var27...)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<a href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var28 templ.SafeURL
- templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + m.Repo + "/issues"))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 89, Col: 54}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "\" class=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var29 string
- templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var27).String())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 1, Col: 0}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var29)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "\">issues</a>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</nav>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-func Index(p IndexPage) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var30 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var30 == nil {
- templ_7745c5c3_Var30 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var31 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<h1>Repositories</h1>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if len(p.Repos) == 0 {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "<p class=\"empty\">No repositories found.</p>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, " <ul class=\"repo-list\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, r := range p.Repos {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "<li><a class=\"repo-link\" href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var32 templ.SafeURL
- templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + r.Name))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 103, Col: 62}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var33 string
- templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(r.Name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 103, Col: 73}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</a> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if r.Description != "" {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<span class=\"repo-desc\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var34 string
- templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(r.Description)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 105, Col: 45}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</span> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- if r.Last != nil {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "<span class=\"repo-meta\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var35 string
- templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(r.Last.Subject)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 108, Col: 46}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, " · ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var36 string
- templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(FmtTime(r.Last.When))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 108, Col: 74}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "</span>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "</li>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "</ul>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
- templ_7745c5c3_Err = Layout(p.Meta).Render(templ.WithChildren(ctx, templ_7745c5c3_Var31), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-func Repo(p RepoPage) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var37 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var37 == nil {
- templ_7745c5c3_Var37 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var38 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "<div class=\"repo-summary\"><span class=\"ref-pill\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var39 string
- templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(p.DefaultBranch)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 119, Col: 43}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "</span> <span>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var40 string
- templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(p.Branches))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 120, Col: 35}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, " branches</span> <span>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var41 string
- templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(p.Tags))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 121, Col: 31}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, " tags</span></div>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if p.Last != nil {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "<p class=\"last-commit\"><a href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var42 templ.SafeURL
- templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + p.Meta.Repo + "/commit/" + p.Last.Hash))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 125, Col: 75}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "\" class=\"commit-id\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var43 string
- templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(p.Last.Short)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 125, Col: 110}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "</a> <span class=\"commit-title\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var44 string
- templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(p.Last.Subject)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 126, Col: 47}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "</span> <span class=\"muted\">— ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var45 string
- templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(p.Last.Author)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 127, Col: 43}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, ", ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var46 string
- templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(FmtTime(p.Last.When))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 127, Col: 69}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "</span></p>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if len(p.Entries) > 0 {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "<table class=\"tree\"><tbody>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, e := range p.Entries {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "<tr><td>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if e.IsDir {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "<a class=\"tree-dir\" href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var47 templ.SafeURL
- templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + p.Meta.Repo + "/tree/" + p.DefaultBranch + "/" + e.Path))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 137, Col: 114}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var48 string
- templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(e.Name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 137, Col: 125}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "/</a>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- } else {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "<a class=\"tree-file\" href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var49 templ.SafeURL
- templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + p.Meta.Repo + "/blob/" + p.DefaultBranch + "/" + e.Path))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 139, Col: 115}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var50 string
- templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.JoinStringErrs(e.Name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 139, Col: 126}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var50))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "</a>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "</td><td class=\"num\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if !e.IsDir {
- var templ_7745c5c3_Var51 string
- templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(HumanSize(e.Size))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 144, Col: 28}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "</td></tr>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "</tbody></table>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- return nil
- })
- templ_7745c5c3_Err = Layout(p.Meta).Render(templ.WithChildren(ctx, templ_7745c5c3_Var38), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-func Readme(p ReadmePage) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var52 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var52 == nil {
- templ_7745c5c3_Var52 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var53 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "<article class=\"markdown readme\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templ.Raw(p.Readme).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "</article>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
- templ_7745c5c3_Err = Layout(p.Meta).Render(templ.WithChildren(ctx, templ_7745c5c3_Var53), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-func Tree(p TreePage) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var54 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var54 == nil {
- templ_7745c5c3_Var54 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var55 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = crumbs(p.Meta, p.Crumbs).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, " <table class=\"tree\"><tbody>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if p.Path != "" {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "<tr><td colspan=\"2\"><a href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var56 templ.SafeURL
- templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(parentTreeURL(p.Meta, p.Path)))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 169, Col: 79}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "\">..</a></td></tr>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- for _, e := range p.Entries {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "<tr><td>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if e.IsDir {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "<a class=\"tree-dir\" href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var57 templ.SafeURL
- templ_7745c5c3_Var57, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + p.Meta.Repo + "/tree/" + p.Meta.Ref + "/" + e.Path))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 175, Col: 108}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var57))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var58 string
- templ_7745c5c3_Var58, templ_7745c5c3_Err = templ.JoinStringErrs(e.Name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 175, Col: 119}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var58))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "/</a>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- } else {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "<a class=\"tree-file\" href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var59 templ.SafeURL
- templ_7745c5c3_Var59, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + p.Meta.Repo + "/blob/" + p.Meta.Ref + "/" + e.Path))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 177, Col: 109}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var59))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var60 string
- templ_7745c5c3_Var60, templ_7745c5c3_Err = templ.JoinStringErrs(e.Name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 177, Col: 120}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var60))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "</a>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, "</td><td class=\"num\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if !e.IsDir {
- var templ_7745c5c3_Var61 string
- templ_7745c5c3_Var61, templ_7745c5c3_Err = templ.JoinStringErrs(HumanSize(e.Size))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 182, Col: 27}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var61))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, "</td></tr>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "</tbody></table>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
- templ_7745c5c3_Err = Layout(p.Meta).Render(templ.WithChildren(ctx, templ_7745c5c3_Var55), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-func Blob(p BlobPage) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var62 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var62 == nil {
- templ_7745c5c3_Var62 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var63 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = crumbs(p.Meta, p.Crumbs).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, " <div class=\"blob-bar\"><span class=\"muted\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var64 string
- templ_7745c5c3_Var64, templ_7745c5c3_Err = templ.JoinStringErrs(HumanSize(p.Size))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 196, Col: 42}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var64))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "</span> <a href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var65 templ.SafeURL
- templ_7745c5c3_Var65, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + p.Meta.Repo + "/raw/" + p.Meta.Ref + "/" + p.Path))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 197, Col: 85}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var65))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, "\">raw</a></div>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if p.IsBinary {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, "<p class=\"empty\">Binary file not shown.</p>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- } else if p.IsMarkdown {
- if len(p.Frontmatter) > 0 {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 99, "<dl class=\"frontmatter\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, kv := range p.Frontmatter {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 100, "<div class=\"fm-row\"><dt>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var66 string
- templ_7745c5c3_Var66, templ_7745c5c3_Err = templ.JoinStringErrs(kv.Key)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 206, Col: 19}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var66))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 101, "</dt><dd>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var67 string
- templ_7745c5c3_Var67, templ_7745c5c3_Err = templ.JoinStringErrs(kv.Value)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 207, Col: 21}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var67))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 102, "</dd></div>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 103, "</dl>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 104, " <article class=\"markdown\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templ.Raw(p.Markdown).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 105, "</article>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- } else {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 106, "<div class=\"code\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templ.Raw(p.Code).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 107, "</div>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- return nil
- })
- templ_7745c5c3_Err = Layout(p.Meta).Render(templ.WithChildren(ctx, templ_7745c5c3_Var63), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-func Log(p LogPage) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var68 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var68 == nil {
- templ_7745c5c3_Var68 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var69 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 108, "<h1>Commits</h1><ul class=\"log\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, c := range p.Commits {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 109, "<li><a class=\"commit-id\" href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var70 templ.SafeURL
- templ_7745c5c3_Var70, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + p.Meta.Repo + "/commit/" + c.Hash))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 229, Col: 89}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var70))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 110, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var71 string
- templ_7745c5c3_Var71, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 229, Col: 101}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var71))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 111, "</a> <span class=\"commit-title\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var72 string
- templ_7745c5c3_Var72, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 230, Col: 43}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var72))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 112, "</span> <span class=\"muted\">— ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var73 string
- templ_7745c5c3_Var73, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 231, Col: 39}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var73))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 113, ", ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var74 string
- templ_7745c5c3_Var74, templ_7745c5c3_Err = templ.JoinStringErrs(FmtTime(c.When))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 231, Col: 60}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var74))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 114, "</span></li>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 115, "</ul>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
- templ_7745c5c3_Err = Layout(p.Meta).Render(templ.WithChildren(ctx, templ_7745c5c3_Var69), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-func Commit(p CommitPage) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var75 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var75 == nil {
- templ_7745c5c3_Var75 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var76 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 116, "<h1 class=\"commit-subject\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var77 string
- templ_7745c5c3_Var77, templ_7745c5c3_Err = templ.JoinStringErrs(p.Detail.Commit.Subject)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 240, Col: 54}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var77))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 117, "</h1><p class=\"muted\"><code>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var78 string
- templ_7745c5c3_Var78, templ_7745c5c3_Err = templ.JoinStringErrs(p.Detail.Commit.Hash)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 242, Col: 31}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var78))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 118, "</code><br>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var79 string
- templ_7745c5c3_Var79, templ_7745c5c3_Err = templ.JoinStringErrs(p.Detail.Commit.Author)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 243, Col: 27}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var79))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 119, " <")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var80 string
- templ_7745c5c3_Var80, templ_7745c5c3_Err = templ.JoinStringErrs(p.Detail.Commit.Email)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 243, Col: 57}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var80))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 120, "> · ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var81 string
- templ_7745c5c3_Var81, templ_7745c5c3_Err = templ.JoinStringErrs(FmtTime(p.Detail.Commit.When))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 243, Col: 98}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var81))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 121, "</p>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, parent := range p.Detail.Parents {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 122, "<p class=\"muted\">parent <a href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var82 templ.SafeURL
- templ_7745c5c3_Var82, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + p.Meta.Repo + "/commit/" + parent))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 246, Col: 93}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var82))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 123, "\"><code>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var83 string
- templ_7745c5c3_Var83, templ_7745c5c3_Err = templ.JoinStringErrs(ShortHash(parent))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 246, Col: 121}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var83))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 124, "</code></a></p>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 125, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if p.Detail.Commit.Message != p.Detail.Commit.Subject {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 126, "<pre class=\"commit-body\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var84 string
- templ_7745c5c3_Var84, templ_7745c5c3_Err = templ.JoinStringErrs(p.Detail.Commit.Message)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 249, Col: 53}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var84))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 127, "</pre>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 128, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if len(p.Files) == 0 {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 129, "<p class=\"empty\">No changes.</p>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 130, " <p class=\"muted diff-summary\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var85 string
- templ_7745c5c3_Var85, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(len(p.Files)))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 254, Col: 60}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var85))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 131, " files changed</p>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, f := range p.Files {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 132, "<div class=\"filediff\"><div class=\"filediff-head\"><span class=\"filediff-name\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var86 string
- templ_7745c5c3_Var86, templ_7745c5c3_Err = templ.JoinStringErrs(f.Name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 258, Col: 41}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var86))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 133, "</span> <span class=\"diffstat\"><span class=\"add\">+")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var87 string
- templ_7745c5c3_Var87, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(f.Added))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 260, Col: 48}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var87))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 134, "</span> <span class=\"del\">−")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var88 string
- templ_7745c5c3_Var88, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(f.Deleted))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 261, Col: 52}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var88))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 135, "</span></span></div>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if f.Binary {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 136, "<p class=\"empty\">Binary file.</p>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- } else if f.HTML != "" {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 137, "<div class=\"code diff\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templ.Raw(f.HTML).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 138, "</div>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- } else {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 139, "<p class=\"empty\">No textual changes.</p>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 140, "</div>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- return nil
- })
- templ_7745c5c3_Err = Layout(p.Meta).Render(templ.WithChildren(ctx, templ_7745c5c3_Var76), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-func Refs(p RefsPage) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var89 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var89 == nil {
- templ_7745c5c3_Var89 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var90 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 141, "<h1>Branches</h1><ul class=\"refs\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, b := range p.Refs.Branches {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 142, "<li><a href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var91 templ.SafeURL
- templ_7745c5c3_Var91, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + p.Meta.Repo + "/tree/" + b.Name + "/"))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 284, Col: 75}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var91))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 143, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var92 string
- templ_7745c5c3_Var92, templ_7745c5c3_Err = templ.JoinStringErrs(b.Name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 284, Col: 86}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var92))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 144, "</a> <span class=\"muted\"><code>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var93 string
- templ_7745c5c3_Var93, templ_7745c5c3_Err = templ.JoinStringErrs(ShortHash(b.Hash))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 285, Col: 50}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var93))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 145, "</code></span></li>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 146, "</ul><h1>Tags</h1>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if len(p.Refs.Tags) == 0 {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 147, "<p class=\"empty\">No tags.</p>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 148, " <ul class=\"refs\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, t := range p.Refs.Tags {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 149, "<li><a href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var94 templ.SafeURL
- templ_7745c5c3_Var94, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + p.Meta.Repo + "/log/" + t.Name))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 296, Col: 68}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var94))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 150, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var95 string
- templ_7745c5c3_Var95, templ_7745c5c3_Err = templ.JoinStringErrs(t.Name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 296, Col: 79}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var95))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 151, "</a> <span class=\"muted\"><code>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var96 string
- templ_7745c5c3_Var96, templ_7745c5c3_Err = templ.JoinStringErrs(ShortHash(t.Hash))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 297, Col: 50}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var96))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 152, "</code></span></li>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 153, "</ul>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
- templ_7745c5c3_Err = Layout(p.Meta).Render(templ.WithChildren(ctx, templ_7745c5c3_Var90), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-func Issues(p IssuesPage) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var97 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var97 == nil {
- templ_7745c5c3_Var97 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var98 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 154, "<h1>Issues <span class=\"muted\">(")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var99 string
- templ_7745c5c3_Var99, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(p.Total))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 306, Col: 57}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var99))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 155, ")</span></h1>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if p.Total == 0 {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 156, "<p class=\"empty\">No issues.</p>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- for _, g := range p.Groups {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 157, "<section class=\"issue-group\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var100 = []any{"status", "status--" + StatusKind(g.Status)}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var100...)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 158, "<h2 class=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var101 string
- templ_7745c5c3_Var101, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var100).String())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 1, Col: 0}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var101)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 159, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var102 string
- templ_7745c5c3_Var102, templ_7745c5c3_Err = templ.JoinStringErrs(g.Status)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 312, Col: 72}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var102))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 160, " <span class=\"muted\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var103 string
- templ_7745c5c3_Var103, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(len(g.Tasks)))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 312, Col: 123}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var103))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 161, "</span></h2><ul class=\"issue-list\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, t := range g.Tasks {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 162, "<li>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if t.Type() != "" {
- var templ_7745c5c3_Var104 = []any{"badge", "badge--" + LabelColor(t.Type())}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var104...)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 163, "<span class=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var105 string
- templ_7745c5c3_Var105, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var104).String())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 1, Col: 0}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var105)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 164, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var106 string
- templ_7745c5c3_Var106, templ_7745c5c3_Err = templ.JoinStringErrs(t.Type())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 317, Col: 76}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var106))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 165, "</span> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 166, "<a class=\"issue-link\" href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var107 templ.SafeURL
- templ_7745c5c3_Var107, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + p.Meta.Repo + "/issues/" + t.Key()))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 319, Col: 93}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var107))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 167, "\"><span class=\"issue-id\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var108 string
- templ_7745c5c3_Var108, templ_7745c5c3_Err = templ.JoinStringErrs(t.ID)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 320, Col: 37}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var108))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 168, "</span> <span class=\"issue-title\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var109 string
- templ_7745c5c3_Var109, templ_7745c5c3_Err = templ.JoinStringErrs(t.Title)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 321, Col: 43}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var109))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 169, "</span></a> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if PriorityClass(t.Priority) != "" {
- var templ_7745c5c3_Var110 = []any{"prio", "prio--" + PriorityClass(t.Priority)}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var110...)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 170, "<span class=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var111 string
- templ_7745c5c3_Var111, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var110).String())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 1, Col: 0}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var111)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 171, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var112 string
- templ_7745c5c3_Var112, templ_7745c5c3_Err = templ.JoinStringErrs(t.Priority)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 324, Col: 81}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var112))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 172, "</span> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- for _, l := range t.OtherLabels() {
- var templ_7745c5c3_Var113 = []any{"chip", "chip--" + LabelColor(l)}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var113...)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 173, "<span class=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var114 string
- templ_7745c5c3_Var114, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var113).String())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 1, Col: 0}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var114)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 174, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var115 string
- templ_7745c5c3_Var115, templ_7745c5c3_Err = templ.JoinStringErrs(l)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 327, Col: 60}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var115))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 175, "</span>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 176, "</li>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 177, "</ul></section>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- return nil
- })
- templ_7745c5c3_Err = Layout(p.Meta).Render(templ.WithChildren(ctx, templ_7745c5c3_Var98), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-func Issue(p IssuePage) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var116 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var116 == nil {
- templ_7745c5c3_Var116 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var117 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 178, "<p class=\"muted\"><a href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var118 templ.SafeURL
- templ_7745c5c3_Var118, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + p.Meta.Repo + "/issues"))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 339, Col: 75}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var118))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 179, "\">← issues</a></p><h1 class=\"issue-detail-title\"><span class=\"issue-id\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var119 string
- templ_7745c5c3_Var119, templ_7745c5c3_Err = templ.JoinStringErrs(p.Task.ID)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 341, Col: 37}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var119))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 180, "</span> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var120 string
- templ_7745c5c3_Var120, templ_7745c5c3_Err = templ.JoinStringErrs(p.Task.Title)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 342, Col: 17}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var120))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 181, "</h1><div class=\"issue-meta\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var121 = []any{"status", "status--" + StatusKind(p.Task.Status)}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var121...)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 182, "<span class=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var122 string
- templ_7745c5c3_Var122, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var121).String())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 1, Col: 0}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var122)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 183, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var123 string
- templ_7745c5c3_Var123, templ_7745c5c3_Err = templ.JoinStringErrs(p.Task.Status)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 345, Col: 83}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var123))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 184, "</span> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if p.Task.Type() != "" {
- var templ_7745c5c3_Var124 = []any{"badge", "badge--" + LabelColor(p.Task.Type())}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var124...)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 185, "<span class=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var125 string
- templ_7745c5c3_Var125, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var124).String())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 1, Col: 0}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var125)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 186, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var126 string
- templ_7745c5c3_Var126, templ_7745c5c3_Err = templ.JoinStringErrs(p.Task.Type())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 347, Col: 82}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var126))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 187, "</span> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- if PriorityClass(p.Task.Priority) != "" {
- var templ_7745c5c3_Var127 = []any{"prio", "prio--" + PriorityClass(p.Task.Priority)}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var127...)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 188, "<span class=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var128 string
- templ_7745c5c3_Var128, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var127).String())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 1, Col: 0}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var128)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 189, "\">priority: ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var129 string
- templ_7745c5c3_Var129, templ_7745c5c3_Err = templ.JoinStringErrs(p.Task.Priority)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 350, Col: 97}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var129))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 190, "</span> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- } else if p.Task.Priority != "" {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 191, "<span class=\"muted\">priority: ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var130 string
- templ_7745c5c3_Var130, templ_7745c5c3_Err = templ.JoinStringErrs(p.Task.Priority)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 352, Col: 51}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var130))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 192, "</span> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- for _, l := range p.Task.OtherLabels() {
- var templ_7745c5c3_Var131 = []any{"chip", "chip--" + LabelColor(l)}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var131...)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 193, "<span class=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var132 string
- templ_7745c5c3_Var132, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var131).String())
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 1, Col: 0}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var132)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 194, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var133 string
- templ_7745c5c3_Var133, templ_7745c5c3_Err = templ.JoinStringErrs(l)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 355, Col: 56}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var133))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 195, "</span>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 196, "</div>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if p.Task.Created != "" || p.Task.Updated != "" {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 197, "<p class=\"muted issue-dates\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if p.Task.Created != "" {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 198, "<span>created ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var134 string
- templ_7745c5c3_Var134, templ_7745c5c3_Err = templ.JoinStringErrs(p.Task.Created)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 361, Col: 35}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var134))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 199, "</span> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- if p.Task.Updated != "" {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 200, "<span>· updated ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var135 string
- templ_7745c5c3_Var135, templ_7745c5c3_Err = templ.JoinStringErrs(p.Task.Updated)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 364, Col: 38}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var135))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 201, "</span>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 202, "</p>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 203, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if len(p.Task.Deps) > 0 {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 204, "<p class=\"muted\">depends on: ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var136 string
- templ_7745c5c3_Var136, templ_7745c5c3_Err = templ.JoinStringErrs(strings.Join(p.Task.Deps, ", "))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 369, Col: 65}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var136))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 205, "</p>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 206, " ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if p.Body != "" {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 207, "<article class=\"markdown issue-body\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templ.Raw(p.Body).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 208, "</article>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- return nil
- })
- templ_7745c5c3_Err = Layout(p.Meta).Render(templ.WithChildren(ctx, templ_7745c5c3_Var117), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-func Error(m Meta, code int, msg string) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var137 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var137 == nil {
- templ_7745c5c3_Var137 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var138 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 209, "<h1>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var139 string
- templ_7745c5c3_Var139, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(code))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 381, Col: 26}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var139))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 210, "</h1><p>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var140 string
- templ_7745c5c3_Var140, templ_7745c5c3_Err = templ.JoinStringErrs(msg)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 382, Col: 10}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var140))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 211, "</p><p><a href=\"/\">← back to repositories</a></p>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
- templ_7745c5c3_Err = Layout(m).Render(templ.WithChildren(ctx, templ_7745c5c3_Var138), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-func crumbs(m Meta, cs []Crumb) templ.Component {
- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
- return templ_7745c5c3_CtxErr
- }
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
- if !templ_7745c5c3_IsBuffer {
- defer func() {
- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err == nil {
- templ_7745c5c3_Err = templ_7745c5c3_BufErr
- }
- }()
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var141 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var141 == nil {
- templ_7745c5c3_Var141 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 212, "<nav class=\"crumbs\"><a href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var142 templ.SafeURL
- templ_7745c5c3_Var142, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + m.Repo + "/tree/" + m.Ref + "/"))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 389, Col: 66}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var142))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 213, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var143 string
- templ_7745c5c3_Var143, templ_7745c5c3_Err = templ.JoinStringErrs(m.Repo)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 389, Col: 77}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var143))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 214, "</a> ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, c := range cs {
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 215, "<span class=\"sep\">/</span> <a href=\"")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var144 templ.SafeURL
- templ_7745c5c3_Var144, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/r/" + m.Repo + "/tree/" + m.Ref + "/" + c.Path))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 392, Col: 76}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var144))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 216, "\">")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var145 string
- templ_7745c5c3_Var145, templ_7745c5c3_Err = templ.JoinStringErrs(c.Name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/templates.templ`, Line: 392, Col: 87}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var145))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 217, "</a>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 218, "</nav>")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- return nil
- })
-}
-
-// pageTitle builds the <title> from page meta.
-func pageTitle(m Meta) string {
- if m.Title != "" {
- return m.Title
- }
- if m.Repo != "" {
- return m.Repo + " · humdrum codex"
- }
- return "humdrum codex"
-}
-
-func refOr(ref string) string {
- if ref == "" {
- return "HEAD"
- }
- return ref
-}
-
-func themeOr(t string) string {
- if t == "" {
- return DefaultTheme
- }
- return t
-}
-
-// themeLabel renders a human-friendly name for the theme picker.
-func themeLabel(t string) string {
- switch t {
- case "flexoki":
- return "Flexoki"
- case "flexoki-dark":
- return "Flexoki Dark"
- case "uchu":
- return "Uchu"
- case "uchu-dark":
- return "Uchu Dark"
- case "humdrum":
- return "Humdrum"
- case "humdrum-dark":
- return "Humdrum Dark"
- case "eink":
- return "E-ink"
- }
- return t
-}
-
-// parentTreeURL returns the tree URL one directory up from path.
-func parentTreeURL(m Meta, path string) string {
- parent := ""
- if i := lastSlash(path); i >= 0 {
- parent = path[:i]
- }
- return fmt.Sprintf("/r/%s/tree/%s/%s", m.Repo, m.Ref, parent)
-}
-
-func lastSlash(s string) int {
- for i := len(s) - 1; i >= 0; i-- {
- if s[i] == '/' {
- return i
- }
- }
- return -1
-}
-
-var _ = templruntime.GeneratedTemplate
web/templates/view.go +0 −285
@@ -1,285 +0,0 @@
-// Package templates holds the templ components and their view-models. View-models
-// are plain structs assembled by the server layer; templ files render them.
-package templates
-
-import (
- "fmt"
- "html/template"
- "strings"
- "time"
-
- "git.kortum.world/custard/internal/backlog"
- "git.kortum.world/custard/internal/gitread"
- "git.kortum.world/custard/internal/render"
-)
-
-// Meta is the shared chrome data every page needs.
-type Meta struct {
- Title string
- Repo string // current repo name, "" on the index
- Ref string // current ref, when relevant
- HasIssues bool // show the issues tab only when backlog tasks exist
- HasReadme bool // show the readme tab only when a README exists
- Theme string // active data-theme, resolved from cookie (default flexoki)
- Tab string // active repo tab: code | readme | log | refs | issues
- CloneURL string // read-only HTTP clone URL, shown in the footer bar
-}
-
-// Families is what the picker offers: a palette choice only. Light vs dark is
-// resolved from the OS (prefers-color-scheme), not chosen here. E-ink is its own
-// fixed mode. The cookie stores one of these; the client resolves the actual
-// data-theme (e.g. flexoki → flexoki-dark) before paint.
-var Families = []string{"flexoki", "uchu", "humdrum", "eink"}
-
-// DefaultTheme (family) applies when no valid cookie is present.
-const DefaultTheme = "flexoki"
-
-// ValidTheme returns t if it is a known family, else DefaultTheme.
-func ValidTheme(t string) string {
- for _, k := range Families {
- if k == t {
- return t
- }
- }
- return DefaultTheme
-}
-
-// Crumb is one breadcrumb segment with the cumulative path up to it.
-type Crumb struct {
- Name string
- Path string
-}
-
-// IndexPage lists all repositories.
-type IndexPage struct {
- Meta Meta
- Repos []gitread.Repo
-}
-
-// RepoPage is the repo home (code tab): the root file tree plus a ref summary.
-// The README lives on its own tab, not here.
-type RepoPage struct {
- Meta Meta
- DefaultBranch string
- Entries []gitread.Entry
- Last *gitread.Commit
- Branches int
- Tags int
-}
-
-// ReadmePage renders a repo's README on its own tab.
-type ReadmePage struct {
- Meta Meta
- Readme template.HTML
-}
-
-// TreePage browses a directory at a ref.
-type TreePage struct {
- Meta Meta
- Path string
- Crumbs []Crumb
- Entries []gitread.Entry
-}
-
-// BlobPage shows one file.
-type BlobPage struct {
- Meta Meta
- Path string
- Crumbs []Crumb
- Size int64
- IsMarkdown bool
- Markdown template.HTML
- Frontmatter []render.FMPair // YAML frontmatter of a Markdown file, if any
- Code template.HTML
- IsBinary bool
-}
-
-// LogPage is a commit list for a ref.
-type LogPage struct {
- Meta Meta
- Commits []gitread.Commit
-}
-
-// CommitPage shows a single commit and its diff, split per file.
-type CommitPage struct {
- Meta Meta
- Detail *gitread.CommitDetail
- Files []render.FileDiff
-}
-
-// RefsPage lists branches and tags.
-type RefsPage struct {
- Meta Meta
- Refs *gitread.Refs
-}
-
-// IssueGroup is a status bucket of tasks in the issues list.
-type IssueGroup struct {
- Status string
- Tasks []backlog.Task
-}
-
-// IssuesPage is the GitHub-issues-style list, grouped by status.
-type IssuesPage struct {
- Meta Meta
- Groups []IssueGroup
- Total int
-}
-
-// IssuePage is a single task with its rendered Markdown body.
-type IssuePage struct {
- Meta Meta
- Task backlog.Task
- Body template.HTML
-}
-
-// GroupByStatus buckets tasks following the repo's configured status order,
-// appending any statuses not in that order at the end. Empty buckets are
-// dropped. An empty order falls back to the Backlog.md defaults.
-func GroupByStatus(tasks []backlog.Task, order []string) []IssueGroup {
- if len(order) == 0 {
- order = backlog.DefaultStatuses
- }
- known := make(map[string]bool, len(order))
- for _, s := range order {
- known[s] = true
- }
- byStatus := map[string][]backlog.Task{}
- var extra []string
- for _, t := range tasks {
- if _, seen := byStatus[t.Status]; !seen && !known[t.Status] {
- extra = append(extra, t.Status)
- }
- byStatus[t.Status] = append(byStatus[t.Status], t)
- }
- var groups []IssueGroup
- for _, s := range append(append([]string{}, order...), extra...) {
- if ts := byStatus[s]; len(ts) > 0 {
- groups = append(groups, IssueGroup{Status: s, Tasks: ts})
- }
- }
- return groups
-}
-
-// LabelColor maps a label to a theme color-family name (phase-3 tokens key off
-// these via .chip--<color>). Unknown labels get the neutral accent.
-func LabelColor(label string) string {
- switch strings.ToLower(label) {
- case "bug":
- return "red"
- case "feature":
- return "green"
- case "enhancement", "ui":
- return "blue"
- case "docs", "documentation":
- return "cyan"
- case "chore", "refactor":
- return "purple"
- case "question":
- return "yellow"
- default:
- return "accent"
- }
-}
-
-// PriorityClass maps a priority to a css-class-safe level (high/medium/low),
-// or "" when absent so the template can skip the pill.
-func PriorityClass(p string) string {
- switch strings.ToLower(strings.TrimSpace(p)) {
- case "high", "critical", "urgent":
- return "high"
- case "medium", "med", "normal":
- return "medium"
- case "low", "minor":
- return "low"
- default:
- return ""
- }
-}
-
-// StatusKind maps an arbitrary status label (emoji and all) to a stable
-// css-class-safe kind, so themes can style columns regardless of the exact
-// wording a repo uses. Unrecognized statuses fall back to an alnum slug.
-func StatusKind(status string) string {
- s := strings.ToLower(status)
- switch {
- case strings.Contains(s, "progress"):
- return "in-progress"
- case strings.Contains(s, "done"), strings.Contains(s, "ship"), strings.Contains(s, "complete"):
- return "done"
- case strings.Contains(s, "paus"), strings.Contains(s, "block"), strings.Contains(s, "hold"):
- return "paused"
- case strings.Contains(s, "backlog"), strings.Contains(s, "to do"), strings.Contains(s, "todo"):
- return "backlog"
- }
- return slugify(s)
-}
-
-// slugify reduces a string to lowercase alphanumerics joined by single dashes.
-func slugify(s string) string {
- var b strings.Builder
- dash := false
- for _, r := range strings.ToLower(s) {
- switch {
- case r >= 'a' && r <= 'z', r >= '0' && r <= '9':
- if dash && b.Len() > 0 {
- b.WriteByte('-')
- }
- b.WriteRune(r)
- dash = false
- default:
- dash = true
- }
- }
- if b.Len() == 0 {
- return "other"
- }
- return b.String()
-}
-
-// HumanSize formats a byte count as a short human-readable string.
-func HumanSize(n int64) string {
- const unit = 1024
- if n < unit {
- return fmt.Sprintf("%d B", n)
- }
- div, exp := int64(unit), 0
- for m := n / unit; m >= unit; m /= unit {
- div *= unit
- exp++
- }
- return fmt.Sprintf("%.1f %cB", float64(n)/float64(div), "KMGT"[exp])
-}
-
-// ShortHash returns the first 8 characters of a hash, or the whole thing.
-func ShortHash(h string) string {
- if len(h) >= 8 {
- return h[:8]
- }
- return h
-}
-
-// FmtTime renders a timestamp in a compact, stable form.
-func FmtTime(t time.Time) string {
- return t.Format("2006-01-02 15:04")
-}
-
-// BuildCrumbs splits a "/"-separated path into cumulative breadcrumbs.
-func BuildCrumbs(path string) []Crumb {
- path = strings.Trim(path, "/")
- if path == "" {
- return nil
- }
- parts := strings.Split(path, "/")
- crumbs := make([]Crumb, 0, len(parts))
- acc := ""
- for _, p := range parts {
- if acc == "" {
- acc = p
- } else {
- acc = acc + "/" + p
- }
- crumbs = append(crumbs, Crumb{Name: p, Path: acc})
- }
- return crumbs
-}