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/chrome.html

Line attribution on main · 205 line(s)

c717d4eb
Keenan Nemetz
13d ago
1 {{/* Shared page chrome: the document shell, the global bar and account menu,
2 the repository header with its tabs, and the one error page every 404 and
3 500 renders through. Every page template opens with {{template "header" .}}
4 and closes with {{template "footer" .}}. */}}
f09ef39e
Keenan Nemetz
15d ago
5 {{define "header"}}<!doctype html>
f7a34d87
Keenan Nemetz
1d ago
6 <html lang="en" data-theme="everforest-dark-medium" data-font-scale="medium"{{if .InstanceThemes}} data-themes="{{range $i, $t := .InstanceThemes}}{{if $i}},{{end}}{{$t.ID}}{{end}}"{{end}}>
f09ef39e
Keenan Nemetz
15d ago
7 <head>
8 <meta charset="utf-8">
9 <meta name="viewport" content="width=device-width,initial-scale=1">
10 <meta name="csrf" content="{{.CSRF}}">
5cab7b18
Keenan Nemetz
15d ago
11 <meta name="color-scheme" content="dark light">
a6b42433
Keenan Nemetz
7d ago
12 {{if .NoIndex}}<meta name="robots" content="noindex">{{end}}
ac00929f
Keenan Nemetz
12d ago
13 <meta name="htmx-config" content='{"includeIndicatorStyles":false}'>
3d707ea7
Keenan Nemetz
18h ago
14 <title>{{.Title}} · {{.InstanceName}}</title>
ed03b3d1
Keenan Nemetz
3h ago
15 <link rel="icon" type="image/png" sizes="32x32" href="/-/static/favicon-32.png" media="(prefers-color-scheme: light)">
16 <link rel="icon" type="image/png" sizes="32x32" href="/-/static/favicon-32-dark.png" media="(prefers-color-scheme: dark)">
17 <link rel="icon" type="image/png" sizes="16x16" href="/-/static/favicon-16.png" media="(prefers-color-scheme: light)">
18 <link rel="icon" type="image/png" sizes="16x16" href="/-/static/favicon-16-dark.png" media="(prefers-color-scheme: dark)">
19 <link rel="icon" type="image/svg+xml" href="/-/static/favicon.svg" sizes="any">
6ce635a8
Keenan Nemetz
1d ago
20 {{if .InstanceFavicon}}<link rel="icon" type="image/svg+xml" href="{{.InstanceFavicon}}" sizes="any">{{end}}
ed03b3d1
Keenan Nemetz
3h ago
21 <script src="/-/static/theme.js"></script>
22 <link rel="stylesheet" href="/-/static/vole.css">
23 <link rel="stylesheet" href="/-/static/everforest.css">
24 <link rel="stylesheet" href="/-/static/palettes.css">
f7a34d87
Keenan Nemetz
1d ago
25 {{range .InstanceThemes}}<link rel="stylesheet" href="{{.URL}}">
26 {{end}}{{if .InstanceCSS}}<link rel="stylesheet" href="{{.InstanceCSS}}">{{end}}
ed03b3d1
Keenan Nemetz
3h ago
27 <script src="/-/static/htmx.min.js" defer></script>
28 <script src="/-/static/auth.js" defer></script>
29 <script src="/-/static/app.js" defer></script>
f09ef39e
Keenan Nemetz
15d ago
30 </head>
31 <body>
32 <header class="topbar">
3d707ea7
Keenan Nemetz
18h ago
33 <a class="brand" href="/" aria-label="{{.InstanceName}} home"><span class="mark" aria-hidden="true"></span></a>
f09ef39e
Keenan Nemetz
15d ago
34 <nav class="topnav" aria-label="Global">
6c03e6d3
Keenan Nemetz
9d ago
35 <a class="{{if eq .Section "home"}}active{{end}}" href="/"{{if eq .Section "home"}} aria-current="page"{{end}}>{{icon "house"}}Home</a>
ed03b3d1
Keenan Nemetz
3h ago
36 <a class="{{if eq .Section "activity"}}active{{end}}" href="/-/activity"{{if eq .Section "activity"}} aria-current="page"{{end}}>{{icon "activity"}}Activity</a>
37 <a class="{{if eq .Section "people"}}active{{end}}" href="/-/u"{{if eq .Section "people"}} aria-current="page"{{end}}>{{icon "users"}}Users</a>
38 {{if .InstanceAdmin}}<a class="{{if eq .Section "admin"}}active{{end}}" href="/-/admin"{{if eq .Section "admin"}} aria-current="page"{{end}}>{{icon "sliders-horizontal"}}Admin</a>{{end}}
f09ef39e
Keenan Nemetz
15d ago
39 </nav>
1265ea55
Keenan Nemetz
9d ago
40 <div class="topsearch" role="search">
41 <button type="button" class="topsearch-box" data-palette-open aria-haspopup="dialog" aria-controls="search-palette" aria-keyshortcuts="Control+K Meta+K">
6c03e6d3
Keenan Nemetz
9d ago
42 {{icon "search"}}
1265ea55
Keenan Nemetz
9d ago
43 <span class="topsearch-label">Search issues and changes</span>
6c03e6d3
Keenan Nemetz
9d ago
44 <kbd class="topsearch-key" data-palette-key aria-hidden="true">Ctrl K</kbd>
45 </button>
1265ea55
Keenan Nemetz
9d ago
46 </div>
f09ef39e
Keenan Nemetz
15d ago
47 <div class="topright">
48 {{if .LoggedIn}}
074ae160
Keenan Nemetz
14d ago
49 <details class="account-menu">
50 <summary>
28b19c74
Keenan Nemetz
7d ago
51 {{icon "user"}}<span class="account-name">{{.User}}</span>
ab326d3a
Keenan Nemetz
14d ago
52 {{if .DevLogin}}<span class="label" title="Development sign-in">dev</span>{{end}}
074ae160
Keenan Nemetz
14d ago
53 <span class="account-caret">{{icon "chevron-right"}}</span>
54 </summary>
55 <div class="account-pop">
ed03b3d1
Keenan Nemetz
3h ago
56 <a href="/-/account">{{icon "user"}}Account</a>
074ae160
Keenan Nemetz
14d ago
57 <div class="account-sep"></div>
ed03b3d1
Keenan Nemetz
3h ago
58 <form method="post" action="/-/logout"><input type="hidden" name="csrf" value="{{.CSRF}}"><button type="submit">{{icon "log-out"}}Sign out</button></form>
074ae160
Keenan Nemetz
14d ago
59 </div>
60 </details>
61 {{else}}
ed03b3d1
Keenan Nemetz
3h ago
62 {{if .DevLogin}}<a href="/-/dev/login">{{icon "user"}}Dev sign-in</a>{{end}}
63 <a href="/-/login">{{icon "user"}}Sign in</a>
074ae160
Keenan Nemetz
14d ago
64 {{end}}
f09ef39e
Keenan Nemetz
15d ago
65 </div>
66 </header>
1265ea55
Keenan Nemetz
9d ago
67 {{/* The command palette is the whole search surface: one field over every
68 issue, change request and comment the reader can see. The launcher in the
69 global bar opens it; the dialog itself never navigates to a page. */}}
6c03e6d3
Keenan Nemetz
9d ago
70 <dialog class="palette" id="search-palette" aria-label="Search issues, changes and comments">
1265ea55
Keenan Nemetz
9d ago
71 <form class="palette-search" role="search"
ed03b3d1
Keenan Nemetz
3h ago
72 hx-get="/-/search/palette" hx-trigger="input changed delay:250ms, search"
6c03e6d3
Keenan Nemetz
9d ago
73 hx-target="#palette-results" hx-swap="innerHTML" hx-sync="this:replace">
74 <span aria-hidden="true">{{icon "search"}}</span>
75 <input type="search" name="q" id="search-palette-input"
76 placeholder="Search issues, changes and comments"
77 aria-label="Search issues, changes and comments" autocomplete="off"
78 role="combobox" aria-expanded="true" aria-controls="palette-results" aria-autocomplete="list">
79 <button type="button" class="palette-close" data-palette-close aria-label="Close search">{{icon "x"}}</button>
80 </form>
81 <div class="palette-results" id="palette-results" role="listbox" aria-label="Search results">
82 {{template "palette_hint"}}
83 </div>
84 <div class="palette-foot" aria-hidden="true">
85 <span><kbd>↑</kbd> <kbd>↓</kbd> move</span>
86 <span><kbd>Enter</kbd> open</span>
87 <span><kbd>Esc</kbd> close</span>
88 </div>
89 </dialog>
5cab7b18
Keenan Nemetz
15d ago
90 <a class="skip-link" href="#main">Skip to content</a>
91 <main class="page" id="main">
f09ef39e
Keenan Nemetz
15d ago
92 {{if .Error}}<div class="flash bad">{{.Error}}</div>{{end}}
93 {{end}}
94
95 {{define "footer"}}
96 </main>
97 </body>
98 </html>
99 {{end}}
100
a2ec9561
Keenan Nemetz
9d ago
101 {{/* The open counts beside the Issues and Changes tabs. They are templates of
102 their own so the header's poll can replace the badges and nothing else: the
103 tab the reader is on, the clone menu and any open form stay where they are.
104 The ids are what the out-of-band swap matches, so the page and the poll
105 agree on where a count goes. */}}
106 {{define "repo_issue_count"}}<span class="chip-count" id="repo-issue-count"{{if .Live}} hx-swap-oob="outerHTML"{{end}}>{{.OpenIssues}}</span>{{end}}
107 {{define "repo_request_count"}}<span class="chip-count" id="repo-request-count"{{if .Live}} hx-swap-oob="outerHTML"{{end}}>{{.OpenRequests}}</span>{{end}}
108 {{define "repo_counts_live"}}
109 {{template "repo_issue_count" .}}
110 {{template "repo_request_count" .}}
111 {{end}}
112
f09ef39e
Keenan Nemetz
15d ago
113 {{define "repohead"}}
114 <div class="repohead">
074ae160
Keenan Nemetz
14d ago
115 <div class="repo-title">
816cc543
Keenan Nemetz
11d ago
116 <div class="repo-title-row">
117 <div class="repo-name"><span class="repo-vis" title="{{if .Private}}Private repository{{else}}Public repository{{end}}">{{if .Private}}{{icon "lock"}}{{else}}{{icon "globe"}}{{end}}</span>{{.Name}}</div>
118 <div class="repo-title-actions">
119 {{if .Site}}<a class="button secondary" href="{{.Site}}">{{icon "external-link"}}Site</a>{{end}}
120 <details class="clone">
121 <summary>{{icon "copy"}}Clone</summary>
122 <div class="clone-pop">
123 <p class="muted">Read only, no credentials:</p>
124 <div class="clone-field">
125 <code>{{.CloneURL}}</code>
126 <button class="secondary" type="button" data-copy="{{.CloneURL}}" title="Copy HTTPS URL" aria-label="Copy HTTPS URL">{{icon "copy"}}</button>
127 </div>
128 <p class="muted">Write over SSH with a registered key:</p>
129 <div class="clone-field">
130 <code>git clone {{.SSHURL}}</code>
131 <button class="secondary" type="button" data-copy="git clone {{.SSHURL}}" title="Copy SSH command" aria-label="Copy SSH command">{{icon "copy"}}</button>
132 </div>
133 </div>
134 </details>
135 </div>
136 </div>
074ae160
Keenan Nemetz
14d ago
137 {{if .Description}}<p class="repo-desc">{{.Description}}</p>{{end}}
138 </div>
a2ec9561
Keenan Nemetz
9d ago
139 {{/* One poll for the header's counts. The response is out-of-band updates to
140 the two badges, so the tabs are not re-rendered and the page keeps its
141 state. It pauses while the tab is hidden, so a forgotten page does not
142 keep asking. */}}
143 <nav class="tabs" aria-label="Repository"
ed03b3d1
Keenan Nemetz
3h ago
144 hx-get="/{{.Name}}/counts"
a2ec9561
Keenan Nemetz
9d ago
145 hx-trigger="every 30s [document.visibilityState === 'visible']"
146 hx-sync="this:replace"
147 hx-swap="none">
ed03b3d1
Keenan Nemetz
3h ago
148 <a class="repotab{{if eq .Tab "overview"}} active{{end}}" href="/{{.Name}}"{{if eq .Tab "overview"}} aria-current="page"{{end}}>{{icon "layout-dashboard"}}Overview</a>
149 <a href="/{{.Name}}/activity"{{if eq .Tab "activity"}} class="active" aria-current="page"{{end}}>{{icon "activity"}}Activity</a>
150 <a href="/{{.Name}}/code"{{if eq .Tab "code"}} class="active" aria-current="page"{{end}}>{{icon "code"}}Code</a>
151 <a href="/{{.Name}}/issues"{{if eq .Tab "issues"}} class="active" aria-current="page"{{end}}>{{icon "circle-dot"}}Issues {{template "repo_issue_count" .}}</a>
152 <a href="/{{.Name}}/changes"{{if eq .Tab "changes"}} class="active" aria-current="page"{{end}}>{{icon "git-pull-request"}}Changes {{template "repo_request_count" .}}</a>
153 <a href="/{{.Name}}/releases"{{if eq .Tab "releases"}} class="active" aria-current="page"{{end}}>{{icon "tag"}}Releases</a>
154 <a href="/{{.Name}}/runs"{{if eq .Tab "runs"}} class="active" aria-current="page"{{end}}>{{icon "play"}}Runs</a>
155 <a href="/{{.Name}}/locks"{{if eq .Tab "locks"}} class="active" aria-current="page"{{end}}>{{icon "lock"}}Locks</a>
156 {{if .CanAdmin}}<a href="/{{.Name}}/settings"{{if eq .Tab "settings"}} class="active" aria-current="page"{{end}}>{{icon "sliders-horizontal"}}Settings</a>{{end}}
f09ef39e
Keenan Nemetz
15d ago
157 </nav>
158 </div>
159 {{end}}
160
e84a66f5
Keenan Nemetz
15d ago
161 {{define "errorpage"}}
f09ef39e
Keenan Nemetz
15d ago
162 {{template "header" .}}
163 <div class="empty-page">
164 <p class="eyebrow">Error</p>
165 <h1>{{.Title}}</h1>
166 <p class="muted">{{.Message}}</p>
167 <p><a class="button" href="/">Back to repositories</a></p>
168 </div>
169 {{template "footer" .}}
170 {{end}}
171
e84a66f5
Keenan Nemetz
15d ago
172 {{define "notfound"}}{{template "errorpage" .}}{{end}}
173
174 {{define "servererror"}}{{template "errorpage" .}}{{end}}
1265ea55
Keenan Nemetz
9d ago
175
a6b42433
Keenan Nemetz
7d ago
176 {{/* An account name, as a link to the profile it belongs to. `user` returns an
177 href only when the name belongs to an account, so a commit author who never
178 registered, and a typo in a mention, stay text rather than becoming a link
179 that leads to a 404. Use `who` instead where a link would be wrong: inside
180 another link, inside a button, in an <option>, or in an attribute. */}}
181 {{define "userlink"}}{{if .Known}}<a class="user" href="{{.Href}}">{{.Name}}</a>{{else}}{{.Name}}{{end}}{{end}}
1265ea55
Keenan Nemetz
9d ago
182 {{/* The command palette's initial state and the rows HTMX swaps in. The dialog
183 shell above carries both, so they live beside the chrome that owns them. */}}
184 {{define "palette_hint"}}
185 <p class="palette-empty" role="option" aria-disabled="true">Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.</p>
186 {{end}}
187
188 {{define "palette_results"}}
189 {{if .Message}}
190 <p class="palette-empty" role="option" aria-disabled="true">{{.Message}}</p>
191 {{else if not .Query}}
192 {{template "palette_hint"}}
193 {{else if .Results}}
194 {{range .Results}}
195 <a class="palette-row" data-palette-row role="option" aria-selected="false" href="{{.URL}}">
196 <span class="palette-row-title">{{.Title}}</span>
197 <span class="palette-row-where">{{.KindLabel}} · {{.Repo}}{{if .Number}} · <span class="sha">{{num .Number}}</span>{{end}}</span>
198 {{if .Snippet}}<span class="search-snippet">{{.Snippet}}</span>{{end}}
199 </a>
200 {{end}}
201 {{if .Limited}}<p class="palette-empty" role="option" aria-disabled="true">Showing the first results. Add a word to narrow the search.</p>{{end}}
202 {{else}}
203 <p class="palette-empty" role="option" aria-disabled="true">Nothing matches “{{.Query}}”.</p>
204 {{end}}
205 {{end}}