internal/web/templates/layout.html

8e6fc2062ca26f7a848bdf6ad6345fd345351231
gitbay/internal/web/templates/layout.html history · blame · raw

192 lines · 14195 bytes

  1{{define "layout"}}<!DOCTYPE html>
  2<html lang="en">
  3<head>
  4<meta charset="utf-8">
  5<meta name="viewport" content="width=device-width, initial-scale=1">
  6<title>{{template "title" .}}</title>
  7<link rel="stylesheet" href="/static/style.css">
  8<link rel="icon" href="/favicon.svg" type="image/svg+xml">
  9{{with field . "Feed"}}<link rel="alternate" type="application/atom+xml" href="{{.}}">
 10{{end}}</head>
 11<body>
 12<a class="skip" href="#content">Skip to content</a>
 13<div class="shell">
 14
 15{{$here := ""}}{{with field . "Repo"}}{{$here = printf "%s/%s" .OwnerName .Name}}{{end}}
 16<nav class="rail" aria-label="Site">
 17  <a class="brand" href="/">{{template "mark"}}<span>{{.Site}}</span></a>
 18  <div class="railbody">
 19    <form method="get" action="/search" class="railsearch" role="search">
 20      <input type="search" name="q" aria-label="Search {{.Site}}" placeholder="Search">
 21    </form>
 22    <ul class="raillist">
 23      {{if .Viewer}}<li><a href="/">Dashboard</a></li>{{end}}
 24      <li><a {{if eq (str . "Tab") "explore"}}aria-current="page" {{end}}href="/explore">Explore</a></li>
 25      {{if .Viewer}}<li><a {{if eq (str . "Tab") "notifications"}}aria-current="page" {{end}}href="/notifications">Notifications{{with .Rail.Unread}} <b class="count">{{.}}</b>{{end}}</a></li>
 26      <li><a {{if eq (str . "Tab") "bookmarks"}}aria-current="page" {{end}}href="/bookmarks">Bookmarks</a></li>
 27      <li><a href="/new">New repository</a></li>{{end}}
 28    </ul>
 29    {{with .Rail.Pinned}}
 30    <div class="railgroup">
 31    <p class="raillabel" id="rail-pinned">Pinned</p>
 32    <ul class="raillist" aria-labelledby="rail-pinned">
 33      {{range .}}<li><a {{if eq $here (printf "%s/%s" .Owner .Name)}}aria-current="page" {{end}}href="/{{.Owner}}/{{.Name}}"><span class="owner">{{.Owner}}/</span>{{.Name}}</a></li>
 34      {{end}}
 35    </ul>
 36    </div>
 37    {{end}}
 38    {{with .Rail.Reviews}}
 39    <div class="railgroup">
 40    <p class="raillabel" id="rail-reviews">Waiting on you <b class="count">{{len .}}</b></p>
 41    <ul class="raillist wide" aria-labelledby="rail-reviews">
 42      {{range .}}<li><a href="/{{.RepoPath}}/mrs/{{.Number}}"><b>!{{.Number}} {{.Title}}</b><span class="owner">{{.RepoPath}}</span></a></li>
 43      {{end}}
 44    </ul>
 45    </div>
 46    {{end}}
 47  </div>
 48  <ul class="raillist railpinned">
 49    {{if .Viewer}}<li><a {{if eq (str . "Tab") "account"}}aria-current="page" {{end}}href="/settings">Settings</a></li>{{end}}
 50    {{if .Admin}}<li><a {{if eq (str . "Tab") "admin"}}aria-current="page" {{end}}href="/admin">Admin</a></li>{{end}}
 51  </ul>
 52  <div class="railfoot">
 53    {{if .Viewer}}<a class="railuser" href="/{{.Viewer}}"><span class="avatar">{{initial .Viewer}}</span><span class="uname">{{.Viewer}}</span></a>
 54    <form method="post" action="/logout"><button type="submit" class="linklike">Log out</button></form>
 55    {{else}}<a href="/login">Sign in</a>{{end}}
 56  </div>
 57</nav>
 58
 59<div class="pane">
 60{{with field . "Repo"}}
 61<header class="repohead">
 62  <div class="identity">
 63    {{if field $ "RepoHome"}}<h1 class="repotitle"><a class="owner" href="/{{.OwnerName}}">{{.OwnerName}}</a><span class="sep">/</span>{{.Name}}</h1>
 64    {{else}}<p class="repotitle"><a class="owner" href="/{{.OwnerName}}">{{.OwnerName}}</a><span class="sep">/</span><a href="/{{.OwnerName}}/{{.Name}}">{{.Name}}</a></p>{{end}}
 65    {{if eq .Visibility "private"}}<span class="chip">Private</span>{{end}}
 66    {{if .Settings.Archived}}<span class="chip">Archived</span>{{end}}
 67    <span class="grow"></span>
 68    {{if $.Viewer}}<form method="post" action="/{{.OwnerName}}/{{.Name}}/pin" class="inline"><button type="submit" class="btn" aria-pressed="{{if field $ "Pinned"}}true{{else}}false{{end}}"><span aria-hidden="true">{{if field $ "Pinned"}}★{{else}}☆{{end}}</span> {{if field $ "Pinned"}}Pinned{{else}}Pin{{end}}</button></form>
 69    <form method="post" action="/{{.OwnerName}}/{{.Name}}/watch" class="inline"><button type="submit" class="btn" aria-pressed="{{if eq (str $ "Watch") "watching"}}true{{else}}false{{end}}">{{if eq (str $ "Watch") "watching"}}Watching{{else}}Watch{{end}}</button></form>
 70    <form method="post" action="/{{.OwnerName}}/{{.Name}}/bookmark" class="inline"><button type="submit" class="btn" aria-pressed="{{if field $ "Marked"}}true{{else}}false{{end}}">{{if field $ "Marked"}}Bookmarked{{else}}Bookmark{{end}}</button></form>
 71    <form method="post" action="/{{.OwnerName}}/{{.Name}}/fork" class="inline"><button type="submit" class="btn">Fork</button></form>{{end}}
 72  </div>
 73  {{$top := topTab (str $ "Tab")}}
 74  {{/* The header is the same on every tab: it sits above the tab bar,
 75       and anything shown on one tab and not another would move the
 76       navigation between clicks of that navigation. */}}
 77  {{with field $ "Desc"}}<p class="repodesc">{{.}}</p>{{end}}
 78  {{with field $ "Topics"}}<p class="topics">{{range .}}<a class="chip topic" href="/explore?q={{.}}">{{.}}</a> {{end}}</p>{{end}}
 79  {{with $.Repo.Settings.Website}}<p class="repometa"><a href="{{.}}" rel="nofollow">{{.}}</a></p>{{end}}
 80  {{range field $ "Mirrors"}}<p class="repometa">{{if eq .Direction "push"}}Mirrors to{{else}}Mirrors from{{end}} <a href="{{.URL}}" rel="nofollow">{{.Target}}</a>{{if .Error}} · <span class="bad">sync error: {{.Error}}</span>{{else if .Synced}} · synced {{.Synced}}{{end}}</p>
 81  {{end}}
 82  <nav class="tabs" aria-label="Repository">
 83    <a {{if eq $top "code"}}aria-current="page" {{end}}href="/{{.OwnerName}}/{{.Name}}">Code</a>
 84    <a {{if eq $top "issues"}}aria-current="page" {{end}}href="/{{.OwnerName}}/{{.Name}}/issues">Issues{{with field $ "OpenIssues"}}{{if .}} <i>{{.}}</i>{{end}}{{end}}</a>
 85    <a {{if eq $top "mrs"}}aria-current="page" {{end}}href="/{{.OwnerName}}/{{.Name}}/mrs">Merge requests{{with field $ "OpenMRs"}}{{if .}} <i>{{.}}</i>{{end}}{{end}}</a>
 86    <a {{if eq $top "builds"}}aria-current="page" {{end}}href="/{{.OwnerName}}/{{.Name}}/builds">Builds</a>
 87    <a {{if eq $top "releases"}}aria-current="page" {{end}}href="/{{.OwnerName}}/{{.Name}}/releases">Releases</a>
 88    {{if field $ "HasWiki"}}<a {{if eq $top "wiki"}}aria-current="page" {{end}}href="/{{.OwnerName}}/{{.Name}}/wiki">Wiki</a>{{end}}
 89    {{if field $ "CanAdmin"}}<a {{if eq $top "settings"}}aria-current="page" {{end}}href="/{{.OwnerName}}/{{.Name}}/settings">Settings</a>{{end}}
 90  </nav>
 91</header>
 92{{end}}
 93
 94<main id="content" class="content">
 95{{template "content" .}}
 96</main>
 97
 98<footer>
 99  <p>Powered by <a href="https://gitbay.org/krz/gitbay">gitbay</a>{{with gitbayVersion}} · <code><a href="https://gitbay.org/krz/gitbay/commit/{{gitbayCommit}}">{{.}}</a></code>{{end}} · <a href="/privacy">Privacy</a></p>
