feat(web,cli): live mergeability in the merge box, merge modes and inline merge errors
- commit
- b5b4bf99dbfc084d3ae3141928c279c9e43dd162
- author
- brice · · 2026-10-19 14:23 UTC
- committer
- brice · · 2026-10-19 18:30 UTC
- parent
- fc9623b
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
37 files · +2392 -849
M README.md +1 -1
| @@ -122,7 +122,7 @@ ermite mr show myrepo 1 --since-last-read --mark-read | |||
| 122 | 122 | **5. You merge** (or click Merge in the web interface): | |
| 123 | 123 | ||
| 124 | 124 | ```sh | |
| 125 | - | ermite mr merge myrepo 1 --head <head id> --squash --delete-branch | |
| 125 | + | ermite mr merge myrepo 1 --head <head id> --mode squash --delete-branch | |
| 126 | 126 | ``` | |
| 127 | 127 | ||
| 128 | 128 | Give the agent the [`skills/ermite`](skills/ermite/SKILL.md) skill so it knows the CLI. Rules of agent tokens: [docs/tokens.md](docs/tokens.md). | |
M docs/cli.md +5 -4
| @@ -54,10 +54,10 @@ ermite repo delete <name> [--yes] | |||
| 54 | 54 | ermite token create <label> [--preset cli|agent|ci|runner] [--scope …] [--repo …] [--branch-prefix …] [--expires 30d] | |
| 55 | 55 | ermite token list | revoke <id> | |
| 56 | 56 | ||
| 57 | - | ermite mr create <repo> --source <branch> --title … | list <repo> | show <repo> <n> [--diff] | |
| 57 | + | ermite mr create <repo> --source <branch> --title … | list <repo> | show <repo> <n> [--diff] # show prints the mergeability: up to date, behind, conflict | |
| 58 | 58 | ermite mr comment <repo> <n> -m … [--path … --line N|A-B --side old|new] [--reply <id>] [--approve|--request-changes] | |
| 59 | 59 | ermite mr resolve <repo> <n> <comment> | edit-comment <repo> <n> <comment> -m … | delete-comment <repo> <n> <comment> | |
| 60 | - | ermite mr merge <repo> <n> --head <id> [--squash] [--delete-branch] | close <repo> <n> | |
| 60 | + | ermite mr merge <repo> <n> --head <id> [--mode fast-forward|squash|merge] [--delete-branch] | close <repo> <n> | |
| 61 | 61 | ||
| 62 | 62 | ermite issue create <repo> --title … [--body … | --body-file …] | list <repo> [--state open|closed|all] | |
| 63 | 63 | ermite issue show <repo> <n> | comment <repo> <n> -m … | close <repo> <n> | reopen <repo> <n> | |
| @@ -78,7 +78,7 @@ ermite environment create|list|delete … --repo <repo> | |||
| 78 | 78 | ermite web login # one-time sign-in link (60 s) | |
| 79 | 79 | ermite web logout --all # sign every browser out | |
| 80 | 80 | ermite web passkey list | remove <id> | |
| 81 | - | ermite signing-key show # public key of squash commits | |
| 81 | + | ermite signing-key show # public key of squash and merge commits (server-signed) | |
| 82 | 82 | ermite oidc rotate-key | |
| 83 | 83 | ermite watch [repo] [--json] # follow live events | |
| 84 | 84 | ermite version # client and server versions | |
| @@ -90,7 +90,8 @@ Issues need a server with the `issues` feature and `ermite repo edit <repo> --is | |||
| 90 | 90 | ## Output | |
| 91 | 91 | ||
| 92 | 92 | - `--json` prints stable field names (listed in each `--help`) on `repo list|show|create|edit`, `issue create|list|show|comment|close|reopen`, `token create|list`, `run list|view|cancel|rerun`, `runner list` and `version`. | |
| 93 | - | - `watch --json` prints one object per line. | |
| 93 | + | - `watch --json` prints one object per line. `merge_request_mergeability` events carry `mergeability` (`state`, `behind`, `conflict_paths`…) and print as `repo!n mergeability: behind by 2`. | |
| 94 | + | - `mr merge --mode squash|merge` creates a commit signed by the server's own key (generated on first start); the output names its fingerprint, `ermite signing-key show` prints the key. Every mode is allowed by default; `repo edit --merge-modes fast-forward,squash` disables `merge`. | |
| 94 | 95 | - Errors are one line on stderr (`ermite: …`) with exit status 1. | |
| 95 | 96 | - Shell completion completes repository names from the server. | |
| 96 | 97 | ||
M docs/commit-signatures.md +1 -1
| @@ -36,5 +36,5 @@ ermite signing-key trust remove <id> | |||
| 36 | 36 | ||
| 37 | 37 | - A key may sign for 1 to 10 emails; up to 100 keys. | |
| 38 | 38 | - The id is the fingerprint: `SHA256:…` for SSH, the primary key fingerprint for OpenPGP. | |
| 39 | - | - The server's own key (`ermite signing-key show`), which signs squash merges, is always trusted for the committer email and cannot be removed. | |
| 39 | + | - The server's own key (`ermite signing-key show`), which signs squash and merge commits (generated automatically on first start), is always trusted for the committer email and cannot be removed. | |
| 40 | 40 | - Private keys are refused. | |
M docs/configuration.md +2 -2
| @@ -34,8 +34,8 @@ ermite serve | |||
| 34 | 34 | |---|---|---| | |
| 35 | 35 | | `ERMITE_GIT` | `git` | git 2.45 or newer | | |
| 36 | 36 | | `ERMITE_GIT_READER` | `ermite-git` | `ermite-git` reads with the [ermite-git](https://github.com/bnema/ermite-git) library where it is faster, and falls back to `git`. `cli` uses `git` for every read: set it and restart if a read looks wrong | | |
| 37 | - | | `ERMITE_COMMITTER_NAME` | `ermite` | committer of squash merges | | |
| 38 | - | | `ERMITE_COMMITTER_EMAIL` | `ermite@<public host>` | committer email of squash merges | | |
| 37 | + | | `ERMITE_COMMITTER_NAME` | `ermite` | committer of squash and merge commits | | |
| 38 | + | | `ERMITE_COMMITTER_EMAIL` | `ermite@<public host>` | committer email of squash and merge commits | | |
| 39 | 39 | ||
| 40 | 40 | ## CI | |
| 41 | 41 | ||
M docs/merge-requests.md +45 -7
| @@ -5,26 +5,64 @@ A merge request proposes a branch for a target (the default branch unless set). | |||
| 5 | 5 | ```sh | |
| 6 | 6 | ermite mr create myrepo --source feature --title "Add feature" | |
| 7 | 7 | ermite mr show myrepo 1 --diff | |
| 8 | - | ermite mr merge myrepo 1 --head <commit> [--squash] [--delete-branch] | |
| 8 | + | ermite mr merge myrepo 1 --head <commit> [--mode fast-forward|squash|merge] [--delete-branch] | |
| 9 | 9 | ``` | |
| 10 | 10 | ||
| 11 | 11 | `ermite mr create` and `ermite mr close` print a confirmation followed by the full | |
| 12 | 12 | merge request web URL on its own line. With `--json`, merge request objects | |
| 13 | 13 | (create, show, list, close and merge) include `url` alongside the other fields. | |
| 14 | 14 | ||
| 15 | - | - **Fast-forward** moves the target. **Squash** writes one commit signed by the server's SSH key. | |
| 16 | - | - The merge is refused when the branch moved since the reviewed `--head`, or when the target is not an ancestor of it. | |
| 15 | + | ## Merge modes | |
| 16 | + | ||
| 17 | + | | Mode | Result | Needs | | |
| 18 | + | |---|---|---| | |
| 19 | + | | `fast-forward` (default) | the target moves to the head, no commit | the target is an ancestor of the head | | |
| 20 | + | | `squash` | one new commit with the merged tree, the target tip as only parent | the branch merges without conflicts | | |
| 21 | + | | `merge` | a merge commit with two parents, the target tip and the head | the branch merges without conflicts | | |
| 22 | + | ||
| 23 | + | - The server never rebases. A branch behind its target merges with `squash` or `merge`, or is rebased and pushed again for a fast-forward. | |
| 24 | + | - The server checks the mode again when it merges, whatever the badge showed, and refuses with the reason (the target moved, a conflict with the paths). | |
| 25 | + | - The merge is also refused when the branch moved since the reviewed `--head`. | |
| 17 | 26 | - With `failing_checks_block` on (default), the merge is refused while a check of the head failed or is still running. | |
| 18 | - | - `--delete-branch` deletes the source branch, unless it is the default branch or another open request uses it. | |
| 19 | - | - `ermite repo edit --merge-modes` limits the modes per repository. | |
| 27 | + | - The merge answers as soon as the target has moved. `--delete-branch` then deletes the source branch right after, in the background (unless it is the default branch or another open request uses it); if the branch moved meanwhile, it is kept. | |
| 28 | + | - Every mode is allowed by default, including in repositories that had only `fast-forward` and `squash` before `merge` existed: those gain `merge`. Disable a mode per repository with `ermite repo edit <repo> --merge-modes fast-forward,squash` (list the modes to keep). | |
| 20 | 29 | - Scopes: `mr:write` to open and comment, `mr:review` to give a verdict, `mr:merge` to merge. | |
| 21 | 30 | ||
| 22 | - | Verify squash commits with the `allowed_signers` line from `ermite signing-key show`: | |
| 31 | + | ## Signed commits | |
| 32 | + | ||
| 33 | + | Squash and merge commits are signed by ermite's own key. The key is generated automatically the first time the server starts, belongs to the server, and its committer identity is `ERMITE_COMMITTER_NAME` / `ERMITE_COMMITTER_EMAIL`. `ermite signing-key show` prints its fingerprint and the `allowed_signers` line; use it to verify locally, or register the public key on GitHub or Forgejo. | |
| 34 | + | ||
| 35 | + | ```sh | |
| 36 | + | ermite signing-key show --allowed-signers > allowed_signers | |
| 37 | + | git -c gpg.format=ssh -c gpg.ssh.allowedSignersFile=allowed_signers verify-commit <commit> | |
| 38 | + | ``` | |
| 39 | + | ||
| 40 | + | The merge box of the web page and the output of `mr merge --mode squash|merge` repeat this with the fingerprint. | |
| 41 | + | ||
| 42 | + | ## Mergeability | |
| 43 | + | ||
| 44 | + | The server checks in the background whether each open request merges with its target, and again whenever the source or the target moves. `mr show` prints it; the web merge box shows it as a live badge; `ermite watch` reports each change. | |
| 45 | + | ||
| 46 | + | | State | Meaning | Modes possible | | |
| 47 | + | |---|---|---| | |
| 48 | + | | `checking` | no result yet for the current branches | none (wait) | | |
| 49 | + | | `up_to_date` | the target is an ancestor of the head | all | | |
| 50 | + | | `behind` | the head lacks `behind` commits of the target (`behind_capped`: at least 1000) but merges without conflicts | `squash`, `merge` | | |
| 51 | + | | `conflict` | the merge conflicts; up to 20 `conflict_paths` are listed (`conflict_paths_truncated` when there are more); no paths means no common history | none (rebase) | | |
| 23 | 52 | ||
| 24 | 53 | ```sh | |
| 25 | - | git verify-commit <commit> | |
| 54 | + | ermite mr show myrepo 1 | |
| 55 | + | # mergeability behind by 2, merges without conflicts (squash or merge commit; fast-forward needs a rebase) | |
| 26 | 56 | ``` | |
| 27 | 57 | ||
| 58 | + | In `mr show --json`, `mergeability` holds `state`, `behind`, `behind_capped`, `conflict_paths`, `conflict_paths_truncated`, `target_id` and `head_id`; it is `null` unless the request is open. `ermite watch` prints `merge_request_mergeability` events when the state changes (not on every push): | |
| 59 | + | ||
| 60 | + | ```text | |
| 61 | + | 2026-10-19T14:00:00Z merge_request_mergeability myrepo!1 mergeability: behind by 2 | |
| 62 | + | ``` | |
| 63 | + | ||
| 64 | + | In the web merge box a mode that is not possible is disabled with its reason, and the Merge button too when no mode is possible. A merge the server refuses (target moved, conflict, branch moved) shows the page again with the reason in the box. | |
| 65 | + | ||
| 28 | 66 | ## Notes | |
| 29 | 67 | ||
| 30 | 68 | A note is the only review object. It is published as soon as it is written: there is no draft and no submit step. | |
M internal/adapters/web/events.go +6 -2
| @@ -31,7 +31,7 @@ import ( | |||
| 31 | 31 | // GET /-/events?topic=repo:<name>&view=summary|refs | |
| 32 | 32 | // GET /-/events?topic=repo:<name>&view=commits&ref=<branch>&head=<sha> | |
| 33 | 33 | // GET /-/events?topic=repo:<name>&view=mrs | |
| 34 | - | // GET /-/events?topic=repo:<name>&view=mr&n=<number>&ref=<source branch> | |
| 34 | + | // GET /-/events?topic=repo:<name>&view=mr&n=<number>&ref=<source branch>&target=<target branch> | |
| 35 | 35 | // GET /-/events?topic=repo:<name>&view=issues | |
| 36 | 36 | // GET /-/events?topic=repo:<name>&view=issue&n=<number> | |
| 37 | 37 | // GET /-/events?topic=repo:<name>&view=actions | |
| @@ -56,6 +56,10 @@ import ( | |||
| 56 | 56 | // refs-list data: <tbody id="ref-rows">…</tbody>, and again with | |
| 57 | 57 | // <dl id="refs-kv">…</dl>, the facts of the rail | |
| 58 | 58 | // commits-head data: <div id="commits-head">N new commits — reload</div> | |
| 59 | + | // mr-mergeability data: <div id="mr-mergeability">…</div>, on the merge request pages: | |
| 60 | + | // the badge of the merge box (a slot like mr-notice), when the | |
| 61 | + | // mergeability of the request shown settles or a push moves its | |
| 62 | + | // source or target | |
| 59 | 63 | // mr-notice data: <div id="mr-notice">…</div>, on the merge request and issue pages: | |
| 60 | 64 | // something changed, with the link that reloads the page | |
| 61 | 65 | // ci-run data: <tr id="run-<id>">…</tr> (the list of runs), | |
| @@ -254,7 +258,7 @@ func (s *Server) events(w http.ResponseWriter, r *http.Request) { | |||
| 254 | 258 | return | |
| 255 | 259 | } | |
| 256 | 260 | ||
| 257 | - | st := &liveStream{s: s, repo: repo, view: q.Get("view"), ref: q.Get("ref"), head: q.Get("head"), n: q.Get("n"), job: q.Get("job"), sha: sha} | |
| 261 | + | st := &liveStream{s: s, repo: repo, view: q.Get("view"), ref: q.Get("ref"), head: q.Get("head"), n: q.Get("n"), target: q.Get("target"), job: q.Get("job"), sha: sha} | |
| 258 | 262 | if topic == ownerTopic { | |
| 259 | 263 | st.filter = q.Get("repo") | |
| 260 | 264 | } | |
M internal/adapters/web/fragments.go +58 -8
| @@ -28,6 +28,7 @@ type liveStream struct { | |||
| 28 | 28 | head string // commits: the commit listed first | |
| 29 | 29 | n string // mr: the number of the merge request shown; run: the run id | |
| 30 | 30 | job string // job: the id of the job shown | |
| 31 | + | target string // mr: the target branch of the merge request shown | |
| 31 | 32 | // sha is the commit of the run of the job shown, read once when the stream | |
| 32 | 33 | // is set up: the annotations link to files at it, and it never changes. | |
| 33 | 34 | sha string | |
| @@ -60,7 +61,7 @@ func liveEvent(we ports.WatchEvent) bool { | |||
| 60 | 61 | } | |
| 61 | 62 | switch we.Event.(type) { | |
| 62 | 63 | case domain.RepoCreated, domain.RepoUpdated, domain.RepoDeleted, domain.PushEvent, | |
| 63 | - | domain.MergeRequestOpened, domain.MergeRequestMerged, domain.MergeRequestClosed, domain.MergeRequestReady, domain.CommentAdded, domain.CommentEdited, domain.CommentDeleted, domain.CommentResolved, | |
| 64 | + | domain.MergeRequestOpened, domain.MergeRequestMerged, domain.MergeRequestClosed, domain.MergeRequestReady, domain.MergeRequestMergeability, domain.CommentAdded, domain.CommentEdited, domain.CommentDeleted, domain.CommentResolved, | |
| 64 | 65 | domain.RunUpdated, domain.JobUpdated, | |
| 65 | 66 | domain.IssueOpened, domain.IssueClosed, domain.IssueReopened, domain.IssueCommented, domain.IssueSubmitted, domain.IssuePendingRemoved: | |
| 66 | 67 | return true | |
| @@ -188,6 +189,11 @@ func (l *liveStream) repoPage(ctx context.Context, we ports.WatchEvent) ([]fragm | |||
| 188 | 189 | return l.mrNotice(ctx, ev.MergeRequest.Number, "this merge request was closed — reload") | |
| 189 | 190 | case domain.MergeRequestReady: | |
| 190 | 191 | return l.mrNotice(ctx, ev.MergeRequest.Number, "this merge request is ready for review — reload") | |
| 192 | + | case domain.MergeRequestMergeability: | |
| 193 | + | if l.view != "mr" || strconv.FormatInt(ev.Number, 10) != l.n { | |
| 194 | + | return nil, nil | |
| 195 | + | } | |
| 196 | + | return l.mrMergeability(ctx) | |
| 191 | 197 | case domain.CommentEdited: | |
| 192 | 198 | return l.mrNotice(ctx, ev.Number, "a note was edited — reload") | |
| 193 | 199 | case domain.CommentDeleted: | |
| @@ -454,21 +460,65 @@ func (l *liveStream) issueNotice(ctx context.Context, number int64, text string) | |||
| 454 | 460 | return nil, nil | |
| 455 | 461 | } | |
| 456 | 462 | ||
| 457 | - | // mrPushed announces a push to the source branch of the merge request shown. | |
| 463 | + | // mrPushed announces a push to the source branch of the merge request shown, | |
| 464 | + | // and has the badge follow a push to its source or its target. | |
| 458 | 465 | func (l *liveStream) mrPushed(ctx context.Context, p domain.PushEvent) ([]fragment, error) { | |
| 459 | - | if l.ref == "" { | |
| 466 | + | n, err := strconv.ParseInt(l.n, 10, 64) | |
| 467 | + | if err != nil { | |
| 460 | 468 | return nil, nil | |
| 461 | 469 | } | |
| 470 | + | var out []fragment | |
| 471 | + | moved := false | |
| 462 | 472 | for _, u := range p.Updates { | |
| 463 | - | if u.Ref == "refs/heads/"+l.ref { | |
| 464 | - | n, err := strconv.ParseInt(l.n, 10, 64) | |
| 473 | + | switch { | |
| 474 | + | case l.ref != "" && u.Ref == "refs/heads/"+l.ref: | |
| 475 | + | notice, err := l.notice(ctx, "the branch changed — reload", mrHref(l.repo, n)) | |
| 465 | 476 | if err != nil { | |
| 466 | - | return nil, nil | |
| 477 | + | return nil, err | |
| 467 | 478 | } | |
| 468 | - | return l.notice(ctx, "the branch changed — reload", mrHref(l.repo, n)) | |
| 479 | + | out = append(out, notice...) | |
| 480 | + | moved = true | |
| 481 | + | case l.target != "" && u.Ref == "refs/heads/"+l.target: | |
| 482 | + | moved = true | |
| 483 | + | } | |
| 484 | + | if moved && len(out) > 0 { | |
| 485 | + | break | |
| 469 | 486 | } | |
| 470 | 487 | } | |
| 471 | - | return nil, nil | |
| 488 | + | if !moved { | |
| 489 | + | return nil, nil | |
| 490 | + | } | |
| 491 | + | badge, err := l.mrMergeability(ctx) | |
| 492 | + | return append(out, badge...), err | |
| 493 | + | } | |
| 494 | + | ||
| 495 | + | // mrMergeability is the badge of the merge box, read again: the result for the | |
| 496 | + | // tips as they are now, "checking" until the server has one (it then sends the | |
| 497 | + | // MergeRequestMergeability that turns the badge). Nothing for a request that | |
| 498 | + | // is not open. | |
| 499 | + | func (l *liveStream) mrMergeability(ctx context.Context) ([]fragment, error) { | |
| 500 | + | if !SignedIn(ctx) || l.s.cfg.MergeRequests == nil { | |
| 501 | + | return nil, nil // the box is the owner's | |
| 502 | + | } | |
| 503 | + | name, err := domain.ParseRepoName(l.repo) | |
| 504 | + | if err != nil { | |
| 505 | + | return nil, nil | |
| 506 | + | } | |
| 507 | + | n, err := strconv.ParseInt(l.n, 10, 64) | |
| 508 | + | if err != nil { | |
| 509 | + | return nil, nil | |
| 510 | + | } | |
| 511 | + | m, err := l.s.cfg.MergeRequests.Mergeability(ctx, principal(ctx), name, n) | |
| 512 | + | switch { | |
| 513 | + | case errors.Is(err, domain.ErrNotFound): | |
| 514 | + | return gap, nil | |
| 515 | + | case err != nil: | |
| 516 | + | return nil, err | |
| 517 | + | case m.State == "": | |
| 518 | + | return nil, nil | |
| 519 | + | } | |
| 520 | + | f, err := renderFragment(ctx, "mr-mergeability", MRMergeabilityFragment(mergeabilityView(m, l.target))) | |
| 521 | + | return []fragment{f}, err | |
| 472 | 522 | } | |
| 473 | 523 | ||
| 474 | 524 | func (l *liveStream) notice(ctx context.Context, text, href string) ([]fragment, error) { | |
M internal/adapters/web/fragments.templ +1 -1
| @@ -102,7 +102,7 @@ templ Live(l Layout) { | |||
| 102 | 102 | htmx:sse:close, htmx:sse:error -> ermiteLive.lost(); | |
| 103 | 103 | htmx:sse:error -> ermiteLive.error(event.detail.status); | |
| 104 | 104 | htmx:sse:before:connection -> ermiteLive.gate(event); | |
| 105 | - | home-list, repo-last-push, refs-list, commits-head, mr-notice, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data); | |
| 105 | + | home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data); | |
| 106 | 106 | gap -> ermiteLive.reload()" | |
| 107 | 107 | ></span> | |
| 108 | 108 | } | |
M internal/adapters/web/fragments_templ.go +1 -1 generated file: collapsed
M internal/adapters/web/fragments_test.go +1 -0
| @@ -33,6 +33,7 @@ func TestLiveSlotsAreStableStatusRegions(t *testing.T) { | |||
| 33 | 33 | }{ | |
| 34 | 34 | {"empty commits slot", LiveSlot("commits-head"), "commits-head", ""}, | |
| 35 | 35 | {"empty mr slot", LiveSlot("mr-notice"), "mr-notice", ""}, | |
| 36 | + | {"mergeability slot, as sent by the stream", MRMergeabilityFragment(MergeabilityView{State: "up_to_date", Label: "Ready to merge", FFOk: true, MergeOk: true}), "mr-mergeability", "Ready to merge"}, | |
| 36 | 37 | {"commits banner", CommitsBanner("2 new commits — reload", "/x/commits/main"), "commits-head", "2 new commits — reload"}, | |
| 37 | 38 | {"mr notice", MRNotice("a comment was added — reload", "/x/merge-requests/1"), "mr-notice", "a comment was added — reload"}, | |
| 38 | 39 | } { | |
M internal/adapters/web/mr.go +149 -18
| @@ -196,10 +196,38 @@ type MergeBox struct { | |||
| 196 | 196 | // CanDeleteBranch offers to delete the source after the merge: it is not | |
| 197 | 197 | // the default branch. The server checks again, with the open requests. | |
| 198 | 198 | CanDeleteBranch bool | |
| 199 | + | // Mergeability is the live badge of the box and what the modes depend on. | |
| 200 | + | Mergeability MergeabilityView | |
| 201 | + | // Signed is true when a mode that creates a commit (squash, merge commit) | |
| 202 | + | // is offered; Fingerprint is then the one of the key that signs it, empty | |
| 203 | + | // when the server does not tell. | |
| 204 | + | Signed bool | |
| 205 | + | Fingerprint string | |
| 206 | + | // Error is why the last merge was refused, shown in the box. | |
| 207 | + | Error string | |
| 199 | 208 | } | |
| 200 | 209 | ||
| 201 | - | // ModeOption is a merge mode the repository allows. | |
| 202 | - | type ModeOption struct{ Value, Label string } | |
| 210 | + | // ModeOption is a merge mode the repository allows. Disabled says that the | |
| 211 | + | // mode is not possible for the current state of the request, Reason why. | |
| 212 | + | type ModeOption struct { | |
| 213 | + | Value, Label string | |
| 214 | + | Disabled bool | |
| 215 | + | Reason string | |
| 216 | + | } | |
| 217 | + | ||
| 218 | + | // MergeabilityView is the mergeability of an open request, ready to show: the | |
| 219 | + | // badge, and what the merge modes depend on (the flags are what the controls | |
| 220 | + | // of the box read, live). | |
| 221 | + | type MergeabilityView struct { | |
| 222 | + | State, Label, Detail string | |
| 223 | + | Paths []string | |
| 224 | + | // PathsNote says that more paths conflict than are listed. | |
| 225 | + | PathsNote string | |
| 226 | + | // FFOk and MergeOk say whether fast-forward, squash and merge commit are | |
| 227 | + | // possible; the reasons say why not. | |
| 228 | + | FFOk, MergeOk bool | |
| 229 | + | FFReason, MergeReason string | |
| 230 | + | } | |
| 203 | 231 | ||
| 204 | 232 | // SinceNav switches between the whole diff and the diff since the last read. | |
| 205 | 233 | type SinceNav struct { | |
| @@ -233,6 +261,12 @@ type MRPage struct { | |||
| 233 | 261 | MergedShort string | |
| 234 | 262 | MergedHref string | |
| 235 | 263 | MergeMode string | |
| 264 | + | // Deleting is the source branch that a merge just asked to delete: the | |
| 265 | + | // deletion follows the answer. | |
| 266 | + | Deleting string | |
| 267 | + | // MergeError is why a merge was refused when the page has no merge box | |
| 268 | + | // to show it in (the request is not open any more). | |
| 269 | + | MergeError string | |
| 236 | 270 | ||
| 237 | 271 | Since *SinceNav | |
| 238 | 272 | SinceNote string | |
| @@ -346,10 +380,59 @@ func modeName(m domain.MergeMode) string { | |||
| 346 | 380 | return "fast-forward" | |
| 347 | 381 | case domain.MergeModeSquash: | |
| 348 | 382 | return "squash" | |
| 383 | + | case domain.MergeModeMerge: | |
| 384 | + | return "merge commit" | |
| 349 | 385 | } | |
| 350 | 386 | return "" | |
| 351 | 387 | } | |
| 352 | 388 | ||
| 389 | + | // mergeabilityView converts the mergeability of a request into the badge and | |
| 390 | + | // the flags of the controls of the merge box. A request without result is | |
| 391 | + | // being checked. | |
| 392 | + | func mergeabilityView(m domain.Mergeability, target string) MergeabilityView { | |
| 393 | + | v := MergeabilityView{State: string(domain.MergeabilityChecking), Label: "Checking…"} | |
| 394 | + | v.FFOk, v.FFReason = m.Allows(domain.MergeModeFastForward) | |
| 395 | + | v.MergeOk, v.MergeReason = m.Allows(domain.MergeModeSquash) | |
| 396 | + | target = visibleText(target) | |
| 397 | + | switch m.State { | |
| 398 | + | case domain.MergeabilityUpToDate: | |
| 399 | + | v.State, v.Label = string(m.State), "Ready to merge" | |
| 400 | + | case domain.MergeabilityBehind: | |
| 401 | + | v.State, v.Label, v.Detail = string(m.State), "Behind "+target+" by "+behindText(m), "merges without conflicts" | |
| 402 | + | case domain.MergeabilityConflict: | |
| 403 | + | v.State, v.Label, v.Detail = string(m.State), "Conflicts with "+target, "rebase needed" | |
| 404 | + | v.Paths, v.PathsNote = conflictList(m) | |
| 405 | + | } | |
| 406 | + | return v | |
| 407 | + | } | |
| 408 | + | ||
| 409 | + | // behindText is "2 commits", "1 commit" or "1000+ commits". | |
| 410 | + | func behindText(m domain.Mergeability) string { | |
| 411 | + | if m.Behind == 1 && !m.BehindCapped { | |
| 412 | + | return "1 commit" | |
| 413 | + | } | |
| 414 | + | n := itoa(m.Behind) | |
| 415 | + | if m.BehindCapped { | |
| 416 | + | n += "+" | |
| 417 | + | } | |
| 418 | + | return n + " commits" | |
| 419 | + | } | |
| 420 | + | ||
| 421 | + | // conflictList is the conflicting paths to show, and the note that says what | |
| 422 | + | // the list lacks. | |
| 423 | + | func conflictList(m domain.Mergeability) (paths []string, note string) { | |
| 424 | + | for _, p := range m.ConflictPaths { | |
| 425 | + | paths = append(paths, visibleText(p)) | |
| 426 | + | } | |
| 427 | + | switch { | |
| 428 | + | case len(paths) == 0: | |
| 429 | + | note = "no common history." | |
| 430 | + | case m.ConflictPathsTruncated: | |
| 431 | + | note = "more files conflict than are listed." | |
| 432 | + | } | |
| 433 | + | return paths, note | |
| 434 | + | } | |
| 435 | + | ||
| 353 | 436 | // mrRow converts a merge request of a listing. | |
| 354 | 437 | func (s *Server) mrRow(repo string, m domain.MergeRequest) MRRow { | |
| 355 | 438 | row := MRRow{ | |
| @@ -540,6 +623,9 @@ func (s *Server) mrPage(ctx context.Context, r *http.Request, since bool) (templ | |||
| 540 | 623 | p.MergedHref = repoHref(d.name(), "commit", m.MergedCommitID.String()) | |
| 541 | 624 | p.MergeMode = modeName(m.MergeMode) | |
| 542 | 625 | } | |
| 626 | + | if signed && m.State == domain.MRStateMerged && r.URL.Query().Get("deleting") == "1" && d.hasBranch(m.Source) { | |
| 627 | + | p.Deleting = visibleText(m.Source) | |
| 628 | + | } | |
| 543 | 629 | for _, mode := range d.repo.Settings.AllowedMergeModes { | |
| 544 | 630 | if name := modeName(mode); name != "" { | |
| 545 | 631 | p.Modes = append(p.Modes, name) | |
| @@ -624,11 +710,15 @@ func (s *Server) mrPage(ctx context.Context, r *http.Request, since bool) (templ | |||
| 624 | 710 | p.Invisible = invisible | |
| 625 | 711 | p.DiffView = newDiffView(r, lines, withFrom(self+"?view=unified", from), withFrom(self+"?view=split", from)) | |
| 626 | 712 | if signed && open { | |
| 627 | - | p.Box = s.mergeBox(d, n, m.Source, head, lastRead) | |
| 713 | + | p.Box = s.mergeBox(d, n, m.Source, head, lastRead, mergeabilityView(res.Mergeability, m.Target)) | |
| 714 | + | p.Box.Error = mergeErrorOf(ctx) | |
| 628 | 715 | if m.IsDraft() { | |
| 629 | 716 | p.Box.ReadyAction = mrHref(d.name(), n, "ready") | |
| 630 | 717 | } | |
| 631 | 718 | } | |
| 719 | + | if p.Box == nil { | |
| 720 | + | p.MergeError = mergeErrorOf(ctx) // the request is not open any more | |
| 721 | + | } | |
| 632 | 722 | if canWrite { | |
| 633 | 723 | p.GeneralForm = &CommentForm{ | |
| 634 | 724 | ID: "cf-general", Action: mrHref(d.name(), n, "comments"), Title: "comment on the merge request", | |
| @@ -652,7 +742,7 @@ func (s *Server) mrPage(ctx context.Context, r *http.Request, since bool) (templ | |||
| 652 | 742 | l.Crumbs = append(l.Crumbs, Crumb{Label: "since", Current: true}) | |
| 653 | 743 | } | |
| 654 | 744 | p.Live = true | |
| 655 | - | 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)) | |
| 745 | + | 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)+"&target="+url.QueryEscape(m.Target)) | |
| 656 | 746 | l.HasRail = true | |
| 657 | 747 | l.MR = true | |
| 658 | 748 | return MergeRequest(l, p), http.StatusOK, nil | |
| @@ -669,37 +759,78 @@ func (d *repoData) hasBranch(name string) bool { | |||
| 669 | 759 | } | |
| 670 | 760 | ||
| 671 | 761 | // mergeBox is what the owner can do with an open request. | |
| 672 | - | func (s *Server) mergeBox(d *repoData, n int64, source, head, lastRead string) *MergeBox { | |
| 762 | + | func (s *Server) mergeBox(d *repoData, n int64, source, head, lastRead string, mg MergeabilityView) *MergeBox { | |
| 673 | 763 | b := &MergeBox{ | |
| 674 | 764 | MergeAction: mrHref(d.name(), n, "merge"), CloseAction: mrHref(d.name(), n, "close"), | |
| 675 | 765 | ReadAction: mrHref(d.name(), n, "read"), Head: head, HeadGone: head == "", | |
| 676 | 766 | CanMarkRead: head != "" && head != lastRead, | |
| 677 | 767 | CanDeleteBranch: source != d.repo.DefaultBranch, | |
| 768 | + | Mergeability: mg, | |
| 769 | + | } | |
| 770 | + | var lines []string | |
| 771 | + | cmd := "" | |
| 772 | + | if head != "" { | |
| 773 | + | cmd = "ermite mr merge " + shellWord(d.name()) + " " + strconv.FormatInt(n, 10) + " --head " + head | |
| 678 | 774 | } | |
| 679 | - | ff, sq := false, false | |
| 680 | 775 | for _, mode := range d.repo.Settings.AllowedMergeModes { | |
| 681 | 776 | switch mode { | |
| 682 | 777 | case domain.MergeModeFastForward: | |
| 683 | - | ff = true | |
| 684 | - | b.Modes = append(b.Modes, ModeOption{Value: "fast-forward", Label: "fast-forward"}) | |
| 778 | + | b.Modes = append(b.Modes, ModeOption{Value: "fast-forward", Label: "fast-forward", Disabled: !mg.FFOk, Reason: reasonIf(!mg.FFOk, mg.FFReason)}) | |
| 779 | + | lines = append(lines, cmd) | |
| 685 | 780 | case domain.MergeModeSquash: | |
| 686 | - | sq = true | |
| 687 | - | b.Modes = append(b.Modes, ModeOption{Value: "squash", Label: "squash (one signed commit)"}) | |
| 781 | + | b.Signed = true | |
| 782 | + | b.Modes = append(b.Modes, ModeOption{Value: "squash", Label: "squash (one signed commit)", Disabled: !mg.MergeOk, Reason: reasonIf(!mg.MergeOk, mg.MergeReason)}) | |
| 783 | + | lines = append(lines, cmd+" --mode squash") | |
| 784 | + | case domain.MergeModeMerge: | |
| 785 | + | b.Signed = true | |
| 786 | + | b.Modes = append(b.Modes, ModeOption{Value: "merge", Label: "merge commit (signed)", Disabled: !mg.MergeOk, Reason: reasonIf(!mg.MergeOk, mg.MergeReason)}) | |
| 787 | + | lines = append(lines, cmd+" --mode merge") | |
| 688 | 788 | } | |
| 689 | 789 | } | |
| 790 | + | if b.Signed && s.cfg.SigningKey != nil { | |
| 791 | + | b.Fingerprint = s.cfg.SigningKey.Info().Key.Fingerprint | |
| 792 | + | } | |
| 690 | 793 | if head != "" { | |
| 691 | - | cmd := "ermite mr merge " + shellWord(d.name()) + " " + strconv.FormatInt(n, 10) + " --head " + head | |
| 692 | - | b.CLI = Clone{ID: "cli-merge"} | |
| 693 | - | if ff { | |
| 694 | - | b.CLI.Lines = append(b.CLI.Lines, cmd) | |
| 695 | - | } | |
| 696 | - | if sq { | |
| 697 | - | b.CLI.Lines = append(b.CLI.Lines, cmd+" --squash") | |
| 698 | - | } | |
| 794 | + | b.CLI = Clone{ID: "cli-merge", Lines: lines} | |
| 699 | 795 | } | |
| 700 | 796 | return b | |
| 701 | 797 | } | |
| 702 | 798 | ||
| 799 | + | func reasonIf(cond bool, reason string) string { | |
| 800 | + | if cond { | |
| 801 | + | return reason | |
| 802 | + | } | |
| 803 | + | return "" | |
| 804 | + | } | |
| 805 | + | ||
| 806 | + | // firstModeOK is the index of the first mode that is possible, -1 for none. | |
| 807 | + | func (b *MergeBox) firstModeOK() int { | |
| 808 | + | for i, m := range b.Modes { | |
| 809 | + | if !m.Disabled { | |
| 810 | + | return i | |
| 811 | + | } | |
| 812 | + | } | |
| 813 | + | return -1 | |
| 814 | + | } | |
| 815 | + | ||
| 816 | + | // Checked is the mode the form has checked: the first that is possible, else | |
| 817 | + | // the first (it is disabled, and a live update may enable it). | |
| 818 | + | func (b *MergeBox) Checked() int { return max(b.firstModeOK(), 0) } | |
| 819 | + | ||
| 820 | + | // CanMerge tells whether some mode is possible: the button is enabled. | |
| 821 | + | func (b *MergeBox) CanMerge() bool { return b.firstModeOK() >= 0 } | |
| 822 | + | ||
| 823 | + | // MergeTitle is why the button is disabled, empty when it is not. | |
| 824 | + | func (b *MergeBox) MergeTitle() string { | |
| 825 | + | if b.CanMerge() { | |
| 826 | + | return "" | |
| 827 | + | } | |
| 828 | + | if len(b.Modes) == 0 { | |
| 829 | + | return "the repository allows no merge mode" | |
| 830 | + | } | |
| 831 | + | return b.Modes[0].Reason | |
| 832 | + | } | |
| 833 | + | ||
| 703 | 834 | // shellWord quotes s for a shell unless it is made of safe characters, which | |
| 704 | 835 | // a repository name always is. | |
| 705 | 836 | func shellWord(s string) string { | |
M internal/adapters/web/mr.templ +89 -4
| @@ -2,7 +2,10 @@ | |||
| 2 | 2 | ||
| 3 | 3 | package web | |
| 4 | 4 | ||
| 5 | - | import "strings" | |
| 5 | + | import ( | |
| 6 | + | "strconv" | |
| 7 | + | "strings" | |
| 8 | + | ) | |
| 6 | 9 | ||
| 7 | 10 | // MRList is the list of the merge requests of a repository: the open ones, | |
| 8 | 11 | // then the merged and closed ones. | |
| @@ -145,6 +148,12 @@ templ MergeRequest(l Layout, p MRPage) { | |||
| 145 | 148 | </article> | |
| 146 | 149 | } | |
| 147 | 150 | </section> | |
| 151 | + | if p.Deleting != "" { | |
| 152 | + | <p class="note" role="status">merged. the branch { p.Deleting } is being deleted.</p> | |
| 153 | + | } | |
| 154 | + | if p.MergeError != "" { | |
| 155 | + | <p class="d-err" role="alert">{ p.MergeError }</p> | |
| 156 | + | } | |
| 148 | 157 | @LiveSlot("mr-notice") | |
| 149 | 158 | if p.Since != nil { | |
| 150 | 159 | <nav class="since" aria-label="Diff range"> | |
| @@ -446,8 +455,13 @@ templ mrBar(p MRPage) { | |||
| 446 | 455 | // mergeMenu is the merge of an open request, for the owner: the form (it | |
| 447 | 456 | // carries the head that was read), or "Ready for review" for a draft. The | |
| 448 | 457 | // mode and the branch deletion have keys (1-3, D) while it is open. | |
| 458 | + | // | |
| 459 | + | // The badge is a live slot (mr-mergeability); the modes and the button read | |
| 460 | + | // its data-* attributes (hx-live bindings), so that they follow the slot when | |
| 461 | + | // the stream replaces its content. The server renders the same state in the | |
| 462 | + | // attributes, for a page without scripts. | |
| 449 | 463 | templ mergeMenu(p MRPage) { | |
| 450 | - | <details class="menu mmenu" id="merge-menu" data-state="open"> | |
| 464 | + | <details class="menu mmenu" id="merge-menu" data-state="open" open?={ p.Box.Error != "" }> | |
| 451 | 465 | <summary class="btn primary" aria-keyshortcuts="Shift+M"> | |
| 452 | 466 | if p.Box.ReadyAction != "" { | |
| 453 | 467 | Draft | |
| @@ -469,19 +483,32 @@ templ mergeMenu(p MRPage) { | |||
| 469 | 483 | <dt>into</dt> | |
| 470 | 484 | <dd>{ p.Target }</dd> | |
| 471 | 485 | </dl> | |
| 486 | + | if p.Box.Error != "" { | |
| 487 | + | <p class="d-err" role="alert">{ p.Box.Error }</p> | |
| 488 | + | } | |
| 472 | 489 | if p.Box.ReadyAction != "" { | |
| 473 | 490 | <p class="d-draft">this is a draft: it cannot be merged until it is ready for review.</p> | |
| 474 | 491 | <form class="d-btns" method="post" action={ templ.SafeURL(p.Box.ReadyAction) }> | |
| 475 | 492 | <button type="submit" class="btn primary" data-default title="remove the draft prefix from the title">Ready for review</button> | |
| 476 | 493 | </form> | |
| 477 | 494 | } else if !p.Box.HeadGone { | |
| 495 | + | @mrMergeabilitySlot(p.Box.Mergeability) | |
| 478 | 496 | <form class="d-acts" method="post" action={ templ.SafeURL(p.Box.MergeAction) }> | |
| 479 | 497 | <input type="hidden" name="head_id" value={ p.Box.Head }/> | |
| 480 | 498 | <fieldset class="d-modes"> | |
| 481 | 499 | <legend class="sr">merge mode</legend> | |
| 482 | 500 | for i, m := range p.Box.Modes { | |
| 483 | 501 | <label> | |
| 484 | - | <input type="radio" name="mode" value={ m.Value } checked?={ i == 0 }/> | |
| 502 | + | <input | |
| 503 | + | type="radio" | |
| 504 | + | name="mode" | |
| 505 | + | value={ m.Value } | |
| 506 | + | checked?={ i == p.Box.Checked() } | |
| 507 | + | disabled?={ m.Disabled } | |
| 508 | + | title={ m.Reason } | |
| 509 | + | :disabled="this.value === 'fast-forward' ? q('[data-mergeability]').data.ffOk !== true : q('[data-mergeability]').data.mergeOk !== true" | |
| 510 | + | :title="this.value === 'fast-forward' ? q('[data-mergeability]').data.ffReason : q('[data-mergeability]').data.mergeReason" | |
| 511 | + | /> | |
| 485 | 512 | { m.Label } | |
| 486 | 513 | if i < 9 { | |
| 487 | 514 | <kbd aria-hidden="true">{ itoa(i + 1) }</kbd> | |
| @@ -489,11 +516,28 @@ templ mergeMenu(p MRPage) { | |||
| 489 | 516 | </label> | |
| 490 | 517 | } | |
| 491 | 518 | </fieldset> | |
| 519 | + | if p.Box.Signed { | |
| 520 | + | <p class="d-note" id="mr-signing"> | |
| 521 | + | squash and merge commits are signed with ermite's own key, generated automatically on first start. | |
| 522 | + | if p.Box.Fingerprint != "" { | |
| 523 | + | { " " }key <code>{ p.Box.Fingerprint }</code>. | |
| 524 | + | } | |
| 525 | + | { " " }<code>ermite signing-key show</code> prints the public key to verify them. | |
| 526 | + | </p> | |
| 527 | + | } | |
| 492 | 528 | if p.Box.CanDeleteBranch { | |
| 493 | 529 | <label class="d-check"><input type="checkbox" name="delete_branch" value="1"/> delete { p.Source } after the merge <kbd aria-hidden="true">D</kbd></label> | |
| 494 | 530 | } | |
| 495 | 531 | <div class="d-btns"> | |
| 496 | - | <button type="submit" class="btn primary" data-default>Merge into { p.Target }</button> | |
| 532 | + | <button | |
| 533 | + | type="submit" | |
| 534 | + | class="btn primary" | |
| 535 | + | data-default | |
| 536 | + | disabled?={ !p.Box.CanMerge() } | |
| 537 | + | title={ p.Box.MergeTitle() } | |
| 538 | + | :disabled="!this.form.querySelector('input[name=mode]:checked:not(:disabled)')" | |
| 539 | + | :title="(this.form.querySelector('input[name=mode]:checked') || {}).title || ''" | |
| 540 | + | >Merge into { p.Target }</button> | |
| 497 | 541 | </div> | |
| 498 | 542 | </form> | |
| 499 | 543 | } | |
| @@ -501,6 +545,47 @@ templ mergeMenu(p MRPage) { | |||
| 501 | 545 | </details> | |
| 502 | 546 | } | |
| 503 | 547 | ||
| 548 | + | // MRMergeability is the content of the mergeability slot: the badge, and the | |
| 549 | + | // state the controls of the merge box read. | |
| 550 | + | templ MRMergeability(v MergeabilityView) { | |
| 551 | + | <span | |
| 552 | + | data-mergeability | |
| 553 | + | data-state={ v.State } | |
| 554 | + | data-ff-ok={ strconv.FormatBool(v.FFOk) } | |
| 555 | + | data-merge-ok={ strconv.FormatBool(v.MergeOk) } | |
| 556 | + | data-ff-reason={ v.FFReason } | |
| 557 | + | data-merge-reason={ v.MergeReason } | |
| 558 | + | > | |
| 559 | + | <span class="badge" data-state={ v.State }>{ v.Label }</span> | |
| 560 | + | if v.Detail != "" { | |
| 561 | + | { " " }<span class="dim">{ v.Detail }</span> | |
| 562 | + | } | |
| 563 | + | </span> | |
| 564 | + | if len(v.Paths) > 0 { | |
| 565 | + | <ul class="mg-paths" aria-label="conflicting files"> | |
| 566 | + | for _, path := range v.Paths { | |
| 567 | + | <li><code>{ path }</code></li> | |
| 568 | + | } | |
| 569 | + | </ul> | |
| 570 | + | } | |
| 571 | + | if v.PathsNote != "" { | |
| 572 | + | <p class="dim">{ v.PathsNote }</p> | |
| 573 | + | } | |
| 574 | + | } | |
| 575 | + | ||
| 576 | + | // mrMergeabilitySlot is the status region of the badge, with its content: the | |
| 577 | + | // stream replaces the children, never the element (see LiveSlot). | |
| 578 | + | templ mrMergeabilitySlot(v MergeabilityView) { | |
| 579 | + | <div id="mr-mergeability" class="live-slot" role="status" data-slot> | |
| 580 | + | @MRMergeability(v) | |
| 581 | + | </div> | |
| 582 | + | } | |
| 583 | + | ||
| 584 | + | // MRMergeabilityFragment is the slot as the stream sends it. | |
| 585 | + | templ MRMergeabilityFragment(v MergeabilityView) { | |
| 586 | + | @mrMergeabilitySlot(v) | |
| 587 | + | } | |
| 588 | + | ||
| 504 | 589 | // moreMenu is what else the owner does with an open request: mark the head | |
| 505 | 590 | // read, close it, merge it from a terminal. | |
| 506 | 591 | templ moreMenu(p MRPage) { | |
M internal/adapters/web/mr_post.go +55 -3
| @@ -60,6 +60,10 @@ func (s *Server) mrAction(h func(ctx context.Context, r *http.Request) (to strin | |||
| 60 | 60 | return | |
| 61 | 61 | } | |
| 62 | 62 | to, back, err := h(ctx, r) | |
| 63 | + | var re *rerender | |
| 64 | + | if errors.As(err, &re) && s.rerenderMR(ctx, w, r, re) { | |
| 65 | + | return | |
| 66 | + | } | |
| 63 | 67 | if err != nil { | |
| 64 | 68 | s.actionFailed(ctx, w, r, err, back) | |
| 65 | 69 | return | |
| @@ -68,6 +72,43 @@ func (s *Server) mrAction(h func(ctx context.Context, r *http.Request) (to strin | |||
| 68 | 72 | } | |
| 69 | 73 | } | |
| 70 | 74 | ||
| 75 | + | // rerender is the error of a merge the use case refused for a reason the | |
| 76 | + | // owner reads on the page of the request: the page is shown again, with the | |
| 77 | + | // status, and the reason in its merge box. Any other error keeps actionFailed. | |
| 78 | + | type rerender struct { | |
| 79 | + | status int | |
| 80 | + | msg string | |
| 81 | + | err error | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | func (e *rerender) Error() string { return e.err.Error() } | |
| 85 | + | func (e *rerender) Unwrap() error { return e.err } | |
| 86 | + | ||
| 87 | + | type mergeErrorKey struct{} | |
| 88 | + | ||
| 89 | + | // withMergeError carries the reason of a refused merge to the page. | |
| 90 | + | func withMergeError(ctx context.Context, msg string) context.Context { | |
| 91 | + | return context.WithValue(ctx, mergeErrorKey{}, msg) | |
| 92 | + | } | |
| 93 | + | ||
| 94 | + | func mergeErrorOf(ctx context.Context) string { | |
| 95 | + | msg, _ := ctx.Value(mergeErrorKey{}).(string) | |
| 96 | + | return msg | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | // rerenderMR writes the page of the request with the reason of the refusal. | |
| 100 | + | // It reports false when the page cannot be built; the caller then answers as | |
| 101 | + | // for any refused action. | |
| 102 | + | func (s *Server) rerenderMR(ctx context.Context, w http.ResponseWriter, r *http.Request, re *rerender) bool { | |
| 103 | + | c, _, err := s.mrPage(withMergeError(ctx, re.msg), r, false) | |
| 104 | + | if err != nil { | |
| 105 | + | return false | |
| 106 | + | } | |
| 107 | + | w.Header().Set("Cache-Control", "no-store") | |
| 108 | + | s.write(ctx, w, r, re.status, c) | |
| 109 | + | return true | |
| 110 | + | } | |
| 111 | + | ||
| 71 | 112 | // actionFailed answers a refused action. | |
| 72 | 113 | func (s *Server) actionFailed(ctx context.Context, w http.ResponseWriter, r *http.Request, err error, back string) { | |
| 73 | 114 | // The words of the use case, for the rules the owner can fix. | |
| @@ -322,8 +363,11 @@ func (s *Server) mrMerge(ctx context.Context, r *http.Request) (string, string, | |||
| 322 | 363 | mode = domain.MergeModeFastForward | |
| 323 | 364 | case "squash": | |
| 324 | 365 | mode = domain.MergeModeSquash | |
| 366 | + | case "merge": | |
| 367 | + | mode = domain.MergeModeMerge | |
| 325 | 368 | default: | |
| 326 | - | return "", back, formError("mode must be fast-forward or squash") | |
| 369 | + | err := formError("mode must be fast-forward, squash or merge") | |
| 370 | + | return "", back, &rerender{status: http.StatusBadRequest, msg: err.Error(), err: err} | |
| 327 | 371 | } | |
| 328 | 372 | name, err := domain.ParseRepoName(repo) | |
| 329 | 373 | if err != nil { | |
| @@ -333,11 +377,19 @@ func (s *Server) mrMerge(ctx context.Context, r *http.Request) (string, string, | |||
| 333 | 377 | if err != nil { | |
| 334 | 378 | return "", back, err | |
| 335 | 379 | } | |
| 336 | - | if _, err := s.cfg.MergeRequests.Merge(ctx, principal(ctx), name, n, ports.MergeParams{ | |
| 380 | + | res, err := s.cfg.MergeRequests.Merge(ctx, principal(ctx), name, n, ports.MergeParams{ | |
| 337 | 381 | Mode: mode, HeadID: head, DeleteSource: r.PostFormValue("delete_branch") == "1", | |
| 338 | - | }); err != nil { | |
| 382 | + | }) | |
| 383 | + | switch { | |
| 384 | + | case errors.Is(err, domain.ErrPreconditionFailed), errors.Is(err, domain.ErrConflict): | |
| 385 | + | return "", back, &rerender{status: http.StatusConflict, msg: rule(err.Error(), "the merge request is not in a state that allows this."), err: err} | |
| 386 | + | case err != nil: | |
| 339 | 387 | return "", back, err | |
| 340 | 388 | } | |
| 389 | + | if res.SourceDeleted { | |
| 390 | + | // The deletion follows the answer: the page says it is being done. | |
| 391 | + | return back + "?deleting=1", back, nil | |
| 392 | + | } | |
| 341 | 393 | return back, back, nil | |
| 342 | 394 | } | |
| 343 | 395 | ||
M internal/adapters/web/mr_templ.go +1184 -737
| @@ -10,7 +10,10 @@ package web | |||
| 10 | 10 | import "github.com/a-h/templ" | |
| 11 | 11 | import templruntime "github.com/a-h/templ/runtime" | |
| 12 | 12 | ||
| 13 | - | import "strings" | |
| 13 | + | import ( | |
| 14 | + | "strconv" | |
| 15 | + | "strings" | |
| 16 | + | ) | |
| 14 | 17 | ||
| 15 | 18 | // MRList is the list of the merge requests of a repository: the open ones, | |
| 16 | 19 | // then the merged and closed ones. | |
| @@ -65,7 +68,7 @@ func MRList(l Layout, p MRListPage) templ.Component { | |||
| 65 | 68 | var templ_7745c5c3_Var3 string | |
| 66 | 69 | templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Open))) | |
| 67 | 70 | if templ_7745c5c3_Err != nil { | |
| 68 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 26, Col: 78} | |
| 71 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 29, Col: 78} | |
| 69 | 72 | } | |
| 70 | 73 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) | |
| 71 | 74 | if templ_7745c5c3_Err != nil { | |
| @@ -101,7 +104,7 @@ func MRList(l Layout, p MRListPage) templ.Component { | |||
| 101 | 104 | var templ_7745c5c3_Var4 string | |
| 102 | 105 | templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(p.OpenNote) | |
| 103 | 106 | if templ_7745c5c3_Err != nil { | |
| 104 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 32, Col: 32} | |
| 107 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 35, Col: 32} | |
| 105 | 108 | } | |
| 106 | 109 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) | |
| 107 | 110 | if templ_7745c5c3_Err != nil { | |
| @@ -120,7 +123,7 @@ func MRList(l Layout, p MRListPage) templ.Component { | |||
| 120 | 123 | var templ_7745c5c3_Var5 string | |
| 121 | 124 | templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Done))) | |
| 122 | 125 | if templ_7745c5c3_Err != nil { | |
| 123 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 35, Col: 90} | |
| 126 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 38, Col: 90} | |
| 124 | 127 | } | |
| 125 | 128 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) | |
| 126 | 129 | if templ_7745c5c3_Err != nil { | |
| @@ -161,7 +164,7 @@ func MRList(l Layout, p MRListPage) templ.Component { | |||
| 161 | 164 | var templ_7745c5c3_Var6 templ.SafeURL | |
| 162 | 165 | templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Newest)) | |
| 163 | 166 | if templ_7745c5c3_Err != nil { | |
| 164 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 43, Col: 50} | |
| 167 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 46, Col: 50} | |
| 165 | 168 | } | |
| 166 | 169 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) | |
| 167 | 170 | if templ_7745c5c3_Err != nil { | |
| @@ -185,7 +188,7 @@ func MRList(l Layout, p MRListPage) templ.Component { | |||
| 185 | 188 | var templ_7745c5c3_Var7 templ.SafeURL | |
| 186 | 189 | templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Older)) | |
| 187 | 190 | if templ_7745c5c3_Err != nil { | |
| 188 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 48, Col: 49} | |
| 191 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 51, Col: 49} | |
| 189 | 192 | } | |
| 190 | 193 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) | |
| 191 | 194 | if templ_7745c5c3_Err != nil { | |
| @@ -245,7 +248,7 @@ func mrRows(rows []MRRow, label string) templ.Component { | |||
| 245 | 248 | var templ_7745c5c3_Var9 string | |
| 246 | 249 | templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.ResolveAttributeValue(label) | |
| 247 | 250 | if templ_7745c5c3_Err != nil { | |
| 248 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 59, Col: 44} | |
| 251 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 62, Col: 44} | |
| 249 | 252 | } | |
| 250 | 253 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9) | |
| 251 | 254 | if templ_7745c5c3_Err != nil { | |
| @@ -273,7 +276,7 @@ func mrRows(rows []MRRow, label string) templ.Component { | |||
| 273 | 276 | var templ_7745c5c3_Var10 string | |
| 274 | 277 | templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.State) | |
| 275 | 278 | if templ_7745c5c3_Err != nil { | |
| 276 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 62, Col: 55} | |
| 279 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 65, Col: 55} | |
| 277 | 280 | } | |
| 278 | 281 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10) | |
| 279 | 282 | if templ_7745c5c3_Err != nil { | |
| @@ -286,7 +289,7 @@ func mrRows(rows []MRRow, label string) templ.Component { | |||
| 286 | 289 | var templ_7745c5c3_Var11 string | |
| 287 | 290 | templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(m.Number) | |
| 288 | 291 | if templ_7745c5c3_Err != nil { | |
| 289 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 63, Col: 37} | |
| 292 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 66, Col: 37} | |
| 290 | 293 | } | |
| 291 | 294 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) | |
| 292 | 295 | if templ_7745c5c3_Err != nil { | |
| @@ -299,7 +302,7 @@ func mrRows(rows []MRRow, label string) templ.Component { | |||
| 299 | 302 | var templ_7745c5c3_Var12 string | |
| 300 | 303 | templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(m.State) | |
| 301 | 304 | if templ_7745c5c3_Err != nil { | |
| 302 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 64, Col: 35} | |
| 305 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 67, Col: 35} | |
| 303 | 306 | } | |
| 304 | 307 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) | |
| 305 | 308 | if templ_7745c5c3_Err != nil { | |
| @@ -312,7 +315,7 @@ func mrRows(rows []MRRow, label string) templ.Component { | |||
| 312 | 315 | var templ_7745c5c3_Var13 templ.SafeURL | |
| 313 | 316 | templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Href)) | |
| 314 | 317 | if templ_7745c5c3_Err != nil { | |
| 315 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 65, Col: 59} | |
| 318 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 68, Col: 59} | |
| 316 | 319 | } | |
| 317 | 320 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) | |
| 318 | 321 | if templ_7745c5c3_Err != nil { | |
| @@ -325,7 +328,7 @@ func mrRows(rows []MRRow, label string) templ.Component { | |||
| 325 | 328 | var templ_7745c5c3_Var14 string | |
| 326 | 329 | templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(m.Title) | |
| 327 | 330 | if templ_7745c5c3_Err != nil { | |
| 328 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 65, Col: 71} | |
| 331 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 68, Col: 71} | |
| 329 | 332 | } | |
| 330 | 333 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) | |
| 331 | 334 | if templ_7745c5c3_Err != nil { | |
| @@ -338,7 +341,7 @@ func mrRows(rows []MRRow, label string) templ.Component { | |||
| 338 | 341 | var templ_7745c5c3_Var15 string | |
| 339 | 342 | templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(m.Branches) | |
| 340 | 343 | if templ_7745c5c3_Err != nil { | |
| 341 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 65, Col: 109} | |
| 344 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 68, Col: 109} | |
| 342 | 345 | } | |
| 343 | 346 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) | |
| 344 | 347 | if templ_7745c5c3_Err != nil { | |
| @@ -351,7 +354,7 @@ func mrRows(rows []MRRow, label string) templ.Component { | |||
| 351 | 354 | var templ_7745c5c3_Var16 string | |
| 352 | 355 | templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(m.Author) | |
| 353 | 356 | if templ_7745c5c3_Err != nil { | |
| 354 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 66, Col: 38} | |
| 357 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 69, Col: 38} | |
| 355 | 358 | } | |
| 356 | 359 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) | |
| 357 | 360 | if templ_7745c5c3_Err != nil { | |
| @@ -369,7 +372,7 @@ func mrRows(rows []MRRow, label string) templ.Component { | |||
| 369 | 372 | var templ_7745c5c3_Var17 string | |
| 370 | 373 | templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt) | |
| 371 | 374 | if templ_7745c5c3_Err != nil { | |
| 372 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 69, Col: 33} | |
| 375 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 72, Col: 33} | |
| 373 | 376 | } | |
| 374 | 377 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var17) | |
| 375 | 378 | if templ_7745c5c3_Err != nil { | |
| @@ -382,7 +385,7 @@ func mrRows(rows []MRRow, label string) templ.Component { | |||
| 382 | 385 | var templ_7745c5c3_Var18 string | |
| 383 | 386 | templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt) | |
| 384 | 387 | if templ_7745c5c3_Err != nil { | |
| 385 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 69, Col: 52} | |
| 388 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 72, Col: 52} | |
| 386 | 389 | } | |
| 387 | 390 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18) | |
| 388 | 391 | if templ_7745c5c3_Err != nil { | |
| @@ -395,7 +398,7 @@ func mrRows(rows []MRRow, label string) templ.Component { | |||
| 395 | 398 | var templ_7745c5c3_Var19 string | |
| 396 | 399 | templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(m.When) | |
| 397 | 400 | if templ_7745c5c3_Err != nil { | |
| 398 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 69, Col: 63} | |
| 401 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 72, Col: 63} | |
| 399 | 402 | } | |
| 400 | 403 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) | |
| 401 | 404 | if templ_7745c5c3_Err != nil { | |
| @@ -453,7 +456,7 @@ func mrListRail(p MRListPage) templ.Component { | |||
| 453 | 456 | var templ_7745c5c3_Var21 string | |
| 454 | 457 | templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(p.Default) | |
| 455 | 458 | if templ_7745c5c3_Err != nil { | |
| 456 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 94, Col: 51} | |
| 459 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 97, Col: 51} | |
| 457 | 460 | } | |
| 458 | 461 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21)) | |
| 459 | 462 | if templ_7745c5c3_Err != nil { | |
| @@ -471,7 +474,7 @@ func mrListRail(p MRListPage) templ.Component { | |||
| 471 | 474 | var templ_7745c5c3_Var22 templ.SafeURL | |
| 472 | 475 | templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(b.Href)) | |
| 473 | 476 | if templ_7745c5c3_Err != nil { | |
| 474 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 97, Col: 36} | |
| 477 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 100, Col: 36} | |
| 475 | 478 | } | |
| 476 | 479 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) | |
| 477 | 480 | if templ_7745c5c3_Err != nil { | |
| @@ -484,7 +487,7 @@ func mrListRail(p MRListPage) templ.Component { | |||
| 484 | 487 | var templ_7745c5c3_Var23 string | |
| 485 | 488 | templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(b.Name) | |
| 486 | 489 | if templ_7745c5c3_Err != nil { | |
| 487 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 97, Col: 47} | |
| 490 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 100, Col: 47} | |
| 488 | 491 | } | |
| 489 | 492 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) | |
| 490 | 493 | if templ_7745c5c3_Err != nil { | |
| @@ -560,7 +563,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 560 | 563 | var templ_7745c5c3_Var26 string | |
| 561 | 564 | templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(p.Number) | |
| 562 | 565 | if templ_7745c5c3_Err != nil { | |
| 563 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 114, Col: 37} | |
| 566 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 117, Col: 37} | |
| 564 | 567 | } | |
| 565 | 568 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26)) | |
| 566 | 569 | if templ_7745c5c3_Err != nil { | |
| @@ -573,7 +576,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 573 | 576 | var templ_7745c5c3_Var27 string | |
| 574 | 577 | templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(p.Title) | |
| 575 | 578 | if templ_7745c5c3_Err != nil { | |
| 576 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 114, Col: 56} | |
| 579 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 117, Col: 56} | |
| 577 | 580 | } | |
| 578 | 581 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27)) | |
| 579 | 582 | if templ_7745c5c3_Err != nil { | |
| @@ -586,7 +589,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 586 | 589 | var templ_7745c5c3_Var28 string | |
| 587 | 590 | templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.State) | |
| 588 | 591 | if templ_7745c5c3_Err != nil { | |
| 589 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 117, Col: 46} | |
| 592 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 120, Col: 46} | |
| 590 | 593 | } | |
| 591 | 594 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var28) | |
| 592 | 595 | if templ_7745c5c3_Err != nil { | |
| @@ -599,7 +602,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 599 | 602 | var templ_7745c5c3_Var29 string | |
| 600 | 603 | templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(p.State) | |
| 601 | 604 | if templ_7745c5c3_Err != nil { | |
| 602 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 118, Col: 15} | |
| 605 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 121, Col: 15} | |
| 603 | 606 | } | |
| 604 | 607 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29)) | |
| 605 | 608 | if templ_7745c5c3_Err != nil { | |
| @@ -613,7 +616,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 613 | 616 | var templ_7745c5c3_Var30 string | |
| 614 | 617 | templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(" · ") | |
| 615 | 618 | if templ_7745c5c3_Err != nil { | |
| 616 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 120, Col: 15} | |
| 619 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 123, Col: 15} | |
| 617 | 620 | } | |
| 618 | 621 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) | |
| 619 | 622 | if templ_7745c5c3_Err != nil { | |
| @@ -626,7 +629,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 626 | 629 | var templ_7745c5c3_Var31 templ.SafeURL | |
| 627 | 630 | templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.MergedHref)) | |
| 628 | 631 | if templ_7745c5c3_Err != nil { | |
| 629 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 120, Col: 54} | |
| 632 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 123, Col: 54} | |
| 630 | 633 | } | |
| 631 | 634 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31)) | |
| 632 | 635 | if templ_7745c5c3_Err != nil { | |
| @@ -639,7 +642,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 639 | 642 | var templ_7745c5c3_Var32 string | |
| 640 | 643 | templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(p.MergedShort) | |
| 641 | 644 | if templ_7745c5c3_Err != nil { | |
| 642 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 120, Col: 72} | |
| 645 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 123, Col: 72} | |
| 643 | 646 | } | |
| 644 | 647 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32)) | |
| 645 | 648 | if templ_7745c5c3_Err != nil { | |
| @@ -652,7 +655,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 652 | 655 | var templ_7745c5c3_Var33 string | |
| 653 | 656 | templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(" · " + p.MergeMode) | |
| 654 | 657 | if templ_7745c5c3_Err != nil { | |
| 655 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 120, Col: 100} | |
| 658 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 123, Col: 100} | |
| 656 | 659 | } | |
| 657 | 660 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33)) | |
| 658 | 661 | if templ_7745c5c3_Err != nil { | |
| @@ -670,7 +673,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 670 | 673 | var templ_7745c5c3_Var34 string | |
| 671 | 674 | templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(" → ") | |
| 672 | 675 | if templ_7745c5c3_Err != nil { | |
| 673 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 126, Col: 15} | |
| 676 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 129, Col: 15} | |
| 674 | 677 | } | |
| 675 | 678 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34)) | |
| 676 | 679 | if templ_7745c5c3_Err != nil { | |
| @@ -687,7 +690,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 687 | 690 | var templ_7745c5c3_Var35 string | |
| 688 | 691 | templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(p.Opened.Name) | |
| 689 | 692 | if templ_7745c5c3_Err != nil { | |
| 690 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 131, Col: 21} | |
| 693 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 134, Col: 21} | |
| 691 | 694 | } | |
| 692 | 695 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35)) | |
| 693 | 696 | if templ_7745c5c3_Err != nil { | |
| @@ -701,7 +704,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 701 | 704 | var templ_7745c5c3_Var36 string | |
| 702 | 705 | templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(" · ") | |
| 703 | 706 | if templ_7745c5c3_Err != nil { | |
| 704 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 133, Col: 15} | |
| 707 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 136, Col: 15} | |
| 705 | 708 | } | |
| 706 | 709 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36)) | |
| 707 | 710 | if templ_7745c5c3_Err != nil { | |
| @@ -714,7 +717,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 714 | 717 | var templ_7745c5c3_Var37 string | |
| 715 | 718 | templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Opened.WhenAt) | |
| 716 | 719 | if templ_7745c5c3_Err != nil { | |
| 717 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 133, Col: 49} | |
| 720 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 136, Col: 49} | |
| 718 | 721 | } | |
| 719 | 722 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var37) | |
| 720 | 723 | if templ_7745c5c3_Err != nil { | |
| @@ -727,7 +730,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 727 | 730 | var templ_7745c5c3_Var38 string | |
| 728 | 731 | templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Opened.Abs) | |
| 729 | 732 | if templ_7745c5c3_Err != nil { | |
| 730 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 133, Col: 72} | |
| 733 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 136, Col: 72} | |
| 731 | 734 | } | |
| 732 | 735 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var38) | |
| 733 | 736 | if templ_7745c5c3_Err != nil { | |
| @@ -740,7 +743,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 740 | 743 | var templ_7745c5c3_Var39 string | |
| 741 | 744 | templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(p.Opened.When) | |
| 742 | 745 | if templ_7745c5c3_Err != nil { | |
| 743 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 133, Col: 90} | |
| 746 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 136, Col: 90} | |
| 744 | 747 | } | |
| 745 | 748 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39)) | |
| 746 | 749 | if templ_7745c5c3_Err != nil { | |
| @@ -763,7 +766,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 763 | 766 | var templ_7745c5c3_Var40 string | |
| 764 | 767 | templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.UpdatedAt) | |
| 765 | 768 | if templ_7745c5c3_Err != nil { | |
| 766 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 138, Col: 38} | |
| 769 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 141, Col: 38} | |
| 767 | 770 | } | |
| 768 | 771 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var40) | |
| 769 | 772 | if templ_7745c5c3_Err != nil { | |
| @@ -776,7 +779,7 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 776 | 779 | var templ_7745c5c3_Var41 string | |
| 777 | 780 | templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(p.Updated) | |
| 778 | 781 | if templ_7745c5c3_Err != nil { | |
| 779 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 138, Col: 52} | |
| 782 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 141, Col: 52} | |
| 780 | 783 | } | |
| 781 | 784 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41)) | |
| 782 | 785 | if templ_7745c5c3_Err != nil { | |
| @@ -809,129 +812,175 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 809 | 812 | if templ_7745c5c3_Err != nil { | |
| 810 | 813 | return templ_7745c5c3_Err | |
| 811 | 814 | } | |
| 815 | + | if p.Deleting != "" { | |
| 816 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "<p class=\"note\" role=\"status\">merged. the branch ") | |
| 817 | + | if templ_7745c5c3_Err != nil { | |
| 818 | + | return templ_7745c5c3_Err | |
| 819 | + | } | |
| 820 | + | var templ_7745c5c3_Var42 string | |
| 821 | + | templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(p.Deleting) | |
| 822 | + | if templ_7745c5c3_Err != nil { | |
| 823 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 152, Col: 64} | |
| 824 | + | } | |
| 825 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) | |
| 826 | + | if templ_7745c5c3_Err != nil { | |
| 827 | + | return templ_7745c5c3_Err | |
| 828 | + | } | |
| 829 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, " is being deleted.</p>") | |
| 830 | + | if templ_7745c5c3_Err != nil { | |
| 831 | + | return templ_7745c5c3_Err | |
| 832 | + | } | |
| 833 | + | } | |
| 834 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, " ") | |
| 835 | + | if templ_7745c5c3_Err != nil { | |
| 836 | + | return templ_7745c5c3_Err | |
| 837 | + | } | |
| 838 | + | if p.MergeError != "" { | |
| 839 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "<p class=\"d-err\" role=\"alert\">") | |
| 840 | + | if templ_7745c5c3_Err != nil { | |
| 841 | + | return templ_7745c5c3_Err | |
| 842 | + | } | |
| 843 | + | var templ_7745c5c3_Var43 string | |
| 844 | + | templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(p.MergeError) | |
| 845 | + | if templ_7745c5c3_Err != nil { | |
| 846 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 155, Col: 47} | |
| 847 | + | } | |
| 848 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43)) | |
| 849 | + | if templ_7745c5c3_Err != nil { | |
| 850 | + | return templ_7745c5c3_Err | |
| 851 | + | } | |
| 852 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "</p>") | |
| 853 | + | if templ_7745c5c3_Err != nil { | |
| 854 | + | return templ_7745c5c3_Err | |
| 855 | + | } | |
| 856 | + | } | |
| 857 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, " ") | |
| 858 | + | if templ_7745c5c3_Err != nil { | |
| 859 | + | return templ_7745c5c3_Err | |
| 860 | + | } | |
| 812 | 861 | templ_7745c5c3_Err = LiveSlot("mr-notice").Render(ctx, templ_7745c5c3_Buffer) | |
| 813 | 862 | if templ_7745c5c3_Err != nil { | |
| 814 | 863 | return templ_7745c5c3_Err | |
| 815 | 864 | } | |
| 816 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, " ") | |
| 865 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, " ") | |
| 817 | 866 | if templ_7745c5c3_Err != nil { | |
| 818 | 867 | return templ_7745c5c3_Err | |
| 819 | 868 | } | |
| 820 | 869 | if p.Since != nil { | |
| 821 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "<nav class=\"since\" aria-label=\"Diff range\"><a href=\"") | |
| 870 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "<nav class=\"since\" aria-label=\"Diff range\"><a href=\"") | |
| 822 | 871 | if templ_7745c5c3_Err != nil { | |
| 823 | 872 | return templ_7745c5c3_Err | |
| 824 | 873 | } | |
| 825 | - | var templ_7745c5c3_Var42 templ.SafeURL | |
| 826 | - | templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Since.AllHref)) | |
| 874 | + | var templ_7745c5c3_Var44 templ.SafeURL | |
| 875 | + | templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Since.AllHref)) | |
| 827 | 876 | if templ_7745c5c3_Err != nil { | |
| 828 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 152, Col: 42} | |
| 877 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 161, Col: 42} | |
| 829 | 878 | } | |
| 830 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) | |
| 879 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44)) | |
| 831 | 880 | if templ_7745c5c3_Err != nil { | |
| 832 | 881 | return templ_7745c5c3_Err | |
| 833 | 882 | } | |
| 834 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "\"") | |
| 883 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "\"") | |
| 835 | 884 | if templ_7745c5c3_Err != nil { | |
| 836 | 885 | return templ_7745c5c3_Err | |
| 837 | 886 | } | |
| 838 | 887 | if !p.Since.Current { | |
| 839 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, " aria-current=\"page\"") | |
| 888 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, " aria-current=\"page\"") | |
| 840 | 889 | if templ_7745c5c3_Err != nil { | |
| 841 | 890 | return templ_7745c5c3_Err | |
| 842 | 891 | } | |
| 843 | 892 | } | |
| 844 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, ">all changes <span class=\"mute\">vs ") | |
| 893 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, ">all changes <span class=\"mute\">vs ") | |
| 845 | 894 | if templ_7745c5c3_Err != nil { | |
| 846 | 895 | return templ_7745c5c3_Err | |
| 847 | 896 | } | |
| 848 | - | var templ_7745c5c3_Var43 string | |
| 849 | - | templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 897 | + | var templ_7745c5c3_Var45 string | |
| 898 | + | templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 850 | 899 | if templ_7745c5c3_Err != nil { | |
| 851 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 156, Col: 49} | |
| 900 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 165, Col: 49} | |
| 852 | 901 | } | |
| 853 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43)) | |
| 902 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) | |
| 854 | 903 | if templ_7745c5c3_Err != nil { | |
| 855 | 904 | return templ_7745c5c3_Err | |
| 856 | 905 | } | |
| 857 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "</span></a> <a href=\"") | |
| 906 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "</span></a> <a href=\"") | |
| 858 | 907 | if templ_7745c5c3_Err != nil { | |
| 859 | 908 | return templ_7745c5c3_Err | |
| 860 | 909 | } | |
| 861 | - | var templ_7745c5c3_Var44 templ.SafeURL | |
| 862 | - | templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Since.SinceHref)) | |
| 910 | + | var templ_7745c5c3_Var46 templ.SafeURL | |
| 911 | + | templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Since.SinceHref)) | |
| 863 | 912 | if templ_7745c5c3_Err != nil { | |
| 864 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 158, Col: 44} | |
| 913 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 167, Col: 44} | |
| 865 | 914 | } | |
| 866 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44)) | |
| 915 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46)) | |
| 867 | 916 | if templ_7745c5c3_Err != nil { | |
| 868 | 917 | return templ_7745c5c3_Err | |
| 869 | 918 | } | |
| 870 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "\"") | |
| 919 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "\"") | |
| 871 | 920 | if templ_7745c5c3_Err != nil { | |
| 872 | 921 | return templ_7745c5c3_Err | |
| 873 | 922 | } | |
| 874 | 923 | if p.Since.Current { | |
| 875 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, " aria-current=\"page\"") | |
| 924 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, " aria-current=\"page\"") | |
| 876 | 925 | if templ_7745c5c3_Err != nil { | |
| 877 | 926 | return templ_7745c5c3_Err | |
| 878 | 927 | } | |
| 879 | 928 | } | |
| 880 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, ">since your last read <span class=\"mute\">") | |
| 929 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, ">since your last read <span class=\"mute\">") | |
| 881 | 930 | if templ_7745c5c3_Err != nil { | |
| 882 | 931 | return templ_7745c5c3_Err | |
| 883 | 932 | } | |
| 884 | - | var templ_7745c5c3_Var45 string | |
| 885 | - | templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(p.Since.ReadShort) | |
| 933 | + | var templ_7745c5c3_Var47 string | |
| 934 | + | templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(p.Since.ReadShort) | |
| 886 | 935 | if templ_7745c5c3_Err != nil { | |
| 887 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 162, Col: 64} | |
| 936 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 171, Col: 64} | |
| 888 | 937 | } | |
| 889 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) | |
| 938 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47)) | |
| 890 | 939 | if templ_7745c5c3_Err != nil { | |
| 891 | 940 | return templ_7745c5c3_Err | |
| 892 | 941 | } | |
| 893 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, " → ") | |
| 942 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, " → ") | |
| 894 | 943 | if templ_7745c5c3_Err != nil { | |
| 895 | 944 | return templ_7745c5c3_Err | |
| 896 | 945 | } | |
| 897 | - | var templ_7745c5c3_Var46 string | |
| 898 | - | templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(p.Since.HeadShort) | |
| 946 | + | var templ_7745c5c3_Var48 string | |
| 947 | + | templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(p.Since.HeadShort) | |
| 899 | 948 | if templ_7745c5c3_Err != nil { | |
| 900 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 162, Col: 90} | |
| 949 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 171, Col: 90} | |
| 901 | 950 | } | |
| 902 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46)) | |
| 951 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48)) | |
| 903 | 952 | if templ_7745c5c3_Err != nil { | |
| 904 | 953 | return templ_7745c5c3_Err | |
| 905 | 954 | } | |
| 906 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "</span></a></nav>") | |
| 955 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "</span></a></nav>") | |
| 907 | 956 | if templ_7745c5c3_Err != nil { | |
| 908 | 957 | return templ_7745c5c3_Err | |
| 909 | 958 | } | |
| 910 | 959 | } | |
| 911 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, " ") | |
| 960 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, " ") | |
| 912 | 961 | if templ_7745c5c3_Err != nil { | |
| 913 | 962 | return templ_7745c5c3_Err | |
| 914 | 963 | } | |
| 915 | 964 | if p.SinceNote != "" { | |
| 916 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "<p class=\"notice\">") | |
| 965 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "<p class=\"notice\">") | |
| 917 | 966 | if templ_7745c5c3_Err != nil { | |
| 918 | 967 | return templ_7745c5c3_Err | |
| 919 | 968 | } | |
| 920 | - | var templ_7745c5c3_Var47 string | |
| 921 | - | templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(p.SinceNote) | |
| 969 | + | var templ_7745c5c3_Var49 string | |
| 970 | + | templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(p.SinceNote) | |
| 922 | 971 | if templ_7745c5c3_Err != nil { | |
| 923 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 166, Col: 34} | |
| 972 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 175, Col: 34} | |
| 924 | 973 | } | |
| 925 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47)) | |
| 974 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49)) | |
| 926 | 975 | if templ_7745c5c3_Err != nil { | |
| 927 | 976 | return templ_7745c5c3_Err | |
| 928 | 977 | } | |
| 929 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "</p>") | |
| 978 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "</p>") | |
| 930 | 979 | if templ_7745c5c3_Err != nil { | |
| 931 | 980 | return templ_7745c5c3_Err | |
| 932 | 981 | } | |
| 933 | 982 | } | |
| 934 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, " ") | |
| 983 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, " ") | |
| 935 | 984 | if templ_7745c5c3_Err != nil { | |
| 936 | 985 | return templ_7745c5c3_Err | |
| 937 | 986 | } | |
| @@ -939,87 +988,87 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 939 | 988 | if templ_7745c5c3_Err != nil { | |
| 940 | 989 | return templ_7745c5c3_Err | |
| 941 | 990 | } | |
| 942 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, " ") | |
| 991 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, " ") | |
| 943 | 992 | if templ_7745c5c3_Err != nil { | |
| 944 | 993 | return templ_7745c5c3_Err | |
| 945 | 994 | } | |
| 946 | 995 | if p.Invisible { | |
| 947 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "<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>") | |
| 996 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "<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>") | |
| 948 | 997 | if templ_7745c5c3_Err != nil { | |
| 949 | 998 | return templ_7745c5c3_Err | |
| 950 | 999 | } | |
| 951 | 1000 | } | |
| 952 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, " ") | |
| 1001 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, " ") | |
| 953 | 1002 | if templ_7745c5c3_Err != nil { | |
| 954 | 1003 | return templ_7745c5c3_Err | |
| 955 | 1004 | } | |
| 956 | 1005 | if p.Note != "" { | |
| 957 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "<p class=\"notice\">") | |
| 1006 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "<p class=\"notice\">") | |
| 958 | 1007 | if templ_7745c5c3_Err != nil { | |
| 959 | 1008 | return templ_7745c5c3_Err | |
| 960 | 1009 | } | |
| 961 | - | var templ_7745c5c3_Var48 string | |
| 962 | - | templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(p.Note) | |
| 1010 | + | var templ_7745c5c3_Var50 string | |
| 1011 | + | templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.JoinStringErrs(p.Note) | |
| 963 | 1012 | if templ_7745c5c3_Err != nil { | |
| 964 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 176, Col: 29} | |
| 1013 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 185, Col: 29} | |
| 965 | 1014 | } | |
| 966 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48)) | |
| 1015 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var50)) | |
| 967 | 1016 | if templ_7745c5c3_Err != nil { | |
| 968 | 1017 | return templ_7745c5c3_Err | |
| 969 | 1018 | } | |
| 970 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "</p>") | |
| 1019 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, "</p>") | |
| 971 | 1020 | if templ_7745c5c3_Err != nil { | |
| 972 | 1021 | return templ_7745c5c3_Err | |
| 973 | 1022 | } | |
| 974 | 1023 | } | |
| 975 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, " <div id=\"diffs\" data-mr") | |
| 1024 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, " <div id=\"diffs\" data-mr") | |
| 976 | 1025 | if templ_7745c5c3_Err != nil { | |
| 977 | 1026 | return templ_7745c5c3_Err | |
| 978 | 1027 | } | |
| 979 | 1028 | if p.Both { | |
| 980 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, " data-diff=\"") | |
| 1029 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 99, " data-diff=\"") | |
| 981 | 1030 | if templ_7745c5c3_Err != nil { | |
| 982 | 1031 | return templ_7745c5c3_Err | |
| 983 | 1032 | } | |
| 984 | - | var templ_7745c5c3_Var49 string | |
| 985 | - | templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View) | |
| 1033 | + | var templ_7745c5c3_Var51 string | |
| 1034 | + | templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View) | |
| 986 | 1035 | if templ_7745c5c3_Err != nil { | |
| 987 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 182, Col: 22} | |
| 1036 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 191, Col: 22} | |
| 988 | 1037 | } | |
| 989 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var49) | |
| 1038 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var51) | |
| 990 | 1039 | if templ_7745c5c3_Err != nil { | |
| 991 | 1040 | return templ_7745c5c3_Err | |
| 992 | 1041 | } | |
| 993 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "\"") | |
| 1042 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 100, "\"") | |
| 994 | 1043 | if templ_7745c5c3_Err != nil { | |
| 995 | 1044 | return templ_7745c5c3_Err | |
| 996 | 1045 | } | |
| 997 | 1046 | } else { | |
| 998 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, " data-view=\"") | |
| 1047 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 101, " data-view=\"") | |
| 999 | 1048 | if templ_7745c5c3_Err != nil { | |
| 1000 | 1049 | return templ_7745c5c3_Err | |
| 1001 | 1050 | } | |
| 1002 | - | var templ_7745c5c3_Var50 string | |
| 1003 | - | templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View) | |
| 1051 | + | var templ_7745c5c3_Var52 string | |
| 1052 | + | templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View) | |
| 1004 | 1053 | if templ_7745c5c3_Err != nil { | |
| 1005 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 184, Col: 22} | |
| 1054 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 193, Col: 22} | |
| 1006 | 1055 | } | |
| 1007 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var50) | |
| 1056 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var52) | |
| 1008 | 1057 | if templ_7745c5c3_Err != nil { | |
| 1009 | 1058 | return templ_7745c5c3_Err | |
| 1010 | 1059 | } | |
| 1011 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "\"") | |
| 1060 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 102, "\"") | |
| 1012 | 1061 | if templ_7745c5c3_Err != nil { | |
| 1013 | 1062 | return templ_7745c5c3_Err | |
| 1014 | 1063 | } | |
| 1015 | 1064 | } | |
| 1016 | 1065 | if p.Both && !p.Pinned { | |
| 1017 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, " hx-live=\"this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff\"") | |
| 1066 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 103, " hx-live=\"this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff\"") | |
| 1018 | 1067 | if templ_7745c5c3_Err != nil { | |
| 1019 | 1068 | return templ_7745c5c3_Err | |
| 1020 | 1069 | } | |
| 1021 | 1070 | } | |
| 1022 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, ">") | |
| 1071 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 104, ">") | |
| 1023 | 1072 | if templ_7745c5c3_Err != nil { | |
| 1024 | 1073 | return templ_7745c5c3_Err | |
| 1025 | 1074 | } | |
| @@ -1030,38 +1079,38 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 1030 | 1079 | } | |
| 1031 | 1080 | } | |
| 1032 | 1081 | if len(p.Files) == 0 { | |
| 1033 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 99, "<p class=\"note\">no changed files.</p>") | |
| 1082 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 105, "<p class=\"note\">no changed files.</p>") | |
| 1034 | 1083 | if templ_7745c5c3_Err != nil { | |
| 1035 | 1084 | return templ_7745c5c3_Err | |
| 1036 | 1085 | } | |
| 1037 | 1086 | } | |
| 1038 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 100, "</div><h2 class=\"rule-h\" id=\"comments\"><span>Comments</span><span class=\"count\">") | |
| 1087 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 106, "</div><h2 class=\"rule-h\" id=\"comments\"><span>Comments</span><span class=\"count\">") | |
| 1039 | 1088 | if templ_7745c5c3_Err != nil { | |
| 1040 | 1089 | return templ_7745c5c3_Err | |
| 1041 | 1090 | } | |
| 1042 | - | var templ_7745c5c3_Var51 string | |
| 1043 | - | templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.NUnresolved)) | |
| 1091 | + | var templ_7745c5c3_Var53 string | |
| 1092 | + | templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.NUnresolved)) | |
| 1044 | 1093 | if templ_7745c5c3_Err != nil { | |
| 1045 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 197, Col: 97} | |
| 1094 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 206, Col: 97} | |
| 1046 | 1095 | } | |
| 1047 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51)) | |
| 1096 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var53)) | |
| 1048 | 1097 | if templ_7745c5c3_Err != nil { | |
| 1049 | 1098 | return templ_7745c5c3_Err | |
| 1050 | 1099 | } | |
| 1051 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 101, " open / ") | |
| 1100 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 107, " open / ") | |
| 1052 | 1101 | if templ_7745c5c3_Err != nil { | |
| 1053 | 1102 | return templ_7745c5c3_Err | |
| 1054 | 1103 | } | |
| 1055 | - | var templ_7745c5c3_Var52 string | |
| 1056 | - | templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.NComments)) | |
| 1104 | + | var templ_7745c5c3_Var54 string | |
| 1105 | + | templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.NComments)) | |
| 1057 | 1106 | if templ_7745c5c3_Err != nil { | |
| 1058 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 197, Col: 126} | |
| 1107 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 206, Col: 126} | |
| 1059 | 1108 | } | |
| 1060 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var52)) | |
| 1109 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var54)) | |
| 1061 | 1110 | if templ_7745c5c3_Err != nil { | |
| 1062 | 1111 | return templ_7745c5c3_Err | |
| 1063 | 1112 | } | |
| 1064 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 102, "</span></h2>") | |
| 1113 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 108, "</span></h2>") | |
| 1065 | 1114 | if templ_7745c5c3_Err != nil { | |
| 1066 | 1115 | return templ_7745c5c3_Err | |
| 1067 | 1116 | } | |
| @@ -1077,17 +1126,17 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 1077 | 1126 | return templ_7745c5c3_Err | |
| 1078 | 1127 | } | |
| 1079 | 1128 | } | |
| 1080 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 103, " ") | |
| 1129 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 109, " ") | |
| 1081 | 1130 | if templ_7745c5c3_Err != nil { | |
| 1082 | 1131 | return templ_7745c5c3_Err | |
| 1083 | 1132 | } | |
| 1084 | 1133 | if p.NComments == 0 { | |
| 1085 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 104, "<p class=\"dim\">no comments yet.</p>") | |
| 1134 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 110, "<p class=\"dim\">no comments yet.</p>") | |
| 1086 | 1135 | if templ_7745c5c3_Err != nil { | |
| 1087 | 1136 | return templ_7745c5c3_Err | |
| 1088 | 1137 | } | |
| 1089 | 1138 | } | |
| 1090 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 105, " ") | |
| 1139 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 111, " ") | |
| 1091 | 1140 | if templ_7745c5c3_Err != nil { | |
| 1092 | 1141 | return templ_7745c5c3_Err | |
| 1093 | 1142 | } | |
| @@ -1123,49 +1172,49 @@ func branchLink(name, href string) templ.Component { | |||
| 1123 | 1172 | }() | |
| 1124 | 1173 | } | |
| 1125 | 1174 | ctx = templ.InitializeContext(ctx) | |
| 1126 | - | templ_7745c5c3_Var53 := templ.GetChildren(ctx) | |
| 1127 | - | if templ_7745c5c3_Var53 == nil { | |
| 1128 | - | templ_7745c5c3_Var53 = templ.NopComponent | |
| 1175 | + | templ_7745c5c3_Var55 := templ.GetChildren(ctx) | |
| 1176 | + | if templ_7745c5c3_Var55 == nil { | |
| 1177 | + | templ_7745c5c3_Var55 = templ.NopComponent | |
| 1129 | 1178 | } | |
| 1130 | 1179 | ctx = templ.ClearChildren(ctx) | |
| 1131 | 1180 | if href != "" { | |
| 1132 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 106, "<a href=\"") | |
| 1181 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 112, "<a href=\"") | |
| 1133 | 1182 | if templ_7745c5c3_Err != nil { | |
| 1134 | 1183 | return templ_7745c5c3_Err | |
| 1135 | 1184 | } | |
| 1136 | - | var templ_7745c5c3_Var54 templ.SafeURL | |
| 1137 | - | templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(href)) | |
| 1185 | + | var templ_7745c5c3_Var56 templ.SafeURL | |
| 1186 | + | templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(href)) | |
| 1138 | 1187 | if templ_7745c5c3_Err != nil { | |
| 1139 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 215, Col: 31} | |
| 1188 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 224, Col: 31} | |
| 1140 | 1189 | } | |
| 1141 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var54)) | |
| 1190 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56)) | |
| 1142 | 1191 | if templ_7745c5c3_Err != nil { | |
| 1143 | 1192 | return templ_7745c5c3_Err | |
| 1144 | 1193 | } | |
| 1145 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 107, "\">") | |
| 1194 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 113, "\">") | |
| 1146 | 1195 | if templ_7745c5c3_Err != nil { | |
| 1147 | 1196 | return templ_7745c5c3_Err | |
| 1148 | 1197 | } | |
| 1149 | - | var templ_7745c5c3_Var55 string | |
| 1150 | - | templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.JoinStringErrs(name) | |
| 1198 | + | var templ_7745c5c3_Var57 string | |
| 1199 | + | templ_7745c5c3_Var57, templ_7745c5c3_Err = templ.JoinStringErrs(name) | |
| 1151 | 1200 | if templ_7745c5c3_Err != nil { | |
| 1152 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 215, Col: 40} | |
| 1201 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 224, Col: 40} | |
| 1153 | 1202 | } | |
| 1154 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var55)) | |
| 1203 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var57)) | |
| 1155 | 1204 | if templ_7745c5c3_Err != nil { | |
| 1156 | 1205 | return templ_7745c5c3_Err | |
| 1157 | 1206 | } | |
| 1158 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 108, "</a>") | |
| 1207 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 114, "</a>") | |
| 1159 | 1208 | if templ_7745c5c3_Err != nil { | |
| 1160 | 1209 | return templ_7745c5c3_Err | |
| 1161 | 1210 | } | |
| 1162 | 1211 | } else { | |
| 1163 | - | var templ_7745c5c3_Var56 string | |
| 1164 | - | templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinStringErrs(name) | |
| 1212 | + | var templ_7745c5c3_Var58 string | |
| 1213 | + | templ_7745c5c3_Var58, templ_7745c5c3_Err = templ.JoinStringErrs(name) | |
| 1165 | 1214 | if templ_7745c5c3_Err != nil { | |
| 1166 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 217, Col: 8} | |
| 1215 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 226, Col: 8} | |
| 1167 | 1216 | } | |
| 1168 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56)) | |
| 1217 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var58)) | |
| 1169 | 1218 | if templ_7745c5c3_Err != nil { | |
| 1170 | 1219 | return templ_7745c5c3_Err | |
| 1171 | 1220 | } | |
| @@ -1193,9 +1242,9 @@ func lineNotes(notes []CommentView, form *CommentForm, suffix string) templ.Comp | |||
| 1193 | 1242 | }() | |
| 1194 | 1243 | } | |
| 1195 | 1244 | ctx = templ.InitializeContext(ctx) | |
| 1196 | - | templ_7745c5c3_Var57 := templ.GetChildren(ctx) | |
| 1197 | - | if templ_7745c5c3_Var57 == nil { | |
| 1198 | - | templ_7745c5c3_Var57 = templ.NopComponent | |
| 1245 | + | templ_7745c5c3_Var59 := templ.GetChildren(ctx) | |
| 1246 | + | if templ_7745c5c3_Var59 == nil { | |
| 1247 | + | templ_7745c5c3_Var59 = templ.NopComponent | |
| 1199 | 1248 | } | |
| 1200 | 1249 | ctx = templ.ClearChildren(ctx) | |
| 1201 | 1250 | for _, c := range notes { | |
| @@ -1232,182 +1281,182 @@ func commentView(c CommentView, suffix string) templ.Component { | |||
| 1232 | 1281 | }() | |
| 1233 | 1282 | } | |
| 1234 | 1283 | ctx = templ.InitializeContext(ctx) | |
| 1235 | - | templ_7745c5c3_Var58 := templ.GetChildren(ctx) | |
| 1236 | - | if templ_7745c5c3_Var58 == nil { | |
| 1237 | - | templ_7745c5c3_Var58 = templ.NopComponent | |
| 1284 | + | templ_7745c5c3_Var60 := templ.GetChildren(ctx) | |
| 1285 | + | if templ_7745c5c3_Var60 == nil { | |
| 1286 | + | templ_7745c5c3_Var60 = templ.NopComponent | |
| 1238 | 1287 | } | |
| 1239 | 1288 | ctx = templ.ClearChildren(ctx) | |
| 1240 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 109, "<div class=\"cmt\" id=\"") | |
| 1289 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 115, "<div class=\"cmt\" id=\"") | |
| 1241 | 1290 | if templ_7745c5c3_Err != nil { | |
| 1242 | 1291 | return templ_7745c5c3_Err | |
| 1243 | 1292 | } | |
| 1244 | - | var templ_7745c5c3_Var59 string | |
| 1245 | - | templ_7745c5c3_Var59, templ_7745c5c3_Err = templ.ResolveAttributeValue("c" + c.ID + suffix) | |
| 1293 | + | var templ_7745c5c3_Var61 string | |
| 1294 | + | templ_7745c5c3_Var61, templ_7745c5c3_Err = templ.ResolveAttributeValue("c" + c.ID + suffix) | |
| 1246 | 1295 | if templ_7745c5c3_Err != nil { | |
| 1247 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 236, Col: 42} | |
| 1296 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 245, Col: 42} | |
| 1248 | 1297 | } | |
| 1249 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var59) | |
| 1298 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var61) | |
| 1250 | 1299 | if templ_7745c5c3_Err != nil { | |
| 1251 | 1300 | return templ_7745c5c3_Err | |
| 1252 | 1301 | } | |
| 1253 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 110, "\" data-resolved=\"") | |
| 1302 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 116, "\" data-resolved=\"") | |
| 1254 | 1303 | if templ_7745c5c3_Err != nil { | |
| 1255 | 1304 | return templ_7745c5c3_Err | |
| 1256 | 1305 | } | |
| 1257 | - | var templ_7745c5c3_Var60 string | |
| 1258 | - | templ_7745c5c3_Var60, templ_7745c5c3_Err = templ.ResolveAttributeValue(boolText(c.Resolved)) | |
| 1306 | + | var templ_7745c5c3_Var62 string | |
| 1307 | + | templ_7745c5c3_Var62, templ_7745c5c3_Err = templ.ResolveAttributeValue(boolText(c.Resolved)) | |
| 1259 | 1308 | if templ_7745c5c3_Err != nil { | |
| 1260 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 236, Col: 81} | |
| 1309 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 245, Col: 81} | |
| 1261 | 1310 | } | |
| 1262 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var60) | |
| 1311 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var62) | |
| 1263 | 1312 | if templ_7745c5c3_Err != nil { | |
| 1264 | 1313 | return templ_7745c5c3_Err | |
| 1265 | 1314 | } | |
| 1266 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 111, "\"") | |
| 1315 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 117, "\"") | |
| 1267 | 1316 | if templ_7745c5c3_Err != nil { | |
| 1268 | 1317 | return templ_7745c5c3_Err | |
| 1269 | 1318 | } | |
| 1270 | 1319 | if c.Reply != nil { | |
| 1271 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 112, " data-thread") | |
| 1320 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 118, " data-thread") | |
| 1272 | 1321 | if templ_7745c5c3_Err != nil { | |
| 1273 | 1322 | return templ_7745c5c3_Err | |
| 1274 | 1323 | } | |
| 1275 | 1324 | } | |
| 1276 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 113, "><p class=\"c-head\"><span class=\"c-who\">") | |
| 1325 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 119, "><p class=\"c-head\"><span class=\"c-who\">") | |
| 1277 | 1326 | if templ_7745c5c3_Err != nil { | |
| 1278 | 1327 | return templ_7745c5c3_Err | |
| 1279 | 1328 | } | |
| 1280 | - | var templ_7745c5c3_Var61 string | |
| 1281 | - | templ_7745c5c3_Var61, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author) | |
| 1329 | + | var templ_7745c5c3_Var63 string | |
| 1330 | + | templ_7745c5c3_Var63, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author) | |
| 1282 | 1331 | if templ_7745c5c3_Err != nil { | |
| 1283 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 238, Col: 33} | |
| 1332 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 247, Col: 33} | |
| 1284 | 1333 | } | |
| 1285 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var61)) | |
| 1334 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var63)) | |
| 1286 | 1335 | if templ_7745c5c3_Err != nil { | |
| 1287 | 1336 | return templ_7745c5c3_Err | |
| 1288 | 1337 | } | |
| 1289 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 114, "</span> ") | |
| 1338 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 120, "</span> ") | |
| 1290 | 1339 | if templ_7745c5c3_Err != nil { | |
| 1291 | 1340 | return templ_7745c5c3_Err | |
| 1292 | 1341 | } | |
| 1293 | 1342 | if c.WhenAt != "" { | |
| 1294 | - | var templ_7745c5c3_Var62 string | |
| 1295 | - | templ_7745c5c3_Var62, templ_7745c5c3_Err = templ.JoinStringErrs(" ") | |
| 1343 | + | var templ_7745c5c3_Var64 string | |
| 1344 | + | templ_7745c5c3_Var64, templ_7745c5c3_Err = templ.JoinStringErrs(" ") | |
| 1296 | 1345 | if templ_7745c5c3_Err != nil { | |
| 1297 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 240, Col: 9} | |
| 1346 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 249, Col: 9} | |
| 1298 | 1347 | } | |
| 1299 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var62)) | |
| 1348 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var64)) | |
| 1300 | 1349 | if templ_7745c5c3_Err != nil { | |
| 1301 | 1350 | return templ_7745c5c3_Err | |
| 1302 | 1351 | } | |
| 1303 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 115, "<time class=\"mute\" datetime=\"") | |
| 1352 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 121, "<time class=\"mute\" datetime=\"") | |
| 1304 | 1353 | if templ_7745c5c3_Err != nil { | |
| 1305 | 1354 | return templ_7745c5c3_Err | |
| 1306 | 1355 | } | |
| 1307 | - | var templ_7745c5c3_Var63 string | |
| 1308 | - | templ_7745c5c3_Var63, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | |
| 1356 | + | var templ_7745c5c3_Var65 string | |
| 1357 | + | templ_7745c5c3_Var65, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | |
| 1309 | 1358 | if templ_7745c5c3_Err != nil { | |
| 1310 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 240, Col: 49} | |
| 1359 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 249, Col: 49} | |
| 1311 | 1360 | } | |
| 1312 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var63) | |
| 1361 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var65) | |
| 1313 | 1362 | if templ_7745c5c3_Err != nil { | |
| 1314 | 1363 | return templ_7745c5c3_Err | |
| 1315 | 1364 | } | |
| 1316 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 116, "\" title=\"") | |
| 1365 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 122, "\" title=\"") | |
| 1317 | 1366 | if templ_7745c5c3_Err != nil { | |
| 1318 | 1367 | return templ_7745c5c3_Err | |
| 1319 | 1368 | } | |
| 1320 | - | var templ_7745c5c3_Var64 string | |
| 1321 | - | templ_7745c5c3_Var64, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.Abs) | |
| 1369 | + | var templ_7745c5c3_Var66 string | |
| 1370 | + | templ_7745c5c3_Var66, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.Abs) | |
| 1322 | 1371 | if templ_7745c5c3_Err != nil { | |
| 1323 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 240, Col: 65} | |
| 1372 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 249, Col: 65} | |
| 1324 | 1373 | } | |
| 1325 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var64) | |
| 1374 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var66) | |
| 1326 | 1375 | if templ_7745c5c3_Err != nil { | |
| 1327 | 1376 | return templ_7745c5c3_Err | |
| 1328 | 1377 | } | |
| 1329 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 117, "\">") | |
| 1378 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 123, "\">") | |
| 1330 | 1379 | if templ_7745c5c3_Err != nil { | |
| 1331 | 1380 | return templ_7745c5c3_Err | |
| 1332 | 1381 | } | |
| 1333 | - | var templ_7745c5c3_Var65 string | |
| 1334 | - | templ_7745c5c3_Var65, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) | |
| 1382 | + | var templ_7745c5c3_Var67 string | |
| 1383 | + | templ_7745c5c3_Var67, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) | |
| 1335 | 1384 | if templ_7745c5c3_Err != nil { | |
| 1336 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 240, Col: 76} | |
| 1385 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 249, Col: 76} | |
| 1337 | 1386 | } | |
| 1338 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var65)) | |
| 1387 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var67)) | |
| 1339 | 1388 | if templ_7745c5c3_Err != nil { | |
| 1340 | 1389 | return templ_7745c5c3_Err | |
| 1341 | 1390 | } | |
| 1342 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 118, "</time> ") | |
| 1391 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 124, "</time> ") | |
| 1343 | 1392 | if templ_7745c5c3_Err != nil { | |
| 1344 | 1393 | return templ_7745c5c3_Err | |
| 1345 | 1394 | } | |
| 1346 | 1395 | } | |
| 1347 | 1396 | if c.Resolved { | |
| 1348 | - | var templ_7745c5c3_Var66 string | |
| 1349 | - | templ_7745c5c3_Var66, templ_7745c5c3_Err = templ.JoinStringErrs(" ") | |
| 1397 | + | var templ_7745c5c3_Var68 string | |
| 1398 | + | templ_7745c5c3_Var68, templ_7745c5c3_Err = templ.JoinStringErrs(" ") | |
| 1350 | 1399 | if templ_7745c5c3_Err != nil { | |
| 1351 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 243, Col: 9} | |
| 1400 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 252, Col: 9} | |
| 1352 | 1401 | } | |
| 1353 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var66)) | |
| 1402 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var68)) | |
| 1354 | 1403 | if templ_7745c5c3_Err != nil { | |
| 1355 | 1404 | return templ_7745c5c3_Err | |
| 1356 | 1405 | } | |
| 1357 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 119, "<span class=\"c-state\">resolved</span> ") | |
| 1406 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 125, "<span class=\"c-state\">resolved</span> ") | |
| 1358 | 1407 | if templ_7745c5c3_Err != nil { | |
| 1359 | 1408 | return templ_7745c5c3_Err | |
| 1360 | 1409 | } | |
| 1361 | 1410 | } | |
| 1362 | 1411 | if c.Outdated { | |
| 1363 | - | var templ_7745c5c3_Var67 string | |
| 1364 | - | templ_7745c5c3_Var67, templ_7745c5c3_Err = templ.JoinStringErrs(" ") | |
| 1412 | + | var templ_7745c5c3_Var69 string | |
| 1413 | + | templ_7745c5c3_Var69, templ_7745c5c3_Err = templ.JoinStringErrs(" ") | |
| 1365 | 1414 | if templ_7745c5c3_Err != nil { | |
| 1366 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 246, Col: 9} | |
| 1415 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 255, Col: 9} | |
| 1367 | 1416 | } | |
| 1368 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var67)) | |
| 1417 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var69)) | |
| 1369 | 1418 | if templ_7745c5c3_Err != nil { | |
| 1370 | 1419 | return templ_7745c5c3_Err | |
| 1371 | 1420 | } | |
| 1372 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 120, "<span class=\"c-state\" title=\"written on an earlier head; the line may have moved\">outdated</span> ") | |
| 1421 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 126, "<span class=\"c-state\" title=\"written on an earlier head; the line may have moved\">outdated</span> ") | |
| 1373 | 1422 | if templ_7745c5c3_Err != nil { | |
| 1374 | 1423 | return templ_7745c5c3_Err | |
| 1375 | 1424 | } | |
| 1376 | 1425 | } | |
| 1377 | 1426 | if c.Where != "" { | |
| 1378 | - | var templ_7745c5c3_Var68 string | |
| 1379 | - | templ_7745c5c3_Var68, templ_7745c5c3_Err = templ.JoinStringErrs(" ") | |
| 1427 | + | var templ_7745c5c3_Var70 string | |
| 1428 | + | templ_7745c5c3_Var70, templ_7745c5c3_Err = templ.JoinStringErrs(" ") | |
| 1380 | 1429 | if templ_7745c5c3_Err != nil { | |
| 1381 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 249, Col: 9} | |
| 1430 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 258, Col: 9} | |
| 1382 | 1431 | } | |
| 1383 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var68)) | |
| 1432 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var70)) | |
| 1384 | 1433 | if templ_7745c5c3_Err != nil { | |
| 1385 | 1434 | return templ_7745c5c3_Err | |
| 1386 | 1435 | } | |
| 1387 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 121, "<span class=\"c-where mute\">") | |
| 1436 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 127, "<span class=\"c-where mute\">") | |
| 1388 | 1437 | if templ_7745c5c3_Err != nil { | |
| 1389 | 1438 | return templ_7745c5c3_Err | |
| 1390 | 1439 | } | |
| 1391 | - | var templ_7745c5c3_Var69 string | |
| 1392 | - | templ_7745c5c3_Var69, templ_7745c5c3_Err = templ.JoinStringErrs(c.Where) | |
| 1440 | + | var templ_7745c5c3_Var71 string | |
| 1441 | + | templ_7745c5c3_Var71, templ_7745c5c3_Err = templ.JoinStringErrs(c.Where) | |
| 1393 | 1442 | if templ_7745c5c3_Err != nil { | |
| 1394 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 249, Col: 47} | |
| 1443 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 258, Col: 47} | |
| 1395 | 1444 | } | |
| 1396 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var69)) | |
| 1445 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var71)) | |
| 1397 | 1446 | if templ_7745c5c3_Err != nil { | |
| 1398 | 1447 | return templ_7745c5c3_Err | |
| 1399 | 1448 | } | |
| 1400 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 122, "</span>") | |
| 1449 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 128, "</span>") | |
| 1401 | 1450 | if templ_7745c5c3_Err != nil { | |
| 1402 | 1451 | return templ_7745c5c3_Err | |
| 1403 | 1452 | } | |
| 1404 | 1453 | } | |
| 1405 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 123, "</p>") | |
| 1454 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 129, "</p>") | |
| 1406 | 1455 | if templ_7745c5c3_Err != nil { | |
| 1407 | 1456 | return templ_7745c5c3_Err | |
| 1408 | 1457 | } | |
| 1409 | 1458 | if len(c.Parts) > 0 { | |
| 1410 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 124, "<p class=\"c-text\">") | |
| 1459 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 130, "<p class=\"c-text\">") | |
| 1411 | 1460 | if templ_7745c5c3_Err != nil { | |
| 1412 | 1461 | return templ_7745c5c3_Err | |
| 1413 | 1462 | } | |
| @@ -1415,24 +1464,24 @@ func commentView(c CommentView, suffix string) templ.Component { | |||
| 1415 | 1464 | if templ_7745c5c3_Err != nil { | |
| 1416 | 1465 | return templ_7745c5c3_Err | |
| 1417 | 1466 | } | |
| 1418 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 125, "</p>") | |
| 1467 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 131, "</p>") | |
| 1419 | 1468 | if templ_7745c5c3_Err != nil { | |
| 1420 | 1469 | return templ_7745c5c3_Err | |
| 1421 | 1470 | } | |
| 1422 | 1471 | } else if c.Deleted { | |
| 1423 | 1472 | if c.Verdict != "" { | |
| 1424 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 126, "<p class=\"mute\">text deleted</p>") | |
| 1473 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 132, "<p class=\"mute\">text deleted</p>") | |
| 1425 | 1474 | if templ_7745c5c3_Err != nil { | |
| 1426 | 1475 | return templ_7745c5c3_Err | |
| 1427 | 1476 | } | |
| 1428 | 1477 | } else { | |
| 1429 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 127, "<p class=\"mute\">deleted note</p>") | |
| 1478 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 133, "<p class=\"mute\">deleted note</p>") | |
| 1430 | 1479 | if templ_7745c5c3_Err != nil { | |
| 1431 | 1480 | return templ_7745c5c3_Err | |
| 1432 | 1481 | } | |
| 1433 | 1482 | } | |
| 1434 | 1483 | } else { | |
| 1435 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 128, "<div class=\"c-body prose\">") | |
| 1484 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 134, "<div class=\"c-body prose\">") | |
| 1436 | 1485 | if templ_7745c5c3_Err != nil { | |
| 1437 | 1486 | return templ_7745c5c3_Err | |
| 1438 | 1487 | } | |
| @@ -1447,147 +1496,147 @@ func commentView(c CommentView, suffix string) templ.Component { | |||
| 1447 | 1496 | return templ_7745c5c3_Err | |
| 1448 | 1497 | } | |
| 1449 | 1498 | } | |
| 1450 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 129, "</div>") | |
| 1499 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 135, "</div>") | |
| 1451 | 1500 | if templ_7745c5c3_Err != nil { | |
| 1452 | 1501 | return templ_7745c5c3_Err | |
| 1453 | 1502 | } | |
| 1454 | 1503 | } | |
| 1455 | 1504 | if c.Edited { | |
| 1456 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 130, "<span class=\"mute\">edited</span> ") | |
| 1505 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 136, "<span class=\"mute\">edited</span> ") | |
| 1457 | 1506 | if templ_7745c5c3_Err != nil { | |
| 1458 | 1507 | return templ_7745c5c3_Err | |
| 1459 | 1508 | } | |
| 1460 | 1509 | } | |
| 1461 | 1510 | if c.Verdict != "" { | |
| 1462 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 131, "<span class=\"c-state\">") | |
| 1511 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 137, "<span class=\"c-state\">") | |
| 1463 | 1512 | if templ_7745c5c3_Err != nil { | |
| 1464 | 1513 | return templ_7745c5c3_Err | |
| 1465 | 1514 | } | |
| 1466 | - | var templ_7745c5c3_Var70 string | |
| 1467 | - | templ_7745c5c3_Var70, templ_7745c5c3_Err = templ.JoinStringErrs(c.Verdict) | |
| 1515 | + | var templ_7745c5c3_Var72 string | |
| 1516 | + | templ_7745c5c3_Var72, templ_7745c5c3_Err = templ.JoinStringErrs(c.Verdict) | |
| 1468 | 1517 | if templ_7745c5c3_Err != nil { | |
| 1469 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 273, Col: 36} | |
| 1518 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 282, Col: 36} | |
| 1470 | 1519 | } | |
| 1471 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var70)) | |
| 1520 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var72)) | |
| 1472 | 1521 | if templ_7745c5c3_Err != nil { | |
| 1473 | 1522 | return templ_7745c5c3_Err | |
| 1474 | 1523 | } | |
| 1475 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 132, "</span> ") | |
| 1524 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 138, "</span> ") | |
| 1476 | 1525 | if templ_7745c5c3_Err != nil { | |
| 1477 | 1526 | return templ_7745c5c3_Err | |
| 1478 | 1527 | } | |
| 1479 | 1528 | } | |
| 1480 | 1529 | if c.Range != "" { | |
| 1481 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 133, "<span class=\"mute\">") | |
| 1530 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 139, "<span class=\"mute\">") | |
| 1482 | 1531 | if templ_7745c5c3_Err != nil { | |
| 1483 | 1532 | return templ_7745c5c3_Err | |
| 1484 | 1533 | } | |
| 1485 | - | var templ_7745c5c3_Var71 string | |
| 1486 | - | templ_7745c5c3_Var71, templ_7745c5c3_Err = templ.JoinStringErrs(c.Range) | |
| 1534 | + | var templ_7745c5c3_Var73 string | |
| 1535 | + | templ_7745c5c3_Var73, templ_7745c5c3_Err = templ.JoinStringErrs(c.Range) | |
| 1487 | 1536 | if templ_7745c5c3_Err != nil { | |
| 1488 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 276, Col: 31} | |
| 1537 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 285, Col: 31} | |
| 1489 | 1538 | } | |
| 1490 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var71)) | |
| 1539 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var73)) | |
| 1491 | 1540 | if templ_7745c5c3_Err != nil { | |
| 1492 | 1541 | return templ_7745c5c3_Err | |
| 1493 | 1542 | } | |
| 1494 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 134, "</span> ") | |
| 1543 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 140, "</span> ") | |
| 1495 | 1544 | if templ_7745c5c3_Err != nil { | |
| 1496 | 1545 | return templ_7745c5c3_Err | |
| 1497 | 1546 | } | |
| 1498 | 1547 | } | |
| 1499 | 1548 | if c.Quote != "" { | |
| 1500 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 135, "<button type=\"button\" class=\"btn\" data-js data-quote=\"") | |
| 1549 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 141, "<button type=\"button\" class=\"btn\" data-js data-quote=\"") | |
| 1501 | 1550 | if templ_7745c5c3_Err != nil { | |
| 1502 | 1551 | return templ_7745c5c3_Err | |
| 1503 | 1552 | } | |
| 1504 | - | var templ_7745c5c3_Var72 string | |
| 1505 | - | templ_7745c5c3_Var72, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.Quote) | |
| 1553 | + | var templ_7745c5c3_Var74 string | |
| 1554 | + | templ_7745c5c3_Var74, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.Quote) | |
| 1506 | 1555 | if templ_7745c5c3_Err != nil { | |
| 1507 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 279, Col: 65} | |
| 1556 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 288, Col: 65} | |
| 1508 | 1557 | } | |
| 1509 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var72) | |
| 1558 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var74) | |
| 1510 | 1559 | if templ_7745c5c3_Err != nil { | |
| 1511 | 1560 | return templ_7745c5c3_Err | |
| 1512 | 1561 | } | |
| 1513 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 136, "\">Quote</button> ") | |
| 1562 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 142, "\">Quote</button> ") | |
| 1514 | 1563 | if templ_7745c5c3_Err != nil { | |
| 1515 | 1564 | return templ_7745c5c3_Err | |
| 1516 | 1565 | } | |
| 1517 | 1566 | } | |
| 1518 | 1567 | if c.Edit != nil { | |
| 1519 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 137, "<details><summary>Edit</summary><form method=\"post\" action=\"") | |
| 1568 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 143, "<details><summary>Edit</summary><form method=\"post\" action=\"") | |
| 1520 | 1569 | if templ_7745c5c3_Err != nil { | |
| 1521 | 1570 | return templ_7745c5c3_Err | |
| 1522 | 1571 | } | |
| 1523 | - | var templ_7745c5c3_Var73 templ.SafeURL | |
| 1524 | - | templ_7745c5c3_Var73, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Edit.Action)) | |
| 1572 | + | var templ_7745c5c3_Var75 templ.SafeURL | |
| 1573 | + | templ_7745c5c3_Var75, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Edit.Action)) | |
| 1525 | 1574 | if templ_7745c5c3_Err != nil { | |
| 1526 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 282, Col: 92} | |
| 1575 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 291, Col: 92} | |
| 1527 | 1576 | } | |
| 1528 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var73)) | |
| 1577 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var75)) | |
| 1529 | 1578 | if templ_7745c5c3_Err != nil { | |
| 1530 | 1579 | return templ_7745c5c3_Err | |
| 1531 | 1580 | } | |
| 1532 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 138, "\"><input type=\"hidden\" name=\"from\" value=\"") | |
| 1581 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 144, "\"><input type=\"hidden\" name=\"from\" value=\"") | |
| 1533 | 1582 | if templ_7745c5c3_Err != nil { | |
| 1534 | 1583 | return templ_7745c5c3_Err | |
| 1535 | 1584 | } | |
| 1536 | - | var templ_7745c5c3_Var74 string | |
| 1537 | - | templ_7745c5c3_Var74, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(c.Edit.From)) | |
| 1585 | + | var templ_7745c5c3_Var76 string | |
| 1586 | + | templ_7745c5c3_Var76, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(c.Edit.From)) | |
| 1538 | 1587 | if templ_7745c5c3_Err != nil { | |
| 1539 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 282, Col: 153} | |
| 1588 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 291, Col: 153} | |
| 1540 | 1589 | } | |
| 1541 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var74) | |
| 1590 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var76) | |
| 1542 | 1591 | if templ_7745c5c3_Err != nil { | |
| 1543 | 1592 | return templ_7745c5c3_Err | |
| 1544 | 1593 | } | |
| 1545 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 139, "\"><textarea name=\"text\" maxlength=\"8000\">") | |
| 1594 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 145, "\"><textarea name=\"text\" maxlength=\"8000\">") | |
| 1546 | 1595 | if templ_7745c5c3_Err != nil { | |
| 1547 | 1596 | return templ_7745c5c3_Err | |
| 1548 | 1597 | } | |
| 1549 | - | var templ_7745c5c3_Var75 string | |
| 1550 | - | templ_7745c5c3_Var75, templ_7745c5c3_Err = templ.JoinStringErrs(c.Edit.Text) | |
| 1598 | + | var templ_7745c5c3_Var77 string | |
| 1599 | + | templ_7745c5c3_Var77, templ_7745c5c3_Err = templ.JoinStringErrs(c.Edit.Text) | |
| 1551 | 1600 | if templ_7745c5c3_Err != nil { | |
| 1552 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 282, Col: 209} | |
| 1601 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 291, Col: 209} | |
| 1553 | 1602 | } | |
| 1554 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var75)) | |
| 1603 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var77)) | |
| 1555 | 1604 | if templ_7745c5c3_Err != nil { | |
| 1556 | 1605 | return templ_7745c5c3_Err | |
| 1557 | 1606 | } | |
| 1558 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 140, "</textarea><button class=\"btn\" type=\"submit\">Save</button></form></details> ") | |
| 1607 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 146, "</textarea><button class=\"btn\" type=\"submit\">Save</button></form></details> ") | |
| 1559 | 1608 | if templ_7745c5c3_Err != nil { | |
| 1560 | 1609 | return templ_7745c5c3_Err | |
| 1561 | 1610 | } | |
| 1562 | 1611 | } | |
| 1563 | 1612 | if c.Delete != nil { | |
| 1564 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 141, "<details><summary>Delete</summary><form method=\"post\" action=\"") | |
| 1613 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 147, "<details><summary>Delete</summary><form method=\"post\" action=\"") | |
| 1565 | 1614 | if templ_7745c5c3_Err != nil { | |
| 1566 | 1615 | return templ_7745c5c3_Err | |
| 1567 | 1616 | } | |
| 1568 | - | var templ_7745c5c3_Var76 templ.SafeURL | |
| 1569 | - | templ_7745c5c3_Var76, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Delete.Action)) | |
| 1617 | + | var templ_7745c5c3_Var78 templ.SafeURL | |
| 1618 | + | templ_7745c5c3_Var78, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Delete.Action)) | |
| 1570 | 1619 | if templ_7745c5c3_Err != nil { | |
| 1571 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 285, Col: 96} | |
| 1620 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 294, Col: 96} | |
| 1572 | 1621 | } | |
| 1573 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var76)) | |
| 1622 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var78)) | |
| 1574 | 1623 | if templ_7745c5c3_Err != nil { | |
| 1575 | 1624 | return templ_7745c5c3_Err | |
| 1576 | 1625 | } | |
| 1577 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 142, "\"><input type=\"hidden\" name=\"from\" value=\"") | |
| 1626 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 148, "\"><input type=\"hidden\" name=\"from\" value=\"") | |
| 1578 | 1627 | if templ_7745c5c3_Err != nil { | |
| 1579 | 1628 | return templ_7745c5c3_Err | |
| 1580 | 1629 | } | |
| 1581 | - | var templ_7745c5c3_Var77 string | |
| 1582 | - | templ_7745c5c3_Var77, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(c.Delete.From)) | |
| 1630 | + | var templ_7745c5c3_Var79 string | |
| 1631 | + | templ_7745c5c3_Var79, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(c.Delete.From)) | |
| 1583 | 1632 | if templ_7745c5c3_Err != nil { | |
| 1584 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 285, Col: 159} | |
| 1633 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 294, Col: 159} | |
| 1585 | 1634 | } | |
| 1586 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var77) | |
| 1635 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var79) | |
| 1587 | 1636 | if templ_7745c5c3_Err != nil { | |
| 1588 | 1637 | return templ_7745c5c3_Err | |
| 1589 | 1638 | } | |
| 1590 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 143, "\"><p>Delete this note?</p><button class=\"btn\" type=\"submit\">Confirm delete</button></form></details> ") | |
| 1639 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 149, "\"><p>Delete this note?</p><button class=\"btn\" type=\"submit\">Confirm delete</button></form></details> ") | |
| 1591 | 1640 | if templ_7745c5c3_Err != nil { | |
| 1592 | 1641 | return templ_7745c5c3_Err | |
| 1593 | 1642 | } | |
| @@ -1599,7 +1648,7 @@ func commentView(c CommentView, suffix string) templ.Component { | |||
| 1599 | 1648 | } | |
| 1600 | 1649 | } | |
| 1601 | 1650 | if c.Reply != nil { | |
| 1602 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 144, "<details data-reply><summary>Reply</summary>") | |
| 1651 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 150, "<details data-reply><summary>Reply</summary>") | |
| 1603 | 1652 | if templ_7745c5c3_Err != nil { | |
| 1604 | 1653 | return templ_7745c5c3_Err | |
| 1605 | 1654 | } | |
| @@ -1607,80 +1656,80 @@ func commentView(c CommentView, suffix string) templ.Component { | |||
| 1607 | 1656 | if templ_7745c5c3_Err != nil { | |
| 1608 | 1657 | return templ_7745c5c3_Err | |
| 1609 | 1658 | } | |
| 1610 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 145, "</details> ") | |
| 1659 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 151, "</details> ") | |
| 1611 | 1660 | if templ_7745c5c3_Err != nil { | |
| 1612 | 1661 | return templ_7745c5c3_Err | |
| 1613 | 1662 | } | |
| 1614 | 1663 | } | |
| 1615 | 1664 | if c.Resolve != nil { | |
| 1616 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 146, "<form class=\"c-act\" method=\"post\" action=\"") | |
| 1665 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 152, "<form class=\"c-act\" method=\"post\" action=\"") | |
| 1617 | 1666 | if templ_7745c5c3_Err != nil { | |
| 1618 | 1667 | return templ_7745c5c3_Err | |
| 1619 | 1668 | } | |
| 1620 | - | var templ_7745c5c3_Var78 templ.SafeURL | |
| 1621 | - | templ_7745c5c3_Var78, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Resolve.Action)) | |
| 1669 | + | var templ_7745c5c3_Var80 templ.SafeURL | |
| 1670 | + | templ_7745c5c3_Var80, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Resolve.Action)) | |
| 1622 | 1671 | if templ_7745c5c3_Err != nil { | |
| 1623 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 296, Col: 77} | |
| 1672 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 305, Col: 77} | |
| 1624 | 1673 | } | |
| 1625 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var78)) | |
| 1674 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var80)) | |
| 1626 | 1675 | if templ_7745c5c3_Err != nil { | |
| 1627 | 1676 | return templ_7745c5c3_Err | |
| 1628 | 1677 | } | |
| 1629 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 147, "\"><input type=\"hidden\" name=\"resolved\" value=\"") | |
| 1678 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 153, "\"><input type=\"hidden\" name=\"resolved\" value=\"") | |
| 1630 | 1679 | if templ_7745c5c3_Err != nil { | |
| 1631 | 1680 | return templ_7745c5c3_Err | |
| 1632 | 1681 | } | |
| 1633 | - | var templ_7745c5c3_Var79 string | |
| 1634 | - | templ_7745c5c3_Var79, templ_7745c5c3_Err = templ.ResolveAttributeValue(boolText(c.Resolve.Value)) | |
| 1682 | + | var templ_7745c5c3_Var81 string | |
| 1683 | + | templ_7745c5c3_Var81, templ_7745c5c3_Err = templ.ResolveAttributeValue(boolText(c.Resolve.Value)) | |
| 1635 | 1684 | if templ_7745c5c3_Err != nil { | |
| 1636 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 297, Col: 74} | |
| 1685 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 306, Col: 74} | |
| 1637 | 1686 | } | |
| 1638 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var79) | |
| 1687 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var81) | |
| 1639 | 1688 | if templ_7745c5c3_Err != nil { | |
| 1640 | 1689 | return templ_7745c5c3_Err | |
| 1641 | 1690 | } | |
| 1642 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 148, "\"> ") | |
| 1691 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 154, "\"> ") | |
| 1643 | 1692 | if templ_7745c5c3_Err != nil { | |
| 1644 | 1693 | return templ_7745c5c3_Err | |
| 1645 | 1694 | } | |
| 1646 | 1695 | if c.Resolve.From > 0 { | |
| 1647 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 149, "<input type=\"hidden\" name=\"from\" value=\"") | |
| 1696 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 155, "<input type=\"hidden\" name=\"from\" value=\"") | |
| 1648 | 1697 | if templ_7745c5c3_Err != nil { | |
| 1649 | 1698 | return templ_7745c5c3_Err | |
| 1650 | 1699 | } | |
| 1651 | - | var templ_7745c5c3_Var80 string | |
| 1652 | - | templ_7745c5c3_Var80, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(c.Resolve.From)) | |
| 1700 | + | var templ_7745c5c3_Var82 string | |
| 1701 | + | templ_7745c5c3_Var82, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(c.Resolve.From)) | |
| 1653 | 1702 | if templ_7745c5c3_Err != nil { | |
| 1654 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 299, Col: 66} | |
| 1703 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 308, Col: 66} | |
| 1655 | 1704 | } | |
| 1656 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var80) | |
| 1705 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var82) | |
| 1657 | 1706 | if templ_7745c5c3_Err != nil { | |
| 1658 | 1707 | return templ_7745c5c3_Err | |
| 1659 | 1708 | } | |
| 1660 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 150, "\"> ") | |
| 1709 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 156, "\"> ") | |
| 1661 | 1710 | if templ_7745c5c3_Err != nil { | |
| 1662 | 1711 | return templ_7745c5c3_Err | |
| 1663 | 1712 | } | |
| 1664 | 1713 | } | |
| 1665 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 151, "<button type=\"submit\" class=\"btn\">") | |
| 1714 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 157, "<button type=\"submit\" class=\"btn\">") | |
| 1666 | 1715 | if templ_7745c5c3_Err != nil { | |
| 1667 | 1716 | return templ_7745c5c3_Err | |
| 1668 | 1717 | } | |
| 1669 | - | var templ_7745c5c3_Var81 string | |
| 1670 | - | templ_7745c5c3_Var81, templ_7745c5c3_Err = templ.JoinStringErrs(c.Resolve.Label) | |
| 1718 | + | var templ_7745c5c3_Var83 string | |
| 1719 | + | templ_7745c5c3_Var83, templ_7745c5c3_Err = templ.JoinStringErrs(c.Resolve.Label) | |
| 1671 | 1720 | if templ_7745c5c3_Err != nil { | |
| 1672 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 301, Col: 55} | |
| 1721 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 310, Col: 55} | |
| 1673 | 1722 | } | |
| 1674 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var81)) | |
| 1723 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var83)) | |
| 1675 | 1724 | if templ_7745c5c3_Err != nil { | |
| 1676 | 1725 | return templ_7745c5c3_Err | |
| 1677 | 1726 | } | |
| 1678 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 152, "</button></form>") | |
| 1727 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 158, "</button></form>") | |
| 1679 | 1728 | if templ_7745c5c3_Err != nil { | |
| 1680 | 1729 | return templ_7745c5c3_Err | |
| 1681 | 1730 | } | |
| 1682 | 1731 | } | |
| 1683 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 153, "</div>") | |
| 1732 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 159, "</div>") | |
| 1684 | 1733 | if templ_7745c5c3_Err != nil { | |
| 1685 | 1734 | return templ_7745c5c3_Err | |
| 1686 | 1735 | } | |
| @@ -1712,214 +1761,214 @@ func commentForm(f CommentForm, suffix string) templ.Component { | |||
| 1712 | 1761 | }() | |
| 1713 | 1762 | } | |
| 1714 | 1763 | ctx = templ.InitializeContext(ctx) | |
| 1715 | - | templ_7745c5c3_Var82 := templ.GetChildren(ctx) | |
| 1716 | - | if templ_7745c5c3_Var82 == nil { | |
| 1717 | - | templ_7745c5c3_Var82 = templ.NopComponent | |
| 1764 | + | templ_7745c5c3_Var84 := templ.GetChildren(ctx) | |
| 1765 | + | if templ_7745c5c3_Var84 == nil { | |
| 1766 | + | templ_7745c5c3_Var84 = templ.NopComponent | |
| 1718 | 1767 | } | |
| 1719 | 1768 | ctx = templ.ClearChildren(ctx) | |
| 1720 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 154, "<form class=\"cform\" method=\"post\" action=\"") | |
| 1769 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 160, "<form class=\"cform\" method=\"post\" action=\"") | |
| 1721 | 1770 | if templ_7745c5c3_Err != nil { | |
| 1722 | 1771 | return templ_7745c5c3_Err | |
| 1723 | 1772 | } | |
| 1724 | - | var templ_7745c5c3_Var83 templ.SafeURL | |
| 1725 | - | templ_7745c5c3_Var83, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(f.Action)) | |
| 1773 | + | var templ_7745c5c3_Var85 templ.SafeURL | |
| 1774 | + | templ_7745c5c3_Var85, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(f.Action)) | |
| 1726 | 1775 | if templ_7745c5c3_Err != nil { | |
| 1727 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 316, Col: 67} | |
| 1776 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 325, Col: 67} | |
| 1728 | 1777 | } | |
| 1729 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var83)) | |
| 1778 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var85)) | |
| 1730 | 1779 | if templ_7745c5c3_Err != nil { | |
| 1731 | 1780 | return templ_7745c5c3_Err | |
| 1732 | 1781 | } | |
| 1733 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 155, "\"><label for=\"") | |
| 1782 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 161, "\"><label for=\"") | |
| 1734 | 1783 | if templ_7745c5c3_Err != nil { | |
| 1735 | 1784 | return templ_7745c5c3_Err | |
| 1736 | 1785 | } | |
| 1737 | - | var templ_7745c5c3_Var84 string | |
| 1738 | - | templ_7745c5c3_Var84, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.ID + suffix) | |
| 1786 | + | var templ_7745c5c3_Var86 string | |
| 1787 | + | templ_7745c5c3_Var86, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.ID + suffix) | |
| 1739 | 1788 | if templ_7745c5c3_Err != nil { | |
| 1740 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 317, Col: 28} | |
| 1789 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 326, Col: 28} | |
| 1741 | 1790 | } | |
| 1742 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var84) | |
| 1791 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var86) | |
| 1743 | 1792 | if templ_7745c5c3_Err != nil { | |
| 1744 | 1793 | return templ_7745c5c3_Err | |
| 1745 | 1794 | } | |
| 1746 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 156, "\">") | |
| 1795 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 162, "\">") | |
| 1747 | 1796 | if templ_7745c5c3_Err != nil { | |
| 1748 | 1797 | return templ_7745c5c3_Err | |
| 1749 | 1798 | } | |
| 1750 | - | var templ_7745c5c3_Var85 string | |
| 1751 | - | templ_7745c5c3_Var85, templ_7745c5c3_Err = templ.JoinStringErrs(f.Title) | |
| 1799 | + | var templ_7745c5c3_Var87 string | |
| 1800 | + | templ_7745c5c3_Var87, templ_7745c5c3_Err = templ.JoinStringErrs(f.Title) | |
| 1752 | 1801 | if templ_7745c5c3_Err != nil { | |
| 1753 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 317, Col: 40} | |
| 1802 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 326, Col: 40} | |
| 1754 | 1803 | } | |
| 1755 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var85)) | |
| 1804 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var87)) | |
| 1756 | 1805 | if templ_7745c5c3_Err != nil { | |
| 1757 | 1806 | return templ_7745c5c3_Err | |
| 1758 | 1807 | } | |
| 1759 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 157, "</label> <textarea id=\"") | |
| 1808 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 163, "</label> <textarea id=\"") | |
| 1760 | 1809 | if templ_7745c5c3_Err != nil { | |
| 1761 | 1810 | return templ_7745c5c3_Err | |
| 1762 | 1811 | } | |
| 1763 | - | var templ_7745c5c3_Var86 string | |
| 1764 | - | templ_7745c5c3_Var86, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.ID + suffix) | |
| 1812 | + | var templ_7745c5c3_Var88 string | |
| 1813 | + | templ_7745c5c3_Var88, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.ID + suffix) | |
| 1765 | 1814 | if templ_7745c5c3_Err != nil { | |
| 1766 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 319, Col: 21} | |
| 1815 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 328, Col: 21} | |
| 1767 | 1816 | } | |
| 1768 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var86) | |
| 1817 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var88) | |
| 1769 | 1818 | if templ_7745c5c3_Err != nil { | |
| 1770 | 1819 | return templ_7745c5c3_Err | |
| 1771 | 1820 | } | |
| 1772 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 158, "\" name=\"text\" rows=\"3\" maxlength=\"8000\"") | |
| 1821 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 164, "\" name=\"text\" rows=\"3\" maxlength=\"8000\"") | |
| 1773 | 1822 | if templ_7745c5c3_Err != nil { | |
| 1774 | 1823 | return templ_7745c5c3_Err | |
| 1775 | 1824 | } | |
| 1776 | 1825 | if f.Plain { | |
| 1777 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 159, " required placeholder=\"plain text, 8000 characters at most\"") | |
| 1826 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 165, " required placeholder=\"plain text, 8000 characters at most\"") | |
| 1778 | 1827 | if templ_7745c5c3_Err != nil { | |
| 1779 | 1828 | return templ_7745c5c3_Err | |
| 1780 | 1829 | } | |
| 1781 | 1830 | } else { | |
| 1782 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 160, " placeholder=\"Markdown, 8000 characters at most\"") | |
| 1831 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 166, " placeholder=\"Markdown, 8000 characters at most\"") | |
| 1783 | 1832 | if templ_7745c5c3_Err != nil { | |
| 1784 | 1833 | return templ_7745c5c3_Err | |
| 1785 | 1834 | } | |
| 1786 | 1835 | } | |
| 1787 | 1836 | if f.Focus { | |
| 1788 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 161, " autofocus") | |
| 1837 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 167, " autofocus") | |
| 1789 | 1838 | if templ_7745c5c3_Err != nil { | |
| 1790 | 1839 | return templ_7745c5c3_Err | |
| 1791 | 1840 | } | |
| 1792 | 1841 | } | |
| 1793 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 162, "></textarea> ") | |
| 1842 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 168, "></textarea> ") | |
| 1794 | 1843 | if templ_7745c5c3_Err != nil { | |
| 1795 | 1844 | return templ_7745c5c3_Err | |
| 1796 | 1845 | } | |
| 1797 | 1846 | if f.Path != "" { | |
| 1798 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 163, "<input type=\"hidden\" name=\"path\" value=\"") | |
| 1847 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 169, "<input type=\"hidden\" name=\"path\" value=\"") | |
| 1799 | 1848 | if templ_7745c5c3_Err != nil { | |
| 1800 | 1849 | return templ_7745c5c3_Err | |
| 1801 | 1850 | } | |
| 1802 | - | var templ_7745c5c3_Var87 string | |
| 1803 | - | templ_7745c5c3_Var87, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.Path) | |
| 1851 | + | var templ_7745c5c3_Var89 string | |
| 1852 | + | templ_7745c5c3_Var89, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.Path) | |
| 1804 | 1853 | if templ_7745c5c3_Err != nil { | |
| 1805 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 334, Col: 50} | |
| 1854 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 343, Col: 50} | |
| 1806 | 1855 | } | |
| 1807 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var87) | |
| 1856 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var89) | |
| 1808 | 1857 | if templ_7745c5c3_Err != nil { | |
| 1809 | 1858 | return templ_7745c5c3_Err | |
| 1810 | 1859 | } | |
| 1811 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 164, "\"> <input type=\"hidden\" name=\"line\" value=\"") | |
| 1860 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 170, "\"> <input type=\"hidden\" name=\"line\" value=\"") | |
| 1812 | 1861 | if templ_7745c5c3_Err != nil { | |
| 1813 | 1862 | return templ_7745c5c3_Err | |
| 1814 | 1863 | } | |
| 1815 | - | var templ_7745c5c3_Var88 string | |
| 1816 | - | templ_7745c5c3_Var88, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.Line) | |
| 1864 | + | var templ_7745c5c3_Var90 string | |
| 1865 | + | templ_7745c5c3_Var90, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.Line) | |
| 1817 | 1866 | if templ_7745c5c3_Err != nil { | |
| 1818 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 335, Col: 50} | |
| 1867 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 344, Col: 50} | |
| 1819 | 1868 | } | |
| 1820 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var88) | |
| 1869 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var90) | |
| 1821 | 1870 | if templ_7745c5c3_Err != nil { | |
| 1822 | 1871 | return templ_7745c5c3_Err | |
| 1823 | 1872 | } | |
| 1824 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 165, "\"> <input type=\"hidden\" name=\"side\" value=\"") | |
| 1873 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 171, "\"> <input type=\"hidden\" name=\"side\" value=\"") | |
| 1825 | 1874 | if templ_7745c5c3_Err != nil { | |
| 1826 | 1875 | return templ_7745c5c3_Err | |
| 1827 | 1876 | } | |
| 1828 | - | var templ_7745c5c3_Var89 string | |
| 1829 | - | templ_7745c5c3_Var89, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.Side) | |
| 1877 | + | var templ_7745c5c3_Var91 string | |
| 1878 | + | templ_7745c5c3_Var91, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.Side) | |
| 1830 | 1879 | if templ_7745c5c3_Err != nil { | |
| 1831 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 336, Col: 50} | |
| 1880 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 345, Col: 50} | |
| 1832 | 1881 | } | |
| 1833 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var89) | |
| 1882 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var91) | |
| 1834 | 1883 | if templ_7745c5c3_Err != nil { | |
| 1835 | 1884 | return templ_7745c5c3_Err | |
| 1836 | 1885 | } | |
| 1837 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 166, "\"> <label>from line <input type=\"number\" name=\"start_line\" min=\"1\" value=\"") | |
| 1886 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 172, "\"> <label>from line <input type=\"number\" name=\"start_line\" min=\"1\" value=\"") | |
| 1838 | 1887 | if templ_7745c5c3_Err != nil { | |
| 1839 | 1888 | return templ_7745c5c3_Err | |
| 1840 | 1889 | } | |
| 1841 | - | var templ_7745c5c3_Var90 string | |
| 1842 | - | templ_7745c5c3_Var90, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.StartLine) | |
| 1890 | + | var templ_7745c5c3_Var92 string | |
| 1891 | + | templ_7745c5c3_Var92, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.StartLine) | |
| 1843 | 1892 | if templ_7745c5c3_Err != nil { | |
| 1844 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 337, Col: 86} | |
| 1893 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 346, Col: 86} | |
| 1845 | 1894 | } | |
| 1846 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var90) | |
| 1895 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var92) | |
| 1847 | 1896 | if templ_7745c5c3_Err != nil { | |
| 1848 | 1897 | return templ_7745c5c3_Err | |
| 1849 | 1898 | } | |
| 1850 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 167, "\"></label> ") | |
| 1899 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 173, "\"></label> ") | |
| 1851 | 1900 | if templ_7745c5c3_Err != nil { | |
| 1852 | 1901 | return templ_7745c5c3_Err | |
| 1853 | 1902 | } | |
| 1854 | 1903 | } | |
| 1855 | 1904 | if f.ReplyTo != "" { | |
| 1856 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 168, "<input type=\"hidden\" name=\"reply_to\" value=\"") | |
| 1905 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 174, "<input type=\"hidden\" name=\"reply_to\" value=\"") | |
| 1857 | 1906 | if templ_7745c5c3_Err != nil { | |
| 1858 | 1907 | return templ_7745c5c3_Err | |
| 1859 | 1908 | } | |
| 1860 | - | var templ_7745c5c3_Var91 string | |
| 1861 | - | templ_7745c5c3_Var91, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.ReplyTo) | |
| 1909 | + | var templ_7745c5c3_Var93 string | |
| 1910 | + | templ_7745c5c3_Var93, templ_7745c5c3_Err = templ.ResolveAttributeValue(f.ReplyTo) | |
| 1862 | 1911 | if templ_7745c5c3_Err != nil { | |
| 1863 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 340, Col: 57} | |
| 1912 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 349, Col: 57} | |
| 1864 | 1913 | } | |
| 1865 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var91) | |
| 1914 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var93) | |
| 1866 | 1915 | if templ_7745c5c3_Err != nil { | |
| 1867 | 1916 | return templ_7745c5c3_Err | |
| 1868 | 1917 | } | |
| 1869 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 169, "\"> ") | |
| 1918 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 175, "\"> ") | |
| 1870 | 1919 | if templ_7745c5c3_Err != nil { | |
| 1871 | 1920 | return templ_7745c5c3_Err | |
| 1872 | 1921 | } | |
| 1873 | 1922 | } | |
| 1874 | 1923 | if f.Path == "" && f.ReplyTo == "" && !f.Plain { | |
| 1875 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 170, "<fieldset><legend>Verdict</legend><label><input type=\"radio\" name=\"verdict\" value=\"\" checked>Note</label><label><input type=\"radio\" name=\"verdict\" value=\"approve\">Approve</label><label><input type=\"radio\" name=\"verdict\" value=\"changes_requested\">Request changes</label></fieldset>") | |
| 1924 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 176, "<fieldset><legend>Verdict</legend><label><input type=\"radio\" name=\"verdict\" value=\"\" checked>Note</label><label><input type=\"radio\" name=\"verdict\" value=\"approve\">Approve</label><label><input type=\"radio\" name=\"verdict\" value=\"changes_requested\">Request changes</label></fieldset>") | |
| 1876 | 1925 | if templ_7745c5c3_Err != nil { | |
| 1877 | 1926 | return templ_7745c5c3_Err | |
| 1878 | 1927 | } | |
| 1879 | 1928 | } | |
| 1880 | 1929 | if f.From > 0 { | |
| 1881 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 171, "<input type=\"hidden\" name=\"from\" value=\"") | |
| 1930 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 177, "<input type=\"hidden\" name=\"from\" value=\"") | |
| 1882 | 1931 | if templ_7745c5c3_Err != nil { | |
| 1883 | 1932 | return templ_7745c5c3_Err | |
| 1884 | 1933 | } | |
| 1885 | - | var templ_7745c5c3_Var92 string | |
| 1886 | - | templ_7745c5c3_Var92, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(f.From)) | |
| 1934 | + | var templ_7745c5c3_Var94 string | |
| 1935 | + | templ_7745c5c3_Var94, templ_7745c5c3_Err = templ.ResolveAttributeValue(itoa(f.From)) | |
| 1887 | 1936 | if templ_7745c5c3_Err != nil { | |
| 1888 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 346, Col: 56} | |
| 1937 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 355, Col: 56} | |
| 1889 | 1938 | } | |
| 1890 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var92) | |
| 1939 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var94) | |
| 1891 | 1940 | if templ_7745c5c3_Err != nil { | |
| 1892 | 1941 | return templ_7745c5c3_Err | |
| 1893 | 1942 | } | |
| 1894 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 172, "\">") | |
| 1943 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 178, "\">") | |
| 1895 | 1944 | if templ_7745c5c3_Err != nil { | |
| 1896 | 1945 | return templ_7745c5c3_Err | |
| 1897 | 1946 | } | |
| 1898 | 1947 | } | |
| 1899 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 173, "<div class=\"f-btns\"><button type=\"submit\" class=\"btn primary\">Comment</button> ") | |
| 1948 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 179, "<div class=\"f-btns\"><button type=\"submit\" class=\"btn primary\">Comment</button> ") | |
| 1900 | 1949 | if templ_7745c5c3_Err != nil { | |
| 1901 | 1950 | return templ_7745c5c3_Err | |
| 1902 | 1951 | } | |
| 1903 | 1952 | if f.Cancel != "" { | |
| 1904 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 174, "<a class=\"btn\" href=\"") | |
| 1953 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 180, "<a class=\"btn\" href=\"") | |
| 1905 | 1954 | if templ_7745c5c3_Err != nil { | |
| 1906 | 1955 | return templ_7745c5c3_Err | |
| 1907 | 1956 | } | |
| 1908 | - | var templ_7745c5c3_Var93 templ.SafeURL | |
| 1909 | - | templ_7745c5c3_Var93, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(f.Cancel)) | |
| 1957 | + | var templ_7745c5c3_Var95 templ.SafeURL | |
| 1958 | + | templ_7745c5c3_Var95, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(f.Cancel)) | |
| 1910 | 1959 | if templ_7745c5c3_Err != nil { | |
| 1911 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 351, Col: 49} | |
| 1960 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 360, Col: 49} | |
| 1912 | 1961 | } | |
| 1913 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var93)) | |
| 1962 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var95)) | |
| 1914 | 1963 | if templ_7745c5c3_Err != nil { | |
| 1915 | 1964 | return templ_7745c5c3_Err | |
| 1916 | 1965 | } | |
| 1917 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 175, "\">Cancel</a>") | |
| 1966 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 181, "\">Cancel</a>") | |
| 1918 | 1967 | if templ_7745c5c3_Err != nil { | |
| 1919 | 1968 | return templ_7745c5c3_Err | |
| 1920 | 1969 | } | |
| 1921 | 1970 | } | |
| 1922 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 176, "</div></form>") | |
| 1971 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 182, "</div></form>") | |
| 1923 | 1972 | if templ_7745c5c3_Err != nil { | |
| 1924 | 1973 | return templ_7745c5c3_Err | |
| 1925 | 1974 | } | |
| @@ -1943,25 +1992,25 @@ func mrRail(p MRPage) templ.Component { | |||
| 1943 | 1992 | }() | |
| 1944 | 1993 | } | |
| 1945 | 1994 | ctx = templ.InitializeContext(ctx) | |
| 1946 | - | templ_7745c5c3_Var94 := templ.GetChildren(ctx) | |
| 1947 | - | if templ_7745c5c3_Var94 == nil { | |
| 1948 | - | templ_7745c5c3_Var94 = templ.NopComponent | |
| 1995 | + | templ_7745c5c3_Var96 := templ.GetChildren(ctx) | |
| 1996 | + | if templ_7745c5c3_Var96 == nil { | |
| 1997 | + | templ_7745c5c3_Var96 = templ.NopComponent | |
| 1949 | 1998 | } | |
| 1950 | 1999 | ctx = templ.ClearChildren(ctx) | |
| 1951 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 177, "<section aria-labelledby=\"r-files\" class=\"r-files\"><h2 id=\"r-files\">Files <span class=\"count\">") | |
| 2000 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 183, "<section aria-labelledby=\"r-files\" class=\"r-files\"><h2 id=\"r-files\">Files <span class=\"count\">") | |
| 1952 | 2001 | if templ_7745c5c3_Err != nil { | |
| 1953 | 2002 | return templ_7745c5c3_Err | |
| 1954 | 2003 | } | |
| 1955 | - | var templ_7745c5c3_Var95 string | |
| 1956 | - | templ_7745c5c3_Var95, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Files))) | |
| 2004 | + | var templ_7745c5c3_Var97 string | |
| 2005 | + | templ_7745c5c3_Var97, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Files))) | |
| 1957 | 2006 | if templ_7745c5c3_Err != nil { | |
| 1958 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 360, Col: 49} | |
| 2007 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 369, Col: 49} | |
| 1959 | 2008 | } | |
| 1960 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var95)) | |
| 2009 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var97)) | |
| 1961 | 2010 | if templ_7745c5c3_Err != nil { | |
| 1962 | 2011 | return templ_7745c5c3_Err | |
| 1963 | 2012 | } | |
| 1964 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 178, "</span><kbd aria-hidden=\"true\">g f</kbd> <button type=\"button\" class=\"btn icon fold\" data-js aria-expanded=\"true\" title=\"fold the list of files\" hx-on:click=\"ermiteMR.foldFiles()\"><span class=\"ico i-chevron\" aria-hidden=\"true\"></span><span class=\"sr\">fold the list of files</span></button></h2>") | |
| 2013 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 184, "</span><kbd aria-hidden=\"true\">g f</kbd> <button type=\"button\" class=\"btn icon fold\" data-js aria-expanded=\"true\" title=\"fold the list of files\" hx-on:click=\"ermiteMR.foldFiles()\"><span class=\"ico i-chevron\" aria-hidden=\"true\"></span><span class=\"sr\">fold the list of files</span></button></h2>") | |
| 1965 | 2014 | if templ_7745c5c3_Err != nil { | |
| 1966 | 2015 | return templ_7745c5c3_Err | |
| 1967 | 2016 | } | |
| @@ -1969,71 +2018,71 @@ func mrRail(p MRPage) templ.Component { | |||
| 1969 | 2018 | if templ_7745c5c3_Err != nil { | |
| 1970 | 2019 | return templ_7745c5c3_Err | |
| 1971 | 2020 | } | |
| 1972 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 179, "</section>") | |
| 2021 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 185, "</section>") | |
| 1973 | 2022 | if templ_7745c5c3_Err != nil { | |
| 1974 | 2023 | return templ_7745c5c3_Err | |
| 1975 | 2024 | } | |
| 1976 | 2025 | if len(p.Verdicts) > 0 { | |
| 1977 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 180, "<section><h2>Verdicts</h2><ul>") | |
| 2026 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 186, "<section><h2>Verdicts</h2><ul>") | |
| 1978 | 2027 | if templ_7745c5c3_Err != nil { | |
| 1979 | 2028 | return templ_7745c5c3_Err | |
| 1980 | 2029 | } | |
| 1981 | 2030 | for _, v := range p.Verdicts { | |
| 1982 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 181, "<li><a href=\"") | |
| 2031 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 187, "<li><a href=\"") | |
| 1983 | 2032 | if templ_7745c5c3_Err != nil { | |
| 1984 | 2033 | return templ_7745c5c3_Err | |
| 1985 | 2034 | } | |
| 1986 | - | var templ_7745c5c3_Var96 templ.SafeURL | |
| 1987 | - | templ_7745c5c3_Var96, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(v.Href)) | |
| 2035 | + | var templ_7745c5c3_Var98 templ.SafeURL | |
| 2036 | + | templ_7745c5c3_Var98, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(v.Href)) | |
| 1988 | 2037 | if templ_7745c5c3_Err != nil { | |
| 1989 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 373, Col: 37} | |
| 2038 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 382, Col: 37} | |
| 1990 | 2039 | } | |
| 1991 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var96)) | |
| 2040 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var98)) | |
| 1992 | 2041 | if templ_7745c5c3_Err != nil { | |
| 1993 | 2042 | return templ_7745c5c3_Err | |
| 1994 | 2043 | } | |
| 1995 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 182, "\">") | |
| 2044 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 188, "\">") | |
| 1996 | 2045 | if templ_7745c5c3_Err != nil { | |
| 1997 | 2046 | return templ_7745c5c3_Err | |
| 1998 | 2047 | } | |
| 1999 | - | var templ_7745c5c3_Var97 string | |
| 2000 | - | templ_7745c5c3_Var97, templ_7745c5c3_Err = templ.JoinStringErrs(v.Author) | |
| 2048 | + | var templ_7745c5c3_Var99 string | |
| 2049 | + | templ_7745c5c3_Var99, templ_7745c5c3_Err = templ.JoinStringErrs(v.Author) | |
| 2001 | 2050 | if templ_7745c5c3_Err != nil { | |
| 2002 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 373, Col: 50} | |
| 2051 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 382, Col: 50} | |
| 2003 | 2052 | } | |
| 2004 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var97)) | |
| 2053 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var99)) | |
| 2005 | 2054 | if templ_7745c5c3_Err != nil { | |
| 2006 | 2055 | return templ_7745c5c3_Err | |
| 2007 | 2056 | } | |
| 2008 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 183, ": ") | |
| 2057 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 189, ": ") | |
| 2009 | 2058 | if templ_7745c5c3_Err != nil { | |
| 2010 | 2059 | return templ_7745c5c3_Err | |
| 2011 | 2060 | } | |
| 2012 | - | var templ_7745c5c3_Var98 string | |
| 2013 | - | templ_7745c5c3_Var98, templ_7745c5c3_Err = templ.JoinStringErrs(v.Label) | |
| 2061 | + | var templ_7745c5c3_Var100 string | |
| 2062 | + | templ_7745c5c3_Var100, templ_7745c5c3_Err = templ.JoinStringErrs(v.Label) | |
| 2014 | 2063 | if templ_7745c5c3_Err != nil { | |
| 2015 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 373, Col: 63} | |
| 2064 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 382, Col: 63} | |
| 2016 | 2065 | } | |
| 2017 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var98)) | |
| 2066 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var100)) | |
| 2018 | 2067 | if templ_7745c5c3_Err != nil { | |
| 2019 | 2068 | return templ_7745c5c3_Err | |
| 2020 | 2069 | } | |
| 2021 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 184, "</a> ") | |
| 2070 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 190, "</a> ") | |
| 2022 | 2071 | if templ_7745c5c3_Err != nil { | |
| 2023 | 2072 | return templ_7745c5c3_Err | |
| 2024 | 2073 | } | |
| 2025 | 2074 | if v.Stale { | |
| 2026 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 185, "<span class=\"mute\">(stale)</span>") | |
| 2075 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 191, "<span class=\"mute\">(stale)</span>") | |
| 2027 | 2076 | if templ_7745c5c3_Err != nil { | |
| 2028 | 2077 | return templ_7745c5c3_Err | |
| 2029 | 2078 | } | |
| 2030 | 2079 | } | |
| 2031 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 186, "</li>") | |
| 2080 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 192, "</li>") | |
| 2032 | 2081 | if templ_7745c5c3_Err != nil { | |
| 2033 | 2082 | return templ_7745c5c3_Err | |
| 2034 | 2083 | } | |
| 2035 | 2084 | } | |
| 2036 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 187, "</ul></section>") | |
| 2085 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 193, "</ul></section>") | |
| 2037 | 2086 | if templ_7745c5c3_Err != nil { | |
| 2038 | 2087 | return templ_7745c5c3_Err | |
| 2039 | 2088 | } | |
| @@ -2042,74 +2091,74 @@ func mrRail(p MRPage) templ.Component { | |||
| 2042 | 2091 | if templ_7745c5c3_Err != nil { | |
| 2043 | 2092 | return templ_7745c5c3_Err | |
| 2044 | 2093 | } | |
| 2045 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 188, "<section aria-labelledby=\"r-commits\"><h2 id=\"r-commits\">Commits <span class=\"count\">") | |
| 2094 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 194, "<section aria-labelledby=\"r-commits\"><h2 id=\"r-commits\">Commits <span class=\"count\">") | |
| 2046 | 2095 | if templ_7745c5c3_Err != nil { | |
| 2047 | 2096 | return templ_7745c5c3_Err | |
| 2048 | 2097 | } | |
| 2049 | - | var templ_7745c5c3_Var99 string | |
| 2050 | - | templ_7745c5c3_Var99, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Commits))) | |
| 2098 | + | var templ_7745c5c3_Var101 string | |
| 2099 | + | templ_7745c5c3_Var101, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Commits))) | |
| 2051 | 2100 | if templ_7745c5c3_Err != nil { | |
| 2052 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 384, Col: 71} | |
| 2101 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 393, Col: 71} | |
| 2053 | 2102 | } | |
| 2054 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var99)) | |
| 2103 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var101)) | |
| 2055 | 2104 | if templ_7745c5c3_Err != nil { | |
| 2056 | 2105 | return templ_7745c5c3_Err | |
| 2057 | 2106 | } | |
| 2058 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 189, "</span></h2><ul class=\"pcommits\">") | |
| 2107 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 195, "</span></h2><ul class=\"pcommits\">") | |
| 2059 | 2108 | if templ_7745c5c3_Err != nil { | |
| 2060 | 2109 | return templ_7745c5c3_Err | |
| 2061 | 2110 | } | |
| 2062 | 2111 | for _, c := range p.Commits { | |
| 2063 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 190, "<li") | |
| 2112 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 196, "<li") | |
| 2064 | 2113 | if templ_7745c5c3_Err != nil { | |
| 2065 | 2114 | return templ_7745c5c3_Err | |
| 2066 | 2115 | } | |
| 2067 | 2116 | if c.New { | |
| 2068 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 191, " data-new") | |
| 2117 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 197, " data-new") | |
| 2069 | 2118 | if templ_7745c5c3_Err != nil { | |
| 2070 | 2119 | return templ_7745c5c3_Err | |
| 2071 | 2120 | } | |
| 2072 | 2121 | } | |
| 2073 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 192, "><a href=\"") | |
| 2122 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 198, "><a href=\"") | |
| 2074 | 2123 | if templ_7745c5c3_Err != nil { | |
| 2075 | 2124 | return templ_7745c5c3_Err | |
| 2076 | 2125 | } | |
| 2077 | - | var templ_7745c5c3_Var100 templ.SafeURL | |
| 2078 | - | templ_7745c5c3_Var100, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Href)) | |
| 2126 | + | var templ_7745c5c3_Var102 templ.SafeURL | |
| 2127 | + | templ_7745c5c3_Var102, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.Href)) | |
| 2079 | 2128 | if templ_7745c5c3_Err != nil { | |
| 2080 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 388, Col: 36} | |
| 2129 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 397, Col: 36} | |
| 2081 | 2130 | } | |
| 2082 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var100)) | |
| 2131 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var102)) | |
| 2083 | 2132 | if templ_7745c5c3_Err != nil { | |
| 2084 | 2133 | return templ_7745c5c3_Err | |
| 2085 | 2134 | } | |
| 2086 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 193, "\">") | |
| 2135 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 199, "\">") | |
| 2087 | 2136 | if templ_7745c5c3_Err != nil { | |
| 2088 | 2137 | return templ_7745c5c3_Err | |
| 2089 | 2138 | } | |
| 2090 | - | var templ_7745c5c3_Var101 string | |
| 2091 | - | templ_7745c5c3_Var101, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short) | |
| 2139 | + | var templ_7745c5c3_Var103 string | |
| 2140 | + | templ_7745c5c3_Var103, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short) | |
| 2092 | 2141 | if templ_7745c5c3_Err != nil { | |
| 2093 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 388, Col: 48} | |
| 2142 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 397, Col: 48} | |
| 2094 | 2143 | } | |
| 2095 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var101)) | |
| 2144 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var103)) | |
| 2096 | 2145 | if templ_7745c5c3_Err != nil { | |
| 2097 | 2146 | return templ_7745c5c3_Err | |
| 2098 | 2147 | } | |
| 2099 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 194, "</a> <span class=\"s\">") | |
| 2148 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 200, "</a> <span class=\"s\">") | |
| 2100 | 2149 | if templ_7745c5c3_Err != nil { | |
| 2101 | 2150 | return templ_7745c5c3_Err | |
| 2102 | 2151 | } | |
| 2103 | - | var templ_7745c5c3_Var102 string | |
| 2104 | - | templ_7745c5c3_Var102, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject) | |
| 2152 | + | var templ_7745c5c3_Var104 string | |
| 2153 | + | templ_7745c5c3_Var104, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject) | |
| 2105 | 2154 | if templ_7745c5c3_Err != nil { | |
| 2106 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 389, Col: 32} | |
| 2155 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 398, Col: 32} | |
| 2107 | 2156 | } | |
| 2108 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var102)) | |
| 2157 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var104)) | |
| 2109 | 2158 | if templ_7745c5c3_Err != nil { | |
| 2110 | 2159 | return templ_7745c5c3_Err | |
| 2111 | 2160 | } | |
| 2112 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 195, "</span> ") | |
| 2161 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 201, "</span> ") | |
| 2113 | 2162 | if templ_7745c5c3_Err != nil { | |
| 2114 | 2163 | return templ_7745c5c3_Err | |
| 2115 | 2164 | } | |
| @@ -2119,138 +2168,138 @@ func mrRail(p MRPage) templ.Component { | |||
| 2119 | 2168 | return templ_7745c5c3_Err | |
| 2120 | 2169 | } | |
| 2121 | 2170 | } | |
| 2122 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 196, "<span class=\"mute\">") | |
| 2171 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 202, "<span class=\"mute\">") | |
| 2123 | 2172 | if templ_7745c5c3_Err != nil { | |
| 2124 | 2173 | return templ_7745c5c3_Err | |
| 2125 | 2174 | } | |
| 2126 | - | var templ_7745c5c3_Var103 string | |
| 2127 | - | templ_7745c5c3_Var103, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author) | |
| 2175 | + | var templ_7745c5c3_Var105 string | |
| 2176 | + | templ_7745c5c3_Var105, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author) | |
| 2128 | 2177 | if templ_7745c5c3_Err != nil { | |
| 2129 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 394, Col: 16} | |
| 2178 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 403, Col: 16} | |
| 2130 | 2179 | } | |
| 2131 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var103)) | |
| 2180 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var105)) | |
| 2132 | 2181 | if templ_7745c5c3_Err != nil { | |
| 2133 | 2182 | return templ_7745c5c3_Err | |
| 2134 | 2183 | } | |
| 2135 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 197, " ") | |
| 2184 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 203, " ") | |
| 2136 | 2185 | if templ_7745c5c3_Err != nil { | |
| 2137 | 2186 | return templ_7745c5c3_Err | |
| 2138 | 2187 | } | |
| 2139 | 2188 | if c.WhenAt != "" { | |
| 2140 | - | var templ_7745c5c3_Var104 string | |
| 2141 | - | templ_7745c5c3_Var104, templ_7745c5c3_Err = templ.JoinStringErrs(" · ") | |
| 2189 | + | var templ_7745c5c3_Var106 string | |
| 2190 | + | templ_7745c5c3_Var106, templ_7745c5c3_Err = templ.JoinStringErrs(" · ") | |
| 2142 | 2191 | if templ_7745c5c3_Err != nil { | |
| 2143 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 396, Col: 15} | |
| 2192 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 405, Col: 15} | |
| 2144 | 2193 | } | |
| 2145 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var104)) | |
| 2194 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var106)) | |
| 2146 | 2195 | if templ_7745c5c3_Err != nil { | |
| 2147 | 2196 | return templ_7745c5c3_Err | |
| 2148 | 2197 | } | |
| 2149 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 198, "<time datetime=\"") | |
| 2198 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 204, "<time datetime=\"") | |
| 2150 | 2199 | if templ_7745c5c3_Err != nil { | |
| 2151 | 2200 | return templ_7745c5c3_Err | |
| 2152 | 2201 | } | |
| 2153 | - | var templ_7745c5c3_Var105 string | |
| 2154 | - | templ_7745c5c3_Var105, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | |
| 2202 | + | var templ_7745c5c3_Var107 string | |
| 2203 | + | templ_7745c5c3_Var107, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | |
| 2155 | 2204 | if templ_7745c5c3_Err != nil { | |
| 2156 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 396, Col: 42} | |
| 2205 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 405, Col: 42} | |
| 2157 | 2206 | } | |
| 2158 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var105) | |
| 2207 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var107) | |
| 2159 | 2208 | if templ_7745c5c3_Err != nil { | |
| 2160 | 2209 | return templ_7745c5c3_Err | |
| 2161 | 2210 | } | |
| 2162 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 199, "\">") | |
| 2211 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 205, "\">") | |
| 2163 | 2212 | if templ_7745c5c3_Err != nil { | |
| 2164 | 2213 | return templ_7745c5c3_Err | |
| 2165 | 2214 | } | |
| 2166 | - | var templ_7745c5c3_Var106 string | |
| 2167 | - | templ_7745c5c3_Var106, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) | |
| 2215 | + | var templ_7745c5c3_Var108 string | |
| 2216 | + | templ_7745c5c3_Var108, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) | |
| 2168 | 2217 | if templ_7745c5c3_Err != nil { | |
| 2169 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 396, Col: 53} | |
| 2218 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 405, Col: 53} | |
| 2170 | 2219 | } | |
| 2171 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var106)) | |
| 2220 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var108)) | |
| 2172 | 2221 | if templ_7745c5c3_Err != nil { | |
| 2173 | 2222 | return templ_7745c5c3_Err | |
| 2174 | 2223 | } | |
| 2175 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 200, "</time> ") | |
| 2224 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 206, "</time> ") | |
| 2176 | 2225 | if templ_7745c5c3_Err != nil { | |
| 2177 | 2226 | return templ_7745c5c3_Err | |
| 2178 | 2227 | } | |
| 2179 | 2228 | } | |
| 2180 | 2229 | if c.New { | |
| 2181 | - | var templ_7745c5c3_Var107 string | |
| 2182 | - | templ_7745c5c3_Var107, templ_7745c5c3_Err = templ.JoinStringErrs(" · new") | |
| 2230 | + | var templ_7745c5c3_Var109 string | |
| 2231 | + | templ_7745c5c3_Var109, templ_7745c5c3_Err = templ.JoinStringErrs(" · new") | |
| 2183 | 2232 | if templ_7745c5c3_Err != nil { | |
| 2184 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 399, Col: 18} | |
| 2233 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 408, Col: 18} | |
| 2185 | 2234 | } | |
| 2186 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var107)) | |
| 2235 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var109)) | |
| 2187 | 2236 | if templ_7745c5c3_Err != nil { | |
| 2188 | 2237 | return templ_7745c5c3_Err | |
| 2189 | 2238 | } | |
| 2190 | 2239 | } | |
| 2191 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 201, "</span></li>") | |
| 2240 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 207, "</span></li>") | |
| 2192 | 2241 | if templ_7745c5c3_Err != nil { | |
| 2193 | 2242 | return templ_7745c5c3_Err | |
| 2194 | 2243 | } | |
| 2195 | 2244 | } | |
| 2196 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 202, "</ul>") | |
| 2245 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 208, "</ul>") | |
| 2197 | 2246 | if templ_7745c5c3_Err != nil { | |
| 2198 | 2247 | return templ_7745c5c3_Err | |
| 2199 | 2248 | } | |
| 2200 | 2249 | if len(p.Commits) == 0 { | |
| 2201 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 203, "<p class=\"dim\">no commit to merge.</p>") | |
| 2250 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 209, "<p class=\"dim\">no commit to merge.</p>") | |
| 2202 | 2251 | if templ_7745c5c3_Err != nil { | |
| 2203 | 2252 | return templ_7745c5c3_Err | |
| 2204 | 2253 | } | |
| 2205 | 2254 | } | |
| 2206 | 2255 | if p.CommitsNote != "" { | |
| 2207 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 204, "<p class=\"note\">") | |
| 2256 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 210, "<p class=\"note\">") | |
| 2208 | 2257 | if templ_7745c5c3_Err != nil { | |
| 2209 | 2258 | return templ_7745c5c3_Err | |
| 2210 | 2259 | } | |
| 2211 | - | var templ_7745c5c3_Var108 string | |
| 2212 | - | templ_7745c5c3_Var108, templ_7745c5c3_Err = templ.JoinStringErrs(p.CommitsNote) | |
| 2260 | + | var templ_7745c5c3_Var110 string | |
| 2261 | + | templ_7745c5c3_Var110, templ_7745c5c3_Err = templ.JoinStringErrs(p.CommitsNote) | |
| 2213 | 2262 | if templ_7745c5c3_Err != nil { | |
| 2214 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 409, Col: 34} | |
| 2263 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 418, Col: 34} | |
| 2215 | 2264 | } | |
| 2216 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var108)) | |
| 2265 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var110)) | |
| 2217 | 2266 | if templ_7745c5c3_Err != nil { | |
| 2218 | 2267 | return templ_7745c5c3_Err | |
| 2219 | 2268 | } | |
| 2220 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 205, "</p>") | |
| 2269 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 211, "</p>") | |
| 2221 | 2270 | if templ_7745c5c3_Err != nil { | |
| 2222 | 2271 | return templ_7745c5c3_Err | |
| 2223 | 2272 | } | |
| 2224 | 2273 | } | |
| 2225 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 206, "</section><section aria-labelledby=\"r-settings\"><h2 id=\"r-settings\">Settings</h2><dl class=\"kv\"><dt>modes</dt><dd>") | |
| 2274 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 212, "</section><section aria-labelledby=\"r-settings\"><h2 id=\"r-settings\">Settings</h2><dl class=\"kv\"><dt>modes</dt><dd>") | |
| 2226 | 2275 | if templ_7745c5c3_Err != nil { | |
| 2227 | 2276 | return templ_7745c5c3_Err | |
| 2228 | 2277 | } | |
| 2229 | - | var templ_7745c5c3_Var109 string | |
| 2230 | - | templ_7745c5c3_Var109, templ_7745c5c3_Err = templ.JoinStringErrs(strings.Join(p.Modes, ", ")) | |
| 2278 | + | var templ_7745c5c3_Var111 string | |
| 2279 | + | templ_7745c5c3_Var111, templ_7745c5c3_Err = templ.JoinStringErrs(strings.Join(p.Modes, ", ")) | |
| 2231 | 2280 | if templ_7745c5c3_Err != nil { | |
| 2232 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 416, Col: 36} | |
| 2281 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 425, Col: 36} | |
| 2233 | 2282 | } | |
| 2234 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var109)) | |
| 2283 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var111)) | |
| 2235 | 2284 | if templ_7745c5c3_Err != nil { | |
| 2236 | 2285 | return templ_7745c5c3_Err | |
| 2237 | 2286 | } | |
| 2238 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 207, "</dd><dt>checks</dt><dd>") | |
| 2287 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 213, "</dd><dt>checks</dt><dd>") | |
| 2239 | 2288 | if templ_7745c5c3_Err != nil { | |
| 2240 | 2289 | return templ_7745c5c3_Err | |
| 2241 | 2290 | } | |
| 2242 | 2291 | if p.ChecksBlock { | |
| 2243 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 208, "failing ones block the merge") | |
| 2292 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 214, "failing ones block the merge") | |
| 2244 | 2293 | if templ_7745c5c3_Err != nil { | |
| 2245 | 2294 | return templ_7745c5c3_Err | |
| 2246 | 2295 | } | |
| 2247 | 2296 | } else { | |
| 2248 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 209, "do not block the merge") | |
| 2297 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 215, "do not block the merge") | |
| 2249 | 2298 | if templ_7745c5c3_Err != nil { | |
| 2250 | 2299 | return templ_7745c5c3_Err | |
| 2251 | 2300 | } | |
| 2252 | 2301 | } | |
| 2253 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 210, "</dd></dl></section>") | |
| 2302 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 216, "</dd></dl></section>") | |
| 2254 | 2303 | if templ_7745c5c3_Err != nil { | |
| 2255 | 2304 | return templ_7745c5c3_Err | |
| 2256 | 2305 | } | |
| @@ -2278,51 +2327,51 @@ func mrBar(p MRPage) templ.Component { | |||
| 2278 | 2327 | }() | |
| 2279 | 2328 | } | |
| 2280 | 2329 | ctx = templ.InitializeContext(ctx) | |
| 2281 | - | templ_7745c5c3_Var110 := templ.GetChildren(ctx) | |
| 2282 | - | if templ_7745c5c3_Var110 == nil { | |
| 2283 | - | templ_7745c5c3_Var110 = templ.NopComponent | |
| 2330 | + | templ_7745c5c3_Var112 := templ.GetChildren(ctx) | |
| 2331 | + | if templ_7745c5c3_Var112 == nil { | |
| 2332 | + | templ_7745c5c3_Var112 = templ.NopComponent | |
| 2284 | 2333 | } | |
| 2285 | 2334 | ctx = templ.ClearChildren(ctx) | |
| 2286 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 211, "<div class=\"diff-tools mr-bar\" role=\"group\" aria-label=\"Diff view and merge\"><span class=\"dim\">") | |
| 2335 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 217, "<div class=\"diff-tools mr-bar\" role=\"group\" aria-label=\"Diff view and merge\"><span class=\"dim\">") | |
| 2287 | 2336 | if templ_7745c5c3_Err != nil { | |
| 2288 | 2337 | return templ_7745c5c3_Err | |
| 2289 | 2338 | } | |
| 2290 | - | var templ_7745c5c3_Var111 string | |
| 2291 | - | templ_7745c5c3_Var111, templ_7745c5c3_Err = templ.JoinStringErrs(plural(p.NFiles, "file", "files")) | |
| 2339 | + | var templ_7745c5c3_Var113 string | |
| 2340 | + | templ_7745c5c3_Var113, templ_7745c5c3_Err = templ.JoinStringErrs(plural(p.NFiles, "file", "files")) | |
| 2292 | 2341 | if templ_7745c5c3_Err != nil { | |
| 2293 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 435, Col: 55} | |
| 2342 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 444, Col: 55} | |
| 2294 | 2343 | } | |
| 2295 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var111)) | |
| 2344 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var113)) | |
| 2296 | 2345 | if templ_7745c5c3_Err != nil { | |
| 2297 | 2346 | return templ_7745c5c3_Err | |
| 2298 | 2347 | } | |
| 2299 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 212, " · <span class=\"plus\">+") | |
| 2348 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 218, " · <span class=\"plus\">+") | |
| 2300 | 2349 | if templ_7745c5c3_Err != nil { | |
| 2301 | 2350 | return templ_7745c5c3_Err | |
| 2302 | 2351 | } | |
| 2303 | - | var templ_7745c5c3_Var112 string | |
| 2304 | - | templ_7745c5c3_Var112, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Adds)) | |
| 2352 | + | var templ_7745c5c3_Var114 string | |
| 2353 | + | templ_7745c5c3_Var114, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Adds)) | |
| 2305 | 2354 | if templ_7745c5c3_Err != nil { | |
| 2306 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 435, Col: 95} | |
| 2355 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 444, Col: 95} | |
| 2307 | 2356 | } | |
| 2308 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var112)) | |
| 2357 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var114)) | |
| 2309 | 2358 | if templ_7745c5c3_Err != nil { | |
| 2310 | 2359 | return templ_7745c5c3_Err | |
| 2311 | 2360 | } | |
| 2312 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 213, "</span> <span class=\"minus\">-") | |
| 2361 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 219, "</span> <span class=\"minus\">-") | |
| 2313 | 2362 | if templ_7745c5c3_Err != nil { | |
| 2314 | 2363 | return templ_7745c5c3_Err | |
| 2315 | 2364 | } | |
| 2316 | - | var templ_7745c5c3_Var113 string | |
| 2317 | - | templ_7745c5c3_Var113, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Dels)) | |
| 2365 | + | var templ_7745c5c3_Var115 string | |
| 2366 | + | templ_7745c5c3_Var115, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Dels)) | |
| 2318 | 2367 | if templ_7745c5c3_Err != nil { | |
| 2319 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 435, Col: 140} | |
| 2368 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 444, Col: 140} | |
| 2320 | 2369 | } | |
| 2321 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var113)) | |
| 2370 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var115)) | |
| 2322 | 2371 | if templ_7745c5c3_Err != nil { | |
| 2323 | 2372 | return templ_7745c5c3_Err | |
| 2324 | 2373 | } | |
| 2325 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 214, "</span></span>") | |
| 2374 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 220, "</span></span>") | |
| 2326 | 2375 | if templ_7745c5c3_Err != nil { | |
| 2327 | 2376 | return templ_7745c5c3_Err | |
| 2328 | 2377 | } | |
| @@ -2330,7 +2379,7 @@ func mrBar(p MRPage) templ.Component { | |||
| 2330 | 2379 | if templ_7745c5c3_Err != nil { | |
| 2331 | 2380 | return templ_7745c5c3_Err | |
| 2332 | 2381 | } | |
| 2333 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 215, "<span class=\"grow\"></span>") | |
| 2382 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 221, "<span class=\"grow\"></span>") | |
| 2334 | 2383 | if templ_7745c5c3_Err != nil { | |
| 2335 | 2384 | return templ_7745c5c3_Err | |
| 2336 | 2385 | } | |
| @@ -2343,7 +2392,7 @@ func mrBar(p MRPage) templ.Component { | |||
| 2343 | 2392 | if templ_7745c5c3_Err != nil { | |
| 2344 | 2393 | return templ_7745c5c3_Err | |
| 2345 | 2394 | } | |
| 2346 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 216, " ") | |
| 2395 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 222, " ") | |
| 2347 | 2396 | if templ_7745c5c3_Err != nil { | |
| 2348 | 2397 | return templ_7745c5c3_Err | |
| 2349 | 2398 | } | |
| @@ -2352,7 +2401,7 @@ func mrBar(p MRPage) templ.Component { | |||
| 2352 | 2401 | return templ_7745c5c3_Err | |
| 2353 | 2402 | } | |
| 2354 | 2403 | } | |
| 2355 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 217, "</div>") | |
| 2404 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 223, "</div>") | |
| 2356 | 2405 | if templ_7745c5c3_Err != nil { | |
| 2357 | 2406 | return templ_7745c5c3_Err | |
| 2358 | 2407 | } | |
| @@ -2363,6 +2412,11 @@ func mrBar(p MRPage) templ.Component { | |||
| 2363 | 2412 | // mergeMenu is the merge of an open request, for the owner: the form (it | |
| 2364 | 2413 | // carries the head that was read), or "Ready for review" for a draft. The | |
| 2365 | 2414 | // mode and the branch deletion have keys (1-3, D) while it is open. | |
| 2415 | + | // | |
| 2416 | + | // The badge is a live slot (mr-mergeability); the modes and the button read | |
| 2417 | + | // its data-* attributes (hx-live bindings), so that they follow the slot when | |
| 2418 | + | // the stream replaces its content. The server renders the same state in the | |
| 2419 | + | // attributes, for a page without scripts. | |
| 2366 | 2420 | func mergeMenu(p MRPage) templ.Component { | |
| 2367 | 2421 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | |
| 2368 | 2422 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | |
| @@ -2379,219 +2433,612 @@ func mergeMenu(p MRPage) templ.Component { | |||
| 2379 | 2433 | }() | |
| 2380 | 2434 | } | |
| 2381 | 2435 | ctx = templ.InitializeContext(ctx) | |
| 2382 | - | templ_7745c5c3_Var114 := templ.GetChildren(ctx) | |
| 2383 | - | if templ_7745c5c3_Var114 == nil { | |
| 2384 | - | templ_7745c5c3_Var114 = templ.NopComponent | |
| 2436 | + | templ_7745c5c3_Var116 := templ.GetChildren(ctx) | |
| 2437 | + | if templ_7745c5c3_Var116 == nil { | |
| 2438 | + | templ_7745c5c3_Var116 = templ.NopComponent | |
| 2385 | 2439 | } | |
| 2386 | 2440 | ctx = templ.ClearChildren(ctx) | |
| 2387 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 218, "<details class=\"menu mmenu\" id=\"merge-menu\" data-state=\"open\"><summary class=\"btn primary\" aria-keyshortcuts=\"Shift+M\">") | |
| 2441 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 224, "<details class=\"menu mmenu\" id=\"merge-menu\" data-state=\"open\"") | |
| 2442 | + | if templ_7745c5c3_Err != nil { | |
| 2443 | + | return templ_7745c5c3_Err | |
| 2444 | + | } | |
| 2445 | + | if p.Box.Error != "" { | |
| 2446 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 225, " open") | |
| 2447 | + | if templ_7745c5c3_Err != nil { | |
| 2448 | + | return templ_7745c5c3_Err | |
| 2449 | + | } | |
| 2450 | + | } | |
| 2451 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 226, "><summary class=\"btn primary\" aria-keyshortcuts=\"Shift+M\">") | |
| 2388 | 2452 | if templ_7745c5c3_Err != nil { | |
| 2389 | 2453 | return templ_7745c5c3_Err | |
| 2390 | 2454 | } | |
| 2391 | 2455 | if p.Box.ReadyAction != "" { | |
| 2392 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 219, "Draft ") | |
| 2456 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 227, "Draft ") | |
| 2393 | 2457 | if templ_7745c5c3_Err != nil { | |
| 2394 | 2458 | return templ_7745c5c3_Err | |
| 2395 | 2459 | } | |
| 2396 | 2460 | } else { | |
| 2397 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 220, "Merge ") | |
| 2461 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 228, "Merge ") | |
| 2398 | 2462 | if templ_7745c5c3_Err != nil { | |
| 2399 | 2463 | return templ_7745c5c3_Err | |
| 2400 | 2464 | } | |
| 2401 | 2465 | } | |
| 2402 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 221, "<kbd aria-hidden=\"true\">M</kbd></summary><div class=\"menu-pop\" role=\"group\" aria-label=\"Merge\"><dl class=\"kv\"><dt>head</dt><dd>") | |
| 2466 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 229, "<kbd aria-hidden=\"true\">M</kbd></summary><div class=\"menu-pop\" role=\"group\" aria-label=\"Merge\"><dl class=\"kv\"><dt>head</dt><dd>") | |
| 2403 | 2467 | if templ_7745c5c3_Err != nil { | |
| 2404 | 2468 | return templ_7745c5c3_Err | |
| 2405 | 2469 | } | |
| 2406 | 2470 | if p.Box.HeadGone { | |
| 2407 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 222, "gone: the source branch was deleted") | |
| 2471 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 230, "gone: the source branch was deleted") | |
| 2408 | 2472 | if templ_7745c5c3_Err != nil { | |
| 2409 | 2473 | return templ_7745c5c3_Err | |
| 2410 | 2474 | } | |
| 2411 | 2475 | } else { | |
| 2412 | - | var templ_7745c5c3_Var115 string | |
| 2413 | - | templ_7745c5c3_Var115, templ_7745c5c3_Err = templ.JoinStringErrs(p.HeadShort) | |
| 2476 | + | var templ_7745c5c3_Var117 string | |
| 2477 | + | templ_7745c5c3_Var117, templ_7745c5c3_Err = templ.JoinStringErrs(p.HeadShort) | |
| 2414 | 2478 | if templ_7745c5c3_Err != nil { | |
| 2415 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 466, Col: 19} | |
| 2479 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 480, Col: 19} | |
| 2416 | 2480 | } | |
| 2417 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var115)) | |
| 2481 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var117)) | |
| 2418 | 2482 | if templ_7745c5c3_Err != nil { | |
| 2419 | 2483 | return templ_7745c5c3_Err | |
| 2420 | 2484 | } | |
| 2421 | 2485 | } | |
| 2422 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 223, "</dd><dt>into</dt><dd>") | |
| 2486 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 231, "</dd><dt>into</dt><dd>") | |
| 2423 | 2487 | if templ_7745c5c3_Err != nil { | |
| 2424 | 2488 | return templ_7745c5c3_Err | |
| 2425 | 2489 | } | |
| 2426 | - | var templ_7745c5c3_Var116 string | |
| 2427 | - | templ_7745c5c3_Var116, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 2490 | + | var templ_7745c5c3_Var118 string | |
| 2491 | + | templ_7745c5c3_Var118, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 2428 | 2492 | if templ_7745c5c3_Err != nil { | |
| 2429 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 470, Col: 18} | |
| 2493 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 484, Col: 18} | |
| 2430 | 2494 | } | |
| 2431 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var116)) | |
| 2495 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var118)) | |
| 2432 | 2496 | if templ_7745c5c3_Err != nil { | |
| 2433 | 2497 | return templ_7745c5c3_Err | |
| 2434 | 2498 | } | |
| 2435 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 224, "</dd></dl>") | |
| 2499 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 232, "</dd></dl>") | |
| 2436 | 2500 | if templ_7745c5c3_Err != nil { | |
| 2437 | 2501 | return templ_7745c5c3_Err | |
| 2438 | 2502 | } | |
| 2503 | + | if p.Box.Error != "" { | |
| 2504 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 233, "<p class=\"d-err\" role=\"alert\">") | |
| 2505 | + | if templ_7745c5c3_Err != nil { | |
| 2506 | + | return templ_7745c5c3_Err | |
| 2507 | + | } | |
| 2508 | + | var templ_7745c5c3_Var119 string | |
| 2509 | + | templ_7745c5c3_Var119, templ_7745c5c3_Err = templ.JoinStringErrs(p.Box.Error) | |
| 2510 | + | if templ_7745c5c3_Err != nil { | |
| 2511 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 487, Col: 47} | |
| 2512 | + | } | |
| 2513 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var119)) | |
| 2514 | + | if templ_7745c5c3_Err != nil { | |
| 2515 | + | return templ_7745c5c3_Err | |
| 2516 | + | } | |
| 2517 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 234, "</p>") | |
| 2518 | + | if templ_7745c5c3_Err != nil { | |
| 2519 | + | return templ_7745c5c3_Err | |
| 2520 | + | } | |
| 2521 | + | } | |
| 2439 | 2522 | if p.Box.ReadyAction != "" { | |
| 2440 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 225, "<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=\"") | |
| 2523 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 235, "<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=\"") | |
| 2441 | 2524 | if templ_7745c5c3_Err != nil { | |
| 2442 | 2525 | return templ_7745c5c3_Err | |
| 2443 | 2526 | } | |
| 2444 | - | var templ_7745c5c3_Var117 templ.SafeURL | |
| 2445 | - | templ_7745c5c3_Var117, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.ReadyAction)) | |
| 2527 | + | var templ_7745c5c3_Var120 templ.SafeURL | |
| 2528 | + | templ_7745c5c3_Var120, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.ReadyAction)) | |
| 2446 | 2529 | if templ_7745c5c3_Err != nil { | |
| 2447 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 474, Col: 80} | |
| 2530 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 491, Col: 80} | |
| 2448 | 2531 | } | |
| 2449 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var117)) | |
| 2532 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var120)) | |
| 2450 | 2533 | if templ_7745c5c3_Err != nil { | |
| 2451 | 2534 | return templ_7745c5c3_Err | |
| 2452 | 2535 | } | |
| 2453 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 226, "\"><button type=\"submit\" class=\"btn primary\" data-default title=\"remove the draft prefix from the title\">Ready for review</button></form>") | |
| 2536 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 236, "\"><button type=\"submit\" class=\"btn primary\" data-default title=\"remove the draft prefix from the title\">Ready for review</button></form>") | |
| 2454 | 2537 | if templ_7745c5c3_Err != nil { | |
| 2455 | 2538 | return templ_7745c5c3_Err | |
| 2456 | 2539 | } | |
| 2457 | 2540 | } else if !p.Box.HeadGone { | |
| 2458 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 227, "<form class=\"d-acts\" method=\"post\" action=\"") | |
| 2541 | + | templ_7745c5c3_Err = mrMergeabilitySlot(p.Box.Mergeability).Render(ctx, templ_7745c5c3_Buffer) | |
| 2542 | + | if templ_7745c5c3_Err != nil { | |
| 2543 | + | return templ_7745c5c3_Err | |
| 2544 | + | } | |
| 2545 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 237, " <form class=\"d-acts\" method=\"post\" action=\"") | |
| 2459 | 2546 | if templ_7745c5c3_Err != nil { | |
| 2460 | 2547 | return templ_7745c5c3_Err | |
| 2461 | 2548 | } | |
| 2462 | - | var templ_7745c5c3_Var118 templ.SafeURL | |
| 2463 | - | templ_7745c5c3_Var118, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.MergeAction)) | |
| 2549 | + | var templ_7745c5c3_Var121 templ.SafeURL | |
| 2550 | + | templ_7745c5c3_Var121, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.MergeAction)) | |
| 2464 | 2551 | if templ_7745c5c3_Err != nil { | |
| 2465 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 478, Col: 80} | |
| 2552 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 496, Col: 80} | |
| 2466 | 2553 | } | |
| 2467 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var118)) | |
| 2554 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var121)) | |
| 2468 | 2555 | if templ_7745c5c3_Err != nil { | |
| 2469 | 2556 | return templ_7745c5c3_Err | |
| 2470 | 2557 | } | |
| 2471 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 228, "\"><input type=\"hidden\" name=\"head_id\" value=\"") | |
| 2558 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 238, "\"><input type=\"hidden\" name=\"head_id\" value=\"") | |
| 2472 | 2559 | if templ_7745c5c3_Err != nil { | |
| 2473 | 2560 | return templ_7745c5c3_Err | |
| 2474 | 2561 | } | |
| 2475 | - | var templ_7745c5c3_Var119 string | |
| 2476 | - | templ_7745c5c3_Var119, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Box.Head) | |
| 2562 | + | var templ_7745c5c3_Var122 string | |
| 2563 | + | templ_7745c5c3_Var122, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Box.Head) | |
| 2477 | 2564 | if templ_7745c5c3_Err != nil { | |
| 2478 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 479, Col: 59} | |
| 2565 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 497, Col: 59} | |
| 2479 | 2566 | } | |
| 2480 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var119) | |
| 2567 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var122) | |
| 2481 | 2568 | if templ_7745c5c3_Err != nil { | |
| 2482 | 2569 | return templ_7745c5c3_Err | |
| 2483 | 2570 | } | |
| 2484 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 229, "\"><fieldset class=\"d-modes\"><legend class=\"sr\">merge mode</legend> ") | |
| 2571 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 239, "\"><fieldset class=\"d-modes\"><legend class=\"sr\">merge mode</legend> ") | |
| 2485 | 2572 | if templ_7745c5c3_Err != nil { | |
| 2486 | 2573 | return templ_7745c5c3_Err | |
| 2487 | 2574 | } | |
| 2488 | 2575 | for i, m := range p.Box.Modes { | |
| 2489 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 230, "<label><input type=\"radio\" name=\"mode\" value=\"") | |
| 2576 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 240, "<label><input type=\"radio\" name=\"mode\" value=\"") | |
| 2490 | 2577 | if templ_7745c5c3_Err != nil { | |
| 2491 | 2578 | return templ_7745c5c3_Err | |
| 2492 | 2579 | } | |
| 2493 | - | var templ_7745c5c3_Var120 string | |
| 2494 | - | templ_7745c5c3_Var120, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.Value) | |
| 2580 | + | var templ_7745c5c3_Var123 string | |
| 2581 | + | templ_7745c5c3_Var123, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.Value) | |
| 2495 | 2582 | if templ_7745c5c3_Err != nil { | |
| 2496 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 484, Col: 55} | |
| 2583 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 505, Col: 24} | |
| 2497 | 2584 | } | |
| 2498 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var120) | |
| 2585 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var123) | |
| 2499 | 2586 | if templ_7745c5c3_Err != nil { | |
| 2500 | 2587 | return templ_7745c5c3_Err | |
| 2501 | 2588 | } | |
| 2502 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 231, "\"") | |
| 2589 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 241, "\"") | |
| 2503 | 2590 | if templ_7745c5c3_Err != nil { | |
| 2504 | 2591 | return templ_7745c5c3_Err | |
| 2505 | 2592 | } | |
| 2506 | - | if i == 0 { | |
| 2507 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 232, " checked") | |
| 2593 | + | if i == p.Box.Checked() { | |
| 2594 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 242, " checked") | |
| 2595 | + | if templ_7745c5c3_Err != nil { | |
| 2596 | + | return templ_7745c5c3_Err | |
| 2597 | + | } | |
| 2598 | + | } | |
| 2599 | + | if m.Disabled { | |
| 2600 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 243, " disabled") | |
| 2508 | 2601 | if templ_7745c5c3_Err != nil { | |
| 2509 | 2602 | return templ_7745c5c3_Err | |
| 2510 | 2603 | } | |
| 2511 | 2604 | } | |
| 2512 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 233, "> ") | |
| 2605 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 244, " title=\"") | |
| 2513 | 2606 | if templ_7745c5c3_Err != nil { | |
| 2514 | 2607 | return templ_7745c5c3_Err | |
| 2515 | 2608 | } | |
| 2516 | - | var templ_7745c5c3_Var121 string | |
| 2517 | - | templ_7745c5c3_Var121, templ_7745c5c3_Err = templ.JoinStringErrs(m.Label) | |
| 2609 | + | var templ_7745c5c3_Var124 string | |
| 2610 | + | templ_7745c5c3_Var124, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.Reason) | |
| 2518 | 2611 | if templ_7745c5c3_Err != nil { | |
| 2519 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 485, Col: 17} | |
| 2612 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 508, Col: 25} | |
| 2520 | 2613 | } | |
| 2521 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var121)) | |
| 2614 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var124) | |
| 2522 | 2615 | if templ_7745c5c3_Err != nil { | |
| 2523 | 2616 | return templ_7745c5c3_Err | |
| 2524 | 2617 | } | |
| 2525 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 234, " ") | |
| 2618 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 245, "\" :disabled=\"this.value === 'fast-forward' ? q('[data-mergeability]').data.ffOk !== true : q('[data-mergeability]').data.mergeOk !== true\" :title=\"this.value === 'fast-forward' ? q('[data-mergeability]').data.ffReason : q('[data-mergeability]').data.mergeReason\"> ") | |
| 2619 | + | if templ_7745c5c3_Err != nil { | |
| 2620 | + | return templ_7745c5c3_Err | |
| 2621 | + | } | |
| 2622 | + | var templ_7745c5c3_Var125 string | |
| 2623 | + | templ_7745c5c3_Var125, templ_7745c5c3_Err = templ.JoinStringErrs(m.Label) | |
| 2624 | + | if templ_7745c5c3_Err != nil { | |
| 2625 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 512, Col: 17} | |
| 2626 | + | } | |
| 2627 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var125)) | |
| 2628 | + | if templ_7745c5c3_Err != nil { | |
| 2629 | + | return templ_7745c5c3_Err | |
| 2630 | + | } | |
| 2631 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 246, " ") | |
| 2526 | 2632 | if templ_7745c5c3_Err != nil { | |
| 2527 | 2633 | return templ_7745c5c3_Err | |
| 2528 | 2634 | } | |
| 2529 | 2635 | if i < 9 { | |
| 2530 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 235, "<kbd aria-hidden=\"true\">") | |
| 2636 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 247, "<kbd aria-hidden=\"true\">") | |
| 2531 | 2637 | if templ_7745c5c3_Err != nil { | |
| 2532 | 2638 | return templ_7745c5c3_Err | |
| 2533 | 2639 | } | |
| 2534 | - | var templ_7745c5c3_Var122 string | |
| 2535 | - | templ_7745c5c3_Var122, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(i + 1)) | |
| 2640 | + | var templ_7745c5c3_Var126 string | |
| 2641 | + | templ_7745c5c3_Var126, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(i + 1)) | |
| 2536 | 2642 | if templ_7745c5c3_Err != nil { | |
| 2537 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 487, Col: 46} | |
| 2643 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 514, Col: 46} | |
| 2538 | 2644 | } | |
| 2539 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var122)) | |
| 2645 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var126)) | |
| 2540 | 2646 | if templ_7745c5c3_Err != nil { | |
| 2541 | 2647 | return templ_7745c5c3_Err | |
| 2542 | 2648 | } | |
| 2543 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 236, "</kbd>") | |
| 2649 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 248, "</kbd>") | |
| 2544 | 2650 | if templ_7745c5c3_Err != nil { | |
| 2545 | 2651 | return templ_7745c5c3_Err | |
| 2546 | 2652 | } | |
| 2547 | 2653 | } | |
| 2548 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 237, "</label>") | |
| 2654 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 249, "</label>") | |
| 2549 | 2655 | if templ_7745c5c3_Err != nil { | |
| 2550 | 2656 | return templ_7745c5c3_Err | |
| 2551 | 2657 | } | |
| 2552 | 2658 | } | |
| 2553 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 238, "</fieldset>") | |
| 2659 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 250, "</fieldset>") | |
| 2554 | 2660 | if templ_7745c5c3_Err != nil { | |
| 2555 | 2661 | return templ_7745c5c3_Err | |
| 2556 | 2662 | } | |
| 2663 | + | if p.Box.Signed { | |
| 2664 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 251, "<p class=\"d-note\" id=\"mr-signing\">squash and merge commits are signed with ermite's own key, generated automatically on first start. ") | |
| 2665 | + | if templ_7745c5c3_Err != nil { | |
| 2666 | + | return templ_7745c5c3_Err | |
| 2667 | + | } | |
| 2668 | + | if p.Box.Fingerprint != "" { | |
| 2669 | + | var templ_7745c5c3_Var127 string | |
| 2670 | + | templ_7745c5c3_Var127, templ_7745c5c3_Err = templ.JoinStringErrs(" ") | |
| 2671 | + | if templ_7745c5c3_Err != nil { | |
| 2672 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 523, Col: 13} | |
| 2673 | + | } | |
| 2674 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var127)) | |
| 2675 | + | if templ_7745c5c3_Err != nil { | |
| 2676 | + | return templ_7745c5c3_Err | |
| 2677 | + | } | |
| 2678 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 252, "key <code>") | |
| 2679 | + | if templ_7745c5c3_Err != nil { | |
| 2680 | + | return templ_7745c5c3_Err | |
| 2681 | + | } | |
| 2682 | + | var templ_7745c5c3_Var128 string | |
| 2683 | + | templ_7745c5c3_Var128, templ_7745c5c3_Err = templ.JoinStringErrs(p.Box.Fingerprint) | |
| 2684 | + | if templ_7745c5c3_Err != nil { | |
| 2685 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 523, Col: 44} | |
| 2686 | + | } | |
| 2687 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var128)) | |
| 2688 | + | if templ_7745c5c3_Err != nil { | |
| 2689 | + | return templ_7745c5c3_Err | |
| 2690 | + | } | |
| 2691 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 253, "</code>. ") | |
| 2692 | + | if templ_7745c5c3_Err != nil { | |
| 2693 | + | return templ_7745c5c3_Err | |
| 2694 | + | } | |
| 2695 | + | } | |
| 2696 | + | var templ_7745c5c3_Var129 string | |
| 2697 | + | templ_7745c5c3_Var129, templ_7745c5c3_Err = templ.JoinStringErrs(" ") | |
| 2698 | + | if templ_7745c5c3_Err != nil { | |
| 2699 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 525, Col: 12} | |
| 2700 | + | } | |
| 2701 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var129)) | |
| 2702 | + | if templ_7745c5c3_Err != nil { | |
| 2703 | + | return templ_7745c5c3_Err | |
| 2704 | + | } | |
| 2705 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 254, "<code>ermite signing-key show</code> prints the public key to verify them.</p>") | |
| 2706 | + | if templ_7745c5c3_Err != nil { | |
| 2707 | + | return templ_7745c5c3_Err | |
| 2708 | + | } | |
| 2709 | + | } | |
| 2557 | 2710 | if p.Box.CanDeleteBranch { | |
| 2558 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 239, "<label class=\"d-check\"><input type=\"checkbox\" name=\"delete_branch\" value=\"1\"> delete ") | |
| 2711 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 255, "<label class=\"d-check\"><input type=\"checkbox\" name=\"delete_branch\" value=\"1\"> delete ") | |
| 2559 | 2712 | if templ_7745c5c3_Err != nil { | |
| 2560 | 2713 | return templ_7745c5c3_Err | |
| 2561 | 2714 | } | |
| 2562 | - | var templ_7745c5c3_Var123 string | |
| 2563 | - | templ_7745c5c3_Var123, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) | |
| 2715 | + | var templ_7745c5c3_Var130 string | |
| 2716 | + | templ_7745c5c3_Var130, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) | |
| 2717 | + | if templ_7745c5c3_Err != nil { | |
| 2718 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 529, Col: 102} | |
| 2719 | + | } | |
| 2720 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var130)) | |
| 2721 | + | if templ_7745c5c3_Err != nil { | |
| 2722 | + | return templ_7745c5c3_Err | |
| 2723 | + | } | |
| 2724 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 256, " after the merge <kbd aria-hidden=\"true\">D</kbd></label>") | |
| 2725 | + | if templ_7745c5c3_Err != nil { | |
| 2726 | + | return templ_7745c5c3_Err | |
| 2727 | + | } | |
| 2728 | + | } | |
| 2729 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 257, "<div class=\"d-btns\"><button type=\"submit\" class=\"btn primary\" data-default") | |
| 2730 | + | if templ_7745c5c3_Err != nil { | |
| 2731 | + | return templ_7745c5c3_Err | |
| 2732 | + | } | |
| 2733 | + | if !p.Box.CanMerge() { | |
| 2734 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 258, " disabled") | |
| 2735 | + | if templ_7745c5c3_Err != nil { | |
| 2736 | + | return templ_7745c5c3_Err | |
| 2737 | + | } | |
| 2738 | + | } | |
| 2739 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 259, " title=\"") | |
| 2740 | + | if templ_7745c5c3_Err != nil { | |
| 2741 | + | return templ_7745c5c3_Err | |
| 2742 | + | } | |
| 2743 | + | var templ_7745c5c3_Var131 string | |
| 2744 | + | templ_7745c5c3_Var131, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Box.MergeTitle()) | |
| 2745 | + | if templ_7745c5c3_Err != nil { | |
| 2746 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 537, Col: 33} | |
| 2747 | + | } | |
| 2748 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var131) | |
| 2749 | + | if templ_7745c5c3_Err != nil { | |
| 2750 | + | return templ_7745c5c3_Err | |
| 2751 | + | } | |
| 2752 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 260, "\" :disabled=\"!this.form.querySelector('input[name=mode]:checked:not(:disabled)')\" :title=\"(this.form.querySelector('input[name=mode]:checked') || {}).title || ''\">Merge into ") | |
| 2753 | + | if templ_7745c5c3_Err != nil { | |
| 2754 | + | return templ_7745c5c3_Err | |
| 2755 | + | } | |
| 2756 | + | var templ_7745c5c3_Var132 string | |
| 2757 | + | templ_7745c5c3_Var132, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 2758 | + | if templ_7745c5c3_Err != nil { | |
| 2759 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 540, Col: 28} | |
| 2760 | + | } | |
| 2761 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var132)) | |
| 2762 | + | if templ_7745c5c3_Err != nil { | |
| 2763 | + | return templ_7745c5c3_Err | |
| 2764 | + | } | |
| 2765 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 261, "</button></div></form>") | |
| 2766 | + | if templ_7745c5c3_Err != nil { | |
| 2767 | + | return templ_7745c5c3_Err | |
| 2768 | + | } | |
| 2769 | + | } | |
| 2770 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 262, "</div></details>") | |
| 2771 | + | if templ_7745c5c3_Err != nil { | |
| 2772 | + | return templ_7745c5c3_Err | |
| 2773 | + | } | |
| 2774 | + | return nil | |
| 2775 | + | }) | |
| 2776 | + | } | |
| 2777 | + | ||
| 2778 | + | // MRMergeability is the content of the mergeability slot: the badge, and the | |
| 2779 | + | // state the controls of the merge box read. | |
| 2780 | + | func MRMergeability(v MergeabilityView) templ.Component { | |
| 2781 | + | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | |
| 2782 | + | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | |
| 2783 | + | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | |
| 2784 | + | return templ_7745c5c3_CtxErr | |
| 2785 | + | } | |
| 2786 | + | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) | |
| 2787 | + | if !templ_7745c5c3_IsBuffer { | |
| 2788 | + | defer func() { | |
| 2789 | + | templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) | |
| 2790 | + | if templ_7745c5c3_Err == nil { | |
| 2791 | + | templ_7745c5c3_Err = templ_7745c5c3_BufErr | |
| 2792 | + | } | |
| 2793 | + | }() | |
| 2794 | + | } | |
| 2795 | + | ctx = templ.InitializeContext(ctx) | |
| 2796 | + | templ_7745c5c3_Var133 := templ.GetChildren(ctx) | |
| 2797 | + | if templ_7745c5c3_Var133 == nil { | |
| 2798 | + | templ_7745c5c3_Var133 = templ.NopComponent | |
| 2799 | + | } | |
| 2800 | + | ctx = templ.ClearChildren(ctx) | |
| 2801 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 263, "<span data-mergeability data-state=\"") | |
| 2802 | + | if templ_7745c5c3_Err != nil { | |
| 2803 | + | return templ_7745c5c3_Err | |
| 2804 | + | } | |
| 2805 | + | var templ_7745c5c3_Var134 string | |
| 2806 | + | templ_7745c5c3_Var134, templ_7745c5c3_Err = templ.ResolveAttributeValue(v.State) | |
| 2807 | + | if templ_7745c5c3_Err != nil { | |
| 2808 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 553, Col: 22} | |
| 2809 | + | } | |
| 2810 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var134) | |
| 2811 | + | if templ_7745c5c3_Err != nil { | |
| 2812 | + | return templ_7745c5c3_Err | |
| 2813 | + | } | |
| 2814 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 264, "\" data-ff-ok=\"") | |
| 2815 | + | if templ_7745c5c3_Err != nil { | |
| 2816 | + | return templ_7745c5c3_Err | |
| 2817 | + | } | |
| 2818 | + | var templ_7745c5c3_Var135 string | |
| 2819 | + | templ_7745c5c3_Var135, templ_7745c5c3_Err = templ.ResolveAttributeValue(strconv.FormatBool(v.FFOk)) | |
| 2820 | + | if templ_7745c5c3_Err != nil { | |
| 2821 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 554, Col: 41} | |
| 2822 | + | } | |
| 2823 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var135) | |
| 2824 | + | if templ_7745c5c3_Err != nil { | |
| 2825 | + | return templ_7745c5c3_Err | |
| 2826 | + | } | |
| 2827 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 265, "\" data-merge-ok=\"") | |
| 2828 | + | if templ_7745c5c3_Err != nil { | |
| 2829 | + | return templ_7745c5c3_Err | |
| 2830 | + | } | |
| 2831 | + | var templ_7745c5c3_Var136 string | |
| 2832 | + | templ_7745c5c3_Var136, templ_7745c5c3_Err = templ.ResolveAttributeValue(strconv.FormatBool(v.MergeOk)) | |
| 2833 | + | if templ_7745c5c3_Err != nil { | |
| 2834 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 555, Col: 47} | |
| 2835 | + | } | |
| 2836 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var136) | |
| 2837 | + | if templ_7745c5c3_Err != nil { | |
| 2838 | + | return templ_7745c5c3_Err | |
| 2839 | + | } | |
| 2840 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 266, "\" data-ff-reason=\"") | |
| 2841 | + | if templ_7745c5c3_Err != nil { | |
| 2842 | + | return templ_7745c5c3_Err | |
| 2843 | + | } | |
| 2844 | + | var templ_7745c5c3_Var137 string | |
| 2845 | + | templ_7745c5c3_Var137, templ_7745c5c3_Err = templ.ResolveAttributeValue(v.FFReason) | |
| 2846 | + | if templ_7745c5c3_Err != nil { | |
| 2847 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 556, Col: 29} | |
| 2848 | + | } | |
| 2849 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var137) | |
| 2850 | + | if templ_7745c5c3_Err != nil { | |
| 2851 | + | return templ_7745c5c3_Err | |
| 2852 | + | } | |
| 2853 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 267, "\" data-merge-reason=\"") | |
| 2854 | + | if templ_7745c5c3_Err != nil { | |
| 2855 | + | return templ_7745c5c3_Err | |
| 2856 | + | } | |
| 2857 | + | var templ_7745c5c3_Var138 string | |
| 2858 | + | templ_7745c5c3_Var138, templ_7745c5c3_Err = templ.ResolveAttributeValue(v.MergeReason) | |
| 2859 | + | if templ_7745c5c3_Err != nil { | |
| 2860 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 557, Col: 35} | |
| 2861 | + | } | |
| 2862 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var138) | |
| 2863 | + | if templ_7745c5c3_Err != nil { | |
| 2864 | + | return templ_7745c5c3_Err | |
| 2865 | + | } | |
| 2866 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 268, "\"><span class=\"badge\" data-state=\"") | |
| 2867 | + | if templ_7745c5c3_Err != nil { | |
| 2868 | + | return templ_7745c5c3_Err | |
| 2869 | + | } | |
| 2870 | + | var templ_7745c5c3_Var139 string | |
| 2871 | + | templ_7745c5c3_Var139, templ_7745c5c3_Err = templ.ResolveAttributeValue(v.State) | |
| 2872 | + | if templ_7745c5c3_Err != nil { | |
| 2873 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 559, Col: 42} | |
| 2874 | + | } | |
| 2875 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var139) | |
| 2876 | + | if templ_7745c5c3_Err != nil { | |
| 2877 | + | return templ_7745c5c3_Err | |
| 2878 | + | } | |
| 2879 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 269, "\">") | |
| 2880 | + | if templ_7745c5c3_Err != nil { | |
| 2881 | + | return templ_7745c5c3_Err | |
| 2882 | + | } | |
| 2883 | + | var templ_7745c5c3_Var140 string | |
| 2884 | + | templ_7745c5c3_Var140, templ_7745c5c3_Err = templ.JoinStringErrs(v.Label) | |
| 2885 | + | if templ_7745c5c3_Err != nil { | |
| 2886 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 559, Col: 54} | |
| 2887 | + | } | |
| 2888 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var140)) | |
| 2889 | + | if templ_7745c5c3_Err != nil { | |
| 2890 | + | return templ_7745c5c3_Err | |
| 2891 | + | } | |
| 2892 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 270, "</span> ") | |
| 2893 | + | if templ_7745c5c3_Err != nil { | |
| 2894 | + | return templ_7745c5c3_Err | |
| 2895 | + | } | |
| 2896 | + | if v.Detail != "" { | |
| 2897 | + | var templ_7745c5c3_Var141 string | |
| 2898 | + | templ_7745c5c3_Var141, templ_7745c5c3_Err = templ.JoinStringErrs(" ") | |
| 2899 | + | if templ_7745c5c3_Err != nil { | |
| 2900 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 561, Col: 8} | |
| 2901 | + | } | |
| 2902 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var141)) | |
| 2903 | + | if templ_7745c5c3_Err != nil { | |
| 2904 | + | return templ_7745c5c3_Err | |
| 2905 | + | } | |
| 2906 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 271, "<span class=\"dim\">") | |
| 2907 | + | if templ_7745c5c3_Err != nil { | |
| 2908 | + | return templ_7745c5c3_Err | |
| 2909 | + | } | |
| 2910 | + | var templ_7745c5c3_Var142 string | |
| 2911 | + | templ_7745c5c3_Var142, templ_7745c5c3_Err = templ.JoinStringErrs(v.Detail) | |
| 2912 | + | if templ_7745c5c3_Err != nil { | |
| 2913 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 561, Col: 38} | |
| 2914 | + | } | |
| 2915 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var142)) | |
| 2916 | + | if templ_7745c5c3_Err != nil { | |
| 2917 | + | return templ_7745c5c3_Err | |
| 2918 | + | } | |
| 2919 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 272, "</span>") | |
| 2920 | + | if templ_7745c5c3_Err != nil { | |
| 2921 | + | return templ_7745c5c3_Err | |
| 2922 | + | } | |
| 2923 | + | } | |
| 2924 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 273, "</span> ") | |
| 2925 | + | if templ_7745c5c3_Err != nil { | |
| 2926 | + | return templ_7745c5c3_Err | |
| 2927 | + | } | |
| 2928 | + | if len(v.Paths) > 0 { | |
| 2929 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 274, "<ul class=\"mg-paths\" aria-label=\"conflicting files\">") | |
| 2930 | + | if templ_7745c5c3_Err != nil { | |
| 2931 | + | return templ_7745c5c3_Err | |
| 2932 | + | } | |
| 2933 | + | for _, path := range v.Paths { | |
| 2934 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 275, "<li><code>") | |
| 2564 | 2935 | if templ_7745c5c3_Err != nil { | |
| 2565 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 493, Col: 102} | |
| 2936 | + | return templ_7745c5c3_Err | |
| 2937 | + | } | |
| 2938 | + | var templ_7745c5c3_Var143 string | |
| 2939 | + | templ_7745c5c3_Var143, templ_7745c5c3_Err = templ.JoinStringErrs(path) | |
| 2940 | + | if templ_7745c5c3_Err != nil { | |
| 2941 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 567, Col: 20} | |
| 2566 | 2942 | } | |
| 2567 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var123)) | |
| 2943 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var143)) | |
| 2568 | 2944 | if templ_7745c5c3_Err != nil { | |
| 2569 | 2945 | return templ_7745c5c3_Err | |
| 2570 | 2946 | } | |
| 2571 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 240, " after the merge <kbd aria-hidden=\"true\">D</kbd></label>") | |
| 2947 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 276, "</code></li>") | |
| 2572 | 2948 | if templ_7745c5c3_Err != nil { | |
| 2573 | 2949 | return templ_7745c5c3_Err | |
| 2574 | 2950 | } | |
| 2575 | 2951 | } | |
| 2576 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 241, "<div class=\"d-btns\"><button type=\"submit\" class=\"btn primary\" data-default>Merge into ") | |
| 2952 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 277, "</ul>") | |
| 2577 | 2953 | if templ_7745c5c3_Err != nil { | |
| 2578 | 2954 | return templ_7745c5c3_Err | |
| 2579 | 2955 | } | |
| 2580 | - | var templ_7745c5c3_Var124 string | |
| 2581 | - | templ_7745c5c3_Var124, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 2956 | + | } | |
| 2957 | + | if v.PathsNote != "" { | |
| 2958 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 278, "<p class=\"dim\">") | |
| 2582 | 2959 | if templ_7745c5c3_Err != nil { | |
| 2583 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 496, Col: 82} | |
| 2960 | + | return templ_7745c5c3_Err | |
| 2584 | 2961 | } | |
| 2585 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var124)) | |
| 2962 | + | var templ_7745c5c3_Var144 string | |
| 2963 | + | templ_7745c5c3_Var144, templ_7745c5c3_Err = templ.JoinStringErrs(v.PathsNote) | |
| 2964 | + | if templ_7745c5c3_Err != nil { | |
| 2965 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 572, Col: 30} | |
| 2966 | + | } | |
| 2967 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var144)) | |
| 2586 | 2968 | if templ_7745c5c3_Err != nil { | |
| 2587 | 2969 | return templ_7745c5c3_Err | |
| 2588 | 2970 | } | |
| 2589 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 242, "</button></div></form>") | |
| 2971 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 279, "</p>") | |
| 2590 | 2972 | if templ_7745c5c3_Err != nil { | |
| 2591 | 2973 | return templ_7745c5c3_Err | |
| 2592 | 2974 | } | |
| 2593 | 2975 | } | |
| 2594 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 243, "</div></details>") | |
| 2976 | + | return nil | |
| 2977 | + | }) | |
| 2978 | + | } | |
| 2979 | + | ||
| 2980 | + | // mrMergeabilitySlot is the status region of the badge, with its content: the | |
| 2981 | + | // stream replaces the children, never the element (see LiveSlot). | |
| 2982 | + | func mrMergeabilitySlot(v MergeabilityView) templ.Component { | |
| 2983 | + | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | |
| 2984 | + | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | |
| 2985 | + | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | |
| 2986 | + | return templ_7745c5c3_CtxErr | |
| 2987 | + | } | |
| 2988 | + | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) | |
| 2989 | + | if !templ_7745c5c3_IsBuffer { | |
| 2990 | + | defer func() { | |
| 2991 | + | templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) | |
| 2992 | + | if templ_7745c5c3_Err == nil { | |
| 2993 | + | templ_7745c5c3_Err = templ_7745c5c3_BufErr | |
| 2994 | + | } | |
| 2995 | + | }() | |
| 2996 | + | } | |
| 2997 | + | ctx = templ.InitializeContext(ctx) | |
| 2998 | + | templ_7745c5c3_Var145 := templ.GetChildren(ctx) | |
| 2999 | + | if templ_7745c5c3_Var145 == nil { | |
| 3000 | + | templ_7745c5c3_Var145 = templ.NopComponent | |
| 3001 | + | } | |
| 3002 | + | ctx = templ.ClearChildren(ctx) | |
| 3003 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 280, "<div id=\"mr-mergeability\" class=\"live-slot\" role=\"status\" data-slot>") | |
| 3004 | + | if templ_7745c5c3_Err != nil { | |
| 3005 | + | return templ_7745c5c3_Err | |
| 3006 | + | } | |
| 3007 | + | templ_7745c5c3_Err = MRMergeability(v).Render(ctx, templ_7745c5c3_Buffer) | |
| 3008 | + | if templ_7745c5c3_Err != nil { | |
| 3009 | + | return templ_7745c5c3_Err | |
| 3010 | + | } | |
| 3011 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 281, "</div>") | |
| 3012 | + | if templ_7745c5c3_Err != nil { | |
| 3013 | + | return templ_7745c5c3_Err | |
| 3014 | + | } | |
| 3015 | + | return nil | |
| 3016 | + | }) | |
| 3017 | + | } | |
| 3018 | + | ||
| 3019 | + | // MRMergeabilityFragment is the slot as the stream sends it. | |
| 3020 | + | func MRMergeabilityFragment(v MergeabilityView) templ.Component { | |
| 3021 | + | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | |
| 3022 | + | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | |
| 3023 | + | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | |
| 3024 | + | return templ_7745c5c3_CtxErr | |
| 3025 | + | } | |
| 3026 | + | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) | |
| 3027 | + | if !templ_7745c5c3_IsBuffer { | |
| 3028 | + | defer func() { | |
| 3029 | + | templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) | |
| 3030 | + | if templ_7745c5c3_Err == nil { | |
| 3031 | + | templ_7745c5c3_Err = templ_7745c5c3_BufErr | |
| 3032 | + | } | |
| 3033 | + | }() | |
| 3034 | + | } | |
| 3035 | + | ctx = templ.InitializeContext(ctx) | |
| 3036 | + | templ_7745c5c3_Var146 := templ.GetChildren(ctx) | |
| 3037 | + | if templ_7745c5c3_Var146 == nil { | |
| 3038 | + | templ_7745c5c3_Var146 = templ.NopComponent | |
| 3039 | + | } | |
| 3040 | + | ctx = templ.ClearChildren(ctx) | |
| 3041 | + | templ_7745c5c3_Err = mrMergeabilitySlot(v).Render(ctx, templ_7745c5c3_Buffer) | |
| 2595 | 3042 | if templ_7745c5c3_Err != nil { | |
| 2596 | 3043 | return templ_7745c5c3_Err | |
| 2597 | 3044 | } | |
| @@ -2617,66 +3064,66 @@ func moreMenu(p MRPage) templ.Component { | |||
| 2617 | 3064 | }() | |
| 2618 | 3065 | } | |
| 2619 | 3066 | ctx = templ.InitializeContext(ctx) | |
| 2620 | - | templ_7745c5c3_Var125 := templ.GetChildren(ctx) | |
| 2621 | - | if templ_7745c5c3_Var125 == nil { | |
| 2622 | - | templ_7745c5c3_Var125 = templ.NopComponent | |
| 3067 | + | templ_7745c5c3_Var147 := templ.GetChildren(ctx) | |
| 3068 | + | if templ_7745c5c3_Var147 == nil { | |
| 3069 | + | templ_7745c5c3_Var147 = templ.NopComponent | |
| 2623 | 3070 | } | |
| 2624 | 3071 | ctx = templ.ClearChildren(ctx) | |
| 2625 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 244, "<details class=\"menu omenu\"><summary class=\"btn icon\" aria-label=\"More actions\" title=\"more actions\">⋯</summary><div class=\"menu-pop\" role=\"group\" aria-label=\"More actions\">") | |
| 3072 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 282, "<details class=\"menu omenu\"><summary class=\"btn icon\" aria-label=\"More actions\" title=\"more actions\">⋯</summary><div class=\"menu-pop\" role=\"group\" aria-label=\"More actions\">") | |
| 2626 | 3073 | if templ_7745c5c3_Err != nil { | |
| 2627 | 3074 | return templ_7745c5c3_Err | |
| 2628 | 3075 | } | |
| 2629 | 3076 | if p.Box.CanMarkRead { | |
| 2630 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 245, "<form method=\"post\" action=\"") | |
| 3077 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 283, "<form method=\"post\" action=\"") | |
| 2631 | 3078 | if templ_7745c5c3_Err != nil { | |
| 2632 | 3079 | return templ_7745c5c3_Err | |
| 2633 | 3080 | } | |
| 2634 | - | var templ_7745c5c3_Var126 templ.SafeURL | |
| 2635 | - | templ_7745c5c3_Var126, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.ReadAction)) | |
| 3081 | + | var templ_7745c5c3_Var148 templ.SafeURL | |
| 3082 | + | templ_7745c5c3_Var148, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.ReadAction)) | |
| 2636 | 3083 | if templ_7745c5c3_Err != nil { | |
| 2637 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 511, Col: 64} | |
| 3084 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 596, Col: 64} | |
| 2638 | 3085 | } | |
| 2639 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var126)) | |
| 3086 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var148)) | |
| 2640 | 3087 | if templ_7745c5c3_Err != nil { | |
| 2641 | 3088 | return templ_7745c5c3_Err | |
| 2642 | 3089 | } | |
| 2643 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 246, "\"><input type=\"hidden\" name=\"head_id\" value=\"") | |
| 3090 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 284, "\"><input type=\"hidden\" name=\"head_id\" value=\"") | |
| 2644 | 3091 | if templ_7745c5c3_Err != nil { | |
| 2645 | 3092 | return templ_7745c5c3_Err | |
| 2646 | 3093 | } | |
| 2647 | - | var templ_7745c5c3_Var127 string | |
| 2648 | - | templ_7745c5c3_Var127, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Box.Head) | |
| 3094 | + | var templ_7745c5c3_Var149 string | |
| 3095 | + | templ_7745c5c3_Var149, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Box.Head) | |
| 2649 | 3096 | if templ_7745c5c3_Err != nil { | |
| 2650 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 512, Col: 59} | |
| 3097 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 597, Col: 59} | |
| 2651 | 3098 | } | |
| 2652 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var127) | |
| 3099 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var149) | |
| 2653 | 3100 | if templ_7745c5c3_Err != nil { | |
| 2654 | 3101 | return templ_7745c5c3_Err | |
| 2655 | 3102 | } | |
| 2656 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 247, "\"> <button type=\"submit\" title=\"the next visit can show only what changed after this head\" class=\"btn\" aria-keyshortcuts=\"Shift+R\" data-read>Mark read <kbd aria-hidden=\"true\">R</kbd></button></form>") | |
| 3103 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 285, "\"> <button type=\"submit\" title=\"the next visit can show only what changed after this head\" class=\"btn\" aria-keyshortcuts=\"Shift+R\" data-read>Mark read <kbd aria-hidden=\"true\">R</kbd></button></form>") | |
| 2657 | 3104 | if templ_7745c5c3_Err != nil { | |
| 2658 | 3105 | return templ_7745c5c3_Err | |
| 2659 | 3106 | } | |
| 2660 | 3107 | } | |
| 2661 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 248, "<form method=\"post\" action=\"") | |
| 3108 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 286, "<form method=\"post\" action=\"") | |
| 2662 | 3109 | if templ_7745c5c3_Err != nil { | |
| 2663 | 3110 | return templ_7745c5c3_Err | |
| 2664 | 3111 | } | |
| 2665 | - | var templ_7745c5c3_Var128 templ.SafeURL | |
| 2666 | - | templ_7745c5c3_Var128, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.CloseAction)) | |
| 3112 | + | var templ_7745c5c3_Var150 templ.SafeURL | |
| 3113 | + | templ_7745c5c3_Var150, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Box.CloseAction)) | |
| 2667 | 3114 | if templ_7745c5c3_Err != nil { | |
| 2668 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 516, Col: 64} | |
| 3115 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 601, Col: 64} | |
| 2669 | 3116 | } | |
| 2670 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var128)) | |
| 3117 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var150)) | |
| 2671 | 3118 | if templ_7745c5c3_Err != nil { | |
| 2672 | 3119 | return templ_7745c5c3_Err | |
| 2673 | 3120 | } | |
| 2674 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 249, "\"><button type=\"submit\" title=\"close without merging; the branch is kept\" class=\"btn danger\">Close</button></form>") | |
| 3121 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 287, "\"><button type=\"submit\" title=\"close without merging; the branch is kept\" class=\"btn danger\">Close</button></form>") | |
| 2675 | 3122 | if templ_7745c5c3_Err != nil { | |
| 2676 | 3123 | return templ_7745c5c3_Err | |
| 2677 | 3124 | } | |
| 2678 | 3125 | if !p.Box.HeadGone && p.Box.ReadyAction == "" { | |
| 2679 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 250, "<details class=\"d-or\"><summary class=\"mute\">or from a terminal</summary>") | |
| 3126 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 288, "<details class=\"d-or\"><summary class=\"mute\">or from a terminal</summary>") | |
| 2680 | 3127 | if templ_7745c5c3_Err != nil { | |
| 2681 | 3128 | return templ_7745c5c3_Err | |
| 2682 | 3129 | } | |
| @@ -2684,12 +3131,12 @@ func moreMenu(p MRPage) templ.Component { | |||
| 2684 | 3131 | if templ_7745c5c3_Err != nil { | |
| 2685 | 3132 | return templ_7745c5c3_Err | |
| 2686 | 3133 | } | |
| 2687 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 251, "</details>") | |
| 3134 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 289, "</details>") | |
| 2688 | 3135 | if templ_7745c5c3_Err != nil { | |
| 2689 | 3136 | return templ_7745c5c3_Err | |
| 2690 | 3137 | } | |
| 2691 | 3138 | } | |
| 2692 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 252, "</div></details>") | |
| 3139 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 290, "</div></details>") | |
| 2693 | 3140 | if templ_7745c5c3_Err != nil { | |
| 2694 | 3141 | return templ_7745c5c3_Err | |
| 2695 | 3142 | } | |
| @@ -2713,12 +3160,12 @@ func Compare(l Layout, p ComparePage) templ.Component { | |||
| 2713 | 3160 | }() | |
| 2714 | 3161 | } | |
| 2715 | 3162 | ctx = templ.InitializeContext(ctx) | |
| 2716 | - | templ_7745c5c3_Var129 := templ.GetChildren(ctx) | |
| 2717 | - | if templ_7745c5c3_Var129 == nil { | |
| 2718 | - | templ_7745c5c3_Var129 = templ.NopComponent | |
| 3163 | + | templ_7745c5c3_Var151 := templ.GetChildren(ctx) | |
| 3164 | + | if templ_7745c5c3_Var151 == nil { | |
| 3165 | + | templ_7745c5c3_Var151 = templ.NopComponent | |
| 2719 | 3166 | } | |
| 2720 | 3167 | ctx = templ.ClearChildren(ctx) | |
| 2721 | - | templ_7745c5c3_Var130 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | |
| 3168 | + | templ_7745c5c3_Var152 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | |
| 2722 | 3169 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | |
| 2723 | 3170 | templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) | |
| 2724 | 3171 | if !templ_7745c5c3_IsBuffer { | |
| @@ -2730,263 +3177,263 @@ func Compare(l Layout, p ComparePage) templ.Component { | |||
| 2730 | 3177 | }() | |
| 2731 | 3178 | } | |
| 2732 | 3179 | ctx = templ.InitializeContext(ctx) | |
| 2733 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 253, "<header class=\"show mr-h\"><h1>") | |
| 3180 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 291, "<header class=\"show mr-h\"><h1>") | |
| 2734 | 3181 | if templ_7745c5c3_Err != nil { | |
| 2735 | 3182 | return templ_7745c5c3_Err | |
| 2736 | 3183 | } | |
| 2737 | - | var templ_7745c5c3_Var131 string | |
| 2738 | - | templ_7745c5c3_Var131, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) | |
| 3184 | + | var templ_7745c5c3_Var153 string | |
| 3185 | + | templ_7745c5c3_Var153, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) | |
| 2739 | 3186 | if templ_7745c5c3_Err != nil { | |
| 2740 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 532, Col: 17} | |
| 3187 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 617, Col: 17} | |
| 2741 | 3188 | } | |
| 2742 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var131)) | |
| 3189 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var153)) | |
| 2743 | 3190 | if templ_7745c5c3_Err != nil { | |
| 2744 | 3191 | return templ_7745c5c3_Err | |
| 2745 | 3192 | } | |
| 2746 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 254, " → ") | |
| 3193 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 292, " → ") | |
| 2747 | 3194 | if templ_7745c5c3_Err != nil { | |
| 2748 | 3195 | return templ_7745c5c3_Err | |
| 2749 | 3196 | } | |
| 2750 | - | var templ_7745c5c3_Var132 string | |
| 2751 | - | templ_7745c5c3_Var132, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 3197 | + | var templ_7745c5c3_Var154 string | |
| 3198 | + | templ_7745c5c3_Var154, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 2752 | 3199 | if templ_7745c5c3_Err != nil { | |
| 2753 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 532, Col: 34} | |
| 3200 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 617, Col: 34} | |
| 2754 | 3201 | } | |
| 2755 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var132)) | |
| 3202 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var154)) | |
| 2756 | 3203 | if templ_7745c5c3_Err != nil { | |
| 2757 | 3204 | return templ_7745c5c3_Err | |
| 2758 | 3205 | } | |
| 2759 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 255, "</h1><dl class=\"kv show-kv\"><dt>branch</dt><dd><a href=\"") | |
| 3206 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 293, "</h1><dl class=\"kv show-kv\"><dt>branch</dt><dd><a href=\"") | |
| 2760 | 3207 | if templ_7745c5c3_Err != nil { | |
| 2761 | 3208 | return templ_7745c5c3_Err | |
| 2762 | 3209 | } | |
| 2763 | - | var templ_7745c5c3_Var133 templ.SafeURL | |
| 2764 | - | templ_7745c5c3_Var133, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.SourceHref)) | |
| 3210 | + | var templ_7745c5c3_Var155 templ.SafeURL | |
| 3211 | + | templ_7745c5c3_Var155, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.SourceHref)) | |
| 2765 | 3212 | if templ_7745c5c3_Err != nil { | |
| 2766 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 535, Col: 45} | |
| 3213 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 620, Col: 45} | |
| 2767 | 3214 | } | |
| 2768 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var133)) | |
| 3215 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var155)) | |
| 2769 | 3216 | if templ_7745c5c3_Err != nil { | |
| 2770 | 3217 | return templ_7745c5c3_Err | |
| 2771 | 3218 | } | |
| 2772 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 256, "\">") | |
| 3219 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 294, "\">") | |
| 2773 | 3220 | if templ_7745c5c3_Err != nil { | |
| 2774 | 3221 | return templ_7745c5c3_Err | |
| 2775 | 3222 | } | |
| 2776 | - | var templ_7745c5c3_Var134 string | |
| 2777 | - | templ_7745c5c3_Var134, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) | |
| 3223 | + | var templ_7745c5c3_Var156 string | |
| 3224 | + | templ_7745c5c3_Var156, templ_7745c5c3_Err = templ.JoinStringErrs(p.Source) | |
| 2778 | 3225 | if templ_7745c5c3_Err != nil { | |
| 2779 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 535, Col: 58} | |
| 3226 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 620, Col: 58} | |
| 2780 | 3227 | } | |
| 2781 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var134)) | |
| 3228 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var156)) | |
| 2782 | 3229 | if templ_7745c5c3_Err != nil { | |
| 2783 | 3230 | return templ_7745c5c3_Err | |
| 2784 | 3231 | } | |
| 2785 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 257, "</a></dd><dt>target</dt><dd><a href=\"") | |
| 3232 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 295, "</a></dd><dt>target</dt><dd><a href=\"") | |
| 2786 | 3233 | if templ_7745c5c3_Err != nil { | |
| 2787 | 3234 | return templ_7745c5c3_Err | |
| 2788 | 3235 | } | |
| 2789 | - | var templ_7745c5c3_Var135 templ.SafeURL | |
| 2790 | - | templ_7745c5c3_Var135, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.TargetHref)) | |
| 3236 | + | var templ_7745c5c3_Var157 templ.SafeURL | |
| 3237 | + | templ_7745c5c3_Var157, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.TargetHref)) | |
| 2791 | 3238 | if templ_7745c5c3_Err != nil { | |
| 2792 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 537, Col: 45} | |
| 3239 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 622, Col: 45} | |
| 2793 | 3240 | } | |
| 2794 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var135)) | |
| 3241 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var157)) | |
| 2795 | 3242 | if templ_7745c5c3_Err != nil { | |
| 2796 | 3243 | return templ_7745c5c3_Err | |
| 2797 | 3244 | } | |
| 2798 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 258, "\">") | |
| 3245 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 296, "\">") | |
| 2799 | 3246 | if templ_7745c5c3_Err != nil { | |
| 2800 | 3247 | return templ_7745c5c3_Err | |
| 2801 | 3248 | } | |
| 2802 | - | var templ_7745c5c3_Var136 string | |
| 2803 | - | templ_7745c5c3_Var136, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 3249 | + | var templ_7745c5c3_Var158 string | |
| 3250 | + | templ_7745c5c3_Var158, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 2804 | 3251 | if templ_7745c5c3_Err != nil { | |
| 2805 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 537, Col: 58} | |
| 3252 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 622, Col: 58} | |
| 2806 | 3253 | } | |
| 2807 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var136)) | |
| 3254 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var158)) | |
| 2808 | 3255 | if templ_7745c5c3_Err != nil { | |
| 2809 | 3256 | return templ_7745c5c3_Err | |
| 2810 | 3257 | } | |
| 2811 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 259, "</a></dd><dt>ahead</dt><dd>") | |
| 3258 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 297, "</a></dd><dt>ahead</dt><dd>") | |
| 2812 | 3259 | if templ_7745c5c3_Err != nil { | |
| 2813 | 3260 | return templ_7745c5c3_Err | |
| 2814 | 3261 | } | |
| 2815 | - | var templ_7745c5c3_Var137 string | |
| 2816 | - | templ_7745c5c3_Var137, templ_7745c5c3_Err = templ.JoinStringErrs(p.AheadText) | |
| 3262 | + | var templ_7745c5c3_Var159 string | |
| 3263 | + | templ_7745c5c3_Var159, templ_7745c5c3_Err = templ.JoinStringErrs(p.AheadText) | |
| 2817 | 3264 | if templ_7745c5c3_Err != nil { | |
| 2818 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 539, Col: 21} | |
| 3265 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 624, Col: 21} | |
| 2819 | 3266 | } | |
| 2820 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var137)) | |
| 3267 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var159)) | |
| 2821 | 3268 | if templ_7745c5c3_Err != nil { | |
| 2822 | 3269 | return templ_7745c5c3_Err | |
| 2823 | 3270 | } | |
| 2824 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 260, "</dd></dl></header>") | |
| 3271 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 298, "</dd></dl></header>") | |
| 2825 | 3272 | if templ_7745c5c3_Err != nil { | |
| 2826 | 3273 | return templ_7745c5c3_Err | |
| 2827 | 3274 | } | |
| 2828 | 3275 | if p.Note != "" { | |
| 2829 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 261, "<p class=\"notice\">") | |
| 3276 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 299, "<p class=\"notice\">") | |
| 2830 | 3277 | if templ_7745c5c3_Err != nil { | |
| 2831 | 3278 | return templ_7745c5c3_Err | |
| 2832 | 3279 | } | |
| 2833 | - | var templ_7745c5c3_Var138 string | |
| 2834 | - | templ_7745c5c3_Var138, templ_7745c5c3_Err = templ.JoinStringErrs(p.Note) | |
| 3280 | + | var templ_7745c5c3_Var160 string | |
| 3281 | + | templ_7745c5c3_Var160, templ_7745c5c3_Err = templ.JoinStringErrs(p.Note) | |
| 2835 | 3282 | if templ_7745c5c3_Err != nil { | |
| 2836 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 543, Col: 29} | |
| 3283 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 628, Col: 29} | |
| 2837 | 3284 | } | |
| 2838 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var138)) | |
| 3285 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var160)) | |
| 2839 | 3286 | if templ_7745c5c3_Err != nil { | |
| 2840 | 3287 | return templ_7745c5c3_Err | |
| 2841 | 3288 | } | |
| 2842 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 262, "</p>") | |
| 3289 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 300, "</p>") | |
| 2843 | 3290 | if templ_7745c5c3_Err != nil { | |
| 2844 | 3291 | return templ_7745c5c3_Err | |
| 2845 | 3292 | } | |
| 2846 | 3293 | } | |
| 2847 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 263, " ") | |
| 3294 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 301, " ") | |
| 2848 | 3295 | if templ_7745c5c3_Err != nil { | |
| 2849 | 3296 | return templ_7745c5c3_Err | |
| 2850 | 3297 | } | |
| 2851 | 3298 | if p.Existing != nil { | |
| 2852 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 264, "<p class=\"notice\">an open merge request already covers these branches: <a href=\"") | |
| 3299 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 302, "<p class=\"notice\">an open merge request already covers these branches: <a href=\"") | |
| 2853 | 3300 | if templ_7745c5c3_Err != nil { | |
| 2854 | 3301 | return templ_7745c5c3_Err | |
| 2855 | 3302 | } | |
| 2856 | - | var templ_7745c5c3_Var139 templ.SafeURL | |
| 2857 | - | templ_7745c5c3_Var139, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Existing.Href)) | |
| 3303 | + | var templ_7745c5c3_Var161 templ.SafeURL | |
| 3304 | + | templ_7745c5c3_Var161, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Existing.Href)) | |
| 2858 | 3305 | if templ_7745c5c3_Err != nil { | |
| 2859 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 546, Col: 114} | |
| 3306 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 631, Col: 114} | |
| 2860 | 3307 | } | |
| 2861 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var139)) | |
| 3308 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var161)) | |
| 2862 | 3309 | if templ_7745c5c3_Err != nil { | |
| 2863 | 3310 | return templ_7745c5c3_Err | |
| 2864 | 3311 | } | |
| 2865 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 265, "\">") | |
| 3312 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 303, "\">") | |
| 2866 | 3313 | if templ_7745c5c3_Err != nil { | |
| 2867 | 3314 | return templ_7745c5c3_Err | |
| 2868 | 3315 | } | |
| 2869 | - | var templ_7745c5c3_Var140 string | |
| 2870 | - | templ_7745c5c3_Var140, templ_7745c5c3_Err = templ.JoinStringErrs(p.Existing.Label) | |
| 3316 | + | var templ_7745c5c3_Var162 string | |
| 3317 | + | templ_7745c5c3_Var162, templ_7745c5c3_Err = templ.JoinStringErrs(p.Existing.Label) | |
| 2871 | 3318 | if templ_7745c5c3_Err != nil { | |
| 2872 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 546, Col: 135} | |
| 3319 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 631, Col: 135} | |
| 2873 | 3320 | } | |
| 2874 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var140)) | |
| 3321 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var162)) | |
| 2875 | 3322 | if templ_7745c5c3_Err != nil { | |
| 2876 | 3323 | return templ_7745c5c3_Err | |
| 2877 | 3324 | } | |
| 2878 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 266, "</a></p>") | |
| 3325 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 304, "</a></p>") | |
| 2879 | 3326 | if templ_7745c5c3_Err != nil { | |
| 2880 | 3327 | return templ_7745c5c3_Err | |
| 2881 | 3328 | } | |
| 2882 | 3329 | } | |
| 2883 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 267, " ") | |
| 3330 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 305, " ") | |
| 2884 | 3331 | if templ_7745c5c3_Err != nil { | |
| 2885 | 3332 | return templ_7745c5c3_Err | |
| 2886 | 3333 | } | |
| 2887 | 3334 | if p.Create != nil { | |
| 2888 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 268, "<form class=\"cform create\" method=\"post\" action=\"") | |
| 3335 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 306, "<form class=\"cform create\" method=\"post\" action=\"") | |
| 2889 | 3336 | if templ_7745c5c3_Err != nil { | |
| 2890 | 3337 | return templ_7745c5c3_Err | |
| 2891 | 3338 | } | |
| 2892 | - | var templ_7745c5c3_Var141 templ.SafeURL | |
| 2893 | - | templ_7745c5c3_Var141, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Create.Action)) | |
| 3339 | + | var templ_7745c5c3_Var163 templ.SafeURL | |
| 3340 | + | templ_7745c5c3_Var163, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Create.Action)) | |
| 2894 | 3341 | if templ_7745c5c3_Err != nil { | |
| 2895 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 549, Col: 83} | |
| 3342 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 634, Col: 83} | |
| 2896 | 3343 | } | |
| 2897 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var141)) | |
| 3344 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var163)) | |
| 2898 | 3345 | if templ_7745c5c3_Err != nil { | |
| 2899 | 3346 | return templ_7745c5c3_Err | |
| 2900 | 3347 | } | |
| 2901 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 269, "\"><input type=\"hidden\" name=\"source\" value=\"") | |
| 3348 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 307, "\"><input type=\"hidden\" name=\"source\" value=\"") | |
| 2902 | 3349 | if templ_7745c5c3_Err != nil { | |
| 2903 | 3350 | return templ_7745c5c3_Err | |
| 2904 | 3351 | } | |
| 2905 | - | var templ_7745c5c3_Var142 string | |
| 2906 | - | templ_7745c5c3_Var142, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Source) | |
| 3352 | + | var templ_7745c5c3_Var164 string | |
| 3353 | + | templ_7745c5c3_Var164, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Source) | |
| 2907 | 3354 | if templ_7745c5c3_Err != nil { | |
| 2908 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 550, Col: 62} | |
| 3355 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 635, Col: 62} | |
| 2909 | 3356 | } | |
| 2910 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var142) | |
| 3357 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var164) | |
| 2911 | 3358 | if templ_7745c5c3_Err != nil { | |
| 2912 | 3359 | return templ_7745c5c3_Err | |
| 2913 | 3360 | } | |
| 2914 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 270, "\"> <input type=\"hidden\" name=\"target\" value=\"") | |
| 3361 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 308, "\"> <input type=\"hidden\" name=\"target\" value=\"") | |
| 2915 | 3362 | if templ_7745c5c3_Err != nil { | |
| 2916 | 3363 | return templ_7745c5c3_Err | |
| 2917 | 3364 | } | |
| 2918 | - | var templ_7745c5c3_Var143 string | |
| 2919 | - | templ_7745c5c3_Var143, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Target) | |
| 3365 | + | var templ_7745c5c3_Var165 string | |
| 3366 | + | templ_7745c5c3_Var165, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Target) | |
| 2920 | 3367 | if templ_7745c5c3_Err != nil { | |
| 2921 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 551, Col: 62} | |
| 3368 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 636, Col: 62} | |
| 2922 | 3369 | } | |
| 2923 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var143) | |
| 3370 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var165) | |
| 2924 | 3371 | if templ_7745c5c3_Err != nil { | |
| 2925 | 3372 | return templ_7745c5c3_Err | |
| 2926 | 3373 | } | |
| 2927 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 271, "\"> <label for=\"mr-title\">title</label> <input id=\"mr-title\" name=\"title\" type=\"text\" maxlength=\"120\" required value=\"") | |
| 3374 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 309, "\"> <label for=\"mr-title\">title</label> <input id=\"mr-title\" name=\"title\" type=\"text\" maxlength=\"120\" required value=\"") | |
| 2928 | 3375 | if templ_7745c5c3_Err != nil { | |
| 2929 | 3376 | return templ_7745c5c3_Err | |
| 2930 | 3377 | } | |
| 2931 | - | var templ_7745c5c3_Var144 string | |
| 2932 | - | templ_7745c5c3_Var144, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Title) | |
| 3378 | + | var templ_7745c5c3_Var166 string | |
| 3379 | + | templ_7745c5c3_Var166, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Create.Title) | |
| 2933 | 3380 | if templ_7745c5c3_Err != nil { | |
| 2934 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 553, Col: 97} | |
| 3381 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 638, Col: 97} | |
| 2935 | 3382 | } | |
| 2936 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var144) | |
| 3383 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var166) | |
| 2937 | 3384 | if templ_7745c5c3_Err != nil { | |
| 2938 | 3385 | return templ_7745c5c3_Err | |
| 2939 | 3386 | } | |
| 2940 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 272, "\" autocomplete=\"off\"> <label for=\"mr-desc\">description</label> <textarea id=\"mr-desc\" name=\"description\" rows=\"4\" maxlength=\"8000\" placeholder=\"Markdown, optional\"></textarea><div class=\"f-btns\"><button type=\"submit\" class=\"btn primary\">Create merge request</button></div></form>") | |
| 3387 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 310, "\" autocomplete=\"off\"> <label for=\"mr-desc\">description</label> <textarea id=\"mr-desc\" name=\"description\" rows=\"4\" maxlength=\"8000\" placeholder=\"Markdown, optional\"></textarea><div class=\"f-btns\"><button type=\"submit\" class=\"btn primary\">Create merge request</button></div></form>") | |
| 2941 | 3388 | if templ_7745c5c3_Err != nil { | |
| 2942 | 3389 | return templ_7745c5c3_Err | |
| 2943 | 3390 | } | |
| 2944 | 3391 | } | |
| 2945 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 273, " ") | |
| 3392 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 311, " ") | |
| 2946 | 3393 | if templ_7745c5c3_Err != nil { | |
| 2947 | 3394 | return templ_7745c5c3_Err | |
| 2948 | 3395 | } | |
| 2949 | 3396 | if p.HasDiff { | |
| 2950 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 274, "<div class=\"diff-tools\" role=\"group\" aria-label=\"Diff view\"><span class=\"dim\">") | |
| 3397 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 312, "<div class=\"diff-tools\" role=\"group\" aria-label=\"Diff view\"><span class=\"dim\">") | |
| 2951 | 3398 | if templ_7745c5c3_Err != nil { | |
| 2952 | 3399 | return templ_7745c5c3_Err | |
| 2953 | 3400 | } | |
| 2954 | - | var templ_7745c5c3_Var145 string | |
| 2955 | - | templ_7745c5c3_Var145, templ_7745c5c3_Err = templ.JoinStringErrs(plural(p.NFiles, "file", "files")) | |
| 3401 | + | var templ_7745c5c3_Var167 string | |
| 3402 | + | templ_7745c5c3_Var167, templ_7745c5c3_Err = templ.JoinStringErrs(plural(p.NFiles, "file", "files")) | |
| 2956 | 3403 | if templ_7745c5c3_Err != nil { | |
| 2957 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 563, Col: 57} | |
| 3404 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 648, Col: 57} | |
| 2958 | 3405 | } | |
| 2959 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var145)) | |
| 3406 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var167)) | |
| 2960 | 3407 | if templ_7745c5c3_Err != nil { | |
| 2961 | 3408 | return templ_7745c5c3_Err | |
| 2962 | 3409 | } | |
| 2963 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 275, " · <span class=\"plus\">+") | |
| 3410 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 313, " · <span class=\"plus\">+") | |
| 2964 | 3411 | if templ_7745c5c3_Err != nil { | |
| 2965 | 3412 | return templ_7745c5c3_Err | |
| 2966 | 3413 | } | |
| 2967 | - | var templ_7745c5c3_Var146 string | |
| 2968 | - | templ_7745c5c3_Var146, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Adds)) | |
| 3414 | + | var templ_7745c5c3_Var168 string | |
| 3415 | + | templ_7745c5c3_Var168, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Adds)) | |
| 2969 | 3416 | if templ_7745c5c3_Err != nil { | |
| 2970 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 563, Col: 97} | |
| 3417 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 648, Col: 97} | |
| 2971 | 3418 | } | |
| 2972 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var146)) | |
| 3419 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var168)) | |
| 2973 | 3420 | if templ_7745c5c3_Err != nil { | |
| 2974 | 3421 | return templ_7745c5c3_Err | |
| 2975 | 3422 | } | |
| 2976 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 276, "</span> <span class=\"minus\">-") | |
| 3423 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 314, "</span> <span class=\"minus\">-") | |
| 2977 | 3424 | if templ_7745c5c3_Err != nil { | |
| 2978 | 3425 | return templ_7745c5c3_Err | |
| 2979 | 3426 | } | |
| 2980 | - | var templ_7745c5c3_Var147 string | |
| 2981 | - | templ_7745c5c3_Var147, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Dels)) | |
| 3427 | + | var templ_7745c5c3_Var169 string | |
| 3428 | + | templ_7745c5c3_Var169, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Dels)) | |
| 2982 | 3429 | if templ_7745c5c3_Err != nil { | |
| 2983 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 563, Col: 142} | |
| 3430 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 648, Col: 142} | |
| 2984 | 3431 | } | |
| 2985 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var147)) | |
| 3432 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var169)) | |
| 2986 | 3433 | if templ_7745c5c3_Err != nil { | |
| 2987 | 3434 | return templ_7745c5c3_Err | |
| 2988 | 3435 | } | |
| 2989 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 277, "</span></span> <span class=\"grow\"></span>") | |
| 3436 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 315, "</span></span> <span class=\"grow\"></span>") | |
| 2990 | 3437 | if templ_7745c5c3_Err != nil { | |
| 2991 | 3438 | return templ_7745c5c3_Err | |
| 2992 | 3439 | } | |
| @@ -2994,64 +3441,64 @@ func Compare(l Layout, p ComparePage) templ.Component { | |||
| 2994 | 3441 | if templ_7745c5c3_Err != nil { | |
| 2995 | 3442 | return templ_7745c5c3_Err | |
| 2996 | 3443 | } | |
| 2997 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 278, "</div>") | |
| 3444 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 316, "</div>") | |
| 2998 | 3445 | if templ_7745c5c3_Err != nil { | |
| 2999 | 3446 | return templ_7745c5c3_Err | |
| 3000 | 3447 | } | |
| 3001 | 3448 | if p.Invisible { | |
| 3002 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 279, "<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>") | |
| 3449 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 317, "<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>") | |
| 3003 | 3450 | if templ_7745c5c3_Err != nil { | |
| 3004 | 3451 | return templ_7745c5c3_Err | |
| 3005 | 3452 | } | |
| 3006 | 3453 | } | |
| 3007 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 280, " <div id=\"diffs\"") | |
| 3454 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 318, " <div id=\"diffs\"") | |
| 3008 | 3455 | if templ_7745c5c3_Err != nil { | |
| 3009 | 3456 | return templ_7745c5c3_Err | |
| 3010 | 3457 | } | |
| 3011 | 3458 | if p.Both { | |
| 3012 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 281, " data-diff=\"") | |
| 3459 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 319, " data-diff=\"") | |
| 3013 | 3460 | if templ_7745c5c3_Err != nil { | |
| 3014 | 3461 | return templ_7745c5c3_Err | |
| 3015 | 3462 | } | |
| 3016 | - | var templ_7745c5c3_Var148 string | |
| 3017 | - | templ_7745c5c3_Var148, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View) | |
| 3463 | + | var templ_7745c5c3_Var170 string | |
| 3464 | + | templ_7745c5c3_Var170, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View) | |
| 3018 | 3465 | if templ_7745c5c3_Err != nil { | |
| 3019 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 576, Col: 23} | |
| 3466 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 661, Col: 23} | |
| 3020 | 3467 | } | |
| 3021 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var148) | |
| 3468 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var170) | |
| 3022 | 3469 | if templ_7745c5c3_Err != nil { | |
| 3023 | 3470 | return templ_7745c5c3_Err | |
| 3024 | 3471 | } | |
| 3025 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 282, "\"") | |
| 3472 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 320, "\"") | |
| 3026 | 3473 | if templ_7745c5c3_Err != nil { | |
| 3027 | 3474 | return templ_7745c5c3_Err | |
| 3028 | 3475 | } | |
| 3029 | 3476 | } else { | |
| 3030 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 283, " data-view=\"") | |
| 3477 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 321, " data-view=\"") | |
| 3031 | 3478 | if templ_7745c5c3_Err != nil { | |
| 3032 | 3479 | return templ_7745c5c3_Err | |
| 3033 | 3480 | } | |
| 3034 | - | var templ_7745c5c3_Var149 string | |
| 3035 | - | templ_7745c5c3_Var149, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View) | |
| 3481 | + | var templ_7745c5c3_Var171 string | |
| 3482 | + | templ_7745c5c3_Var171, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.View) | |
| 3036 | 3483 | if templ_7745c5c3_Err != nil { | |
| 3037 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 578, Col: 23} | |
| 3484 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 663, Col: 23} | |
| 3038 | 3485 | } | |
| 3039 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var149) | |
| 3486 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var171) | |
| 3040 | 3487 | if templ_7745c5c3_Err != nil { | |
| 3041 | 3488 | return templ_7745c5c3_Err | |
| 3042 | 3489 | } | |
| 3043 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 284, "\"") | |
| 3490 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 322, "\"") | |
| 3044 | 3491 | if templ_7745c5c3_Err != nil { | |
| 3045 | 3492 | return templ_7745c5c3_Err | |
| 3046 | 3493 | } | |
| 3047 | 3494 | } | |
| 3048 | 3495 | if p.Both && !p.Pinned { | |
| 3049 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 285, " hx-live=\"this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff\"") | |
| 3496 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 323, " hx-live=\"this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff\"") | |
| 3050 | 3497 | if templ_7745c5c3_Err != nil { | |
| 3051 | 3498 | return templ_7745c5c3_Err | |
| 3052 | 3499 | } | |
| 3053 | 3500 | } | |
| 3054 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 286, ">") | |
| 3501 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 324, ">") | |
| 3055 | 3502 | if templ_7745c5c3_Err != nil { | |
| 3056 | 3503 | return templ_7745c5c3_Err | |
| 3057 | 3504 | } | |
| @@ -3061,14 +3508,14 @@ func Compare(l Layout, p ComparePage) templ.Component { | |||
| 3061 | 3508 | return templ_7745c5c3_Err | |
| 3062 | 3509 | } | |
| 3063 | 3510 | } | |
| 3064 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 287, "</div>") | |
| 3511 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 325, "</div>") | |
| 3065 | 3512 | if templ_7745c5c3_Err != nil { | |
| 3066 | 3513 | return templ_7745c5c3_Err | |
| 3067 | 3514 | } | |
| 3068 | 3515 | } | |
| 3069 | 3516 | return nil | |
| 3070 | 3517 | }) | |
| 3071 | - | templ_7745c5c3_Err = Shell(l, compareRail(p)).Render(templ.WithChildren(ctx, templ_7745c5c3_Var130), templ_7745c5c3_Buffer) | |
| 3518 | + | templ_7745c5c3_Err = Shell(l, compareRail(p)).Render(templ.WithChildren(ctx, templ_7745c5c3_Var152), templ_7745c5c3_Buffer) | |
| 3072 | 3519 | if templ_7745c5c3_Err != nil { | |
| 3073 | 3520 | return templ_7745c5c3_Err | |
| 3074 | 3521 | } | |
| @@ -3092,173 +3539,173 @@ func compareRail(p ComparePage) templ.Component { | |||
| 3092 | 3539 | }() | |
| 3093 | 3540 | } | |
| 3094 | 3541 | ctx = templ.InitializeContext(ctx) | |
| 3095 | - | templ_7745c5c3_Var150 := templ.GetChildren(ctx) | |
| 3096 | - | if templ_7745c5c3_Var150 == nil { | |
| 3097 | - | templ_7745c5c3_Var150 = templ.NopComponent | |
| 3542 | + | templ_7745c5c3_Var172 := templ.GetChildren(ctx) | |
| 3543 | + | if templ_7745c5c3_Var172 == nil { | |
| 3544 | + | templ_7745c5c3_Var172 = templ.NopComponent | |
| 3098 | 3545 | } | |
| 3099 | 3546 | ctx = templ.ClearChildren(ctx) | |
| 3100 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 288, "<section aria-labelledby=\"r-ahead\"><h2 id=\"r-ahead\">Commits <span class=\"count\">") | |
| 3547 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 326, "<section aria-labelledby=\"r-ahead\"><h2 id=\"r-ahead\">Commits <span class=\"count\">") | |
| 3101 | 3548 | if templ_7745c5c3_Err != nil { | |
| 3102 | 3549 | return templ_7745c5c3_Err | |
| 3103 | 3550 | } | |
| 3104 | - | var templ_7745c5c3_Var151 string | |
| 3105 | - | templ_7745c5c3_Var151, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Ahead))) | |
| 3551 | + | var templ_7745c5c3_Var173 string | |
| 3552 | + | templ_7745c5c3_Var173, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Ahead))) | |
| 3106 | 3553 | if templ_7745c5c3_Err != nil { | |
| 3107 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 594, Col: 67} | |
| 3554 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 679, Col: 67} | |
| 3108 | 3555 | } | |
| 3109 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var151)) | |
| 3556 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var173)) | |
| 3110 | 3557 | if templ_7745c5c3_Err != nil { | |
| 3111 | 3558 | return templ_7745c5c3_Err | |
| 3112 | 3559 | } | |
| 3113 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 289, "</span></h2><ul class=\"pcommits\">") | |
| 3560 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 327, "</span></h2><ul class=\"pcommits\">") | |
| 3114 | 3561 | if templ_7745c5c3_Err != nil { | |
| 3115 | 3562 | return templ_7745c5c3_Err | |
| 3116 | 3563 | } | |
| 3117 | 3564 | for _, c := range p.Ahead { | |
| 3118 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 290, "<li><a href=\"") | |
| 3565 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 328, "<li><a href=\"") | |
| 3119 | 3566 | if templ_7745c5c3_Err != nil { | |
| 3120 | 3567 | return templ_7745c5c3_Err | |
| 3121 | 3568 | } | |
| 3122 | - | var templ_7745c5c3_Var152 templ.SafeURL | |
| 3123 | - | templ_7745c5c3_Var152, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref)) | |
| 3569 | + | var templ_7745c5c3_Var174 templ.SafeURL | |
| 3570 | + | templ_7745c5c3_Var174, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(c.CHref)) | |
| 3124 | 3571 | if templ_7745c5c3_Err != nil { | |
| 3125 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 598, Col: 37} | |
| 3572 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 683, Col: 37} | |
| 3126 | 3573 | } | |
| 3127 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var152)) | |
| 3574 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var174)) | |
| 3128 | 3575 | if templ_7745c5c3_Err != nil { | |
| 3129 | 3576 | return templ_7745c5c3_Err | |
| 3130 | 3577 | } | |
| 3131 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 291, "\">") | |
| 3578 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 329, "\">") | |
| 3132 | 3579 | if templ_7745c5c3_Err != nil { | |
| 3133 | 3580 | return templ_7745c5c3_Err | |
| 3134 | 3581 | } | |
| 3135 | - | var templ_7745c5c3_Var153 string | |
| 3136 | - | templ_7745c5c3_Var153, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short) | |
| 3582 | + | var templ_7745c5c3_Var175 string | |
| 3583 | + | templ_7745c5c3_Var175, templ_7745c5c3_Err = templ.JoinStringErrs(c.Short) | |
| 3137 | 3584 | if templ_7745c5c3_Err != nil { | |
| 3138 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 598, Col: 49} | |
| 3585 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 683, Col: 49} | |
| 3139 | 3586 | } | |
| 3140 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var153)) | |
| 3587 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var175)) | |
| 3141 | 3588 | if templ_7745c5c3_Err != nil { | |
| 3142 | 3589 | return templ_7745c5c3_Err | |
| 3143 | 3590 | } | |
| 3144 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 292, "</a> <span class=\"s\">") | |
| 3591 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 330, "</a> <span class=\"s\">") | |
| 3145 | 3592 | if templ_7745c5c3_Err != nil { | |
| 3146 | 3593 | return templ_7745c5c3_Err | |
| 3147 | 3594 | } | |
| 3148 | - | var templ_7745c5c3_Var154 string | |
| 3149 | - | templ_7745c5c3_Var154, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject) | |
| 3595 | + | var templ_7745c5c3_Var176 string | |
| 3596 | + | templ_7745c5c3_Var176, templ_7745c5c3_Err = templ.JoinStringErrs(c.Subject) | |
| 3150 | 3597 | if templ_7745c5c3_Err != nil { | |
| 3151 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 599, Col: 32} | |
| 3598 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 684, Col: 32} | |
| 3152 | 3599 | } | |
| 3153 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var154)) | |
| 3600 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var176)) | |
| 3154 | 3601 | if templ_7745c5c3_Err != nil { | |
| 3155 | 3602 | return templ_7745c5c3_Err | |
| 3156 | 3603 | } | |
| 3157 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 293, "</span> <span class=\"mute\">") | |
| 3604 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 331, "</span> <span class=\"mute\">") | |
| 3158 | 3605 | if templ_7745c5c3_Err != nil { | |
| 3159 | 3606 | return templ_7745c5c3_Err | |
| 3160 | 3607 | } | |
| 3161 | - | var templ_7745c5c3_Var155 string | |
| 3162 | - | templ_7745c5c3_Var155, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author) | |
| 3608 | + | var templ_7745c5c3_Var177 string | |
| 3609 | + | templ_7745c5c3_Var177, templ_7745c5c3_Err = templ.JoinStringErrs(c.Author) | |
| 3163 | 3610 | if templ_7745c5c3_Err != nil { | |
| 3164 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 601, Col: 16} | |
| 3611 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 686, Col: 16} | |
| 3165 | 3612 | } | |
| 3166 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var155)) | |
| 3613 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var177)) | |
| 3167 | 3614 | if templ_7745c5c3_Err != nil { | |
| 3168 | 3615 | return templ_7745c5c3_Err | |
| 3169 | 3616 | } | |
| 3170 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 294, " ") | |
| 3617 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 332, " ") | |
| 3171 | 3618 | if templ_7745c5c3_Err != nil { | |
| 3172 | 3619 | return templ_7745c5c3_Err | |
| 3173 | 3620 | } | |
| 3174 | 3621 | if c.WhenAt != "" { | |
| 3175 | - | var templ_7745c5c3_Var156 string | |
| 3176 | - | templ_7745c5c3_Var156, templ_7745c5c3_Err = templ.JoinStringErrs(" · ") | |
| 3622 | + | var templ_7745c5c3_Var178 string | |
| 3623 | + | templ_7745c5c3_Var178, templ_7745c5c3_Err = templ.JoinStringErrs(" · ") | |
| 3177 | 3624 | if templ_7745c5c3_Err != nil { | |
| 3178 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 603, Col: 15} | |
| 3625 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 688, Col: 15} | |
| 3179 | 3626 | } | |
| 3180 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var156)) | |
| 3627 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var178)) | |
| 3181 | 3628 | if templ_7745c5c3_Err != nil { | |
| 3182 | 3629 | return templ_7745c5c3_Err | |
| 3183 | 3630 | } | |
| 3184 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 295, "<time datetime=\"") | |
| 3631 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 333, "<time datetime=\"") | |
| 3185 | 3632 | if templ_7745c5c3_Err != nil { | |
| 3186 | 3633 | return templ_7745c5c3_Err | |
| 3187 | 3634 | } | |
| 3188 | - | var templ_7745c5c3_Var157 string | |
| 3189 | - | templ_7745c5c3_Var157, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | |
| 3635 | + | var templ_7745c5c3_Var179 string | |
| 3636 | + | templ_7745c5c3_Var179, templ_7745c5c3_Err = templ.ResolveAttributeValue(c.WhenAt) | |
| 3190 | 3637 | if templ_7745c5c3_Err != nil { | |
| 3191 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 603, Col: 42} | |
| 3638 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 688, Col: 42} | |
| 3192 | 3639 | } | |
| 3193 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var157) | |
| 3640 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var179) | |
| 3194 | 3641 | if templ_7745c5c3_Err != nil { | |
| 3195 | 3642 | return templ_7745c5c3_Err | |
| 3196 | 3643 | } | |
| 3197 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 296, "\">") | |
| 3644 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 334, "\">") | |
| 3198 | 3645 | if templ_7745c5c3_Err != nil { | |
| 3199 | 3646 | return templ_7745c5c3_Err | |
| 3200 | 3647 | } | |
| 3201 | - | var templ_7745c5c3_Var158 string | |
| 3202 | - | templ_7745c5c3_Var158, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) | |
| 3648 | + | var templ_7745c5c3_Var180 string | |
| 3649 | + | templ_7745c5c3_Var180, templ_7745c5c3_Err = templ.JoinStringErrs(c.When) | |
| 3203 | 3650 | if templ_7745c5c3_Err != nil { | |
| 3204 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 603, Col: 53} | |
| 3651 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 688, Col: 53} | |
| 3205 | 3652 | } | |
| 3206 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var158)) | |
| 3653 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var180)) | |
| 3207 | 3654 | if templ_7745c5c3_Err != nil { | |
| 3208 | 3655 | return templ_7745c5c3_Err | |
| 3209 | 3656 | } | |
| 3210 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 297, "</time>") | |
| 3657 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 335, "</time>") | |
| 3211 | 3658 | if templ_7745c5c3_Err != nil { | |
| 3212 | 3659 | return templ_7745c5c3_Err | |
| 3213 | 3660 | } | |
| 3214 | 3661 | } | |
| 3215 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 298, "</span></li>") | |
| 3662 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 336, "</span></li>") | |
| 3216 | 3663 | if templ_7745c5c3_Err != nil { | |
| 3217 | 3664 | return templ_7745c5c3_Err | |
| 3218 | 3665 | } | |
| 3219 | 3666 | } | |
| 3220 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 299, "</ul>") | |
| 3667 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 337, "</ul>") | |
| 3221 | 3668 | if templ_7745c5c3_Err != nil { | |
| 3222 | 3669 | return templ_7745c5c3_Err | |
| 3223 | 3670 | } | |
| 3224 | 3671 | if len(p.Ahead) == 0 { | |
| 3225 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 300, "<p class=\"dim\">no commit ahead of ") | |
| 3672 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 338, "<p class=\"dim\">no commit ahead of ") | |
| 3226 | 3673 | if templ_7745c5c3_Err != nil { | |
| 3227 | 3674 | return templ_7745c5c3_Err | |
| 3228 | 3675 | } | |
| 3229 | - | var templ_7745c5c3_Var159 string | |
| 3230 | - | templ_7745c5c3_Var159, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 3676 | + | var templ_7745c5c3_Var181 string | |
| 3677 | + | templ_7745c5c3_Var181, templ_7745c5c3_Err = templ.JoinStringErrs(p.Target) | |
| 3231 | 3678 | if templ_7745c5c3_Err != nil { | |
| 3232 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 610, Col: 47} | |
| 3679 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 695, Col: 47} | |
| 3233 | 3680 | } | |
| 3234 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var159)) | |
| 3681 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var181)) | |
| 3235 | 3682 | if templ_7745c5c3_Err != nil { | |
| 3236 | 3683 | return templ_7745c5c3_Err | |
| 3237 | 3684 | } | |
| 3238 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 301, ".</p>") | |
| 3685 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 339, ".</p>") | |
| 3239 | 3686 | if templ_7745c5c3_Err != nil { | |
| 3240 | 3687 | return templ_7745c5c3_Err | |
| 3241 | 3688 | } | |
| 3242 | 3689 | } | |
| 3243 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 302, "</section>") | |
| 3690 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 340, "</section>") | |
| 3244 | 3691 | if templ_7745c5c3_Err != nil { | |
| 3245 | 3692 | return templ_7745c5c3_Err | |
| 3246 | 3693 | } | |
| 3247 | 3694 | if p.HasDiff { | |
| 3248 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 303, "<section aria-labelledby=\"r-files\"><h2 id=\"r-files\">Files <span class=\"count\">") | |
| 3695 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 341, "<section aria-labelledby=\"r-files\"><h2 id=\"r-files\">Files <span class=\"count\">") | |
| 3249 | 3696 | if templ_7745c5c3_Err != nil { | |
| 3250 | 3697 | return templ_7745c5c3_Err | |
| 3251 | 3698 | } | |
| 3252 | - | var templ_7745c5c3_Var160 string | |
| 3253 | - | templ_7745c5c3_Var160, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Files))) | |
| 3699 | + | var templ_7745c5c3_Var182 string | |
| 3700 | + | templ_7745c5c3_Var182, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Files))) | |
| 3254 | 3701 | if templ_7745c5c3_Err != nil { | |
| 3255 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 615, Col: 66} | |
| 3702 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/mr.templ`, Line: 700, Col: 66} | |
| 3256 | 3703 | } | |
| 3257 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var160)) | |
| 3704 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var182)) | |
| 3258 | 3705 | if templ_7745c5c3_Err != nil { | |
| 3259 | 3706 | return templ_7745c5c3_Err | |
| 3260 | 3707 | } | |
| 3261 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 304, "</span></h2>") | |
| 3708 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 342, "</span></h2>") | |
| 3262 | 3709 | if templ_7745c5c3_Err != nil { | |
| 3263 | 3710 | return templ_7745c5c3_Err | |
| 3264 | 3711 | } | |
| @@ -3266,12 +3713,12 @@ func compareRail(p ComparePage) templ.Component { | |||
| 3266 | 3713 | if templ_7745c5c3_Err != nil { | |
| 3267 | 3714 | return templ_7745c5c3_Err | |
| 3268 | 3715 | } | |
| 3269 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 305, "</section>") | |
| 3716 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 343, "</section>") | |
| 3270 | 3717 | if templ_7745c5c3_Err != nil { | |
| 3271 | 3718 | return templ_7745c5c3_Err | |
| 3272 | 3719 | } | |
| 3273 | 3720 | } | |
| 3274 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 306, "<section aria-labelledby=\"r-cli\"><h2 id=\"r-cli\">From a terminal</h2>") | |
| 3721 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 344, "<section aria-labelledby=\"r-cli\"><h2 id=\"r-cli\">From a terminal</h2>") | |
| 3275 | 3722 | if templ_7745c5c3_Err != nil { | |
| 3276 | 3723 | return templ_7745c5c3_Err | |
| 3277 | 3724 | } | |
| @@ -3279,7 +3726,7 @@ func compareRail(p ComparePage) templ.Component { | |||
| 3279 | 3726 | if templ_7745c5c3_Err != nil { | |
| 3280 | 3727 | return templ_7745c5c3_Err | |
| 3281 | 3728 | } | |
| 3282 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 307, "</section>") | |
| 3729 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 345, "</section>") | |
| 3283 | 3730 | if templ_7745c5c3_Err != nil { | |
| 3284 | 3731 | return templ_7745c5c3_Err | |
| 3285 | 3732 | } | |
A internal/adapters/web/mr_test.go +130 -0
| @@ -0,0 +1,130 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | package web | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "context" | |
| 7 | + | "testing" | |
| 8 | + | ||
| 9 | + | "github.com/stretchr/testify/assert" | |
| 10 | + | "github.com/stretchr/testify/mock" | |
| 11 | + | "github.com/stretchr/testify/require" | |
| 12 | + | ||
| 13 | + | "bnema.dev/ermite/internal/domain" | |
| 14 | + | "bnema.dev/ermite/internal/ports" | |
| 15 | + | "bnema.dev/ermite/internal/ports/mocks" | |
| 16 | + | ) | |
| 17 | + | ||
| 18 | + | func repoWithModes(modes ...domain.MergeMode) ports.RepoView { | |
| 19 | + | var v ports.RepoView | |
| 20 | + | v.Settings.AllowedMergeModes = modes | |
| 21 | + | return v | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | func watchOf(ev domain.Event) ports.WatchEvent { return ports.WatchEvent{Event: ev} } | |
| 25 | + | ||
| 26 | + | func TestMergeabilityView(t *testing.T) { | |
| 27 | + | t.Parallel() | |
| 28 | + | for _, tt := range []struct { | |
| 29 | + | name string | |
| 30 | + | m domain.Mergeability | |
| 31 | + | label, detail string | |
| 32 | + | ffOK, mergeOK bool | |
| 33 | + | note string | |
| 34 | + | paths []string | |
| 35 | + | }{ | |
| 36 | + | {"up to date", domain.Mergeability{State: domain.MergeabilityUpToDate}, "Ready to merge", "", true, true, "", nil}, | |
| 37 | + | {"behind", domain.Mergeability{State: domain.MergeabilityBehind, Behind: 2}, "Behind main by 2 commits", "merges without conflicts", false, true, "", nil}, | |
| 38 | + | {"behind by one", domain.Mergeability{State: domain.MergeabilityBehind, Behind: 1}, "Behind main by 1 commit", "merges without conflicts", false, true, "", nil}, | |
| 39 | + | {"behind, capped", domain.Mergeability{State: domain.MergeabilityBehind, Behind: 1000, BehindCapped: true}, "Behind main by 1000+ commits", "merges without conflicts", false, true, "", nil}, | |
| 40 | + | {"conflict", domain.Mergeability{State: domain.MergeabilityConflict, ConflictPaths: []string{"a.go"}}, "Conflicts with main", "rebase needed", false, false, "", []string{"a.go"}}, | |
| 41 | + | {"conflict, truncated", domain.Mergeability{State: domain.MergeabilityConflict, ConflictPaths: []string{"a.go"}, ConflictPathsTruncated: true}, "Conflicts with main", "rebase needed", false, false, "more files conflict than are listed.", []string{"a.go"}}, | |
| 42 | + | {"no common history", domain.Mergeability{State: domain.MergeabilityConflict}, "Conflicts with main", "rebase needed", false, false, "no common history.", nil}, | |
| 43 | + | {"checking", domain.Mergeability{State: domain.MergeabilityChecking}, "Checking…", "", false, false, "", nil}, | |
| 44 | + | {"none", domain.Mergeability{}, "Checking…", "", false, false, "", nil}, | |
| 45 | + | } { | |
| 46 | + | t.Run(tt.name, func(t *testing.T) { | |
| 47 | + | t.Parallel() | |
| 48 | + | v := mergeabilityView(tt.m, "main") | |
| 49 | + | assert.Equal(t, tt.label, v.Label) | |
| 50 | + | assert.Equal(t, tt.detail, v.Detail) | |
| 51 | + | assert.Equal(t, tt.ffOK, v.FFOk) | |
| 52 | + | assert.Equal(t, tt.mergeOK, v.MergeOk) | |
| 53 | + | assert.Equal(t, tt.note, v.PathsNote) | |
| 54 | + | assert.Equal(t, tt.paths, v.Paths) | |
| 55 | + | assert.Equal(t, !tt.ffOK, v.FFReason != "", "a mode that is not possible says why") | |
| 56 | + | assert.Equal(t, !tt.mergeOK, v.MergeReason != "") | |
| 57 | + | }) | |
| 58 | + | } | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | func TestMergeBoxModes(t *testing.T) { | |
| 62 | + | t.Parallel() | |
| 63 | + | d := &repoData{repo: repoWithModes(domain.MergeModes()...)} | |
| 64 | + | s := &Server{} | |
| 65 | + | ||
| 66 | + | behind := s.mergeBox(d, 1, "feat", "abc", "", mergeabilityView(domain.Mergeability{State: domain.MergeabilityBehind, Behind: 1}, "main")) | |
| 67 | + | require.Len(t, behind.Modes, 3) | |
| 68 | + | assert.True(t, behind.Modes[0].Disabled, "fast-forward is not possible when behind") | |
| 69 | + | assert.NotEmpty(t, behind.Modes[0].Reason) | |
| 70 | + | assert.False(t, behind.Modes[1].Disabled) | |
| 71 | + | assert.False(t, behind.Modes[2].Disabled) | |
| 72 | + | assert.Equal(t, 1, behind.Checked(), "the first possible mode is checked") | |
| 73 | + | assert.True(t, behind.CanMerge()) | |
| 74 | + | assert.True(t, behind.Signed) | |
| 75 | + | ||
| 76 | + | conflict := s.mergeBox(d, 1, "feat", "abc", "", mergeabilityView(domain.Mergeability{State: domain.MergeabilityConflict}, "main")) | |
| 77 | + | assert.False(t, conflict.CanMerge()) | |
| 78 | + | assert.NotEmpty(t, conflict.MergeTitle(), "the button says why it is disabled") | |
| 79 | + | assert.Equal(t, 0, conflict.Checked()) | |
| 80 | + | assert.Contains(t, conflict.CLI.Lines[2], "--mode merge") | |
| 81 | + | assert.NotContains(t, conflict.CLI.Lines[1], "--squash") | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | func TestMergeBoxOnlyFastForwardIsNotSigned(t *testing.T) { | |
| 85 | + | t.Parallel() | |
| 86 | + | d := &repoData{repo: repoWithModes(domain.MergeModeFastForward)} | |
| 87 | + | b := (&Server{}).mergeBox(d, 1, "feat", "abc", "", mergeabilityView(domain.Mergeability{State: domain.MergeabilityUpToDate}, "main")) | |
| 88 | + | assert.False(t, b.Signed, "no commit is created, nothing to explain") | |
| 89 | + | assert.Len(t, b.Modes, 1) | |
| 90 | + | } | |
| 91 | + | ||
| 92 | + | func TestMergeBoxNamesTheSigningKey(t *testing.T) { | |
| 93 | + | t.Parallel() | |
| 94 | + | key := mocks.NewSigningKeyService(t) | |
| 95 | + | key.EXPECT().Info().Return(domain.SigningKeyInfo{Key: domain.SigningPublicKey{Fingerprint: "SHA256:abc"}}) | |
| 96 | + | d := &repoData{repo: repoWithModes(domain.MergeModes()...)} | |
| 97 | + | b := (&Server{cfg: Config{SigningKey: key}}).mergeBox(d, 1, "feat", "abc", "", mergeabilityView(domain.Mergeability{}, "main")) | |
| 98 | + | assert.Equal(t, "SHA256:abc", b.Fingerprint) | |
| 99 | + | } | |
| 100 | + | ||
| 101 | + | func TestMergeErrorTravelsInTheContext(t *testing.T) { | |
| 102 | + | t.Parallel() | |
| 103 | + | assert.Empty(t, mergeErrorOf(context.Background())) | |
| 104 | + | assert.Equal(t, "boom", mergeErrorOf(withMergeError(context.Background(), "boom"))) | |
| 105 | + | } | |
| 106 | + | ||
| 107 | + | func TestMergeabilityLiveEvents(t *testing.T) { | |
| 108 | + | t.Parallel() | |
| 109 | + | assert.True(t, liveEvent(watchOf(domain.MergeRequestMergeability{Number: 1}))) | |
| 110 | + | svc := mocks.NewMergeRequestService(t) | |
| 111 | + | svc.EXPECT().Mergeability(mock.Anything, mock.Anything, mock.Anything, int64(1)). | |
| 112 | + | Return(domain.Mergeability{State: domain.MergeabilityBehind, Behind: 3}, nil) | |
| 113 | + | l := liveStream{s: &Server{cfg: Config{MergeRequests: svc}}, repo: "demo", view: "mr", n: "1", target: "main"} | |
| 114 | + | ctx := context.WithValue(context.Background(), signedInKey{}, struct{}{}) | |
| 115 | + | ||
| 116 | + | fs, err := l.repoPage(ctx, watchOf(domain.MergeRequestMergeability{Number: 1})) | |
| 117 | + | require.NoError(t, err) | |
| 118 | + | require.Len(t, fs, 1) | |
| 119 | + | assert.Equal(t, "mr-mergeability", fs[0].event) | |
| 120 | + | assert.Contains(t, fs[0].html, "Behind main by 3 commits") | |
| 121 | + | ||
| 122 | + | // Another request, or a page that is not one: nothing. | |
| 123 | + | fs, err = l.repoPage(ctx, watchOf(domain.MergeRequestMergeability{Number: 2})) | |
| 124 | + | require.NoError(t, err) | |
| 125 | + | assert.Empty(t, fs) | |
| 126 | + | // The box is the owner's. | |
| 127 | + | fs, err = l.repoPage(context.Background(), watchOf(domain.MergeRequestMergeability{Number: 1})) | |
| 128 | + | require.NoError(t, err) | |
| 129 | + | assert.Empty(t, fs) | |
| 130 | + | } | |
M internal/adapters/web/static/css/60-mr.css +16 -1
| @@ -78,7 +78,22 @@ | |||
| 78 | 78 | & .d-btns { display: flex; margin-top: var(--s1); } | |
| 79 | 79 | & .d-btns .btn { flex: 1; justify-content: center; } | |
| 80 | 80 | & .d-draft { color: var(--ink-dim); } | |
| 81 | - | } | |
| 81 | + | & .d-note { margin: 0; color: var(--ink-dim); } | |
| 82 | + | & .d-err { margin: 0; padding: var(--s2) var(--s3); color: var(--ink-hi); border: 1px solid var(--red); border-left-width: 3px; border-radius: var(--r-s); overflow-wrap: anywhere; } | |
| 83 | + | /* the badge is a live slot, but not a banner */ | |
| 84 | + | & #mr-mergeability:not(:empty) { padding: 0; font-weight: normal; background: none; border: 0; display: grid; gap: var(--s1); } | |
| 85 | + | & .mg-paths { margin: 0; padding-left: 2ch; max-height: 8em; overflow: auto; } | |
| 86 | + | & .mg-paths code { overflow-wrap: anywhere; } | |
| 87 | + | & .d-modes label:has(:disabled) { color: var(--ink-mute); cursor: not-allowed; } | |
| 88 | + | & .d-modes label:has(:disabled):hover { background: none; } | |
| 89 | + | & .btn:disabled { opacity: .5; cursor: not-allowed; } | |
| 90 | + | } | |
| 91 | + | .badge[data-state] { display: inline-block; padding: 0 var(--s2); border: 1px solid var(--border-strong); border-radius: var(--r-s); font-weight: 600; color: var(--ink-hi); } | |
| 92 | + | .badge[data-state="up_to_date"] { border-color: var(--teal); color: var(--teal); } | |
| 93 | + | .badge[data-state="behind"] { border-color: var(--teal-2); } | |
| 94 | + | .badge[data-state="conflict"] { border-color: var(--red); color: var(--red); } | |
| 95 | + | .badge[data-state="checking"] { color: var(--ink-dim); border-style: dashed; } | |
| 96 | + | .note[role="status"] { color: var(--ink-dim); } | |
| 82 | 97 | .omenu { | |
| 83 | 98 | & > summary { font-size: 18px; line-height: 1; } | |
| 84 | 99 | & .menu-pop { width: 280px; } | |
M internal/adapters/web/static/js/mr.js +1 -1
| @@ -213,7 +213,7 @@ window.ermiteMR = (() => { | |||
| 213 | 213 | const box = $('input[name="delete_branch"]', m); | |
| 214 | 214 | if (box) box.checked = !box.checked; | |
| 215 | 215 | } else { | |
| 216 | - | radios[act.mode].checked = true; | |
| 216 | + | if (!radios[act.mode].disabled) radios[act.mode].checked = true; | |
| 217 | 217 | } | |
| 218 | 218 | $('[data-default]', m)?.focus(); | |
| 219 | 219 | return true; | |
M internal/adapters/web/web.go +4 -0
| @@ -76,6 +76,10 @@ type Config struct { | |||
| 76 | 76 | // leads to it. Without it there is neither. Leave it unset rather than | |
| 77 | 77 | // assigning a typed nil pointer. | |
| 78 | 78 | Inbox ports.InboxService | |
| 79 | + | // SigningKey tells the key that signs the squash and merge commits, shown | |
| 80 | + | // in the merge box. Without it the box does not name the key. Leave it | |
| 81 | + | // unset rather than assigning a typed nil pointer. | |
| 82 | + | SigningKey ports.SigningKeyService | |
| 79 | 83 | // Runs serves the CI pages (/<repo>/actions) and the checks shown next to | |
| 80 | 84 | // commits and merge requests. Without it there are no such pages. Leave it | |
| 81 | 85 | // unset rather than assigning a typed nil pointer. | |
M internal/app/cli_mr.go +143 -19
| @@ -8,6 +8,7 @@ import ( | |||
| 8 | 8 | "fmt" | |
| 9 | 9 | "io" | |
| 10 | 10 | "net/url" | |
| 11 | + | "slices" | |
| 11 | 12 | "strconv" | |
| 12 | 13 | "strings" | |
| 13 | 14 | ||
| @@ -122,6 +123,71 @@ type jsonMergeRequest struct { | |||
| 122 | 123 | MergeMode string `json:"merge_mode"` | |
| 123 | 124 | } | |
| 124 | 125 | ||
| 126 | + | // jsonMergeability is the stable JSON shape of the mergeability of an open | |
| 127 | + | // merge request. | |
| 128 | + | type jsonMergeability struct { | |
| 129 | + | // State is "checking", "up_to_date", "behind" or "conflict". | |
| 130 | + | State string `json:"state"` | |
| 131 | + | // Behind counts the commits of the target the head lacks (behind only); | |
| 132 | + | // BehindCapped says it is a lower bound. | |
| 133 | + | Behind int32 `json:"behind"` | |
| 134 | + | BehindCapped bool `json:"behind_capped"` | |
| 135 | + | ConflictPaths []string `json:"conflict_paths"` | |
| 136 | + | // ConflictPathsTruncated says that more paths conflict than listed. | |
| 137 | + | ConflictPathsTruncated bool `json:"conflict_paths_truncated"` | |
| 138 | + | TargetID string `json:"target_id"` | |
| 139 | + | HeadID string `json:"head_id"` | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | func mergeabilityName(s ermite.MergeabilityState) string { | |
| 143 | + | switch s { | |
| 144 | + | case ermite.MergeabilityState_MERGEABILITY_STATE_CHECKING: | |
| 145 | + | return "checking" | |
| 146 | + | case ermite.MergeabilityState_MERGEABILITY_STATE_UP_TO_DATE: | |
| 147 | + | return "up_to_date" | |
| 148 | + | case ermite.MergeabilityState_MERGEABILITY_STATE_BEHIND: | |
| 149 | + | return "behind" | |
| 150 | + | case ermite.MergeabilityState_MERGEABILITY_STATE_CONFLICT: | |
| 151 | + | return "conflict" | |
| 152 | + | } | |
| 153 | + | return "" | |
| 154 | + | } | |
| 155 | + | ||
| 156 | + | // toJSONMergeability maps a mergeability; nil when the server sent none. | |
| 157 | + | func toJSONMergeability(m *ermite.Mergeability) *jsonMergeability { | |
| 158 | + | if m == nil { | |
| 159 | + | return nil | |
| 160 | + | } | |
| 161 | + | return &jsonMergeability{ | |
| 162 | + | State: mergeabilityName(m.GetState()), Behind: m.GetBehind(), BehindCapped: m.GetBehindCapped(), | |
| 163 | + | ConflictPaths: append([]string{}, m.GetConflictPaths()...), ConflictPathsTruncated: m.GetConflictPathsTruncated(), | |
| 164 | + | TargetID: m.GetTargetId(), HeadID: m.GetHeadId(), | |
| 165 | + | } | |
| 166 | + | } | |
| 167 | + | ||
| 168 | + | // mergeabilityText is the human form of a mergeability: "up to date", | |
| 169 | + | // "behind by 2", "conflict (a, b, ...)", "checking". | |
| 170 | + | func mergeabilityText(m *jsonMergeability) string { | |
| 171 | + | switch m.State { | |
| 172 | + | case "up_to_date": | |
| 173 | + | return "up to date with the target, can be fast-forwarded" | |
| 174 | + | case "behind": | |
| 175 | + | return fmt.Sprintf("behind by %d%s, merges without conflicts (squash or merge commit; fast-forward needs a rebase)", m.Behind, plus(m.BehindCapped)) | |
| 176 | + | case "conflict": | |
| 177 | + | if len(m.ConflictPaths) == 0 { | |
| 178 | + | return "conflict (no common history with the target), rebase needed" | |
| 179 | + | } | |
| 180 | + | more := "" | |
| 181 | + | if m.ConflictPathsTruncated { | |
| 182 | + | more = ", ..." | |
| 183 | + | } | |
| 184 | + | return "conflict (" + strings.Join(m.ConflictPaths, ", ") + more + "), rebase needed" | |
| 185 | + | case "checking": | |
| 186 | + | return "checking" | |
| 187 | + | } | |
| 188 | + | return m.State | |
| 189 | + | } | |
| 190 | + | ||
| 125 | 191 | // jsonMerged is the output of `mr merge --json`. | |
| 126 | 192 | type jsonMerged struct { | |
| 127 | 193 | jsonMergeRequest | |
| @@ -178,9 +244,15 @@ const jsonMRHelp = ` | |||
| 178 | 244 | JSON fields of a merge request (--json): repo, url (full web URL), number, title, description, source_branch, | |
| 179 | 245 | target_branch, state ("open", "merged" or "closed"), author (token label), created_at and | |
| 180 | 246 | updated_at (RFC 3339 UTC), head_id (the commit the source branch points to; empty when the | |
| 181 | - | branch is gone), merged_commit_id and merge_mode ("fast-forward" or "squash"; both empty | |
| 247 | + | branch is gone), merged_commit_id and merge_mode ("fast-forward", "squash" or "merge"; both empty | |
| 182 | 248 | unless merged).` | |
| 183 | 249 | ||
| 250 | + | const jsonMergeabilityHelp = ` | |
| 251 | + | mr show --json adds mergeability, null unless the request is open: state ("checking", "up_to_date", | |
| 252 | + | "behind" or "conflict"), behind and behind_capped (commits of the target the head lacks; behind | |
| 253 | + | only), conflict_paths and conflict_paths_truncated (conflict only), target_id and head_id (the | |
| 254 | + | commits the result is for).` | |
| 255 | + | ||
| 184 | 256 | const jsonCommentHelp = ` | |
| 185 | 257 | JSON fields of a comment: id, author, path, line and side ("old" or "new"; empty path, line 0 | |
| 186 | 258 | and empty side for a general comment), commit_id (the head when it was written), text, | |
| @@ -417,6 +489,8 @@ type jsonCommit struct { | |||
| 417 | 489 | // jsonMRDetail is the stable JSON shape of `mr show --json`. | |
| 418 | 490 | type jsonMRDetail struct { | |
| 419 | 491 | jsonMergeRequest | |
| 492 | + | // Mergeability is null unless the request is open and the server computes it. | |
| 493 | + | Mergeability *jsonMergeability `json:"mergeability"` | |
| 420 | 494 | Commits []jsonCommit `json:"commits"` | |
| 421 | 495 | CommitsTruncated bool `json:"commits_truncated"` | |
| 422 | 496 | DiffBaseID string `json:"diff_base_id"` | |
| @@ -449,6 +523,7 @@ func lineKindName(k ermite.LineKind) string { | |||
| 449 | 523 | func toJSONDetail(d *ermite.GetMergeRequestResponse, withDiff bool, baseURL string) jsonMRDetail { | |
| 450 | 524 | out := jsonMRDetail{ | |
| 451 | 525 | jsonMergeRequest: toJSONMergeRequest(d.GetMergeRequest(), baseURL), | |
| 526 | + | Mergeability: toJSONMergeability(d.GetMergeRequest().GetMergeability()), | |
| 452 | 527 | Commits: []jsonCommit{}, CommitsTruncated: d.GetCommitsTruncated(), | |
| 453 | 528 | DiffBaseID: d.GetDiffBaseId(), | |
| 454 | 529 | Stats: jsonStats{ | |
| @@ -501,7 +576,9 @@ func newMRShowCmd(env *cliEnv) *cobra.Command { | |||
| 501 | 576 | "--since-last-read shows the diff since the head recorded by --mark-read (or by the web) instead of\n" + | |
| 502 | 577 | "the whole change; after a force-push it still works when the recorded commit exists, else the\n" + | |
| 503 | 578 | "whole change is shown and the output says so. --mark-read records the head shown as read; it needs\n" + | |
| 504 | - | "the mr:write scope." + jsonMRHelp + jsonCommentHelp + "\n" + | |
| 579 | + | "the mr:write scope. The mergeability line says whether the branch merges with its target now: up to\n" + | |
| 580 | + | "date (can be fast-forwarded), behind by N commits (merges without conflicts, squash or merge commit),\n" + | |
| 581 | + | "or in conflict with the paths that conflict (rebase needed)." + jsonMRHelp + jsonMergeabilityHelp + jsonCommentHelp + "\n" + | |
| 505 | 582 | "--json adds to the merge request fields: commits (id, author, summary), commits_truncated,\n" + | |
| 506 | 583 | "diff_base_id (the commit the diff starts from), stats (files_changed, additions, deletions),\n" + | |
| 507 | 584 | "diff_truncated, files (old_path, new_path, status, additions, deletions, binary, truncated and,\n" + | |
| @@ -574,6 +651,9 @@ func printMRDetail(w io.Writer, d *ermite.GetMergeRequestResponse, withDiff, sin | |||
| 574 | 651 | if m.GetState() == ermite.MergeRequestState_MERGE_REQUEST_STATE_MERGED { | |
| 575 | 652 | pairs = append(pairs, [2]string{"merged", orDash(m.GetMergedCommitId()) + " (" + modeName(m.GetMergeMode()) + ")"}) | |
| 576 | 653 | } | |
| 654 | + | if mg := toJSONMergeability(m.GetMergeability()); mg != nil && m.GetState() == ermite.MergeRequestState_MERGE_REQUEST_STATE_OPEN { | |
| 655 | + | pairs = append(pairs, [2]string{"mergeability", mergeabilityText(mg)}) | |
| 656 | + | } | |
| 577 | 657 | if len(d.GetVerdicts()) > 0 { | |
| 578 | 658 | var verdicts []string | |
| 579 | 659 | for _, v := range d.GetVerdicts() { | |
| @@ -962,37 +1042,52 @@ func newMRResolveCmd(env *cliEnv) *cobra.Command { | |||
| 962 | 1042 | } | |
| 963 | 1043 | ||
| 964 | 1044 | func newMRMergeCmd(env *cliEnv) *cobra.Command { | |
| 965 | - | var squash, deleteBranch bool | |
| 1045 | + | var mode string | |
| 1046 | + | var deleteBranch bool | |
| 966 | 1047 | var head string | |
| 967 | 1048 | cmd := &cobra.Command{ | |
| 968 | 1049 | Use: "merge <repo> <number>", | |
| 969 | 1050 | Short: "Merge a merge request", | |
| 970 | - | Long: "Merge an open merge request. The default is a fast-forward: the target branch moves to the head\n" + | |
| 971 | - | "and no commit is created. --squash makes one new commit with the tree of the head and the target\n" + | |
| 972 | - | "as only parent, signed by the server's key (see `ermite signing-key show`). The target must be an\n" + | |
| 973 | - | "ancestor of the head, else rebase the branch and push it again; the repository settings may allow\n" + | |
| 974 | - | "only one of the modes.\n\n" + | |
| 1051 | + | Long: "Merge an open merge request with --mode (default fast-forward; the repository settings may allow\n" + | |
| 1052 | + | "only some modes, see `ermite repo edit --merge-modes`):\n" + | |
| 1053 | + | " fast-forward the target branch moves to the head and no commit is created. The target must be\n" + | |
| 1054 | + | " an ancestor of the head, else rebase the branch and push it again, or use another mode.\n" + | |
| 1055 | + | " squash one new commit with the tree of the merge of the head and the target, the target tip\n" + | |
| 1056 | + | " as only parent. The branch must merge without conflicts.\n" + | |
| 1057 | + | " merge a merge commit with two parents, the target tip and the head. The branch must merge\n" + | |
| 1058 | + | " without conflicts.\n" + | |
| 1059 | + | "A squash and a merge commit are signed with the server's own key, generated automatically when the\n" + | |
| 1060 | + | "server first starts; `ermite signing-key show` prints it. The server never rebases. `ermite mr show`\n" + | |
| 1061 | + | "prints the mergeability (up to date, behind, conflict); the server checks again when merging and\n" + | |
| 1062 | + | "refuses a merge that is not possible, saying why.\n\n" + | |
| 975 | 1063 | "The server merges only if the source branch still points to the head you reviewed, so --head is\n" + | |
| 976 | 1064 | "required: give the full id of the head that `ermite mr show <repo> <number>` printed when you\n" + | |
| 977 | 1065 | "reviewed the change. If the branch moved since, the merge is refused: review again. Needs the\n" + | |
| 978 | 1066 | "mr:merge scope.\n\n" + | |
| 979 | - | "--delete-branch asks the server to delete the source branch right after the merge, in the\n" + | |
| 980 | - | "background. The merge is refused when the branch is the default branch or used by another open\n" + | |
| 981 | - | "merge request. If the deletion later fails (the branch moved, say), the merge stands and the\n" + | |
| 982 | - | "branch is kept." + jsonMRHelp + | |
| 1067 | + | "The merge answers as soon as the target branch has moved. --delete-branch asks the server to delete\n" + | |
| 1068 | + | "the source branch right after, in the background. The merge is refused when the branch is the\n" + | |
| 1069 | + | "default branch or used by another open merge request. If the deletion later fails (the branch\n" + | |
| 1070 | + | "moved, say), the merge stands and the branch is kept." + jsonMRHelp + | |
| 983 | 1071 | "\n--json adds source_branch_deleted (true when the deletion was requested: the server deletes the branch right after the merge).", | |
| 984 | 1072 | Args: cobra.ExactArgs(2), | |
| 985 | 1073 | ValidArgsFunction: completeMRRepoThenNumber(env), | |
| 986 | 1074 | } | |
| 987 | - | cmd.Flags().BoolVar(&squash, "squash", false, "squash the branch into one signed commit instead of a fast-forward") | |
| 1075 | + | cmd.Flags().StringVar(&mode, "mode", modeFastForward, "merge mode: fast-forward, squash or merge (a merge commit)") | |
| 988 | 1076 | cmd.Flags().StringVar(&head, "head", "", "full id of the head you reviewed, from `ermite mr show` (required)") | |
| 989 | 1077 | cmd.Flags().BoolVar(&deleteBranch, "delete-branch", false, "delete the source branch after the merge") | |
| 1078 | + | _ = cmd.RegisterFlagCompletionFunc("mode", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { | |
| 1079 | + | return []string{modeFastForward, modeSquash, modeMerge}, cobra.ShellCompDirectiveNoFileComp | |
| 1080 | + | }) | |
| 990 | 1081 | asJSON := addJSONFlag(cmd) | |
| 991 | 1082 | cmd.RunE = func(cmd *cobra.Command, args []string) error { | |
| 992 | 1083 | n, err := parseNumber(args[1]) | |
| 993 | 1084 | if err != nil { | |
| 994 | 1085 | return err | |
| 995 | 1086 | } | |
| 1087 | + | pbMode, err := parseMode(mode) | |
| 1088 | + | if err != nil { | |
| 1089 | + | return err | |
| 1090 | + | } | |
| 996 | 1091 | head = strings.TrimSpace(head) | |
| 997 | 1092 | if head == "" { | |
| 998 | 1093 | return fmt.Errorf("--head is required: run `ermite mr show %s %d` and give the head id it prints, "+ | |
| @@ -1003,12 +1098,13 @@ func newMRMergeCmd(env *cliEnv) *cobra.Command { | |||
| 1003 | 1098 | return err | |
| 1004 | 1099 | } | |
| 1005 | 1100 | ctx := env.ctx(cmd) | |
| 1006 | - | mode := ermite.MergeMode_MERGE_MODE_FAST_FORWARD | |
| 1007 | - | if squash { | |
| 1008 | - | mode = ermite.MergeMode_MERGE_MODE_SQUASH | |
| 1101 | + | if pbMode == ermite.MergeMode_MERGE_MODE_MERGE_COMMIT { | |
| 1102 | + | if err := requireFeature(ctx, c, client.FeatureMergeability, "has no merge commit mode (--mode merge)"); err != nil { | |
| 1103 | + | return err | |
| 1104 | + | } | |
| 1009 | 1105 | } | |
| 1010 | 1106 | res, err := c.MergeRequests().MergeMergeRequest(ctx, connect.NewRequest(&ermite.MergeMergeRequestRequest{ | |
| 1011 | - | Repo: args[0], Number: n, Mode: mode, HeadId: head, DeleteSourceBranch: deleteBranch, | |
| 1107 | + | Repo: args[0], Number: n, Mode: pbMode, HeadId: head, DeleteSourceBranch: deleteBranch, | |
| 1012 | 1108 | })) | |
| 1013 | 1109 | if err != nil { | |
| 1014 | 1110 | return c.Describe(err) | |
| @@ -1018,16 +1114,44 @@ func newMRMergeCmd(env *cliEnv) *cobra.Command { | |||
| 1018 | 1114 | if *asJSON { | |
| 1019 | 1115 | return writeJSON(cmd.OutOrStdout(), jsonMerged{jsonMergeRequest: toJSONMergeRequest(m, c.URL()), SourceBranchDeleted: deleted}) | |
| 1020 | 1116 | } | |
| 1021 | - | _, err = fmt.Fprintf(cmd.OutOrStdout(), "merged %s!%d into %s (%s) at %s\n", clean(m.GetRepo()), m.GetNumber(), | |
| 1117 | + | out := cmd.OutOrStdout() | |
| 1118 | + | _, err = fmt.Fprintf(out, "merged %s!%d into %s (%s) at %s\n", clean(m.GetRepo()), m.GetNumber(), | |
| 1022 | 1119 | clean(m.GetTargetBranch()), modeName(m.GetMergeMode()), shortID(m.GetMergedCommitId())) | |
| 1120 | + | if err == nil && pbMode != ermite.MergeMode_MERGE_MODE_FAST_FORWARD { | |
| 1121 | + | _, err = fmt.Fprintln(out, signedNote(ctx, c)) | |
| 1122 | + | } | |
| 1023 | 1123 | if err == nil && deleted { | |
| 1024 | - | _, err = fmt.Fprintf(cmd.OutOrStdout(), "deleting branch %s (done by the server after the merge)\n", clean(m.GetSourceBranch())) | |
| 1124 | + | _, err = fmt.Fprintf(out, "deleting branch %s (done by the server after the merge)\n", clean(m.GetSourceBranch())) | |
| 1025 | 1125 | } | |
| 1026 | 1126 | return err | |
| 1027 | 1127 | } | |
| 1028 | 1128 | return cmd | |
| 1029 | 1129 | } | |
| 1030 | 1130 | ||
| 1131 | + | // signedNote says that a squash or merge commit is signed by the server's | |
| 1132 | + | // key, with its fingerprint when the server tells it. | |
| 1133 | + | func signedNote(ctx context.Context, c *client.Client) string { | |
| 1134 | + | const how = "; `ermite signing-key show` prints the key to verify it" | |
| 1135 | + | res, err := c.Server().GetSigningKey(ctx, connect.NewRequest(&ermite.GetSigningKeyRequest{})) | |
| 1136 | + | if err != nil || res.Msg.GetFingerprint() == "" { | |
| 1137 | + | return "the commit is signed by the server's key" + how | |
| 1138 | + | } | |
| 1139 | + | return "the commit is signed by the server's key " + clean(res.Msg.GetFingerprint()) + how | |
| 1140 | + | } | |
| 1141 | + | ||
| 1142 | + | // requireFeature refuses to go on with a server that does not list the | |
| 1143 | + | // feature. A server whose features cannot be read is refused too. | |
| 1144 | + | func requireFeature(ctx context.Context, c *client.Client, feature, what string) error { | |
| 1145 | + | res, err := c.Server().GetVersion(ctx, connect.NewRequest(&ermite.GetVersionRequest{})) | |
| 1146 | + | if err != nil { | |
| 1147 | + | return c.Describe(err) | |
| 1148 | + | } | |
| 1149 | + | if !slices.Contains(res.Msg.GetFeatures(), feature) { | |
| 1150 | + | return fmt.Errorf("the server does not support %s, so it %s (upgrade the server, see `ermite version`)", feature, what) | |
| 1151 | + | } | |
| 1152 | + | return nil | |
| 1153 | + | } | |
| 1154 | + | ||
| 1031 | 1155 | func newMRCloseCmd(env *cliEnv) *cobra.Command { | |
| 1032 | 1156 | cmd := &cobra.Command{ | |
| 1033 | 1157 | Use: "close <repo> <number>", | |
M internal/app/cli_mr_e2e_test.go +5 -5
| @@ -260,19 +260,19 @@ func TestMergeRequestsCLIEndToEnd(t *testing.T) { | |||
| 260 | 260 | require.Error(t, res.err) | |
| 261 | 261 | assert.Contains(t, res.err.Error(), "precondition failed") | |
| 262 | 262 | ||
| 263 | - | // Squash, with --squash. Without --head the merge is refused before it reaches the server. | |
| 263 | + | // Squash, with --mode squash. Without --head the merge is refused before it reaches the server. | |
| 264 | 264 | c.mustGit(wt, "checkout", "-q", "-b", "feat/two", head3) | |
| 265 | 265 | c.mustGit(wt, "-c", "user.name=Cy", "-c", "user.email=cy@example.org", "commit", "-q", "--allow-empty", "-m", "empty by Cy") | |
| 266 | 266 | c.writeFile(wt, "two.txt", []byte("two\n")) | |
| 267 | 267 | c.mustGit(wt, "push", "-q", url, "feat/two") | |
| 268 | 268 | c.cli(t, secret, "mr", "create", "proj", "--source", "feat/two", "--title", "Second").ok(t) | |
| 269 | 269 | w.nextOfType(t, "merge_request_opened") | |
| 270 | - | _, stderr, code = c.bin(t, secret, "mr", "merge", "proj", "2", "--squash") | |
| 270 | + | _, stderr, code = c.bin(t, secret, "mr", "merge", "proj", "2", "--mode", "squash") | |
| 271 | 271 | requireOneLineError(t, stderr, code, "--head is required", "ermite mr show proj 2") | |
| 272 | 272 | head4 := c.bareGit("proj", "rev-parse", "feat/two") | |
| 273 | - | res = c.cli(t, secret, "mr", "merge", "proj", "2", "--squash", "--head", head4) | |
| 273 | + | res = c.cli(t, secret, "mr", "merge", "proj", "2", "--mode", "squash", "--head", head4) | |
| 274 | 274 | require.NoError(t, res.err, res.stderr) | |
| 275 | - | assert.Regexp(t, `^merged proj!2 into main \(squash\) at [0-9a-f]{10}\n$`, res.stdout) | |
| 275 | + | assert.Regexp(t, `^merged proj!2 into main \(squash\) at [0-9a-f]{10}\nthe commit is signed by the server's key SHA256:\S+; .ermite signing-key show. prints the key to verify it\n$`, res.stdout) | |
| 276 | 276 | sq := c.bareGit("proj", "rev-parse", "main") | |
| 277 | 277 | assert.Equal(t, head3, c.bareGit("proj", "rev-parse", sq+"^")) | |
| 278 | 278 | assert.Equal(t, "Second (!2)", c.bareGit("proj", "log", "-1", "--format=%s", sq)) | |
| @@ -338,7 +338,7 @@ func TestSigningKeyShowVerifiesASquashCommit(t *testing.T) { | |||
| 338 | 338 | c.writeFile(wt, "x.txt", []byte("x\n")) | |
| 339 | 339 | c.mustGit(wt, "push", "-q", url, "main", "feat") | |
| 340 | 340 | c.cli(t, adminToken, "mr", "create", "proj", "--source", "feat", "--title", "Add x").ok(t) | |
| 341 | - | res := c.cli(t, adminToken, "mr", "merge", "proj", "1", "--squash", "--head", c.bareGit("proj", "rev-parse", "feat")) | |
| 341 | + | res := c.cli(t, adminToken, "mr", "merge", "proj", "1", "--mode", "squash", "--head", c.bareGit("proj", "rev-parse", "feat")) | |
| 342 | 342 | require.NoError(t, res.err, res.stderr) | |
| 343 | 343 | sq := c.bareGit("proj", "rev-parse", "main") | |
| 344 | 344 | ||
M internal/app/cli_mr_test.go +80 -4
| @@ -3,6 +3,8 @@ | |||
| 3 | 3 | package app | |
| 4 | 4 | ||
| 5 | 5 | import ( | |
| 6 | + | "encoding/json" | |
| 7 | + | "regexp" | |
| 6 | 8 | "strings" | |
| 7 | 9 | "testing" | |
| 8 | 10 | ||
| @@ -174,6 +176,53 @@ func TestMRShow(t *testing.T) { | |||
| 174 | 176 | assert.Contains(t, res.stderr, "head you last read is gone") | |
| 175 | 177 | } | |
| 176 | 178 | ||
| 179 | + | // The mergeability of an open request is a line of `mr show` and a field of | |
| 180 | + | // its JSON. | |
| 181 | + | func TestMRShowMergeability(t *testing.T) { | |
| 182 | + | withState := func(mg *ermite.Mergeability) *ermite.GetMergeRequestResponse { | |
| 183 | + | m := proto.Clone(mrOne).(*ermite.MergeRequest) | |
| 184 | + | m.Mergeability = mg | |
| 185 | + | return &ermite.GetMergeRequestResponse{MergeRequest: m} | |
| 186 | + | } | |
| 187 | + | var cur *ermite.GetMergeRequestResponse | |
| 188 | + | f := &fakeAPI{getMR: func(*ermite.GetMergeRequestRequest) (*ermite.GetMergeRequestResponse, error) { return cur, nil }} | |
| 189 | + | url := f.serve(t) | |
| 190 | + | for _, tt := range []struct { | |
| 191 | + | mg *ermite.Mergeability | |
| 192 | + | line string | |
| 193 | + | }{ | |
| 194 | + | {&ermite.Mergeability{State: ermite.MergeabilityState_MERGEABILITY_STATE_UP_TO_DATE}, "up to date"}, | |
| 195 | + | {&ermite.Mergeability{State: ermite.MergeabilityState_MERGEABILITY_STATE_BEHIND, Behind: 1000, BehindCapped: true}, "behind by 1000+"}, | |
| 196 | + | {&ermite.Mergeability{State: ermite.MergeabilityState_MERGEABILITY_STATE_CONFLICT, ConflictPaths: []string{"a.txt", "b/c.go"}, ConflictPathsTruncated: true}, "conflict (a.txt, b/c.go, ...)"}, | |
| 197 | + | {&ermite.Mergeability{State: ermite.MergeabilityState_MERGEABILITY_STATE_CHECKING}, "checking"}, | |
| 198 | + | } { | |
| 199 | + | cur = withState(tt.mg) | |
| 200 | + | res := runCLI(t, envFor(url), "", "mr", "show", "alpha", "1").ok(t) | |
| 201 | + | assert.Regexp(t, `(?m)^mergeability\s+.*`+regexp.QuoteMeta(tt.line), res.stdout) | |
| 202 | + | } | |
| 203 | + | cur = withState(&ermite.Mergeability{State: ermite.MergeabilityState_MERGEABILITY_STATE_BEHIND, Behind: 2, TargetId: headA}) | |
| 204 | + | res := runCLI(t, envFor(url), "", "mr", "show", "alpha", "1", "--json").ok(t) | |
| 205 | + | var got struct { | |
| 206 | + | Mergeability *struct { | |
| 207 | + | State string `json:"state"` | |
| 208 | + | Behind int `json:"behind"` | |
| 209 | + | TargetID string `json:"target_id"` | |
| 210 | + | } `json:"mergeability"` | |
| 211 | + | } | |
| 212 | + | require.NoError(t, json.Unmarshal([]byte(res.stdout), &got)) | |
| 213 | + | require.NotNil(t, got.Mergeability) | |
| 214 | + | assert.Equal(t, "behind", got.Mergeability.State) | |
| 215 | + | assert.Equal(t, 2, got.Mergeability.Behind) | |
| 216 | + | assert.Equal(t, headA, got.Mergeability.TargetID) | |
| 217 | + | ||
| 218 | + | // No mergeability (a merged request, or an older server): no line, null. | |
| 219 | + | cur = withState(nil) | |
| 220 | + | res = runCLI(t, envFor(url), "", "mr", "show", "alpha", "1").ok(t) | |
| 221 | + | assert.NotContains(t, res.stdout, "mergeability") | |
| 222 | + | res = runCLI(t, envFor(url), "", "mr", "show", "alpha", "1", "--json").ok(t) | |
| 223 | + | assert.Regexp(t, `"mergeability":\s*null`, res.stdout) | |
| 224 | + | } | |
| 225 | + | ||
| 177 | 226 | func TestMRShowMarkRead(t *testing.T) { | |
| 178 | 227 | f := &fakeAPI{ | |
| 179 | 228 | getMR: func(*ermite.GetMergeRequestRequest) (*ermite.GetMergeRequestResponse, error) { | |
| @@ -252,6 +301,12 @@ func TestMRResolveMergeAndClose(t *testing.T) { | |||
| 252 | 301 | closeMR: func(*ermite.CloseMergeRequestRequest) (*ermite.CloseMergeRequestResponse, error) { | |
| 253 | 302 | return &ermite.CloseMergeRequestResponse{MergeRequest: closed}, nil | |
| 254 | 303 | }, | |
| 304 | + | getSigningKey: func(*ermite.GetSigningKeyRequest) (*ermite.GetSigningKeyResponse, error) { | |
| 305 | + | return &ermite.GetSigningKeyResponse{Fingerprint: "SHA256:abc"}, nil | |
| 306 | + | }, | |
| 307 | + | getVersion: func(*ermite.GetVersionRequest) (*ermite.GetVersionResponse, error) { | |
| 308 | + | return &ermite.GetVersionResponse{Version: "test", Features: []string{"merge_requests.mergeability"}}, nil | |
| 309 | + | }, | |
| 255 | 310 | } | |
| 256 | 311 | url := f.serve(t) | |
| 257 | 312 | ||
| @@ -265,16 +320,37 @@ func TestMRResolveMergeAndClose(t *testing.T) { | |||
| 265 | 320 | assert.False(t, reqs[1].(*ermite.ResolveCommentRequest).GetResolved()) | |
| 266 | 321 | ||
| 267 | 322 | // Merge with an explicit head and mode. | |
| 268 | - | res = runCLI(t, envFor(url), "", "mr", "merge", "alpha", "1", "--squash", "--head", headA).ok(t) | |
| 269 | - | assert.Equal(t, "merged alpha!1 into main (squash) at dddddddddd\n", res.stdout) | |
| 323 | + | res = runCLI(t, envFor(url), "", "mr", "merge", "alpha", "1", "--mode", "squash", "--head", headA).ok(t) | |
| 324 | + | assert.Equal(t, "merged alpha!1 into main (squash) at dddddddddd\n"+ | |
| 325 | + | "the commit is signed by the server's key SHA256:abc; `ermite signing-key show` prints the key to verify it\n", res.stdout) | |
| 270 | 326 | assert.Empty(t, res.stderr) | |
| 271 | 327 | calls, reqs := f.snapshot() | |
| 272 | - | assert.Equal(t, []string{"ResolveComment", "ResolveComment", "MergeMergeRequest"}, calls, "an explicit head needs no read first") | |
| 328 | + | assert.Equal(t, []string{"ResolveComment", "ResolveComment", "MergeMergeRequest", "GetSigningKey"}, calls, "an explicit head needs no read first; the signing key is for the note") | |
| 273 | 329 | m := reqs[2].(*ermite.MergeMergeRequestRequest) | |
| 274 | 330 | assert.Equal(t, ermite.MergeMode_MERGE_MODE_SQUASH, m.GetMode()) | |
| 275 | 331 | assert.Equal(t, headA, m.GetHeadId()) | |
| 276 | 332 | assert.EqualValues(t, 1, m.GetNumber()) | |
| 277 | 333 | ||
| 334 | + | // A merge commit, and a mode that does not exist. | |
| 335 | + | merged.MergeMode = ermite.MergeMode_MERGE_MODE_MERGE_COMMIT | |
| 336 | + | res = runCLI(t, envFor(url), "", "mr", "merge", "alpha", "1", "--mode", "merge", "--head", headA).ok(t) | |
| 337 | + | assert.Contains(t, res.stdout, "merged alpha!1 into main (merge) at dddddddddd\n") | |
| 338 | + | assert.Contains(t, res.stdout, "signed by the server's key SHA256:abc") | |
| 339 | + | _, reqs = f.snapshot() | |
| 340 | + | var lastMerge *ermite.MergeMergeRequestRequest | |
| 341 | + | for _, r := range reqs { | |
| 342 | + | if mr, ok := r.(*ermite.MergeMergeRequestRequest); ok { | |
| 343 | + | lastMerge = mr | |
| 344 | + | } | |
| 345 | + | } | |
| 346 | + | require.NotNil(t, lastMerge) | |
| 347 | + | assert.Equal(t, ermite.MergeMode_MERGE_MODE_MERGE_COMMIT, lastMerge.GetMode()) | |
| 348 | + | bad := runCLI(t, envFor(url), "", "mr", "merge", "alpha", "1", "--mode", "rebase", "--head", headA) | |
| 349 | + | require.Error(t, bad.err) | |
| 350 | + | assert.Contains(t, bad.err.Error(), "invalid merge mode") | |
| 351 | + | assert.Error(t, runCLI(t, envFor(url), "", "mr", "merge", "alpha", "1", "--squash", "--head", headA).err, "--squash is gone") | |
| 352 | + | merged.MergeMode = ermite.MergeMode_MERGE_MODE_SQUASH | |
| 353 | + | ||
| 278 | 354 | // The default mode is a fast-forward. | |
| 279 | 355 | res = runCLI(t, envFor(url), "", "mr", "merge", "alpha", "1", "--head", headA, "--json").ok(t) | |
| 280 | 356 | assert.Equal(t, "merged", decode[mrOut](t, res.stdout).State) | |
| @@ -299,7 +375,7 @@ func TestMRMergeNeedsTheHead(t *testing.T) { | |||
| 299 | 375 | url := f.serve(t) | |
| 300 | 376 | for _, args := range [][]string{ | |
| 301 | 377 | {"mr", "merge", "alpha", "1"}, | |
| 302 | - | {"mr", "merge", "alpha", "1", "--squash"}, | |
| 378 | + | {"mr", "merge", "alpha", "1", "--mode", "squash"}, | |
| 303 | 379 | {"mr", "merge", "alpha", "1", "--head", " "}, | |
| 304 | 380 | } { | |
| 305 | 381 | res := runCLI(t, envFor(url), "", args...) | |
M internal/app/cli_repo.go +9 -7
| @@ -33,7 +33,7 @@ type jsonRepo struct { | |||
| 33 | 33 | DefaultBranch string `json:"default_branch"` | |
| 34 | 34 | CreatedAt *string `json:"created_at"` | |
| 35 | 35 | PushedAt *string `json:"pushed_at"` | |
| 36 | - | // MergeModes are the allowed merge modes ("fast-forward", "squash"). | |
| 36 | + | // MergeModes are the allowed merge modes ("fast-forward", "squash", "merge"). | |
| 37 | 37 | MergeModes []string `json:"merge_modes"` | |
| 38 | 38 | FailingChecksBlock bool `json:"failing_checks_block"` | |
| 39 | 39 | // Issues is the issue mode ("off", "owner" or "public"). | |
| @@ -114,8 +114,8 @@ func parseVisibility(s string) (ermite.Visibility, error) { | |||
| 114 | 114 | const jsonRepoHelp = ` | |
| 115 | 115 | JSON fields of a repository (--json): name, description, visibility ("public" or | |
| 116 | 116 | "private"), default_branch, created_at and pushed_at (RFC 3339 UTC; pushed_at is | |
| 117 | - | null until the first push), merge_modes (array of the allowed merge modes, "fast-forward" and/or | |
| 118 | - | "squash"), failing_checks_block (whether a failing check prevents a merge) and issues (the issue | |
| 117 | + | null until the first push), merge_modes (array of the allowed merge modes, "fast-forward", "squash" and/or | |
| 118 | + | "merge"), failing_checks_block (whether a failing check prevents a merge) and issues (the issue | |
| 119 | 119 | mode: "off", "owner" or "public").` | |
| 120 | 120 | ||
| 121 | 121 | func newRepoCmd(env *cliEnv) *cobra.Command { | |
| @@ -315,7 +315,9 @@ func newRepoEditCmd(env *cliEnv) *cobra.Command { | |||
| 315 | 315 | Short: "Change a repository's description, topics, visibility, default branch, merge settings or issues", | |
| 316 | 316 | Long: "Change the fields given as flags and leave the others alone; --description \"\" clears the\n" + | |
| 317 | 317 | "description and --topics \"\" clears the topics. The default branch must already exist. --merge-modes lists the modes a merge request\n" + | |
| 318 | - | "may be merged with (fast-forward, squash; default for a new repository: both), and\n" + | |
| 318 | + | "may be merged with (fast-forward, squash, merge; every mode is allowed by default, so to disable one,\n" + | |
| 319 | + | "list the others: --merge-modes fast-forward,squash disables the merge commit; squash and merge\n" + | |
| 320 | + | "commits are signed with the server's key, see `ermite signing-key show`), and\n" + | |
| 319 | 321 | "--failing-checks-block says whether a failing check prevents a merge (a failed or unfinished CI check of the head; see README, CI).\n" + | |
| 320 | 322 | "--issues sets who may use issues: off (default), owner (the owner and tokens with issue:write) or\n" + | |
| 321 | 323 | "public (visitors of a public repository may also propose issues through a moderated form).\nNeeds a server with the issues feature.\n" + | |
| @@ -327,7 +329,7 @@ func newRepoEditCmd(env *cliEnv) *cobra.Command { | |||
| 327 | 329 | cmd.Flags().StringSliceVar(&topics, "topics", nil, "new topics, comma-separated (empty clears them)") | |
| 328 | 330 | cmd.Flags().StringVar(&visibility, "visibility", "", "new visibility: public or private") | |
| 329 | 331 | cmd.Flags().StringVar(&defaultBranch, "default-branch", "", "short name of the new default branch") | |
| 330 | - | cmd.Flags().StringSliceVar(&mergeModes, "merge-modes", nil, "allowed merge modes, comma-separated: fast-forward, squash") | |
| 332 | + | cmd.Flags().StringSliceVar(&mergeModes, "merge-modes", nil, "allowed merge modes, comma-separated: fast-forward, squash, merge (all by default; list the ones to keep)") | |
| 331 | 333 | cmd.Flags().BoolVar(&failingChecksBlock, "failing-checks-block", true, "a failing check prevents a merge (--failing-checks-block=false to allow it)") | |
| 332 | 334 | cmd.Flags().StringVar(&issues, "issues", "", "issue mode: off, owner or public") | |
| 333 | 335 | _ = cmd.RegisterFlagCompletionFunc("issues", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { | |
| @@ -335,7 +337,7 @@ func newRepoEditCmd(env *cliEnv) *cobra.Command { | |||
| 335 | 337 | }) | |
| 336 | 338 | _ = cmd.RegisterFlagCompletionFunc("visibility", visibilityCompletion) | |
| 337 | 339 | _ = cmd.RegisterFlagCompletionFunc("merge-modes", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { | |
| 338 | - | return []string{modeFastForward, modeSquash}, cobra.ShellCompDirectiveNoFileComp | |
| 340 | + | return []string{modeFastForward, modeSquash, modeMerge}, cobra.ShellCompDirectiveNoFileComp | |
| 339 | 341 | }) | |
| 340 | 342 | asJSON := addJSONFlag(cmd) | |
| 341 | 343 | cmd.RunE = func(cmd *cobra.Command, args []string) error { | |
| @@ -400,7 +402,7 @@ func buildUpdateRepo(cmd *cobra.Command, name, description string, topics []stri | |||
| 400 | 402 | modes = append(modes, mode) | |
| 401 | 403 | } | |
| 402 | 404 | if len(modes) == 0 { | |
| 403 | - | return nil, errors.New("--merge-modes must name at least one mode: fast-forward, squash") | |
| 405 | + | return nil, errors.New("--merge-modes must name at least one mode: fast-forward, squash, merge") | |
| 404 | 406 | } | |
| 405 | 407 | req.Settings = &ermite.RepoSettings{AllowedMergeModes: modes} | |
| 406 | 408 | paths = append(paths, "settings.allowed_merge_modes") | |
M internal/app/cli_watch.go +26 -4
| @@ -29,8 +29,8 @@ type jsonEvent struct { | |||
| 29 | 29 | MergeRequest *jsonMergeRequest `json:"merge_request,omitempty"` | |
| 30 | 30 | Number int64 `json:"number,omitempty"` | |
| 31 | 31 | Comment *jsonComment `json:"comment,omitempty"` | |
| 32 | - | // Mergeability is the state of a merge_request_mergeability event. | |
| 33 | - | Mergeability string `json:"mergeability,omitempty"` | |
| 32 | + | // Mergeability is set for a merge_request_mergeability event. | |
| 33 | + | Mergeability *jsonMergeability `json:"mergeability,omitempty"` | |
| 34 | 34 | } | |
| 35 | 35 | ||
| 36 | 36 | type jsonRefUpdate struct { | |
| @@ -99,7 +99,7 @@ func toJSONEvent(ev *ermite.Event, baseURL string) (jsonEvent, bool) { | |||
| 99 | 99 | out.Type, out.Repo, out.Number, out.Comment = eventCommentResolved, k.CommentResolved.GetRepo(), k.CommentResolved.GetNumber(), &c | |
| 100 | 100 | case *ermite.Event_MergeRequestMergeability: | |
| 101 | 101 | out.Type, out.Repo, out.Number = eventMRMergeability, k.MergeRequestMergeability.GetRepo(), k.MergeRequestMergeability.GetNumber() | |
| 102 | - | out.Mergeability = k.MergeRequestMergeability.GetMergeability().GetState().String() | |
| 102 | + | out.Mergeability = toJSONMergeability(k.MergeRequestMergeability.GetMergeability()) | |
| 103 | 103 | case *ermite.Event_Gap: | |
| 104 | 104 | out.Type = eventGap | |
| 105 | 105 | default: | |
| @@ -133,13 +133,35 @@ func eventLine(e jsonEvent) string { | |||
| 133 | 133 | case eventCommentAdded, eventCommentEdited, eventCommentDeleted, eventCommentResolved: | |
| 134 | 134 | detail = fmt.Sprintf("%s!%d by %s", e.Repo, e.Number, e.Comment.Author) | |
| 135 | 135 | case eventMRMergeability: | |
| 136 | - | detail = fmt.Sprintf("%s!%d %s", e.Repo, e.Number, e.Mergeability) | |
| 136 | + | detail = fmt.Sprintf("%s!%d mergeability: %s", e.Repo, e.Number, mergeabilityEventText(e.Mergeability)) | |
| 137 | 137 | case eventGap: | |
| 138 | 138 | detail = "events were lost, reload what you display" | |
| 139 | 139 | } | |
| 140 | 140 | return clean(ts + " " + e.Type + " " + detail) | |
| 141 | 141 | } | |
| 142 | 142 | ||
| 143 | + | // mergeabilityEventText is the short form of a mergeability for a watch line: | |
| 144 | + | // "up_to_date", "behind by 2", "conflict (a, b)", "checking". | |
| 145 | + | func mergeabilityEventText(m *jsonMergeability) string { | |
| 146 | + | if m == nil { | |
| 147 | + | return "unknown" | |
| 148 | + | } | |
| 149 | + | switch m.State { | |
| 150 | + | case "behind": | |
| 151 | + | return fmt.Sprintf("behind by %d%s", m.Behind, plus(m.BehindCapped)) | |
| 152 | + | case "conflict": | |
| 153 | + | if len(m.ConflictPaths) == 0 { | |
| 154 | + | return "conflict (no common history)" | |
| 155 | + | } | |
| 156 | + | more := "" | |
| 157 | + | if m.ConflictPathsTruncated { | |
| 158 | + | more = ", ..." | |
| 159 | + | } | |
| 160 | + | return "conflict (" + strings.Join(m.ConflictPaths, ", ") + more + ")" | |
| 161 | + | } | |
| 162 | + | return m.State | |
| 163 | + | } | |
| 164 | + | ||
| 143 | 165 | func refUpdateText(u jsonRefUpdate) string { | |
| 144 | 166 | short := func(id string) string { | |
| 145 | 167 | if len(id) > 8 { | |
M internal/app/cli_watch_test.go +43 -0
| @@ -200,6 +200,49 @@ func TestWatchMergeRequestEvents(t *testing.T) { | |||
| 200 | 200 | assert.Equal(t, "merge_request_closed", decode[eventOut](t, lines[3]).Type) | |
| 201 | 201 | } | |
| 202 | 202 | ||
| 203 | + | // A mergeability event shows the state, with how far behind or the paths. | |
| 204 | + | func TestWatchMergeabilityEvents(t *testing.T) { | |
| 205 | + | mg := func(m *ermite.Mergeability) func(*ermite.Event) { | |
| 206 | + | return func(e *ermite.Event) { | |
| 207 | + | e.Kind = &ermite.Event_MergeRequestMergeability{MergeRequestMergeability: &ermite.MergeRequestMergeability{Repo: "alpha", Number: 1, Mergeability: m}} | |
| 208 | + | } | |
| 209 | + | } | |
| 210 | + | events := []*ermite.WatchResponse{ | |
| 211 | + | evt("1", mg(&ermite.Mergeability{State: ermite.MergeabilityState_MERGEABILITY_STATE_UP_TO_DATE})), | |
| 212 | + | evt("2", mg(&ermite.Mergeability{State: ermite.MergeabilityState_MERGEABILITY_STATE_BEHIND, Behind: 2})), | |
| 213 | + | evt("3", mg(&ermite.Mergeability{State: ermite.MergeabilityState_MERGEABILITY_STATE_CONFLICT, ConflictPaths: []string{"a.txt", "b.txt"}})), | |
| 214 | + | } | |
| 215 | + | f := &fakeAPI{watch: finishing(events)} | |
| 216 | + | url := f.serve(t) | |
| 217 | + | ||
| 218 | + | res := runCLI(t, envFor(url), "", "watch") | |
| 219 | + | require.Error(t, res.err) | |
| 220 | + | assert.Equal(t, ""+ | |
| 221 | + | "2026-10-10T12:00:00Z merge_request_mergeability alpha!1 mergeability: up_to_date\n"+ | |
| 222 | + | "2026-10-10T12:00:00Z merge_request_mergeability alpha!1 mergeability: behind by 2\n"+ | |
| 223 | + | "2026-10-10T12:00:00Z merge_request_mergeability alpha!1 mergeability: conflict (a.txt, b.txt)\n", res.stdout) | |
| 224 | + | ||
| 225 | + | res = runCLI(t, envFor(url), "", "watch", "--json") | |
| 226 | + | lines := strings.Split(strings.TrimSpace(res.stdout), "\n") | |
| 227 | + | require.Len(t, lines, 3) | |
| 228 | + | var ev struct { | |
| 229 | + | Type string `json:"type"` | |
| 230 | + | Number int64 `json:"number"` | |
| 231 | + | Mergeability struct { | |
| 232 | + | State string `json:"state"` | |
| 233 | + | Behind int `json:"behind"` | |
| 234 | + | ConflictPaths []string `json:"conflict_paths"` | |
| 235 | + | } `json:"mergeability"` | |
| 236 | + | } | |
| 237 | + | require.NoError(t, json.Unmarshal([]byte(lines[1]), &ev)) | |
| 238 | + | assert.Equal(t, "merge_request_mergeability", ev.Type) | |
| 239 | + | assert.EqualValues(t, 1, ev.Number) | |
| 240 | + | assert.Equal(t, "behind", ev.Mergeability.State) | |
| 241 | + | assert.Equal(t, 2, ev.Mergeability.Behind) | |
| 242 | + | require.NoError(t, json.Unmarshal([]byte(lines[2]), &ev)) | |
| 243 | + | assert.Equal(t, []string{"a.txt", "b.txt"}, ev.Mergeability.ConflictPaths) | |
| 244 | + | } | |
| 245 | + | ||
| 203 | 246 | func TestNoteWatchEvents(t *testing.T) { | |
| 204 | 247 | note := &ermite.Comment{Id: "4", ReplyTo: "2", StartLine: 10, Line: 24, Verdict: ermite.Verdict_VERDICT_APPROVE, EditedAt: ts("2026-10-10T12:00:00Z"), Deleted: true, Author: "owner"} | |
| 205 | 248 | events := []*ermite.Event{ | |
M internal/app/testdata/web/commits-root.html +6 -0
| @@ -972,7 +972,13 @@ | |||
| 972 | 972 | htmx:sse:close, htmx:sse:error -> ermiteLive.lost(); | |
| 973 | 973 | htmx:sse:error -> ermiteLive.error(event.detail.status); | |
| 974 | 974 | htmx:sse:before:connection -> ermiteLive.gate(event); | |
| 975 | + | <<<<<<< HEAD | |
| 975 | 976 | home-list, repo-last-push, refs-list, commits-head, mr-notice, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data); | |
| 977 | + | ||||||| parent of 7eacfea (feat(web,cli): live mergeability in the merge box, merge modes and inline merge errors) | |
| 978 | + | home-list, repo-last-push, refs-list, commits-head, mr-notice, ci-run, ci-job -> ermiteLive.swap(event.detail.data); | |
| 979 | + | ======= | |
| 980 | + | home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job -> ermiteLive.swap(event.detail.data); | |
| 981 | + | >>>>>>> 7eacfea (feat(web,cli): live mergeability in the merge box, merge modes and inline merge errors) | |
| 976 | 982 | gap -> ermiteLive.reload()"> | |
| 977 | 983 | </span> | |
| 978 | 984 | </div> | |
M internal/app/testdata/web/commits-small.html +6 -0
| @@ -317,7 +317,13 @@ | |||
| 317 | 317 | htmx:sse:close, htmx:sse:error -> ermiteLive.lost(); | |
| 318 | 318 | htmx:sse:error -> ermiteLive.error(event.detail.status); | |
| 319 | 319 | htmx:sse:before:connection -> ermiteLive.gate(event); | |
| 320 | + | <<<<<<< HEAD | |
| 320 | 321 | home-list, repo-last-push, refs-list, commits-head, mr-notice, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data); | |
| 322 | + | ||||||| parent of 7eacfea (feat(web,cli): live mergeability in the merge box, merge modes and inline merge errors) | |
| 323 | + | home-list, repo-last-push, refs-list, commits-head, mr-notice, ci-run, ci-job -> ermiteLive.swap(event.detail.data); | |
| 324 | + | ======= | |
| 325 | + | home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job -> ermiteLive.swap(event.detail.data); | |
| 326 | + | >>>>>>> 7eacfea (feat(web,cli): live mergeability in the merge box, merge modes and inline merge errors) | |
| 321 | 327 | gap -> ermiteLive.reload()"> | |
| 322 | 328 | </span> | |
| 323 | 329 | </div> | |
M internal/app/testdata/web/home.html +1 -1
| @@ -193,7 +193,7 @@ | |||
| 193 | 193 | htmx:sse:close, htmx:sse:error -> ermiteLive.lost(); | |
| 194 | 194 | htmx:sse:error -> ermiteLive.error(event.detail.status); | |
| 195 | 195 | htmx:sse:before:connection -> ermiteLive.gate(event); | |
| 196 | - | home-list, repo-last-push, refs-list, commits-head, mr-notice, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data); | |
| 196 | + | home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data); | |
| 197 | 197 | gap -> ermiteLive.reload()"> | |
| 198 | 198 | </span> | |
| 199 | 199 | </div> | |
M internal/app/testdata/web/refs.html +1 -1
| @@ -199,7 +199,7 @@ | |||
| 199 | 199 | htmx:sse:close, htmx:sse:error -> ermiteLive.lost(); | |
| 200 | 200 | htmx:sse:error -> ermiteLive.error(event.detail.status); | |
| 201 | 201 | htmx:sse:before:connection -> ermiteLive.gate(event); | |
| 202 | - | home-list, repo-last-push, refs-list, commits-head, mr-notice, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data); | |
| 202 | + | home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data); | |
| 203 | 203 | gap -> ermiteLive.reload()"> | |
| 204 | 204 | </span> | |
| 205 | 205 | </div> | |
M internal/app/testdata/web/summary.html +1 -1
| @@ -225,7 +225,7 @@ | |||
| 225 | 225 | htmx:sse:close, htmx:sse:error -> ermiteLive.lost(); | |
| 226 | 226 | htmx:sse:error -> ermiteLive.error(event.detail.status); | |
| 227 | 227 | htmx:sse:before:connection -> ermiteLive.gate(event); | |
| 228 | - | home-list, repo-last-push, refs-list, commits-head, mr-notice, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data); | |
| 228 | + | home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data); | |
| 229 | 229 | gap -> ermiteLive.reload()"> | |
| 230 | 230 | </span> | |
| 231 | 231 | </div> | |
M internal/app/web.go +5 -0
| @@ -35,6 +35,10 @@ func newWeb(cfg config.Config, f *forge, limiter *clientlimit.Limiter) (http.Han | |||
| 35 | 35 | if f.Passkeys != nil { | |
| 36 | 36 | passkeys = webPasskeys{f.Passkeys} | |
| 37 | 37 | } | |
| 38 | + | var signing ports.SigningKeyService | |
| 39 | + | if f.SigningKey != nil { | |
| 40 | + | signing = f.SigningKey | |
| 41 | + | } | |
| 38 | 42 | // Without artifacts (ERMITE_CI_ARTIFACTS=0) the run page has no section | |
| 39 | 43 | // for them and no download route. | |
| 40 | 44 | var artifacts ports.ArtifactService | |
| @@ -50,6 +54,7 @@ func newWeb(cfg config.Config, f *forge, limiter *clientlimit.Limiter) (http.Han | |||
| 50 | 54 | Inbox: f.Inbox, | |
| 51 | 55 | IssueForm: f.IssueSubmissions, | |
| 52 | 56 | Moderation: f.IssueModeration, | |
| 57 | + | SigningKey: signing, | |
| 53 | 58 | Runs: f.CI, | |
| 54 | 59 | Artifacts: artifacts, | |
| 55 | 60 | Events: f.EventLog, | |
M internal/app/web_events_test.go +9 -0
| @@ -37,6 +37,12 @@ type sseStream struct { | |||
| 37 | 37 | // background. It returns once the server answered, which it does when the | |
| 38 | 38 | // watch is registered: what happens after is not missed. | |
| 39 | 39 | func (h *harness) sse(query, lastID string) *sseStream { | |
| 40 | + | h.t.Helper() | |
| 41 | + | return h.sseAs(query, lastID, nil) | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | // sseAs opens the stream with the headers given (the owner's cookie). | |
| 45 | + | func (h *harness) sseAs(query, lastID string, headers map[string]string) *sseStream { | |
| 40 | 46 | h.t.Helper() | |
| 41 | 47 | ctx, cancel := context.WithCancel(h.ctx) | |
| 42 | 48 | h.t.Cleanup(cancel) | |
| @@ -45,6 +51,9 @@ func (h *harness) sse(query, lastID string) *sseStream { | |||
| 45 | 51 | if lastID != "" { | |
| 46 | 52 | req.Header.Set("Last-Event-ID", lastID) | |
| 47 | 53 | } | |
| 54 | + | for k, v := range headers { | |
| 55 | + | req.Header.Set(k, v) | |
| 56 | + | } | |
| 48 | 57 | resp, err := http.DefaultClient.Do(req) | |
| 49 | 58 | require.NoError(h.t, err) | |
| 50 | 59 | t := h.t | |
M internal/app/web_mr_test.go +197 -10
| @@ -10,6 +10,7 @@ import ( | |||
| 10 | 10 | "regexp" | |
| 11 | 11 | "strings" | |
| 12 | 12 | "testing" | |
| 13 | + | "time" | |
| 13 | 14 | ||
| 14 | 15 | "github.com/stretchr/testify/assert" | |
| 15 | 16 | "github.com/stretchr/testify/require" | |
| @@ -165,12 +166,15 @@ func TestWebMergeRequestPagesRender(t *testing.T) { | |||
| 165 | 166 | assert.Equal(t, head, formHead(t, own.body)) | |
| 166 | 167 | assert.Contains(t, own.body, `action="/proj/merge-requests/1/merge"`) | |
| 167 | 168 | assert.Contains(t, own.body, `id="merge-menu"`, "the merge is a menu of the bar above the diff") | |
| 168 | - | assert.Contains(t, own.body, `<input type="radio" name="mode" value="fast-forward" checked>`, "the first mode is the default") | |
| 169 | - | assert.Contains(t, own.body, `<input type="radio" name="mode" value="squash">`) | |
| 169 | + | assert.Contains(t, own.body, `name="mode" value="fast-forward" checked`, "the first possible mode is the default") | |
| 170 | + | assert.Contains(t, own.body, `name="mode" value="squash"`) | |
| 171 | + | assert.Contains(t, own.body, `name="mode" value="merge"`, "every mode is allowed by default") | |
| 170 | 172 | assert.Contains(t, own.body, "ermite mr merge proj 1 --head "+head) | |
| 171 | 173 | assert.Contains(t, own.body, `name="delete_branch" value="1"> delete feat/search after the merge <kbd aria-hidden="true">D</kbd>`) | |
| 172 | 174 | assert.NotContains(t, own.body, `name="delete_branch" value="1" checked>`, "unchecked by default") | |
| 173 | - | assert.Contains(t, own.body, "ermite mr merge proj 1 --head "+head+" --squash") | |
| 175 | + | assert.Contains(t, own.body, "ermite mr merge proj 1 --head "+head+" --mode squash") | |
| 176 | + | assert.Contains(t, own.body, "ermite mr merge proj 1 --head "+head+" --mode merge") | |
| 177 | + | assert.NotContains(t, own.body, "--squash") | |
| 174 | 178 | assert.Contains(t, own.body, `action="/proj/merge-requests/1/comments"`) | |
| 175 | 179 | assert.Contains(t, own.body, `action="/proj/merge-requests/1/close"`) | |
| 176 | 180 | assert.Contains(t, own.body, `href="/proj/merge-requests/1?on=new:1:search.go#f`, "line numbers link to their form") | |
| @@ -573,8 +577,7 @@ func TestWebMergeRequestEvents(t *testing.T) { | |||
| 573 | 577 | r := newMRRepo(t, domain.VisibilityPublic) | |
| 574 | 578 | list := r.sse("topic=repo:proj&view=mrs", "") | |
| 575 | 579 | r.cli(t, adminToken, "mr", "create", "proj", "--source", "feat/search", "--title", "Secret <title>").ok(t) | |
| 576 | - | ev := list.next(t, watchWait) | |
| 577 | - | assert.Equal(t, "mr-notice", ev.name) | |
| 580 | + | ev := nextNamed(t, list, "mr-notice") | |
| 578 | 581 | assert.Contains(t, ev.data, `<div id="mr-notice" class="live-slot" role="status" data-slot>`) | |
| 579 | 582 | assert.Contains(t, ev.data, `href="/proj/merge-requests"`) | |
| 580 | 583 | assert.NotContains(t, ev.data, "Secret") | |
| @@ -582,19 +585,18 @@ func TestWebMergeRequestEvents(t *testing.T) { | |||
| 582 | 585 | one := r.sse("topic=repo:proj&view=mr&n=1&ref=feat/search", "") | |
| 583 | 586 | other := r.sse("topic=repo:proj&view=mr&n=2&ref=feat/other", "") | |
| 584 | 587 | r.cli(t, adminToken, "mr", "comment", "proj", "1", "-m", "private words").ok(t) | |
| 585 | - | ev = one.next(t, watchWait) | |
| 586 | - | assert.Equal(t, "mr-notice", ev.name) | |
| 588 | + | ev = nextNamed(t, one, "mr-notice") | |
| 587 | 589 | assert.Contains(t, ev.data, "a comment was added") | |
| 588 | 590 | assert.Contains(t, ev.data, `href="/proj/merge-requests/1"`) | |
| 589 | 591 | assert.NotContains(t, ev.data, "private words") | |
| 590 | 592 | ||
| 591 | 593 | r.writeFile(r.wt, "more.txt", []byte("more\n")) | |
| 592 | 594 | r.mustGit(r.wt, "push", "-q", r.url, "feat/search") | |
| 593 | - | ev = one.next(t, watchWait) | |
| 595 | + | ev = nextNamed(t, one, "mr-notice") | |
| 594 | 596 | assert.Contains(t, ev.data, "the branch changed") | |
| 595 | 597 | ||
| 596 | 598 | r.cli(t, adminToken, "mr", "close", "proj", "1").ok(t) | |
| 597 | - | assert.Contains(t, one.next(t, watchWait).data, "was closed") | |
| 599 | + | assert.Contains(t, nextNamed(t, one, "mr-notice").data, "was closed") | |
| 598 | 600 | ||
| 599 | 601 | // A page of another request hears nothing of this one. | |
| 600 | 602 | for drained := false; !drained; { | |
| @@ -607,7 +609,20 @@ func TestWebMergeRequestEvents(t *testing.T) { | |||
| 607 | 609 | drained = true | |
| 608 | 610 | } | |
| 609 | 611 | } | |
| 610 | - | 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")+`"`) | |
| 612 | + | 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")+`&target=main"`) | |
| 613 | + | } | |
| 614 | + | ||
| 615 | + | // nextNamed returns the next event that carries the fragment event: the | |
| 616 | + | // events that change nothing on the page (an id alone) are skipped. | |
| 617 | + | func nextNamed(t *testing.T, s *sseStream, name string) sseEvent { | |
| 618 | + | t.Helper() | |
| 619 | + | for range 10 { | |
| 620 | + | if ev := s.next(t, watchWait); ev.name == name { | |
| 621 | + | return ev | |
| 622 | + | } | |
| 623 | + | } | |
| 624 | + | t.Fatalf("no %s event", name) | |
| 625 | + | return sseEvent{} | |
| 611 | 626 | } | |
| 612 | 627 | ||
| 613 | 628 | func (r *mrRepo) get1(path string) string { | |
| @@ -726,3 +741,175 @@ func TestWebMergeRequestFileWindows(t *testing.T) { | |||
| 726 | 741 | } | |
| 727 | 742 | ||
| 728 | 743 | var reWindowSrc = regexp.MustCompile(`data-src="([^"]*from=25[^"]*)"`) | |
| 744 | + | ||
| 745 | + | // advanceMain pushes a commit to main that the feature branch lacks. file is | |
| 746 | + | // the file it adds (give search.go to conflict with the feature branch). | |
| 747 | + | func (r *mrRepo) advanceMain(file, content string) { | |
| 748 | + | r.t.Helper() | |
| 749 | + | r.mustGit(r.wt, "checkout", "-q", "main") | |
| 750 | + | r.writeFile(r.wt, file, []byte(content)) | |
| 751 | + | r.mustGit(r.wt, "push", "-q", r.url, "main") | |
| 752 | + | r.mustGit(r.wt, "checkout", "-q", "feat/search") | |
| 753 | + | } | |
| 754 | + | ||
| 755 | + | // mrPageWith polls the owner's page of !1 until it contains want. | |
| 756 | + | func (r *mrRepo) mrPageWith(token, want string) string { | |
| 757 | + | r.t.Helper() | |
| 758 | + | var body string | |
| 759 | + | require.Eventually(r.t, func() bool { | |
| 760 | + | body = browser{r.harness}.get("/proj/merge-requests/1", as(token)).body | |
| 761 | + | return strings.Contains(body, want) | |
| 762 | + | }, 10*time.Second, 25*time.Millisecond, "the page never showed %q", want) | |
| 763 | + | return body | |
| 764 | + | } | |
| 765 | + | ||
| 766 | + | // The merge box shows the mergeability of the request: ready, then behind | |
| 767 | + | // (fast-forward is disabled with its reason, the other modes are not), then | |
| 768 | + | // in conflict (no mode is possible and the button says why). | |
| 769 | + | func TestWebMergeRequestMergeabilityBadge(t *testing.T) { | |
| 770 | + | r := newMRRepo(t, domain.VisibilityPublic) | |
| 771 | + | token := r.signIn(t) | |
| 772 | + | r.cli(t, adminToken, "mr", "create", "proj", "--source", "feat/search", "--title", "Add search").ok(t) | |
| 773 | + | ||
| 774 | + | body := r.mrPageWith(token, "Ready to merge") | |
| 775 | + | assert.Contains(t, body, `<div id="mr-mergeability" class="live-slot" role="status" data-slot>`) | |
| 776 | + | assert.Contains(t, body, `data-state="up_to_date"`) | |
| 777 | + | assert.Contains(t, body, `name="mode" value="fast-forward" checked`) | |
| 778 | + | assert.NotContains(t, body, `value="fast-forward" checked disabled`) | |
| 779 | + | assert.Contains(t, body, `id="mr-signing"`, "squash and merge commits say who signs them") | |
| 780 | + | assert.Contains(t, body, "signed with ermite's own key, generated automatically on first start") | |
| 781 | + | assert.Contains(t, body, "<code>ermite signing-key show</code>") | |
| 782 | + | assert.Contains(t, body, "SHA256:", "the fingerprint of the signing key") | |
| 783 | + | assert.Contains(t, body, `:disabled="`, "the controls follow the badge") | |
| 784 | + | assertNoInline(t, body) | |
| 785 | + | ||
| 786 | + | // Main moves on: the branch is behind by one commit and merges cleanly. | |
| 787 | + | r.advanceMain("other.txt", "other\n") | |
| 788 | + | body = r.mrPageWith(token, "Behind main by 1 commit") | |
| 789 | + | assert.Contains(t, body, "merges without conflicts") | |
| 790 | + | assert.Contains(t, body, `data-ff-ok="false"`) | |
| 791 | + | assert.Contains(t, body, `data-merge-ok="true"`) | |
| 792 | + | assert.Regexp(t, `<input type="radio" name="mode" value="fast-forward"[^>]* disabled`, body) | |
| 793 | + | assert.Contains(t, body, `title="behind the target: rebase or use squash/merge"`) | |
| 794 | + | assert.Regexp(t, `name="mode" value="squash" checked`, body, "the first possible mode is checked") | |
| 795 | + | assert.NotRegexp(t, `name="mode" value="squash"[^>]* disabled`, body) | |
| 796 | + | ||
| 797 | + | // A conflicting change to main: every mode is disabled and the files are listed. | |
| 798 | + | r.mustGit(r.wt, "checkout", "-q", "main") | |
| 799 | + | r.writeFile(r.wt, "search.go", []byte("package other\n")) | |
| 800 | + | r.mustGit(r.wt, "push", "-q", r.url, "main") | |
| 801 | + | body = r.mrPageWith(token, "Conflicts with main") | |
| 802 | + | assert.Contains(t, body, "rebase needed") | |
| 803 | + | assert.Contains(t, body, `<ul class="mg-paths" aria-label="conflicting files"><li><code>search.go</code></li></ul>`) | |
| 804 | + | assert.Regexp(t, `<button type="submit" class="btn primary" data-default disabled title="conflicts with the target"`, body) | |
| 805 | + | assert.NotContains(t, body, `name="mode" value="squash" checked`) | |
| 806 | + | ||
| 807 | + | // The server refuses it too, and says so in the box of the page. | |
| 808 | + | res := browser{r.harness}.form("/proj/merge-requests/1/merge", token, url.Values{"mode": {"squash"}, "head_id": {formHead(t, body)}}) | |
| 809 | + | assert.Equal(t, http.StatusConflict, res.status) | |
| 810 | + | assert.Contains(t, res.body, `<p class="d-err" role="alert">`) | |
| 811 | + | assert.Contains(t, res.body, "conflicts with main") | |
| 812 | + | } | |
| 813 | + | ||
| 814 | + | // A merge the server refuses (the target moved after the page was read) shows | |
| 815 | + | // the page of the request again, 409, with the reason in its merge box. | |
| 816 | + | func TestWebMergeRequestInlineMergeError(t *testing.T) { | |
| 817 | + | r := newMRRepo(t, domain.VisibilityPublic) | |
| 818 | + | b := browser{r.harness} | |
| 819 | + | token := r.signIn(t) | |
| 820 | + | r.cli(t, adminToken, "mr", "create", "proj", "--source", "feat/search", "--title", "Add search").ok(t) | |
| 821 | + | body := r.mrPageWith(token, "Ready to merge") | |
| 822 | + | head := formHead(t, body) | |
| 823 | + | ||
| 824 | + | r.advanceMain("other.txt", "other\n") // raced: the form below still says fast-forward | |
| 825 | + | res := b.form("/proj/merge-requests/1/merge", token, url.Values{"mode": {"fast-forward"}, "head_id": {head}}) | |
| 826 | + | assert.Equal(t, http.StatusConflict, res.status) | |
| 827 | + | assert.Equal(t, "no-store", res.hdr.Get("Cache-Control")) | |
| 828 | + | assert.Contains(t, res.body, `id="merge-menu"`, "the page of the request, not a state page") | |
| 829 | + | assert.Contains(t, res.body, `<p class="d-err" role="alert">`) | |
| 830 | + | assert.Contains(t, res.body, "the target has moved") | |
| 831 | + | assert.Contains(t, res.body, `<details class="menu mmenu" id="merge-menu" data-state="open" open>`, "the box is open on the error") | |
| 832 | + | assert.Equal(t, head, r.headOf("feat/search")) | |
| 833 | + | assert.NotContains(t, b.get("/proj/merge-requests/1", as(token)).body, "d-err", "a reload shows no error") | |
| 834 | + | ||
| 835 | + | // A head that moved is refused the same way. | |
| 836 | + | r.writeFile(r.wt, "more.txt", []byte("more\n")) | |
| 837 | + | r.mustGit(r.wt, "push", "-q", r.url, "feat/search") | |
| 838 | + | res = b.form("/proj/merge-requests/1/merge", token, url.Values{"mode": {"squash"}, "head_id": {head}}) | |
| 839 | + | assert.Equal(t, http.StatusConflict, res.status) | |
| 840 | + | assert.Contains(t, res.body, `<p class="d-err" role="alert">`) | |
| 841 | + | assert.Contains(t, res.body, "branch moved") | |
| 842 | + | ||
| 843 | + | // Other refusals keep the state page; a mode that does not exist is a 400. | |
| 844 | + | assert.Equal(t, http.StatusBadRequest, b.form("/proj/merge-requests/1/merge", token, url.Values{"mode": {"rebase"}, "head_id": {head}}).status) | |
| 845 | + | } | |
| 846 | + | ||
| 847 | + | // A merge commit through the page; with the branch deletion requested the | |
| 848 | + | // merged page says the branch is being deleted, not that it is gone. | |
| 849 | + | func TestWebMergeRequestMergeCommitAndDeletingNote(t *testing.T) { | |
| 850 | + | r := newMRRepo(t, domain.VisibilityPublic) | |
| 851 | + | b := browser{r.harness} | |
| 852 | + | token := r.signIn(t) | |
| 853 | + | r.cli(t, adminToken, "mr", "create", "proj", "--source", "feat/search", "--title", "Add search").ok(t) | |
| 854 | + | r.advanceMain("other.txt", "other\n") | |
| 855 | + | body := r.mrPageWith(token, "Behind main by 1 commit") | |
| 856 | + | head := formHead(t, body) | |
| 857 | + | main0 := r.headOf("main") | |
| 858 | + | ||
| 859 | + | res := b.form("/proj/merge-requests/1/merge", token, url.Values{"mode": {"merge"}, "head_id": {head}, "delete_branch": {"1"}}) | |
| 860 | + | require.Equal(t, http.StatusSeeOther, res.status, res.body) | |
| 861 | + | assert.Equal(t, "/proj/merge-requests/1?deleting=1", res.hdr.Get("Location")) | |
| 862 | + | merged := r.headOf("main") | |
| 863 | + | assert.Equal(t, main0, r.bareGit("proj", "rev-parse", merged+"^1")) | |
| 864 | + | assert.Equal(t, head, r.bareGit("proj", "rev-parse", merged+"^2")) | |
| 865 | + | assert.Contains(t, r.bareGit("proj", "cat-file", "-p", merged), "gpgsig") | |
| 866 | + | page := b.get("/proj/merge-requests/1?deleting=1", as(token)) | |
| 867 | + | assert.Contains(t, page.body, "merge commit") | |
| 868 | + | if strings.Contains(page.body, "is being deleted") { | |
| 869 | + | assert.Contains(t, page.body, "feat/search is being deleted") | |
| 870 | + | } | |
| 871 | + | assert.NotContains(t, page.body, "is deleted") | |
| 872 | + | r.forge.MergeRequests.Wait(r.ctx) | |
| 873 | + | assert.Empty(t, r.bareGit("proj", "for-each-ref", "refs/heads/feat/search")) | |
| 874 | + | assert.NotContains(t, b.get("/proj/merge-requests/1?deleting=1", as(token)).body, "is being deleted", "once gone, nothing is being deleted") | |
| 875 | + | } | |
| 876 | + | ||
| 877 | + | // The badge follows the request live: a push to the target turns the fragment | |
| 878 | + | // of the page to "checking" or "behind", and the settled result follows. | |
| 879 | + | func TestWebMergeRequestMergeabilityLive(t *testing.T) { | |
| 880 | + | r := newMRRepo(t, domain.VisibilityPublic) | |
| 881 | + | token := r.signIn(t) | |
| 882 | + | r.cli(t, adminToken, "mr", "create", "proj", "--source", "feat/search", "--title", "Add search").ok(t) | |
| 883 | + | r.mrPageWith(token, "Ready to merge") | |
| 884 | + | ||
| 885 | + | one := r.sseAs("topic=repo:proj&view=mr&n=1&ref=feat/search&target=main", "", as(token)) | |
| 886 | + | anon := r.sse("topic=repo:proj&view=mr&n=1&ref=feat/search&target=main", "") | |
| 887 | + | r.advanceMain("other.txt", "other\n") | |
| 888 | + | deadline := time.After(watchWait) | |
| 889 | + | for done := false; !done; { | |
| 890 | + | ev := nextNamed(t, one, "mr-mergeability") | |
| 891 | + | select { | |
| 892 | + | case <-deadline: | |
| 893 | + | t.Fatal("the badge never showed the result") | |
| 894 | + | default: | |
| 895 | + | } | |
| 896 | + | if strings.Contains(ev.data, "Behind main by 1 commit") { | |
| 897 | + | assert.Contains(t, ev.data, `<div id="mr-mergeability" class="live-slot" role="status" data-slot>`) | |
| 898 | + | assert.Contains(t, ev.data, `data-ff-ok="false"`) | |
| 899 | + | assert.Contains(t, ev.data, `data-merge-ok="true"`) | |
| 900 | + | done = true | |
| 901 | + | } else { | |
| 902 | + | assert.Contains(t, ev.data, "Checking…", "before the result, the badge says it checks") | |
| 903 | + | } | |
| 904 | + | } | |
| 905 | + | // The box is the owner's: a visitor's stream carries no badge. | |
| 906 | + | for { | |
| 907 | + | select { | |
| 908 | + | case ev := <-anon.events: | |
| 909 | + | assert.NotEqual(t, "mr-mergeability", ev.name) | |
| 910 | + | continue | |
| 911 | + | default: | |
| 912 | + | } | |
| 913 | + | break | |
| 914 | + | } | |
| 915 | + | } | |
M internal/ports/mocks/MergeRequestService.go +78 -0
| @@ -942,6 +942,84 @@ func (_c *MergeRequestService_Merge_Call) RunAndReturn(run func(ctx context.Cont | |||
| 942 | 942 | return _c | |
| 943 | 943 | } | |
| 944 | 944 | ||
| 945 | + | // Mergeability provides a mock function for the type MergeRequestService | |
| 946 | + | func (_mock *MergeRequestService) Mergeability(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.Mergeability, error) { | |
| 947 | + | ret := _mock.Called(ctx, p, name, number) | |
| 948 | + | ||
| 949 | + | if len(ret) == 0 { | |
| 950 | + | panic("no return value specified for Mergeability") | |
| 951 | + | } | |
| 952 | + | ||
| 953 | + | var r0 domain.Mergeability | |
| 954 | + | var r1 error | |
| 955 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64) (domain.Mergeability, error)); ok { | |
| 956 | + | return returnFunc(ctx, p, name, number) | |
| 957 | + | } | |
| 958 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64) domain.Mergeability); ok { | |
| 959 | + | r0 = returnFunc(ctx, p, name, number) | |
| 960 | + | } else { | |
| 961 | + | r0 = ret.Get(0).(domain.Mergeability) | |
| 962 | + | } | |
| 963 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, int64) error); ok { | |
| 964 | + | r1 = returnFunc(ctx, p, name, number) | |
| 965 | + | } else { | |
| 966 | + | r1 = ret.Error(1) | |
| 967 | + | } | |
| 968 | + | return r0, r1 | |
| 969 | + | } | |
| 970 | + | ||
| 971 | + | // MergeRequestService_Mergeability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Mergeability' | |
| 972 | + | type MergeRequestService_Mergeability_Call struct { | |
| 973 | + | *mock.Call | |
| 974 | + | } | |
| 975 | + | ||
| 976 | + | // Mergeability is a helper method to define mock.On call | |
| 977 | + | // - ctx context.Context | |
| 978 | + | // - p domain.Principal | |
| 979 | + | // - name domain.RepoName | |
| 980 | + | // - number int64 | |
| 981 | + | func (_e *MergeRequestService_Expecter) Mergeability(ctx any, p any, name any, number any) *MergeRequestService_Mergeability_Call { | |
| 982 | + | return &MergeRequestService_Mergeability_Call{Call: _e.mock.On("Mergeability", ctx, p, name, number)} | |
| 983 | + | } | |
| 984 | + | ||
| 985 | + | func (_c *MergeRequestService_Mergeability_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64)) *MergeRequestService_Mergeability_Call { | |
| 986 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 987 | + | var arg0 context.Context | |
| 988 | + | if args[0] != nil { | |
| 989 | + | arg0 = args[0].(context.Context) | |
| 990 | + | } | |
| 991 | + | var arg1 domain.Principal | |
| 992 | + | if args[1] != nil { | |
| 993 | + | arg1 = args[1].(domain.Principal) | |
| 994 | + | } | |
| 995 | + | var arg2 domain.RepoName | |
| 996 | + | if args[2] != nil { | |
| 997 | + | arg2 = args[2].(domain.RepoName) | |
| 998 | + | } | |
| 999 | + | var arg3 int64 | |
| 1000 | + | if args[3] != nil { | |
| 1001 | + | arg3 = args[3].(int64) | |
| 1002 | + | } | |
| 1003 | + | run( | |
| 1004 | + | arg0, | |
| 1005 | + | arg1, | |
| 1006 | + | arg2, | |
| 1007 | + | arg3, | |
| 1008 | + | ) | |
| 1009 | + | }) | |
| 1010 | + | return _c | |
| 1011 | + | } | |
| 1012 | + | ||
| 1013 | + | func (_c *MergeRequestService_Mergeability_Call) Return(mergeability domain.Mergeability, err error) *MergeRequestService_Mergeability_Call { | |
| 1014 | + | _c.Call.Return(mergeability, err) | |
| 1015 | + | return _c | |
| 1016 | + | } | |
| 1017 | + | ||
| 1018 | + | func (_c *MergeRequestService_Mergeability_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.Mergeability, error)) *MergeRequestService_Mergeability_Call { | |
| 1019 | + | _c.Call.Return(run) | |
| 1020 | + | return _c | |
| 1021 | + | } | |
| 1022 | + | ||
| 945 | 1023 | // ResolveComment provides a mock function for the type MergeRequestService | |
| 946 | 1024 | func (_mock *MergeRequestService) ResolveComment(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, commentID int64, resolved bool) (domain.Comment, error) { | |
| 947 | 1025 | ret := _mock.Called(ctx, p, name, number, commentID, resolved) | |
M internal/ports/services.go +4 -0
| @@ -124,6 +124,10 @@ type MergeRequestService interface { | |||
| 124 | 124 | // CountOpen is the number of open merge requests of a repository the | |
| 125 | 125 | // principal can read. It reads no Git: the tab of every page shows it. | |
| 126 | 126 | CountOpen(ctx context.Context, p domain.Principal, name domain.RepoName) (int, error) | |
| 127 | + | // Mergeability is the mergeability of an open merge request, "checking" | |
| 128 | + | // until the server has a result for the current tips; zero for a request | |
| 129 | + | // that is not open. It reads no diff: the live pages call it on each change. | |
| 130 | + | Mergeability(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.Mergeability, error) | |
| 127 | 131 | } | |
| 128 | 132 | ||
| 129 | 133 | // IssueService is the issue use cases. *usecase.Issues implements it. | |
M internal/usecase/mergerequests.go +22 -0
| @@ -440,6 +440,28 @@ func (m *MergeRequests) CountOpen(ctx context.Context, p domain.Principal, name | |||
| 440 | 440 | return n, nil | |
| 441 | 441 | } | |
| 442 | 442 | ||
| 443 | + | // Mergeability implements ports.MergeRequestService: the mergeability of an | |
| 444 | + | // open merge request for the current tips, queued when there is no up-to-date | |
| 445 | + | // result. It is the zero value for a request that is not open, or without a | |
| 446 | + | // mergeability service. | |
| 447 | + | func (m *MergeRequests) Mergeability(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.Mergeability, error) { | |
| 448 | + | _, mr, err := m.get(ctx, p, name, number) | |
| 449 | + | if err != nil { | |
| 450 | + | return domain.Mergeability{}, err | |
| 451 | + | } | |
| 452 | + | if m.mergeab == nil || !mr.IsOpen() { | |
| 453 | + | return domain.Mergeability{}, nil | |
| 454 | + | } | |
| 455 | + | if mr, err = m.withHead(ctx, mr); err != nil { | |
| 456 | + | return domain.Mergeability{}, err | |
| 457 | + | } | |
| 458 | + | tip, err := m.branchOrZero(ctx, name, mr.Target) | |
| 459 | + | if err != nil { | |
| 460 | + | return domain.Mergeability{}, err | |
| 461 | + | } | |
| 462 | + | return m.mergeab.Read(ctx, mr, tip), nil | |
| 463 | + | } | |
| 464 | + | ||
| 443 | 465 | func encodeMRToken(last domain.MergeRequest) string { | |
| 444 | 466 | return base64.RawURLEncoding.EncodeToString([]byte(mrTokenPrefix + strconv.FormatInt(last.Number, 10))) | |
| 445 | 467 | } | |
M skills/ermite/CLI.md +5 -4
| @@ -11,7 +11,7 @@ Errors are one line on standard error (`ermite: ...`) with exit status 1. `--jso | |||
| 11 | 11 | | `repo list [--json]` | none | repositories you can see | | |
| 12 | 12 | | `repo show <name> [--json]` | none | | | |
| 13 | 13 | | `repo create <name> [--description TEXT] [--visibility public]` | `repo:write` | private by default | | |
| 14 | - | | `repo edit <name> [--description TEXT] [--visibility V] [--default-branch B] [--merge-modes fast-forward,squash] [--failing-checks-block=false]` | `repo:admin` | only the given flags change; `--description ""` clears it; the branch must exist | | |
| 14 | + | | `repo edit <name> [--description TEXT] [--visibility V] [--default-branch B] [--merge-modes fast-forward,squash,merge] [--failing-checks-block=false]` | `repo:admin` | only the given flags change; every merge mode is allowed by default, list the ones to keep; `--description ""` clears it; the branch must exist | | |
| 15 | 15 | | `repo delete <name> [--yes]` | `repo:admin` | | | |
| 16 | 16 | ||
| 17 | 17 | Repository JSON: `name`, `description`, `visibility`, `default_branch`, `created_at`, `pushed_at` (null before the first push), `merge_modes`, `failing_checks_block`. | |
| @@ -56,12 +56,12 @@ A token can never grant more than the token that creates it. `--repo` (repeatabl | |||
| 56 | 56 | |---|---|---| | |
| 57 | 57 | | `mr create <repo> --source B --title T [--target B] [--description TEXT\|-]` | `mr:write` | target defaults to the default branch; title of 120 characters max; `-` reads standard input | | |
| 58 | 58 | | `mr list <repo> [--state open\|merged\|closed\|all] [--json]` | none | default `open` | | |
| 59 | - | | `mr show <repo> <n> [--diff] [--since-last-read] [--mark-read] [--json]` | `mr:write` for `--mark-read` | commits, changed files and comments; `--diff` adds hunks with line numbers | | |
| 59 | + | | `mr show <repo> <n> [--diff] [--since-last-read] [--mark-read] [--json]` | `mr:write` for `--mark-read` | commits, changed files and comments; `--diff` adds hunks with line numbers; the `mergeability` line (JSON `mergeability`, null unless open) is `up_to_date`, `behind` (N commits, merges cleanly), `conflict` (paths) or `checking` | | |
| 60 | 60 | | `mr comment <repo> <n> -m TEXT\|- [--path P --line N\|A-B --side old\|new] [--reply ID] [--approve\|--request-changes]` | `mr:write`; verdicts `mr:review` | Markdown note (8000 characters max); the lines must be in the current diff on one side; a deleted file uses its old path; `--reply` takes a root note (`12`, `#12` or `^12`), inherits its anchor and cannot carry a verdict; a verdict note may have no message | | |
| 61 | 61 | | `mr edit-comment <repo> <n> <comment-id> -m TEXT\|-` | `mr:write` | author only, open merge request; the verdict is unchanged | | |
| 62 | 62 | | `mr delete-comment <repo> <n> <comment-id>` | `mr:write` | author only; the text is lost, the verdict is kept | | |
| 63 | 63 | | `mr resolve <repo> <n> <comment-id> [--undo]` | `mr:write` | on the thread root, covers its replies; doing it twice is not an error | | |
| 64 | - | | `mr merge <repo> <n> --head <full-id> [--squash] [--delete-branch]` | `mr:merge` | refused if the branch moved or the target is not an ancestor of the head; `--delete-branch` deletes the source after the merge (refused for the default branch or a branch another open request uses; JSON adds `source_branch_deleted`) | | |
| 64 | + | | `mr merge <repo> <n> --head <full-id> [--mode fast-forward\|squash\|merge] [--delete-branch]` | `mr:merge` | refused if the branch moved, if `fast-forward` and the target is not an ancestor of the head (behind: use `squash` or `merge`), or if `squash`/`merge` conflict; squash and merge commits are signed by the server key (`signing-key show`); answers once the target moved; `--delete-branch` deletes the source after the merge (refused for the default branch or a branch another open request uses; JSON adds `source_branch_deleted`) | | |
| 65 | 65 | | `mr close <repo> <n>` | `mr:write` | a closed merge request cannot be reopened | | |
| 66 | 66 | ||
| 67 | 67 | `ermite mr create` and `ermite mr close` print a confirmation followed by the full | |
| @@ -106,6 +106,7 @@ Workflow JSON: `repo`, `file` (always `.github/workflows/<file>`), `name` (the ` | |||
| 106 | 106 | | `repo_created`, `repo_updated`, `repo_deleted` | `repo`, `repository` | | |
| 107 | 107 | | `pushed` | `repo`, `ref_updates` (`ref`, `old_id`, `new_id`; an empty id means created or deleted) | | |
| 108 | 108 | | `merge_request_opened`, `merge_request_merged`, `merge_request_closed` | `repo`, `merge_request` | | |
| 109 | + | | `merge_request_mergeability` | `repo`, `number`, `mergeability` (`state`: `checking`, `up_to_date`, `behind`, `conflict`; `behind`, `behind_capped`, `conflict_paths`, `conflict_paths_truncated`); sent when the state changes | | |
| 109 | 110 | | `comment_added`, `comment_edited`, `comment_deleted`, `comment_resolved` | `repo`, `number`, `comment` (a verdict note has `verdict` set) | | |
| 110 | 111 | | `gap` | none: events were lost, reload the state | | |
| 111 | 112 | ||
| @@ -116,7 +117,7 @@ Workflow JSON: `repo`, `file` (always `.github/workflows/<file>`), `name` (the ` | |||
| 116 | 117 | | `serve` | configured by `ERMITE_*` environment variables only (see the project README) | | |
| 117 | 118 | | `version [--json]` | client version; with a server URL (`ERMITE_URL` or the config file), the server version and features | | |
| 118 | 119 | | `config show [--json]` / `config set <url\|token.cmd> <value>` / `config unset <key>` | the CLI config file; `token.cmd` is a command that prints the token (any secret manager), never the token itself; `show` never runs it | | |
| 119 | - | | `signing-key show` | public key and `allowed_signers` line to check squash commits with `git verify-commit` | | |
| 120 | + | | `signing-key show` | public key and `allowed_signers` line to check squash and merge commits with `git verify-commit` | | |
| 120 | 121 | | `signing-key trust list [--json]` | keys commit signatures are checked against (the server key first) | | |
| 121 | 122 | | `signing-key trust add --format ssh\|gpg --title <t> --email <e>... <file\|->` / `trust remove <id>` | owner only; a commit shows verified when its committer email is listed for the key | | |
| 122 | 123 | | `web login [--json]` | one-time sign-in link (about 60 s); needs the admin token or a token with every scope, no repository list and no branch prefix; the link is a secret | | |
M skills/ermite/SKILL.md +2 -2
| @@ -37,9 +37,9 @@ git push https://git.example.com/<repo>.git main # password = token, | |||
| 37 | 37 | 2. Read: `ermite mr show <repo> <n> --diff --json`. Keep `head_id`; take line numbers from the hunks. | |
| 38 | 38 | 3. Comment on a line: `ermite mr comment <repo> <n> -m "text" --path <file> --line <n> --side new`; `--line 10-14` covers a range on one side. Without `--path` the comment is general. The line must appear in the diff. Notes are Markdown; `^N` refers to note N. | |
| 39 | 39 | 4. End a review with a verdict (needs `mr:review`): `--approve` or `--request-changes`, alone or with a message. Verdicts are informative and cannot be edited; a new verdict replaces the previous one. | |
| 40 | - | 5. Merge only when asked: `ermite mr merge <repo> <n> --head <head_id>`, with `--squash` for one commit signed by the server and `--delete-branch` to delete the source branch afterwards. | |
| 40 | + | 5. Merge only when asked: `ermite mr merge <repo> <n> --head <head_id>`, with `--mode squash` for one commit or `--mode merge` for a merge commit (both signed by the server's key, see `ermite signing-key show`) and `--delete-branch` to delete the source branch afterwards. | |
| 41 | 41 | ||
| 42 | - | The merge is refused when the branch moved since `head_id` (review again) or when the target is not an ancestor of the head (rebase and push again). The repository may allow only one merge mode. | |
| 42 | + | The merge is refused when the branch moved since `head_id` (review again) or, for `fast-forward`, when the target is not an ancestor of the head (rebase and push again, or use `squash`/`merge`), or when `squash`/`merge` conflict. `ermite mr show` prints the `mergeability` line (up to date, behind by N, conflict with paths). The repository may allow only some merge modes. | |
| 43 | 43 | ||
| 44 | 44 | ### Act on a review | |
| 45 | 45 | ||