detached 0a9e349

#6 feat(web): review page polish and home page ordered by last activity

state
merged · 4ce8534 · squash
branch
feat/mr-header-card → main
opened
bnema ·
updated

Review page and home page polish, plus "last activity" ordering of the repositories.

Merge request page

  • Title, facts and description in one card; the header a shade lighter than the description. Issues share it.
  • Unified / Split / Collapse all in one view menu (the existing details.menu component); u, d and x still work, escape closes menus on every page and the focus goes back to the menu button.
  • Wide mode on every page with w (listed in ?), kept in localStorage; store.js loads without defer and sets it before the first paint.
  • Hunk headers and hidden-line rows are quieter (background and border), text keeps its contrast.
  • A shift + click range is one block (one border, none between its lines, per side in split); the form title names the whole range (a.go:21-149); lines loaded later from a gap inside the range are selected too.

Home page

  • Inbox button next to the logo, home page only, with a mailbox icon (Lucide) and a darker count badge.
  • Default order is the last activity: push (incl. merge), merge request opened / merged / closed / ready, comment on a merge request, issue opened / approved / closed / reopened / commented. Moderation (submissions, rejections) stays owner-only and does not count.
  • The row of a repository with activity moves to the top live (activity order only; CI updates do not move it).

Storage

  • Repo.ActiveAt (storage proto field 8; falls back to the later of pushed and created).
  • RepoMetaStore.Update is replaced by Mutate: a compare-and-swap on the version read, retried up to 8 times. The previous Update checked the version read at write time, so a read-modify-write could overwrite a concurrent change (e.g. a visibility change).

No change to ermite-api.

Checks: go vet ./..., go test ./..., go test -race on usecase and kvstore, JS tests (160), golden pages refreshed, manual checks on the dev server. Reviewed by Go and JS reviewers; findings fixed.

CommitSubjectAuthorDateSignatureStatus
0a9e349test: run the activity store test outside the usecase package and its home row subtests in parallelbriceGPG Verified
9ca7c68fix(repos): mutate metadata atomically and record approved issue activitybriceGPG Verified
32e9fe7fix(web): match loaded lines to a selection by file key, keep the focus on a closing menu, close menus with escape on every page and restore the hunk header contrastbriceGPG Verified
4277264feat(web): order the home page by last activity and move repositories up livebriceGPG Verified
4fc291efeat(web): move the inbox button of the home page next to the logo, with a mailbox icon and a darker count badgebriceGPG Verified
5f6d2c6test(app): refresh the web golden pages for the view menu, the w shortcut and the store scriptbriceGPG Verified
a772c1bfix(web): a selected line range is one block, its title names the whole range and its hidden lines stay selected once loadedbriceGPG Verified
502935erefactor(web): drop the Wide button of the diff bar, the w shortcut covers itbriceGPG Verified
a1f8e85feat(web): make the wide mode a global w shortcut, set before the first paint, and tighten the merge request headerbriceGPG Verified
fcc7703feat(web): one merge request card, a diff view menu, a wide diff mode and quieter hunk rowsbriceGPG Verified

Checks 4

