feat(web): live MR checks, draft merge requests, sticky review rail (#47)
- commit
- 0cea2758ac9d47b10e83c4d30c134423025e7bed
- author
- brice · · 2026-10-17 17:44 UTC
- committer
- bnema · · 2026-10-17 17:44 UTC
- parent
- ab8e178
- signature
- SSH Unverified
SHA256:VOCA5lYARHcbKDZ7GhTV45VmcjWnJ6ZQIBazj17LivAThe key is unknown - refs
- tag: v0.18.0
59 files · +1177 -456
M internal/adapters/kvstore/mergerequests.go +7 -0
| @@ -344,6 +344,13 @@ func (m *MergeRequests) ListOpen(ctx context.Context, repo domain.RepoName) ([]d | |||
| 344 | 344 | return out, nil | |
| 345 | 345 | } | |
| 346 | 346 | ||
| 347 | + | // CountOpen counts the open merge requests of the repository from the keys of | |
| 348 | + | // the index: no request is decoded. | |
| 349 | + | func (m *MergeRequests) CountOpen(ctx context.Context, repo domain.RepoName) (int, error) { | |
| 350 | + | numbers, _, err := m.scanIndex(ctx, []string{mrOpenRepoPrefix(repo)}, 0) | |
| 351 | + | return len(numbers), err | |
| 352 | + | } | |
| 353 | + | ||
| 347 | 354 | // Update replaces the merge request with a version precondition. | |
| 348 | 355 | // | |
| 349 | 356 | //hexcheck:ignore suspicious-business-logic-in-adapter port method name only | |
| @@ -344,6 +344,13 @@ func (m *MergeRequests) ListOpen(ctx context.Context, repo domain.RepoName) ([]d | |||||
| 344 | return out, nil | 344 | return out, nil | ||
| 345 | } | 345 | } | ||
| 346 | 346 | ||||
| 347 | + | // CountOpen counts the open merge requests of the repository from the keys of | |||
| 348 | + | // the index: no request is decoded. | |||
| 349 | + | func (m *MergeRequests) CountOpen(ctx context.Context, repo domain.RepoName) (int, error) { | |||
| 350 | + | numbers, _, err := m.scanIndex(ctx, []string{mrOpenRepoPrefix(repo)}, 0) | |||
| 351 | + | return len(numbers), err | |||
| 352 | + | } | |||
| 353 | + | ||||
| 347 | // Update replaces the merge request with a version precondition. | 354 | // Update replaces the merge request with a version precondition. | ||
| 348 | // | 355 | // | ||
| 349 | //hexcheck:ignore suspicious-business-logic-in-adapter port method name only | 356 | //hexcheck:ignore suspicious-business-logic-in-adapter port method name only | ||
M internal/adapters/web/actions_pages.go +2 -2
| @@ -237,7 +237,7 @@ func (s *Server) runRow(repo string, run domain.CIRun, now time.Time) RunRow { | |||
| 237 | 237 | Kind: ciKind(run.Status, run.Conclusion), State: ciState(run.Status, run.Conclusion), | |
| 238 | 238 | Workflow: visibleText(run.WorkflowName), Event: visibleText(run.Event), Title: visibleText(run.Title), | |
| 239 | 239 | Ref: visibleText(strings.TrimPrefix(strings.TrimPrefix(run.Ref, "refs/heads/"), "refs/tags/")), | |
| 240 | - | Short: short(run.Sha), CHref: repoHref(repo, "commit", run.Sha), Actor: visibleText(run.Actor), | |
| 240 | + | Short: short(run.Sha), CHref: repoHref(repo, "commit", run.Sha), Actor: s.who(run.Actor), | |
| 241 | 241 | Duration: duration(run.StartedAt, run.CompletedAt, now), Number: "#" + strconv.FormatInt(run.Number, 10), Attempt: run.Attempt, | |
| 242 | 242 | } | |
| 243 | 243 | row.RefHref = repoHref(repo, "tree", strings.TrimPrefix(strings.TrimPrefix(run.Ref, "refs/heads/"), "refs/tags/")) | |
| @@ -248,7 +248,7 @@ func (s *Server) runRow(repo string, run domain.CIRun, now time.Time) RunRow { | |||
| 248 | 248 | if ok { | |
| 249 | 249 | // A pull_request run is for refs/pull/<n>/merge, a pull_request_target | |
| 250 | 250 | // run for the default branch: both show the merge request. | |
| 251 | - | row.Ref, row.RefHref = "!"+strconv.FormatInt(n, 10), mrHref(repo, n) | |
| 251 | + | row.Ref, row.RefHref = "#"+strconv.FormatInt(n, 10), mrHref(repo, n) | |
| 252 | 252 | } | |
| 253 | 253 | row.When, row.WhenAt = whenOf(now, run.CreatedAt) | |
| 254 | 254 | return row | |
| @@ -237,7 +237,7 @@ func (s *Server) runRow(repo string, run domain.CIRun, now time.Time) RunRow { | |||||
| 237 | Kind: ciKind(run.Status, run.Conclusion), State: ciState(run.Status, run.Conclusion), | 237 | Kind: ciKind(run.Status, run.Conclusion), State: ciState(run.Status, run.Conclusion), | ||
| 238 | Workflow: visibleText(run.WorkflowName), Event: visibleText(run.Event), Title: visibleText(run.Title), | 238 | Workflow: visibleText(run.WorkflowName), Event: visibleText(run.Event), Title: visibleText(run.Title), | ||
| 239 | Ref: visibleText(strings.TrimPrefix(strings.TrimPrefix(run.Ref, "refs/heads/"), "refs/tags/")), | 239 | Ref: visibleText(strings.TrimPrefix(strings.TrimPrefix(run.Ref, "refs/heads/"), "refs/tags/")), | ||
| 240 | - | Short: short(run.Sha), CHref: repoHref(repo, "commit", run.Sha), Actor: visibleText(run.Actor), | 240 | + | Short: short(run.Sha), CHref: repoHref(repo, "commit", run.Sha), Actor: s.who(run.Actor), |
| 241 | Duration: duration(run.StartedAt, run.CompletedAt, now), Number: "#" + strconv.FormatInt(run.Number, 10), Attempt: run.Attempt, | 241 | Duration: duration(run.StartedAt, run.CompletedAt, now), Number: "#" + strconv.FormatInt(run.Number, 10), Attempt: run.Attempt, | ||
| 242 | } | 242 | } | ||
| 243 | row.RefHref = repoHref(repo, "tree", strings.TrimPrefix(strings.TrimPrefix(run.Ref, "refs/heads/"), "refs/tags/")) | 243 | row.RefHref = repoHref(repo, "tree", strings.TrimPrefix(strings.TrimPrefix(run.Ref, "refs/heads/"), "refs/tags/")) | ||
| @@ -248,7 +248,7 @@ func (s *Server) runRow(repo string, run domain.CIRun, now time.Time) RunRow { | |||||
| 248 | if ok { | 248 | if ok { | ||
| 249 | // A pull_request run is for refs/pull/<n>/merge, a pull_request_target | 249 | // A pull_request run is for refs/pull/<n>/merge, a pull_request_target | ||
| 250 | // run for the default branch: both show the merge request. | 250 | // run for the default branch: both show the merge request. | ||
| 251 | - | row.Ref, row.RefHref = "!"+strconv.FormatInt(n, 10), mrHref(repo, n) | 251 | + | row.Ref, row.RefHref = "#"+strconv.FormatInt(n, 10), mrHref(repo, n) |
| 252 | } | 252 | } | ||
| 253 | row.When, row.WhenAt = whenOf(now, run.CreatedAt) | 253 | row.When, row.WhenAt = whenOf(now, run.CreatedAt) | ||
| 254 | return row | 254 | return row | ||
M internal/adapters/web/compare.go +1 -1
| @@ -111,7 +111,7 @@ func (s *Server) compare(ctx context.Context, r *http.Request) (templ.Component, | |||
| 111 | 111 | } | |
| 112 | 112 | switch { | |
| 113 | 113 | case existing != nil: | |
| 114 | - | p.Existing = &Link{Label: "!" + itoa(int(existing.Number)) + " " + visibleText(existing.Title.String()), Href: mrHref(name, existing.Number)} | |
| 114 | + | p.Existing = &Link{Label: "#" + itoa(int(existing.Number)) + " " + visibleText(existing.Title.String()), Href: mrHref(name, existing.Number)} | |
| 115 | 115 | case SignedIn(ctx) && !same && len(ahead) > 0: | |
| 116 | 116 | title := "" | |
| 117 | 117 | if len(ahead) == 1 { | |
| @@ -111,7 +111,7 @@ func (s *Server) compare(ctx context.Context, r *http.Request) (templ.Component, | |||||
| 111 | } | 111 | } | ||
| 112 | switch { | 112 | switch { | ||
| 113 | case existing != nil: | 113 | case existing != nil: | ||
| 114 | - | p.Existing = &Link{Label: "!" + itoa(int(existing.Number)) + " " + visibleText(existing.Title.String()), Href: mrHref(name, existing.Number)} | 114 | + | p.Existing = &Link{Label: "#" + itoa(int(existing.Number)) + " " + visibleText(existing.Title.String()), Href: mrHref(name, existing.Number)} |
| 115 | case SignedIn(ctx) && !same && len(ahead) > 0: | 115 | case SignedIn(ctx) && !same && len(ahead) > 0: | ||
| 116 | title := "" | 116 | title := "" | ||
| 117 | if len(ahead) == 1 { | 117 | if len(ahead) == 1 { | ||
M internal/adapters/web/format.go +3 -3
| @@ -10,8 +10,8 @@ import ( | |||
| 10 | 10 | ||
| 11 | 11 | func itoa(n int) string { return strconv.Itoa(n) } | |
| 12 | 12 | ||
| 13 | - | // ago renders a short relative date: now, 5m ago, 3h ago, 12d ago, 5mo ago, | |
| 14 | - | // 2y ago. | |
| 13 | + | // ago renders a short relative date: now, 5 min ago, 3h ago, 12d ago, 5mo | |
| 14 | + | // ago, 2y ago. Minutes are "min", so that they never read as months. | |
| 15 | 15 | func ago(now, t time.Time) string { | |
| 16 | 16 | if t.IsZero() { | |
| 17 | 17 | return "never" | |
| @@ -21,7 +21,7 @@ func ago(now, t time.Time) string { | |||
| 21 | 21 | case d < time.Minute: | |
| 22 | 22 | return "now" | |
| 23 | 23 | case d < time.Hour: | |
| 24 | - | return fmt.Sprintf("%dm ago", int(d.Minutes())) | |
| 24 | + | return fmt.Sprintf("%d min ago", int(d.Minutes())) | |
| 25 | 25 | case d < 48*time.Hour: | |
| 26 | 26 | return fmt.Sprintf("%dh ago", int(d.Hours())) | |
| 27 | 27 | case d < 60*24*time.Hour: | |
| @@ -10,8 +10,8 @@ import ( | |||||
| 10 | 10 | ||||
| 11 | func itoa(n int) string { return strconv.Itoa(n) } | 11 | func itoa(n int) string { return strconv.Itoa(n) } | ||
| 12 | 12 | ||||
| 13 | - | // ago renders a short relative date: now, 5m ago, 3h ago, 12d ago, 5mo ago, | 13 | + | // ago renders a short relative date: now, 5 min ago, 3h ago, 12d ago, 5mo |
| 14 | - | // 2y ago. | 14 | + | // ago, 2y ago. Minutes are "min", so that they never read as months. |
| 15 | func ago(now, t time.Time) string { | 15 | func ago(now, t time.Time) string { | ||
| 16 | if t.IsZero() { | 16 | if t.IsZero() { | ||
| 17 | return "never" | 17 | return "never" | ||
| @@ -21,7 +21,7 @@ func ago(now, t time.Time) string { | |||||
| 21 | case d < time.Minute: | 21 | case d < time.Minute: | ||
| 22 | return "now" | 22 | return "now" | ||
| 23 | case d < time.Hour: | 23 | case d < time.Hour: | ||
| 24 | - | return fmt.Sprintf("%dm ago", int(d.Minutes())) | 24 | + | return fmt.Sprintf("%d min ago", int(d.Minutes())) |
| 25 | case d < 48*time.Hour: | 25 | case d < 48*time.Hour: | ||
| 26 | return fmt.Sprintf("%dh ago", int(d.Hours())) | 26 | return fmt.Sprintf("%dh ago", int(d.Hours())) | ||
| 27 | case d < 60*24*time.Hour: | 27 | case d < 60*24*time.Hour: | ||
M internal/adapters/web/fragments.go +75 -1
| @@ -29,8 +29,15 @@ type liveStream struct { | |||
| 29 | 29 | // sha is the commit of the run of the job shown, read once when the stream | |
| 30 | 30 | // is set up: the annotations link to files at it, and it never changes. | |
| 31 | 31 | sha string | |
| 32 | + | // headRuns says, on a merge request page, whether a run is one of the | |
| 33 | + | // head shown (its checks are on that head); a job event does not carry | |
| 34 | + | // its commit, so its run is read once. One stream reads it, no lock. | |
| 35 | + | headRuns map[int64]bool | |
| 32 | 36 | } | |
| 33 | 37 | ||
| 38 | + | // maxHeadRuns bounds headRuns: past it the map starts again. | |
| 39 | + | const maxHeadRuns = 256 | |
| 40 | + | ||
| 34 | 41 | // renderFragment renders a component to the data of an event. | |
| 35 | 42 | func renderFragment(ctx context.Context, event string, c templ.Component) (fragment, error) { | |
| 36 | 43 | var b bytes.Buffer | |
| @@ -51,7 +58,7 @@ func liveEvent(we ports.WatchEvent) bool { | |||
| 51 | 58 | } | |
| 52 | 59 | switch we.Event.(type) { | |
| 53 | 60 | case domain.RepoCreated, domain.RepoUpdated, domain.RepoDeleted, domain.PushEvent, | |
| 54 | - | domain.MergeRequestOpened, domain.MergeRequestMerged, domain.MergeRequestClosed, domain.CommentAdded, | |
| 61 | + | domain.MergeRequestOpened, domain.MergeRequestMerged, domain.MergeRequestClosed, domain.MergeRequestReady, domain.CommentAdded, | |
| 55 | 62 | domain.RunUpdated, domain.JobUpdated: | |
| 56 | 63 | return true | |
| 57 | 64 | } | |
| @@ -148,6 +155,8 @@ func (l *liveStream) repoPage(ctx context.Context, we ports.WatchEvent) ([]fragm | |||
| 148 | 155 | return l.mrNotice(ctx, ev.MergeRequest.Number, "this merge request was merged — reload") | |
| 149 | 156 | case domain.MergeRequestClosed: | |
| 150 | 157 | return l.mrNotice(ctx, ev.MergeRequest.Number, "this merge request was closed — reload") | |
| 158 | + | case domain.MergeRequestReady: | |
| 159 | + | return l.mrNotice(ctx, ev.MergeRequest.Number, "this merge request is ready for review — reload") | |
| 151 | 160 | case domain.CommentAdded: | |
| 152 | 161 | return l.mrNotice(ctx, ev.Number, "a comment was added — reload") | |
| 153 | 162 | case domain.PushEvent: | |
| @@ -431,10 +440,66 @@ func (l *liveStream) runEvent(ctx context.Context, ev domain.RunUpdated) ([]frag | |||
| 431 | 440 | return nil, nil | |
| 432 | 441 | } | |
| 433 | 442 | return l.runBody(ctx) | |
| 443 | + | case "mr": | |
| 444 | + | ofHead := ev.Run.CheckSha() == l.head | |
| 445 | + | l.rememberRun(ev.Run.ID, ofHead) | |
| 446 | + | if !ofHead { | |
| 447 | + | return nil, nil | |
| 448 | + | } | |
| 449 | + | return l.mrChecks(ctx) | |
| 434 | 450 | } | |
| 435 | 451 | return nil, nil | |
| 436 | 452 | } | |
| 437 | 453 | ||
| 454 | + | // rememberRun records whether a run is one of the head of the page. | |
| 455 | + | func (l *liveStream) rememberRun(id int64, ofHead bool) { | |
| 456 | + | if l.headRuns == nil || len(l.headRuns) >= maxHeadRuns { | |
| 457 | + | l.headRuns = make(map[int64]bool) | |
| 458 | + | } | |
| 459 | + | l.headRuns[id] = ofHead | |
| 460 | + | } | |
| 461 | + | ||
| 462 | + | // jobOfHead tells whether the run of a job is one of the head of the page, | |
| 463 | + | // reading the run the first time. | |
| 464 | + | func (l *liveStream) jobOfHead(ctx context.Context, job domain.CIJob) (bool, error) { | |
| 465 | + | if ofHead, known := l.headRuns[job.RunID]; known { | |
| 466 | + | return ofHead, nil | |
| 467 | + | } | |
| 468 | + | run, err := l.s.cfg.Runs.GetRun(ctx, principal(ctx), job.Repo, job.RunID, 0) | |
| 469 | + | switch { | |
| 470 | + | case errors.Is(err, domain.ErrNotFound): | |
| 471 | + | return false, nil | |
| 472 | + | case err != nil: | |
| 473 | + | return false, err | |
| 474 | + | } | |
| 475 | + | ofHead := run.Run.CheckSha() == l.head | |
| 476 | + | l.rememberRun(job.RunID, ofHead) | |
| 477 | + | return ofHead, nil | |
| 478 | + | } | |
| 479 | + | ||
| 480 | + | // mrChecks is the checks block of the merge request page, read again for the | |
| 481 | + | // head the page shows: a run or a job of that head changed. A job event does | |
| 482 | + | // not name its commit; reading the checks of the head is one store read, and | |
| 483 | + | // a job of another commit leaves them as they were. | |
| 484 | + | func (l *liveStream) mrChecks(ctx context.Context) ([]fragment, error) { | |
| 485 | + | if !isFullID(l.head) { | |
| 486 | + | return nil, nil | |
| 487 | + | } | |
| 488 | + | name, err := domain.ParseRepoName(l.repo) | |
| 489 | + | if err != nil { | |
| 490 | + | return nil, nil | |
| 491 | + | } | |
| 492 | + | repo, err := l.s.repoRecord(ctx, name) | |
| 493 | + | switch { | |
| 494 | + | case errors.Is(err, domain.ErrNotFound): | |
| 495 | + | return gap, nil | |
| 496 | + | case err != nil: | |
| 497 | + | return nil, err | |
| 498 | + | } | |
| 499 | + | f, err := renderFragment(ctx, "ci-run", MRChecks(checksSection(repo, l.s.checksFor(ctx, name, l.head)))) | |
| 500 | + | return []fragment{f}, err | |
| 501 | + | } | |
| 502 | + | ||
| 438 | 503 | // recentActions is the "Recent actions" block of the summary rail, read again | |
| 439 | 504 | // as the principal of the stream: the newest runs change with every event, and | |
| 440 | 505 | // a viewer who cannot read CI any more gets the block hidden and empty. | |
| @@ -470,6 +535,15 @@ func (l *liveStream) jobEvent(ctx context.Context, ev domain.JobUpdated) ([]frag | |||
| 470 | 535 | return nil, nil | |
| 471 | 536 | } | |
| 472 | 537 | return l.jobState(ctx) | |
| 538 | + | case "mr": | |
| 539 | + | if !isFullID(l.head) { | |
| 540 | + | return nil, nil | |
| 541 | + | } | |
| 542 | + | ofHead, err := l.jobOfHead(ctx, ev.Job) | |
| 543 | + | if err != nil || !ofHead { | |
| 544 | + | return nil, err | |
| 545 | + | } | |
| 546 | + | return l.mrChecks(ctx) | |
| 473 | 547 | } | |
| 474 | 548 | return nil, nil | |
| 475 | 549 | } | |
| @@ -29,8 +29,15 @@ type liveStream struct { | |||||
| 29 | // sha is the commit of the run of the job shown, read once when the stream | 29 | // sha is the commit of the run of the job shown, read once when the stream | ||
| 30 | // is set up: the annotations link to files at it, and it never changes. | 30 | // is set up: the annotations link to files at it, and it never changes. | ||
| 31 | sha string | 31 | sha string | ||
| 32 | + | // headRuns says, on a merge request page, whether a run is one of the | |||
| 33 | + | // head shown (its checks are on that head); a job event does not carry | |||
| 34 | + | // its commit, so its run is read once. One stream reads it, no lock. | |||
| 35 | + | headRuns map[int64]bool | |||
| 32 | } | 36 | } | ||
| 33 | 37 | ||||
| 38 | + | // maxHeadRuns bounds headRuns: past it the map starts again. | |||
| 39 | + | const maxHeadRuns = 256 | |||
| 40 | + | ||||
| 34 | // renderFragment renders a component to the data of an event. | 41 | // renderFragment renders a component to the data of an event. | ||
| 35 | func renderFragment(ctx context.Context, event string, c templ.Component) (fragment, error) { | 42 | func renderFragment(ctx context.Context, event string, c templ.Component) (fragment, error) { | ||
| 36 | var b bytes.Buffer | 43 | var b bytes.Buffer | ||
| @@ -51,7 +58,7 @@ func liveEvent(we ports.WatchEvent) bool { | |||||
| 51 | } | 58 | } | ||
| 52 | switch we.Event.(type) { | 59 | switch we.Event.(type) { | ||
| 53 | case domain.RepoCreated, domain.RepoUpdated, domain.RepoDeleted, domain.PushEvent, | 60 | case domain.RepoCreated, domain.RepoUpdated, domain.RepoDeleted, domain.PushEvent, | ||
| 54 | - | domain.MergeRequestOpened, domain.MergeRequestMerged, domain.MergeRequestClosed, domain.CommentAdded, | 61 | + | domain.MergeRequestOpened, domain.MergeRequestMerged, domain.MergeRequestClosed, domain.MergeRequestReady, domain.CommentAdded, |
| 55 | domain.RunUpdated, domain.JobUpdated: | 62 | domain.RunUpdated, domain.JobUpdated: | ||
| 56 | return true | 63 | return true | ||
| 57 | } | 64 | } | ||
| @@ -148,6 +155,8 @@ func (l *liveStream) repoPage(ctx context.Context, we ports.WatchEvent) ([]fragm | |||||
| 148 | return l.mrNotice(ctx, ev.MergeRequest.Number, "this merge request was merged — reload") | 155 | return l.mrNotice(ctx, ev.MergeRequest.Number, "this merge request was merged — reload") | ||
| 149 | case domain.MergeRequestClosed: | 156 | case domain.MergeRequestClosed: | ||
| 150 | return l.mrNotice(ctx, ev.MergeRequest.Number, "this merge request was closed — reload") | 157 | return l.mrNotice(ctx, ev.MergeRequest.Number, "this merge request was closed — reload") | ||
| 158 | + | case domain.MergeRequestReady: | |||
| 159 | + | return l.mrNotice(ctx, ev.MergeRequest.Number, "this merge request is ready for review — reload") | |||
| 151 | case domain.CommentAdded: | 160 | case domain.CommentAdded: | ||
| 152 | return l.mrNotice(ctx, ev.Number, "a comment was added — reload") | 161 | return l.mrNotice(ctx, ev.Number, "a comment was added — reload") | ||
| 153 | case domain.PushEvent: | 162 | case domain.PushEvent: | ||
| @@ -431,10 +440,66 @@ func (l *liveStream) runEvent(ctx context.Context, ev domain.RunUpdated) ([]frag | |||||
| 431 | return nil, nil | 440 | return nil, nil | ||
| 432 | } | 441 | } | ||
| 433 | return l.runBody(ctx) | 442 | return l.runBody(ctx) | ||
| 443 | + | case "mr": | |||
| 444 | + | ofHead := ev.Run.CheckSha() == l.head | |||
| 445 | + | l.rememberRun(ev.Run.ID, ofHead) | |||
| 446 | + | if !ofHead { | |||
| 447 | + | return nil, nil | |||
| 448 | + | } | |||
| 449 | + | return l.mrChecks(ctx) | |||
| 434 | } | 450 | } | ||
| 435 | return nil, nil | 451 | return nil, nil | ||
| 436 | } | 452 | } | ||
| 437 | 453 | ||||
| 454 | + | // rememberRun records whether a run is one of the head of the page. | |||
| 455 | + | func (l *liveStream) rememberRun(id int64, ofHead bool) { | |||
| 456 | + | if l.headRuns == nil || len(l.headRuns) >= maxHeadRuns { | |||
| 457 | + | l.headRuns = make(map[int64]bool) | |||
| 458 | + | } | |||
| 459 | + | l.headRuns[id] = ofHead | |||
| 460 | + | } | |||
| 461 | + | ||||
| 462 | + | // jobOfHead tells whether the run of a job is one of the head of the page, | |||
| 463 | + | // reading the run the first time. | |||
| 464 | + | func (l *liveStream) jobOfHead(ctx context.Context, job domain.CIJob) (bool, error) { | |||
| 465 | + | if ofHead, known := l.headRuns[job.RunID]; known { | |||
| 466 | + | return ofHead, nil | |||
| 467 | + | } | |||
| 468 | + | run, err := l.s.cfg.Runs.GetRun(ctx, principal(ctx), job.Repo, job.RunID, 0) | |||
| 469 | + | switch { | |||
| 470 | + | case errors.Is(err, domain.ErrNotFound): | |||
| 471 | + | return false, nil | |||
| 472 | + | case err != nil: | |||
| 473 | + | return false, err | |||
| 474 | + | } | |||
| 475 | + | ofHead := run.Run.CheckSha() == l.head | |||
| 476 | + | l.rememberRun(job.RunID, ofHead) | |||
| 477 | + | return ofHead, nil | |||
| 478 | + | } | |||
| 479 | + | ||||
| 480 | + | // mrChecks is the checks block of the merge request page, read again for the | |||
| 481 | + | // head the page shows: a run or a job of that head changed. A job event does | |||
| 482 | + | // not name its commit; reading the checks of the head is one store read, and | |||
| 483 | + | // a job of another commit leaves them as they were. | |||
| 484 | + | func (l *liveStream) mrChecks(ctx context.Context) ([]fragment, error) { | |||
| 485 | + | if !isFullID(l.head) { | |||
| 486 | + | return nil, nil | |||
| 487 | + | } | |||
| 488 | + | name, err := domain.ParseRepoName(l.repo) | |||
| 489 | + | if err != nil { | |||
| 490 | + | return nil, nil | |||
| 491 | + | } | |||
| 492 | + | repo, err := l.s.repoRecord(ctx, name) | |||
| 493 | + | switch { | |||
| 494 | + | case errors.Is(err, domain.ErrNotFound): | |||
| 495 | + | return gap, nil | |||
| 496 | + | case err != nil: | |||
| 497 | + | return nil, err | |||
| 498 | + | } | |||
| 499 | + | f, err := renderFragment(ctx, "ci-run", MRChecks(checksSection(repo, l.s.checksFor(ctx, name, l.head)))) | |||
| 500 | + | return []fragment{f}, err | |||
| 501 | + | } | |||
| 502 | + | ||||
| 438 | // recentActions is the "Recent actions" block of the summary rail, read again | 503 | // recentActions is the "Recent actions" block of the summary rail, read again | ||
| 439 | // as the principal of the stream: the newest runs change with every event, and | 504 | // as the principal of the stream: the newest runs change with every event, and | ||
| 440 | // a viewer who cannot read CI any more gets the block hidden and empty. | 505 | // a viewer who cannot read CI any more gets the block hidden and empty. | ||
| @@ -470,6 +535,15 @@ func (l *liveStream) jobEvent(ctx context.Context, ev domain.JobUpdated) ([]frag | |||||
| 470 | return nil, nil | 535 | return nil, nil | ||
| 471 | } | 536 | } | ||
| 472 | return l.jobState(ctx) | 537 | return l.jobState(ctx) | ||
| 538 | + | case "mr": | |||
| 539 | + | if !isFullID(l.head) { | |||
| 540 | + | return nil, nil | |||
| 541 | + | } | |||
| 542 | + | ofHead, err := l.jobOfHead(ctx, ev.Job) | |||
| 543 | + | if err != nil || !ofHead { | |||
| 544 | + | return nil, err | |||
| 545 | + | } | |||
| 546 | + | return l.mrChecks(ctx) | |||
| 473 | } | 547 | } | ||
| 474 | return nil, nil | 548 | return nil, nil | ||
| 475 | } | 549 | } | ||
M internal/adapters/web/mr.go +23 -8
| @@ -138,6 +138,9 @@ type MRCommit struct { | |||
| 138 | 138 | type MergeBox struct { | |
| 139 | 139 | MergeAction string | |
| 140 | 140 | CloseAction string | |
| 141 | + | // ReadyAction takes a draft out of draft; empty when it is not one: a | |
| 142 | + | // draft shows it in place of the merge form. | |
| 143 | + | ReadyAction string | |
| 141 | 144 | ReadAction string | |
| 142 | 145 | Head string | |
| 143 | 146 | Modes []ModeOption | |
| @@ -262,6 +265,15 @@ func mrNumber(r *http.Request) (int64, error) { | |||
| 262 | 265 | return n, nil | |
| 263 | 266 | } | |
| 264 | 267 | ||
| 268 | + | // mrStateName is the state a page shows: "draft" for an open request whose | |
| 269 | + | // title marks it so. | |
| 270 | + | func mrStateName(m domain.MergeRequest) string { | |
| 271 | + | if m.IsOpen() && m.IsDraft() { | |
| 272 | + | return "draft" | |
| 273 | + | } | |
| 274 | + | return stateName(m.State) | |
| 275 | + | } | |
| 276 | + | ||
| 265 | 277 | func stateName(s domain.MRState) string { | |
| 266 | 278 | switch s { | |
| 267 | 279 | case domain.MRStateOpen: | |
| @@ -287,10 +299,10 @@ func modeName(m domain.MergeMode) string { | |||
| 287 | 299 | // mrRow converts a merge request of a listing. | |
| 288 | 300 | func (s *Server) mrRow(repo string, m domain.MergeRequest) MRRow { | |
| 289 | 301 | row := MRRow{ | |
| 290 | - | Number: "!" + strconv.FormatInt(m.Number, 10), Href: mrHref(repo, m.Number), | |
| 291 | - | Title: visibleText(m.Title.String()), State: stateName(m.State), | |
| 302 | + | Number: "#" + strconv.FormatInt(m.Number, 10), Href: mrHref(repo, m.Number), | |
| 303 | + | Title: visibleText(m.Title.String()), State: mrStateName(m), | |
| 292 | 304 | Branches: visibleText(m.Source) + " → " + visibleText(m.Target), | |
| 293 | - | Author: visibleText(m.Author), | |
| 305 | + | Author: s.who(m.Author), | |
| 294 | 306 | } | |
| 295 | 307 | ts := m.UpdatedAt | |
| 296 | 308 | if ts.IsZero() { | |
| @@ -422,10 +434,10 @@ func (s *Server) mrPage(ctx context.Context, r *http.Request, since bool) (templ | |||
| 422 | 434 | } | |
| 423 | 435 | ||
| 424 | 436 | p := MRPage{ | |
| 425 | - | Number: "!" + strconv.FormatInt(n, 10), Title: visibleText(m.Title.String()), | |
| 426 | - | State: stateName(m.State), Open: open, Signed: signed, | |
| 437 | + | Number: "#" + strconv.FormatInt(n, 10), Title: visibleText(m.Title.String()), | |
| 438 | + | State: mrStateName(m), Open: open, Signed: signed, | |
| 427 | 439 | Source: visibleText(m.Source), Target: visibleText(m.Target), | |
| 428 | - | Author: visibleText(m.Author), | |
| 440 | + | Author: s.who(m.Author), | |
| 429 | 441 | Desc: plainParts(m.Description.String()), | |
| 430 | 442 | Head: head, HeadShort: short(head), | |
| 431 | 443 | NFiles: res.Stats.FilesChanged, | |
| @@ -529,6 +541,9 @@ func (s *Server) mrPage(ctx context.Context, r *http.Request, since bool) (templ | |||
| 529 | 541 | p.DiffView = newDiffView(r, lines, self+"?view=unified", self+"?view=split") | |
| 530 | 542 | if signed && open { | |
| 531 | 543 | p.Box = s.mergeBox(d, n, m.Source, head, lastRead) | |
| 544 | + | if m.IsDraft() { | |
| 545 | + | p.Box.ReadyAction = mrHref(d.name(), n, "ready") | |
| 546 | + | } | |
| 532 | 547 | } | |
| 533 | 548 | if canWrite { | |
| 534 | 549 | p.GeneralForm = &CommentForm{ | |
| @@ -553,7 +568,7 @@ func (s *Server) mrPage(ctx context.Context, r *http.Request, since bool) (templ | |||
| 553 | 568 | l.Crumbs = append(l.Crumbs, Crumb{Label: "since", Current: true}) | |
| 554 | 569 | } | |
| 555 | 570 | p.Live = true | |
| 556 | - | l.Live, l.LiveURL = true, liveURL("repo:"+d.name(), "view=mr&n="+strconv.FormatInt(n, 10)+"&ref="+url.QueryEscape(m.Source)) | |
| 571 | + | l.Live, l.LiveURL = true, liveURL("repo:"+d.name(), "view=mr&n="+strconv.FormatInt(n, 10)+"&ref="+url.QueryEscape(m.Source)+"&head="+url.QueryEscape(head)) | |
| 557 | 572 | l.HasRail = true | |
| 558 | 573 | return MergeRequest(l, p), http.StatusOK, nil | |
| 559 | 574 | } | |
| @@ -683,7 +698,7 @@ func (s *Server) placeComments(p *MRPage, repo string, n int64, comments []domai | |||
| 683 | 698 | for _, c := range comments { | |
| 684 | 699 | cid := strconv.FormatInt(c.ID, 10) | |
| 685 | 700 | v := CommentView{ | |
| 686 | - | ID: cid, Author: visibleText(c.Author), | |
| 701 | + | ID: cid, Author: s.who(c.Author), | |
| 687 | 702 | Parts: plainParts(c.Text.String()), Resolved: c.Resolved, | |
| 688 | 703 | Outdated: !c.CommitID.IsZero() && c.CommitID.String() != head, | |
| 689 | 704 | } | |
| @@ -138,6 +138,9 @@ type MRCommit struct { | |||||
| 138 | type MergeBox struct { | 138 | type MergeBox struct { | ||
| 139 | MergeAction string | 139 | MergeAction string | ||
| 140 | CloseAction string | 140 | CloseAction string | ||
| 141 | + | // ReadyAction takes a draft out of draft; empty when it is not one: a | |||
| 142 | + | // draft shows it in place of the merge form. | |||
| 143 | + | ReadyAction string | |||
| 141 | ReadAction string | 144 | ReadAction string | ||
| 142 | Head string | 145 | Head string | ||
| 143 | Modes []ModeOption | 146 | Modes []ModeOption | ||
| @@ -262,6 +265,15 @@ func mrNumber(r *http.Request) (int64, error) { | |||||
| 262 | return n, nil | 265 | return n, nil | ||
| 263 | } | 266 | } | ||
| 264 | 267 | ||||
| 268 | + | // mrStateName is the state a page shows: "draft" for an open request whose | |||
| 269 | + | // title marks it so. | |||
| 270 | + | func mrStateName(m domain.MergeRequest) string { | |||
| 271 | + | if m.IsOpen() && m.IsDraft() { | |||
| 272 | + | return "draft" | |||
| 273 | + | } | |||
| 274 | + | return stateName(m.State) | |||
| 275 | + | } | |||
| 276 | + | ||||
| 265 | func stateName(s domain.MRState) string { | 277 | func stateName(s domain.MRState) string { | ||
| 266 | switch s { | 278 | switch s { | ||
| 267 | case domain.MRStateOpen: | 279 | case domain.MRStateOpen: | ||
| @@ -287,10 +299,10 @@ func modeName(m domain.MergeMode) string { | |||||
| 287 | // mrRow converts a merge request of a listing. | 299 | // mrRow converts a merge request of a listing. | ||
| 288 | func (s *Server) mrRow(repo string, m domain.MergeRequest) MRRow { | 300 | func (s *Server) mrRow(repo string, m domain.MergeRequest) MRRow { | ||
| 289 | row := MRRow{ | 301 | row := MRRow{ | ||
| 290 | - | Number: "!" + strconv.FormatInt(m.Number, 10), Href: mrHref(repo, m.Number), | 302 | + | Number: "#" + strconv.FormatInt(m.Number, 10), Href: mrHref(repo, m.Number), |
| 291 | - | Title: visibleText(m.Title.String()), State: stateName(m.State), | 303 | + | Title: visibleText(m.Title.String()), State: mrStateName(m), |
| 292 | Branches: visibleText(m.Source) + " → " + visibleText(m.Target), | 304 | Branches: visibleText(m.Source) + " → " + visibleText(m.Target), | ||
| 293 | - | Author: visibleText(m.Author), | 305 | + | Author: s.who(m.Author), |
| 294 | } | 306 | } | ||
| 295 | ts := m.UpdatedAt | 307 | ts := m.UpdatedAt | ||
| 296 | if ts.IsZero() { | 308 | if ts.IsZero() { | ||
| @@ -422,10 +434,10 @@ func (s *Server) mrPage(ctx context.Context, r *http.Request, since bool) (templ | |||||
| 422 | } | 434 | } | ||
| 423 | 435 | ||||
| 424 | p := MRPage{ | 436 | p := MRPage{ | ||
| 425 | - | Number: "!" + strconv.FormatInt(n, 10), Title: visibleText(m.Title.String()), | 437 | + | Number: "#" + strconv.FormatInt(n, 10), Title: visibleText(m.Title.String()), |
| 426 | - | State: stateName(m.State), Open: open, Signed: signed, | 438 | + | State: mrStateName(m), Open: open, Signed: signed, |
| 427 | Source: visibleText(m.Source), Target: visibleText(m.Target), | 439 | Source: visibleText(m.Source), Target: visibleText(m.Target), | ||
| 428 | - | Author: visibleText(m.Author), | 440 | + | Author: s.who(m.Author), |
| 429 | Desc: plainParts(m.Description.String()), | 441 | Desc: plainParts(m.Description.String()), | ||
| 430 | Head: head, HeadShort: short(head), | 442 | Head: head, HeadShort: short(head), | ||
| 431 | NFiles: res.Stats.FilesChanged, | 443 | NFiles: res.Stats.FilesChanged, | ||
| @@ -529,6 +541,9 @@ func (s *Server) mrPage(ctx context.Context, r *http.Request, since bool) (templ | |||||
| 529 | p.DiffView = newDiffView(r, lines, self+"?view=unified", self+"?view=split") | 541 | p.DiffView = newDiffView(r, lines, self+"?view=unified", self+"?view=split") | ||
| 530 | if signed && open { | 542 | if signed && open { | ||
| 531 | p.Box = s.mergeBox(d, n, m.Source, head, lastRead) | 543 | p.Box = s.mergeBox(d, n, m.Source, head, lastRead) | ||
| 544 | + | if m.IsDraft() { | |||
| 545 | + | p.Box.ReadyAction = mrHref(d.name(), n, "ready") | |||
| 546 | + | } | |||
| 532 | } | 547 | } | ||
| 533 | if canWrite { | 548 | if canWrite { | ||
| 534 | p.GeneralForm = &CommentForm{ | 549 | p.GeneralForm = &CommentForm{ | ||
| @@ -553,7 +568,7 @@ func (s *Server) mrPage(ctx context.Context, r *http.Request, since bool) (templ | |||||
| 553 | l.Crumbs = append(l.Crumbs, Crumb{Label: "since", Current: true}) | 568 | l.Crumbs = append(l.Crumbs, Crumb{Label: "since", Current: true}) | ||
| 554 | } | 569 | } | ||
| 555 | p.Live = true | 570 | p.Live = true | ||
| 556 | - | l.Live, l.LiveURL = true, liveURL("repo:"+d.name(), "view=mr&n="+strconv.FormatInt(n, 10)+"&ref="+url.QueryEscape(m.Source)) | 571 | + | l.Live, l.LiveURL = true, liveURL("repo:"+d.name(), "view=mr&n="+strconv.FormatInt(n, 10)+"&ref="+url.QueryEscape(m.Source)+"&head="+url.QueryEscape(head)) |
| 557 | l.HasRail = true | 572 | l.HasRail = true | ||
| 558 | return MergeRequest(l, p), http.StatusOK, nil | 573 | return MergeRequest(l, p), http.StatusOK, nil | ||
| 559 | } | 574 | } | ||
| @@ -683,7 +698,7 @@ func (s *Server) placeComments(p *MRPage, repo string, n int64, comments []domai | |||||
| 683 | for _, c := range comments { | 698 | for _, c := range comments { | ||
| 684 | cid := strconv.FormatInt(c.ID, 10) | 699 | cid := strconv.FormatInt(c.ID, 10) | ||
| 685 | v := CommentView{ | 700 | v := CommentView{ | ||
| 686 | - | ID: cid, Author: visibleText(c.Author), | 701 | + | ID: cid, Author: s.who(c.Author), |
| 687 | Parts: plainParts(c.Text.String()), Resolved: c.Resolved, | 702 | Parts: plainParts(c.Text.String()), Resolved: c.Resolved, | ||
| 688 | Outdated: !c.CommitID.IsZero() && c.CommitID.String() != head, | 703 | Outdated: !c.CommitID.IsZero() && c.CommitID.String() != head, | ||
| 689 | } | 704 | } | ||
M internal/adapters/web/mr.templ +11 -4
| @@ -368,7 +368,12 @@ templ mergeBox(p MRPage) { | |||
| 368 | 368 | <dt>into</dt> | |
| 369 | 369 | <dd>{ p.Target }</dd> | |
| 370 | 370 | </dl> | |
| 371 | - | if !p.Box.HeadGone { | |
| 371 | + | if p.Box.ReadyAction != "" { | |
| 372 | + | <p class="d-draft">this is a draft: it cannot be merged until it is ready for review.</p> | |
| 373 | + | <form class="d-btns" method="post" action={ templ.SafeURL(p.Box.ReadyAction) }> | |
| 374 | + | <button type="submit" class="btn primary" title="remove the draft prefix from the title">Ready for review</button> | |
| 375 | + | </form> | |
| 376 | + | } else if !p.Box.HeadGone { | |
| 372 | 377 | <form class="d-acts" method="post" action={ templ.SafeURL(p.Box.MergeAction) }> | |
| 373 | 378 | <input type="hidden" name="head_id" value={ p.Box.Head }/> | |
| 374 | 379 | <label class="sr" for="merge-mode">merge mode</label> | |
| @@ -396,9 +401,11 @@ templ mergeBox(p MRPage) { | |||
| 396 | 401 | <button type="submit" title="close without merging; the branch is kept" class="btn danger">Close</button> | |
| 397 | 402 | </form> | |
| 398 | 403 | </div> | |
| 399 | - | if !p.Box.HeadGone { | |
| 400 | - | <p class="mute d-or">or from a terminal</p> | |
| 401 | - | @CloneBlock(p.Box.CLI) | |
| 404 | + | if !p.Box.HeadGone && p.Box.ReadyAction == "" { | |
| 405 | + | <details class="d-or"> | |
| 406 | + | <summary class="mute">or from a terminal</summary> | |
| 407 | + | @CloneBlock(p.Box.CLI) | |
| 408 | + | </details> | |
| 402 | 409 | } | |
| 403 | 410 | </section> | |
| 404 | 411 | } | |
| @@ -368,7 +368,12 @@ templ mergeBox(p MRPage) { | |||||
| 368 | <dt>into</dt> | 368 | <dt>into</dt> | ||
| 369 | <dd>{ p.Target }</dd> | 369 | <dd>{ p.Target }</dd> | ||
| 370 | </dl> | 370 | </dl> | ||
| 371 | - | if !p.Box.HeadGone { | 371 | + | if p.Box.ReadyAction != "" { |
| 372 | + | <p class="d-draft">this is a draft: it cannot be merged until it is ready for review.</p> | |||
| 373 | + | <form class="d-btns" method="post" action={ templ.SafeURL(p.Box.ReadyAction) }> | |||
| 374 | + | <button type="submit" class="btn primary" title="remove the draft prefix from the title">Ready for review</button> | |||
| 375 | + | </form> | |||
| 376 | + | } else if !p.Box.HeadGone { | |||
| 372 | <form class="d-acts" method="post" action={ templ.SafeURL(p.Box.MergeAction) }> | 377 | <form class="d-acts" method="post" action={ templ.SafeURL(p.Box.MergeAction) }> | ||
| 373 | <input type="hidden" name="head_id" value={ p.Box.Head }/> | 378 | <input type="hidden" name="head_id" value={ p.Box.Head }/> | ||
| 374 | <label class="sr" for="merge-mode">merge mode</label> | 379 | <label class="sr" for="merge-mode">merge mode</label> | ||
| @@ -396,9 +401,11 @@ templ mergeBox(p MRPage) { | |||||
| 396 | <button type="submit" title="close without merging; the branch is kept" class="btn danger">Close</button> | 401 | <button type="submit" title="close without merging; the branch is kept" class="btn danger">Close</button> | ||
| 397 | </form> | 402 | </form> | ||
| 398 | </div> | 403 | </div> | ||
| 399 | - | if !p.Box.HeadGone { | 404 | + | if !p.Box.HeadGone && p.Box.ReadyAction == "" { |
| 400 | - | <p class="mute d-or">or from a terminal</p> | 405 | + | <details class="d-or"> |
| 401 | - | @CloneBlock(p.Box.CLI) | 406 | + | <summary class="mute">or from a terminal</summary> |
| 407 | + | @CloneBlock(p.Box.CLI) | |||
| 408 | + | </details> | |||
| 402 | } | 409 | } | ||
| 403 | </section> | 410 | </section> | ||
| 404 | } | 411 | } | ||
M internal/adapters/web/mr_post.go +19 -0
| @@ -260,6 +260,25 @@ func (s *Server) mrClose(ctx context.Context, r *http.Request) (string, string, | |||
| 260 | 260 | return back, back, nil | |
| 261 | 261 | } | |
| 262 | 262 | ||
| 263 | + | // mrReady is POST /<repo>/merge-requests/<n>/ready: the draft prefix leaves | |
| 264 | + | // the title. | |
| 265 | + | func (s *Server) mrReady(ctx context.Context, r *http.Request) (string, string, error) { | |
| 266 | + | repo := r.PathValue("repo") | |
| 267 | + | n, err := number(r) | |
| 268 | + | if err != nil { | |
| 269 | + | return "", "", err | |
| 270 | + | } | |
| 271 | + | back := mrHref(repo, n) | |
| 272 | + | name, err := domain.ParseRepoName(repo) | |
| 273 | + | if err != nil { | |
| 274 | + | return "", back, err | |
| 275 | + | } | |
| 276 | + | if _, err := s.cfg.MergeRequests.MarkReady(ctx, principal(ctx), name, n); err != nil { | |
| 277 | + | return "", back, err | |
| 278 | + | } | |
| 279 | + | return back, back, nil | |
| 280 | + | } | |
| 281 | + | ||
| 263 | 282 | // mrMerge is POST /<repo>/merge-requests/<n>/merge. The form carries the mode, | |
| 264 | 283 | // the head the owner was shown (head_id): the server merges only if the | |
| 265 | 284 | // branch still points to it, and delete_branch when the owner asked to delete | |
| @@ -260,6 +260,25 @@ func (s *Server) mrClose(ctx context.Context, r *http.Request) (string, string, | |||||
| 260 | return back, back, nil | 260 | return back, back, nil | ||
| 261 | } | 261 | } | ||
| 262 | 262 | ||||
| 263 | + | // mrReady is POST /<repo>/merge-requests/<n>/ready: the draft prefix leaves | |||
| 264 | + | // the title. | |||
| 265 | + | func (s *Server) mrReady(ctx context.Context, r *http.Request) (string, string, error) { | |||
| 266 | + | repo := r.PathValue("repo") | |||
| 267 | + | n, err := number(r) | |||
| 268 | + | if err != nil { | |||
| 269 | + | return "", "", err | |||
| 270 | + | } | |||
| 271 | + | back := mrHref(repo, n) | |||
| 272 | + | name, err := domain.ParseRepoName(repo) | |||
| 273 | + | if err != nil { | |||
| 274 | + | return "", back, err | |||
| 275 | + | } | |||
| 276 | + | if _, err := s.cfg.MergeRequests.MarkReady(ctx, principal(ctx), name, n); err != nil { | |||
| 277 | + | return "", back, err | |||
| 278 | + | } | |||
| 279 | + | return back, back, nil | |||
| 280 | + | } | |||
| 281 | + | ||||
| 263 | // mrMerge is POST /<repo>/merge-requests/<n>/merge. The form carries the mode, | 282 | // mrMerge is POST /<repo>/merge-requests/<n>/merge. The form carries the mode, | ||
| 264 | // the head the owner was shown (head_id): the server merges only if the | 283 | // the head the owner was shown (head_id): the server merges only if the | ||
| 265 | // branch still points to it, and delete_branch when the owner asked to delete | 284 | // branch still points to it, and delete_branch when the owner asked to delete | ||
M internal/adapters/web/mr_templ.go +251 -229
| @@ -1995,152 +1995,170 @@ func mergeBox(p MRPage) templ.Component { | |||
| 1995 | 1995 | if templ_7745c5c3_Err != nil { | |
| 1996 | 1996 | return templ_7745c5c3_Err | |
| 1997 | 1997 | } | |
| 1998 | - | if !p.Box.HeadGone { | |
| 1999 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 174, "<form class=\"d-acts\" method=\"post\" action=\"") | |
| 1998 | + | if p.Box.ReadyAction != "" { | |
| 1999 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 174, "<p class=\"d-draft\">this is a draft: it cannot be merged until it is ready for review.</p><form class=\"d-btns\" method=\"post\" action=\"") | |
| 2000 | 2000 | if templ_7745c5c3_Err != nil { | |
| 2001 | 2001 | return templ_7745c5c3_Err | |
| 2002 | 2002 | } | |
| 2003 | 2003 | var templ_7745c5c3_Var101 templ.SafeURL | |
| 2004 | - | templ_7745c5c3_Var101, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.MergeAction)) | |
| 2004 | + | templ_7745c5c3_Var101, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.ReadyAction)) | |
| 2005 | 2005 | if templ_7745c5c3_Err != nil { | |
| 2006 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 372, Col: 79} | |
| 2006 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 373, Col: 79} | |
| 2007 | 2007 | } | |
| 2008 | 2008 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var101)) | |
| 2009 | 2009 | if templ_7745c5c3_Err != nil { | |
| 2010 | 2010 | return templ_7745c5c3_Err | |
| 2011 | 2011 | } | |
| 2012 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 175, "\"><input type=\"hidden\" name=\"head_id\" value=\"") | |
| 2012 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 175, "\"><button type=\"submit\" class=\"btn primary\" title=\"remove the draft prefix from the title\">Ready for review</button></form>") | |
| 2013 | 2013 | if templ_7745c5c3_Err != nil { | |
| 2014 | 2014 | return templ_7745c5c3_Err | |
| 2015 | 2015 | } | |
| 2016 | - | var templ_7745c5c3_Var102 string | |
| 2017 | - | templ_7745c5c3_Var102, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Box.Head) | |
| 2016 | + | } else if !p.Box.HeadGone { | |
| 2017 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 176, "<form class=\"d-acts\" method=\"post\" action=\"") | |
| 2018 | 2018 | if templ_7745c5c3_Err != nil { | |
| 2019 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 373, Col: 58} | |
| 2019 | + | return templ_7745c5c3_Err | |
| 2020 | + | } | |
| 2021 | + | var templ_7745c5c3_Var102 templ.SafeURL | |
| 2022 | + | templ_7745c5c3_Var102, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.MergeAction)) | |
| 2023 | + | if templ_7745c5c3_Err != nil { | |
| 2024 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 377, Col: 79} | |
| 2025 | + | } | |
| 2026 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var102)) | |
| 2027 | + | if templ_7745c5c3_Err != nil { | |
| 2028 | + | return templ_7745c5c3_Err | |
| 2029 | + | } | |
| 2030 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 177, "\"><input type=\"hidden\" name=\"head_id\" value=\"") | |
| 2031 | + | if templ_7745c5c3_Err != nil { | |
| 2032 | + | return templ_7745c5c3_Err | |
| 2033 | + | } | |
| 2034 | + | var templ_7745c5c3_Var103 string | |
| 2035 | + | templ_7745c5c3_Var103, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Box.Head) | |
| 2036 | + | if templ_7745c5c3_Err != nil { | |
| 2037 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 378, Col: 58} | |
| 2020 | 2038 | } | |
| 2021 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var102) | |
| 2039 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var103) | |
| 2022 | 2040 | if templ_7745c5c3_Err != nil { | |
| 2023 | 2041 | return templ_7745c5c3_Err | |
| 2024 | 2042 | } | |
| 2025 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 176, "\"> <label class=\"sr\" for=\"merge-mode\">merge mode</label> <select id=\"merge-mode\" name=\"mode\">") | |
| 2043 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 178, "\"> <label class=\"sr\" for=\"merge-mode\">merge mode</label> <select id=\"merge-mode\" name=\"mode\">") | |
| 2026 | 2044 | if templ_7745c5c3_Err != nil { | |
| 2027 | 2045 | return templ_7745c5c3_Err | |
| 2028 | 2046 | } | |
| 2029 | 2047 | for _, m := range p.Box.Modes { | |
| 2030 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 177, "<option value=\"") | |
| 2048 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 179, "<option value=\"") | |
| 2031 | 2049 | if templ_7745c5c3_Err != nil { | |
| 2032 | 2050 | return templ_7745c5c3_Err | |
| 2033 | 2051 | } | |
| 2034 | - | var templ_7745c5c3_Var103 string | |
| 2035 | - | templ_7745c5c3_Var103, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.Value) | |
| 2052 | + | var templ_7745c5c3_Var104 string | |
| 2053 | + | templ_7745c5c3_Var104, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.Value) | |
| 2036 | 2054 | if templ_7745c5c3_Err != nil { | |
| 2037 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 377, Col: 29} | |
| 2055 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 382, Col: 29} | |
| 2038 | 2056 | } | |
| 2039 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var103) | |
| 2057 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var104) | |
| 2040 | 2058 | if templ_7745c5c3_Err != nil { | |
| 2041 | 2059 | return templ_7745c5c3_Err | |
| 2042 | 2060 | } | |
| 2043 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 178, "\">") | |
| 2061 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 180, "\">") | |
| 2044 | 2062 | if templ_7745c5c3_Err != nil { | |
| 2045 | 2063 | return templ_7745c5c3_Err | |
| 2046 | 2064 | } | |
| 2047 | - | var templ_7745c5c3_Var104 string | |
| 2048 | - | templ_7745c5c3_Var104, templ_7745c5c3_Err = templ.JoinStringErrs(m.Label) | |
| 2065 | + | var templ_7745c5c3_Var105 string | |
| 2066 | + | templ_7745c5c3_Var105, templ_7745c5c3_Err = templ.JoinStringErrs(m.Label) | |
| 2049 | 2067 | if templ_7745c5c3_Err != nil { | |
| 2050 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 377, Col: 41} | |
| 2068 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 382, Col: 41} | |
| 2051 | 2069 | } | |
| 2052 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var104)) | |
| 2070 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var105)) | |
| 2053 | 2071 | if templ_7745c5c3_Err != nil { | |
| 2054 | 2072 | return templ_7745c5c3_Err | |
| 2055 | 2073 | } | |
| 2056 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 179, "</option>") | |
| 2074 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 181, "</option>") | |
| 2057 | 2075 | if templ_7745c5c3_Err != nil { | |
| 2058 | 2076 | return templ_7745c5c3_Err | |
| 2059 | 2077 | } | |
| 2060 | 2078 | } | |
| 2061 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 180, "</select> ") | |
| 2079 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 182, "</select> ") | |
| 2062 | 2080 | if templ_7745c5c3_Err != nil { | |
| 2063 | 2081 | return templ_7745c5c3_Err | |
| 2064 | 2082 | } | |
| 2065 | 2083 | if p.Box.CanDeleteBranch { | |
| 2066 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 181, "<label class=\"d-check\"><input type=\"checkbox\" name=\"delete_branch\" value=\"1\"> delete ") | |
| 2084 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 183, "<label class=\"d-check\"><input type=\"checkbox\" name=\"delete_branch\" value=\"1\"> delete ") | |
| 2067 | 2085 | if templ_7745c5c3_Err != nil { | |
| 2068 | 2086 | return templ_7745c5c3_Err | |
| 2069 | 2087 | } | |
| 2070 | - | var templ_7745c5c3_Var105 string | |
| 2071 | - | templ_7745c5c3_Var105, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) | |
| 2088 | + | var templ_7745c5c3_Var106 string | |
| 2089 | + | templ_7745c5c3_Var106, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) | |
| 2072 | 2090 | if templ_7745c5c3_Err != nil { | |
| 2073 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 381, Col: 101} | |
| 2091 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 386, Col: 101} | |
| 2074 | 2092 | } | |
| 2075 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var105)) | |
| 2093 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var106)) | |
| 2076 | 2094 | if templ_7745c5c3_Err != nil { | |
| 2077 | 2095 | return templ_7745c5c3_Err | |
| 2078 | 2096 | } | |
| 2079 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 182, " after the merge</label>") | |
| 2097 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 184, " after the merge</label>") | |
| 2080 | 2098 | if templ_7745c5c3_Err != nil { | |
| 2081 | 2099 | return templ_7745c5c3_Err | |
| 2082 | 2100 | } | |
| 2083 | 2101 | } | |
| 2084 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 183, "<div class=\"d-btns\"><button type=\"submit\" class=\"btn primary\">Merge</button></div></form>") | |
| 2102 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 185, "<div class=\"d-btns\"><button type=\"submit\" class=\"btn primary\">Merge</button></div></form>") | |
| 2085 | 2103 | if templ_7745c5c3_Err != nil { | |
| 2086 | 2104 | return templ_7745c5c3_Err | |
| 2087 | 2105 | } | |
| 2088 | 2106 | } | |
| 2089 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 184, "<div class=\"d-btns\">") | |
| 2107 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 186, "<div class=\"d-btns\">") | |
| 2090 | 2108 | if templ_7745c5c3_Err != nil { | |
| 2091 | 2109 | return templ_7745c5c3_Err | |
| 2092 | 2110 | } | |
| 2093 | 2111 | if p.Box.CanMarkRead { | |
| 2094 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 185, "<form method=\"post\" action=\"") | |
| 2112 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 187, "<form method=\"post\" action=\"") | |
| 2095 | 2113 | if templ_7745c5c3_Err != nil { | |
| 2096 | 2114 | return templ_7745c5c3_Err | |
| 2097 | 2115 | } | |
| 2098 | - | var templ_7745c5c3_Var106 templ.SafeURL | |
| 2099 | - | templ_7745c5c3_Var106, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.ReadAction)) | |
| 2116 | + | var templ_7745c5c3_Var107 templ.SafeURL | |
| 2117 | + | templ_7745c5c3_Var107, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.ReadAction)) | |
| 2100 | 2118 | if templ_7745c5c3_Err != nil { | |
| 2101 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 390, Col: 64} | |
| 2119 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 395, Col: 64} | |
| 2102 | 2120 | } | |
| 2103 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var106)) | |
| 2121 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var107)) | |
| 2104 | 2122 | if templ_7745c5c3_Err != nil { | |
| 2105 | 2123 | return templ_7745c5c3_Err | |
| 2106 | 2124 | } | |
| 2107 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 186, "\"><input type=\"hidden\" name=\"head_id\" value=\"") | |
| 2125 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 188, "\"><input type=\"hidden\" name=\"head_id\" value=\"") | |
| 2108 | 2126 | if templ_7745c5c3_Err != nil { | |
| 2109 | 2127 | return templ_7745c5c3_Err | |
| 2110 | 2128 | } | |
| 2111 | - | var templ_7745c5c3_Var107 string | |
| 2112 | - | templ_7745c5c3_Var107, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Box.Head) | |
| 2129 | + | var templ_7745c5c3_Var108 string | |
| 2130 | + | templ_7745c5c3_Var108, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Box.Head) | |
| 2113 | 2131 | if templ_7745c5c3_Err != nil { | |
| 2114 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 391, Col: 59} | |
| 2132 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 396, Col: 59} | |
| 2115 | 2133 | } | |
| 2116 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var107) | |
| 2134 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var108) | |
| 2117 | 2135 | if templ_7745c5c3_Err != nil { | |
| 2118 | 2136 | return templ_7745c5c3_Err | |
| 2119 | 2137 | } | |
| 2120 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 187, "\"> <button type=\"submit\" title=\"the next visit can show only what changed after this head\" class=\"btn\">Mark read</button></form>") | |
| 2138 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 189, "\"> <button type=\"submit\" title=\"the next visit can show only what changed after this head\" class=\"btn\">Mark read</button></form>") | |
| 2121 | 2139 | if templ_7745c5c3_Err != nil { | |
| 2122 | 2140 | return templ_7745c5c3_Err | |
| 2123 | 2141 | } | |
| 2124 | 2142 | } | |
| 2125 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 188, "<form method=\"post\" action=\"") | |
| 2143 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 190, "<form method=\"post\" action=\"") | |
| 2126 | 2144 | if templ_7745c5c3_Err != nil { | |
| 2127 | 2145 | return templ_7745c5c3_Err | |
| 2128 | 2146 | } | |
| 2129 | - | var templ_7745c5c3_Var108 templ.SafeURL | |
| 2130 | - | templ_7745c5c3_Var108, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.CloseAction)) | |
| 2147 | + | var templ_7745c5c3_Var109 templ.SafeURL | |
| 2148 | + | templ_7745c5c3_Var109, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.CloseAction)) | |
| 2131 | 2149 | if templ_7745c5c3_Err != nil { | |
| 2132 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 395, Col: 64} | |
| 2150 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 400, Col: 64} | |
| 2133 | 2151 | } | |
| 2134 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var108)) | |
| 2152 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var109)) | |
| 2135 | 2153 | if templ_7745c5c3_Err != nil { | |
| 2136 | 2154 | return templ_7745c5c3_Err | |
| 2137 | 2155 | } | |
| 2138 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 189, "\"><button type=\"submit\" title=\"close without merging; the branch is kept\" class=\"btn danger\">Close</button></form></div>") | |
| 2156 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 191, "\"><button type=\"submit\" title=\"close without merging; the branch is kept\" class=\"btn danger\">Close</button></form></div>") | |
| 2139 | 2157 | if templ_7745c5c3_Err != nil { | |
| 2140 | 2158 | return templ_7745c5c3_Err | |
| 2141 | 2159 | } | |
| 2142 | - | if !p.Box.HeadGone { | |
| 2143 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 190, "<p class=\"mute d-or\">or from a terminal</p>") | |
| 2160 | + | if !p.Box.HeadGone && p.Box.ReadyAction == "" { | |
| 2161 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 192, "<details class=\"d-or\"><summary class=\"mute\">or from a terminal</summary>") | |
| 2144 | 2162 | if templ_7745c5c3_Err != nil { | |
| 2145 | 2163 | return templ_7745c5c3_Err | |
| 2146 | 2164 | } | |
| @@ -2148,8 +2166,12 @@ func mergeBox(p MRPage) templ.Component { | |||
| 2148 | 2166 | if templ_7745c5c3_Err != nil { | |
| 2149 | 2167 | return templ_7745c5c3_Err | |
| 2150 | 2168 | } | |
| 2169 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 193, "</details>") | |
| 2170 | + | if templ_7745c5c3_Err != nil { | |
| 2171 | + | return templ_7745c5c3_Err | |
| 2172 | + | } | |
| 2151 | 2173 | } | |
| 2152 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 191, "</section>") | |
| 2174 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 194, "</section>") | |
| 2153 | 2175 | if templ_7745c5c3_Err != nil { | |
| 2154 | 2176 | return templ_7745c5c3_Err | |
| 2155 | 2177 | } | |
| @@ -2175,12 +2197,12 @@ func Compare(l Layout, p ComparePage) templ.Component { | |||
| 2175 | 2197 | }() | |
| 2176 | 2198 | } | |
| 2177 | 2199 | ctx = templ.InitializeContext(ctx) | |
| 2178 | - | templ_7745c5c3_Var109 := templ.GetChildren(ctx) | |
| 2179 | - | if templ_7745c5c3_Var109 == nil { | |
| 2180 | - | templ_7745c5c3_Var109 = templ.NopComponent | |
| 2200 | + | templ_7745c5c3_Var110 := templ.GetChildren(ctx) | |
| 2201 | + | if templ_7745c5c3_Var110 == nil { | |
| 2202 | + | templ_7745c5c3_Var110 = templ.NopComponent | |
| 2181 | 2203 | } | |
| 2182 | 2204 | ctx = templ.ClearChildren(ctx) | |
| 2183 | - | templ_7745c5c3_Var110 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | |
| 2205 | + | templ_7745c5c3_Var111 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | |
| 2184 | 2206 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | |
| 2185 | 2207 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) | |
| 2186 | 2208 | if !templ_7745c5c3_IsBuffer { | |
| @@ -2192,263 +2214,263 @@ func Compare(l Layout, p ComparePage) templ.Component { | |||
| 2192 | 2214 | }() | |
| 2193 | 2215 | } | |
| 2194 | 2216 | ctx = templ.InitializeContext(ctx) | |
| 2195 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 192, "<header class=\"show mr-h\"><h1>") | |
| 2196 | - | if templ_7745c5c3_Err != nil { | |
| 2197 | - | return templ_7745c5c3_Err | |
| 2198 | - | } | |
| 2199 | - | var templ_7745c5c3_Var111 string | |
| 2200 | - | templ_7745c5c3_Var111, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) | |
| 2201 | - | if templ_7745c5c3_Err != nil { | |
| 2202 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 411, Col: 17} | |
| 2203 | - | } | |
| 2204 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var111)) | |
| 2205 | - | if templ_7745c5c3_Err != nil { | |
| 2206 | - | return templ_7745c5c3_Err | |
| 2207 | - | } | |
| 2208 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 193, " → ") | |
| 2217 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 195, "<header class=\"show mr-h\"><h1>") | |
| 2209 | 2218 | if templ_7745c5c3_Err != nil { | |
| 2210 | 2219 | return templ_7745c5c3_Err | |
| 2211 | 2220 | } | |
| 2212 | 2221 | var templ_7745c5c3_Var112 string | |
| 2213 | - | templ_7745c5c3_Var112, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 2222 | + | templ_7745c5c3_Var112, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) | |
| 2214 | 2223 | if templ_7745c5c3_Err != nil { | |
| 2215 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 411, Col: 34} | |
| 2224 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 418, Col: 17} | |
| 2216 | 2225 | } | |
| 2217 | 2226 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var112)) | |
| 2218 | 2227 | if templ_7745c5c3_Err != nil { | |
| 2219 | 2228 | return templ_7745c5c3_Err | |
| 2220 | 2229 | } | |
| 2221 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 194, "</h1><dl class=\"kv show-kv\"><dt>branch</dt><dd><a href=\"") | |
| 2230 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 196, " → ") | |
| 2222 | 2231 | if templ_7745c5c3_Err != nil { | |
| 2223 | 2232 | return templ_7745c5c3_Err | |
| 2224 | 2233 | } | |
| 2225 | - | var templ_7745c5c3_Var113 templ.SafeURL | |
| 2226 | - | templ_7745c5c3_Var113, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.SourceHref)) | |
| 2234 | + | var templ_7745c5c3_Var113 string | |
| 2235 | + | templ_7745c5c3_Var113, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 2227 | 2236 | if templ_7745c5c3_Err != nil { | |
| 2228 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 414, Col: 45} | |
| 2237 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 418, Col: 34} | |
| 2229 | 2238 | } | |
| 2230 | 2239 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var113)) | |
| 2231 | 2240 | if templ_7745c5c3_Err != nil { | |
| 2232 | 2241 | return templ_7745c5c3_Err | |
| 2233 | 2242 | } | |
| 2234 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 195, "\">") | |
| 2243 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 197, "</h1><dl class=\"kv show-kv\"><dt>branch</dt><dd><a href=\"") | |
| 2235 | 2244 | if templ_7745c5c3_Err != nil { | |
| 2236 | 2245 | return templ_7745c5c3_Err | |
| 2237 | 2246 | } | |
| 2238 | - | var templ_7745c5c3_Var114 string | |
| 2239 | - | templ_7745c5c3_Var114, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) | |
| 2247 | + | var templ_7745c5c3_Var114 templ.SafeURL | |
| 2248 | + | templ_7745c5c3_Var114, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.SourceHref)) | |
| 2240 | 2249 | if templ_7745c5c3_Err != nil { | |
| 2241 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 414, Col: 58} | |
| 2250 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 421, Col: 45} | |
| 2242 | 2251 | } | |
| 2243 | 2252 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var114)) | |
| 2244 | 2253 | if templ_7745c5c3_Err != nil { | |
| 2245 | 2254 | return templ_7745c5c3_Err | |
| 2246 | 2255 | } | |
| 2247 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 196, "</a></dd><dt>target</dt><dd><a href=\"") | |
| 2256 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 198, "\">") | |
| 2248 | 2257 | if templ_7745c5c3_Err != nil { | |
| 2249 | 2258 | return templ_7745c5c3_Err | |
| 2250 | 2259 | } | |
| 2251 | - | var templ_7745c5c3_Var115 templ.SafeURL | |
| 2252 | - | templ_7745c5c3_Var115, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.TargetHref)) | |
| 2260 | + | var templ_7745c5c3_Var115 string | |
| 2261 | + | templ_7745c5c3_Var115, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) | |
| 2253 | 2262 | if templ_7745c5c3_Err != nil { | |
| 2254 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 416, Col: 45} | |
| 2263 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 421, Col: 58} | |
| 2255 | 2264 | } | |
| 2256 | 2265 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var115)) | |
| 2257 | 2266 | if templ_7745c5c3_Err != nil { | |
| 2258 | 2267 | return templ_7745c5c3_Err | |
| 2259 | 2268 | } | |
| 2260 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 197, "\">") | |
| 2269 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 199, "</a></dd><dt>target</dt><dd><a href=\"") | |
| 2261 | 2270 | if templ_7745c5c3_Err != nil { | |
| 2262 | 2271 | return templ_7745c5c3_Err | |
| 2263 | 2272 | } | |
| 2264 | - | var templ_7745c5c3_Var116 string | |
| 2265 | - | templ_7745c5c3_Var116, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 2273 | + | var templ_7745c5c3_Var116 templ.SafeURL | |
| 2274 | + | templ_7745c5c3_Var116, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.TargetHref)) | |
| 2266 | 2275 | if templ_7745c5c3_Err != nil { | |
| 2267 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 416, Col: 58} | |
| 2276 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 423, Col: 45} | |
| 2268 | 2277 | } | |
| 2269 | 2278 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var116)) | |
| 2270 | 2279 | if templ_7745c5c3_Err != nil { | |
| 2271 | 2280 | return templ_7745c5c3_Err | |
| 2272 | 2281 | } | |
| 2273 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 198, "</a></dd><dt>ahead</dt><dd>") | |
| 2282 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 200, "\">") | |
| 2274 | 2283 | if templ_7745c5c3_Err != nil { | |
| 2275 | 2284 | return templ_7745c5c3_Err | |
| 2276 | 2285 | } | |
| 2277 | 2286 | var templ_7745c5c3_Var117 string | |
| 2278 | - | templ_7745c5c3_Var117, templ_7745c5c3_Err = templ.JoinStringErrs(p.AheadText) | |
| 2287 | + | templ_7745c5c3_Var117, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 2279 | 2288 | if templ_7745c5c3_Err != nil { | |
| 2280 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 418, Col: 21} | |
| 2289 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 423, Col: 58} | |
| 2281 | 2290 | } | |
| 2282 | 2291 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var117)) | |
| 2283 | 2292 | if templ_7745c5c3_Err != nil { | |
| 2284 | 2293 | return templ_7745c5c3_Err | |
| 2285 | 2294 | } | |
| 2286 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 199, "</dd></dl></header>") | |
| 2295 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 201, "</a></dd><dt>ahead</dt><dd>") | |
| 2296 | + | if templ_7745c5c3_Err != nil { | |
| 2297 | + | return templ_7745c5c3_Err | |
| 2298 | + | } | |
| 2299 | + | var templ_7745c5c3_Var118 string | |
| 2300 | + | templ_7745c5c3_Var118, templ_7745c5c3_Err = templ.JoinStringErrs(p.AheadText) | |
| 2301 | + | if templ_7745c5c3_Err != nil { | |
| 2302 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 425, Col: 21} | |
| 2303 | + | } | |
| 2304 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var118)) | |
| 2305 | + | if templ_7745c5c3_Err != nil { | |
| 2306 | + | return templ_7745c5c3_Err | |
| 2307 | + | } | |
| 2308 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 202, "</dd></dl></header>") | |
| 2287 | 2309 | if templ_7745c5c3_Err != nil { | |
| 2288 | 2310 | return templ_7745c5c3_Err | |
| 2289 | 2311 | } | |
| 2290 | 2312 | if p.Note != "" { | |
| 2291 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 200, "<p class=\"notice\">") | |
| 2313 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 203, "<p class=\"notice\">") | |
| 2292 | 2314 | if templ_7745c5c3_Err != nil { | |
| 2293 | 2315 | return templ_7745c5c3_Err | |
| 2294 | 2316 | } | |
| 2295 | - | var templ_7745c5c3_Var118 string | |
| 2296 | - | templ_7745c5c3_Var118, templ_7745c5c3_Err = templ.JoinStringErrs(p.Note) | |
| 2317 | + | var templ_7745c5c3_Var119 string | |
| 2318 | + | templ_7745c5c3_Var119, templ_7745c5c3_Err = templ.JoinStringErrs(p.Note) | |
| 2297 | 2319 | if templ_7745c5c3_Err != nil { | |
| 2298 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 422, Col: 29} | |
| 2320 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 429, Col: 29} | |
| 2299 | 2321 | } | |
| 2300 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var118)) | |
| 2322 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var119)) | |
| 2301 | 2323 | if templ_7745c5c3_Err != nil { | |
| 2302 | 2324 | return templ_7745c5c3_Err | |
| 2303 | 2325 | } | |
| 2304 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 201, "</p>") | |
| 2326 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 204, "</p>") | |
| 2305 | 2327 | if templ_7745c5c3_Err != nil { | |
| 2306 | 2328 | return templ_7745c5c3_Err | |
| 2307 | 2329 | } | |
| 2308 | 2330 | } | |
| 2309 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 202, " ") | |
| 2331 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 205, " ") | |
| 2310 | 2332 | if templ_7745c5c3_Err != nil { | |
| 2311 | 2333 | return templ_7745c5c3_Err | |
| 2312 | 2334 | } | |
| 2313 | 2335 | if p.Existing != nil { | |
| 2314 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 203, "<p class=\"notice\">an open merge request already covers these branches: <a href=\"") | |
| 2336 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 206, "<p class=\"notice\">an open merge request already covers these branches: <a href=\"") | |
| 2315 | 2337 | if templ_7745c5c3_Err != nil { | |
| 2316 | 2338 | return templ_7745c5c3_Err | |
| 2317 | 2339 | } | |
| 2318 | - | var templ_7745c5c3_Var119 templ.SafeURL | |
| 2319 | - | templ_7745c5c3_Var119, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Existing.Href)) | |
| 2340 | + | var templ_7745c5c3_Var120 templ.SafeURL | |
| 2341 | + | templ_7745c5c3_Var120, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Existing.Href)) | |
| 2320 | 2342 | if templ_7745c5c3_Err != nil { | |
| 2321 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 425, Col: 114} | |
| 2343 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 432, Col: 114} | |
| 2322 | 2344 | } | |
| 2323 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var119)) | |
| 2345 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var120)) | |
| 2324 | 2346 | if templ_7745c5c3_Err != nil { | |
| 2325 | 2347 | return templ_7745c5c3_Err | |
| 2326 | 2348 | } | |
| 2327 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 204, "\">") | |
| 2349 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 207, "\">") | |
| 2328 | 2350 | if templ_7745c5c3_Err != nil { | |
| 2329 | 2351 | return templ_7745c5c3_Err | |
| 2330 | 2352 | } | |
| 2331 | - | var templ_7745c5c3_Var120 string | |
| 2332 | - | templ_7745c5c3_Var120, templ_7745c5c3_Err = templ.JoinStringErrs(p.Existing.Label) | |
| 2353 | + | var templ_7745c5c3_Var121 string | |
| 2354 | + | templ_7745c5c3_Var121, templ_7745c5c3_Err = templ.JoinStringErrs(p.Existing.Label) | |
| 2333 | 2355 | if templ_7745c5c3_Err != nil { | |
| 2334 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 425, Col: 135} | |
| 2356 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 432, Col: 135} | |
| 2335 | 2357 | } | |
| 2336 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var120)) | |
| 2358 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var121)) | |
| 2337 | 2359 | if templ_7745c5c3_Err != nil { | |
| 2338 | 2360 | return templ_7745c5c3_Err | |
| 2339 | 2361 | } | |
| 2340 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 205, "</a></p>") | |
| 2362 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 208, "</a></p>") | |
| 2341 | 2363 | if templ_7745c5c3_Err != nil { | |
| 2342 | 2364 | return templ_7745c5c3_Err | |
| 2343 | 2365 | } | |
| 2344 | 2366 | } | |
| 2345 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 206, " ") | |
| 2367 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 209, " ") | |
| 2346 | 2368 | if templ_7745c5c3_Err != nil { | |
| 2347 | 2369 | return templ_7745c5c3_Err | |
| 2348 | 2370 | } | |
| 2349 | 2371 | if p.Create != nil { | |
| 2350 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 207, "<form class=\"cform create\" method=\"post\" action=\"") | |
| 2372 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 210, "<form class=\"cform create\" method=\"post\" action=\"") | |
| 2351 | 2373 | if templ_7745c5c3_Err != nil { | |
| 2352 | 2374 | return templ_7745c5c3_Err | |
| 2353 | 2375 | } | |
| 2354 | - | var templ_7745c5c3_Var121 templ.SafeURL | |
| 2355 | - | templ_7745c5c3_Var121, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Create.Action)) | |
| 2376 | + | var templ_7745c5c3_Var122 templ.SafeURL | |
| 2377 | + | templ_7745c5c3_Var122, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Create.Action)) | |
| 2356 | 2378 | if templ_7745c5c3_Err != nil { | |
| 2357 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 428, Col: 83} | |
| 2379 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 435, Col: 83} | |
| 2358 | 2380 | } | |
| 2359 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var121)) | |
| 2381 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var122)) | |
| 2360 | 2382 | if templ_7745c5c3_Err != nil { | |
| 2361 | 2383 | return templ_7745c5c3_Err | |
| 2362 | 2384 | } | |
| 2363 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 208, "\"><input type=\"hidden\" name=\"source\" value=\"") | |
| 2385 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 211, "\"><input type=\"hidden\" name=\"source\" value=\"") | |
| 2364 | 2386 | if templ_7745c5c3_Err != nil { | |
| 2365 | 2387 | return templ_7745c5c3_Err | |
| 2366 | 2388 | } | |
| 2367 | - | var templ_7745c5c3_Var122 string | |
| 2368 | - | templ_7745c5c3_Var122, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Source) | |
| 2389 | + | var templ_7745c5c3_Var123 string | |
| 2390 | + | templ_7745c5c3_Var123, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Source) | |
| 2369 | 2391 | if templ_7745c5c3_Err != nil { | |
| 2370 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 429, Col: 62} | |
| 2392 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 436, Col: 62} | |
| 2371 | 2393 | } | |
| 2372 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var122) | |
| 2394 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var123) | |
| 2373 | 2395 | if templ_7745c5c3_Err != nil { | |
| 2374 | 2396 | return templ_7745c5c3_Err | |
| 2375 | 2397 | } | |
| 2376 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 209, "\"> <input type=\"hidden\" name=\"target\" value=\"") | |
| 2398 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 212, "\"> <input type=\"hidden\" name=\"target\" value=\"") | |
| 2377 | 2399 | if templ_7745c5c3_Err != nil { | |
| 2378 | 2400 | return templ_7745c5c3_Err | |
| 2379 | 2401 | } | |
| 2380 | - | var templ_7745c5c3_Var123 string | |
| 2381 | - | templ_7745c5c3_Var123, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Target) | |
| 2402 | + | var templ_7745c5c3_Var124 string | |
| 2403 | + | templ_7745c5c3_Var124, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Target) | |
| 2382 | 2404 | if templ_7745c5c3_Err != nil { | |
| 2383 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 430, Col: 62} | |
| 2405 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 437, Col: 62} | |
| 2384 | 2406 | } | |
| 2385 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var123) | |
| 2407 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var124) | |
| 2386 | 2408 | if templ_7745c5c3_Err != nil { | |
| 2387 | 2409 | return templ_7745c5c3_Err | |
| 2388 | 2410 | } | |
| 2389 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 210, "\"> <label for=\"mr-title\">title</label> <input id=\"mr-title\" name=\"title\" type=\"text\" maxlength=\"120\" required value=\"") | |
| 2411 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 213, "\"> <label for=\"mr-title\">title</label> <input id=\"mr-title\" name=\"title\" type=\"text\" maxlength=\"120\" required value=\"") | |
| 2390 | 2412 | if templ_7745c5c3_Err != nil { | |
| 2391 | 2413 | return templ_7745c5c3_Err | |
| 2392 | 2414 | } | |
| 2393 | - | var templ_7745c5c3_Var124 string | |
| 2394 | - | templ_7745c5c3_Var124, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Title) | |
| 2415 | + | var templ_7745c5c3_Var125 string | |
| 2416 | + | templ_7745c5c3_Var125, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Title) | |
| 2395 | 2417 | if templ_7745c5c3_Err != nil { | |
| 2396 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 432, Col: 97} | |
| 2418 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 439, Col: 97} | |
| 2397 | 2419 | } | |
| 2398 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var124) | |
| 2420 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var125) | |
| 2399 | 2421 | if templ_7745c5c3_Err != nil { | |
| 2400 | 2422 | return templ_7745c5c3_Err | |
| 2401 | 2423 | } | |
| 2402 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 211, "\" autocomplete=\"off\"> <label for=\"mr-desc\">description</label> <textarea id=\"mr-desc\" name=\"description\" rows=\"4\" maxlength=\"8000\" placeholder=\"plain text, optional\"></textarea><div class=\"f-btns\"><button type=\"submit\" class=\"btn primary\">Create merge request</button></div></form>") | |
| 2424 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 214, "\" autocomplete=\"off\"> <label for=\"mr-desc\">description</label> <textarea id=\"mr-desc\" name=\"description\" rows=\"4\" maxlength=\"8000\" placeholder=\"plain text, optional\"></textarea><div class=\"f-btns\"><button type=\"submit\" class=\"btn primary\">Create merge request</button></div></form>") | |
| 2403 | 2425 | if templ_7745c5c3_Err != nil { | |
| 2404 | 2426 | return templ_7745c5c3_Err | |
| 2405 | 2427 | } | |
| 2406 | 2428 | } | |
| 2407 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 212, " ") | |
| 2429 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 215, " ") | |
| 2408 | 2430 | if templ_7745c5c3_Err != nil { | |
| 2409 | 2431 | return templ_7745c5c3_Err | |
| 2410 | 2432 | } | |
| 2411 | 2433 | if p.HasDiff { | |
| 2412 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 213, "<div class=\"diff-tools\" role=\"toolbar\" aria-label=\"Diff view\"><span class=\"dim\">") | |
| 2434 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 216, "<div class=\"diff-tools\" role=\"toolbar\" aria-label=\"Diff view\"><span class=\"dim\">") | |
| 2413 | 2435 | if templ_7745c5c3_Err != nil { | |
| 2414 | 2436 | return templ_7745c5c3_Err | |
| 2415 | 2437 | } | |
| 2416 | - | var templ_7745c5c3_Var125 string | |
| 2417 | - | templ_7745c5c3_Var125, templ_7745c5c3_Err = templ.JoinStringErrs(plural(p.NFiles, "file", "files")) | |
| 2438 | + | var templ_7745c5c3_Var126 string | |
| 2439 | + | templ_7745c5c3_Var126, templ_7745c5c3_Err = templ.JoinStringErrs(plural(p.NFiles, "file", "files")) | |
| 2418 | 2440 | if templ_7745c5c3_Err != nil { | |
| 2419 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 442, Col: 57} | |
| 2441 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 449, Col: 57} | |
| 2420 | 2442 | } | |
| 2421 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var125)) | |
| 2443 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var126)) | |
| 2422 | 2444 | if templ_7745c5c3_Err != nil { | |
| 2423 | 2445 | return templ_7745c5c3_Err | |
| 2424 | 2446 | } | |
| 2425 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 214, " · <span class=\"plus\">+") | |
| 2447 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 217, " · <span class=\"plus\">+") | |
| 2426 | 2448 | if templ_7745c5c3_Err != nil { | |
| 2427 | 2449 | return templ_7745c5c3_Err | |
| 2428 | 2450 | } | |
| 2429 | - | var templ_7745c5c3_Var126 string | |
| 2430 | - | templ_7745c5c3_Var126, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Adds)) | |
| 2451 | + | var templ_7745c5c3_Var127 string | |
| 2452 | + | templ_7745c5c3_Var127, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Adds)) | |
| 2431 | 2453 | if templ_7745c5c3_Err != nil { | |
| 2432 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 442, Col: 97} | |
| 2454 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 449, Col: 97} | |
| 2433 | 2455 | } | |
| 2434 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var126)) | |
| 2456 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var127)) | |
| 2435 | 2457 | if templ_7745c5c3_Err != nil { | |
| 2436 | 2458 | return templ_7745c5c3_Err | |
| 2437 | 2459 | } | |
| 2438 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 215, "</span> <span class=\"minus\">-") | |
| 2460 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 218, "</span> <span class=\"minus\">-") | |
| 2439 | 2461 | if templ_7745c5c3_Err != nil { | |
| 2440 | 2462 | return templ_7745c5c3_Err | |
| 2441 | 2463 | } | |
| 2442 | - | var templ_7745c5c3_Var127 string | |
| 2443 | - | templ_7745c5c3_Var127, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Dels)) | |
| 2464 | + | var templ_7745c5c3_Var128 string | |
| 2465 | + | templ_7745c5c3_Var128, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Dels)) | |
| 2444 | 2466 | if templ_7745c5c3_Err != nil { | |
| 2445 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 442, Col: 142} | |
| 2467 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 449, Col: 142} | |
| 2446 | 2468 | } | |
| 2447 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var127)) | |
| 2469 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var128)) | |
| 2448 | 2470 | if templ_7745c5c3_Err != nil { | |
| 2449 | 2471 | return templ_7745c5c3_Err | |
| 2450 | 2472 | } | |
| 2451 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 216, "</span></span> <span class=\"grow\"></span>") | |
| 2473 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 219, "</span></span> <span class=\"grow\"></span>") | |
| 2452 | 2474 | if templ_7745c5c3_Err != nil { | |
| 2453 | 2475 | return templ_7745c5c3_Err | |
| 2454 | 2476 | } | |
| @@ -2464,64 +2486,64 @@ func Compare(l Layout, p ComparePage) templ.Component { | |||
| 2464 | 2486 | if templ_7745c5c3_Err != nil { | |
| 2465 | 2487 | return templ_7745c5c3_Err | |
| 2466 | 2488 | } | |
| 2467 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 217, "</div>") | |
| 2489 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 220, "</div>") | |
| 2468 | 2490 | if templ_7745c5c3_Err != nil { | |
| 2469 | 2491 | return templ_7745c5c3_Err | |
| 2470 | 2492 | } | |
| 2471 | 2493 | if p.Invisible { | |
| 2472 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 218, "<p class=\"notice\">this diff holds invisible characters (direction controls, zero-width characters). They are shown as <span class=\"inv\"><U+XXXX></span> markers, never applied.</p>") | |
| 2494 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 221, "<p class=\"notice\">this diff holds invisible characters (direction controls, zero-width characters). They are shown as <span class=\"inv\"><U+XXXX></span> markers, never applied.</p>") | |
| 2473 | 2495 | if templ_7745c5c3_Err != nil { | |
| 2474 | 2496 | return templ_7745c5c3_Err | |
| 2475 | 2497 | } | |
| 2476 | 2498 | } | |
| 2477 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 219, " <div id=\"diffs\"") | |
| 2499 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 222, " <div id=\"diffs\"") | |
| 2478 | 2500 | if templ_7745c5c3_Err != nil { | |
| 2479 | 2501 | return templ_7745c5c3_Err | |
| 2480 | 2502 | } | |
| 2481 | 2503 | if p.Both { | |
| 2482 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 220, " data-diff=\"") | |
| 2504 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 223, " data-diff=\"") | |
| 2483 | 2505 | if templ_7745c5c3_Err != nil { | |
| 2484 | 2506 | return templ_7745c5c3_Err | |
| 2485 | 2507 | } | |
| 2486 | - | var templ_7745c5c3_Var128 string | |
| 2487 | - | templ_7745c5c3_Var128, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View) | |
| 2508 | + | var templ_7745c5c3_Var129 string | |
| 2509 | + | templ_7745c5c3_Var129, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View) | |
| 2488 | 2510 | if templ_7745c5c3_Err != nil { | |
| 2489 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 457, Col: 23} | |
| 2511 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 464, Col: 23} | |
| 2490 | 2512 | } | |
| 2491 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var128) | |
| 2513 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var129) | |
| 2492 | 2514 | if templ_7745c5c3_Err != nil { | |
| 2493 | 2515 | return templ_7745c5c3_Err | |
| 2494 | 2516 | } | |
| 2495 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 221, "\"") | |
| 2517 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 224, "\"") | |
| 2496 | 2518 | if templ_7745c5c3_Err != nil { | |
| 2497 | 2519 | return templ_7745c5c3_Err | |
| 2498 | 2520 | } | |
| 2499 | 2521 | } else { | |
| 2500 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 222, " data-view=\"") | |
| 2522 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 225, " data-view=\"") | |
| 2501 | 2523 | if templ_7745c5c3_Err != nil { | |
| 2502 | 2524 | return templ_7745c5c3_Err | |
| 2503 | 2525 | } | |
| 2504 | - | var templ_7745c5c3_Var129 string | |
| 2505 | - | templ_7745c5c3_Var129, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View) | |
| 2526 | + | var templ_7745c5c3_Var130 string | |
| 2527 | + | templ_7745c5c3_Var130, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View) | |
| 2506 | 2528 | if templ_7745c5c3_Err != nil { | |
| 2507 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 459, Col: 23} | |
| 2529 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 466, Col: 23} | |
| 2508 | 2530 | } | |
| 2509 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var129) | |
| 2531 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var130) | |
| 2510 | 2532 | if templ_7745c5c3_Err != nil { | |
| 2511 | 2533 | return templ_7745c5c3_Err | |
| 2512 | 2534 | } | |
| 2513 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 223, "\"") | |
| 2535 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 226, "\"") | |
| 2514 | 2536 | if templ_7745c5c3_Err != nil { | |
| 2515 | 2537 | return templ_7745c5c3_Err | |
| 2516 | 2538 | } | |
| 2517 | 2539 | } | |
| 2518 | 2540 | if p.Both && !p.Pinned { | |
| 2519 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 224, " hx-live=\"this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff\"") | |
| 2541 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 227, " hx-live=\"this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff\"") | |
| 2520 | 2542 | if templ_7745c5c3_Err != nil { | |
| 2521 | 2543 | return templ_7745c5c3_Err | |
| 2522 | 2544 | } | |
| 2523 | 2545 | } | |
| 2524 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 225, ">") | |
| 2546 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 228, ">") | |
| 2525 | 2547 | if templ_7745c5c3_Err != nil { | |
| 2526 | 2548 | return templ_7745c5c3_Err | |
| 2527 | 2549 | } | |
| @@ -2531,14 +2553,14 @@ func Compare(l Layout, p ComparePage) templ.Component { | |||
| 2531 | 2553 | return templ_7745c5c3_Err | |
| 2532 | 2554 | } | |
| 2533 | 2555 | } | |
| 2534 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 226, "</div>") | |
| 2556 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 229, "</div>") | |
| 2535 | 2557 | if templ_7745c5c3_Err != nil { | |
| 2536 | 2558 | return templ_7745c5c3_Err | |
| 2537 | 2559 | } | |
| 2538 | 2560 | } | |
| 2539 | 2561 | return nil | |
| 2540 | 2562 | }) | |
| 2541 | - | templ_7745c5c3_Err = Shell(l, compareRail(p)).Render(templ.WithChildren(ctx, templ_7745c5c3_Var110), templ_7745c5c3_Buffer) | |
| 2563 | + | templ_7745c5c3_Err = Shell(l, compareRail(p)).Render(templ.WithChildren(ctx, templ_7745c5c3_Var111), templ_7745c5c3_Buffer) | |
| 2542 | 2564 | if templ_7745c5c3_Err != nil { | |
| 2543 | 2565 | return templ_7745c5c3_Err | |
| 2544 | 2566 | } | |
| @@ -2562,173 +2584,173 @@ func compareRail(p ComparePage) templ.Component { | |||
| 2562 | 2584 | }() | |
| 2563 | 2585 | } | |
| 2564 | 2586 | ctx = templ.InitializeContext(ctx) | |
| 2565 | - | templ_7745c5c3_Var130 := templ.GetChildren(ctx) | |
| 2566 | - | if templ_7745c5c3_Var130 == nil { | |
| 2567 | - | templ_7745c5c3_Var130 = templ.NopComponent | |
| 2587 | + | templ_7745c5c3_Var131 := templ.GetChildren(ctx) | |
| 2588 | + | if templ_7745c5c3_Var131 == nil { | |
| 2589 | + | templ_7745c5c3_Var131 = templ.NopComponent | |
| 2568 | 2590 | } | |
| 2569 | 2591 | ctx = templ.ClearChildren(ctx) | |
| 2570 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 227, "<section aria-labelledby=\"r-ahead\"><h2 id=\"r-ahead\">Commits <span class=\"count\">") | |
| 2592 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 230, "<section aria-labelledby=\"r-ahead\"><h2 id=\"r-ahead\">Commits <span class=\"count\">") | |
| 2571 | 2593 | if templ_7745c5c3_Err != nil { | |
| 2572 | 2594 | return templ_7745c5c3_Err | |
| 2573 | 2595 | } | |
| 2574 | - | var templ_7745c5c3_Var131 string | |
| 2575 | - | templ_7745c5c3_Var131, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Ahead))) | |
| 2596 | + | var templ_7745c5c3_Var132 string | |
| 2597 | + | templ_7745c5c3_Var132, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Ahead))) | |
| 2576 | 2598 | if templ_7745c5c3_Err != nil { | |
| 2577 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 475, Col: 67} | |
| 2599 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 482, Col: 67} | |
| 2578 | 2600 | } | |
| 2579 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var131)) | |
| 2601 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var132)) | |
| 2580 | 2602 | if templ_7745c5c3_Err != nil { | |
| 2581 | 2603 | return templ_7745c5c3_Err | |
| 2582 | 2604 | } | |
| 2583 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 228, "</span></h2><ul class=\"pcommits\">") | |
| 2605 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 231, "</span></h2><ul class=\"pcommits\">") | |
| 2584 | 2606 | if templ_7745c5c3_Err != nil { | |
| 2585 | 2607 | return templ_7745c5c3_Err | |
| 2586 | 2608 | } | |
| 2587 | 2609 | for _, c := range p.Ahead { | |
| 2588 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 229, "<li><a href=\"") | |
| 2610 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 232, "<li><a href=\"") | |
| 2589 | 2611 | if templ_7745c5c3_Err != nil { | |
| 2590 | 2612 | return templ_7745c5c3_Err | |
| 2591 | 2613 | } | |
| 2592 | - | var templ_7745c5c3_Var132 templ.SafeURL | |
| 2593 | - | templ_7745c5c3_Var132, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref)) | |
| 2614 | + | var templ_7745c5c3_Var133 templ.SafeURL | |
| 2615 | + | templ_7745c5c3_Var133, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref)) | |
| 2594 | 2616 | if templ_7745c5c3_Err != nil { | |
| 2595 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 479, Col: 37} | |
| 2617 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 486, Col: 37} | |
| 2596 | 2618 | } | |
| 2597 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var132)) | |
| 2619 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var133)) | |
| 2598 | 2620 | if templ_7745c5c3_Err != nil { | |
| 2599 | 2621 | return templ_7745c5c3_Err | |
| 2600 | 2622 | } | |
| 2601 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 230, "\">") | |
| 2623 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 233, "\">") | |
| 2602 | 2624 | if templ_7745c5c3_Err != nil { | |
| 2603 | 2625 | return templ_7745c5c3_Err | |
| 2604 | 2626 | } | |
| 2605 | - | var templ_7745c5c3_Var133 string | |
| 2606 | - | templ_7745c5c3_Var133, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short) | |
| 2627 | + | var templ_7745c5c3_Var134 string | |
| 2628 | + | templ_7745c5c3_Var134, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short) | |
| 2607 | 2629 | if templ_7745c5c3_Err != nil { | |
| 2608 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 479, Col: 49} | |
| 2630 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 486, Col: 49} | |
| 2609 | 2631 | } | |
| 2610 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var133)) | |
| 2632 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var134)) | |
| 2611 | 2633 | if templ_7745c5c3_Err != nil { | |
| 2612 | 2634 | return templ_7745c5c3_Err | |
| 2613 | 2635 | } | |
| 2614 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 231, "</a> <span class=\"s\">") | |
| 2636 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 234, "</a> <span class=\"s\">") | |
| 2615 | 2637 | if templ_7745c5c3_Err != nil { | |
| 2616 | 2638 | return templ_7745c5c3_Err | |
| 2617 | 2639 | } | |
| 2618 | - | var templ_7745c5c3_Var134 string | |
| 2619 | - | templ_7745c5c3_Var134, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject) | |
| 2640 | + | var templ_7745c5c3_Var135 string | |
| 2641 | + | templ_7745c5c3_Var135, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject) | |
| 2620 | 2642 | if templ_7745c5c3_Err != nil { | |
| 2621 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 480, Col: 32} | |
| 2643 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 487, Col: 32} | |
| 2622 | 2644 | } | |
| 2623 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var134)) | |
| 2645 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var135)) | |
| 2624 | 2646 | if templ_7745c5c3_Err != nil { | |
| 2625 | 2647 | return templ_7745c5c3_Err | |
| 2626 | 2648 | } | |
| 2627 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 232, "</span> <span class=\"mute\">") | |
| 2649 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 235, "</span> <span class=\"mute\">") | |
| 2628 | 2650 | if templ_7745c5c3_Err != nil { | |
| 2629 | 2651 | return templ_7745c5c3_Err | |
| 2630 | 2652 | } | |
| 2631 | - | var templ_7745c5c3_Var135 string | |
| 2632 | - | templ_7745c5c3_Var135, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author) | |
| 2653 | + | var templ_7745c5c3_Var136 string | |
| 2654 | + | templ_7745c5c3_Var136, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author) | |
| 2633 | 2655 | if templ_7745c5c3_Err != nil { | |
| 2634 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 482, Col: 16} | |
| 2656 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 489, Col: 16} | |
| 2635 | 2657 | } | |
| 2636 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var135)) | |
| 2658 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var136)) | |
| 2637 | 2659 | if templ_7745c5c3_Err != nil { | |
| 2638 | 2660 | return templ_7745c5c3_Err | |
| 2639 | 2661 | } | |
| 2640 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 233, " ") | |
| 2662 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 236, " ") | |
| 2641 | 2663 | if templ_7745c5c3_Err != nil { | |
| 2642 | 2664 | return templ_7745c5c3_Err | |
| 2643 | 2665 | } | |
| 2644 | 2666 | if c.WhenAt != "" { | |
| 2645 | - | var templ_7745c5c3_Var136 string | |
| 2646 | - | templ_7745c5c3_Var136, templ_7745c5c3_Err = templ.JoinStringErrs(" · ") | |
| 2667 | + | var templ_7745c5c3_Var137 string | |
| 2668 | + | templ_7745c5c3_Var137, templ_7745c5c3_Err = templ.JoinStringErrs(" · ") | |
| 2647 | 2669 | if templ_7745c5c3_Err != nil { | |
| 2648 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 484, Col: 15} | |
| 2670 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 491, Col: 15} | |
| 2649 | 2671 | } | |
| 2650 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var136)) | |
| 2672 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var137)) | |
| 2651 | 2673 | if templ_7745c5c3_Err != nil { | |
| 2652 | 2674 | return templ_7745c5c3_Err | |
| 2653 | 2675 | } | |
| 2654 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 234, "<time datetime=\"") | |
| 2676 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 237, "<time datetime=\"") | |
| 2655 | 2677 | if templ_7745c5c3_Err != nil { | |
| 2656 | 2678 | return templ_7745c5c3_Err | |
| 2657 | 2679 | } | |
| 2658 | - | var templ_7745c5c3_Var137 string | |
| 2659 | - | templ_7745c5c3_Var137, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | |
| 2680 | + | var templ_7745c5c3_Var138 string | |
| 2681 | + | templ_7745c5c3_Var138, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | |
| 2660 | 2682 | if templ_7745c5c3_Err != nil { | |
| 2661 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 484, Col: 42} | |
| 2683 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 491, Col: 42} | |
| 2662 | 2684 | } | |
| 2663 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var137) | |
| 2685 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var138) | |
| 2664 | 2686 | if templ_7745c5c3_Err != nil { | |
| 2665 | 2687 | return templ_7745c5c3_Err | |
| 2666 | 2688 | } | |
| 2667 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 235, "\">") | |
| 2689 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 238, "\">") | |
| 2668 | 2690 | if templ_7745c5c3_Err != nil { | |
| 2669 | 2691 | return templ_7745c5c3_Err | |
| 2670 | 2692 | } | |
| 2671 | - | var templ_7745c5c3_Var138 string | |
| 2672 | - | templ_7745c5c3_Var138, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) | |
| 2693 | + | var templ_7745c5c3_Var139 string | |
| 2694 | + | templ_7745c5c3_Var139, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) | |
| 2673 | 2695 | if templ_7745c5c3_Err != nil { | |
| 2674 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 484, Col: 53} | |
| 2696 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 491, Col: 53} | |
| 2675 | 2697 | } | |
| 2676 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var138)) | |
| 2698 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var139)) | |
| 2677 | 2699 | if templ_7745c5c3_Err != nil { | |
| 2678 | 2700 | return templ_7745c5c3_Err | |
| 2679 | 2701 | } | |
| 2680 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 236, "</time>") | |
| 2702 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 239, "</time>") | |
| 2681 | 2703 | if templ_7745c5c3_Err != nil { | |
| 2682 | 2704 | return templ_7745c5c3_Err | |
| 2683 | 2705 | } | |
| 2684 | 2706 | } | |
| 2685 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 237, "</span></li>") | |
| 2707 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 240, "</span></li>") | |
| 2686 | 2708 | if templ_7745c5c3_Err != nil { | |
| 2687 | 2709 | return templ_7745c5c3_Err | |
| 2688 | 2710 | } | |
| 2689 | 2711 | } | |
| 2690 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 238, "</ul>") | |
| 2712 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 241, "</ul>") | |
| 2691 | 2713 | if templ_7745c5c3_Err != nil { | |
| 2692 | 2714 | return templ_7745c5c3_Err | |
| 2693 | 2715 | } | |
| 2694 | 2716 | if len(p.Ahead) == 0 { | |
| 2695 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 239, "<p class=\"dim\">no commit ahead of ") | |
| 2717 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 242, "<p class=\"dim\">no commit ahead of ") | |
| 2696 | 2718 | if templ_7745c5c3_Err != nil { | |
| 2697 | 2719 | return templ_7745c5c3_Err | |
| 2698 | 2720 | } | |
| 2699 | - | var templ_7745c5c3_Var139 string | |
| 2700 | - | templ_7745c5c3_Var139, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 2721 | + | var templ_7745c5c3_Var140 string | |
| 2722 | + | templ_7745c5c3_Var140, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 2701 | 2723 | if templ_7745c5c3_Err != nil { | |
| 2702 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 491, Col: 47} | |
| 2724 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 498, Col: 47} | |
| 2703 | 2725 | } | |
| 2704 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var139)) | |
| 2726 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var140)) | |
| 2705 | 2727 | if templ_7745c5c3_Err != nil { | |
| 2706 | 2728 | return templ_7745c5c3_Err | |
| 2707 | 2729 | } | |
| 2708 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 240, ".</p>") | |
| 2730 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 243, ".</p>") | |
| 2709 | 2731 | if templ_7745c5c3_Err != nil { | |
| 2710 | 2732 | return templ_7745c5c3_Err | |
| 2711 | 2733 | } | |
| 2712 | 2734 | } | |
| 2713 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 241, "</section>") | |
| 2735 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 244, "</section>") | |
| 2714 | 2736 | if templ_7745c5c3_Err != nil { | |
| 2715 | 2737 | return templ_7745c5c3_Err | |
| 2716 | 2738 | } | |
| 2717 | 2739 | if p.HasDiff { | |
| 2718 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 242, "<section aria-labelledby=\"r-files\"><h2 id=\"r-files\">Files <span class=\"count\">") | |
| 2740 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 245, "<section aria-labelledby=\"r-files\"><h2 id=\"r-files\">Files <span class=\"count\">") | |
| 2719 | 2741 | if templ_7745c5c3_Err != nil { | |
| 2720 | 2742 | return templ_7745c5c3_Err | |
| 2721 | 2743 | } | |
| 2722 | - | var templ_7745c5c3_Var140 string | |
| 2723 | - | templ_7745c5c3_Var140, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Files))) | |
| 2744 | + | var templ_7745c5c3_Var141 string | |
| 2745 | + | templ_7745c5c3_Var141, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Files))) | |
| 2724 | 2746 | if templ_7745c5c3_Err != nil { | |
| 2725 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 496, Col: 66} | |
| 2747 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 503, Col: 66} | |
| 2726 | 2748 | } | |
| 2727 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var140)) | |
| 2749 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var141)) | |
| 2728 | 2750 | if templ_7745c5c3_Err != nil { | |
| 2729 | 2751 | return templ_7745c5c3_Err | |
| 2730 | 2752 | } | |
| 2731 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 243, "</span></h2>") | |
| 2753 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 246, "</span></h2>") | |
| 2732 | 2754 | if templ_7745c5c3_Err != nil { | |
| 2733 | 2755 | return templ_7745c5c3_Err | |
| 2734 | 2756 | } | |
| @@ -2736,12 +2758,12 @@ func compareRail(p ComparePage) templ.Component { | |||
| 2736 | 2758 | if templ_7745c5c3_Err != nil { | |
| 2737 | 2759 | return templ_7745c5c3_Err | |
| 2738 | 2760 | } | |
| 2739 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 244, "</section>") | |
| 2761 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 247, "</section>") | |
| 2740 | 2762 | if templ_7745c5c3_Err != nil { | |
| 2741 | 2763 | return templ_7745c5c3_Err | |
| 2742 | 2764 | } | |
| 2743 | 2765 | } | |
| 2744 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 245, "<section aria-labelledby=\"r-cli\"><h2 id=\"r-cli\">From a terminal</h2>") | |
| 2766 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 248, "<section aria-labelledby=\"r-cli\"><h2 id=\"r-cli\">From a terminal</h2>") | |
| 2745 | 2767 | if templ_7745c5c3_Err != nil { | |
| 2746 | 2768 | return templ_7745c5c3_Err | |
| 2747 | 2769 | } | |
| @@ -2749,7 +2771,7 @@ func compareRail(p ComparePage) templ.Component { | |||
| 2749 | 2771 | if templ_7745c5c3_Err != nil { | |
| 2750 | 2772 | return templ_7745c5c3_Err | |
| 2751 | 2773 | } | |
| 2752 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 246, "</section>") | |
| 2774 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 249, "</section>") | |
| 2753 | 2775 | if templ_7745c5c3_Err != nil { | |
| 2754 | 2776 | return templ_7745c5c3_Err | |
| 2755 | 2777 | } | |
| @@ -1995,152 +1995,170 @@ func mergeBox(p MRPage) templ.Component { | |||||
| 1995 | if templ_7745c5c3_Err != nil { | 1995 | if templ_7745c5c3_Err != nil { | ||
| 1996 | return templ_7745c5c3_Err | 1996 | return templ_7745c5c3_Err | ||
| 1997 | } | 1997 | } | ||
| 1998 | - | if !p.Box.HeadGone { | 1998 | + | if p.Box.ReadyAction != "" { |
| 1999 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 174, "<form class=\"d-acts\" method=\"post\" action=\"") | 1999 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 174, "<p class=\"d-draft\">this is a draft: it cannot be merged until it is ready for review.</p><form class=\"d-btns\" method=\"post\" action=\"") |
| 2000 | if templ_7745c5c3_Err != nil { | 2000 | if templ_7745c5c3_Err != nil { | ||
| 2001 | return templ_7745c5c3_Err | 2001 | return templ_7745c5c3_Err | ||
| 2002 | } | 2002 | } | ||
| 2003 | var templ_7745c5c3_Var101 templ.SafeURL | 2003 | var templ_7745c5c3_Var101 templ.SafeURL | ||
| 2004 | - | templ_7745c5c3_Var101, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.MergeAction)) | 2004 | + | templ_7745c5c3_Var101, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.ReadyAction)) |
| 2005 | if templ_7745c5c3_Err != nil { | 2005 | if templ_7745c5c3_Err != nil { | ||
| 2006 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 372, Col: 79} | 2006 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 373, Col: 79} |
| 2007 | } | 2007 | } | ||
| 2008 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var101)) | 2008 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var101)) | ||
| 2009 | if templ_7745c5c3_Err != nil { | 2009 | if templ_7745c5c3_Err != nil { | ||
| 2010 | return templ_7745c5c3_Err | 2010 | return templ_7745c5c3_Err | ||
| 2011 | } | 2011 | } | ||
| 2012 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 175, "\"><input type=\"hidden\" name=\"head_id\" value=\"") | 2012 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 175, "\"><button type=\"submit\" class=\"btn primary\" title=\"remove the draft prefix from the title\">Ready for review</button></form>") |
| 2013 | if templ_7745c5c3_Err != nil { | 2013 | if templ_7745c5c3_Err != nil { | ||
| 2014 | return templ_7745c5c3_Err | 2014 | return templ_7745c5c3_Err | ||
| 2015 | } | 2015 | } | ||
| 2016 | - | var templ_7745c5c3_Var102 string | 2016 | + | } else if !p.Box.HeadGone { |
| 2017 | - | templ_7745c5c3_Var102, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Box.Head) | 2017 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 176, "<form class=\"d-acts\" method=\"post\" action=\"") |
| 2018 | if templ_7745c5c3_Err != nil { | 2018 | if templ_7745c5c3_Err != nil { | ||
| 2019 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 373, Col: 58} | 2019 | + | return templ_7745c5c3_Err |
| 2020 | + | } | |||
| 2021 | + | var templ_7745c5c3_Var102 templ.SafeURL | |||
| 2022 | + | templ_7745c5c3_Var102, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.MergeAction)) | |||
| 2023 | + | if templ_7745c5c3_Err != nil { | |||
| 2024 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 377, Col: 79} | |||
| 2025 | + | } | |||
| 2026 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var102)) | |||
| 2027 | + | if templ_7745c5c3_Err != nil { | |||
| 2028 | + | return templ_7745c5c3_Err | |||
| 2029 | + | } | |||
| 2030 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 177, "\"><input type=\"hidden\" name=\"head_id\" value=\"") | |||
| 2031 | + | if templ_7745c5c3_Err != nil { | |||
| 2032 | + | return templ_7745c5c3_Err | |||
| 2033 | + | } | |||
| 2034 | + | var templ_7745c5c3_Var103 string | |||
| 2035 | + | templ_7745c5c3_Var103, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Box.Head) | |||
| 2036 | + | if templ_7745c5c3_Err != nil { | |||
| 2037 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 378, Col: 58} | |||
| 2020 | } | 2038 | } | ||
| 2021 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var102) | 2039 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var103) |
| 2022 | if templ_7745c5c3_Err != nil { | 2040 | if templ_7745c5c3_Err != nil { | ||
| 2023 | return templ_7745c5c3_Err | 2041 | return templ_7745c5c3_Err | ||
| 2024 | } | 2042 | } | ||
| 2025 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 176, "\"> <label class=\"sr\" for=\"merge-mode\">merge mode</label> <select id=\"merge-mode\" name=\"mode\">") | 2043 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 178, "\"> <label class=\"sr\" for=\"merge-mode\">merge mode</label> <select id=\"merge-mode\" name=\"mode\">") |
| 2026 | if templ_7745c5c3_Err != nil { | 2044 | if templ_7745c5c3_Err != nil { | ||
| 2027 | return templ_7745c5c3_Err | 2045 | return templ_7745c5c3_Err | ||
| 2028 | } | 2046 | } | ||
| 2029 | for _, m := range p.Box.Modes { | 2047 | for _, m := range p.Box.Modes { | ||
| 2030 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 177, "<option value=\"") | 2048 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 179, "<option value=\"") |
| 2031 | if templ_7745c5c3_Err != nil { | 2049 | if templ_7745c5c3_Err != nil { | ||
| 2032 | return templ_7745c5c3_Err | 2050 | return templ_7745c5c3_Err | ||
| 2033 | } | 2051 | } | ||
| 2034 | - | var templ_7745c5c3_Var103 string | 2052 | + | var templ_7745c5c3_Var104 string |
| 2035 | - | templ_7745c5c3_Var103, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.Value) | 2053 | + | templ_7745c5c3_Var104, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.Value) |
| 2036 | if templ_7745c5c3_Err != nil { | 2054 | if templ_7745c5c3_Err != nil { | ||
| 2037 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 377, Col: 29} | 2055 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 382, Col: 29} |
| 2038 | } | 2056 | } | ||
| 2039 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var103) | 2057 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var104) |
| 2040 | if templ_7745c5c3_Err != nil { | 2058 | if templ_7745c5c3_Err != nil { | ||
| 2041 | return templ_7745c5c3_Err | 2059 | return templ_7745c5c3_Err | ||
| 2042 | } | 2060 | } | ||
| 2043 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 178, "\">") | 2061 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 180, "\">") |
| 2044 | if templ_7745c5c3_Err != nil { | 2062 | if templ_7745c5c3_Err != nil { | ||
| 2045 | return templ_7745c5c3_Err | 2063 | return templ_7745c5c3_Err | ||
| 2046 | } | 2064 | } | ||
| 2047 | - | var templ_7745c5c3_Var104 string | 2065 | + | var templ_7745c5c3_Var105 string |
| 2048 | - | templ_7745c5c3_Var104, templ_7745c5c3_Err = templ.JoinStringErrs(m.Label) | 2066 | + | templ_7745c5c3_Var105, templ_7745c5c3_Err = templ.JoinStringErrs(m.Label) |
| 2049 | if templ_7745c5c3_Err != nil { | 2067 | if templ_7745c5c3_Err != nil { | ||
| 2050 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 377, Col: 41} | 2068 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 382, Col: 41} |
| 2051 | } | 2069 | } | ||
| 2052 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var104)) | 2070 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var105)) |
| 2053 | if templ_7745c5c3_Err != nil { | 2071 | if templ_7745c5c3_Err != nil { | ||
| 2054 | return templ_7745c5c3_Err | 2072 | return templ_7745c5c3_Err | ||
| 2055 | } | 2073 | } | ||
| 2056 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 179, "</option>") | 2074 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 181, "</option>") |
| 2057 | if templ_7745c5c3_Err != nil { | 2075 | if templ_7745c5c3_Err != nil { | ||
| 2058 | return templ_7745c5c3_Err | 2076 | return templ_7745c5c3_Err | ||
| 2059 | } | 2077 | } | ||
| 2060 | } | 2078 | } | ||
| 2061 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 180, "</select> ") | 2079 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 182, "</select> ") |
| 2062 | if templ_7745c5c3_Err != nil { | 2080 | if templ_7745c5c3_Err != nil { | ||
| 2063 | return templ_7745c5c3_Err | 2081 | return templ_7745c5c3_Err | ||
| 2064 | } | 2082 | } | ||
| 2065 | if p.Box.CanDeleteBranch { | 2083 | if p.Box.CanDeleteBranch { | ||
| 2066 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 181, "<label class=\"d-check\"><input type=\"checkbox\" name=\"delete_branch\" value=\"1\"> delete ") | 2084 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 183, "<label class=\"d-check\"><input type=\"checkbox\" name=\"delete_branch\" value=\"1\"> delete ") |
| 2067 | if templ_7745c5c3_Err != nil { | 2085 | if templ_7745c5c3_Err != nil { | ||
| 2068 | return templ_7745c5c3_Err | 2086 | return templ_7745c5c3_Err | ||
| 2069 | } | 2087 | } | ||
| 2070 | - | var templ_7745c5c3_Var105 string | 2088 | + | var templ_7745c5c3_Var106 string |
| 2071 | - | templ_7745c5c3_Var105, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) | 2089 | + | templ_7745c5c3_Var106, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) |
| 2072 | if templ_7745c5c3_Err != nil { | 2090 | if templ_7745c5c3_Err != nil { | ||
| 2073 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 381, Col: 101} | 2091 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 386, Col: 101} |
| 2074 | } | 2092 | } | ||
| 2075 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var105)) | 2093 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var106)) |
| 2076 | if templ_7745c5c3_Err != nil { | 2094 | if templ_7745c5c3_Err != nil { | ||
| 2077 | return templ_7745c5c3_Err | 2095 | return templ_7745c5c3_Err | ||
| 2078 | } | 2096 | } | ||
| 2079 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 182, " after the merge</label>") | 2097 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 184, " after the merge</label>") |
| 2080 | if templ_7745c5c3_Err != nil { | 2098 | if templ_7745c5c3_Err != nil { | ||
| 2081 | return templ_7745c5c3_Err | 2099 | return templ_7745c5c3_Err | ||
| 2082 | } | 2100 | } | ||
| 2083 | } | 2101 | } | ||
| 2084 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 183, "<div class=\"d-btns\"><button type=\"submit\" class=\"btn primary\">Merge</button></div></form>") | 2102 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 185, "<div class=\"d-btns\"><button type=\"submit\" class=\"btn primary\">Merge</button></div></form>") |
| 2085 | if templ_7745c5c3_Err != nil { | 2103 | if templ_7745c5c3_Err != nil { | ||
| 2086 | return templ_7745c5c3_Err | 2104 | return templ_7745c5c3_Err | ||
| 2087 | } | 2105 | } | ||
| 2088 | } | 2106 | } | ||
| 2089 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 184, "<div class=\"d-btns\">") | 2107 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 186, "<div class=\"d-btns\">") |
| 2090 | if templ_7745c5c3_Err != nil { | 2108 | if templ_7745c5c3_Err != nil { | ||
| 2091 | return templ_7745c5c3_Err | 2109 | return templ_7745c5c3_Err | ||
| 2092 | } | 2110 | } | ||
| 2093 | if p.Box.CanMarkRead { | 2111 | if p.Box.CanMarkRead { | ||
| 2094 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 185, "<form method=\"post\" action=\"") | 2112 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 187, "<form method=\"post\" action=\"") |
| 2095 | if templ_7745c5c3_Err != nil { | 2113 | if templ_7745c5c3_Err != nil { | ||
| 2096 | return templ_7745c5c3_Err | 2114 | return templ_7745c5c3_Err | ||
| 2097 | } | 2115 | } | ||
| 2098 | - | var templ_7745c5c3_Var106 templ.SafeURL | 2116 | + | var templ_7745c5c3_Var107 templ.SafeURL |
| 2099 | - | templ_7745c5c3_Var106, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.ReadAction)) | 2117 | + | templ_7745c5c3_Var107, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.ReadAction)) |
| 2100 | if templ_7745c5c3_Err != nil { | 2118 | if templ_7745c5c3_Err != nil { | ||
| 2101 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 390, Col: 64} | 2119 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 395, Col: 64} |
| 2102 | } | 2120 | } | ||
| 2103 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var106)) | 2121 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var107)) |
| 2104 | if templ_7745c5c3_Err != nil { | 2122 | if templ_7745c5c3_Err != nil { | ||
| 2105 | return templ_7745c5c3_Err | 2123 | return templ_7745c5c3_Err | ||
| 2106 | } | 2124 | } | ||
| 2107 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 186, "\"><input type=\"hidden\" name=\"head_id\" value=\"") | 2125 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 188, "\"><input type=\"hidden\" name=\"head_id\" value=\"") |
| 2108 | if templ_7745c5c3_Err != nil { | 2126 | if templ_7745c5c3_Err != nil { | ||
| 2109 | return templ_7745c5c3_Err | 2127 | return templ_7745c5c3_Err | ||
| 2110 | } | 2128 | } | ||
| 2111 | - | var templ_7745c5c3_Var107 string | 2129 | + | var templ_7745c5c3_Var108 string |
| 2112 | - | templ_7745c5c3_Var107, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Box.Head) | 2130 | + | templ_7745c5c3_Var108, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Box.Head) |
| 2113 | if templ_7745c5c3_Err != nil { | 2131 | if templ_7745c5c3_Err != nil { | ||
| 2114 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 391, Col: 59} | 2132 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 396, Col: 59} |
| 2115 | } | 2133 | } | ||
| 2116 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var107) | 2134 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var108) |
| 2117 | if templ_7745c5c3_Err != nil { | 2135 | if templ_7745c5c3_Err != nil { | ||
| 2118 | return templ_7745c5c3_Err | 2136 | return templ_7745c5c3_Err | ||
| 2119 | } | 2137 | } | ||
| 2120 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 187, "\"> <button type=\"submit\" title=\"the next visit can show only what changed after this head\" class=\"btn\">Mark read</button></form>") | 2138 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 189, "\"> <button type=\"submit\" title=\"the next visit can show only what changed after this head\" class=\"btn\">Mark read</button></form>") |
| 2121 | if templ_7745c5c3_Err != nil { | 2139 | if templ_7745c5c3_Err != nil { | ||
| 2122 | return templ_7745c5c3_Err | 2140 | return templ_7745c5c3_Err | ||
| 2123 | } | 2141 | } | ||
| 2124 | } | 2142 | } | ||
| 2125 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 188, "<form method=\"post\" action=\"") | 2143 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 190, "<form method=\"post\" action=\"") |
| 2126 | if templ_7745c5c3_Err != nil { | 2144 | if templ_7745c5c3_Err != nil { | ||
| 2127 | return templ_7745c5c3_Err | 2145 | return templ_7745c5c3_Err | ||
| 2128 | } | 2146 | } | ||
| 2129 | - | var templ_7745c5c3_Var108 templ.SafeURL | 2147 | + | var templ_7745c5c3_Var109 templ.SafeURL |
| 2130 | - | templ_7745c5c3_Var108, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.CloseAction)) | 2148 | + | templ_7745c5c3_Var109, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.CloseAction)) |
| 2131 | if templ_7745c5c3_Err != nil { | 2149 | if templ_7745c5c3_Err != nil { | ||
| 2132 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 395, Col: 64} | 2150 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 400, Col: 64} |
| 2133 | } | 2151 | } | ||
| 2134 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var108)) | 2152 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var109)) |
| 2135 | if templ_7745c5c3_Err != nil { | 2153 | if templ_7745c5c3_Err != nil { | ||
| 2136 | return templ_7745c5c3_Err | 2154 | return templ_7745c5c3_Err | ||
| 2137 | } | 2155 | } | ||
| 2138 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 189, "\"><button type=\"submit\" title=\"close without merging; the branch is kept\" class=\"btn danger\">Close</button></form></div>") | 2156 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 191, "\"><button type=\"submit\" title=\"close without merging; the branch is kept\" class=\"btn danger\">Close</button></form></div>") |
| 2139 | if templ_7745c5c3_Err != nil { | 2157 | if templ_7745c5c3_Err != nil { | ||
| 2140 | return templ_7745c5c3_Err | 2158 | return templ_7745c5c3_Err | ||
| 2141 | } | 2159 | } | ||
| 2142 | - | if !p.Box.HeadGone { | 2160 | + | if !p.Box.HeadGone && p.Box.ReadyAction == "" { |
| 2143 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 190, "<p class=\"mute d-or\">or from a terminal</p>") | 2161 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 192, "<details class=\"d-or\"><summary class=\"mute\">or from a terminal</summary>") |
| 2144 | if templ_7745c5c3_Err != nil { | 2162 | if templ_7745c5c3_Err != nil { | ||
| 2145 | return templ_7745c5c3_Err | 2163 | return templ_7745c5c3_Err | ||
| 2146 | } | 2164 | } | ||
| @@ -2148,8 +2166,12 @@ func mergeBox(p MRPage) templ.Component { | |||||
| 2148 | if templ_7745c5c3_Err != nil { | 2166 | if templ_7745c5c3_Err != nil { | ||
| 2149 | return templ_7745c5c3_Err | 2167 | return templ_7745c5c3_Err | ||
| 2150 | } | 2168 | } | ||
| 2169 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 193, "</details>") | |||
| 2170 | + | if templ_7745c5c3_Err != nil { | |||
| 2171 | + | return templ_7745c5c3_Err | |||
| 2172 | + | } | |||
| 2151 | } | 2173 | } | ||
| 2152 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 191, "</section>") | 2174 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 194, "</section>") |
| 2153 | if templ_7745c5c3_Err != nil { | 2175 | if templ_7745c5c3_Err != nil { | ||
| 2154 | return templ_7745c5c3_Err | 2176 | return templ_7745c5c3_Err | ||
| 2155 | } | 2177 | } | ||
| @@ -2175,12 +2197,12 @@ func Compare(l Layout, p ComparePage) templ.Component { | |||||
| 2175 | }() | 2197 | }() | ||
| 2176 | } | 2198 | } | ||
| 2177 | ctx = templ.InitializeContext(ctx) | 2199 | ctx = templ.InitializeContext(ctx) | ||
| 2178 | - | templ_7745c5c3_Var109 := templ.GetChildren(ctx) | 2200 | + | templ_7745c5c3_Var110 := templ.GetChildren(ctx) |
| 2179 | - | if templ_7745c5c3_Var109 == nil { | 2201 | + | if templ_7745c5c3_Var110 == nil { |
| 2180 | - | templ_7745c5c3_Var109 = templ.NopComponent | 2202 | + | templ_7745c5c3_Var110 = templ.NopComponent |
| 2181 | } | 2203 | } | ||
| 2182 | ctx = templ.ClearChildren(ctx) | 2204 | ctx = templ.ClearChildren(ctx) | ||
| 2183 | - | templ_7745c5c3_Var110 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | 2205 | + | templ_7745c5c3_Var111 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { |
| 2184 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | 2206 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | ||
| 2185 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) | 2207 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) | ||
| 2186 | if !templ_7745c5c3_IsBuffer { | 2208 | if !templ_7745c5c3_IsBuffer { | ||
| @@ -2192,263 +2214,263 @@ func Compare(l Layout, p ComparePage) templ.Component { | |||||
| 2192 | }() | 2214 | }() | ||
| 2193 | } | 2215 | } | ||
| 2194 | ctx = templ.InitializeContext(ctx) | 2216 | ctx = templ.InitializeContext(ctx) | ||
| 2195 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 192, "<header class=\"show mr-h\"><h1>") | 2217 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 195, "<header class=\"show mr-h\"><h1>") |
| 2196 | - | if templ_7745c5c3_Err != nil { | |||
| 2197 | - | return templ_7745c5c3_Err | |||
| 2198 | - | } | |||
| 2199 | - | var templ_7745c5c3_Var111 string | |||
| 2200 | - | templ_7745c5c3_Var111, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) | |||
| 2201 | - | if templ_7745c5c3_Err != nil { | |||
| 2202 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 411, Col: 17} | |||
| 2203 | - | } | |||
| 2204 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var111)) | |||
| 2205 | - | if templ_7745c5c3_Err != nil { | |||
| 2206 | - | return templ_7745c5c3_Err | |||
| 2207 | - | } | |||
| 2208 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 193, " → ") | |||
| 2209 | if templ_7745c5c3_Err != nil { | 2218 | if templ_7745c5c3_Err != nil { | ||
| 2210 | return templ_7745c5c3_Err | 2219 | return templ_7745c5c3_Err | ||
| 2211 | } | 2220 | } | ||
| 2212 | var templ_7745c5c3_Var112 string | 2221 | var templ_7745c5c3_Var112 string | ||
| 2213 | - | templ_7745c5c3_Var112, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | 2222 | + | templ_7745c5c3_Var112, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) |
| 2214 | if templ_7745c5c3_Err != nil { | 2223 | if templ_7745c5c3_Err != nil { | ||
| 2215 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 411, Col: 34} | 2224 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 418, Col: 17} |
| 2216 | } | 2225 | } | ||
| 2217 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var112)) | 2226 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var112)) | ||
| 2218 | if templ_7745c5c3_Err != nil { | 2227 | if templ_7745c5c3_Err != nil { | ||
| 2219 | return templ_7745c5c3_Err | 2228 | return templ_7745c5c3_Err | ||
| 2220 | } | 2229 | } | ||
| 2221 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 194, "</h1><dl class=\"kv show-kv\"><dt>branch</dt><dd><a href=\"") | 2230 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 196, " → ") |
| 2222 | if templ_7745c5c3_Err != nil { | 2231 | if templ_7745c5c3_Err != nil { | ||
| 2223 | return templ_7745c5c3_Err | 2232 | return templ_7745c5c3_Err | ||
| 2224 | } | 2233 | } | ||
| 2225 | - | var templ_7745c5c3_Var113 templ.SafeURL | 2234 | + | var templ_7745c5c3_Var113 string |
| 2226 | - | templ_7745c5c3_Var113, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.SourceHref)) | 2235 | + | templ_7745c5c3_Var113, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) |
| 2227 | if templ_7745c5c3_Err != nil { | 2236 | if templ_7745c5c3_Err != nil { | ||
| 2228 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 414, Col: 45} | 2237 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 418, Col: 34} |
| 2229 | } | 2238 | } | ||
| 2230 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var113)) | 2239 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var113)) | ||
| 2231 | if templ_7745c5c3_Err != nil { | 2240 | if templ_7745c5c3_Err != nil { | ||
| 2232 | return templ_7745c5c3_Err | 2241 | return templ_7745c5c3_Err | ||
| 2233 | } | 2242 | } | ||
| 2234 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 195, "\">") | 2243 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 197, "</h1><dl class=\"kv show-kv\"><dt>branch</dt><dd><a href=\"") |
| 2235 | if templ_7745c5c3_Err != nil { | 2244 | if templ_7745c5c3_Err != nil { | ||
| 2236 | return templ_7745c5c3_Err | 2245 | return templ_7745c5c3_Err | ||
| 2237 | } | 2246 | } | ||
| 2238 | - | var templ_7745c5c3_Var114 string | 2247 | + | var templ_7745c5c3_Var114 templ.SafeURL |
| 2239 | - | templ_7745c5c3_Var114, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) | 2248 | + | templ_7745c5c3_Var114, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.SourceHref)) |
| 2240 | if templ_7745c5c3_Err != nil { | 2249 | if templ_7745c5c3_Err != nil { | ||
| 2241 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 414, Col: 58} | 2250 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 421, Col: 45} |
| 2242 | } | 2251 | } | ||
| 2243 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var114)) | 2252 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var114)) | ||
| 2244 | if templ_7745c5c3_Err != nil { | 2253 | if templ_7745c5c3_Err != nil { | ||
| 2245 | return templ_7745c5c3_Err | 2254 | return templ_7745c5c3_Err | ||
| 2246 | } | 2255 | } | ||
| 2247 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 196, "</a></dd><dt>target</dt><dd><a href=\"") | 2256 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 198, "\">") |
| 2248 | if templ_7745c5c3_Err != nil { | 2257 | if templ_7745c5c3_Err != nil { | ||
| 2249 | return templ_7745c5c3_Err | 2258 | return templ_7745c5c3_Err | ||
| 2250 | } | 2259 | } | ||
| 2251 | - | var templ_7745c5c3_Var115 templ.SafeURL | 2260 | + | var templ_7745c5c3_Var115 string |
| 2252 | - | templ_7745c5c3_Var115, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.TargetHref)) | 2261 | + | templ_7745c5c3_Var115, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) |
| 2253 | if templ_7745c5c3_Err != nil { | 2262 | if templ_7745c5c3_Err != nil { | ||
| 2254 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 416, Col: 45} | 2263 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 421, Col: 58} |
| 2255 | } | 2264 | } | ||
| 2256 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var115)) | 2265 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var115)) | ||
| 2257 | if templ_7745c5c3_Err != nil { | 2266 | if templ_7745c5c3_Err != nil { | ||
| 2258 | return templ_7745c5c3_Err | 2267 | return templ_7745c5c3_Err | ||
| 2259 | } | 2268 | } | ||
| 2260 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 197, "\">") | 2269 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 199, "</a></dd><dt>target</dt><dd><a href=\"") |
| 2261 | if templ_7745c5c3_Err != nil { | 2270 | if templ_7745c5c3_Err != nil { | ||
| 2262 | return templ_7745c5c3_Err | 2271 | return templ_7745c5c3_Err | ||
| 2263 | } | 2272 | } | ||
| 2264 | - | var templ_7745c5c3_Var116 string | 2273 | + | var templ_7745c5c3_Var116 templ.SafeURL |
| 2265 | - | templ_7745c5c3_Var116, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | 2274 | + | templ_7745c5c3_Var116, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.TargetHref)) |
| 2266 | if templ_7745c5c3_Err != nil { | 2275 | if templ_7745c5c3_Err != nil { | ||
| 2267 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 416, Col: 58} | 2276 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 423, Col: 45} |
| 2268 | } | 2277 | } | ||
| 2269 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var116)) | 2278 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var116)) | ||
| 2270 | if templ_7745c5c3_Err != nil { | 2279 | if templ_7745c5c3_Err != nil { | ||
| 2271 | return templ_7745c5c3_Err | 2280 | return templ_7745c5c3_Err | ||
| 2272 | } | 2281 | } | ||
| 2273 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 198, "</a></dd><dt>ahead</dt><dd>") | 2282 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 200, "\">") |
| 2274 | if templ_7745c5c3_Err != nil { | 2283 | if templ_7745c5c3_Err != nil { | ||
| 2275 | return templ_7745c5c3_Err | 2284 | return templ_7745c5c3_Err | ||
| 2276 | } | 2285 | } | ||
| 2277 | var templ_7745c5c3_Var117 string | 2286 | var templ_7745c5c3_Var117 string | ||
| 2278 | - | templ_7745c5c3_Var117, templ_7745c5c3_Err = templ.JoinStringErrs(p.AheadText) | 2287 | + | templ_7745c5c3_Var117, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) |
| 2279 | if templ_7745c5c3_Err != nil { | 2288 | if templ_7745c5c3_Err != nil { | ||
| 2280 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 418, Col: 21} | 2289 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 423, Col: 58} |
| 2281 | } | 2290 | } | ||
| 2282 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var117)) | 2291 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var117)) | ||
| 2283 | if templ_7745c5c3_Err != nil { | 2292 | if templ_7745c5c3_Err != nil { | ||
| 2284 | return templ_7745c5c3_Err | 2293 | return templ_7745c5c3_Err | ||
| 2285 | } | 2294 | } | ||
| 2286 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 199, "</dd></dl></header>") | 2295 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 201, "</a></dd><dt>ahead</dt><dd>") |
| 2296 | + | if templ_7745c5c3_Err != nil { | |||
| 2297 | + | return templ_7745c5c3_Err | |||
| 2298 | + | } | |||
| 2299 | + | var templ_7745c5c3_Var118 string | |||
| 2300 | + | templ_7745c5c3_Var118, templ_7745c5c3_Err = templ.JoinStringErrs(p.AheadText) | |||
| 2301 | + | if templ_7745c5c3_Err != nil { | |||
| 2302 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 425, Col: 21} | |||
| 2303 | + | } | |||
| 2304 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var118)) | |||
| 2305 | + | if templ_7745c5c3_Err != nil { | |||
| 2306 | + | return templ_7745c5c3_Err | |||
| 2307 | + | } | |||
| 2308 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 202, "</dd></dl></header>") | |||
| 2287 | if templ_7745c5c3_Err != nil { | 2309 | if templ_7745c5c3_Err != nil { | ||
| 2288 | return templ_7745c5c3_Err | 2310 | return templ_7745c5c3_Err | ||
| 2289 | } | 2311 | } | ||
| 2290 | if p.Note != "" { | 2312 | if p.Note != "" { | ||
| 2291 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 200, "<p class=\"notice\">") | 2313 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 203, "<p class=\"notice\">") |
| 2292 | if templ_7745c5c3_Err != nil { | 2314 | if templ_7745c5c3_Err != nil { | ||
| 2293 | return templ_7745c5c3_Err | 2315 | return templ_7745c5c3_Err | ||
| 2294 | } | 2316 | } | ||
| 2295 | - | var templ_7745c5c3_Var118 string | 2317 | + | var templ_7745c5c3_Var119 string |
| 2296 | - | templ_7745c5c3_Var118, templ_7745c5c3_Err = templ.JoinStringErrs(p.Note) | 2318 | + | templ_7745c5c3_Var119, templ_7745c5c3_Err = templ.JoinStringErrs(p.Note) |
| 2297 | if templ_7745c5c3_Err != nil { | 2319 | if templ_7745c5c3_Err != nil { | ||
| 2298 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 422, Col: 29} | 2320 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 429, Col: 29} |
| 2299 | } | 2321 | } | ||
| 2300 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var118)) | 2322 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var119)) |
| 2301 | if templ_7745c5c3_Err != nil { | 2323 | if templ_7745c5c3_Err != nil { | ||
| 2302 | return templ_7745c5c3_Err | 2324 | return templ_7745c5c3_Err | ||
| 2303 | } | 2325 | } | ||
| 2304 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 201, "</p>") | 2326 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 204, "</p>") |
| 2305 | if templ_7745c5c3_Err != nil { | 2327 | if templ_7745c5c3_Err != nil { | ||
| 2306 | return templ_7745c5c3_Err | 2328 | return templ_7745c5c3_Err | ||
| 2307 | } | 2329 | } | ||
| 2308 | } | 2330 | } | ||
| 2309 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 202, " ") | 2331 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 205, " ") |
| 2310 | if templ_7745c5c3_Err != nil { | 2332 | if templ_7745c5c3_Err != nil { | ||
| 2311 | return templ_7745c5c3_Err | 2333 | return templ_7745c5c3_Err | ||
| 2312 | } | 2334 | } | ||
| 2313 | if p.Existing != nil { | 2335 | if p.Existing != nil { | ||
| 2314 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 203, "<p class=\"notice\">an open merge request already covers these branches: <a href=\"") | 2336 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 206, "<p class=\"notice\">an open merge request already covers these branches: <a href=\"") |
| 2315 | if templ_7745c5c3_Err != nil { | 2337 | if templ_7745c5c3_Err != nil { | ||
| 2316 | return templ_7745c5c3_Err | 2338 | return templ_7745c5c3_Err | ||
| 2317 | } | 2339 | } | ||
| 2318 | - | var templ_7745c5c3_Var119 templ.SafeURL | 2340 | + | var templ_7745c5c3_Var120 templ.SafeURL |
| 2319 | - | templ_7745c5c3_Var119, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Existing.Href)) | 2341 | + | templ_7745c5c3_Var120, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Existing.Href)) |
| 2320 | if templ_7745c5c3_Err != nil { | 2342 | if templ_7745c5c3_Err != nil { | ||
| 2321 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 425, Col: 114} | 2343 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 432, Col: 114} |
| 2322 | } | 2344 | } | ||
| 2323 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var119)) | 2345 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var120)) |
| 2324 | if templ_7745c5c3_Err != nil { | 2346 | if templ_7745c5c3_Err != nil { | ||
| 2325 | return templ_7745c5c3_Err | 2347 | return templ_7745c5c3_Err | ||
| 2326 | } | 2348 | } | ||
| 2327 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 204, "\">") | 2349 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 207, "\">") |
| 2328 | if templ_7745c5c3_Err != nil { | 2350 | if templ_7745c5c3_Err != nil { | ||
| 2329 | return templ_7745c5c3_Err | 2351 | return templ_7745c5c3_Err | ||
| 2330 | } | 2352 | } | ||
| 2331 | - | var templ_7745c5c3_Var120 string | 2353 | + | var templ_7745c5c3_Var121 string |
| 2332 | - | templ_7745c5c3_Var120, templ_7745c5c3_Err = templ.JoinStringErrs(p.Existing.Label) | 2354 | + | templ_7745c5c3_Var121, templ_7745c5c3_Err = templ.JoinStringErrs(p.Existing.Label) |
| 2333 | if templ_7745c5c3_Err != nil { | 2355 | if templ_7745c5c3_Err != nil { | ||
| 2334 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 425, Col: 135} | 2356 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 432, Col: 135} |
| 2335 | } | 2357 | } | ||
| 2336 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var120)) | 2358 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var121)) |
| 2337 | if templ_7745c5c3_Err != nil { | 2359 | if templ_7745c5c3_Err != nil { | ||
| 2338 | return templ_7745c5c3_Err | 2360 | return templ_7745c5c3_Err | ||
| 2339 | } | 2361 | } | ||
| 2340 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 205, "</a></p>") | 2362 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 208, "</a></p>") |
| 2341 | if templ_7745c5c3_Err != nil { | 2363 | if templ_7745c5c3_Err != nil { | ||
| 2342 | return templ_7745c5c3_Err | 2364 | return templ_7745c5c3_Err | ||
| 2343 | } | 2365 | } | ||
| 2344 | } | 2366 | } | ||
| 2345 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 206, " ") | 2367 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 209, " ") |
| 2346 | if templ_7745c5c3_Err != nil { | 2368 | if templ_7745c5c3_Err != nil { | ||
| 2347 | return templ_7745c5c3_Err | 2369 | return templ_7745c5c3_Err | ||
| 2348 | } | 2370 | } | ||
| 2349 | if p.Create != nil { | 2371 | if p.Create != nil { | ||
| 2350 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 207, "<form class=\"cform create\" method=\"post\" action=\"") | 2372 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 210, "<form class=\"cform create\" method=\"post\" action=\"") |
| 2351 | if templ_7745c5c3_Err != nil { | 2373 | if templ_7745c5c3_Err != nil { | ||
| 2352 | return templ_7745c5c3_Err | 2374 | return templ_7745c5c3_Err | ||
| 2353 | } | 2375 | } | ||
| 2354 | - | var templ_7745c5c3_Var121 templ.SafeURL | 2376 | + | var templ_7745c5c3_Var122 templ.SafeURL |
| 2355 | - | templ_7745c5c3_Var121, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Create.Action)) | 2377 | + | templ_7745c5c3_Var122, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Create.Action)) |
| 2356 | if templ_7745c5c3_Err != nil { | 2378 | if templ_7745c5c3_Err != nil { | ||
| 2357 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 428, Col: 83} | 2379 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 435, Col: 83} |
| 2358 | } | 2380 | } | ||
| 2359 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var121)) | 2381 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var122)) |
| 2360 | if templ_7745c5c3_Err != nil { | 2382 | if templ_7745c5c3_Err != nil { | ||
| 2361 | return templ_7745c5c3_Err | 2383 | return templ_7745c5c3_Err | ||
| 2362 | } | 2384 | } | ||
| 2363 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 208, "\"><input type=\"hidden\" name=\"source\" value=\"") | 2385 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 211, "\"><input type=\"hidden\" name=\"source\" value=\"") |
| 2364 | if templ_7745c5c3_Err != nil { | 2386 | if templ_7745c5c3_Err != nil { | ||
| 2365 | return templ_7745c5c3_Err | 2387 | return templ_7745c5c3_Err | ||
| 2366 | } | 2388 | } | ||
| 2367 | - | var templ_7745c5c3_Var122 string | 2389 | + | var templ_7745c5c3_Var123 string |
| 2368 | - | templ_7745c5c3_Var122, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Source) | 2390 | + | templ_7745c5c3_Var123, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Source) |
| 2369 | if templ_7745c5c3_Err != nil { | 2391 | if templ_7745c5c3_Err != nil { | ||
| 2370 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 429, Col: 62} | 2392 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 436, Col: 62} |
| 2371 | } | 2393 | } | ||
| 2372 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var122) | 2394 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var123) |
| 2373 | if templ_7745c5c3_Err != nil { | 2395 | if templ_7745c5c3_Err != nil { | ||
| 2374 | return templ_7745c5c3_Err | 2396 | return templ_7745c5c3_Err | ||
| 2375 | } | 2397 | } | ||
| 2376 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 209, "\"> <input type=\"hidden\" name=\"target\" value=\"") | 2398 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 212, "\"> <input type=\"hidden\" name=\"target\" value=\"") |
| 2377 | if templ_7745c5c3_Err != nil { | 2399 | if templ_7745c5c3_Err != nil { | ||
| 2378 | return templ_7745c5c3_Err | 2400 | return templ_7745c5c3_Err | ||
| 2379 | } | 2401 | } | ||
| 2380 | - | var templ_7745c5c3_Var123 string | 2402 | + | var templ_7745c5c3_Var124 string |
| 2381 | - | templ_7745c5c3_Var123, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Target) | 2403 | + | templ_7745c5c3_Var124, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Target) |
| 2382 | if templ_7745c5c3_Err != nil { | 2404 | if templ_7745c5c3_Err != nil { | ||
| 2383 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 430, Col: 62} | 2405 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 437, Col: 62} |
| 2384 | } | 2406 | } | ||
| 2385 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var123) | 2407 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var124) |
| 2386 | if templ_7745c5c3_Err != nil { | 2408 | if templ_7745c5c3_Err != nil { | ||
| 2387 | return templ_7745c5c3_Err | 2409 | return templ_7745c5c3_Err | ||
| 2388 | } | 2410 | } | ||
| 2389 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 210, "\"> <label for=\"mr-title\">title</label> <input id=\"mr-title\" name=\"title\" type=\"text\" maxlength=\"120\" required value=\"") | 2411 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 213, "\"> <label for=\"mr-title\">title</label> <input id=\"mr-title\" name=\"title\" type=\"text\" maxlength=\"120\" required value=\"") |
| 2390 | if templ_7745c5c3_Err != nil { | 2412 | if templ_7745c5c3_Err != nil { | ||
| 2391 | return templ_7745c5c3_Err | 2413 | return templ_7745c5c3_Err | ||
| 2392 | } | 2414 | } | ||
| 2393 | - | var templ_7745c5c3_Var124 string | 2415 | + | var templ_7745c5c3_Var125 string |
| 2394 | - | templ_7745c5c3_Var124, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Title) | 2416 | + | templ_7745c5c3_Var125, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Title) |
| 2395 | if templ_7745c5c3_Err != nil { | 2417 | if templ_7745c5c3_Err != nil { | ||
| 2396 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 432, Col: 97} | 2418 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 439, Col: 97} |
| 2397 | } | 2419 | } | ||
| 2398 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var124) | 2420 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var125) |
| 2399 | if templ_7745c5c3_Err != nil { | 2421 | if templ_7745c5c3_Err != nil { | ||
| 2400 | return templ_7745c5c3_Err | 2422 | return templ_7745c5c3_Err | ||
| 2401 | } | 2423 | } | ||
| 2402 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 211, "\" autocomplete=\"off\"> <label for=\"mr-desc\">description</label> <textarea id=\"mr-desc\" name=\"description\" rows=\"4\" maxlength=\"8000\" placeholder=\"plain text, optional\"></textarea><div class=\"f-btns\"><button type=\"submit\" class=\"btn primary\">Create merge request</button></div></form>") | 2424 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 214, "\" autocomplete=\"off\"> <label for=\"mr-desc\">description</label> <textarea id=\"mr-desc\" name=\"description\" rows=\"4\" maxlength=\"8000\" placeholder=\"plain text, optional\"></textarea><div class=\"f-btns\"><button type=\"submit\" class=\"btn primary\">Create merge request</button></div></form>") |
| 2403 | if templ_7745c5c3_Err != nil { | 2425 | if templ_7745c5c3_Err != nil { | ||
| 2404 | return templ_7745c5c3_Err | 2426 | return templ_7745c5c3_Err | ||
| 2405 | } | 2427 | } | ||
| 2406 | } | 2428 | } | ||
| 2407 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 212, " ") | 2429 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 215, " ") |
| 2408 | if templ_7745c5c3_Err != nil { | 2430 | if templ_7745c5c3_Err != nil { | ||
| 2409 | return templ_7745c5c3_Err | 2431 | return templ_7745c5c3_Err | ||
| 2410 | } | 2432 | } | ||
| 2411 | if p.HasDiff { | 2433 | if p.HasDiff { | ||
| 2412 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 213, "<div class=\"diff-tools\" role=\"toolbar\" aria-label=\"Diff view\"><span class=\"dim\">") | 2434 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 216, "<div class=\"diff-tools\" role=\"toolbar\" aria-label=\"Diff view\"><span class=\"dim\">") |
| 2413 | if templ_7745c5c3_Err != nil { | 2435 | if templ_7745c5c3_Err != nil { | ||
| 2414 | return templ_7745c5c3_Err | 2436 | return templ_7745c5c3_Err | ||
| 2415 | } | 2437 | } | ||
| 2416 | - | var templ_7745c5c3_Var125 string | 2438 | + | var templ_7745c5c3_Var126 string |
| 2417 | - | templ_7745c5c3_Var125, templ_7745c5c3_Err = templ.JoinStringErrs(plural(p.NFiles, "file", "files")) | 2439 | + | templ_7745c5c3_Var126, templ_7745c5c3_Err = templ.JoinStringErrs(plural(p.NFiles, "file", "files")) |
| 2418 | if templ_7745c5c3_Err != nil { | 2440 | if templ_7745c5c3_Err != nil { | ||
| 2419 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 442, Col: 57} | 2441 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 449, Col: 57} |
| 2420 | } | 2442 | } | ||
| 2421 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var125)) | 2443 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var126)) |
| 2422 | if templ_7745c5c3_Err != nil { | 2444 | if templ_7745c5c3_Err != nil { | ||
| 2423 | return templ_7745c5c3_Err | 2445 | return templ_7745c5c3_Err | ||
| 2424 | } | 2446 | } | ||
| 2425 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 214, " · <span class=\"plus\">+") | 2447 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 217, " · <span class=\"plus\">+") |
| 2426 | if templ_7745c5c3_Err != nil { | 2448 | if templ_7745c5c3_Err != nil { | ||
| 2427 | return templ_7745c5c3_Err | 2449 | return templ_7745c5c3_Err | ||
| 2428 | } | 2450 | } | ||
| 2429 | - | var templ_7745c5c3_Var126 string | 2451 | + | var templ_7745c5c3_Var127 string |
| 2430 | - | templ_7745c5c3_Var126, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Adds)) | 2452 | + | templ_7745c5c3_Var127, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Adds)) |
| 2431 | if templ_7745c5c3_Err != nil { | 2453 | if templ_7745c5c3_Err != nil { | ||
| 2432 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 442, Col: 97} | 2454 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 449, Col: 97} |
| 2433 | } | 2455 | } | ||
| 2434 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var126)) | 2456 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var127)) |
| 2435 | if templ_7745c5c3_Err != nil { | 2457 | if templ_7745c5c3_Err != nil { | ||
| 2436 | return templ_7745c5c3_Err | 2458 | return templ_7745c5c3_Err | ||
| 2437 | } | 2459 | } | ||
| 2438 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 215, "</span> <span class=\"minus\">-") | 2460 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 218, "</span> <span class=\"minus\">-") |
| 2439 | if templ_7745c5c3_Err != nil { | 2461 | if templ_7745c5c3_Err != nil { | ||
| 2440 | return templ_7745c5c3_Err | 2462 | return templ_7745c5c3_Err | ||
| 2441 | } | 2463 | } | ||
| 2442 | - | var templ_7745c5c3_Var127 string | 2464 | + | var templ_7745c5c3_Var128 string |
| 2443 | - | templ_7745c5c3_Var127, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Dels)) | 2465 | + | templ_7745c5c3_Var128, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Dels)) |
| 2444 | if templ_7745c5c3_Err != nil { | 2466 | if templ_7745c5c3_Err != nil { | ||
| 2445 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 442, Col: 142} | 2467 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 449, Col: 142} |
| 2446 | } | 2468 | } | ||
| 2447 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var127)) | 2469 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var128)) |
| 2448 | if templ_7745c5c3_Err != nil { | 2470 | if templ_7745c5c3_Err != nil { | ||
| 2449 | return templ_7745c5c3_Err | 2471 | return templ_7745c5c3_Err | ||
| 2450 | } | 2472 | } | ||
| 2451 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 216, "</span></span> <span class=\"grow\"></span>") | 2473 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 219, "</span></span> <span class=\"grow\"></span>") |
| 2452 | if templ_7745c5c3_Err != nil { | 2474 | if templ_7745c5c3_Err != nil { | ||
| 2453 | return templ_7745c5c3_Err | 2475 | return templ_7745c5c3_Err | ||
| 2454 | } | 2476 | } | ||
| @@ -2464,64 +2486,64 @@ func Compare(l Layout, p ComparePage) templ.Component { | |||||
| 2464 | if templ_7745c5c3_Err != nil { | 2486 | if templ_7745c5c3_Err != nil { | ||
| 2465 | return templ_7745c5c3_Err | 2487 | return templ_7745c5c3_Err | ||
| 2466 | } | 2488 | } | ||
| 2467 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 217, "</div>") | 2489 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 220, "</div>") |
| 2468 | if templ_7745c5c3_Err != nil { | 2490 | if templ_7745c5c3_Err != nil { | ||
| 2469 | return templ_7745c5c3_Err | 2491 | return templ_7745c5c3_Err | ||
| 2470 | } | 2492 | } | ||
| 2471 | if p.Invisible { | 2493 | if p.Invisible { | ||
| 2472 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 218, "<p class=\"notice\">this diff holds invisible characters (direction controls, zero-width characters). They are shown as <span class=\"inv\"><U+XXXX></span> markers, never applied.</p>") | 2494 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 221, "<p class=\"notice\">this diff holds invisible characters (direction controls, zero-width characters). They are shown as <span class=\"inv\"><U+XXXX></span> markers, never applied.</p>") |
| 2473 | if templ_7745c5c3_Err != nil { | 2495 | if templ_7745c5c3_Err != nil { | ||
| 2474 | return templ_7745c5c3_Err | 2496 | return templ_7745c5c3_Err | ||
| 2475 | } | 2497 | } | ||
| 2476 | } | 2498 | } | ||
| 2477 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 219, " <div id=\"diffs\"") | 2499 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 222, " <div id=\"diffs\"") |
| 2478 | if templ_7745c5c3_Err != nil { | 2500 | if templ_7745c5c3_Err != nil { | ||
| 2479 | return templ_7745c5c3_Err | 2501 | return templ_7745c5c3_Err | ||
| 2480 | } | 2502 | } | ||
| 2481 | if p.Both { | 2503 | if p.Both { | ||
| 2482 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 220, " data-diff=\"") | 2504 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 223, " data-diff=\"") |
| 2483 | if templ_7745c5c3_Err != nil { | 2505 | if templ_7745c5c3_Err != nil { | ||
| 2484 | return templ_7745c5c3_Err | 2506 | return templ_7745c5c3_Err | ||
| 2485 | } | 2507 | } | ||
| 2486 | - | var templ_7745c5c3_Var128 string | 2508 | + | var templ_7745c5c3_Var129 string |
| 2487 | - | templ_7745c5c3_Var128, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View) | 2509 | + | templ_7745c5c3_Var129, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View) |
| 2488 | if templ_7745c5c3_Err != nil { | 2510 | if templ_7745c5c3_Err != nil { | ||
| 2489 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 457, Col: 23} | 2511 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 464, Col: 23} |
| 2490 | } | 2512 | } | ||
| 2491 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var128) | 2513 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var129) |
| 2492 | if templ_7745c5c3_Err != nil { | 2514 | if templ_7745c5c3_Err != nil { | ||
| 2493 | return templ_7745c5c3_Err | 2515 | return templ_7745c5c3_Err | ||
| 2494 | } | 2516 | } | ||
| 2495 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 221, "\"") | 2517 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 224, "\"") |
| 2496 | if templ_7745c5c3_Err != nil { | 2518 | if templ_7745c5c3_Err != nil { | ||
| 2497 | return templ_7745c5c3_Err | 2519 | return templ_7745c5c3_Err | ||
| 2498 | } | 2520 | } | ||
| 2499 | } else { | 2521 | } else { | ||
| 2500 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 222, " data-view=\"") | 2522 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 225, " data-view=\"") |
| 2501 | if templ_7745c5c3_Err != nil { | 2523 | if templ_7745c5c3_Err != nil { | ||
| 2502 | return templ_7745c5c3_Err | 2524 | return templ_7745c5c3_Err | ||
| 2503 | } | 2525 | } | ||
| 2504 | - | var templ_7745c5c3_Var129 string | 2526 | + | var templ_7745c5c3_Var130 string |
| 2505 | - | templ_7745c5c3_Var129, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View) | 2527 | + | templ_7745c5c3_Var130, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View) |
| 2506 | if templ_7745c5c3_Err != nil { | 2528 | if templ_7745c5c3_Err != nil { | ||
| 2507 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 459, Col: 23} | 2529 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 466, Col: 23} |
| 2508 | } | 2530 | } | ||
| 2509 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var129) | 2531 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var130) |
| 2510 | if templ_7745c5c3_Err != nil { | 2532 | if templ_7745c5c3_Err != nil { | ||
| 2511 | return templ_7745c5c3_Err | 2533 | return templ_7745c5c3_Err | ||
| 2512 | } | 2534 | } | ||
| 2513 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 223, "\"") | 2535 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 226, "\"") |
| 2514 | if templ_7745c5c3_Err != nil { | 2536 | if templ_7745c5c3_Err != nil { | ||
| 2515 | return templ_7745c5c3_Err | 2537 | return templ_7745c5c3_Err | ||
| 2516 | } | 2538 | } | ||
| 2517 | } | 2539 | } | ||
| 2518 | if p.Both && !p.Pinned { | 2540 | if p.Both && !p.Pinned { | ||
| 2519 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 224, " hx-live=\"this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff\"") | 2541 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 227, " hx-live=\"this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff\"") |
| 2520 | if templ_7745c5c3_Err != nil { | 2542 | if templ_7745c5c3_Err != nil { | ||
| 2521 | return templ_7745c5c3_Err | 2543 | return templ_7745c5c3_Err | ||
| 2522 | } | 2544 | } | ||
| 2523 | } | 2545 | } | ||
| 2524 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 225, ">") | 2546 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 228, ">") |
| 2525 | if templ_7745c5c3_Err != nil { | 2547 | if templ_7745c5c3_Err != nil { | ||
| 2526 | return templ_7745c5c3_Err | 2548 | return templ_7745c5c3_Err | ||
| 2527 | } | 2549 | } | ||
| @@ -2531,14 +2553,14 @@ func Compare(l Layout, p ComparePage) templ.Component { | |||||
| 2531 | return templ_7745c5c3_Err | 2553 | return templ_7745c5c3_Err | ||
| 2532 | } | 2554 | } | ||
| 2533 | } | 2555 | } | ||
| 2534 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 226, "</div>") | 2556 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 229, "</div>") |
| 2535 | if templ_7745c5c3_Err != nil { | 2557 | if templ_7745c5c3_Err != nil { | ||
| 2536 | return templ_7745c5c3_Err | 2558 | return templ_7745c5c3_Err | ||
| 2537 | } | 2559 | } | ||
| 2538 | } | 2560 | } | ||
| 2539 | return nil | 2561 | return nil | ||
| 2540 | }) | 2562 | }) | ||
| 2541 | - | templ_7745c5c3_Err = Shell(l, compareRail(p)).Render(templ.WithChildren(ctx, templ_7745c5c3_Var110), templ_7745c5c3_Buffer) | 2563 | + | templ_7745c5c3_Err = Shell(l, compareRail(p)).Render(templ.WithChildren(ctx, templ_7745c5c3_Var111), templ_7745c5c3_Buffer) |
| 2542 | if templ_7745c5c3_Err != nil { | 2564 | if templ_7745c5c3_Err != nil { | ||
| 2543 | return templ_7745c5c3_Err | 2565 | return templ_7745c5c3_Err | ||
| 2544 | } | 2566 | } | ||
| @@ -2562,173 +2584,173 @@ func compareRail(p ComparePage) templ.Component { | |||||
| 2562 | }() | 2584 | }() | ||
| 2563 | } | 2585 | } | ||
| 2564 | ctx = templ.InitializeContext(ctx) | 2586 | ctx = templ.InitializeContext(ctx) | ||
| 2565 | - | templ_7745c5c3_Var130 := templ.GetChildren(ctx) | 2587 | + | templ_7745c5c3_Var131 := templ.GetChildren(ctx) |
| 2566 | - | if templ_7745c5c3_Var130 == nil { | 2588 | + | if templ_7745c5c3_Var131 == nil { |
| 2567 | - | templ_7745c5c3_Var130 = templ.NopComponent | 2589 | + | templ_7745c5c3_Var131 = templ.NopComponent |
| 2568 | } | 2590 | } | ||
| 2569 | ctx = templ.ClearChildren(ctx) | 2591 | ctx = templ.ClearChildren(ctx) | ||
| 2570 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 227, "<section aria-labelledby=\"r-ahead\"><h2 id=\"r-ahead\">Commits <span class=\"count\">") | 2592 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 230, "<section aria-labelledby=\"r-ahead\"><h2 id=\"r-ahead\">Commits <span class=\"count\">") |
| 2571 | if templ_7745c5c3_Err != nil { | 2593 | if templ_7745c5c3_Err != nil { | ||
| 2572 | return templ_7745c5c3_Err | 2594 | return templ_7745c5c3_Err | ||
| 2573 | } | 2595 | } | ||
| 2574 | - | var templ_7745c5c3_Var131 string | 2596 | + | var templ_7745c5c3_Var132 string |
| 2575 | - | templ_7745c5c3_Var131, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Ahead))) | 2597 | + | templ_7745c5c3_Var132, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Ahead))) |
| 2576 | if templ_7745c5c3_Err != nil { | 2598 | if templ_7745c5c3_Err != nil { | ||
| 2577 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 475, Col: 67} | 2599 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 482, Col: 67} |
| 2578 | } | 2600 | } | ||
| 2579 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var131)) | 2601 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var132)) |
| 2580 | if templ_7745c5c3_Err != nil { | 2602 | if templ_7745c5c3_Err != nil { | ||
| 2581 | return templ_7745c5c3_Err | 2603 | return templ_7745c5c3_Err | ||
| 2582 | } | 2604 | } | ||
| 2583 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 228, "</span></h2><ul class=\"pcommits\">") | 2605 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 231, "</span></h2><ul class=\"pcommits\">") |
| 2584 | if templ_7745c5c3_Err != nil { | 2606 | if templ_7745c5c3_Err != nil { | ||
| 2585 | return templ_7745c5c3_Err | 2607 | return templ_7745c5c3_Err | ||
| 2586 | } | 2608 | } | ||
| 2587 | for _, c := range p.Ahead { | 2609 | for _, c := range p.Ahead { | ||
| 2588 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 229, "<li><a href=\"") | 2610 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 232, "<li><a href=\"") |
| 2589 | if templ_7745c5c3_Err != nil { | 2611 | if templ_7745c5c3_Err != nil { | ||
| 2590 | return templ_7745c5c3_Err | 2612 | return templ_7745c5c3_Err | ||
| 2591 | } | 2613 | } | ||
| 2592 | - | var templ_7745c5c3_Var132 templ.SafeURL | 2614 | + | var templ_7745c5c3_Var133 templ.SafeURL |
| 2593 | - | templ_7745c5c3_Var132, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref)) | 2615 | + | templ_7745c5c3_Var133, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref)) |
| 2594 | if templ_7745c5c3_Err != nil { | 2616 | if templ_7745c5c3_Err != nil { | ||
| 2595 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 479, Col: 37} | 2617 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 486, Col: 37} |
| 2596 | } | 2618 | } | ||
| 2597 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var132)) | 2619 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var133)) |
| 2598 | if templ_7745c5c3_Err != nil { | 2620 | if templ_7745c5c3_Err != nil { | ||
| 2599 | return templ_7745c5c3_Err | 2621 | return templ_7745c5c3_Err | ||
| 2600 | } | 2622 | } | ||
| 2601 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 230, "\">") | 2623 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 233, "\">") |
| 2602 | if templ_7745c5c3_Err != nil { | 2624 | if templ_7745c5c3_Err != nil { | ||
| 2603 | return templ_7745c5c3_Err | 2625 | return templ_7745c5c3_Err | ||
| 2604 | } | 2626 | } | ||
| 2605 | - | var templ_7745c5c3_Var133 string | 2627 | + | var templ_7745c5c3_Var134 string |
| 2606 | - | templ_7745c5c3_Var133, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short) | 2628 | + | templ_7745c5c3_Var134, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short) |
| 2607 | if templ_7745c5c3_Err != nil { | 2629 | if templ_7745c5c3_Err != nil { | ||
| 2608 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 479, Col: 49} | 2630 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 486, Col: 49} |
| 2609 | } | 2631 | } | ||
| 2610 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var133)) | 2632 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var134)) |
| 2611 | if templ_7745c5c3_Err != nil { | 2633 | if templ_7745c5c3_Err != nil { | ||
| 2612 | return templ_7745c5c3_Err | 2634 | return templ_7745c5c3_Err | ||
| 2613 | } | 2635 | } | ||
| 2614 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 231, "</a> <span class=\"s\">") | 2636 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 234, "</a> <span class=\"s\">") |
| 2615 | if templ_7745c5c3_Err != nil { | 2637 | if templ_7745c5c3_Err != nil { | ||
| 2616 | return templ_7745c5c3_Err | 2638 | return templ_7745c5c3_Err | ||
| 2617 | } | 2639 | } | ||
| 2618 | - | var templ_7745c5c3_Var134 string | 2640 | + | var templ_7745c5c3_Var135 string |
| 2619 | - | templ_7745c5c3_Var134, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject) | 2641 | + | templ_7745c5c3_Var135, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject) |
| 2620 | if templ_7745c5c3_Err != nil { | 2642 | if templ_7745c5c3_Err != nil { | ||
| 2621 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 480, Col: 32} | 2643 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 487, Col: 32} |
| 2622 | } | 2644 | } | ||
| 2623 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var134)) | 2645 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var135)) |
| 2624 | if templ_7745c5c3_Err != nil { | 2646 | if templ_7745c5c3_Err != nil { | ||
| 2625 | return templ_7745c5c3_Err | 2647 | return templ_7745c5c3_Err | ||
| 2626 | } | 2648 | } | ||
| 2627 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 232, "</span> <span class=\"mute\">") | 2649 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 235, "</span> <span class=\"mute\">") |
| 2628 | if templ_7745c5c3_Err != nil { | 2650 | if templ_7745c5c3_Err != nil { | ||
| 2629 | return templ_7745c5c3_Err | 2651 | return templ_7745c5c3_Err | ||
| 2630 | } | 2652 | } | ||
| 2631 | - | var templ_7745c5c3_Var135 string | 2653 | + | var templ_7745c5c3_Var136 string |
| 2632 | - | templ_7745c5c3_Var135, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author) | 2654 | + | templ_7745c5c3_Var136, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author) |
| 2633 | if templ_7745c5c3_Err != nil { | 2655 | if templ_7745c5c3_Err != nil { | ||
| 2634 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 482, Col: 16} | 2656 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 489, Col: 16} |
| 2635 | } | 2657 | } | ||
| 2636 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var135)) | 2658 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var136)) |
| 2637 | if templ_7745c5c3_Err != nil { | 2659 | if templ_7745c5c3_Err != nil { | ||
| 2638 | return templ_7745c5c3_Err | 2660 | return templ_7745c5c3_Err | ||
| 2639 | } | 2661 | } | ||
| 2640 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 233, " ") | 2662 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 236, " ") |
| 2641 | if templ_7745c5c3_Err != nil { | 2663 | if templ_7745c5c3_Err != nil { | ||
| 2642 | return templ_7745c5c3_Err | 2664 | return templ_7745c5c3_Err | ||
| 2643 | } | 2665 | } | ||
| 2644 | if c.WhenAt != "" { | 2666 | if c.WhenAt != "" { | ||
| 2645 | - | var templ_7745c5c3_Var136 string | 2667 | + | var templ_7745c5c3_Var137 string |
| 2646 | - | templ_7745c5c3_Var136, templ_7745c5c3_Err = templ.JoinStringErrs(" · ") | 2668 | + | templ_7745c5c3_Var137, templ_7745c5c3_Err = templ.JoinStringErrs(" · ") |
| 2647 | if templ_7745c5c3_Err != nil { | 2669 | if templ_7745c5c3_Err != nil { | ||
| 2648 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 484, Col: 15} | 2670 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 491, Col: 15} |
| 2649 | } | 2671 | } | ||
| 2650 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var136)) | 2672 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var137)) |
| 2651 | if templ_7745c5c3_Err != nil { | 2673 | if templ_7745c5c3_Err != nil { | ||
| 2652 | return templ_7745c5c3_Err | 2674 | return templ_7745c5c3_Err | ||
| 2653 | } | 2675 | } | ||
| 2654 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 234, "<time datetime=\"") | 2676 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 237, "<time datetime=\"") |
| 2655 | if templ_7745c5c3_Err != nil { | 2677 | if templ_7745c5c3_Err != nil { | ||
| 2656 | return templ_7745c5c3_Err | 2678 | return templ_7745c5c3_Err | ||
| 2657 | } | 2679 | } | ||
| 2658 | - | var templ_7745c5c3_Var137 string | 2680 | + | var templ_7745c5c3_Var138 string |
| 2659 | - | templ_7745c5c3_Var137, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | 2681 | + | templ_7745c5c3_Var138, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) |
| 2660 | if templ_7745c5c3_Err != nil { | 2682 | if templ_7745c5c3_Err != nil { | ||
| 2661 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 484, Col: 42} | 2683 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 491, Col: 42} |
| 2662 | } | 2684 | } | ||
| 2663 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var137) | 2685 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var138) |
| 2664 | if templ_7745c5c3_Err != nil { | 2686 | if templ_7745c5c3_Err != nil { | ||
| 2665 | return templ_7745c5c3_Err | 2687 | return templ_7745c5c3_Err | ||
| 2666 | } | 2688 | } | ||
| 2667 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 235, "\">") | 2689 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 238, "\">") |
| 2668 | if templ_7745c5c3_Err != nil { | 2690 | if templ_7745c5c3_Err != nil { | ||
| 2669 | return templ_7745c5c3_Err | 2691 | return templ_7745c5c3_Err | ||
| 2670 | } | 2692 | } | ||
| 2671 | - | var templ_7745c5c3_Var138 string | 2693 | + | var templ_7745c5c3_Var139 string |
| 2672 | - | templ_7745c5c3_Var138, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) | 2694 | + | templ_7745c5c3_Var139, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) |
| 2673 | if templ_7745c5c3_Err != nil { | 2695 | if templ_7745c5c3_Err != nil { | ||
| 2674 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 484, Col: 53} | 2696 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 491, Col: 53} |
| 2675 | } | 2697 | } | ||
| 2676 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var138)) | 2698 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var139)) |
| 2677 | if templ_7745c5c3_Err != nil { | 2699 | if templ_7745c5c3_Err != nil { | ||
| 2678 | return templ_7745c5c3_Err | 2700 | return templ_7745c5c3_Err | ||
| 2679 | } | 2701 | } | ||
| 2680 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 236, "</time>") | 2702 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 239, "</time>") |
| 2681 | if templ_7745c5c3_Err != nil { | 2703 | if templ_7745c5c3_Err != nil { | ||
| 2682 | return templ_7745c5c3_Err | 2704 | return templ_7745c5c3_Err | ||
| 2683 | } | 2705 | } | ||
| 2684 | } | 2706 | } | ||
| 2685 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 237, "</span></li>") | 2707 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 240, "</span></li>") |
| 2686 | if templ_7745c5c3_Err != nil { | 2708 | if templ_7745c5c3_Err != nil { | ||
| 2687 | return templ_7745c5c3_Err | 2709 | return templ_7745c5c3_Err | ||
| 2688 | } | 2710 | } | ||
| 2689 | } | 2711 | } | ||
| 2690 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 238, "</ul>") | 2712 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 241, "</ul>") |
| 2691 | if templ_7745c5c3_Err != nil { | 2713 | if templ_7745c5c3_Err != nil { | ||
| 2692 | return templ_7745c5c3_Err | 2714 | return templ_7745c5c3_Err | ||
| 2693 | } | 2715 | } | ||
| 2694 | if len(p.Ahead) == 0 { | 2716 | if len(p.Ahead) == 0 { | ||
| 2695 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 239, "<p class=\"dim\">no commit ahead of ") | 2717 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 242, "<p class=\"dim\">no commit ahead of ") |
| 2696 | if templ_7745c5c3_Err != nil { | 2718 | if templ_7745c5c3_Err != nil { | ||
| 2697 | return templ_7745c5c3_Err | 2719 | return templ_7745c5c3_Err | ||
| 2698 | } | 2720 | } | ||
| 2699 | - | var templ_7745c5c3_Var139 string | 2721 | + | var templ_7745c5c3_Var140 string |
| 2700 | - | templ_7745c5c3_Var139, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | 2722 | + | templ_7745c5c3_Var140, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) |
| 2701 | if templ_7745c5c3_Err != nil { | 2723 | if templ_7745c5c3_Err != nil { | ||
| 2702 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 491, Col: 47} | 2724 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 498, Col: 47} |
| 2703 | } | 2725 | } | ||
| 2704 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var139)) | 2726 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var140)) |
| 2705 | if templ_7745c5c3_Err != nil { | 2727 | if templ_7745c5c3_Err != nil { | ||
| 2706 | return templ_7745c5c3_Err | 2728 | return templ_7745c5c3_Err | ||
| 2707 | } | 2729 | } | ||
| 2708 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 240, ".</p>") | 2730 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 243, ".</p>") |
| 2709 | if templ_7745c5c3_Err != nil { | 2731 | if templ_7745c5c3_Err != nil { | ||
| 2710 | return templ_7745c5c3_Err | 2732 | return templ_7745c5c3_Err | ||
| 2711 | } | 2733 | } | ||
| 2712 | } | 2734 | } | ||
| 2713 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 241, "</section>") | 2735 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 244, "</section>") |
| 2714 | if templ_7745c5c3_Err != nil { | 2736 | if templ_7745c5c3_Err != nil { | ||
| 2715 | return templ_7745c5c3_Err | 2737 | return templ_7745c5c3_Err | ||
| 2716 | } | 2738 | } | ||
| 2717 | if p.HasDiff { | 2739 | if p.HasDiff { | ||
| 2718 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 242, "<section aria-labelledby=\"r-files\"><h2 id=\"r-files\">Files <span class=\"count\">") | 2740 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 245, "<section aria-labelledby=\"r-files\"><h2 id=\"r-files\">Files <span class=\"count\">") |
| 2719 | if templ_7745c5c3_Err != nil { | 2741 | if templ_7745c5c3_Err != nil { | ||
| 2720 | return templ_7745c5c3_Err | 2742 | return templ_7745c5c3_Err | ||
| 2721 | } | 2743 | } | ||
| 2722 | - | var templ_7745c5c3_Var140 string | 2744 | + | var templ_7745c5c3_Var141 string |
| 2723 | - | templ_7745c5c3_Var140, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Files))) | 2745 | + | templ_7745c5c3_Var141, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Files))) |
| 2724 | if templ_7745c5c3_Err != nil { | 2746 | if templ_7745c5c3_Err != nil { | ||
| 2725 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 496, Col: 66} | 2747 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 503, Col: 66} |
| 2726 | } | 2748 | } | ||
| 2727 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var140)) | 2749 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var141)) |
| 2728 | if templ_7745c5c3_Err != nil { | 2750 | if templ_7745c5c3_Err != nil { | ||
| 2729 | return templ_7745c5c3_Err | 2751 | return templ_7745c5c3_Err | ||
| 2730 | } | 2752 | } | ||
| 2731 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 243, "</span></h2>") | 2753 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 246, "</span></h2>") |
| 2732 | if templ_7745c5c3_Err != nil { | 2754 | if templ_7745c5c3_Err != nil { | ||
| 2733 | return templ_7745c5c3_Err | 2755 | return templ_7745c5c3_Err | ||
| 2734 | } | 2756 | } | ||
| @@ -2736,12 +2758,12 @@ func compareRail(p ComparePage) templ.Component { | |||||
| 2736 | if templ_7745c5c3_Err != nil { | 2758 | if templ_7745c5c3_Err != nil { | ||
| 2737 | return templ_7745c5c3_Err | 2759 | return templ_7745c5c3_Err | ||
| 2738 | } | 2760 | } | ||
| 2739 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 244, "</section>") | 2761 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 247, "</section>") |
| 2740 | if templ_7745c5c3_Err != nil { | 2762 | if templ_7745c5c3_Err != nil { | ||
| 2741 | return templ_7745c5c3_Err | 2763 | return templ_7745c5c3_Err | ||
| 2742 | } | 2764 | } | ||
| 2743 | } | 2765 | } | ||
| 2744 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 245, "<section aria-labelledby=\"r-cli\"><h2 id=\"r-cli\">From a terminal</h2>") | 2766 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 248, "<section aria-labelledby=\"r-cli\"><h2 id=\"r-cli\">From a terminal</h2>") |
| 2745 | if templ_7745c5c3_Err != nil { | 2767 | if templ_7745c5c3_Err != nil { | ||
| 2746 | return templ_7745c5c3_Err | 2768 | return templ_7745c5c3_Err | ||
| 2747 | } | 2769 | } | ||
| @@ -2749,7 +2771,7 @@ func compareRail(p ComparePage) templ.Component { | |||||
| 2749 | if templ_7745c5c3_Err != nil { | 2771 | if templ_7745c5c3_Err != nil { | ||
| 2750 | return templ_7745c5c3_Err | 2772 | return templ_7745c5c3_Err | ||
| 2751 | } | 2773 | } | ||
| 2752 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 246, "</section>") | 2774 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 249, "</section>") |
| 2753 | if templ_7745c5c3_Err != nil { | 2775 | if templ_7745c5c3_Err != nil { | ||
| 2754 | return templ_7745c5c3_Err | 2776 | return templ_7745c5c3_Err | ||
| 2755 | } | 2777 | } | ||
M internal/adapters/web/repo.go +27 -7
| @@ -35,18 +35,26 @@ type repoData struct { | |||
| 35 | 35 | // the Git state of its summary. A missing or private repository is | |
| 36 | 36 | // domain.ErrNotFound. | |
| 37 | 37 | func (s *Server) repoView(ctx context.Context, name domain.RepoName) (ports.RepoView, error) { | |
| 38 | - | repo, err := s.cfg.Repos.Get(ctx, principal(ctx), name) | |
| 38 | + | repo, err := s.repoRecord(ctx, name) | |
| 39 | 39 | if err != nil { | |
| 40 | 40 | return ports.RepoView{}, err | |
| 41 | 41 | } | |
| 42 | - | view, err := s.cfg.Repos.View(ctx, repo) | |
| 42 | + | return s.cfg.Repos.View(ctx, repo) | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | // repoRecord reads the repository record only, without Git: its name, | |
| 46 | + | // visibility and settings (the defaults when it has none). | |
| 47 | + | // | |
| 48 | + | //hexcheck:ignore suspicious-business-logic-in-adapter reads the repository from the use case; filling in the default settings for display decides no rule | |
| 49 | + | func (s *Server) repoRecord(ctx context.Context, name domain.RepoName) (domain.Repo, error) { | |
| 50 | + | repo, err := s.cfg.Repos.Get(ctx, principal(ctx), name) | |
| 43 | 51 | if err != nil { | |
| 44 | - | return ports.RepoView{}, err | |
| 52 | + | return domain.Repo{}, err | |
| 45 | 53 | } | |
| 46 | - | if view.Settings.IsZero() { | |
| 47 | - | view.Settings = domain.DefaultRepoSettings() | |
| 54 | + | if repo.Settings.IsZero() { | |
| 55 | + | repo.Settings = domain.DefaultRepoSettings() | |
| 48 | 56 | } | |
| 49 | - | return view, nil | |
| 57 | + | return repo, nil | |
| 50 | 58 | } | |
| 51 | 59 | ||
| 52 | 60 | // loadRepo reads a repository and its refs. A missing, private or invalidly | |
| @@ -110,6 +118,16 @@ func (d *repoData) allRefs() []domain.Ref { | |||
| 110 | 118 | // name is the repository name. | |
| 111 | 119 | func (d *repoData) name() string { return d.repo.Name.String() } | |
| 112 | 120 | ||
| 121 | + | // openMRCount is the number of open merge requests shown on the tab, empty | |
| 122 | + | // when there are none or they cannot be counted (the tab still works). | |
| 123 | + | func (s *Server) openMRCount(ctx context.Context, name domain.RepoName) string { | |
| 124 | + | n, err := s.cfg.MergeRequests.CountOpen(ctx, principal(ctx), name) | |
| 125 | + | if err != nil || n == 0 { | |
| 126 | + | return "" | |
| 127 | + | } | |
| 128 | + | return itoa(n) | |
| 129 | + | } | |
| 130 | + | ||
| 113 | 131 | // layout is the Layout of the pages of a repository: the sections, the ref | |
| 114 | 132 | // switcher on the head ref and the ":" targets. The caller sets the title, | |
| 115 | 133 | // the crumbs, the status and the rail. | |
| @@ -133,7 +151,9 @@ func (s *Server) repoLayout(ctx context.Context, d *repoData, section string) La | |||
| 133 | 151 | mk("r", "Branches & tags", repoHref(d.name(), "refs"), "refs"), | |
| 134 | 152 | } | |
| 135 | 153 | if s.cfg.MergeRequests != nil { | |
| 136 | - | l.Sections = append(l.Sections, mk("m", "Merge requests", mrBase(d.name()), "merge-requests")) | |
| 154 | + | mrs := mk("m", "Merge requests", mrBase(d.name()), "merge-requests") | |
| 155 | + | mrs.Count = s.openMRCount(ctx, d.repo.Name) | |
| 156 | + | l.Sections = append(l.Sections, mrs) | |
| 137 | 157 | } | |
| 138 | 158 | l.Sections = s.withActions(l.Sections, d.name(), section) | |
| 139 | 159 | l.Crumbs = []Crumb{{Label: d.name(), Href: repoHref(d.name()), Current: section == "summary"}} | |
| @@ -35,18 +35,26 @@ type repoData struct { | |||||
| 35 | // the Git state of its summary. A missing or private repository is | 35 | // the Git state of its summary. A missing or private repository is | ||
| 36 | // domain.ErrNotFound. | 36 | // domain.ErrNotFound. | ||
| 37 | func (s *Server) repoView(ctx context.Context, name domain.RepoName) (ports.RepoView, error) { | 37 | func (s *Server) repoView(ctx context.Context, name domain.RepoName) (ports.RepoView, error) { | ||
| 38 | - | repo, err := s.cfg.Repos.Get(ctx, principal(ctx), name) | 38 | + | repo, err := s.repoRecord(ctx, name) |
| 39 | if err != nil { | 39 | if err != nil { | ||
| 40 | return ports.RepoView{}, err | 40 | return ports.RepoView{}, err | ||
| 41 | } | 41 | } | ||
| 42 | - | view, err := s.cfg.Repos.View(ctx, repo) | 42 | + | return s.cfg.Repos.View(ctx, repo) |
| 43 | + | } | |||
| 44 | + | ||||
| 45 | + | // repoRecord reads the repository record only, without Git: its name, | |||
| 46 | + | // visibility and settings (the defaults when it has none). | |||
| 47 | + | // | |||
| 48 | + | //hexcheck:ignore suspicious-business-logic-in-adapter reads the repository from the use case; filling in the default settings for display decides no rule | |||
| 49 | + | func (s *Server) repoRecord(ctx context.Context, name domain.RepoName) (domain.Repo, error) { | |||
| 50 | + | repo, err := s.cfg.Repos.Get(ctx, principal(ctx), name) | |||
| 43 | if err != nil { | 51 | if err != nil { | ||
| 44 | - | return ports.RepoView{}, err | 52 | + | return domain.Repo{}, err |
| 45 | } | 53 | } | ||
| 46 | - | if view.Settings.IsZero() { | 54 | + | if repo.Settings.IsZero() { |
| 47 | - | view.Settings = domain.DefaultRepoSettings() | 55 | + | repo.Settings = domain.DefaultRepoSettings() |
| 48 | } | 56 | } | ||
| 49 | - | return view, nil | 57 | + | return repo, nil |
| 50 | } | 58 | } | ||
| 51 | 59 | ||||
| 52 | // loadRepo reads a repository and its refs. A missing, private or invalidly | 60 | // loadRepo reads a repository and its refs. A missing, private or invalidly | ||
| @@ -110,6 +118,16 @@ func (d *repoData) allRefs() []domain.Ref { | |||||
| 110 | // name is the repository name. | 118 | // name is the repository name. | ||
| 111 | func (d *repoData) name() string { return d.repo.Name.String() } | 119 | func (d *repoData) name() string { return d.repo.Name.String() } | ||
| 112 | 120 | ||||
| 121 | + | // openMRCount is the number of open merge requests shown on the tab, empty | |||
| 122 | + | // when there are none or they cannot be counted (the tab still works). | |||
| 123 | + | func (s *Server) openMRCount(ctx context.Context, name domain.RepoName) string { | |||
| 124 | + | n, err := s.cfg.MergeRequests.CountOpen(ctx, principal(ctx), name) | |||
| 125 | + | if err != nil || n == 0 { | |||
| 126 | + | return "" | |||
| 127 | + | } | |||
| 128 | + | return itoa(n) | |||
| 129 | + | } | |||
| 130 | + | ||||
| 113 | // layout is the Layout of the pages of a repository: the sections, the ref | 131 | // layout is the Layout of the pages of a repository: the sections, the ref | ||
| 114 | // switcher on the head ref and the ":" targets. The caller sets the title, | 132 | // switcher on the head ref and the ":" targets. The caller sets the title, | ||
| 115 | // the crumbs, the status and the rail. | 133 | // the crumbs, the status and the rail. | ||
| @@ -133,7 +151,9 @@ func (s *Server) repoLayout(ctx context.Context, d *repoData, section string) La | |||||
| 133 | mk("r", "Branches & tags", repoHref(d.name(), "refs"), "refs"), | 151 | mk("r", "Branches & tags", repoHref(d.name(), "refs"), "refs"), | ||
| 134 | } | 152 | } | ||
| 135 | if s.cfg.MergeRequests != nil { | 153 | if s.cfg.MergeRequests != nil { | ||
| 136 | - | l.Sections = append(l.Sections, mk("m", "Merge requests", mrBase(d.name()), "merge-requests")) | 154 | + | mrs := mk("m", "Merge requests", mrBase(d.name()), "merge-requests") |
| 155 | + | mrs.Count = s.openMRCount(ctx, d.repo.Name) | |||
| 156 | + | l.Sections = append(l.Sections, mrs) | |||
| 137 | } | 157 | } | ||
| 138 | l.Sections = s.withActions(l.Sections, d.name(), section) | 158 | l.Sections = s.withActions(l.Sections, d.name(), section) | ||
| 139 | l.Crumbs = []Crumb{{Label: d.name(), Href: repoHref(d.name()), Current: section == "summary"}} | 159 | l.Crumbs = []Crumb{{Label: d.name(), Href: repoHref(d.name()), Current: section == "summary"}} | ||
M internal/adapters/web/shell.templ +7 -1
| @@ -150,7 +150,13 @@ templ Header(l Layout) { | |||
| 150 | 150 | if s.Current { | |
| 151 | 151 | aria-current="page" | |
| 152 | 152 | } | |
| 153 | - | >{ s.Label }<kbd aria-hidden="true">{ s.Key }</kbd></a> | |
| 153 | + | > | |
| 154 | + | { s.Label } | |
| 155 | + | if s.Count != "" { | |
| 156 | + | <span class="count">{ s.Count }<span class="sr">{ " open" }</span></span> | |
| 157 | + | } | |
| 158 | + | <kbd aria-hidden="true">{ s.Key }</kbd> | |
| 159 | + | </a> | |
| 154 | 160 | </li> | |
| 155 | 161 | } | |
| 156 | 162 | </ul> | |
| @@ -150,7 +150,13 @@ templ Header(l Layout) { | |||||
| 150 | if s.Current { | 150 | if s.Current { | ||
| 151 | aria-current="page" | 151 | aria-current="page" | ||
| 152 | } | 152 | } | ||
| 153 | - | >{ s.Label }<kbd aria-hidden="true">{ s.Key }</kbd></a> | 153 | + | > |
| 154 | + | { s.Label } | |||
| 155 | + | if s.Count != "" { | |||
| 156 | + | <span class="count">{ s.Count }<span class="sr">{ " open" }</span></span> | |||
| 157 | + | } | |||
| 158 | + | <kbd aria-hidden="true">{ s.Key }</kbd> | |||
| 159 | + | </a> | |||
| 154 | </li> | 160 | </li> | ||
| 155 | } | 161 | } | ||
| 156 | </ul> | 162 | </ul> | ||
M internal/adapters/web/shell_templ.go +64 -28 generated file: collapsed
M internal/adapters/web/static/css/60-mr.css +21 -0
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | .mr-state[data-state="open"]::before { content: "● "; color: var(--teal); } | |
| 6 | 6 | .mr-state[data-state="merged"]::before { content: "✓ "; color: var(--teal); } | |
| 7 | 7 | .mr-state[data-state="closed"]::before { content: "✗ "; color: var(--red); } | |
| 8 | + | .mr-state[data-state="draft"]::before { content: "◌ "; color: var(--ink-dim); } | |
| 8 | 9 | .mr-desc { max-width: var(--measure); white-space: pre-wrap; overflow-wrap: anywhere; padding: var(--s4) var(--s5); background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); } | |
| 9 | 10 | .mr-desc a, .c-text a { text-decoration: underline; } | |
| 10 | 11 | ||
| @@ -29,11 +30,31 @@ | |||
| 29 | 30 | & .d-check input { accent-color: var(--ink-hi); margin: 0; } | |
| 30 | 31 | & .d-btns { display: flex; gap: var(--s2); margin-top: var(--s3); flex-wrap: wrap; } | |
| 31 | 32 | & .d-btns .primary { flex: 1; justify-content: center; } | |
| 33 | + | /* the terminal lines fold away: a reviewer who merges in the page does | |
| 34 | + | not need them in sight */ | |
| 35 | + | & .d-draft { font-size: var(--fs-s); color: var(--ink-dim); } | |
| 32 | 36 | & .d-or { font-size: var(--fs-s); } | |
| 37 | + | & .d-or > summary { cursor: pointer; list-style: none; } | |
| 38 | + | & .d-or > summary::-webkit-details-marker { display: none; } | |
| 39 | + | & .d-or > summary::before { content: "▸ "; } | |
| 40 | + | & .d-or[open] > summary::before { content: "▾ "; } | |
| 41 | + | & .d-or > summary:hover { color: var(--ink-hi); } | |
| 42 | + | & .d-or[open] > .clone { margin-top: var(--s2); } | |
| 33 | 43 | & .clone { margin-top: 0; } | |
| 34 | 44 | & .clone code { white-space: pre-wrap; overflow-wrap: anywhere; } | |
| 35 | 45 | } | |
| 36 | 46 | ||
| 47 | + | /* the rail of a merge request follows the review: the merge block and the | |
| 48 | + | checks stay in reach at the end of a long diff. It scrolls on its own | |
| 49 | + | when it is taller than the window. Only on a two-column layout. */ | |
| 50 | + | @media (min-width: 1081px) { | |
| 51 | + | .work.has-rail:has(#mr-checks) .rail { | |
| 52 | + | position: sticky; top: calc(var(--hdr-h, 0px) + var(--s4)); | |
| 53 | + | max-height: calc(100dvh - var(--hdr-h, 0px) - var(--s4) * 2); | |
| 54 | + | overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; | |
| 55 | + | } | |
| 56 | + | } | |
| 57 | + | ||
| 37 | 58 | .pcommits { font-size: var(--fs-s); display: grid; gap: var(--s1); } | |
| 38 | 59 | .pcommits li { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 2px var(--s2); margin: 0 calc(-1 * var(--s2)); border-radius: 4px; } | |
| 39 | 60 | .pcommits li a { font-family: var(--mono); } | |
| @@ -5,6 +5,7 @@ | |||||
| 5 | .mr-state[data-state="open"]::before { content: "● "; color: var(--teal); } | 5 | .mr-state[data-state="open"]::before { content: "● "; color: var(--teal); } | ||
| 6 | .mr-state[data-state="merged"]::before { content: "✓ "; color: var(--teal); } | 6 | .mr-state[data-state="merged"]::before { content: "✓ "; color: var(--teal); } | ||
| 7 | .mr-state[data-state="closed"]::before { content: "✗ "; color: var(--red); } | 7 | .mr-state[data-state="closed"]::before { content: "✗ "; color: var(--red); } | ||
| 8 | + | .mr-state[data-state="draft"]::before { content: "◌ "; color: var(--ink-dim); } | |||
| 8 | .mr-desc { max-width: var(--measure); white-space: pre-wrap; overflow-wrap: anywhere; padding: var(--s4) var(--s5); background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); } | 9 | .mr-desc { max-width: var(--measure); white-space: pre-wrap; overflow-wrap: anywhere; padding: var(--s4) var(--s5); background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); } | ||
| 9 | .mr-desc a, .c-text a { text-decoration: underline; } | 10 | .mr-desc a, .c-text a { text-decoration: underline; } | ||
| 10 | 11 | ||||
| @@ -29,11 +30,31 @@ | |||||
| 29 | & .d-check input { accent-color: var(--ink-hi); margin: 0; } | 30 | & .d-check input { accent-color: var(--ink-hi); margin: 0; } | ||
| 30 | & .d-btns { display: flex; gap: var(--s2); margin-top: var(--s3); flex-wrap: wrap; } | 31 | & .d-btns { display: flex; gap: var(--s2); margin-top: var(--s3); flex-wrap: wrap; } | ||
| 31 | & .d-btns .primary { flex: 1; justify-content: center; } | 32 | & .d-btns .primary { flex: 1; justify-content: center; } | ||
| 33 | + | /* the terminal lines fold away: a reviewer who merges in the page does | |||
| 34 | + | not need them in sight */ | |||
| 35 | + | & .d-draft { font-size: var(--fs-s); color: var(--ink-dim); } | |||
| 32 | & .d-or { font-size: var(--fs-s); } | 36 | & .d-or { font-size: var(--fs-s); } | ||
| 37 | + | & .d-or > summary { cursor: pointer; list-style: none; } | |||
| 38 | + | & .d-or > summary::-webkit-details-marker { display: none; } | |||
| 39 | + | & .d-or > summary::before { content: "▸ "; } | |||
| 40 | + | & .d-or[open] > summary::before { content: "▾ "; } | |||
| 41 | + | & .d-or > summary:hover { color: var(--ink-hi); } | |||
| 42 | + | & .d-or[open] > .clone { margin-top: var(--s2); } | |||
| 33 | & .clone { margin-top: 0; } | 43 | & .clone { margin-top: 0; } | ||
| 34 | & .clone code { white-space: pre-wrap; overflow-wrap: anywhere; } | 44 | & .clone code { white-space: pre-wrap; overflow-wrap: anywhere; } | ||
| 35 | } | 45 | } | ||
| 36 | 46 | ||||
| 47 | + | /* the rail of a merge request follows the review: the merge block and the | |||
| 48 | + | checks stay in reach at the end of a long diff. It scrolls on its own | |||
| 49 | + | when it is taller than the window. Only on a two-column layout. */ | |||
| 50 | + | @media (min-width: 1081px) { | |||
| 51 | + | .work.has-rail:has(#mr-checks) .rail { | |||
| 52 | + | position: sticky; top: calc(var(--hdr-h, 0px) + var(--s4)); | |||
| 53 | + | max-height: calc(100dvh - var(--hdr-h, 0px) - var(--s4) * 2); | |||
| 54 | + | overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; | |||
| 55 | + | } | |||
| 56 | + | } | |||
| 57 | + | ||||
| 37 | .pcommits { font-size: var(--fs-s); display: grid; gap: var(--s1); } | 58 | .pcommits { font-size: var(--fs-s); display: grid; gap: var(--s1); } | ||
| 38 | .pcommits li { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 2px var(--s2); margin: 0 calc(-1 * var(--s2)); border-radius: 4px; } | 59 | .pcommits li { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 2px var(--s2); margin: 0 calc(-1 * var(--s2)); border-radius: 4px; } | ||
| 39 | .pcommits li a { font-family: var(--mono); } | 60 | .pcommits li a { font-family: var(--mono); } | ||
M internal/adapters/web/static/css/65-ci.css +6 -1
| @@ -50,7 +50,12 @@ | |||
| 50 | 50 | .jobs > li { padding: var(--s3) var(--s4); background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); } | |
| 51 | 51 | .job-h { display: flex; align-items: center; gap: var(--s2); font-weight: 700; } | |
| 52 | 52 | .steps { display: grid; gap: 2px; padding: 0; margin: var(--s2) 0 0; list-style: none; font-size: var(--fs-s); } | |
| 53 | - | .steps li { display: flex; align-items: center; gap: var(--s1); } | |
| 53 | + | /* a long step name (a whole shell command) wraps inside the rail; the icon | |
| 54 | + | stays on its first line and the duration at the right */ | |
| 55 | + | .steps li { display: flex; align-items: flex-start; gap: var(--s1); min-width: 0; } | |
| 56 | + | .steps li > :is(a, span:not(.dim)) { flex: 1; min-width: 0; overflow-wrap: anywhere; } | |
| 57 | + | .steps li > .dim { flex: none; margin-left: auto; } | |
| 58 | + | .steps li > :first-child { margin-top: 3px; flex: none; } | |
| 54 | 59 | .attempts { padding: 0; margin: var(--s2) 0 0; list-style: none; font-size: var(--fs-s); } | |
| 55 | 60 | .checks { display: grid; gap: var(--s1); padding: 0; margin: 0; list-style: none; font-size: var(--fs-s); } | |
| 56 | 61 | .checks li { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1); } | |
| @@ -50,7 +50,12 @@ | |||||
| 50 | .jobs > li { padding: var(--s3) var(--s4); background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); } | 50 | .jobs > li { padding: var(--s3) var(--s4); background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); } | ||
| 51 | .job-h { display: flex; align-items: center; gap: var(--s2); font-weight: 700; } | 51 | .job-h { display: flex; align-items: center; gap: var(--s2); font-weight: 700; } | ||
| 52 | .steps { display: grid; gap: 2px; padding: 0; margin: var(--s2) 0 0; list-style: none; font-size: var(--fs-s); } | 52 | .steps { display: grid; gap: 2px; padding: 0; margin: var(--s2) 0 0; list-style: none; font-size: var(--fs-s); } | ||
| 53 | - | .steps li { display: flex; align-items: center; gap: var(--s1); } | 53 | + | /* a long step name (a whole shell command) wraps inside the rail; the icon |
| 54 | + | stays on its first line and the duration at the right */ | |||
| 55 | + | .steps li { display: flex; align-items: flex-start; gap: var(--s1); min-width: 0; } | |||
| 56 | + | .steps li > :is(a, span:not(.dim)) { flex: 1; min-width: 0; overflow-wrap: anywhere; } | |||
| 57 | + | .steps li > .dim { flex: none; margin-left: auto; } | |||
| 58 | + | .steps li > :first-child { margin-top: 3px; flex: none; } | |||
| 54 | .attempts { padding: 0; margin: var(--s2) 0 0; list-style: none; font-size: var(--fs-s); } | 59 | .attempts { padding: 0; margin: var(--s2) 0 0; list-style: none; font-size: var(--fs-s); } | ||
| 55 | .checks { display: grid; gap: var(--s1); padding: 0; margin: 0; list-style: none; font-size: var(--fs-s); } | 60 | .checks { display: grid; gap: var(--s1); padding: 0; margin: 0; list-style: none; font-size: var(--fs-s); } | ||
| 56 | .checks li { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1); } | 61 | .checks li { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1); } | ||
M internal/adapters/web/text.go +16 -0
| @@ -26,6 +26,22 @@ func invisible(r rune) bool { | |||
| 26 | 26 | // marker is the visible form of an invisible character. | |
| 27 | 27 | func marker(r rune) string { return fmt.Sprintf("<U+%04X>", r) } | |
| 28 | 28 | ||
| 29 | + | // who is the name shown for the author of a merge request or a comment, or the | |
| 30 | + | // actor of a run. ermite has one user: the owner's web session, root and every | |
| 31 | + | // stored token act for the owner, so they show the owner's name (ERMITE_OWNER), | |
| 32 | + | // not the label of the token, which names a device ("laptop"). Only CI is | |
| 33 | + | // someone else, shown as stored: a job token ("ci job") and the workflows a job | |
| 34 | + | // dispatched ("github-actions[bot]"). | |
| 35 | + | func (s *Server) who(stored string) string { | |
| 36 | + | if strings.HasPrefix(stored, "ci ") || strings.HasSuffix(stored, "[bot]") { | |
| 37 | + | return visibleText(stored) | |
| 38 | + | } | |
| 39 | + | if s.cfg.Owner != "" { | |
| 40 | + | return s.cfg.Owner | |
| 41 | + | } | |
| 42 | + | return "owner" | |
| 43 | + | } | |
| 44 | + | ||
| 29 | 45 | // visibleText is s with every invisible character replaced by its marker, | |
| 30 | 46 | // for the places that show text as a plain string: paths, names, subjects. | |
| 31 | 47 | func visibleText(s string) string { | |
| @@ -26,6 +26,22 @@ func invisible(r rune) bool { | |||||
| 26 | // marker is the visible form of an invisible character. | 26 | // marker is the visible form of an invisible character. | ||
| 27 | func marker(r rune) string { return fmt.Sprintf("<U+%04X>", r) } | 27 | func marker(r rune) string { return fmt.Sprintf("<U+%04X>", r) } | ||
| 28 | 28 | ||||
| 29 | + | // who is the name shown for the author of a merge request or a comment, or the | |||
| 30 | + | // actor of a run. ermite has one user: the owner's web session, root and every | |||
| 31 | + | // stored token act for the owner, so they show the owner's name (ERMITE_OWNER), | |||
| 32 | + | // not the label of the token, which names a device ("laptop"). Only CI is | |||
| 33 | + | // someone else, shown as stored: a job token ("ci job") and the workflows a job | |||
| 34 | + | // dispatched ("github-actions[bot]"). | |||
| 35 | + | func (s *Server) who(stored string) string { | |||
| 36 | + | if strings.HasPrefix(stored, "ci ") || strings.HasSuffix(stored, "[bot]") { | |||
| 37 | + | return visibleText(stored) | |||
| 38 | + | } | |||
| 39 | + | if s.cfg.Owner != "" { | |||
| 40 | + | return s.cfg.Owner | |||
| 41 | + | } | |||
| 42 | + | return "owner" | |||
| 43 | + | } | |||
| 44 | + | ||||
| 29 | // visibleText is s with every invisible character replaced by its marker, | 45 | // visibleText is s with every invisible character replaced by its marker, | ||
| 30 | // for the places that show text as a plain string: paths, names, subjects. | 46 | // for the places that show text as a plain string: paths, names, subjects. | ||
| 31 | func visibleText(s string) string { | 47 | func visibleText(s string) string { | ||
M internal/adapters/web/text_test.go +12 -0
| @@ -8,6 +8,18 @@ import ( | |||
| 8 | 8 | "github.com/stretchr/testify/assert" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | + | // People show as the owner's name, whatever token they used; CI as stored. | |
| 12 | + | func TestWho(t *testing.T) { | |
| 13 | + | s := &Server{cfg: Config{Owner: "alice"}} | |
| 14 | + | for stored, want := range map[string]string{ | |
| 15 | + | "owner": "alice", "root": "alice", "laptop": "alice", "": "alice", | |
| 16 | + | "ci job": "ci job", "github-actions[bot]": "github-actions[bot]", | |
| 17 | + | } { | |
| 18 | + | assert.Equal(t, want, s.who(stored), stored) | |
| 19 | + | } | |
| 20 | + | assert.Equal(t, "owner", (&Server{}).who("laptop"), "no owner configured") | |
| 21 | + | } | |
| 22 | + | ||
| 11 | 23 | // Invisible characters in names and subjects show as markers. | |
| 12 | 24 | func TestVisibleText(t *testing.T) { | |
| 13 | 25 | t.Parallel() | |
| @@ -8,6 +8,18 @@ import ( | |||||
| 8 | "github.com/stretchr/testify/assert" | 8 | "github.com/stretchr/testify/assert" | ||
| 9 | ) | 9 | ) | ||
| 10 | 10 | ||||
| 11 | + | // People show as the owner's name, whatever token they used; CI as stored. | |||
| 12 | + | func TestWho(t *testing.T) { | |||
| 13 | + | s := &Server{cfg: Config{Owner: "alice"}} | |||
| 14 | + | for stored, want := range map[string]string{ | |||
| 15 | + | "owner": "alice", "root": "alice", "laptop": "alice", "": "alice", | |||
| 16 | + | "ci job": "ci job", "github-actions[bot]": "github-actions[bot]", | |||
| 17 | + | } { | |||
| 18 | + | assert.Equal(t, want, s.who(stored), stored) | |||
| 19 | + | } | |||
| 20 | + | assert.Equal(t, "owner", (&Server{}).who("laptop"), "no owner configured") | |||
| 21 | + | } | |||
| 22 | + | ||||
| 11 | // Invisible characters in names and subjects show as markers. | 23 | // Invisible characters in names and subjects show as markers. | ||
| 12 | func TestVisibleText(t *testing.T) { | 24 | func TestVisibleText(t *testing.T) { | ||
| 13 | t.Parallel() | 25 | t.Parallel() | ||
M internal/adapters/web/view.go +3 -0
| @@ -20,6 +20,9 @@ type Section struct { | |||
| 20 | 20 | Label string | |
| 21 | 21 | Href string | |
| 22 | 22 | Current bool | |
| 23 | + | // Count is shown after the label when it is not empty: the open merge | |
| 24 | + | // requests. | |
| 25 | + | Count string | |
| 23 | 26 | } | |
| 24 | 27 | ||
| 25 | 28 | // RefItem is a branch or tag in the ref switcher. | |
| @@ -20,6 +20,9 @@ type Section struct { | |||||
| 20 | Label string | 20 | Label string | ||
| 21 | Href string | 21 | Href string | ||
| 22 | Current bool | 22 | Current bool | ||
| 23 | + | // Count is shown after the label when it is not empty: the open merge | |||
| 24 | + | // requests. | |||
| 25 | + | Count string | |||
| 23 | } | 26 | } | ||
| 24 | 27 | ||||
| 25 | // RefItem is a branch or tag in the ref switcher. | 28 | // RefItem is a branch or tag in the ref switcher. | ||
M internal/adapters/web/web.go +1 -0
| @@ -184,6 +184,7 @@ func (s *Server) routes() *http.ServeMux { | |||
| 184 | 184 | mux.HandleFunc("POST /{repo}/merge-requests/{n}/comments/{id}/resolve", s.mrAction(s.mrResolve)) | |
| 185 | 185 | mux.HandleFunc("POST /{repo}/merge-requests/{n}/merge", s.mrAction(s.mrMerge)) | |
| 186 | 186 | mux.HandleFunc("POST /{repo}/merge-requests/{n}/close", s.mrAction(s.mrClose)) | |
| 187 | + | mux.HandleFunc("POST /{repo}/merge-requests/{n}/ready", s.mrAction(s.mrReady)) | |
| 187 | 188 | mux.HandleFunc("POST /{repo}/merge-requests/{n}/read", s.mrAction(s.mrRead)) | |
| 188 | 189 | } | |
| 189 | 190 | if s.cfg.Runs != nil { | |
| @@ -184,6 +184,7 @@ func (s *Server) routes() *http.ServeMux { | |||||
| 184 | mux.HandleFunc("POST /{repo}/merge-requests/{n}/comments/{id}/resolve", s.mrAction(s.mrResolve)) | 184 | mux.HandleFunc("POST /{repo}/merge-requests/{n}/comments/{id}/resolve", s.mrAction(s.mrResolve)) | ||
| 185 | mux.HandleFunc("POST /{repo}/merge-requests/{n}/merge", s.mrAction(s.mrMerge)) | 185 | mux.HandleFunc("POST /{repo}/merge-requests/{n}/merge", s.mrAction(s.mrMerge)) | ||
| 186 | mux.HandleFunc("POST /{repo}/merge-requests/{n}/close", s.mrAction(s.mrClose)) | 186 | mux.HandleFunc("POST /{repo}/merge-requests/{n}/close", s.mrAction(s.mrClose)) | ||
| 187 | + | mux.HandleFunc("POST /{repo}/merge-requests/{n}/ready", s.mrAction(s.mrReady)) | |||
| 187 | mux.HandleFunc("POST /{repo}/merge-requests/{n}/read", s.mrAction(s.mrRead)) | 188 | mux.HandleFunc("POST /{repo}/merge-requests/{n}/read", s.mrAction(s.mrRead)) | ||
| 188 | } | 189 | } | ||
| 189 | if s.cfg.Runs != nil { | 190 | if s.cfg.Runs != nil { | ||
M internal/app/ci_pull_target_test.go +1 -1
| @@ -355,7 +355,7 @@ func TestWebShowsTheMergeRequestOfAPullRequestTargetRun(t *testing.T) { | |||
| 355 | 355 | res := b.get(page, nil) | |
| 356 | 356 | require.Equal(t, 200, res.status, res.body) | |
| 357 | 357 | assert.Contains(t, res.body, fmt.Sprintf(`href="/proj/merge-requests/%d"`, n), page) | |
| 358 | - | assert.Contains(t, res.body, fmt.Sprintf("!%d", n), page) | |
| 358 | + | assert.Contains(t, res.body, fmt.Sprintf(">#%d<", n), page) | |
| 359 | 359 | } | |
| 360 | 360 | } | |
| 361 | 361 | ||
| @@ -355,7 +355,7 @@ func TestWebShowsTheMergeRequestOfAPullRequestTargetRun(t *testing.T) { | |||||
| 355 | res := b.get(page, nil) | 355 | res := b.get(page, nil) | ||
| 356 | require.Equal(t, 200, res.status, res.body) | 356 | require.Equal(t, 200, res.status, res.body) | ||
| 357 | assert.Contains(t, res.body, fmt.Sprintf(`href="/proj/merge-requests/%d"`, n), page) | 357 | assert.Contains(t, res.body, fmt.Sprintf(`href="/proj/merge-requests/%d"`, n), page) | ||
| 358 | - | assert.Contains(t, res.body, fmt.Sprintf("!%d", n), page) | 358 | + | assert.Contains(t, res.body, fmt.Sprintf(">#%d<", n), page) |
| 359 | } | 359 | } | ||
| 360 | } | 360 | } | ||
| 361 | 361 | ||||
M internal/app/ci_web_test.go +13 -5
| @@ -40,7 +40,7 @@ func TestWebActionsPages(t *testing.T) { | |||
| 40 | 40 | assert.Contains(t, list.body, `href="/proj/actions/runs/1"`) | |
| 41 | 41 | assert.Contains(t, list.body, `aria-label="queued"`) | |
| 42 | 42 | assert.Contains(t, list.body, "/-/events?topic=repo%3Aproj&view=actions", "the page follows the repository stream") | |
| 43 | - | assert.Contains(t, list.body, ">Actions<", "the tab") | |
| 43 | + | assert.Contains(t, list.body, `aria-current="page">Actions <kbd`, "the tab") | |
| 44 | 44 | ||
| 45 | 45 | // The MR page says "waiting for a runner" only when no runner matches. | |
| 46 | 46 | mr, err := ci.root.mr.CreateMergeRequest(ci.ctx, connect.NewRequest(&pb.CreateMergeRequestRequest{Repo: "proj", Title: "Add CI", SourceBranch: "feat"})) | |
| @@ -134,7 +134,8 @@ func TestWebActionsPages(t *testing.T) { | |||
| 134 | 134 | } | |
| 135 | 135 | ||
| 136 | 136 | // The live updates of the pages: the run page gets the body of the run when a | |
| 137 | - | // job changes, and the job page its state, never a line of the log. | |
| 137 | + | // job changes, the job page its state, never a line of the log, and the page | |
| 138 | + | // of a merge request the checks of the head it shows (and only of that head). | |
| 138 | 139 | func TestWebActionsLiveFragments(t *testing.T) { | |
| 139 | 140 | ci := newCIHarness(t, nil) | |
| 140 | 141 | c := &cliHarness{harness: ci.harness, url: ci.srv.URL} | |
| @@ -151,12 +152,15 @@ func TestWebActionsLiveFragments(t *testing.T) { | |||
| 151 | 152 | list := ci.sse("topic=repo:proj&view=actions", "") | |
| 152 | 153 | job := runner.await() | |
| 153 | 154 | jobStream := ci.sse("topic=repo:proj&view=job&job="+job.GetJobId(), "") | |
| 155 | + | sha := ci.runs()[0].GetSha() | |
| 156 | + | mrPage := ci.sse("topic=repo:proj&view=mr&n=1&ref=main&head="+sha, "") | |
| 157 | + | otherHead := ci.sse("topic=repo:proj&view=mr&n=2&ref=x&head="+strings.Repeat("0", 40), "") | |
| 154 | 158 | _ = token | |
| 155 | 159 | ||
| 156 | 160 | runner.runJob(job, pb.CiConclusion_CI_CONCLUSION_SUCCESS, nil, "a secret value in the log") | |
| 157 | 161 | deadline := time.After(10 * time.Second) | |
| 158 | - | var sawRun, sawList, sawJob bool | |
| 159 | - | for !sawRun || !sawList || !sawJob { | |
| 162 | + | var sawRun, sawList, sawJob, sawChecks bool | |
| 163 | + | for !sawRun || !sawList || !sawJob || !sawChecks { | |
| 160 | 164 | select { | |
| 161 | 165 | case ev := <-run.events: | |
| 162 | 166 | sawRun = sawRun || (ev.name == "ci-run" && strings.Contains(ev.data, `id="run-body"`)) | |
| @@ -166,8 +170,12 @@ func TestWebActionsLiveFragments(t *testing.T) { | |||
| 166 | 170 | case ev := <-jobStream.events: | |
| 167 | 171 | sawJob = sawJob || (ev.name == "ci-job" && strings.Contains(ev.data, `id="job-head"`)) | |
| 168 | 172 | assert.NotContains(t, ev.data, "a secret value in the log", "the stream of states never carries the log") | |
| 173 | + | case ev := <-mrPage.events: | |
| 174 | + | sawChecks = sawChecks || (ev.name == "ci-run" && strings.Contains(ev.data, `id="mr-checks"`) && strings.Contains(ev.data, "success")) | |
| 175 | + | case ev := <-otherHead.events: | |
| 176 | + | assert.NotEqual(t, "ci-run", ev.name, "the checks of another head are not sent") | |
| 169 | 177 | case <-deadline: | |
| 170 | - | t.Fatalf("fragments: run %v, list %v, job %v", sawRun, sawList, sawJob) | |
| 178 | + | t.Fatalf("fragments: run %v, list %v, job %v, checks %v", sawRun, sawList, sawJob, sawChecks) | |
| 171 | 179 | } | |
| 172 | 180 | } | |
| 173 | 181 | _ = domain.CISuccess | |
| @@ -40,7 +40,7 @@ func TestWebActionsPages(t *testing.T) { | |||||
| 40 | assert.Contains(t, list.body, `href="/proj/actions/runs/1"`) | 40 | assert.Contains(t, list.body, `href="/proj/actions/runs/1"`) | ||
| 41 | assert.Contains(t, list.body, `aria-label="queued"`) | 41 | assert.Contains(t, list.body, `aria-label="queued"`) | ||
| 42 | assert.Contains(t, list.body, "/-/events?topic=repo%3Aproj&view=actions", "the page follows the repository stream") | 42 | assert.Contains(t, list.body, "/-/events?topic=repo%3Aproj&view=actions", "the page follows the repository stream") | ||
| 43 | - | assert.Contains(t, list.body, ">Actions<", "the tab") | 43 | + | assert.Contains(t, list.body, `aria-current="page">Actions <kbd`, "the tab") |
| 44 | 44 | ||||
| 45 | // The MR page says "waiting for a runner" only when no runner matches. | 45 | // The MR page says "waiting for a runner" only when no runner matches. | ||
| 46 | mr, err := ci.root.mr.CreateMergeRequest(ci.ctx, connect.NewRequest(&pb.CreateMergeRequestRequest{Repo: "proj", Title: "Add CI", SourceBranch: "feat"})) | 46 | mr, err := ci.root.mr.CreateMergeRequest(ci.ctx, connect.NewRequest(&pb.CreateMergeRequestRequest{Repo: "proj", Title: "Add CI", SourceBranch: "feat"})) | ||
| @@ -134,7 +134,8 @@ func TestWebActionsPages(t *testing.T) { | |||||
| 134 | } | 134 | } | ||
| 135 | 135 | ||||
| 136 | // The live updates of the pages: the run page gets the body of the run when a | 136 | // The live updates of the pages: the run page gets the body of the run when a | ||
| 137 | - | // job changes, and the job page its state, never a line of the log. | 137 | + | // job changes, the job page its state, never a line of the log, and the page |
| 138 | + | // of a merge request the checks of the head it shows (and only of that head). | |||
| 138 | func TestWebActionsLiveFragments(t *testing.T) { | 139 | func TestWebActionsLiveFragments(t *testing.T) { | ||
| 139 | ci := newCIHarness(t, nil) | 140 | ci := newCIHarness(t, nil) | ||
| 140 | c := &cliHarness{harness: ci.harness, url: ci.srv.URL} | 141 | c := &cliHarness{harness: ci.harness, url: ci.srv.URL} | ||
| @@ -151,12 +152,15 @@ func TestWebActionsLiveFragments(t *testing.T) { | |||||
| 151 | list := ci.sse("topic=repo:proj&view=actions", "") | 152 | list := ci.sse("topic=repo:proj&view=actions", "") | ||
| 152 | job := runner.await() | 153 | job := runner.await() | ||
| 153 | jobStream := ci.sse("topic=repo:proj&view=job&job="+job.GetJobId(), "") | 154 | jobStream := ci.sse("topic=repo:proj&view=job&job="+job.GetJobId(), "") | ||
| 155 | + | sha := ci.runs()[0].GetSha() | |||
| 156 | + | mrPage := ci.sse("topic=repo:proj&view=mr&n=1&ref=main&head="+sha, "") | |||
| 157 | + | otherHead := ci.sse("topic=repo:proj&view=mr&n=2&ref=x&head="+strings.Repeat("0", 40), "") | |||
| 154 | _ = token | 158 | _ = token | ||
| 155 | 159 | ||||
| 156 | runner.runJob(job, pb.CiConclusion_CI_CONCLUSION_SUCCESS, nil, "a secret value in the log") | 160 | runner.runJob(job, pb.CiConclusion_CI_CONCLUSION_SUCCESS, nil, "a secret value in the log") | ||
| 157 | deadline := time.After(10 * time.Second) | 161 | deadline := time.After(10 * time.Second) | ||
| 158 | - | var sawRun, sawList, sawJob bool | 162 | + | var sawRun, sawList, sawJob, sawChecks bool |
| 159 | - | for !sawRun || !sawList || !sawJob { | 163 | + | for !sawRun || !sawList || !sawJob || !sawChecks { |
| 160 | select { | 164 | select { | ||
| 161 | case ev := <-run.events: | 165 | case ev := <-run.events: | ||
| 162 | sawRun = sawRun || (ev.name == "ci-run" && strings.Contains(ev.data, `id="run-body"`)) | 166 | sawRun = sawRun || (ev.name == "ci-run" && strings.Contains(ev.data, `id="run-body"`)) | ||
| @@ -166,8 +170,12 @@ func TestWebActionsLiveFragments(t *testing.T) { | |||||
| 166 | case ev := <-jobStream.events: | 170 | case ev := <-jobStream.events: | ||
| 167 | sawJob = sawJob || (ev.name == "ci-job" && strings.Contains(ev.data, `id="job-head"`)) | 171 | sawJob = sawJob || (ev.name == "ci-job" && strings.Contains(ev.data, `id="job-head"`)) | ||
| 168 | assert.NotContains(t, ev.data, "a secret value in the log", "the stream of states never carries the log") | 172 | assert.NotContains(t, ev.data, "a secret value in the log", "the stream of states never carries the log") | ||
| 173 | + | case ev := <-mrPage.events: | |||
| 174 | + | sawChecks = sawChecks || (ev.name == "ci-run" && strings.Contains(ev.data, `id="mr-checks"`) && strings.Contains(ev.data, "success")) | |||
| 175 | + | case ev := <-otherHead.events: | |||
| 176 | + | assert.NotEqual(t, "ci-run", ev.name, "the checks of another head are not sent") | |||
| 169 | case <-deadline: | 177 | case <-deadline: | ||
| 170 | - | t.Fatalf("fragments: run %v, list %v, job %v", sawRun, sawList, sawJob) | 178 | + | t.Fatalf("fragments: run %v, list %v, job %v, checks %v", sawRun, sawList, sawJob, sawChecks) |
| 171 | } | 179 | } | ||
| 172 | } | 180 | } | ||
| 173 | _ = domain.CISuccess | 181 | _ = domain.CISuccess | ||
M internal/app/testdata/web/blob-binary.html +6 -6
| @@ -71,27 +71,27 @@ | |||
| 71 | 71 | <nav class="tabs" aria-label="Sections"> | |
| 72 | 72 | <ul> | |
| 73 | 73 | <li> | |
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 75 | 75 | </a> | |
| 76 | 76 | </li> | |
| 77 | 77 | <li> | |
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | |
| 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> | |
| 79 | 79 | </a> | |
| 80 | 80 | </li> | |
| 81 | 81 | <li> | |
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | |
| 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 83 | 83 | </a> | |
| 84 | 84 | </li> | |
| 85 | 85 | <li> | |
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | 87 | </a> | |
| 88 | 88 | </li> | |
| 89 | 89 | <li> | |
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 91 | 91 | </a> | |
| 92 | 92 | </li> | |
| 93 | 93 | <li> | |
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 95 | 95 | </a> | |
| 96 | 96 | </li> | |
| 97 | 97 | </ul> | |
| @@ -71,27 +71,27 @@ | |||||
| 71 | <nav class="tabs" aria-label="Sections"> | 71 | <nav class="tabs" aria-label="Sections"> | ||
| 72 | <ul> | 72 | <ul> | ||
| 73 | <li> | 73 | <li> | ||
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 75 | </a> | 75 | </a> | ||
| 76 | </li> | 76 | </li> | ||
| 77 | <li> | 77 | <li> | ||
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> |
| 79 | </a> | 79 | </a> | ||
| 80 | </li> | 80 | </li> | ||
| 81 | <li> | 81 | <li> | ||
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> |
| 83 | </a> | 83 | </a> | ||
| 84 | </li> | 84 | </li> | ||
| 85 | <li> | 85 | <li> | ||
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 87 | </a> | 87 | </a> | ||
| 88 | </li> | 88 | </li> | ||
| 89 | <li> | 89 | <li> | ||
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 91 | </a> | 91 | </a> | ||
| 92 | </li> | 92 | </li> | ||
| 93 | <li> | 93 | <li> | ||
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 95 | </a> | 95 | </a> | ||
| 96 | </li> | 96 | </li> | ||
| 97 | </ul> | 97 | </ul> | ||
M internal/app/testdata/web/blob-code.html +6 -6
| @@ -71,27 +71,27 @@ | |||
| 71 | 71 | <nav class="tabs" aria-label="Sections"> | |
| 72 | 72 | <ul> | |
| 73 | 73 | <li> | |
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 75 | 75 | </a> | |
| 76 | 76 | </li> | |
| 77 | 77 | <li> | |
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | |
| 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> | |
| 79 | 79 | </a> | |
| 80 | 80 | </li> | |
| 81 | 81 | <li> | |
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | |
| 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 83 | 83 | </a> | |
| 84 | 84 | </li> | |
| 85 | 85 | <li> | |
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | 87 | </a> | |
| 88 | 88 | </li> | |
| 89 | 89 | <li> | |
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 91 | 91 | </a> | |
| 92 | 92 | </li> | |
| 93 | 93 | <li> | |
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 95 | 95 | </a> | |
| 96 | 96 | </li> | |
| 97 | 97 | </ul> | |
| @@ -71,27 +71,27 @@ | |||||
| 71 | <nav class="tabs" aria-label="Sections"> | 71 | <nav class="tabs" aria-label="Sections"> | ||
| 72 | <ul> | 72 | <ul> | ||
| 73 | <li> | 73 | <li> | ||
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 75 | </a> | 75 | </a> | ||
| 76 | </li> | 76 | </li> | ||
| 77 | <li> | 77 | <li> | ||
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> |
| 79 | </a> | 79 | </a> | ||
| 80 | </li> | 80 | </li> | ||
| 81 | <li> | 81 | <li> | ||
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> |
| 83 | </a> | 83 | </a> | ||
| 84 | </li> | 84 | </li> | ||
| 85 | <li> | 85 | <li> | ||
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 87 | </a> | 87 | </a> | ||
| 88 | </li> | 88 | </li> | ||
| 89 | <li> | 89 | <li> | ||
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 91 | </a> | 91 | </a> | ||
| 92 | </li> | 92 | </li> | ||
| 93 | <li> | 93 | <li> | ||
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 95 | </a> | 95 | </a> | ||
| 96 | </li> | 96 | </li> | ||
| 97 | </ul> | 97 | </ul> | ||
M internal/app/testdata/web/blob-empty.html +6 -6
| @@ -71,27 +71,27 @@ | |||
| 71 | 71 | <nav class="tabs" aria-label="Sections"> | |
| 72 | 72 | <ul> | |
| 73 | 73 | <li> | |
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 75 | 75 | </a> | |
| 76 | 76 | </li> | |
| 77 | 77 | <li> | |
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | |
| 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> | |
| 79 | 79 | </a> | |
| 80 | 80 | </li> | |
| 81 | 81 | <li> | |
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | |
| 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 83 | 83 | </a> | |
| 84 | 84 | </li> | |
| 85 | 85 | <li> | |
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | 87 | </a> | |
| 88 | 88 | </li> | |
| 89 | 89 | <li> | |
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 91 | 91 | </a> | |
| 92 | 92 | </li> | |
| 93 | 93 | <li> | |
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 95 | 95 | </a> | |
| 96 | 96 | </li> | |
| 97 | 97 | </ul> | |
| @@ -71,27 +71,27 @@ | |||||
| 71 | <nav class="tabs" aria-label="Sections"> | 71 | <nav class="tabs" aria-label="Sections"> | ||
| 72 | <ul> | 72 | <ul> | ||
| 73 | <li> | 73 | <li> | ||
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 75 | </a> | 75 | </a> | ||
| 76 | </li> | 76 | </li> | ||
| 77 | <li> | 77 | <li> | ||
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> |
| 79 | </a> | 79 | </a> | ||
| 80 | </li> | 80 | </li> | ||
| 81 | <li> | 81 | <li> | ||
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> |
| 83 | </a> | 83 | </a> | ||
| 84 | </li> | 84 | </li> | ||
| 85 | <li> | 85 | <li> | ||
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 87 | </a> | 87 | </a> | ||
| 88 | </li> | 88 | </li> | ||
| 89 | <li> | 89 | <li> | ||
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 91 | </a> | 91 | </a> | ||
| 92 | </li> | 92 | </li> | ||
| 93 | <li> | 93 | <li> | ||
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 95 | </a> | 95 | </a> | ||
| 96 | </li> | 96 | </li> | ||
| 97 | </ul> | 97 | </ul> | ||
M internal/app/testdata/web/blob-image.html +6 -6
| @@ -71,27 +71,27 @@ | |||
| 71 | 71 | <nav class="tabs" aria-label="Sections"> | |
| 72 | 72 | <ul> | |
| 73 | 73 | <li> | |
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 75 | 75 | </a> | |
| 76 | 76 | </li> | |
| 77 | 77 | <li> | |
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | |
| 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> | |
| 79 | 79 | </a> | |
| 80 | 80 | </li> | |
| 81 | 81 | <li> | |
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | |
| 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 83 | 83 | </a> | |
| 84 | 84 | </li> | |
| 85 | 85 | <li> | |
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | 87 | </a> | |
| 88 | 88 | </li> | |
| 89 | 89 | <li> | |
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 91 | 91 | </a> | |
| 92 | 92 | </li> | |
| 93 | 93 | <li> | |
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 95 | 95 | </a> | |
| 96 | 96 | </li> | |
| 97 | 97 | </ul> | |
| @@ -71,27 +71,27 @@ | |||||
| 71 | <nav class="tabs" aria-label="Sections"> | 71 | <nav class="tabs" aria-label="Sections"> | ||
| 72 | <ul> | 72 | <ul> | ||
| 73 | <li> | 73 | <li> | ||
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 75 | </a> | 75 | </a> | ||
| 76 | </li> | 76 | </li> | ||
| 77 | <li> | 77 | <li> | ||
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> |
| 79 | </a> | 79 | </a> | ||
| 80 | </li> | 80 | </li> | ||
| 81 | <li> | 81 | <li> | ||
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> |
| 83 | </a> | 83 | </a> | ||
| 84 | </li> | 84 | </li> | ||
| 85 | <li> | 85 | <li> | ||
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 87 | </a> | 87 | </a> | ||
| 88 | </li> | 88 | </li> | ||
| 89 | <li> | 89 | <li> | ||
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 91 | </a> | 91 | </a> | ||
| 92 | </li> | 92 | </li> | ||
| 93 | <li> | 93 | <li> | ||
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 95 | </a> | 95 | </a> | ||
| 96 | </li> | 96 | </li> | ||
| 97 | </ul> | 97 | </ul> | ||
M internal/app/testdata/web/blob-markdown.html +6 -6
| @@ -74,27 +74,27 @@ | |||
| 74 | 74 | <nav class="tabs" aria-label="Sections"> | |
| 75 | 75 | <ul> | |
| 76 | 76 | <li> | |
| 77 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 77 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 78 | 78 | </a> | |
| 79 | 79 | </li> | |
| 80 | 80 | <li> | |
| 81 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | |
| 81 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> | |
| 82 | 82 | </a> | |
| 83 | 83 | </li> | |
| 84 | 84 | <li> | |
| 85 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | |
| 85 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 86 | 86 | </a> | |
| 87 | 87 | </li> | |
| 88 | 88 | <li> | |
| 89 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 89 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 90 | 90 | </a> | |
| 91 | 91 | </li> | |
| 92 | 92 | <li> | |
| 93 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 93 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 94 | 94 | </a> | |
| 95 | 95 | </li> | |
| 96 | 96 | <li> | |
| 97 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 97 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 98 | 98 | </a> | |
| 99 | 99 | </li> | |
| 100 | 100 | </ul> | |
| @@ -74,27 +74,27 @@ | |||||
| 74 | <nav class="tabs" aria-label="Sections"> | 74 | <nav class="tabs" aria-label="Sections"> | ||
| 75 | <ul> | 75 | <ul> | ||
| 76 | <li> | 76 | <li> | ||
| 77 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 77 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 78 | </a> | 78 | </a> | ||
| 79 | </li> | 79 | </li> | ||
| 80 | <li> | 80 | <li> | ||
| 81 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | 81 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> |
| 82 | </a> | 82 | </a> | ||
| 83 | </li> | 83 | </li> | ||
| 84 | <li> | 84 | <li> | ||
| 85 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | 85 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> |
| 86 | </a> | 86 | </a> | ||
| 87 | </li> | 87 | </li> | ||
| 88 | <li> | 88 | <li> | ||
| 89 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 89 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 90 | </a> | 90 | </a> | ||
| 91 | </li> | 91 | </li> | ||
| 92 | <li> | 92 | <li> | ||
| 93 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 93 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 94 | </a> | 94 | </a> | ||
| 95 | </li> | 95 | </li> | ||
| 96 | <li> | 96 | <li> | ||
| 97 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 97 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 98 | </a> | 98 | </a> | ||
| 99 | </li> | 99 | </li> | ||
| 100 | </ul> | 100 | </ul> | ||
M internal/app/testdata/web/blob-svg.html +6 -6
| @@ -71,27 +71,27 @@ | |||
| 71 | 71 | <nav class="tabs" aria-label="Sections"> | |
| 72 | 72 | <ul> | |
| 73 | 73 | <li> | |
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 75 | 75 | </a> | |
| 76 | 76 | </li> | |
| 77 | 77 | <li> | |
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | |
| 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> | |
| 79 | 79 | </a> | |
| 80 | 80 | </li> | |
| 81 | 81 | <li> | |
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | |
| 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 83 | 83 | </a> | |
| 84 | 84 | </li> | |
| 85 | 85 | <li> | |
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | 87 | </a> | |
| 88 | 88 | </li> | |
| 89 | 89 | <li> | |
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 91 | 91 | </a> | |
| 92 | 92 | </li> | |
| 93 | 93 | <li> | |
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 95 | 95 | </a> | |
| 96 | 96 | </li> | |
| 97 | 97 | </ul> | |
| @@ -71,27 +71,27 @@ | |||||
| 71 | <nav class="tabs" aria-label="Sections"> | 71 | <nav class="tabs" aria-label="Sections"> | ||
| 72 | <ul> | 72 | <ul> | ||
| 73 | <li> | 73 | <li> | ||
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 75 | </a> | 75 | </a> | ||
| 76 | </li> | 76 | </li> | ||
| 77 | <li> | 77 | <li> | ||
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> |
| 79 | </a> | 79 | </a> | ||
| 80 | </li> | 80 | </li> | ||
| 81 | <li> | 81 | <li> | ||
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> |
| 83 | </a> | 83 | </a> | ||
| 84 | </li> | 84 | </li> | ||
| 85 | <li> | 85 | <li> | ||
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 87 | </a> | 87 | </a> | ||
| 88 | </li> | 88 | </li> | ||
| 89 | <li> | 89 | <li> | ||
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 91 | </a> | 91 | </a> | ||
| 92 | </li> | 92 | </li> | ||
| 93 | <li> | 93 | <li> | ||
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 95 | </a> | 95 | </a> | ||
| 96 | </li> | 96 | </li> | ||
| 97 | </ul> | 97 | </ul> | ||
M internal/app/testdata/web/blob-text.html +6 -6
| @@ -71,27 +71,27 @@ | |||
| 71 | 71 | <nav class="tabs" aria-label="Sections"> | |
| 72 | 72 | <ul> | |
| 73 | 73 | <li> | |
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 75 | 75 | </a> | |
| 76 | 76 | </li> | |
| 77 | 77 | <li> | |
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | |
| 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> | |
| 79 | 79 | </a> | |
| 80 | 80 | </li> | |
| 81 | 81 | <li> | |
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | |
| 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 83 | 83 | </a> | |
| 84 | 84 | </li> | |
| 85 | 85 | <li> | |
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | 87 | </a> | |
| 88 | 88 | </li> | |
| 89 | 89 | <li> | |
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 91 | 91 | </a> | |
| 92 | 92 | </li> | |
| 93 | 93 | <li> | |
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 95 | 95 | </a> | |
| 96 | 96 | </li> | |
| 97 | 97 | </ul> | |
| @@ -71,27 +71,27 @@ | |||||
| 71 | <nav class="tabs" aria-label="Sections"> | 71 | <nav class="tabs" aria-label="Sections"> | ||
| 72 | <ul> | 72 | <ul> | ||
| 73 | <li> | 73 | <li> | ||
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 75 | </a> | 75 | </a> | ||
| 76 | </li> | 76 | </li> | ||
| 77 | <li> | 77 | <li> | ||
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> |
| 79 | </a> | 79 | </a> | ||
| 80 | </li> | 80 | </li> | ||
| 81 | <li> | 81 | <li> | ||
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> |
| 83 | </a> | 83 | </a> | ||
| 84 | </li> | 84 | </li> | ||
| 85 | <li> | 85 | <li> | ||
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 87 | </a> | 87 | </a> | ||
| 88 | </li> | 88 | </li> | ||
| 89 | <li> | 89 | <li> | ||
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 91 | </a> | 91 | </a> | ||
| 92 | </li> | 92 | </li> | ||
| 93 | <li> | 93 | <li> | ||
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 95 | </a> | 95 | </a> | ||
| 96 | </li> | 96 | </li> | ||
| 97 | </ul> | 97 | </ul> | ||
M internal/app/testdata/web/blob-too-large.html +6 -6
| @@ -71,27 +71,27 @@ | |||
| 71 | 71 | <nav class="tabs" aria-label="Sections"> | |
| 72 | 72 | <ul> | |
| 73 | 73 | <li> | |
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 75 | 75 | </a> | |
| 76 | 76 | </li> | |
| 77 | 77 | <li> | |
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | |
| 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> | |
| 79 | 79 | </a> | |
| 80 | 80 | </li> | |
| 81 | 81 | <li> | |
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | |
| 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 83 | 83 | </a> | |
| 84 | 84 | </li> | |
| 85 | 85 | <li> | |
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | 87 | </a> | |
| 88 | 88 | </li> | |
| 89 | 89 | <li> | |
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 91 | 91 | </a> | |
| 92 | 92 | </li> | |
| 93 | 93 | <li> | |
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 95 | 95 | </a> | |
| 96 | 96 | </li> | |
| 97 | 97 | </ul> | |
| @@ -71,27 +71,27 @@ | |||||
| 71 | <nav class="tabs" aria-label="Sections"> | 71 | <nav class="tabs" aria-label="Sections"> | ||
| 72 | <ul> | 72 | <ul> | ||
| 73 | <li> | 73 | <li> | ||
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 75 | </a> | 75 | </a> | ||
| 76 | </li> | 76 | </li> | ||
| 77 | <li> | 77 | <li> | ||
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> |
| 79 | </a> | 79 | </a> | ||
| 80 | </li> | 80 | </li> | ||
| 81 | <li> | 81 | <li> | ||
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> |
| 83 | </a> | 83 | </a> | ||
| 84 | </li> | 84 | </li> | ||
| 85 | <li> | 85 | <li> | ||
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 87 | </a> | 87 | </a> | ||
| 88 | </li> | 88 | </li> | ||
| 89 | <li> | 89 | <li> | ||
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 91 | </a> | 91 | </a> | ||
| 92 | </li> | 92 | </li> | ||
| 93 | <li> | 93 | <li> | ||
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 95 | </a> | 95 | </a> | ||
| 96 | </li> | 96 | </li> | ||
| 97 | </ul> | 97 | </ul> | ||
M internal/app/testdata/web/blob-trojan.html +6 -6
| @@ -71,27 +71,27 @@ | |||
| 71 | 71 | <nav class="tabs" aria-label="Sections"> | |
| 72 | 72 | <ul> | |
| 73 | 73 | <li> | |
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 75 | 75 | </a> | |
| 76 | 76 | </li> | |
| 77 | 77 | <li> | |
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | |
| 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> | |
| 79 | 79 | </a> | |
| 80 | 80 | </li> | |
| 81 | 81 | <li> | |
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | |
| 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 83 | 83 | </a> | |
| 84 | 84 | </li> | |
| 85 | 85 | <li> | |
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | 87 | </a> | |
| 88 | 88 | </li> | |
| 89 | 89 | <li> | |
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 91 | 91 | </a> | |
| 92 | 92 | </li> | |
| 93 | 93 | <li> | |
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 95 | 95 | </a> | |
| 96 | 96 | </li> | |
| 97 | 97 | </ul> | |
| @@ -71,27 +71,27 @@ | |||||
| 71 | <nav class="tabs" aria-label="Sections"> | 71 | <nav class="tabs" aria-label="Sections"> | ||
| 72 | <ul> | 72 | <ul> | ||
| 73 | <li> | 73 | <li> | ||
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 75 | </a> | 75 | </a> | ||
| 76 | </li> | 76 | </li> | ||
| 77 | <li> | 77 | <li> | ||
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> |
| 79 | </a> | 79 | </a> | ||
| 80 | </li> | 80 | </li> | ||
| 81 | <li> | 81 | <li> | ||
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> |
| 83 | </a> | 83 | </a> | ||
| 84 | </li> | 84 | </li> | ||
| 85 | <li> | 85 | <li> | ||
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 87 | </a> | 87 | </a> | ||
| 88 | </li> | 88 | </li> | ||
| 89 | <li> | 89 | <li> | ||
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 91 | </a> | 91 | </a> | ||
| 92 | </li> | 92 | </li> | ||
| 93 | <li> | 93 | <li> | ||
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 95 | </a> | 95 | </a> | ||
| 96 | </li> | 96 | </li> | ||
| 97 | </ul> | 97 | </ul> | ||
M internal/app/testdata/web/commit-big.html +6 -6
| @@ -68,27 +68,27 @@ | |||
| 68 | 68 | <nav class="tabs" aria-label="Sections"> | |
| 69 | 69 | <ul> | |
| 70 | 70 | <li> | |
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 72 | 72 | </a> | |
| 73 | 73 | </li> | |
| 74 | 74 | <li> | |
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | |
| 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 76 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 78 | <li> | |
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | |
| 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> | |
| 80 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 82 | <li> | |
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 84 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 90 | <li> | |
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 92 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 94 | </ul> | |
| @@ -68,27 +68,27 @@ | |||||
| 68 | <nav class="tabs" aria-label="Sections"> | 68 | <nav class="tabs" aria-label="Sections"> | ||
| 69 | <ul> | 69 | <ul> | ||
| 70 | <li> | 70 | <li> | ||
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 72 | </a> | 72 | </a> | ||
| 73 | </li> | 73 | </li> | ||
| 74 | <li> | 74 | <li> | ||
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> |
| 76 | </a> | 76 | </a> | ||
| 77 | </li> | 77 | </li> | ||
| 78 | <li> | 78 | <li> | ||
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> |
| 80 | </a> | 80 | </a> | ||
| 81 | </li> | 81 | </li> | ||
| 82 | <li> | 82 | <li> | ||
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 84 | </a> | 84 | </a> | ||
| 85 | </li> | 85 | </li> | ||
| 86 | <li> | 86 | <li> | ||
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 88 | </a> | 88 | </a> | ||
| 89 | </li> | 89 | </li> | ||
| 90 | <li> | 90 | <li> | ||
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 92 | </a> | 92 | </a> | ||
| 93 | </li> | 93 | </li> | ||
| 94 | </ul> | 94 | </ul> | ||
M internal/app/testdata/web/commit-binary.html +6 -6
| @@ -68,27 +68,27 @@ | |||
| 68 | 68 | <nav class="tabs" aria-label="Sections"> | |
| 69 | 69 | <ul> | |
| 70 | 70 | <li> | |
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 72 | 72 | </a> | |
| 73 | 73 | </li> | |
| 74 | 74 | <li> | |
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | |
| 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 76 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 78 | <li> | |
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | |
| 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> | |
| 80 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 82 | <li> | |
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 84 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 90 | <li> | |
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 92 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 94 | </ul> | |
| @@ -68,27 +68,27 @@ | |||||
| 68 | <nav class="tabs" aria-label="Sections"> | 68 | <nav class="tabs" aria-label="Sections"> | ||
| 69 | <ul> | 69 | <ul> | ||
| 70 | <li> | 70 | <li> | ||
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 72 | </a> | 72 | </a> | ||
| 73 | </li> | 73 | </li> | ||
| 74 | <li> | 74 | <li> | ||
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> |
| 76 | </a> | 76 | </a> | ||
| 77 | </li> | 77 | </li> | ||
| 78 | <li> | 78 | <li> | ||
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> |
| 80 | </a> | 80 | </a> | ||
| 81 | </li> | 81 | </li> | ||
| 82 | <li> | 82 | <li> | ||
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 84 | </a> | 84 | </a> | ||
| 85 | </li> | 85 | </li> | ||
| 86 | <li> | 86 | <li> | ||
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 88 | </a> | 88 | </a> | ||
| 89 | </li> | 89 | </li> | ||
| 90 | <li> | 90 | <li> | ||
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 92 | </a> | 92 | </a> | ||
| 93 | </li> | 93 | </li> | ||
| 94 | </ul> | 94 | </ul> | ||
M internal/app/testdata/web/commit-merge.html +6 -6
| @@ -68,27 +68,27 @@ | |||
| 68 | 68 | <nav class="tabs" aria-label="Sections"> | |
| 69 | 69 | <ul> | |
| 70 | 70 | <li> | |
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 72 | 72 | </a> | |
| 73 | 73 | </li> | |
| 74 | 74 | <li> | |
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | |
| 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 76 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 78 | <li> | |
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | |
| 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> | |
| 80 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 82 | <li> | |
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 84 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 90 | <li> | |
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 92 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 94 | </ul> | |
| @@ -68,27 +68,27 @@ | |||||
| 68 | <nav class="tabs" aria-label="Sections"> | 68 | <nav class="tabs" aria-label="Sections"> | ||
| 69 | <ul> | 69 | <ul> | ||
| 70 | <li> | 70 | <li> | ||
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 72 | </a> | 72 | </a> | ||
| 73 | </li> | 73 | </li> | ||
| 74 | <li> | 74 | <li> | ||
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> |
| 76 | </a> | 76 | </a> | ||
| 77 | </li> | 77 | </li> | ||
| 78 | <li> | 78 | <li> | ||
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> |
| 80 | </a> | 80 | </a> | ||
| 81 | </li> | 81 | </li> | ||
| 82 | <li> | 82 | <li> | ||
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 84 | </a> | 84 | </a> | ||
| 85 | </li> | 85 | </li> | ||
| 86 | <li> | 86 | <li> | ||
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 88 | </a> | 88 | </a> | ||
| 89 | </li> | 89 | </li> | ||
| 90 | <li> | 90 | <li> | ||
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 92 | </a> | 92 | </a> | ||
| 93 | </li> | 93 | </li> | ||
| 94 | </ul> | 94 | </ul> | ||
M internal/app/testdata/web/commit-modify-split.html +6 -6
| @@ -68,27 +68,27 @@ | |||
| 68 | 68 | <nav class="tabs" aria-label="Sections"> | |
| 69 | 69 | <ul> | |
| 70 | 70 | <li> | |
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 72 | 72 | </a> | |
| 73 | 73 | </li> | |
| 74 | 74 | <li> | |
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | |
| 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 76 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 78 | <li> | |
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | |
| 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> | |
| 80 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 82 | <li> | |
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 84 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 90 | <li> | |
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 92 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 94 | </ul> | |
| @@ -68,27 +68,27 @@ | |||||
| 68 | <nav class="tabs" aria-label="Sections"> | 68 | <nav class="tabs" aria-label="Sections"> | ||
| 69 | <ul> | 69 | <ul> | ||
| 70 | <li> | 70 | <li> | ||
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 72 | </a> | 72 | </a> | ||
| 73 | </li> | 73 | </li> | ||
| 74 | <li> | 74 | <li> | ||
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> |
| 76 | </a> | 76 | </a> | ||
| 77 | </li> | 77 | </li> | ||
| 78 | <li> | 78 | <li> | ||
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> |
| 80 | </a> | 80 | </a> | ||
| 81 | </li> | 81 | </li> | ||
| 82 | <li> | 82 | <li> | ||
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 84 | </a> | 84 | </a> | ||
| 85 | </li> | 85 | </li> | ||
| 86 | <li> | 86 | <li> | ||
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 88 | </a> | 88 | </a> | ||
| 89 | </li> | 89 | </li> | ||
| 90 | <li> | 90 | <li> | ||
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 92 | </a> | 92 | </a> | ||
| 93 | </li> | 93 | </li> | ||
| 94 | </ul> | 94 | </ul> | ||
M internal/app/testdata/web/commit-modify.html +6 -6
| @@ -68,27 +68,27 @@ | |||
| 68 | 68 | <nav class="tabs" aria-label="Sections"> | |
| 69 | 69 | <ul> | |
| 70 | 70 | <li> | |
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 72 | 72 | </a> | |
| 73 | 73 | </li> | |
| 74 | 74 | <li> | |
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | |
| 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 76 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 78 | <li> | |
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | |
| 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> | |
| 80 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 82 | <li> | |
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 84 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 90 | <li> | |
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 92 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 94 | </ul> | |
| @@ -68,27 +68,27 @@ | |||||
| 68 | <nav class="tabs" aria-label="Sections"> | 68 | <nav class="tabs" aria-label="Sections"> | ||
| 69 | <ul> | 69 | <ul> | ||
| 70 | <li> | 70 | <li> | ||
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 72 | </a> | 72 | </a> | ||
| 73 | </li> | 73 | </li> | ||
| 74 | <li> | 74 | <li> | ||
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> |
| 76 | </a> | 76 | </a> | ||
| 77 | </li> | 77 | </li> | ||
| 78 | <li> | 78 | <li> | ||
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> |
| 80 | </a> | 80 | </a> | ||
| 81 | </li> | 81 | </li> | ||
| 82 | <li> | 82 | <li> | ||
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 84 | </a> | 84 | </a> | ||
| 85 | </li> | 85 | </li> | ||
| 86 | <li> | 86 | <li> | ||
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 88 | </a> | 88 | </a> | ||
| 89 | </li> | 89 | </li> | ||
| 90 | <li> | 90 | <li> | ||
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 92 | </a> | 92 | </a> | ||
| 93 | </li> | 93 | </li> | ||
| 94 | </ul> | 94 | </ul> | ||
M internal/app/testdata/web/commit-rename.html +6 -6
| @@ -68,27 +68,27 @@ | |||
| 68 | 68 | <nav class="tabs" aria-label="Sections"> | |
| 69 | 69 | <ul> | |
| 70 | 70 | <li> | |
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 72 | 72 | </a> | |
| 73 | 73 | </li> | |
| 74 | 74 | <li> | |
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | |
| 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 76 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 78 | <li> | |
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | |
| 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> | |
| 80 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 82 | <li> | |
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 84 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 90 | <li> | |
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 92 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 94 | </ul> | |
| @@ -68,27 +68,27 @@ | |||||
| 68 | <nav class="tabs" aria-label="Sections"> | 68 | <nav class="tabs" aria-label="Sections"> | ||
| 69 | <ul> | 69 | <ul> | ||
| 70 | <li> | 70 | <li> | ||
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 72 | </a> | 72 | </a> | ||
| 73 | </li> | 73 | </li> | ||
| 74 | <li> | 74 | <li> | ||
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> |
| 76 | </a> | 76 | </a> | ||
| 77 | </li> | 77 | </li> | ||
| 78 | <li> | 78 | <li> | ||
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> |
| 80 | </a> | 80 | </a> | ||
| 81 | </li> | 81 | </li> | ||
| 82 | <li> | 82 | <li> | ||
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 84 | </a> | 84 | </a> | ||
| 85 | </li> | 85 | </li> | ||
| 86 | <li> | 86 | <li> | ||
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 88 | </a> | 88 | </a> | ||
| 89 | </li> | 89 | </li> | ||
| 90 | <li> | 90 | <li> | ||
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 92 | </a> | 92 | </a> | ||
| 93 | </li> | 93 | </li> | ||
| 94 | </ul> | 94 | </ul> | ||
M internal/app/testdata/web/commit-root.html +6 -6
| @@ -68,27 +68,27 @@ | |||
| 68 | 68 | <nav class="tabs" aria-label="Sections"> | |
| 69 | 69 | <ul> | |
| 70 | 70 | <li> | |
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 72 | 72 | </a> | |
| 73 | 73 | </li> | |
| 74 | 74 | <li> | |
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | |
| 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 76 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 78 | <li> | |
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | |
| 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> | |
| 80 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 82 | <li> | |
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 84 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 90 | <li> | |
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 92 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 94 | </ul> | |
| @@ -68,27 +68,27 @@ | |||||
| 68 | <nav class="tabs" aria-label="Sections"> | 68 | <nav class="tabs" aria-label="Sections"> | ||
| 69 | <ul> | 69 | <ul> | ||
| 70 | <li> | 70 | <li> | ||
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 72 | </a> | 72 | </a> | ||
| 73 | </li> | 73 | </li> | ||
| 74 | <li> | 74 | <li> | ||
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> |
| 76 | </a> | 76 | </a> | ||
| 77 | </li> | 77 | </li> | ||
| 78 | <li> | 78 | <li> | ||
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> |
| 80 | </a> | 80 | </a> | ||
| 81 | </li> | 81 | </li> | ||
| 82 | <li> | 82 | <li> | ||
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 84 | </a> | 84 | </a> | ||
| 85 | </li> | 85 | </li> | ||
| 86 | <li> | 86 | <li> | ||
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 88 | </a> | 88 | </a> | ||
| 89 | </li> | 89 | </li> | ||
| 90 | <li> | 90 | <li> | ||
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 92 | </a> | 92 | </a> | ||
| 93 | </li> | 93 | </li> | ||
| 94 | </ul> | 94 | </ul> | ||
M internal/app/testdata/web/commit-trojan.html +6 -6
| @@ -68,27 +68,27 @@ | |||
| 68 | 68 | <nav class="tabs" aria-label="Sections"> | |
| 69 | 69 | <ul> | |
| 70 | 70 | <li> | |
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 72 | 72 | </a> | |
| 73 | 73 | </li> | |
| 74 | 74 | <li> | |
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | |
| 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 76 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 78 | <li> | |
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | |
| 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> | |
| 80 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 82 | <li> | |
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 84 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 90 | <li> | |
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 92 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 94 | </ul> | |
| @@ -68,27 +68,27 @@ | |||||
| 68 | <nav class="tabs" aria-label="Sections"> | 68 | <nav class="tabs" aria-label="Sections"> | ||
| 69 | <ul> | 69 | <ul> | ||
| 70 | <li> | 70 | <li> | ||
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 72 | </a> | 72 | </a> | ||
| 73 | </li> | 73 | </li> | ||
| 74 | <li> | 74 | <li> | ||
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> |
| 76 | </a> | 76 | </a> | ||
| 77 | </li> | 77 | </li> | ||
| 78 | <li> | 78 | <li> | ||
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> |
| 80 | </a> | 80 | </a> | ||
| 81 | </li> | 81 | </li> | ||
| 82 | <li> | 82 | <li> | ||
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 84 | </a> | 84 | </a> | ||
| 85 | </li> | 85 | </li> | ||
| 86 | <li> | 86 | <li> | ||
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 88 | </a> | 88 | </a> | ||
| 89 | </li> | 89 | </li> | ||
| 90 | <li> | 90 | <li> | ||
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 92 | </a> | 92 | </a> | ||
| 93 | </li> | 93 | </li> | ||
| 94 | </ul> | 94 | </ul> | ||
M internal/app/testdata/web/commits-page2.html +6 -6
| @@ -68,27 +68,27 @@ | |||
| 68 | 68 | <nav class="tabs" aria-label="Sections"> | |
| 69 | 69 | <ul> | |
| 70 | 70 | <li> | |
| 71 | - | <a href="/long" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 71 | + | <a href="/long" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 72 | 72 | </a> | |
| 73 | 73 | </li> | |
| 74 | 74 | <li> | |
| 75 | - | <a href="/long/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | |
| 75 | + | <a href="/long/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 76 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 78 | <li> | |
| 79 | - | <a href="/long/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | |
| 79 | + | <a href="/long/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> | |
| 80 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 82 | <li> | |
| 83 | - | <a href="/long/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 83 | + | <a href="/long/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 84 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/long/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 87 | + | <a href="/long/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 90 | <li> | |
| 91 | - | <a href="/long/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 91 | + | <a href="/long/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 92 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 94 | </ul> | |
| @@ -68,27 +68,27 @@ | |||||
| 68 | <nav class="tabs" aria-label="Sections"> | 68 | <nav class="tabs" aria-label="Sections"> | ||
| 69 | <ul> | 69 | <ul> | ||
| 70 | <li> | 70 | <li> | ||
| 71 | - | <a href="/long" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 71 | + | <a href="/long" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 72 | </a> | 72 | </a> | ||
| 73 | </li> | 73 | </li> | ||
| 74 | <li> | 74 | <li> | ||
| 75 | - | <a href="/long/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | 75 | + | <a href="/long/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> |
| 76 | </a> | 76 | </a> | ||
| 77 | </li> | 77 | </li> | ||
| 78 | <li> | 78 | <li> | ||
| 79 | - | <a href="/long/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | 79 | + | <a href="/long/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> |
| 80 | </a> | 80 | </a> | ||
| 81 | </li> | 81 | </li> | ||
| 82 | <li> | 82 | <li> | ||
| 83 | - | <a href="/long/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 83 | + | <a href="/long/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 84 | </a> | 84 | </a> | ||
| 85 | </li> | 85 | </li> | ||
| 86 | <li> | 86 | <li> | ||
| 87 | - | <a href="/long/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 87 | + | <a href="/long/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 88 | </a> | 88 | </a> | ||
| 89 | </li> | 89 | </li> | ||
| 90 | <li> | 90 | <li> | ||
| 91 | - | <a href="/long/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 91 | + | <a href="/long/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 92 | </a> | 92 | </a> | ||
| 93 | </li> | 93 | </li> | ||
| 94 | </ul> | 94 | </ul> | ||
M internal/app/testdata/web/commits-path.html +6 -6
| @@ -71,27 +71,27 @@ | |||
| 71 | 71 | <nav class="tabs" aria-label="Sections"> | |
| 72 | 72 | <ul> | |
| 73 | 73 | <li> | |
| 74 | - | <a href="/long" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 74 | + | <a href="/long" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 75 | 75 | </a> | |
| 76 | 76 | </li> | |
| 77 | 77 | <li> | |
| 78 | - | <a href="/long/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | |
| 78 | + | <a href="/long/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 79 | 79 | </a> | |
| 80 | 80 | </li> | |
| 81 | 81 | <li> | |
| 82 | - | <a href="/long/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | |
| 82 | + | <a href="/long/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> | |
| 83 | 83 | </a> | |
| 84 | 84 | </li> | |
| 85 | 85 | <li> | |
| 86 | - | <a href="/long/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 86 | + | <a href="/long/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | 87 | </a> | |
| 88 | 88 | </li> | |
| 89 | 89 | <li> | |
| 90 | - | <a href="/long/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 90 | + | <a href="/long/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 91 | 91 | </a> | |
| 92 | 92 | </li> | |
| 93 | 93 | <li> | |
| 94 | - | <a href="/long/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 94 | + | <a href="/long/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 95 | 95 | </a> | |
| 96 | 96 | </li> | |
| 97 | 97 | </ul> | |
| @@ -71,27 +71,27 @@ | |||||
| 71 | <nav class="tabs" aria-label="Sections"> | 71 | <nav class="tabs" aria-label="Sections"> | ||
| 72 | <ul> | 72 | <ul> | ||
| 73 | <li> | 73 | <li> | ||
| 74 | - | <a href="/long" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 74 | + | <a href="/long" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 75 | </a> | 75 | </a> | ||
| 76 | </li> | 76 | </li> | ||
| 77 | <li> | 77 | <li> | ||
| 78 | - | <a href="/long/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | 78 | + | <a href="/long/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> |
| 79 | </a> | 79 | </a> | ||
| 80 | </li> | 80 | </li> | ||
| 81 | <li> | 81 | <li> | ||
| 82 | - | <a href="/long/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | 82 | + | <a href="/long/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> |
| 83 | </a> | 83 | </a> | ||
| 84 | </li> | 84 | </li> | ||
| 85 | <li> | 85 | <li> | ||
| 86 | - | <a href="/long/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 86 | + | <a href="/long/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 87 | </a> | 87 | </a> | ||
| 88 | </li> | 88 | </li> | ||
| 89 | <li> | 89 | <li> | ||
| 90 | - | <a href="/long/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 90 | + | <a href="/long/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 91 | </a> | 91 | </a> | ||
| 92 | </li> | 92 | </li> | ||
| 93 | <li> | 93 | <li> | ||
| 94 | - | <a href="/long/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 94 | + | <a href="/long/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 95 | </a> | 95 | </a> | ||
| 96 | </li> | 96 | </li> | ||
| 97 | </ul> | 97 | </ul> | ||
M internal/app/testdata/web/commits-root.html +6 -6
| @@ -68,27 +68,27 @@ | |||
| 68 | 68 | <nav class="tabs" aria-label="Sections"> | |
| 69 | 69 | <ul> | |
| 70 | 70 | <li> | |
| 71 | - | <a href="/long" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 71 | + | <a href="/long" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 72 | 72 | </a> | |
| 73 | 73 | </li> | |
| 74 | 74 | <li> | |
| 75 | - | <a href="/long/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | |
| 75 | + | <a href="/long/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 76 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 78 | <li> | |
| 79 | - | <a href="/long/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | |
| 79 | + | <a href="/long/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> | |
| 80 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 82 | <li> | |
| 83 | - | <a href="/long/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 83 | + | <a href="/long/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 84 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/long/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 87 | + | <a href="/long/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 90 | <li> | |
| 91 | - | <a href="/long/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 91 | + | <a href="/long/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 92 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 94 | </ul> | |
| @@ -68,27 +68,27 @@ | |||||
| 68 | <nav class="tabs" aria-label="Sections"> | 68 | <nav class="tabs" aria-label="Sections"> | ||
| 69 | <ul> | 69 | <ul> | ||
| 70 | <li> | 70 | <li> | ||
| 71 | - | <a href="/long" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 71 | + | <a href="/long" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 72 | </a> | 72 | </a> | ||
| 73 | </li> | 73 | </li> | ||
| 74 | <li> | 74 | <li> | ||
| 75 | - | <a href="/long/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | 75 | + | <a href="/long/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> |
| 76 | </a> | 76 | </a> | ||
| 77 | </li> | 77 | </li> | ||
| 78 | <li> | 78 | <li> | ||
| 79 | - | <a href="/long/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | 79 | + | <a href="/long/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> |
| 80 | </a> | 80 | </a> | ||
| 81 | </li> | 81 | </li> | ||
| 82 | <li> | 82 | <li> | ||
| 83 | - | <a href="/long/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 83 | + | <a href="/long/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 84 | </a> | 84 | </a> | ||
| 85 | </li> | 85 | </li> | ||
| 86 | <li> | 86 | <li> | ||
| 87 | - | <a href="/long/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 87 | + | <a href="/long/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 88 | </a> | 88 | </a> | ||
| 89 | </li> | 89 | </li> | ||
| 90 | <li> | 90 | <li> | ||
| 91 | - | <a href="/long/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 91 | + | <a href="/long/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 92 | </a> | 92 | </a> | ||
| 93 | </li> | 93 | </li> | ||
| 94 | </ul> | 94 | </ul> | ||
M internal/app/testdata/web/commits-small.html +6 -6
| @@ -68,27 +68,27 @@ | |||
| 68 | 68 | <nav class="tabs" aria-label="Sections"> | |
| 69 | 69 | <ul> | |
| 70 | 70 | <li> | |
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 72 | 72 | </a> | |
| 73 | 73 | </li> | |
| 74 | 74 | <li> | |
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | |
| 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 76 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 78 | <li> | |
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | |
| 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> | |
| 80 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 82 | <li> | |
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 84 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 90 | <li> | |
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 92 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 94 | </ul> | |
| @@ -68,27 +68,27 @@ | |||||
| 68 | <nav class="tabs" aria-label="Sections"> | 68 | <nav class="tabs" aria-label="Sections"> | ||
| 69 | <ul> | 69 | <ul> | ||
| 70 | <li> | 70 | <li> | ||
| 71 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 71 | + | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 72 | </a> | 72 | </a> | ||
| 73 | </li> | 73 | </li> | ||
| 74 | <li> | 74 | <li> | ||
| 75 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | 75 | + | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> |
| 76 | </a> | 76 | </a> | ||
| 77 | </li> | 77 | </li> | ||
| 78 | <li> | 78 | <li> | ||
| 79 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits<kbd aria-hidden="true">c</kbd> | 79 | + | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> |
| 80 | </a> | 80 | </a> | ||
| 81 | </li> | 81 | </li> | ||
| 82 | <li> | 82 | <li> | ||
| 83 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 83 | + | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 84 | </a> | 84 | </a> | ||
| 85 | </li> | 85 | </li> | ||
| 86 | <li> | 86 | <li> | ||
| 87 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 87 | + | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 88 | </a> | 88 | </a> | ||
| 89 | </li> | 89 | </li> | ||
| 90 | <li> | 90 | <li> | ||
| 91 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 91 | + | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 92 | </a> | 92 | </a> | ||
| 93 | </li> | 93 | </li> | ||
| 94 | </ul> | 94 | </ul> | ||
M internal/app/testdata/web/refs.html +6 -6
| @@ -65,27 +65,27 @@ | |||
| 65 | 65 | <nav class="tabs" aria-label="Sections"> | |
| 66 | 66 | <ul> | |
| 67 | 67 | <li> | |
| 68 | - | <a href="/demo" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 68 | + | <a href="/demo" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 69 | 69 | </a> | |
| 70 | 70 | </li> | |
| 71 | 71 | <li> | |
| 72 | - | <a href="/demo/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | |
| 72 | + | <a href="/demo/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 73 | 73 | </a> | |
| 74 | 74 | </li> | |
| 75 | 75 | <li> | |
| 76 | - | <a href="/demo/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | |
| 76 | + | <a href="/demo/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 77 | 77 | </a> | |
| 78 | 78 | </li> | |
| 79 | 79 | <li> | |
| 80 | - | <a href="/demo/refs" data-key="r" aria-keyshortcuts="r" aria-current="page">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 80 | + | <a href="/demo/refs" data-key="r" aria-keyshortcuts="r" aria-current="page">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 81 | 81 | </a> | |
| 82 | 82 | </li> | |
| 83 | 83 | <li> | |
| 84 | - | <a href="/demo/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 84 | + | <a href="/demo/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 85 | 85 | </a> | |
| 86 | 86 | </li> | |
| 87 | 87 | <li> | |
| 88 | - | <a href="/demo/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 88 | + | <a href="/demo/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 89 | 89 | </a> | |
| 90 | 90 | </li> | |
| 91 | 91 | </ul> | |
| @@ -65,27 +65,27 @@ | |||||
| 65 | <nav class="tabs" aria-label="Sections"> | 65 | <nav class="tabs" aria-label="Sections"> | ||
| 66 | <ul> | 66 | <ul> | ||
| 67 | <li> | 67 | <li> | ||
| 68 | - | <a href="/demo" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 68 | + | <a href="/demo" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 69 | </a> | 69 | </a> | ||
| 70 | </li> | 70 | </li> | ||
| 71 | <li> | 71 | <li> | ||
| 72 | - | <a href="/demo/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | 72 | + | <a href="/demo/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> |
| 73 | </a> | 73 | </a> | ||
| 74 | </li> | 74 | </li> | ||
| 75 | <li> | 75 | <li> | ||
| 76 | - | <a href="/demo/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | 76 | + | <a href="/demo/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> |
| 77 | </a> | 77 | </a> | ||
| 78 | </li> | 78 | </li> | ||
| 79 | <li> | 79 | <li> | ||
| 80 | - | <a href="/demo/refs" data-key="r" aria-keyshortcuts="r" aria-current="page">Branches & tags<kbd aria-hidden="true">r</kbd> | 80 | + | <a href="/demo/refs" data-key="r" aria-keyshortcuts="r" aria-current="page">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 81 | </a> | 81 | </a> | ||
| 82 | </li> | 82 | </li> | ||
| 83 | <li> | 83 | <li> | ||
| 84 | - | <a href="/demo/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 84 | + | <a href="/demo/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 85 | </a> | 85 | </a> | ||
| 86 | </li> | 86 | </li> | ||
| 87 | <li> | 87 | <li> | ||
| 88 | - | <a href="/demo/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 88 | + | <a href="/demo/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 89 | </a> | 89 | </a> | ||
| 90 | </li> | 90 | </li> | ||
| 91 | </ul> | 91 | </ul> | ||
M internal/app/testdata/web/summary.html +6 -6
| @@ -62,27 +62,27 @@ | |||
| 62 | 62 | <nav class="tabs" aria-label="Sections"> | |
| 63 | 63 | <ul> | |
| 64 | 64 | <li> | |
| 65 | - | <a href="/demo" data-key="s" aria-keyshortcuts="s" aria-current="page">Summary<kbd aria-hidden="true">s</kbd> | |
| 65 | + | <a href="/demo" data-key="s" aria-keyshortcuts="s" aria-current="page">Summary <kbd aria-hidden="true">s</kbd> | |
| 66 | 66 | </a> | |
| 67 | 67 | </li> | |
| 68 | 68 | <li> | |
| 69 | - | <a href="/demo/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | |
| 69 | + | <a href="/demo/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 70 | 70 | </a> | |
| 71 | 71 | </li> | |
| 72 | 72 | <li> | |
| 73 | - | <a href="/demo/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | |
| 73 | + | <a href="/demo/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 74 | 74 | </a> | |
| 75 | 75 | </li> | |
| 76 | 76 | <li> | |
| 77 | - | <a href="/demo/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 77 | + | <a href="/demo/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 78 | 78 | </a> | |
| 79 | 79 | </li> | |
| 80 | 80 | <li> | |
| 81 | - | <a href="/demo/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 81 | + | <a href="/demo/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 82 | 82 | </a> | |
| 83 | 83 | </li> | |
| 84 | 84 | <li> | |
| 85 | - | <a href="/demo/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 85 | + | <a href="/demo/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 86 | 86 | </a> | |
| 87 | 87 | </li> | |
| 88 | 88 | </ul> | |
| @@ -62,27 +62,27 @@ | |||||
| 62 | <nav class="tabs" aria-label="Sections"> | 62 | <nav class="tabs" aria-label="Sections"> | ||
| 63 | <ul> | 63 | <ul> | ||
| 64 | <li> | 64 | <li> | ||
| 65 | - | <a href="/demo" data-key="s" aria-keyshortcuts="s" aria-current="page">Summary<kbd aria-hidden="true">s</kbd> | 65 | + | <a href="/demo" data-key="s" aria-keyshortcuts="s" aria-current="page">Summary <kbd aria-hidden="true">s</kbd> |
| 66 | </a> | 66 | </a> | ||
| 67 | </li> | 67 | </li> | ||
| 68 | <li> | 68 | <li> | ||
| 69 | - | <a href="/demo/tree/main" data-key="t" aria-keyshortcuts="t">Files<kbd aria-hidden="true">t</kbd> | 69 | + | <a href="/demo/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> |
| 70 | </a> | 70 | </a> | ||
| 71 | </li> | 71 | </li> | ||
| 72 | <li> | 72 | <li> | ||
| 73 | - | <a href="/demo/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | 73 | + | <a href="/demo/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> |
| 74 | </a> | 74 | </a> | ||
| 75 | </li> | 75 | </li> | ||
| 76 | <li> | 76 | <li> | ||
| 77 | - | <a href="/demo/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 77 | + | <a href="/demo/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 78 | </a> | 78 | </a> | ||
| 79 | </li> | 79 | </li> | ||
| 80 | <li> | 80 | <li> | ||
| 81 | - | <a href="/demo/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 81 | + | <a href="/demo/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 82 | </a> | 82 | </a> | ||
| 83 | </li> | 83 | </li> | ||
| 84 | <li> | 84 | <li> | ||
| 85 | - | <a href="/demo/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 85 | + | <a href="/demo/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 86 | </a> | 86 | </a> | ||
| 87 | </li> | 87 | </li> | ||
| 88 | </ul> | 88 | </ul> | ||
M internal/app/testdata/web/tree-nested.html +6 -6
| @@ -74,27 +74,27 @@ | |||
| 74 | 74 | <nav class="tabs" aria-label="Sections"> | |
| 75 | 75 | <ul> | |
| 76 | 76 | <li> | |
| 77 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 77 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 78 | 78 | </a> | |
| 79 | 79 | </li> | |
| 80 | 80 | <li> | |
| 81 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | |
| 81 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> | |
| 82 | 82 | </a> | |
| 83 | 83 | </li> | |
| 84 | 84 | <li> | |
| 85 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | |
| 85 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 86 | 86 | </a> | |
| 87 | 87 | </li> | |
| 88 | 88 | <li> | |
| 89 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 89 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 90 | 90 | </a> | |
| 91 | 91 | </li> | |
| 92 | 92 | <li> | |
| 93 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 93 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 94 | 94 | </a> | |
| 95 | 95 | </li> | |
| 96 | 96 | <li> | |
| 97 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 97 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 98 | 98 | </a> | |
| 99 | 99 | </li> | |
| 100 | 100 | </ul> | |
| @@ -74,27 +74,27 @@ | |||||
| 74 | <nav class="tabs" aria-label="Sections"> | 74 | <nav class="tabs" aria-label="Sections"> | ||
| 75 | <ul> | 75 | <ul> | ||
| 76 | <li> | 76 | <li> | ||
| 77 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 77 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 78 | </a> | 78 | </a> | ||
| 79 | </li> | 79 | </li> | ||
| 80 | <li> | 80 | <li> | ||
| 81 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | 81 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> |
| 82 | </a> | 82 | </a> | ||
| 83 | </li> | 83 | </li> | ||
| 84 | <li> | 84 | <li> | ||
| 85 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | 85 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> |
| 86 | </a> | 86 | </a> | ||
| 87 | </li> | 87 | </li> | ||
| 88 | <li> | 88 | <li> | ||
| 89 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 89 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 90 | </a> | 90 | </a> | ||
| 91 | </li> | 91 | </li> | ||
| 92 | <li> | 92 | <li> | ||
| 93 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 93 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 94 | </a> | 94 | </a> | ||
| 95 | </li> | 95 | </li> | ||
| 96 | <li> | 96 | <li> | ||
| 97 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 97 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 98 | </a> | 98 | </a> | ||
| 99 | </li> | 99 | </li> | ||
| 100 | </ul> | 100 | </ul> | ||
M internal/app/testdata/web/tree-root.html +6 -6
| @@ -68,27 +68,27 @@ | |||
| 68 | 68 | <nav class="tabs" aria-label="Sections"> | |
| 69 | 69 | <ul> | |
| 70 | 70 | <li> | |
| 71 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 71 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 72 | 72 | </a> | |
| 73 | 73 | </li> | |
| 74 | 74 | <li> | |
| 75 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | |
| 75 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> | |
| 76 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 78 | <li> | |
| 79 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | |
| 79 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 80 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 82 | <li> | |
| 83 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 83 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 84 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 87 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 90 | <li> | |
| 91 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 91 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 92 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 94 | </ul> | |
| @@ -68,27 +68,27 @@ | |||||
| 68 | <nav class="tabs" aria-label="Sections"> | 68 | <nav class="tabs" aria-label="Sections"> | ||
| 69 | <ul> | 69 | <ul> | ||
| 70 | <li> | 70 | <li> | ||
| 71 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 71 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 72 | </a> | 72 | </a> | ||
| 73 | </li> | 73 | </li> | ||
| 74 | <li> | 74 | <li> | ||
| 75 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | 75 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> |
| 76 | </a> | 76 | </a> | ||
| 77 | </li> | 77 | </li> | ||
| 78 | <li> | 78 | <li> | ||
| 79 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | 79 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> |
| 80 | </a> | 80 | </a> | ||
| 81 | </li> | 81 | </li> | ||
| 82 | <li> | 82 | <li> | ||
| 83 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 83 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 84 | </a> | 84 | </a> | ||
| 85 | </li> | 85 | </li> | ||
| 86 | <li> | 86 | <li> | ||
| 87 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 87 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 88 | </a> | 88 | </a> | ||
| 89 | </li> | 89 | </li> | ||
| 90 | <li> | 90 | <li> | ||
| 91 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 91 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 92 | </a> | 92 | </a> | ||
| 93 | </li> | 93 | </li> | ||
| 94 | </ul> | 94 | </ul> | ||
M internal/app/testdata/web/tree-slash-ref.html +6 -6
| @@ -71,27 +71,27 @@ | |||
| 71 | 71 | <nav class="tabs" aria-label="Sections"> | |
| 72 | 72 | <ul> | |
| 73 | 73 | <li> | |
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 75 | 75 | </a> | |
| 76 | 76 | </li> | |
| 77 | 77 | <li> | |
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | |
| 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> | |
| 79 | 79 | </a> | |
| 80 | 80 | </li> | |
| 81 | 81 | <li> | |
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | |
| 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 83 | 83 | </a> | |
| 84 | 84 | </li> | |
| 85 | 85 | <li> | |
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | 87 | </a> | |
| 88 | 88 | </li> | |
| 89 | 89 | <li> | |
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 91 | 91 | </a> | |
| 92 | 92 | </li> | |
| 93 | 93 | <li> | |
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 95 | 95 | </a> | |
| 96 | 96 | </li> | |
| 97 | 97 | </ul> | |
| @@ -71,27 +71,27 @@ | |||||
| 71 | <nav class="tabs" aria-label="Sections"> | 71 | <nav class="tabs" aria-label="Sections"> | ||
| 72 | <ul> | 72 | <ul> | ||
| 73 | <li> | 73 | <li> | ||
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 75 | </a> | 75 | </a> | ||
| 76 | </li> | 76 | </li> | ||
| 77 | <li> | 77 | <li> | ||
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> |
| 79 | </a> | 79 | </a> | ||
| 80 | </li> | 80 | </li> | ||
| 81 | <li> | 81 | <li> | ||
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> |
| 83 | </a> | 83 | </a> | ||
| 84 | </li> | 84 | </li> | ||
| 85 | <li> | 85 | <li> | ||
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 87 | </a> | 87 | </a> | ||
| 88 | </li> | 88 | </li> | ||
| 89 | <li> | 89 | <li> | ||
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 91 | </a> | 91 | </a> | ||
| 92 | </li> | 92 | </li> | ||
| 93 | <li> | 93 | <li> | ||
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 95 | </a> | 95 | </a> | ||
| 96 | </li> | 96 | </li> | ||
| 97 | </ul> | 97 | </ul> | ||
M internal/app/testdata/web/tree-subdir.html +6 -6
| @@ -71,27 +71,27 @@ | |||
| 71 | 71 | <nav class="tabs" aria-label="Sections"> | |
| 72 | 72 | <ul> | |
| 73 | 73 | <li> | |
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | |
| 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 75 | 75 | </a> | |
| 76 | 76 | </li> | |
| 77 | 77 | <li> | |
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | |
| 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> | |
| 79 | 79 | </a> | |
| 80 | 80 | </li> | |
| 81 | 81 | <li> | |
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | |
| 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 83 | 83 | </a> | |
| 84 | 84 | </li> | |
| 85 | 85 | <li> | |
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | |
| 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | 87 | </a> | |
| 88 | 88 | </li> | |
| 89 | 89 | <li> | |
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | |
| 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 91 | 91 | </a> | |
| 92 | 92 | </li> | |
| 93 | 93 | <li> | |
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | |
| 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 95 | 95 | </a> | |
| 96 | 96 | </li> | |
| 97 | 97 | </ul> | |
| @@ -71,27 +71,27 @@ | |||||
| 71 | <nav class="tabs" aria-label="Sections"> | 71 | <nav class="tabs" aria-label="Sections"> | ||
| 72 | <ul> | 72 | <ul> | ||
| 73 | <li> | 73 | <li> | ||
| 74 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary<kbd aria-hidden="true">s</kbd> | 74 | + | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> |
| 75 | </a> | 75 | </a> | ||
| 76 | </li> | 76 | </li> | ||
| 77 | <li> | 77 | <li> | ||
| 78 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files<kbd aria-hidden="true">t</kbd> | 78 | + | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> |
| 79 | </a> | 79 | </a> | ||
| 80 | </li> | 80 | </li> | ||
| 81 | <li> | 81 | <li> | ||
| 82 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits<kbd aria-hidden="true">c</kbd> | 82 | + | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> |
| 83 | </a> | 83 | </a> | ||
| 84 | </li> | 84 | </li> | ||
| 85 | <li> | 85 | <li> | ||
| 86 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags<kbd aria-hidden="true">r</kbd> | 86 | + | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> |
| 87 | </a> | 87 | </a> | ||
| 88 | </li> | 88 | </li> | ||
| 89 | <li> | 89 | <li> | ||
| 90 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests<kbd aria-hidden="true">m</kbd> | 90 | + | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> |
| 91 | </a> | 91 | </a> | ||
| 92 | </li> | 92 | </li> | ||
| 93 | <li> | 93 | <li> | ||
| 94 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions<kbd aria-hidden="true">a</kbd> | 94 | + | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> |
| 95 | </a> | 95 | </a> | ||
| 96 | </li> | 96 | </li> | ||
| 97 | </ul> | 97 | </ul> | ||
M internal/app/web_mr_test.go +51 -2
| @@ -130,7 +130,7 @@ func TestWebMergeRequestPagesRender(t *testing.T) { | |||
| 130 | 130 | require.Equal(t, 200, mr.status, mr.body) | |
| 131 | 131 | assert.Equal(t, "no-cache", mr.hdr.Get("Cache-Control")) | |
| 132 | 132 | assert.Equal(t, []string{"Accept-Encoding"}, mr.hdr.Values("Vary")) | |
| 133 | - | assert.Contains(t, mr.body, "<h1><span class=\"mute\">!1</span> Add <b>search</b></h1>") | |
| 133 | + | assert.Contains(t, mr.body, "<h1><span class=\"mute\">#1</span> Add <b>search</b></h1>") | |
| 134 | 134 | assert.NotContains(t, mr.body, "<b>search</b>") | |
| 135 | 135 | assert.Contains(t, mr.body, "feat/search") | |
| 136 | 136 | assert.Contains(t, mr.body, "package search</td>") | |
| @@ -218,6 +218,7 @@ func TestWebMergeRequestWritesNeedSessionAndOrigin(t *testing.T) { | |||
| 218 | 218 | "/proj/merge-requests/1/merge": "mode=fast-forward&head_id=" + head, | |
| 219 | 219 | "/proj/merge-requests/1/close": "", | |
| 220 | 220 | "/proj/merge-requests/1/read": "head_id=" + head, | |
| 221 | + | "/proj/merge-requests/1/ready": "", | |
| 221 | 222 | } | |
| 222 | 223 | for path, body := range paths { | |
| 223 | 224 | noOrigin := b.do("POST", path, as(token), body) | |
| @@ -430,6 +431,54 @@ func TestWebMergeRequestSquashCloseAndList(t *testing.T) { | |||
| 430 | 431 | assert.Contains(t, stale.body, "has moved on") | |
| 431 | 432 | } | |
| 432 | 433 | ||
| 434 | + | // A "WIP:" request is a draft: the list and the page say so, the box offers | |
| 435 | + | // "Ready for review" instead of the merge form and the terminal lines, the | |
| 436 | + | // merge is refused; ready drops the prefix and the merge form comes back. The | |
| 437 | + | // tab counts the open requests. | |
| 438 | + | func TestWebMergeRequestDraft(t *testing.T) { | |
| 439 | + | r := newMRRepo(t, domain.VisibilityPublic) | |
| 440 | + | b := browser{r.harness} | |
| 441 | + | token := r.signIn(t) | |
| 442 | + | head := r.headOf("feat/search") | |
| 443 | + | r.cli(t, adminToken, "mr", "create", "proj", "--source", "feat/search", "--title", "WIP: search").ok(t) | |
| 444 | + | ||
| 445 | + | page := b.get("/proj/merge-requests/1", as(token)).body | |
| 446 | + | assert.Contains(t, page, `data-state="draft"`) | |
| 447 | + | assert.Contains(t, page, `action="/proj/merge-requests/1/ready"`) | |
| 448 | + | assert.NotContains(t, page, `action="/proj/merge-requests/1/merge"`) | |
| 449 | + | assert.NotContains(t, page, "ermite mr merge", "no terminal line for a draft") | |
| 450 | + | assert.Contains(t, page, `<span class="count">1<span class="sr"> open</span></span>`, "the tab counts the open requests") | |
| 451 | + | assert.Contains(t, b.get("/proj/merge-requests", nil).body, `data-state="draft"`) | |
| 452 | + | ||
| 453 | + | res := b.form("/proj/merge-requests/1/merge", token, url.Values{"mode": {"fast-forward"}, "head_id": {head}}) | |
| 454 | + | assert.Equal(t, http.StatusConflict, res.status, "a draft is not merged") | |
| 455 | + | ||
| 456 | + | res = b.form("/proj/merge-requests/1/ready", token, nil) | |
| 457 | + | require.Equal(t, http.StatusSeeOther, res.status, res.body) | |
| 458 | + | page = b.get("/proj/merge-requests/1", as(token)).body | |
| 459 | + | assert.Contains(t, page, "> search</h1>", "the prefix is gone") | |
| 460 | + | assert.Contains(t, page, `data-state="open"`) | |
| 461 | + | assert.Contains(t, page, `action="/proj/merge-requests/1/merge"`) | |
| 462 | + | assert.Contains(t, page, `<details class="d-or">`, "the terminal lines fold away") | |
| 463 | + | assert.Equal(t, http.StatusConflict, b.form("/proj/merge-requests/1/ready", token, nil).status, "not a draft any more") | |
| 464 | + | } | |
| 465 | + | ||
| 466 | + | // A request opened with a token shows the owner's name, not the label of the | |
| 467 | + | // token, which names a device. | |
| 468 | + | func TestWebMergeRequestAuthorIsTheOwner(t *testing.T) { | |
| 469 | + | r := newMRRepo(t, domain.VisibilityPublic) | |
| 470 | + | out, errOut, code := r.bin(t, adminToken, "token", "create", "laptop", "--preset", "cli") | |
| 471 | + | require.Equal(t, 0, code, errOut) | |
| 472 | + | r.cli(t, strings.TrimSpace(out), "mr", "create", "proj", "--source", "feat/search", "--title", "search").ok(t) | |
| 473 | + | ||
| 474 | + | b := browser{r.harness} | |
| 475 | + | for _, path := range []string{"/proj/merge-requests/1", "/proj/merge-requests"} { | |
| 476 | + | body := b.get(path, nil).body | |
| 477 | + | assert.NotContains(t, body, "laptop", path) | |
| 478 | + | } | |
| 479 | + | assert.Contains(t, b.get("/proj/merge-requests", nil).body, `<td class="c-who dim">owner</td>`) | |
| 480 | + | } | |
| 481 | + | ||
| 433 | 482 | // The box offers no deletion for the default branch; a fast-forward with the | |
| 434 | 483 | // box checked deletes the source. | |
| 435 | 484 | func TestWebMergeDeleteBranchBox(t *testing.T) { | |
| @@ -543,7 +592,7 @@ func TestWebMergeRequestEvents(t *testing.T) { | |||
| 543 | 592 | drained = true | |
| 544 | 593 | } | |
| 545 | 594 | } | |
| 546 | - | assert.Contains(t, r.get1("/proj/merge-requests/1"), `hx-sse:connect="/-/events?topic=repo%3Aproj&view=mr&n=1&ref=feat%2Fsearch"`) | |
| 595 | + | assert.Contains(t, r.get1("/proj/merge-requests/1"), `hx-sse:connect="/-/events?topic=repo%3Aproj&view=mr&n=1&ref=feat%2Fsearch&head=`+r.headOf("feat/search")+`"`) | |
| 547 | 596 | } | |
| 548 | 597 | ||
| 549 | 598 | func (r *mrRepo) get1(path string) string { | |
| @@ -130,7 +130,7 @@ func TestWebMergeRequestPagesRender(t *testing.T) { | |||||
| 130 | require.Equal(t, 200, mr.status, mr.body) | 130 | require.Equal(t, 200, mr.status, mr.body) | ||
| 131 | assert.Equal(t, "no-cache", mr.hdr.Get("Cache-Control")) | 131 | assert.Equal(t, "no-cache", mr.hdr.Get("Cache-Control")) | ||
| 132 | assert.Equal(t, []string{"Accept-Encoding"}, mr.hdr.Values("Vary")) | 132 | assert.Equal(t, []string{"Accept-Encoding"}, mr.hdr.Values("Vary")) | ||
| 133 | - | assert.Contains(t, mr.body, "<h1><span class=\"mute\">!1</span> Add <b>search</b></h1>") | 133 | + | assert.Contains(t, mr.body, "<h1><span class=\"mute\">#1</span> Add <b>search</b></h1>") |
| 134 | assert.NotContains(t, mr.body, "<b>search</b>") | 134 | assert.NotContains(t, mr.body, "<b>search</b>") | ||
| 135 | assert.Contains(t, mr.body, "feat/search") | 135 | assert.Contains(t, mr.body, "feat/search") | ||
| 136 | assert.Contains(t, mr.body, "package search</td>") | 136 | assert.Contains(t, mr.body, "package search</td>") | ||
| @@ -218,6 +218,7 @@ func TestWebMergeRequestWritesNeedSessionAndOrigin(t *testing.T) { | |||||
| 218 | "/proj/merge-requests/1/merge": "mode=fast-forward&head_id=" + head, | 218 | "/proj/merge-requests/1/merge": "mode=fast-forward&head_id=" + head, | ||
| 219 | "/proj/merge-requests/1/close": "", | 219 | "/proj/merge-requests/1/close": "", | ||
| 220 | "/proj/merge-requests/1/read": "head_id=" + head, | 220 | "/proj/merge-requests/1/read": "head_id=" + head, | ||
| 221 | + | "/proj/merge-requests/1/ready": "", | |||
| 221 | } | 222 | } | ||
| 222 | for path, body := range paths { | 223 | for path, body := range paths { | ||
| 223 | noOrigin := b.do("POST", path, as(token), body) | 224 | noOrigin := b.do("POST", path, as(token), body) | ||
| @@ -430,6 +431,54 @@ func TestWebMergeRequestSquashCloseAndList(t *testing.T) { | |||||
| 430 | assert.Contains(t, stale.body, "has moved on") | 431 | assert.Contains(t, stale.body, "has moved on") | ||
| 431 | } | 432 | } | ||
| 432 | 433 | ||||
| 434 | + | // A "WIP:" request is a draft: the list and the page say so, the box offers | |||
| 435 | + | // "Ready for review" instead of the merge form and the terminal lines, the | |||
| 436 | + | // merge is refused; ready drops the prefix and the merge form comes back. The | |||
| 437 | + | // tab counts the open requests. | |||
| 438 | + | func TestWebMergeRequestDraft(t *testing.T) { | |||
| 439 | + | r := newMRRepo(t, domain.VisibilityPublic) | |||
| 440 | + | b := browser{r.harness} | |||
| 441 | + | token := r.signIn(t) | |||
| 442 | + | head := r.headOf("feat/search") | |||
| 443 | + | r.cli(t, adminToken, "mr", "create", "proj", "--source", "feat/search", "--title", "WIP: search").ok(t) | |||
| 444 | + | ||||
| 445 | + | page := b.get("/proj/merge-requests/1", as(token)).body | |||
| 446 | + | assert.Contains(t, page, `data-state="draft"`) | |||
| 447 | + | assert.Contains(t, page, `action="/proj/merge-requests/1/ready"`) | |||
| 448 | + | assert.NotContains(t, page, `action="/proj/merge-requests/1/merge"`) | |||
| 449 | + | assert.NotContains(t, page, "ermite mr merge", "no terminal line for a draft") | |||
| 450 | + | assert.Contains(t, page, `<span class="count">1<span class="sr"> open</span></span>`, "the tab counts the open requests") | |||
| 451 | + | assert.Contains(t, b.get("/proj/merge-requests", nil).body, `data-state="draft"`) | |||
| 452 | + | ||||
| 453 | + | res := b.form("/proj/merge-requests/1/merge", token, url.Values{"mode": {"fast-forward"}, "head_id": {head}}) | |||
| 454 | + | assert.Equal(t, http.StatusConflict, res.status, "a draft is not merged") | |||
| 455 | + | ||||
| 456 | + | res = b.form("/proj/merge-requests/1/ready", token, nil) | |||
| 457 | + | require.Equal(t, http.StatusSeeOther, res.status, res.body) | |||
| 458 | + | page = b.get("/proj/merge-requests/1", as(token)).body | |||
| 459 | + | assert.Contains(t, page, "> search</h1>", "the prefix is gone") | |||
| 460 | + | assert.Contains(t, page, `data-state="open"`) | |||
| 461 | + | assert.Contains(t, page, `action="/proj/merge-requests/1/merge"`) | |||
| 462 | + | assert.Contains(t, page, `<details class="d-or">`, "the terminal lines fold away") | |||
| 463 | + | assert.Equal(t, http.StatusConflict, b.form("/proj/merge-requests/1/ready", token, nil).status, "not a draft any more") | |||
| 464 | + | } | |||
| 465 | + | ||||
| 466 | + | // A request opened with a token shows the owner's name, not the label of the | |||
| 467 | + | // token, which names a device. | |||
| 468 | + | func TestWebMergeRequestAuthorIsTheOwner(t *testing.T) { | |||
| 469 | + | r := newMRRepo(t, domain.VisibilityPublic) | |||
| 470 | + | out, errOut, code := r.bin(t, adminToken, "token", "create", "laptop", "--preset", "cli") | |||
| 471 | + | require.Equal(t, 0, code, errOut) | |||
| 472 | + | r.cli(t, strings.TrimSpace(out), "mr", "create", "proj", "--source", "feat/search", "--title", "search").ok(t) | |||
| 473 | + | ||||
| 474 | + | b := browser{r.harness} | |||
| 475 | + | for _, path := range []string{"/proj/merge-requests/1", "/proj/merge-requests"} { | |||
| 476 | + | body := b.get(path, nil).body | |||
| 477 | + | assert.NotContains(t, body, "laptop", path) | |||
| 478 | + | } | |||
| 479 | + | assert.Contains(t, b.get("/proj/merge-requests", nil).body, `<td class="c-who dim">owner</td>`) | |||
| 480 | + | } | |||
| 481 | + | ||||
| 433 | // The box offers no deletion for the default branch; a fast-forward with the | 482 | // The box offers no deletion for the default branch; a fast-forward with the | ||
| 434 | // box checked deletes the source. | 483 | // box checked deletes the source. | ||
| 435 | func TestWebMergeDeleteBranchBox(t *testing.T) { | 484 | func TestWebMergeDeleteBranchBox(t *testing.T) { | ||
| @@ -543,7 +592,7 @@ func TestWebMergeRequestEvents(t *testing.T) { | |||||
| 543 | drained = true | 592 | drained = true | ||
| 544 | } | 593 | } | ||
| 545 | } | 594 | } | ||
| 546 | - | assert.Contains(t, r.get1("/proj/merge-requests/1"), `hx-sse:connect="/-/events?topic=repo%3Aproj&view=mr&n=1&ref=feat%2Fsearch"`) | 595 | + | assert.Contains(t, r.get1("/proj/merge-requests/1"), `hx-sse:connect="/-/events?topic=repo%3Aproj&view=mr&n=1&ref=feat%2Fsearch&head=`+r.headOf("feat/search")+`"`) |
| 547 | } | 596 | } | ||
| 548 | 597 | ||||
| 549 | func (r *mrRepo) get1(path string) string { | 598 | func (r *mrRepo) get1(path string) string { | ||
M internal/domain/event.go +12 -0
| @@ -148,6 +148,18 @@ type MergeRequestClosed struct { | |||
| 148 | 148 | // EventKind implements Event. | |
| 149 | 149 | func (MergeRequestClosed) EventKind() string { return "mr.closed" } | |
| 150 | 150 | ||
| 151 | + | // MergeRequestReady is published when a draft merge request was marked ready | |
| 152 | + | // for review: its title lost the draft prefix. | |
| 153 | + | type MergeRequestReady struct { | |
| 154 | + | MergeRequest MergeRequest | |
| 155 | + | At time.Time | |
| 156 | + | Visibility Visibility | |
| 157 | + | CreatedAt time.Time | |
| 158 | + | } | |
| 159 | + | ||
| 160 | + | // EventKind implements Event. | |
| 161 | + | func (MergeRequestReady) EventKind() string { return "mr.ready" } | |
| 162 | + | ||
| 151 | 163 | // CommentAdded is published when a comment was added to a merge request. | |
| 152 | 164 | type CommentAdded struct { | |
| 153 | 165 | Repo RepoName | |
| @@ -148,6 +148,18 @@ type MergeRequestClosed struct { | |||||
| 148 | // EventKind implements Event. | 148 | // EventKind implements Event. | ||
| 149 | func (MergeRequestClosed) EventKind() string { return "mr.closed" } | 149 | func (MergeRequestClosed) EventKind() string { return "mr.closed" } | ||
| 150 | 150 | ||||
| 151 | + | // MergeRequestReady is published when a draft merge request was marked ready | |||
| 152 | + | // for review: its title lost the draft prefix. | |||
| 153 | + | type MergeRequestReady struct { | |||
| 154 | + | MergeRequest MergeRequest | |||
| 155 | + | At time.Time | |||
| 156 | + | Visibility Visibility | |||
| 157 | + | CreatedAt time.Time | |||
| 158 | + | } | |||
| 159 | + | ||||
| 160 | + | // EventKind implements Event. | |||
| 161 | + | func (MergeRequestReady) EventKind() string { return "mr.ready" } | |||
| 162 | + | ||||
| 151 | // CommentAdded is published when a comment was added to a merge request. | 163 | // CommentAdded is published when a comment was added to a merge request. | ||
| 152 | type CommentAdded struct { | 164 | type CommentAdded struct { | ||
| 153 | Repo RepoName | 165 | Repo RepoName | ||
M internal/domain/mergerequest.go +44 -0
| @@ -137,6 +137,50 @@ type MergeRequest struct { | |||
| 137 | 137 | Version uint64 | |
| 138 | 138 | } | |
| 139 | 139 | ||
| 140 | + | // draftPrefixes mark a draft at the start of a title, compared without regard | |
| 141 | + | // to case, as Forgejo and Gitea do. | |
| 142 | + | var draftPrefixes = []string{"wip:", "[wip]"} | |
| 143 | + | ||
| 144 | + | // IsDraft reports whether the title marks the request as a draft: it starts | |
| 145 | + | // with "WIP:" or "[WIP]". A draft cannot be merged. | |
| 146 | + | func (mr MergeRequest) IsDraft() bool { | |
| 147 | + | _, ok := draftRest(mr.Title.String()) | |
| 148 | + | return ok | |
| 149 | + | } | |
| 150 | + | ||
| 151 | + | // ReadyTitle is the title without its draft prefixes (all of them, so that | |
| 152 | + | // "WIP: [WIP] x" is no longer a draft), and false when the title has none or | |
| 153 | + | // nothing would be left of it. | |
| 154 | + | func ReadyTitle(t MRTitle) (MRTitle, bool) { | |
| 155 | + | rest, ok := draftRest(t.String()) | |
| 156 | + | if !ok { | |
| 157 | + | return MRTitle{}, false | |
| 158 | + | } | |
| 159 | + | for { | |
| 160 | + | next, more := draftRest(rest) | |
| 161 | + | if !more { | |
| 162 | + | break | |
| 163 | + | } | |
| 164 | + | rest = next | |
| 165 | + | } | |
| 166 | + | ready, err := ParseMRTitle(rest) | |
| 167 | + | if err != nil { | |
| 168 | + | return MRTitle{}, false | |
| 169 | + | } | |
| 170 | + | return ready, true | |
| 171 | + | } | |
| 172 | + | ||
| 173 | + | // draftRest is the title after its draft prefix and the spaces that follow. | |
| 174 | + | func draftRest(title string) (string, bool) { | |
| 175 | + | t := strings.TrimLeft(title, " ") | |
| 176 | + | for _, p := range draftPrefixes { | |
| 177 | + | if len(t) >= len(p) && strings.EqualFold(t[:len(p)], p) { | |
| 178 | + | return strings.TrimLeft(t[len(p):], " "), true | |
| 179 | + | } | |
| 180 | + | } | |
| 181 | + | return "", false | |
| 182 | + | } | |
| 183 | + | ||
| 140 | 184 | // IsOpen reports whether the request awaits review. | |
| 141 | 185 | func (m MergeRequest) IsOpen() bool { return m.State == MRStateOpen } | |
| 142 | 186 | ||
| @@ -137,6 +137,50 @@ type MergeRequest struct { | |||||
| 137 | Version uint64 | 137 | Version uint64 | ||
| 138 | } | 138 | } | ||
| 139 | 139 | ||||
| 140 | + | // draftPrefixes mark a draft at the start of a title, compared without regard | |||
| 141 | + | // to case, as Forgejo and Gitea do. | |||
| 142 | + | var draftPrefixes = []string{"wip:", "[wip]"} | |||
| 143 | + | ||||
| 144 | + | // IsDraft reports whether the title marks the request as a draft: it starts | |||
| 145 | + | // with "WIP:" or "[WIP]". A draft cannot be merged. | |||
| 146 | + | func (mr MergeRequest) IsDraft() bool { | |||
| 147 | + | _, ok := draftRest(mr.Title.String()) | |||
| 148 | + | return ok | |||
| 149 | + | } | |||
| 150 | + | ||||
| 151 | + | // ReadyTitle is the title without its draft prefixes (all of them, so that | |||
| 152 | + | // "WIP: [WIP] x" is no longer a draft), and false when the title has none or | |||
| 153 | + | // nothing would be left of it. | |||
| 154 | + | func ReadyTitle(t MRTitle) (MRTitle, bool) { | |||
| 155 | + | rest, ok := draftRest(t.String()) | |||
| 156 | + | if !ok { | |||
| 157 | + | return MRTitle{}, false | |||
| 158 | + | } | |||
| 159 | + | for { | |||
| 160 | + | next, more := draftRest(rest) | |||
| 161 | + | if !more { | |||
| 162 | + | break | |||
| 163 | + | } | |||
| 164 | + | rest = next | |||
| 165 | + | } | |||
| 166 | + | ready, err := ParseMRTitle(rest) | |||
| 167 | + | if err != nil { | |||
| 168 | + | return MRTitle{}, false | |||
| 169 | + | } | |||
| 170 | + | return ready, true | |||
| 171 | + | } | |||
| 172 | + | ||||
| 173 | + | // draftRest is the title after its draft prefix and the spaces that follow. | |||
| 174 | + | func draftRest(title string) (string, bool) { | |||
| 175 | + | t := strings.TrimLeft(title, " ") | |||
| 176 | + | for _, p := range draftPrefixes { | |||
| 177 | + | if len(t) >= len(p) && strings.EqualFold(t[:len(p)], p) { | |||
| 178 | + | return strings.TrimLeft(t[len(p):], " "), true | |||
| 179 | + | } | |||
| 180 | + | } | |||
| 181 | + | return "", false | |||
| 182 | + | } | |||
| 183 | + | ||||
| 140 | // IsOpen reports whether the request awaits review. | 184 | // IsOpen reports whether the request awaits review. | ||
| 141 | func (m MergeRequest) IsOpen() bool { return m.State == MRStateOpen } | 185 | func (m MergeRequest) IsOpen() bool { return m.State == MRStateOpen } | ||
| 142 | 186 | ||||
M internal/domain/mergerequest_test.go +24 -0
| @@ -211,3 +211,27 @@ func TestParseBranchName(t *testing.T) { | |||
| 211 | 211 | assert.ErrorIs(t, err, domain.ErrInvalidRevision, "%q", bad) | |
| 212 | 212 | } | |
| 213 | 213 | } | |
| 214 | + | ||
| 215 | + | // A title that starts with "WIP:" or "[WIP]", in any case, is a draft; ready | |
| 216 | + | // drops the prefix and the spaces after it. | |
| 217 | + | func TestMergeRequestDraft(t *testing.T) { | |
| 218 | + | for _, tc := range []struct { | |
| 219 | + | title, ready string | |
| 220 | + | draft bool | |
| 221 | + | }{ | |
| 222 | + | {"WIP: feat: drag and drop", "feat: drag and drop", true}, | |
| 223 | + | {"wip:fix", "fix", true}, | |
| 224 | + | {" [WIP] tidy", "tidy", true}, | |
| 225 | + | {"WIP: [wip] both", "both", true}, // every prefix goes | |
| 226 | + | {"feat: wip: later", "", false}, | |
| 227 | + | {"WIPE the cache", "", false}, | |
| 228 | + | {"WIP:", "", true}, // a draft, but nothing would be left | |
| 229 | + | } { | |
| 230 | + | title, err := domain.ParseMRTitle(tc.title) | |
| 231 | + | require.NoError(t, err) | |
| 232 | + | assert.Equal(t, tc.draft, domain.MergeRequest{Title: title}.IsDraft(), tc.title) | |
| 233 | + | ready, ok := domain.ReadyTitle(title) | |
| 234 | + | assert.Equal(t, tc.ready != "", ok, tc.title) | |
| 235 | + | assert.Equal(t, tc.ready, ready.String(), tc.title) | |
| 236 | + | } | |
| 237 | + | } | |
| @@ -211,3 +211,27 @@ func TestParseBranchName(t *testing.T) { | |||||
| 211 | assert.ErrorIs(t, err, domain.ErrInvalidRevision, "%q", bad) | 211 | assert.ErrorIs(t, err, domain.ErrInvalidRevision, "%q", bad) | ||
| 212 | } | 212 | } | ||
| 213 | } | 213 | } | ||
| 214 | + | ||||
| 215 | + | // A title that starts with "WIP:" or "[WIP]", in any case, is a draft; ready | |||
| 216 | + | // drops the prefix and the spaces after it. | |||
| 217 | + | func TestMergeRequestDraft(t *testing.T) { | |||
| 218 | + | for _, tc := range []struct { | |||
| 219 | + | title, ready string | |||
| 220 | + | draft bool | |||
| 221 | + | }{ | |||
| 222 | + | {"WIP: feat: drag and drop", "feat: drag and drop", true}, | |||
| 223 | + | {"wip:fix", "fix", true}, | |||
| 224 | + | {" [WIP] tidy", "tidy", true}, | |||
| 225 | + | {"WIP: [wip] both", "both", true}, // every prefix goes | |||
| 226 | + | {"feat: wip: later", "", false}, | |||
| 227 | + | {"WIPE the cache", "", false}, | |||
| 228 | + | {"WIP:", "", true}, // a draft, but nothing would be left | |||
| 229 | + | } { | |||
| 230 | + | title, err := domain.ParseMRTitle(tc.title) | |||
| 231 | + | require.NoError(t, err) | |||
| 232 | + | assert.Equal(t, tc.draft, domain.MergeRequest{Title: title}.IsDraft(), tc.title) | |||
| 233 | + | ready, ok := domain.ReadyTitle(title) | |||
| 234 | + | assert.Equal(t, tc.ready != "", ok, tc.title) | |||
| 235 | + | assert.Equal(t, tc.ready, ready.String(), tc.title) | |||
| 236 | + | } | |||
| 237 | + | } | |||
M internal/ports/dto.go +1 -1
| @@ -141,7 +141,7 @@ type WatchEvent struct { | |||
| 141 | 141 | Gap bool | |
| 142 | 142 | // Event is a domain.RepoCreated, RepoUpdated, RepoDeleted, PushEvent, | |
| 143 | 143 | // MergeRequestOpened, MergeRequestMerged, MergeRequestClosed, | |
| 144 | - | // CommentAdded, RunUpdated or JobUpdated. | |
| 144 | + | // MergeRequestReady, CommentAdded, RunUpdated or JobUpdated. | |
| 145 | 145 | Event domain.Event | |
| 146 | 146 | // Repo is the state of the repository at delivery, for RepoCreated and | |
| 147 | 147 | // RepoUpdated. | |
| @@ -141,7 +141,7 @@ type WatchEvent struct { | |||||
| 141 | Gap bool | 141 | Gap bool | ||
| 142 | // Event is a domain.RepoCreated, RepoUpdated, RepoDeleted, PushEvent, | 142 | // Event is a domain.RepoCreated, RepoUpdated, RepoDeleted, PushEvent, | ||
| 143 | // MergeRequestOpened, MergeRequestMerged, MergeRequestClosed, | 143 | // MergeRequestOpened, MergeRequestMerged, MergeRequestClosed, | ||
| 144 | - | // CommentAdded, RunUpdated or JobUpdated. | 144 | + | // MergeRequestReady, CommentAdded, RunUpdated or JobUpdated. |
| 145 | Event domain.Event | 145 | Event domain.Event | ||
| 146 | // Repo is the state of the repository at delivery, for RepoCreated and | 146 | // Repo is the state of the repository at delivery, for RepoCreated and | ||
| 147 | // RepoUpdated. | 147 | // RepoUpdated. | ||
M internal/ports/mocks/MergeRequestService.go +150 -0
| @@ -210,6 +210,78 @@ func (_c *MergeRequestService_Close_Call) RunAndReturn(run func(ctx context.Cont | |||
| 210 | 210 | return _c | |
| 211 | 211 | } | |
| 212 | 212 | ||
| 213 | + | // CountOpen provides a mock function for the type MergeRequestService | |
| 214 | + | func (_mock *MergeRequestService) CountOpen(ctx context.Context, p domain.Principal, name domain.RepoName) (int, error) { | |
| 215 | + | ret := _mock.Called(ctx, p, name) | |
| 216 | + | ||
| 217 | + | if len(ret) == 0 { | |
| 218 | + | panic("no return value specified for CountOpen") | |
| 219 | + | } | |
| 220 | + | ||
| 221 | + | var r0 int | |
| 222 | + | var r1 error | |
| 223 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName) (int, error)); ok { | |
| 224 | + | return returnFunc(ctx, p, name) | |
| 225 | + | } | |
| 226 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName) int); ok { | |
| 227 | + | r0 = returnFunc(ctx, p, name) | |
| 228 | + | } else { | |
| 229 | + | r0 = ret.Get(0).(int) | |
| 230 | + | } | |
| 231 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName) error); ok { | |
| 232 | + | r1 = returnFunc(ctx, p, name) | |
| 233 | + | } else { | |
| 234 | + | r1 = ret.Error(1) | |
| 235 | + | } | |
| 236 | + | return r0, r1 | |
| 237 | + | } | |
| 238 | + | ||
| 239 | + | // MergeRequestService_CountOpen_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CountOpen' | |
| 240 | + | type MergeRequestService_CountOpen_Call struct { | |
| 241 | + | *mock.Call | |
| 242 | + | } | |
| 243 | + | ||
| 244 | + | // CountOpen is a helper method to define mock.On call | |
| 245 | + | // - ctx context.Context | |
| 246 | + | // - p domain.Principal | |
| 247 | + | // - name domain.RepoName | |
| 248 | + | func (_e *MergeRequestService_Expecter) CountOpen(ctx any, p any, name any) *MergeRequestService_CountOpen_Call { | |
| 249 | + | return &MergeRequestService_CountOpen_Call{Call: _e.mock.On("CountOpen", ctx, p, name)} | |
| 250 | + | } | |
| 251 | + | ||
| 252 | + | func (_c *MergeRequestService_CountOpen_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName)) *MergeRequestService_CountOpen_Call { | |
| 253 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 254 | + | var arg0 context.Context | |
| 255 | + | if args[0] != nil { | |
| 256 | + | arg0 = args[0].(context.Context) | |
| 257 | + | } | |
| 258 | + | var arg1 domain.Principal | |
| 259 | + | if args[1] != nil { | |
| 260 | + | arg1 = args[1].(domain.Principal) | |
| 261 | + | } | |
| 262 | + | var arg2 domain.RepoName | |
| 263 | + | if args[2] != nil { | |
| 264 | + | arg2 = args[2].(domain.RepoName) | |
| 265 | + | } | |
| 266 | + | run( | |
| 267 | + | arg0, | |
| 268 | + | arg1, | |
| 269 | + | arg2, | |
| 270 | + | ) | |
| 271 | + | }) | |
| 272 | + | return _c | |
| 273 | + | } | |
| 274 | + | ||
| 275 | + | func (_c *MergeRequestService_CountOpen_Call) Return(n int, err error) *MergeRequestService_CountOpen_Call { | |
| 276 | + | _c.Call.Return(n, err) | |
| 277 | + | return _c | |
| 278 | + | } | |
| 279 | + | ||
| 280 | + | func (_c *MergeRequestService_CountOpen_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName) (int, error)) *MergeRequestService_CountOpen_Call { | |
| 281 | + | _c.Call.Return(run) | |
| 282 | + | return _c | |
| 283 | + | } | |
| 284 | + | ||
| 213 | 285 | // Create provides a mock function for the type MergeRequestService | |
| 214 | 286 | func (_mock *MergeRequestService) Create(ctx context.Context, p domain.Principal, name domain.RepoName, params ports.CreateMergeRequestParams) (domain.MergeRequest, error) { | |
| 215 | 287 | ret := _mock.Called(ctx, p, name, params) | |
| @@ -534,6 +606,84 @@ func (_c *MergeRequestService_MarkRead_Call) RunAndReturn(run func(ctx context.C | |||
| 534 | 606 | return _c | |
| 535 | 607 | } | |
| 536 | 608 | ||
| 609 | + | // MarkReady provides a mock function for the type MergeRequestService | |
| 610 | + | func (_mock *MergeRequestService) MarkReady(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error) { | |
| 611 | + | ret := _mock.Called(ctx, p, name, number) | |
| 612 | + | ||
| 613 | + | if len(ret) == 0 { | |
| 614 | + | panic("no return value specified for MarkReady") | |
| 615 | + | } | |
| 616 | + | ||
| 617 | + | var r0 domain.MergeRequest | |
| 618 | + | var r1 error | |
| 619 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64) (domain.MergeRequest, error)); ok { | |
| 620 | + | return returnFunc(ctx, p, name, number) | |
| 621 | + | } | |
| 622 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64) domain.MergeRequest); ok { | |
| 623 | + | r0 = returnFunc(ctx, p, name, number) | |
| 624 | + | } else { | |
| 625 | + | r0 = ret.Get(0).(domain.MergeRequest) | |
| 626 | + | } | |
| 627 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, int64) error); ok { | |
| 628 | + | r1 = returnFunc(ctx, p, name, number) | |
| 629 | + | } else { | |
| 630 | + | r1 = ret.Error(1) | |
| 631 | + | } | |
| 632 | + | return r0, r1 | |
| 633 | + | } | |
| 634 | + | ||
| 635 | + | // MergeRequestService_MarkReady_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarkReady' | |
| 636 | + | type MergeRequestService_MarkReady_Call struct { | |
| 637 | + | *mock.Call | |
| 638 | + | } | |
| 639 | + | ||
| 640 | + | // MarkReady is a helper method to define mock.On call | |
| 641 | + | // - ctx context.Context | |
| 642 | + | // - p domain.Principal | |
| 643 | + | // - name domain.RepoName | |
| 644 | + | // - number int64 | |
| 645 | + | func (_e *MergeRequestService_Expecter) MarkReady(ctx any, p any, name any, number any) *MergeRequestService_MarkReady_Call { | |
| 646 | + | return &MergeRequestService_MarkReady_Call{Call: _e.mock.On("MarkReady", ctx, p, name, number)} | |
| 647 | + | } | |
| 648 | + | ||
| 649 | + | func (_c *MergeRequestService_MarkReady_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64)) *MergeRequestService_MarkReady_Call { | |
| 650 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 651 | + | var arg0 context.Context | |
| 652 | + | if args[0] != nil { | |
| 653 | + | arg0 = args[0].(context.Context) | |
| 654 | + | } | |
| 655 | + | var arg1 domain.Principal | |
| 656 | + | if args[1] != nil { | |
| 657 | + | arg1 = args[1].(domain.Principal) | |
| 658 | + | } | |
| 659 | + | var arg2 domain.RepoName | |
| 660 | + | if args[2] != nil { | |
| 661 | + | arg2 = args[2].(domain.RepoName) | |
| 662 | + | } | |
| 663 | + | var arg3 int64 | |
| 664 | + | if args[3] != nil { | |
| 665 | + | arg3 = args[3].(int64) | |
| 666 | + | } | |
| 667 | + | run( | |
| 668 | + | arg0, | |
| 669 | + | arg1, | |
| 670 | + | arg2, | |
| 671 | + | arg3, | |
| 672 | + | ) | |
| 673 | + | }) | |
| 674 | + | return _c | |
| 675 | + | } | |
| 676 | + | ||
| 677 | + | func (_c *MergeRequestService_MarkReady_Call) Return(mergeRequest domain.MergeRequest, err error) *MergeRequestService_MarkReady_Call { | |
| 678 | + | _c.Call.Return(mergeRequest, err) | |
| 679 | + | return _c | |
| 680 | + | } | |
| 681 | + | ||
| 682 | + | func (_c *MergeRequestService_MarkReady_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error)) *MergeRequestService_MarkReady_Call { | |
| 683 | + | _c.Call.Return(run) | |
| 684 | + | return _c | |
| 685 | + | } | |
| 686 | + | ||
| 537 | 687 | // Merge provides a mock function for the type MergeRequestService | |
| 538 | 688 | func (_mock *MergeRequestService) Merge(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params ports.MergeParams) (ports.MergeResult, error) { | |
| 539 | 689 | ret := _mock.Called(ctx, p, name, number, params) | |
| @@ -210,6 +210,78 @@ func (_c *MergeRequestService_Close_Call) RunAndReturn(run func(ctx context.Cont | |||||
| 210 | return _c | 210 | return _c | ||
| 211 | } | 211 | } | ||
| 212 | 212 | ||||
| 213 | + | // CountOpen provides a mock function for the type MergeRequestService | |||
| 214 | + | func (_mock *MergeRequestService) CountOpen(ctx context.Context, p domain.Principal, name domain.RepoName) (int, error) { | |||
| 215 | + | ret := _mock.Called(ctx, p, name) | |||
| 216 | + | ||||
| 217 | + | if len(ret) == 0 { | |||
| 218 | + | panic("no return value specified for CountOpen") | |||
| 219 | + | } | |||
| 220 | + | ||||
| 221 | + | var r0 int | |||
| 222 | + | var r1 error | |||
| 223 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName) (int, error)); ok { | |||
| 224 | + | return returnFunc(ctx, p, name) | |||
| 225 | + | } | |||
| 226 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName) int); ok { | |||
| 227 | + | r0 = returnFunc(ctx, p, name) | |||
| 228 | + | } else { | |||
| 229 | + | r0 = ret.Get(0).(int) | |||
| 230 | + | } | |||
| 231 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName) error); ok { | |||
| 232 | + | r1 = returnFunc(ctx, p, name) | |||
| 233 | + | } else { | |||
| 234 | + | r1 = ret.Error(1) | |||
| 235 | + | } | |||
| 236 | + | return r0, r1 | |||
| 237 | + | } | |||
| 238 | + | ||||
| 239 | + | // MergeRequestService_CountOpen_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CountOpen' | |||
| 240 | + | type MergeRequestService_CountOpen_Call struct { | |||
| 241 | + | *mock.Call | |||
| 242 | + | } | |||
| 243 | + | ||||
| 244 | + | // CountOpen is a helper method to define mock.On call | |||
| 245 | + | // - ctx context.Context | |||
| 246 | + | // - p domain.Principal | |||
| 247 | + | // - name domain.RepoName | |||
| 248 | + | func (_e *MergeRequestService_Expecter) CountOpen(ctx any, p any, name any) *MergeRequestService_CountOpen_Call { | |||
| 249 | + | return &MergeRequestService_CountOpen_Call{Call: _e.mock.On("CountOpen", ctx, p, name)} | |||
| 250 | + | } | |||
| 251 | + | ||||
| 252 | + | func (_c *MergeRequestService_CountOpen_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName)) *MergeRequestService_CountOpen_Call { | |||
| 253 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 254 | + | var arg0 context.Context | |||
| 255 | + | if args[0] != nil { | |||
| 256 | + | arg0 = args[0].(context.Context) | |||
| 257 | + | } | |||
| 258 | + | var arg1 domain.Principal | |||
| 259 | + | if args[1] != nil { | |||
| 260 | + | arg1 = args[1].(domain.Principal) | |||
| 261 | + | } | |||
| 262 | + | var arg2 domain.RepoName | |||
| 263 | + | if args[2] != nil { | |||
| 264 | + | arg2 = args[2].(domain.RepoName) | |||
| 265 | + | } | |||
| 266 | + | run( | |||
| 267 | + | arg0, | |||
| 268 | + | arg1, | |||
| 269 | + | arg2, | |||
| 270 | + | ) | |||
| 271 | + | }) | |||
| 272 | + | return _c | |||
| 273 | + | } | |||
| 274 | + | ||||
| 275 | + | func (_c *MergeRequestService_CountOpen_Call) Return(n int, err error) *MergeRequestService_CountOpen_Call { | |||
| 276 | + | _c.Call.Return(n, err) | |||
| 277 | + | return _c | |||
| 278 | + | } | |||
| 279 | + | ||||
| 280 | + | func (_c *MergeRequestService_CountOpen_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName) (int, error)) *MergeRequestService_CountOpen_Call { | |||
| 281 | + | _c.Call.Return(run) | |||
| 282 | + | return _c | |||
| 283 | + | } | |||
| 284 | + | ||||
| 213 | // Create provides a mock function for the type MergeRequestService | 285 | // Create provides a mock function for the type MergeRequestService | ||
| 214 | func (_mock *MergeRequestService) Create(ctx context.Context, p domain.Principal, name domain.RepoName, params ports.CreateMergeRequestParams) (domain.MergeRequest, error) { | 286 | func (_mock *MergeRequestService) Create(ctx context.Context, p domain.Principal, name domain.RepoName, params ports.CreateMergeRequestParams) (domain.MergeRequest, error) { | ||
| 215 | ret := _mock.Called(ctx, p, name, params) | 287 | ret := _mock.Called(ctx, p, name, params) | ||
| @@ -534,6 +606,84 @@ func (_c *MergeRequestService_MarkRead_Call) RunAndReturn(run func(ctx context.C | |||||
| 534 | return _c | 606 | return _c | ||
| 535 | } | 607 | } | ||
| 536 | 608 | ||||
| 609 | + | // MarkReady provides a mock function for the type MergeRequestService | |||
| 610 | + | func (_mock *MergeRequestService) MarkReady(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error) { | |||
| 611 | + | ret := _mock.Called(ctx, p, name, number) | |||
| 612 | + | ||||
| 613 | + | if len(ret) == 0 { | |||
| 614 | + | panic("no return value specified for MarkReady") | |||
| 615 | + | } | |||
| 616 | + | ||||
| 617 | + | var r0 domain.MergeRequest | |||
| 618 | + | var r1 error | |||
| 619 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64) (domain.MergeRequest, error)); ok { | |||
| 620 | + | return returnFunc(ctx, p, name, number) | |||
| 621 | + | } | |||
| 622 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64) domain.MergeRequest); ok { | |||
| 623 | + | r0 = returnFunc(ctx, p, name, number) | |||
| 624 | + | } else { | |||
| 625 | + | r0 = ret.Get(0).(domain.MergeRequest) | |||
| 626 | + | } | |||
| 627 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, int64) error); ok { | |||
| 628 | + | r1 = returnFunc(ctx, p, name, number) | |||
| 629 | + | } else { | |||
| 630 | + | r1 = ret.Error(1) | |||
| 631 | + | } | |||
| 632 | + | return r0, r1 | |||
| 633 | + | } | |||
| 634 | + | ||||
| 635 | + | // MergeRequestService_MarkReady_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarkReady' | |||
| 636 | + | type MergeRequestService_MarkReady_Call struct { | |||
| 637 | + | *mock.Call | |||
| 638 | + | } | |||
| 639 | + | ||||
| 640 | + | // MarkReady is a helper method to define mock.On call | |||
| 641 | + | // - ctx context.Context | |||
| 642 | + | // - p domain.Principal | |||
| 643 | + | // - name domain.RepoName | |||
| 644 | + | // - number int64 | |||
| 645 | + | func (_e *MergeRequestService_Expecter) MarkReady(ctx any, p any, name any, number any) *MergeRequestService_MarkReady_Call { | |||
| 646 | + | return &MergeRequestService_MarkReady_Call{Call: _e.mock.On("MarkReady", ctx, p, name, number)} | |||
| 647 | + | } | |||
| 648 | + | ||||
| 649 | + | func (_c *MergeRequestService_MarkReady_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64)) *MergeRequestService_MarkReady_Call { | |||
| 650 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 651 | + | var arg0 context.Context | |||
| 652 | + | if args[0] != nil { | |||
| 653 | + | arg0 = args[0].(context.Context) | |||
| 654 | + | } | |||
| 655 | + | var arg1 domain.Principal | |||
| 656 | + | if args[1] != nil { | |||
| 657 | + | arg1 = args[1].(domain.Principal) | |||
| 658 | + | } | |||
| 659 | + | var arg2 domain.RepoName | |||
| 660 | + | if args[2] != nil { | |||
| 661 | + | arg2 = args[2].(domain.RepoName) | |||
| 662 | + | } | |||
| 663 | + | var arg3 int64 | |||
| 664 | + | if args[3] != nil { | |||
| 665 | + | arg3 = args[3].(int64) | |||
| 666 | + | } | |||
| 667 | + | run( | |||
| 668 | + | arg0, | |||
| 669 | + | arg1, | |||
| 670 | + | arg2, | |||
| 671 | + | arg3, | |||
| 672 | + | ) | |||
| 673 | + | }) | |||
| 674 | + | return _c | |||
| 675 | + | } | |||
| 676 | + | ||||
| 677 | + | func (_c *MergeRequestService_MarkReady_Call) Return(mergeRequest domain.MergeRequest, err error) *MergeRequestService_MarkReady_Call { | |||
| 678 | + | _c.Call.Return(mergeRequest, err) | |||
| 679 | + | return _c | |||
| 680 | + | } | |||
| 681 | + | ||||
| 682 | + | func (_c *MergeRequestService_MarkReady_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error)) *MergeRequestService_MarkReady_Call { | |||
| 683 | + | _c.Call.Return(run) | |||
| 684 | + | return _c | |||
| 685 | + | } | |||
| 686 | + | ||||
| 537 | // Merge provides a mock function for the type MergeRequestService | 687 | // Merge provides a mock function for the type MergeRequestService | ||
| 538 | func (_mock *MergeRequestService) Merge(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params ports.MergeParams) (ports.MergeResult, error) { | 688 | func (_mock *MergeRequestService) Merge(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params ports.MergeParams) (ports.MergeResult, error) { | ||
| 539 | ret := _mock.Called(ctx, p, name, number, params) | 689 | ret := _mock.Called(ctx, p, name, number, params) | ||
M internal/ports/mocks/MergeRequestStore.go +66 -0
| @@ -47,6 +47,72 @@ func (_m *MergeRequestStore) EXPECT() *MergeRequestStore_Expecter { | |||
| 47 | 47 | return &MergeRequestStore_Expecter{mock: &_m.Mock} | |
| 48 | 48 | } | |
| 49 | 49 | ||
| 50 | + | // CountOpen provides a mock function for the type MergeRequestStore | |
| 51 | + | func (_mock *MergeRequestStore) CountOpen(ctx context.Context, repo domain.RepoName) (int, error) { | |
| 52 | + | ret := _mock.Called(ctx, repo) | |
| 53 | + | ||
| 54 | + | if len(ret) == 0 { | |
| 55 | + | panic("no return value specified for CountOpen") | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | var r0 int | |
| 59 | + | var r1 error | |
| 60 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) (int, error)); ok { | |
| 61 | + | return returnFunc(ctx, repo) | |
| 62 | + | } | |
| 63 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) int); ok { | |
| 64 | + | r0 = returnFunc(ctx, repo) | |
| 65 | + | } else { | |
| 66 | + | r0 = ret.Get(0).(int) | |
| 67 | + | } | |
| 68 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName) error); ok { | |
| 69 | + | r1 = returnFunc(ctx, repo) | |
| 70 | + | } else { | |
| 71 | + | r1 = ret.Error(1) | |
| 72 | + | } | |
| 73 | + | return r0, r1 | |
| 74 | + | } | |
| 75 | + | ||
| 76 | + | // MergeRequestStore_CountOpen_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CountOpen' | |
| 77 | + | type MergeRequestStore_CountOpen_Call struct { | |
| 78 | + | *mock.Call | |
| 79 | + | } | |
| 80 | + | ||
| 81 | + | // CountOpen is a helper method to define mock.On call | |
| 82 | + | // - ctx context.Context | |
| 83 | + | // - repo domain.RepoName | |
| 84 | + | func (_e *MergeRequestStore_Expecter) CountOpen(ctx any, repo any) *MergeRequestStore_CountOpen_Call { | |
| 85 | + | return &MergeRequestStore_CountOpen_Call{Call: _e.mock.On("CountOpen", ctx, repo)} | |
| 86 | + | } | |
| 87 | + | ||
| 88 | + | func (_c *MergeRequestStore_CountOpen_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *MergeRequestStore_CountOpen_Call { | |
| 89 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 90 | + | var arg0 context.Context | |
| 91 | + | if args[0] != nil { | |
| 92 | + | arg0 = args[0].(context.Context) | |
| 93 | + | } | |
| 94 | + | var arg1 domain.RepoName | |
| 95 | + | if args[1] != nil { | |
| 96 | + | arg1 = args[1].(domain.RepoName) | |
| 97 | + | } | |
| 98 | + | run( | |
| 99 | + | arg0, | |
| 100 | + | arg1, | |
| 101 | + | ) | |
| 102 | + | }) | |
| 103 | + | return _c | |
| 104 | + | } | |
| 105 | + | ||
| 106 | + | func (_c *MergeRequestStore_CountOpen_Call) Return(n int, err error) *MergeRequestStore_CountOpen_Call { | |
| 107 | + | _c.Call.Return(n, err) | |
| 108 | + | return _c | |
| 109 | + | } | |
| 110 | + | ||
| 111 | + | func (_c *MergeRequestStore_CountOpen_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) (int, error)) *MergeRequestStore_CountOpen_Call { | |
| 112 | + | _c.Call.Return(run) | |
| 113 | + | return _c | |
| 114 | + | } | |
| 115 | + | ||
| 50 | 116 | // Create provides a mock function for the type MergeRequestStore | |
| 51 | 117 | func (_mock *MergeRequestStore) Create(ctx context.Context, mr domain.MergeRequest) (domain.MergeRequest, error) { | |
| 52 | 118 | ret := _mock.Called(ctx, mr) | |
| @@ -47,6 +47,72 @@ func (_m *MergeRequestStore) EXPECT() *MergeRequestStore_Expecter { | |||||
| 47 | return &MergeRequestStore_Expecter{mock: &_m.Mock} | 47 | return &MergeRequestStore_Expecter{mock: &_m.Mock} | ||
| 48 | } | 48 | } | ||
| 49 | 49 | ||||
| 50 | + | // CountOpen provides a mock function for the type MergeRequestStore | |||
| 51 | + | func (_mock *MergeRequestStore) CountOpen(ctx context.Context, repo domain.RepoName) (int, error) { | |||
| 52 | + | ret := _mock.Called(ctx, repo) | |||
| 53 | + | ||||
| 54 | + | if len(ret) == 0 { | |||
| 55 | + | panic("no return value specified for CountOpen") | |||
| 56 | + | } | |||
| 57 | + | ||||
| 58 | + | var r0 int | |||
| 59 | + | var r1 error | |||
| 60 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) (int, error)); ok { | |||
| 61 | + | return returnFunc(ctx, repo) | |||
| 62 | + | } | |||
| 63 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) int); ok { | |||
| 64 | + | r0 = returnFunc(ctx, repo) | |||
| 65 | + | } else { | |||
| 66 | + | r0 = ret.Get(0).(int) | |||
| 67 | + | } | |||
| 68 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName) error); ok { | |||
| 69 | + | r1 = returnFunc(ctx, repo) | |||
| 70 | + | } else { | |||
| 71 | + | r1 = ret.Error(1) | |||
| 72 | + | } | |||
| 73 | + | return r0, r1 | |||
| 74 | + | } | |||
| 75 | + | ||||
| 76 | + | // MergeRequestStore_CountOpen_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CountOpen' | |||
| 77 | + | type MergeRequestStore_CountOpen_Call struct { | |||
| 78 | + | *mock.Call | |||
| 79 | + | } | |||
| 80 | + | ||||
| 81 | + | // CountOpen is a helper method to define mock.On call | |||
| 82 | + | // - ctx context.Context | |||
| 83 | + | // - repo domain.RepoName | |||
| 84 | + | func (_e *MergeRequestStore_Expecter) CountOpen(ctx any, repo any) *MergeRequestStore_CountOpen_Call { | |||
| 85 | + | return &MergeRequestStore_CountOpen_Call{Call: _e.mock.On("CountOpen", ctx, repo)} | |||
| 86 | + | } | |||
| 87 | + | ||||
| 88 | + | func (_c *MergeRequestStore_CountOpen_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *MergeRequestStore_CountOpen_Call { | |||
| 89 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 90 | + | var arg0 context.Context | |||
| 91 | + | if args[0] != nil { | |||
| 92 | + | arg0 = args[0].(context.Context) | |||
| 93 | + | } | |||
| 94 | + | var arg1 domain.RepoName | |||
| 95 | + | if args[1] != nil { | |||
| 96 | + | arg1 = args[1].(domain.RepoName) | |||
| 97 | + | } | |||
| 98 | + | run( | |||
| 99 | + | arg0, | |||
| 100 | + | arg1, | |||
| 101 | + | ) | |||
| 102 | + | }) | |||
| 103 | + | return _c | |||
| 104 | + | } | |||
| 105 | + | ||||
| 106 | + | func (_c *MergeRequestStore_CountOpen_Call) Return(n int, err error) *MergeRequestStore_CountOpen_Call { | |||
| 107 | + | _c.Call.Return(n, err) | |||
| 108 | + | return _c | |||
| 109 | + | } | |||
| 110 | + | ||||
| 111 | + | func (_c *MergeRequestStore_CountOpen_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) (int, error)) *MergeRequestStore_CountOpen_Call { | |||
| 112 | + | _c.Call.Return(run) | |||
| 113 | + | return _c | |||
| 114 | + | } | |||
| 115 | + | ||||
| 50 | // Create provides a mock function for the type MergeRequestStore | 116 | // Create provides a mock function for the type MergeRequestStore | ||
| 51 | func (_mock *MergeRequestStore) Create(ctx context.Context, mr domain.MergeRequest) (domain.MergeRequest, error) { | 117 | func (_mock *MergeRequestStore) Create(ctx context.Context, mr domain.MergeRequest) (domain.MergeRequest, error) { | ||
| 52 | ret := _mock.Called(ctx, mr) | 118 | ret := _mock.Called(ctx, mr) | ||
M internal/ports/ports.go +3 -0
| @@ -248,6 +248,9 @@ type MergeRequestStore interface { | |||
| 248 | 248 | // ListOpen returns the open merge requests of the repository, highest | |
| 249 | 249 | // number first, reading only the open ones (through an index). | |
| 250 | 250 | ListOpen(ctx context.Context, repo domain.RepoName) ([]domain.MergeRequest, error) | |
| 251 | + | // CountOpen returns the number of open merge requests of the repository | |
| 252 | + | // from the index of keys: no request is read. | |
| 253 | + | CountOpen(ctx context.Context, repo domain.RepoName) (int, error) | |
| 251 | 254 | // OpenBySource returns the numbers of the open merge requests whose source | |
| 252 | 255 | // branch is one of branches (short names), from an index of keys: a push | |
| 253 | 256 | // looks up the branches it moved, whatever the number of merge requests the | |
| @@ -248,6 +248,9 @@ type MergeRequestStore interface { | |||||
| 248 | // ListOpen returns the open merge requests of the repository, highest | 248 | // ListOpen returns the open merge requests of the repository, highest | ||
| 249 | // number first, reading only the open ones (through an index). | 249 | // number first, reading only the open ones (through an index). | ||
| 250 | ListOpen(ctx context.Context, repo domain.RepoName) ([]domain.MergeRequest, error) | 250 | ListOpen(ctx context.Context, repo domain.RepoName) ([]domain.MergeRequest, error) | ||
| 251 | + | // CountOpen returns the number of open merge requests of the repository | |||
| 252 | + | // from the index of keys: no request is read. | |||
| 253 | + | CountOpen(ctx context.Context, repo domain.RepoName) (int, error) | |||
| 251 | // OpenBySource returns the numbers of the open merge requests whose source | 254 | // OpenBySource returns the numbers of the open merge requests whose source | ||
| 252 | // branch is one of branches (short names), from an index of keys: a push | 255 | // branch is one of branches (short names), from an index of keys: a push | ||
| 253 | // looks up the branches it moved, whatever the number of merge requests the | 256 | // looks up the branches it moved, whatever the number of merge requests the | ||
M internal/ports/services.go +5 -0
| @@ -114,6 +114,11 @@ type MergeRequestService interface { | |||
| 114 | 114 | Close(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error) | |
| 115 | 115 | MarkRead(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, head domain.ObjectID) (domain.ObjectID, error) | |
| 116 | 116 | Merge(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params MergeParams) (MergeResult, error) | |
| 117 | + | // MarkReady takes a draft out of draft: its title loses its "WIP:" prefix. | |
| 118 | + | MarkReady(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error) | |
| 119 | + | // CountOpen is the number of open merge requests of a repository the | |
| 120 | + | // principal can read. It reads no Git: the tab of every page shows it. | |
| 121 | + | CountOpen(ctx context.Context, p domain.Principal, name domain.RepoName) (int, error) | |
| 117 | 122 | } | |
| 118 | 123 | ||
| 119 | 124 | // SigningKeyService tells which key signs the commits of the forge. | |
| @@ -114,6 +114,11 @@ type MergeRequestService interface { | |||||
| 114 | Close(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error) | 114 | Close(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error) | ||
| 115 | MarkRead(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, head domain.ObjectID) (domain.ObjectID, error) | 115 | MarkRead(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, head domain.ObjectID) (domain.ObjectID, error) | ||
| 116 | Merge(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params MergeParams) (MergeResult, error) | 116 | Merge(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params MergeParams) (MergeResult, error) | ||
| 117 | + | // MarkReady takes a draft out of draft: its title loses its "WIP:" prefix. | |||
| 118 | + | MarkReady(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error) | |||
| 119 | + | // CountOpen is the number of open merge requests of a repository the | |||
| 120 | + | // principal can read. It reads no Git: the tab of every page shows it. | |||
| 121 | + | CountOpen(ctx context.Context, p domain.Principal, name domain.RepoName) (int, error) | |||
| 117 | } | 122 | } | ||
| 118 | 123 | ||||
| 119 | // SigningKeyService tells which key signs the commits of the forge. | 124 | // SigningKeyService tells which key signs the commits of the forge. | ||
M internal/usecase/events.go +4 -0
| @@ -174,6 +174,8 @@ func (e *Events) snapshot(ctx context.Context, ev domain.Event) bufferedEvent { | |||
| 174 | 174 | return repoSnapshot(ev.Visibility, ev.CreatedAt) | |
| 175 | 175 | case domain.MergeRequestClosed: | |
| 176 | 176 | return repoSnapshot(ev.Visibility, ev.CreatedAt) | |
| 177 | + | case domain.MergeRequestReady: | |
| 178 | + | return repoSnapshot(ev.Visibility, ev.CreatedAt) | |
| 177 | 179 | case domain.CommentAdded: | |
| 178 | 180 | return repoSnapshot(ev.Visibility, ev.CreatedAt) | |
| 179 | 181 | case domain.RunUpdated: | |
| @@ -481,6 +483,8 @@ func (e *Events) deliver(ctx context.Context, w *watch, be bufferedEvent, send f | |||
| 481 | 483 | return e.deliverRepo(ctx, w, ev.MergeRequest.Repo, false, be, out, send) | |
| 482 | 484 | case domain.MergeRequestClosed: | |
| 483 | 485 | return e.deliverRepo(ctx, w, ev.MergeRequest.Repo, false, be, out, send) | |
| 486 | + | case domain.MergeRequestReady: | |
| 487 | + | return e.deliverRepo(ctx, w, ev.MergeRequest.Repo, false, be, out, send) | |
| 484 | 488 | case domain.CommentAdded: | |
| 485 | 489 | return e.deliverRepo(ctx, w, ev.Repo, false, be, out, send) | |
| 486 | 490 | case domain.RunUpdated: | |
| @@ -174,6 +174,8 @@ func (e *Events) snapshot(ctx context.Context, ev domain.Event) bufferedEvent { | |||||
| 174 | return repoSnapshot(ev.Visibility, ev.CreatedAt) | 174 | return repoSnapshot(ev.Visibility, ev.CreatedAt) | ||
| 175 | case domain.MergeRequestClosed: | 175 | case domain.MergeRequestClosed: | ||
| 176 | return repoSnapshot(ev.Visibility, ev.CreatedAt) | 176 | return repoSnapshot(ev.Visibility, ev.CreatedAt) | ||
| 177 | + | case domain.MergeRequestReady: | |||
| 178 | + | return repoSnapshot(ev.Visibility, ev.CreatedAt) | |||
| 177 | case domain.CommentAdded: | 179 | case domain.CommentAdded: | ||
| 178 | return repoSnapshot(ev.Visibility, ev.CreatedAt) | 180 | return repoSnapshot(ev.Visibility, ev.CreatedAt) | ||
| 179 | case domain.RunUpdated: | 181 | case domain.RunUpdated: | ||
| @@ -481,6 +483,8 @@ func (e *Events) deliver(ctx context.Context, w *watch, be bufferedEvent, send f | |||||
| 481 | return e.deliverRepo(ctx, w, ev.MergeRequest.Repo, false, be, out, send) | 483 | return e.deliverRepo(ctx, w, ev.MergeRequest.Repo, false, be, out, send) | ||
| 482 | case domain.MergeRequestClosed: | 484 | case domain.MergeRequestClosed: | ||
| 483 | return e.deliverRepo(ctx, w, ev.MergeRequest.Repo, false, be, out, send) | 485 | return e.deliverRepo(ctx, w, ev.MergeRequest.Repo, false, be, out, send) | ||
| 486 | + | case domain.MergeRequestReady: | |||
| 487 | + | return e.deliverRepo(ctx, w, ev.MergeRequest.Repo, false, be, out, send) | |||
| 484 | case domain.CommentAdded: | 488 | case domain.CommentAdded: | ||
| 485 | return e.deliverRepo(ctx, w, ev.Repo, false, be, out, send) | 489 | return e.deliverRepo(ctx, w, ev.Repo, false, be, out, send) | ||
| 486 | case domain.RunUpdated: | 490 | case domain.RunUpdated: | ||
M internal/usecase/mergerequests.go +55 -0
| @@ -333,6 +333,58 @@ func (m *MergeRequests) List(ctx context.Context, p domain.Principal, name domai | |||
| 333 | 333 | return page, nil | |
| 334 | 334 | } | |
| 335 | 335 | ||
| 336 | + | // MarkReady implements ports.MergeRequestService: the title of an open draft | |
| 337 | + | // loses its draft prefix, so the request can be merged. The principal needs | |
| 338 | + | // mr:write. A request that is not open, or not a draft, gives | |
| 339 | + | // domain.ErrPreconditionFailed. | |
| 340 | + | func (m *MergeRequests) MarkReady(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error) { | |
| 341 | + | repo, err := m.writable(ctx, p, name, domain.ScopeMRWrite) | |
| 342 | + | if err != nil { | |
| 343 | + | return domain.MergeRequest{}, err | |
| 344 | + | } | |
| 345 | + | unlock, err := m.lockRepo(ctx, name) // a merge sees the request as it is after | |
| 346 | + | if err != nil { | |
| 347 | + | return domain.MergeRequest{}, err | |
| 348 | + | } | |
| 349 | + | defer unlock() | |
| 350 | + | for range maxWriteRetries { | |
| 351 | + | mr, err := m.store.Get(ctx, name, number) | |
| 352 | + | if err != nil { | |
| 353 | + | return domain.MergeRequest{}, err | |
| 354 | + | } | |
| 355 | + | if !mr.IsOpen() { | |
| 356 | + | return domain.MergeRequest{}, fmt.Errorf("%w: !%d is %s", domain.ErrPreconditionFailed, number, mr.State) | |
| 357 | + | } | |
| 358 | + | title, ok := domain.ReadyTitle(mr.Title) | |
| 359 | + | if !ok { | |
| 360 | + | return domain.MergeRequest{}, fmt.Errorf("%w: !%d is not a draft", domain.ErrPreconditionFailed, number) | |
| 361 | + | } | |
| 362 | + | mr.Title, mr.UpdatedAt = title, m.clock.Now().UTC() | |
| 363 | + | err = m.store.Update(ctx, mr) | |
| 364 | + | if errors.Is(err, domain.ErrConflict) { | |
| 365 | + | continue | |
| 366 | + | } | |
| 367 | + | if err != nil { | |
| 368 | + | return domain.MergeRequest{}, fmt.Errorf("mark !%d of %q ready: %w", number, name, err) | |
| 369 | + | } | |
| 370 | + | m.bus.Publish(ctx, domain.MergeRequestReady{MergeRequest: mr, At: mr.UpdatedAt, Visibility: repo.Visibility, CreatedAt: repo.CreatedAt}) | |
| 371 | + | return m.withHead(ctx, mr) | |
| 372 | + | } | |
| 373 | + | return domain.MergeRequest{}, fmt.Errorf("mark !%d of %q ready: %w", number, name, domain.ErrConflict) | |
| 374 | + | } | |
| 375 | + | ||
| 376 | + | // CountOpen implements ports.MergeRequestService. | |
| 377 | + | func (m *MergeRequests) CountOpen(ctx context.Context, p domain.Principal, name domain.RepoName) (int, error) { | |
| 378 | + | if _, err := m.readable(ctx, p, name); err != nil { | |
| 379 | + | return 0, err | |
| 380 | + | } | |
| 381 | + | n, err := m.store.CountOpen(ctx, name) | |
| 382 | + | if err != nil { | |
| 383 | + | return 0, fmt.Errorf("count open merge requests of %q: %w", name, err) | |
| 384 | + | } | |
| 385 | + | return n, nil | |
| 386 | + | } | |
| 387 | + | ||
| 336 | 388 | func encodeMRToken(last domain.MergeRequest) string { | |
| 337 | 389 | return base64.RawURLEncoding.EncodeToString([]byte(mrTokenPrefix + strconv.FormatInt(last.Number, 10))) | |
| 338 | 390 | } | |
| @@ -750,6 +802,9 @@ func (m *MergeRequests) merge(ctx context.Context, p domain.Principal, name doma | |||
| 750 | 802 | if !mr.IsOpen() { | |
| 751 | 803 | return domain.MergeRequest{}, false, fmt.Errorf("%w: !%d is %s", domain.ErrPreconditionFailed, number, mr.State) | |
| 752 | 804 | } | |
| 805 | + | if mr.IsDraft() { | |
| 806 | + | return domain.MergeRequest{}, false, fmt.Errorf("%w: !%d is a draft: mark it ready for review first", domain.ErrPreconditionFailed, number) | |
| 807 | + | } | |
| 753 | 808 | // The settings are read again under the lock: a change applies at once. | |
| 754 | 809 | repo, err = m.meta.Get(ctx, name) | |
| 755 | 810 | if err != nil { | |
| @@ -333,6 +333,58 @@ func (m *MergeRequests) List(ctx context.Context, p domain.Principal, name domai | |||||
| 333 | return page, nil | 333 | return page, nil | ||
| 334 | } | 334 | } | ||
| 335 | 335 | ||||
| 336 | + | // MarkReady implements ports.MergeRequestService: the title of an open draft | |||
| 337 | + | // loses its draft prefix, so the request can be merged. The principal needs | |||
| 338 | + | // mr:write. A request that is not open, or not a draft, gives | |||
| 339 | + | // domain.ErrPreconditionFailed. | |||
| 340 | + | func (m *MergeRequests) MarkReady(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error) { | |||
| 341 | + | repo, err := m.writable(ctx, p, name, domain.ScopeMRWrite) | |||
| 342 | + | if err != nil { | |||
| 343 | + | return domain.MergeRequest{}, err | |||
| 344 | + | } | |||
| 345 | + | unlock, err := m.lockRepo(ctx, name) // a merge sees the request as it is after | |||
| 346 | + | if err != nil { | |||
| 347 | + | return domain.MergeRequest{}, err | |||
| 348 | + | } | |||
| 349 | + | defer unlock() | |||
| 350 | + | for range maxWriteRetries { | |||
| 351 | + | mr, err := m.store.Get(ctx, name, number) | |||
| 352 | + | if err != nil { | |||
| 353 | + | return domain.MergeRequest{}, err | |||
| 354 | + | } | |||
| 355 | + | if !mr.IsOpen() { | |||
| 356 | + | return domain.MergeRequest{}, fmt.Errorf("%w: !%d is %s", domain.ErrPreconditionFailed, number, mr.State) | |||
| 357 | + | } | |||
| 358 | + | title, ok := domain.ReadyTitle(mr.Title) | |||
| 359 | + | if !ok { | |||
| 360 | + | return domain.MergeRequest{}, fmt.Errorf("%w: !%d is not a draft", domain.ErrPreconditionFailed, number) | |||
| 361 | + | } | |||
| 362 | + | mr.Title, mr.UpdatedAt = title, m.clock.Now().UTC() | |||
| 363 | + | err = m.store.Update(ctx, mr) | |||
| 364 | + | if errors.Is(err, domain.ErrConflict) { | |||
| 365 | + | continue | |||
| 366 | + | } | |||
| 367 | + | if err != nil { | |||
| 368 | + | return domain.MergeRequest{}, fmt.Errorf("mark !%d of %q ready: %w", number, name, err) | |||
| 369 | + | } | |||
| 370 | + | m.bus.Publish(ctx, domain.MergeRequestReady{MergeRequest: mr, At: mr.UpdatedAt, Visibility: repo.Visibility, CreatedAt: repo.CreatedAt}) | |||
| 371 | + | return m.withHead(ctx, mr) | |||
| 372 | + | } | |||
| 373 | + | return domain.MergeRequest{}, fmt.Errorf("mark !%d of %q ready: %w", number, name, domain.ErrConflict) | |||
| 374 | + | } | |||
| 375 | + | ||||
| 376 | + | // CountOpen implements ports.MergeRequestService. | |||
| 377 | + | func (m *MergeRequests) CountOpen(ctx context.Context, p domain.Principal, name domain.RepoName) (int, error) { | |||
| 378 | + | if _, err := m.readable(ctx, p, name); err != nil { | |||
| 379 | + | return 0, err | |||
| 380 | + | } | |||
| 381 | + | n, err := m.store.CountOpen(ctx, name) | |||
| 382 | + | if err != nil { | |||
| 383 | + | return 0, fmt.Errorf("count open merge requests of %q: %w", name, err) | |||
| 384 | + | } | |||
| 385 | + | return n, nil | |||
| 386 | + | } | |||
| 387 | + | ||||
| 336 | func encodeMRToken(last domain.MergeRequest) string { | 388 | func encodeMRToken(last domain.MergeRequest) string { | ||
| 337 | return base64.RawURLEncoding.EncodeToString([]byte(mrTokenPrefix + strconv.FormatInt(last.Number, 10))) | 389 | return base64.RawURLEncoding.EncodeToString([]byte(mrTokenPrefix + strconv.FormatInt(last.Number, 10))) | ||
| 338 | } | 390 | } | ||
| @@ -750,6 +802,9 @@ func (m *MergeRequests) merge(ctx context.Context, p domain.Principal, name doma | |||||
| 750 | if !mr.IsOpen() { | 802 | if !mr.IsOpen() { | ||
| 751 | return domain.MergeRequest{}, false, fmt.Errorf("%w: !%d is %s", domain.ErrPreconditionFailed, number, mr.State) | 803 | return domain.MergeRequest{}, false, fmt.Errorf("%w: !%d is %s", domain.ErrPreconditionFailed, number, mr.State) | ||
| 752 | } | 804 | } | ||
| 805 | + | if mr.IsDraft() { | |||
| 806 | + | return domain.MergeRequest{}, false, fmt.Errorf("%w: !%d is a draft: mark it ready for review first", domain.ErrPreconditionFailed, number) | |||
| 807 | + | } | |||
| 753 | // The settings are read again under the lock: a change applies at once. | 808 | // The settings are read again under the lock: a change applies at once. | ||
| 754 | repo, err = m.meta.Get(ctx, name) | 809 | repo, err = m.meta.Get(ctx, name) | ||
| 755 | if err != nil { | 810 | if err != nil { | ||
M internal/usecase/mergerequests_integration_test.go +37 -0
| @@ -889,6 +889,43 @@ func TestMergeFastForward(t *testing.T) { | |||
| 889 | 889 | assert.ErrorIs(t, err, domain.ErrPreconditionFailed) | |
| 890 | 890 | } | |
| 891 | 891 | ||
| 892 | + | // A draft is not merged until it is marked ready; ready drops the prefix of | |
| 893 | + | // the title, and only for an open draft. CountOpen counts drafts as open. | |
| 894 | + | func TestDraftMergeRequest(t *testing.T) { | |
| 895 | + | r := newMRRig(t) | |
| 896 | + | r.seed() | |
| 897 | + | head := r.feature("feat") | |
| 898 | + | mr, err := r.mrs.Create(r.ctx, root, r.repo, ports.CreateMergeRequestParams{Title: "WIP: add feat", Source: "feat"}) | |
| 899 | + | require.NoError(t, err) | |
| 900 | + | ||
| 901 | + | _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head}) | |
| 902 | + | require.ErrorIs(t, err, domain.ErrPreconditionFailed) | |
| 903 | + | assert.Contains(t, err.Error(), "draft") | |
| 904 | + | ||
| 905 | + | n, err := r.mrs.CountOpen(r.ctx, reader, r.repo) | |
| 906 | + | require.NoError(t, err) | |
| 907 | + | assert.Equal(t, 1, n) | |
| 908 | + | ||
| 909 | + | _, err = r.mrs.MarkReady(r.ctx, reader, r.repo, mr.Number) | |
| 910 | + | require.Error(t, err, "mr:write is needed") | |
| 911 | + | r.drain() | |
| 912 | + | ready, err := r.mrs.MarkReady(r.ctx, mrW, r.repo, mr.Number) | |
| 913 | + | require.NoError(t, err) | |
| 914 | + | assert.Equal(t, "add feat", ready.Title.String()) | |
| 915 | + | assert.False(t, ready.IsDraft()) | |
| 916 | + | evs := r.drain() | |
| 917 | + | require.Len(t, evs, 1, "live pages learn the request is ready") | |
| 918 | + | assert.Equal(t, "add feat", evs[0].(domain.MergeRequestReady).MergeRequest.Title.String()) | |
| 919 | + | _, err = r.mrs.MarkReady(r.ctx, mrW, r.repo, mr.Number) | |
| 920 | + | require.ErrorIs(t, err, domain.ErrPreconditionFailed, "not a draft any more") | |
| 921 | + | ||
| 922 | + | _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head}) | |
| 923 | + | require.NoError(t, err) | |
| 924 | + | n, err = r.mrs.CountOpen(r.ctx, reader, r.repo) | |
| 925 | + | require.NoError(t, err) | |
| 926 | + | assert.Zero(t, n) | |
| 927 | + | } | |
| 928 | + | ||
| 892 | 929 | // AC4: the checks of a merge. | |
| 893 | 930 | func TestMergeRefusals(t *testing.T) { | |
| 894 | 931 | t.Run("a branch that moved since the review", func(t *testing.T) { | |
| @@ -889,6 +889,43 @@ func TestMergeFastForward(t *testing.T) { | |||||
| 889 | assert.ErrorIs(t, err, domain.ErrPreconditionFailed) | 889 | assert.ErrorIs(t, err, domain.ErrPreconditionFailed) | ||
| 890 | } | 890 | } | ||
| 891 | 891 | ||||
| 892 | + | // A draft is not merged until it is marked ready; ready drops the prefix of | |||
| 893 | + | // the title, and only for an open draft. CountOpen counts drafts as open. | |||
| 894 | + | func TestDraftMergeRequest(t *testing.T) { | |||
| 895 | + | r := newMRRig(t) | |||
| 896 | + | r.seed() | |||
| 897 | + | head := r.feature("feat") | |||
| 898 | + | mr, err := r.mrs.Create(r.ctx, root, r.repo, ports.CreateMergeRequestParams{Title: "WIP: add feat", Source: "feat"}) | |||
| 899 | + | require.NoError(t, err) | |||
| 900 | + | ||||
| 901 | + | _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head}) | |||
| 902 | + | require.ErrorIs(t, err, domain.ErrPreconditionFailed) | |||
| 903 | + | assert.Contains(t, err.Error(), "draft") | |||
| 904 | + | ||||
| 905 | + | n, err := r.mrs.CountOpen(r.ctx, reader, r.repo) | |||
| 906 | + | require.NoError(t, err) | |||
| 907 | + | assert.Equal(t, 1, n) | |||
| 908 | + | ||||
| 909 | + | _, err = r.mrs.MarkReady(r.ctx, reader, r.repo, mr.Number) | |||
| 910 | + | require.Error(t, err, "mr:write is needed") | |||
| 911 | + | r.drain() | |||
| 912 | + | ready, err := r.mrs.MarkReady(r.ctx, mrW, r.repo, mr.Number) | |||
| 913 | + | require.NoError(t, err) | |||
| 914 | + | assert.Equal(t, "add feat", ready.Title.String()) | |||
| 915 | + | assert.False(t, ready.IsDraft()) | |||
| 916 | + | evs := r.drain() | |||
| 917 | + | require.Len(t, evs, 1, "live pages learn the request is ready") | |||
| 918 | + | assert.Equal(t, "add feat", evs[0].(domain.MergeRequestReady).MergeRequest.Title.String()) | |||
| 919 | + | _, err = r.mrs.MarkReady(r.ctx, mrW, r.repo, mr.Number) | |||
| 920 | + | require.ErrorIs(t, err, domain.ErrPreconditionFailed, "not a draft any more") | |||
| 921 | + | ||||
| 922 | + | _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head}) | |||
| 923 | + | require.NoError(t, err) | |||
| 924 | + | n, err = r.mrs.CountOpen(r.ctx, reader, r.repo) | |||
| 925 | + | require.NoError(t, err) | |||
| 926 | + | assert.Zero(t, n) | |||
| 927 | + | } | |||
| 928 | + | ||||
| 892 | // AC4: the checks of a merge. | 929 | // AC4: the checks of a merge. | ||
| 893 | func TestMergeRefusals(t *testing.T) { | 930 | func TestMergeRefusals(t *testing.T) { | ||
| 894 | t.Run("a branch that moved since the review", func(t *testing.T) { | 931 | t.Run("a branch that moved since the review", func(t *testing.T) { | ||