~bigbes/sr-ht-dolt

25541bcd042d7cd20f38634f9a39dbdde3bc8487 — Eugene Blikh 9 days ago e8a202e
gitignore the in-repo git worktree directories

Agent worktrees are created under .worktrees/<branch> (and, in the older
repos of this family, .claude/worktrees/<branch>) so that they never
scatter as sibling directories next to the checkout. Neither path was
ignored here, so a worktree showed up as untracked in every git status
taken from the main checkout.
4 files changed, 6 insertions(+), 187 deletions(-)

M .gitignore
D web/chrome.go
D web/templates/icons/circle.svg
D web/templates/nav.html
M .gitignore => .gitignore +6 -0
@@ 8,5 8,11 @@
/static/main.min.css
/static/main.min.*.css

# Git worktrees, kept inside the repo so they never scatter as sibling dirs.
# Both paths, because the two conventions in use across these repos are
# .worktrees/<branch> and the older .claude/worktrees/<branch>.
/.worktrees/
/.claude/worktrees/

# macOS
.DS_Store

D web/chrome.go => web/chrome.go +0 -150
@@ 1,150 0,0 @@
package web

import (
	"net/http"
	"net/url"
	"sort"
	"strings"

	"sourcecraft.dev/bigbes/sr-ht-core/auth"
	"sourcecraft.dev/bigbes/sr-ht-core/config"
	"github.com/vaughan0/go-ini"

	"sourcecraft.dev/bigbes/sr-ht-dolt/authn"
)

// serviceName is our own service key in the shared config and nav.
const serviceName = "dolt.sr.ht"

// networkOrder is upstream core.sr.ht's fixed nav ordering (flask.py
// _network_order). Services present in the config but not listed here sort
// after these, alphabetically. paste.sr.ht and pages.sr.ht are excluded from
// the network entirely (they have no user-facing nav), mirroring _network.
var networkOrder = []string{
	"hub.sr.ht",
	"git.sr.ht",
	"hg.sr.ht",
	"lists.sr.ht",
	"todo.sr.ht",
	"builds.sr.ht",
	"man.sr.ht",
	"meta.sr.ht",
}

var networkExcluded = map[string]bool{
	"paste.sr.ht": true,
	"pages.sr.ht": true,
}

// navEntry is one service link in the shared nav.
type navEntry struct {
	// Name is the leading segment of the service (e.g. "git" for git.sr.ht),
	// rendered as the link text exactly as upstream nav.html does.
	Name string
	// Site is the full service key (e.g. "git.sr.ht").
	Site string
	// Origin is the external URL for the service.
	Origin string
	// Active is true for our own service, which highlights it in the nav.
	Active bool
}

// basePage is the chrome model shared by every rendered page. Handler view
// structs embed it so templates reference its fields directly (e.g. .SiteName).
type basePage struct {
	Title         string
	Site          string
	SiteLabel     string
	SiteName      string
	Environment   string
	ShowEnvBanner bool
	Network       []navEntry
	MetaOrigin    string
	SelfOrigin    string
	LoginURL      string
	LogoutURL     string
	StyleHref     string
	// CurrentUser is the authenticated caller, or nil for an anonymous request.
	CurrentUser *auth.AuthContext
}

// buildNetwork returns the ordered nav entries for conf: every section ending
// ".sr.ht" that is not excluded, ordered by networkOrder then alphabetically
// for unknown services. Origins are resolved externally via config.GetOrigin.
func buildNetwork(conf ini.File) []navEntry {
	var sites []string
	for section := range conf {
		if !strings.HasSuffix(section, ".sr.ht") || networkExcluded[section] {
			continue
		}
		sites = append(sites, section)
	}

	orderIndex := func(s string) int {
		for i, n := range networkOrder {
			if n == s {
				return i
			}
		}
		return len(networkOrder) // unknown services sort after the fixed set
	}
	sort.Slice(sites, func(i, j int) bool {
		oi, oj := orderIndex(sites[i]), orderIndex(sites[j])
		if oi != oj {
			return oi < oj
		}
		return sites[i] < sites[j] // stable, alphabetical among unknowns
	})

	entries := make([]navEntry, 0, len(sites))
	for _, s := range sites {
		entries = append(entries, navEntry{
			Name:   strings.SplitN(s, ".", 2)[0],
			Site:   s,
			Origin: config.GetOrigin(conf, s, true),
			Active: s == serviceName,
		})
	}
	return entries
}

// newBasePage builds the chrome model for a request. Title is the per-page
// <title>; the caller sets any page-specific fields on its own view struct.
func (a *app) newBasePage(r *http.Request, title string) basePage {
	conf := a.cfg.Conf
	env := config.GetString(conf, "sr.ht", "environment", "development")
	self := config.GetOrigin(conf, serviceName, true)
	meta := config.GetOrigin(conf, "meta.sr.ht", true)

	return basePage{
		Title:         title,
		Site:          serviceName,
		SiteLabel:     strings.SplitN(serviceName, ".", 2)[0],
		SiteName:      config.GetString(conf, "sr.ht", "site-name", "sr.ht"),
		Environment:   env,
		ShowEnvBanner: env != "production",
		Network:       buildNetwork(conf),
		MetaOrigin:    meta,
		SelfOrigin:    self,
		LoginURL:      loginURL(self, meta, r),
		LogoutURL:     logoutURL(self, meta),
		StyleHref:     a.styleHref,
		CurrentUser:   authn.CallerFromContext(r.Context()),
	}
}

// loginURL mirrors core.sr.ht flask.py: {meta}/login?return_to={self+full_path}.
// full_path is the request path with its query string, so login round-trips the
// user back to exactly where they were.
func loginURL(self, meta string, r *http.Request) string {
	returnTo := self + r.URL.EscapedPath()
	if r.URL.RawQuery != "" {
		returnTo += "?" + r.URL.RawQuery
	}
	return meta + "/login?return_to=" + url.QueryEscape(returnTo)
}

// logoutURL mirrors core.sr.ht flask.py: {meta}/logout?return_to={self origin}.
func logoutURL(self, meta string) string {
	return meta + "/logout?return_to=" + url.QueryEscape(self)
}

D web/templates/icons/circle.svg => web/templates/icons/circle.svg +0 -1
@@ 1,1 0,0 @@
<svg width="22" height="22" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200z"/></svg>

D web/templates/nav.html => web/templates/nav.html +0 -36
@@ 1,36 0,0 @@
{{define "nav" -}}
<span class="navbar-brand">
  {{icon "circle"}}
  <a class="navbar-brand" href="/">
    {{.SiteName}}
    <span class="text-danger">{{.SiteLabel}}</span>
  </a>
</span>
<ul class="navbar-nav">
  {{if .CurrentUser}}
  {{range .Network}}
  {{if ne .Site "hub.sr.ht"}}
  <li class="nav-item {{if .Active}}active{{end}}">
    <a class="nav-link" href="{{.Origin}}">{{.Name}}</a>
  </li>
  {{end}}
  {{end}}
  {{end}}
</ul>
<div class="login">
  {{if .CurrentUser}}
  <span class="navbar-text">
    Logged in as
    <a href="{{.MetaOrigin}}/profile">{{.CurrentUser.Username}}</a>
    &mdash;
    <a href="{{.LogoutURL}}">Log out</a>
  </span>
  {{else}}
  <span class="navbar-text">
    <a href="{{.LoginURL}}" rel="nofollow">Log in</a>
    &mdash;
    <a href="{{.MetaOrigin}}">Register</a>
  </span>
  {{end}}
</div>
{{- end}}