vitrine

add moonshot-1-mem to the served roots

commit 0d0727cbf91c289098a1e336123b3333ca512e37 · parent c213fcb807 · browse this tree
Ximianth, 2026-07-24 08:32:57 UTC

diff --git a/main.go b/main.go
index a1b5c57..793c96f 100644
--- a/main.go
+++ b/main.go
@@ -15,7 +15,8 @@ import (
 // roots maps a URL name to a filesystem directory.
 // Hardcoded for now; lift into a config file when we want more than a couple.
 var roots = map[string]string{
-	"moonshot-1": "/home/exedev/fusion/moonshot-1",
+	"moonshot-1":     "/home/exedev/fusion/moonshot-1",
+	"moonshot-1-mem": "/home/exedev/fusion/moonshot-1-mem",
 }
 
 func main() {
diff --git a/render.go b/render.go
index 0c62e3d..3fabbf6 100644
--- a/render.go
+++ b/render.go
@@ -2,7 +2,6 @@ package main
 
 import (
 	"bytes"
-	_ "embed"
 	"fmt"
 	"html/template"
 	"net/http"
@@ -11,24 +10,13 @@ import (
 
 	"github.com/yuin/goldmark"
 	gmext "github.com/yuin/goldmark/extension"
-	"github.com/yuin/goldmark/parser"
-	gmutil "github.com/yuin/goldmark/util"
 )
 
 // maxRenderSize caps how much we'll read into memory for rendered views.
 // Bigger files still work fine via ?fmt=raw, which streams.
 const maxRenderSize = 4 << 20
 
-var markdown = goldmark.New(
-	goldmark.WithExtensions(gmext.GFM),
-	goldmark.WithParserOptions(
-		parser.WithAutoHeadingID(),
-		parser.WithASTTransformers(gmutil.Prioritized(headingAnchors{}, 100)),
-	),
-)
-
-//go:embed style.css
-var styleCSS []byte
+var markdown = goldmark.New(goldmark.WithExtensions(gmext.GFM))
 
 type dirEntry struct {
 	Name string
@@ -85,9 +73,7 @@ func serveRendered(w http.ResponseWriter, r *http.Request, root *os.Root, name,
 			return
 		}
 		var buf bytes.Buffer
-		// A fresh IDs registry per document keeps duplicate-heading suffixes deterministic.
-		ctx := parser.NewContext(parser.WithIDs(newSlugIDs()))
-		if err := markdown.Convert(src, &buf, parser.WithContext(ctx)); err != nil {
+		if err := markdown.Convert(src, &buf); err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			return
 		}
@@ -104,7 +90,18 @@ var pageTmpl = template.Must(template.New("page").Parse(`<!doctype html>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <title>{{.Title}}</title>
-<link rel="stylesheet" href="/-/style.css">
+<style>
+body { font-family: sans-serif; max-width: 56rem; margin: 1rem auto; padding: 0 1rem; }
+nav { border-bottom: 1px solid #ccc; padding-bottom: .5rem; margin-bottom: 1rem; }
+nav .raw { float: right; }
+ul.listing { list-style: none; padding-left: 0; }
+ul.listing li { padding: .1rem 0; }
+iframe.passthrough { width: 100%; height: 75vh; border: 1px solid #ccc; }
+article pre { background: #f4f4f4; padding: .5rem; overflow-x: auto; }
+article code { background: #f4f4f4; }
+article table { border-collapse: collapse; }
+article th, article td { border: 1px solid #ccc; padding: .2rem .5rem; }
+</style>
 </head>
 <body>
 <nav>
diff --git a/serve.go b/serve.go
index 860de1e..8b88259 100644
--- a/serve.go
+++ b/serve.go
@@ -34,13 +34,6 @@ func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		s.serveIndex(w, r)
 		return
 	}
-	// The "/-/" prefix is reserved for server-internal assets,
-	// keeping it distinct from any configured root name.
-	if r.URL.Path == "/-/style.css" {
-		w.Header().Set("Content-Type", "text/css; charset=utf-8")
-		w.Write(styleCSS)
-		return
-	}
 
 	name, rest, _ := strings.Cut(strings.TrimPrefix(r.URL.Path, "/"), "/")
 	root, ok := s.trees[name]
diff --git a/serve_test.go b/serve_test.go
index 2e2b21d..0f34748 100644
--- a/serve_test.go
+++ b/serve_test.go
@@ -17,8 +17,8 @@ func newTestServer(t *testing.T) *server {
 		"README.md":     "# Hello\n\nsome *markdown* & <angle> stuff\n",
 		"sub/notes.txt": "plain text here\n",
 		"sub/deeper.md": "nested doc\n",
-		"dup.md":        "## Twice\n\n## Twice\n",
-		".git/config":   "should be hidden\n"}
+		".git/config":   "should be hidden\n",
+	}
 	for name, content := range files {
 		p := filepath.Join(dir, filepath.FromSlash(name))
 		if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil {
diff --git a/slug.go b/slug.go
deleted file mode 100644
index ac36e22..0000000
--- a/slug.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package main
-
-import (
-	"strconv"
-	"strings"
-
-	"github.com/yuin/goldmark/ast"
-	"github.com/yuin/goldmark/parser"
-	"github.com/yuin/goldmark/text"
-)
-
-// slugify turns heading text into a URL fragment:
-// lowercase, alphanumerics kept, every other run of characters becomes one dash.
-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('-')
-			}
-			dash = false
-			b.WriteRune(r)
-		default:
-			dash = true
-		}
-	}
-	return b.String()
-}
-
-// slugIDs implements parser.IDs so goldmark assigns our slugs as heading ids.
-// Duplicate headings get "-2", "-3", ... suffixes.
-type slugIDs struct {
-	used map[string]bool
-}
-
-func newSlugIDs() *slugIDs {
-	return &slugIDs{used: make(map[string]bool)}
-}
-
-func (s *slugIDs) Generate(value []byte, kind ast.NodeKind) []byte {
-	slug := slugify(string(value))
-	if slug == "" {
-		slug = "section"
-	}
-	candidate := slug
-	for n := 2; s.used[candidate]; n++ {
-		candidate = slug + "-" + strconv.Itoa(n)
-	}
-	s.used[candidate] = true
-	return []byte(candidate)
-}
-
-func (s *slugIDs) Put(value []byte) {
-	s.used[string(value)] = true
-}
-
-// headingAnchors is a goldmark AST transformer that appends a chainlink
-// anchor to every heading that received an id, linking to its own fragment.
-// Pure HTML output: works without javascript.
-type headingAnchors struct{}
-
-func (headingAnchors) Transform(doc *ast.Document, reader text.Reader, pc parser.Context) {
-	_ = ast.Walk(doc, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
-		if !entering {
-			return ast.WalkContinue, nil
-		}
-		h, ok := n.(*ast.Heading)
-		if !ok {
-			return ast.WalkContinue, nil
-		}
-		id, ok := h.AttributeString("id")
-		if !ok {
-			return ast.WalkContinue, nil
-		}
-		link := ast.NewLink()
-		link.Destination = append([]byte("#"), id.([]byte)...)
-		link.SetAttributeString("class", []byte("anchor"))
-		link.AppendChild(link, ast.NewString([]byte("\U0001F517"))) // chainlink emoji
-		h.AppendChild(h, link)
-		return ast.WalkContinue, nil
-	})
-}
diff --git a/slug_test.go b/slug_test.go
deleted file mode 100644
index 55a0050..0000000
--- a/slug_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package main
-
-import (
-	"strings"
-	"testing"
-)
-
-func TestSlugify(t *testing.T) {
-	cases := map[string]string{
-		"What the verifier catches": "what-the-verifier-catches",
-		"Use before definition":     "use-before-definition",
-		"C++ / FFI (part 2)":        "c-ffi-part-2",
-		"  spaces  everywhere  ":    "spaces-everywhere",
-		"???":                       "",
-	}
-	for in, want := range cases {
-		if got := slugify(in); got != want {
-			t.Errorf("slugify(%q) = %q, want %q", in, got, want)
-		}
-	}
-}
-
-func TestHeadingAnchorsInRenderedPage(t *testing.T) {
-	srv := newTestServer(t)
-	rec := get(t, srv, "/demo/README.md")
-	body := rec.Body.String()
-	if !strings.Contains(body, `<h1 id="hello">`) {
-		t.Errorf("heading missing slug id; body:\n%s", body)
-	}
-	if !strings.Contains(body, `<a href="#hello" class="anchor">`) {
-		t.Errorf("heading missing chainlink anchor; body:\n%s", body)
-	}
-}
-
-func TestDuplicateHeadingsGetDistinctIDs(t *testing.T) {
-	srv := newTestServer(t)
-	// dup.md is written by newTestServer with two identical headings.
-	rec := get(t, srv, "/demo/dup.md")
-	body := rec.Body.String()
-	if !strings.Contains(body, `id="twice"`) || !strings.Contains(body, `id="twice-2"`) {
-		t.Errorf("duplicate headings not disambiguated; body:\n%s", body)
-	}
-}
-
-func TestStylesheetServed(t *testing.T) {
-	rec := get(t, newTestServer(t), "/-/style.css")
-	if rec.Code != 200 || !strings.HasPrefix(rec.Header().Get("Content-Type"), "text/css") {
-		t.Errorf("status = %d, content-type = %q", rec.Code, rec.Header().Get("Content-Type"))
-	}
-}
diff --git a/style.css b/style.css
deleted file mode 100644
index 34676d3..0000000
--- a/style.css
+++ /dev/null
@@ -1,102 +0,0 @@
-/* repoview stylesheet.  Embedded in the binary; served at /-/style.css. */
-
-:root {
-	--ink: #24292f;
-	--ink-soft: #57606a;
-	--accent: #0a5fa4;
-	--rule: #d0d7de;
-	--paper: #fdfdfc;
-	--well: #f3f5f7;
-	--well-edge: #c9d1d9;
-}
-
-body {
-	font-family: sans-serif;
-	color: var(--ink);
-	background: var(--paper);
-	max-width: 56rem;
-	margin: 1rem auto;
-	padding: 0 1rem;
-	line-height: 1.5;
-}
-
-a { color: var(--accent); }
-
-nav {
-	border-bottom: 2px solid var(--accent);
-	padding-bottom: .5rem;
-	margin-bottom: 1rem;
-}
-nav .raw {
-	float: right;
-	font-size: .85rem;
-	border: 1px solid var(--rule);
-	border-radius: .25rem;
-	padding: .1rem .5rem;
-	text-decoration: none;
-}
-nav .raw:hover { background: var(--well); }
-
-ul.listing { list-style: none; padding-left: 1.5rem; }
-ul.listing li { padding: .15rem 0; }
-
-iframe.passthrough {
-	width: 100%;
-	height: 75vh;
-	border: 1px solid var(--well-edge);
-	border-radius: .25rem;
-	background: white;
-}
-
-/* Headings sit flush left; all other article content is inset,
-   so headings read as structure at a glance. */
-article > * { margin-left: 1.5rem; }
-article > h1, article > h2, article > h3,
-article > h4, article > h5, article > h6 { margin-left: 0; }
-
-article h1, article h2 {
-	border-bottom: 1px solid var(--rule);
-	padding-bottom: .2rem;
-}
-article h1 { color: var(--accent); }
-
-/* Chainlink anchors: subtle until the heading is hovered. */
-a.anchor {
-	font-size: .7em;
-	text-decoration: none;
-	margin-left: .4rem;
-	opacity: 0;
-}
-h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor,
-h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor,
-a.anchor:focus { opacity: .6; }
-
-/* Code blocks: inset further than prose, bordered, in a well. */
-article pre {
-	margin-left: 3rem;
-	background: var(--well);
-	border: 1px solid var(--well-edge);
-	border-left: 3px solid var(--accent);
-	border-radius: .25rem;
-	padding: .6rem .8rem;
-	overflow-x: auto;
-}
-article code {
-	background: var(--well);
-	border-radius: .2rem;
-	padding: .05rem .25rem;
-}
-article pre code { background: none; padding: 0; }
-
-article blockquote {
-	border-left: 3px solid var(--rule);
-	color: var(--ink-soft);
-	padding-left: .8rem;
-}
-
-article table { border-collapse: collapse; }
-article th, article td {
-	border: 1px solid var(--rule);
-	padding: .2rem .5rem;
-}
-article th { background: var(--well); }