Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Blame · server/web/templates/components.html

Line attribution on main · 174 line(s)

c717d4eb
Keenan Nemetz
13d ago
1 {{/* Small components shared by more than one surface: the label/assignee token
2 input, the commit list, the comment card, the file tree, and the pager. All
3 of them are rendered from a page's own data rather than by a call to a
4 handler. */}}
5 {{define "token_input"}}
6 <div class="label-input" data-label-input data-label-item="{{.Item}}">
7 <div class="label-tokens" data-label-tokens>
306f36fc
Keenan Nemetz
12d ago
8 <input type="text" class="label-entry" value="{{join .Selected ", "}}" data-label-entry autocomplete="off" placeholder="{{.Placeholder}}" aria-label="{{.Placeholder}}">
c717d4eb
Keenan Nemetz
13d ago
9 </div>
10 <div class="label-suggest" data-label-suggest hidden>
11 {{range .All}}<button type="button" class="label-option" data-label-option="{{.}}">{{.}}</button>{{end}}
12 </div>
13 </div>
14 {{end}}
15
16 {{define "commitlist"}}
17 {{if .Commits}}
18 <table class="commits">
19 <tbody>
20 {{range .Commits}}
21 <tr>
ed03b3d1
Keenan Nemetz
2h ago
22 <td class="sha"><a href="/{{$.Name}}/commit/{{.SHA}}">{{short .SHA}}</a></td>
23 <td><a href="/{{$.Name}}/commit/{{.SHA}}">{{.Subject}}</a></td>
c717d4eb
Keenan Nemetz
13d ago
24 <td class="muted">{{.Author}}</td>
25 <td class="muted nowrap">{{since .When}}</td>
26 </tr>
27 {{end}}
28 </tbody>
29 </table>
30 {{else}}
c7b9094f
Keenan Nemetz
1d ago
31 <div class="empty-state"><p>No commits yet.</p></div>
c717d4eb
Keenan Nemetz
13d ago
32 {{end}}
33 {{end}}
34
628d4523
Keenan Nemetz
11d ago
35 {{/* One tree for every browser. A code row opens a blob and carries the file
36 icon for its language; a changed-file row opens the file's diff and carries
37 the A/M/D/R mark instead. The context says which is which: with Review and
38 Commit both empty this is the code browser. This template lives here
39 because the code browser owns the tree; request_files.html and commit.html
40 render it with a change or a commit. */}}
c717d4eb
Keenan Nemetz
13d ago
41 {{define "tree_nodes"}}
42 <ul class="tree-list">
43 {{range .Nodes}}
44 <li>
45 {{if eq .Type "tree"}}
46 <details class="tree-dir{{if .Active}} active{{end}}" data-path="{{.Path}}"{{if .Open}} open{{end}}>
47 <summary class="tree-row">
48 <span class="tree-chevron">{{icon "chevron-right"}}</span>
49 <span class="tree-icon">{{icon "folder"}}</span>
50 <span class="tree-name" title="{{.Path}}">{{.Name}}</span>
51 </summary>
628d4523
Keenan Nemetz
11d ago
52 <div class="tree-children">{{template "tree_nodes" ($.Branch .Children)}}</div>
c717d4eb
Keenan Nemetz
13d ago
53 </details>
54 {{else}}
628d4523
Keenan Nemetz
11d ago
55 {{$href := $.Href .Path}}
56 <a class="tree-row tree-file{{if .Active}} active{{end}}" data-path="{{.Path}}" href="{{$href}}" hx-get="{{$href}}" hx-target="{{if $.Diff}}#review-main{{else}}#code-main{{end}}" hx-swap="outerHTML" hx-push-url="true" title="{{.Path}}">
57 {{if $.Diff}}<span class="review-mark" data-status="{{.Status}}">{{.Status}}</span>{{else}}<span class="tree-icon {{fileclass .Name}}">{{icon (fileicon .Name)}}</span>{{end}}
c717d4eb
Keenan Nemetz
13d ago
58 <span class="tree-name">{{.Name}}</span>
59 </a>
60 {{end}}
61 </li>
62 {{end}}
63 </ul>
64 {{end}}
65
66 {{/* One page control for every list that can run past a screen: issues, change
67 requests, and the instance activity feed. The handler owns the URLs and the
68 words ("Issue pages"); this only draws them. */}}
69 {{/* One card for a comment, on either surface. An issue comment has no anchor, so
70 it shows no snippet; a change comment anchored to a line shows the lines it
71 was written about, read from the blob as it stood then, and marks the card as
72 stale when the file has moved on. Edit swaps .Target, so the card does not
73 need to know which thread it is in. */}}
74 {{define "comment_card"}}
75 <article class="comment{{if .Deleted}} deleted{{end}}" id="c-{{.ID}}">
76 {{if .Deleted}}
77 <div class="comment-deleted">{{icon "circle-x"}}Comment deleted</div>
78 {{else}}
79 <header class="comment-head">
a6b42433
Keenan Nemetz
7d ago
80 {{$u := user .Author}}
81 <span class="comment-author">{{template "userlink" $u}}</span>
c717d4eb
Keenan Nemetz
13d ago
82 {{if .ReplyTo}}<span class="reply-context">replying to {{if .ReplyToDeleted}}<a href="#c-{{.ReplyTo}}">a deleted comment</a>{{else}}<a href="#c-{{.ReplyTo}}">{{who .ReplyToName}}</a>{{end}}</span>{{end}}
83 <span class="muted">{{since .Created.Unix}}</span>
84 {{if and .Anchor (not .Current)}}<span class="conflict">the file has changed since this comment</span>{{end}}
85 {{if and .Anchor (not .Snippet)}}<span class="muted">on <code>{{.Anchor.Path}}:{{.Anchor.Line}}</code> ({{.Anchor.Side}} side)</span>{{end}}
86 <div class="comment-menu" data-comment-menu>
87 <button type="button" class="comment-menu-toggle" data-comment-menu-toggle aria-haspopup="menu" aria-expanded="false" aria-label="Comment actions">&#8943;</button>
88 <div class="comment-menu-list" data-comment-menu-list role="menu" hidden>
89 {{if .CanReply}}<button type="button" role="menuitem" data-reply-to="{{.ID}}" data-reply-author="{{who .Author}}">Reply</button>{{end}}
802e0e52
Keenan Nemetz
4d ago
90 <button type="button" role="menuitem" data-copy-query="{{urlquery .Source}}">Copy text</button>
c717d4eb
Keenan Nemetz
13d ago
91 <button type="button" role="menuitem" data-copy-link="{{.ID}}">Copy link</button>
92 {{if .CanEdit}}<button type="button" role="menuitem" data-comment-edit>Edit</button>{{end}}
93 {{if .CanDelete}}
94 <form method="post" action="{{.DeleteURL}}" data-confirm="Delete this comment?">
95 <input type="hidden" name="csrf" value="{{.CSRF}}">
96 <button type="submit" role="menuitem" class="menu-danger">Delete</button>
97 </form>
98 {{end}}
99 </div>
100 </div>
101 </header>
102 <div class="comment-body md">{{.Body}}</div>
103 {{if .Snippet}}
104 <details class="comment-snippet">
105 <summary>
106 <span class="snippet-chevron">{{icon "chevron-right"}}</span>
107 <code>{{.Snippet.Path}}:{{.Snippet.Line}}</code>
108 <span class="muted">({{.Snippet.Side}} side)</span>
109 {{if not .Current}}<span class="conflict">original line</span>{{end}}
110 </summary>
111 <div class="snippet-code">{{range .Snippet.Lines}}<div class="snippet-line{{if .Anchor}} anchor{{end}}"><span class="snippet-no">{{.No}}</span><span class="snippet-text">{{.Text}}</span></div>{{end}}</div>
112 <div class="snippet-actions"><a href="{{.Snippet.FilesURL}}">View in Files</a></div>
113 </details>
114 {{end}}
115 {{if .CanEdit}}
116 <form class="comment-edit-form" data-comment-edit-form hidden method="post" action="{{.EditURL}}" hx-post="{{.EditURL}}" hx-target="{{.Target}}" hx-swap="outerHTML" hx-disabled-elt="find button[type='submit']">
117 <input type="hidden" name="csrf" value="{{.CSRF}}">
802e0e52
Keenan Nemetz
4d ago
118 <textarea name="body" required data-autosize>{{.Source}}</textarea>
c717d4eb
Keenan Nemetz
13d ago
119 <div class="form-actions">
120 <button class="secondary" type="button" data-comment-edit-cancel>Cancel</button>
121 <button type="submit">Save</button>
122 </div>
123 </form>
124 {{end}}
125 {{end}}
126 </article>
127 {{end}}
128
129 {{/* The discussion section of an issue or a change request: heading, threaded
130 comments, one composer. The two surfaces pass the same view, so the section
131 id, the composer's URL and the heading word come from the handler rather than
132 from the template. */}}
133 {{define "comment_thread"}}
134 <div id="{{.Section}}">
135 <h2>{{icon "message-square"}}{{.Title}} <span class="muted">({{.CommentCount}})</span></h2>
136 {{if .ThreadError}}<p class="error">{{.ThreadError}}</p>{{end}}
137 {{with .Target}}<p class="anchor-note muted">Commenting on <code>{{.Path}}:{{.Line}}</code> ({{.Side}} side) · <a href="{{$.BackURL}}#{{$.Composer}}">cancel</a></p>{{end}}
138 {{range .Comments}}
139 {{template "comment_card" .}}
140 {{if .Replies}}
141 <details class="thread-replies" open>
142 <summary><span class="thread-chevron">{{icon "chevron-right"}}</span>{{len .Replies}} {{if eq (len .Replies) 1}}reply{{else}}replies{{end}}</summary>
143 <div class="comment-replies">{{range .Replies}}{{template "comment_card" .}}{{end}}</div>
144 </details>
145 {{end}}
146 {{else}}
c7b9094f
Keenan Nemetz
1d ago
147 <div class="empty-state"><p>{{.EmptyText}}</p></div>
c717d4eb
Keenan Nemetz
13d ago
148 {{end}}
149
150 {{if .CanDiscuss}}
151 <form id="{{.Composer}}" method="post" action="{{.PostURL}}" class="form-block discussion-composer" hx-post="{{.PostURL}}" hx-target="#{{.Section}}" hx-swap="outerHTML"{{if .PushURL}} hx-push-url="{{.PushURL}}"{{end}} hx-disabled-elt="find button[type='submit']">
152 <input type="hidden" name="csrf" value="{{.CSRF}}">
153 <input type="hidden" name="reply_to" value="" data-reply-input>
154 {{with .Target}}<input type="hidden" name="path" value="{{.Path}}"><input type="hidden" name="line" value="{{.Line}}"><input type="hidden" name="side" value="{{.Side}}">{{end}}
155 <div class="reply-target" data-reply-target hidden>
156 <span>Replying to <strong data-reply-name></strong></span>
157 <button class="link" type="button" data-reply-clear>Cancel reply</button>
158 </div>
159 <textarea name="body" required placeholder="Leave a comment. Markdown is welcome." aria-label="Comment"{{if .Target}} autofocus{{end}}></textarea>
160 <div class="form-actions"><button type="submit">{{icon "message-square"}}Comment</button></div>
161 </form>
162 {{end}}
163 </div>
164 {{end}}
165
166 {{define "pager"}}
167 {{if gt .Pages 1}}
168 <nav class="pager" aria-label="{{.Label}}">
169 {{if .PrevURL}}<a class="button secondary" href="{{.PrevURL}}"{{if .Target}} hx-get="{{.PrevURL}}" hx-target="{{.Target}}" hx-swap="outerHTML" hx-push-url="true"{{end}}>Previous</a>{{else}}<span></span>{{end}}
170 <span class="muted">Showing {{.From}}–{{.To}} of {{.Total}} · page {{.Page}} of {{.Pages}}</span>
171 {{if .NextURL}}<a class="button secondary" href="{{.NextURL}}"{{if .Target}} hx-get="{{.NextURL}}" hx-target="{{.Target}}" hx-swap="outerHTML" hx-push-url="true"{{end}}>Next</a>{{else}}<span></span>{{end}}
172 </nav>
173 {{end}}
174 {{end}}