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
+34 -213
Interdiff #0 โ†’ #1
appview/db/star.go

This file has not been changed.

+1 -1
appview/pages/pages.go
··· 675 675 IsStarred bool 676 676 SubjectAt syntax.ATURI 677 677 StarCount int 678 - HxSwapOob bool 679 678 StarsHref string 679 + HxSwapOob bool 680 680 } 681 681 682 682 func (p *Pages) StarBtnFragment(w io.Writer, params StarBtnFragmentParams) error {
+3 -3
appview/pages/templates/fragments/starBtn.html
··· 2 2 {{/* NOTE: this fragment is always replaced with hx-swap-oob */}} 3 3 <div 4 4 id="starBtn" 5 - class="flex w-full items-stretch overflow-hidden rounded border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 shadow-sm" 5 + class="flex w-full min-h-[30px] items-stretch overflow-hidden rounded border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 shadow-sm" 6 6 data-star-subject-at="{{ .SubjectAt }}" 7 7 {{ if .HxSwapOob }}hx-swap-oob='outerHTML:#starBtn[data-star-subject-at="{{ .SubjectAt }}"]'{{ end }} 8 8 > 9 9 <button 10 10 class="flex flex-1 justify-center gap-2 items-center px-2 group disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50 dark:hover:bg-gray-700" 11 11 {{ if .IsStarred }} 12 - hx-delete="/star?subject={{ .SubjectAt }}&countHint={{ .StarCount }}&starsHref={{ .StarsHref }}" 12 + hx-delete="/star?subject={{ .SubjectAt }}&countHint={{ .StarCount }}" 13 13 {{ else }} 14 - hx-post="/star?subject={{ .SubjectAt }}&countHint={{ .StarCount }}&starsHref={{ .StarsHref }}" 14 + hx-post="/star?subject={{ .SubjectAt }}&countHint={{ .StarCount }}" 15 15 {{ end }} 16 16 hx-trigger="click" 17 17 hx-disabled-elt="this"
+1 -1
appview/pages/templates/layouts/repobase.html
··· 116 116 (dict "SubjectAt" .RepoInfo.RepoAt 117 117 "IsStarred" .RepoInfo.IsStarred 118 118 "StarCount" .RepoInfo.Stats.StarCount 119 - "StarsHref" (printf "/%s/stars" .RepoInfo.FullName)) }} 119 + "StarsHref" .RepoInfo.StarsHref) }} 120 120 <a 121 121 class="btn text-sm no-underline hover:no-underline flex items-center gap-2 group" 122 122 hx-boost="true"
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.

