Monorepo for Tangled tangled.org

appview: add "starred-by" page to repos #1112

open opened by pdewey.com targeting master from pdewey.com/tangled-core: feat-starred-by-page

Adds a page and route for each repo that shows all users that have starred a given repo. This divides the star button within a repo page, adding an icon to the right side that can be clicked to open the new stars page.

Closes #427

Labels

None yet.

assignee

None yet.

Participants 2
AT URI
at://did:plc:hm5f3dnm6jdhrc55qp2npdja/sh.tangled.repo.pull/3mg7bpskm4n22
+206 -7
Interdiff #1 โ†’ #2
appview/db/star.go

This file has not been changed.

+38 -3
appview/oauth/handler.go
··· 6 6 "encoding/json" 7 7 "errors" 8 8 "fmt" 9 + "log/slog" 9 10 "net/http" 10 11 "slices" 11 12 "time" ··· 130 131 } 131 132 132 133 l.Debug("adding to default spindle") 134 + session, err := o.getAppPasswordSession() 133 - session, err := CreateAppPasswordSession(o.IdResolver, o.Config.Core.AppPassword, consts.TangledDid, o.Config.Core.RateLimitBypass) 134 135 if err != nil { 135 136 l.Error("failed to create session", "err", err) 136 137 return ··· 144 145 } 145 146 146 147 if err := session.putRecord(record, tangled.SpindleMemberNSID); err != nil { 148 + o.invalidateAppPasswordSession() 147 149 l.Error("failed to add to default spindle", "err", err) 148 150 return 149 151 } ··· 169 171 } 170 172 171 173 l.Debug("adding to default knot") 174 + session, err := o.getAppPasswordSession() 172 - session, err := CreateAppPasswordSession(o.IdResolver, o.Config.Core.AppPassword, consts.TangledDid, o.Config.Core.RateLimitBypass) 173 175 if err != nil { 174 176 l.Error("failed to create session", "err", err) 175 177 return ··· 183 185 } 184 186 185 187 if err := session.putRecord(record, tangled.KnotMemberNSID); err != nil { 188 + o.invalidateAppPasswordSession() 186 189 l.Error("failed to add to default knot", "err", err) 187 190 return 188 191 } ··· 248 251 PdsEndpoint string 249 252 Did string 250 253 RateLimitBypass string 254 + Logger *slog.Logger 251 255 } 252 256 257 + func CreateAppPasswordSession(res *idresolver.Resolver, appPassword, did, rateLimitBypass string, logger *slog.Logger) (*AppPasswordSession, error) { 253 - func CreateAppPasswordSession(res *idresolver.Resolver, appPassword, did, rateLimitBypass string) (*AppPasswordSession, error) { 254 258 if appPassword == "" { 255 259 return nil, fmt.Errorf("no app password configured") 256 260 } ··· 284 288 sessionReq.Header.Set("x-ratelimit-bypass", rateLimitBypass) 285 289 } 286 290 291 + logger.Debug("creating app password session", "url", sessionURL, "headers", sessionReq.Header) 292 + 287 293 client := &http.Client{Timeout: 30 * time.Second} 288 294 sessionResp, err := client.Do(sessionReq) 289 295 if err != nil { ··· 303 309 session.PdsEndpoint = pdsEndpoint 304 310 session.Did = did 305 311 session.RateLimitBypass = rateLimitBypass 312 + session.Logger = logger 306 313 307 314 return &session, nil 308 315 } ··· 337 344 req.Header.Set("x-ratelimit-bypass", s.RateLimitBypass) 338 345 } 339 346 347 + s.Logger.Debug("putting record", "url", url, "collection", collection, "headers", req.Header) 348 + 340 349 client := &http.Client{Timeout: 30 * time.Second} 341 350 resp, err := client.Do(req) 342 351 if err != nil { ··· 350 359 351 360 return nil 352 361 } 362 + 363 + // getAppPasswordSession returns a cached AppPasswordSession, creating one if needed. 364 + func (o *OAuth) getAppPasswordSession() (*AppPasswordSession, error) { 365 + o.appPasswordSessionMu.Lock() 366 + defer o.appPasswordSessionMu.Unlock() 367 + 368 + if o.appPasswordSession != nil { 369 + return o.appPasswordSession, nil 370 + } 371 + 372 + session, err := CreateAppPasswordSession(o.IdResolver, o.Config.Core.AppPassword, consts.TangledDid, o.Config.Core.RateLimitBypass, o.Logger) 373 + if err != nil { 374 + return nil, err 375 + } 376 + 377 + o.appPasswordSession = session 378 + return session, nil 379 + } 380 + 381 + // invalidateAppPasswordSession clears the cached session so the next call to 382 + // getAppPasswordSession will create a fresh one. 383 + func (o *OAuth) invalidateAppPasswordSession() { 384 + o.appPasswordSessionMu.Lock() 385 + defer o.appPasswordSessionMu.Unlock() 386 + o.appPasswordSession = nil 387 + }
+4
appview/oauth/oauth.go
··· 5 5 "fmt" 6 6 "log/slog" 7 7 "net/http" 8 + "sync" 8 9 "time" 9 10 10 11 comatproto "github.com/bluesky-social/indigo/api/atproto" ··· 33 34 Enforcer *rbac.Enforcer 34 35 IdResolver *idresolver.Resolver 35 36 Logger *slog.Logger 37 + 38 + appPasswordSession *AppPasswordSession 39 + appPasswordSessionMu sync.Mutex 36 40 } 37 41 38 42 func New(config *config.Config, ph posthog.Client, db *db.DB, enforcer *rbac.Enforcer, res *idresolver.Resolver, logger *slog.Logger) (*OAuth, error) {
+5
appview/pages/markup/markdown.go
··· 22 22 "github.com/yuin/goldmark/text" 23 23 "github.com/yuin/goldmark/util" 24 24 callout "gitlab.com/staticnoise/goldmark-callout" 25 + "go.abhg.dev/goldmark/mermaid" 25 26 htmlparse "golang.org/x/net/html" 26 27 27 28 "tangled.org/core/api/tangled" ··· 56 57 md := goldmark.New( 57 58 goldmark.WithExtensions( 58 59 extension.GFM, 60 + &mermaid.Extender{ 61 + RenderMode: mermaid.RenderModeClient, 62 + NoScript: true, 63 + }, 59 64 highlighting.NewHighlighting( 60 65 highlighting.WithFormatOptions( 61 66 chromahtml.Standalone(false),
+46
appview/pages/markup/markdown_test.go
··· 2 2 3 3 import ( 4 4 "bytes" 5 + "strings" 5 6 "testing" 6 7 ) 8 + 9 + func TestMermaidExtension(t *testing.T) { 10 + tests := []struct { 11 + name string 12 + markdown string 13 + contains string 14 + notContains string 15 + }{ 16 + { 17 + name: "mermaid block produces pre.mermaid", 18 + markdown: "```mermaid\ngraph TD\n A-->B\n```", 19 + contains: `<pre class="mermaid">`, 20 + notContains: `<code class="language-mermaid"`, 21 + }, 22 + { 23 + name: "mermaid block contains diagram source", 24 + markdown: "```mermaid\ngraph TD\n A-->B\n```", 25 + contains: "graph TD", 26 + }, 27 + { 28 + name: "non-mermaid code block is not affected", 29 + markdown: "```go\nfunc main() {}\n```", 30 + contains: `<pre class="chroma">`, 31 + }, 32 + } 33 + 34 + for _, tt := range tests { 35 + t.Run(tt.name, func(t *testing.T) { 36 + md := NewMarkdown("tangled.org") 37 + 38 + var buf bytes.Buffer 39 + if err := md.Convert([]byte(tt.markdown), &buf); err != nil { 40 + t.Fatalf("failed to convert markdown: %v", err) 41 + } 42 + 43 + result := buf.String() 44 + if !strings.Contains(result, tt.contains) { 45 + t.Errorf("expected output to contain:\n%s\ngot:\n%s", tt.contains, result) 46 + } 47 + if tt.notContains != "" && strings.Contains(result, tt.notContains) { 48 + t.Errorf("expected output NOT to contain:\n%s\ngot:\n%s", tt.notContains, result) 49 + } 50 + }) 51 + } 52 + } 7 53 8 54 func TestAtExtension_Rendering(t *testing.T) { 9 55 tests := []struct {
+1 -1
appview/pages/markup/sanitizer.go
··· 72 72 policy.AllowAttrs("checked", "disabled", "data-source-position").OnElements("input") 73 73 74 74 // for code blocks 75 + policy.AllowAttrs("class").Matching(regexp.MustCompile(`chroma|mermaid`)).OnElements("pre") 75 - policy.AllowAttrs("class").Matching(regexp.MustCompile(`chroma`)).OnElements("pre") 76 76 policy.AllowAttrs("class").Matching(regexp.MustCompile(`anchor|footnote-ref|footnote-backref`)).OnElements("a") 77 77 policy.AllowAttrs("class").Matching(regexp.MustCompile(`heading`)).OnElements("h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8") 78 78 policy.AllowAttrs("class").Matching(regexp.MustCompile(strings.Join(slices.Collect(maps.Values(chroma.StandardTypes)), "|"))).OnElements("span")
appview/pages/pages.go

This file has not been changed.

appview/pages/repoinfo/repoinfo.go

This file has not been changed.

appview/pages/templates/fragments/starBtn.html

This file has not been changed.

+17
appview/pages/templates/layouts/base.html
··· 39 39 <link rel="preload" href="/static/fonts/InterVariable.woff2" as="font" type="font/woff2" crossorigin /> 40 40 41 41 <link rel="stylesheet" href="/static/tw.css?{{ cssContentHash }}" type="text/css" /> 42 + 43 + <script> 44 + document.addEventListener('DOMContentLoaded', () => { 45 + const nodes = document.querySelectorAll('pre.mermaid'); 46 + if (!nodes.length) return; 47 + const script = document.createElement('script'); 48 + script.src = '/static/mermaid.min.js'; 49 + script.onload = async () => { 50 + mermaid.initialize({ 51 + startOnLoad: true, 52 + theme: window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'default', 53 + }); 54 + await mermaid.run({ nodes }); 55 + }; 56 + document.head.appendChild(script); 57 + }); 58 + </script> 42 59 <title>{{ block "title" . }}{{ end }}</title> 43 60 {{ block "extrameta" . }}{{ end }} 44 61 </head>
appview/pages/templates/layouts/repobase.html

This file has not been changed.

+46
appview/pages/templates/repo/fragments/diff.html
··· 11 11 {{ template "diffTopbar" . }} 12 12 {{ block "diffLayout" . }} {{ end }} 13 13 {{ template "fragments/resizable" }} 14 + {{ template "activeFileHighlight" }} 14 15 {{ end }} 15 16 16 17 {{ define "diffTopbar" }} ··· 229 230 }); 230 231 </script> 231 232 {{ end }} 233 + 234 + {{ define "activeFileHighlight" }} 235 + <script> 236 + document.addEventListener('DOMContentLoaded', function() { 237 + const diffFiles = document.querySelectorAll('details[id^="file-"]'); 238 + const filetreeLinks = document.querySelectorAll('.filetree-link'); 239 + if (diffFiles.length === 0 || filetreeLinks.length === 0) return; 240 + 241 + const linkMap = new Map(); 242 + filetreeLinks.forEach(link => { 243 + const path = link.getAttribute('data-path'); 244 + if (path) linkMap.set('file-' + path, link); 245 + }); 246 + 247 + let currentActive = null; 248 + function setActive(link) { 249 + if (link && link !== currentActive) { 250 + if (currentActive) currentActive.classList.remove('font-bold'); 251 + link.classList.add('font-bold'); 252 + currentActive = link; 253 + } 254 + } 255 + 256 + filetreeLinks.forEach(link => { 257 + link.addEventListener('click', () => setActive(link)); 258 + }); 259 + 260 + const topbar = document.querySelector('.sticky.top-0.z-30'); 261 + const headerHeight = topbar ? topbar.offsetHeight : 0; 262 + 263 + function updateActiveFile() { 264 + for (const file of diffFiles) { 265 + const rect = file.getBoundingClientRect(); 266 + if (rect.top <= headerHeight && rect.bottom > headerHeight) { 267 + setActive(linkMap.get(file.id)); 268 + return; 269 + } 270 + } 271 + } 272 + 273 + document.addEventListener('scroll', updateActiveFile); 274 + updateActiveFile(); 275 + }); 276 + </script> 277 + {{ end }}
+1 -1
appview/pages/templates/repo/fragments/fileTree.html
··· 16 16 {{ else if .Name }} 17 17 <div class="tree-file flex items-center gap-2 pt-1"> 18 18 {{ i "file" "flex-shrink-0 size-4" }} 19 + <a href="#file-{{ .Path }}" data-path="{{ .Path }}" class="filetree-link filename truncate text-black dark:text-white no-underline hover:underline">{{ .Name }}</a> 19 - <a href="#file-{{ .Path }}" class="filename truncate text-black dark:text-white no-underline hover:underline">{{ .Name }}</a> 20 20 </div> 21 21 {{ else }} 22 22 {{ range $child := .Children }}
appview/pages/templates/repo/stars.html

This file has not been changed.

appview/repo/repo.go

This file has not been changed.

appview/repo/router.go

This file has not been changed.

appview/state/star.go

This file has not been changed.

+1 -1
appview/state/state.go
··· 622 622 return 623 623 } 624 624 625 + session, err := oauth.CreateAppPasswordSession(res, config.Core.AppPassword, consts.TangledDid, config.Core.RateLimitBypass, logger) 625 - session, err := oauth.CreateAppPasswordSession(res, config.Core.AppPassword, consts.TangledDid, config.Core.RateLimitBypass) 626 626 if err != nil { 627 627 logger.Error("failed to create appassword session... skipping fetch", "err", err) 628 628 return
+13
flake.lock
··· 148 148 "url": "https://github.com/lucide-icons/lucide/releases/download/0.536.0/lucide-icons-0.536.0.zip" 149 149 } 150 150 }, 151 + "mermaid-src": { 152 + "flake": false, 153 + "locked": { 154 + "narHash": "sha256-/YOdECG2V5c3kJ1QfGvhziTT6K/Dx/4mOk2mr3Fs/do=", 155 + "type": "file", 156 + "url": "https://cdn.jsdelivr.net/npm/mermaid@11.12.3/dist/mermaid.min.js" 157 + }, 158 + "original": { 159 + "type": "file", 160 + "url": "https://cdn.jsdelivr.net/npm/mermaid@11.12.3/dist/mermaid.min.js" 161 + } 162 + }, 151 163 "nixpkgs": { 152 164 "locked": { 153 165 "lastModified": 1766070988, ··· 175 187 "indigo": "indigo", 176 188 "inter-fonts-src": "inter-fonts-src", 177 189 "lucide-src": "lucide-src", 190 + "mermaid-src": "mermaid-src", 178 191 "nixpkgs": "nixpkgs", 179 192 "sqlite-lib-src": "sqlite-lib-src" 180 193 }
+7 -1
flake.nix
··· 37 37 url = "git+https://tangled.org/@jakelazaroff.com/actor-typeahead"; 38 38 flake = false; 39 39 }; 40 + mermaid-src = { 41 + url = "https://cdn.jsdelivr.net/npm/mermaid@11.12.3/dist/mermaid.min.js"; 42 + flake = false; 43 + }; 40 44 ibm-plex-mono-src = { 41 45 url = "https://github.com/IBM/plex/releases/download/%40ibm%2Fplex-mono%401.1.0/ibm-plex-mono.zip"; 42 46 flake = false; ··· 59 63 sqlite-lib-src, 60 64 ibm-plex-mono-src, 61 65 actor-typeahead-src, 66 + mermaid-src, 62 67 ... 63 68 }: let 64 69 supportedSystems = ["x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin"]; ··· 85 90 lexgen = self.callPackage ./nix/pkgs/lexgen.nix {inherit indigo;}; 86 91 goat = self.callPackage ./nix/pkgs/goat.nix {inherit indigo;}; 87 92 appview-static-files = self.callPackage ./nix/pkgs/appview-static-files.nix { 93 + inherit htmx-src htmx-ws-src lucide-src inter-fonts-src ibm-plex-mono-src actor-typeahead-src mermaid-src; 88 - inherit htmx-src htmx-ws-src lucide-src inter-fonts-src ibm-plex-mono-src actor-typeahead-src; 89 94 }; 90 95 appview = self.callPackage ./nix/pkgs/appview.nix {}; 91 96 docs = self.callPackage ./nix/pkgs/docs.nix { ··· 218 223 type = "app"; 219 224 program = toString (pkgs.writeShellScript "watch-appview" '' 220 225 echo "copying static files to appview/pages/static..." 226 + mkdir -p appview/pages/static 221 227 ${pkgs.coreutils}/bin/cp -fr --no-preserve=ownership ${packages'.appview-static-files}/* appview/pages/static 222 228 ${air-watcher "appview" ""}/bin/run 223 229 '');
+1
go.mod
··· 48 48 github.com/yuin/goldmark-emoji v1.0.6 49 49 github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc 50 50 gitlab.com/staticnoise/goldmark-callout v0.0.0-20240609120641-6366b799e4ab 51 + go.abhg.dev/goldmark/mermaid v0.6.0 51 52 golang.org/x/crypto v0.40.0 52 53 golang.org/x/image v0.31.0 53 54 golang.org/x/net v0.42.0
+16
go.sum
··· 105 105 github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= 106 106 github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= 107 107 github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= 108 + github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d h1:ZtA1sedVbEW7EW80Iz2GR3Ye6PwbJAJXjv7D74xG6HU= 109 + github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k= 110 + github.com/chromedp/chromedp v0.14.0 h1:/xE5m6wEBwivhalHwlCOyYfBcAJNwg4nLw96QiCfYr0= 111 + github.com/chromedp/chromedp v0.14.0/go.mod h1:rHzAv60xDE7VNy/MYtTUrYreSc0ujt2O1/C3bzctYBo= 112 + github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM= 113 + github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8= 108 114 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 109 115 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 110 116 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= ··· 175 181 github.com/go-git/go-git-fixtures/v5 v5.0.0-20241203230421-0753e18f8f03/go.mod h1:hMKrMnUE4W0SJ7bFyM00dyz/HoknZoptGWzrj6M+dEM= 176 182 github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY= 177 183 github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= 184 + github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 h1:iizUGZ9pEquQS5jTGkh4AqeeHCMbfbjeb0zMt0aEFzs= 185 + github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M= 178 186 github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= 179 187 github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 180 188 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= ··· 189 197 github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= 190 198 github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= 191 199 github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= 200 + github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= 201 + github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= 202 + github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= 203 + github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= 204 + github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs= 205 + github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc= 192 206 github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= 193 207 github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 194 208 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= ··· 516 530 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8= 517 531 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q= 518 532 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02/go.mod h1:JTnUj0mpYiAsuZLmKjTx/ex3AtMowcCgnE7YNyCEP0I= 533 + go.abhg.dev/goldmark/mermaid v0.6.0 h1:VvkYFWuOjD6cmSBVJpLAtzpVCGM1h0B7/DQ9IzERwzY= 534 + go.abhg.dev/goldmark/mermaid v0.6.0/go.mod h1:uMc+PcnIH2NVL7zjH10Q1wr7hL3+4n4jUMifhyBYB9I= 519 535 go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= 520 536 go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= 521 537 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+5
input.css
··· 215 215 @apply disabled:accent-blue-500 checked:accent-blue-500 disabled:checked:accent-blue-500; 216 216 } 217 217 218 + /* Mermaid diagrams */ 219 + .prose pre.mermaid { 220 + @apply flex justify-center my-4 overflow-x-auto bg-transparent border-0; 221 + } 222 + 218 223 /* Base callout */ 219 224 details[data-callout] { 220 225 @apply border-l-4 pl-3 py-2 text-gray-800 dark:text-gray-200 my-4;
+3
nix/gomod2nix.toml
··· 545 545 [mod."gitlab.com/yawning/tuplehash"] 546 546 version = "v0.0.0-20230713102510-df83abbf9a02" 547 547 hash = "sha256-pehQduoaJRLchebhgvMYacVvbuNIBA++XkiqCuqdato=" 548 + [mod."go.abhg.dev/goldmark/mermaid"] 549 + version = "v0.6.0" 550 + hash = "sha256-JmjaCfzJU/M/R0TnXSzNwBaHmoLLooiXwQJeVRbZ3AQ=" 548 551 [mod."go.etcd.io/bbolt"] 549 552 version = "v1.4.0" 550 553 hash = "sha256-nR/YGQjwz6ue99IFbgw/01Pl8PhoOjpKiwVy5sJxlps="
+2
nix/pkgs/appview-static-files.nix
··· 6 6 inter-fonts-src, 7 7 ibm-plex-mono-src, 8 8 actor-typeahead-src, 9 + mermaid-src, 9 10 sqlite-lib, 10 11 tailwindcss, 11 12 dolly, ··· 21 22 mkdir -p $out/{fonts,icons,logos} && cd $out 22 23 cp -f ${htmx-src} htmx.min.js 23 24 cp -f ${htmx-ws-src} htmx-ext-ws.min.js 25 + cp -f ${mermaid-src} mermaid.min.js 24 26 cp -rf ${lucide-src}/*.svg icons/ 25 27 cp -f ${inter-fonts-src}/web/InterVariable*.woff2 fonts/ 26 28 cp -f ${inter-fonts-src}/web/InterDisplay*.woff2 fonts/

History

3 rounds 8 comments
sign up or login to add to the discussion
3 commits
expand
appview/db: add GetStarrers to list stargazers for a repo
appview: add "starred-by" page at /{user}/{repo}/stars
appview/pages: split star button to include starrers link
no conflicts, ready to merge
expand 0 comments
3 commits
expand
appview/db: add GetStarrers to list stargazers for a repo
appview: add "starred-by" page at /{user}/{repo}/stars
appview/pages: split star button to include starrers link
expand 4 comments

Looks like the diff got bigger from a bunch of unrelated changes. None of those were changed in any of commits, do I just need to rebase?

*any of my commits

Yeah that's a bug from our implementation ๐Ÿ˜… Rebasing to master will fix it.

๐Ÿ‘

3 commits
expand
appview/db: add GetStarrers to list stargazers for a repo
appview: add "starred-by" page at /{user}/{repo}/stars
appview/pages: split star button to include starrers link
expand 4 comments

Also, screenshots of what this new page and the stars button look like can be seen in the tangled discord

Thank you for the contribution! As .StarsHref is pretty constant for the repository, can we generate it from star handlers instead of receiving as a url query?

Or we can just not include the surrounding div on add Star htmx api.

I like the idea of generating it from a handler, I'll implement that.