100</footer>
101</div>
102</div>
103</body>
104</html>{{end}}
105
106{{define "mark"}}<svg class="mark" width="19" height="19" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2.25 18.75 12H5.25z" fill="#ff6b3d"/><rect x="3" y="13.5" width="18" height="3" fill="currentColor"/><rect x="7.5" y="18" width="9" height="3" fill="currentColor"/></svg>{{end}}
107
108{{define "reporow"}}<li>
109  <p class="reponame"><a href="/{{.OwnerName}}/{{.Name}}">{{.OwnerName}}<span class="sep">/</span><strong>{{.Name}}</strong></a>{{if eq .Visibility "private"}} <span class="chip">Private</span>{{end}}{{if .Archived}} <span class="chip">Archived</span>{{end}}</p>
110  {{if .Desc}}<p class="desc">{{.Desc}}</p>{{end}}
111  {{if .Topics}}<p class="topics">{{range .Topics}}<a class="chip topic" href="/explore?q={{.}}">{{.}}</a> {{end}}</p>{{end}}
112  <p class="meta">{{template "branchicon"}} {{.DefaultBranch}}{{if .License}} · {{.License}}{{end}}{{if .Updated}} · updated {{.Updated}}{{end}}</p>
113</li>{{end}}
114
115{{define "refmenu"}}{{if .Branches}}<details class="refmenu">
116  <summary class="refchip">{{template "branchicon"}} {{.Ref}}</summary>
117  <div class="refdrop">
118  {{range .Branches}}<a href="/{{$.Repo.OwnerName}}/{{$.Repo.Name}}/{{$.RefKind}}/{{.Name}}/{{$.DirPath}}">{{.Name}}</a>
119  {{end}}<a class="allrefs" href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/refs">All refs →</a>
120  </div>
121</details>{{else}}<span class="refchip">{{template "branchicon"}} {{.Ref}}</span>{{end}}{{end}}
122
123{{define "branchicon"}}<svg class="icon" width="12" height="12" viewBox="0 0 16 16" aria-hidden="true" fill="currentColor"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628a2.25 2.25 0 0 1-1.5-2.122ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM3.5 3.25a.75.75 0 1 1 1.5 0 .75.75 0 0 1-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"/></svg>{{end}}
124
125{{define "sigbadge"}}<span class="badge badge-{{.State}}" title="{{.Fingerprint}}">{{sigLabel .State}}{{if .Signer}} · {{.Signer}}{{end}}</span>{{end}}
126
127{{define "authorname"}}{{if .User}}<a class="authorlink" href="/{{.User}}" title="{{.Email}}">{{.Name}}</a>{{else}}<span title="{{.Email}}">{{.Name}}</span>{{end}}{{end}}
128
129{{/* formatpicker is the md/org choice on a body: issue and MR create, and
130     a profile's about text. The argument is the currently selected
131     format; empty selects Markdown, today's default. */}}
132{{define "formatpicker"}}<p class="branchpick">
133  <label for="format">Body markup</label>
134  <select id="format" name="format">
135    <option value="md"{{if ne . "org"}} selected{{end}}>Markdown</option>
136    <option value="org"{{if eq . "org"}} selected{{end}}>Org</option>
137  </select>
138</p>{{end}}
139
140{{/* confirmfield is the typed-name check beside a destructive control.
141     The argument is the text the person must type. */}}
142{{define "confirmfield"}}<input type="text" name="confirm" aria-label="Type {{.}} to confirm" placeholder="type {{.}} to confirm" size="{{len .}}" autocomplete="off">{{end}}
143
144{{/* difffiles renders a parsed diff: one foldable section per file, with
145     line-number gutters and review threads inline. Base is the MR's
146     endpoint and Viewer the signed-in account; the commit page passes
147     neither and gets the same diff without review controls. */}}
148{{define "difffiles"}}{{$base := .Base}}{{$viewer := .Viewer}}
149{{range $fi, $f := .Files}}<details class="difffold" id="f{{$fi}}"{{if .Open}} open{{end}}>
150  <summary>
151    <span class="fpath">{{if eq .Status "renamed"}}<span class="was">{{.OldPath}} →</span> {{end}}{{.Path}}</span>
152    {{if ne .Status "modified"}}<span class="chip">{{.Status}}</span>{{end}}
153    <span class="fstat">{{if .Adds}}<span class="add">+{{.Adds}}</span>{{end}}{{if .Dels}} <span class="del">−{{.Dels}}</span>{{end}}</span>
154  </summary>
155  {{if .Binary}}<p class="none">Binary file not shown.</p>
156  {{else}}<div class="tablewrap"><table class="difftable">
157  {{range .Lines}}{{if eq .Class "hunk"}}<tr class="hunk"><td class="ln" colspan="2"></td><td class="src">{{.Text}}</td></tr>
158  {{else if eq .Class "meta"}}<tr class="dmeta"><td class="ln" colspan="2"></td><td class="src">{{.Text}}</td></tr>
159  {{else}}<tr class="{{.Class}}" id="f{{$fi}}-{{if eq .Class "del"}}o{{.OldLine}}{{else}}n{{.NewLine}}{{end}}"><td class="ln">{{if .OldLine}}{{if eq .Class "del"}}{{template "cmtln" dict "N" .OldLine "Path" .Path "Side" "old" "Base" $base "Viewer" $viewer}}{{else}}{{.OldLine}}{{end}}{{end}}</td><td class="ln">{{if .NewLine}}{{template "cmtln" dict "N" .NewLine "Path" .Path "Side" "new" "Base" $base "Viewer" $viewer}}{{end}}</td><td class="src chroma">{{if .Code}}{{.Code}}{{else}}{{.Content}}{{end}}</td></tr>
160  {{if .Compose}}<tr class="threadrow"><td colspan="3"><form id="compose" method="post" action="{{$base}}/diff-comment" class="thread composing">
161    <input type="hidden" name="path" value="{{.Path}}">
162    <input type="hidden" name="line" value="{{if eq .Class "del"}}{{.OldLine}}{{else}}{{.NewLine}}{{end}}">
163    <input type="hidden" name="side" value="{{if eq .Class "del"}}old{{else}}new{{end}}">
164    <p><textarea name="body" aria-label="Comment on {{.Path}}" rows="3" placeholder="Comment on this line" autofocus></textarea></p>
165    <p><button type="submit">Comment</button>
166       <button type="submit" name="pending" value="on">Add to review</button>
167       <a href="{{$base}}?view=diff">Cancel</a></p>
168  </form></td></tr>
169  {{end}}{{end}}{{range .Threads}}<tr class="threadrow"><td colspan="3">{{template "thread" dict "T" . "Base" $base "Viewer" $viewer "Class" ""}}</td></tr>
170  {{end}}{{end}}
171  </table></div>{{end}}
172</details>
173{{end}}{{end}}
174
175{{/* cmtln turns a line number into the link that opens the comment form
176     on that line. No JavaScript: the anchor travels in the query. */}}
177{{define "cmtln"}}{{if and .Viewer .Base}}<a class="cmt" title="Comment on this line" href="{{.Base}}?view=diff&amp;cpath={{.Path}}&amp;cline={{.N}}&amp;cside={{.Side}}#compose">{{.N}}</a>{{else}}{{.N}}{{end}}{{end}}
178
179{{/* thread renders one review thread with its reply and resolve controls.
180     Class carries "stale" for threads whose anchor is gone. */}}
181{{define "thread"}}{{$t := .T}}<div class="thread{{if $t.Resolved}} resolved{{end}}{{if $t.Pending}} pending{{end}}{{if .Class}} {{.Class}}{{end}}" id="thread-{{$t.ID}}">
182{{if $t.Pending}}<p class="threadstate">pending — only you can see this until you submit your review</p>{{end}}
183{{if or $t.Resolved (and .Class $t.Stale)}}<p class="threadstate">{{if and .Class $t.Stale}}stale{{end}}{{if $t.Resolved}}{{if and .Class $t.Stale}} · {{end}}resolved by {{$t.Resolved}}{{end}}</p>{{end}}
184{{range $t.Comments}}<p class="commenthead"><strong>{{.Author}}</strong> <span class="when">{{when .CreatedAt}}</span></p><div class="rendered">{{.BodyHTML}}</div>{{end}}
185{{if .Viewer}}<details class="threadreply"><summary>Reply</summary>
186<form method="post" action="{{.Base}}/diff-comment">
187  <input type="hidden" name="reply" value="{{$t.ID}}">
188  <p><textarea name="body" aria-label="Reply to thread {{$t.ID}}" rows="2" placeholder="Reply as {{.Viewer}}"></textarea></p>
189  <p><button type="submit">Reply</button></p>
190</form></details>{{end}}
191{{if $t.CanResolve}}<form method="post" action="{{.Base}}/thread" class="threadact"><input type="hidden" name="thread" value="{{$t.ID}}"><button type="submit" name="action" value="{{if $t.Resolved}}unresolve{{else}}resolve{{end}}" class="linklike">{{if $t.Resolved}}Reopen thread{{else}}Resolve thread{{end}}</button></form>{{end}}
192</div>{{end}}