feat(web): home rows show commits, last 30 days, latest tag and last activity (#62)
- commit
- 333c71081622ec84d1a304246618ab0f31ccc1f2
- author
- brice · · 2026-10-18 09:52 UTC
- committer
- bnema · · 2026-10-18 09:52 UTC
- parent
- 4c072b1
- signature
- SSH Unverified
SHA256:VOCA5lYARHcbKDZ7GhTV45VmcjWnJ6ZQIBazj17LivAThe key is unknown - refs
- tag: v0.23.0
32 files · +1328 -572
M internal/adapters/ermitegit/ermitegit.go +11 -0
| @@ -415,3 +415,14 @@ func (a *Adapter) ListCommits(ctx context.Context, repo domain.RepoName, q ports | |||
| 415 | 415 | func (a *Adapter) GetCommit(ctx context.Context, repo domain.RepoName, rev domain.Revision, limits domain.DiffLimits) (domain.CommitDetail, error) { | |
| 416 | 416 | return a.cli.GetCommit(ctx, repo, rev, limits) | |
| 417 | 417 | } | |
| 418 | + | ||
| 419 | + | // CountCommits is served by the wrapped reader: git counts from the commit | |
| 420 | + | // graph. | |
| 421 | + | func (a *Adapter) CountCommits(ctx context.Context, repo domain.RepoName, head domain.ObjectID, since time.Time) (int, error) { | |
| 422 | + | return a.cli.CountCommits(ctx, repo, head, since) | |
| 423 | + | } | |
| 424 | + | ||
| 425 | + | // LatestTag is served by the wrapped reader. | |
| 426 | + | func (a *Adapter) LatestTag(ctx context.Context, repo domain.RepoName) (domain.TagInfo, bool, error) { | |
| 427 | + | return a.cli.LatestTag(ctx, repo) | |
| 428 | + | } | |
| @@ -415,3 +415,14 @@ func (a *Adapter) ListCommits(ctx context.Context, repo domain.RepoName, q ports | |||||
| 415 | func (a *Adapter) GetCommit(ctx context.Context, repo domain.RepoName, rev domain.Revision, limits domain.DiffLimits) (domain.CommitDetail, error) { | 415 | func (a *Adapter) GetCommit(ctx context.Context, repo domain.RepoName, rev domain.Revision, limits domain.DiffLimits) (domain.CommitDetail, error) { | ||
| 416 | return a.cli.GetCommit(ctx, repo, rev, limits) | 416 | return a.cli.GetCommit(ctx, repo, rev, limits) | ||
| 417 | } | 417 | } | ||
| 418 | + | ||||
| 419 | + | // CountCommits is served by the wrapped reader: git counts from the commit | |||
| 420 | + | // graph. | |||
| 421 | + | func (a *Adapter) CountCommits(ctx context.Context, repo domain.RepoName, head domain.ObjectID, since time.Time) (int, error) { | |||
| 422 | + | return a.cli.CountCommits(ctx, repo, head, since) | |||
| 423 | + | } | |||
| 424 | + | ||||
| 425 | + | // LatestTag is served by the wrapped reader. | |||
| 426 | + | func (a *Adapter) LatestTag(ctx context.Context, repo domain.RepoName) (domain.TagInfo, bool, error) { | |||
| 427 | + | return a.cli.LatestTag(ctx, repo) | |||
| 428 | + | } | |||
A internal/adapters/gitcli/stats.go +88 -0
| @@ -0,0 +1,88 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | package gitcli | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "context" | |
| 7 | + | "fmt" | |
| 8 | + | "strconv" | |
| 9 | + | "strings" | |
| 10 | + | "time" | |
| 11 | + | ||
| 12 | + | "bnema.dev/ermite/internal/domain" | |
| 13 | + | ) | |
| 14 | + | ||
| 15 | + | // CountCommits runs `git rev-list --count`, from the commit graph when the | |
| 16 | + | // repository has one. A bound uses --since-as-filter, which looks at every | |
| 17 | + | // reachable commit: --since stops the walk at the first older one, and | |
| 18 | + | // misses newer commits behind it when dates are out of order. | |
| 19 | + | // | |
| 20 | + | //hexcheck:ignore suspicious-business-logic-in-adapter reads the count git prints | |
| 21 | + | func (g *Git) CountCommits(ctx context.Context, repo domain.RepoName, head domain.ObjectID, since time.Time) (int, error) { | |
| 22 | + | if head.IsZero() { | |
| 23 | + | return 0, fmt.Errorf("count commits: %w: no head", domain.ErrInvalid) | |
| 24 | + | } | |
| 25 | + | dir, err := g.repoDir(repo) | |
| 26 | + | if err != nil { | |
| 27 | + | return 0, err | |
| 28 | + | } | |
| 29 | + | args := []string{"rev-list", "--count"} | |
| 30 | + | if !since.IsZero() { | |
| 31 | + | args = append(args, "--since-as-filter=@"+strconv.FormatInt(since.Unix(), 10)) | |
| 32 | + | } | |
| 33 | + | out, err := g.run(ctx, readArgs(dir, append(args, "--end-of-options", head.String())...)...) | |
| 34 | + | if err != nil { | |
| 35 | + | if exitCode(err) == 128 { | |
| 36 | + | return 0, fmt.Errorf("count commits of %q: %w", repo, domain.ErrNotFound) | |
| 37 | + | } | |
| 38 | + | return 0, fmt.Errorf("count commits of %q: %w", repo, err) | |
| 39 | + | } | |
| 40 | + | n, err := strconv.Atoi(strings.TrimSpace(out)) | |
| 41 | + | if err != nil { | |
| 42 | + | return 0, fmt.Errorf("count commits of %q: git printed %q", repo, strings.TrimSpace(out)) | |
| 43 | + | } | |
| 44 | + | return n, nil | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | // LatestTag runs `git for-each-ref --sort=-creatordate --count=1` on the tags. | |
| 48 | + | // | |
| 49 | + | //hexcheck:ignore suspicious-business-logic-in-adapter reads the line git prints | |
| 50 | + | func (g *Git) LatestTag(ctx context.Context, repo domain.RepoName) (domain.TagInfo, bool, error) { | |
| 51 | + | dir, err := g.repoDir(repo) | |
| 52 | + | if err != nil { | |
| 53 | + | return domain.TagInfo{}, false, err | |
| 54 | + | } | |
| 55 | + | out, err := g.run(ctx, readArgs(dir, "for-each-ref", "--sort=-creatordate", "--count=1", | |
| 56 | + | "--format=%(refname:strip=2)%00%(creatordate:unix)%00%(objecttype)%00%(objectname)%00%(*objecttype)%00%(*objectname)", tagsRefPrefix)...) | |
| 57 | + | if err != nil { | |
| 58 | + | return domain.TagInfo{}, false, fmt.Errorf("latest tag of %q: %w", repo, err) | |
| 59 | + | } | |
| 60 | + | line := strings.TrimSuffix(out, "\n") | |
| 61 | + | if line == "" { | |
| 62 | + | return domain.TagInfo{}, false, nil | |
| 63 | + | } | |
| 64 | + | f := strings.Split(line, "\x00") | |
| 65 | + | if len(f) != 6 || f[0] == "" { | |
| 66 | + | return domain.TagInfo{}, false, fmt.Errorf("latest tag of %q: unexpected line %q", repo, truncateText(line)) | |
| 67 | + | } | |
| 68 | + | sec, err := strconv.ParseInt(f[1], 10, 64) | |
| 69 | + | if err != nil { | |
| 70 | + | return domain.TagInfo{}, false, fmt.Errorf("latest tag of %q: unexpected line %q", repo, truncateText(line)) | |
| 71 | + | } | |
| 72 | + | tag := domain.TagInfo{Name: domain.ValidGitText(f[0]), At: time.Unix(sec, 0).UTC()} | |
| 73 | + | // A lightweight tag names its commit; an annotated one is peeled to the | |
| 74 | + | // end of its chain of tags. | |
| 75 | + | commit := "" | |
| 76 | + | switch { | |
| 77 | + | case f[2] == "commit": | |
| 78 | + | commit = f[3] | |
| 79 | + | case f[4] == "commit": | |
| 80 | + | commit = f[5] | |
| 81 | + | } | |
| 82 | + | if commit != "" { | |
| 83 | + | if tag.CommitID, err = domain.ParseObjectID(commit); err != nil { | |
| 84 | + | return domain.TagInfo{}, false, fmt.Errorf("latest tag of %q: %w", repo, err) | |
| 85 | + | } | |
| 86 | + | } | |
| 87 | + | return tag, true, nil | |
| 88 | + | } | |
| @@ -0,0 +1,88 @@ | |||||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |||
| 2 | + | ||||
| 3 | + | package gitcli | |||
| 4 | + | ||||
| 5 | + | import ( | |||
| 6 | + | "context" | |||
| 7 | + | "fmt" | |||
| 8 | + | "strconv" | |||
| 9 | + | "strings" | |||
| 10 | + | "time" | |||
| 11 | + | ||||
| 12 | + | "bnema.dev/ermite/internal/domain" | |||
| 13 | + | ) | |||
| 14 | + | ||||
| 15 | + | // CountCommits runs `git rev-list --count`, from the commit graph when the | |||
| 16 | + | // repository has one. A bound uses --since-as-filter, which looks at every | |||
| 17 | + | // reachable commit: --since stops the walk at the first older one, and | |||
| 18 | + | // misses newer commits behind it when dates are out of order. | |||
| 19 | + | // | |||
| 20 | + | //hexcheck:ignore suspicious-business-logic-in-adapter reads the count git prints | |||
| 21 | + | func (g *Git) CountCommits(ctx context.Context, repo domain.RepoName, head domain.ObjectID, since time.Time) (int, error) { | |||
| 22 | + | if head.IsZero() { | |||
| 23 | + | return 0, fmt.Errorf("count commits: %w: no head", domain.ErrInvalid) | |||
| 24 | + | } | |||
| 25 | + | dir, err := g.repoDir(repo) | |||
| 26 | + | if err != nil { | |||
| 27 | + | return 0, err | |||
| 28 | + | } | |||
| 29 | + | args := []string{"rev-list", "--count"} | |||
| 30 | + | if !since.IsZero() { | |||
| 31 | + | args = append(args, "--since-as-filter=@"+strconv.FormatInt(since.Unix(), 10)) | |||
| 32 | + | } | |||
| 33 | + | out, err := g.run(ctx, readArgs(dir, append(args, "--end-of-options", head.String())...)...) | |||
| 34 | + | if err != nil { | |||
| 35 | + | if exitCode(err) == 128 { | |||
| 36 | + | return 0, fmt.Errorf("count commits of %q: %w", repo, domain.ErrNotFound) | |||
| 37 | + | } | |||
| 38 | + | return 0, fmt.Errorf("count commits of %q: %w", repo, err) | |||
| 39 | + | } | |||
| 40 | + | n, err := strconv.Atoi(strings.TrimSpace(out)) | |||
| 41 | + | if err != nil { | |||
| 42 | + | return 0, fmt.Errorf("count commits of %q: git printed %q", repo, strings.TrimSpace(out)) | |||
| 43 | + | } | |||
| 44 | + | return n, nil | |||
| 45 | + | } | |||
| 46 | + | ||||
| 47 | + | // LatestTag runs `git for-each-ref --sort=-creatordate --count=1` on the tags. | |||
| 48 | + | // | |||
| 49 | + | //hexcheck:ignore suspicious-business-logic-in-adapter reads the line git prints | |||
| 50 | + | func (g *Git) LatestTag(ctx context.Context, repo domain.RepoName) (domain.TagInfo, bool, error) { | |||
| 51 | + | dir, err := g.repoDir(repo) | |||
| 52 | + | if err != nil { | |||
| 53 | + | return domain.TagInfo{}, false, err | |||
| 54 | + | } | |||
| 55 | + | out, err := g.run(ctx, readArgs(dir, "for-each-ref", "--sort=-creatordate", "--count=1", | |||
| 56 | + | "--format=%(refname:strip=2)%00%(creatordate:unix)%00%(objecttype)%00%(objectname)%00%(*objecttype)%00%(*objectname)", tagsRefPrefix)...) | |||
| 57 | + | if err != nil { | |||
| 58 | + | return domain.TagInfo{}, false, fmt.Errorf("latest tag of %q: %w", repo, err) | |||
| 59 | + | } | |||
| 60 | + | line := strings.TrimSuffix(out, "\n") | |||
| 61 | + | if line == "" { | |||
| 62 | + | return domain.TagInfo{}, false, nil | |||
| 63 | + | } | |||
| 64 | + | f := strings.Split(line, "\x00") | |||
| 65 | + | if len(f) != 6 || f[0] == "" { | |||
| 66 | + | return domain.TagInfo{}, false, fmt.Errorf("latest tag of %q: unexpected line %q", repo, truncateText(line)) | |||
| 67 | + | } | |||
| 68 | + | sec, err := strconv.ParseInt(f[1], 10, 64) | |||
| 69 | + | if err != nil { | |||
| 70 | + | return domain.TagInfo{}, false, fmt.Errorf("latest tag of %q: unexpected line %q", repo, truncateText(line)) | |||
| 71 | + | } | |||
| 72 | + | tag := domain.TagInfo{Name: domain.ValidGitText(f[0]), At: time.Unix(sec, 0).UTC()} | |||
| 73 | + | // A lightweight tag names its commit; an annotated one is peeled to the | |||
| 74 | + | // end of its chain of tags. | |||
| 75 | + | commit := "" | |||
| 76 | + | switch { | |||
| 77 | + | case f[2] == "commit": | |||
| 78 | + | commit = f[3] | |||
| 79 | + | case f[4] == "commit": | |||
| 80 | + | commit = f[5] | |||
| 81 | + | } | |||
| 82 | + | if commit != "" { | |||
| 83 | + | if tag.CommitID, err = domain.ParseObjectID(commit); err != nil { | |||
| 84 | + | return domain.TagInfo{}, false, fmt.Errorf("latest tag of %q: %w", repo, err) | |||
| 85 | + | } | |||
| 86 | + | } | |||
| 87 | + | return tag, true, nil | |||
| 88 | + | } | |||
M internal/adapters/web/components.templ +23 -10
| @@ -52,16 +52,30 @@ templ HomeRowView(r HomeRow) { | |||
| 52 | 52 | } else { | |
| 53 | 53 | <p class="repo-desc mute">no description</p> | |
| 54 | 54 | } | |
| 55 | - | <p class="repo-meta"> | |
| 56 | - | if r.Branch != "" { | |
| 57 | - | <span class="repo-branch"><span class="ico i-branch" aria-hidden="true"></span>{ r.Branch }</span> | |
| 58 | - | } | |
| 59 | - | if r.PushedAt != "" { | |
| 60 | - | <span>pushed <time datetime={ r.PushedAt } title={ r.PushedAt }>{ r.Pushed }</time></span> | |
| 61 | - | } else { | |
| 62 | - | <span>never pushed</span> | |
| 55 | + | <dl class="repo-stats" aria-label={ "Figures of " + r.Name }> | |
| 56 | + | if s := r.Stats; s != nil { | |
| 57 | + | <div><dt>commits</dt><dd>{ s.Commits }</dd></div> | |
| 58 | + | <div><dt>last 30 days</dt><dd>{ s.Recent }</dd></div> | |
| 59 | + | if s.Tag != "" { | |
| 60 | + | <div> | |
| 61 | + | <dt>latest tag</dt> | |
| 62 | + | if s.TagHref != "" { | |
| 63 | + | <dd><a href={ templ.SafeURL(s.TagHref) } title={ "made " + s.TagWhen }>{ s.Tag }</a></dd> | |
| 64 | + | } else { | |
| 65 | + | <dd title={ "made " + s.TagWhen }>{ s.Tag }</dd> | |
| 66 | + | } | |
| 67 | + | </div> | |
| 68 | + | } | |
| 63 | 69 | } | |
| 64 | - | </p> | |
| 70 | + | <div> | |
| 71 | + | <dt>last activity</dt> | |
| 72 | + | if r.ActiveAt != "" { | |
| 73 | + | <dd><time datetime={ r.ActiveAt } title={ r.ActiveAt }>{ r.Active }</time></dd> | |
| 74 | + | } else { | |
| 75 | + | <dd class="mute">never</dd> | |
| 76 | + | } | |
| 77 | + | </div> | |
| 78 | + | </dl> | |
| 65 | 79 | </div> | |
| 66 | 80 | if r.CI != nil { | |
| 67 | 81 | <a class="repo-ci" href={ templ.SafeURL(r.CI.Href) } title={ r.CI.Title }> | |
| @@ -131,7 +145,6 @@ templ CommitRows(rows []CommitRow) { | |||
| 131 | 145 | } | |
| 132 | 146 | data-cursor?={ c.Cursor } | |
| 133 | 147 | > | |
| 134 | - | <td class="cur" aria-hidden="true"></td> | |
| 135 | 148 | <td class="c-sha mute">{ c.Short }</td> | |
| 136 | 149 | <td class="c-subject"> | |
| 137 | 150 | <a href={ templ.SafeURL(c.CHref) }>{ c.Subject }</a> | |
| @@ -52,16 +52,30 @@ templ HomeRowView(r HomeRow) { | |||||
| 52 | } else { | 52 | } else { | ||
| 53 | <p class="repo-desc mute">no description</p> | 53 | <p class="repo-desc mute">no description</p> | ||
| 54 | } | 54 | } | ||
| 55 | - | <p class="repo-meta"> | 55 | + | <dl class="repo-stats" aria-label={ "Figures of " + r.Name }> |
| 56 | - | if r.Branch != "" { | 56 | + | if s := r.Stats; s != nil { |
| 57 | - | <span class="repo-branch"><span class="ico i-branch" aria-hidden="true"></span>{ r.Branch }</span> | 57 | + | <div><dt>commits</dt><dd>{ s.Commits }</dd></div> |
| 58 | - | } | 58 | + | <div><dt>last 30 days</dt><dd>{ s.Recent }</dd></div> |
| 59 | - | if r.PushedAt != "" { | 59 | + | if s.Tag != "" { |
| 60 | - | <span>pushed <time datetime={ r.PushedAt } title={ r.PushedAt }>{ r.Pushed }</time></span> | 60 | + | <div> |
| 61 | - | } else { | 61 | + | <dt>latest tag</dt> |
| 62 | - | <span>never pushed</span> | 62 | + | if s.TagHref != "" { |
| 63 | + | <dd><a href={ templ.SafeURL(s.TagHref) } title={ "made " + s.TagWhen }>{ s.Tag }</a></dd> | |||
| 64 | + | } else { | |||
| 65 | + | <dd title={ "made " + s.TagWhen }>{ s.Tag }</dd> | |||
| 66 | + | } | |||
| 67 | + | </div> | |||
| 68 | + | } | |||
| 63 | } | 69 | } | ||
| 64 | - | </p> | 70 | + | <div> |
| 71 | + | <dt>last activity</dt> | |||
| 72 | + | if r.ActiveAt != "" { | |||
| 73 | + | <dd><time datetime={ r.ActiveAt } title={ r.ActiveAt }>{ r.Active }</time></dd> | |||
| 74 | + | } else { | |||
| 75 | + | <dd class="mute">never</dd> | |||
| 76 | + | } | |||
| 77 | + | </div> | |||
| 78 | + | </dl> | |||
| 65 | </div> | 79 | </div> | ||
| 66 | if r.CI != nil { | 80 | if r.CI != nil { | ||
| 67 | <a class="repo-ci" href={ templ.SafeURL(r.CI.Href) } title={ r.CI.Title }> | 81 | <a class="repo-ci" href={ templ.SafeURL(r.CI.Href) } title={ r.CI.Title }> | ||
| @@ -131,7 +145,6 @@ templ CommitRows(rows []CommitRow) { | |||||
| 131 | } | 145 | } | ||
| 132 | data-cursor?={ c.Cursor } | 146 | data-cursor?={ c.Cursor } | ||
| 133 | > | 147 | > | ||
| 134 | - | <td class="cur" aria-hidden="true"></td> | |||
| 135 | <td class="c-sha mute">{ c.Short }</td> | 148 | <td class="c-sha mute">{ c.Short }</td> | ||
| 136 | <td class="c-subject"> | 149 | <td class="c-subject"> | ||
| 137 | <a href={ templ.SafeURL(c.CHref) }>{ c.Subject }</a> | 150 | <a href={ templ.SafeURL(c.CHref) }>{ c.Subject }</a> | ||
M internal/adapters/web/components_templ.go +325 -209
| @@ -264,286 +264,402 @@ func HomeRowView(r HomeRow) templ.Component { | |||
| 264 | 264 | return templ_7745c5c3_Err | |
| 265 | 265 | } | |
| 266 | 266 | } | |
| 267 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<p class=\"repo-meta\">") | |
| 267 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<dl class=\"repo-stats\" aria-label=\"") | |
| 268 | 268 | if templ_7745c5c3_Err != nil { | |
| 269 | 269 | return templ_7745c5c3_Err | |
| 270 | 270 | } | |
| 271 | - | if r.Branch != "" { | |
| 272 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<span class=\"repo-branch\"><span class=\"ico i-branch\" aria-hidden=\"true\"></span>") | |
| 271 | + | var templ_7745c5c3_Var12 string | |
| 272 | + | templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.ResolveAttributeValue("Figures of " + r.Name) | |
| 273 | + | if templ_7745c5c3_Err != nil { | |
| 274 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 55, Col: 61} | |
| 275 | + | } | |
| 276 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var12) | |
| 277 | + | if templ_7745c5c3_Err != nil { | |
| 278 | + | return templ_7745c5c3_Err | |
| 279 | + | } | |
| 280 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\">") | |
| 281 | + | if templ_7745c5c3_Err != nil { | |
| 282 | + | return templ_7745c5c3_Err | |
| 283 | + | } | |
| 284 | + | if s := r.Stats; s != nil { | |
| 285 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<div><dt>commits</dt><dd>") | |
| 286 | + | if templ_7745c5c3_Err != nil { | |
| 287 | + | return templ_7745c5c3_Err | |
| 288 | + | } | |
| 289 | + | var templ_7745c5c3_Var13 string | |
| 290 | + | templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(s.Commits) | |
| 291 | + | if templ_7745c5c3_Err != nil { | |
| 292 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 57, Col: 41} | |
| 293 | + | } | |
| 294 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) | |
| 295 | + | if templ_7745c5c3_Err != nil { | |
| 296 | + | return templ_7745c5c3_Err | |
| 297 | + | } | |
| 298 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</dd></div><div><dt>last 30 days</dt><dd>") | |
| 273 | 299 | if templ_7745c5c3_Err != nil { | |
| 274 | 300 | return templ_7745c5c3_Err | |
| 275 | 301 | } | |
| 276 | - | var templ_7745c5c3_Var12 string | |
| 277 | - | templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(r.Branch) | |
| 302 | + | var templ_7745c5c3_Var14 string | |
| 303 | + | templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(s.Recent) | |
| 278 | 304 | if templ_7745c5c3_Err != nil { | |
| 279 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 57, Col: 94} | |
| 305 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 58, Col: 45} | |
| 280 | 306 | } | |
| 281 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) | |
| 307 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) | |
| 282 | 308 | if templ_7745c5c3_Err != nil { | |
| 283 | 309 | return templ_7745c5c3_Err | |
| 284 | 310 | } | |
| 285 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</span> ") | |
| 311 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</dd></div>") | |
| 286 | 312 | if templ_7745c5c3_Err != nil { | |
| 287 | 313 | return templ_7745c5c3_Err | |
| 288 | 314 | } | |
| 315 | + | if s.Tag != "" { | |
| 316 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<div><dt>latest tag</dt>") | |
| 317 | + | if templ_7745c5c3_Err != nil { | |
| 318 | + | return templ_7745c5c3_Err | |
| 319 | + | } | |
| 320 | + | if s.TagHref != "" { | |
| 321 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<dd><a href=\"") | |
| 322 | + | if templ_7745c5c3_Err != nil { | |
| 323 | + | return templ_7745c5c3_Err | |
| 324 | + | } | |
| 325 | + | var templ_7745c5c3_Var15 templ.SafeURL | |
| 326 | + | templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(s.TagHref)) | |
| 327 | + | if templ_7745c5c3_Err != nil { | |
| 328 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 63, Col: 46} | |
| 329 | + | } | |
| 330 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) | |
| 331 | + | if templ_7745c5c3_Err != nil { | |
| 332 | + | return templ_7745c5c3_Err | |
| 333 | + | } | |
| 334 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\" title=\"") | |
| 335 | + | if templ_7745c5c3_Err != nil { | |
| 336 | + | return templ_7745c5c3_Err | |
| 337 | + | } | |
| 338 | + | var templ_7745c5c3_Var16 string | |
| 339 | + | templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.ResolveAttributeValue("made " + s.TagWhen) | |
| 340 | + | if templ_7745c5c3_Err != nil { | |
| 341 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 63, Col: 76} | |
| 342 | + | } | |
| 343 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16) | |
| 344 | + | if templ_7745c5c3_Err != nil { | |
| 345 | + | return templ_7745c5c3_Err | |
| 346 | + | } | |
| 347 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\">") | |
| 348 | + | if templ_7745c5c3_Err != nil { | |
| 349 | + | return templ_7745c5c3_Err | |
| 350 | + | } | |
| 351 | + | var templ_7745c5c3_Var17 string | |
| 352 | + | templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(s.Tag) | |
| 353 | + | if templ_7745c5c3_Err != nil { | |
| 354 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 63, Col: 86} | |
| 355 | + | } | |
| 356 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) | |
| 357 | + | if templ_7745c5c3_Err != nil { | |
| 358 | + | return templ_7745c5c3_Err | |
| 359 | + | } | |
| 360 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</a></dd>") | |
| 361 | + | if templ_7745c5c3_Err != nil { | |
| 362 | + | return templ_7745c5c3_Err | |
| 363 | + | } | |
| 364 | + | } else { | |
| 365 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<dd title=\"") | |
| 366 | + | if templ_7745c5c3_Err != nil { | |
| 367 | + | return templ_7745c5c3_Err | |
| 368 | + | } | |
| 369 | + | var templ_7745c5c3_Var18 string | |
| 370 | + | templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.ResolveAttributeValue("made " + s.TagWhen) | |
| 371 | + | if templ_7745c5c3_Err != nil { | |
| 372 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 65, Col: 39} | |
| 373 | + | } | |
| 374 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18) | |
| 375 | + | if templ_7745c5c3_Err != nil { | |
| 376 | + | return templ_7745c5c3_Err | |
| 377 | + | } | |
| 378 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "\">") | |
| 379 | + | if templ_7745c5c3_Err != nil { | |
| 380 | + | return templ_7745c5c3_Err | |
| 381 | + | } | |
| 382 | + | var templ_7745c5c3_Var19 string | |
| 383 | + | templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(s.Tag) | |
| 384 | + | if templ_7745c5c3_Err != nil { | |
| 385 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 65, Col: 49} | |
| 386 | + | } | |
| 387 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) | |
| 388 | + | if templ_7745c5c3_Err != nil { | |
| 389 | + | return templ_7745c5c3_Err | |
| 390 | + | } | |
| 391 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</dd>") | |
| 392 | + | if templ_7745c5c3_Err != nil { | |
| 393 | + | return templ_7745c5c3_Err | |
| 394 | + | } | |
| 395 | + | } | |
| 396 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</div>") | |
| 397 | + | if templ_7745c5c3_Err != nil { | |
| 398 | + | return templ_7745c5c3_Err | |
| 399 | + | } | |
| 400 | + | } | |
| 289 | 401 | } | |
| 290 | - | if r.PushedAt != "" { | |
| 291 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<span>pushed <time datetime=\"") | |
| 402 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div><dt>last activity</dt>") | |
| 403 | + | if templ_7745c5c3_Err != nil { | |
| 404 | + | return templ_7745c5c3_Err | |
| 405 | + | } | |
| 406 | + | if r.ActiveAt != "" { | |
| 407 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<dd><time datetime=\"") | |
| 292 | 408 | if templ_7745c5c3_Err != nil { | |
| 293 | 409 | return templ_7745c5c3_Err | |
| 294 | 410 | } | |
| 295 | - | var templ_7745c5c3_Var13 string | |
| 296 | - | templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.PushedAt) | |
| 411 | + | var templ_7745c5c3_Var20 string | |
| 412 | + | templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.ActiveAt) | |
| 297 | 413 | if templ_7745c5c3_Err != nil { | |
| 298 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 60, Col: 45} | |
| 414 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 73, Col: 37} | |
| 299 | 415 | } | |
| 300 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13) | |
| 416 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var20) | |
| 301 | 417 | if templ_7745c5c3_Err != nil { | |
| 302 | 418 | return templ_7745c5c3_Err | |
| 303 | 419 | } | |
| 304 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" title=\"") | |
| 420 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "\" title=\"") | |
| 305 | 421 | if templ_7745c5c3_Err != nil { | |
| 306 | 422 | return templ_7745c5c3_Err | |
| 307 | 423 | } | |
| 308 | - | var templ_7745c5c3_Var14 string | |
| 309 | - | templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.PushedAt) | |
| 424 | + | var templ_7745c5c3_Var21 string | |
| 425 | + | templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.ActiveAt) | |
| 310 | 426 | if templ_7745c5c3_Err != nil { | |
| 311 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 60, Col: 66} | |
| 427 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 73, Col: 58} | |
| 312 | 428 | } | |
| 313 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14) | |
| 429 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var21) | |
| 314 | 430 | if templ_7745c5c3_Err != nil { | |
| 315 | 431 | return templ_7745c5c3_Err | |
| 316 | 432 | } | |
| 317 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\">") | |
| 433 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "\">") | |
| 318 | 434 | if templ_7745c5c3_Err != nil { | |
| 319 | 435 | return templ_7745c5c3_Err | |
| 320 | 436 | } | |
| 321 | - | var templ_7745c5c3_Var15 string | |
| 322 | - | templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(r.Pushed) | |
| 437 | + | var templ_7745c5c3_Var22 string | |
| 438 | + | templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(r.Active) | |
| 323 | 439 | if templ_7745c5c3_Err != nil { | |
| 324 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 60, Col: 79} | |
| 440 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 73, Col: 71} | |
| 325 | 441 | } | |
| 326 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) | |
| 442 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) | |
| 327 | 443 | if templ_7745c5c3_Err != nil { | |
| 328 | 444 | return templ_7745c5c3_Err | |
| 329 | 445 | } | |
| 330 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</time></span>") | |
| 446 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</time></dd>") | |
| 331 | 447 | if templ_7745c5c3_Err != nil { | |
| 332 | 448 | return templ_7745c5c3_Err | |
| 333 | 449 | } | |
| 334 | 450 | } else { | |
| 335 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<span>never pushed</span>") | |
| 451 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<dd class=\"mute\">never</dd>") | |
| 336 | 452 | if templ_7745c5c3_Err != nil { | |
| 337 | 453 | return templ_7745c5c3_Err | |
| 338 | 454 | } | |
| 339 | 455 | } | |
| 340 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</p></div>") | |
| 456 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</div></dl></div>") | |
| 341 | 457 | if templ_7745c5c3_Err != nil { | |
| 342 | 458 | return templ_7745c5c3_Err | |
| 343 | 459 | } | |
| 344 | 460 | if r.CI != nil { | |
| 345 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<a class=\"repo-ci\" href=\"") | |
| 461 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "<a class=\"repo-ci\" href=\"") | |
| 346 | 462 | if templ_7745c5c3_Err != nil { | |
| 347 | 463 | return templ_7745c5c3_Err | |
| 348 | 464 | } | |
| 349 | - | var templ_7745c5c3_Var16 templ.SafeURL | |
| 350 | - | templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.CI.Href)) | |
| 465 | + | var templ_7745c5c3_Var23 templ.SafeURL | |
| 466 | + | templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.CI.Href)) | |
| 351 | 467 | if templ_7745c5c3_Err != nil { | |
| 352 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 67, Col: 53} | |
| 468 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 81, Col: 53} | |
| 353 | 469 | } | |
| 354 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) | |
| 470 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) | |
| 355 | 471 | if templ_7745c5c3_Err != nil { | |
| 356 | 472 | return templ_7745c5c3_Err | |
| 357 | 473 | } | |
| 358 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\" title=\"") | |
| 474 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "\" title=\"") | |
| 359 | 475 | if templ_7745c5c3_Err != nil { | |
| 360 | 476 | return templ_7745c5c3_Err | |
| 361 | 477 | } | |
| 362 | - | var templ_7745c5c3_Var17 string | |
| 363 | - | templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.CI.Title) | |
| 478 | + | var templ_7745c5c3_Var24 string | |
| 479 | + | templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.CI.Title) | |
| 364 | 480 | if templ_7745c5c3_Err != nil { | |
| 365 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 67, Col: 74} | |
| 481 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 81, Col: 74} | |
| 366 | 482 | } | |
| 367 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var17) | |
| 483 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var24) | |
| 368 | 484 | if templ_7745c5c3_Err != nil { | |
| 369 | 485 | return templ_7745c5c3_Err | |
| 370 | 486 | } | |
| 371 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "\">") | |
| 487 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\">") | |
| 372 | 488 | if templ_7745c5c3_Err != nil { | |
| 373 | 489 | return templ_7745c5c3_Err | |
| 374 | 490 | } | |
| 375 | - | var templ_7745c5c3_Var18 = []any{"ck", "ck-" + r.CI.Kind} | |
| 376 | - | templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var18...) | |
| 491 | + | var templ_7745c5c3_Var25 = []any{"ck", "ck-" + r.CI.Kind} | |
| 492 | + | templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var25...) | |
| 377 | 493 | if templ_7745c5c3_Err != nil { | |
| 378 | 494 | return templ_7745c5c3_Err | |
| 379 | 495 | } | |
| 380 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<span class=\"") | |
| 496 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<span class=\"") | |
| 381 | 497 | if templ_7745c5c3_Err != nil { | |
| 382 | 498 | return templ_7745c5c3_Err | |
| 383 | 499 | } | |
| 384 | - | var templ_7745c5c3_Var19 string | |
| 385 | - | templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var18).String()) | |
| 500 | + | var templ_7745c5c3_Var26 string | |
| 501 | + | templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var25).String()) | |
| 386 | 502 | if templ_7745c5c3_Err != nil { | |
| 387 | 503 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 1, Col: 0} | |
| 388 | 504 | } | |
| 389 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var19) | |
| 505 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var26) | |
| 390 | 506 | if templ_7745c5c3_Err != nil { | |
| 391 | 507 | return templ_7745c5c3_Err | |
| 392 | 508 | } | |
| 393 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "\" aria-hidden=\"true\"></span> <span class=\"sr\">CI: </span>") | |
| 509 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "\" aria-hidden=\"true\"></span> <span class=\"sr\">CI: </span>") | |
| 394 | 510 | if templ_7745c5c3_Err != nil { | |
| 395 | 511 | return templ_7745c5c3_Err | |
| 396 | 512 | } | |
| 397 | - | var templ_7745c5c3_Var20 string | |
| 398 | - | templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(r.CI.Label) | |
| 513 | + | var templ_7745c5c3_Var27 string | |
| 514 | + | templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(r.CI.Label) | |
| 399 | 515 | if templ_7745c5c3_Err != nil { | |
| 400 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 69, Col: 44} | |
| 516 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 83, Col: 44} | |
| 401 | 517 | } | |
| 402 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) | |
| 518 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27)) | |
| 403 | 519 | if templ_7745c5c3_Err != nil { | |
| 404 | 520 | return templ_7745c5c3_Err | |
| 405 | 521 | } | |
| 406 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "</a> ") | |
| 522 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</a> ") | |
| 407 | 523 | if templ_7745c5c3_Err != nil { | |
| 408 | 524 | return templ_7745c5c3_Err | |
| 409 | 525 | } | |
| 410 | 526 | } | |
| 411 | 527 | if len(r.Commits) > 0 { | |
| 412 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<ul class=\"repo-commits\" aria-label=\"") | |
| 528 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "<ul class=\"repo-commits\" aria-label=\"") | |
| 413 | 529 | if templ_7745c5c3_Err != nil { | |
| 414 | 530 | return templ_7745c5c3_Err | |
| 415 | 531 | } | |
| 416 | - | var templ_7745c5c3_Var21 string | |
| 417 | - | templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.ResolveAttributeValue("Recent commits of " + r.Name) | |
| 532 | + | var templ_7745c5c3_Var28 string | |
| 533 | + | templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.ResolveAttributeValue("Recent commits of " + r.Name) | |
| 418 | 534 | if templ_7745c5c3_Err != nil { | |
| 419 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 73, Col: 70} | |
| 535 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 87, Col: 70} | |
| 420 | 536 | } | |
| 421 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var21) | |
| 537 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var28) | |
| 422 | 538 | if templ_7745c5c3_Err != nil { | |
| 423 | 539 | return templ_7745c5c3_Err | |
| 424 | 540 | } | |
| 425 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "\">") | |
| 541 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "\">") | |
| 426 | 542 | if templ_7745c5c3_Err != nil { | |
| 427 | 543 | return templ_7745c5c3_Err | |
| 428 | 544 | } | |
| 429 | 545 | for _, c := range r.Commits { | |
| 430 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<li><a class=\"rc-sha\" href=\"") | |
| 546 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "<li><a class=\"rc-sha\" href=\"") | |
| 431 | 547 | if templ_7745c5c3_Err != nil { | |
| 432 | 548 | return templ_7745c5c3_Err | |
| 433 | 549 | } | |
| 434 | - | var templ_7745c5c3_Var22 templ.SafeURL | |
| 435 | - | templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref)) | |
| 550 | + | var templ_7745c5c3_Var29 templ.SafeURL | |
| 551 | + | templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref)) | |
| 436 | 552 | if templ_7745c5c3_Err != nil { | |
| 437 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 76, Col: 53} | |
| 553 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 90, Col: 53} | |
| 438 | 554 | } | |
| 439 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) | |
| 555 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29)) | |
| 440 | 556 | if templ_7745c5c3_Err != nil { | |
| 441 | 557 | return templ_7745c5c3_Err | |
| 442 | 558 | } | |
| 443 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "\">") | |
| 559 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "\">") | |
| 444 | 560 | if templ_7745c5c3_Err != nil { | |
| 445 | 561 | return templ_7745c5c3_Err | |
| 446 | 562 | } | |
| 447 | - | var templ_7745c5c3_Var23 string | |
| 448 | - | templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short) | |
| 563 | + | var templ_7745c5c3_Var30 string | |
| 564 | + | templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short) | |
| 449 | 565 | if templ_7745c5c3_Err != nil { | |
| 450 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 76, Col: 65} | |
| 566 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 90, Col: 65} | |
| 451 | 567 | } | |
| 452 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) | |
| 568 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) | |
| 453 | 569 | if templ_7745c5c3_Err != nil { | |
| 454 | 570 | return templ_7745c5c3_Err | |
| 455 | 571 | } | |
| 456 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</a> <span class=\"rc-subject\" title=\"") | |
| 572 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "</a> <span class=\"rc-subject\" title=\"") | |
| 457 | 573 | if templ_7745c5c3_Err != nil { | |
| 458 | 574 | return templ_7745c5c3_Err | |
| 459 | 575 | } | |
| 460 | - | var templ_7745c5c3_Var24 string | |
| 461 | - | templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.Subject) | |
| 576 | + | var templ_7745c5c3_Var31 string | |
| 577 | + | templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.Subject) | |
| 462 | 578 | if templ_7745c5c3_Err != nil { | |
| 463 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 77, Col: 48} | |
| 579 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 91, Col: 48} | |
| 464 | 580 | } | |
| 465 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var24) | |
| 581 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var31) | |
| 466 | 582 | if templ_7745c5c3_Err != nil { | |
| 467 | 583 | return templ_7745c5c3_Err | |
| 468 | 584 | } | |
| 469 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "\">") | |
| 585 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\">") | |
| 470 | 586 | if templ_7745c5c3_Err != nil { | |
| 471 | 587 | return templ_7745c5c3_Err | |
| 472 | 588 | } | |
| 473 | - | var templ_7745c5c3_Var25 string | |
| 474 | - | templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject) | |
| 589 | + | var templ_7745c5c3_Var32 string | |
| 590 | + | templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject) | |
| 475 | 591 | if templ_7745c5c3_Err != nil { | |
| 476 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 77, Col: 62} | |
| 592 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 91, Col: 62} | |
| 477 | 593 | } | |
| 478 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25)) | |
| 594 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32)) | |
| 479 | 595 | if templ_7745c5c3_Err != nil { | |
| 480 | 596 | return templ_7745c5c3_Err | |
| 481 | 597 | } | |
| 482 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</span> ") | |
| 598 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "</span> ") | |
| 483 | 599 | if templ_7745c5c3_Err != nil { | |
| 484 | 600 | return templ_7745c5c3_Err | |
| 485 | 601 | } | |
| 486 | 602 | if c.WhenAt != "" { | |
| 487 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<time class=\"dim\" datetime=\"") | |
| 603 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "<time class=\"dim\" datetime=\"") | |
| 488 | 604 | if templ_7745c5c3_Err != nil { | |
| 489 | 605 | return templ_7745c5c3_Err | |
| 490 | 606 | } | |
| 491 | - | var templ_7745c5c3_Var26 string | |
| 492 | - | templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | |
| 607 | + | var templ_7745c5c3_Var33 string | |
| 608 | + | templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | |
| 493 | 609 | if templ_7745c5c3_Err != nil { | |
| 494 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 79, Col: 44} | |
| 610 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 93, Col: 44} | |
| 495 | 611 | } | |
| 496 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var26) | |
| 612 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var33) | |
| 497 | 613 | if templ_7745c5c3_Err != nil { | |
| 498 | 614 | return templ_7745c5c3_Err | |
| 499 | 615 | } | |
| 500 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\" title=\"") | |
| 616 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\" title=\"") | |
| 501 | 617 | if templ_7745c5c3_Err != nil { | |
| 502 | 618 | return templ_7745c5c3_Err | |
| 503 | 619 | } | |
| 504 | - | var templ_7745c5c3_Var27 string | |
| 505 | - | templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | |
| 620 | + | var templ_7745c5c3_Var34 string | |
| 621 | + | templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | |
| 506 | 622 | if templ_7745c5c3_Err != nil { | |
| 507 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 79, Col: 63} | |
| 623 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 93, Col: 63} | |
| 508 | 624 | } | |
| 509 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var27) | |
| 625 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var34) | |
| 510 | 626 | if templ_7745c5c3_Err != nil { | |
| 511 | 627 | return templ_7745c5c3_Err | |
| 512 | 628 | } | |
| 513 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "\">") | |
| 629 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "\">") | |
| 514 | 630 | if templ_7745c5c3_Err != nil { | |
| 515 | 631 | return templ_7745c5c3_Err | |
| 516 | 632 | } | |
| 517 | - | var templ_7745c5c3_Var28 string | |
| 518 | - | templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) | |
| 633 | + | var templ_7745c5c3_Var35 string | |
| 634 | + | templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) | |
| 519 | 635 | if templ_7745c5c3_Err != nil { | |
| 520 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 79, Col: 74} | |
| 636 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 93, Col: 74} | |
| 521 | 637 | } | |
| 522 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) | |
| 638 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35)) | |
| 523 | 639 | if templ_7745c5c3_Err != nil { | |
| 524 | 640 | return templ_7745c5c3_Err | |
| 525 | 641 | } | |
| 526 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</time>") | |
| 642 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "</time>") | |
| 527 | 643 | if templ_7745c5c3_Err != nil { | |
| 528 | 644 | return templ_7745c5c3_Err | |
| 529 | 645 | } | |
| 530 | 646 | } | |
| 531 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</li>") | |
| 647 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "</li>") | |
| 532 | 648 | if templ_7745c5c3_Err != nil { | |
| 533 | 649 | return templ_7745c5c3_Err | |
| 534 | 650 | } | |
| 535 | 651 | } | |
| 536 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</ul>") | |
| 652 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "</ul>") | |
| 537 | 653 | if templ_7745c5c3_Err != nil { | |
| 538 | 654 | return templ_7745c5c3_Err | |
| 539 | 655 | } | |
| 540 | 656 | } else if r.NoCommits { | |
| 541 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<p class=\"repo-commits mute\">no commits yet</p>") | |
| 657 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "<p class=\"repo-commits mute\">no commits yet</p>") | |
| 542 | 658 | if templ_7745c5c3_Err != nil { | |
| 543 | 659 | return templ_7745c5c3_Err | |
| 544 | 660 | } | |
| 545 | 661 | } | |
| 546 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</li>") | |
| 662 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "</li>") | |
| 547 | 663 | if templ_7745c5c3_Err != nil { | |
| 548 | 664 | return templ_7745c5c3_Err | |
| 549 | 665 | } | |
| @@ -568,66 +684,66 @@ func Outline(headings []Heading) templ.Component { | |||
| 568 | 684 | }() | |
| 569 | 685 | } | |
| 570 | 686 | ctx = templ.InitializeContext(ctx) | |
| 571 | - | templ_7745c5c3_Var29 := templ.GetChildren(ctx) | |
| 572 | - | if templ_7745c5c3_Var29 == nil { | |
| 573 | - | templ_7745c5c3_Var29 = templ.NopComponent | |
| 687 | + | templ_7745c5c3_Var36 := templ.GetChildren(ctx) | |
| 688 | + | if templ_7745c5c3_Var36 == nil { | |
| 689 | + | templ_7745c5c3_Var36 = templ.NopComponent | |
| 574 | 690 | } | |
| 575 | 691 | ctx = templ.ClearChildren(ctx) | |
| 576 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "<nav class=\"outline\" aria-label=\"Outline\">") | |
| 692 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "<nav class=\"outline\" aria-label=\"Outline\">") | |
| 577 | 693 | if templ_7745c5c3_Err != nil { | |
| 578 | 694 | return templ_7745c5c3_Err | |
| 579 | 695 | } | |
| 580 | 696 | for _, h := range headings { | |
| 581 | - | var templ_7745c5c3_Var30 = []any{"l" + itoa(h.Level)} | |
| 582 | - | templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var30...) | |
| 697 | + | var templ_7745c5c3_Var37 = []any{"l" + itoa(h.Level)} | |
| 698 | + | templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var37...) | |
| 583 | 699 | if templ_7745c5c3_Err != nil { | |
| 584 | 700 | return templ_7745c5c3_Err | |
| 585 | 701 | } | |
| 586 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "<a class=\"") | |
| 702 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "<a class=\"") | |
| 587 | 703 | if templ_7745c5c3_Err != nil { | |
| 588 | 704 | return templ_7745c5c3_Err | |
| 589 | 705 | } | |
| 590 | - | var templ_7745c5c3_Var31 string | |
| 591 | - | templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var30).String()) | |
| 706 | + | var templ_7745c5c3_Var38 string | |
| 707 | + | templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var37).String()) | |
| 592 | 708 | if templ_7745c5c3_Err != nil { | |
| 593 | 709 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 1, Col: 0} | |
| 594 | 710 | } | |
| 595 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var31) | |
| 711 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var38) | |
| 596 | 712 | if templ_7745c5c3_Err != nil { | |
| 597 | 713 | return templ_7745c5c3_Err | |
| 598 | 714 | } | |
| 599 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\" href=\"") | |
| 715 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "\" href=\"") | |
| 600 | 716 | if templ_7745c5c3_Err != nil { | |
| 601 | 717 | return templ_7745c5c3_Err | |
| 602 | 718 | } | |
| 603 | - | var templ_7745c5c3_Var32 templ.SafeURL | |
| 604 | - | templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("#" + h.ID)) | |
| 719 | + | var templ_7745c5c3_Var39 templ.SafeURL | |
| 720 | + | templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("#" + h.ID)) | |
| 605 | 721 | if templ_7745c5c3_Err != nil { | |
| 606 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 94, Col: 68} | |
| 722 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 108, Col: 68} | |
| 607 | 723 | } | |
| 608 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32)) | |
| 724 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39)) | |
| 609 | 725 | if templ_7745c5c3_Err != nil { | |
| 610 | 726 | return templ_7745c5c3_Err | |
| 611 | 727 | } | |
| 612 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\">") | |
| 728 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "\">") | |
| 613 | 729 | if templ_7745c5c3_Err != nil { | |
| 614 | 730 | return templ_7745c5c3_Err | |
| 615 | 731 | } | |
| 616 | - | var templ_7745c5c3_Var33 string | |
| 617 | - | templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(h.Text) | |
| 732 | + | var templ_7745c5c3_Var40 string | |
| 733 | + | templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(h.Text) | |
| 618 | 734 | if templ_7745c5c3_Err != nil { | |
| 619 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 94, Col: 79} | |
| 735 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 108, Col: 79} | |
| 620 | 736 | } | |
| 621 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33)) | |
| 737 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40)) | |
| 622 | 738 | if templ_7745c5c3_Err != nil { | |
| 623 | 739 | return templ_7745c5c3_Err | |
| 624 | 740 | } | |
| 625 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "</a>") | |
| 741 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "</a>") | |
| 626 | 742 | if templ_7745c5c3_Err != nil { | |
| 627 | 743 | return templ_7745c5c3_Err | |
| 628 | 744 | } | |
| 629 | 745 | } | |
| 630 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</nav>") | |
| 746 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "</nav>") | |
| 631 | 747 | if templ_7745c5c3_Err != nil { | |
| 632 | 748 | return templ_7745c5c3_Err | |
| 633 | 749 | } | |
| @@ -652,25 +768,25 @@ func ReadmeCard(r Readme) templ.Component { | |||
| 652 | 768 | }() | |
| 653 | 769 | } | |
| 654 | 770 | ctx = templ.InitializeContext(ctx) | |
| 655 | - | templ_7745c5c3_Var34 := templ.GetChildren(ctx) | |
| 656 | - | if templ_7745c5c3_Var34 == nil { | |
| 657 | - | templ_7745c5c3_Var34 = templ.NopComponent | |
| 771 | + | templ_7745c5c3_Var41 := templ.GetChildren(ctx) | |
| 772 | + | if templ_7745c5c3_Var41 == nil { | |
| 773 | + | templ_7745c5c3_Var41 = templ.NopComponent | |
| 658 | 774 | } | |
| 659 | 775 | ctx = templ.ClearChildren(ctx) | |
| 660 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<section class=\"card readme\" aria-labelledby=\"readme-h\"><h2 class=\"card-h\" id=\"readme-h\"><span class=\"ico i-file\" aria-hidden=\"true\"></span> ") | |
| 776 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "<section class=\"card readme\" aria-labelledby=\"readme-h\"><h2 class=\"card-h\" id=\"readme-h\"><span class=\"ico i-file\" aria-hidden=\"true\"></span> ") | |
| 661 | 777 | if templ_7745c5c3_Err != nil { | |
| 662 | 778 | return templ_7745c5c3_Err | |
| 663 | 779 | } | |
| 664 | - | var templ_7745c5c3_Var35 string | |
| 665 | - | templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(r.Name) | |
| 780 | + | var templ_7745c5c3_Var42 string | |
| 781 | + | templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(r.Name) | |
| 666 | 782 | if templ_7745c5c3_Err != nil { | |
| 667 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 104, Col: 11} | |
| 783 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 118, Col: 11} | |
| 668 | 784 | } | |
| 669 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35)) | |
| 785 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) | |
| 670 | 786 | if templ_7745c5c3_Err != nil { | |
| 671 | 787 | return templ_7745c5c3_Err | |
| 672 | 788 | } | |
| 673 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "</h2>") | |
| 789 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "</h2>") | |
| 674 | 790 | if templ_7745c5c3_Err != nil { | |
| 675 | 791 | return templ_7745c5c3_Err | |
| 676 | 792 | } | |
| @@ -678,7 +794,7 @@ func ReadmeCard(r Readme) templ.Component { | |||
| 678 | 794 | if templ_7745c5c3_Err != nil { | |
| 679 | 795 | return templ_7745c5c3_Err | |
| 680 | 796 | } | |
| 681 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "</section>") | |
| 797 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "</section>") | |
| 682 | 798 | if templ_7745c5c3_Err != nil { | |
| 683 | 799 | return templ_7745c5c3_Err | |
| 684 | 800 | } | |
| @@ -704,43 +820,43 @@ func ReadmeView(r Readme) templ.Component { | |||
| 704 | 820 | }() | |
| 705 | 821 | } | |
| 706 | 822 | ctx = templ.InitializeContext(ctx) | |
| 707 | - | templ_7745c5c3_Var36 := templ.GetChildren(ctx) | |
| 708 | - | if templ_7745c5c3_Var36 == nil { | |
| 709 | - | templ_7745c5c3_Var36 = templ.NopComponent | |
| 823 | + | templ_7745c5c3_Var43 := templ.GetChildren(ctx) | |
| 824 | + | if templ_7745c5c3_Var43 == nil { | |
| 825 | + | templ_7745c5c3_Var43 = templ.NopComponent | |
| 710 | 826 | } | |
| 711 | 827 | ctx = templ.ClearChildren(ctx) | |
| 712 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "<article class=\"prose\" aria-label=\"") | |
| 828 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "<article class=\"prose\" aria-label=\"") | |
| 713 | 829 | if templ_7745c5c3_Err != nil { | |
| 714 | 830 | return templ_7745c5c3_Err | |
| 715 | 831 | } | |
| 716 | - | var templ_7745c5c3_Var37 string | |
| 717 | - | templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.Name) | |
| 832 | + | var templ_7745c5c3_Var44 string | |
| 833 | + | templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.Name) | |
| 718 | 834 | if templ_7745c5c3_Err != nil { | |
| 719 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 113, Col: 43} | |
| 835 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 127, Col: 43} | |
| 720 | 836 | } | |
| 721 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var37) | |
| 837 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var44) | |
| 722 | 838 | if templ_7745c5c3_Err != nil { | |
| 723 | 839 | return templ_7745c5c3_Err | |
| 724 | 840 | } | |
| 725 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "\" hx-ignore>") | |
| 841 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "\" hx-ignore>") | |
| 726 | 842 | if templ_7745c5c3_Err != nil { | |
| 727 | 843 | return templ_7745c5c3_Err | |
| 728 | 844 | } | |
| 729 | 845 | if r.Note != "" { | |
| 730 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "<p class=\"dim\">") | |
| 846 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "<p class=\"dim\">") | |
| 731 | 847 | if templ_7745c5c3_Err != nil { | |
| 732 | 848 | return templ_7745c5c3_Err | |
| 733 | 849 | } | |
| 734 | - | var templ_7745c5c3_Var38 string | |
| 735 | - | templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(r.Note) | |
| 850 | + | var templ_7745c5c3_Var45 string | |
| 851 | + | templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(r.Note) | |
| 736 | 852 | if templ_7745c5c3_Err != nil { | |
| 737 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 115, Col: 26} | |
| 853 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 129, Col: 26} | |
| 738 | 854 | } | |
| 739 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38)) | |
| 855 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) | |
| 740 | 856 | if templ_7745c5c3_Err != nil { | |
| 741 | 857 | return templ_7745c5c3_Err | |
| 742 | 858 | } | |
| 743 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "</p>") | |
| 859 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "</p>") | |
| 744 | 860 | if templ_7745c5c3_Err != nil { | |
| 745 | 861 | return templ_7745c5c3_Err | |
| 746 | 862 | } | |
| @@ -750,7 +866,7 @@ func ReadmeView(r Readme) templ.Component { | |||
| 750 | 866 | return templ_7745c5c3_Err | |
| 751 | 867 | } | |
| 752 | 868 | } | |
| 753 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "</article>") | |
| 869 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "</article>") | |
| 754 | 870 | if templ_7745c5c3_Err != nil { | |
| 755 | 871 | return templ_7745c5c3_Err | |
| 756 | 872 | } | |
| @@ -776,133 +892,133 @@ func CommitRows(rows []CommitRow) templ.Component { | |||
| 776 | 892 | }() | |
| 777 | 893 | } | |
| 778 | 894 | ctx = templ.InitializeContext(ctx) | |
| 779 | - | templ_7745c5c3_Var39 := templ.GetChildren(ctx) | |
| 780 | - | if templ_7745c5c3_Var39 == nil { | |
| 781 | - | templ_7745c5c3_Var39 = templ.NopComponent | |
| 895 | + | templ_7745c5c3_Var46 := templ.GetChildren(ctx) | |
| 896 | + | if templ_7745c5c3_Var46 == nil { | |
| 897 | + | templ_7745c5c3_Var46 = templ.NopComponent | |
| 782 | 898 | } | |
| 783 | 899 | ctx = templ.ClearChildren(ctx) | |
| 784 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "<table class=\"list commits\" aria-label=\"Commits\"><tbody data-rows>") | |
| 900 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "<table class=\"list commits\" aria-label=\"Commits\"><tbody data-rows>") | |
| 785 | 901 | if templ_7745c5c3_Err != nil { | |
| 786 | 902 | return templ_7745c5c3_Err | |
| 787 | 903 | } | |
| 788 | 904 | for _, c := range rows { | |
| 789 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "<tr") | |
| 905 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "<tr") | |
| 790 | 906 | if templ_7745c5c3_Err != nil { | |
| 791 | 907 | return templ_7745c5c3_Err | |
| 792 | 908 | } | |
| 793 | 909 | if c.ID != "" { | |
| 794 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, " id=\"") | |
| 910 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, " id=\"") | |
| 795 | 911 | if templ_7745c5c3_Err != nil { | |
| 796 | 912 | return templ_7745c5c3_Err | |
| 797 | 913 | } | |
| 798 | - | var templ_7745c5c3_Var40 string | |
| 799 | - | templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.ID) | |
| 914 | + | var templ_7745c5c3_Var47 string | |
| 915 | + | templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.ID) | |
| 800 | 916 | if templ_7745c5c3_Err != nil { | |
| 801 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 130, Col: 15} | |
| 917 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 144, Col: 15} | |
| 802 | 918 | } | |
| 803 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var40) | |
| 919 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var47) | |
| 804 | 920 | if templ_7745c5c3_Err != nil { | |
| 805 | 921 | return templ_7745c5c3_Err | |
| 806 | 922 | } | |
| 807 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "\"") | |
| 923 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "\"") | |
| 808 | 924 | if templ_7745c5c3_Err != nil { | |
| 809 | 925 | return templ_7745c5c3_Err | |
| 810 | 926 | } | |
| 811 | 927 | } | |
| 812 | 928 | if c.Cursor { | |
| 813 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, " data-cursor") | |
| 929 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, " data-cursor") | |
| 814 | 930 | if templ_7745c5c3_Err != nil { | |
| 815 | 931 | return templ_7745c5c3_Err | |
| 816 | 932 | } | |
| 817 | 933 | } | |
| 818 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "><td class=\"cur\" aria-hidden=\"true\"></td><td class=\"c-sha mute\">") | |
| 934 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "><td class=\"c-sha mute\">") | |
| 819 | 935 | if templ_7745c5c3_Err != nil { | |
| 820 | 936 | return templ_7745c5c3_Err | |
| 821 | 937 | } | |
| 822 | - | var templ_7745c5c3_Var41 string | |
| 823 | - | templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short) | |
| 938 | + | var templ_7745c5c3_Var48 string | |
| 939 | + | templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short) | |
| 824 | 940 | if templ_7745c5c3_Err != nil { | |
| 825 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 135, Col: 37} | |
| 941 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 148, Col: 37} | |
| 826 | 942 | } | |
| 827 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41)) | |
| 943 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48)) | |
| 828 | 944 | if templ_7745c5c3_Err != nil { | |
| 829 | 945 | return templ_7745c5c3_Err | |
| 830 | 946 | } | |
| 831 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "</td><td class=\"c-subject\"><a href=\"") | |
| 947 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "</td><td class=\"c-subject\"><a href=\"") | |
| 832 | 948 | if templ_7745c5c3_Err != nil { | |
| 833 | 949 | return templ_7745c5c3_Err | |
| 834 | 950 | } | |
| 835 | - | var templ_7745c5c3_Var42 templ.SafeURL | |
| 836 | - | templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref)) | |
| 951 | + | var templ_7745c5c3_Var49 templ.SafeURL | |
| 952 | + | templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref)) | |
| 837 | 953 | if templ_7745c5c3_Err != nil { | |
| 838 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 137, Col: 38} | |
| 954 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 150, Col: 38} | |
| 839 | 955 | } | |
| 840 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) | |
| 956 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49)) | |
| 841 | 957 | if templ_7745c5c3_Err != nil { | |
| 842 | 958 | return templ_7745c5c3_Err | |
| 843 | 959 | } | |
| 844 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "\">") | |
| 960 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "\">") | |
| 845 | 961 | if templ_7745c5c3_Err != nil { | |
| 846 | 962 | return templ_7745c5c3_Err | |
| 847 | 963 | } | |
| 848 | - | var templ_7745c5c3_Var43 string | |
| 849 | - | templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject) | |
| 964 | + | var templ_7745c5c3_Var50 string | |
| 965 | + | templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject) | |
| 850 | 966 | if templ_7745c5c3_Err != nil { | |
| 851 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 137, Col: 52} | |
| 967 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 150, Col: 52} | |
| 852 | 968 | } | |
| 853 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43)) | |
| 969 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var50)) | |
| 854 | 970 | if templ_7745c5c3_Err != nil { | |
| 855 | 971 | return templ_7745c5c3_Err | |
| 856 | 972 | } | |
| 857 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "</a> ") | |
| 973 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "</a> ") | |
| 858 | 974 | if templ_7745c5c3_Err != nil { | |
| 859 | 975 | return templ_7745c5c3_Err | |
| 860 | 976 | } | |
| 861 | 977 | if c.Merge { | |
| 862 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "<span class=\"pill\">merge</span> ") | |
| 978 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "<span class=\"pill\">merge</span> ") | |
| 863 | 979 | if templ_7745c5c3_Err != nil { | |
| 864 | 980 | return templ_7745c5c3_Err | |
| 865 | 981 | } | |
| 866 | 982 | } | |
| 867 | 983 | for _, b := range c.Branches { | |
| 868 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "<span class=\"pill\">") | |
| 984 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "<span class=\"pill\">") | |
| 869 | 985 | if templ_7745c5c3_Err != nil { | |
| 870 | 986 | return templ_7745c5c3_Err | |
| 871 | 987 | } | |
| 872 | - | var templ_7745c5c3_Var44 string | |
| 873 | - | templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(b) | |
| 988 | + | var templ_7745c5c3_Var51 string | |
| 989 | + | templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(b) | |
| 874 | 990 | if templ_7745c5c3_Err != nil { | |
| 875 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 142, Col: 29} | |
| 991 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 155, Col: 29} | |
| 876 | 992 | } | |
| 877 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44)) | |
| 993 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51)) | |
| 878 | 994 | if templ_7745c5c3_Err != nil { | |
| 879 | 995 | return templ_7745c5c3_Err | |
| 880 | 996 | } | |
| 881 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "</span> ") | |
| 997 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "</span> ") | |
| 882 | 998 | if templ_7745c5c3_Err != nil { | |
| 883 | 999 | return templ_7745c5c3_Err | |
| 884 | 1000 | } | |
| 885 | 1001 | } | |
| 886 | 1002 | for _, t := range c.Tags { | |
| 887 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "<span class=\"pill tag\"><span class=\"sr\">tag </span>") | |
| 1003 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "<span class=\"pill tag\"><span class=\"sr\">tag </span>") | |
| 888 | 1004 | if templ_7745c5c3_Err != nil { | |
| 889 | 1005 | return templ_7745c5c3_Err | |
| 890 | 1006 | } | |
| 891 | - | var templ_7745c5c3_Var45 string | |
| 892 | - | templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(t) | |
| 1007 | + | var templ_7745c5c3_Var52 string | |
| 1008 | + | templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.JoinStringErrs(t) | |
| 893 | 1009 | if templ_7745c5c3_Err != nil { | |
| 894 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 145, Col: 61} | |
| 1010 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 158, Col: 61} | |
| 895 | 1011 | } | |
| 896 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) | |
| 1012 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var52)) | |
| 897 | 1013 | if templ_7745c5c3_Err != nil { | |
| 898 | 1014 | return templ_7745c5c3_Err | |
| 899 | 1015 | } | |
| 900 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "</span>") | |
| 1016 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, "</span>") | |
| 901 | 1017 | if templ_7745c5c3_Err != nil { | |
| 902 | 1018 | return templ_7745c5c3_Err | |
| 903 | 1019 | } | |
| 904 | 1020 | } | |
| 905 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "</td><td class=\"c-ck\">") | |
| 1021 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, "</td><td class=\"c-ck\">") | |
| 906 | 1022 | if templ_7745c5c3_Err != nil { | |
| 907 | 1023 | return templ_7745c5c3_Err | |
| 908 | 1024 | } | |
| @@ -910,64 +1026,64 @@ func CommitRows(rows []CommitRow) templ.Component { | |||
| 910 | 1026 | if templ_7745c5c3_Err != nil { | |
| 911 | 1027 | return templ_7745c5c3_Err | |
| 912 | 1028 | } | |
| 913 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "</td><td class=\"c-who dim\">") | |
| 1029 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "</td><td class=\"c-who dim\">") | |
| 914 | 1030 | if templ_7745c5c3_Err != nil { | |
| 915 | 1031 | return templ_7745c5c3_Err | |
| 916 | 1032 | } | |
| 917 | - | var templ_7745c5c3_Var46 string | |
| 918 | - | templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author) | |
| 1033 | + | var templ_7745c5c3_Var53 string | |
| 1034 | + | templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author) | |
| 919 | 1035 | if templ_7745c5c3_Err != nil { | |
| 920 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 149, Col: 37} | |
| 1036 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 162, Col: 37} | |
| 921 | 1037 | } | |
| 922 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46)) | |
| 1038 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var53)) | |
| 923 | 1039 | if templ_7745c5c3_Err != nil { | |
| 924 | 1040 | return templ_7745c5c3_Err | |
| 925 | 1041 | } | |
| 926 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "</td><td class=\"c-when nowrap dim\"><time datetime=\"") | |
| 1042 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, "</td><td class=\"c-when nowrap dim\"><time datetime=\"") | |
| 927 | 1043 | if templ_7745c5c3_Err != nil { | |
| 928 | 1044 | return templ_7745c5c3_Err | |
| 929 | 1045 | } | |
| 930 | - | var templ_7745c5c3_Var47 string | |
| 931 | - | templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | |
| 1046 | + | var templ_7745c5c3_Var54 string | |
| 1047 | + | templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | |
| 932 | 1048 | if templ_7745c5c3_Err != nil { | |
| 933 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 150, Col: 60} | |
| 1049 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 163, Col: 60} | |
| 934 | 1050 | } | |
| 935 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var47) | |
| 1051 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var54) | |
| 936 | 1052 | if templ_7745c5c3_Err != nil { | |
| 937 | 1053 | return templ_7745c5c3_Err | |
| 938 | 1054 | } | |
| 939 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "\" title=\"") | |
| 1055 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "\" title=\"") | |
| 940 | 1056 | if templ_7745c5c3_Err != nil { | |
| 941 | 1057 | return templ_7745c5c3_Err | |
| 942 | 1058 | } | |
| 943 | - | var templ_7745c5c3_Var48 string | |
| 944 | - | templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | |
| 1059 | + | var templ_7745c5c3_Var55 string | |
| 1060 | + | templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | |
| 945 | 1061 | if templ_7745c5c3_Err != nil { | |
| 946 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 150, Col: 79} | |
| 1062 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 163, Col: 79} | |
| 947 | 1063 | } | |
| 948 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var48) | |
| 1064 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var55) | |
| 949 | 1065 | if templ_7745c5c3_Err != nil { | |
| 950 | 1066 | return templ_7745c5c3_Err | |
| 951 | 1067 | } | |
| 952 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "\">") | |
| 1068 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, "\">") | |
| 953 | 1069 | if templ_7745c5c3_Err != nil { | |
| 954 | 1070 | return templ_7745c5c3_Err | |
| 955 | 1071 | } | |
| 956 | - | var templ_7745c5c3_Var49 string | |
| 957 | - | templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) | |
| 1072 | + | var templ_7745c5c3_Var56 string | |
| 1073 | + | templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) | |
| 958 | 1074 | if templ_7745c5c3_Err != nil { | |
| 959 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 150, Col: 90} | |
| 1075 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 163, Col: 90} | |
| 960 | 1076 | } | |
| 961 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49)) | |
| 1077 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56)) | |
| 962 | 1078 | if templ_7745c5c3_Err != nil { | |
| 963 | 1079 | return templ_7745c5c3_Err | |
| 964 | 1080 | } | |
| 965 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "</time></td></tr>") | |
| 1081 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, "</time></td></tr>") | |
| 966 | 1082 | if templ_7745c5c3_Err != nil { | |
| 967 | 1083 | return templ_7745c5c3_Err | |
| 968 | 1084 | } | |
| 969 | 1085 | } | |
| 970 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "</tbody></table>") | |
| 1086 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 99, "</tbody></table>") | |
| 971 | 1087 | if templ_7745c5c3_Err != nil { | |
| 972 | 1088 | return templ_7745c5c3_Err | |
| 973 | 1089 | } | |
| @@ -264,286 +264,402 @@ func HomeRowView(r HomeRow) templ.Component { | |||||
| 264 | return templ_7745c5c3_Err | 264 | return templ_7745c5c3_Err | ||
| 265 | } | 265 | } | ||
| 266 | } | 266 | } | ||
| 267 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<p class=\"repo-meta\">") | 267 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<dl class=\"repo-stats\" aria-label=\"") |
| 268 | if templ_7745c5c3_Err != nil { | 268 | if templ_7745c5c3_Err != nil { | ||
| 269 | return templ_7745c5c3_Err | 269 | return templ_7745c5c3_Err | ||
| 270 | } | 270 | } | ||
| 271 | - | if r.Branch != "" { | 271 | + | var templ_7745c5c3_Var12 string |
| 272 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<span class=\"repo-branch\"><span class=\"ico i-branch\" aria-hidden=\"true\"></span>") | 272 | + | templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.ResolveAttributeValue("Figures of " + r.Name) |
| 273 | + | if templ_7745c5c3_Err != nil { | |||
| 274 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 55, Col: 61} | |||
| 275 | + | } | |||
| 276 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var12) | |||
| 277 | + | if templ_7745c5c3_Err != nil { | |||
| 278 | + | return templ_7745c5c3_Err | |||
| 279 | + | } | |||
| 280 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\">") | |||
| 281 | + | if templ_7745c5c3_Err != nil { | |||
| 282 | + | return templ_7745c5c3_Err | |||
| 283 | + | } | |||
| 284 | + | if s := r.Stats; s != nil { | |||
| 285 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<div><dt>commits</dt><dd>") | |||
| 286 | + | if templ_7745c5c3_Err != nil { | |||
| 287 | + | return templ_7745c5c3_Err | |||
| 288 | + | } | |||
| 289 | + | var templ_7745c5c3_Var13 string | |||
| 290 | + | templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(s.Commits) | |||
| 291 | + | if templ_7745c5c3_Err != nil { | |||
| 292 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 57, Col: 41} | |||
| 293 | + | } | |||
| 294 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) | |||
| 295 | + | if templ_7745c5c3_Err != nil { | |||
| 296 | + | return templ_7745c5c3_Err | |||
| 297 | + | } | |||
| 298 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</dd></div><div><dt>last 30 days</dt><dd>") | |||
| 273 | if templ_7745c5c3_Err != nil { | 299 | if templ_7745c5c3_Err != nil { | ||
| 274 | return templ_7745c5c3_Err | 300 | return templ_7745c5c3_Err | ||
| 275 | } | 301 | } | ||
| 276 | - | var templ_7745c5c3_Var12 string | 302 | + | var templ_7745c5c3_Var14 string |
| 277 | - | templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(r.Branch) | 303 | + | templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(s.Recent) |
| 278 | if templ_7745c5c3_Err != nil { | 304 | if templ_7745c5c3_Err != nil { | ||
| 279 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 57, Col: 94} | 305 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 58, Col: 45} |
| 280 | } | 306 | } | ||
| 281 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) | 307 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) |
| 282 | if templ_7745c5c3_Err != nil { | 308 | if templ_7745c5c3_Err != nil { | ||
| 283 | return templ_7745c5c3_Err | 309 | return templ_7745c5c3_Err | ||
| 284 | } | 310 | } | ||
| 285 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</span> ") | 311 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</dd></div>") |
| 286 | if templ_7745c5c3_Err != nil { | 312 | if templ_7745c5c3_Err != nil { | ||
| 287 | return templ_7745c5c3_Err | 313 | return templ_7745c5c3_Err | ||
| 288 | } | 314 | } | ||
| 315 | + | if s.Tag != "" { | |||
| 316 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<div><dt>latest tag</dt>") | |||
| 317 | + | if templ_7745c5c3_Err != nil { | |||
| 318 | + | return templ_7745c5c3_Err | |||
| 319 | + | } | |||
| 320 | + | if s.TagHref != "" { | |||
| 321 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<dd><a href=\"") | |||
| 322 | + | if templ_7745c5c3_Err != nil { | |||
| 323 | + | return templ_7745c5c3_Err | |||
| 324 | + | } | |||
| 325 | + | var templ_7745c5c3_Var15 templ.SafeURL | |||
| 326 | + | templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(s.TagHref)) | |||
| 327 | + | if templ_7745c5c3_Err != nil { | |||
| 328 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 63, Col: 46} | |||
| 329 | + | } | |||
| 330 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) | |||
| 331 | + | if templ_7745c5c3_Err != nil { | |||
| 332 | + | return templ_7745c5c3_Err | |||
| 333 | + | } | |||
| 334 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\" title=\"") | |||
| 335 | + | if templ_7745c5c3_Err != nil { | |||
| 336 | + | return templ_7745c5c3_Err | |||
| 337 | + | } | |||
| 338 | + | var templ_7745c5c3_Var16 string | |||
| 339 | + | templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.ResolveAttributeValue("made " + s.TagWhen) | |||
| 340 | + | if templ_7745c5c3_Err != nil { | |||
| 341 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 63, Col: 76} | |||
| 342 | + | } | |||
| 343 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16) | |||
| 344 | + | if templ_7745c5c3_Err != nil { | |||
| 345 | + | return templ_7745c5c3_Err | |||
| 346 | + | } | |||
| 347 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\">") | |||
| 348 | + | if templ_7745c5c3_Err != nil { | |||
| 349 | + | return templ_7745c5c3_Err | |||
| 350 | + | } | |||
| 351 | + | var templ_7745c5c3_Var17 string | |||
| 352 | + | templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(s.Tag) | |||
| 353 | + | if templ_7745c5c3_Err != nil { | |||
| 354 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 63, Col: 86} | |||
| 355 | + | } | |||
| 356 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) | |||
| 357 | + | if templ_7745c5c3_Err != nil { | |||
| 358 | + | return templ_7745c5c3_Err | |||
| 359 | + | } | |||
| 360 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</a></dd>") | |||
| 361 | + | if templ_7745c5c3_Err != nil { | |||
| 362 | + | return templ_7745c5c3_Err | |||
| 363 | + | } | |||
| 364 | + | } else { | |||
| 365 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<dd title=\"") | |||
| 366 | + | if templ_7745c5c3_Err != nil { | |||
| 367 | + | return templ_7745c5c3_Err | |||
| 368 | + | } | |||
| 369 | + | var templ_7745c5c3_Var18 string | |||
| 370 | + | templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.ResolveAttributeValue("made " + s.TagWhen) | |||
| 371 | + | if templ_7745c5c3_Err != nil { | |||
| 372 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 65, Col: 39} | |||
| 373 | + | } | |||
| 374 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18) | |||
| 375 | + | if templ_7745c5c3_Err != nil { | |||
| 376 | + | return templ_7745c5c3_Err | |||
| 377 | + | } | |||
| 378 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "\">") | |||
| 379 | + | if templ_7745c5c3_Err != nil { | |||
| 380 | + | return templ_7745c5c3_Err | |||
| 381 | + | } | |||
| 382 | + | var templ_7745c5c3_Var19 string | |||
| 383 | + | templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(s.Tag) | |||
| 384 | + | if templ_7745c5c3_Err != nil { | |||
| 385 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 65, Col: 49} | |||
| 386 | + | } | |||
| 387 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) | |||
| 388 | + | if templ_7745c5c3_Err != nil { | |||
| 389 | + | return templ_7745c5c3_Err | |||
| 390 | + | } | |||
| 391 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</dd>") | |||
| 392 | + | if templ_7745c5c3_Err != nil { | |||
| 393 | + | return templ_7745c5c3_Err | |||
| 394 | + | } | |||
| 395 | + | } | |||
| 396 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</div>") | |||
| 397 | + | if templ_7745c5c3_Err != nil { | |||
| 398 | + | return templ_7745c5c3_Err | |||
| 399 | + | } | |||
| 400 | + | } | |||
| 289 | } | 401 | } | ||
| 290 | - | if r.PushedAt != "" { | 402 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div><dt>last activity</dt>") |
| 291 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<span>pushed <time datetime=\"") | 403 | + | if templ_7745c5c3_Err != nil { |
| 404 | + | return templ_7745c5c3_Err | |||
| 405 | + | } | |||
| 406 | + | if r.ActiveAt != "" { | |||
| 407 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<dd><time datetime=\"") | |||
| 292 | if templ_7745c5c3_Err != nil { | 408 | if templ_7745c5c3_Err != nil { | ||
| 293 | return templ_7745c5c3_Err | 409 | return templ_7745c5c3_Err | ||
| 294 | } | 410 | } | ||
| 295 | - | var templ_7745c5c3_Var13 string | 411 | + | var templ_7745c5c3_Var20 string |
| 296 | - | templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.PushedAt) | 412 | + | templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.ActiveAt) |
| 297 | if templ_7745c5c3_Err != nil { | 413 | if templ_7745c5c3_Err != nil { | ||
| 298 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 60, Col: 45} | 414 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 73, Col: 37} |
| 299 | } | 415 | } | ||
| 300 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13) | 416 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var20) |
| 301 | if templ_7745c5c3_Err != nil { | 417 | if templ_7745c5c3_Err != nil { | ||
| 302 | return templ_7745c5c3_Err | 418 | return templ_7745c5c3_Err | ||
| 303 | } | 419 | } | ||
| 304 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" title=\"") | 420 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "\" title=\"") |
| 305 | if templ_7745c5c3_Err != nil { | 421 | if templ_7745c5c3_Err != nil { | ||
| 306 | return templ_7745c5c3_Err | 422 | return templ_7745c5c3_Err | ||
| 307 | } | 423 | } | ||
| 308 | - | var templ_7745c5c3_Var14 string | 424 | + | var templ_7745c5c3_Var21 string |
| 309 | - | templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.PushedAt) | 425 | + | templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.ActiveAt) |
| 310 | if templ_7745c5c3_Err != nil { | 426 | if templ_7745c5c3_Err != nil { | ||
| 311 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 60, Col: 66} | 427 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 73, Col: 58} |
| 312 | } | 428 | } | ||
| 313 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14) | 429 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var21) |
| 314 | if templ_7745c5c3_Err != nil { | 430 | if templ_7745c5c3_Err != nil { | ||
| 315 | return templ_7745c5c3_Err | 431 | return templ_7745c5c3_Err | ||
| 316 | } | 432 | } | ||
| 317 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\">") | 433 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "\">") |
| 318 | if templ_7745c5c3_Err != nil { | 434 | if templ_7745c5c3_Err != nil { | ||
| 319 | return templ_7745c5c3_Err | 435 | return templ_7745c5c3_Err | ||
| 320 | } | 436 | } | ||
| 321 | - | var templ_7745c5c3_Var15 string | 437 | + | var templ_7745c5c3_Var22 string |
| 322 | - | templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(r.Pushed) | 438 | + | templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(r.Active) |
| 323 | if templ_7745c5c3_Err != nil { | 439 | if templ_7745c5c3_Err != nil { | ||
| 324 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 60, Col: 79} | 440 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 73, Col: 71} |
| 325 | } | 441 | } | ||
| 326 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) | 442 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) |
| 327 | if templ_7745c5c3_Err != nil { | 443 | if templ_7745c5c3_Err != nil { | ||
| 328 | return templ_7745c5c3_Err | 444 | return templ_7745c5c3_Err | ||
| 329 | } | 445 | } | ||
| 330 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</time></span>") | 446 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</time></dd>") |
| 331 | if templ_7745c5c3_Err != nil { | 447 | if templ_7745c5c3_Err != nil { | ||
| 332 | return templ_7745c5c3_Err | 448 | return templ_7745c5c3_Err | ||
| 333 | } | 449 | } | ||
| 334 | } else { | 450 | } else { | ||
| 335 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<span>never pushed</span>") | 451 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<dd class=\"mute\">never</dd>") |
| 336 | if templ_7745c5c3_Err != nil { | 452 | if templ_7745c5c3_Err != nil { | ||
| 337 | return templ_7745c5c3_Err | 453 | return templ_7745c5c3_Err | ||
| 338 | } | 454 | } | ||
| 339 | } | 455 | } | ||
| 340 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</p></div>") | 456 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</div></dl></div>") |
| 341 | if templ_7745c5c3_Err != nil { | 457 | if templ_7745c5c3_Err != nil { | ||
| 342 | return templ_7745c5c3_Err | 458 | return templ_7745c5c3_Err | ||
| 343 | } | 459 | } | ||
| 344 | if r.CI != nil { | 460 | if r.CI != nil { | ||
| 345 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<a class=\"repo-ci\" href=\"") | 461 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "<a class=\"repo-ci\" href=\"") |
| 346 | if templ_7745c5c3_Err != nil { | 462 | if templ_7745c5c3_Err != nil { | ||
| 347 | return templ_7745c5c3_Err | 463 | return templ_7745c5c3_Err | ||
| 348 | } | 464 | } | ||
| 349 | - | var templ_7745c5c3_Var16 templ.SafeURL | 465 | + | var templ_7745c5c3_Var23 templ.SafeURL |
| 350 | - | templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.CI.Href)) | 466 | + | templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.CI.Href)) |
| 351 | if templ_7745c5c3_Err != nil { | 467 | if templ_7745c5c3_Err != nil { | ||
| 352 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 67, Col: 53} | 468 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 81, Col: 53} |
| 353 | } | 469 | } | ||
| 354 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) | 470 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) |
| 355 | if templ_7745c5c3_Err != nil { | 471 | if templ_7745c5c3_Err != nil { | ||
| 356 | return templ_7745c5c3_Err | 472 | return templ_7745c5c3_Err | ||
| 357 | } | 473 | } | ||
| 358 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\" title=\"") | 474 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "\" title=\"") |
| 359 | if templ_7745c5c3_Err != nil { | 475 | if templ_7745c5c3_Err != nil { | ||
| 360 | return templ_7745c5c3_Err | 476 | return templ_7745c5c3_Err | ||
| 361 | } | 477 | } | ||
| 362 | - | var templ_7745c5c3_Var17 string | 478 | + | var templ_7745c5c3_Var24 string |
| 363 | - | templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.CI.Title) | 479 | + | templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.CI.Title) |
| 364 | if templ_7745c5c3_Err != nil { | 480 | if templ_7745c5c3_Err != nil { | ||
| 365 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 67, Col: 74} | 481 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 81, Col: 74} |
| 366 | } | 482 | } | ||
| 367 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var17) | 483 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var24) |
| 368 | if templ_7745c5c3_Err != nil { | 484 | if templ_7745c5c3_Err != nil { | ||
| 369 | return templ_7745c5c3_Err | 485 | return templ_7745c5c3_Err | ||
| 370 | } | 486 | } | ||
| 371 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "\">") | 487 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\">") |
| 372 | if templ_7745c5c3_Err != nil { | 488 | if templ_7745c5c3_Err != nil { | ||
| 373 | return templ_7745c5c3_Err | 489 | return templ_7745c5c3_Err | ||
| 374 | } | 490 | } | ||
| 375 | - | var templ_7745c5c3_Var18 = []any{"ck", "ck-" + r.CI.Kind} | 491 | + | var templ_7745c5c3_Var25 = []any{"ck", "ck-" + r.CI.Kind} |
| 376 | - | templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var18...) | 492 | + | templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var25...) |
| 377 | if templ_7745c5c3_Err != nil { | 493 | if templ_7745c5c3_Err != nil { | ||
| 378 | return templ_7745c5c3_Err | 494 | return templ_7745c5c3_Err | ||
| 379 | } | 495 | } | ||
| 380 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<span class=\"") | 496 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<span class=\"") |
| 381 | if templ_7745c5c3_Err != nil { | 497 | if templ_7745c5c3_Err != nil { | ||
| 382 | return templ_7745c5c3_Err | 498 | return templ_7745c5c3_Err | ||
| 383 | } | 499 | } | ||
| 384 | - | var templ_7745c5c3_Var19 string | 500 | + | var templ_7745c5c3_Var26 string |
| 385 | - | templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var18).String()) | 501 | + | templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var25).String()) |
| 386 | if templ_7745c5c3_Err != nil { | 502 | if templ_7745c5c3_Err != nil { | ||
| 387 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 1, Col: 0} | 503 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 1, Col: 0} | ||
| 388 | } | 504 | } | ||
| 389 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var19) | 505 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var26) |
| 390 | if templ_7745c5c3_Err != nil { | 506 | if templ_7745c5c3_Err != nil { | ||
| 391 | return templ_7745c5c3_Err | 507 | return templ_7745c5c3_Err | ||
| 392 | } | 508 | } | ||
| 393 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "\" aria-hidden=\"true\"></span> <span class=\"sr\">CI: </span>") | 509 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "\" aria-hidden=\"true\"></span> <span class=\"sr\">CI: </span>") |
| 394 | if templ_7745c5c3_Err != nil { | 510 | if templ_7745c5c3_Err != nil { | ||
| 395 | return templ_7745c5c3_Err | 511 | return templ_7745c5c3_Err | ||
| 396 | } | 512 | } | ||
| 397 | - | var templ_7745c5c3_Var20 string | 513 | + | var templ_7745c5c3_Var27 string |
| 398 | - | templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(r.CI.Label) | 514 | + | templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(r.CI.Label) |
| 399 | if templ_7745c5c3_Err != nil { | 515 | if templ_7745c5c3_Err != nil { | ||
| 400 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 69, Col: 44} | 516 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 83, Col: 44} |
| 401 | } | 517 | } | ||
| 402 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) | 518 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27)) |
| 403 | if templ_7745c5c3_Err != nil { | 519 | if templ_7745c5c3_Err != nil { | ||
| 404 | return templ_7745c5c3_Err | 520 | return templ_7745c5c3_Err | ||
| 405 | } | 521 | } | ||
| 406 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "</a> ") | 522 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</a> ") |
| 407 | if templ_7745c5c3_Err != nil { | 523 | if templ_7745c5c3_Err != nil { | ||
| 408 | return templ_7745c5c3_Err | 524 | return templ_7745c5c3_Err | ||
| 409 | } | 525 | } | ||
| 410 | } | 526 | } | ||
| 411 | if len(r.Commits) > 0 { | 527 | if len(r.Commits) > 0 { | ||
| 412 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<ul class=\"repo-commits\" aria-label=\"") | 528 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "<ul class=\"repo-commits\" aria-label=\"") |
| 413 | if templ_7745c5c3_Err != nil { | 529 | if templ_7745c5c3_Err != nil { | ||
| 414 | return templ_7745c5c3_Err | 530 | return templ_7745c5c3_Err | ||
| 415 | } | 531 | } | ||
| 416 | - | var templ_7745c5c3_Var21 string | 532 | + | var templ_7745c5c3_Var28 string |
| 417 | - | templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.ResolveAttributeValue("Recent commits of " + r.Name) | 533 | + | templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.ResolveAttributeValue("Recent commits of " + r.Name) |
| 418 | if templ_7745c5c3_Err != nil { | 534 | if templ_7745c5c3_Err != nil { | ||
| 419 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 73, Col: 70} | 535 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 87, Col: 70} |
| 420 | } | 536 | } | ||
| 421 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var21) | 537 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var28) |
| 422 | if templ_7745c5c3_Err != nil { | 538 | if templ_7745c5c3_Err != nil { | ||
| 423 | return templ_7745c5c3_Err | 539 | return templ_7745c5c3_Err | ||
| 424 | } | 540 | } | ||
| 425 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "\">") | 541 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "\">") |
| 426 | if templ_7745c5c3_Err != nil { | 542 | if templ_7745c5c3_Err != nil { | ||
| 427 | return templ_7745c5c3_Err | 543 | return templ_7745c5c3_Err | ||
| 428 | } | 544 | } | ||
| 429 | for _, c := range r.Commits { | 545 | for _, c := range r.Commits { | ||
| 430 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<li><a class=\"rc-sha\" href=\"") | 546 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "<li><a class=\"rc-sha\" href=\"") |
| 431 | if templ_7745c5c3_Err != nil { | 547 | if templ_7745c5c3_Err != nil { | ||
| 432 | return templ_7745c5c3_Err | 548 | return templ_7745c5c3_Err | ||
| 433 | } | 549 | } | ||
| 434 | - | var templ_7745c5c3_Var22 templ.SafeURL | 550 | + | var templ_7745c5c3_Var29 templ.SafeURL |
| 435 | - | templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref)) | 551 | + | templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref)) |
| 436 | if templ_7745c5c3_Err != nil { | 552 | if templ_7745c5c3_Err != nil { | ||
| 437 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 76, Col: 53} | 553 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 90, Col: 53} |
| 438 | } | 554 | } | ||
| 439 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) | 555 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29)) |
| 440 | if templ_7745c5c3_Err != nil { | 556 | if templ_7745c5c3_Err != nil { | ||
| 441 | return templ_7745c5c3_Err | 557 | return templ_7745c5c3_Err | ||
| 442 | } | 558 | } | ||
| 443 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "\">") | 559 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "\">") |
| 444 | if templ_7745c5c3_Err != nil { | 560 | if templ_7745c5c3_Err != nil { | ||
| 445 | return templ_7745c5c3_Err | 561 | return templ_7745c5c3_Err | ||
| 446 | } | 562 | } | ||
| 447 | - | var templ_7745c5c3_Var23 string | 563 | + | var templ_7745c5c3_Var30 string |
| 448 | - | templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short) | 564 | + | templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short) |
| 449 | if templ_7745c5c3_Err != nil { | 565 | if templ_7745c5c3_Err != nil { | ||
| 450 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 76, Col: 65} | 566 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 90, Col: 65} |
| 451 | } | 567 | } | ||
| 452 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) | 568 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) |
| 453 | if templ_7745c5c3_Err != nil { | 569 | if templ_7745c5c3_Err != nil { | ||
| 454 | return templ_7745c5c3_Err | 570 | return templ_7745c5c3_Err | ||
| 455 | } | 571 | } | ||
| 456 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</a> <span class=\"rc-subject\" title=\"") | 572 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "</a> <span class=\"rc-subject\" title=\"") |
| 457 | if templ_7745c5c3_Err != nil { | 573 | if templ_7745c5c3_Err != nil { | ||
| 458 | return templ_7745c5c3_Err | 574 | return templ_7745c5c3_Err | ||
| 459 | } | 575 | } | ||
| 460 | - | var templ_7745c5c3_Var24 string | 576 | + | var templ_7745c5c3_Var31 string |
| 461 | - | templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.Subject) | 577 | + | templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.Subject) |
| 462 | if templ_7745c5c3_Err != nil { | 578 | if templ_7745c5c3_Err != nil { | ||
| 463 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 77, Col: 48} | 579 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 91, Col: 48} |
| 464 | } | 580 | } | ||
| 465 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var24) | 581 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var31) |
| 466 | if templ_7745c5c3_Err != nil { | 582 | if templ_7745c5c3_Err != nil { | ||
| 467 | return templ_7745c5c3_Err | 583 | return templ_7745c5c3_Err | ||
| 468 | } | 584 | } | ||
| 469 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "\">") | 585 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\">") |
| 470 | if templ_7745c5c3_Err != nil { | 586 | if templ_7745c5c3_Err != nil { | ||
| 471 | return templ_7745c5c3_Err | 587 | return templ_7745c5c3_Err | ||
| 472 | } | 588 | } | ||
| 473 | - | var templ_7745c5c3_Var25 string | 589 | + | var templ_7745c5c3_Var32 string |
| 474 | - | templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject) | 590 | + | templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject) |
| 475 | if templ_7745c5c3_Err != nil { | 591 | if templ_7745c5c3_Err != nil { | ||
| 476 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 77, Col: 62} | 592 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 91, Col: 62} |
| 477 | } | 593 | } | ||
| 478 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25)) | 594 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32)) |
| 479 | if templ_7745c5c3_Err != nil { | 595 | if templ_7745c5c3_Err != nil { | ||
| 480 | return templ_7745c5c3_Err | 596 | return templ_7745c5c3_Err | ||
| 481 | } | 597 | } | ||
| 482 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</span> ") | 598 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "</span> ") |
| 483 | if templ_7745c5c3_Err != nil { | 599 | if templ_7745c5c3_Err != nil { | ||
| 484 | return templ_7745c5c3_Err | 600 | return templ_7745c5c3_Err | ||
| 485 | } | 601 | } | ||
| 486 | if c.WhenAt != "" { | 602 | if c.WhenAt != "" { | ||
| 487 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<time class=\"dim\" datetime=\"") | 603 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "<time class=\"dim\" datetime=\"") |
| 488 | if templ_7745c5c3_Err != nil { | 604 | if templ_7745c5c3_Err != nil { | ||
| 489 | return templ_7745c5c3_Err | 605 | return templ_7745c5c3_Err | ||
| 490 | } | 606 | } | ||
| 491 | - | var templ_7745c5c3_Var26 string | 607 | + | var templ_7745c5c3_Var33 string |
| 492 | - | templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | 608 | + | templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) |
| 493 | if templ_7745c5c3_Err != nil { | 609 | if templ_7745c5c3_Err != nil { | ||
| 494 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 79, Col: 44} | 610 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 93, Col: 44} |
| 495 | } | 611 | } | ||
| 496 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var26) | 612 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var33) |
| 497 | if templ_7745c5c3_Err != nil { | 613 | if templ_7745c5c3_Err != nil { | ||
| 498 | return templ_7745c5c3_Err | 614 | return templ_7745c5c3_Err | ||
| 499 | } | 615 | } | ||
| 500 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\" title=\"") | 616 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\" title=\"") |
| 501 | if templ_7745c5c3_Err != nil { | 617 | if templ_7745c5c3_Err != nil { | ||
| 502 | return templ_7745c5c3_Err | 618 | return templ_7745c5c3_Err | ||
| 503 | } | 619 | } | ||
| 504 | - | var templ_7745c5c3_Var27 string | 620 | + | var templ_7745c5c3_Var34 string |
| 505 | - | templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | 621 | + | templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) |
| 506 | if templ_7745c5c3_Err != nil { | 622 | if templ_7745c5c3_Err != nil { | ||
| 507 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 79, Col: 63} | 623 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 93, Col: 63} |
| 508 | } | 624 | } | ||
| 509 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var27) | 625 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var34) |
| 510 | if templ_7745c5c3_Err != nil { | 626 | if templ_7745c5c3_Err != nil { | ||
| 511 | return templ_7745c5c3_Err | 627 | return templ_7745c5c3_Err | ||
| 512 | } | 628 | } | ||
| 513 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "\">") | 629 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "\">") |
| 514 | if templ_7745c5c3_Err != nil { | 630 | if templ_7745c5c3_Err != nil { | ||
| 515 | return templ_7745c5c3_Err | 631 | return templ_7745c5c3_Err | ||
| 516 | } | 632 | } | ||
| 517 | - | var templ_7745c5c3_Var28 string | 633 | + | var templ_7745c5c3_Var35 string |
| 518 | - | templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) | 634 | + | templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) |
| 519 | if templ_7745c5c3_Err != nil { | 635 | if templ_7745c5c3_Err != nil { | ||
| 520 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 79, Col: 74} | 636 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 93, Col: 74} |
| 521 | } | 637 | } | ||
| 522 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) | 638 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35)) |
| 523 | if templ_7745c5c3_Err != nil { | 639 | if templ_7745c5c3_Err != nil { | ||
| 524 | return templ_7745c5c3_Err | 640 | return templ_7745c5c3_Err | ||
| 525 | } | 641 | } | ||
| 526 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</time>") | 642 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "</time>") |
| 527 | if templ_7745c5c3_Err != nil { | 643 | if templ_7745c5c3_Err != nil { | ||
| 528 | return templ_7745c5c3_Err | 644 | return templ_7745c5c3_Err | ||
| 529 | } | 645 | } | ||
| 530 | } | 646 | } | ||
| 531 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</li>") | 647 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "</li>") |
| 532 | if templ_7745c5c3_Err != nil { | 648 | if templ_7745c5c3_Err != nil { | ||
| 533 | return templ_7745c5c3_Err | 649 | return templ_7745c5c3_Err | ||
| 534 | } | 650 | } | ||
| 535 | } | 651 | } | ||
| 536 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</ul>") | 652 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "</ul>") |
| 537 | if templ_7745c5c3_Err != nil { | 653 | if templ_7745c5c3_Err != nil { | ||
| 538 | return templ_7745c5c3_Err | 654 | return templ_7745c5c3_Err | ||
| 539 | } | 655 | } | ||
| 540 | } else if r.NoCommits { | 656 | } else if r.NoCommits { | ||
| 541 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<p class=\"repo-commits mute\">no commits yet</p>") | 657 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "<p class=\"repo-commits mute\">no commits yet</p>") |
| 542 | if templ_7745c5c3_Err != nil { | 658 | if templ_7745c5c3_Err != nil { | ||
| 543 | return templ_7745c5c3_Err | 659 | return templ_7745c5c3_Err | ||
| 544 | } | 660 | } | ||
| 545 | } | 661 | } | ||
| 546 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</li>") | 662 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "</li>") |
| 547 | if templ_7745c5c3_Err != nil { | 663 | if templ_7745c5c3_Err != nil { | ||
| 548 | return templ_7745c5c3_Err | 664 | return templ_7745c5c3_Err | ||
| 549 | } | 665 | } | ||
| @@ -568,66 +684,66 @@ func Outline(headings []Heading) templ.Component { | |||||
| 568 | }() | 684 | }() | ||
| 569 | } | 685 | } | ||
| 570 | ctx = templ.InitializeContext(ctx) | 686 | ctx = templ.InitializeContext(ctx) | ||
| 571 | - | templ_7745c5c3_Var29 := templ.GetChildren(ctx) | 687 | + | templ_7745c5c3_Var36 := templ.GetChildren(ctx) |
| 572 | - | if templ_7745c5c3_Var29 == nil { | 688 | + | if templ_7745c5c3_Var36 == nil { |
| 573 | - | templ_7745c5c3_Var29 = templ.NopComponent | 689 | + | templ_7745c5c3_Var36 = templ.NopComponent |
| 574 | } | 690 | } | ||
| 575 | ctx = templ.ClearChildren(ctx) | 691 | ctx = templ.ClearChildren(ctx) | ||
| 576 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "<nav class=\"outline\" aria-label=\"Outline\">") | 692 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "<nav class=\"outline\" aria-label=\"Outline\">") |
| 577 | if templ_7745c5c3_Err != nil { | 693 | if templ_7745c5c3_Err != nil { | ||
| 578 | return templ_7745c5c3_Err | 694 | return templ_7745c5c3_Err | ||
| 579 | } | 695 | } | ||
| 580 | for _, h := range headings { | 696 | for _, h := range headings { | ||
| 581 | - | var templ_7745c5c3_Var30 = []any{"l" + itoa(h.Level)} | 697 | + | var templ_7745c5c3_Var37 = []any{"l" + itoa(h.Level)} |
| 582 | - | templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var30...) | 698 | + | templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var37...) |
| 583 | if templ_7745c5c3_Err != nil { | 699 | if templ_7745c5c3_Err != nil { | ||
| 584 | return templ_7745c5c3_Err | 700 | return templ_7745c5c3_Err | ||
| 585 | } | 701 | } | ||
| 586 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "<a class=\"") | 702 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "<a class=\"") |
| 587 | if templ_7745c5c3_Err != nil { | 703 | if templ_7745c5c3_Err != nil { | ||
| 588 | return templ_7745c5c3_Err | 704 | return templ_7745c5c3_Err | ||
| 589 | } | 705 | } | ||
| 590 | - | var templ_7745c5c3_Var31 string | 706 | + | var templ_7745c5c3_Var38 string |
| 591 | - | templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var30).String()) | 707 | + | templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var37).String()) |
| 592 | if templ_7745c5c3_Err != nil { | 708 | if templ_7745c5c3_Err != nil { | ||
| 593 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 1, Col: 0} | 709 | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 1, Col: 0} | ||
| 594 | } | 710 | } | ||
| 595 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var31) | 711 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var38) |
| 596 | if templ_7745c5c3_Err != nil { | 712 | if templ_7745c5c3_Err != nil { | ||
| 597 | return templ_7745c5c3_Err | 713 | return templ_7745c5c3_Err | ||
| 598 | } | 714 | } | ||
| 599 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\" href=\"") | 715 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "\" href=\"") |
| 600 | if templ_7745c5c3_Err != nil { | 716 | if templ_7745c5c3_Err != nil { | ||
| 601 | return templ_7745c5c3_Err | 717 | return templ_7745c5c3_Err | ||
| 602 | } | 718 | } | ||
| 603 | - | var templ_7745c5c3_Var32 templ.SafeURL | 719 | + | var templ_7745c5c3_Var39 templ.SafeURL |
| 604 | - | templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("#" + h.ID)) | 720 | + | templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("#" + h.ID)) |
| 605 | if templ_7745c5c3_Err != nil { | 721 | if templ_7745c5c3_Err != nil { | ||
| 606 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 94, Col: 68} | 722 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 108, Col: 68} |
| 607 | } | 723 | } | ||
| 608 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32)) | 724 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39)) |
| 609 | if templ_7745c5c3_Err != nil { | 725 | if templ_7745c5c3_Err != nil { | ||
| 610 | return templ_7745c5c3_Err | 726 | return templ_7745c5c3_Err | ||
| 611 | } | 727 | } | ||
| 612 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\">") | 728 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "\">") |
| 613 | if templ_7745c5c3_Err != nil { | 729 | if templ_7745c5c3_Err != nil { | ||
| 614 | return templ_7745c5c3_Err | 730 | return templ_7745c5c3_Err | ||
| 615 | } | 731 | } | ||
| 616 | - | var templ_7745c5c3_Var33 string | 732 | + | var templ_7745c5c3_Var40 string |
| 617 | - | templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(h.Text) | 733 | + | templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(h.Text) |
| 618 | if templ_7745c5c3_Err != nil { | 734 | if templ_7745c5c3_Err != nil { | ||
| 619 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 94, Col: 79} | 735 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 108, Col: 79} |
| 620 | } | 736 | } | ||
| 621 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33)) | 737 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40)) |
| 622 | if templ_7745c5c3_Err != nil { | 738 | if templ_7745c5c3_Err != nil { | ||
| 623 | return templ_7745c5c3_Err | 739 | return templ_7745c5c3_Err | ||
| 624 | } | 740 | } | ||
| 625 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "</a>") | 741 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "</a>") |
| 626 | if templ_7745c5c3_Err != nil { | 742 | if templ_7745c5c3_Err != nil { | ||
| 627 | return templ_7745c5c3_Err | 743 | return templ_7745c5c3_Err | ||
| 628 | } | 744 | } | ||
| 629 | } | 745 | } | ||
| 630 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</nav>") | 746 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "</nav>") |
| 631 | if templ_7745c5c3_Err != nil { | 747 | if templ_7745c5c3_Err != nil { | ||
| 632 | return templ_7745c5c3_Err | 748 | return templ_7745c5c3_Err | ||
| 633 | } | 749 | } | ||
| @@ -652,25 +768,25 @@ func ReadmeCard(r Readme) templ.Component { | |||||
| 652 | }() | 768 | }() | ||
| 653 | } | 769 | } | ||
| 654 | ctx = templ.InitializeContext(ctx) | 770 | ctx = templ.InitializeContext(ctx) | ||
| 655 | - | templ_7745c5c3_Var34 := templ.GetChildren(ctx) | 771 | + | templ_7745c5c3_Var41 := templ.GetChildren(ctx) |
| 656 | - | if templ_7745c5c3_Var34 == nil { | 772 | + | if templ_7745c5c3_Var41 == nil { |
| 657 | - | templ_7745c5c3_Var34 = templ.NopComponent | 773 | + | templ_7745c5c3_Var41 = templ.NopComponent |
| 658 | } | 774 | } | ||
| 659 | ctx = templ.ClearChildren(ctx) | 775 | ctx = templ.ClearChildren(ctx) | ||
| 660 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<section class=\"card readme\" aria-labelledby=\"readme-h\"><h2 class=\"card-h\" id=\"readme-h\"><span class=\"ico i-file\" aria-hidden=\"true\"></span> ") | 776 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "<section class=\"card readme\" aria-labelledby=\"readme-h\"><h2 class=\"card-h\" id=\"readme-h\"><span class=\"ico i-file\" aria-hidden=\"true\"></span> ") |
| 661 | if templ_7745c5c3_Err != nil { | 777 | if templ_7745c5c3_Err != nil { | ||
| 662 | return templ_7745c5c3_Err | 778 | return templ_7745c5c3_Err | ||
| 663 | } | 779 | } | ||
| 664 | - | var templ_7745c5c3_Var35 string | 780 | + | var templ_7745c5c3_Var42 string |
| 665 | - | templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(r.Name) | 781 | + | templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(r.Name) |
| 666 | if templ_7745c5c3_Err != nil { | 782 | if templ_7745c5c3_Err != nil { | ||
| 667 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 104, Col: 11} | 783 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 118, Col: 11} |
| 668 | } | 784 | } | ||
| 669 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35)) | 785 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) |
| 670 | if templ_7745c5c3_Err != nil { | 786 | if templ_7745c5c3_Err != nil { | ||
| 671 | return templ_7745c5c3_Err | 787 | return templ_7745c5c3_Err | ||
| 672 | } | 788 | } | ||
| 673 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "</h2>") | 789 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "</h2>") |
| 674 | if templ_7745c5c3_Err != nil { | 790 | if templ_7745c5c3_Err != nil { | ||
| 675 | return templ_7745c5c3_Err | 791 | return templ_7745c5c3_Err | ||
| 676 | } | 792 | } | ||
| @@ -678,7 +794,7 @@ func ReadmeCard(r Readme) templ.Component { | |||||
| 678 | if templ_7745c5c3_Err != nil { | 794 | if templ_7745c5c3_Err != nil { | ||
| 679 | return templ_7745c5c3_Err | 795 | return templ_7745c5c3_Err | ||
| 680 | } | 796 | } | ||
| 681 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "</section>") | 797 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "</section>") |
| 682 | if templ_7745c5c3_Err != nil { | 798 | if templ_7745c5c3_Err != nil { | ||
| 683 | return templ_7745c5c3_Err | 799 | return templ_7745c5c3_Err | ||
| 684 | } | 800 | } | ||
| @@ -704,43 +820,43 @@ func ReadmeView(r Readme) templ.Component { | |||||
| 704 | }() | 820 | }() | ||
| 705 | } | 821 | } | ||
| 706 | ctx = templ.InitializeContext(ctx) | 822 | ctx = templ.InitializeContext(ctx) | ||
| 707 | - | templ_7745c5c3_Var36 := templ.GetChildren(ctx) | 823 | + | templ_7745c5c3_Var43 := templ.GetChildren(ctx) |
| 708 | - | if templ_7745c5c3_Var36 == nil { | 824 | + | if templ_7745c5c3_Var43 == nil { |
| 709 | - | templ_7745c5c3_Var36 = templ.NopComponent | 825 | + | templ_7745c5c3_Var43 = templ.NopComponent |
| 710 | } | 826 | } | ||
| 711 | ctx = templ.ClearChildren(ctx) | 827 | ctx = templ.ClearChildren(ctx) | ||
| 712 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "<article class=\"prose\" aria-label=\"") | 828 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "<article class=\"prose\" aria-label=\"") |
| 713 | if templ_7745c5c3_Err != nil { | 829 | if templ_7745c5c3_Err != nil { | ||
| 714 | return templ_7745c5c3_Err | 830 | return templ_7745c5c3_Err | ||
| 715 | } | 831 | } | ||
| 716 | - | var templ_7745c5c3_Var37 string | 832 | + | var templ_7745c5c3_Var44 string |
| 717 | - | templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.Name) | 833 | + | templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.Name) |
| 718 | if templ_7745c5c3_Err != nil { | 834 | if templ_7745c5c3_Err != nil { | ||
| 719 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 113, Col: 43} | 835 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 127, Col: 43} |
| 720 | } | 836 | } | ||
| 721 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var37) | 837 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var44) |
| 722 | if templ_7745c5c3_Err != nil { | 838 | if templ_7745c5c3_Err != nil { | ||
| 723 | return templ_7745c5c3_Err | 839 | return templ_7745c5c3_Err | ||
| 724 | } | 840 | } | ||
| 725 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "\" hx-ignore>") | 841 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "\" hx-ignore>") |
| 726 | if templ_7745c5c3_Err != nil { | 842 | if templ_7745c5c3_Err != nil { | ||
| 727 | return templ_7745c5c3_Err | 843 | return templ_7745c5c3_Err | ||
| 728 | } | 844 | } | ||
| 729 | if r.Note != "" { | 845 | if r.Note != "" { | ||
| 730 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "<p class=\"dim\">") | 846 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "<p class=\"dim\">") |
| 731 | if templ_7745c5c3_Err != nil { | 847 | if templ_7745c5c3_Err != nil { | ||
| 732 | return templ_7745c5c3_Err | 848 | return templ_7745c5c3_Err | ||
| 733 | } | 849 | } | ||
| 734 | - | var templ_7745c5c3_Var38 string | 850 | + | var templ_7745c5c3_Var45 string |
| 735 | - | templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(r.Note) | 851 | + | templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(r.Note) |
| 736 | if templ_7745c5c3_Err != nil { | 852 | if templ_7745c5c3_Err != nil { | ||
| 737 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 115, Col: 26} | 853 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 129, Col: 26} |
| 738 | } | 854 | } | ||
| 739 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38)) | 855 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) |
| 740 | if templ_7745c5c3_Err != nil { | 856 | if templ_7745c5c3_Err != nil { | ||
| 741 | return templ_7745c5c3_Err | 857 | return templ_7745c5c3_Err | ||
| 742 | } | 858 | } | ||
| 743 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "</p>") | 859 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "</p>") |
| 744 | if templ_7745c5c3_Err != nil { | 860 | if templ_7745c5c3_Err != nil { | ||
| 745 | return templ_7745c5c3_Err | 861 | return templ_7745c5c3_Err | ||
| 746 | } | 862 | } | ||
| @@ -750,7 +866,7 @@ func ReadmeView(r Readme) templ.Component { | |||||
| 750 | return templ_7745c5c3_Err | 866 | return templ_7745c5c3_Err | ||
| 751 | } | 867 | } | ||
| 752 | } | 868 | } | ||
| 753 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "</article>") | 869 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "</article>") |
| 754 | if templ_7745c5c3_Err != nil { | 870 | if templ_7745c5c3_Err != nil { | ||
| 755 | return templ_7745c5c3_Err | 871 | return templ_7745c5c3_Err | ||
| 756 | } | 872 | } | ||
| @@ -776,133 +892,133 @@ func CommitRows(rows []CommitRow) templ.Component { | |||||
| 776 | }() | 892 | }() | ||
| 777 | } | 893 | } | ||
| 778 | ctx = templ.InitializeContext(ctx) | 894 | ctx = templ.InitializeContext(ctx) | ||
| 779 | - | templ_7745c5c3_Var39 := templ.GetChildren(ctx) | 895 | + | templ_7745c5c3_Var46 := templ.GetChildren(ctx) |
| 780 | - | if templ_7745c5c3_Var39 == nil { | 896 | + | if templ_7745c5c3_Var46 == nil { |
| 781 | - | templ_7745c5c3_Var39 = templ.NopComponent | 897 | + | templ_7745c5c3_Var46 = templ.NopComponent |
| 782 | } | 898 | } | ||
| 783 | ctx = templ.ClearChildren(ctx) | 899 | ctx = templ.ClearChildren(ctx) | ||
| 784 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "<table class=\"list commits\" aria-label=\"Commits\"><tbody data-rows>") | 900 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "<table class=\"list commits\" aria-label=\"Commits\"><tbody data-rows>") |
| 785 | if templ_7745c5c3_Err != nil { | 901 | if templ_7745c5c3_Err != nil { | ||
| 786 | return templ_7745c5c3_Err | 902 | return templ_7745c5c3_Err | ||
| 787 | } | 903 | } | ||
| 788 | for _, c := range rows { | 904 | for _, c := range rows { | ||
| 789 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "<tr") | 905 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "<tr") |
| 790 | if templ_7745c5c3_Err != nil { | 906 | if templ_7745c5c3_Err != nil { | ||
| 791 | return templ_7745c5c3_Err | 907 | return templ_7745c5c3_Err | ||
| 792 | } | 908 | } | ||
| 793 | if c.ID != "" { | 909 | if c.ID != "" { | ||
| 794 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, " id=\"") | 910 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, " id=\"") |
| 795 | if templ_7745c5c3_Err != nil { | 911 | if templ_7745c5c3_Err != nil { | ||
| 796 | return templ_7745c5c3_Err | 912 | return templ_7745c5c3_Err | ||
| 797 | } | 913 | } | ||
| 798 | - | var templ_7745c5c3_Var40 string | 914 | + | var templ_7745c5c3_Var47 string |
| 799 | - | templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.ID) | 915 | + | templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.ID) |
| 800 | if templ_7745c5c3_Err != nil { | 916 | if templ_7745c5c3_Err != nil { | ||
| 801 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 130, Col: 15} | 917 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 144, Col: 15} |
| 802 | } | 918 | } | ||
| 803 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var40) | 919 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var47) |
| 804 | if templ_7745c5c3_Err != nil { | 920 | if templ_7745c5c3_Err != nil { | ||
| 805 | return templ_7745c5c3_Err | 921 | return templ_7745c5c3_Err | ||
| 806 | } | 922 | } | ||
| 807 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "\"") | 923 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "\"") |
| 808 | if templ_7745c5c3_Err != nil { | 924 | if templ_7745c5c3_Err != nil { | ||
| 809 | return templ_7745c5c3_Err | 925 | return templ_7745c5c3_Err | ||
| 810 | } | 926 | } | ||
| 811 | } | 927 | } | ||
| 812 | if c.Cursor { | 928 | if c.Cursor { | ||
| 813 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, " data-cursor") | 929 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, " data-cursor") |
| 814 | if templ_7745c5c3_Err != nil { | 930 | if templ_7745c5c3_Err != nil { | ||
| 815 | return templ_7745c5c3_Err | 931 | return templ_7745c5c3_Err | ||
| 816 | } | 932 | } | ||
| 817 | } | 933 | } | ||
| 818 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "><td class=\"cur\" aria-hidden=\"true\"></td><td class=\"c-sha mute\">") | 934 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "><td class=\"c-sha mute\">") |
| 819 | if templ_7745c5c3_Err != nil { | 935 | if templ_7745c5c3_Err != nil { | ||
| 820 | return templ_7745c5c3_Err | 936 | return templ_7745c5c3_Err | ||
| 821 | } | 937 | } | ||
| 822 | - | var templ_7745c5c3_Var41 string | 938 | + | var templ_7745c5c3_Var48 string |
| 823 | - | templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short) | 939 | + | templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short) |
| 824 | if templ_7745c5c3_Err != nil { | 940 | if templ_7745c5c3_Err != nil { | ||
| 825 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 135, Col: 37} | 941 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 148, Col: 37} |
| 826 | } | 942 | } | ||
| 827 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41)) | 943 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48)) |
| 828 | if templ_7745c5c3_Err != nil { | 944 | if templ_7745c5c3_Err != nil { | ||
| 829 | return templ_7745c5c3_Err | 945 | return templ_7745c5c3_Err | ||
| 830 | } | 946 | } | ||
| 831 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "</td><td class=\"c-subject\"><a href=\"") | 947 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "</td><td class=\"c-subject\"><a href=\"") |
| 832 | if templ_7745c5c3_Err != nil { | 948 | if templ_7745c5c3_Err != nil { | ||
| 833 | return templ_7745c5c3_Err | 949 | return templ_7745c5c3_Err | ||
| 834 | } | 950 | } | ||
| 835 | - | var templ_7745c5c3_Var42 templ.SafeURL | 951 | + | var templ_7745c5c3_Var49 templ.SafeURL |
| 836 | - | templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref)) | 952 | + | templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref)) |
| 837 | if templ_7745c5c3_Err != nil { | 953 | if templ_7745c5c3_Err != nil { | ||
| 838 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 137, Col: 38} | 954 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 150, Col: 38} |
| 839 | } | 955 | } | ||
| 840 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) | 956 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49)) |
| 841 | if templ_7745c5c3_Err != nil { | 957 | if templ_7745c5c3_Err != nil { | ||
| 842 | return templ_7745c5c3_Err | 958 | return templ_7745c5c3_Err | ||
| 843 | } | 959 | } | ||
| 844 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "\">") | 960 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "\">") |
| 845 | if templ_7745c5c3_Err != nil { | 961 | if templ_7745c5c3_Err != nil { | ||
| 846 | return templ_7745c5c3_Err | 962 | return templ_7745c5c3_Err | ||
| 847 | } | 963 | } | ||
| 848 | - | var templ_7745c5c3_Var43 string | 964 | + | var templ_7745c5c3_Var50 string |
| 849 | - | templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject) | 965 | + | templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject) |
| 850 | if templ_7745c5c3_Err != nil { | 966 | if templ_7745c5c3_Err != nil { | ||
| 851 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 137, Col: 52} | 967 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 150, Col: 52} |
| 852 | } | 968 | } | ||
| 853 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43)) | 969 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var50)) |
| 854 | if templ_7745c5c3_Err != nil { | 970 | if templ_7745c5c3_Err != nil { | ||
| 855 | return templ_7745c5c3_Err | 971 | return templ_7745c5c3_Err | ||
| 856 | } | 972 | } | ||
| 857 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "</a> ") | 973 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "</a> ") |
| 858 | if templ_7745c5c3_Err != nil { | 974 | if templ_7745c5c3_Err != nil { | ||
| 859 | return templ_7745c5c3_Err | 975 | return templ_7745c5c3_Err | ||
| 860 | } | 976 | } | ||
| 861 | if c.Merge { | 977 | if c.Merge { | ||
| 862 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "<span class=\"pill\">merge</span> ") | 978 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "<span class=\"pill\">merge</span> ") |
| 863 | if templ_7745c5c3_Err != nil { | 979 | if templ_7745c5c3_Err != nil { | ||
| 864 | return templ_7745c5c3_Err | 980 | return templ_7745c5c3_Err | ||
| 865 | } | 981 | } | ||
| 866 | } | 982 | } | ||
| 867 | for _, b := range c.Branches { | 983 | for _, b := range c.Branches { | ||
| 868 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "<span class=\"pill\">") | 984 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "<span class=\"pill\">") |
| 869 | if templ_7745c5c3_Err != nil { | 985 | if templ_7745c5c3_Err != nil { | ||
| 870 | return templ_7745c5c3_Err | 986 | return templ_7745c5c3_Err | ||
| 871 | } | 987 | } | ||
| 872 | - | var templ_7745c5c3_Var44 string | 988 | + | var templ_7745c5c3_Var51 string |
| 873 | - | templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(b) | 989 | + | templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(b) |
| 874 | if templ_7745c5c3_Err != nil { | 990 | if templ_7745c5c3_Err != nil { | ||
| 875 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 142, Col: 29} | 991 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 155, Col: 29} |
| 876 | } | 992 | } | ||
| 877 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44)) | 993 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51)) |
| 878 | if templ_7745c5c3_Err != nil { | 994 | if templ_7745c5c3_Err != nil { | ||
| 879 | return templ_7745c5c3_Err | 995 | return templ_7745c5c3_Err | ||
| 880 | } | 996 | } | ||
| 881 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "</span> ") | 997 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "</span> ") |
| 882 | if templ_7745c5c3_Err != nil { | 998 | if templ_7745c5c3_Err != nil { | ||
| 883 | return templ_7745c5c3_Err | 999 | return templ_7745c5c3_Err | ||
| 884 | } | 1000 | } | ||
| 885 | } | 1001 | } | ||
| 886 | for _, t := range c.Tags { | 1002 | for _, t := range c.Tags { | ||
| 887 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "<span class=\"pill tag\"><span class=\"sr\">tag </span>") | 1003 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "<span class=\"pill tag\"><span class=\"sr\">tag </span>") |
| 888 | if templ_7745c5c3_Err != nil { | 1004 | if templ_7745c5c3_Err != nil { | ||
| 889 | return templ_7745c5c3_Err | 1005 | return templ_7745c5c3_Err | ||
| 890 | } | 1006 | } | ||
| 891 | - | var templ_7745c5c3_Var45 string | 1007 | + | var templ_7745c5c3_Var52 string |
| 892 | - | templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(t) | 1008 | + | templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.JoinStringErrs(t) |
| 893 | if templ_7745c5c3_Err != nil { | 1009 | if templ_7745c5c3_Err != nil { | ||
| 894 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 145, Col: 61} | 1010 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 158, Col: 61} |
| 895 | } | 1011 | } | ||
| 896 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) | 1012 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var52)) |
| 897 | if templ_7745c5c3_Err != nil { | 1013 | if templ_7745c5c3_Err != nil { | ||
| 898 | return templ_7745c5c3_Err | 1014 | return templ_7745c5c3_Err | ||
| 899 | } | 1015 | } | ||
| 900 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "</span>") | 1016 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, "</span>") |
| 901 | if templ_7745c5c3_Err != nil { | 1017 | if templ_7745c5c3_Err != nil { | ||
| 902 | return templ_7745c5c3_Err | 1018 | return templ_7745c5c3_Err | ||
| 903 | } | 1019 | } | ||
| 904 | } | 1020 | } | ||
| 905 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "</td><td class=\"c-ck\">") | 1021 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, "</td><td class=\"c-ck\">") |
| 906 | if templ_7745c5c3_Err != nil { | 1022 | if templ_7745c5c3_Err != nil { | ||
| 907 | return templ_7745c5c3_Err | 1023 | return templ_7745c5c3_Err | ||
| 908 | } | 1024 | } | ||
| @@ -910,64 +1026,64 @@ func CommitRows(rows []CommitRow) templ.Component { | |||||
| 910 | if templ_7745c5c3_Err != nil { | 1026 | if templ_7745c5c3_Err != nil { | ||
| 911 | return templ_7745c5c3_Err | 1027 | return templ_7745c5c3_Err | ||
| 912 | } | 1028 | } | ||
| 913 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "</td><td class=\"c-who dim\">") | 1029 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "</td><td class=\"c-who dim\">") |
| 914 | if templ_7745c5c3_Err != nil { | 1030 | if templ_7745c5c3_Err != nil { | ||
| 915 | return templ_7745c5c3_Err | 1031 | return templ_7745c5c3_Err | ||
| 916 | } | 1032 | } | ||
| 917 | - | var templ_7745c5c3_Var46 string | 1033 | + | var templ_7745c5c3_Var53 string |
| 918 | - | templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author) | 1034 | + | templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author) |
| 919 | if templ_7745c5c3_Err != nil { | 1035 | if templ_7745c5c3_Err != nil { | ||
| 920 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 149, Col: 37} | 1036 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 162, Col: 37} |
| 921 | } | 1037 | } | ||
| 922 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46)) | 1038 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var53)) |
| 923 | if templ_7745c5c3_Err != nil { | 1039 | if templ_7745c5c3_Err != nil { | ||
| 924 | return templ_7745c5c3_Err | 1040 | return templ_7745c5c3_Err | ||
| 925 | } | 1041 | } | ||
| 926 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "</td><td class=\"c-when nowrap dim\"><time datetime=\"") | 1042 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, "</td><td class=\"c-when nowrap dim\"><time datetime=\"") |
| 927 | if templ_7745c5c3_Err != nil { | 1043 | if templ_7745c5c3_Err != nil { | ||
| 928 | return templ_7745c5c3_Err | 1044 | return templ_7745c5c3_Err | ||
| 929 | } | 1045 | } | ||
| 930 | - | var templ_7745c5c3_Var47 string | 1046 | + | var templ_7745c5c3_Var54 string |
| 931 | - | templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | 1047 | + | templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) |
| 932 | if templ_7745c5c3_Err != nil { | 1048 | if templ_7745c5c3_Err != nil { | ||
| 933 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 150, Col: 60} | 1049 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 163, Col: 60} |
| 934 | } | 1050 | } | ||
| 935 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var47) | 1051 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var54) |
| 936 | if templ_7745c5c3_Err != nil { | 1052 | if templ_7745c5c3_Err != nil { | ||
| 937 | return templ_7745c5c3_Err | 1053 | return templ_7745c5c3_Err | ||
| 938 | } | 1054 | } | ||
| 939 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "\" title=\"") | 1055 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "\" title=\"") |
| 940 | if templ_7745c5c3_Err != nil { | 1056 | if templ_7745c5c3_Err != nil { | ||
| 941 | return templ_7745c5c3_Err | 1057 | return templ_7745c5c3_Err | ||
| 942 | } | 1058 | } | ||
| 943 | - | var templ_7745c5c3_Var48 string | 1059 | + | var templ_7745c5c3_Var55 string |
| 944 | - | templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | 1060 | + | templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) |
| 945 | if templ_7745c5c3_Err != nil { | 1061 | if templ_7745c5c3_Err != nil { | ||
| 946 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 150, Col: 79} | 1062 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 163, Col: 79} |
| 947 | } | 1063 | } | ||
| 948 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var48) | 1064 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var55) |
| 949 | if templ_7745c5c3_Err != nil { | 1065 | if templ_7745c5c3_Err != nil { | ||
| 950 | return templ_7745c5c3_Err | 1066 | return templ_7745c5c3_Err | ||
| 951 | } | 1067 | } | ||
| 952 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "\">") | 1068 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, "\">") |
| 953 | if templ_7745c5c3_Err != nil { | 1069 | if templ_7745c5c3_Err != nil { | ||
| 954 | return templ_7745c5c3_Err | 1070 | return templ_7745c5c3_Err | ||
| 955 | } | 1071 | } | ||
| 956 | - | var templ_7745c5c3_Var49 string | 1072 | + | var templ_7745c5c3_Var56 string |
| 957 | - | templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) | 1073 | + | templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) |
| 958 | if templ_7745c5c3_Err != nil { | 1074 | if templ_7745c5c3_Err != nil { | ||
| 959 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 150, Col: 90} | 1075 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/components.templ`, Line: 163, Col: 90} |
| 960 | } | 1076 | } | ||
| 961 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49)) | 1077 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56)) |
| 962 | if templ_7745c5c3_Err != nil { | 1078 | if templ_7745c5c3_Err != nil { | ||
| 963 | return templ_7745c5c3_Err | 1079 | return templ_7745c5c3_Err | ||
| 964 | } | 1080 | } | ||
| 965 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "</time></td></tr>") | 1081 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, "</time></td></tr>") |
| 966 | if templ_7745c5c3_Err != nil { | 1082 | if templ_7745c5c3_Err != nil { | ||
| 967 | return templ_7745c5c3_Err | 1083 | return templ_7745c5c3_Err | ||
| 968 | } | 1084 | } | ||
| 969 | } | 1085 | } | ||
| 970 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "</tbody></table>") | 1086 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 99, "</tbody></table>") |
| 971 | if templ_7745c5c3_Err != nil { | 1087 | if templ_7745c5c3_Err != nil { | ||
| 972 | return templ_7745c5c3_Err | 1088 | return templ_7745c5c3_Err | ||
| 973 | } | 1089 | } | ||
M internal/adapters/web/format.go +16 -0
| @@ -45,6 +45,22 @@ func rfc3339(t time.Time) string { | |||
| 45 | 45 | return t.UTC().Format(time.RFC3339) | |
| 46 | 46 | } | |
| 47 | 47 | ||
| 48 | + | // grouped writes a count with thousands separated by a thin space: 12 345. | |
| 49 | + | func grouped(n int) string { | |
| 50 | + | s := itoa(n) | |
| 51 | + | if n < 0 || len(s) <= 3 { | |
| 52 | + | return s | |
| 53 | + | } | |
| 54 | + | var b []byte | |
| 55 | + | for i := range len(s) { | |
| 56 | + | if i > 0 && (len(s)-i)%3 == 0 { | |
| 57 | + | b = append(b, "\u202f"...) | |
| 58 | + | } | |
| 59 | + | b = append(b, s[i]) | |
| 60 | + | } | |
| 61 | + | return string(b) | |
| 62 | + | } | |
| 63 | + | ||
| 48 | 64 | func plural(n int, one, many string) string { | |
| 49 | 65 | if n == 1 { | |
| 50 | 66 | return "1 " + one | |
| @@ -45,6 +45,22 @@ func rfc3339(t time.Time) string { | |||||
| 45 | return t.UTC().Format(time.RFC3339) | 45 | return t.UTC().Format(time.RFC3339) | ||
| 46 | } | 46 | } | ||
| 47 | 47 | ||||
| 48 | + | // grouped writes a count with thousands separated by a thin space: 12 345. | |||
| 49 | + | func grouped(n int) string { | |||
| 50 | + | s := itoa(n) | |||
| 51 | + | if n < 0 || len(s) <= 3 { | |||
| 52 | + | return s | |||
| 53 | + | } | |||
| 54 | + | var b []byte | |||
| 55 | + | for i := range len(s) { | |||
| 56 | + | if i > 0 && (len(s)-i)%3 == 0 { | |||
| 57 | + | b = append(b, "\u202f"...) | |||
| 58 | + | } | |||
| 59 | + | b = append(b, s[i]) | |||
| 60 | + | } | |||
| 61 | + | return string(b) | |||
| 62 | + | } | |||
| 63 | + | ||||
| 48 | func plural(n int, one, many string) string { | 64 | func plural(n int, one, many string) string { | ||
| 49 | if n == 1 { | 65 | if n == 1 { | ||
| 50 | return "1 " + one | 66 | return "1 " + one | ||
M internal/adapters/web/home.go +30 -2
| @@ -168,6 +168,7 @@ func (s *Server) homeRowOf(ctx context.Context, r ports.RepoView, now time.Time, | |||
| 168 | 168 | row.Commits, head, row.NoCommits = s.homeCommits(ctx, r, now) | |
| 169 | 169 | if head != "" { | |
| 170 | 170 | row.CI = s.homeCI(ctx, r, head) | |
| 171 | + | row.Stats = s.homeStats(ctx, r, head, now) | |
| 171 | 172 | } | |
| 172 | 173 | return row | |
| 173 | 174 | } | |
| @@ -195,6 +196,33 @@ func (s *Server) homeCommits(ctx context.Context, r ports.RepoView, now time.Tim | |||
| 195 | 196 | return rows, head, len(rows) == 0 | |
| 196 | 197 | } | |
| 197 | 198 | ||
| 199 | + | // homeStats are the figures of a row; nil when the read fails (logged), since | |
| 200 | + | // the row does not depend on them. | |
| 201 | + | func (s *Server) homeStats(ctx context.Context, r ports.RepoView, head string, now time.Time) *HomeStats { | |
| 202 | + | id, err := domain.ParseObjectID(head) | |
| 203 | + | if err != nil { | |
| 204 | + | return nil | |
| 205 | + | } | |
| 206 | + | st, err := s.cfg.Browse.RepoStats(ctx, principal(ctx), r.Name, id, now) | |
| 207 | + | if err != nil { | |
| 208 | + | if !errors.Is(err, domain.ErrNotFound) { | |
| 209 | + | log := zerowrap.FromCtx(ctx) | |
| 210 | + | log.Warn().Err(err).Str("repo", r.Name.String()).Msg("read the figures of the home page") | |
| 211 | + | } | |
| 212 | + | return nil | |
| 213 | + | } | |
| 214 | + | hs := &HomeStats{Commits: grouped(st.Commits), Recent: grouped(st.Recent)} | |
| 215 | + | if t := st.LatestTag; t != nil { | |
| 216 | + | hs.Tag, hs.TagWhen = t.Name, ago(now, t.At) | |
| 217 | + | if !t.CommitID.IsZero() { | |
| 218 | + | // By its commit: a branch of the same name would win over the | |
| 219 | + | // tag in a tree path. | |
| 220 | + | hs.TagHref = repoHref(r.Name.String(), "tree", t.CommitID.String()) | |
| 221 | + | } | |
| 222 | + | } | |
| 223 | + | return hs | |
| 224 | + | } | |
| 225 | + | ||
| 198 | 226 | // homeCI is the state of the CI on the head of the default branch, as the | |
| 199 | 227 | // caller of the request sees it: the combined checks of that commit, read by | |
| 200 | 228 | // its id so the cost does not grow with the CI history. nil when the server | |
| @@ -231,11 +259,11 @@ func homeRow(r ports.RepoView, now time.Time, cursor, signed bool) HomeRow { | |||
| 231 | 259 | name := r.Name.String() | |
| 232 | 260 | row := HomeRow{ | |
| 233 | 261 | ID: "repo-" + name, Name: name, Href: repoHref(name), | |
| 234 | - | Desc: r.Description, Branch: r.DefaultBranch, Cursor: cursor, | |
| 262 | + | Desc: r.Description, Cursor: cursor, | |
| 235 | 263 | Private: signed && r.Visibility == domain.VisibilityPrivate, | |
| 236 | 264 | } | |
| 237 | 265 | if !r.PushedAt.IsZero() { | |
| 238 | - | row.Pushed, row.PushedAt = ago(now, r.PushedAt), rfc3339(r.PushedAt) | |
| 266 | + | row.Active, row.ActiveAt = ago(now, r.PushedAt), rfc3339(r.PushedAt) | |
| 239 | 267 | } | |
| 240 | 268 | return row | |
| 241 | 269 | } | |
| @@ -168,6 +168,7 @@ func (s *Server) homeRowOf(ctx context.Context, r ports.RepoView, now time.Time, | |||||
| 168 | row.Commits, head, row.NoCommits = s.homeCommits(ctx, r, now) | 168 | row.Commits, head, row.NoCommits = s.homeCommits(ctx, r, now) | ||
| 169 | if head != "" { | 169 | if head != "" { | ||
| 170 | row.CI = s.homeCI(ctx, r, head) | 170 | row.CI = s.homeCI(ctx, r, head) | ||
| 171 | + | row.Stats = s.homeStats(ctx, r, head, now) | |||
| 171 | } | 172 | } | ||
| 172 | return row | 173 | return row | ||
| 173 | } | 174 | } | ||
| @@ -195,6 +196,33 @@ func (s *Server) homeCommits(ctx context.Context, r ports.RepoView, now time.Tim | |||||
| 195 | return rows, head, len(rows) == 0 | 196 | return rows, head, len(rows) == 0 | ||
| 196 | } | 197 | } | ||
| 197 | 198 | ||||
| 199 | + | // homeStats are the figures of a row; nil when the read fails (logged), since | |||
| 200 | + | // the row does not depend on them. | |||
| 201 | + | func (s *Server) homeStats(ctx context.Context, r ports.RepoView, head string, now time.Time) *HomeStats { | |||
| 202 | + | id, err := domain.ParseObjectID(head) | |||
| 203 | + | if err != nil { | |||
| 204 | + | return nil | |||
| 205 | + | } | |||
| 206 | + | st, err := s.cfg.Browse.RepoStats(ctx, principal(ctx), r.Name, id, now) | |||
| 207 | + | if err != nil { | |||
| 208 | + | if !errors.Is(err, domain.ErrNotFound) { | |||
| 209 | + | log := zerowrap.FromCtx(ctx) | |||
| 210 | + | log.Warn().Err(err).Str("repo", r.Name.String()).Msg("read the figures of the home page") | |||
| 211 | + | } | |||
| 212 | + | return nil | |||
| 213 | + | } | |||
| 214 | + | hs := &HomeStats{Commits: grouped(st.Commits), Recent: grouped(st.Recent)} | |||
| 215 | + | if t := st.LatestTag; t != nil { | |||
| 216 | + | hs.Tag, hs.TagWhen = t.Name, ago(now, t.At) | |||
| 217 | + | if !t.CommitID.IsZero() { | |||
| 218 | + | // By its commit: a branch of the same name would win over the | |||
| 219 | + | // tag in a tree path. | |||
| 220 | + | hs.TagHref = repoHref(r.Name.String(), "tree", t.CommitID.String()) | |||
| 221 | + | } | |||
| 222 | + | } | |||
| 223 | + | return hs | |||
| 224 | + | } | |||
| 225 | + | ||||
| 198 | // homeCI is the state of the CI on the head of the default branch, as the | 226 | // homeCI is the state of the CI on the head of the default branch, as the | ||
| 199 | // caller of the request sees it: the combined checks of that commit, read by | 227 | // caller of the request sees it: the combined checks of that commit, read by | ||
| 200 | // its id so the cost does not grow with the CI history. nil when the server | 228 | // its id so the cost does not grow with the CI history. nil when the server | ||
| @@ -231,11 +259,11 @@ func homeRow(r ports.RepoView, now time.Time, cursor, signed bool) HomeRow { | |||||
| 231 | name := r.Name.String() | 259 | name := r.Name.String() | ||
| 232 | row := HomeRow{ | 260 | row := HomeRow{ | ||
| 233 | ID: "repo-" + name, Name: name, Href: repoHref(name), | 261 | ID: "repo-" + name, Name: name, Href: repoHref(name), | ||
| 234 | - | Desc: r.Description, Branch: r.DefaultBranch, Cursor: cursor, | 262 | + | Desc: r.Description, Cursor: cursor, |
| 235 | Private: signed && r.Visibility == domain.VisibilityPrivate, | 263 | Private: signed && r.Visibility == domain.VisibilityPrivate, | ||
| 236 | } | 264 | } | ||
| 237 | if !r.PushedAt.IsZero() { | 265 | if !r.PushedAt.IsZero() { | ||
| 238 | - | row.Pushed, row.PushedAt = ago(now, r.PushedAt), rfc3339(r.PushedAt) | 266 | + | row.Active, row.ActiveAt = ago(now, r.PushedAt), rfc3339(r.PushedAt) |
| 239 | } | 267 | } | ||
| 240 | return row | 268 | return row | ||
| 241 | } | 269 | } | ||
M internal/adapters/web/home_test.go +57 -7
| @@ -45,6 +45,55 @@ func homeCommits(t testing.TB, n int) []domain.Commit { | |||
| 45 | 45 | return out | |
| 46 | 46 | } | |
| 47 | 47 | ||
| 48 | + | // homeBrowse is a BrowseService whose figures are always there: 1234 | |
| 49 | + | // commits, 7 of them recent, no tag. Tests that care set their own. | |
| 50 | + | func homeBrowse(t *testing.T) *mocks.BrowseService { | |
| 51 | + | t.Helper() | |
| 52 | + | b := mocks.NewBrowseService(t) | |
| 53 | + | b.EXPECT().RepoStats(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). | |
| 54 | + | Return(ports.RepoStats{Commits: 1234, Recent: 7}, nil).Maybe() | |
| 55 | + | return b | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | // Each row shows the figures of its repository: commits, recent commits, | |
| 59 | + | // latest tag and last activity; no branch name. | |
| 60 | + | func TestHomeRowFigures(t *testing.T) { | |
| 61 | + | t.Parallel() | |
| 62 | + | repo := repoName(t, "proj") | |
| 63 | + | head := objectID(t, fmt.Sprintf("%040x", 2)) | |
| 64 | + | now := time.Now() | |
| 65 | + | browse := mocks.NewBrowseService(t) | |
| 66 | + | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repo, mock.Anything).Return(ports.HistoryPage{Commits: homeCommits(t, 2)}, nil) | |
| 67 | + | browse.EXPECT().RepoStats(mock.Anything, mock.Anything, repo, head, now).Return(ports.RepoStats{ | |
| 68 | + | Commits: 12345, Recent: 42, LatestTag: &domain.TagInfo{Name: "v1.2.0", At: now.Add(-3 * time.Hour), CommitID: head}, | |
| 69 | + | }, nil).Once() | |
| 70 | + | srv := newTestServer(t, Config{Repos: reposServing(t, pushedRepo(t, "proj")), Browse: browse}) | |
| 71 | + | ||
| 72 | + | row := srv.homeRowOf(context.Background(), pushedRepo(t, "proj"), now, false) | |
| 73 | + | require.NotNil(t, row.Stats) | |
| 74 | + | out := renderRow(t, row) | |
| 75 | + | assert.Contains(t, out, "<dt>commits</dt><dd>12\u202f345</dd>") | |
| 76 | + | assert.Contains(t, out, "<dt>last 30 days</dt><dd>42</dd>") | |
| 77 | + | assert.Contains(t, out, `<a href="/proj/tree/`+head.String()+`" title="made 3h ago">v1.2.0</a>`, "by its commit: a branch may share its name") | |
| 78 | + | assert.Contains(t, out, "<dt>last activity</dt>") | |
| 79 | + | assert.NotContains(t, out, "i-branch", "the default branch is not shown") | |
| 80 | + | assert.NotContains(t, out, "pushed") | |
| 81 | + | ||
| 82 | + | // A failed read leaves the figures out, not the row. | |
| 83 | + | browse.EXPECT().RepoStats(mock.Anything, mock.Anything, repo, head, now).Return(ports.RepoStats{}, errors.New("boom")).Once() | |
| 84 | + | row = srv.homeRowOf(context.Background(), pushedRepo(t, "proj"), now, false) | |
| 85 | + | assert.Nil(t, row.Stats) | |
| 86 | + | assert.Len(t, row.Commits, 2) | |
| 87 | + | assert.NotContains(t, renderRow(t, row), "<dt>commits</dt>") | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | func TestGrouped(t *testing.T) { | |
| 91 | + | t.Parallel() | |
| 92 | + | for n, want := range map[int]string{0: "0", 999: "999", 1000: "1\u202f000", 1234567: "1\u202f234\u202f567"} { | |
| 93 | + | assert.Equal(t, want, grouped(n), n) | |
| 94 | + | } | |
| 95 | + | } | |
| 96 | + | ||
| 48 | 97 | func renderRow(t testing.TB, r HomeRow) string { | |
| 49 | 98 | t.Helper() | |
| 50 | 99 | var b bytes.Buffer | |
| @@ -63,7 +112,7 @@ func mustRevision(t testing.TB, s string) domain.Revision { | |||
| 63 | 112 | // name of the branch. | |
| 64 | 113 | func TestHomeRowShowsFiveCommits(t *testing.T) { | |
| 65 | 114 | t.Parallel() | |
| 66 | - | browse := mocks.NewBrowseService(t) | |
| 115 | + | browse := homeBrowse(t) | |
| 67 | 116 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repoName(t, "proj"), ports.ListCommitsParams{ | |
| 68 | 117 | Revision: mustRevision(t, "refs/heads/main"), PageSize: homeRecent, | |
| 69 | 118 | }).Return(ports.HistoryPage{Commits: homeCommits(t, 7)}, nil).Once() | |
| @@ -90,7 +139,8 @@ func TestHomeRowEmptyRepository(t *testing.T) { | |||
| 90 | 139 | assert.True(t, row.NoCommits) | |
| 91 | 140 | out := renderRow(t, row) | |
| 92 | 141 | assert.Contains(t, out, "no commits yet") | |
| 93 | - | assert.Contains(t, out, "never pushed") | |
| 142 | + | assert.Contains(t, out, `<dt>last activity</dt><dd class="mute">never</dd>`) | |
| 143 | + | assert.NotContains(t, out, "<dt>commits</dt>", "no figures without a commit") | |
| 94 | 144 | assert.NotContains(t, out, "repo-commits\" aria-label") | |
| 95 | 145 | } | |
| 96 | 146 | ||
| @@ -119,7 +169,7 @@ func TestHomeRowCI(t *testing.T) { | |||
| 119 | 169 | for _, tt := range tests { | |
| 120 | 170 | t.Run(tt.name, func(t *testing.T) { | |
| 121 | 171 | t.Parallel() | |
| 122 | - | browse := mocks.NewBrowseService(t) | |
| 172 | + | browse := homeBrowse(t) | |
| 123 | 173 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repo, mock.Anything).Return(ports.HistoryPage{Commits: homeCommits(t, 2)}, nil) | |
| 124 | 174 | runs := mocks.NewRunService(t) | |
| 125 | 175 | runs.EXPECT().ListChecks(mock.Anything, mock.Anything, repo, head). | |
| @@ -147,7 +197,7 @@ func TestHomeRowPrivateBadge(t *testing.T) { | |||
| 147 | 197 | t.Parallel() | |
| 148 | 198 | priv := pushedRepo(t, "secret") | |
| 149 | 199 | priv.Visibility = domain.VisibilityPrivate | |
| 150 | - | browse := mocks.NewBrowseService(t) | |
| 200 | + | browse := homeBrowse(t) | |
| 151 | 201 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(ports.HistoryPage{}, nil).Maybe() | |
| 152 | 202 | srv := newTestServer(t, Config{Repos: reposServing(t, priv, pushedRepo(t, "open")), Browse: browse}) | |
| 153 | 203 | ||
| @@ -162,7 +212,7 @@ func TestRunUpdatedRefreshesTheHomeRow(t *testing.T) { | |||
| 162 | 212 | t.Parallel() | |
| 163 | 213 | repo := repoName(t, "proj") | |
| 164 | 214 | head := fmt.Sprintf("%040x", 2) | |
| 165 | - | browse := mocks.NewBrowseService(t) | |
| 215 | + | browse := homeBrowse(t) | |
| 166 | 216 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repo, mock.Anything).Return(ports.HistoryPage{Commits: homeCommits(t, 2)}, nil) | |
| 167 | 217 | runs := mocks.NewRunService(t) | |
| 168 | 218 | runs.EXPECT().ListChecks(mock.Anything, mock.Anything, repo, head).Return(ports.ChecksResult{Checks: []ports.CheckView{ | |
| @@ -196,7 +246,7 @@ func TestRunUpdatedRefreshesTheHomeRow(t *testing.T) { | |||
| 196 | 246 | // served; the others are not affected. | |
| 197 | 247 | func TestHomeSurvivesABrokenRepository(t *testing.T) { | |
| 198 | 248 | t.Parallel() | |
| 199 | - | browse := mocks.NewBrowseService(t) | |
| 249 | + | browse := homeBrowse(t) | |
| 200 | 250 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repoName(t, "broken"), mock.Anything). | |
| 201 | 251 | Return(ports.HistoryPage{}, errors.New("git exploded")).Once() | |
| 202 | 252 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repoName(t, "fine"), mock.Anything). | |
| @@ -222,7 +272,7 @@ func TestHomeRowsAreBoundedAndOrdered(t *testing.T) { | |||
| 222 | 272 | views = append(views, pushedRepo(t, fmt.Sprintf("r%02d", i))) | |
| 223 | 273 | } | |
| 224 | 274 | var running, peak atomic.Int32 | |
| 225 | - | browse := mocks.NewBrowseService(t) | |
| 275 | + | browse := homeBrowse(t) | |
| 226 | 276 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, mock.Anything, mock.Anything).RunAndReturn( | |
| 227 | 277 | func(context.Context, domain.Principal, domain.RepoName, ports.ListCommitsParams) (ports.HistoryPage, error) { | |
| 228 | 278 | cur := running.Add(1) | |
| @@ -45,6 +45,55 @@ func homeCommits(t testing.TB, n int) []domain.Commit { | |||||
| 45 | return out | 45 | return out | ||
| 46 | } | 46 | } | ||
| 47 | 47 | ||||
| 48 | + | // homeBrowse is a BrowseService whose figures are always there: 1234 | |||
| 49 | + | // commits, 7 of them recent, no tag. Tests that care set their own. | |||
| 50 | + | func homeBrowse(t *testing.T) *mocks.BrowseService { | |||
| 51 | + | t.Helper() | |||
| 52 | + | b := mocks.NewBrowseService(t) | |||
| 53 | + | b.EXPECT().RepoStats(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). | |||
| 54 | + | Return(ports.RepoStats{Commits: 1234, Recent: 7}, nil).Maybe() | |||
| 55 | + | return b | |||
| 56 | + | } | |||
| 57 | + | ||||
| 58 | + | // Each row shows the figures of its repository: commits, recent commits, | |||
| 59 | + | // latest tag and last activity; no branch name. | |||
| 60 | + | func TestHomeRowFigures(t *testing.T) { | |||
| 61 | + | t.Parallel() | |||
| 62 | + | repo := repoName(t, "proj") | |||
| 63 | + | head := objectID(t, fmt.Sprintf("%040x", 2)) | |||
| 64 | + | now := time.Now() | |||
| 65 | + | browse := mocks.NewBrowseService(t) | |||
| 66 | + | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repo, mock.Anything).Return(ports.HistoryPage{Commits: homeCommits(t, 2)}, nil) | |||
| 67 | + | browse.EXPECT().RepoStats(mock.Anything, mock.Anything, repo, head, now).Return(ports.RepoStats{ | |||
| 68 | + | Commits: 12345, Recent: 42, LatestTag: &domain.TagInfo{Name: "v1.2.0", At: now.Add(-3 * time.Hour), CommitID: head}, | |||
| 69 | + | }, nil).Once() | |||
| 70 | + | srv := newTestServer(t, Config{Repos: reposServing(t, pushedRepo(t, "proj")), Browse: browse}) | |||
| 71 | + | ||||
| 72 | + | row := srv.homeRowOf(context.Background(), pushedRepo(t, "proj"), now, false) | |||
| 73 | + | require.NotNil(t, row.Stats) | |||
| 74 | + | out := renderRow(t, row) | |||
| 75 | + | assert.Contains(t, out, "<dt>commits</dt><dd>12\u202f345</dd>") | |||
| 76 | + | assert.Contains(t, out, "<dt>last 30 days</dt><dd>42</dd>") | |||
| 77 | + | assert.Contains(t, out, `<a href="/proj/tree/`+head.String()+`" title="made 3h ago">v1.2.0</a>`, "by its commit: a branch may share its name") | |||
| 78 | + | assert.Contains(t, out, "<dt>last activity</dt>") | |||
| 79 | + | assert.NotContains(t, out, "i-branch", "the default branch is not shown") | |||
| 80 | + | assert.NotContains(t, out, "pushed") | |||
| 81 | + | ||||
| 82 | + | // A failed read leaves the figures out, not the row. | |||
| 83 | + | browse.EXPECT().RepoStats(mock.Anything, mock.Anything, repo, head, now).Return(ports.RepoStats{}, errors.New("boom")).Once() | |||
| 84 | + | row = srv.homeRowOf(context.Background(), pushedRepo(t, "proj"), now, false) | |||
| 85 | + | assert.Nil(t, row.Stats) | |||
| 86 | + | assert.Len(t, row.Commits, 2) | |||
| 87 | + | assert.NotContains(t, renderRow(t, row), "<dt>commits</dt>") | |||
| 88 | + | } | |||
| 89 | + | ||||
| 90 | + | func TestGrouped(t *testing.T) { | |||
| 91 | + | t.Parallel() | |||
| 92 | + | for n, want := range map[int]string{0: "0", 999: "999", 1000: "1\u202f000", 1234567: "1\u202f234\u202f567"} { | |||
| 93 | + | assert.Equal(t, want, grouped(n), n) | |||
| 94 | + | } | |||
| 95 | + | } | |||
| 96 | + | ||||
| 48 | func renderRow(t testing.TB, r HomeRow) string { | 97 | func renderRow(t testing.TB, r HomeRow) string { | ||
| 49 | t.Helper() | 98 | t.Helper() | ||
| 50 | var b bytes.Buffer | 99 | var b bytes.Buffer | ||
| @@ -63,7 +112,7 @@ func mustRevision(t testing.TB, s string) domain.Revision { | |||||
| 63 | // name of the branch. | 112 | // name of the branch. | ||
| 64 | func TestHomeRowShowsFiveCommits(t *testing.T) { | 113 | func TestHomeRowShowsFiveCommits(t *testing.T) { | ||
| 65 | t.Parallel() | 114 | t.Parallel() | ||
| 66 | - | browse := mocks.NewBrowseService(t) | 115 | + | browse := homeBrowse(t) |
| 67 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repoName(t, "proj"), ports.ListCommitsParams{ | 116 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repoName(t, "proj"), ports.ListCommitsParams{ | ||
| 68 | Revision: mustRevision(t, "refs/heads/main"), PageSize: homeRecent, | 117 | Revision: mustRevision(t, "refs/heads/main"), PageSize: homeRecent, | ||
| 69 | }).Return(ports.HistoryPage{Commits: homeCommits(t, 7)}, nil).Once() | 118 | }).Return(ports.HistoryPage{Commits: homeCommits(t, 7)}, nil).Once() | ||
| @@ -90,7 +139,8 @@ func TestHomeRowEmptyRepository(t *testing.T) { | |||||
| 90 | assert.True(t, row.NoCommits) | 139 | assert.True(t, row.NoCommits) | ||
| 91 | out := renderRow(t, row) | 140 | out := renderRow(t, row) | ||
| 92 | assert.Contains(t, out, "no commits yet") | 141 | assert.Contains(t, out, "no commits yet") | ||
| 93 | - | assert.Contains(t, out, "never pushed") | 142 | + | assert.Contains(t, out, `<dt>last activity</dt><dd class="mute">never</dd>`) |
| 143 | + | assert.NotContains(t, out, "<dt>commits</dt>", "no figures without a commit") | |||
| 94 | assert.NotContains(t, out, "repo-commits\" aria-label") | 144 | assert.NotContains(t, out, "repo-commits\" aria-label") | ||
| 95 | } | 145 | } | ||
| 96 | 146 | ||||
| @@ -119,7 +169,7 @@ func TestHomeRowCI(t *testing.T) { | |||||
| 119 | for _, tt := range tests { | 169 | for _, tt := range tests { | ||
| 120 | t.Run(tt.name, func(t *testing.T) { | 170 | t.Run(tt.name, func(t *testing.T) { | ||
| 121 | t.Parallel() | 171 | t.Parallel() | ||
| 122 | - | browse := mocks.NewBrowseService(t) | 172 | + | browse := homeBrowse(t) |
| 123 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repo, mock.Anything).Return(ports.HistoryPage{Commits: homeCommits(t, 2)}, nil) | 173 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repo, mock.Anything).Return(ports.HistoryPage{Commits: homeCommits(t, 2)}, nil) | ||
| 124 | runs := mocks.NewRunService(t) | 174 | runs := mocks.NewRunService(t) | ||
| 125 | runs.EXPECT().ListChecks(mock.Anything, mock.Anything, repo, head). | 175 | runs.EXPECT().ListChecks(mock.Anything, mock.Anything, repo, head). | ||
| @@ -147,7 +197,7 @@ func TestHomeRowPrivateBadge(t *testing.T) { | |||||
| 147 | t.Parallel() | 197 | t.Parallel() | ||
| 148 | priv := pushedRepo(t, "secret") | 198 | priv := pushedRepo(t, "secret") | ||
| 149 | priv.Visibility = domain.VisibilityPrivate | 199 | priv.Visibility = domain.VisibilityPrivate | ||
| 150 | - | browse := mocks.NewBrowseService(t) | 200 | + | browse := homeBrowse(t) |
| 151 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(ports.HistoryPage{}, nil).Maybe() | 201 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(ports.HistoryPage{}, nil).Maybe() | ||
| 152 | srv := newTestServer(t, Config{Repos: reposServing(t, priv, pushedRepo(t, "open")), Browse: browse}) | 202 | srv := newTestServer(t, Config{Repos: reposServing(t, priv, pushedRepo(t, "open")), Browse: browse}) | ||
| 153 | 203 | ||||
| @@ -162,7 +212,7 @@ func TestRunUpdatedRefreshesTheHomeRow(t *testing.T) { | |||||
| 162 | t.Parallel() | 212 | t.Parallel() | ||
| 163 | repo := repoName(t, "proj") | 213 | repo := repoName(t, "proj") | ||
| 164 | head := fmt.Sprintf("%040x", 2) | 214 | head := fmt.Sprintf("%040x", 2) | ||
| 165 | - | browse := mocks.NewBrowseService(t) | 215 | + | browse := homeBrowse(t) |
| 166 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repo, mock.Anything).Return(ports.HistoryPage{Commits: homeCommits(t, 2)}, nil) | 216 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repo, mock.Anything).Return(ports.HistoryPage{Commits: homeCommits(t, 2)}, nil) | ||
| 167 | runs := mocks.NewRunService(t) | 217 | runs := mocks.NewRunService(t) | ||
| 168 | runs.EXPECT().ListChecks(mock.Anything, mock.Anything, repo, head).Return(ports.ChecksResult{Checks: []ports.CheckView{ | 218 | runs.EXPECT().ListChecks(mock.Anything, mock.Anything, repo, head).Return(ports.ChecksResult{Checks: []ports.CheckView{ | ||
| @@ -196,7 +246,7 @@ func TestRunUpdatedRefreshesTheHomeRow(t *testing.T) { | |||||
| 196 | // served; the others are not affected. | 246 | // served; the others are not affected. | ||
| 197 | func TestHomeSurvivesABrokenRepository(t *testing.T) { | 247 | func TestHomeSurvivesABrokenRepository(t *testing.T) { | ||
| 198 | t.Parallel() | 248 | t.Parallel() | ||
| 199 | - | browse := mocks.NewBrowseService(t) | 249 | + | browse := homeBrowse(t) |
| 200 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repoName(t, "broken"), mock.Anything). | 250 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repoName(t, "broken"), mock.Anything). | ||
| 201 | Return(ports.HistoryPage{}, errors.New("git exploded")).Once() | 251 | Return(ports.HistoryPage{}, errors.New("git exploded")).Once() | ||
| 202 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repoName(t, "fine"), mock.Anything). | 252 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, repoName(t, "fine"), mock.Anything). | ||
| @@ -222,7 +272,7 @@ func TestHomeRowsAreBoundedAndOrdered(t *testing.T) { | |||||
| 222 | views = append(views, pushedRepo(t, fmt.Sprintf("r%02d", i))) | 272 | views = append(views, pushedRepo(t, fmt.Sprintf("r%02d", i))) | ||
| 223 | } | 273 | } | ||
| 224 | var running, peak atomic.Int32 | 274 | var running, peak atomic.Int32 | ||
| 225 | - | browse := mocks.NewBrowseService(t) | 275 | + | browse := homeBrowse(t) |
| 226 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, mock.Anything, mock.Anything).RunAndReturn( | 276 | browse.EXPECT().ListCommits(mock.Anything, mock.Anything, mock.Anything, mock.Anything).RunAndReturn( | ||
| 227 | func(context.Context, domain.Principal, domain.RepoName, ports.ListCommitsParams) (ports.HistoryPage, error) { | 277 | func(context.Context, domain.Principal, domain.RepoName, ports.ListCommitsParams) (ports.HistoryPage, error) { | ||
| 228 | cur := running.Add(1) | 278 | cur := running.Add(1) | ||
M internal/adapters/web/mr.go +6 -6
| @@ -199,12 +199,12 @@ type MRPage struct { | |||
| 199 | 199 | UpdatedAt string | |
| 200 | 200 | // Desc is the description rendered as Markdown (as a README: no raw | |
| 201 | 201 | // HTML, filtered URLs), "" when it is empty. | |
| 202 | - | Desc template.HTML | |
| 203 | - | Head string | |
| 204 | - | HeadShort string | |
| 205 | - | MergedShort string | |
| 206 | - | MergedHref string | |
| 207 | - | MergeMode string | |
| 202 | + | Desc template.HTML | |
| 203 | + | Head string | |
| 204 | + | HeadShort string | |
| 205 | + | MergedShort string | |
| 206 | + | MergedHref string | |
| 207 | + | MergeMode string | |
| 208 | 208 | ||
| 209 | 209 | Since *SinceNav | |
| 210 | 210 | SinceNote string | |
| @@ -199,12 +199,12 @@ type MRPage struct { | |||||
| 199 | UpdatedAt string | 199 | UpdatedAt string | ||
| 200 | // Desc is the description rendered as Markdown (as a README: no raw | 200 | // Desc is the description rendered as Markdown (as a README: no raw | ||
| 201 | // HTML, filtered URLs), "" when it is empty. | 201 | // HTML, filtered URLs), "" when it is empty. | ||
| 202 | - | Desc template.HTML | 202 | + | Desc template.HTML |
| 203 | - | Head string | 203 | + | Head string |
| 204 | - | HeadShort string | 204 | + | HeadShort string |
| 205 | - | MergedShort string | 205 | + | MergedShort string |
| 206 | - | MergedHref string | 206 | + | MergedHref string |
| 207 | - | MergeMode string | 207 | + | MergeMode string |
| 208 | 208 | ||||
| 209 | Since *SinceNav | 209 | Since *SinceNav | ||
| 210 | SinceNote string | 210 | SinceNote string | ||
M internal/adapters/web/mr.templ +0 -1
| @@ -60,7 +60,6 @@ templ mrRows(rows []MRRow, label string) { | |||
| 60 | 60 | <tbody data-rows> | |
| 61 | 61 | for _, m := range rows { | |
| 62 | 62 | <tr data-cursor?={ m.Cursor } data-state={ m.State }> | |
| 63 | - | <td class="cur" aria-hidden="true"></td> | |
| 64 | 63 | <td class="c-n mute">{ m.Number }</td> | |
| 65 | 64 | <td class="c-state">{ m.State }</td> | |
| 66 | 65 | <td class="c-subject"><a href={ templ.SafeURL(m.Href) }>{ m.Title }</a> <span class="mute">{ m.Branches }</span></td> | |
| @@ -60,7 +60,6 @@ templ mrRows(rows []MRRow, label string) { | |||||
| 60 | <tbody data-rows> | 60 | <tbody data-rows> | ||
| 61 | for _, m := range rows { | 61 | for _, m := range rows { | ||
| 62 | <tr data-cursor?={ m.Cursor } data-state={ m.State }> | 62 | <tr data-cursor?={ m.Cursor } data-state={ m.State }> | ||
| 63 | - | <td class="cur" aria-hidden="true"></td> | |||
| 64 | <td class="c-n mute">{ m.Number }</td> | 63 | <td class="c-n mute">{ m.Number }</td> | ||
| 65 | <td class="c-state">{ m.State }</td> | 64 | <td class="c-state">{ m.State }</td> | ||
| 66 | <td class="c-subject"><a href={ templ.SafeURL(m.Href) }>{ m.Title }</a> <span class="mute">{ m.Branches }</span></td> | 65 | <td class="c-subject"><a href={ templ.SafeURL(m.Href) }>{ m.Title }</a> <span class="mute">{ m.Branches }</span></td> | ||
M internal/adapters/web/mr_templ.go +124 -124 generated file: collapsed
M internal/adapters/web/pages.templ +1 -4
| @@ -122,7 +122,7 @@ templ Refs(l Layout, p RefsPage) { | |||
| 122 | 122 | </div> | |
| 123 | 123 | <table class="list refs" aria-label="Branches and tags"> | |
| 124 | 124 | <thead> | |
| 125 | - | <tr><th class="cur"></th><th class="c-kind">kind</th><th class="c-name">name</th><th class="c-sha">commit</th><th class="c-sub">subject</th><th class="c-when">date</th></tr> | |
| 125 | + | <tr><th class="c-kind">kind</th><th class="c-name">name</th><th class="c-sha">commit</th><th class="c-sub">subject</th><th class="c-when">date</th></tr> | |
| 126 | 126 | </thead> | |
| 127 | 127 | @RefsBody(p) | |
| 128 | 128 | </table> | |
| @@ -134,7 +134,6 @@ templ Refs(l Layout, p RefsPage) { | |||
| 134 | 134 | ||
| 135 | 135 | templ refRow(kind string, r RefRow) { | |
| 136 | 136 | <tr data-cursor?={ r.Cursor }> | |
| 137 | - | <td class="cur" aria-hidden="true"></td> | |
| 138 | 137 | <td class="c-kind mute">{ kind }</td> | |
| 139 | 138 | <td class="c-name"> | |
| 140 | 139 | <a href={ templ.SafeURL(r.Href) }>{ r.Name }</a> | |
| @@ -234,7 +233,6 @@ templ Passkeys(l Layout, p PasskeysPage) { | |||
| 234 | 233 | <table class="list passkeys" aria-label="Passkeys"> | |
| 235 | 234 | <thead> | |
| 236 | 235 | <tr> | |
| 237 | - | <th class="cur"></th> | |
| 238 | 236 | <th class="c-name">Name</th> | |
| 239 | 237 | <th class="c-when">Created</th> | |
| 240 | 238 | <th class="c-when">Last used</th> | |
| @@ -245,7 +243,6 @@ templ Passkeys(l Layout, p PasskeysPage) { | |||
| 245 | 243 | <tbody> | |
| 246 | 244 | for _, r := range p.Rows { | |
| 247 | 245 | <tr> | |
| 248 | - | <td class="cur" aria-hidden="true"></td> | |
| 249 | 246 | <td class="c-name">{ r.Name }</td> | |
| 250 | 247 | <td class="c-when nowrap"> | |
| 251 | 248 | <time | |
| @@ -122,7 +122,7 @@ templ Refs(l Layout, p RefsPage) { | |||||
| 122 | </div> | 122 | </div> | ||
| 123 | <table class="list refs" aria-label="Branches and tags"> | 123 | <table class="list refs" aria-label="Branches and tags"> | ||
| 124 | <thead> | 124 | <thead> | ||
| 125 | - | <tr><th class="cur"></th><th class="c-kind">kind</th><th class="c-name">name</th><th class="c-sha">commit</th><th class="c-sub">subject</th><th class="c-when">date</th></tr> | 125 | + | <tr><th class="c-kind">kind</th><th class="c-name">name</th><th class="c-sha">commit</th><th class="c-sub">subject</th><th class="c-when">date</th></tr> |
| 126 | </thead> | 126 | </thead> | ||
| 127 | @RefsBody(p) | 127 | @RefsBody(p) | ||
| 128 | </table> | 128 | </table> | ||
| @@ -134,7 +134,6 @@ templ Refs(l Layout, p RefsPage) { | |||||
| 134 | 134 | ||||
| 135 | templ refRow(kind string, r RefRow) { | 135 | templ refRow(kind string, r RefRow) { | ||
| 136 | <tr data-cursor?={ r.Cursor }> | 136 | <tr data-cursor?={ r.Cursor }> | ||
| 137 | - | <td class="cur" aria-hidden="true"></td> | |||
| 138 | <td class="c-kind mute">{ kind }</td> | 137 | <td class="c-kind mute">{ kind }</td> | ||
| 139 | <td class="c-name"> | 138 | <td class="c-name"> | ||
| 140 | <a href={ templ.SafeURL(r.Href) }>{ r.Name }</a> | 139 | <a href={ templ.SafeURL(r.Href) }>{ r.Name }</a> | ||
| @@ -234,7 +233,6 @@ templ Passkeys(l Layout, p PasskeysPage) { | |||||
| 234 | <table class="list passkeys" aria-label="Passkeys"> | 233 | <table class="list passkeys" aria-label="Passkeys"> | ||
| 235 | <thead> | 234 | <thead> | ||
| 236 | <tr> | 235 | <tr> | ||
| 237 | - | <th class="cur"></th> | |||
| 238 | <th class="c-name">Name</th> | 236 | <th class="c-name">Name</th> | ||
| 239 | <th class="c-when">Created</th> | 237 | <th class="c-when">Created</th> | ||
| 240 | <th class="c-when">Last used</th> | 238 | <th class="c-when">Last used</th> | ||
| @@ -245,7 +243,6 @@ templ Passkeys(l Layout, p PasskeysPage) { | |||||
| 245 | <tbody> | 243 | <tbody> | ||
| 246 | for _, r := range p.Rows { | 244 | for _, r := range p.Rows { | ||
| 247 | <tr> | 245 | <tr> | ||
| 248 | - | <td class="cur" aria-hidden="true"></td> | |||
| 249 | <td class="c-name">{ r.Name }</td> | 246 | <td class="c-name">{ r.Name }</td> | ||
| 250 | <td class="c-when nowrap"> | 247 | <td class="c-when nowrap"> | ||
| 251 | <time | 248 | <time | ||
M internal/adapters/web/pages_templ.go +39 -39 generated file: collapsed
M internal/adapters/web/static/css/30-list.css +12 -8
| @@ -14,10 +14,9 @@ | |||
| 14 | 14 | & th[aria-sort="ascending"], & th[aria-sort="descending"] { color: var(--ink-hi); } | |
| 15 | 15 | & td { height: 40px; padding: 0 var(--s3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border-top: 1px solid var(--border); } | |
| 16 | 16 | & tbody tr:first-child td { border-top: 0; } | |
| 17 | - | & th.cur, & td.cur { width: 3px; padding: 0; } | |
| 18 | 17 | & tbody tr:hover { background: var(--bg-raised); } | |
| 19 | - | & tbody tr[data-cursor] { background: var(--bg-raised); } | |
| 20 | - | & tbody tr[data-cursor] .cur { background: var(--ink-hi); } | |
| 18 | + | /* the cursor: the row raised, framed by one even line */ | |
| 19 | + | & tbody tr[data-cursor] { background: var(--bg-raised); outline: 1px solid var(--ink-mute); outline-offset: -1px; } | |
| 21 | 20 | & tbody tr[hidden] { display: none; } | |
| 22 | 21 | & td > a { text-decoration: none; } | |
| 23 | 22 | & td > a:first-child { color: var(--ink-hi); } | |
| @@ -54,7 +53,7 @@ | |||
| 54 | 53 | padding: var(--s4); background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); | |
| 55 | 54 | } | |
| 56 | 55 | & .repo:hover { background: var(--bg-raised); border-color: var(--border-strong); } | |
| 57 | - | & .repo[data-cursor] { background: var(--bg-raised); border-color: var(--ink-mute); box-shadow: inset 3px 0 0 var(--ink-hi); } | |
| 56 | + | & .repo[data-cursor] { background: var(--bg-raised); border-color: var(--ink-mute); } | |
| 58 | 57 | & .repo[hidden] { display: none; } | |
| 59 | 58 | & .repo-main { grid-area: main; display: flex; flex-direction: column; gap: var(--s2); min-width: 0; } | |
| 60 | 59 | & .repo-name { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s1) var(--s2); font-family: var(--mono); font-size: 17px; line-height: 26px; overflow-wrap: anywhere; } | |
| @@ -66,9 +65,14 @@ | |||
| 66 | 65 | display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; | |
| 67 | 66 | } | |
| 68 | 67 | & .repo-desc.mute { color: var(--ink-mute); font-style: italic; } | |
| 69 | - | & .repo-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s4); font-size: var(--fs-s); color: var(--ink-mute); } | |
| 70 | - | & .repo-meta time { color: var(--ink-dim); } | |
| 71 | - | & .repo-branch { display: inline-flex; align-items: center; gap: 6px; font-family: var(--mono); color: var(--ink-dim); } | |
| 68 | + | /* the figures: a small label over a mono value, side by side */ | |
| 69 | + | & .repo-stats { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); margin: var(--s1) 0 0; } | |
| 70 | + | & .repo-stats div { display: flex; flex-direction: column; gap: 2px; min-width: 0; } | |
| 71 | + | & .repo-stats dt { font-size: 11px; line-height: 14px; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-mute); } | |
| 72 | + | & .repo-stats dd { margin: 0; font-family: var(--mono); font-size: 15px; line-height: 20px; color: var(--ink-hi); font-variant-numeric: tabular-nums; white-space: nowrap; } | |
| 73 | + | & .repo-stats dd.mute { color: var(--ink-mute); } | |
| 74 | + | & .repo-stats dd a { color: inherit; text-decoration: none; } | |
| 75 | + | & .repo-stats dd a:hover { text-decoration: underline; } | |
| 72 | 76 | & .repo-ci { | |
| 73 | 77 | grid-area: ci; justify-self: end; display: inline-flex; align-items: center; gap: var(--s1); margin-bottom: var(--s2); | |
| 74 | 78 | font-size: var(--fs-s); color: var(--ink-dim); text-decoration: none; | |
| @@ -168,7 +172,7 @@ | |||
| 168 | 172 | min-height: var(--rowh); padding-right: var(--s4); font-size: var(--fs-s); | |
| 169 | 173 | } | |
| 170 | 174 | & > li:hover, & > li[data-cursor] { background: var(--bg-raised); } | |
| 171 | - | & > li[data-cursor] { box-shadow: inset 3px 0 0 var(--ink-hi); } | |
| 175 | + | & > li[data-cursor] { outline: 1px solid var(--ink-mute); outline-offset: -1px; } | |
| 172 | 176 | & > li:focus-visible { outline-offset: -2px; border-radius: 0; } | |
| 173 | 177 | & .t-main { display: flex; align-items: stretch; min-width: 0; min-height: var(--rowh); padding-left: var(--s3); } | |
| 174 | 178 | & .t-twig { | |
| @@ -14,10 +14,9 @@ | |||||
| 14 | & th[aria-sort="ascending"], & th[aria-sort="descending"] { color: var(--ink-hi); } | 14 | & th[aria-sort="ascending"], & th[aria-sort="descending"] { color: var(--ink-hi); } | ||
| 15 | & td { height: 40px; padding: 0 var(--s3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border-top: 1px solid var(--border); } | 15 | & td { height: 40px; padding: 0 var(--s3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border-top: 1px solid var(--border); } | ||
| 16 | & tbody tr:first-child td { border-top: 0; } | 16 | & tbody tr:first-child td { border-top: 0; } | ||
| 17 | - | & th.cur, & td.cur { width: 3px; padding: 0; } | |||
| 18 | & tbody tr:hover { background: var(--bg-raised); } | 17 | & tbody tr:hover { background: var(--bg-raised); } | ||
| 19 | - | & tbody tr[data-cursor] { background: var(--bg-raised); } | 18 | + | /* the cursor: the row raised, framed by one even line */ |
| 20 | - | & tbody tr[data-cursor] .cur { background: var(--ink-hi); } | 19 | + | & tbody tr[data-cursor] { background: var(--bg-raised); outline: 1px solid var(--ink-mute); outline-offset: -1px; } |
| 21 | & tbody tr[hidden] { display: none; } | 20 | & tbody tr[hidden] { display: none; } | ||
| 22 | & td > a { text-decoration: none; } | 21 | & td > a { text-decoration: none; } | ||
| 23 | & td > a:first-child { color: var(--ink-hi); } | 22 | & td > a:first-child { color: var(--ink-hi); } | ||
| @@ -54,7 +53,7 @@ | |||||
| 54 | padding: var(--s4); background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); | 53 | padding: var(--s4); background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); | ||
| 55 | } | 54 | } | ||
| 56 | & .repo:hover { background: var(--bg-raised); border-color: var(--border-strong); } | 55 | & .repo:hover { background: var(--bg-raised); border-color: var(--border-strong); } | ||
| 57 | - | & .repo[data-cursor] { background: var(--bg-raised); border-color: var(--ink-mute); box-shadow: inset 3px 0 0 var(--ink-hi); } | 56 | + | & .repo[data-cursor] { background: var(--bg-raised); border-color: var(--ink-mute); } |
| 58 | & .repo[hidden] { display: none; } | 57 | & .repo[hidden] { display: none; } | ||
| 59 | & .repo-main { grid-area: main; display: flex; flex-direction: column; gap: var(--s2); min-width: 0; } | 58 | & .repo-main { grid-area: main; display: flex; flex-direction: column; gap: var(--s2); min-width: 0; } | ||
| 60 | & .repo-name { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s1) var(--s2); font-family: var(--mono); font-size: 17px; line-height: 26px; overflow-wrap: anywhere; } | 59 | & .repo-name { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s1) var(--s2); font-family: var(--mono); font-size: 17px; line-height: 26px; overflow-wrap: anywhere; } | ||
| @@ -66,9 +65,14 @@ | |||||
| 66 | display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; | 65 | display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; | ||
| 67 | } | 66 | } | ||
| 68 | & .repo-desc.mute { color: var(--ink-mute); font-style: italic; } | 67 | & .repo-desc.mute { color: var(--ink-mute); font-style: italic; } | ||
| 69 | - | & .repo-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s4); font-size: var(--fs-s); color: var(--ink-mute); } | 68 | + | /* the figures: a small label over a mono value, side by side */ |
| 70 | - | & .repo-meta time { color: var(--ink-dim); } | 69 | + | & .repo-stats { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); margin: var(--s1) 0 0; } |
| 71 | - | & .repo-branch { display: inline-flex; align-items: center; gap: 6px; font-family: var(--mono); color: var(--ink-dim); } | 70 | + | & .repo-stats div { display: flex; flex-direction: column; gap: 2px; min-width: 0; } |
| 71 | + | & .repo-stats dt { font-size: 11px; line-height: 14px; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-mute); } | |||
| 72 | + | & .repo-stats dd { margin: 0; font-family: var(--mono); font-size: 15px; line-height: 20px; color: var(--ink-hi); font-variant-numeric: tabular-nums; white-space: nowrap; } | |||
| 73 | + | & .repo-stats dd.mute { color: var(--ink-mute); } | |||
| 74 | + | & .repo-stats dd a { color: inherit; text-decoration: none; } | |||
| 75 | + | & .repo-stats dd a:hover { text-decoration: underline; } | |||
| 72 | & .repo-ci { | 76 | & .repo-ci { | ||
| 73 | grid-area: ci; justify-self: end; display: inline-flex; align-items: center; gap: var(--s1); margin-bottom: var(--s2); | 77 | grid-area: ci; justify-self: end; display: inline-flex; align-items: center; gap: var(--s1); margin-bottom: var(--s2); | ||
| 74 | font-size: var(--fs-s); color: var(--ink-dim); text-decoration: none; | 78 | font-size: var(--fs-s); color: var(--ink-dim); text-decoration: none; | ||
| @@ -168,7 +172,7 @@ | |||||
| 168 | min-height: var(--rowh); padding-right: var(--s4); font-size: var(--fs-s); | 172 | min-height: var(--rowh); padding-right: var(--s4); font-size: var(--fs-s); | ||
| 169 | } | 173 | } | ||
| 170 | & > li:hover, & > li[data-cursor] { background: var(--bg-raised); } | 174 | & > li:hover, & > li[data-cursor] { background: var(--bg-raised); } | ||
| 171 | - | & > li[data-cursor] { box-shadow: inset 3px 0 0 var(--ink-hi); } | 175 | + | & > li[data-cursor] { outline: 1px solid var(--ink-mute); outline-offset: -1px; } |
| 172 | & > li:focus-visible { outline-offset: -2px; border-radius: 0; } | 176 | & > li:focus-visible { outline-offset: -2px; border-radius: 0; } | ||
| 173 | & .t-main { display: flex; align-items: stretch; min-width: 0; min-height: var(--rowh); padding-left: var(--s3); } | 177 | & .t-main { display: flex; align-items: stretch; min-width: 0; min-height: var(--rowh); padding-left: var(--s3); } | ||
| 174 | & .t-twig { | 178 | & .t-twig { | ||
M internal/adapters/web/static/css/50-code.css +3 -4
| @@ -119,11 +119,11 @@ | |||
| 119 | 119 | a pixel below the row, enough to give the list a scroll bar */ | |
| 120 | 120 | display: grid; grid-template-columns: 1.5ch minmax(0, 1fr) auto auto; column-gap: var(--s2); align-items: center; | |
| 121 | 121 | height: 26px; line-height: 26px; padding: 0 var(--s2); margin: 0 calc(-1 * var(--s2)); | |
| 122 | - | border-radius: 4px; border-left: 2px solid transparent; text-decoration: none; font-family: var(--mono); color: var(--ink); | |
| 122 | + | border-radius: 4px; border: 1px solid transparent; text-decoration: none; font-family: var(--mono); color: var(--ink); | |
| 123 | 123 | } | |
| 124 | 124 | & a:hover { background: var(--bg-raised); } | |
| 125 | 125 | & a:hover .p { text-decoration: underline; } | |
| 126 | - | & a[aria-current="true"] { background: var(--row); border-left-color: var(--ink-hi); } | |
| 126 | + | & a[aria-current="true"] { background: var(--row); border-color: var(--ink-mute); } | |
| 127 | 127 | & a[aria-current="true"] .p { font-weight: bold; color: var(--ink-hi); } | |
| 128 | 128 | & .st { color: var(--ink-mute); font-weight: bold; } | |
| 129 | 129 | & .st[data-st="A"] { color: var(--teal); } | |
| @@ -158,8 +158,7 @@ | |||
| 158 | 158 | .rail .list.commits { | |
| 159 | 159 | border: 0; border-radius: 0; background: none; table-layout: auto; | |
| 160 | 160 | & td { height: 34px; padding: 0 var(--s2); width: 1%; white-space: nowrap; } | |
| 161 | - | & td.cur { width: 3px; padding: 0; } | |
| 162 | - | & td:nth-child(2) { padding-left: var(--s3); } | |
| 161 | + | & td:first-child { padding-left: var(--s3); } | |
| 163 | 162 | & td:last-child { padding-right: var(--s4); } | |
| 164 | 163 | & .c-who { display: none; } | |
| 165 | 164 | & .c-subject { width: auto; max-width: 0; font-size: var(--fs-s); } | |
| @@ -119,11 +119,11 @@ | |||||
| 119 | a pixel below the row, enough to give the list a scroll bar */ | 119 | a pixel below the row, enough to give the list a scroll bar */ | ||
| 120 | display: grid; grid-template-columns: 1.5ch minmax(0, 1fr) auto auto; column-gap: var(--s2); align-items: center; | 120 | display: grid; grid-template-columns: 1.5ch minmax(0, 1fr) auto auto; column-gap: var(--s2); align-items: center; | ||
| 121 | height: 26px; line-height: 26px; padding: 0 var(--s2); margin: 0 calc(-1 * var(--s2)); | 121 | height: 26px; line-height: 26px; padding: 0 var(--s2); margin: 0 calc(-1 * var(--s2)); | ||
| 122 | - | border-radius: 4px; border-left: 2px solid transparent; text-decoration: none; font-family: var(--mono); color: var(--ink); | 122 | + | border-radius: 4px; border: 1px solid transparent; text-decoration: none; font-family: var(--mono); color: var(--ink); |
| 123 | } | 123 | } | ||
| 124 | & a:hover { background: var(--bg-raised); } | 124 | & a:hover { background: var(--bg-raised); } | ||
| 125 | & a:hover .p { text-decoration: underline; } | 125 | & a:hover .p { text-decoration: underline; } | ||
| 126 | - | & a[aria-current="true"] { background: var(--row); border-left-color: var(--ink-hi); } | 126 | + | & a[aria-current="true"] { background: var(--row); border-color: var(--ink-mute); } |
| 127 | & a[aria-current="true"] .p { font-weight: bold; color: var(--ink-hi); } | 127 | & a[aria-current="true"] .p { font-weight: bold; color: var(--ink-hi); } | ||
| 128 | & .st { color: var(--ink-mute); font-weight: bold; } | 128 | & .st { color: var(--ink-mute); font-weight: bold; } | ||
| 129 | & .st[data-st="A"] { color: var(--teal); } | 129 | & .st[data-st="A"] { color: var(--teal); } | ||
| @@ -158,8 +158,7 @@ | |||||
| 158 | .rail .list.commits { | 158 | .rail .list.commits { | ||
| 159 | border: 0; border-radius: 0; background: none; table-layout: auto; | 159 | border: 0; border-radius: 0; background: none; table-layout: auto; | ||
| 160 | & td { height: 34px; padding: 0 var(--s2); width: 1%; white-space: nowrap; } | 160 | & td { height: 34px; padding: 0 var(--s2); width: 1%; white-space: nowrap; } | ||
| 161 | - | & td.cur { width: 3px; padding: 0; } | 161 | + | & td:first-child { padding-left: var(--s3); } |
| 162 | - | & td:nth-child(2) { padding-left: var(--s3); } | |||
| 163 | & td:last-child { padding-right: var(--s4); } | 162 | & td:last-child { padding-right: var(--s4); } | ||
| 164 | & .c-who { display: none; } | 163 | & .c-who { display: none; } | ||
| 165 | & .c-subject { width: auto; max-width: 0; font-size: var(--fs-s); } | 164 | & .c-subject { width: auto; max-width: 0; font-size: var(--fs-s); } | ||
M internal/adapters/web/view.go +19 -7
| @@ -76,14 +76,17 @@ type Link struct{ Label, Href string } | |||
| 76 | 76 | ||
| 77 | 77 | // HomeRow is one repository in the home list. | |
| 78 | 78 | type HomeRow struct { | |
| 79 | - | ID string | |
| 80 | - | Name string | |
| 81 | - | Href string | |
| 82 | - | Desc string | |
| 83 | - | Pushed string | |
| 84 | - | PushedAt string // RFC 3339, "" when never pushed | |
| 85 | - | Branch string | |
| 79 | + | ID string | |
| 80 | + | Name string | |
| 81 | + | Href string | |
| 82 | + | Desc string | |
| 83 | + | // Active is when the repository last changed (its last push or merge), | |
| 84 | + | // ActiveAt its RFC 3339 form, "" when it never got a commit. | |
| 85 | + | Active string | |
| 86 | + | ActiveAt string | |
| 86 | 87 | Cursor bool | |
| 88 | + | // Stats are the figures of the row; nil when they could not be read. | |
| 89 | + | Stats *HomeStats | |
| 87 | 90 | // Private marks a private repository; only set for a signed-in visitor. | |
| 88 | 91 | Private bool | |
| 89 | 92 | // Fresh marks a row that a push just updated (live updates only). | |
| @@ -97,6 +100,15 @@ type HomeRow struct { | |||
| 97 | 100 | CI *HomeCI | |
| 98 | 101 | } | |
| 99 | 102 | ||
| 103 | + | // HomeStats are the figures of a home row: commits of the default branch, | |
| 104 | + | // those of the last 30 days, and the latest tag. | |
| 105 | + | type HomeStats struct { | |
| 106 | + | Commits, Recent string | |
| 107 | + | // Tag is the latest tag, "" without tags; TagHref links its tree, TagWhen | |
| 108 | + | // tells when it was made. | |
| 109 | + | Tag, TagHref, TagWhen string | |
| 110 | + | } | |
| 111 | + | ||
| 100 | 112 | // HomeCI is the state of the latest run of the default branch of a repository. | |
| 101 | 113 | type HomeCI struct { | |
| 102 | 114 | Href string | |
| @@ -76,14 +76,17 @@ type Link struct{ Label, Href string } | |||||
| 76 | 76 | ||||
| 77 | // HomeRow is one repository in the home list. | 77 | // HomeRow is one repository in the home list. | ||
| 78 | type HomeRow struct { | 78 | type HomeRow struct { | ||
| 79 | - | ID string | 79 | + | ID string |
| 80 | - | Name string | 80 | + | Name string |
| 81 | - | Href string | 81 | + | Href string |
| 82 | - | Desc string | 82 | + | Desc string |
| 83 | - | Pushed string | 83 | + | // Active is when the repository last changed (its last push or merge), |
| 84 | - | PushedAt string // RFC 3339, "" when never pushed | 84 | + | // ActiveAt its RFC 3339 form, "" when it never got a commit. |
| 85 | - | Branch string | 85 | + | Active string |
| 86 | + | ActiveAt string | |||
| 86 | Cursor bool | 87 | Cursor bool | ||
| 88 | + | // Stats are the figures of the row; nil when they could not be read. | |||
| 89 | + | Stats *HomeStats | |||
| 87 | // Private marks a private repository; only set for a signed-in visitor. | 90 | // Private marks a private repository; only set for a signed-in visitor. | ||
| 88 | Private bool | 91 | Private bool | ||
| 89 | // Fresh marks a row that a push just updated (live updates only). | 92 | // Fresh marks a row that a push just updated (live updates only). | ||
| @@ -97,6 +100,15 @@ type HomeRow struct { | |||||
| 97 | CI *HomeCI | 100 | CI *HomeCI | ||
| 98 | } | 101 | } | ||
| 99 | 102 | ||||
| 103 | + | // HomeStats are the figures of a home row: commits of the default branch, | |||
| 104 | + | // those of the last 30 days, and the latest tag. | |||
| 105 | + | type HomeStats struct { | |||
| 106 | + | Commits, Recent string | |||
| 107 | + | // Tag is the latest tag, "" without tags; TagHref links its tree, TagWhen | |||
| 108 | + | // tells when it was made. | |||
| 109 | + | Tag, TagHref, TagWhen string | |||
| 110 | + | } | |||
| 111 | + | ||||
| 100 | // HomeCI is the state of the latest run of the default branch of a repository. | 112 | // HomeCI is the state of the latest run of the default branch of a repository. | ||
| 101 | type HomeCI struct { | 113 | type HomeCI struct { | ||
| 102 | Href string | 114 | Href string | ||
M internal/app/testdata/web/commits-page2.html +0 -4
| @@ -108,8 +108,6 @@ | |||
| 108 | 108 | <table class="list commits" aria-label="Commits"> | |
| 109 | 109 | <tbody data-rows> | |
| 110 | 110 | <tr id="c-SHA7" data-cursor> | |
| 111 | - | <td class="cur" aria-hidden="true"> | |
| 112 | - | </td> | |
| 113 | 111 | <td class="c-sha mute">SHA7</td> | |
| 114 | 112 | <td class="c-subject"> | |
| 115 | 113 | <a href="/long/commit/SHA40">commit number 1</a> | |
| @@ -122,8 +120,6 @@ | |||
| 122 | 120 | </td> | |
| 123 | 121 | </tr> | |
| 124 | 122 | <tr id="c-SHA7"> | |
| 125 | - | <td class="cur" aria-hidden="true"> | |
| 126 | - | </td> | |
| 127 | 123 | <td class="c-sha mute">SHA7</td> | |
| 128 | 124 | <td class="c-subject"> | |
| 129 | 125 | <a href="/long/commit/SHA40">commit number 0</a> | |
| @@ -108,8 +108,6 @@ | |||||
| 108 | <table class="list commits" aria-label="Commits"> | 108 | <table class="list commits" aria-label="Commits"> | ||
| 109 | <tbody data-rows> | 109 | <tbody data-rows> | ||
| 110 | <tr id="c-SHA7" data-cursor> | 110 | <tr id="c-SHA7" data-cursor> | ||
| 111 | - | <td class="cur" aria-hidden="true"> | |||
| 112 | - | </td> | |||
| 113 | <td class="c-sha mute">SHA7</td> | 111 | <td class="c-sha mute">SHA7</td> | ||
| 114 | <td class="c-subject"> | 112 | <td class="c-subject"> | ||
| 115 | <a href="/long/commit/SHA40">commit number 1</a> | 113 | <a href="/long/commit/SHA40">commit number 1</a> | ||
| @@ -122,8 +120,6 @@ | |||||
| 122 | </td> | 120 | </td> | ||
| 123 | </tr> | 121 | </tr> | ||
| 124 | <tr id="c-SHA7"> | 122 | <tr id="c-SHA7"> | ||
| 125 | - | <td class="cur" aria-hidden="true"> | |||
| 126 | - | </td> | |||
| 127 | <td class="c-sha mute">SHA7</td> | 123 | <td class="c-sha mute">SHA7</td> | ||
| 128 | <td class="c-subject"> | 124 | <td class="c-subject"> | ||
| 129 | <a href="/long/commit/SHA40">commit number 0</a> | 125 | <a href="/long/commit/SHA40">commit number 0</a> | ||
M internal/app/testdata/web/commits-path.html +0 -4
| @@ -112,8 +112,6 @@ | |||
| 112 | 112 | <table class="list commits" aria-label="Commits"> | |
| 113 | 113 | <tbody data-rows> | |
| 114 | 114 | <tr id="c-SHA7" data-cursor> | |
| 115 | - | <td class="cur" aria-hidden="true"> | |
| 116 | - | </td> | |
| 117 | 115 | <td class="c-sha mute">SHA7</td> | |
| 118 | 116 | <td class="c-subject"> | |
| 119 | 117 | <a href="/long/commit/SHA40">commit number 30</a> | |
| @@ -135,8 +133,6 @@ | |||
| 135 | 133 | <table class="list commits" aria-label="Commits"> | |
| 136 | 134 | <tbody data-rows> | |
| 137 | 135 | <tr id="c-SHA7"> | |
| 138 | - | <td class="cur" aria-hidden="true"> | |
| 139 | - | </td> | |
| 140 | 136 | <td class="c-sha mute">SHA7</td> | |
| 141 | 137 | <td class="c-subject"> | |
| 142 | 138 | <a href="/long/commit/SHA40">commit number 3</a> | |
| @@ -112,8 +112,6 @@ | |||||
| 112 | <table class="list commits" aria-label="Commits"> | 112 | <table class="list commits" aria-label="Commits"> | ||
| 113 | <tbody data-rows> | 113 | <tbody data-rows> | ||
| 114 | <tr id="c-SHA7" data-cursor> | 114 | <tr id="c-SHA7" data-cursor> | ||
| 115 | - | <td class="cur" aria-hidden="true"> | |||
| 116 | - | </td> | |||
| 117 | <td class="c-sha mute">SHA7</td> | 115 | <td class="c-sha mute">SHA7</td> | ||
| 118 | <td class="c-subject"> | 116 | <td class="c-subject"> | ||
| 119 | <a href="/long/commit/SHA40">commit number 30</a> | 117 | <a href="/long/commit/SHA40">commit number 30</a> | ||
| @@ -135,8 +133,6 @@ | |||||
| 135 | <table class="list commits" aria-label="Commits"> | 133 | <table class="list commits" aria-label="Commits"> | ||
| 136 | <tbody data-rows> | 134 | <tbody data-rows> | ||
| 137 | <tr id="c-SHA7"> | 135 | <tr id="c-SHA7"> | ||
| 138 | - | <td class="cur" aria-hidden="true"> | |||
| 139 | - | </td> | |||
| 140 | <td class="c-sha mute">SHA7</td> | 136 | <td class="c-sha mute">SHA7</td> | ||
| 141 | <td class="c-subject"> | 137 | <td class="c-subject"> | ||
| 142 | <a href="/long/commit/SHA40">commit number 3</a> | 138 | <a href="/long/commit/SHA40">commit number 3</a> | ||
M internal/app/testdata/web/commits-root.html +0 -100
| @@ -110,8 +110,6 @@ | |||
| 110 | 110 | <table class="list commits" aria-label="Commits"> | |
| 111 | 111 | <tbody data-rows> | |
| 112 | 112 | <tr id="c-SHA7" data-cursor> | |
| 113 | - | <td class="cur" aria-hidden="true"> | |
| 114 | - | </td> | |
| 115 | 113 | <td class="c-sha mute">SHA7</td> | |
| 116 | 114 | <td class="c-subject"> | |
| 117 | 115 | <a href="/long/commit/SHA40">commit number 51</a> | |
| @@ -125,8 +123,6 @@ | |||
| 125 | 123 | </td> | |
| 126 | 124 | </tr> | |
| 127 | 125 | <tr id="c-SHA7"> | |
| 128 | - | <td class="cur" aria-hidden="true"> | |
| 129 | - | </td> | |
| 130 | 126 | <td class="c-sha mute">SHA7</td> | |
| 131 | 127 | <td class="c-subject"> | |
| 132 | 128 | <a href="/long/commit/SHA40">commit number 50</a> | |
| @@ -148,8 +144,6 @@ | |||
| 148 | 144 | <table class="list commits" aria-label="Commits"> | |
| 149 | 145 | <tbody data-rows> | |
| 150 | 146 | <tr id="c-SHA7"> | |
| 151 | - | <td class="cur" aria-hidden="true"> | |
| 152 | - | </td> | |
| 153 | 147 | <td class="c-sha mute">SHA7</td> | |
| 154 | 148 | <td class="c-subject"> | |
| 155 | 149 | <a href="/long/commit/SHA40">commit number 49</a> | |
| @@ -162,8 +156,6 @@ | |||
| 162 | 156 | </td> | |
| 163 | 157 | </tr> | |
| 164 | 158 | <tr id="c-SHA7"> | |
| 165 | - | <td class="cur" aria-hidden="true"> | |
| 166 | - | </td> | |
| 167 | 159 | <td class="c-sha mute">SHA7</td> | |
| 168 | 160 | <td class="c-subject"> | |
| 169 | 161 | <a href="/long/commit/SHA40">commit number 48</a> | |
| @@ -185,8 +177,6 @@ | |||
| 185 | 177 | <table class="list commits" aria-label="Commits"> | |
| 186 | 178 | <tbody data-rows> | |
| 187 | 179 | <tr id="c-SHA7"> | |
| 188 | - | <td class="cur" aria-hidden="true"> | |
| 189 | - | </td> | |
| 190 | 180 | <td class="c-sha mute">SHA7</td> | |
| 191 | 181 | <td class="c-subject"> | |
| 192 | 182 | <a href="/long/commit/SHA40">commit number 47</a> | |
| @@ -199,8 +189,6 @@ | |||
| 199 | 189 | </td> | |
| 200 | 190 | </tr> | |
| 201 | 191 | <tr id="c-SHA7"> | |
| 202 | - | <td class="cur" aria-hidden="true"> | |
| 203 | - | </td> | |
| 204 | 192 | <td class="c-sha mute">SHA7</td> | |
| 205 | 193 | <td class="c-subject"> | |
| 206 | 194 | <a href="/long/commit/SHA40">commit number 46</a> | |
| @@ -222,8 +210,6 @@ | |||
| 222 | 210 | <table class="list commits" aria-label="Commits"> | |
| 223 | 211 | <tbody data-rows> | |
| 224 | 212 | <tr id="c-SHA7"> | |
| 225 | - | <td class="cur" aria-hidden="true"> | |
| 226 | - | </td> | |
| 227 | 213 | <td class="c-sha mute">SHA7</td> | |
| 228 | 214 | <td class="c-subject"> | |
| 229 | 215 | <a href="/long/commit/SHA40">commit number 45</a> | |
| @@ -236,8 +222,6 @@ | |||
| 236 | 222 | </td> | |
| 237 | 223 | </tr> | |
| 238 | 224 | <tr id="c-SHA7"> | |
| 239 | - | <td class="cur" aria-hidden="true"> | |
| 240 | - | </td> | |
| 241 | 225 | <td class="c-sha mute">SHA7</td> | |
| 242 | 226 | <td class="c-subject"> | |
| 243 | 227 | <a href="/long/commit/SHA40">commit number 44</a> | |
| @@ -259,8 +243,6 @@ | |||
| 259 | 243 | <table class="list commits" aria-label="Commits"> | |
| 260 | 244 | <tbody data-rows> | |
| 261 | 245 | <tr id="c-SHA7"> | |
| 262 | - | <td class="cur" aria-hidden="true"> | |
| 263 | - | </td> | |
| 264 | 246 | <td class="c-sha mute">SHA7</td> | |
| 265 | 247 | <td class="c-subject"> | |
| 266 | 248 | <a href="/long/commit/SHA40">commit number 43</a> | |
| @@ -273,8 +255,6 @@ | |||
| 273 | 255 | </td> | |
| 274 | 256 | </tr> | |
| 275 | 257 | <tr id="c-SHA7"> | |
| 276 | - | <td class="cur" aria-hidden="true"> | |
| 277 | - | </td> | |
| 278 | 258 | <td class="c-sha mute">SHA7</td> | |
| 279 | 259 | <td class="c-subject"> | |
| 280 | 260 | <a href="/long/commit/SHA40">commit number 42</a> | |
| @@ -296,8 +276,6 @@ | |||
| 296 | 276 | <table class="list commits" aria-label="Commits"> | |
| 297 | 277 | <tbody data-rows> | |
| 298 | 278 | <tr id="c-SHA7"> | |
| 299 | - | <td class="cur" aria-hidden="true"> | |
| 300 | - | </td> | |
| 301 | 279 | <td class="c-sha mute">SHA7</td> | |
| 302 | 280 | <td class="c-subject"> | |
| 303 | 281 | <a href="/long/commit/SHA40">commit number 41</a> | |
| @@ -310,8 +288,6 @@ | |||
| 310 | 288 | </td> | |
| 311 | 289 | </tr> | |
| 312 | 290 | <tr id="c-SHA7"> | |
| 313 | - | <td class="cur" aria-hidden="true"> | |
| 314 | - | </td> | |
| 315 | 291 | <td class="c-sha mute">SHA7</td> | |
| 316 | 292 | <td class="c-subject"> | |
| 317 | 293 | <a href="/long/commit/SHA40">commit number 40</a> | |
| @@ -333,8 +309,6 @@ | |||
| 333 | 309 | <table class="list commits" aria-label="Commits"> | |
| 334 | 310 | <tbody data-rows> | |
| 335 | 311 | <tr id="c-SHA7"> | |
| 336 | - | <td class="cur" aria-hidden="true"> | |
| 337 | - | </td> | |
| 338 | 312 | <td class="c-sha mute">SHA7</td> | |
| 339 | 313 | <td class="c-subject"> | |
| 340 | 314 | <a href="/long/commit/SHA40">commit number 39</a> | |
| @@ -347,8 +321,6 @@ | |||
| 347 | 321 | </td> | |
| 348 | 322 | </tr> | |
| 349 | 323 | <tr id="c-SHA7"> | |
| 350 | - | <td class="cur" aria-hidden="true"> | |
| 351 | - | </td> | |
| 352 | 324 | <td class="c-sha mute">SHA7</td> | |
| 353 | 325 | <td class="c-subject"> | |
| 354 | 326 | <a href="/long/commit/SHA40">commit number 38</a> | |
| @@ -370,8 +342,6 @@ | |||
| 370 | 342 | <table class="list commits" aria-label="Commits"> | |
| 371 | 343 | <tbody data-rows> | |
| 372 | 344 | <tr id="c-SHA7"> | |
| 373 | - | <td class="cur" aria-hidden="true"> | |
| 374 | - | </td> | |
| 375 | 345 | <td class="c-sha mute">SHA7</td> | |
| 376 | 346 | <td class="c-subject"> | |
| 377 | 347 | <a href="/long/commit/SHA40">commit number 37</a> | |
| @@ -384,8 +354,6 @@ | |||
| 384 | 354 | </td> | |
| 385 | 355 | </tr> | |
| 386 | 356 | <tr id="c-SHA7"> | |
| 387 | - | <td class="cur" aria-hidden="true"> | |
| 388 | - | </td> | |
| 389 | 357 | <td class="c-sha mute">SHA7</td> | |
| 390 | 358 | <td class="c-subject"> | |
| 391 | 359 | <a href="/long/commit/SHA40">commit number 36</a> | |
| @@ -407,8 +375,6 @@ | |||
| 407 | 375 | <table class="list commits" aria-label="Commits"> | |
| 408 | 376 | <tbody data-rows> | |
| 409 | 377 | <tr id="c-SHA7"> | |
| 410 | - | <td class="cur" aria-hidden="true"> | |
| 411 | - | </td> | |
| 412 | 378 | <td class="c-sha mute">SHA7</td> | |
| 413 | 379 | <td class="c-subject"> | |
| 414 | 380 | <a href="/long/commit/SHA40">commit number 35</a> | |
| @@ -421,8 +387,6 @@ | |||
| 421 | 387 | </td> | |
| 422 | 388 | </tr> | |
| 423 | 389 | <tr id="c-SHA7"> | |
| 424 | - | <td class="cur" aria-hidden="true"> | |
| 425 | - | </td> | |
| 426 | 390 | <td class="c-sha mute">SHA7</td> | |
| 427 | 391 | <td class="c-subject"> | |
| 428 | 392 | <a href="/long/commit/SHA40">commit number 34</a> | |
| @@ -444,8 +408,6 @@ | |||
| 444 | 408 | <table class="list commits" aria-label="Commits"> | |
| 445 | 409 | <tbody data-rows> | |
| 446 | 410 | <tr id="c-SHA7"> | |
| 447 | - | <td class="cur" aria-hidden="true"> | |
| 448 | - | </td> | |
| 449 | 411 | <td class="c-sha mute">SHA7</td> | |
| 450 | 412 | <td class="c-subject"> | |
| 451 | 413 | <a href="/long/commit/SHA40">commit number 33</a> | |
| @@ -458,8 +420,6 @@ | |||
| 458 | 420 | </td> | |
| 459 | 421 | </tr> | |
| 460 | 422 | <tr id="c-SHA7"> | |
| 461 | - | <td class="cur" aria-hidden="true"> | |
| 462 | - | </td> | |
| 463 | 423 | <td class="c-sha mute">SHA7</td> | |
| 464 | 424 | <td class="c-subject"> | |
| 465 | 425 | <a href="/long/commit/SHA40">commit number 32</a> | |
| @@ -481,8 +441,6 @@ | |||
| 481 | 441 | <table class="list commits" aria-label="Commits"> | |
| 482 | 442 | <tbody data-rows> | |
| 483 | 443 | <tr id="c-SHA7"> | |
| 484 | - | <td class="cur" aria-hidden="true"> | |
| 485 | - | </td> | |
| 486 | 444 | <td class="c-sha mute">SHA7</td> | |
| 487 | 445 | <td class="c-subject"> | |
| 488 | 446 | <a href="/long/commit/SHA40">commit number 31</a> | |
| @@ -495,8 +453,6 @@ | |||
| 495 | 453 | </td> | |
| 496 | 454 | </tr> | |
| 497 | 455 | <tr id="c-SHA7"> | |
| 498 | - | <td class="cur" aria-hidden="true"> | |
| 499 | - | </td> | |
| 500 | 456 | <td class="c-sha mute">SHA7</td> | |
| 501 | 457 | <td class="c-subject"> | |
| 502 | 458 | <a href="/long/commit/SHA40">commit number 30</a> | |
| @@ -518,8 +474,6 @@ | |||
| 518 | 474 | <table class="list commits" aria-label="Commits"> | |
| 519 | 475 | <tbody data-rows> | |
| 520 | 476 | <tr id="c-SHA7"> | |
| 521 | - | <td class="cur" aria-hidden="true"> | |
| 522 | - | </td> | |
| 523 | 477 | <td class="c-sha mute">SHA7</td> | |
| 524 | 478 | <td class="c-subject"> | |
| 525 | 479 | <a href="/long/commit/SHA40">commit number 29</a> | |
| @@ -532,8 +486,6 @@ | |||
| 532 | 486 | </td> | |
| 533 | 487 | </tr> | |
| 534 | 488 | <tr id="c-SHA7"> | |
| 535 | - | <td class="cur" aria-hidden="true"> | |
| 536 | - | </td> | |
| 537 | 489 | <td class="c-sha mute">SHA7</td> | |
| 538 | 490 | <td class="c-subject"> | |
| 539 | 491 | <a href="/long/commit/SHA40">commit number 28</a> | |
| @@ -555,8 +507,6 @@ | |||
| 555 | 507 | <table class="list commits" aria-label="Commits"> | |
| 556 | 508 | <tbody data-rows> | |
| 557 | 509 | <tr id="c-SHA7"> | |
| 558 | - | <td class="cur" aria-hidden="true"> | |
| 559 | - | </td> | |
| 560 | 510 | <td class="c-sha mute">SHA7</td> | |
| 561 | 511 | <td class="c-subject"> | |
| 562 | 512 | <a href="/long/commit/SHA40">commit number 27</a> | |
| @@ -569,8 +519,6 @@ | |||
| 569 | 519 | </td> | |
| 570 | 520 | </tr> | |
| 571 | 521 | <tr id="c-SHA7"> | |
| 572 | - | <td class="cur" aria-hidden="true"> | |
| 573 | - | </td> | |
| 574 | 522 | <td class="c-sha mute">SHA7</td> | |
| 575 | 523 | <td class="c-subject"> | |
| 576 | 524 | <a href="/long/commit/SHA40">commit number 26</a> | |
| @@ -592,8 +540,6 @@ | |||
| 592 | 540 | <table class="list commits" aria-label="Commits"> | |
| 593 | 541 | <tbody data-rows> | |
| 594 | 542 | <tr id="c-SHA7"> | |
| 595 | - | <td class="cur" aria-hidden="true"> | |
| 596 | - | </td> | |
| 597 | 543 | <td class="c-sha mute">SHA7</td> | |
| 598 | 544 | <td class="c-subject"> | |
| 599 | 545 | <a href="/long/commit/SHA40">commit number 25</a> | |
| @@ -606,8 +552,6 @@ | |||
| 606 | 552 | </td> | |
| 607 | 553 | </tr> | |
| 608 | 554 | <tr id="c-SHA7"> | |
| 609 | - | <td class="cur" aria-hidden="true"> | |
| 610 | - | </td> | |
| 611 | 555 | <td class="c-sha mute">SHA7</td> | |
| 612 | 556 | <td class="c-subject"> | |
| 613 | 557 | <a href="/long/commit/SHA40">commit number 24</a> | |
| @@ -629,8 +573,6 @@ | |||
| 629 | 573 | <table class="list commits" aria-label="Commits"> | |
| 630 | 574 | <tbody data-rows> | |
| 631 | 575 | <tr id="c-SHA7"> | |
| 632 | - | <td class="cur" aria-hidden="true"> | |
| 633 | - | </td> | |
| 634 | 576 | <td class="c-sha mute">SHA7</td> | |
| 635 | 577 | <td class="c-subject"> | |
| 636 | 578 | <a href="/long/commit/SHA40">commit number 23</a> | |
| @@ -643,8 +585,6 @@ | |||
| 643 | 585 | </td> | |
| 644 | 586 | </tr> | |
| 645 | 587 | <tr id="c-SHA7"> | |
| 646 | - | <td class="cur" aria-hidden="true"> | |
| 647 | - | </td> | |
| 648 | 588 | <td class="c-sha mute">SHA7</td> | |
| 649 | 589 | <td class="c-subject"> | |
| 650 | 590 | <a href="/long/commit/SHA40">commit number 22</a> | |
| @@ -666,8 +606,6 @@ | |||
| 666 | 606 | <table class="list commits" aria-label="Commits"> | |
| 667 | 607 | <tbody data-rows> | |
| 668 | 608 | <tr id="c-SHA7"> | |
| 669 | - | <td class="cur" aria-hidden="true"> | |
| 670 | - | </td> | |
| 671 | 609 | <td class="c-sha mute">SHA7</td> | |
| 672 | 610 | <td class="c-subject"> | |
| 673 | 611 | <a href="/long/commit/SHA40">commit number 21</a> | |
| @@ -680,8 +618,6 @@ | |||
| 680 | 618 | </td> | |
| 681 | 619 | </tr> | |
| 682 | 620 | <tr id="c-SHA7"> | |
| 683 | - | <td class="cur" aria-hidden="true"> | |
| 684 | - | </td> | |
| 685 | 621 | <td class="c-sha mute">SHA7</td> | |
| 686 | 622 | <td class="c-subject"> | |
| 687 | 623 | <a href="/long/commit/SHA40">commit number 20</a> | |
| @@ -703,8 +639,6 @@ | |||
| 703 | 639 | <table class="list commits" aria-label="Commits"> | |
| 704 | 640 | <tbody data-rows> | |
| 705 | 641 | <tr id="c-SHA7"> | |
| 706 | - | <td class="cur" aria-hidden="true"> | |
| 707 | - | </td> | |
| 708 | 642 | <td class="c-sha mute">SHA7</td> | |
| 709 | 643 | <td class="c-subject"> | |
| 710 | 644 | <a href="/long/commit/SHA40">commit number 19</a> | |
| @@ -717,8 +651,6 @@ | |||
| 717 | 651 | </td> | |
| 718 | 652 | </tr> | |
| 719 | 653 | <tr id="c-SHA7"> | |
| 720 | - | <td class="cur" aria-hidden="true"> | |
| 721 | - | </td> | |
| 722 | 654 | <td class="c-sha mute">SHA7</td> | |
| 723 | 655 | <td class="c-subject"> | |
| 724 | 656 | <a href="/long/commit/SHA40">commit number 18</a> | |
| @@ -740,8 +672,6 @@ | |||
| 740 | 672 | <table class="list commits" aria-label="Commits"> | |
| 741 | 673 | <tbody data-rows> | |
| 742 | 674 | <tr id="c-SHA7"> | |
| 743 | - | <td class="cur" aria-hidden="true"> | |
| 744 | - | </td> | |
| 745 | 675 | <td class="c-sha mute">SHA7</td> | |
| 746 | 676 | <td class="c-subject"> | |
| 747 | 677 | <a href="/long/commit/SHA40">commit number 17</a> | |
| @@ -754,8 +684,6 @@ | |||
| 754 | 684 | </td> | |
| 755 | 685 | </tr> | |
| 756 | 686 | <tr id="c-SHA7"> | |
| 757 | - | <td class="cur" aria-hidden="true"> | |
| 758 | - | </td> | |
| 759 | 687 | <td class="c-sha mute">SHA7</td> | |
| 760 | 688 | <td class="c-subject"> | |
| 761 | 689 | <a href="/long/commit/SHA40">commit number 16</a> | |
| @@ -777,8 +705,6 @@ | |||
| 777 | 705 | <table class="list commits" aria-label="Commits"> | |
| 778 | 706 | <tbody data-rows> | |
| 779 | 707 | <tr id="c-SHA7"> | |
| 780 | - | <td class="cur" aria-hidden="true"> | |
| 781 | - | </td> | |
| 782 | 708 | <td class="c-sha mute">SHA7</td> | |
| 783 | 709 | <td class="c-subject"> | |
| 784 | 710 | <a href="/long/commit/SHA40">commit number 15</a> | |
| @@ -791,8 +717,6 @@ | |||
| 791 | 717 | </td> | |
| 792 | 718 | </tr> | |
| 793 | 719 | <tr id="c-SHA7"> | |
| 794 | - | <td class="cur" aria-hidden="true"> | |
| 795 | - | </td> | |
| 796 | 720 | <td class="c-sha mute">SHA7</td> | |
| 797 | 721 | <td class="c-subject"> | |
| 798 | 722 | <a href="/long/commit/SHA40">commit number 14</a> | |
| @@ -814,8 +738,6 @@ | |||
| 814 | 738 | <table class="list commits" aria-label="Commits"> | |
| 815 | 739 | <tbody data-rows> | |
| 816 | 740 | <tr id="c-SHA7"> | |
| 817 | - | <td class="cur" aria-hidden="true"> | |
| 818 | - | </td> | |
| 819 | 741 | <td class="c-sha mute">SHA7</td> | |
| 820 | 742 | <td class="c-subject"> | |
| 821 | 743 | <a href="/long/commit/SHA40">commit number 13</a> | |
| @@ -828,8 +750,6 @@ | |||
| 828 | 750 | </td> | |
| 829 | 751 | </tr> | |
| 830 | 752 | <tr id="c-SHA7"> | |
| 831 | - | <td class="cur" aria-hidden="true"> | |
| 832 | - | </td> | |
| 833 | 753 | <td class="c-sha mute">SHA7</td> | |
| 834 | 754 | <td class="c-subject"> | |
| 835 | 755 | <a href="/long/commit/SHA40">commit number 12</a> | |
| @@ -851,8 +771,6 @@ | |||
| 851 | 771 | <table class="list commits" aria-label="Commits"> | |
| 852 | 772 | <tbody data-rows> | |
| 853 | 773 | <tr id="c-SHA7"> | |
| 854 | - | <td class="cur" aria-hidden="true"> | |
| 855 | - | </td> | |
| 856 | 774 | <td class="c-sha mute">SHA7</td> | |
| 857 | 775 | <td class="c-subject"> | |
| 858 | 776 | <a href="/long/commit/SHA40">commit number 11</a> | |
| @@ -865,8 +783,6 @@ | |||
| 865 | 783 | </td> | |
| 866 | 784 | </tr> | |
| 867 | 785 | <tr id="c-SHA7"> | |
| 868 | - | <td class="cur" aria-hidden="true"> | |
| 869 | - | </td> | |
| 870 | 786 | <td class="c-sha mute">SHA7</td> | |
| 871 | 787 | <td class="c-subject"> | |
| 872 | 788 | <a href="/long/commit/SHA40">commit number 10</a> | |
| @@ -888,8 +804,6 @@ | |||
| 888 | 804 | <table class="list commits" aria-label="Commits"> | |
| 889 | 805 | <tbody data-rows> | |
| 890 | 806 | <tr id="c-SHA7"> | |
| 891 | - | <td class="cur" aria-hidden="true"> | |
| 892 | - | </td> | |
| 893 | 807 | <td class="c-sha mute">SHA7</td> | |
| 894 | 808 | <td class="c-subject"> | |
| 895 | 809 | <a href="/long/commit/SHA40">commit number 9</a> | |
| @@ -902,8 +816,6 @@ | |||
| 902 | 816 | </td> | |
| 903 | 817 | </tr> | |
| 904 | 818 | <tr id="c-SHA7"> | |
| 905 | - | <td class="cur" aria-hidden="true"> | |
| 906 | - | </td> | |
| 907 | 819 | <td class="c-sha mute">SHA7</td> | |
| 908 | 820 | <td class="c-subject"> | |
| 909 | 821 | <a href="/long/commit/SHA40">commit number 8</a> | |
| @@ -925,8 +837,6 @@ | |||
| 925 | 837 | <table class="list commits" aria-label="Commits"> | |
| 926 | 838 | <tbody data-rows> | |
| 927 | 839 | <tr id="c-SHA7"> | |
| 928 | - | <td class="cur" aria-hidden="true"> | |
| 929 | - | </td> | |
| 930 | 840 | <td class="c-sha mute">SHA7</td> | |
| 931 | 841 | <td class="c-subject"> | |
| 932 | 842 | <a href="/long/commit/SHA40">commit number 7</a> | |
| @@ -939,8 +849,6 @@ | |||
| 939 | 849 | </td> | |
| 940 | 850 | </tr> | |
| 941 | 851 | <tr id="c-SHA7"> | |
| 942 | - | <td class="cur" aria-hidden="true"> | |
| 943 | - | </td> | |
| 944 | 852 | <td class="c-sha mute">SHA7</td> | |
| 945 | 853 | <td class="c-subject"> | |
| 946 | 854 | <a href="/long/commit/SHA40">commit number 6</a> | |
| @@ -962,8 +870,6 @@ | |||
| 962 | 870 | <table class="list commits" aria-label="Commits"> | |
| 963 | 871 | <tbody data-rows> | |
| 964 | 872 | <tr id="c-SHA7"> | |
| 965 | - | <td class="cur" aria-hidden="true"> | |
| 966 | - | </td> | |
| 967 | 873 | <td class="c-sha mute">SHA7</td> | |
| 968 | 874 | <td class="c-subject"> | |
| 969 | 875 | <a href="/long/commit/SHA40">commit number 5</a> | |
| @@ -976,8 +882,6 @@ | |||
| 976 | 882 | </td> | |
| 977 | 883 | </tr> | |
| 978 | 884 | <tr id="c-SHA7"> | |
| 979 | - | <td class="cur" aria-hidden="true"> | |
| 980 | - | </td> | |
| 981 | 885 | <td class="c-sha mute">SHA7</td> | |
| 982 | 886 | <td class="c-subject"> | |
| 983 | 887 | <a href="/long/commit/SHA40">commit number 4</a> | |
| @@ -999,8 +903,6 @@ | |||
| 999 | 903 | <table class="list commits" aria-label="Commits"> | |
| 1000 | 904 | <tbody data-rows> | |
| 1001 | 905 | <tr id="c-SHA7"> | |
| 1002 | - | <td class="cur" aria-hidden="true"> | |
| 1003 | - | </td> | |
| 1004 | 906 | <td class="c-sha mute">SHA7</td> | |
| 1005 | 907 | <td class="c-subject"> | |
| 1006 | 908 | <a href="/long/commit/SHA40">commit number 3</a> | |
| @@ -1013,8 +915,6 @@ | |||
| 1013 | 915 | </td> | |
| 1014 | 916 | </tr> | |
| 1015 | 917 | <tr id="c-SHA7"> | |
| 1016 | - | <td class="cur" aria-hidden="true"> | |
| 1017 | - | </td> | |
| 1018 | 918 | <td class="c-sha mute">SHA7</td> | |
| 1019 | 919 | <td class="c-subject"> | |
| 1020 | 920 | <a href="/long/commit/SHA40">commit number 2</a> | |
| @@ -110,8 +110,6 @@ | |||||
| 110 | <table class="list commits" aria-label="Commits"> | 110 | <table class="list commits" aria-label="Commits"> | ||
| 111 | <tbody data-rows> | 111 | <tbody data-rows> | ||
| 112 | <tr id="c-SHA7" data-cursor> | 112 | <tr id="c-SHA7" data-cursor> | ||
| 113 | - | <td class="cur" aria-hidden="true"> | |||
| 114 | - | </td> | |||
| 115 | <td class="c-sha mute">SHA7</td> | 113 | <td class="c-sha mute">SHA7</td> | ||
| 116 | <td class="c-subject"> | 114 | <td class="c-subject"> | ||
| 117 | <a href="/long/commit/SHA40">commit number 51</a> | 115 | <a href="/long/commit/SHA40">commit number 51</a> | ||
| @@ -125,8 +123,6 @@ | |||||
| 125 | </td> | 123 | </td> | ||
| 126 | </tr> | 124 | </tr> | ||
| 127 | <tr id="c-SHA7"> | 125 | <tr id="c-SHA7"> | ||
| 128 | - | <td class="cur" aria-hidden="true"> | |||
| 129 | - | </td> | |||
| 130 | <td class="c-sha mute">SHA7</td> | 126 | <td class="c-sha mute">SHA7</td> | ||
| 131 | <td class="c-subject"> | 127 | <td class="c-subject"> | ||
| 132 | <a href="/long/commit/SHA40">commit number 50</a> | 128 | <a href="/long/commit/SHA40">commit number 50</a> | ||
| @@ -148,8 +144,6 @@ | |||||
| 148 | <table class="list commits" aria-label="Commits"> | 144 | <table class="list commits" aria-label="Commits"> | ||
| 149 | <tbody data-rows> | 145 | <tbody data-rows> | ||
| 150 | <tr id="c-SHA7"> | 146 | <tr id="c-SHA7"> | ||
| 151 | - | <td class="cur" aria-hidden="true"> | |||
| 152 | - | </td> | |||
| 153 | <td class="c-sha mute">SHA7</td> | 147 | <td class="c-sha mute">SHA7</td> | ||
| 154 | <td class="c-subject"> | 148 | <td class="c-subject"> | ||
| 155 | <a href="/long/commit/SHA40">commit number 49</a> | 149 | <a href="/long/commit/SHA40">commit number 49</a> | ||
| @@ -162,8 +156,6 @@ | |||||
| 162 | </td> | 156 | </td> | ||
| 163 | </tr> | 157 | </tr> | ||
| 164 | <tr id="c-SHA7"> | 158 | <tr id="c-SHA7"> | ||
| 165 | - | <td class="cur" aria-hidden="true"> | |||
| 166 | - | </td> | |||
| 167 | <td class="c-sha mute">SHA7</td> | 159 | <td class="c-sha mute">SHA7</td> | ||
| 168 | <td class="c-subject"> | 160 | <td class="c-subject"> | ||
| 169 | <a href="/long/commit/SHA40">commit number 48</a> | 161 | <a href="/long/commit/SHA40">commit number 48</a> | ||
| @@ -185,8 +177,6 @@ | |||||
| 185 | <table class="list commits" aria-label="Commits"> | 177 | <table class="list commits" aria-label="Commits"> | ||
| 186 | <tbody data-rows> | 178 | <tbody data-rows> | ||
| 187 | <tr id="c-SHA7"> | 179 | <tr id="c-SHA7"> | ||
| 188 | - | <td class="cur" aria-hidden="true"> | |||
| 189 | - | </td> | |||
| 190 | <td class="c-sha mute">SHA7</td> | 180 | <td class="c-sha mute">SHA7</td> | ||
| 191 | <td class="c-subject"> | 181 | <td class="c-subject"> | ||
| 192 | <a href="/long/commit/SHA40">commit number 47</a> | 182 | <a href="/long/commit/SHA40">commit number 47</a> | ||
| @@ -199,8 +189,6 @@ | |||||
| 199 | </td> | 189 | </td> | ||
| 200 | </tr> | 190 | </tr> | ||
| 201 | <tr id="c-SHA7"> | 191 | <tr id="c-SHA7"> | ||
| 202 | - | <td class="cur" aria-hidden="true"> | |||
| 203 | - | </td> | |||
| 204 | <td class="c-sha mute">SHA7</td> | 192 | <td class="c-sha mute">SHA7</td> | ||
| 205 | <td class="c-subject"> | 193 | <td class="c-subject"> | ||
| 206 | <a href="/long/commit/SHA40">commit number 46</a> | 194 | <a href="/long/commit/SHA40">commit number 46</a> | ||
| @@ -222,8 +210,6 @@ | |||||
| 222 | <table class="list commits" aria-label="Commits"> | 210 | <table class="list commits" aria-label="Commits"> | ||
| 223 | <tbody data-rows> | 211 | <tbody data-rows> | ||
| 224 | <tr id="c-SHA7"> | 212 | <tr id="c-SHA7"> | ||
| 225 | - | <td class="cur" aria-hidden="true"> | |||
| 226 | - | </td> | |||
| 227 | <td class="c-sha mute">SHA7</td> | 213 | <td class="c-sha mute">SHA7</td> | ||
| 228 | <td class="c-subject"> | 214 | <td class="c-subject"> | ||
| 229 | <a href="/long/commit/SHA40">commit number 45</a> | 215 | <a href="/long/commit/SHA40">commit number 45</a> | ||
| @@ -236,8 +222,6 @@ | |||||
| 236 | </td> | 222 | </td> | ||
| 237 | </tr> | 223 | </tr> | ||
| 238 | <tr id="c-SHA7"> | 224 | <tr id="c-SHA7"> | ||
| 239 | - | <td class="cur" aria-hidden="true"> | |||
| 240 | - | </td> | |||
| 241 | <td class="c-sha mute">SHA7</td> | 225 | <td class="c-sha mute">SHA7</td> | ||
| 242 | <td class="c-subject"> | 226 | <td class="c-subject"> | ||
| 243 | <a href="/long/commit/SHA40">commit number 44</a> | 227 | <a href="/long/commit/SHA40">commit number 44</a> | ||
| @@ -259,8 +243,6 @@ | |||||
| 259 | <table class="list commits" aria-label="Commits"> | 243 | <table class="list commits" aria-label="Commits"> | ||
| 260 | <tbody data-rows> | 244 | <tbody data-rows> | ||
| 261 | <tr id="c-SHA7"> | 245 | <tr id="c-SHA7"> | ||
| 262 | - | <td class="cur" aria-hidden="true"> | |||
| 263 | - | </td> | |||
| 264 | <td class="c-sha mute">SHA7</td> | 246 | <td class="c-sha mute">SHA7</td> | ||
| 265 | <td class="c-subject"> | 247 | <td class="c-subject"> | ||
| 266 | <a href="/long/commit/SHA40">commit number 43</a> | 248 | <a href="/long/commit/SHA40">commit number 43</a> | ||
| @@ -273,8 +255,6 @@ | |||||
| 273 | </td> | 255 | </td> | ||
| 274 | </tr> | 256 | </tr> | ||
| 275 | <tr id="c-SHA7"> | 257 | <tr id="c-SHA7"> | ||
| 276 | - | <td class="cur" aria-hidden="true"> | |||
| 277 | - | </td> | |||
| 278 | <td class="c-sha mute">SHA7</td> | 258 | <td class="c-sha mute">SHA7</td> | ||
| 279 | <td class="c-subject"> | 259 | <td class="c-subject"> | ||
| 280 | <a href="/long/commit/SHA40">commit number 42</a> | 260 | <a href="/long/commit/SHA40">commit number 42</a> | ||
| @@ -296,8 +276,6 @@ | |||||
| 296 | <table class="list commits" aria-label="Commits"> | 276 | <table class="list commits" aria-label="Commits"> | ||
| 297 | <tbody data-rows> | 277 | <tbody data-rows> | ||
| 298 | <tr id="c-SHA7"> | 278 | <tr id="c-SHA7"> | ||
| 299 | - | <td class="cur" aria-hidden="true"> | |||
| 300 | - | </td> | |||
| 301 | <td class="c-sha mute">SHA7</td> | 279 | <td class="c-sha mute">SHA7</td> | ||
| 302 | <td class="c-subject"> | 280 | <td class="c-subject"> | ||
| 303 | <a href="/long/commit/SHA40">commit number 41</a> | 281 | <a href="/long/commit/SHA40">commit number 41</a> | ||
| @@ -310,8 +288,6 @@ | |||||
| 310 | </td> | 288 | </td> | ||
| 311 | </tr> | 289 | </tr> | ||
| 312 | <tr id="c-SHA7"> | 290 | <tr id="c-SHA7"> | ||
| 313 | - | <td class="cur" aria-hidden="true"> | |||
| 314 | - | </td> | |||
| 315 | <td class="c-sha mute">SHA7</td> | 291 | <td class="c-sha mute">SHA7</td> | ||
| 316 | <td class="c-subject"> | 292 | <td class="c-subject"> | ||
| 317 | <a href="/long/commit/SHA40">commit number 40</a> | 293 | <a href="/long/commit/SHA40">commit number 40</a> | ||
| @@ -333,8 +309,6 @@ | |||||
| 333 | <table class="list commits" aria-label="Commits"> | 309 | <table class="list commits" aria-label="Commits"> | ||
| 334 | <tbody data-rows> | 310 | <tbody data-rows> | ||
| 335 | <tr id="c-SHA7"> | 311 | <tr id="c-SHA7"> | ||
| 336 | - | <td class="cur" aria-hidden="true"> | |||
| 337 | - | </td> | |||
| 338 | <td class="c-sha mute">SHA7</td> | 312 | <td class="c-sha mute">SHA7</td> | ||
| 339 | <td class="c-subject"> | 313 | <td class="c-subject"> | ||
| 340 | <a href="/long/commit/SHA40">commit number 39</a> | 314 | <a href="/long/commit/SHA40">commit number 39</a> | ||
| @@ -347,8 +321,6 @@ | |||||
| 347 | </td> | 321 | </td> | ||
| 348 | </tr> | 322 | </tr> | ||
| 349 | <tr id="c-SHA7"> | 323 | <tr id="c-SHA7"> | ||
| 350 | - | <td class="cur" aria-hidden="true"> | |||
| 351 | - | </td> | |||
| 352 | <td class="c-sha mute">SHA7</td> | 324 | <td class="c-sha mute">SHA7</td> | ||
| 353 | <td class="c-subject"> | 325 | <td class="c-subject"> | ||
| 354 | <a href="/long/commit/SHA40">commit number 38</a> | 326 | <a href="/long/commit/SHA40">commit number 38</a> | ||
| @@ -370,8 +342,6 @@ | |||||
| 370 | <table class="list commits" aria-label="Commits"> | 342 | <table class="list commits" aria-label="Commits"> | ||
| 371 | <tbody data-rows> | 343 | <tbody data-rows> | ||
| 372 | <tr id="c-SHA7"> | 344 | <tr id="c-SHA7"> | ||
| 373 | - | <td class="cur" aria-hidden="true"> | |||
| 374 | - | </td> | |||
| 375 | <td class="c-sha mute">SHA7</td> | 345 | <td class="c-sha mute">SHA7</td> | ||
| 376 | <td class="c-subject"> | 346 | <td class="c-subject"> | ||
| 377 | <a href="/long/commit/SHA40">commit number 37</a> | 347 | <a href="/long/commit/SHA40">commit number 37</a> | ||
| @@ -384,8 +354,6 @@ | |||||
| 384 | </td> | 354 | </td> | ||
| 385 | </tr> | 355 | </tr> | ||
| 386 | <tr id="c-SHA7"> | 356 | <tr id="c-SHA7"> | ||
| 387 | - | <td class="cur" aria-hidden="true"> | |||
| 388 | - | </td> | |||
| 389 | <td class="c-sha mute">SHA7</td> | 357 | <td class="c-sha mute">SHA7</td> | ||
| 390 | <td class="c-subject"> | 358 | <td class="c-subject"> | ||
| 391 | <a href="/long/commit/SHA40">commit number 36</a> | 359 | <a href="/long/commit/SHA40">commit number 36</a> | ||
| @@ -407,8 +375,6 @@ | |||||
| 407 | <table class="list commits" aria-label="Commits"> | 375 | <table class="list commits" aria-label="Commits"> | ||
| 408 | <tbody data-rows> | 376 | <tbody data-rows> | ||
| 409 | <tr id="c-SHA7"> | 377 | <tr id="c-SHA7"> | ||
| 410 | - | <td class="cur" aria-hidden="true"> | |||
| 411 | - | </td> | |||
| 412 | <td class="c-sha mute">SHA7</td> | 378 | <td class="c-sha mute">SHA7</td> | ||
| 413 | <td class="c-subject"> | 379 | <td class="c-subject"> | ||
| 414 | <a href="/long/commit/SHA40">commit number 35</a> | 380 | <a href="/long/commit/SHA40">commit number 35</a> | ||
| @@ -421,8 +387,6 @@ | |||||
| 421 | </td> | 387 | </td> | ||
| 422 | </tr> | 388 | </tr> | ||
| 423 | <tr id="c-SHA7"> | 389 | <tr id="c-SHA7"> | ||
| 424 | - | <td class="cur" aria-hidden="true"> | |||
| 425 | - | </td> | |||
| 426 | <td class="c-sha mute">SHA7</td> | 390 | <td class="c-sha mute">SHA7</td> | ||
| 427 | <td class="c-subject"> | 391 | <td class="c-subject"> | ||
| 428 | <a href="/long/commit/SHA40">commit number 34</a> | 392 | <a href="/long/commit/SHA40">commit number 34</a> | ||
| @@ -444,8 +408,6 @@ | |||||
| 444 | <table class="list commits" aria-label="Commits"> | 408 | <table class="list commits" aria-label="Commits"> | ||
| 445 | <tbody data-rows> | 409 | <tbody data-rows> | ||
| 446 | <tr id="c-SHA7"> | 410 | <tr id="c-SHA7"> | ||
| 447 | - | <td class="cur" aria-hidden="true"> | |||
| 448 | - | </td> | |||
| 449 | <td class="c-sha mute">SHA7</td> | 411 | <td class="c-sha mute">SHA7</td> | ||
| 450 | <td class="c-subject"> | 412 | <td class="c-subject"> | ||
| 451 | <a href="/long/commit/SHA40">commit number 33</a> | 413 | <a href="/long/commit/SHA40">commit number 33</a> | ||
| @@ -458,8 +420,6 @@ | |||||
| 458 | </td> | 420 | </td> | ||
| 459 | </tr> | 421 | </tr> | ||
| 460 | <tr id="c-SHA7"> | 422 | <tr id="c-SHA7"> | ||
| 461 | - | <td class="cur" aria-hidden="true"> | |||
| 462 | - | </td> | |||
| 463 | <td class="c-sha mute">SHA7</td> | 423 | <td class="c-sha mute">SHA7</td> | ||
| 464 | <td class="c-subject"> | 424 | <td class="c-subject"> | ||
| 465 | <a href="/long/commit/SHA40">commit number 32</a> | 425 | <a href="/long/commit/SHA40">commit number 32</a> | ||
| @@ -481,8 +441,6 @@ | |||||
| 481 | <table class="list commits" aria-label="Commits"> | 441 | <table class="list commits" aria-label="Commits"> | ||
| 482 | <tbody data-rows> | 442 | <tbody data-rows> | ||
| 483 | <tr id="c-SHA7"> | 443 | <tr id="c-SHA7"> | ||
| 484 | - | <td class="cur" aria-hidden="true"> | |||
| 485 | - | </td> | |||
| 486 | <td class="c-sha mute">SHA7</td> | 444 | <td class="c-sha mute">SHA7</td> | ||
| 487 | <td class="c-subject"> | 445 | <td class="c-subject"> | ||
| 488 | <a href="/long/commit/SHA40">commit number 31</a> | 446 | <a href="/long/commit/SHA40">commit number 31</a> | ||
| @@ -495,8 +453,6 @@ | |||||
| 495 | </td> | 453 | </td> | ||
| 496 | </tr> | 454 | </tr> | ||
| 497 | <tr id="c-SHA7"> | 455 | <tr id="c-SHA7"> | ||
| 498 | - | <td class="cur" aria-hidden="true"> | |||
| 499 | - | </td> | |||
| 500 | <td class="c-sha mute">SHA7</td> | 456 | <td class="c-sha mute">SHA7</td> | ||
| 501 | <td class="c-subject"> | 457 | <td class="c-subject"> | ||
| 502 | <a href="/long/commit/SHA40">commit number 30</a> | 458 | <a href="/long/commit/SHA40">commit number 30</a> | ||
| @@ -518,8 +474,6 @@ | |||||
| 518 | <table class="list commits" aria-label="Commits"> | 474 | <table class="list commits" aria-label="Commits"> | ||
| 519 | <tbody data-rows> | 475 | <tbody data-rows> | ||
| 520 | <tr id="c-SHA7"> | 476 | <tr id="c-SHA7"> | ||
| 521 | - | <td class="cur" aria-hidden="true"> | |||
| 522 | - | </td> | |||
| 523 | <td class="c-sha mute">SHA7</td> | 477 | <td class="c-sha mute">SHA7</td> | ||
| 524 | <td class="c-subject"> | 478 | <td class="c-subject"> | ||
| 525 | <a href="/long/commit/SHA40">commit number 29</a> | 479 | <a href="/long/commit/SHA40">commit number 29</a> | ||
| @@ -532,8 +486,6 @@ | |||||
| 532 | </td> | 486 | </td> | ||
| 533 | </tr> | 487 | </tr> | ||
| 534 | <tr id="c-SHA7"> | 488 | <tr id="c-SHA7"> | ||
| 535 | - | <td class="cur" aria-hidden="true"> | |||
| 536 | - | </td> | |||
| 537 | <td class="c-sha mute">SHA7</td> | 489 | <td class="c-sha mute">SHA7</td> | ||
| 538 | <td class="c-subject"> | 490 | <td class="c-subject"> | ||
| 539 | <a href="/long/commit/SHA40">commit number 28</a> | 491 | <a href="/long/commit/SHA40">commit number 28</a> | ||
| @@ -555,8 +507,6 @@ | |||||
| 555 | <table class="list commits" aria-label="Commits"> | 507 | <table class="list commits" aria-label="Commits"> | ||
| 556 | <tbody data-rows> | 508 | <tbody data-rows> | ||
| 557 | <tr id="c-SHA7"> | 509 | <tr id="c-SHA7"> | ||
| 558 | - | <td class="cur" aria-hidden="true"> | |||
| 559 | - | </td> | |||
| 560 | <td class="c-sha mute">SHA7</td> | 510 | <td class="c-sha mute">SHA7</td> | ||
| 561 | <td class="c-subject"> | 511 | <td class="c-subject"> | ||
| 562 | <a href="/long/commit/SHA40">commit number 27</a> | 512 | <a href="/long/commit/SHA40">commit number 27</a> | ||
| @@ -569,8 +519,6 @@ | |||||
| 569 | </td> | 519 | </td> | ||
| 570 | </tr> | 520 | </tr> | ||
| 571 | <tr id="c-SHA7"> | 521 | <tr id="c-SHA7"> | ||
| 572 | - | <td class="cur" aria-hidden="true"> | |||
| 573 | - | </td> | |||
| 574 | <td class="c-sha mute">SHA7</td> | 522 | <td class="c-sha mute">SHA7</td> | ||
| 575 | <td class="c-subject"> | 523 | <td class="c-subject"> | ||
| 576 | <a href="/long/commit/SHA40">commit number 26</a> | 524 | <a href="/long/commit/SHA40">commit number 26</a> | ||
| @@ -592,8 +540,6 @@ | |||||
| 592 | <table class="list commits" aria-label="Commits"> | 540 | <table class="list commits" aria-label="Commits"> | ||
| 593 | <tbody data-rows> | 541 | <tbody data-rows> | ||
| 594 | <tr id="c-SHA7"> | 542 | <tr id="c-SHA7"> | ||
| 595 | - | <td class="cur" aria-hidden="true"> | |||
| 596 | - | </td> | |||
| 597 | <td class="c-sha mute">SHA7</td> | 543 | <td class="c-sha mute">SHA7</td> | ||
| 598 | <td class="c-subject"> | 544 | <td class="c-subject"> | ||
| 599 | <a href="/long/commit/SHA40">commit number 25</a> | 545 | <a href="/long/commit/SHA40">commit number 25</a> | ||
| @@ -606,8 +552,6 @@ | |||||
| 606 | </td> | 552 | </td> | ||
| 607 | </tr> | 553 | </tr> | ||
| 608 | <tr id="c-SHA7"> | 554 | <tr id="c-SHA7"> | ||
| 609 | - | <td class="cur" aria-hidden="true"> | |||
| 610 | - | </td> | |||
| 611 | <td class="c-sha mute">SHA7</td> | 555 | <td class="c-sha mute">SHA7</td> | ||
| 612 | <td class="c-subject"> | 556 | <td class="c-subject"> | ||
| 613 | <a href="/long/commit/SHA40">commit number 24</a> | 557 | <a href="/long/commit/SHA40">commit number 24</a> | ||
| @@ -629,8 +573,6 @@ | |||||
| 629 | <table class="list commits" aria-label="Commits"> | 573 | <table class="list commits" aria-label="Commits"> | ||
| 630 | <tbody data-rows> | 574 | <tbody data-rows> | ||
| 631 | <tr id="c-SHA7"> | 575 | <tr id="c-SHA7"> | ||
| 632 | - | <td class="cur" aria-hidden="true"> | |||
| 633 | - | </td> | |||
| 634 | <td class="c-sha mute">SHA7</td> | 576 | <td class="c-sha mute">SHA7</td> | ||
| 635 | <td class="c-subject"> | 577 | <td class="c-subject"> | ||
| 636 | <a href="/long/commit/SHA40">commit number 23</a> | 578 | <a href="/long/commit/SHA40">commit number 23</a> | ||
| @@ -643,8 +585,6 @@ | |||||
| 643 | </td> | 585 | </td> | ||
| 644 | </tr> | 586 | </tr> | ||
| 645 | <tr id="c-SHA7"> | 587 | <tr id="c-SHA7"> | ||
| 646 | - | <td class="cur" aria-hidden="true"> | |||
| 647 | - | </td> | |||
| 648 | <td class="c-sha mute">SHA7</td> | 588 | <td class="c-sha mute">SHA7</td> | ||
| 649 | <td class="c-subject"> | 589 | <td class="c-subject"> | ||
| 650 | <a href="/long/commit/SHA40">commit number 22</a> | 590 | <a href="/long/commit/SHA40">commit number 22</a> | ||
| @@ -666,8 +606,6 @@ | |||||
| 666 | <table class="list commits" aria-label="Commits"> | 606 | <table class="list commits" aria-label="Commits"> | ||
| 667 | <tbody data-rows> | 607 | <tbody data-rows> | ||
| 668 | <tr id="c-SHA7"> | 608 | <tr id="c-SHA7"> | ||
| 669 | - | <td class="cur" aria-hidden="true"> | |||
| 670 | - | </td> | |||
| 671 | <td class="c-sha mute">SHA7</td> | 609 | <td class="c-sha mute">SHA7</td> | ||
| 672 | <td class="c-subject"> | 610 | <td class="c-subject"> | ||
| 673 | <a href="/long/commit/SHA40">commit number 21</a> | 611 | <a href="/long/commit/SHA40">commit number 21</a> | ||
| @@ -680,8 +618,6 @@ | |||||
| 680 | </td> | 618 | </td> | ||
| 681 | </tr> | 619 | </tr> | ||
| 682 | <tr id="c-SHA7"> | 620 | <tr id="c-SHA7"> | ||
| 683 | - | <td class="cur" aria-hidden="true"> | |||
| 684 | - | </td> | |||
| 685 | <td class="c-sha mute">SHA7</td> | 621 | <td class="c-sha mute">SHA7</td> | ||
| 686 | <td class="c-subject"> | 622 | <td class="c-subject"> | ||
| 687 | <a href="/long/commit/SHA40">commit number 20</a> | 623 | <a href="/long/commit/SHA40">commit number 20</a> | ||
| @@ -703,8 +639,6 @@ | |||||
| 703 | <table class="list commits" aria-label="Commits"> | 639 | <table class="list commits" aria-label="Commits"> | ||
| 704 | <tbody data-rows> | 640 | <tbody data-rows> | ||
| 705 | <tr id="c-SHA7"> | 641 | <tr id="c-SHA7"> | ||
| 706 | - | <td class="cur" aria-hidden="true"> | |||
| 707 | - | </td> | |||
| 708 | <td class="c-sha mute">SHA7</td> | 642 | <td class="c-sha mute">SHA7</td> | ||
| 709 | <td class="c-subject"> | 643 | <td class="c-subject"> | ||
| 710 | <a href="/long/commit/SHA40">commit number 19</a> | 644 | <a href="/long/commit/SHA40">commit number 19</a> | ||
| @@ -717,8 +651,6 @@ | |||||
| 717 | </td> | 651 | </td> | ||
| 718 | </tr> | 652 | </tr> | ||
| 719 | <tr id="c-SHA7"> | 653 | <tr id="c-SHA7"> | ||
| 720 | - | <td class="cur" aria-hidden="true"> | |||
| 721 | - | </td> | |||
| 722 | <td class="c-sha mute">SHA7</td> | 654 | <td class="c-sha mute">SHA7</td> | ||
| 723 | <td class="c-subject"> | 655 | <td class="c-subject"> | ||
| 724 | <a href="/long/commit/SHA40">commit number 18</a> | 656 | <a href="/long/commit/SHA40">commit number 18</a> | ||
| @@ -740,8 +672,6 @@ | |||||
| 740 | <table class="list commits" aria-label="Commits"> | 672 | <table class="list commits" aria-label="Commits"> | ||
| 741 | <tbody data-rows> | 673 | <tbody data-rows> | ||
| 742 | <tr id="c-SHA7"> | 674 | <tr id="c-SHA7"> | ||
| 743 | - | <td class="cur" aria-hidden="true"> | |||
| 744 | - | </td> | |||
| 745 | <td class="c-sha mute">SHA7</td> | 675 | <td class="c-sha mute">SHA7</td> | ||
| 746 | <td class="c-subject"> | 676 | <td class="c-subject"> | ||
| 747 | <a href="/long/commit/SHA40">commit number 17</a> | 677 | <a href="/long/commit/SHA40">commit number 17</a> | ||
| @@ -754,8 +684,6 @@ | |||||
| 754 | </td> | 684 | </td> | ||
| 755 | </tr> | 685 | </tr> | ||
| 756 | <tr id="c-SHA7"> | 686 | <tr id="c-SHA7"> | ||
| 757 | - | <td class="cur" aria-hidden="true"> | |||
| 758 | - | </td> | |||
| 759 | <td class="c-sha mute">SHA7</td> | 687 | <td class="c-sha mute">SHA7</td> | ||
| 760 | <td class="c-subject"> | 688 | <td class="c-subject"> | ||
| 761 | <a href="/long/commit/SHA40">commit number 16</a> | 689 | <a href="/long/commit/SHA40">commit number 16</a> | ||
| @@ -777,8 +705,6 @@ | |||||
| 777 | <table class="list commits" aria-label="Commits"> | 705 | <table class="list commits" aria-label="Commits"> | ||
| 778 | <tbody data-rows> | 706 | <tbody data-rows> | ||
| 779 | <tr id="c-SHA7"> | 707 | <tr id="c-SHA7"> | ||
| 780 | - | <td class="cur" aria-hidden="true"> | |||
| 781 | - | </td> | |||
| 782 | <td class="c-sha mute">SHA7</td> | 708 | <td class="c-sha mute">SHA7</td> | ||
| 783 | <td class="c-subject"> | 709 | <td class="c-subject"> | ||
| 784 | <a href="/long/commit/SHA40">commit number 15</a> | 710 | <a href="/long/commit/SHA40">commit number 15</a> | ||
| @@ -791,8 +717,6 @@ | |||||
| 791 | </td> | 717 | </td> | ||
| 792 | </tr> | 718 | </tr> | ||
| 793 | <tr id="c-SHA7"> | 719 | <tr id="c-SHA7"> | ||
| 794 | - | <td class="cur" aria-hidden="true"> | |||
| 795 | - | </td> | |||
| 796 | <td class="c-sha mute">SHA7</td> | 720 | <td class="c-sha mute">SHA7</td> | ||
| 797 | <td class="c-subject"> | 721 | <td class="c-subject"> | ||
| 798 | <a href="/long/commit/SHA40">commit number 14</a> | 722 | <a href="/long/commit/SHA40">commit number 14</a> | ||
| @@ -814,8 +738,6 @@ | |||||
| 814 | <table class="list commits" aria-label="Commits"> | 738 | <table class="list commits" aria-label="Commits"> | ||
| 815 | <tbody data-rows> | 739 | <tbody data-rows> | ||
| 816 | <tr id="c-SHA7"> | 740 | <tr id="c-SHA7"> | ||
| 817 | - | <td class="cur" aria-hidden="true"> | |||
| 818 | - | </td> | |||
| 819 | <td class="c-sha mute">SHA7</td> | 741 | <td class="c-sha mute">SHA7</td> | ||
| 820 | <td class="c-subject"> | 742 | <td class="c-subject"> | ||
| 821 | <a href="/long/commit/SHA40">commit number 13</a> | 743 | <a href="/long/commit/SHA40">commit number 13</a> | ||
| @@ -828,8 +750,6 @@ | |||||
| 828 | </td> | 750 | </td> | ||
| 829 | </tr> | 751 | </tr> | ||
| 830 | <tr id="c-SHA7"> | 752 | <tr id="c-SHA7"> | ||
| 831 | - | <td class="cur" aria-hidden="true"> | |||
| 832 | - | </td> | |||
| 833 | <td class="c-sha mute">SHA7</td> | 753 | <td class="c-sha mute">SHA7</td> | ||
| 834 | <td class="c-subject"> | 754 | <td class="c-subject"> | ||
| 835 | <a href="/long/commit/SHA40">commit number 12</a> | 755 | <a href="/long/commit/SHA40">commit number 12</a> | ||
| @@ -851,8 +771,6 @@ | |||||
| 851 | <table class="list commits" aria-label="Commits"> | 771 | <table class="list commits" aria-label="Commits"> | ||
| 852 | <tbody data-rows> | 772 | <tbody data-rows> | ||
| 853 | <tr id="c-SHA7"> | 773 | <tr id="c-SHA7"> | ||
| 854 | - | <td class="cur" aria-hidden="true"> | |||
| 855 | - | </td> | |||
| 856 | <td class="c-sha mute">SHA7</td> | 774 | <td class="c-sha mute">SHA7</td> | ||
| 857 | <td class="c-subject"> | 775 | <td class="c-subject"> | ||
| 858 | <a href="/long/commit/SHA40">commit number 11</a> | 776 | <a href="/long/commit/SHA40">commit number 11</a> | ||
| @@ -865,8 +783,6 @@ | |||||
| 865 | </td> | 783 | </td> | ||
| 866 | </tr> | 784 | </tr> | ||
| 867 | <tr id="c-SHA7"> | 785 | <tr id="c-SHA7"> | ||
| 868 | - | <td class="cur" aria-hidden="true"> | |||
| 869 | - | </td> | |||
| 870 | <td class="c-sha mute">SHA7</td> | 786 | <td class="c-sha mute">SHA7</td> | ||
| 871 | <td class="c-subject"> | 787 | <td class="c-subject"> | ||
| 872 | <a href="/long/commit/SHA40">commit number 10</a> | 788 | <a href="/long/commit/SHA40">commit number 10</a> | ||
| @@ -888,8 +804,6 @@ | |||||
| 888 | <table class="list commits" aria-label="Commits"> | 804 | <table class="list commits" aria-label="Commits"> | ||
| 889 | <tbody data-rows> | 805 | <tbody data-rows> | ||
| 890 | <tr id="c-SHA7"> | 806 | <tr id="c-SHA7"> | ||
| 891 | - | <td class="cur" aria-hidden="true"> | |||
| 892 | - | </td> | |||
| 893 | <td class="c-sha mute">SHA7</td> | 807 | <td class="c-sha mute">SHA7</td> | ||
| 894 | <td class="c-subject"> | 808 | <td class="c-subject"> | ||
| 895 | <a href="/long/commit/SHA40">commit number 9</a> | 809 | <a href="/long/commit/SHA40">commit number 9</a> | ||
| @@ -902,8 +816,6 @@ | |||||
| 902 | </td> | 816 | </td> | ||
| 903 | </tr> | 817 | </tr> | ||
| 904 | <tr id="c-SHA7"> | 818 | <tr id="c-SHA7"> | ||
| 905 | - | <td class="cur" aria-hidden="true"> | |||
| 906 | - | </td> | |||
| 907 | <td class="c-sha mute">SHA7</td> | 819 | <td class="c-sha mute">SHA7</td> | ||
| 908 | <td class="c-subject"> | 820 | <td class="c-subject"> | ||
| 909 | <a href="/long/commit/SHA40">commit number 8</a> | 821 | <a href="/long/commit/SHA40">commit number 8</a> | ||
| @@ -925,8 +837,6 @@ | |||||
| 925 | <table class="list commits" aria-label="Commits"> | 837 | <table class="list commits" aria-label="Commits"> | ||
| 926 | <tbody data-rows> | 838 | <tbody data-rows> | ||
| 927 | <tr id="c-SHA7"> | 839 | <tr id="c-SHA7"> | ||
| 928 | - | <td class="cur" aria-hidden="true"> | |||
| 929 | - | </td> | |||
| 930 | <td class="c-sha mute">SHA7</td> | 840 | <td class="c-sha mute">SHA7</td> | ||
| 931 | <td class="c-subject"> | 841 | <td class="c-subject"> | ||
| 932 | <a href="/long/commit/SHA40">commit number 7</a> | 842 | <a href="/long/commit/SHA40">commit number 7</a> | ||
| @@ -939,8 +849,6 @@ | |||||
| 939 | </td> | 849 | </td> | ||
| 940 | </tr> | 850 | </tr> | ||
| 941 | <tr id="c-SHA7"> | 851 | <tr id="c-SHA7"> | ||
| 942 | - | <td class="cur" aria-hidden="true"> | |||
| 943 | - | </td> | |||
| 944 | <td class="c-sha mute">SHA7</td> | 852 | <td class="c-sha mute">SHA7</td> | ||
| 945 | <td class="c-subject"> | 853 | <td class="c-subject"> | ||
| 946 | <a href="/long/commit/SHA40">commit number 6</a> | 854 | <a href="/long/commit/SHA40">commit number 6</a> | ||
| @@ -962,8 +870,6 @@ | |||||
| 962 | <table class="list commits" aria-label="Commits"> | 870 | <table class="list commits" aria-label="Commits"> | ||
| 963 | <tbody data-rows> | 871 | <tbody data-rows> | ||
| 964 | <tr id="c-SHA7"> | 872 | <tr id="c-SHA7"> | ||
| 965 | - | <td class="cur" aria-hidden="true"> | |||
| 966 | - | </td> | |||
| 967 | <td class="c-sha mute">SHA7</td> | 873 | <td class="c-sha mute">SHA7</td> | ||
| 968 | <td class="c-subject"> | 874 | <td class="c-subject"> | ||
| 969 | <a href="/long/commit/SHA40">commit number 5</a> | 875 | <a href="/long/commit/SHA40">commit number 5</a> | ||
| @@ -976,8 +882,6 @@ | |||||
| 976 | </td> | 882 | </td> | ||
| 977 | </tr> | 883 | </tr> | ||
| 978 | <tr id="c-SHA7"> | 884 | <tr id="c-SHA7"> | ||
| 979 | - | <td class="cur" aria-hidden="true"> | |||
| 980 | - | </td> | |||
| 981 | <td class="c-sha mute">SHA7</td> | 885 | <td class="c-sha mute">SHA7</td> | ||
| 982 | <td class="c-subject"> | 886 | <td class="c-subject"> | ||
| 983 | <a href="/long/commit/SHA40">commit number 4</a> | 887 | <a href="/long/commit/SHA40">commit number 4</a> | ||
| @@ -999,8 +903,6 @@ | |||||
| 999 | <table class="list commits" aria-label="Commits"> | 903 | <table class="list commits" aria-label="Commits"> | ||
| 1000 | <tbody data-rows> | 904 | <tbody data-rows> | ||
| 1001 | <tr id="c-SHA7"> | 905 | <tr id="c-SHA7"> | ||
| 1002 | - | <td class="cur" aria-hidden="true"> | |||
| 1003 | - | </td> | |||
| 1004 | <td class="c-sha mute">SHA7</td> | 906 | <td class="c-sha mute">SHA7</td> | ||
| 1005 | <td class="c-subject"> | 907 | <td class="c-subject"> | ||
| 1006 | <a href="/long/commit/SHA40">commit number 3</a> | 908 | <a href="/long/commit/SHA40">commit number 3</a> | ||
| @@ -1013,8 +915,6 @@ | |||||
| 1013 | </td> | 915 | </td> | ||
| 1014 | </tr> | 916 | </tr> | ||
| 1015 | <tr id="c-SHA7"> | 917 | <tr id="c-SHA7"> | ||
| 1016 | - | <td class="cur" aria-hidden="true"> | |||
| 1017 | - | </td> | |||
| 1018 | <td class="c-sha mute">SHA7</td> | 918 | <td class="c-sha mute">SHA7</td> | ||
| 1019 | <td class="c-subject"> | 919 | <td class="c-subject"> | ||
| 1020 | <a href="/long/commit/SHA40">commit number 2</a> | 920 | <a href="/long/commit/SHA40">commit number 2</a> | ||
M internal/app/testdata/web/commits-small.html +0 -16
| @@ -110,8 +110,6 @@ | |||
| 110 | 110 | <table class="list commits" aria-label="Commits"> | |
| 111 | 111 | <tbody data-rows> | |
| 112 | 112 | <tr id="c-SHA7" data-cursor> | |
| 113 | - | <td class="cur" aria-hidden="true"> | |
| 114 | - | </td> | |
| 115 | 113 | <td class="c-sha mute">SHA7</td> | |
| 116 | 114 | <td class="c-subject"> | |
| 117 | 115 | <a href="/diffs/commit/SHA40">add a very large file</a> | |
| @@ -134,8 +132,6 @@ | |||
| 134 | 132 | <table class="list commits" aria-label="Commits"> | |
| 135 | 133 | <tbody data-rows> | |
| 136 | 134 | <tr id="c-SHA7"> | |
| 137 | - | <td class="cur" aria-hidden="true"> | |
| 138 | - | </td> | |
| 139 | 135 | <td class="c-sha mute">SHA7</td> | |
| 140 | 136 | <td class="c-subject"> | |
| 141 | 137 | <a href="/diffs/commit/SHA40">add evil.go</a> | |
| @@ -157,8 +153,6 @@ | |||
| 157 | 153 | <table class="list commits" aria-label="Commits"> | |
| 158 | 154 | <tbody data-rows> | |
| 159 | 155 | <tr id="c-SHA7"> | |
| 160 | - | <td class="cur" aria-hidden="true"> | |
| 161 | - | </td> | |
| 162 | 156 | <td class="c-sha mute">SHA7</td> | |
| 163 | 157 | <td class="c-subject"> | |
| 164 | 158 | <a href="/diffs/commit/SHA40">merge the side branch</a> | |
| @@ -181,8 +175,6 @@ | |||
| 181 | 175 | <table class="list commits" aria-label="Commits"> | |
| 182 | 176 | <tbody data-rows> | |
| 183 | 177 | <tr id="c-SHA7"> | |
| 184 | - | <td class="cur" aria-hidden="true"> | |
| 185 | - | </td> | |
| 186 | 178 | <td class="c-sha mute">SHA7</td> | |
| 187 | 179 | <td class="c-subject"> | |
| 188 | 180 | <a href="/diffs/commit/SHA40">work on the side</a> | |
| @@ -205,8 +197,6 @@ | |||
| 205 | 197 | <table class="list commits" aria-label="Commits"> | |
| 206 | 198 | <tbody data-rows> | |
| 207 | 199 | <tr id="c-SHA7"> | |
| 208 | - | <td class="cur" aria-hidden="true"> | |
| 209 | - | </td> | |
| 210 | 200 | <td class="c-sha mute">SHA7</td> | |
| 211 | 201 | <td class="c-subject"> | |
| 212 | 202 | <a href="/diffs/commit/SHA40">change a binary file</a> | |
| @@ -228,8 +218,6 @@ | |||
| 228 | 218 | <table class="list commits" aria-label="Commits"> | |
| 229 | 219 | <tbody data-rows> | |
| 230 | 220 | <tr id="c-SHA7"> | |
| 231 | - | <td class="cur" aria-hidden="true"> | |
| 232 | - | </td> | |
| 233 | 221 | <td class="c-sha mute">SHA7</td> | |
| 234 | 222 | <td class="c-subject"> | |
| 235 | 223 | <a href="/diffs/commit/SHA40">rename a file and delete another</a> | |
| @@ -251,8 +239,6 @@ | |||
| 251 | 239 | <table class="list commits" aria-label="Commits"> | |
| 252 | 240 | <tbody data-rows> | |
| 253 | 241 | <tr id="c-SHA7"> | |
| 254 | - | <td class="cur" aria-hidden="true"> | |
| 255 | - | </td> | |
| 256 | 242 | <td class="c-sha mute">SHA7</td> | |
| 257 | 243 | <td class="c-subject"> | |
| 258 | 244 | <a href="/diffs/commit/SHA40">modify a file and make a script executable</a> | |
| @@ -276,8 +262,6 @@ | |||
| 276 | 262 | <table class="list commits" aria-label="Commits"> | |
| 277 | 263 | <tbody data-rows> | |
| 278 | 264 | <tr id="c-SHA7"> | |
| 279 | - | <td class="cur" aria-hidden="true"> | |
| 280 | - | </td> | |
| 281 | 265 | <td class="c-sha mute">SHA7</td> | |
| 282 | 266 | <td class="c-subject"> | |
| 283 | 267 | <a href="/diffs/commit/SHA40">root: the first files</a> | |
| @@ -110,8 +110,6 @@ | |||||
| 110 | <table class="list commits" aria-label="Commits"> | 110 | <table class="list commits" aria-label="Commits"> | ||
| 111 | <tbody data-rows> | 111 | <tbody data-rows> | ||
| 112 | <tr id="c-SHA7" data-cursor> | 112 | <tr id="c-SHA7" data-cursor> | ||
| 113 | - | <td class="cur" aria-hidden="true"> | |||
| 114 | - | </td> | |||
| 115 | <td class="c-sha mute">SHA7</td> | 113 | <td class="c-sha mute">SHA7</td> | ||
| 116 | <td class="c-subject"> | 114 | <td class="c-subject"> | ||
| 117 | <a href="/diffs/commit/SHA40">add a very large file</a> | 115 | <a href="/diffs/commit/SHA40">add a very large file</a> | ||
| @@ -134,8 +132,6 @@ | |||||
| 134 | <table class="list commits" aria-label="Commits"> | 132 | <table class="list commits" aria-label="Commits"> | ||
| 135 | <tbody data-rows> | 133 | <tbody data-rows> | ||
| 136 | <tr id="c-SHA7"> | 134 | <tr id="c-SHA7"> | ||
| 137 | - | <td class="cur" aria-hidden="true"> | |||
| 138 | - | </td> | |||
| 139 | <td class="c-sha mute">SHA7</td> | 135 | <td class="c-sha mute">SHA7</td> | ||
| 140 | <td class="c-subject"> | 136 | <td class="c-subject"> | ||
| 141 | <a href="/diffs/commit/SHA40">add evil.go</a> | 137 | <a href="/diffs/commit/SHA40">add evil.go</a> | ||
| @@ -157,8 +153,6 @@ | |||||
| 157 | <table class="list commits" aria-label="Commits"> | 153 | <table class="list commits" aria-label="Commits"> | ||
| 158 | <tbody data-rows> | 154 | <tbody data-rows> | ||
| 159 | <tr id="c-SHA7"> | 155 | <tr id="c-SHA7"> | ||
| 160 | - | <td class="cur" aria-hidden="true"> | |||
| 161 | - | </td> | |||
| 162 | <td class="c-sha mute">SHA7</td> | 156 | <td class="c-sha mute">SHA7</td> | ||
| 163 | <td class="c-subject"> | 157 | <td class="c-subject"> | ||
| 164 | <a href="/diffs/commit/SHA40">merge the side branch</a> | 158 | <a href="/diffs/commit/SHA40">merge the side branch</a> | ||
| @@ -181,8 +175,6 @@ | |||||
| 181 | <table class="list commits" aria-label="Commits"> | 175 | <table class="list commits" aria-label="Commits"> | ||
| 182 | <tbody data-rows> | 176 | <tbody data-rows> | ||
| 183 | <tr id="c-SHA7"> | 177 | <tr id="c-SHA7"> | ||
| 184 | - | <td class="cur" aria-hidden="true"> | |||
| 185 | - | </td> | |||
| 186 | <td class="c-sha mute">SHA7</td> | 178 | <td class="c-sha mute">SHA7</td> | ||
| 187 | <td class="c-subject"> | 179 | <td class="c-subject"> | ||
| 188 | <a href="/diffs/commit/SHA40">work on the side</a> | 180 | <a href="/diffs/commit/SHA40">work on the side</a> | ||
| @@ -205,8 +197,6 @@ | |||||
| 205 | <table class="list commits" aria-label="Commits"> | 197 | <table class="list commits" aria-label="Commits"> | ||
| 206 | <tbody data-rows> | 198 | <tbody data-rows> | ||
| 207 | <tr id="c-SHA7"> | 199 | <tr id="c-SHA7"> | ||
| 208 | - | <td class="cur" aria-hidden="true"> | |||
| 209 | - | </td> | |||
| 210 | <td class="c-sha mute">SHA7</td> | 200 | <td class="c-sha mute">SHA7</td> | ||
| 211 | <td class="c-subject"> | 201 | <td class="c-subject"> | ||
| 212 | <a href="/diffs/commit/SHA40">change a binary file</a> | 202 | <a href="/diffs/commit/SHA40">change a binary file</a> | ||
| @@ -228,8 +218,6 @@ | |||||
| 228 | <table class="list commits" aria-label="Commits"> | 218 | <table class="list commits" aria-label="Commits"> | ||
| 229 | <tbody data-rows> | 219 | <tbody data-rows> | ||
| 230 | <tr id="c-SHA7"> | 220 | <tr id="c-SHA7"> | ||
| 231 | - | <td class="cur" aria-hidden="true"> | |||
| 232 | - | </td> | |||
| 233 | <td class="c-sha mute">SHA7</td> | 221 | <td class="c-sha mute">SHA7</td> | ||
| 234 | <td class="c-subject"> | 222 | <td class="c-subject"> | ||
| 235 | <a href="/diffs/commit/SHA40">rename a file and delete another</a> | 223 | <a href="/diffs/commit/SHA40">rename a file and delete another</a> | ||
| @@ -251,8 +239,6 @@ | |||||
| 251 | <table class="list commits" aria-label="Commits"> | 239 | <table class="list commits" aria-label="Commits"> | ||
| 252 | <tbody data-rows> | 240 | <tbody data-rows> | ||
| 253 | <tr id="c-SHA7"> | 241 | <tr id="c-SHA7"> | ||
| 254 | - | <td class="cur" aria-hidden="true"> | |||
| 255 | - | </td> | |||
| 256 | <td class="c-sha mute">SHA7</td> | 242 | <td class="c-sha mute">SHA7</td> | ||
| 257 | <td class="c-subject"> | 243 | <td class="c-subject"> | ||
| 258 | <a href="/diffs/commit/SHA40">modify a file and make a script executable</a> | 244 | <a href="/diffs/commit/SHA40">modify a file and make a script executable</a> | ||
| @@ -276,8 +262,6 @@ | |||||
| 276 | <table class="list commits" aria-label="Commits"> | 262 | <table class="list commits" aria-label="Commits"> | ||
| 277 | <tbody data-rows> | 263 | <tbody data-rows> | ||
| 278 | <tr id="c-SHA7"> | 264 | <tr id="c-SHA7"> | ||
| 279 | - | <td class="cur" aria-hidden="true"> | |||
| 280 | - | </td> | |||
| 281 | <td class="c-sha mute">SHA7</td> | 265 | <td class="c-sha mute">SHA7</td> | ||
| 282 | <td class="c-subject"> | 266 | <td class="c-subject"> | ||
| 283 | <a href="/diffs/commit/SHA40">root: the first files</a> | 267 | <a href="/diffs/commit/SHA40">root: the first files</a> | ||
M internal/app/testdata/web/home.html +28 -13
| @@ -72,13 +72,28 @@ | |||
| 72 | 72 | <a href="/demo">demo</a> | |
| 73 | 73 | </h2> | |
| 74 | 74 | <p class="repo-desc">A demo repository</p> | |
| 75 | - | <p class="repo-meta"> | |
| 76 | - | <span class="repo-branch"> | |
| 77 | - | <span class="ico i-branch" aria-hidden="true"> | |
| 78 | - | </span>main</span> | |
| 79 | - | <span>pushed <time datetime="STAMP" title="STAMP">AGO</time> | |
| 80 | - | </span> | |
| 81 | - | </p> | |
| 75 | + | <dl class="repo-stats" aria-label="Figures of demo"> | |
| 76 | + | <div> | |
| 77 | + | <dt>commits</dt> | |
| 78 | + | <dd>2</dd> | |
| 79 | + | </div> | |
| 80 | + | <div> | |
| 81 | + | <dt>last 30 days</dt> | |
| 82 | + | <dd>0</dd> | |
| 83 | + | </div> | |
| 84 | + | <div> | |
| 85 | + | <dt>latest tag</dt> | |
| 86 | + | <dd> | |
| 87 | + | <a href="/demo/tree/SHA40" title="made AGO">v0.2.0</a> | |
| 88 | + | </dd> | |
| 89 | + | </div> | |
| 90 | + | <div> | |
| 91 | + | <dt>last activity</dt> | |
| 92 | + | <dd> | |
| 93 | + | <time datetime="STAMP" title="STAMP">AGO</time> | |
| 94 | + | </dd> | |
| 95 | + | </div> | |
| 96 | + | </dl> | |
| 82 | 97 | </div> | |
| 83 | 98 | <ul class="repo-commits" aria-label="Recent commits of demo"> | |
| 84 | 99 | <li> | |
| @@ -99,12 +114,12 @@ | |||
| 99 | 114 | <a href="/quiet">quiet</a> | |
| 100 | 115 | </h2> | |
| 101 | 116 | <p class="repo-desc mute">no description</p> | |
| 102 | - | <p class="repo-meta"> | |
| 103 | - | <span class="repo-branch"> | |
| 104 | - | <span class="ico i-branch" aria-hidden="true"> | |
| 105 | - | </span>main</span> | |
| 106 | - | <span>never pushed</span> | |
| 107 | - | </p> | |
| 117 | + | <dl class="repo-stats" aria-label="Figures of quiet"> | |
| 118 | + | <div> | |
| 119 | + | <dt>last activity</dt> | |
| 120 | + | <dd class="mute">never</dd> | |
| 121 | + | </div> | |
| 122 | + | </dl> | |
| 108 | 123 | </div> | |
| 109 | 124 | <p class="repo-commits mute">no commits yet</p> | |
| 110 | 125 | </li> | |
| @@ -72,13 +72,28 @@ | |||||
| 72 | <a href="/demo">demo</a> | 72 | <a href="/demo">demo</a> | ||
| 73 | </h2> | 73 | </h2> | ||
| 74 | <p class="repo-desc">A demo repository</p> | 74 | <p class="repo-desc">A demo repository</p> | ||
| 75 | - | <p class="repo-meta"> | 75 | + | <dl class="repo-stats" aria-label="Figures of demo"> |
| 76 | - | <span class="repo-branch"> | 76 | + | <div> |
| 77 | - | <span class="ico i-branch" aria-hidden="true"> | 77 | + | <dt>commits</dt> |
| 78 | - | </span>main</span> | 78 | + | <dd>2</dd> |
| 79 | - | <span>pushed <time datetime="STAMP" title="STAMP">AGO</time> | 79 | + | </div> |
| 80 | - | </span> | 80 | + | <div> |
| 81 | - | </p> | 81 | + | <dt>last 30 days</dt> |
| 82 | + | <dd>0</dd> | |||
| 83 | + | </div> | |||
| 84 | + | <div> | |||
| 85 | + | <dt>latest tag</dt> | |||
| 86 | + | <dd> | |||
| 87 | + | <a href="/demo/tree/SHA40" title="made AGO">v0.2.0</a> | |||
| 88 | + | </dd> | |||
| 89 | + | </div> | |||
| 90 | + | <div> | |||
| 91 | + | <dt>last activity</dt> | |||
| 92 | + | <dd> | |||
| 93 | + | <time datetime="STAMP" title="STAMP">AGO</time> | |||
| 94 | + | </dd> | |||
| 95 | + | </div> | |||
| 96 | + | </dl> | |||
| 82 | </div> | 97 | </div> | ||
| 83 | <ul class="repo-commits" aria-label="Recent commits of demo"> | 98 | <ul class="repo-commits" aria-label="Recent commits of demo"> | ||
| 84 | <li> | 99 | <li> | ||
| @@ -99,12 +114,12 @@ | |||||
| 99 | <a href="/quiet">quiet</a> | 114 | <a href="/quiet">quiet</a> | ||
| 100 | </h2> | 115 | </h2> | ||
| 101 | <p class="repo-desc mute">no description</p> | 116 | <p class="repo-desc mute">no description</p> | ||
| 102 | - | <p class="repo-meta"> | 117 | + | <dl class="repo-stats" aria-label="Figures of quiet"> |
| 103 | - | <span class="repo-branch"> | 118 | + | <div> |
| 104 | - | <span class="ico i-branch" aria-hidden="true"> | 119 | + | <dt>last activity</dt> |
| 105 | - | </span>main</span> | 120 | + | <dd class="mute">never</dd> |
| 106 | - | <span>never pushed</span> | 121 | + | </div> |
| 107 | - | </p> | 122 | + | </dl> |
| 108 | </div> | 123 | </div> | ||
| 109 | <p class="repo-commits mute">no commits yet</p> | 124 | <p class="repo-commits mute">no commits yet</p> | ||
| 110 | </li> | 125 | </li> | ||
M internal/app/testdata/web/refs.html +0 -10
| @@ -100,8 +100,6 @@ | |||
| 100 | 100 | <table class="list refs" aria-label="Branches and tags"> | |
| 101 | 101 | <thead> | |
| 102 | 102 | <tr> | |
| 103 | - | <th class="cur"> | |
| 104 | - | </th> | |
| 105 | 103 | <th class="c-kind">kind</th> | |
| 106 | 104 | <th class="c-name">name</th> | |
| 107 | 105 | <th class="c-sha">commit</th> | |
| @@ -111,8 +109,6 @@ | |||
| 111 | 109 | </thead> | |
| 112 | 110 | <tbody id="ref-rows" data-rows> | |
| 113 | 111 | <tr data-cursor> | |
| 114 | - | <td class="cur" aria-hidden="true"> | |
| 115 | - | </td> | |
| 116 | 112 | <td class="c-kind mute">branch</td> | |
| 117 | 113 | <td class="c-name"> | |
| 118 | 114 | <a href="/demo/tree/main">main</a> | |
| @@ -127,8 +123,6 @@ | |||
| 127 | 123 | </td> | |
| 128 | 124 | </tr> | |
| 129 | 125 | <tr> | |
| 130 | - | <td class="cur" aria-hidden="true"> | |
| 131 | - | </td> | |
| 132 | 126 | <td class="c-kind mute">branch</td> | |
| 133 | 127 | <td class="c-name"> | |
| 134 | 128 | <a href="/demo/tree/feature/x">feature/x</a> | |
| @@ -142,8 +136,6 @@ | |||
| 142 | 136 | </td> | |
| 143 | 137 | </tr> | |
| 144 | 138 | <tr> | |
| 145 | - | <td class="cur" aria-hidden="true"> | |
| 146 | - | </td> | |
| 147 | 139 | <td class="c-kind mute">tag</td> | |
| 148 | 140 | <td class="c-name"> | |
| 149 | 141 | <a href="/demo/tree/v0.2.0">v0.2.0</a> | |
| @@ -157,8 +149,6 @@ | |||
| 157 | 149 | </td> | |
| 158 | 150 | </tr> | |
| 159 | 151 | <tr> | |
| 160 | - | <td class="cur" aria-hidden="true"> | |
| 161 | - | </td> | |
| 162 | 152 | <td class="c-kind mute">tag</td> | |
| 163 | 153 | <td class="c-name"> | |
| 164 | 154 | <a href="/demo/tree/v0.1.0">v0.1.0</a> | |
| @@ -100,8 +100,6 @@ | |||||
| 100 | <table class="list refs" aria-label="Branches and tags"> | 100 | <table class="list refs" aria-label="Branches and tags"> | ||
| 101 | <thead> | 101 | <thead> | ||
| 102 | <tr> | 102 | <tr> | ||
| 103 | - | <th class="cur"> | |||
| 104 | - | </th> | |||
| 105 | <th class="c-kind">kind</th> | 103 | <th class="c-kind">kind</th> | ||
| 106 | <th class="c-name">name</th> | 104 | <th class="c-name">name</th> | ||
| 107 | <th class="c-sha">commit</th> | 105 | <th class="c-sha">commit</th> | ||
| @@ -111,8 +109,6 @@ | |||||
| 111 | </thead> | 109 | </thead> | ||
| 112 | <tbody id="ref-rows" data-rows> | 110 | <tbody id="ref-rows" data-rows> | ||
| 113 | <tr data-cursor> | 111 | <tr data-cursor> | ||
| 114 | - | <td class="cur" aria-hidden="true"> | |||
| 115 | - | </td> | |||
| 116 | <td class="c-kind mute">branch</td> | 112 | <td class="c-kind mute">branch</td> | ||
| 117 | <td class="c-name"> | 113 | <td class="c-name"> | ||
| 118 | <a href="/demo/tree/main">main</a> | 114 | <a href="/demo/tree/main">main</a> | ||
| @@ -127,8 +123,6 @@ | |||||
| 127 | </td> | 123 | </td> | ||
| 128 | </tr> | 124 | </tr> | ||
| 129 | <tr> | 125 | <tr> | ||
| 130 | - | <td class="cur" aria-hidden="true"> | |||
| 131 | - | </td> | |||
| 132 | <td class="c-kind mute">branch</td> | 126 | <td class="c-kind mute">branch</td> | ||
| 133 | <td class="c-name"> | 127 | <td class="c-name"> | ||
| 134 | <a href="/demo/tree/feature/x">feature/x</a> | 128 | <a href="/demo/tree/feature/x">feature/x</a> | ||
| @@ -142,8 +136,6 @@ | |||||
| 142 | </td> | 136 | </td> | ||
| 143 | </tr> | 137 | </tr> | ||
| 144 | <tr> | 138 | <tr> | ||
| 145 | - | <td class="cur" aria-hidden="true"> | |||
| 146 | - | </td> | |||
| 147 | <td class="c-kind mute">tag</td> | 139 | <td class="c-kind mute">tag</td> | ||
| 148 | <td class="c-name"> | 140 | <td class="c-name"> | ||
| 149 | <a href="/demo/tree/v0.2.0">v0.2.0</a> | 141 | <a href="/demo/tree/v0.2.0">v0.2.0</a> | ||
| @@ -157,8 +149,6 @@ | |||||
| 157 | </td> | 149 | </td> | ||
| 158 | </tr> | 150 | </tr> | ||
| 159 | <tr> | 151 | <tr> | ||
| 160 | - | <td class="cur" aria-hidden="true"> | |||
| 161 | - | </td> | |||
| 162 | <td class="c-kind mute">tag</td> | 152 | <td class="c-kind mute">tag</td> | ||
| 163 | <td class="c-name"> | 153 | <td class="c-name"> | ||
| 164 | <a href="/demo/tree/v0.1.0">v0.1.0</a> | 154 | <a href="/demo/tree/v0.1.0">v0.1.0</a> | ||
M internal/app/testdata/web/summary.html +0 -4
| @@ -159,8 +159,6 @@ | |||
| 159 | 159 | <table class="list commits" aria-label="Commits"> | |
| 160 | 160 | <tbody data-rows> | |
| 161 | 161 | <tr data-cursor> | |
| 162 | - | <td class="cur" aria-hidden="true"> | |
| 163 | - | </td> | |
| 164 | 162 | <td class="c-sha mute">SHA7</td> | |
| 165 | 163 | <td class="c-subject"> | |
| 166 | 164 | <a href="/demo/commit/SHA40">add main.go</a> | |
| @@ -176,8 +174,6 @@ | |||
| 176 | 174 | </td> | |
| 177 | 175 | </tr> | |
| 178 | 176 | <tr> | |
| 179 | - | <td class="cur" aria-hidden="true"> | |
| 180 | - | </td> | |
| 181 | 177 | <td class="c-sha mute">SHA7</td> | |
| 182 | 178 | <td class="c-subject"> | |
| 183 | 179 | <a href="/demo/commit/SHA40">first commit</a> | |
| @@ -159,8 +159,6 @@ | |||||
| 159 | <table class="list commits" aria-label="Commits"> | 159 | <table class="list commits" aria-label="Commits"> | ||
| 160 | <tbody data-rows> | 160 | <tbody data-rows> | ||
| 161 | <tr data-cursor> | 161 | <tr data-cursor> | ||
| 162 | - | <td class="cur" aria-hidden="true"> | |||
| 163 | - | </td> | |||
| 164 | <td class="c-sha mute">SHA7</td> | 162 | <td class="c-sha mute">SHA7</td> | ||
| 165 | <td class="c-subject"> | 163 | <td class="c-subject"> | ||
| 166 | <a href="/demo/commit/SHA40">add main.go</a> | 164 | <a href="/demo/commit/SHA40">add main.go</a> | ||
| @@ -176,8 +174,6 @@ | |||||
| 176 | </td> | 174 | </td> | ||
| 177 | </tr> | 175 | </tr> | ||
| 178 | <tr> | 176 | <tr> | ||
| 179 | - | <td class="cur" aria-hidden="true"> | |||
| 180 | - | </td> | |||
| 181 | <td class="c-sha mute">SHA7</td> | 177 | <td class="c-sha mute">SHA7</td> | ||
| 182 | <td class="c-subject"> | 178 | <td class="c-subject"> | ||
| 183 | <a href="/demo/commit/SHA40">first commit</a> | 179 | <a href="/demo/commit/SHA40">first commit</a> | ||
M internal/domain/git.go +10 -0
| @@ -253,6 +253,16 @@ type Ref struct { | |||
| 253 | 253 | CommitID ObjectID | |
| 254 | 254 | } | |
| 255 | 255 | ||
| 256 | + | // TagInfo is a tag and when it was made: the tagger date of an annotated | |
| 257 | + | // tag, the committer date of the commit of a lightweight one. | |
| 258 | + | type TagInfo struct { | |
| 259 | + | Name string // short name, without refs/tags/ | |
| 260 | + | At time.Time | |
| 261 | + | // CommitID is the commit the tag leads to; zero for a tag of a tree or | |
| 262 | + | // a blob. | |
| 263 | + | CommitID ObjectID | |
| 264 | + | } | |
| 265 | + | ||
| 256 | 266 | // ResolvedRevision is the result of resolving a Revision. | |
| 257 | 267 | type ResolvedRevision struct { | |
| 258 | 268 | // CommitID is the commit the revision designates, tags followed. | |
| @@ -253,6 +253,16 @@ type Ref struct { | |||||
| 253 | CommitID ObjectID | 253 | CommitID ObjectID | ||
| 254 | } | 254 | } | ||
| 255 | 255 | ||||
| 256 | + | // TagInfo is a tag and when it was made: the tagger date of an annotated | |||
| 257 | + | // tag, the committer date of the commit of a lightweight one. | |||
| 258 | + | type TagInfo struct { | |||
| 259 | + | Name string // short name, without refs/tags/ | |||
| 260 | + | At time.Time | |||
| 261 | + | // CommitID is the commit the tag leads to; zero for a tag of a tree or | |||
| 262 | + | // a blob. | |||
| 263 | + | CommitID ObjectID | |||
| 264 | + | } | |||
| 265 | + | ||||
| 256 | // ResolvedRevision is the result of resolving a Revision. | 266 | // ResolvedRevision is the result of resolving a Revision. | ||
| 257 | type ResolvedRevision struct { | 267 | type ResolvedRevision struct { | ||
| 258 | // CommitID is the commit the revision designates, tags followed. | 268 | // CommitID is the commit the revision designates, tags followed. | ||
M internal/ports/dto.go +12 -0
| @@ -114,6 +114,18 @@ type HistoryPage struct { | |||
| 114 | 114 | NextPageToken string | |
| 115 | 115 | } | |
| 116 | 116 | ||
| 117 | + | // RepoStats are the figures of a repository the home page shows. | |
| 118 | + | type RepoStats struct { | |
| 119 | + | // Commits is the number of commits of the default branch; Recent of them | |
| 120 | + | // were committed in the last RecentDays days. | |
| 121 | + | Commits, Recent int | |
| 122 | + | // LatestTag is the most recently created tag; nil without tags. | |
| 123 | + | LatestTag *domain.TagInfo | |
| 124 | + | } | |
| 125 | + | ||
| 126 | + | // RecentDays is the window of RepoStats.Recent. | |
| 127 | + | const RecentDays = 30 | |
| 128 | + | ||
| 117 | 129 | // WatchParams select the events of a watch. | |
| 118 | 130 | type WatchParams struct { | |
| 119 | 131 | // Topics are TopicHome or "repo:<name>"; empty means TopicHome. | |
| @@ -114,6 +114,18 @@ type HistoryPage struct { | |||||
| 114 | NextPageToken string | 114 | NextPageToken string | ||
| 115 | } | 115 | } | ||
| 116 | 116 | ||||
| 117 | + | // RepoStats are the figures of a repository the home page shows. | |||
| 118 | + | type RepoStats struct { | |||
| 119 | + | // Commits is the number of commits of the default branch; Recent of them | |||
| 120 | + | // were committed in the last RecentDays days. | |||
| 121 | + | Commits, Recent int | |||
| 122 | + | // LatestTag is the most recently created tag; nil without tags. | |||
| 123 | + | LatestTag *domain.TagInfo | |||
| 124 | + | } | |||
| 125 | + | ||||
| 126 | + | // RecentDays is the window of RepoStats.Recent. | |||
| 127 | + | const RecentDays = 30 | |||
| 128 | + | ||||
| 117 | // WatchParams select the events of a watch. | 129 | // WatchParams select the events of a watch. | ||
| 118 | type WatchParams struct { | 130 | type WatchParams struct { | ||
| 119 | // Topics are TopicHome or "repo:<name>"; empty means TopicHome. | 131 | // Topics are TopicHome or "repo:<name>"; empty means TopicHome. | ||
M internal/ports/mocks/BrowseService.go +85 -0
| @@ -7,6 +7,7 @@ package mocks | |||
| 7 | 7 | import ( | |
| 8 | 8 | "context" | |
| 9 | 9 | "io" | |
| 10 | + | "time" | |
| 10 | 11 | ||
| 11 | 12 | "bnema.dev/ermite/internal/domain" | |
| 12 | 13 | "bnema.dev/ermite/internal/ports" | |
| @@ -543,6 +544,90 @@ func (_c *BrowseService_OpenBlob_Call) RunAndReturn(run func(ctx context.Context | |||
| 543 | 544 | return _c | |
| 544 | 545 | } | |
| 545 | 546 | ||
| 547 | + | // RepoStats provides a mock function for the type BrowseService | |
| 548 | + | func (_mock *BrowseService) RepoStats(ctx context.Context, p domain.Principal, repo domain.RepoName, head domain.ObjectID, now time.Time) (ports.RepoStats, error) { | |
| 549 | + | ret := _mock.Called(ctx, p, repo, head, now) | |
| 550 | + | ||
| 551 | + | if len(ret) == 0 { | |
| 552 | + | panic("no return value specified for RepoStats") | |
| 553 | + | } | |
| 554 | + | ||
| 555 | + | var r0 ports.RepoStats | |
| 556 | + | var r1 error | |
| 557 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, domain.ObjectID, time.Time) (ports.RepoStats, error)); ok { | |
| 558 | + | return returnFunc(ctx, p, repo, head, now) | |
| 559 | + | } | |
| 560 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, domain.ObjectID, time.Time) ports.RepoStats); ok { | |
| 561 | + | r0 = returnFunc(ctx, p, repo, head, now) | |
| 562 | + | } else { | |
| 563 | + | r0 = ret.Get(0).(ports.RepoStats) | |
| 564 | + | } | |
| 565 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, domain.ObjectID, time.Time) error); ok { | |
| 566 | + | r1 = returnFunc(ctx, p, repo, head, now) | |
| 567 | + | } else { | |
| 568 | + | r1 = ret.Error(1) | |
| 569 | + | } | |
| 570 | + | return r0, r1 | |
| 571 | + | } | |
| 572 | + | ||
| 573 | + | // BrowseService_RepoStats_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RepoStats' | |
| 574 | + | type BrowseService_RepoStats_Call struct { | |
| 575 | + | *mock.Call | |
| 576 | + | } | |
| 577 | + | ||
| 578 | + | // RepoStats is a helper method to define mock.On call | |
| 579 | + | // - ctx context.Context | |
| 580 | + | // - p domain.Principal | |
| 581 | + | // - repo domain.RepoName | |
| 582 | + | // - head domain.ObjectID | |
| 583 | + | // - now time.Time | |
| 584 | + | func (_e *BrowseService_Expecter) RepoStats(ctx any, p any, repo any, head any, now any) *BrowseService_RepoStats_Call { | |
| 585 | + | return &BrowseService_RepoStats_Call{Call: _e.mock.On("RepoStats", ctx, p, repo, head, now)} | |
| 586 | + | } | |
| 587 | + | ||
| 588 | + | func (_c *BrowseService_RepoStats_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, head domain.ObjectID, now time.Time)) *BrowseService_RepoStats_Call { | |
| 589 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 590 | + | var arg0 context.Context | |
| 591 | + | if args[0] != nil { | |
| 592 | + | arg0 = args[0].(context.Context) | |
| 593 | + | } | |
| 594 | + | var arg1 domain.Principal | |
| 595 | + | if args[1] != nil { | |
| 596 | + | arg1 = args[1].(domain.Principal) | |
| 597 | + | } | |
| 598 | + | var arg2 domain.RepoName | |
| 599 | + | if args[2] != nil { | |
| 600 | + | arg2 = args[2].(domain.RepoName) | |
| 601 | + | } | |
| 602 | + | var arg3 domain.ObjectID | |
| 603 | + | if args[3] != nil { | |
| 604 | + | arg3 = args[3].(domain.ObjectID) | |
| 605 | + | } | |
| 606 | + | var arg4 time.Time | |
| 607 | + | if args[4] != nil { | |
| 608 | + | arg4 = args[4].(time.Time) | |
| 609 | + | } | |
| 610 | + | run( | |
| 611 | + | arg0, | |
| 612 | + | arg1, | |
| 613 | + | arg2, | |
| 614 | + | arg3, | |
| 615 | + | arg4, | |
| 616 | + | ) | |
| 617 | + | }) | |
| 618 | + | return _c | |
| 619 | + | } | |
| 620 | + | ||
| 621 | + | func (_c *BrowseService_RepoStats_Call) Return(repoStats ports.RepoStats, err error) *BrowseService_RepoStats_Call { | |
| 622 | + | _c.Call.Return(repoStats, err) | |
| 623 | + | return _c | |
| 624 | + | } | |
| 625 | + | ||
| 626 | + | func (_c *BrowseService_RepoStats_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, head domain.ObjectID, now time.Time) (ports.RepoStats, error)) *BrowseService_RepoStats_Call { | |
| 627 | + | _c.Call.Return(run) | |
| 628 | + | return _c | |
| 629 | + | } | |
| 630 | + | ||
| 546 | 631 | // ResolveRevision provides a mock function for the type BrowseService | |
| 547 | 632 | func (_mock *BrowseService) ResolveRevision(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.ResolvedRevision, error) { | |
| 548 | 633 | ret := _mock.Called(ctx, p, repo, rev) | |
| @@ -7,6 +7,7 @@ package mocks | |||||
| 7 | import ( | 7 | import ( | ||
| 8 | "context" | 8 | "context" | ||
| 9 | "io" | 9 | "io" | ||
| 10 | + | "time" | |||
| 10 | 11 | ||||
| 11 | "bnema.dev/ermite/internal/domain" | 12 | "bnema.dev/ermite/internal/domain" | ||
| 12 | "bnema.dev/ermite/internal/ports" | 13 | "bnema.dev/ermite/internal/ports" | ||
| @@ -543,6 +544,90 @@ func (_c *BrowseService_OpenBlob_Call) RunAndReturn(run func(ctx context.Context | |||||
| 543 | return _c | 544 | return _c | ||
| 544 | } | 545 | } | ||
| 545 | 546 | ||||
| 547 | + | // RepoStats provides a mock function for the type BrowseService | |||
| 548 | + | func (_mock *BrowseService) RepoStats(ctx context.Context, p domain.Principal, repo domain.RepoName, head domain.ObjectID, now time.Time) (ports.RepoStats, error) { | |||
| 549 | + | ret := _mock.Called(ctx, p, repo, head, now) | |||
| 550 | + | ||||
| 551 | + | if len(ret) == 0 { | |||
| 552 | + | panic("no return value specified for RepoStats") | |||
| 553 | + | } | |||
| 554 | + | ||||
| 555 | + | var r0 ports.RepoStats | |||
| 556 | + | var r1 error | |||
| 557 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, domain.ObjectID, time.Time) (ports.RepoStats, error)); ok { | |||
| 558 | + | return returnFunc(ctx, p, repo, head, now) | |||
| 559 | + | } | |||
| 560 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, domain.ObjectID, time.Time) ports.RepoStats); ok { | |||
| 561 | + | r0 = returnFunc(ctx, p, repo, head, now) | |||
| 562 | + | } else { | |||
| 563 | + | r0 = ret.Get(0).(ports.RepoStats) | |||
| 564 | + | } | |||
| 565 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, domain.ObjectID, time.Time) error); ok { | |||
| 566 | + | r1 = returnFunc(ctx, p, repo, head, now) | |||
| 567 | + | } else { | |||
| 568 | + | r1 = ret.Error(1) | |||
| 569 | + | } | |||
| 570 | + | return r0, r1 | |||
| 571 | + | } | |||
| 572 | + | ||||
| 573 | + | // BrowseService_RepoStats_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RepoStats' | |||
| 574 | + | type BrowseService_RepoStats_Call struct { | |||
| 575 | + | *mock.Call | |||
| 576 | + | } | |||
| 577 | + | ||||
| 578 | + | // RepoStats is a helper method to define mock.On call | |||
| 579 | + | // - ctx context.Context | |||
| 580 | + | // - p domain.Principal | |||
| 581 | + | // - repo domain.RepoName | |||
| 582 | + | // - head domain.ObjectID | |||
| 583 | + | // - now time.Time | |||
| 584 | + | func (_e *BrowseService_Expecter) RepoStats(ctx any, p any, repo any, head any, now any) *BrowseService_RepoStats_Call { | |||
| 585 | + | return &BrowseService_RepoStats_Call{Call: _e.mock.On("RepoStats", ctx, p, repo, head, now)} | |||
| 586 | + | } | |||
| 587 | + | ||||
| 588 | + | func (_c *BrowseService_RepoStats_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, head domain.ObjectID, now time.Time)) *BrowseService_RepoStats_Call { | |||
| 589 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 590 | + | var arg0 context.Context | |||
| 591 | + | if args[0] != nil { | |||
| 592 | + | arg0 = args[0].(context.Context) | |||
| 593 | + | } | |||
| 594 | + | var arg1 domain.Principal | |||
| 595 | + | if args[1] != nil { | |||
| 596 | + | arg1 = args[1].(domain.Principal) | |||
| 597 | + | } | |||
| 598 | + | var arg2 domain.RepoName | |||
| 599 | + | if args[2] != nil { | |||
| 600 | + | arg2 = args[2].(domain.RepoName) | |||
| 601 | + | } | |||
| 602 | + | var arg3 domain.ObjectID | |||
| 603 | + | if args[3] != nil { | |||
| 604 | + | arg3 = args[3].(domain.ObjectID) | |||
| 605 | + | } | |||
| 606 | + | var arg4 time.Time | |||
| 607 | + | if args[4] != nil { | |||
| 608 | + | arg4 = args[4].(time.Time) | |||
| 609 | + | } | |||
| 610 | + | run( | |||
| 611 | + | arg0, | |||
| 612 | + | arg1, | |||
| 613 | + | arg2, | |||
| 614 | + | arg3, | |||
| 615 | + | arg4, | |||
| 616 | + | ) | |||
| 617 | + | }) | |||
| 618 | + | return _c | |||
| 619 | + | } | |||
| 620 | + | ||||
| 621 | + | func (_c *BrowseService_RepoStats_Call) Return(repoStats ports.RepoStats, err error) *BrowseService_RepoStats_Call { | |||
| 622 | + | _c.Call.Return(repoStats, err) | |||
| 623 | + | return _c | |||
| 624 | + | } | |||
| 625 | + | ||||
| 626 | + | func (_c *BrowseService_RepoStats_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, head domain.ObjectID, now time.Time) (ports.RepoStats, error)) *BrowseService_RepoStats_Call { | |||
| 627 | + | _c.Call.Return(run) | |||
| 628 | + | return _c | |||
| 629 | + | } | |||
| 630 | + | ||||
| 546 | // ResolveRevision provides a mock function for the type BrowseService | 631 | // ResolveRevision provides a mock function for the type BrowseService | ||
| 547 | func (_mock *BrowseService) ResolveRevision(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.ResolvedRevision, error) { | 632 | func (_mock *BrowseService) ResolveRevision(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.ResolvedRevision, error) { | ||
| 548 | ret := _mock.Called(ctx, p, repo, rev) | 633 | ret := _mock.Called(ctx, p, repo, rev) | ||
M internal/ports/mocks/RepoReader.go +151 -0
| @@ -7,6 +7,7 @@ package mocks | |||
| 7 | 7 | import ( | |
| 8 | 8 | "context" | |
| 9 | 9 | "io" | |
| 10 | + | "time" | |
| 10 | 11 | ||
| 11 | 12 | "bnema.dev/ermite/internal/domain" | |
| 12 | 13 | "bnema.dev/ermite/internal/ports" | |
| @@ -49,6 +50,84 @@ func (_m *RepoReader) EXPECT() *RepoReader_Expecter { | |||
| 49 | 50 | return &RepoReader_Expecter{mock: &_m.Mock} | |
| 50 | 51 | } | |
| 51 | 52 | ||
| 53 | + | // CountCommits provides a mock function for the type RepoReader | |
| 54 | + | func (_mock *RepoReader) CountCommits(ctx context.Context, repo domain.RepoName, head domain.ObjectID, since time.Time) (int, error) { | |
| 55 | + | ret := _mock.Called(ctx, repo, head, since) | |
| 56 | + | ||
| 57 | + | if len(ret) == 0 { | |
| 58 | + | panic("no return value specified for CountCommits") | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | var r0 int | |
| 62 | + | var r1 error | |
| 63 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, time.Time) (int, error)); ok { | |
| 64 | + | return returnFunc(ctx, repo, head, since) | |
| 65 | + | } | |
| 66 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, time.Time) int); ok { | |
| 67 | + | r0 = returnFunc(ctx, repo, head, since) | |
| 68 | + | } else { | |
| 69 | + | r0 = ret.Get(0).(int) | |
| 70 | + | } | |
| 71 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, domain.ObjectID, time.Time) error); ok { | |
| 72 | + | r1 = returnFunc(ctx, repo, head, since) | |
| 73 | + | } else { | |
| 74 | + | r1 = ret.Error(1) | |
| 75 | + | } | |
| 76 | + | return r0, r1 | |
| 77 | + | } | |
| 78 | + | ||
| 79 | + | // RepoReader_CountCommits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CountCommits' | |
| 80 | + | type RepoReader_CountCommits_Call struct { | |
| 81 | + | *mock.Call | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | // CountCommits is a helper method to define mock.On call | |
| 85 | + | // - ctx context.Context | |
| 86 | + | // - repo domain.RepoName | |
| 87 | + | // - head domain.ObjectID | |
| 88 | + | // - since time.Time | |
| 89 | + | func (_e *RepoReader_Expecter) CountCommits(ctx any, repo any, head any, since any) *RepoReader_CountCommits_Call { | |
| 90 | + | return &RepoReader_CountCommits_Call{Call: _e.mock.On("CountCommits", ctx, repo, head, since)} | |
| 91 | + | } | |
| 92 | + | ||
| 93 | + | func (_c *RepoReader_CountCommits_Call) Run(run func(ctx context.Context, repo domain.RepoName, head domain.ObjectID, since time.Time)) *RepoReader_CountCommits_Call { | |
| 94 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 95 | + | var arg0 context.Context | |
| 96 | + | if args[0] != nil { | |
| 97 | + | arg0 = args[0].(context.Context) | |
| 98 | + | } | |
| 99 | + | var arg1 domain.RepoName | |
| 100 | + | if args[1] != nil { | |
| 101 | + | arg1 = args[1].(domain.RepoName) | |
| 102 | + | } | |
| 103 | + | var arg2 domain.ObjectID | |
| 104 | + | if args[2] != nil { | |
| 105 | + | arg2 = args[2].(domain.ObjectID) | |
| 106 | + | } | |
| 107 | + | var arg3 time.Time | |
| 108 | + | if args[3] != nil { | |
| 109 | + | arg3 = args[3].(time.Time) | |
| 110 | + | } | |
| 111 | + | run( | |
| 112 | + | arg0, | |
| 113 | + | arg1, | |
| 114 | + | arg2, | |
| 115 | + | arg3, | |
| 116 | + | ) | |
| 117 | + | }) | |
| 118 | + | return _c | |
| 119 | + | } | |
| 120 | + | ||
| 121 | + | func (_c *RepoReader_CountCommits_Call) Return(n int, err error) *RepoReader_CountCommits_Call { | |
| 122 | + | _c.Call.Return(n, err) | |
| 123 | + | return _c | |
| 124 | + | } | |
| 125 | + | ||
| 126 | + | func (_c *RepoReader_CountCommits_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, head domain.ObjectID, since time.Time) (int, error)) *RepoReader_CountCommits_Call { | |
| 127 | + | _c.Call.Return(run) | |
| 128 | + | return _c | |
| 129 | + | } | |
| 130 | + | ||
| 52 | 131 | // GetCommit provides a mock function for the type RepoReader | |
| 53 | 132 | func (_mock *RepoReader) GetCommit(ctx context.Context, repo domain.RepoName, rev domain.Revision, limits domain.DiffLimits) (domain.CommitDetail, error) { | |
| 54 | 133 | ret := _mock.Called(ctx, repo, rev, limits) | |
| @@ -211,6 +290,78 @@ func (_c *RepoReader_GetTree_Call) RunAndReturn(run func(ctx context.Context, re | |||
| 211 | 290 | return _c | |
| 212 | 291 | } | |
| 213 | 292 | ||
| 293 | + | // LatestTag provides a mock function for the type RepoReader | |
| 294 | + | func (_mock *RepoReader) LatestTag(ctx context.Context, repo domain.RepoName) (domain.TagInfo, bool, error) { | |
| 295 | + | ret := _mock.Called(ctx, repo) | |
| 296 | + | ||
| 297 | + | if len(ret) == 0 { | |
| 298 | + | panic("no return value specified for LatestTag") | |
| 299 | + | } | |
| 300 | + | ||
| 301 | + | var r0 domain.TagInfo | |
| 302 | + | var r1 bool | |
| 303 | + | var r2 error | |
| 304 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) (domain.TagInfo, bool, error)); ok { | |
| 305 | + | return returnFunc(ctx, repo) | |
| 306 | + | } | |
| 307 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) domain.TagInfo); ok { | |
| 308 | + | r0 = returnFunc(ctx, repo) | |
| 309 | + | } else { | |
| 310 | + | r0 = ret.Get(0).(domain.TagInfo) | |
| 311 | + | } | |
| 312 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName) bool); ok { | |
| 313 | + | r1 = returnFunc(ctx, repo) | |
| 314 | + | } else { | |
| 315 | + | r1 = ret.Get(1).(bool) | |
| 316 | + | } | |
| 317 | + | if returnFunc, ok := ret.Get(2).(func(context.Context, domain.RepoName) error); ok { | |
| 318 | + | r2 = returnFunc(ctx, repo) | |
| 319 | + | } else { | |
| 320 | + | r2 = ret.Error(2) | |
| 321 | + | } | |
| 322 | + | return r0, r1, r2 | |
| 323 | + | } | |
| 324 | + | ||
| 325 | + | // RepoReader_LatestTag_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestTag' | |
| 326 | + | type RepoReader_LatestTag_Call struct { | |
| 327 | + | *mock.Call | |
| 328 | + | } | |
| 329 | + | ||
| 330 | + | // LatestTag is a helper method to define mock.On call | |
| 331 | + | // - ctx context.Context | |
| 332 | + | // - repo domain.RepoName | |
| 333 | + | func (_e *RepoReader_Expecter) LatestTag(ctx any, repo any) *RepoReader_LatestTag_Call { | |
| 334 | + | return &RepoReader_LatestTag_Call{Call: _e.mock.On("LatestTag", ctx, repo)} | |
| 335 | + | } | |
| 336 | + | ||
| 337 | + | func (_c *RepoReader_LatestTag_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *RepoReader_LatestTag_Call { | |
| 338 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 339 | + | var arg0 context.Context | |
| 340 | + | if args[0] != nil { | |
| 341 | + | arg0 = args[0].(context.Context) | |
| 342 | + | } | |
| 343 | + | var arg1 domain.RepoName | |
| 344 | + | if args[1] != nil { | |
| 345 | + | arg1 = args[1].(domain.RepoName) | |
| 346 | + | } | |
| 347 | + | run( | |
| 348 | + | arg0, | |
| 349 | + | arg1, | |
| 350 | + | ) | |
| 351 | + | }) | |
| 352 | + | return _c | |
| 353 | + | } | |
| 354 | + | ||
| 355 | + | func (_c *RepoReader_LatestTag_Call) Return(tag domain.TagInfo, ok bool, err error) *RepoReader_LatestTag_Call { | |
| 356 | + | _c.Call.Return(tag, ok, err) | |
| 357 | + | return _c | |
| 358 | + | } | |
| 359 | + | ||
| 360 | + | func (_c *RepoReader_LatestTag_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) (domain.TagInfo, bool, error)) *RepoReader_LatestTag_Call { | |
| 361 | + | _c.Call.Return(run) | |
| 362 | + | return _c | |
| 363 | + | } | |
| 364 | + | ||
| 214 | 365 | // ListCommits provides a mock function for the type RepoReader | |
| 215 | 366 | func (_mock *RepoReader) ListCommits(ctx context.Context, repo domain.RepoName, q ports.CommitQuery) (ports.CommitPage, error) { | |
| 216 | 367 | ret := _mock.Called(ctx, repo, q) | |
| @@ -7,6 +7,7 @@ package mocks | |||||
| 7 | import ( | 7 | import ( | ||
| 8 | "context" | 8 | "context" | ||
| 9 | "io" | 9 | "io" | ||
| 10 | + | "time" | |||
| 10 | 11 | ||||
| 11 | "bnema.dev/ermite/internal/domain" | 12 | "bnema.dev/ermite/internal/domain" | ||
| 12 | "bnema.dev/ermite/internal/ports" | 13 | "bnema.dev/ermite/internal/ports" | ||
| @@ -49,6 +50,84 @@ func (_m *RepoReader) EXPECT() *RepoReader_Expecter { | |||||
| 49 | return &RepoReader_Expecter{mock: &_m.Mock} | 50 | return &RepoReader_Expecter{mock: &_m.Mock} | ||
| 50 | } | 51 | } | ||
| 51 | 52 | ||||
| 53 | + | // CountCommits provides a mock function for the type RepoReader | |||
| 54 | + | func (_mock *RepoReader) CountCommits(ctx context.Context, repo domain.RepoName, head domain.ObjectID, since time.Time) (int, error) { | |||
| 55 | + | ret := _mock.Called(ctx, repo, head, since) | |||
| 56 | + | ||||
| 57 | + | if len(ret) == 0 { | |||
| 58 | + | panic("no return value specified for CountCommits") | |||
| 59 | + | } | |||
| 60 | + | ||||
| 61 | + | var r0 int | |||
| 62 | + | var r1 error | |||
| 63 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, time.Time) (int, error)); ok { | |||
| 64 | + | return returnFunc(ctx, repo, head, since) | |||
| 65 | + | } | |||
| 66 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, time.Time) int); ok { | |||
| 67 | + | r0 = returnFunc(ctx, repo, head, since) | |||
| 68 | + | } else { | |||
| 69 | + | r0 = ret.Get(0).(int) | |||
| 70 | + | } | |||
| 71 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, domain.ObjectID, time.Time) error); ok { | |||
| 72 | + | r1 = returnFunc(ctx, repo, head, since) | |||
| 73 | + | } else { | |||
| 74 | + | r1 = ret.Error(1) | |||
| 75 | + | } | |||
| 76 | + | return r0, r1 | |||
| 77 | + | } | |||
| 78 | + | ||||
| 79 | + | // RepoReader_CountCommits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CountCommits' | |||
| 80 | + | type RepoReader_CountCommits_Call struct { | |||
| 81 | + | *mock.Call | |||
| 82 | + | } | |||
| 83 | + | ||||
| 84 | + | // CountCommits is a helper method to define mock.On call | |||
| 85 | + | // - ctx context.Context | |||
| 86 | + | // - repo domain.RepoName | |||
| 87 | + | // - head domain.ObjectID | |||
| 88 | + | // - since time.Time | |||
| 89 | + | func (_e *RepoReader_Expecter) CountCommits(ctx any, repo any, head any, since any) *RepoReader_CountCommits_Call { | |||
| 90 | + | return &RepoReader_CountCommits_Call{Call: _e.mock.On("CountCommits", ctx, repo, head, since)} | |||
| 91 | + | } | |||
| 92 | + | ||||
| 93 | + | func (_c *RepoReader_CountCommits_Call) Run(run func(ctx context.Context, repo domain.RepoName, head domain.ObjectID, since time.Time)) *RepoReader_CountCommits_Call { | |||
| 94 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 95 | + | var arg0 context.Context | |||
| 96 | + | if args[0] != nil { | |||
| 97 | + | arg0 = args[0].(context.Context) | |||
| 98 | + | } | |||
| 99 | + | var arg1 domain.RepoName | |||
| 100 | + | if args[1] != nil { | |||
| 101 | + | arg1 = args[1].(domain.RepoName) | |||
| 102 | + | } | |||
| 103 | + | var arg2 domain.ObjectID | |||
| 104 | + | if args[2] != nil { | |||
| 105 | + | arg2 = args[2].(domain.ObjectID) | |||
| 106 | + | } | |||
| 107 | + | var arg3 time.Time | |||
| 108 | + | if args[3] != nil { | |||
| 109 | + | arg3 = args[3].(time.Time) | |||
| 110 | + | } | |||
| 111 | + | run( | |||
| 112 | + | arg0, | |||
| 113 | + | arg1, | |||
| 114 | + | arg2, | |||
| 115 | + | arg3, | |||
| 116 | + | ) | |||
| 117 | + | }) | |||
| 118 | + | return _c | |||
| 119 | + | } | |||
| 120 | + | ||||
| 121 | + | func (_c *RepoReader_CountCommits_Call) Return(n int, err error) *RepoReader_CountCommits_Call { | |||
| 122 | + | _c.Call.Return(n, err) | |||
| 123 | + | return _c | |||
| 124 | + | } | |||
| 125 | + | ||||
| 126 | + | func (_c *RepoReader_CountCommits_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, head domain.ObjectID, since time.Time) (int, error)) *RepoReader_CountCommits_Call { | |||
| 127 | + | _c.Call.Return(run) | |||
| 128 | + | return _c | |||
| 129 | + | } | |||
| 130 | + | ||||
| 52 | // GetCommit provides a mock function for the type RepoReader | 131 | // GetCommit provides a mock function for the type RepoReader | ||
| 53 | func (_mock *RepoReader) GetCommit(ctx context.Context, repo domain.RepoName, rev domain.Revision, limits domain.DiffLimits) (domain.CommitDetail, error) { | 132 | func (_mock *RepoReader) GetCommit(ctx context.Context, repo domain.RepoName, rev domain.Revision, limits domain.DiffLimits) (domain.CommitDetail, error) { | ||
| 54 | ret := _mock.Called(ctx, repo, rev, limits) | 133 | ret := _mock.Called(ctx, repo, rev, limits) | ||
| @@ -211,6 +290,78 @@ func (_c *RepoReader_GetTree_Call) RunAndReturn(run func(ctx context.Context, re | |||||
| 211 | return _c | 290 | return _c | ||
| 212 | } | 291 | } | ||
| 213 | 292 | ||||
| 293 | + | // LatestTag provides a mock function for the type RepoReader | |||
| 294 | + | func (_mock *RepoReader) LatestTag(ctx context.Context, repo domain.RepoName) (domain.TagInfo, bool, error) { | |||
| 295 | + | ret := _mock.Called(ctx, repo) | |||
| 296 | + | ||||
| 297 | + | if len(ret) == 0 { | |||
| 298 | + | panic("no return value specified for LatestTag") | |||
| 299 | + | } | |||
| 300 | + | ||||
| 301 | + | var r0 domain.TagInfo | |||
| 302 | + | var r1 bool | |||
| 303 | + | var r2 error | |||
| 304 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) (domain.TagInfo, bool, error)); ok { | |||
| 305 | + | return returnFunc(ctx, repo) | |||
| 306 | + | } | |||
| 307 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) domain.TagInfo); ok { | |||
| 308 | + | r0 = returnFunc(ctx, repo) | |||
| 309 | + | } else { | |||
| 310 | + | r0 = ret.Get(0).(domain.TagInfo) | |||
| 311 | + | } | |||
| 312 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName) bool); ok { | |||
| 313 | + | r1 = returnFunc(ctx, repo) | |||
| 314 | + | } else { | |||
| 315 | + | r1 = ret.Get(1).(bool) | |||
| 316 | + | } | |||
| 317 | + | if returnFunc, ok := ret.Get(2).(func(context.Context, domain.RepoName) error); ok { | |||
| 318 | + | r2 = returnFunc(ctx, repo) | |||
| 319 | + | } else { | |||
| 320 | + | r2 = ret.Error(2) | |||
| 321 | + | } | |||
| 322 | + | return r0, r1, r2 | |||
| 323 | + | } | |||
| 324 | + | ||||
| 325 | + | // RepoReader_LatestTag_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestTag' | |||
| 326 | + | type RepoReader_LatestTag_Call struct { | |||
| 327 | + | *mock.Call | |||
| 328 | + | } | |||
| 329 | + | ||||
| 330 | + | // LatestTag is a helper method to define mock.On call | |||
| 331 | + | // - ctx context.Context | |||
| 332 | + | // - repo domain.RepoName | |||
| 333 | + | func (_e *RepoReader_Expecter) LatestTag(ctx any, repo any) *RepoReader_LatestTag_Call { | |||
| 334 | + | return &RepoReader_LatestTag_Call{Call: _e.mock.On("LatestTag", ctx, repo)} | |||
| 335 | + | } | |||
| 336 | + | ||||
| 337 | + | func (_c *RepoReader_LatestTag_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *RepoReader_LatestTag_Call { | |||
| 338 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 339 | + | var arg0 context.Context | |||
| 340 | + | if args[0] != nil { | |||
| 341 | + | arg0 = args[0].(context.Context) | |||
| 342 | + | } | |||
| 343 | + | var arg1 domain.RepoName | |||
| 344 | + | if args[1] != nil { | |||
| 345 | + | arg1 = args[1].(domain.RepoName) | |||
| 346 | + | } | |||
| 347 | + | run( | |||
| 348 | + | arg0, | |||
| 349 | + | arg1, | |||
| 350 | + | ) | |||
| 351 | + | }) | |||
| 352 | + | return _c | |||
| 353 | + | } | |||
| 354 | + | ||||
| 355 | + | func (_c *RepoReader_LatestTag_Call) Return(tag domain.TagInfo, ok bool, err error) *RepoReader_LatestTag_Call { | |||
| 356 | + | _c.Call.Return(tag, ok, err) | |||
| 357 | + | return _c | |||
| 358 | + | } | |||
| 359 | + | ||||
| 360 | + | func (_c *RepoReader_LatestTag_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) (domain.TagInfo, bool, error)) *RepoReader_LatestTag_Call { | |||
| 361 | + | _c.Call.Return(run) | |||
| 362 | + | return _c | |||
| 363 | + | } | |||
| 364 | + | ||||
| 214 | // ListCommits provides a mock function for the type RepoReader | 365 | // ListCommits provides a mock function for the type RepoReader | ||
| 215 | func (_mock *RepoReader) ListCommits(ctx context.Context, repo domain.RepoName, q ports.CommitQuery) (ports.CommitPage, error) { | 366 | func (_mock *RepoReader) ListCommits(ctx context.Context, repo domain.RepoName, q ports.CommitQuery) (ports.CommitPage, error) { | ||
| 216 | ret := _mock.Called(ctx, repo, q) | 367 | ret := _mock.Called(ctx, repo, q) | ||
M internal/ports/ports.go +6 -0
| @@ -178,6 +178,12 @@ type RepoReader interface { | |||
| 178 | 178 | // GetCommit returns a commit with its statistics and its diff against the | |
| 179 | 179 | // first parent, bounded by limits. | |
| 180 | 180 | GetCommit(ctx context.Context, repo domain.RepoName, rev domain.Revision, limits domain.DiffLimits) (domain.CommitDetail, error) | |
| 181 | + | // CountCommits counts the commits reachable from head; with a non-zero | |
| 182 | + | // since, only those committed at or after it. | |
| 183 | + | CountCommits(ctx context.Context, repo domain.RepoName, head domain.ObjectID, since time.Time) (int, error) | |
| 184 | + | // LatestTag is the most recently created tag (the commit date for a | |
| 185 | + | // lightweight tag); ok is false when the repository has no tag. | |
| 186 | + | LatestTag(ctx context.Context, repo domain.RepoName) (tag domain.TagInfo, ok bool, err error) | |
| 181 | 187 | } | |
| 182 | 188 | ||
| 183 | 189 | // HTTPBackend tells how to run `git http-backend` for one request. | |
| @@ -178,6 +178,12 @@ type RepoReader interface { | |||||
| 178 | // GetCommit returns a commit with its statistics and its diff against the | 178 | // GetCommit returns a commit with its statistics and its diff against the | ||
| 179 | // first parent, bounded by limits. | 179 | // first parent, bounded by limits. | ||
| 180 | GetCommit(ctx context.Context, repo domain.RepoName, rev domain.Revision, limits domain.DiffLimits) (domain.CommitDetail, error) | 180 | GetCommit(ctx context.Context, repo domain.RepoName, rev domain.Revision, limits domain.DiffLimits) (domain.CommitDetail, error) | ||
| 181 | + | // CountCommits counts the commits reachable from head; with a non-zero | |||
| 182 | + | // since, only those committed at or after it. | |||
| 183 | + | CountCommits(ctx context.Context, repo domain.RepoName, head domain.ObjectID, since time.Time) (int, error) | |||
| 184 | + | // LatestTag is the most recently created tag (the commit date for a | |||
| 185 | + | // lightweight tag); ok is false when the repository has no tag. | |||
| 186 | + | LatestTag(ctx context.Context, repo domain.RepoName) (tag domain.TagInfo, ok bool, err error) | |||
| 181 | } | 187 | } | ||
| 182 | 188 | ||||
| 183 | // HTTPBackend tells how to run `git http-backend` for one request. | 189 | // HTTPBackend tells how to run `git http-backend` for one request. | ||
M internal/ports/services.go +3 -0
| @@ -96,6 +96,9 @@ type BrowseService interface { | |||
| 96 | 96 | OpenBlob(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath) (domain.BlobInfo, io.ReadCloser, error) | |
| 97 | 97 | ListCommits(ctx context.Context, p domain.Principal, repo domain.RepoName, params ListCommitsParams) (HistoryPage, error) | |
| 98 | 98 | GetCommit(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.CommitDetail, error) | |
| 99 | + | // RepoStats counts the commits of the branch whose head is given, and | |
| 100 | + | // names the latest tag. | |
| 101 | + | RepoStats(ctx context.Context, p domain.Principal, repo domain.RepoName, head domain.ObjectID, now time.Time) (RepoStats, error) | |
| 99 | 102 | } | |
| 100 | 103 | ||
| 101 | 104 | // EventService streams the events of the forge. *usecase.Events implements it. | |
| @@ -96,6 +96,9 @@ type BrowseService interface { | |||||
| 96 | OpenBlob(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath) (domain.BlobInfo, io.ReadCloser, error) | 96 | OpenBlob(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath) (domain.BlobInfo, io.ReadCloser, error) | ||
| 97 | ListCommits(ctx context.Context, p domain.Principal, repo domain.RepoName, params ListCommitsParams) (HistoryPage, error) | 97 | ListCommits(ctx context.Context, p domain.Principal, repo domain.RepoName, params ListCommitsParams) (HistoryPage, error) | ||
| 98 | GetCommit(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.CommitDetail, error) | 98 | GetCommit(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.CommitDetail, error) | ||
| 99 | + | // RepoStats counts the commits of the branch whose head is given, and | |||
| 100 | + | // names the latest tag. | |||
| 101 | + | RepoStats(ctx context.Context, p domain.Principal, repo domain.RepoName, head domain.ObjectID, now time.Time) (RepoStats, error) | |||
| 99 | } | 102 | } | ||
| 100 | 103 | ||||
| 101 | // EventService streams the events of the forge. *usecase.Events implements it. | 104 | // EventService streams the events of the forge. *usecase.Events implements it. | ||
M internal/readertest/conformance.go +62 -0
| @@ -49,6 +49,7 @@ func Conformance(t *testing.T, mk Maker) { | |||
| 49 | 49 | {"GetTreeIsBounded", testGetTreeIsBounded}, | |
| 50 | 50 | {"GetTreeRefusesAGitlinkToATree", testGetTreeRefusesAGitlinkToATree}, | |
| 51 | 51 | {"ListPaths", testListPaths}, | |
| 52 | + | {"CountCommitsAndLatestTag", testCountCommitsAndLatestTag}, | |
| 52 | 53 | } { | |
| 53 | 54 | t.Run(tc.name, func(t *testing.T) { tc.fn(t, mk) }) | |
| 54 | 55 | } | |
| @@ -104,6 +105,67 @@ func testListRefs(t *testing.T, mk Maker) { | |||
| 104 | 105 | }, got) | |
| 105 | 106 | } | |
| 106 | 107 | ||
| 108 | + | func testCountCommitsAndLatestTag(t *testing.T, mk Maker) { | |
| 109 | + | ctx := context.Background() | |
| 110 | + | g, data := mk(t) | |
| 111 | + | f := NewFixture(t, data, "stats") | |
| 112 | + | ||
| 113 | + | _, ok, err := g.LatestTag(ctx, f.Name) | |
| 114 | + | require.NoError(t, err) | |
| 115 | + | assert.False(t, ok, "no tag yet") | |
| 116 | + | ||
| 117 | + | var ids []string | |
| 118 | + | for i := range 4 { // committed at 12:01, 12:02, 12:03, 12:04 | |
| 119 | + | ids = append(ids, f.Commit("c"+itoa(i))) | |
| 120 | + | } | |
| 121 | + | head := MustID(t, ids[3]) | |
| 122 | + | n, err := g.CountCommits(ctx, f.Name, head, time.Time{}) | |
| 123 | + | require.NoError(t, err) | |
| 124 | + | assert.Equal(t, 4, n) | |
| 125 | + | n, err = g.CountCommits(ctx, f.Name, head, f.When.Add(-90*time.Second)) | |
| 126 | + | require.NoError(t, err) | |
| 127 | + | assert.Equal(t, 2, n, "the commits of 12:03 and 12:04") | |
| 128 | + | n, err = g.CountCommits(ctx, f.Name, MustID(t, ids[1]), time.Time{}) | |
| 129 | + | require.NoError(t, err) | |
| 130 | + | assert.Equal(t, 2, n, "from another commit") | |
| 131 | + | _, err = g.CountCommits(ctx, f.Name, MustID(t, strings.Repeat("ab", 20)), time.Time{}) | |
| 132 | + | assert.ErrorIs(t, err, domain.ErrNotFound) | |
| 133 | + | ||
| 134 | + | // Dates out of order: a commit dated in the past on top of recent ones | |
| 135 | + | // does not hide them. | |
| 136 | + | recent := f.When | |
| 137 | + | f.When = recent.Add(-48 * time.Hour) | |
| 138 | + | old := f.Commit("backdated") | |
| 139 | + | f.When = recent | |
| 140 | + | n, err = g.CountCommits(ctx, f.Name, MustID(t, old), recent.Add(-90*time.Second)) | |
| 141 | + | require.NoError(t, err) | |
| 142 | + | assert.Equal(t, 2, n, "the recent commits behind an old one count") | |
| 143 | + | ||
| 144 | + | // The latest tag is the one made last: an annotated tag by its own date, | |
| 145 | + | // a lightweight one by the date of its commit. | |
| 146 | + | f.Git("tag", "light-old", ids[0]) | |
| 147 | + | f.When = f.When.Add(time.Hour) | |
| 148 | + | f.Git("tag", "-a", "-m", "new", "v2", ids[0]) | |
| 149 | + | f.Git("tag", "light-new", ids[3]) | |
| 150 | + | tag, ok, err := g.LatestTag(ctx, f.Name) | |
| 151 | + | require.NoError(t, err) | |
| 152 | + | require.True(t, ok) | |
| 153 | + | assert.Equal(t, "v2", tag.Name) | |
| 154 | + | assert.True(t, tag.At.Equal(f.When), "%v", tag.At) | |
| 155 | + | assert.Equal(t, ids[0], tag.CommitID.String(), "an annotated tag is peeled to its commit") | |
| 156 | + | ||
| 157 | + | f.When = f.When.Add(time.Hour) | |
| 158 | + | f.Git("tag", "-a", "-m", "tree", "tree-tag", ids[0]+"^{tree}") | |
| 159 | + | tag, ok, err = g.LatestTag(ctx, f.Name) | |
| 160 | + | require.NoError(t, err) | |
| 161 | + | require.True(t, ok) | |
| 162 | + | assert.Equal(t, "tree-tag", tag.Name) | |
| 163 | + | assert.True(t, tag.CommitID.IsZero(), "a tag of a tree has no commit") | |
| 164 | + | ||
| 165 | + | _, _, err = g.LatestTag(ctx, RepoName(t, "nope")) | |
| 166 | + | assert.ErrorIs(t, err, domain.ErrNotFound) | |
| 167 | + | } | |
| 168 | + | ||
| 107 | 169 | func testListRefsEmptyAndMissing(t *testing.T, mk Maker) { | |
| 108 | 170 | ctx := context.Background() | |
| 109 | 171 | g, data := mk(t) | |
| @@ -49,6 +49,7 @@ func Conformance(t *testing.T, mk Maker) { | |||||
| 49 | {"GetTreeIsBounded", testGetTreeIsBounded}, | 49 | {"GetTreeIsBounded", testGetTreeIsBounded}, | ||
| 50 | {"GetTreeRefusesAGitlinkToATree", testGetTreeRefusesAGitlinkToATree}, | 50 | {"GetTreeRefusesAGitlinkToATree", testGetTreeRefusesAGitlinkToATree}, | ||
| 51 | {"ListPaths", testListPaths}, | 51 | {"ListPaths", testListPaths}, | ||
| 52 | + | {"CountCommitsAndLatestTag", testCountCommitsAndLatestTag}, | |||
| 52 | } { | 53 | } { | ||
| 53 | t.Run(tc.name, func(t *testing.T) { tc.fn(t, mk) }) | 54 | t.Run(tc.name, func(t *testing.T) { tc.fn(t, mk) }) | ||
| 54 | } | 55 | } | ||
| @@ -104,6 +105,67 @@ func testListRefs(t *testing.T, mk Maker) { | |||||
| 104 | }, got) | 105 | }, got) | ||
| 105 | } | 106 | } | ||
| 106 | 107 | ||||
| 108 | + | func testCountCommitsAndLatestTag(t *testing.T, mk Maker) { | |||
| 109 | + | ctx := context.Background() | |||
| 110 | + | g, data := mk(t) | |||
| 111 | + | f := NewFixture(t, data, "stats") | |||
| 112 | + | ||||
| 113 | + | _, ok, err := g.LatestTag(ctx, f.Name) | |||
| 114 | + | require.NoError(t, err) | |||
| 115 | + | assert.False(t, ok, "no tag yet") | |||
| 116 | + | ||||
| 117 | + | var ids []string | |||
| 118 | + | for i := range 4 { // committed at 12:01, 12:02, 12:03, 12:04 | |||
| 119 | + | ids = append(ids, f.Commit("c"+itoa(i))) | |||
| 120 | + | } | |||
| 121 | + | head := MustID(t, ids[3]) | |||
| 122 | + | n, err := g.CountCommits(ctx, f.Name, head, time.Time{}) | |||
| 123 | + | require.NoError(t, err) | |||
| 124 | + | assert.Equal(t, 4, n) | |||
| 125 | + | n, err = g.CountCommits(ctx, f.Name, head, f.When.Add(-90*time.Second)) | |||
| 126 | + | require.NoError(t, err) | |||
| 127 | + | assert.Equal(t, 2, n, "the commits of 12:03 and 12:04") | |||
| 128 | + | n, err = g.CountCommits(ctx, f.Name, MustID(t, ids[1]), time.Time{}) | |||
| 129 | + | require.NoError(t, err) | |||
| 130 | + | assert.Equal(t, 2, n, "from another commit") | |||
| 131 | + | _, err = g.CountCommits(ctx, f.Name, MustID(t, strings.Repeat("ab", 20)), time.Time{}) | |||
| 132 | + | assert.ErrorIs(t, err, domain.ErrNotFound) | |||
| 133 | + | ||||
| 134 | + | // Dates out of order: a commit dated in the past on top of recent ones | |||
| 135 | + | // does not hide them. | |||
| 136 | + | recent := f.When | |||
| 137 | + | f.When = recent.Add(-48 * time.Hour) | |||
| 138 | + | old := f.Commit("backdated") | |||
| 139 | + | f.When = recent | |||
| 140 | + | n, err = g.CountCommits(ctx, f.Name, MustID(t, old), recent.Add(-90*time.Second)) | |||
| 141 | + | require.NoError(t, err) | |||
| 142 | + | assert.Equal(t, 2, n, "the recent commits behind an old one count") | |||
| 143 | + | ||||
| 144 | + | // The latest tag is the one made last: an annotated tag by its own date, | |||
| 145 | + | // a lightweight one by the date of its commit. | |||
| 146 | + | f.Git("tag", "light-old", ids[0]) | |||
| 147 | + | f.When = f.When.Add(time.Hour) | |||
| 148 | + | f.Git("tag", "-a", "-m", "new", "v2", ids[0]) | |||
| 149 | + | f.Git("tag", "light-new", ids[3]) | |||
| 150 | + | tag, ok, err := g.LatestTag(ctx, f.Name) | |||
| 151 | + | require.NoError(t, err) | |||
| 152 | + | require.True(t, ok) | |||
| 153 | + | assert.Equal(t, "v2", tag.Name) | |||
| 154 | + | assert.True(t, tag.At.Equal(f.When), "%v", tag.At) | |||
| 155 | + | assert.Equal(t, ids[0], tag.CommitID.String(), "an annotated tag is peeled to its commit") | |||
| 156 | + | ||||
| 157 | + | f.When = f.When.Add(time.Hour) | |||
| 158 | + | f.Git("tag", "-a", "-m", "tree", "tree-tag", ids[0]+"^{tree}") | |||
| 159 | + | tag, ok, err = g.LatestTag(ctx, f.Name) | |||
| 160 | + | require.NoError(t, err) | |||
| 161 | + | require.True(t, ok) | |||
| 162 | + | assert.Equal(t, "tree-tag", tag.Name) | |||
| 163 | + | assert.True(t, tag.CommitID.IsZero(), "a tag of a tree has no commit") | |||
| 164 | + | ||||
| 165 | + | _, _, err = g.LatestTag(ctx, RepoName(t, "nope")) | |||
| 166 | + | assert.ErrorIs(t, err, domain.ErrNotFound) | |||
| 167 | + | } | |||
| 168 | + | ||||
| 107 | func testListRefsEmptyAndMissing(t *testing.T, mk Maker) { | 169 | func testListRefsEmptyAndMissing(t *testing.T, mk Maker) { | ||
| 108 | ctx := context.Background() | 170 | ctx := context.Background() | ||
| 109 | g, data := mk(t) | 171 | g, data := mk(t) | ||
M internal/usecase/browse.go +2 -0
| @@ -29,6 +29,8 @@ type Browse struct { | |||
| 29 | 29 | // they never change, and computing them costs a history walk. Nil | |
| 30 | 30 | // disables it. | |
| 31 | 31 | history *historyCache | |
| 32 | + | // stats keeps the figures of the home page. | |
| 33 | + | stats statsCache | |
| 32 | 34 | } | |
| 33 | 35 | ||
| 34 | 36 | var _ ports.BrowseService = (*Browse)(nil) | |
| @@ -29,6 +29,8 @@ type Browse struct { | |||||
| 29 | // they never change, and computing them costs a history walk. Nil | 29 | // they never change, and computing them costs a history walk. Nil | ||
| 30 | // disables it. | 30 | // disables it. | ||
| 31 | history *historyCache | 31 | history *historyCache | ||
| 32 | + | // stats keeps the figures of the home page. | |||
| 33 | + | stats statsCache | |||
| 32 | } | 34 | } | ||
| 33 | 35 | ||||
| 34 | var _ ports.BrowseService = (*Browse)(nil) | 36 | var _ ports.BrowseService = (*Browse)(nil) | ||
A internal/usecase/browse_stats.go +122 -0
| @@ -0,0 +1,122 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | package usecase | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "context" | |
| 7 | + | "sync" | |
| 8 | + | "time" | |
| 9 | + | ||
| 10 | + | "bnema.dev/ermite/internal/domain" | |
| 11 | + | "bnema.dev/ermite/internal/ports" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | const ( | |
| 15 | + | // maxStatsEntries bounds the cache of figures; when full it is emptied, | |
| 16 | + | // and the entries of the current heads come back on the next read. | |
| 17 | + | maxStatsEntries = 1024 | |
| 18 | + | // statsTTL is how long figures are served: the 30-day window moves | |
| 19 | + | // with time even when the repository does not. | |
| 20 | + | statsTTL = 10 * time.Minute | |
| 21 | + | ) | |
| 22 | + | ||
| 23 | + | // statsKey names the figures of one state of a repository: its life (the | |
| 24 | + | // creation time), its last push or merge (which every ref change, tags | |
| 25 | + | // included, moves) and the head counted from. | |
| 26 | + | type statsKey struct { | |
| 27 | + | repo domain.RepoName | |
| 28 | + | created int64 | |
| 29 | + | pushed int64 | |
| 30 | + | head domain.ObjectID | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | type statsEntry struct { | |
| 34 | + | st ports.RepoStats | |
| 35 | + | at time.Time | |
| 36 | + | err error | |
| 37 | + | // done is closed when st and err are set; until then the entry is the | |
| 38 | + | // read in flight that the other callers wait for. | |
| 39 | + | done chan struct{} | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | // statsCache keeps the figures of the home page, so that a request reads Git | |
| 43 | + | // once per repository state and per statsTTL, and concurrent requests share | |
| 44 | + | // one read. | |
| 45 | + | type statsCache struct { | |
| 46 | + | mu sync.Mutex | |
| 47 | + | m map[statsKey]*statsEntry | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | // get returns the figures of key, computing them with read when they are | |
| 51 | + | // missing, failed or older than statsTTL. | |
| 52 | + | func (c *statsCache) get(ctx context.Context, key statsKey, now time.Time, read func() (ports.RepoStats, error)) (ports.RepoStats, error) { | |
| 53 | + | c.mu.Lock() | |
| 54 | + | e, ok := c.m[key] | |
| 55 | + | if ok { | |
| 56 | + | select { | |
| 57 | + | case <-e.done: | |
| 58 | + | if e.err != nil || now.Sub(e.at) >= statsTTL || now.Before(e.at) { | |
| 59 | + | ok = false | |
| 60 | + | } | |
| 61 | + | default: // in flight | |
| 62 | + | } | |
| 63 | + | } | |
| 64 | + | if !ok { | |
| 65 | + | if c.m == nil || len(c.m) >= maxStatsEntries { | |
| 66 | + | c.m = make(map[statsKey]*statsEntry) | |
| 67 | + | } | |
| 68 | + | e = &statsEntry{at: now, done: make(chan struct{})} | |
| 69 | + | c.m[key] = e | |
| 70 | + | c.mu.Unlock() | |
| 71 | + | e.st, e.err = read() | |
| 72 | + | close(e.done) | |
| 73 | + | return cloneStats(e.st), e.err | |
| 74 | + | } | |
| 75 | + | c.mu.Unlock() | |
| 76 | + | select { | |
| 77 | + | case <-e.done: | |
| 78 | + | return cloneStats(e.st), e.err | |
| 79 | + | case <-ctx.Done(): | |
| 80 | + | return ports.RepoStats{}, ctx.Err() | |
| 81 | + | } | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | func cloneStats(st ports.RepoStats) ports.RepoStats { | |
| 85 | + | if st.LatestTag != nil { | |
| 86 | + | t := *st.LatestTag | |
| 87 | + | st.LatestTag = &t | |
| 88 | + | } | |
| 89 | + | return st | |
| 90 | + | } | |
| 91 | + | ||
| 92 | + | // RepoStats counts the commits reachable from head, all of them and those of | |
| 93 | + | // the last ports.RecentDays days before now, and names the latest tag. The | |
| 94 | + | // answer is kept for statsTTL, or until the repository is pushed to. | |
| 95 | + | func (b *Browse) RepoStats(ctx context.Context, p domain.Principal, repo domain.RepoName, head domain.ObjectID, now time.Time) (ports.RepoStats, error) { | |
| 96 | + | r, err := b.authorize(ctx, p, repo) | |
| 97 | + | if err != nil { | |
| 98 | + | return ports.RepoStats{}, err | |
| 99 | + | } | |
| 100 | + | key := statsKey{repo: r.Name, created: repoEpoch(r), pushed: r.PushedAt.UnixNano(), head: head} | |
| 101 | + | return b.stats.get(ctx, key, now, func() (ports.RepoStats, error) { | |
| 102 | + | // The read outlives a caller that gives up: the others wait for it. | |
| 103 | + | rctx := context.WithoutCancel(ctx) | |
| 104 | + | var st ports.RepoStats | |
| 105 | + | var err error | |
| 106 | + | if st.Commits, err = b.reader.CountCommits(rctx, repo, head, time.Time{}); err != nil { | |
| 107 | + | return ports.RepoStats{}, err | |
| 108 | + | } | |
| 109 | + | since := now.AddDate(0, 0, -ports.RecentDays) | |
| 110 | + | if st.Recent, err = b.reader.CountCommits(rctx, repo, head, since); err != nil { | |
| 111 | + | return ports.RepoStats{}, err | |
| 112 | + | } | |
| 113 | + | tag, ok, err := b.reader.LatestTag(rctx, repo) | |
| 114 | + | if err != nil { | |
| 115 | + | return ports.RepoStats{}, err | |
| 116 | + | } | |
| 117 | + | if ok { | |
| 118 | + | st.LatestTag = &tag | |
| 119 | + | } | |
| 120 | + | return st, nil | |
| 121 | + | }) | |
| 122 | + | } | |
| @@ -0,0 +1,122 @@ | |||||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |||
| 2 | + | ||||
| 3 | + | package usecase | |||
| 4 | + | ||||
| 5 | + | import ( | |||
| 6 | + | "context" | |||
| 7 | + | "sync" | |||
| 8 | + | "time" | |||
| 9 | + | ||||
| 10 | + | "bnema.dev/ermite/internal/domain" | |||
| 11 | + | "bnema.dev/ermite/internal/ports" | |||
| 12 | + | ) | |||
| 13 | + | ||||
| 14 | + | const ( | |||
| 15 | + | // maxStatsEntries bounds the cache of figures; when full it is emptied, | |||
| 16 | + | // and the entries of the current heads come back on the next read. | |||
| 17 | + | maxStatsEntries = 1024 | |||
| 18 | + | // statsTTL is how long figures are served: the 30-day window moves | |||
| 19 | + | // with time even when the repository does not. | |||
| 20 | + | statsTTL = 10 * time.Minute | |||
| 21 | + | ) | |||
| 22 | + | ||||
| 23 | + | // statsKey names the figures of one state of a repository: its life (the | |||
| 24 | + | // creation time), its last push or merge (which every ref change, tags | |||
| 25 | + | // included, moves) and the head counted from. | |||
| 26 | + | type statsKey struct { | |||
| 27 | + | repo domain.RepoName | |||
| 28 | + | created int64 | |||
| 29 | + | pushed int64 | |||
| 30 | + | head domain.ObjectID | |||
| 31 | + | } | |||
| 32 | + | ||||
| 33 | + | type statsEntry struct { | |||
| 34 | + | st ports.RepoStats | |||
| 35 | + | at time.Time | |||
| 36 | + | err error | |||
| 37 | + | // done is closed when st and err are set; until then the entry is the | |||
| 38 | + | // read in flight that the other callers wait for. | |||
| 39 | + | done chan struct{} | |||
| 40 | + | } | |||
| 41 | + | ||||
| 42 | + | // statsCache keeps the figures of the home page, so that a request reads Git | |||
| 43 | + | // once per repository state and per statsTTL, and concurrent requests share | |||
| 44 | + | // one read. | |||
| 45 | + | type statsCache struct { | |||
| 46 | + | mu sync.Mutex | |||
| 47 | + | m map[statsKey]*statsEntry | |||
| 48 | + | } | |||
| 49 | + | ||||
| 50 | + | // get returns the figures of key, computing them with read when they are | |||
| 51 | + | // missing, failed or older than statsTTL. | |||
| 52 | + | func (c *statsCache) get(ctx context.Context, key statsKey, now time.Time, read func() (ports.RepoStats, error)) (ports.RepoStats, error) { | |||
| 53 | + | c.mu.Lock() | |||
| 54 | + | e, ok := c.m[key] | |||
| 55 | + | if ok { | |||
| 56 | + | select { | |||
| 57 | + | case <-e.done: | |||
| 58 | + | if e.err != nil || now.Sub(e.at) >= statsTTL || now.Before(e.at) { | |||
| 59 | + | ok = false | |||
| 60 | + | } | |||
| 61 | + | default: // in flight | |||
| 62 | + | } | |||
| 63 | + | } | |||
| 64 | + | if !ok { | |||
| 65 | + | if c.m == nil || len(c.m) >= maxStatsEntries { | |||
| 66 | + | c.m = make(map[statsKey]*statsEntry) | |||
| 67 | + | } | |||
| 68 | + | e = &statsEntry{at: now, done: make(chan struct{})} | |||
| 69 | + | c.m[key] = e | |||
| 70 | + | c.mu.Unlock() | |||
| 71 | + | e.st, e.err = read() | |||
| 72 | + | close(e.done) | |||
| 73 | + | return cloneStats(e.st), e.err | |||
| 74 | + | } | |||
| 75 | + | c.mu.Unlock() | |||
| 76 | + | select { | |||
| 77 | + | case <-e.done: | |||
| 78 | + | return cloneStats(e.st), e.err | |||
| 79 | + | case <-ctx.Done(): | |||
| 80 | + | return ports.RepoStats{}, ctx.Err() | |||
| 81 | + | } | |||
| 82 | + | } | |||
| 83 | + | ||||
| 84 | + | func cloneStats(st ports.RepoStats) ports.RepoStats { | |||
| 85 | + | if st.LatestTag != nil { | |||
| 86 | + | t := *st.LatestTag | |||
| 87 | + | st.LatestTag = &t | |||
| 88 | + | } | |||
| 89 | + | return st | |||
| 90 | + | } | |||
| 91 | + | ||||
| 92 | + | // RepoStats counts the commits reachable from head, all of them and those of | |||
| 93 | + | // the last ports.RecentDays days before now, and names the latest tag. The | |||
| 94 | + | // answer is kept for statsTTL, or until the repository is pushed to. | |||
| 95 | + | func (b *Browse) RepoStats(ctx context.Context, p domain.Principal, repo domain.RepoName, head domain.ObjectID, now time.Time) (ports.RepoStats, error) { | |||
| 96 | + | r, err := b.authorize(ctx, p, repo) | |||
| 97 | + | if err != nil { | |||
| 98 | + | return ports.RepoStats{}, err | |||
| 99 | + | } | |||
| 100 | + | key := statsKey{repo: r.Name, created: repoEpoch(r), pushed: r.PushedAt.UnixNano(), head: head} | |||
| 101 | + | return b.stats.get(ctx, key, now, func() (ports.RepoStats, error) { | |||
| 102 | + | // The read outlives a caller that gives up: the others wait for it. | |||
| 103 | + | rctx := context.WithoutCancel(ctx) | |||
| 104 | + | var st ports.RepoStats | |||
| 105 | + | var err error | |||
| 106 | + | if st.Commits, err = b.reader.CountCommits(rctx, repo, head, time.Time{}); err != nil { | |||
| 107 | + | return ports.RepoStats{}, err | |||
| 108 | + | } | |||
| 109 | + | since := now.AddDate(0, 0, -ports.RecentDays) | |||
| 110 | + | if st.Recent, err = b.reader.CountCommits(rctx, repo, head, since); err != nil { | |||
| 111 | + | return ports.RepoStats{}, err | |||
| 112 | + | } | |||
| 113 | + | tag, ok, err := b.reader.LatestTag(rctx, repo) | |||
| 114 | + | if err != nil { | |||
| 115 | + | return ports.RepoStats{}, err | |||
| 116 | + | } | |||
| 117 | + | if ok { | |||
| 118 | + | st.LatestTag = &tag | |||
| 119 | + | } | |||
| 120 | + | return st, nil | |||
| 121 | + | }) | |||
| 122 | + | } | |||
M internal/usecase/browse_test.go +93 -0
| @@ -5,11 +5,16 @@ package usecase_test | |||
| 5 | 5 | import ( | |
| 6 | 6 | "context" | |
| 7 | 7 | "encoding/base64" | |
| 8 | + | "errors" | |
| 8 | 9 | "fmt" | |
| 9 | 10 | "io" | |
| 11 | + | "runtime" | |
| 10 | 12 | "strconv" | |
| 11 | 13 | "strings" | |
| 14 | + | "sync" | |
| 15 | + | "sync/atomic" | |
| 12 | 16 | "testing" | |
| 17 | + | "time" | |
| 13 | 18 | ||
| 14 | 19 | "github.com/stretchr/testify/assert" | |
| 15 | 20 | "github.com/stretchr/testify/mock" | |
| @@ -104,7 +109,95 @@ func browseOps() []browseOp { | |||
| 104 | 109 | }, func(r *mocks.RepoReader) { | |
| 105 | 110 | r.EXPECT().GetCommit(mock.Anything, mock.Anything, mock.Anything, domain.DefaultDiffLimits()).Return(domain.CommitDetail{}, nil) | |
| 106 | 111 | }}, | |
| 112 | + | {"RepoStats", func(b *usecase.Browse, ctx context.Context, p domain.Principal, repo domain.RepoName) error { | |
| 113 | + | _, err := b.RepoStats(ctx, p, repo, oid("a"), time.Now()) | |
| 114 | + | return err | |
| 115 | + | }, func(r *mocks.RepoReader) { | |
| 116 | + | r.EXPECT().CountCommits(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(1, nil) | |
| 117 | + | r.EXPECT().LatestTag(mock.Anything, mock.Anything).Return(domain.TagInfo{}, false, nil) | |
| 118 | + | }}, | |
| 119 | + | } | |
| 120 | + | } | |
| 121 | + | ||
| 122 | + | // RepoStats reads Git once per state of the repository (head and last push) | |
| 123 | + | // and per 10 minutes; concurrent callers share the read, and a failure is | |
| 124 | + | // not kept. | |
| 125 | + | func TestRepoStatsCache(t *testing.T) { | |
| 126 | + | t.Parallel() | |
| 127 | + | ctx := context.Background() | |
| 128 | + | f := newBrowseFixture(t) | |
| 129 | + | repo := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic, CreatedAt: time.Unix(100, 0), PushedAt: time.Unix(200, 0)} | |
| 130 | + | var mu sync.Mutex | |
| 131 | + | f.meta.EXPECT().Get(mock.Anything, repo.Name).RunAndReturn(func(context.Context, domain.RepoName) (domain.Repo, error) { | |
| 132 | + | mu.Lock() | |
| 133 | + | defer mu.Unlock() | |
| 134 | + | return repo, nil | |
| 135 | + | }) | |
| 136 | + | now := time.Date(2026, 10, 18, 0, 0, 0, 0, time.UTC) | |
| 137 | + | var reads atomic.Int32 | |
| 138 | + | gate := make(chan struct{}) | |
| 139 | + | f.reader.EXPECT().CountCommits(mock.Anything, repo.Name, mock.Anything, time.Time{}).RunAndReturn( | |
| 140 | + | func(context.Context, domain.RepoName, domain.ObjectID, time.Time) (int, error) { | |
| 141 | + | reads.Add(1) | |
| 142 | + | <-gate | |
| 143 | + | return 900, nil | |
| 144 | + | }) | |
| 145 | + | f.reader.EXPECT().CountCommits(mock.Anything, repo.Name, mock.Anything, mock.MatchedBy(func(s time.Time) bool { return !s.IsZero() })).Return(12, nil) | |
| 146 | + | tag := domain.TagInfo{Name: "v1", At: now, CommitID: oid("c")} | |
| 147 | + | f.reader.EXPECT().LatestTag(mock.Anything, repo.Name).Return(tag, true, nil) | |
| 148 | + | ||
| 149 | + | stats := func(head string, at time.Time) ports.RepoStats { | |
| 150 | + | st, err := f.browse.RepoStats(ctx, domain.Anonymous(), repo.Name, oid(head), at) | |
| 151 | + | require.NoError(t, err) | |
| 152 | + | return st | |
| 153 | + | } | |
| 154 | + | ||
| 155 | + | // Eight concurrent requests: one read. | |
| 156 | + | var wg sync.WaitGroup | |
| 157 | + | for range 8 { | |
| 158 | + | wg.Go(func() { | |
| 159 | + | st := stats("a", now) | |
| 160 | + | assert.Equal(t, 900, st.Commits) | |
| 161 | + | assert.Equal(t, 12, st.Recent) | |
| 162 | + | assert.Equal(t, tag, *st.LatestTag) | |
| 163 | + | }) | |
| 164 | + | } | |
| 165 | + | for reads.Load() == 0 { | |
| 166 | + | runtime.Gosched() | |
| 107 | 167 | } | |
| 168 | + | close(gate) | |
| 169 | + | wg.Wait() | |
| 170 | + | assert.EqualValues(t, 1, reads.Load(), "concurrent callers share the read") | |
| 171 | + | ||
| 172 | + | stats("a", now.Add(9*time.Minute)) | |
| 173 | + | assert.EqualValues(t, 1, reads.Load(), "served from the cache") | |
| 174 | + | stats("a", now.Add(10*time.Minute)) | |
| 175 | + | assert.EqualValues(t, 2, reads.Load(), "expired") | |
| 176 | + | stats("b", now.Add(10*time.Minute)) | |
| 177 | + | assert.EqualValues(t, 3, reads.Load(), "another head") | |
| 178 | + | mu.Lock() | |
| 179 | + | repo.PushedAt = time.Unix(300, 0) // a push, a tag-only one included | |
| 180 | + | mu.Unlock() | |
| 181 | + | stats("b", now.Add(10*time.Minute)) | |
| 182 | + | assert.EqualValues(t, 4, reads.Load(), "a push forgets the figures") | |
| 183 | + | } | |
| 184 | + | ||
| 185 | + | func TestRepoStatsDoesNotKeepAFailure(t *testing.T) { | |
| 186 | + | t.Parallel() | |
| 187 | + | ctx := context.Background() | |
| 188 | + | f := newBrowseFixture(t) | |
| 189 | + | repo := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic} | |
| 190 | + | f.meta.EXPECT().Get(ctx, repo.Name).Return(repo, nil) | |
| 191 | + | now := time.Now() | |
| 192 | + | f.reader.EXPECT().CountCommits(mock.Anything, repo.Name, oid("a"), time.Time{}).Return(0, errors.New("boom")).Once() | |
| 193 | + | _, err := f.browse.RepoStats(ctx, domain.Anonymous(), repo.Name, oid("a"), now) | |
| 194 | + | require.Error(t, err) | |
| 195 | + | f.reader.EXPECT().CountCommits(mock.Anything, repo.Name, oid("a"), mock.Anything).Return(3, nil).Twice() | |
| 196 | + | f.reader.EXPECT().LatestTag(mock.Anything, repo.Name).Return(domain.TagInfo{}, false, nil).Once() | |
| 197 | + | st, err := f.browse.RepoStats(ctx, domain.Anonymous(), repo.Name, oid("a"), now) | |
| 198 | + | require.NoError(t, err) | |
| 199 | + | assert.Equal(t, 3, st.Commits) | |
| 200 | + | assert.Nil(t, st.LatestTag) | |
| 108 | 201 | } | |
| 109 | 202 | ||
| 110 | 203 | // TestBrowseAccess is the access rule over every read use case and every | |
| @@ -5,11 +5,16 @@ package usecase_test | |||||
| 5 | import ( | 5 | import ( | ||
| 6 | "context" | 6 | "context" | ||
| 7 | "encoding/base64" | 7 | "encoding/base64" | ||
| 8 | + | "errors" | |||
| 8 | "fmt" | 9 | "fmt" | ||
| 9 | "io" | 10 | "io" | ||
| 11 | + | "runtime" | |||
| 10 | "strconv" | 12 | "strconv" | ||
| 11 | "strings" | 13 | "strings" | ||
| 14 | + | "sync" | |||
| 15 | + | "sync/atomic" | |||
| 12 | "testing" | 16 | "testing" | ||
| 17 | + | "time" | |||
| 13 | 18 | ||||
| 14 | "github.com/stretchr/testify/assert" | 19 | "github.com/stretchr/testify/assert" | ||
| 15 | "github.com/stretchr/testify/mock" | 20 | "github.com/stretchr/testify/mock" | ||
| @@ -104,7 +109,95 @@ func browseOps() []browseOp { | |||||
| 104 | }, func(r *mocks.RepoReader) { | 109 | }, func(r *mocks.RepoReader) { | ||
| 105 | r.EXPECT().GetCommit(mock.Anything, mock.Anything, mock.Anything, domain.DefaultDiffLimits()).Return(domain.CommitDetail{}, nil) | 110 | r.EXPECT().GetCommit(mock.Anything, mock.Anything, mock.Anything, domain.DefaultDiffLimits()).Return(domain.CommitDetail{}, nil) | ||
| 106 | }}, | 111 | }}, | ||
| 112 | + | {"RepoStats", func(b *usecase.Browse, ctx context.Context, p domain.Principal, repo domain.RepoName) error { | |||
| 113 | + | _, err := b.RepoStats(ctx, p, repo, oid("a"), time.Now()) | |||
| 114 | + | return err | |||
| 115 | + | }, func(r *mocks.RepoReader) { | |||
| 116 | + | r.EXPECT().CountCommits(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(1, nil) | |||
| 117 | + | r.EXPECT().LatestTag(mock.Anything, mock.Anything).Return(domain.TagInfo{}, false, nil) | |||
| 118 | + | }}, | |||
| 119 | + | } | |||
| 120 | + | } | |||
| 121 | + | ||||
| 122 | + | // RepoStats reads Git once per state of the repository (head and last push) | |||
| 123 | + | // and per 10 minutes; concurrent callers share the read, and a failure is | |||
| 124 | + | // not kept. | |||
| 125 | + | func TestRepoStatsCache(t *testing.T) { | |||
| 126 | + | t.Parallel() | |||
| 127 | + | ctx := context.Background() | |||
| 128 | + | f := newBrowseFixture(t) | |||
| 129 | + | repo := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic, CreatedAt: time.Unix(100, 0), PushedAt: time.Unix(200, 0)} | |||
| 130 | + | var mu sync.Mutex | |||
| 131 | + | f.meta.EXPECT().Get(mock.Anything, repo.Name).RunAndReturn(func(context.Context, domain.RepoName) (domain.Repo, error) { | |||
| 132 | + | mu.Lock() | |||
| 133 | + | defer mu.Unlock() | |||
| 134 | + | return repo, nil | |||
| 135 | + | }) | |||
| 136 | + | now := time.Date(2026, 10, 18, 0, 0, 0, 0, time.UTC) | |||
| 137 | + | var reads atomic.Int32 | |||
| 138 | + | gate := make(chan struct{}) | |||
| 139 | + | f.reader.EXPECT().CountCommits(mock.Anything, repo.Name, mock.Anything, time.Time{}).RunAndReturn( | |||
| 140 | + | func(context.Context, domain.RepoName, domain.ObjectID, time.Time) (int, error) { | |||
| 141 | + | reads.Add(1) | |||
| 142 | + | <-gate | |||
| 143 | + | return 900, nil | |||
| 144 | + | }) | |||
| 145 | + | f.reader.EXPECT().CountCommits(mock.Anything, repo.Name, mock.Anything, mock.MatchedBy(func(s time.Time) bool { return !s.IsZero() })).Return(12, nil) | |||
| 146 | + | tag := domain.TagInfo{Name: "v1", At: now, CommitID: oid("c")} | |||
| 147 | + | f.reader.EXPECT().LatestTag(mock.Anything, repo.Name).Return(tag, true, nil) | |||
| 148 | + | ||||
| 149 | + | stats := func(head string, at time.Time) ports.RepoStats { | |||
| 150 | + | st, err := f.browse.RepoStats(ctx, domain.Anonymous(), repo.Name, oid(head), at) | |||
| 151 | + | require.NoError(t, err) | |||
| 152 | + | return st | |||
| 153 | + | } | |||
| 154 | + | ||||
| 155 | + | // Eight concurrent requests: one read. | |||
| 156 | + | var wg sync.WaitGroup | |||
| 157 | + | for range 8 { | |||
| 158 | + | wg.Go(func() { | |||
| 159 | + | st := stats("a", now) | |||
| 160 | + | assert.Equal(t, 900, st.Commits) | |||
| 161 | + | assert.Equal(t, 12, st.Recent) | |||
| 162 | + | assert.Equal(t, tag, *st.LatestTag) | |||
| 163 | + | }) | |||
| 164 | + | } | |||
| 165 | + | for reads.Load() == 0 { | |||
| 166 | + | runtime.Gosched() | |||
| 107 | } | 167 | } | ||
| 168 | + | close(gate) | |||
| 169 | + | wg.Wait() | |||
| 170 | + | assert.EqualValues(t, 1, reads.Load(), "concurrent callers share the read") | |||
| 171 | + | ||||
| 172 | + | stats("a", now.Add(9*time.Minute)) | |||
| 173 | + | assert.EqualValues(t, 1, reads.Load(), "served from the cache") | |||
| 174 | + | stats("a", now.Add(10*time.Minute)) | |||
| 175 | + | assert.EqualValues(t, 2, reads.Load(), "expired") | |||
| 176 | + | stats("b", now.Add(10*time.Minute)) | |||
| 177 | + | assert.EqualValues(t, 3, reads.Load(), "another head") | |||
| 178 | + | mu.Lock() | |||
| 179 | + | repo.PushedAt = time.Unix(300, 0) // a push, a tag-only one included | |||
| 180 | + | mu.Unlock() | |||
| 181 | + | stats("b", now.Add(10*time.Minute)) | |||
| 182 | + | assert.EqualValues(t, 4, reads.Load(), "a push forgets the figures") | |||
| 183 | + | } | |||
| 184 | + | ||||
| 185 | + | func TestRepoStatsDoesNotKeepAFailure(t *testing.T) { | |||
| 186 | + | t.Parallel() | |||
| 187 | + | ctx := context.Background() | |||
| 188 | + | f := newBrowseFixture(t) | |||
| 189 | + | repo := domain.Repo{Name: name(t, "pub"), Visibility: domain.VisibilityPublic} | |||
| 190 | + | f.meta.EXPECT().Get(ctx, repo.Name).Return(repo, nil) | |||
| 191 | + | now := time.Now() | |||
| 192 | + | f.reader.EXPECT().CountCommits(mock.Anything, repo.Name, oid("a"), time.Time{}).Return(0, errors.New("boom")).Once() | |||
| 193 | + | _, err := f.browse.RepoStats(ctx, domain.Anonymous(), repo.Name, oid("a"), now) | |||
| 194 | + | require.Error(t, err) | |||
| 195 | + | f.reader.EXPECT().CountCommits(mock.Anything, repo.Name, oid("a"), mock.Anything).Return(3, nil).Twice() | |||
| 196 | + | f.reader.EXPECT().LatestTag(mock.Anything, repo.Name).Return(domain.TagInfo{}, false, nil).Once() | |||
| 197 | + | st, err := f.browse.RepoStats(ctx, domain.Anonymous(), repo.Name, oid("a"), now) | |||
| 198 | + | require.NoError(t, err) | |||
| 199 | + | assert.Equal(t, 3, st.Commits) | |||
| 200 | + | assert.Nil(t, st.LatestTag) | |||
| 108 | } | 201 | } | ||
| 109 | 202 | ||||
| 110 | // TestBrowseAccess is the access rule over every read use case and every | 203 | // TestBrowseAccess is the access rule over every read use case and every | ||