+18 -2
appview/state/star.go
··· 1 1 package state 2 2 3 3 import ( 4 + "context" 5 + "fmt" 4 6 "log" 5 7 "net/http" 6 8 "time" ··· 30 32 return 31 33 } 32 34 33 - starsHref := r.URL.Query().Get("starsHref") 34 - 35 35 client, err := s.oauth.AuthorizedClient(r) 36 36 if err != nil { 37 37 log.Println("failed to authorize client", err) 38 38 return 39 39 } 40 40 41 + starsHref := s.starsHref(r.Context(), subjectUri) 42 + 41 43 switch r.Method { 42 44 case http.MethodPost: 43 45 ··· 128 130 return 129 131 } 130 132 133 + } 134 + 135 + func (s *State) starsHref(ctx context.Context, subjectUri syntax.ATURI) string { 136 + repo, err := db.GetRepoByAtUri(s.db, subjectUri.String()) 137 + if err != nil { 138 + return "" 139 + } 140 + 141 + id, err := s.idResolver.ResolveIdent(ctx, repo.Did) 142 + if err != nil { 143 + return fmt.Sprintf("/%s/%s/stars", repo.Did, repo.Name) 144 + } 145 + 146 + return fmt.Sprintf("/%s/%s/stars", id.Handle, repo.Name) 131 147 }
+3 -38
appview/oauth/handler.go
··· 6 6 "encoding/json" 7 7 "errors" 8 8 "fmt" 9 - "log/slog" 10 9 "net/http" 11 10 "slices" 12 11 "time" ··· 131 130 } 132 131 133 132 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) 135 134 if err != nil { 136 135 l.Error("failed to create session", "err", err) 137 136 return ··· 145 144 } 146 145 147 146 if err := session.putRecord(record, tangled.SpindleMemberNSID); err != nil { 148 - o.invalidateAppPasswordSession() 149 147 l.Error("failed to add to default spindle", "err", err) 150 148 return 151 149 } ··· 171 169 } 172 170 173 171 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) 175 173 if err != nil { 176 174 l.Error("failed to create session", "err", err) 177 175 return ··· 185 183 } 186 184 187 185 if err := session.putRecord(record, tangled.KnotMemberNSID); err != nil { 188 - o.invalidateAppPasswordSession() 189 186 l.Error("failed to add to default knot", "err", err) 190 187 return 191 188 } ··· 251 248 PdsEndpoint string 252 249 Did string 253 250 RateLimitBypass string 254 - Logger *slog.Logger 255 251 } 256 252 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) { 258 254 if appPassword == "" { 259 255 return nil, fmt.Errorf("no app password configured") 260 256 } ··· 288 284 sessionReq.Header.Set("x-ratelimit-bypass", rateLimitBypass) 289 285 } 290 286 291 - logger.Debug("creating app password session", "url", sessionURL, "headers", sessionReq.Header) 292 - 293 287 client := &http.Client{Timeout: 30 * time.Second} 294 288 sessionResp, err := client.Do(sessionReq) 295 289 if err != nil { ··· 309 303 session.PdsEndpoint = pdsEndpoint 310 304 session.Did = did 311 305 session.RateLimitBypass = rateLimitBypass 312 - session.Logger = logger 313 306 314 307 return &session, nil 315 308 } ··· 344 337 req.Header.Set("x-ratelimit-bypass", s.RateLimitBypass) 345 338 } 346 339 347 - s.Logger.Debug("putting record", "url", url, "collection", collection, "headers", req.Header) 348 - 349 340 client := &http.Client{Timeout: 30 * time.Second} 350 341 resp, err := client.Do(req) 351 342 if err != nil { ··· 359 350 360 351 return nil 361 352 } 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" 9 8 "time" 10 9 11 10 comatproto "github.com/bluesky-social/indigo/api/atproto" ··· 34 33 Enforcer *rbac.Enforcer 35 34 IdResolver *idresolver.Resolver 36 35 Logger *slog.Logger 37 - 38 - appPasswordSession *AppPasswordSession 39 - appPasswordSessionMu sync.Mutex 40 36 } 41 37 42 38 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" 26 25 htmlparse "golang.org/x/net/html" 27 26 28 27 "tangled.org/core/api/tangled" ··· 57 56 md := goldmark.New( 58 57 goldmark.WithExtensions( 59 58 extension.GFM, 60 - &mermaid.Extender{ 61 - RenderMode: mermaid.RenderModeClient, 62 - NoScript: true, 63 - }, 64 59 highlighting.NewHighlighting( 65 60 highlighting.WithFormatOptions( 66 61 chromahtml.Standalone(false),
-46
appview/pages/markup/markdown_test.go
··· 2 2 3 3 import ( 4 4 "bytes" 5 - "strings" 6 5 "testing" 7 6 ) 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 - } 53 7 54 8 func TestAtExtension_Rendering(t *testing.T) { 55 9 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")
+4
appview/pages/repoinfo/repoinfo.go
··· 35 35 return path.Join(r.ownerWithoutAt(), r.Name) 36 36 } 37 37 38 + func (r RepoInfo) StarsHref() string { 39 + return fmt.Sprintf("/%s/stars", r.FullName()) 40 + } 41 + 38 42 func (r RepoInfo) GetTabs() [][]string { 39 43 tabs := [][]string{ 40 44 {"overview", "/", "square-chart-gantt"},
-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> 59 42 <title>{{ block "title" . }}{{ end }}</title> 60 43 {{ block "extrameta" . }}{{ end }} 61 44 </head>
-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" }} 15 14 {{ end }} 16 15 17 16 {{ define "diffTopbar" }} ··· 230 229 }); 231 230 </script> 232 231 {{ 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 }}
+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 - }, 163 151 "nixpkgs": { 164 152 "locked": { 165 153 "lastModified": 1766070988, ··· 187 175 "indigo": "indigo", 188 176 "inter-fonts-src": "inter-fonts-src", 189 177 "lucide-src": "lucide-src", 190 - "mermaid-src": "mermaid-src", 191 178 "nixpkgs": "nixpkgs", 192 179 "sqlite-lib-src": "sqlite-lib-src" 193 180 }
+1 -7
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 - }; 44 40 ibm-plex-mono-src = { 45 41 url = "https://github.com/IBM/plex/releases/download/%40ibm%2Fplex-mono%401.1.0/ibm-plex-mono.zip"; 46 42 flake = false; ··· 63 59 sqlite-lib-src, 64 60 ibm-plex-mono-src, 65 61 actor-typeahead-src, 66 - mermaid-src, 67 62 ... 68 63 }: let 69 64 supportedSystems = ["x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin"]; ··· 90 85 lexgen = self.callPackage ./nix/pkgs/lexgen.nix {inherit indigo;}; 91 86 goat = self.callPackage ./nix/pkgs/goat.nix {inherit indigo;}; 92 87 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; 94 89 }; 95 90 appview = self.callPackage ./nix/pkgs/appview.nix {}; 96 91 docs = self.callPackage ./nix/pkgs/docs.nix { ··· 223 218 type = "app"; 224 219 program = toString (pkgs.writeShellScript "watch-appview" '' 225 220 echo "copying static files to appview/pages/static..." 226 - mkdir -p appview/pages/static 227 221 ${pkgs.coreutils}/bin/cp -fr --no-preserve=ownership ${packages'.appview-static-files}/* appview/pages/static 228 222 ${air-watcher "appview" ""}/bin/run 229 223 '');
-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 52 51 golang.org/x/crypto v0.40.0 53 52 golang.org/x/image v0.31.0 54 53 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= 114 108 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 115 109 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 116 110 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= ··· 181 175 github.com/go-git/go-git-fixtures/v5 v5.0.0-20241203230421-0753e18f8f03/go.mod h1:hMKrMnUE4W0SJ7bFyM00dyz/HoknZoptGWzrj6M+dEM= 182 176 github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY= 183 177 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= 186 178 github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= 187 179 github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 188 180 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= ··· 197 189 github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= 198 190 github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= 199 191 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= 206 192 github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= 207 193 github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 208 194 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= ··· 530 516 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8= 531 517 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q= 532 518 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= 535 519 go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= 536 520 go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= 537 521 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 - 223 218 /* Base callout */ 224 219 details[data-callout] { 225 220 @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=" 551 548 [mod."go.etcd.io/bbolt"] 552 549 version = "v1.4.0" 553 550 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, 10 9 sqlite-lib, 11 10 tailwindcss, 12 11 dolly, ··· 22 21 mkdir -p $out/{fonts,icons,logos} && cd $out 23 22 cp -f ${htmx-src} htmx.min.js 24 23 cp -f ${htmx-ws-src} htmx-ext-ws.min.js 25 - cp -f ${mermaid-src} mermaid.min.js 26 24 cp -rf ${lucide-src}/*.svg icons/ 27 25 cp -f ${inter-fonts-src}/web/InterVariable*.woff2 fonts/ 28 26 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.