summaryrefslogtreecommitdiff
path: root/vrsite/css.go
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-08-02 23:43:08 -0300
committerGabriel Schneider <[email protected]>2026-08-03 09:54:39 -0300
commit5ec72f8723d795d0b02d7e9ebb9f555f0c7e6a2e (patch)
treef12f4b700fff4ff6f7e818d2a542672ba0b8d76e /vrsite/css.go
parenta9263daee9413c5eff3c1f1bebcd224442fd8685 (diff)
downloadnotevi-5ec72f8723d795d0b02d7e9ebb9f555f0c7e6a2e.tar.gz
notevi-5ec72f8723d795d0b02d7e9ebb9f555f0c7e6a2e.zip
rebrand to notevi: one CLI over the jj sidecar
vr and vrsite become a single binary. vrsite/ folds into a web package in one module (0x4200.cafe/notevi); "notevi web" serves and exports exactly what vrsite did, and "notevi read/grep/note/query" is unchanged. The sidecar is renamed with it: notevi_log, notevi-log.jsonl, and the description "private: notevi log". The pre-rebrand names are still recognized, so an old repository opens and reads; it is renamed in place on the first write, or up front with "notevi migrate DIR...". That rename cannot be a single mv inside jj run. jj only auto-tracks a *new* file in the run working copy below a size limit it does not take from the command line, so writing a whole log under a name the change has never held is silently dropped while jj reports success. ensureLogFile creates the file empty first and lets every later byte be a modification of a tracked file, which snapshots at any size; that also fixes the same latent bug when importing a large legacy vr-log.jsonl. Adds a bem-te-vi mark (favicon and nav brand) and a README. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Diffstat (limited to 'vrsite/css.go')
-rw-r--r--vrsite/css.go157
1 files changed, 0 insertions, 157 deletions
diff --git a/vrsite/css.go b/vrsite/css.go
deleted file mode 100644
index f1df936..0000000
--- a/vrsite/css.go
+++ /dev/null
@@ -1,157 +0,0 @@
-// css.go — the stylesheet is generated per site: theme colors become custom
-// properties, one block per scheme, and the per-agent coverage colors are
-// blended from them in the browser so any theme can supply them.
-//
-// Everything switchable is a custom property on :root. That is what lets
-// themes.css hold nothing but one block per theme (writeThemesCSS) and the
-// picker repaint the page by setting a single attribute.
-package main
-
-import (
- "encoding/json"
- "fmt"
- "io"
- "strings"
- texttemplate "text/template"
-)
-
-type cssVar struct{ K, V string }
-
-type synCSS struct{ Class string }
-
-type maskCSS struct {
- Mask uint8
- Mix string
-}
-
-func renderCSS(w io.Writer, site *Site) error {
- css, err := texttemplate.ParseFS(tfs, "assets/style.css.tmpl")
- if err != nil {
- return err
- }
- keys := syntaxKeys(site.Theme, site.Light)
- var syn []synCSS
- for _, k := range keys {
- syn = append(syn, synCSS{Class: cssKey(k)})
- }
- var masks []maskCSS
- for _, m := range site.masks() {
- masks = append(masks, maskCSS{Mask: m, Mix: site.maskMix(m)})
- }
- return css.Execute(w, map[string]any{
- "DarkName": site.Theme.Name, "LightName": site.Light.Name,
- "DarkVars": themeVars(site.Theme, keys), "LightVars": themeVars(site.Light, keys),
- "Syntax": syn, "Masks": masks,
- })
-}
-
-// themeVars is one theme as custom properties: its own colors, the syntax
-// colors under the keys the site paints with (resolved along their dots, so a
-// theme that only names "punctuation" still colors brackets), and the agent
-// colors every coverage blend is mixed from.
-func themeVars(t *Theme, keys []string) []cssVar {
- // bg and panel are what everything else is read against, so they are the
- // two that may not carry a theme's alpha channel through
- vars := []cssVar{
- {"--bg", hex6(t.Bg)}, {"--fg", t.Fg}, {"--panel", hex6(t.Panel)}, {"--border", t.Border},
- {"--ln", t.LineNo}, {"--muted", t.Muted}, {"--accent", t.Accent},
- {"--note", t.Note}, {"--hover", t.Hover},
- }
- for _, k := range keys {
- key := resolveKey(t, k)
- if key == "" {
- continue
- }
- s, c := t.Syntax[key], cssKey(k)
- vars = append(vars, cssVar{"--s-" + c, s.Color})
- if s.FontStyle != nil && *s.FontStyle != "" && *s.FontStyle != "normal" {
- vars = append(vars, cssVar{"--s-" + c + "-fs", *s.FontStyle})
- }
- if s.FontWeight != nil && *s.FontWeight > 0 {
- vars = append(vars, cssVar{"--s-" + c + "-fw", fmt.Sprintf("%.0f", *s.FontWeight)})
- }
- }
- for i := 0; i < maxAgents; i++ {
- vars = append(vars, cssVar{fmt.Sprintf("--ag%d", i), hex6(t.Player(i))})
- }
- return vars
-}
-
-// maskMix is the color of a coverage bitmask: the agent colors it stands for,
-// averaged. The average is written as nested color-mix rather than mixed here,
-// so the same rule holds for every theme the browser can switch to.
-func (s *Site) maskMix(mask uint8) string {
- var idx []int
- for i, a := range s.Agents {
- if a.Bit != 0 && mask&a.Bit != 0 {
- idx = append(idx, i)
- }
- }
- return mixExpr(idx)
-}
-
-func mixExpr(idx []int) string {
- switch len(idx) {
- case 0:
- return "var(--fg)"
- case 1:
- return fmt.Sprintf("var(--ag%d)", idx[0])
- }
- return fmt.Sprintf("color-mix(in srgb, var(--ag%d) %.3f%%, %s)",
- idx[0], 100/float64(len(idx)), mixExpr(idx[1:]))
-}
-
-// ── the switchable themes ─────────────────────────────────────────────────
-
-// writeThemesCSS writes one block per theme found on this machine. It is only
-// fetched once a theme has been picked (or the picker opened), so the page
-// costs nothing for the reader who never switches.
-func writeThemesCSS(w io.Writer, themes []*Theme, keys []string) {
- fmt.Fprintf(w, "/* %d themes, found on this machine — set data-theme on :root to wear one */\n", len(themes))
- for _, t := range themes {
- fmt.Fprintf(w, ":root[data-theme=%q] {\n", t.ID)
- for _, v := range themeVars(t, keys) {
- fmt.Fprintf(w, " %s: %s;\n", v.K, v.V)
- }
- scheme := "dark"
- if t.Appearance == "light" {
- scheme = "light"
- }
- fmt.Fprintf(w, " color-scheme: %s;\n}\n", scheme)
- }
-}
-
-// themeCard is a theme as the picker shows it: a name, where it came from, and
-// the few colors that make it recognizable at a glance.
-type themeCard struct {
- ID string `json:"id"`
- Name string `json:"name"`
- Src string `json:"src"`
- Dark bool `json:"dark"`
- Sw []string `json:"sw"`
-}
-
-// writeThemesJS is the picker's menu. It is a script, not JSON, because the
-// static export is opened from the filesystem, where fetch() is not allowed
-// but a <script src> still loads.
-func writeThemesJS(w io.Writer, themes []*Theme) {
- cards := make([]themeCard, 0, len(themes))
- for _, t := range themes {
- syn := func(key string) string {
- if k := resolveKey(t, key); k != "" {
- return hex6(t.Syntax[k].Color)
- }
- return hex6(t.Fg)
- }
- cards = append(cards, themeCard{ID: t.ID, Name: t.Name, Src: t.Source,
- Dark: t.Appearance != "light",
- Sw: []string{hex6(t.Bg), hex6(t.Fg), hex6(t.Accent),
- syn("keyword"), syn("string"), syn("function")}})
- }
- b, err := json.Marshal(cards)
- if err != nil {
- b = []byte("[]")
- }
- fmt.Fprintf(w, "// %d themes, written by vrsite from this machine's zed and helix installs\n", len(cards))
- fmt.Fprintf(w, "window.VRSITE_THEMES=%s;\n", strings.ReplaceAll(string(b), "},{", "},\n{"))
-}