diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-02 23:43:08 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-03 09:54:39 -0300 |
| commit | 5ec72f8723d795d0b02d7e9ebb9f555f0c7e6a2e (patch) | |
| tree | f12f4b700fff4ff6f7e818d2a542672ba0b8d76e /vrsite/css.go | |
| parent | a9263daee9413c5eff3c1f1bebcd224442fd8685 (diff) | |
| download | notevi-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.go | 157 |
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{")) -} |
