From 36315856aee2ed5ce5527a2c9682b4a25164106b Mon Sep 17 00:00:00 2001 From: Eugene Blikh Date: Sat, 8 Aug 2026 11:10:02 +0300 Subject: [PATCH] chrome: repo-list partial and fixed-width brand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit srht-repo-list renders project listings (repos, databases, spaces) as the family's event-list cards from a neutral ListItem shape, encoding the rules the services converged on: h4 title link, visibility as small muted lowercase text on the right for non-public entries only, description as a paragraph. The empty state is a parameter. The brand in srht-nav gains min-width: 15rem so the service switcher starts at the same x-coordinate on every service — without it the menu shifts by the width of the red service label when hopping between services. --- chrome/chrome.go | 18 ++++++++++++++++++ chrome/chrome_test.go | 33 ++++++++++++++++++++++++++++++++ chrome/templates/chrome.tmpl | 37 +++++++++++++++++++++++++++++++++++- 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/chrome/chrome.go b/chrome/chrome.go index f523cc96c5ba9e17f0334b7b2bdda1a745830d18..3d3c1a02e9b7a313bff1aa5aaf0504f067a1edd9 100644 --- a/chrome/chrome.go +++ b/chrome/chrome.go @@ -139,6 +139,24 @@ type Page struct { ContainerClass string } +// ListItem is one project in a listing — a repository, a database, a space. +// Title is the display name ("~owner/name"); Visibility is the service's +// literal enum value ("PUBLIC"/"UNLISTED"/"PRIVATE", "" to render nothing — +// the srht-repo-list partial shows it lowercase, non-public only). +type ListItem struct { + Href string + Title string + Visibility string + Description string +} + +// RepoList is the dot for the srht-repo-list partial: the items, and the +// muted text shown when there are none. +type RepoList struct { + Items []ListItem + Empty string +} + // Service is the static half of the chrome, built once at startup. The // exported fields may be adjusted between NewService and the first Page call // (they are read, never written, by Page). diff --git a/chrome/chrome_test.go b/chrome/chrome_test.go index e469474095d5a5ff328ca14c653325d1eddb9121..b5f997472b258df41a23d9ab75979a308921468d 100644 --- a/chrome/chrome_test.go +++ b/chrome/chrome_test.go @@ -140,6 +140,39 @@ func TestNavTemplateEmbeddedPage(t *testing.T) { assert.Contains(t, out, "Logged in as") } +// renderList executes the srht-repo-list partial alone against v. +func renderList(t *testing.T, v RepoList) string { + t.Helper() + tpl := MustAttach(template.New("t")) + tpl, err := tpl.Parse(`{{template "srht-repo-list" .}}`) + require.NoError(t, err) + var b strings.Builder + require.NoError(t, tpl.Execute(&b, v)) + return b.String() +} + +func TestRepoListCards(t *testing.T) { + out := renderList(t, RepoList{Items: []ListItem{ + {Href: "/~a/pub", Title: "~a/pub", Visibility: "PUBLIC", Description: "described"}, + {Href: "/~a/unl", Title: "~a/unl", Visibility: "UNLISTED"}, + {Href: "/~a/prv", Title: "~a/prv", Visibility: "PRIVATE"}, + {Href: "/~a/none", Title: "~a/none"}, // service without visibility (spec spaces) + }}) + + assert.Equal(t, 4, strings.Count(out, `class="event"`)) + assert.Contains(t, out, "

described

") + // PUBLIC and empty visibility render no label; the others render lowercase. + assert.Equal(t, 1, strings.Count(out, ">unlisted")) + assert.Equal(t, 1, strings.Count(out, ">private")) + assert.Equal(t, 2, strings.Count(out, "pull-right")) +} + +func TestRepoListEmptyState(t *testing.T) { + out := renderList(t, RepoList{Empty: "No databases yet."}) + assert.NotContains(t, out, "event-list") + assert.Contains(t, out, "No databases yet.") +} + func TestFuncs(t *testing.T) { assert.Equal(t, "12345678", ShortSHA("1234567890abcdef")) assert.Equal(t, "abc", ShortSHA("abc")) diff --git a/chrome/templates/chrome.tmpl b/chrome/templates/chrome.tmpl index 0ef328d936b7774668f65db871c231668a1aeb47..3552a59cd1ae480cc0d2f81990eb3b9f8eb1c9bf 100644 --- a/chrome/templates/chrome.tmpl +++ b/chrome/templates/chrome.tmpl @@ -16,8 +16,43 @@ {{end}} {{- end}} +{{/* + srht-repo-list renders a listing of projects (repos, databases, spaces) as + the family's event-list cards. Dot is a chrome.RepoList. The rules the four + services converged on: h4 title link; visibility as small muted text on the + right, non-public only, lowercase; description as a paragraph when present. +*/}} +{{define "srht-repo-list" -}} +{{if .Items}} +
+ {{range .Items}} +
+

+ {{.Title}} + {{if and .Visibility (ne .Visibility "PUBLIC")}} + {{if eq .Visibility "UNLISTED"}}unlisted{{else}}private{{end}} + {{end}} +

+ {{if .Description}} +

{{.Description}}

+ {{end}} +
+ {{end}} +
+{{else}} +

{{.Empty}}

+{{end}} +{{- end}} + {{define "srht-nav" -}} - +{{/* + The brand carries a fixed min-width so the service switcher starts at the + same x-coordinate on every service: without it the menu shifts by the width + of the red service label ("dolt" vs "compare") when hopping between + services. Sized for the longest label on the instance; inline because the + services build their CSS from the shared core tree and ecore ships none. +*/}} + {{.SiteName}}