92 files · +2426 -1392
4/4
M gen/ermite/storage/repo.pb.go +22 -11 generated file: collapsed
M internal/adapters/kvstore/issues_test.go +4 -2
@@ -258,13 +258,15 @@ func TestRepoSettingsKeepTheIssuesMode(t *testing.T) {
258258 assert.Equal(t, domain.IssuesOff, got.Settings.Issues)
259259 for _, m := range []domain.IssuesMode{domain.IssuesOwner, domain.IssuesPublic, domain.IssuesOff} {
260260 got.Settings.Issues = m
261- require.NoError(t, s.Update(ctx, got))
261+ _, err = s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) { r.Settings = got.Settings; return true, nil })
262+ require.NoError(t, err)
262263 again, err := s.Get(ctx, n)
263264 require.NoError(t, err)
264265 assert.Equal(t, m, again.Settings.Issues)
265266 }
266267 got.Settings.Issues = "everyone"
267- assert.ErrorIs(t, s.Update(ctx, got), domain.ErrInvalid)
268+ _, err = s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) { r.Settings = got.Settings; return true, nil })
269+ assert.ErrorIs(t, err, domain.ErrInvalid)
268270 }
269271
270272 // TestSchema4UpgradesTheTokensWithAllScopes: the tokens that held every
@@ -258,13 +258,15 @@ func TestRepoSettingsKeepTheIssuesMode(t *testing.T) {
258 assert.Equal(t, domain.IssuesOff, got.Settings.Issues)258 assert.Equal(t, domain.IssuesOff, got.Settings.Issues)
259 for _, m := range []domain.IssuesMode{domain.IssuesOwner, domain.IssuesPublic, domain.IssuesOff} {259 for _, m := range []domain.IssuesMode{domain.IssuesOwner, domain.IssuesPublic, domain.IssuesOff} {
260 got.Settings.Issues = m260 got.Settings.Issues = m
261- require.NoError(t, s.Update(ctx, got))261+ _, err = s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) { r.Settings = got.Settings; return true, nil })
262+ require.NoError(t, err)
262 again, err := s.Get(ctx, n)263 again, err := s.Get(ctx, n)
263 require.NoError(t, err)264 require.NoError(t, err)
264 assert.Equal(t, m, again.Settings.Issues)265 assert.Equal(t, m, again.Settings.Issues)
265 }266 }
266 got.Settings.Issues = "everyone"267 got.Settings.Issues = "everyone"
267- assert.ErrorIs(t, s.Update(ctx, got), domain.ErrInvalid)268+ _, err = s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) { r.Settings = got.Settings; return true, nil })
269+ assert.ErrorIs(t, err, domain.ErrInvalid)
268 }270 }
269 271
270 // TestSchema4UpgradesTheTokensWithAllScopes: the tokens that held every272 // TestSchema4UpgradesTheTokensWithAllScopes: the tokens that held every
M internal/adapters/kvstore/mergerequests_test.go +4 -2
@@ -442,13 +442,15 @@ func TestRepoSettings(t *testing.T) {
442442 assert.Equal(t, domain.DefaultRepoSettings(), got.Settings)
443443
444444 got.Settings = domain.RepoSettings{AllowedMergeModes: []domain.MergeMode{domain.MergeModeSquash}, FailingChecksBlock: false, Issues: domain.IssuesOwner}
445- require.NoError(t, s.Update(ctx, got))
445+ _, err = s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) { r.Settings = got.Settings; return true, nil })
446+ require.NoError(t, err)
446447 again, err := s.Get(ctx, n)
447448 require.NoError(t, err)
448449 assert.Equal(t, got.Settings, again.Settings)
449450
450451 got.Settings = domain.RepoSettings{AllowedMergeModes: []domain.MergeMode{"rebase"}}
451- assert.ErrorIs(t, s.Update(ctx, got), domain.ErrInvalid)
452+ _, err = s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) { r.Settings = got.Settings; return true, nil })
453+ assert.ErrorIs(t, err, domain.ErrInvalid)
452454 }
453455
454456 // TestRepoWithoutStoredSettingsReadsTheDefaults: a repository stored before
@@ -442,13 +442,15 @@ func TestRepoSettings(t *testing.T) {
442 assert.Equal(t, domain.DefaultRepoSettings(), got.Settings)442 assert.Equal(t, domain.DefaultRepoSettings(), got.Settings)
443 443
444 got.Settings = domain.RepoSettings{AllowedMergeModes: []domain.MergeMode{domain.MergeModeSquash}, FailingChecksBlock: false, Issues: domain.IssuesOwner}444 got.Settings = domain.RepoSettings{AllowedMergeModes: []domain.MergeMode{domain.MergeModeSquash}, FailingChecksBlock: false, Issues: domain.IssuesOwner}
445- require.NoError(t, s.Update(ctx, got))445+ _, err = s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) { r.Settings = got.Settings; return true, nil })
446+ require.NoError(t, err)
446 again, err := s.Get(ctx, n)447 again, err := s.Get(ctx, n)
447 require.NoError(t, err)448 require.NoError(t, err)
448 assert.Equal(t, got.Settings, again.Settings)449 assert.Equal(t, got.Settings, again.Settings)
449 450
450 got.Settings = domain.RepoSettings{AllowedMergeModes: []domain.MergeMode{"rebase"}}451 got.Settings = domain.RepoSettings{AllowedMergeModes: []domain.MergeMode{"rebase"}}
451- assert.ErrorIs(t, s.Update(ctx, got), domain.ErrInvalid)452+ _, err = s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) { r.Settings = got.Settings; return true, nil })
453+ assert.ErrorIs(t, err, domain.ErrInvalid)
452 }454 }
453 455
454 // TestRepoWithoutStoredSettingsReadsTheDefaults: a repository stored before456 // TestRepoWithoutStoredSettingsReadsTheDefaults: a repository stored before
M internal/adapters/kvstore/repos.go +44 -22
@@ -64,31 +64,51 @@ func (s *Store) List(ctx context.Context) ([]domain.Repo, error) {
6464 return repos, nil
6565 }
6666
67-// Update replaces the stored repository. It returns domain.ErrNotFound if
68-// there is none and domain.ErrConflict if another write changed it since it
69-// was read.
67+const repoMutationTries = 8
68+
69+// Mutate applies a retry-safe mutation under the version it read.
7070 //
71-//hexcheck:ignore suspicious-business-logic-in-adapter port method name only
72-func (s *Store) Update(ctx context.Context, repo domain.Repo) error {
73- val, err := encodeRepo(repo)
74- if err != nil {
75- return err
76- }
77- key := repoKey(repo.Name)
78- item, ok := s.db.Get(ctx, key)
79- if !ok {
80- return domain.ErrNotFound
81- }
82- var b kv.Batch
83- b.RequireVersion(key, item.Version)
84- b.Put(key, val)
85- if _, err := s.db.Apply(ctx, &b); err != nil {
86- if errors.Is(err, kv.ErrConflict) {
87- return domain.ErrConflict
71+//hexcheck:ignore suspicious-business-logic-in-adapter optimistic concurrency is a storage concern
72+func (s *Store) Mutate(ctx context.Context, name domain.RepoName, fn func(*domain.Repo) (bool, error)) (domain.Repo, error) {
73+ key := repoKey(name)
74+ for range repoMutationTries {
75+ if err := ctx.Err(); err != nil {
76+ return domain.Repo{}, err
77+ }
78+ item, ok := s.db.Get(ctx, key)
79+ if !ok {
80+ return domain.Repo{}, domain.ErrNotFound
8881 }
89- return fmt.Errorf("update repository %q: %w", repo.Name, err)
82+ repo, err := decodeRepo(item.Value)
83+ if err != nil {
84+ return domain.Repo{}, err
85+ }
86+ changed, err := fn(&repo)
87+ if err != nil {
88+ return domain.Repo{}, err
89+ }
90+ if repo.Name != name {
91+ return domain.Repo{}, fmt.Errorf("mutate repository name: %w", domain.ErrInvalidName)
92+ }
93+ if !changed {
94+ return decodeRepo(item.Value)
95+ }
96+ val, err := encodeRepo(repo)
97+ if err != nil {
98+ return domain.Repo{}, err
99+ }
100+ var b kv.Batch
101+ b.RequireVersion(key, item.Version)
102+ b.Put(key, val)
103+ if _, err := s.db.Apply(ctx, &b); err != nil {
104+ if errors.Is(err, kv.ErrConflict) {
105+ continue
106+ }
107+ return domain.Repo{}, fmt.Errorf("mutate repository %q: %w", name, err)
108+ }
109+ return decodeRepo(val)
90110 }
91- return nil
111+ return domain.Repo{}, domain.ErrConflict
92112 }
93113
94114 // guardRepoKey makes the batch depend on the repository: it requires the
@@ -208,6 +228,7 @@ func encodeRepo(r domain.Repo) ([]byte, error) {
208228 Visibility: vis,
209229 CreatedAt: encodeTime(r.CreatedAt),
210230 PushedAt: encodeTime(r.PushedAt),
231+ ActiveAt: encodeTime(r.ActiveAt),
211232 Settings: pbSettings,
212233 }
213234 return mustMarshal(pb), nil
@@ -229,6 +250,7 @@ func decodeRepo(val []byte) (domain.Repo, error) {
229250 Visibility: decodeVisibility(pb.GetVisibility()),
230251 CreatedAt: decodeTime(pb.GetCreatedAt()),
231252 PushedAt: decodeTime(pb.GetPushedAt()),
253+ ActiveAt: decodeTime(pb.GetActiveAt()),
232254 Settings: decodeSettings(pb.GetSettings()),
233255 }, nil
234256 }
@@ -64,31 +64,51 @@ func (s *Store) List(ctx context.Context) ([]domain.Repo, error) {
64 return repos, nil64 return repos, nil
65 }65 }
66 66
67-// Update replaces the stored repository. It returns domain.ErrNotFound if67+const repoMutationTries = 8
68-// there is none and domain.ErrConflict if another write changed it since it68+
69-// was read.69+// Mutate applies a retry-safe mutation under the version it read.
70 //70 //
71-//hexcheck:ignore suspicious-business-logic-in-adapter port method name only71+//hexcheck:ignore suspicious-business-logic-in-adapter optimistic concurrency is a storage concern
72-func (s *Store) Update(ctx context.Context, repo domain.Repo) error {72+func (s *Store) Mutate(ctx context.Context, name domain.RepoName, fn func(*domain.Repo) (bool, error)) (domain.Repo, error) {
73- val, err := encodeRepo(repo)73+ key := repoKey(name)
74- if err != nil {74+ for range repoMutationTries {
75- return err75+ if err := ctx.Err(); err != nil {
76- }76+ return domain.Repo{}, err
77- key := repoKey(repo.Name)77+ }
78- item, ok := s.db.Get(ctx, key)78+ item, ok := s.db.Get(ctx, key)
79- if !ok {79+ if !ok {
80- return domain.ErrNotFound80+ return domain.Repo{}, domain.ErrNotFound
81- }
82- var b kv.Batch
83- b.RequireVersion(key, item.Version)
84- b.Put(key, val)
85- if _, err := s.db.Apply(ctx, &b); err != nil {
86- if errors.Is(err, kv.ErrConflict) {
87- return domain.ErrConflict
88 }81 }
89- return fmt.Errorf("update repository %q: %w", repo.Name, err)82+ repo, err := decodeRepo(item.Value)
83+ if err != nil {
84+ return domain.Repo{}, err
85+ }
86+ changed, err := fn(&repo)
87+ if err != nil {
88+ return domain.Repo{}, err
89+ }
90+ if repo.Name != name {
91+ return domain.Repo{}, fmt.Errorf("mutate repository name: %w", domain.ErrInvalidName)
92+ }
93+ if !changed {
94+ return decodeRepo(item.Value)
95+ }
96+ val, err := encodeRepo(repo)
97+ if err != nil {
98+ return domain.Repo{}, err
99+ }
100+ var b kv.Batch
101+ b.RequireVersion(key, item.Version)
102+ b.Put(key, val)
103+ if _, err := s.db.Apply(ctx, &b); err != nil {
104+ if errors.Is(err, kv.ErrConflict) {
105+ continue
106+ }
107+ return domain.Repo{}, fmt.Errorf("mutate repository %q: %w", name, err)
108+ }
109+ return decodeRepo(val)
90 }110 }
91- return nil111+ return domain.Repo{}, domain.ErrConflict
92 }112 }
93 113
94 // guardRepoKey makes the batch depend on the repository: it requires the114 // guardRepoKey makes the batch depend on the repository: it requires the
@@ -208,6 +228,7 @@ func encodeRepo(r domain.Repo) ([]byte, error) {
208 Visibility: vis,228 Visibility: vis,
209 CreatedAt: encodeTime(r.CreatedAt),229 CreatedAt: encodeTime(r.CreatedAt),
210 PushedAt: encodeTime(r.PushedAt),230 PushedAt: encodeTime(r.PushedAt),
231+ ActiveAt: encodeTime(r.ActiveAt),
211 Settings: pbSettings,232 Settings: pbSettings,
212 }233 }
213 return mustMarshal(pb), nil234 return mustMarshal(pb), nil
@@ -229,6 +250,7 @@ func decodeRepo(val []byte) (domain.Repo, error) {
229 Visibility: decodeVisibility(pb.GetVisibility()),250 Visibility: decodeVisibility(pb.GetVisibility()),
230 CreatedAt: decodeTime(pb.GetCreatedAt()),251 CreatedAt: decodeTime(pb.GetCreatedAt()),
231 PushedAt: decodeTime(pb.GetPushedAt()),252 PushedAt: decodeTime(pb.GetPushedAt()),
253+ ActiveAt: decodeTime(pb.GetActiveAt()),
232 Settings: decodeSettings(pb.GetSettings()),254 Settings: decodeSettings(pb.GetSettings()),
233 }, nil255 }, nil
234 }256 }
A internal/adapters/kvstore/repos_mutate_test.go +87 -0
@@ -0,0 +1,87 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+package kvstore_test
4+
5+import (
6+ "errors"
7+ "testing"
8+ "time"
9+
10+ "github.com/stretchr/testify/require"
11+
12+ "bnema.dev/ermite/internal/domain"
13+)
14+
15+func TestRepoMutateInterleavedWrites(t *testing.T) {
16+ ctx := t.Context()
17+ s := openStore(t, t.TempDir())
18+ n := name(t, "proj")
19+ original := domain.Repo{Name: n, Visibility: domain.VisibilityPublic, CreatedAt: created}
20+ require.NoError(t, s.Create(ctx, original))
21+ later := created.Add(2 * time.Hour)
22+ calls := 0
23+ got, err := s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) {
24+ calls++
25+ // Test-only interleaving: inject a committed write after the outer read.
26+ if calls == 1 {
27+ _, err := s.Mutate(ctx, n, func(current *domain.Repo) (bool, error) {
28+ current.Visibility = domain.VisibilityPrivate
29+ current.ActiveAt = later
30+ current.PushedAt = later
31+ return true, nil
32+ })
33+ require.NoError(t, err)
34+ }
35+ r.Description = "updated"
36+ at := created.Add(time.Hour)
37+ if at.After(r.ActiveAt) {
38+ r.ActiveAt = at
39+ }
40+ if at.After(r.PushedAt) {
41+ r.PushedAt = at
42+ }
43+ return true, nil
44+ })
45+ require.NoError(t, err)
46+ require.Equal(t, 2, calls)
47+ require.Equal(t, domain.VisibilityPrivate, got.Visibility)
48+ require.Equal(t, "updated", got.Description)
49+ require.Equal(t, later, got.ActiveAt)
50+ require.Equal(t, later, got.PushedAt)
51+ stored, err := s.Get(ctx, n)
52+ require.NoError(t, err)
53+ require.Equal(t, got, stored)
54+}
55+
56+func TestRepoMutateContracts(t *testing.T) {
57+ ctx := t.Context()
58+ s := openStore(t, t.TempDir())
59+ n := name(t, "proj")
60+ require.NoError(t, s.Create(ctx, domain.Repo{Name: n, Visibility: domain.VisibilityPublic, CreatedAt: created}))
61+ original, err := s.Get(ctx, n)
62+ require.NoError(t, err)
63+ boom := errors.New("callback failed")
64+ _, err = s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) { r.Description = "discard"; return true, boom })
65+ require.ErrorIs(t, err, boom)
66+ got, err := s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) { r.Description = "discard"; return false, nil })
67+ require.NoError(t, err)
68+ require.Equal(t, original, got)
69+ for _, changed := range []bool{false, true} {
70+ _, err = s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) { r.Name = name(t, "other"); return changed, nil })
71+ require.ErrorIs(t, err, domain.ErrInvalidName)
72+ }
73+ got, err = s.Get(ctx, n)
74+ require.NoError(t, err)
75+ require.Equal(t, original, got)
76+ calls := 0
77+ _, err = s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) {
78+ calls++
79+ _, err := s.Mutate(ctx, n, func(current *domain.Repo) (bool, error) { current.Description = "concurrent"; return true, nil })
80+ require.NoError(t, err)
81+ return true, nil
82+ })
83+ require.ErrorIs(t, err, domain.ErrConflict)
84+ require.Equal(t, 8, calls)
85+ _, err = s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) { require.NoError(t, s.Delete(ctx, n)); return true, nil })
86+ require.ErrorIs(t, err, domain.ErrNotFound)
87+}
@@ -0,0 +1,87 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+package kvstore_test
4+
5+import (
6+ "errors"
7+ "testing"
8+ "time"
9+
10+ "github.com/stretchr/testify/require"
11+
12+ "bnema.dev/ermite/internal/domain"
13+)
14+
15+func TestRepoMutateInterleavedWrites(t *testing.T) {
16+ ctx := t.Context()
17+ s := openStore(t, t.TempDir())
18+ n := name(t, "proj")
19+ original := domain.Repo{Name: n, Visibility: domain.VisibilityPublic, CreatedAt: created}
20+ require.NoError(t, s.Create(ctx, original))
21+ later := created.Add(2 * time.Hour)
22+ calls := 0
23+ got, err := s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) {
24+ calls++
25+ // Test-only interleaving: inject a committed write after the outer read.
26+ if calls == 1 {
27+ _, err := s.Mutate(ctx, n, func(current *domain.Repo) (bool, error) {
28+ current.Visibility = domain.VisibilityPrivate
29+ current.ActiveAt = later
30+ current.PushedAt = later
31+ return true, nil
32+ })
33+ require.NoError(t, err)
34+ }
35+ r.Description = "updated"
36+ at := created.Add(time.Hour)
37+ if at.After(r.ActiveAt) {
38+ r.ActiveAt = at
39+ }
40+ if at.After(r.PushedAt) {
41+ r.PushedAt = at
42+ }
43+ return true, nil
44+ })
45+ require.NoError(t, err)
46+ require.Equal(t, 2, calls)
47+ require.Equal(t, domain.VisibilityPrivate, got.Visibility)
48+ require.Equal(t, "updated", got.Description)
49+ require.Equal(t, later, got.ActiveAt)
50+ require.Equal(t, later, got.PushedAt)
51+ stored, err := s.Get(ctx, n)
52+ require.NoError(t, err)
53+ require.Equal(t, got, stored)
54+}
55+
56+func TestRepoMutateContracts(t *testing.T) {
57+ ctx := t.Context()
58+ s := openStore(t, t.TempDir())
59+ n := name(t, "proj")
60+ require.NoError(t, s.Create(ctx, domain.Repo{Name: n, Visibility: domain.VisibilityPublic, CreatedAt: created}))
61+ original, err := s.Get(ctx, n)
62+ require.NoError(t, err)
63+ boom := errors.New("callback failed")
64+ _, err = s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) { r.Description = "discard"; return true, boom })
65+ require.ErrorIs(t, err, boom)
66+ got, err := s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) { r.Description = "discard"; return false, nil })
67+ require.NoError(t, err)
68+ require.Equal(t, original, got)
69+ for _, changed := range []bool{false, true} {
70+ _, err = s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) { r.Name = name(t, "other"); return changed, nil })
71+ require.ErrorIs(t, err, domain.ErrInvalidName)
72+ }
73+ got, err = s.Get(ctx, n)
74+ require.NoError(t, err)
75+ require.Equal(t, original, got)
76+ calls := 0
77+ _, err = s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) {
78+ calls++
79+ _, err := s.Mutate(ctx, n, func(current *domain.Repo) (bool, error) { current.Description = "concurrent"; return true, nil })
80+ require.NoError(t, err)
81+ return true, nil
82+ })
83+ require.ErrorIs(t, err, domain.ErrConflict)
84+ require.Equal(t, 8, calls)
85+ _, err = s.Mutate(ctx, n, func(r *domain.Repo) (bool, error) { require.NoError(t, s.Delete(ctx, n)); return true, nil })
86+ require.ErrorIs(t, err, domain.ErrNotFound)
87+}
M internal/adapters/kvstore/store_test.go +8 -4
@@ -43,7 +43,7 @@ func TestCreateGetListUpdateDelete(t *testing.T) {
4343 s := openStore(t, t.TempDir())
4444
4545 b := domain.Repo{Name: name(t, "b"), Description: "second", Topics: []string{"cli", "go"}, Visibility: domain.VisibilityPublic, CreatedAt: created, Settings: domain.DefaultRepoSettings()}
46- a := domain.Repo{Name: name(t, "a"), Visibility: domain.VisibilityPrivate, CreatedAt: created, PushedAt: created.Add(time.Hour), Settings: domain.DefaultRepoSettings()}
46+ a := domain.Repo{Name: name(t, "a"), Visibility: domain.VisibilityPrivate, CreatedAt: created, PushedAt: created.Add(time.Hour), ActiveAt: created.Add(2 * time.Hour), Settings: domain.DefaultRepoSettings()}
4747 require.NoError(t, s.Create(ctx, b))
4848 require.NoError(t, s.Create(ctx, a))
4949
@@ -54,6 +54,7 @@ func TestCreateGetListUpdateDelete(t *testing.T) {
5454 require.NoError(t, err)
5555 assert.Equal(t, a, got)
5656 assert.True(t, got.PushedAt.Equal(a.PushedAt))
57+ assert.True(t, got.ActiveAt.Equal(a.ActiveAt))
5758
5859 list, err := s.List(ctx)
5960 require.NoError(t, err)
@@ -65,13 +66,15 @@ func TestCreateGetListUpdateDelete(t *testing.T) {
6566 b.Description = "changed"
6667 b.Topics = []string{"forge"}
6768 b.Visibility = domain.VisibilityPrivate
68- require.NoError(t, s.Update(ctx, b))
69+ _, err = s.Mutate(ctx, b.Name, func(r *domain.Repo) (bool, error) { *r = b; return true, nil })
70+ require.NoError(t, err)
6971 got, err = s.Get(ctx, b.Name)
7072 require.NoError(t, err)
7173 assert.Equal(t, b, got)
7274
7375 b.Topics = nil
74- require.NoError(t, s.Update(ctx, b))
76+ _, err = s.Mutate(ctx, b.Name, func(r *domain.Repo) (bool, error) { *r = b; return true, nil })
77+ require.NoError(t, err)
7578 got, err = s.Get(ctx, b.Name)
7679 require.NoError(t, err)
7780 assert.Nil(t, got.Topics, "cleared topics read back as nil")
@@ -80,7 +83,8 @@ func TestCreateGetListUpdateDelete(t *testing.T) {
8083 _, err = s.Get(ctx, b.Name)
8184 assert.ErrorIs(t, err, domain.ErrNotFound)
8285 require.NoError(t, s.Delete(ctx, b.Name), "deleting a missing repository is not an error")
83- assert.ErrorIs(t, s.Update(ctx, b), domain.ErrNotFound)
86+ _, err = s.Mutate(ctx, b.Name, func(r *domain.Repo) (bool, error) { return true, nil })
87+ assert.ErrorIs(t, err, domain.ErrNotFound)
8488
8589 _, err = s.Get(ctx, name(t, "never"))
8690 assert.ErrorIs(t, err, domain.ErrNotFound)
@@ -43,7 +43,7 @@ func TestCreateGetListUpdateDelete(t *testing.T) {
43 s := openStore(t, t.TempDir())43 s := openStore(t, t.TempDir())
44 44
45 b := domain.Repo{Name: name(t, "b"), Description: "second", Topics: []string{"cli", "go"}, Visibility: domain.VisibilityPublic, CreatedAt: created, Settings: domain.DefaultRepoSettings()}45 b := domain.Repo{Name: name(t, "b"), Description: "second", Topics: []string{"cli", "go"}, Visibility: domain.VisibilityPublic, CreatedAt: created, Settings: domain.DefaultRepoSettings()}
46- a := domain.Repo{Name: name(t, "a"), Visibility: domain.VisibilityPrivate, CreatedAt: created, PushedAt: created.Add(time.Hour), Settings: domain.DefaultRepoSettings()}46+ a := domain.Repo{Name: name(t, "a"), Visibility: domain.VisibilityPrivate, CreatedAt: created, PushedAt: created.Add(time.Hour), ActiveAt: created.Add(2 * time.Hour), Settings: domain.DefaultRepoSettings()}
47 require.NoError(t, s.Create(ctx, b))47 require.NoError(t, s.Create(ctx, b))
48 require.NoError(t, s.Create(ctx, a))48 require.NoError(t, s.Create(ctx, a))
49 49
@@ -54,6 +54,7 @@ func TestCreateGetListUpdateDelete(t *testing.T) {
54 require.NoError(t, err)54 require.NoError(t, err)
55 assert.Equal(t, a, got)55 assert.Equal(t, a, got)
56 assert.True(t, got.PushedAt.Equal(a.PushedAt))56 assert.True(t, got.PushedAt.Equal(a.PushedAt))
57+ assert.True(t, got.ActiveAt.Equal(a.ActiveAt))
57 58
58 list, err := s.List(ctx)59 list, err := s.List(ctx)
59 require.NoError(t, err)60 require.NoError(t, err)
@@ -65,13 +66,15 @@ func TestCreateGetListUpdateDelete(t *testing.T) {
65 b.Description = "changed"66 b.Description = "changed"
66 b.Topics = []string{"forge"}67 b.Topics = []string{"forge"}
67 b.Visibility = domain.VisibilityPrivate68 b.Visibility = domain.VisibilityPrivate
68- require.NoError(t, s.Update(ctx, b))69+ _, err = s.Mutate(ctx, b.Name, func(r *domain.Repo) (bool, error) { *r = b; return true, nil })
70+ require.NoError(t, err)
69 got, err = s.Get(ctx, b.Name)71 got, err = s.Get(ctx, b.Name)
70 require.NoError(t, err)72 require.NoError(t, err)
71 assert.Equal(t, b, got)73 assert.Equal(t, b, got)
72 74
73 b.Topics = nil75 b.Topics = nil
74- require.NoError(t, s.Update(ctx, b))76+ _, err = s.Mutate(ctx, b.Name, func(r *domain.Repo) (bool, error) { *r = b; return true, nil })
77+ require.NoError(t, err)
75 got, err = s.Get(ctx, b.Name)78 got, err = s.Get(ctx, b.Name)
76 require.NoError(t, err)79 require.NoError(t, err)
77 assert.Nil(t, got.Topics, "cleared topics read back as nil")80 assert.Nil(t, got.Topics, "cleared topics read back as nil")
@@ -80,7 +83,8 @@ func TestCreateGetListUpdateDelete(t *testing.T) {
80 _, err = s.Get(ctx, b.Name)83 _, err = s.Get(ctx, b.Name)
81 assert.ErrorIs(t, err, domain.ErrNotFound)84 assert.ErrorIs(t, err, domain.ErrNotFound)
82 require.NoError(t, s.Delete(ctx, b.Name), "deleting a missing repository is not an error")85 require.NoError(t, s.Delete(ctx, b.Name), "deleting a missing repository is not an error")
83- assert.ErrorIs(t, s.Update(ctx, b), domain.ErrNotFound)86+ _, err = s.Mutate(ctx, b.Name, func(r *domain.Repo) (bool, error) { return true, nil })
87+ assert.ErrorIs(t, err, domain.ErrNotFound)
84 88
85 _, err = s.Get(ctx, name(t, "never"))89 _, err = s.Get(ctx, name(t, "never"))
86 assert.ErrorIs(t, err, domain.ErrNotFound)90 assert.ErrorIs(t, err, domain.ErrNotFound)
M internal/adapters/web/components.templ +2 -2
@@ -37,7 +37,7 @@ templ CloneBlock(c Clone) {
3737 // right its figures and CI state over its newest commits. Its id is the
3838 // target of SSE swaps; the name link comes first, `enter` follows it.
3939 templ HomeRowView(r HomeRow) {
40- <li id={ r.ID } class="repo" data-cursor?={ r.Cursor }>
40+ <li id={ r.ID } class="repo" data-cursor?={ r.Cursor } data-activity?={ r.Fresh }>
4141 <div class="repo-main">
4242 <h2 class="repo-name">
4343 <a href={ templ.SafeURL(r.Href) }>{ r.Name }</a>
@@ -45,7 +45,7 @@ templ HomeRowView(r HomeRow) {
4545 <span class="pill">private</span>
4646 }
4747 if r.Fresh {
48- <span class="pill new">new push</span>
48+ <span class="pill new">new activity</span>
4949 }
5050 </h2>
5151 if r.Desc != "" {
@@ -37,7 +37,7 @@ templ CloneBlock(c Clone) {
37 // right its figures and CI state over its newest commits. Its id is the37 // right its figures and CI state over its newest commits. Its id is the
38 // target of SSE swaps; the name link comes first, `enter` follows it.38 // target of SSE swaps; the name link comes first, `enter` follows it.
39 templ HomeRowView(r HomeRow) {39 templ HomeRowView(r HomeRow) {
40- <li id={ r.ID } class="repo" data-cursor?={ r.Cursor }>40+ <li id={ r.ID } class="repo" data-cursor?={ r.Cursor } data-activity?={ r.Fresh }>
41 <div class="repo-main">41 <div class="repo-main">
42 <h2 class="repo-name">42 <h2 class="repo-name">
43 <a href={ templ.SafeURL(r.Href) }>{ r.Name }</a>43 <a href={ templ.SafeURL(r.Href) }>{ r.Name }</a>
@@ -45,7 +45,7 @@ templ HomeRowView(r HomeRow) {
45 <span class="pill">private</span>45 <span class="pill">private</span>
46 }46 }
47 if r.Fresh {47 if r.Fresh {
48- <span class="pill new">new push</span>48+ <span class="pill new">new activity</span>
49 }49 }
50 </h2>50 </h2>
51 if r.Desc != "" {51 if r.Desc != "" {
M internal/adapters/web/components_templ.go +119 -113 generated file: collapsed
M internal/adapters/web/diff.templ +22 -4
@@ -72,9 +72,7 @@ templ Commit(l Layout, p CommitPage) {
7272 <div class="diff-tools" role="group" aria-label="Diff view">
7373 <span class="dim">{ plural(p.NFiles, "file", "files") } · <span class="plus">+{ itoa(p.Adds) }</span> <span class="minus">-{ itoa(p.Dels) }</span></span>
7474 <span class="grow"></span>
75- @viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref)
76- @viewLink(p.DiffView, "split", "Split", "d", p.SplitHref)
77- @collapseButton()
75+ @diffViewMenu(p.DiffView)
7876 </div>
7977 if p.Invisible {
8078 <p class="notice">
@@ -112,6 +110,26 @@ templ Commit(l Layout, p CommitPage) {
112110 }
113111 }
114112
113+// diffViewMenu is the layout of the diff (unified, the default, or split) and
114+// the fold of its files, in one menu: a <details> that works without scripts;
115+// u, d and x still act without opening it. Its button names the layout shown.
116+templ diffViewMenu(p DiffView) {
117+ <details class="menu vmenu">
118+ <summary class="btn" aria-label="Diff view">
119+ if p.Both {
120+ <span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">{ viewLabel(p.View) }</span>
121+ } else {
122+ { viewLabel(p.View) }
123+ }
124+ </summary>
125+ <div class="menu-pop" role="group" aria-label="Diff view">
126+ @viewLink(p, "unified", "Unified", "u", p.UnifiedHref)
127+ @viewLink(p, "split", "Split", "d", p.SplitHref)
128+ @collapseButton()
129+ </div>
130+ </details>
131+}
132+
115133 // viewLink is a button of the diff tools. It is a link, so that the split
116134 // layout works without JavaScript; with it, and when both layouts are in the
117135 // page, a click only flips the layout.
@@ -203,7 +221,7 @@ templ DiffFileView(f DiffFile, p DiffView) {
203221 <details
204222 class={ "dfile", "lnw-" + itoa(f.Digits) }
205223 id={ f.ID }
206- data-file
224+ data-file={ f.Key }
207225 if f.Open {
208226 open
209227 }
@@ -72,9 +72,7 @@ templ Commit(l Layout, p CommitPage) {
72 <div class="diff-tools" role="group" aria-label="Diff view">72 <div class="diff-tools" role="group" aria-label="Diff view">
73 <span class="dim">{ plural(p.NFiles, "file", "files") } · <span class="plus">+{ itoa(p.Adds) }</span> <span class="minus">-{ itoa(p.Dels) }</span></span>73 <span class="dim">{ plural(p.NFiles, "file", "files") } · <span class="plus">+{ itoa(p.Adds) }</span> <span class="minus">-{ itoa(p.Dels) }</span></span>
74 <span class="grow"></span>74 <span class="grow"></span>
75- @viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref)75+ @diffViewMenu(p.DiffView)
76- @viewLink(p.DiffView, "split", "Split", "d", p.SplitHref)
77- @collapseButton()
78 </div>76 </div>
79 if p.Invisible {77 if p.Invisible {
80 <p class="notice">78 <p class="notice">
@@ -112,6 +110,26 @@ templ Commit(l Layout, p CommitPage) {
112 }110 }
113 }111 }
114 112
113+// diffViewMenu is the layout of the diff (unified, the default, or split) and
114+// the fold of its files, in one menu: a <details> that works without scripts;
115+// u, d and x still act without opening it. Its button names the layout shown.
116+templ diffViewMenu(p DiffView) {
117+ <details class="menu vmenu">
118+ <summary class="btn" aria-label="Diff view">
119+ if p.Both {
120+ <span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">{ viewLabel(p.View) }</span>
121+ } else {
122+ { viewLabel(p.View) }
123+ }
124+ </summary>
125+ <div class="menu-pop" role="group" aria-label="Diff view">
126+ @viewLink(p, "unified", "Unified", "u", p.UnifiedHref)
127+ @viewLink(p, "split", "Split", "d", p.SplitHref)
128+ @collapseButton()
129+ </div>
130+ </details>
131+}
132+
115 // viewLink is a button of the diff tools. It is a link, so that the split133 // viewLink is a button of the diff tools. It is a link, so that the split
116 // layout works without JavaScript; with it, and when both layouts are in the134 // layout works without JavaScript; with it, and when both layouts are in the
117 // page, a click only flips the layout.135 // page, a click only flips the layout.
@@ -203,7 +221,7 @@ templ DiffFileView(f DiffFile, p DiffView) {
203 <details221 <details
204 class={ "dfile", "lnw-" + itoa(f.Digits) }222 class={ "dfile", "lnw-" + itoa(f.Digits) }
205 id={ f.ID }223 id={ f.ID }
206- data-file224+ data-file={ f.Key }
207 if f.Open {225 if f.Open {
208 open226 open
209 }227 }
M internal/adapters/web/diff_templ.go +523 -437 generated file: collapsed
M internal/adapters/web/events.go +2 -0
@@ -50,6 +50,8 @@ import (
5050 //
5151 // home-list data: <li id="repo-<name>">…</li>, or <li id="repo-<name>" data-gone></li>
5252 // for a repository that is deleted or no longer visible
53+// Activity rows carry data-activity: push, MR lifecycle/comment,
54+// or issue lifecycle/comment (never pending moderation or CI).
5355 // repo-last-push data: <dd id="last-push-<name>">…</dd>
5456 // refs-list data: <tbody id="ref-rows">…</tbody>, and again with
5557 // <dl id="refs-kv">…</dl>, the facts of the rail
@@ -50,6 +50,8 @@ import (
50 //50 //
51 // home-list data: <li id="repo-<name>">…</li>, or <li id="repo-<name>" data-gone></li>51 // home-list data: <li id="repo-<name>">…</li>, or <li id="repo-<name>" data-gone></li>
52 // for a repository that is deleted or no longer visible52 // for a repository that is deleted or no longer visible
53+// Activity rows carry data-activity: push, MR lifecycle/comment,
54+// or issue lifecycle/comment (never pending moderation or CI).
53 // repo-last-push data: <dd id="last-push-<name>">…</dd>55 // repo-last-push data: <dd id="last-push-<name>">…</dd>
54 // refs-list data: <tbody id="ref-rows">…</tbody>, and again with56 // refs-list data: <tbody id="ref-rows">…</tbody>, and again with
55 // <dl id="refs-kv">…</dl>, the facts of the rail57 // <dl id="refs-kv">…</dl>, the facts of the rail
M internal/adapters/web/format.go +8 -0
@@ -61,6 +61,14 @@ func grouped(n int) string {
6161 return string(b)
6262 }
6363
64+// viewLabel names a diff layout on the button of the view menu.
65+func viewLabel(view string) string {
66+ if view == "split" {
67+ return "Split"
68+ }
69+ return "Unified"
70+}
71+
6472 func plural(n int, one, many string) string {
6573 if n == 1 {
6674 return "1 " + one
@@ -61,6 +61,14 @@ func grouped(n int) string {
61 return string(b)61 return string(b)
62 }62 }
63 63
64+// viewLabel names a diff layout on the button of the view menu.
65+func viewLabel(view string) string {
66+ if view == "split" {
67+ return "Split"
68+ }
69+ return "Unified"
70+}
71+
64 func plural(n int, one, many string) string {72 func plural(n int, one, many string) string {
65 if n == 1 {73 if n == 1 {
66 return "1 " + one74 return "1 " + one
M internal/adapters/web/fragments.go +18 -0
@@ -112,6 +112,24 @@ func (l *liveStream) home(ctx context.Context, we ports.WatchEvent) ([]fragment,
112112 name = ev.Repo.String()
113113 case domain.PushEvent:
114114 name, fresh = ev.Repo.String(), true
115+ case domain.MergeRequestOpened:
116+ name, fresh = ev.MergeRequest.Repo.String(), true
117+ case domain.MergeRequestMerged:
118+ name, fresh = ev.MergeRequest.Repo.String(), true
119+ case domain.MergeRequestClosed:
120+ name, fresh = ev.MergeRequest.Repo.String(), true
121+ case domain.MergeRequestReady:
122+ name, fresh = ev.MergeRequest.Repo.String(), true
123+ case domain.CommentAdded:
124+ name, fresh = ev.Repo.String(), true
125+ case domain.IssueOpened:
126+ name, fresh = ev.Issue.Repo.String(), true
127+ case domain.IssueClosed:
128+ name, fresh = ev.Issue.Repo.String(), true
129+ case domain.IssueReopened:
130+ name, fresh = ev.Issue.Repo.String(), true
131+ case domain.IssueCommented:
132+ name, fresh = ev.Repo.String(), true
115133 case domain.RunUpdated:
116134 name = ev.Run.Repo.String() // the CI state of the row
117135 case domain.JobUpdated:
@@ -112,6 +112,24 @@ func (l *liveStream) home(ctx context.Context, we ports.WatchEvent) ([]fragment,
112 name = ev.Repo.String()112 name = ev.Repo.String()
113 case domain.PushEvent:113 case domain.PushEvent:
114 name, fresh = ev.Repo.String(), true114 name, fresh = ev.Repo.String(), true
115+ case domain.MergeRequestOpened:
116+ name, fresh = ev.MergeRequest.Repo.String(), true
117+ case domain.MergeRequestMerged:
118+ name, fresh = ev.MergeRequest.Repo.String(), true
119+ case domain.MergeRequestClosed:
120+ name, fresh = ev.MergeRequest.Repo.String(), true
121+ case domain.MergeRequestReady:
122+ name, fresh = ev.MergeRequest.Repo.String(), true
123+ case domain.CommentAdded:
124+ name, fresh = ev.Repo.String(), true
125+ case domain.IssueOpened:
126+ name, fresh = ev.Issue.Repo.String(), true
127+ case domain.IssueClosed:
128+ name, fresh = ev.Issue.Repo.String(), true
129+ case domain.IssueReopened:
130+ name, fresh = ev.Issue.Repo.String(), true
131+ case domain.IssueCommented:
132+ name, fresh = ev.Repo.String(), true
115 case domain.RunUpdated:133 case domain.RunUpdated:
116 name = ev.Run.Repo.String() // the CI state of the row134 name = ev.Run.Repo.String() // the CI state of the row
117 case domain.JobUpdated:135 case domain.JobUpdated:
M internal/adapters/web/home.go +24 -14
@@ -56,12 +56,12 @@ func (s *Server) listRepos(ctx context.Context) ([]ports.RepoView, error) {
5656 // homeSorts are the orders the home list offers, in order. The query also
5757 // accepts "desc" and "branch", which the page does not link to.
5858 var homeSorts = []struct{ key, label string }{
59- {"push", "last push"},
59+ {"activity", "last activity"},
6060 {"name", "name"},
6161 }
6262
6363 // homeKeys are the keys of ?sort= that sortRepos knows.
64-var homeKeys = []string{"push", "name", "desc", "branch"}
64+var homeKeys = []string{"activity", "name", "desc", "branch"}
6565
6666 // sortRepos orders repos by the column key; ties keep the order by name.
6767 func sortRepos(repos []ports.RepoView, key string, desc bool) {
@@ -71,8 +71,8 @@ func sortRepos(repos []ports.RepoView, key string, desc bool) {
7171 return strings.ToLower(a.Description) < strings.ToLower(b.Description)
7272 case "branch":
7373 return a.DefaultBranch < b.DefaultBranch
74- case "push":
75- return a.PushedAt.Before(b.PushedAt)
74+ case "activity":
75+ return repoActiveAt(a).Before(repoActiveAt(b))
7676 }
7777 return a.Name.String() < b.Name.String()
7878 }
@@ -84,17 +84,17 @@ func sortRepos(repos []ports.RepoView, key string, desc bool) {
8484 })
8585 }
8686
87-// home lists the repositories: /?sort=<col>&dir=asc|desc, newest push first
87+// home lists the repositories: /?sort=<col>&dir=asc|desc, newest activity first
8888 // by default.
8989 func (s *Server) home(ctx context.Context, r *http.Request) (templ.Component, int, error) {
9090 repos, err := s.listRepos(ctx)
9191 if err != nil {
9292 return nil, 0, err
9393 }
94- key, desc := "push", true
94+ key, desc := "activity", true
9595 for _, k := range homeKeys {
9696 if r.URL.Query().Get("sort") == k {
97- key, desc = k, k == "push"
97+ key, desc = k, k == "activity"
9898 }
9999 }
100100 if d := r.URL.Query().Get("dir"); d == "asc" || d == "desc" {
@@ -103,11 +103,11 @@ func (s *Server) home(ctx context.Context, r *http.Request) (templ.Component, in
103103 sortRepos(repos, key, desc)
104104
105105 now := s.cfg.Now()
106- p := HomePage{}
106+ p := HomePage{Sort: key, Desc: desc}
107107 for _, c := range homeSorts {
108108 sl := SortLink{Label: c.label}
109109 next := "asc"
110- if c.key == "push" {
110+ if c.key == "activity" {
111111 next = "desc"
112112 }
113113 if c.key == key {
@@ -121,11 +121,10 @@ func (s *Server) home(ctx context.Context, r *http.Request) (templ.Component, in
121121 p.Sorts = append(p.Sorts, sl)
122122 }
123123 p.Rows = s.homeRows(ctx, repos, now)
124+ l := s.base()
124125 if n, show := s.inboxCount(ctx); show {
125- p.Inbox = &InboxButton{Href: inboxPath, Count: n}
126+ l.Inbox = &InboxButton{Href: inboxPath, Count: n}
126127 }
127-
128- l := s.base()
129128 l.Title = s.instance
130129 l.Live, l.LiveURL = true, liveURL("home", "")
131130 l.Cmd = append([]CmdItem{{Kind: "go", Label: "home", Href: "/"}}, repoCmds(repos, "")...)
@@ -268,8 +267,8 @@ func homeRow(r ports.RepoView, now time.Time, cursor, signed bool) HomeRow {
268267 Desc: r.Description, Topics: r.Topics, Cursor: cursor,
269268 Private: signed && r.Visibility == domain.VisibilityPrivate,
270269 }
271- if !r.PushedAt.IsZero() {
272- row.Active, row.ActiveAt = ago(now, r.PushedAt), rfc3339(r.PushedAt)
270+ if at := repoActiveAt(r); !at.IsZero() {
271+ row.Active, row.ActiveAt = ago(now, at), rfc3339(at)
273272 }
274273 return row
275274 }
@@ -285,3 +284,14 @@ func repoCmds(repos []ports.RepoView, except string) []CmdItem {
285284 }
286285 return out
287286 }
287+
288+// repoActiveAt falls back for repositories without recorded activity.
289+func repoActiveAt(r ports.RepoView) time.Time {
290+ if !r.ActiveAt.IsZero() {
291+ return r.ActiveAt
292+ }
293+ if r.PushedAt.After(r.CreatedAt) {
294+ return r.PushedAt
295+ }
296+ return r.CreatedAt
297+}
@@ -56,12 +56,12 @@ func (s *Server) listRepos(ctx context.Context) ([]ports.RepoView, error) {
56 // homeSorts are the orders the home list offers, in order. The query also56 // homeSorts are the orders the home list offers, in order. The query also
57 // accepts "desc" and "branch", which the page does not link to.57 // accepts "desc" and "branch", which the page does not link to.
58 var homeSorts = []struct{ key, label string }{58 var homeSorts = []struct{ key, label string }{
59- {"push", "last push"},59+ {"activity", "last activity"},
60 {"name", "name"},60 {"name", "name"},
61 }61 }
62 62
63 // homeKeys are the keys of ?sort= that sortRepos knows.63 // homeKeys are the keys of ?sort= that sortRepos knows.
64-var homeKeys = []string{"push", "name", "desc", "branch"}64+var homeKeys = []string{"activity", "name", "desc", "branch"}
65 65
66 // sortRepos orders repos by the column key; ties keep the order by name.66 // sortRepos orders repos by the column key; ties keep the order by name.
67 func sortRepos(repos []ports.RepoView, key string, desc bool) {67 func sortRepos(repos []ports.RepoView, key string, desc bool) {
@@ -71,8 +71,8 @@ func sortRepos(repos []ports.RepoView, key string, desc bool) {
71 return strings.ToLower(a.Description) < strings.ToLower(b.Description)71 return strings.ToLower(a.Description) < strings.ToLower(b.Description)
72 case "branch":72 case "branch":
73 return a.DefaultBranch < b.DefaultBranch73 return a.DefaultBranch < b.DefaultBranch
74- case "push":74+ case "activity":
75- return a.PushedAt.Before(b.PushedAt)75+ return repoActiveAt(a).Before(repoActiveAt(b))
76 }76 }
77 return a.Name.String() < b.Name.String()77 return a.Name.String() < b.Name.String()
78 }78 }
@@ -84,17 +84,17 @@ func sortRepos(repos []ports.RepoView, key string, desc bool) {
84 })84 })
85 }85 }
86 86
87-// home lists the repositories: /?sort=<col>&dir=asc|desc, newest push first87+// home lists the repositories: /?sort=<col>&dir=asc|desc, newest activity first
88 // by default.88 // by default.
89 func (s *Server) home(ctx context.Context, r *http.Request) (templ.Component, int, error) {89 func (s *Server) home(ctx context.Context, r *http.Request) (templ.Component, int, error) {
90 repos, err := s.listRepos(ctx)90 repos, err := s.listRepos(ctx)
91 if err != nil {91 if err != nil {
92 return nil, 0, err92 return nil, 0, err
93 }93 }
94- key, desc := "push", true94+ key, desc := "activity", true
95 for _, k := range homeKeys {95 for _, k := range homeKeys {
96 if r.URL.Query().Get("sort") == k {96 if r.URL.Query().Get("sort") == k {
97- key, desc = k, k == "push"97+ key, desc = k, k == "activity"
98 }98 }
99 }99 }
100 if d := r.URL.Query().Get("dir"); d == "asc" || d == "desc" {100 if d := r.URL.Query().Get("dir"); d == "asc" || d == "desc" {
@@ -103,11 +103,11 @@ func (s *Server) home(ctx context.Context, r *http.Request) (templ.Component, in
103 sortRepos(repos, key, desc)103 sortRepos(repos, key, desc)
104 104
105 now := s.cfg.Now()105 now := s.cfg.Now()
106- p := HomePage{}106+ p := HomePage{Sort: key, Desc: desc}
107 for _, c := range homeSorts {107 for _, c := range homeSorts {
108 sl := SortLink{Label: c.label}108 sl := SortLink{Label: c.label}
109 next := "asc"109 next := "asc"
110- if c.key == "push" {110+ if c.key == "activity" {
111 next = "desc"111 next = "desc"
112 }112 }
113 if c.key == key {113 if c.key == key {
@@ -121,11 +121,10 @@ func (s *Server) home(ctx context.Context, r *http.Request) (templ.Component, in
121 p.Sorts = append(p.Sorts, sl)121 p.Sorts = append(p.Sorts, sl)
122 }122 }
123 p.Rows = s.homeRows(ctx, repos, now)123 p.Rows = s.homeRows(ctx, repos, now)
124+ l := s.base()
124 if n, show := s.inboxCount(ctx); show {125 if n, show := s.inboxCount(ctx); show {
125- p.Inbox = &InboxButton{Href: inboxPath, Count: n}126+ l.Inbox = &InboxButton{Href: inboxPath, Count: n}
126 }127 }
127-
128- l := s.base()
129 l.Title = s.instance128 l.Title = s.instance
130 l.Live, l.LiveURL = true, liveURL("home", "")129 l.Live, l.LiveURL = true, liveURL("home", "")
131 l.Cmd = append([]CmdItem{{Kind: "go", Label: "home", Href: "/"}}, repoCmds(repos, "")...)130 l.Cmd = append([]CmdItem{{Kind: "go", Label: "home", Href: "/"}}, repoCmds(repos, "")...)
@@ -268,8 +267,8 @@ func homeRow(r ports.RepoView, now time.Time, cursor, signed bool) HomeRow {
268 Desc: r.Description, Topics: r.Topics, Cursor: cursor,267 Desc: r.Description, Topics: r.Topics, Cursor: cursor,
269 Private: signed && r.Visibility == domain.VisibilityPrivate,268 Private: signed && r.Visibility == domain.VisibilityPrivate,
270 }269 }
271- if !r.PushedAt.IsZero() {270+ if at := repoActiveAt(r); !at.IsZero() {
272- row.Active, row.ActiveAt = ago(now, r.PushedAt), rfc3339(r.PushedAt)271+ row.Active, row.ActiveAt = ago(now, at), rfc3339(at)
273 }272 }
274 return row273 return row
275 }274 }
@@ -285,3 +284,14 @@ func repoCmds(repos []ports.RepoView, except string) []CmdItem {
285 }284 }
286 return out285 return out
287 }286 }
287+
288+// repoActiveAt falls back for repositories without recorded activity.
289+func repoActiveAt(r ports.RepoView) time.Time {
290+ if !r.ActiveAt.IsZero() {
291+ return r.ActiveAt
292+ }
293+ if r.PushedAt.After(r.CreatedAt) {
294+ return r.PushedAt
295+ }
296+ return r.CreatedAt
297+}
M internal/adapters/web/home_test.go +50 -1
@@ -252,7 +252,7 @@ func TestRunUpdatedRefreshesTheHomeRow(t *testing.T) {
252252 assert.Equal(t, "home-list", got[0].event)
253253 assert.Contains(t, got[0].html, `id="repo-proj"`)
254254 assert.Contains(t, got[0].html, "passing")
255- assert.NotContains(t, got[0].html, "new push", "a run is not a push")
255+ assert.NotContains(t, got[0].html, "new activity", "a run is not a push")
256256
257257 // A job that ended refreshes the row too; one in progress does not.
258258 got, err = (&liveStream{s: srv}).render(t.Context(), ports.WatchEvent{Event: domain.JobUpdated{Job: domain.CIJob{Repo: repo, Phase: domain.CIPhaseRunning}}})
@@ -344,3 +344,52 @@ func TestHomeOffersTheLayouts(t *testing.T) {
344344 require.NotEqual(t, -1, store)
345345 assert.Less(t, store, app, "the store loads before the app script")
346346 }
347+
348+func TestHomeActivityOrderAndFallback(t *testing.T) {
349+ t.Parallel()
350+ now := time.Now().UTC()
351+ a, b, c := publicRepo(t, "a"), publicRepo(t, "b"), publicRepo(t, "c")
352+ a.PushedAt = now.Add(-time.Hour)
353+ a.ActiveAt = now
354+ b.PushedAt = now.Add(-time.Minute)
355+ c.CreatedAt = now.Add(-30 * time.Minute)
356+ repos := []ports.RepoView{b, c, a}
357+ sortRepos(repos, "activity", true)
358+ assert.Equal(t, []ports.RepoView{a, b, c}, repos)
359+ sortRepos(repos, "activity", false)
360+ assert.Equal(t, []ports.RepoView{c, b, a}, repos)
361+ assert.Equal(t, rfc3339(now), homeRow(a, now, false, false).ActiveAt)
362+ c.PushedAt = c.CreatedAt.Add(-time.Hour)
363+ assert.Equal(t, c.CreatedAt, repoActiveAt(c), "fallback is the later of creation and push")
364+}
365+
366+func TestActivityEventsRefreshTheHomeRow(t *testing.T) {
367+ t.Parallel()
368+ repo := publicRepo(t, "proj")
369+ srv := newTestServer(t, Config{Repos: reposServing(t, repo), Browse: homeBrowse(t)})
370+ mr := domain.MergeRequest{Repo: repo.Name}
371+ issue := domain.Issue{Repo: repo.Name}
372+ for _, ev := range []domain.Event{
373+ domain.PushEvent{Repo: repo.Name},
374+ domain.MergeRequestOpened{MergeRequest: mr}, domain.MergeRequestMerged{MergeRequest: mr},
375+ domain.MergeRequestClosed{MergeRequest: mr}, domain.MergeRequestReady{MergeRequest: mr},
376+ domain.CommentAdded{Repo: repo.Name}, domain.IssueOpened{Issue: issue},
377+ domain.IssueClosed{Issue: issue}, domain.IssueReopened{Issue: issue}, domain.IssueCommented{Repo: repo.Name},
378+ } {
379+ t.Run(ev.EventKind(), func(t *testing.T) {
380+ t.Parallel()
381+ got, err := (&liveStream{s: srv}).home(t.Context(), ports.WatchEvent{Event: ev})
382+ require.NoError(t, err)
383+ require.Len(t, got, 1)
384+ assert.Equal(t, "home-list", got[0].event)
385+ assert.Contains(t, got[0].html, `id="repo-proj"`)
386+ assert.Contains(t, got[0].html, `data-activity`)
387+ assert.Contains(t, got[0].html, "new activity")
388+ })
389+ }
390+ for _, ev := range []domain.Event{domain.IssueSubmitted{Repo: repo.Name}, domain.IssuePendingRemoved{Repo: repo.Name}} {
391+ got, err := (&liveStream{s: srv}).home(t.Context(), ports.WatchEvent{Event: ev})
392+ require.NoError(t, err)
393+ assert.Empty(t, got)
394+ }
395+}
@@ -252,7 +252,7 @@ func TestRunUpdatedRefreshesTheHomeRow(t *testing.T) {
252 assert.Equal(t, "home-list", got[0].event)252 assert.Equal(t, "home-list", got[0].event)
253 assert.Contains(t, got[0].html, `id="repo-proj"`)253 assert.Contains(t, got[0].html, `id="repo-proj"`)
254 assert.Contains(t, got[0].html, "passing")254 assert.Contains(t, got[0].html, "passing")
255- assert.NotContains(t, got[0].html, "new push", "a run is not a push")255+ assert.NotContains(t, got[0].html, "new activity", "a run is not a push")
256 256
257 // A job that ended refreshes the row too; one in progress does not.257 // A job that ended refreshes the row too; one in progress does not.
258 got, err = (&liveStream{s: srv}).render(t.Context(), ports.WatchEvent{Event: domain.JobUpdated{Job: domain.CIJob{Repo: repo, Phase: domain.CIPhaseRunning}}})258 got, err = (&liveStream{s: srv}).render(t.Context(), ports.WatchEvent{Event: domain.JobUpdated{Job: domain.CIJob{Repo: repo, Phase: domain.CIPhaseRunning}}})
@@ -344,3 +344,52 @@ func TestHomeOffersTheLayouts(t *testing.T) {
344 require.NotEqual(t, -1, store)344 require.NotEqual(t, -1, store)
345 assert.Less(t, store, app, "the store loads before the app script")345 assert.Less(t, store, app, "the store loads before the app script")
346 }346 }
347+
348+func TestHomeActivityOrderAndFallback(t *testing.T) {
349+ t.Parallel()
350+ now := time.Now().UTC()
351+ a, b, c := publicRepo(t, "a"), publicRepo(t, "b"), publicRepo(t, "c")
352+ a.PushedAt = now.Add(-time.Hour)
353+ a.ActiveAt = now
354+ b.PushedAt = now.Add(-time.Minute)
355+ c.CreatedAt = now.Add(-30 * time.Minute)
356+ repos := []ports.RepoView{b, c, a}
357+ sortRepos(repos, "activity", true)
358+ assert.Equal(t, []ports.RepoView{a, b, c}, repos)
359+ sortRepos(repos, "activity", false)
360+ assert.Equal(t, []ports.RepoView{c, b, a}, repos)
361+ assert.Equal(t, rfc3339(now), homeRow(a, now, false, false).ActiveAt)
362+ c.PushedAt = c.CreatedAt.Add(-time.Hour)
363+ assert.Equal(t, c.CreatedAt, repoActiveAt(c), "fallback is the later of creation and push")
364+}
365+
366+func TestActivityEventsRefreshTheHomeRow(t *testing.T) {
367+ t.Parallel()
368+ repo := publicRepo(t, "proj")
369+ srv := newTestServer(t, Config{Repos: reposServing(t, repo), Browse: homeBrowse(t)})
370+ mr := domain.MergeRequest{Repo: repo.Name}
371+ issue := domain.Issue{Repo: repo.Name}
372+ for _, ev := range []domain.Event{
373+ domain.PushEvent{Repo: repo.Name},
374+ domain.MergeRequestOpened{MergeRequest: mr}, domain.MergeRequestMerged{MergeRequest: mr},
375+ domain.MergeRequestClosed{MergeRequest: mr}, domain.MergeRequestReady{MergeRequest: mr},
376+ domain.CommentAdded{Repo: repo.Name}, domain.IssueOpened{Issue: issue},
377+ domain.IssueClosed{Issue: issue}, domain.IssueReopened{Issue: issue}, domain.IssueCommented{Repo: repo.Name},
378+ } {
379+ t.Run(ev.EventKind(), func(t *testing.T) {
380+ t.Parallel()
381+ got, err := (&liveStream{s: srv}).home(t.Context(), ports.WatchEvent{Event: ev})
382+ require.NoError(t, err)
383+ require.Len(t, got, 1)
384+ assert.Equal(t, "home-list", got[0].event)
385+ assert.Contains(t, got[0].html, `id="repo-proj"`)
386+ assert.Contains(t, got[0].html, `data-activity`)
387+ assert.Contains(t, got[0].html, "new activity")
388+ })
389+ }
390+ for _, ev := range []domain.Event{domain.IssueSubmitted{Repo: repo.Name}, domain.IssuePendingRemoved{Repo: repo.Name}} {
391+ got, err := (&liveStream{s: srv}).home(t.Context(), ports.WatchEvent{Event: ev})
392+ require.NoError(t, err)
393+ assert.Empty(t, got)
394+ }
395+}
M internal/adapters/web/issues.templ +24 -22
@@ -136,32 +136,34 @@ templ issueNewRail() {
136136 // owner, the form to comment and the button that closes or reopens it.
137137 templ IssueShow(l Layout, p IssuePage) {
138138 @Shell(l, issueRail(p)) {
139- <header class="show mr-h">
140- <h1><span class="mute">{ p.Number }</span> { p.Title }</h1>
141- <dl class="kv show-kv">
142- <dt>state</dt>
143- <dd class="mr-state" data-state={ p.State }>{ p.State }</dd>
144- <dt>opened</dt>
145- <dd>
146- { p.Opened.Name }
147- if p.Opened.WhenAt != "" {
148- { " · " }<time datetime={ p.Opened.WhenAt } title={ p.Opened.Abs }>{ p.Opened.When }</time>
139+ <section class="mr-card">
140+ <header class="show mr-h">
141+ <h1><span class="mute">{ p.Number }</span> { p.Title }</h1>
142+ <dl class="kv show-kv">
143+ <dt>state</dt>
144+ <dd class="mr-state" data-state={ p.State }>{ p.State }</dd>
145+ <dt>opened</dt>
146+ <dd>
147+ { p.Opened.Name }
148+ if p.Opened.WhenAt != "" {
149+ { " · " }<time datetime={ p.Opened.WhenAt } title={ p.Opened.Abs }>{ p.Opened.When }</time>
150+ }
151+ </dd>
152+ if p.UpdatedAt != "" {
153+ <dt>updated</dt>
154+ <dd><time datetime={ p.UpdatedAt }>{ p.Updated }</time></dd>
149155 }
150- </dd>
151- if p.UpdatedAt != "" {
152- <dt>updated</dt>
153- <dd><time datetime={ p.UpdatedAt }>{ p.Updated }</time></dd>
154- }
155- </dl>
156- </header>
156+ </dl>
157+ </header>
158+ if p.Desc != "" {
159+ <article class="mr-desc prose" aria-label="description" hx-ignore>
160+ @templ.Raw(string(p.Desc))
161+ </article>
162+ }
163+ </section>
157164 if p.Live {
158165 @LiveSlot("mr-notice")
159166 }
160- if p.Desc != "" {
161- <article class="mr-desc prose" aria-label="description" hx-ignore>
162- @templ.Raw(string(p.Desc))
163- </article>
164- }
165167 <h2 class="rule-h" id="comments"><span>Comments</span><span class="count">{ itoa(len(p.Comments)) }</span></h2>
166168 for _, c := range p.Comments {
167169 @commentView(c, "")
@@ -136,32 +136,34 @@ templ issueNewRail() {
136 // owner, the form to comment and the button that closes or reopens it.136 // owner, the form to comment and the button that closes or reopens it.
137 templ IssueShow(l Layout, p IssuePage) {137 templ IssueShow(l Layout, p IssuePage) {
138 @Shell(l, issueRail(p)) {138 @Shell(l, issueRail(p)) {
139- <header class="show mr-h">139+ <section class="mr-card">
140- <h1><span class="mute">{ p.Number }</span> { p.Title }</h1>140+ <header class="show mr-h">
141- <dl class="kv show-kv">141+ <h1><span class="mute">{ p.Number }</span> { p.Title }</h1>
142- <dt>state</dt>142+ <dl class="kv show-kv">
143- <dd class="mr-state" data-state={ p.State }>{ p.State }</dd>143+ <dt>state</dt>
144- <dt>opened</dt>144+ <dd class="mr-state" data-state={ p.State }>{ p.State }</dd>
145- <dd>145+ <dt>opened</dt>
146- { p.Opened.Name }146+ <dd>
147- if p.Opened.WhenAt != "" {147+ { p.Opened.Name }
148- { " · " }<time datetime={ p.Opened.WhenAt } title={ p.Opened.Abs }>{ p.Opened.When }</time>148+ if p.Opened.WhenAt != "" {
149+ { " · " }<time datetime={ p.Opened.WhenAt } title={ p.Opened.Abs }>{ p.Opened.When }</time>
150+ }
151+ </dd>
152+ if p.UpdatedAt != "" {
153+ <dt>updated</dt>
154+ <dd><time datetime={ p.UpdatedAt }>{ p.Updated }</time></dd>
149 }155 }
150- </dd>156+ </dl>
151- if p.UpdatedAt != "" {157+ </header>
152- <dt>updated</dt>158+ if p.Desc != "" {
153- <dd><time datetime={ p.UpdatedAt }>{ p.Updated }</time></dd>159+ <article class="mr-desc prose" aria-label="description" hx-ignore>
154- }160+ @templ.Raw(string(p.Desc))
155- </dl>161+ </article>
156- </header>162+ }
163+ </section>
157 if p.Live {164 if p.Live {
158 @LiveSlot("mr-notice")165 @LiveSlot("mr-notice")
159 }166 }
160- if p.Desc != "" {
161- <article class="mr-desc prose" aria-label="description" hx-ignore>
162- @templ.Raw(string(p.Desc))
163- </article>
164- }
165 <h2 class="rule-h" id="comments"><span>Comments</span><span class="count">{ itoa(len(p.Comments)) }</span></h2>167 <h2 class="rule-h" id="comments"><span>Comments</span><span class="count">{ itoa(len(p.Comments)) }</span></h2>
166 for _, c := range p.Comments {168 for _, c := range p.Comments {
167 @commentView(c, "")169 @commentView(c, "")
M internal/adapters/web/issues_templ.go +26 -26 generated file: collapsed
M internal/adapters/web/jstest/a11y.test.mjs +25 -0
@@ -8,6 +8,7 @@ import { test } from 'node:test';
88 import { dom, El, el, flushFrames, installDom, settle } from './dom.mjs';
99
1010 // The few things ermite.js uses that dom.mjs does not have.
11+El.prototype.prepend = function (...nodes) { this.nodes.unshift(...this.adopt(nodes)); };
1112 El.prototype.replaceWith = function (n) {
1213 const p = this.parent;
1314 const i = p.nodes.indexOf(this);
@@ -333,3 +334,27 @@ test('the preference is read from storage when the page loads', async () => {
333334 assert.deepEqual(dom.body.events, []);
334335 store.set('keys-off', '');
335336 });
337+
338+
339+test('activity moves a home row up only in newest activity order, preserving focus and cursor', () => {
340+ for (const [sort, desc, activity, moves] of [
341+ ['activity', 'true', true, true],
342+ ['activity', 'false', true, false],
343+ ['name', 'true', true, false],
344+ ['name', 'false', true, false],
345+ ['activity', 'true', false, false], // CI or metadata, not activity
346+ ]) {
347+ const r = rows();
348+ r.list.dataset.sort = sort;
349+ r.list.dataset.desc = desc;
350+ const old = r.list.children[1];
351+ old.setAttribute('data-cursor', '');
352+ active = old.children[0];
353+ const fresh = r.mk('repo-b', '/b');
354+ if (activity) fresh.setAttribute('data-activity', '');
355+ swapWith(`activity-${sort}-${desc}-${activity}`, fresh);
356+ assert.equal(r.list.children.indexOf(fresh), moves ? 0 : 1);
357+ assert.equal(active, fresh.children[0]);
358+ assert.ok(fresh.hasAttribute('data-cursor'));
359+ }
360+});
@@ -8,6 +8,7 @@ import { test } from 'node:test';
8 import { dom, El, el, flushFrames, installDom, settle } from './dom.mjs';8 import { dom, El, el, flushFrames, installDom, settle } from './dom.mjs';
9 9
10 // The few things ermite.js uses that dom.mjs does not have.10 // The few things ermite.js uses that dom.mjs does not have.
11+El.prototype.prepend = function (...nodes) { this.nodes.unshift(...this.adopt(nodes)); };
11 El.prototype.replaceWith = function (n) {12 El.prototype.replaceWith = function (n) {
12 const p = this.parent;13 const p = this.parent;
13 const i = p.nodes.indexOf(this);14 const i = p.nodes.indexOf(this);
@@ -333,3 +334,27 @@ test('the preference is read from storage when the page loads', async () => {
333 assert.deepEqual(dom.body.events, []);334 assert.deepEqual(dom.body.events, []);
334 store.set('keys-off', '');335 store.set('keys-off', '');
335 });336 });
337+
338+
339+test('activity moves a home row up only in newest activity order, preserving focus and cursor', () => {
340+ for (const [sort, desc, activity, moves] of [
341+ ['activity', 'true', true, true],
342+ ['activity', 'false', true, false],
343+ ['name', 'true', true, false],
344+ ['name', 'false', true, false],
345+ ['activity', 'true', false, false], // CI or metadata, not activity
346+ ]) {
347+ const r = rows();
348+ r.list.dataset.sort = sort;
349+ r.list.dataset.desc = desc;
350+ const old = r.list.children[1];
351+ old.setAttribute('data-cursor', '');
352+ active = old.children[0];
353+ const fresh = r.mk('repo-b', '/b');
354+ if (activity) fresh.setAttribute('data-activity', '');
355+ swapWith(`activity-${sort}-${desc}-${activity}`, fresh);
356+ assert.equal(r.list.children.indexOf(fresh), moves ? 0 : 1);
357+ assert.equal(active, fresh.children[0]);
358+ assert.ok(fresh.hasAttribute('data-cursor'));
359+ }
360+});
M internal/adapters/web/keys.templ +1 -0
@@ -32,6 +32,7 @@ templ Keys() {
3232 k-split from:body -> ermite.layout('split');
3333 k-unified from:body -> ermite.layout('unified');
3434 k-collapse from:body -> ermite.collapse();
35+ k-wide from:body -> window.ermiteWide.toggle();
3536 k-expand from:body -> ermite.expandFile();
3637 k-expandAll from:body -> ermite.expandAll();
3738 k-permalink from:body -> ermite.permalink();
@@ -32,6 +32,7 @@ templ Keys() {
32 k-split from:body -> ermite.layout('split');32 k-split from:body -> ermite.layout('split');
33 k-unified from:body -> ermite.layout('unified');33 k-unified from:body -> ermite.layout('unified');
34 k-collapse from:body -> ermite.collapse();34 k-collapse from:body -> ermite.collapse();
35+ k-wide from:body -> window.ermiteWide.toggle();
35 k-expand from:body -> ermite.expandFile();36 k-expand from:body -> ermite.expandFile();
36 k-expandAll from:body -> ermite.expandAll();37 k-expandAll from:body -> ermite.expandAll();
37 k-permalink from:body -> ermite.permalink();38 k-permalink from:body -> ermite.permalink();
M internal/adapters/web/keys_templ.go +1 -1 generated file: collapsed
M internal/adapters/web/mr.go +6 -1
@@ -910,10 +910,15 @@ func (s *Server) placeComments(p *MRPage, repo string, n int64, comments []domai
910910 ln.Selected = true
911911 }
912912 }
913+ // The title names the whole range a shift + click selected: a.go:21-149.
914+ lines := itoa(on.line)
915+ if start < on.line {
916+ lines = itoa(start) + "-" + lines
917+ }
913918 if ln := find(on.path, side, on.line); ln != nil {
914919 ln.Form = &CommentForm{
915920 StartLine: optionalLine(on.start), ID: "cf-line", Action: mrHref(repo, n, "comments"), Path: on.path, Line: itoa(on.line), Side: on.side,
916- Title: "comment on " + visibleText(on.path) + ":" + itoa(on.line) + " (" + on.side + ")",
921+ Title: "comment on " + visibleText(on.path) + ":" + lines + " (" + on.side + ")",
917922 Cancel: withFrom(self, windowStart(fileAt[on.path])) + "#" + fileIDs[on.path], Focus: true,
918923 From: windowStart(fileAt[on.path]),
919924 }
@@ -910,10 +910,15 @@ func (s *Server) placeComments(p *MRPage, repo string, n int64, comments []domai
910 ln.Selected = true910 ln.Selected = true
911 }911 }
912 }912 }
913+ // The title names the whole range a shift + click selected: a.go:21-149.
914+ lines := itoa(on.line)
915+ if start < on.line {
916+ lines = itoa(start) + "-" + lines
917+ }
913 if ln := find(on.path, side, on.line); ln != nil {918 if ln := find(on.path, side, on.line); ln != nil {
914 ln.Form = &CommentForm{919 ln.Form = &CommentForm{
915 StartLine: optionalLine(on.start), ID: "cf-line", Action: mrHref(repo, n, "comments"), Path: on.path, Line: itoa(on.line), Side: on.side,920 StartLine: optionalLine(on.start), ID: "cf-line", Action: mrHref(repo, n, "comments"), Path: on.path, Line: itoa(on.line), Side: on.side,
916- Title: "comment on " + visibleText(on.path) + ":" + itoa(on.line) + " (" + on.side + ")",921+ Title: "comment on " + visibleText(on.path) + ":" + lines + " (" + on.side + ")",
917 Cancel: withFrom(self, windowStart(fileAt[on.path])) + "#" + fileIDs[on.path], Focus: true,922 Cancel: withFrom(self, windowStart(fileAt[on.path])) + "#" + fileIDs[on.path], Focus: true,
918 From: windowStart(fileAt[on.path]),923 From: windowStart(fileAt[on.path]),
919 }924 }
M internal/adapters/web/mr.templ +37 -39
@@ -109,41 +109,43 @@ templ mrListRail(p MRListPage) {
109109 // form, the checks, the settings, the commits and the files.
110110 templ MergeRequest(l Layout, p MRPage) {
111111 @Shell(l, mrRail(p)) {
112- <header class="show mr-h">
113- <h1><span class="mute">{ p.Number }</span> { p.Title }</h1>
114- <dl class="kv show-kv">
115- <dt>state</dt>
116- <dd class="mr-state" data-state={ p.State }>
117- { p.State }
118- if p.MergedShort != "" {
119- { " · " }<a href={ templ.SafeURL(p.MergedHref) }>{ p.MergedShort }</a>{ " · " + p.MergeMode }
120- }
121- </dd>
122- <dt>branch</dt>
123- <dd>
124- @branchLink(p.Source, p.SourceHref)
125- { " → " }
126- @branchLink(p.Target, p.TargetHref)
127- </dd>
128- <dt>opened</dt>
129- <dd>
130- { p.Opened.Name }
131- if p.Opened.WhenAt != "" {
132- { " · " }<time datetime={ p.Opened.WhenAt } title={ p.Opened.Abs }>{ p.Opened.When }</time>
112+ <section class="mr-card">
113+ <header class="show mr-h">
114+ <h1><span class="mute">{ p.Number }</span> { p.Title }</h1>
115+ <dl class="kv show-kv">
116+ <dt>state</dt>
117+ <dd class="mr-state" data-state={ p.State }>
118+ { p.State }
119+ if p.MergedShort != "" {
120+ { " · " }<a href={ templ.SafeURL(p.MergedHref) }>{ p.MergedShort }</a>{ " · " + p.MergeMode }
121+ }
122+ </dd>
123+ <dt>branch</dt>
124+ <dd>
125+ @branchLink(p.Source, p.SourceHref)
126+ { " → " }
127+ @branchLink(p.Target, p.TargetHref)
128+ </dd>
129+ <dt>opened</dt>
130+ <dd>
131+ { p.Opened.Name }
132+ if p.Opened.WhenAt != "" {
133+ { " · " }<time datetime={ p.Opened.WhenAt } title={ p.Opened.Abs }>{ p.Opened.When }</time>
134+ }
135+ </dd>
136+ if p.UpdatedAt != "" {
137+ <dt>updated</dt>
138+ <dd><time datetime={ p.UpdatedAt }>{ p.Updated }</time></dd>
133139 }
134- </dd>
135- if p.UpdatedAt != "" {
136- <dt>updated</dt>
137- <dd><time datetime={ p.UpdatedAt }>{ p.Updated }</time></dd>
138- }
139- </dl>
140- </header>
140+ </dl>
141+ </header>
142+ if p.Desc != "" {
143+ <article class="mr-desc prose" aria-label="description" hx-ignore>
144+ @templ.Raw(string(p.Desc))
145+ </article>
146+ }
147+ </section>
141148 @LiveSlot("mr-notice")
142- if p.Desc != "" {
143- <article class="mr-desc prose" aria-label="description" hx-ignore>
144- @templ.Raw(string(p.Desc))
145- </article>
146- }
147149 if p.Since != nil {
148150 <nav class="since" aria-label="Diff range">
149151 <a
@@ -431,9 +433,7 @@ templ mrRail(p MRPage) {
431433 templ mrBar(p MRPage) {
432434 <div class="diff-tools mr-bar" role="group" aria-label="Diff view and merge">
433435 <span class="dim">{ plural(p.NFiles, "file", "files") } · <span class="plus">+{ itoa(p.Adds) }</span> <span class="minus">-{ itoa(p.Dels) }</span></span>
434- @viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref)
435- @viewLink(p.DiffView, "split", "Split", "d", p.SplitHref)
436- @collapseButton()
436+ @diffViewMenu(p.DiffView)
437437 <span class="grow"></span>
438438 @MRChecksSum(p.ChecksSum)
439439 if p.Box != nil {
@@ -562,9 +562,7 @@ templ Compare(l Layout, p ComparePage) {
562562 <div class="diff-tools" role="group" aria-label="Diff view">
563563 <span class="dim">{ plural(p.NFiles, "file", "files") } · <span class="plus">+{ itoa(p.Adds) }</span> <span class="minus">-{ itoa(p.Dels) }</span></span>
564564 <span class="grow"></span>
565- @viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref)
566- @viewLink(p.DiffView, "split", "Split", "d", p.SplitHref)
567- @collapseButton()
565+ @diffViewMenu(p.DiffView)
568566 </div>
569567 if p.Invisible {
570568 <p class="notice">
@@ -109,41 +109,43 @@ templ mrListRail(p MRListPage) {
109 // form, the checks, the settings, the commits and the files.109 // form, the checks, the settings, the commits and the files.
110 templ MergeRequest(l Layout, p MRPage) {110 templ MergeRequest(l Layout, p MRPage) {
111 @Shell(l, mrRail(p)) {111 @Shell(l, mrRail(p)) {
112- <header class="show mr-h">112+ <section class="mr-card">
113- <h1><span class="mute">{ p.Number }</span> { p.Title }</h1>113+ <header class="show mr-h">
114- <dl class="kv show-kv">114+ <h1><span class="mute">{ p.Number }</span> { p.Title }</h1>
115- <dt>state</dt>115+ <dl class="kv show-kv">
116- <dd class="mr-state" data-state={ p.State }>116+ <dt>state</dt>
117- { p.State }117+ <dd class="mr-state" data-state={ p.State }>
118- if p.MergedShort != "" {118+ { p.State }
119- { " · " }<a href={ templ.SafeURL(p.MergedHref) }>{ p.MergedShort }</a>{ " · " + p.MergeMode }119+ if p.MergedShort != "" {
120- }120+ { " · " }<a href={ templ.SafeURL(p.MergedHref) }>{ p.MergedShort }</a>{ " · " + p.MergeMode }
121- </dd>121+ }
122- <dt>branch</dt>122+ </dd>
123- <dd>123+ <dt>branch</dt>
124- @branchLink(p.Source, p.SourceHref)124+ <dd>
125- { " → " }125+ @branchLink(p.Source, p.SourceHref)
126- @branchLink(p.Target, p.TargetHref)126+ { " → " }
127- </dd>127+ @branchLink(p.Target, p.TargetHref)
128- <dt>opened</dt>128+ </dd>
129- <dd>129+ <dt>opened</dt>
130- { p.Opened.Name }130+ <dd>
131- if p.Opened.WhenAt != "" {131+ { p.Opened.Name }
132- { " · " }<time datetime={ p.Opened.WhenAt } title={ p.Opened.Abs }>{ p.Opened.When }</time>132+ if p.Opened.WhenAt != "" {
133+ { " · " }<time datetime={ p.Opened.WhenAt } title={ p.Opened.Abs }>{ p.Opened.When }</time>
134+ }
135+ </dd>
136+ if p.UpdatedAt != "" {
137+ <dt>updated</dt>
138+ <dd><time datetime={ p.UpdatedAt }>{ p.Updated }</time></dd>
133 }139 }
134- </dd>140+ </dl>
135- if p.UpdatedAt != "" {141+ </header>
136- <dt>updated</dt>142+ if p.Desc != "" {
137- <dd><time datetime={ p.UpdatedAt }>{ p.Updated }</time></dd>143+ <article class="mr-desc prose" aria-label="description" hx-ignore>
138- }144+ @templ.Raw(string(p.Desc))
139- </dl>145+ </article>
140- </header>146+ }
147+ </section>
141 @LiveSlot("mr-notice")148 @LiveSlot("mr-notice")
142- if p.Desc != "" {
143- <article class="mr-desc prose" aria-label="description" hx-ignore>
144- @templ.Raw(string(p.Desc))
145- </article>
146- }
147 if p.Since != nil {149 if p.Since != nil {
148 <nav class="since" aria-label="Diff range">150 <nav class="since" aria-label="Diff range">
149 <a151 <a
@@ -431,9 +433,7 @@ templ mrRail(p MRPage) {
431 templ mrBar(p MRPage) {433 templ mrBar(p MRPage) {
432 <div class="diff-tools mr-bar" role="group" aria-label="Diff view and merge">434 <div class="diff-tools mr-bar" role="group" aria-label="Diff view and merge">
433 <span class="dim">{ plural(p.NFiles, "file", "files") } · <span class="plus">+{ itoa(p.Adds) }</span> <span class="minus">-{ itoa(p.Dels) }</span></span>435 <span class="dim">{ plural(p.NFiles, "file", "files") } · <span class="plus">+{ itoa(p.Adds) }</span> <span class="minus">-{ itoa(p.Dels) }</span></span>
434- @viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref)436+ @diffViewMenu(p.DiffView)
435- @viewLink(p.DiffView, "split", "Split", "d", p.SplitHref)
436- @collapseButton()
437 <span class="grow"></span>437 <span class="grow"></span>
438 @MRChecksSum(p.ChecksSum)438 @MRChecksSum(p.ChecksSum)
439 if p.Box != nil {439 if p.Box != nil {
@@ -562,9 +562,7 @@ templ Compare(l Layout, p ComparePage) {
562 <div class="diff-tools" role="group" aria-label="Diff view">562 <div class="diff-tools" role="group" aria-label="Diff view">
563 <span class="dim">{ plural(p.NFiles, "file", "files") } · <span class="plus">+{ itoa(p.Adds) }</span> <span class="minus">-{ itoa(p.Dels) }</span></span>563 <span class="dim">{ plural(p.NFiles, "file", "files") } · <span class="plus">+{ itoa(p.Adds) }</span> <span class="minus">-{ itoa(p.Dels) }</span></span>
564 <span class="grow"></span>564 <span class="grow"></span>
565- @viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref)565+ @diffViewMenu(p.DiffView)
566- @viewLink(p.DiffView, "split", "Split", "d", p.SplitHref)
567- @collapseButton()
568 </div>566 </div>
569 if p.Invisible {567 if p.Invisible {
570 <p class="notice">568 <p class="notice">
M internal/adapters/web/mr_templ.go +107 -123
@@ -553,14 +553,14 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
553553 }()
554554 }
555555 ctx = templ.InitializeContext(ctx)
556- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "<header class=\"show mr-h\"><h1><span class=\"mute\">")
556+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "<section class=\"mr-card\"><header class=\"show mr-h\"><h1><span class=\"mute\">")
557557 if templ_7745c5c3_Err != nil {
558558 return templ_7745c5c3_Err
559559 }
560560 var templ_7745c5c3_Var26 string
561561 templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(p.Number)
562562 if templ_7745c5c3_Err != nil {
563- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 113, Col: 36}
563+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 114, Col: 37}
564564 }
565565 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
566566 if templ_7745c5c3_Err != nil {
@@ -573,7 +573,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
573573 var templ_7745c5c3_Var27 string
574574 templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(p.Title)
575575 if templ_7745c5c3_Err != nil {
576- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 113, Col: 55}
576+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 114, Col: 56}
577577 }
578578 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
579579 if templ_7745c5c3_Err != nil {
@@ -586,7 +586,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
586586 var templ_7745c5c3_Var28 string
587587 templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.State)
588588 if templ_7745c5c3_Err != nil {
589- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 116, Col: 45}
589+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 117, Col: 46}
590590 }
591591 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var28)
592592 if templ_7745c5c3_Err != nil {
@@ -599,7 +599,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
599599 var templ_7745c5c3_Var29 string
600600 templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(p.State)
601601 if templ_7745c5c3_Err != nil {
602- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 117, Col: 14}
602+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 118, Col: 15}
603603 }
604604 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
605605 if templ_7745c5c3_Err != nil {
@@ -613,7 +613,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
613613 var templ_7745c5c3_Var30 string
614614 templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(" · ")
615615 if templ_7745c5c3_Err != nil {
616- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 119, Col: 14}
616+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 120, Col: 15}
617617 }
618618 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
619619 if templ_7745c5c3_Err != nil {
@@ -626,7 +626,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
626626 var templ_7745c5c3_Var31 templ.SafeURL
627627 templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.MergedHref))
628628 if templ_7745c5c3_Err != nil {
629- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 119, Col: 53}
629+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 120, Col: 54}
630630 }
631631 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
632632 if templ_7745c5c3_Err != nil {
@@ -639,7 +639,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
639639 var templ_7745c5c3_Var32 string
640640 templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(p.MergedShort)
641641 if templ_7745c5c3_Err != nil {
642- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 119, Col: 71}
642+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 120, Col: 72}
643643 }
644644 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
645645 if templ_7745c5c3_Err != nil {
@@ -652,7 +652,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
652652 var templ_7745c5c3_Var33 string
653653 templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(" · " + p.MergeMode)
654654 if templ_7745c5c3_Err != nil {
655- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 119, Col: 99}
655+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 120, Col: 100}
656656 }
657657 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
658658 if templ_7745c5c3_Err != nil {
@@ -670,7 +670,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
670670 var templ_7745c5c3_Var34 string
671671 templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(" → ")
672672 if templ_7745c5c3_Err != nil {
673- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 125, Col: 14}
673+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 126, Col: 15}
674674 }
675675 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
676676 if templ_7745c5c3_Err != nil {
@@ -687,7 +687,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
687687 var templ_7745c5c3_Var35 string
688688 templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(p.Opened.Name)
689689 if templ_7745c5c3_Err != nil {
690- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 130, Col: 20}
690+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 131, Col: 21}
691691 }
692692 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
693693 if templ_7745c5c3_Err != nil {
@@ -701,7 +701,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
701701 var templ_7745c5c3_Var36 string
702702 templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(" · ")
703703 if templ_7745c5c3_Err != nil {
704- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 132, Col: 14}
704+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 133, Col: 15}
705705 }
706706 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
707707 if templ_7745c5c3_Err != nil {
@@ -714,7 +714,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
714714 var templ_7745c5c3_Var37 string
715715 templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Opened.WhenAt)
716716 if templ_7745c5c3_Err != nil {
717- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 132, Col: 48}
717+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 133, Col: 49}
718718 }
719719 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var37)
720720 if templ_7745c5c3_Err != nil {
@@ -727,7 +727,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
727727 var templ_7745c5c3_Var38 string
728728 templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Opened.Abs)
729729 if templ_7745c5c3_Err != nil {
730- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 132, Col: 71}
730+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 133, Col: 72}
731731 }
732732 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var38)
733733 if templ_7745c5c3_Err != nil {
@@ -740,7 +740,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
740740 var templ_7745c5c3_Var39 string
741741 templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(p.Opened.When)
742742 if templ_7745c5c3_Err != nil {
743- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 132, Col: 89}
743+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 133, Col: 90}
744744 }
745745 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
746746 if templ_7745c5c3_Err != nil {
@@ -763,7 +763,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
763763 var templ_7745c5c3_Var40 string
764764 templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.UpdatedAt)
765765 if templ_7745c5c3_Err != nil {
766- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 137, Col: 37}
766+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 138, Col: 38}
767767 }
768768 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var40)
769769 if templ_7745c5c3_Err != nil {
@@ -776,7 +776,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
776776 var templ_7745c5c3_Var41 string
777777 templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(p.Updated)
778778 if templ_7745c5c3_Err != nil {
779- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 137, Col: 51}
779+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 138, Col: 52}
780780 }
781781 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
782782 if templ_7745c5c3_Err != nil {
@@ -791,16 +791,8 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
791791 if templ_7745c5c3_Err != nil {
792792 return templ_7745c5c3_Err
793793 }
794- templ_7745c5c3_Err = LiveSlot("mr-notice").Render(ctx, templ_7745c5c3_Buffer)
795- if templ_7745c5c3_Err != nil {
796- return templ_7745c5c3_Err
797- }
798- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, " ")
799- if templ_7745c5c3_Err != nil {
800- return templ_7745c5c3_Err
801- }
802794 if p.Desc != "" {
803- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "<article class=\"mr-desc prose\" aria-label=\"description\" hx-ignore>")
795+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "<article class=\"mr-desc prose\" aria-label=\"description\" hx-ignore>")
804796 if templ_7745c5c3_Err != nil {
805797 return templ_7745c5c3_Err
806798 }
@@ -808,11 +800,19 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
808800 if templ_7745c5c3_Err != nil {
809801 return templ_7745c5c3_Err
810802 }
811- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "</article>")
803+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "</article>")
812804 if templ_7745c5c3_Err != nil {
813805 return templ_7745c5c3_Err
814806 }
815807 }
808+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "</section>")
809+ if templ_7745c5c3_Err != nil {
810+ return templ_7745c5c3_Err
811+ }
812+ templ_7745c5c3_Err = LiveSlot("mr-notice").Render(ctx, templ_7745c5c3_Buffer)
813+ if templ_7745c5c3_Err != nil {
814+ return templ_7745c5c3_Err
815+ }
816816 templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, " ")
817817 if templ_7745c5c3_Err != nil {
818818 return templ_7745c5c3_Err
@@ -825,7 +825,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
825825 var templ_7745c5c3_Var42 templ.SafeURL
826826 templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Since.AllHref))
827827 if templ_7745c5c3_Err != nil {
828- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 150, Col: 42}
828+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 152, Col: 42}
829829 }
830830 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
831831 if templ_7745c5c3_Err != nil {
@@ -848,7 +848,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
848848 var templ_7745c5c3_Var43 string
849849 templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target)
850850 if templ_7745c5c3_Err != nil {
851- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 154, Col: 49}
851+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 156, Col: 49}
852852 }
853853 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
854854 if templ_7745c5c3_Err != nil {
@@ -861,7 +861,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
861861 var templ_7745c5c3_Var44 templ.SafeURL
862862 templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Since.SinceHref))
863863 if templ_7745c5c3_Err != nil {
864- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 156, Col: 44}
864+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 158, Col: 44}
865865 }
866866 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))
867867 if templ_7745c5c3_Err != nil {
@@ -884,7 +884,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
884884 var templ_7745c5c3_Var45 string
885885 templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(p.Since.ReadShort)
886886 if templ_7745c5c3_Err != nil {
887- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 160, Col: 64}
887+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 162, Col: 64}
888888 }
889889 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
890890 if templ_7745c5c3_Err != nil {
@@ -897,7 +897,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
897897 var templ_7745c5c3_Var46 string
898898 templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(p.Since.HeadShort)
899899 if templ_7745c5c3_Err != nil {
900- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 160, Col: 90}
900+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 162, Col: 90}
901901 }
902902 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))
903903 if templ_7745c5c3_Err != nil {
@@ -920,7 +920,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
920920 var templ_7745c5c3_Var47 string
921921 templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(p.SinceNote)
922922 if templ_7745c5c3_Err != nil {
923- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 164, Col: 34}
923+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 166, Col: 34}
924924 }
925925 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
926926 if templ_7745c5c3_Err != nil {
@@ -961,7 +961,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
961961 var templ_7745c5c3_Var48 string
962962 templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(p.Note)
963963 if templ_7745c5c3_Err != nil {
964- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 174, Col: 29}
964+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 176, Col: 29}
965965 }
966966 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48))
967967 if templ_7745c5c3_Err != nil {
@@ -984,7 +984,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
984984 var templ_7745c5c3_Var49 string
985985 templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View)
986986 if templ_7745c5c3_Err != nil {
987- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 180, Col: 22}
987+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 182, Col: 22}
988988 }
989989 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var49)
990990 if templ_7745c5c3_Err != nil {
@@ -1002,7 +1002,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
10021002 var templ_7745c5c3_Var50 string
10031003 templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View)
10041004 if templ_7745c5c3_Err != nil {
1005- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 182, Col: 22}
1005+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 184, Col: 22}
10061006 }
10071007 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var50)
10081008 if templ_7745c5c3_Err != nil {
@@ -1042,7 +1042,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
10421042 var templ_7745c5c3_Var51 string
10431043 templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.NUnresolved))
10441044 if templ_7745c5c3_Err != nil {
1045- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 195, Col: 97}
1045+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 197, Col: 97}
10461046 }
10471047 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51))
10481048 if templ_7745c5c3_Err != nil {
@@ -1055,7 +1055,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
10551055 var templ_7745c5c3_Var52 string
10561056 templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.NComments))
10571057 if templ_7745c5c3_Err != nil {
1058- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 195, Col: 126}
1058+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 197, Col: 126}
10591059 }
10601060 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var52))
10611061 if templ_7745c5c3_Err != nil {
@@ -1136,7 +1136,7 @@ func branchLink(name, href string) templ.Component {
11361136 var templ_7745c5c3_Var54 templ.SafeURL
11371137 templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(href))
11381138 if templ_7745c5c3_Err != nil {
1139- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 213, Col: 31}
1139+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 215, Col: 31}
11401140 }
11411141 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var54))
11421142 if templ_7745c5c3_Err != nil {
@@ -1149,7 +1149,7 @@ func branchLink(name, href string) templ.Component {
11491149 var templ_7745c5c3_Var55 string
11501150 templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.JoinStringErrs(name)
11511151 if templ_7745c5c3_Err != nil {
1152- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 213, Col: 40}
1152+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 215, Col: 40}
11531153 }
11541154 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var55))
11551155 if templ_7745c5c3_Err != nil {
@@ -1163,7 +1163,7 @@ func branchLink(name, href string) templ.Component {
11631163 var templ_7745c5c3_Var56 string
11641164 templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinStringErrs(name)
11651165 if templ_7745c5c3_Err != nil {
1166- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 215, Col: 8}
1166+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 217, Col: 8}
11671167 }
11681168 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56))
11691169 if templ_7745c5c3_Err != nil {
@@ -1244,7 +1244,7 @@ func commentView(c CommentView, suffix string) templ.Component {
12441244 var templ_7745c5c3_Var59 string
12451245 templ_7745c5c3_Var59, templ_7745c5c3_Err = templ.ResolveAttributeValue("c" + c.ID + suffix)
12461246 if templ_7745c5c3_Err != nil {
1247- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 234, Col: 42}
1247+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 236, Col: 42}
12481248 }
12491249 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var59)
12501250 if templ_7745c5c3_Err != nil {
@@ -1257,7 +1257,7 @@ func commentView(c CommentView, suffix string) templ.Component {
12571257 var templ_7745c5c3_Var60 string
12581258 templ_7745c5c3_Var60, templ_7745c5c3_Err = templ.ResolveAttributeValue(boolText(c.Resolved))
12591259 if templ_7745c5c3_Err != nil {
1260- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 234, Col: 81}
1260+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 236, Col: 81}
12611261 }
12621262 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var60)
12631263 if templ_7745c5c3_Err != nil {
@@ -1280,7 +1280,7 @@ func commentView(c CommentView, suffix string) templ.Component {
12801280 var templ_7745c5c3_Var61 string
12811281 templ_7745c5c3_Var61, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author)
12821282 if templ_7745c5c3_Err != nil {
1283- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 236, Col: 33}
1283+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 238, Col: 33}
12841284 }
12851285 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var61))
12861286 if templ_7745c5c3_Err != nil {
@@ -1294,7 +1294,7 @@ func commentView(c CommentView, suffix string) templ.Component {
12941294 var templ_7745c5c3_Var62 string
12951295 templ_7745c5c3_Var62, templ_7745c5c3_Err = templ.JoinStringErrs(" ")
12961296 if templ_7745c5c3_Err != nil {
1297- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 238, Col: 9}
1297+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 240, Col: 9}
12981298 }
12991299 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var62))
13001300 if templ_7745c5c3_Err != nil {
@@ -1307,7 +1307,7 @@ func commentView(c CommentView, suffix string) templ.Component {
13071307 var templ_7745c5c3_Var63 string
13081308 templ_7745c5c3_Var63, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt)
13091309 if templ_7745c5c3_Err != nil {
1310- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 238, Col: 49}
1310+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 240, Col: 49}
13111311 }
13121312 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var63)
13131313 if templ_7745c5c3_Err != nil {
@@ -1320,7 +1320,7 @@ func commentView(c CommentView, suffix string) templ.Component {
13201320 var templ_7745c5c3_Var64 string
13211321 templ_7745c5c3_Var64, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.Abs)
13221322 if templ_7745c5c3_Err != nil {
1323- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 238, Col: 65}
1323+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 240, Col: 65}
13241324 }
13251325 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var64)
13261326 if templ_7745c5c3_Err != nil {
@@ -1333,7 +1333,7 @@ func commentView(c CommentView, suffix string) templ.Component {
13331333 var templ_7745c5c3_Var65 string
13341334 templ_7745c5c3_Var65, templ_7745c5c3_Err = templ.JoinStringErrs(c.When)
13351335 if templ_7745c5c3_Err != nil {
1336- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 238, Col: 76}
1336+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 240, Col: 76}
13371337 }
13381338 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var65))
13391339 if templ_7745c5c3_Err != nil {
@@ -1348,7 +1348,7 @@ func commentView(c CommentView, suffix string) templ.Component {
13481348 var templ_7745c5c3_Var66 string
13491349 templ_7745c5c3_Var66, templ_7745c5c3_Err = templ.JoinStringErrs(" ")
13501350 if templ_7745c5c3_Err != nil {
1351- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 241, Col: 9}
1351+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 243, Col: 9}
13521352 }
13531353 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var66))
13541354 if templ_7745c5c3_Err != nil {
@@ -1363,7 +1363,7 @@ func commentView(c CommentView, suffix string) templ.Component {
13631363 var templ_7745c5c3_Var67 string
13641364 templ_7745c5c3_Var67, templ_7745c5c3_Err = templ.JoinStringErrs(" ")
13651365 if templ_7745c5c3_Err != nil {
1366- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 244, Col: 9}
1366+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 246, Col: 9}
13671367 }
13681368 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var67))
13691369 if templ_7745c5c3_Err != nil {
@@ -1378,7 +1378,7 @@ func commentView(c CommentView, suffix string) templ.Component {
13781378 var templ_7745c5c3_Var68 string
13791379 templ_7745c5c3_Var68, templ_7745c5c3_Err = templ.JoinStringErrs(" ")
13801380 if templ_7745c5c3_Err != nil {
1381- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 247, Col: 9}
1381+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 249, Col: 9}
13821382 }
13831383 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var68))
13841384 if templ_7745c5c3_Err != nil {
@@ -1391,7 +1391,7 @@ func commentView(c CommentView, suffix string) templ.Component {
13911391 var templ_7745c5c3_Var69 string
13921392 templ_7745c5c3_Var69, templ_7745c5c3_Err = templ.JoinStringErrs(c.Where)
13931393 if templ_7745c5c3_Err != nil {
1394- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 247, Col: 47}
1394+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 249, Col: 47}
13951395 }
13961396 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var69))
13971397 if templ_7745c5c3_Err != nil {
@@ -1466,7 +1466,7 @@ func commentView(c CommentView, suffix string) templ.Component {
14661466 var templ_7745c5c3_Var70 string
14671467 templ_7745c5c3_Var70, templ_7745c5c3_Err = templ.JoinStringErrs(c.Verdict)
14681468 if templ_7745c5c3_Err != nil {
1469- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 271, Col: 36}
1469+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 273, Col: 36}
14701470 }
14711471 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var70))
14721472 if templ_7745c5c3_Err != nil {
@@ -1485,7 +1485,7 @@ func commentView(c CommentView, suffix string) templ.Component {
14851485 var templ_7745c5c3_Var71 string
14861486 templ_7745c5c3_Var71, templ_7745c5c3_Err = templ.JoinStringErrs(c.Range)
14871487 if templ_7745c5c3_Err != nil {
1488- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 274, Col: 31}
1488+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 276, Col: 31}
14891489 }
14901490 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var71))
14911491 if templ_7745c5c3_Err != nil {
@@ -1504,7 +1504,7 @@ func commentView(c CommentView, suffix string) templ.Component {
15041504 var templ_7745c5c3_Var72 string
15051505 templ_7745c5c3_Var72, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.Quote)
15061506 if templ_7745c5c3_Err != nil {
1507- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 277, Col: 65}
1507+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 279, Col: 65}
15081508 }
15091509 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var72)
15101510 if templ_7745c5c3_Err != nil {
@@ -1523,7 +1523,7 @@ func commentView(c CommentView, suffix string) templ.Component {
15231523 var templ_7745c5c3_Var73 templ.SafeURL
15241524 templ_7745c5c3_Var73, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Edit.Action))
15251525 if templ_7745c5c3_Err != nil {
1526- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 280, Col: 92}
1526+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 282, Col: 92}
15271527 }
15281528 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var73))
15291529 if templ_7745c5c3_Err != nil {
@@ -1536,7 +1536,7 @@ func commentView(c CommentView, suffix string) templ.Component {
15361536 var templ_7745c5c3_Var74 string
15371537 templ_7745c5c3_Var74, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(c.Edit.From))
15381538 if templ_7745c5c3_Err != nil {
1539- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 280, Col: 153}
1539+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 282, Col: 153}
15401540 }
15411541 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var74)
15421542 if templ_7745c5c3_Err != nil {
@@ -1549,7 +1549,7 @@ func commentView(c CommentView, suffix string) templ.Component {
15491549 var templ_7745c5c3_Var75 string
15501550 templ_7745c5c3_Var75, templ_7745c5c3_Err = templ.JoinStringErrs(c.Edit.Text)
15511551 if templ_7745c5c3_Err != nil {
1552- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 280, Col: 209}
1552+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 282, Col: 209}
15531553 }
15541554 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var75))
15551555 if templ_7745c5c3_Err != nil {
@@ -1568,7 +1568,7 @@ func commentView(c CommentView, suffix string) templ.Component {
15681568 var templ_7745c5c3_Var76 templ.SafeURL
15691569 templ_7745c5c3_Var76, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Delete.Action))
15701570 if templ_7745c5c3_Err != nil {
1571- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 283, Col: 96}
1571+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 285, Col: 96}
15721572 }
15731573 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var76))
15741574 if templ_7745c5c3_Err != nil {
@@ -1581,7 +1581,7 @@ func commentView(c CommentView, suffix string) templ.Component {
15811581 var templ_7745c5c3_Var77 string
15821582 templ_7745c5c3_Var77, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(c.Delete.From))
15831583 if templ_7745c5c3_Err != nil {
1584- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 283, Col: 159}
1584+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 285, Col: 159}
15851585 }
15861586 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var77)
15871587 if templ_7745c5c3_Err != nil {
@@ -1620,7 +1620,7 @@ func commentView(c CommentView, suffix string) templ.Component {
16201620 var templ_7745c5c3_Var78 templ.SafeURL
16211621 templ_7745c5c3_Var78, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Resolve.Action))
16221622 if templ_7745c5c3_Err != nil {
1623- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 294, Col: 77}
1623+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 296, Col: 77}
16241624 }
16251625 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var78))
16261626 if templ_7745c5c3_Err != nil {
@@ -1633,7 +1633,7 @@ func commentView(c CommentView, suffix string) templ.Component {
16331633 var templ_7745c5c3_Var79 string
16341634 templ_7745c5c3_Var79, templ_7745c5c3_Err = templ.ResolveAttributeValue(boolText(c.Resolve.Value))
16351635 if templ_7745c5c3_Err != nil {
1636- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 295, Col: 74}
1636+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 297, Col: 74}
16371637 }
16381638 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var79)
16391639 if templ_7745c5c3_Err != nil {
@@ -1651,7 +1651,7 @@ func commentView(c CommentView, suffix string) templ.Component {
16511651 var templ_7745c5c3_Var80 string
16521652 templ_7745c5c3_Var80, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(c.Resolve.From))
16531653 if templ_7745c5c3_Err != nil {
1654- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 297, Col: 66}
1654+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 299, Col: 66}
16551655 }
16561656 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var80)
16571657 if templ_7745c5c3_Err != nil {
@@ -1669,7 +1669,7 @@ func commentView(c CommentView, suffix string) templ.Component {
16691669 var templ_7745c5c3_Var81 string
16701670 templ_7745c5c3_Var81, templ_7745c5c3_Err = templ.JoinStringErrs(c.Resolve.Label)
16711671 if templ_7745c5c3_Err != nil {
1672- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 299, Col: 55}
1672+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 301, Col: 55}
16731673 }
16741674 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var81))
16751675 if templ_7745c5c3_Err != nil {
@@ -1724,7 +1724,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
17241724 var templ_7745c5c3_Var83 templ.SafeURL
17251725 templ_7745c5c3_Var83, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(f.Action))
17261726 if templ_7745c5c3_Err != nil {
1727- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 314, Col: 67}
1727+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 316, Col: 67}
17281728 }
17291729 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var83))
17301730 if templ_7745c5c3_Err != nil {
@@ -1737,7 +1737,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
17371737 var templ_7745c5c3_Var84 string
17381738 templ_7745c5c3_Var84, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.ID + suffix)
17391739 if templ_7745c5c3_Err != nil {
1740- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 315, Col: 28}
1740+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 317, Col: 28}
17411741 }
17421742 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var84)
17431743 if templ_7745c5c3_Err != nil {
@@ -1750,7 +1750,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
17501750 var templ_7745c5c3_Var85 string
17511751 templ_7745c5c3_Var85, templ_7745c5c3_Err = templ.JoinStringErrs(f.Title)
17521752 if templ_7745c5c3_Err != nil {
1753- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 315, Col: 40}
1753+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 317, Col: 40}
17541754 }
17551755 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var85))
17561756 if templ_7745c5c3_Err != nil {
@@ -1763,7 +1763,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
17631763 var templ_7745c5c3_Var86 string
17641764 templ_7745c5c3_Var86, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.ID + suffix)
17651765 if templ_7745c5c3_Err != nil {
1766- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 317, Col: 21}
1766+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 319, Col: 21}
17671767 }
17681768 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var86)
17691769 if templ_7745c5c3_Err != nil {
@@ -1802,7 +1802,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
18021802 var templ_7745c5c3_Var87 string
18031803 templ_7745c5c3_Var87, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.Path)
18041804 if templ_7745c5c3_Err != nil {
1805- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 332, Col: 50}
1805+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 334, Col: 50}
18061806 }
18071807 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var87)
18081808 if templ_7745c5c3_Err != nil {
@@ -1815,7 +1815,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
18151815 var templ_7745c5c3_Var88 string
18161816 templ_7745c5c3_Var88, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.Line)
18171817 if templ_7745c5c3_Err != nil {
1818- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 333, Col: 50}
1818+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 335, Col: 50}
18191819 }
18201820 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var88)
18211821 if templ_7745c5c3_Err != nil {
@@ -1828,7 +1828,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
18281828 var templ_7745c5c3_Var89 string
18291829 templ_7745c5c3_Var89, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.Side)
18301830 if templ_7745c5c3_Err != nil {
1831- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 334, Col: 50}
1831+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 336, Col: 50}
18321832 }
18331833 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var89)
18341834 if templ_7745c5c3_Err != nil {
@@ -1841,7 +1841,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
18411841 var templ_7745c5c3_Var90 string
18421842 templ_7745c5c3_Var90, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.StartLine)
18431843 if templ_7745c5c3_Err != nil {
1844- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 335, Col: 86}
1844+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 337, Col: 86}
18451845 }
18461846 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var90)
18471847 if templ_7745c5c3_Err != nil {
@@ -1860,7 +1860,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
18601860 var templ_7745c5c3_Var91 string
18611861 templ_7745c5c3_Var91, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.ReplyTo)
18621862 if templ_7745c5c3_Err != nil {
1863- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 338, Col: 57}
1863+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 340, Col: 57}
18641864 }
18651865 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var91)
18661866 if templ_7745c5c3_Err != nil {
@@ -1885,7 +1885,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
18851885 var templ_7745c5c3_Var92 string
18861886 templ_7745c5c3_Var92, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(f.From))
18871887 if templ_7745c5c3_Err != nil {
1888- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 344, Col: 56}
1888+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 346, Col: 56}
18891889 }
18901890 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var92)
18911891 if templ_7745c5c3_Err != nil {
@@ -1908,7 +1908,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
19081908 var templ_7745c5c3_Var93 templ.SafeURL
19091909 templ_7745c5c3_Var93, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(f.Cancel))
19101910 if templ_7745c5c3_Err != nil {
1911- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 349, Col: 49}
1911+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 351, Col: 49}
19121912 }
19131913 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var93))
19141914 if templ_7745c5c3_Err != nil {
@@ -1955,7 +1955,7 @@ func mrRail(p MRPage) templ.Component {
19551955 var templ_7745c5c3_Var95 string
19561956 templ_7745c5c3_Var95, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Files)))
19571957 if templ_7745c5c3_Err != nil {
1958- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 358, Col: 49}
1958+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 360, Col: 49}
19591959 }
19601960 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var95))
19611961 if templ_7745c5c3_Err != nil {
@@ -1986,7 +1986,7 @@ func mrRail(p MRPage) templ.Component {
19861986 var templ_7745c5c3_Var96 templ.SafeURL
19871987 templ_7745c5c3_Var96, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(v.Href))
19881988 if templ_7745c5c3_Err != nil {
1989- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 371, Col: 37}
1989+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 373, Col: 37}
19901990 }
19911991 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var96))
19921992 if templ_7745c5c3_Err != nil {
@@ -1999,7 +1999,7 @@ func mrRail(p MRPage) templ.Component {
19991999 var templ_7745c5c3_Var97 string
20002000 templ_7745c5c3_Var97, templ_7745c5c3_Err = templ.JoinStringErrs(v.Author)
20012001 if templ_7745c5c3_Err != nil {
2002- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 371, Col: 50}
2002+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 373, Col: 50}
20032003 }
20042004 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var97))
20052005 if templ_7745c5c3_Err != nil {
@@ -2012,7 +2012,7 @@ func mrRail(p MRPage) templ.Component {
20122012 var templ_7745c5c3_Var98 string
20132013 templ_7745c5c3_Var98, templ_7745c5c3_Err = templ.JoinStringErrs(v.Label)
20142014 if templ_7745c5c3_Err != nil {
2015- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 371, Col: 63}
2015+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 373, Col: 63}
20162016 }
20172017 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var98))
20182018 if templ_7745c5c3_Err != nil {
@@ -2049,7 +2049,7 @@ func mrRail(p MRPage) templ.Component {
20492049 var templ_7745c5c3_Var99 string
20502050 templ_7745c5c3_Var99, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Commits)))
20512051 if templ_7745c5c3_Err != nil {
2052- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 382, Col: 71}
2052+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 384, Col: 71}
20532053 }
20542054 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var99))
20552055 if templ_7745c5c3_Err != nil {
@@ -2077,7 +2077,7 @@ func mrRail(p MRPage) templ.Component {
20772077 var templ_7745c5c3_Var100 templ.SafeURL
20782078 templ_7745c5c3_Var100, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Href))
20792079 if templ_7745c5c3_Err != nil {
2080- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 386, Col: 36}
2080+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 388, Col: 36}
20812081 }
20822082 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var100))
20832083 if templ_7745c5c3_Err != nil {
@@ -2090,7 +2090,7 @@ func mrRail(p MRPage) templ.Component {
20902090 var templ_7745c5c3_Var101 string
20912091 templ_7745c5c3_Var101, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short)
20922092 if templ_7745c5c3_Err != nil {
2093- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 386, Col: 48}
2093+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 388, Col: 48}
20942094 }
20952095 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var101))
20962096 if templ_7745c5c3_Err != nil {
@@ -2103,7 +2103,7 @@ func mrRail(p MRPage) templ.Component {
21032103 var templ_7745c5c3_Var102 string
21042104 templ_7745c5c3_Var102, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject)
21052105 if templ_7745c5c3_Err != nil {
2106- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 387, Col: 32}
2106+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 389, Col: 32}
21072107 }
21082108 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var102))
21092109 if templ_7745c5c3_Err != nil {
@@ -2126,7 +2126,7 @@ func mrRail(p MRPage) templ.Component {
21262126 var templ_7745c5c3_Var103 string
21272127 templ_7745c5c3_Var103, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author)
21282128 if templ_7745c5c3_Err != nil {
2129- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 392, Col: 16}
2129+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 394, Col: 16}
21302130 }
21312131 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var103))
21322132 if templ_7745c5c3_Err != nil {
@@ -2140,7 +2140,7 @@ func mrRail(p MRPage) templ.Component {
21402140 var templ_7745c5c3_Var104 string
21412141 templ_7745c5c3_Var104, templ_7745c5c3_Err = templ.JoinStringErrs(" · ")
21422142 if templ_7745c5c3_Err != nil {
2143- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 394, Col: 15}
2143+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 396, Col: 15}
21442144 }
21452145 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var104))
21462146 if templ_7745c5c3_Err != nil {
@@ -2153,7 +2153,7 @@ func mrRail(p MRPage) templ.Component {
21532153 var templ_7745c5c3_Var105 string
21542154 templ_7745c5c3_Var105, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt)
21552155 if templ_7745c5c3_Err != nil {
2156- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 394, Col: 42}
2156+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 396, Col: 42}
21572157 }
21582158 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var105)
21592159 if templ_7745c5c3_Err != nil {
@@ -2166,7 +2166,7 @@ func mrRail(p MRPage) templ.Component {
21662166 var templ_7745c5c3_Var106 string
21672167 templ_7745c5c3_Var106, templ_7745c5c3_Err = templ.JoinStringErrs(c.When)
21682168 if templ_7745c5c3_Err != nil {
2169- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 394, Col: 53}
2169+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 396, Col: 53}
21702170 }
21712171 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var106))
21722172 if templ_7745c5c3_Err != nil {
@@ -2181,7 +2181,7 @@ func mrRail(p MRPage) templ.Component {
21812181 var templ_7745c5c3_Var107 string
21822182 templ_7745c5c3_Var107, templ_7745c5c3_Err = templ.JoinStringErrs(" · new")
21832183 if templ_7745c5c3_Err != nil {
2184- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 397, Col: 18}
2184+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 399, Col: 18}
21852185 }
21862186 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var107))
21872187 if templ_7745c5c3_Err != nil {
@@ -2211,7 +2211,7 @@ func mrRail(p MRPage) templ.Component {
22112211 var templ_7745c5c3_Var108 string
22122212 templ_7745c5c3_Var108, templ_7745c5c3_Err = templ.JoinStringErrs(p.CommitsNote)
22132213 if templ_7745c5c3_Err != nil {
2214- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 407, Col: 34}
2214+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 409, Col: 34}
22152215 }
22162216 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var108))
22172217 if templ_7745c5c3_Err != nil {
@@ -2229,7 +2229,7 @@ func mrRail(p MRPage) templ.Component {
22292229 var templ_7745c5c3_Var109 string
22302230 templ_7745c5c3_Var109, templ_7745c5c3_Err = templ.JoinStringErrs(strings.Join(p.Modes, ", "))
22312231 if templ_7745c5c3_Err != nil {
2232- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 414, Col: 36}
2232+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 416, Col: 36}
22332233 }
22342234 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var109))
22352235 if templ_7745c5c3_Err != nil {
@@ -2290,7 +2290,7 @@ func mrBar(p MRPage) templ.Component {
22902290 var templ_7745c5c3_Var111 string
22912291 templ_7745c5c3_Var111, templ_7745c5c3_Err = templ.JoinStringErrs(plural(p.NFiles, "file", "files"))
22922292 if templ_7745c5c3_Err != nil {
2293- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 433, Col: 55}
2293+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 435, Col: 55}
22942294 }
22952295 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var111))
22962296 if templ_7745c5c3_Err != nil {
@@ -2303,7 +2303,7 @@ func mrBar(p MRPage) templ.Component {
23032303 var templ_7745c5c3_Var112 string
23042304 templ_7745c5c3_Var112, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Adds))
23052305 if templ_7745c5c3_Err != nil {
2306- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 433, Col: 95}
2306+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 435, Col: 95}
23072307 }
23082308 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var112))
23092309 if templ_7745c5c3_Err != nil {
@@ -2316,7 +2316,7 @@ func mrBar(p MRPage) templ.Component {
23162316 var templ_7745c5c3_Var113 string
23172317 templ_7745c5c3_Var113, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Dels))
23182318 if templ_7745c5c3_Err != nil {
2319- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 433, Col: 140}
2319+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 435, Col: 140}
23202320 }
23212321 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var113))
23222322 if templ_7745c5c3_Err != nil {
@@ -2326,15 +2326,7 @@ func mrBar(p MRPage) templ.Component {
23262326 if templ_7745c5c3_Err != nil {
23272327 return templ_7745c5c3_Err
23282328 }
2329- templ_7745c5c3_Err = viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref).Render(ctx, templ_7745c5c3_Buffer)
2330- if templ_7745c5c3_Err != nil {
2331- return templ_7745c5c3_Err
2332- }
2333- templ_7745c5c3_Err = viewLink(p.DiffView, "split", "Split", "d", p.SplitHref).Render(ctx, templ_7745c5c3_Buffer)
2334- if templ_7745c5c3_Err != nil {
2335- return templ_7745c5c3_Err
2336- }
2337- templ_7745c5c3_Err = collapseButton().Render(ctx, templ_7745c5c3_Buffer)
2329+ templ_7745c5c3_Err = diffViewMenu(p.DiffView).Render(ctx, templ_7745c5c3_Buffer)
23382330 if templ_7745c5c3_Err != nil {
23392331 return templ_7745c5c3_Err
23402332 }
@@ -2998,15 +2990,7 @@ func Compare(l Layout, p ComparePage) templ.Component {
29982990 if templ_7745c5c3_Err != nil {
29992991 return templ_7745c5c3_Err
30002992 }
3001- templ_7745c5c3_Err = viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref).Render(ctx, templ_7745c5c3_Buffer)
3002- if templ_7745c5c3_Err != nil {
3003- return templ_7745c5c3_Err
3004- }
3005- templ_7745c5c3_Err = viewLink(p.DiffView, "split", "Split", "d", p.SplitHref).Render(ctx, templ_7745c5c3_Buffer)
3006- if templ_7745c5c3_Err != nil {
3007- return templ_7745c5c3_Err
3008- }
3009- templ_7745c5c3_Err = collapseButton().Render(ctx, templ_7745c5c3_Buffer)
2993+ templ_7745c5c3_Err = diffViewMenu(p.DiffView).Render(ctx, templ_7745c5c3_Buffer)
30102994 if templ_7745c5c3_Err != nil {
30112995 return templ_7745c5c3_Err
30122996 }
@@ -3032,7 +3016,7 @@ func Compare(l Layout, p ComparePage) templ.Component {
30323016 var templ_7745c5c3_Var148 string
30333017 templ_7745c5c3_Var148, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View)
30343018 if templ_7745c5c3_Err != nil {
3035- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 578, Col: 23}
3019+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 576, Col: 23}
30363020 }
30373021 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var148)
30383022 if templ_7745c5c3_Err != nil {
@@ -3050,7 +3034,7 @@ func Compare(l Layout, p ComparePage) templ.Component {
30503034 var templ_7745c5c3_Var149 string
30513035 templ_7745c5c3_Var149, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View)
30523036 if templ_7745c5c3_Err != nil {
3053- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 580, Col: 23}
3037+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 578, Col: 23}
30543038 }
30553039 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var149)
30563040 if templ_7745c5c3_Err != nil {
@@ -3120,7 +3104,7 @@ func compareRail(p ComparePage) templ.Component {
31203104 var templ_7745c5c3_Var151 string
31213105 templ_7745c5c3_Var151, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Ahead)))
31223106 if templ_7745c5c3_Err != nil {
3123- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 596, Col: 67}
3107+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 594, Col: 67}
31243108 }
31253109 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var151))
31263110 if templ_7745c5c3_Err != nil {
@@ -3138,7 +3122,7 @@ func compareRail(p ComparePage) templ.Component {
31383122 var templ_7745c5c3_Var152 templ.SafeURL
31393123 templ_7745c5c3_Var152, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref))
31403124 if templ_7745c5c3_Err != nil {
3141- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 600, Col: 37}
3125+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 598, Col: 37}
31423126 }
31433127 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var152))
31443128 if templ_7745c5c3_Err != nil {
@@ -3151,7 +3135,7 @@ func compareRail(p ComparePage) templ.Component {
31513135 var templ_7745c5c3_Var153 string
31523136 templ_7745c5c3_Var153, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short)
31533137 if templ_7745c5c3_Err != nil {
3154- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 600, Col: 49}
3138+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 598, Col: 49}
31553139 }
31563140 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var153))
31573141 if templ_7745c5c3_Err != nil {
@@ -3164,7 +3148,7 @@ func compareRail(p ComparePage) templ.Component {
31643148 var templ_7745c5c3_Var154 string
31653149 templ_7745c5c3_Var154, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject)
31663150 if templ_7745c5c3_Err != nil {
3167- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 601, Col: 32}
3151+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 599, Col: 32}
31683152 }
31693153 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var154))
31703154 if templ_7745c5c3_Err != nil {
@@ -3177,7 +3161,7 @@ func compareRail(p ComparePage) templ.Component {
31773161 var templ_7745c5c3_Var155 string
31783162 templ_7745c5c3_Var155, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author)
31793163 if templ_7745c5c3_Err != nil {
3180- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 603, Col: 16}
3164+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 601, Col: 16}
31813165 }
31823166 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var155))
31833167 if templ_7745c5c3_Err != nil {
@@ -3191,7 +3175,7 @@ func compareRail(p ComparePage) templ.Component {
31913175 var templ_7745c5c3_Var156 string
31923176 templ_7745c5c3_Var156, templ_7745c5c3_Err = templ.JoinStringErrs(" · ")
31933177 if templ_7745c5c3_Err != nil {
3194- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 605, Col: 15}
3178+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 603, Col: 15}
31953179 }
31963180 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var156))
31973181 if templ_7745c5c3_Err != nil {
@@ -3204,7 +3188,7 @@ func compareRail(p ComparePage) templ.Component {
32043188 var templ_7745c5c3_Var157 string
32053189 templ_7745c5c3_Var157, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt)
32063190 if templ_7745c5c3_Err != nil {
3207- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 605, Col: 42}
3191+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 603, Col: 42}
32083192 }
32093193 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var157)
32103194 if templ_7745c5c3_Err != nil {
@@ -3217,7 +3201,7 @@ func compareRail(p ComparePage) templ.Component {
32173201 var templ_7745c5c3_Var158 string
32183202 templ_7745c5c3_Var158, templ_7745c5c3_Err = templ.JoinStringErrs(c.When)
32193203 if templ_7745c5c3_Err != nil {
3220- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 605, Col: 53}
3204+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 603, Col: 53}
32213205 }
32223206 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var158))
32233207 if templ_7745c5c3_Err != nil {
@@ -3245,7 +3229,7 @@ func compareRail(p ComparePage) templ.Component {
32453229 var templ_7745c5c3_Var159 string
32463230 templ_7745c5c3_Var159, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target)
32473231 if templ_7745c5c3_Err != nil {
3248- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 612, Col: 47}
3232+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 610, Col: 47}
32493233 }
32503234 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var159))
32513235 if templ_7745c5c3_Err != nil {
@@ -3268,7 +3252,7 @@ func compareRail(p ComparePage) templ.Component {
32683252 var templ_7745c5c3_Var160 string
32693253 templ_7745c5c3_Var160, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Files)))
32703254 if templ_7745c5c3_Err != nil {
3271- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 617, Col: 66}
3255+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 615, Col: 66}
32723256 }
32733257 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var160))
32743258 if templ_7745c5c3_Err != nil {
@@ -553,14 +553,14 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
553 }()553 }()
554 }554 }
555 ctx = templ.InitializeContext(ctx)555 ctx = templ.InitializeContext(ctx)
556- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "<header class=\"show mr-h\"><h1><span class=\"mute\">")556+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "<section class=\"mr-card\"><header class=\"show mr-h\"><h1><span class=\"mute\">")
557 if templ_7745c5c3_Err != nil {557 if templ_7745c5c3_Err != nil {
558 return templ_7745c5c3_Err558 return templ_7745c5c3_Err
559 }559 }
560 var templ_7745c5c3_Var26 string560 var templ_7745c5c3_Var26 string
561 templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(p.Number)561 templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(p.Number)
562 if templ_7745c5c3_Err != nil {562 if templ_7745c5c3_Err != nil {
563- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 113, Col: 36}563+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 114, Col: 37}
564 }564 }
565 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))565 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
566 if templ_7745c5c3_Err != nil {566 if templ_7745c5c3_Err != nil {
@@ -573,7 +573,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
573 var templ_7745c5c3_Var27 string573 var templ_7745c5c3_Var27 string
574 templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(p.Title)574 templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(p.Title)
575 if templ_7745c5c3_Err != nil {575 if templ_7745c5c3_Err != nil {
576- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 113, Col: 55}576+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 114, Col: 56}
577 }577 }
578 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))578 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
579 if templ_7745c5c3_Err != nil {579 if templ_7745c5c3_Err != nil {
@@ -586,7 +586,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
586 var templ_7745c5c3_Var28 string586 var templ_7745c5c3_Var28 string
587 templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.State)587 templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.State)
588 if templ_7745c5c3_Err != nil {588 if templ_7745c5c3_Err != nil {
589- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 116, Col: 45}589+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 117, Col: 46}
590 }590 }
591 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var28)591 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var28)
592 if templ_7745c5c3_Err != nil {592 if templ_7745c5c3_Err != nil {
@@ -599,7 +599,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
599 var templ_7745c5c3_Var29 string599 var templ_7745c5c3_Var29 string
600 templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(p.State)600 templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(p.State)
601 if templ_7745c5c3_Err != nil {601 if templ_7745c5c3_Err != nil {
602- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 117, Col: 14}602+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 118, Col: 15}
603 }603 }
604 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))604 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
605 if templ_7745c5c3_Err != nil {605 if templ_7745c5c3_Err != nil {
@@ -613,7 +613,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
613 var templ_7745c5c3_Var30 string613 var templ_7745c5c3_Var30 string
614 templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(" · ")614 templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(" · ")
615 if templ_7745c5c3_Err != nil {615 if templ_7745c5c3_Err != nil {
616- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 119, Col: 14}616+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 120, Col: 15}
617 }617 }
618 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))618 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
619 if templ_7745c5c3_Err != nil {619 if templ_7745c5c3_Err != nil {
@@ -626,7 +626,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
626 var templ_7745c5c3_Var31 templ.SafeURL626 var templ_7745c5c3_Var31 templ.SafeURL
627 templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.MergedHref))627 templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.MergedHref))
628 if templ_7745c5c3_Err != nil {628 if templ_7745c5c3_Err != nil {
629- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 119, Col: 53}629+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 120, Col: 54}
630 }630 }
631 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))631 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
632 if templ_7745c5c3_Err != nil {632 if templ_7745c5c3_Err != nil {
@@ -639,7 +639,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
639 var templ_7745c5c3_Var32 string639 var templ_7745c5c3_Var32 string
640 templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(p.MergedShort)640 templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(p.MergedShort)
641 if templ_7745c5c3_Err != nil {641 if templ_7745c5c3_Err != nil {
642- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 119, Col: 71}642+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 120, Col: 72}
643 }643 }
644 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))644 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
645 if templ_7745c5c3_Err != nil {645 if templ_7745c5c3_Err != nil {
@@ -652,7 +652,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
652 var templ_7745c5c3_Var33 string652 var templ_7745c5c3_Var33 string
653 templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(" · " + p.MergeMode)653 templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(" · " + p.MergeMode)
654 if templ_7745c5c3_Err != nil {654 if templ_7745c5c3_Err != nil {
655- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 119, Col: 99}655+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 120, Col: 100}
656 }656 }
657 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))657 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
658 if templ_7745c5c3_Err != nil {658 if templ_7745c5c3_Err != nil {
@@ -670,7 +670,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
670 var templ_7745c5c3_Var34 string670 var templ_7745c5c3_Var34 string
671 templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(" → ")671 templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(" → ")
672 if templ_7745c5c3_Err != nil {672 if templ_7745c5c3_Err != nil {
673- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 125, Col: 14}673+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 126, Col: 15}
674 }674 }
675 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))675 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
676 if templ_7745c5c3_Err != nil {676 if templ_7745c5c3_Err != nil {
@@ -687,7 +687,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
687 var templ_7745c5c3_Var35 string687 var templ_7745c5c3_Var35 string
688 templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(p.Opened.Name)688 templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(p.Opened.Name)
689 if templ_7745c5c3_Err != nil {689 if templ_7745c5c3_Err != nil {
690- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 130, Col: 20}690+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 131, Col: 21}
691 }691 }
692 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))692 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
693 if templ_7745c5c3_Err != nil {693 if templ_7745c5c3_Err != nil {
@@ -701,7 +701,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
701 var templ_7745c5c3_Var36 string701 var templ_7745c5c3_Var36 string
702 templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(" · ")702 templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(" · ")
703 if templ_7745c5c3_Err != nil {703 if templ_7745c5c3_Err != nil {
704- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 132, Col: 14}704+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 133, Col: 15}
705 }705 }
706 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))706 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
707 if templ_7745c5c3_Err != nil {707 if templ_7745c5c3_Err != nil {
@@ -714,7 +714,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
714 var templ_7745c5c3_Var37 string714 var templ_7745c5c3_Var37 string
715 templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Opened.WhenAt)715 templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Opened.WhenAt)
716 if templ_7745c5c3_Err != nil {716 if templ_7745c5c3_Err != nil {
717- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 132, Col: 48}717+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 133, Col: 49}
718 }718 }
719 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var37)719 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var37)
720 if templ_7745c5c3_Err != nil {720 if templ_7745c5c3_Err != nil {
@@ -727,7 +727,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
727 var templ_7745c5c3_Var38 string727 var templ_7745c5c3_Var38 string
728 templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Opened.Abs)728 templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Opened.Abs)
729 if templ_7745c5c3_Err != nil {729 if templ_7745c5c3_Err != nil {
730- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 132, Col: 71}730+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 133, Col: 72}
731 }731 }
732 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var38)732 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var38)
733 if templ_7745c5c3_Err != nil {733 if templ_7745c5c3_Err != nil {
@@ -740,7 +740,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
740 var templ_7745c5c3_Var39 string740 var templ_7745c5c3_Var39 string
741 templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(p.Opened.When)741 templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(p.Opened.When)
742 if templ_7745c5c3_Err != nil {742 if templ_7745c5c3_Err != nil {
743- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 132, Col: 89}743+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 133, Col: 90}
744 }744 }
745 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))745 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
746 if templ_7745c5c3_Err != nil {746 if templ_7745c5c3_Err != nil {
@@ -763,7 +763,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
763 var templ_7745c5c3_Var40 string763 var templ_7745c5c3_Var40 string
764 templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.UpdatedAt)764 templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.UpdatedAt)
765 if templ_7745c5c3_Err != nil {765 if templ_7745c5c3_Err != nil {
766- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 137, Col: 37}766+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 138, Col: 38}
767 }767 }
768 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var40)768 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var40)
769 if templ_7745c5c3_Err != nil {769 if templ_7745c5c3_Err != nil {
@@ -776,7 +776,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
776 var templ_7745c5c3_Var41 string776 var templ_7745c5c3_Var41 string
777 templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(p.Updated)777 templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(p.Updated)
778 if templ_7745c5c3_Err != nil {778 if templ_7745c5c3_Err != nil {
779- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 137, Col: 51}779+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 138, Col: 52}
780 }780 }
781 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))781 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
782 if templ_7745c5c3_Err != nil {782 if templ_7745c5c3_Err != nil {
@@ -791,16 +791,8 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
791 if templ_7745c5c3_Err != nil {791 if templ_7745c5c3_Err != nil {
792 return templ_7745c5c3_Err792 return templ_7745c5c3_Err
793 }793 }
794- templ_7745c5c3_Err = LiveSlot("mr-notice").Render(ctx, templ_7745c5c3_Buffer)
795- if templ_7745c5c3_Err != nil {
796- return templ_7745c5c3_Err
797- }
798- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, " ")
799- if templ_7745c5c3_Err != nil {
800- return templ_7745c5c3_Err
801- }
802 if p.Desc != "" {794 if p.Desc != "" {
803- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "<article class=\"mr-desc prose\" aria-label=\"description\" hx-ignore>")795+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "<article class=\"mr-desc prose\" aria-label=\"description\" hx-ignore>")
804 if templ_7745c5c3_Err != nil {796 if templ_7745c5c3_Err != nil {
805 return templ_7745c5c3_Err797 return templ_7745c5c3_Err
806 }798 }
@@ -808,11 +800,19 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
808 if templ_7745c5c3_Err != nil {800 if templ_7745c5c3_Err != nil {
809 return templ_7745c5c3_Err801 return templ_7745c5c3_Err
810 }802 }
811- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "</article>")803+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "</article>")
812 if templ_7745c5c3_Err != nil {804 if templ_7745c5c3_Err != nil {
813 return templ_7745c5c3_Err805 return templ_7745c5c3_Err
814 }806 }
815 }807 }
808+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "</section>")
809+ if templ_7745c5c3_Err != nil {
810+ return templ_7745c5c3_Err
811+ }
812+ templ_7745c5c3_Err = LiveSlot("mr-notice").Render(ctx, templ_7745c5c3_Buffer)
813+ if templ_7745c5c3_Err != nil {
814+ return templ_7745c5c3_Err
815+ }
816 templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, " ")816 templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, " ")
817 if templ_7745c5c3_Err != nil {817 if templ_7745c5c3_Err != nil {
818 return templ_7745c5c3_Err818 return templ_7745c5c3_Err
@@ -825,7 +825,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
825 var templ_7745c5c3_Var42 templ.SafeURL825 var templ_7745c5c3_Var42 templ.SafeURL
826 templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Since.AllHref))826 templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Since.AllHref))
827 if templ_7745c5c3_Err != nil {827 if templ_7745c5c3_Err != nil {
828- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 150, Col: 42}828+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 152, Col: 42}
829 }829 }
830 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))830 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
831 if templ_7745c5c3_Err != nil {831 if templ_7745c5c3_Err != nil {
@@ -848,7 +848,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
848 var templ_7745c5c3_Var43 string848 var templ_7745c5c3_Var43 string
849 templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target)849 templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target)
850 if templ_7745c5c3_Err != nil {850 if templ_7745c5c3_Err != nil {
851- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 154, Col: 49}851+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 156, Col: 49}
852 }852 }
853 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))853 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
854 if templ_7745c5c3_Err != nil {854 if templ_7745c5c3_Err != nil {
@@ -861,7 +861,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
861 var templ_7745c5c3_Var44 templ.SafeURL861 var templ_7745c5c3_Var44 templ.SafeURL
862 templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Since.SinceHref))862 templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Since.SinceHref))
863 if templ_7745c5c3_Err != nil {863 if templ_7745c5c3_Err != nil {
864- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 156, Col: 44}864+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 158, Col: 44}
865 }865 }
866 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))866 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))
867 if templ_7745c5c3_Err != nil {867 if templ_7745c5c3_Err != nil {
@@ -884,7 +884,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
884 var templ_7745c5c3_Var45 string884 var templ_7745c5c3_Var45 string
885 templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(p.Since.ReadShort)885 templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(p.Since.ReadShort)
886 if templ_7745c5c3_Err != nil {886 if templ_7745c5c3_Err != nil {
887- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 160, Col: 64}887+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 162, Col: 64}
888 }888 }
889 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))889 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
890 if templ_7745c5c3_Err != nil {890 if templ_7745c5c3_Err != nil {
@@ -897,7 +897,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
897 var templ_7745c5c3_Var46 string897 var templ_7745c5c3_Var46 string
898 templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(p.Since.HeadShort)898 templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(p.Since.HeadShort)
899 if templ_7745c5c3_Err != nil {899 if templ_7745c5c3_Err != nil {
900- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 160, Col: 90}900+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 162, Col: 90}
901 }901 }
902 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))902 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))
903 if templ_7745c5c3_Err != nil {903 if templ_7745c5c3_Err != nil {
@@ -920,7 +920,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
920 var templ_7745c5c3_Var47 string920 var templ_7745c5c3_Var47 string
921 templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(p.SinceNote)921 templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(p.SinceNote)
922 if templ_7745c5c3_Err != nil {922 if templ_7745c5c3_Err != nil {
923- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 164, Col: 34}923+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 166, Col: 34}
924 }924 }
925 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))925 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
926 if templ_7745c5c3_Err != nil {926 if templ_7745c5c3_Err != nil {
@@ -961,7 +961,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
961 var templ_7745c5c3_Var48 string961 var templ_7745c5c3_Var48 string
962 templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(p.Note)962 templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(p.Note)
963 if templ_7745c5c3_Err != nil {963 if templ_7745c5c3_Err != nil {
964- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 174, Col: 29}964+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 176, Col: 29}
965 }965 }
966 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48))966 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48))
967 if templ_7745c5c3_Err != nil {967 if templ_7745c5c3_Err != nil {
@@ -984,7 +984,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
984 var templ_7745c5c3_Var49 string984 var templ_7745c5c3_Var49 string
985 templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View)985 templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View)
986 if templ_7745c5c3_Err != nil {986 if templ_7745c5c3_Err != nil {
987- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 180, Col: 22}987+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 182, Col: 22}
988 }988 }
989 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var49)989 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var49)
990 if templ_7745c5c3_Err != nil {990 if templ_7745c5c3_Err != nil {
@@ -1002,7 +1002,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
1002 var templ_7745c5c3_Var50 string1002 var templ_7745c5c3_Var50 string
1003 templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View)1003 templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View)
1004 if templ_7745c5c3_Err != nil {1004 if templ_7745c5c3_Err != nil {
1005- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 182, Col: 22}1005+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 184, Col: 22}
1006 }1006 }
1007 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var50)1007 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var50)
1008 if templ_7745c5c3_Err != nil {1008 if templ_7745c5c3_Err != nil {
@@ -1042,7 +1042,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
1042 var templ_7745c5c3_Var51 string1042 var templ_7745c5c3_Var51 string
1043 templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.NUnresolved))1043 templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.NUnresolved))
1044 if templ_7745c5c3_Err != nil {1044 if templ_7745c5c3_Err != nil {
1045- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 195, Col: 97}1045+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 197, Col: 97}
1046 }1046 }
1047 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51))1047 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51))
1048 if templ_7745c5c3_Err != nil {1048 if templ_7745c5c3_Err != nil {
@@ -1055,7 +1055,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component {
1055 var templ_7745c5c3_Var52 string1055 var templ_7745c5c3_Var52 string
1056 templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.NComments))1056 templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.NComments))
1057 if templ_7745c5c3_Err != nil {1057 if templ_7745c5c3_Err != nil {
1058- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 195, Col: 126}1058+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 197, Col: 126}
1059 }1059 }
1060 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var52))1060 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var52))
1061 if templ_7745c5c3_Err != nil {1061 if templ_7745c5c3_Err != nil {
@@ -1136,7 +1136,7 @@ func branchLink(name, href string) templ.Component {
1136 var templ_7745c5c3_Var54 templ.SafeURL1136 var templ_7745c5c3_Var54 templ.SafeURL
1137 templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(href))1137 templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(href))
1138 if templ_7745c5c3_Err != nil {1138 if templ_7745c5c3_Err != nil {
1139- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 213, Col: 31}1139+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 215, Col: 31}
1140 }1140 }
1141 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var54))1141 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var54))
1142 if templ_7745c5c3_Err != nil {1142 if templ_7745c5c3_Err != nil {
@@ -1149,7 +1149,7 @@ func branchLink(name, href string) templ.Component {
1149 var templ_7745c5c3_Var55 string1149 var templ_7745c5c3_Var55 string
1150 templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.JoinStringErrs(name)1150 templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.JoinStringErrs(name)
1151 if templ_7745c5c3_Err != nil {1151 if templ_7745c5c3_Err != nil {
1152- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 213, Col: 40}1152+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 215, Col: 40}
1153 }1153 }
1154 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var55))1154 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var55))
1155 if templ_7745c5c3_Err != nil {1155 if templ_7745c5c3_Err != nil {
@@ -1163,7 +1163,7 @@ func branchLink(name, href string) templ.Component {
1163 var templ_7745c5c3_Var56 string1163 var templ_7745c5c3_Var56 string
1164 templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinStringErrs(name)1164 templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinStringErrs(name)
1165 if templ_7745c5c3_Err != nil {1165 if templ_7745c5c3_Err != nil {
1166- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 215, Col: 8}1166+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 217, Col: 8}
1167 }1167 }
1168 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56))1168 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56))
1169 if templ_7745c5c3_Err != nil {1169 if templ_7745c5c3_Err != nil {
@@ -1244,7 +1244,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1244 var templ_7745c5c3_Var59 string1244 var templ_7745c5c3_Var59 string
1245 templ_7745c5c3_Var59, templ_7745c5c3_Err = templ.ResolveAttributeValue("c" + c.ID + suffix)1245 templ_7745c5c3_Var59, templ_7745c5c3_Err = templ.ResolveAttributeValue("c" + c.ID + suffix)
1246 if templ_7745c5c3_Err != nil {1246 if templ_7745c5c3_Err != nil {
1247- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 234, Col: 42}1247+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 236, Col: 42}
1248 }1248 }
1249 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var59)1249 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var59)
1250 if templ_7745c5c3_Err != nil {1250 if templ_7745c5c3_Err != nil {
@@ -1257,7 +1257,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1257 var templ_7745c5c3_Var60 string1257 var templ_7745c5c3_Var60 string
1258 templ_7745c5c3_Var60, templ_7745c5c3_Err = templ.ResolveAttributeValue(boolText(c.Resolved))1258 templ_7745c5c3_Var60, templ_7745c5c3_Err = templ.ResolveAttributeValue(boolText(c.Resolved))
1259 if templ_7745c5c3_Err != nil {1259 if templ_7745c5c3_Err != nil {
1260- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 234, Col: 81}1260+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 236, Col: 81}
1261 }1261 }
1262 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var60)1262 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var60)
1263 if templ_7745c5c3_Err != nil {1263 if templ_7745c5c3_Err != nil {
@@ -1280,7 +1280,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1280 var templ_7745c5c3_Var61 string1280 var templ_7745c5c3_Var61 string
1281 templ_7745c5c3_Var61, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author)1281 templ_7745c5c3_Var61, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author)
1282 if templ_7745c5c3_Err != nil {1282 if templ_7745c5c3_Err != nil {
1283- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 236, Col: 33}1283+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 238, Col: 33}
1284 }1284 }
1285 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var61))1285 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var61))
1286 if templ_7745c5c3_Err != nil {1286 if templ_7745c5c3_Err != nil {
@@ -1294,7 +1294,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1294 var templ_7745c5c3_Var62 string1294 var templ_7745c5c3_Var62 string
1295 templ_7745c5c3_Var62, templ_7745c5c3_Err = templ.JoinStringErrs(" ")1295 templ_7745c5c3_Var62, templ_7745c5c3_Err = templ.JoinStringErrs(" ")
1296 if templ_7745c5c3_Err != nil {1296 if templ_7745c5c3_Err != nil {
1297- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 238, Col: 9}1297+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 240, Col: 9}
1298 }1298 }
1299 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var62))1299 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var62))
1300 if templ_7745c5c3_Err != nil {1300 if templ_7745c5c3_Err != nil {
@@ -1307,7 +1307,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1307 var templ_7745c5c3_Var63 string1307 var templ_7745c5c3_Var63 string
1308 templ_7745c5c3_Var63, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt)1308 templ_7745c5c3_Var63, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt)
1309 if templ_7745c5c3_Err != nil {1309 if templ_7745c5c3_Err != nil {
1310- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 238, Col: 49}1310+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 240, Col: 49}
1311 }1311 }
1312 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var63)1312 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var63)
1313 if templ_7745c5c3_Err != nil {1313 if templ_7745c5c3_Err != nil {
@@ -1320,7 +1320,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1320 var templ_7745c5c3_Var64 string1320 var templ_7745c5c3_Var64 string
1321 templ_7745c5c3_Var64, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.Abs)1321 templ_7745c5c3_Var64, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.Abs)
1322 if templ_7745c5c3_Err != nil {1322 if templ_7745c5c3_Err != nil {
1323- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 238, Col: 65}1323+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 240, Col: 65}
1324 }1324 }
1325 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var64)1325 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var64)
1326 if templ_7745c5c3_Err != nil {1326 if templ_7745c5c3_Err != nil {
@@ -1333,7 +1333,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1333 var templ_7745c5c3_Var65 string1333 var templ_7745c5c3_Var65 string
1334 templ_7745c5c3_Var65, templ_7745c5c3_Err = templ.JoinStringErrs(c.When)1334 templ_7745c5c3_Var65, templ_7745c5c3_Err = templ.JoinStringErrs(c.When)
1335 if templ_7745c5c3_Err != nil {1335 if templ_7745c5c3_Err != nil {
1336- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 238, Col: 76}1336+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 240, Col: 76}
1337 }1337 }
1338 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var65))1338 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var65))
1339 if templ_7745c5c3_Err != nil {1339 if templ_7745c5c3_Err != nil {
@@ -1348,7 +1348,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1348 var templ_7745c5c3_Var66 string1348 var templ_7745c5c3_Var66 string
1349 templ_7745c5c3_Var66, templ_7745c5c3_Err = templ.JoinStringErrs(" ")1349 templ_7745c5c3_Var66, templ_7745c5c3_Err = templ.JoinStringErrs(" ")
1350 if templ_7745c5c3_Err != nil {1350 if templ_7745c5c3_Err != nil {
1351- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 241, Col: 9}1351+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 243, Col: 9}
1352 }1352 }
1353 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var66))1353 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var66))
1354 if templ_7745c5c3_Err != nil {1354 if templ_7745c5c3_Err != nil {
@@ -1363,7 +1363,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1363 var templ_7745c5c3_Var67 string1363 var templ_7745c5c3_Var67 string
1364 templ_7745c5c3_Var67, templ_7745c5c3_Err = templ.JoinStringErrs(" ")1364 templ_7745c5c3_Var67, templ_7745c5c3_Err = templ.JoinStringErrs(" ")
1365 if templ_7745c5c3_Err != nil {1365 if templ_7745c5c3_Err != nil {
1366- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 244, Col: 9}1366+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 246, Col: 9}
1367 }1367 }
1368 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var67))1368 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var67))
1369 if templ_7745c5c3_Err != nil {1369 if templ_7745c5c3_Err != nil {
@@ -1378,7 +1378,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1378 var templ_7745c5c3_Var68 string1378 var templ_7745c5c3_Var68 string
1379 templ_7745c5c3_Var68, templ_7745c5c3_Err = templ.JoinStringErrs(" ")1379 templ_7745c5c3_Var68, templ_7745c5c3_Err = templ.JoinStringErrs(" ")
1380 if templ_7745c5c3_Err != nil {1380 if templ_7745c5c3_Err != nil {
1381- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 247, Col: 9}1381+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 249, Col: 9}
1382 }1382 }
1383 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var68))1383 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var68))
1384 if templ_7745c5c3_Err != nil {1384 if templ_7745c5c3_Err != nil {
@@ -1391,7 +1391,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1391 var templ_7745c5c3_Var69 string1391 var templ_7745c5c3_Var69 string
1392 templ_7745c5c3_Var69, templ_7745c5c3_Err = templ.JoinStringErrs(c.Where)1392 templ_7745c5c3_Var69, templ_7745c5c3_Err = templ.JoinStringErrs(c.Where)
1393 if templ_7745c5c3_Err != nil {1393 if templ_7745c5c3_Err != nil {
1394- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 247, Col: 47}1394+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 249, Col: 47}
1395 }1395 }
1396 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var69))1396 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var69))
1397 if templ_7745c5c3_Err != nil {1397 if templ_7745c5c3_Err != nil {
@@ -1466,7 +1466,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1466 var templ_7745c5c3_Var70 string1466 var templ_7745c5c3_Var70 string
1467 templ_7745c5c3_Var70, templ_7745c5c3_Err = templ.JoinStringErrs(c.Verdict)1467 templ_7745c5c3_Var70, templ_7745c5c3_Err = templ.JoinStringErrs(c.Verdict)
1468 if templ_7745c5c3_Err != nil {1468 if templ_7745c5c3_Err != nil {
1469- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 271, Col: 36}1469+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 273, Col: 36}
1470 }1470 }
1471 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var70))1471 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var70))
1472 if templ_7745c5c3_Err != nil {1472 if templ_7745c5c3_Err != nil {
@@ -1485,7 +1485,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1485 var templ_7745c5c3_Var71 string1485 var templ_7745c5c3_Var71 string
1486 templ_7745c5c3_Var71, templ_7745c5c3_Err = templ.JoinStringErrs(c.Range)1486 templ_7745c5c3_Var71, templ_7745c5c3_Err = templ.JoinStringErrs(c.Range)
1487 if templ_7745c5c3_Err != nil {1487 if templ_7745c5c3_Err != nil {
1488- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 274, Col: 31}1488+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 276, Col: 31}
1489 }1489 }
1490 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var71))1490 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var71))
1491 if templ_7745c5c3_Err != nil {1491 if templ_7745c5c3_Err != nil {
@@ -1504,7 +1504,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1504 var templ_7745c5c3_Var72 string1504 var templ_7745c5c3_Var72 string
1505 templ_7745c5c3_Var72, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.Quote)1505 templ_7745c5c3_Var72, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.Quote)
1506 if templ_7745c5c3_Err != nil {1506 if templ_7745c5c3_Err != nil {
1507- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 277, Col: 65}1507+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 279, Col: 65}
1508 }1508 }
1509 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var72)1509 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var72)
1510 if templ_7745c5c3_Err != nil {1510 if templ_7745c5c3_Err != nil {
@@ -1523,7 +1523,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1523 var templ_7745c5c3_Var73 templ.SafeURL1523 var templ_7745c5c3_Var73 templ.SafeURL
1524 templ_7745c5c3_Var73, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Edit.Action))1524 templ_7745c5c3_Var73, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Edit.Action))
1525 if templ_7745c5c3_Err != nil {1525 if templ_7745c5c3_Err != nil {
1526- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 280, Col: 92}1526+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 282, Col: 92}
1527 }1527 }
1528 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var73))1528 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var73))
1529 if templ_7745c5c3_Err != nil {1529 if templ_7745c5c3_Err != nil {
@@ -1536,7 +1536,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1536 var templ_7745c5c3_Var74 string1536 var templ_7745c5c3_Var74 string
1537 templ_7745c5c3_Var74, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(c.Edit.From))1537 templ_7745c5c3_Var74, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(c.Edit.From))
1538 if templ_7745c5c3_Err != nil {1538 if templ_7745c5c3_Err != nil {
1539- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 280, Col: 153}1539+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 282, Col: 153}
1540 }1540 }
1541 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var74)1541 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var74)
1542 if templ_7745c5c3_Err != nil {1542 if templ_7745c5c3_Err != nil {
@@ -1549,7 +1549,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1549 var templ_7745c5c3_Var75 string1549 var templ_7745c5c3_Var75 string
1550 templ_7745c5c3_Var75, templ_7745c5c3_Err = templ.JoinStringErrs(c.Edit.Text)1550 templ_7745c5c3_Var75, templ_7745c5c3_Err = templ.JoinStringErrs(c.Edit.Text)
1551 if templ_7745c5c3_Err != nil {1551 if templ_7745c5c3_Err != nil {
1552- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 280, Col: 209}1552+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 282, Col: 209}
1553 }1553 }
1554 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var75))1554 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var75))
1555 if templ_7745c5c3_Err != nil {1555 if templ_7745c5c3_Err != nil {
@@ -1568,7 +1568,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1568 var templ_7745c5c3_Var76 templ.SafeURL1568 var templ_7745c5c3_Var76 templ.SafeURL
1569 templ_7745c5c3_Var76, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Delete.Action))1569 templ_7745c5c3_Var76, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Delete.Action))
1570 if templ_7745c5c3_Err != nil {1570 if templ_7745c5c3_Err != nil {
1571- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 283, Col: 96}1571+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 285, Col: 96}
1572 }1572 }
1573 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var76))1573 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var76))
1574 if templ_7745c5c3_Err != nil {1574 if templ_7745c5c3_Err != nil {
@@ -1581,7 +1581,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1581 var templ_7745c5c3_Var77 string1581 var templ_7745c5c3_Var77 string
1582 templ_7745c5c3_Var77, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(c.Delete.From))1582 templ_7745c5c3_Var77, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(c.Delete.From))
1583 if templ_7745c5c3_Err != nil {1583 if templ_7745c5c3_Err != nil {
1584- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 283, Col: 159}1584+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 285, Col: 159}
1585 }1585 }
1586 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var77)1586 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var77)
1587 if templ_7745c5c3_Err != nil {1587 if templ_7745c5c3_Err != nil {
@@ -1620,7 +1620,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1620 var templ_7745c5c3_Var78 templ.SafeURL1620 var templ_7745c5c3_Var78 templ.SafeURL
1621 templ_7745c5c3_Var78, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Resolve.Action))1621 templ_7745c5c3_Var78, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Resolve.Action))
1622 if templ_7745c5c3_Err != nil {1622 if templ_7745c5c3_Err != nil {
1623- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 294, Col: 77}1623+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 296, Col: 77}
1624 }1624 }
1625 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var78))1625 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var78))
1626 if templ_7745c5c3_Err != nil {1626 if templ_7745c5c3_Err != nil {
@@ -1633,7 +1633,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1633 var templ_7745c5c3_Var79 string1633 var templ_7745c5c3_Var79 string
1634 templ_7745c5c3_Var79, templ_7745c5c3_Err = templ.ResolveAttributeValue(boolText(c.Resolve.Value))1634 templ_7745c5c3_Var79, templ_7745c5c3_Err = templ.ResolveAttributeValue(boolText(c.Resolve.Value))
1635 if templ_7745c5c3_Err != nil {1635 if templ_7745c5c3_Err != nil {
1636- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 295, Col: 74}1636+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 297, Col: 74}
1637 }1637 }
1638 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var79)1638 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var79)
1639 if templ_7745c5c3_Err != nil {1639 if templ_7745c5c3_Err != nil {
@@ -1651,7 +1651,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1651 var templ_7745c5c3_Var80 string1651 var templ_7745c5c3_Var80 string
1652 templ_7745c5c3_Var80, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(c.Resolve.From))1652 templ_7745c5c3_Var80, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(c.Resolve.From))
1653 if templ_7745c5c3_Err != nil {1653 if templ_7745c5c3_Err != nil {
1654- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 297, Col: 66}1654+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 299, Col: 66}
1655 }1655 }
1656 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var80)1656 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var80)
1657 if templ_7745c5c3_Err != nil {1657 if templ_7745c5c3_Err != nil {
@@ -1669,7 +1669,7 @@ func commentView(c CommentView, suffix string) templ.Component {
1669 var templ_7745c5c3_Var81 string1669 var templ_7745c5c3_Var81 string
1670 templ_7745c5c3_Var81, templ_7745c5c3_Err = templ.JoinStringErrs(c.Resolve.Label)1670 templ_7745c5c3_Var81, templ_7745c5c3_Err = templ.JoinStringErrs(c.Resolve.Label)
1671 if templ_7745c5c3_Err != nil {1671 if templ_7745c5c3_Err != nil {
1672- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 299, Col: 55}1672+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 301, Col: 55}
1673 }1673 }
1674 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var81))1674 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var81))
1675 if templ_7745c5c3_Err != nil {1675 if templ_7745c5c3_Err != nil {
@@ -1724,7 +1724,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
1724 var templ_7745c5c3_Var83 templ.SafeURL1724 var templ_7745c5c3_Var83 templ.SafeURL
1725 templ_7745c5c3_Var83, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(f.Action))1725 templ_7745c5c3_Var83, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(f.Action))
1726 if templ_7745c5c3_Err != nil {1726 if templ_7745c5c3_Err != nil {
1727- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 314, Col: 67}1727+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 316, Col: 67}
1728 }1728 }
1729 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var83))1729 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var83))
1730 if templ_7745c5c3_Err != nil {1730 if templ_7745c5c3_Err != nil {
@@ -1737,7 +1737,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
1737 var templ_7745c5c3_Var84 string1737 var templ_7745c5c3_Var84 string
1738 templ_7745c5c3_Var84, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.ID + suffix)1738 templ_7745c5c3_Var84, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.ID + suffix)
1739 if templ_7745c5c3_Err != nil {1739 if templ_7745c5c3_Err != nil {
1740- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 315, Col: 28}1740+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 317, Col: 28}
1741 }1741 }
1742 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var84)1742 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var84)
1743 if templ_7745c5c3_Err != nil {1743 if templ_7745c5c3_Err != nil {
@@ -1750,7 +1750,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
1750 var templ_7745c5c3_Var85 string1750 var templ_7745c5c3_Var85 string
1751 templ_7745c5c3_Var85, templ_7745c5c3_Err = templ.JoinStringErrs(f.Title)1751 templ_7745c5c3_Var85, templ_7745c5c3_Err = templ.JoinStringErrs(f.Title)
1752 if templ_7745c5c3_Err != nil {1752 if templ_7745c5c3_Err != nil {
1753- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 315, Col: 40}1753+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 317, Col: 40}
1754 }1754 }
1755 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var85))1755 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var85))
1756 if templ_7745c5c3_Err != nil {1756 if templ_7745c5c3_Err != nil {
@@ -1763,7 +1763,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
1763 var templ_7745c5c3_Var86 string1763 var templ_7745c5c3_Var86 string
1764 templ_7745c5c3_Var86, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.ID + suffix)1764 templ_7745c5c3_Var86, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.ID + suffix)
1765 if templ_7745c5c3_Err != nil {1765 if templ_7745c5c3_Err != nil {
1766- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 317, Col: 21}1766+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 319, Col: 21}
1767 }1767 }
1768 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var86)1768 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var86)
1769 if templ_7745c5c3_Err != nil {1769 if templ_7745c5c3_Err != nil {
@@ -1802,7 +1802,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
1802 var templ_7745c5c3_Var87 string1802 var templ_7745c5c3_Var87 string
1803 templ_7745c5c3_Var87, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.Path)1803 templ_7745c5c3_Var87, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.Path)
1804 if templ_7745c5c3_Err != nil {1804 if templ_7745c5c3_Err != nil {
1805- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 332, Col: 50}1805+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 334, Col: 50}
1806 }1806 }
1807 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var87)1807 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var87)
1808 if templ_7745c5c3_Err != nil {1808 if templ_7745c5c3_Err != nil {
@@ -1815,7 +1815,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
1815 var templ_7745c5c3_Var88 string1815 var templ_7745c5c3_Var88 string
1816 templ_7745c5c3_Var88, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.Line)1816 templ_7745c5c3_Var88, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.Line)
1817 if templ_7745c5c3_Err != nil {1817 if templ_7745c5c3_Err != nil {
1818- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 333, Col: 50}1818+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 335, Col: 50}
1819 }1819 }
1820 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var88)1820 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var88)
1821 if templ_7745c5c3_Err != nil {1821 if templ_7745c5c3_Err != nil {
@@ -1828,7 +1828,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
1828 var templ_7745c5c3_Var89 string1828 var templ_7745c5c3_Var89 string
1829 templ_7745c5c3_Var89, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.Side)1829 templ_7745c5c3_Var89, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.Side)
1830 if templ_7745c5c3_Err != nil {1830 if templ_7745c5c3_Err != nil {
1831- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 334, Col: 50}1831+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 336, Col: 50}
1832 }1832 }
1833 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var89)1833 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var89)
1834 if templ_7745c5c3_Err != nil {1834 if templ_7745c5c3_Err != nil {
@@ -1841,7 +1841,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
1841 var templ_7745c5c3_Var90 string1841 var templ_7745c5c3_Var90 string
1842 templ_7745c5c3_Var90, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.StartLine)1842 templ_7745c5c3_Var90, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.StartLine)
1843 if templ_7745c5c3_Err != nil {1843 if templ_7745c5c3_Err != nil {
1844- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 335, Col: 86}1844+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 337, Col: 86}
1845 }1845 }
1846 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var90)1846 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var90)
1847 if templ_7745c5c3_Err != nil {1847 if templ_7745c5c3_Err != nil {
@@ -1860,7 +1860,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
1860 var templ_7745c5c3_Var91 string1860 var templ_7745c5c3_Var91 string
1861 templ_7745c5c3_Var91, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.ReplyTo)1861 templ_7745c5c3_Var91, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.ReplyTo)
1862 if templ_7745c5c3_Err != nil {1862 if templ_7745c5c3_Err != nil {
1863- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 338, Col: 57}1863+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 340, Col: 57}
1864 }1864 }
1865 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var91)1865 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var91)
1866 if templ_7745c5c3_Err != nil {1866 if templ_7745c5c3_Err != nil {
@@ -1885,7 +1885,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
1885 var templ_7745c5c3_Var92 string1885 var templ_7745c5c3_Var92 string
1886 templ_7745c5c3_Var92, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(f.From))1886 templ_7745c5c3_Var92, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(f.From))
1887 if templ_7745c5c3_Err != nil {1887 if templ_7745c5c3_Err != nil {
1888- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 344, Col: 56}1888+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 346, Col: 56}
1889 }1889 }
1890 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var92)1890 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var92)
1891 if templ_7745c5c3_Err != nil {1891 if templ_7745c5c3_Err != nil {
@@ -1908,7 +1908,7 @@ func commentForm(f CommentForm, suffix string) templ.Component {
1908 var templ_7745c5c3_Var93 templ.SafeURL1908 var templ_7745c5c3_Var93 templ.SafeURL
1909 templ_7745c5c3_Var93, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(f.Cancel))1909 templ_7745c5c3_Var93, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(f.Cancel))
1910 if templ_7745c5c3_Err != nil {1910 if templ_7745c5c3_Err != nil {
1911- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 349, Col: 49}1911+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 351, Col: 49}
1912 }1912 }
1913 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var93))1913 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var93))
1914 if templ_7745c5c3_Err != nil {1914 if templ_7745c5c3_Err != nil {
@@ -1955,7 +1955,7 @@ func mrRail(p MRPage) templ.Component {
1955 var templ_7745c5c3_Var95 string1955 var templ_7745c5c3_Var95 string
1956 templ_7745c5c3_Var95, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Files)))1956 templ_7745c5c3_Var95, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Files)))
1957 if templ_7745c5c3_Err != nil {1957 if templ_7745c5c3_Err != nil {
1958- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 358, Col: 49}1958+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 360, Col: 49}
1959 }1959 }
1960 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var95))1960 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var95))
1961 if templ_7745c5c3_Err != nil {1961 if templ_7745c5c3_Err != nil {
@@ -1986,7 +1986,7 @@ func mrRail(p MRPage) templ.Component {
1986 var templ_7745c5c3_Var96 templ.SafeURL1986 var templ_7745c5c3_Var96 templ.SafeURL
1987 templ_7745c5c3_Var96, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(v.Href))1987 templ_7745c5c3_Var96, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(v.Href))
1988 if templ_7745c5c3_Err != nil {1988 if templ_7745c5c3_Err != nil {
1989- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 371, Col: 37}1989+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 373, Col: 37}
1990 }1990 }
1991 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var96))1991 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var96))
1992 if templ_7745c5c3_Err != nil {1992 if templ_7745c5c3_Err != nil {
@@ -1999,7 +1999,7 @@ func mrRail(p MRPage) templ.Component {
1999 var templ_7745c5c3_Var97 string1999 var templ_7745c5c3_Var97 string
2000 templ_7745c5c3_Var97, templ_7745c5c3_Err = templ.JoinStringErrs(v.Author)2000 templ_7745c5c3_Var97, templ_7745c5c3_Err = templ.JoinStringErrs(v.Author)
2001 if templ_7745c5c3_Err != nil {2001 if templ_7745c5c3_Err != nil {
2002- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 371, Col: 50}2002+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 373, Col: 50}
2003 }2003 }
2004 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var97))2004 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var97))
2005 if templ_7745c5c3_Err != nil {2005 if templ_7745c5c3_Err != nil {
@@ -2012,7 +2012,7 @@ func mrRail(p MRPage) templ.Component {
2012 var templ_7745c5c3_Var98 string2012 var templ_7745c5c3_Var98 string
2013 templ_7745c5c3_Var98, templ_7745c5c3_Err = templ.JoinStringErrs(v.Label)2013 templ_7745c5c3_Var98, templ_7745c5c3_Err = templ.JoinStringErrs(v.Label)
2014 if templ_7745c5c3_Err != nil {2014 if templ_7745c5c3_Err != nil {
2015- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 371, Col: 63}2015+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 373, Col: 63}
2016 }2016 }
2017 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var98))2017 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var98))
2018 if templ_7745c5c3_Err != nil {2018 if templ_7745c5c3_Err != nil {
@@ -2049,7 +2049,7 @@ func mrRail(p MRPage) templ.Component {
2049 var templ_7745c5c3_Var99 string2049 var templ_7745c5c3_Var99 string
2050 templ_7745c5c3_Var99, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Commits)))2050 templ_7745c5c3_Var99, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Commits)))
2051 if templ_7745c5c3_Err != nil {2051 if templ_7745c5c3_Err != nil {
2052- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 382, Col: 71}2052+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 384, Col: 71}
2053 }2053 }
2054 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var99))2054 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var99))
2055 if templ_7745c5c3_Err != nil {2055 if templ_7745c5c3_Err != nil {
@@ -2077,7 +2077,7 @@ func mrRail(p MRPage) templ.Component {
2077 var templ_7745c5c3_Var100 templ.SafeURL2077 var templ_7745c5c3_Var100 templ.SafeURL
2078 templ_7745c5c3_Var100, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Href))2078 templ_7745c5c3_Var100, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Href))
2079 if templ_7745c5c3_Err != nil {2079 if templ_7745c5c3_Err != nil {
2080- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 386, Col: 36}2080+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 388, Col: 36}
2081 }2081 }
2082 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var100))2082 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var100))
2083 if templ_7745c5c3_Err != nil {2083 if templ_7745c5c3_Err != nil {
@@ -2090,7 +2090,7 @@ func mrRail(p MRPage) templ.Component {
2090 var templ_7745c5c3_Var101 string2090 var templ_7745c5c3_Var101 string
2091 templ_7745c5c3_Var101, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short)2091 templ_7745c5c3_Var101, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short)
2092 if templ_7745c5c3_Err != nil {2092 if templ_7745c5c3_Err != nil {
2093- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 386, Col: 48}2093+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 388, Col: 48}
2094 }2094 }
2095 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var101))2095 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var101))
2096 if templ_7745c5c3_Err != nil {2096 if templ_7745c5c3_Err != nil {
@@ -2103,7 +2103,7 @@ func mrRail(p MRPage) templ.Component {
2103 var templ_7745c5c3_Var102 string2103 var templ_7745c5c3_Var102 string
2104 templ_7745c5c3_Var102, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject)2104 templ_7745c5c3_Var102, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject)
2105 if templ_7745c5c3_Err != nil {2105 if templ_7745c5c3_Err != nil {
2106- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 387, Col: 32}2106+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 389, Col: 32}
2107 }2107 }
2108 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var102))2108 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var102))
2109 if templ_7745c5c3_Err != nil {2109 if templ_7745c5c3_Err != nil {
@@ -2126,7 +2126,7 @@ func mrRail(p MRPage) templ.Component {
2126 var templ_7745c5c3_Var103 string2126 var templ_7745c5c3_Var103 string
2127 templ_7745c5c3_Var103, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author)2127 templ_7745c5c3_Var103, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author)
2128 if templ_7745c5c3_Err != nil {2128 if templ_7745c5c3_Err != nil {
2129- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 392, Col: 16}2129+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 394, Col: 16}
2130 }2130 }
2131 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var103))2131 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var103))
2132 if templ_7745c5c3_Err != nil {2132 if templ_7745c5c3_Err != nil {
@@ -2140,7 +2140,7 @@ func mrRail(p MRPage) templ.Component {
2140 var templ_7745c5c3_Var104 string2140 var templ_7745c5c3_Var104 string
2141 templ_7745c5c3_Var104, templ_7745c5c3_Err = templ.JoinStringErrs(" · ")2141 templ_7745c5c3_Var104, templ_7745c5c3_Err = templ.JoinStringErrs(" · ")
2142 if templ_7745c5c3_Err != nil {2142 if templ_7745c5c3_Err != nil {
2143- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 394, Col: 15}2143+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 396, Col: 15}
2144 }2144 }
2145 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var104))2145 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var104))
2146 if templ_7745c5c3_Err != nil {2146 if templ_7745c5c3_Err != nil {
@@ -2153,7 +2153,7 @@ func mrRail(p MRPage) templ.Component {
2153 var templ_7745c5c3_Var105 string2153 var templ_7745c5c3_Var105 string
2154 templ_7745c5c3_Var105, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt)2154 templ_7745c5c3_Var105, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt)
2155 if templ_7745c5c3_Err != nil {2155 if templ_7745c5c3_Err != nil {
2156- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 394, Col: 42}2156+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 396, Col: 42}
2157 }2157 }
2158 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var105)2158 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var105)
2159 if templ_7745c5c3_Err != nil {2159 if templ_7745c5c3_Err != nil {
@@ -2166,7 +2166,7 @@ func mrRail(p MRPage) templ.Component {
2166 var templ_7745c5c3_Var106 string2166 var templ_7745c5c3_Var106 string
2167 templ_7745c5c3_Var106, templ_7745c5c3_Err = templ.JoinStringErrs(c.When)2167 templ_7745c5c3_Var106, templ_7745c5c3_Err = templ.JoinStringErrs(c.When)
2168 if templ_7745c5c3_Err != nil {2168 if templ_7745c5c3_Err != nil {
2169- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 394, Col: 53}2169+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 396, Col: 53}
2170 }2170 }
2171 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var106))2171 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var106))
2172 if templ_7745c5c3_Err != nil {2172 if templ_7745c5c3_Err != nil {
@@ -2181,7 +2181,7 @@ func mrRail(p MRPage) templ.Component {
2181 var templ_7745c5c3_Var107 string2181 var templ_7745c5c3_Var107 string
2182 templ_7745c5c3_Var107, templ_7745c5c3_Err = templ.JoinStringErrs(" · new")2182 templ_7745c5c3_Var107, templ_7745c5c3_Err = templ.JoinStringErrs(" · new")
2183 if templ_7745c5c3_Err != nil {2183 if templ_7745c5c3_Err != nil {
2184- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 397, Col: 18}2184+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 399, Col: 18}
2185 }2185 }
2186 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var107))2186 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var107))
2187 if templ_7745c5c3_Err != nil {2187 if templ_7745c5c3_Err != nil {
@@ -2211,7 +2211,7 @@ func mrRail(p MRPage) templ.Component {
2211 var templ_7745c5c3_Var108 string2211 var templ_7745c5c3_Var108 string
2212 templ_7745c5c3_Var108, templ_7745c5c3_Err = templ.JoinStringErrs(p.CommitsNote)2212 templ_7745c5c3_Var108, templ_7745c5c3_Err = templ.JoinStringErrs(p.CommitsNote)
2213 if templ_7745c5c3_Err != nil {2213 if templ_7745c5c3_Err != nil {
2214- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 407, Col: 34}2214+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 409, Col: 34}
2215 }2215 }
2216 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var108))2216 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var108))
2217 if templ_7745c5c3_Err != nil {2217 if templ_7745c5c3_Err != nil {
@@ -2229,7 +2229,7 @@ func mrRail(p MRPage) templ.Component {
2229 var templ_7745c5c3_Var109 string2229 var templ_7745c5c3_Var109 string
2230 templ_7745c5c3_Var109, templ_7745c5c3_Err = templ.JoinStringErrs(strings.Join(p.Modes, ", "))2230 templ_7745c5c3_Var109, templ_7745c5c3_Err = templ.JoinStringErrs(strings.Join(p.Modes, ", "))
2231 if templ_7745c5c3_Err != nil {2231 if templ_7745c5c3_Err != nil {
2232- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 414, Col: 36}2232+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 416, Col: 36}
2233 }2233 }
2234 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var109))2234 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var109))
2235 if templ_7745c5c3_Err != nil {2235 if templ_7745c5c3_Err != nil {
@@ -2290,7 +2290,7 @@ func mrBar(p MRPage) templ.Component {
2290 var templ_7745c5c3_Var111 string2290 var templ_7745c5c3_Var111 string
2291 templ_7745c5c3_Var111, templ_7745c5c3_Err = templ.JoinStringErrs(plural(p.NFiles, "file", "files"))2291 templ_7745c5c3_Var111, templ_7745c5c3_Err = templ.JoinStringErrs(plural(p.NFiles, "file", "files"))
2292 if templ_7745c5c3_Err != nil {2292 if templ_7745c5c3_Err != nil {
2293- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 433, Col: 55}2293+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 435, Col: 55}
2294 }2294 }
2295 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var111))2295 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var111))
2296 if templ_7745c5c3_Err != nil {2296 if templ_7745c5c3_Err != nil {
@@ -2303,7 +2303,7 @@ func mrBar(p MRPage) templ.Component {
2303 var templ_7745c5c3_Var112 string2303 var templ_7745c5c3_Var112 string
2304 templ_7745c5c3_Var112, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Adds))2304 templ_7745c5c3_Var112, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Adds))
2305 if templ_7745c5c3_Err != nil {2305 if templ_7745c5c3_Err != nil {
2306- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 433, Col: 95}2306+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 435, Col: 95}
2307 }2307 }
2308 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var112))2308 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var112))
2309 if templ_7745c5c3_Err != nil {2309 if templ_7745c5c3_Err != nil {
@@ -2316,7 +2316,7 @@ func mrBar(p MRPage) templ.Component {
2316 var templ_7745c5c3_Var113 string2316 var templ_7745c5c3_Var113 string
2317 templ_7745c5c3_Var113, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Dels))2317 templ_7745c5c3_Var113, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Dels))
2318 if templ_7745c5c3_Err != nil {2318 if templ_7745c5c3_Err != nil {
2319- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 433, Col: 140}2319+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 435, Col: 140}
2320 }2320 }
2321 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var113))2321 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var113))
2322 if templ_7745c5c3_Err != nil {2322 if templ_7745c5c3_Err != nil {
@@ -2326,15 +2326,7 @@ func mrBar(p MRPage) templ.Component {
2326 if templ_7745c5c3_Err != nil {2326 if templ_7745c5c3_Err != nil {
2327 return templ_7745c5c3_Err2327 return templ_7745c5c3_Err
2328 }2328 }
2329- templ_7745c5c3_Err = viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref).Render(ctx, templ_7745c5c3_Buffer)2329+ templ_7745c5c3_Err = diffViewMenu(p.DiffView).Render(ctx, templ_7745c5c3_Buffer)
2330- if templ_7745c5c3_Err != nil {
2331- return templ_7745c5c3_Err
2332- }
2333- templ_7745c5c3_Err = viewLink(p.DiffView, "split", "Split", "d", p.SplitHref).Render(ctx, templ_7745c5c3_Buffer)
2334- if templ_7745c5c3_Err != nil {
2335- return templ_7745c5c3_Err
2336- }
2337- templ_7745c5c3_Err = collapseButton().Render(ctx, templ_7745c5c3_Buffer)
2338 if templ_7745c5c3_Err != nil {2330 if templ_7745c5c3_Err != nil {
2339 return templ_7745c5c3_Err2331 return templ_7745c5c3_Err
2340 }2332 }
@@ -2998,15 +2990,7 @@ func Compare(l Layout, p ComparePage) templ.Component {
2998 if templ_7745c5c3_Err != nil {2990 if templ_7745c5c3_Err != nil {
2999 return templ_7745c5c3_Err2991 return templ_7745c5c3_Err
3000 }2992 }
3001- templ_7745c5c3_Err = viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref).Render(ctx, templ_7745c5c3_Buffer)2993+ templ_7745c5c3_Err = diffViewMenu(p.DiffView).Render(ctx, templ_7745c5c3_Buffer)
3002- if templ_7745c5c3_Err != nil {
3003- return templ_7745c5c3_Err
3004- }
3005- templ_7745c5c3_Err = viewLink(p.DiffView, "split", "Split", "d", p.SplitHref).Render(ctx, templ_7745c5c3_Buffer)
3006- if templ_7745c5c3_Err != nil {
3007- return templ_7745c5c3_Err
3008- }
3009- templ_7745c5c3_Err = collapseButton().Render(ctx, templ_7745c5c3_Buffer)
3010 if templ_7745c5c3_Err != nil {2994 if templ_7745c5c3_Err != nil {
3011 return templ_7745c5c3_Err2995 return templ_7745c5c3_Err
3012 }2996 }
@@ -3032,7 +3016,7 @@ func Compare(l Layout, p ComparePage) templ.Component {
3032 var templ_7745c5c3_Var148 string3016 var templ_7745c5c3_Var148 string
3033 templ_7745c5c3_Var148, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View)3017 templ_7745c5c3_Var148, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View)
3034 if templ_7745c5c3_Err != nil {3018 if templ_7745c5c3_Err != nil {
3035- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 578, Col: 23}3019+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 576, Col: 23}
3036 }3020 }
3037 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var148)3021 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var148)
3038 if templ_7745c5c3_Err != nil {3022 if templ_7745c5c3_Err != nil {
@@ -3050,7 +3034,7 @@ func Compare(l Layout, p ComparePage) templ.Component {
3050 var templ_7745c5c3_Var149 string3034 var templ_7745c5c3_Var149 string
3051 templ_7745c5c3_Var149, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View)3035 templ_7745c5c3_Var149, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View)
3052 if templ_7745c5c3_Err != nil {3036 if templ_7745c5c3_Err != nil {
3053- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 580, Col: 23}3037+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 578, Col: 23}
3054 }3038 }
3055 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var149)3039 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var149)
3056 if templ_7745c5c3_Err != nil {3040 if templ_7745c5c3_Err != nil {
@@ -3120,7 +3104,7 @@ func compareRail(p ComparePage) templ.Component {
3120 var templ_7745c5c3_Var151 string3104 var templ_7745c5c3_Var151 string
3121 templ_7745c5c3_Var151, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Ahead)))3105 templ_7745c5c3_Var151, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Ahead)))
3122 if templ_7745c5c3_Err != nil {3106 if templ_7745c5c3_Err != nil {
3123- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 596, Col: 67}3107+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 594, Col: 67}
3124 }3108 }
3125 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var151))3109 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var151))
3126 if templ_7745c5c3_Err != nil {3110 if templ_7745c5c3_Err != nil {
@@ -3138,7 +3122,7 @@ func compareRail(p ComparePage) templ.Component {
3138 var templ_7745c5c3_Var152 templ.SafeURL3122 var templ_7745c5c3_Var152 templ.SafeURL
3139 templ_7745c5c3_Var152, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref))3123 templ_7745c5c3_Var152, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref))
3140 if templ_7745c5c3_Err != nil {3124 if templ_7745c5c3_Err != nil {
3141- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 600, Col: 37}3125+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 598, Col: 37}
3142 }3126 }
3143 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var152))3127 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var152))
3144 if templ_7745c5c3_Err != nil {3128 if templ_7745c5c3_Err != nil {
@@ -3151,7 +3135,7 @@ func compareRail(p ComparePage) templ.Component {
3151 var templ_7745c5c3_Var153 string3135 var templ_7745c5c3_Var153 string
3152 templ_7745c5c3_Var153, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short)3136 templ_7745c5c3_Var153, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short)
3153 if templ_7745c5c3_Err != nil {3137 if templ_7745c5c3_Err != nil {
3154- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 600, Col: 49}3138+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 598, Col: 49}
3155 }3139 }
3156 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var153))3140 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var153))
3157 if templ_7745c5c3_Err != nil {3141 if templ_7745c5c3_Err != nil {
@@ -3164,7 +3148,7 @@ func compareRail(p ComparePage) templ.Component {
3164 var templ_7745c5c3_Var154 string3148 var templ_7745c5c3_Var154 string
3165 templ_7745c5c3_Var154, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject)3149 templ_7745c5c3_Var154, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject)
3166 if templ_7745c5c3_Err != nil {3150 if templ_7745c5c3_Err != nil {
3167- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 601, Col: 32}3151+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 599, Col: 32}
3168 }3152 }
3169 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var154))3153 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var154))
3170 if templ_7745c5c3_Err != nil {3154 if templ_7745c5c3_Err != nil {
@@ -3177,7 +3161,7 @@ func compareRail(p ComparePage) templ.Component {
3177 var templ_7745c5c3_Var155 string3161 var templ_7745c5c3_Var155 string
3178 templ_7745c5c3_Var155, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author)3162 templ_7745c5c3_Var155, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author)
3179 if templ_7745c5c3_Err != nil {3163 if templ_7745c5c3_Err != nil {
3180- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 603, Col: 16}3164+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 601, Col: 16}
3181 }3165 }
3182 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var155))3166 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var155))
3183 if templ_7745c5c3_Err != nil {3167 if templ_7745c5c3_Err != nil {
@@ -3191,7 +3175,7 @@ func compareRail(p ComparePage) templ.Component {
3191 var templ_7745c5c3_Var156 string3175 var templ_7745c5c3_Var156 string
3192 templ_7745c5c3_Var156, templ_7745c5c3_Err = templ.JoinStringErrs(" · ")3176 templ_7745c5c3_Var156, templ_7745c5c3_Err = templ.JoinStringErrs(" · ")
3193 if templ_7745c5c3_Err != nil {3177 if templ_7745c5c3_Err != nil {
3194- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 605, Col: 15}3178+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 603, Col: 15}
3195 }3179 }
3196 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var156))3180 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var156))
3197 if templ_7745c5c3_Err != nil {3181 if templ_7745c5c3_Err != nil {
@@ -3204,7 +3188,7 @@ func compareRail(p ComparePage) templ.Component {
3204 var templ_7745c5c3_Var157 string3188 var templ_7745c5c3_Var157 string
3205 templ_7745c5c3_Var157, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt)3189 templ_7745c5c3_Var157, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt)
3206 if templ_7745c5c3_Err != nil {3190 if templ_7745c5c3_Err != nil {
3207- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 605, Col: 42}3191+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 603, Col: 42}
3208 }3192 }
3209 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var157)3193 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var157)
3210 if templ_7745c5c3_Err != nil {3194 if templ_7745c5c3_Err != nil {
@@ -3217,7 +3201,7 @@ func compareRail(p ComparePage) templ.Component {
3217 var templ_7745c5c3_Var158 string3201 var templ_7745c5c3_Var158 string
3218 templ_7745c5c3_Var158, templ_7745c5c3_Err = templ.JoinStringErrs(c.When)3202 templ_7745c5c3_Var158, templ_7745c5c3_Err = templ.JoinStringErrs(c.When)
3219 if templ_7745c5c3_Err != nil {3203 if templ_7745c5c3_Err != nil {
3220- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 605, Col: 53}3204+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 603, Col: 53}
3221 }3205 }
3222 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var158))3206 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var158))
3223 if templ_7745c5c3_Err != nil {3207 if templ_7745c5c3_Err != nil {
@@ -3245,7 +3229,7 @@ func compareRail(p ComparePage) templ.Component {
3245 var templ_7745c5c3_Var159 string3229 var templ_7745c5c3_Var159 string
3246 templ_7745c5c3_Var159, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target)3230 templ_7745c5c3_Var159, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target)
3247 if templ_7745c5c3_Err != nil {3231 if templ_7745c5c3_Err != nil {
3248- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 612, Col: 47}3232+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 610, Col: 47}
3249 }3233 }
3250 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var159))3234 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var159))
3251 if templ_7745c5c3_Err != nil {3235 if templ_7745c5c3_Err != nil {
@@ -3268,7 +3252,7 @@ func compareRail(p ComparePage) templ.Component {
3268 var templ_7745c5c3_Var160 string3252 var templ_7745c5c3_Var160 string
3269 templ_7745c5c3_Var160, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Files)))3253 templ_7745c5c3_Var160, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Files)))
3270 if templ_7745c5c3_Err != nil {3254 if templ_7745c5c3_Err != nil {
3271- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 617, Col: 66}3255+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 615, Col: 66}
3272 }3256 }
3273 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var160))3257 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var160))
3274 if templ_7745c5c3_Err != nil {3258 if templ_7745c5c3_Err != nil {
M internal/adapters/web/overlays.templ +1 -0
@@ -28,6 +28,7 @@ templ OverlayHelp(mr bool) {
2828 <div><dt><kbd>b</kbd></dt><dd>{ "switch" } branch or tag</dd></div>
2929 <div><dt><kbd>s</kbd> <kbd>t</kbd> <kbd>c</kbd> <kbd>r</kbd> <kbd>m</kbd> <kbd>i</kbd></dt><dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd></div>
3030 <div><dt><kbd>g h</kbd></dt><dd>home</dd></div>
31+ <div><dt><kbd>w</kbd></dt><dd>wide mode: use the whole width of the screen, or not (remembered)</dd></div>
3132 <div><dt><kbd>y</kbd></dt><dd>copy a permalink pinned to the commit</dd></div>
3233 <div><dt><kbd>d</kbd> <kbd>u</kbd> <kbd>x</kbd></dt><dd>diff: split / unified / collapse or expand all</dd></div>
3334 <div><dt><kbd>e</kbd> <kbd>E</kbd></dt><dd>diff: the whole file in view / every file, with the lines the hunks hide</dd></div>
@@ -28,6 +28,7 @@ templ OverlayHelp(mr bool) {
28 <div><dt><kbd>b</kbd></dt><dd>{ "switch" } branch or tag</dd></div>28 <div><dt><kbd>b</kbd></dt><dd>{ "switch" } branch or tag</dd></div>
29 <div><dt><kbd>s</kbd> <kbd>t</kbd> <kbd>c</kbd> <kbd>r</kbd> <kbd>m</kbd> <kbd>i</kbd></dt><dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd></div>29 <div><dt><kbd>s</kbd> <kbd>t</kbd> <kbd>c</kbd> <kbd>r</kbd> <kbd>m</kbd> <kbd>i</kbd></dt><dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd></div>
30 <div><dt><kbd>g h</kbd></dt><dd>home</dd></div>30 <div><dt><kbd>g h</kbd></dt><dd>home</dd></div>
31+ <div><dt><kbd>w</kbd></dt><dd>wide mode: use the whole width of the screen, or not (remembered)</dd></div>
31 <div><dt><kbd>y</kbd></dt><dd>copy a permalink pinned to the commit</dd></div>32 <div><dt><kbd>y</kbd></dt><dd>copy a permalink pinned to the commit</dd></div>
32 <div><dt><kbd>d</kbd> <kbd>u</kbd> <kbd>x</kbd></dt><dd>diff: split / unified / collapse or expand all</dd></div>33 <div><dt><kbd>d</kbd> <kbd>u</kbd> <kbd>x</kbd></dt><dd>diff: split / unified / collapse or expand all</dd></div>
33 <div><dt><kbd>e</kbd> <kbd>E</kbd></dt><dd>diff: the whole file in view / every file, with the lines the hunks hide</dd></div>34 <div><dt><kbd>e</kbd> <kbd>E</kbd></dt><dd>diff: the whole file in view / every file, with the lines the hunks hide</dd></div>
M internal/adapters/web/overlays_templ.go +12 -12 generated file: collapsed
M internal/adapters/web/pages.templ +3 -9
M internal/adapters/web/pages_templ.go +205 -223
M internal/adapters/web/shell.templ +14 -1
M internal/adapters/web/shell_templ.go +158 -116
M internal/adapters/web/static/css/20-shell.css +9 -0
M internal/adapters/web/static/css/50-code.css +7 -4
M internal/adapters/web/static/css/55-diff.css +25 -0
M internal/adapters/web/static/css/60-mr.css +15 -1
A internal/adapters/web/static/icons/mailbox.svg +1 -0
M internal/adapters/web/static/js/ermite.js +70 -3
M internal/adapters/web/static/js/mr.js +1 -11
M internal/adapters/web/static/js/store.js +5 -0
M internal/adapters/web/view.go +6 -4
M internal/app/testdata/web/blob-binary.html +8 -1
M internal/app/testdata/web/blob-code.html +8 -1
M internal/app/testdata/web/blob-empty.html +8 -1
M internal/app/testdata/web/blob-image.html +8 -1
M internal/app/testdata/web/blob-markdown.html +8 -1
M internal/app/testdata/web/blob-svg.html +8 -1
M internal/app/testdata/web/blob-text.html +8 -1
M internal/app/testdata/web/blob-too-large.html +8 -1
M internal/app/testdata/web/blob-trojan.html +8 -1
M internal/app/testdata/web/commit-big.html +16 -2
M internal/app/testdata/web/commit-binary.html +16 -2
M internal/app/testdata/web/commit-merge.html +16 -2
M internal/app/testdata/web/commit-modify-split.html +17 -3
M internal/app/testdata/web/commit-modify.html +17 -3
M internal/app/testdata/web/commit-rename.html +17 -3
M internal/app/testdata/web/commit-root.html +20 -6
M internal/app/testdata/web/commit-trojan.html +16 -2
M internal/app/testdata/web/commits-page2.html +8 -1
M internal/app/testdata/web/commits-path.html +8 -1
M internal/app/testdata/web/commits-root.html +8 -1
M internal/app/testdata/web/commits-small.html +8 -1
M internal/app/testdata/web/empty.html +8 -1
M internal/app/testdata/web/home.html +40 -31
M internal/app/testdata/web/not-found-page.html +8 -1
M internal/app/testdata/web/not-found.html +8 -1
M internal/app/testdata/web/refs.html +8 -1
M internal/app/testdata/web/summary.html +8 -1
M internal/app/testdata/web/tree-nested.html +8 -1
M internal/app/testdata/web/tree-root.html +8 -1
M internal/app/testdata/web/tree-slash-ref.html +8 -1
M internal/app/testdata/web/tree-subdir.html +8 -1
M internal/app/web_events_test.go +2 -2
M internal/app/web_mr_test.go +2 -0
M internal/domain/repo.go +1 -0
M internal/ports/dto.go +1 -1
M internal/ports/mocks/RepoMetaStore.go +36 -21
M internal/ports/ports.go +7 -4
A internal/usecase/activity.go +43 -0
A internal/usecase/activity_store_test.go +46 -0
A internal/usecase/activity_test.go +47 -0
M internal/usecase/export_test.go +10 -0
M internal/usecase/issue_moderation.go +1 -0
M internal/usecase/issue_moderation_test.go +1 -0
M internal/usecase/issues.go +3 -0
M internal/usecase/issues_integration_test.go +67 -0
M internal/usecase/issues_test.go +2 -0
M internal/usecase/mergerequests.go +5 -0
M internal/usecase/mergerequests_integration_test.go +11 -0
M internal/usecase/pushes.go +21 -29
M internal/usecase/pushes_test.go +11 -16
A internal/usecase/repo_mutation_test.go +34 -0
M internal/usecase/repos_write.go +6 -14
M internal/usecase/repos_write_test.go +20 -21
M proto/ermite/storage/repo.proto +2 -0

Comments0 open / 0

no comments yet.

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
l
merge request: the list of files (j k move, enter opens, esc back)
M
merge request: the merge menu; there 1–3 pick the mode, D deletes the branch, enter merges
R
merge request: mark the head read
esc
close a dialog, leave a field

Branches 2

Tags 42

↑↓ moveenter openesc close