repository.ejs
37,062 bytes
| 1 | <%- include("partials/head", { title: repository.name, bodyClass: "viewer-page repository-page" }) %> |
|---|---|
| 2 | <header class="viewer-header"> |
| 3 | <a class="wordmark wordmark-light" href="/s/<%= share.id %>">profile<span>Share</span></a> |
| 4 | <form class="snapshot-search" action="/s/<%= share.id %>/search" method="get" role="search"> |
| 5 | <input type="hidden" name="repository" value="<%= repository.id %>"> |
| 6 | <% if (!selectedReference.isDefault) { %> |
| 7 | <input type="hidden" name="ref" value="<%= selectedReference.name %>"> |
| 8 | <input type="hidden" name="refType" value="<%= selectedReference.type %>"> |
| 9 | <% } %> |
| 10 | <svg width="16" height="16" viewBox="0 0 24 24" aria-hidden="true"><path d="M10.5 3a7.5 7.5 0 1 1-4.7 13.34l-3.07 3.07-1.42-1.42 3.08-3.07A7.5 7.5 0 0 1 10.5 3Zm0 2a5.5 5.5 0 1 0 0 11 5.5 5.5 0 0 0 0-11Z"/></svg> |
| 11 | <input data-search-input name="q" aria-label="Search <%= repository.name %>" placeholder="Search <%= repository.name %>"> |
| 12 | <kbd>/</kbd> |
| 13 | <button class="sr-only" type="submit">Search</button> |
| 14 | </form> |
| 15 | <nav class="top-nav" aria-label="Snapshot navigation"> |
| 16 | <a href="/s/<%= share.id %>">← Back to profile</a> |
| 17 | <span class="meta-divider"></span> |
| 18 | <span>Snapshot from <%= formatDate(share.created_at) %></span> |
| 19 | </nav> |
| 20 | </header> |
| 21 | |
| 22 | <section class="repo-mast"> |
| 23 | <div class="repo-mast-inner"> |
| 24 | <div class="repo-identity"> |
| 25 | <span class="repo-mark" aria-hidden="true"> |
| 26 | <svg class="repo-icon repo-icon-mark" width="20" height="20" viewBox="0 0 24 24"><path d="M4 4.75A2.75 2.75 0 0 1 6.75 2h10.5A2.75 2.75 0 0 1 20 4.75v14.5A2.75 2.75 0 0 1 17.25 22H6.75A2.75 2.75 0 0 1 4 19.25V4.75Zm3.5-.75A1.5 1.5 0 0 0 6 5.5v13A1.5 1.5 0 0 0 7.5 20H9V4H7.5Z"/></svg> |
| 27 | </span> |
| 28 | <h1> |
| 29 | <a href="/s/<%= share.id %>"><%= share.snapshot.profile.login %></a> |
| 30 | <span>/</span> |
| 31 | <strong><%= repository.name %></strong> |
| 32 | </h1> |
| 33 | <span class="repo-visibility">Read-only snapshot</span> |
| 34 | </div> |
| 35 | <p class="repo-summary"><%= repository.description || "No repository description." %></p> |
| 36 | <div class="repo-facts"> |
| 37 | <span><strong><%= selectedReference.name %></strong> <%= selectedReference.isDefault ? "default branch" : selectedReference.type %></span> |
| 38 | <span><strong><%= repository.files.length %></strong> files</span> |
| 39 | <span>Expires <strong><%= formatDate(share.expires_at) %></strong></span> |
| 40 | </div> |
| 41 | </div> |
| 42 | </section> |
| 43 | |
| 44 | <nav class="repo-tabs" aria-label="Repository sections"> |
| 45 | <div class="repo-tabs-inner"> |
| 46 | <a class="<%= tab === 'code' && !commit ? 'active' : '' %>" href="<%= repoHref() %>" <%- tab === "code" && !commit ? 'aria-current="page"' : "" %>> |
| 47 | <svg class="repo-icon repo-icon-tab" width="16" height="16" viewBox="0 0 24 24" aria-hidden="true"><path d="m8.7 17.3-5-5a1 1 0 0 1 0-1.4l5-5 1.4 1.4L5.8 11.6l4.3 4.3-1.4 1.4Zm6.6 0-1.4-1.4 4.3-4.3-4.3-4.3 1.4-1.4 5 5a1 1 0 0 1 0 1.4l-5 5Z"/></svg> |
| 48 | Code |
| 49 | </a> |
| 50 | <a class="<%= tab === 'issues' ? 'active' : '' %>" href="<%= globalRepoHref({ tab: 'issues' }) %>" <%- tab === "issues" ? 'aria-current="page"' : "" %>> |
| 51 | <svg class="repo-icon repo-icon-tab" width="16" height="16" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2a10 10 0 1 1 0 20 10 10 0 0 1 0-20Zm0 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm-1 3h2v7h-2V7Zm0 9h2v2h-2v-2Z"/></svg> |
| 52 | Issues |
| 53 | <span class="tab-count"><%= repository.issues.filter((item) => item.state === "open").length %></span> |
| 54 | </a> |
| 55 | <a class="<%= tab === 'pulls' ? 'active' : '' %>" href="<%= globalRepoHref({ tab: 'pulls' }) %>" <%- tab === "pulls" ? 'aria-current="page"' : "" %>> |
| 56 | <svg class="repo-icon repo-icon-tab" width="16" height="16" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 3a3 3 0 1 1-1 5.83v6.34A3 3 0 1 1 7 15V8.83A3 3 0 0 1 6 3Zm0 2a1 1 0 1 0 0 2 1 1 0 0 0 0-2Zm0 12a1 1 0 1 0 0 2 1 1 0 0 0 0-2Zm12-2a3 3 0 1 1-2 2.83V15a4 4 0 0 0-4-4h-1V8l-4 4 4 4v-3h1a2 2 0 0 1 2 2v.17A3 3 0 0 1 18 15Zm0 2a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z"/></svg> |
| 57 | Pull requests |
| 58 | <span class="tab-count"><%= repository.pullRequests.filter((item) => item.state === "open").length %></span> |
| 59 | </a> |
| 60 | <a class="<%= tab === 'releases' ? 'active' : '' %>" href="<%= globalRepoHref({ tab: 'releases' }) %>" <%- tab === "releases" ? 'aria-current="page"' : "" %>> |
| 61 | <svg class="repo-icon repo-icon-tab" width="16" height="16" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 4a1 1 0 0 1 1-1h7.59a1 1 0 0 1 .7.29l8.42 8.42a1 1 0 0 1 0 1.41l-7.59 7.59a1 1 0 0 1-1.41 0L3.29 12.3A1 1 0 0 1 3 11.59V4Zm2 1v6.17l7.41 7.42 6.18-6.18L11.17 5H5Zm3 2a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Z"/></svg> |
| 62 | Releases |
| 63 | <span class="tab-count"><%= repository.releases.length %></span> |
| 64 | </a> |
| 65 | <a class="<%= tab === 'commits' || commit ? 'active' : '' %>" href="<%= repoHref({ tab: 'commits' }) %>" <%- tab === "commits" || commit ? 'aria-current="page"' : "" %>> |
| 66 | <svg class="repo-icon repo-icon-tab" width="16" height="16" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 7a5 5 0 1 1-4.9 6H2v-2h5.1A5 5 0 0 1 12 7Zm0 2a3 3 0 1 0 0 6 3 3 0 0 0 0-6Zm4.9 2H22v2h-5.1v-2Z"/></svg> |
| 67 | Commits |
| 68 | <span class="tab-count"><%= repository.commits.length %></span> |
| 69 | </a> |
| 70 | </div> |
| 71 | </nav> |
| 72 | |
| 73 | <main class="repository-shell"> |
| 74 | <% if (commit) { %> |
| 75 | <nav class="repo-breadcrumbs" aria-label="Commit navigation"> |
| 76 | <a href="<%= repoHref({ tab: 'commits' }) %>">Commits</a> |
| 77 | <span>/</span> |
| 78 | <span><%= commit.sha.slice(0, 7) %></span> |
| 79 | </nav> |
| 80 | <section class="commit-detail-card"> |
| 81 | <header class="commit-detail-heading"> |
| 82 | <div class="commit-detail-main"> |
| 83 | <p class="section-kicker">Commit</p> |
| 84 | <h2><%= commit.message.split("\n")[0] %></h2> |
| 85 | <% const commitBody = commit.message.split("\n").slice(1).join("\n").trim(); %> |
| 86 | <% if (commitBody) { %><pre class="commit-message-body"><%= commitBody %></pre><% } %> |
| 87 | <p class="commit-detail-byline"> |
| 88 | <span class="commit-avatar" aria-hidden="true"><%= commit.author.slice(0, 1).toUpperCase() %></span> |
| 89 | <strong><%= commit.author %></strong> committed <%= formatDate(commit.date) %> |
| 90 | </p> |
| 91 | </div> |
| 92 | <div class="commit-detail-side"> |
| 93 | <span class="commit-sha"> |
| 94 | <span>commit</span> |
| 95 | <code><%= commit.sha.slice(0, 7) %></code> |
| 96 | </span> |
| 97 | <button class="quiet-button" type="button" data-copy-text="<%= commit.sha %>">Copy SHA</button> |
| 98 | </div> |
| 99 | </header> |
| 100 | <%- include("partials/diff-list", { |
| 101 | files: commit.files, |
| 102 | diffAdditions: commit.additions, |
| 103 | diffDeletions: commit.deletions, |
| 104 | emptyMessage: "No changed files are available for this commit." |
| 105 | }) %> |
| 106 | </section> |
| 107 | <% } else if (issue) { %> |
| 108 | <nav class="repo-breadcrumbs" aria-label="Issue navigation"> |
| 109 | <a href="<%= globalRepoHref({ tab: 'issues' }) %>">Issues</a> |
| 110 | <span>/</span> |
| 111 | <span>#<%= issue.number %></span> |
| 112 | </nav> |
| 113 | <section class="discussion-page"> |
| 114 | <header class="work-detail-heading"> |
| 115 | <div> |
| 116 | <h2><%= issue.title %> <span>#<%= issue.number %></span></h2> |
| 117 | <p> |
| 118 | <span class="state-badge <%= issue.state %>"><%= issue.state %></span> |
| 119 | <strong><%= issue.author.login %></strong> opened this issue on <%= formatDate(issue.createdAt) %> |
| 120 | · <%= issue.comments.length %> <%= issue.comments.length === 1 ? "comment" : "comments" %> |
| 121 | </p> |
| 122 | </div> |
| 123 | <span class="read-only-badge">Read only</span> |
| 124 | </header> |
| 125 | <div class="discussion-layout"> |
| 126 | <div class="discussion-timeline"> |
| 127 | <article class="discussion-card"> |
| 128 | <header> |
| 129 | <strong><%= issue.author.login %></strong> |
| 130 | <span>opened on <%= formatDate(issue.createdAt) %></span> |
| 131 | </header> |
| 132 | <div class="discussion-body"> |
| 133 | <% if (issue.body) { %><%- renderMarkdown(issue.body) %><% } else { %><p>No description was provided.</p><% } %> |
| 134 | </div> |
| 135 | </article> |
| 136 | <% issue.comments.forEach((comment) => { %> |
| 137 | <article class="discussion-card"> |
| 138 | <header> |
| 139 | <strong><%= comment.author.login %></strong> |
| 140 | <span>commented on <%= formatDate(comment.createdAt) %></span> |
| 141 | </header> |
| 142 | <div class="discussion-body"> |
| 143 | <% if (comment.body) { %><%- renderMarkdown(comment.body) %><% } else { %><p>No comment text was included.</p><% } %> |
| 144 | </div> |
| 145 | </article> |
| 146 | <% }) %> |
| 147 | </div> |
| 148 | <aside class="work-sidebar"> |
| 149 | <section> |
| 150 | <strong>Labels</strong> |
| 151 | <div class="work-labels"> |
| 152 | <% issue.labels.forEach((label) => { %><span class="work-label"><%= label.name %></span><% }) %> |
| 153 | <% if (!issue.labels.length) { %><span>None</span><% } %> |
| 154 | </div> |
| 155 | </section> |
| 156 | <section> |
| 157 | <strong>Status</strong> |
| 158 | <span><%= issue.stateReason || issue.state %></span> |
| 159 | </section> |
| 160 | </aside> |
| 161 | </div> |
| 162 | </section> |
| 163 | <% } else if (pullRequest) { %> |
| 164 | <nav class="repo-breadcrumbs" aria-label="Pull request navigation"> |
| 165 | <a href="<%= globalRepoHref({ tab: 'pulls' }) %>">Pull requests</a> |
| 166 | <span>/</span> |
| 167 | <span>#<%= pullRequest.number %></span> |
| 168 | </nav> |
| 169 | <section class="discussion-page"> |
| 170 | <header class="work-detail-heading"> |
| 171 | <div> |
| 172 | <h2><%= pullRequest.title %> <span>#<%= pullRequest.number %></span></h2> |
| 173 | <p> |
| 174 | <% const pullState = pullRequest.draft ? "draft" : pullRequest.merged ? "merged" : pullRequest.state; %> |
| 175 | <span class="state-badge <%= pullState %>"><%= pullState %></span> |
| 176 | <strong><%= pullRequest.author.login %></strong> opened this pull request on <%= formatDate(pullRequest.createdAt) %> |
| 177 | </p> |
| 178 | </div> |
| 179 | <span class="read-only-badge">Read only</span> |
| 180 | </header> |
| 181 | <div class="pull-summary"> |
| 182 | <span><code><%= pullRequest.head %></code> into <code><%= pullRequest.base %></code></span> |
| 183 | <span><strong><%= pullRequest.commitCount %></strong> commits</span> |
| 184 | <span><strong><%= pullRequest.changedFiles %></strong> changed files</span> |
| 185 | <span class="commit-change"><b>+<%= pullRequest.additions %></b> <i>−<%= pullRequest.deletions %></i></span> |
| 186 | </div> |
| 187 | <div class="discussion-layout"> |
| 188 | <div class="discussion-timeline"> |
| 189 | <article class="discussion-card"> |
| 190 | <header> |
| 191 | <strong><%= pullRequest.author.login %></strong> |
| 192 | <span>opened on <%= formatDate(pullRequest.createdAt) %></span> |
| 193 | </header> |
| 194 | <div class="discussion-body"> |
| 195 | <% if (pullRequest.body) { %><%- renderMarkdown(pullRequest.body) %><% } else { %><p>No description was provided.</p><% } %> |
| 196 | </div> |
| 197 | </article> |
| 198 | <% pullRequest.conversation.forEach((comment) => { %> |
| 199 | <article class="discussion-card"> |
| 200 | <header> |
| 201 | <strong><%= comment.author.login %></strong> |
| 202 | <span> |
| 203 | <% if (comment.type === "review") { %> |
| 204 | submitted a <%= (comment.state || "review").toLocaleLowerCase("en") %> review on <%= formatDate(comment.createdAt) %> |
| 205 | <% } else if (comment.type === "review-comment") { %> |
| 206 | reviewed <%= comment.path || "a changed file" %><%= comment.line ? " at line " + comment.line : "" %> on <%= formatDate(comment.createdAt) %> |
| 207 | <% } else { %> |
| 208 | commented on <%= formatDate(comment.createdAt) %> |
| 209 | <% } %> |
| 210 | </span> |
| 211 | </header> |
| 212 | <div class="discussion-body"> |
| 213 | <% if (comment.body) { %><%- renderMarkdown(comment.body) %><% } else { %><p>No review text was included.</p><% } %> |
| 214 | </div> |
| 215 | </article> |
| 216 | <% }) %> |
| 217 | <section class="pull-files"> |
| 218 | <h3>Files changed <span><%= pullRequest.files.length %></span></h3> |
| 219 | <%- include("partials/diff-list", { |
| 220 | files: pullRequest.files, |
| 221 | diffAdditions: pullRequest.additions, |
| 222 | diffDeletions: pullRequest.deletions, |
| 223 | emptyMessage: "No changed files are included in this snapshot." |
| 224 | }) %> |
| 225 | </section> |
| 226 | </div> |
| 227 | <aside class="work-sidebar"> |
| 228 | <section> |
| 229 | <strong>Labels</strong> |
| 230 | <div class="work-labels"> |
| 231 | <% pullRequest.labels.forEach((label) => { %><span class="work-label"><%= label.name %></span><% }) %> |
| 232 | <% if (!pullRequest.labels.length) { %><span>None</span><% } %> |
| 233 | </div> |
| 234 | </section> |
| 235 | <section> |
| 236 | <strong>Merge status</strong> |
| 237 | <span><%= pullRequest.merged ? "Merged" : pullRequest.draft ? "Draft" : pullRequest.mergeableState || pullRequest.state %></span> |
| 238 | </section> |
| 239 | </aside> |
| 240 | </div> |
| 241 | </section> |
| 242 | <% } else if (release) { %> |
| 243 | <nav class="repo-breadcrumbs" aria-label="Release navigation"> |
| 244 | <a href="<%= globalRepoHref({ tab: 'releases' }) %>">Releases</a> |
| 245 | <span>/</span> |
| 246 | <span><%= release.tagName %></span> |
| 247 | </nav> |
| 248 | <article class="release-detail"> |
| 249 | <header> |
| 250 | <div> |
| 251 | <div class="release-kicker"> |
| 252 | <span class="release-tag"><%= release.tagName %></span> |
| 253 | <% if (release.draft) { %><span class="release-status">Draft</span><% } %> |
| 254 | <% if (release.prerelease) { %><span class="release-status">Pre-release</span><% } %> |
| 255 | </div> |
| 256 | <h2><%= release.name %></h2> |
| 257 | <p> |
| 258 | <strong><%= release.author.login %></strong> |
| 259 | <%= release.publishedAt ? "published" : "created" %> this release on |
| 260 | <%= formatDate(release.publishedAt || release.createdAt) %> |
| 261 | </p> |
| 262 | </div> |
| 263 | <% const releaseTag = references.tags.find((tag) => tag.name === release.tagName); %> |
| 264 | <% if (releaseTag) { %><a class="quiet-button" href="<%= refHref({ ...releaseTag, type: 'tag' }) %>">Browse tag</a><% } %> |
| 265 | </header> |
| 266 | <div class="release-body discussion-body"> |
| 267 | <% if (release.body) { %><%- renderMarkdown(release.body) %><% } else { %><p>No release notes were provided.</p><% } %> |
| 268 | </div> |
| 269 | <section class="release-assets"> |
| 270 | <h3>Assets <span><%= release.assets.length %></span></h3> |
| 271 | <% if (release.assets.length) { %> |
| 272 | <ul> |
| 273 | <% release.assets.forEach((asset) => { %> |
| 274 | <li> |
| 275 | <strong><%= asset.label || asset.name %></strong> |
| 276 | <% if (asset.label) { %><code><%= asset.name %></code><% } %> |
| 277 | <span><%= asset.size.toLocaleString() %> bytes · <%= asset.downloadCount.toLocaleString() %> GitHub downloads</span> |
| 278 | </li> |
| 279 | <% }) %> |
| 280 | </ul> |
| 281 | <p>Assets are listed for reference and are not downloadable from this read-only snapshot.</p> |
| 282 | <% } else { %> |
| 283 | <p>No release assets are included.</p> |
| 284 | <% } %> |
| 285 | </section> |
| 286 | </article> |
| 287 | <% } else if (historyFile) { %> |
| 288 | <nav class="repo-breadcrumbs" aria-label="File history navigation"> |
| 289 | <a href="<%= repoHref() %>"><%= repository.name %></a> |
| 290 | <span>/</span> |
| 291 | <a href="<%= repoHref({ file: historyFile.path }) %>"><%= historyFile.path %></a> |
| 292 | <span>/</span> |
| 293 | <span>History</span> |
| 294 | </nav> |
| 295 | <section class="commits-page file-history-page"> |
| 296 | <header class="section-title-row"> |
| 297 | <div> |
| 298 | <p class="section-kicker">File history</p> |
| 299 | <h2>History for <code><%= historyFile.path %></code></h2> |
| 300 | </div> |
| 301 | <a class="quiet-button" href="<%= repoHref({ file: historyFile.path }) %>">View file</a> |
| 302 | </header> |
| 303 | <ol class="commit-feed"> |
| 304 | <% fileHistory.forEach((item) => { %> |
| 305 | <li> |
| 306 | <span class="commit-avatar" aria-hidden="true"><%= item.author.slice(0, 1).toUpperCase() %></span> |
| 307 | <a class="commit-main" href="<%= repoHref({ commit: item.sha }) %>"> |
| 308 | <strong><%= item.message.split("\n")[0] %></strong> |
| 309 | <span><%= item.author %> committed <%= formatDate(item.date) %></span> |
| 310 | </a> |
| 311 | <span class="commit-change"><b>+<%= item.fileChange.additions %></b> <i>−<%= item.fileChange.deletions %></i></span> |
| 312 | <code><%= item.sha.slice(0, 7) %></code> |
| 313 | </li> |
| 314 | <% }) %> |
| 315 | <% if (!fileHistory.length) { %> |
| 316 | <li class="empty-state">No file history is included in this snapshot.</li> |
| 317 | <% } %> |
| 318 | </ol> |
| 319 | </section> |
| 320 | <% } else if (file) { %> |
| 321 | <nav class="repo-breadcrumbs" aria-label="File path"> |
| 322 | <a href="<%= repoHref() %>"><%= repository.name %></a> |
| 323 | <% let filePath = ""; file.path.split("/").forEach((part, index, parts) => { filePath += (filePath ? "/" : "") + part; %> |
| 324 | <span>/</span> |
| 325 | <% if (index < parts.length - 1) { %> |
| 326 | <a href="<%= repoHref({ path: filePath }) %>"><%= part %></a> |
| 327 | <% } else { %> |
| 328 | <span><%= part %></span> |
| 329 | <% } %> |
| 330 | <% }) %> |
| 331 | </nav> |
| 332 | <section class="file-view-card"> |
| 333 | <header class="file-view-header"> |
| 334 | <div> |
| 335 | <strong><%= file.path.split("/").pop() %></strong> |
| 336 | <span><%= file.size.toLocaleString() %> bytes</span> |
| 337 | </div> |
| 338 | <div class="file-view-actions"> |
| 339 | <button class="quiet-button copy-path" type="button" data-copy-text="<%= file.path %>">Copy path</button> |
| 340 | <a class="quiet-button" href="<%= repoHref({ history: file.path }) %>">History</a> |
| 341 | <a href="<%= repoHref({ path: file.path.includes('/') ? file.path.slice(0, file.path.lastIndexOf('/')) : '' }) %>">← Back to files</a> |
| 342 | </div> |
| 343 | </header> |
| 344 | <% if (markdownFile) { %> |
| 345 | <nav class="file-view-tabs" aria-label="File view"> |
| 346 | <a class="<%= fileView === 'preview' ? 'active' : '' %>" href="<%= repoHref({ file: file.path }) %>"<% if (fileView === "preview") { %> aria-current="page"<% } %>>Preview</a> |
| 347 | <a class="<%= fileView === 'source' ? 'active' : '' %>" href="<%= repoHref({ file: file.path, view: 'source' }) %>"<% if (fileView === "source") { %> aria-current="page"<% } %>>Source</a> |
| 348 | </nav> |
| 349 | <% } %> |
| 350 | <% if (markdownFile && fileView === "preview" && file.content !== null) { %> |
| 351 | <article class="markdown-file-preview markdown-body"><%- renderMarkdown(file.content, { |
| 352 | baseUrl: "/s/" + share.id + "/repositories/" + repository.id, |
| 353 | readmePath: file.path, |
| 354 | files: repository.files, |
| 355 | repositoryUrl: repoHref |
| 356 | }) %></article> |
| 357 | <% } else if (file.binaryContent && file.mediaType) { %> |
| 358 | <div class="image-file-preview"> |
| 359 | <img src="data:<%= file.mediaType %>;base64,<%= file.binaryContent %>" alt="<%= file.path.split('/').pop() %>"> |
| 360 | </div> |
| 361 | <% } else if (file.content === null) { %> |
| 362 | <div class="empty-state">This file cannot be displayed as text<%= file.truncated ? " because it is larger than the snapshot preview limit." : "." %></div> |
| 363 | <% } else { %> |
| 364 | <div class="code-view"> |
| 365 | <table class="code-table" aria-label="<%= file.path %> source"> |
| 366 | <tbody> |
| 367 | <% file.content.split(/\r?\n/).forEach((line, index) => { const lineNumber = index + 1; %> |
| 368 | <tr id="L<%= lineNumber %>"> |
| 369 | <th scope="row"><a href="#L<%= lineNumber %>" aria-label="Line <%= lineNumber %>"><%= lineNumber %></a></th> |
| 370 | <td><code><%= line %></code></td> |
| 371 | </tr> |
| 372 | <% }) %> |
| 373 | </tbody> |
| 374 | </table> |
| 375 | </div> |
| 376 | <% } %> |
| 377 | </section> |
| 378 | <% } else if (tab === "commits") { %> |
| 379 | <section class="commits-page"> |
| 380 | <header class="section-title-row"> |
| 381 | <div> |
| 382 | <p class="section-kicker"><%= selectedReference.name %> history</p> |
| 383 | <h2><%= repository.commits.length %> <%= repository.commits.length === 1 ? "commit" : "commits" %></h2> |
| 384 | </div> |
| 385 | <a class="quiet-button" href="<%= repoHref() %>">Browse code</a> |
| 386 | </header> |
| 387 | <ol class="commit-feed"> |
| 388 | <% repository.commits.forEach((item) => { %> |
| 389 | <li> |
| 390 | <span class="commit-avatar" aria-hidden="true"><%= item.author.slice(0, 1).toUpperCase() %></span> |
| 391 | <a class="commit-main" href="<%= repoHref({ commit: item.sha }) %>"> |
| 392 | <strong><%= item.message.split("\n")[0] %></strong> |
| 393 | <span><%= item.author %> committed <%= formatDate(item.date) %></span> |
| 394 | </a> |
| 395 | <span class="commit-change"><b>+<%= item.additions %></b> <i>−<%= item.deletions %></i></span> |
| 396 | <code><%= item.sha.slice(0, 7) %></code> |
| 397 | </li> |
| 398 | <% }) %> |
| 399 | <% if (!repository.commits.length) { %> |
| 400 | <li class="empty-state">No commits are included in this snapshot.</li> |
| 401 | <% } %> |
| 402 | </ol> |
| 403 | </section> |
| 404 | <% } else if (tab === "issues") { %> |
| 405 | <section class="work-list-page"> |
| 406 | <header class="work-list-header"> |
| 407 | <div> |
| 408 | <h2>Issues</h2> |
| 409 | <p>Questions, bugs, and planned work captured with this snapshot.</p> |
| 410 | </div> |
| 411 | <nav aria-label="Issue state"> |
| 412 | <a class="<%= stateFilter === 'all' ? 'active' : '' %>" href="<%= globalRepoHref({ tab: 'issues' }) %>">All <span><%= repository.issues.length %></span></a> |
| 413 | <a class="<%= stateFilter === 'open' ? 'active' : '' %>" href="<%= globalRepoHref({ tab: 'issues', state: 'open' }) %>">Open <span><%= repository.issues.filter((item) => item.state === "open").length %></span></a> |
| 414 | <a class="<%= stateFilter === 'closed' ? 'active' : '' %>" href="<%= globalRepoHref({ tab: 'issues', state: 'closed' }) %>">Closed <span><%= repository.issues.filter((item) => item.state === "closed").length %></span></a> |
| 415 | </nav> |
| 416 | </header> |
| 417 | <div class="work-list"> |
| 418 | <% visibleIssues.forEach((item) => { %> |
| 419 | <article> |
| 420 | <span class="work-state-icon <%= item.state %>" aria-label="<%= item.state %> issue"></span> |
| 421 | <div> |
| 422 | <a class="work-title" href="<%= globalRepoHref({ issue: item.number }) %>"><%= item.title %></a> |
| 423 | <div class="work-labels"> |
| 424 | <% item.labels.forEach((label) => { %><span class="work-label"><%= label.name %></span><% }) %> |
| 425 | </div> |
| 426 | <p>#<%= item.number %> opened on <%= formatDate(item.createdAt) %> by <%= item.author.login %></p> |
| 427 | </div> |
| 428 | <span class="work-comments"><strong><%= item.comments.length %></strong> comments</span> |
| 429 | </article> |
| 430 | <% }) %> |
| 431 | <% if (!visibleIssues.length) { %> |
| 432 | <div class="empty-state">No <%= stateFilter === "all" ? "" : stateFilter + " " %>issues are included in this snapshot.</div> |
| 433 | <% } %> |
| 434 | </div> |
| 435 | </section> |
| 436 | <% } else if (tab === "pulls") { %> |
| 437 | <section class="work-list-page"> |
| 438 | <header class="work-list-header"> |
| 439 | <div> |
| 440 | <h2>Pull requests</h2> |
| 441 | <p>Proposed changes, reviews, and file diffs captured with this snapshot.</p> |
| 442 | </div> |
| 443 | <nav aria-label="Pull request state"> |
| 444 | <a class="<%= stateFilter === 'all' ? 'active' : '' %>" href="<%= globalRepoHref({ tab: 'pulls' }) %>">All <span><%= repository.pullRequests.length %></span></a> |
| 445 | <a class="<%= stateFilter === 'open' ? 'active' : '' %>" href="<%= globalRepoHref({ tab: 'pulls', state: 'open' }) %>">Open <span><%= repository.pullRequests.filter((item) => item.state === "open").length %></span></a> |
| 446 | <a class="<%= stateFilter === 'closed' ? 'active' : '' %>" href="<%= globalRepoHref({ tab: 'pulls', state: 'closed' }) %>">Closed <span><%= repository.pullRequests.filter((item) => item.state === "closed").length %></span></a> |
| 447 | </nav> |
| 448 | </header> |
| 449 | <div class="work-list"> |
| 450 | <% visiblePullRequests.forEach((item) => { const itemState = item.draft ? "draft" : item.merged ? "merged" : item.state; %> |
| 451 | <article> |
| 452 | <span class="work-state-icon <%= itemState %>" aria-label="<%= itemState %> pull request"></span> |
| 453 | <div> |
| 454 | <a class="work-title" href="<%= globalRepoHref({ pull: item.number }) %>"><%= item.title %></a> |
| 455 | <div class="work-labels"> |
| 456 | <% item.labels.forEach((label) => { %><span class="work-label"><%= label.name %></span><% }) %> |
| 457 | </div> |
| 458 | <p>#<%= item.number %> opened on <%= formatDate(item.createdAt) %> by <%= item.author.login %> · <%= item.head %> into <%= item.base %></p> |
| 459 | </div> |
| 460 | <span class="work-comments"><strong><%= item.conversation.length %></strong> updates</span> |
| 461 | </article> |
| 462 | <% }) %> |
| 463 | <% if (!visiblePullRequests.length) { %> |
| 464 | <div class="empty-state">No <%= stateFilter === "all" ? "" : stateFilter + " " %>pull requests are included in this snapshot.</div> |
| 465 | <% } %> |
| 466 | </div> |
| 467 | </section> |
| 468 | <% } else if (tab === "releases") { %> |
| 469 | <section class="release-list-page"> |
| 470 | <header> |
| 471 | <div> |
| 472 | <h2>Releases</h2> |
| 473 | <p>Published versions and release notes captured with this snapshot.</p> |
| 474 | </div> |
| 475 | <span><strong><%= repository.releases.length %></strong> total</span> |
| 476 | </header> |
| 477 | <div class="release-list"> |
| 478 | <% repository.releases.forEach((item, index) => { %> |
| 479 | <article> |
| 480 | <aside> |
| 481 | <span class="release-tag"><%= item.tagName %></span> |
| 482 | <time><%= formatDate(item.publishedAt || item.createdAt) %></time> |
| 483 | </aside> |
| 484 | <div> |
| 485 | <div class="release-list-title"> |
| 486 | <a href="<%= globalRepoHref({ release: item.id }) %>"><%= item.name %></a> |
| 487 | <% if (index === 0 && !item.draft && !item.prerelease) { %><span class="latest-release">Latest</span><% } %> |
| 488 | <% if (item.draft) { %><span class="release-status">Draft</span><% } %> |
| 489 | <% if (item.prerelease) { %><span class="release-status">Pre-release</span><% } %> |
| 490 | </div> |
| 491 | <p><%= item.body ? item.body.replace(/[#*_`>-]/g, "").trim().slice(0, 260) : "No release notes were provided." %></p> |
| 492 | <span>Published by <strong><%= item.author.login %></strong> · <%= item.assets.length %> <%= item.assets.length === 1 ? "asset" : "assets" %></span> |
| 493 | </div> |
| 494 | </article> |
| 495 | <% }) %> |
| 496 | <% if (!repository.releases.length) { %><div class="empty-state">No releases are included in this snapshot.</div><% } %> |
| 497 | </div> |
| 498 | </section> |
| 499 | <% } else { %> |
| 500 | <section class="code-overview"> |
| 501 | <div class="code-toolbar"> |
| 502 | <% if (references.branches.length + references.tags.length > 1) { %> |
| 503 | <details class="ref-switcher"> |
| 504 | <summary class="branch-pill"> |
| 505 | <svg class="repo-icon repo-icon-branch" width="14" height="14" viewBox="0 0 24 24" aria-hidden="true"><path d="M7 3a3 3 0 1 1-1 5.83v6.34A3.001 3.001 0 1 1 8 18v-3h4a4 4 0 0 0 4-4V8.83a3.001 3.001 0 1 1 2 0V11a6 6 0 0 1-6 6H8v1a3 3 0 0 1-2-2.83V8.83A3.001 3.001 0 0 1 7 3Z"/></svg> |
| 506 | <span><%= selectedReference.name %></span> |
| 507 | <svg class="ref-chevron" width="12" height="12" viewBox="0 0 12 12" aria-hidden="true"><path d="m2.5 4 3.5 4 3.5-4Z"/></svg> |
| 508 | </summary> |
| 509 | <div class="ref-menu"> |
| 510 | <header> |
| 511 | <strong>Switch branches or tags</strong> |
| 512 | <span><%= references.branches.length %> <%= references.branches.length === 1 ? "branch" : "branches" %>, <%= references.tags.length %> <%= references.tags.length === 1 ? "tag" : "tags" %></span> |
| 513 | </header> |
| 514 | <label class="sr-only" for="ref-filter">Find a branch or tag</label> |
| 515 | <input id="ref-filter" data-ref-filter type="search" placeholder="Find a branch or tag"> |
| 516 | <div class="ref-options"> |
| 517 | <% if (references.branches.length) { %><p>Branches</p><% } %> |
| 518 | <% references.branches.forEach((branch) => { const isSelected = selectedReference.type === "branch" && selectedReference.name === branch.name; %> |
| 519 | <a class="<%= isSelected ? 'selected' : '' %>" data-ref-option data-ref-name="<%= branch.name.toLocaleLowerCase('en') %>" href="<%= refHref({ ...branch, type: 'branch' }) %>"> |
| 520 | <span class="ref-check" aria-hidden="true"></span> |
| 521 | <span><%= branch.name %></span> |
| 522 | <% if (branch.name === repository.defaultBranch) { %><small>default</small><% } %> |
| 523 | </a> |
| 524 | <% }) %> |
| 525 | <% if (references.tags.length) { %><p>Tags</p><% } %> |
| 526 | <% references.tags.forEach((tag) => { const isSelected = selectedReference.type === "tag" && selectedReference.name === tag.name; %> |
| 527 | <a class="<%= isSelected ? 'selected' : '' %>" data-ref-option data-ref-name="<%= tag.name.toLocaleLowerCase('en') %>" href="<%= refHref({ ...tag, type: 'tag' }) %>"> |
| 528 | <span class="ref-check" aria-hidden="true"></span> |
| 529 | <span><%= tag.name %></span> |
| 530 | <small>tag</small> |
| 531 | </a> |
| 532 | <% }) %> |
| 533 | <p class="ref-no-results" data-ref-empty hidden>No branches or tags match.</p> |
| 534 | </div> |
| 535 | </div> |
| 536 | </details> |
| 537 | <% } else { %> |
| 538 | <span class="branch-pill"> |
| 539 | <svg class="repo-icon repo-icon-branch" width="14" height="14" viewBox="0 0 24 24" aria-hidden="true"><path d="M7 3a3 3 0 1 1-1 5.83v6.34A3.001 3.001 0 1 1 8 18v-3h4a4 4 0 0 0 4-4V8.83a3.001 3.001 0 1 1 2 0V11a6 6 0 0 1-6 6H8v1a3 3 0 0 1-2-2.83V8.83A3.001 3.001 0 1 1 7 3Z"/></svg> |
| 540 | <%= selectedReference.name %> |
| 541 | </span> |
| 542 | <% } %> |
| 543 | <% if (repository.headSha) { %> |
| 544 | <span class="head-reference">Snapshot head <code><%= repository.headSha.slice(0, 7) %></code></span> |
| 545 | <% } %> |
| 546 | <a class="commit-shortcut" href="<%= repoHref({ tab: 'commits' }) %>"> |
| 547 | <svg class="repo-icon repo-icon-branch" width="14" height="14" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 7a5 5 0 1 1-4.9 6H2v-2h5.1A5 5 0 0 1 12 7Zm0 2a3 3 0 1 0 0 6 3 3 0 0 0 0-6Zm4.9 2H22v2h-5.1v-2Z"/></svg> |
| 548 | <strong><%= repository.commits.length %></strong> commits |
| 549 | </a> |
| 550 | </div> |
| 551 | |
| 552 | <div class="<%= path ? 'repository-code-only' : 'repository-overview-layout' %>"> |
| 553 | <div class="repository-code-column"> |
| 554 | <section class="browser-card"> |
| 555 | <% const latestCommit = repository.commits[0]; %> |
| 556 | <% if (latestCommit) { %> |
| 557 | <a class="latest-commit" href="<%= repoHref({ commit: latestCommit.sha }) %>"> |
| 558 | <span class="commit-avatar" aria-hidden="true"><%= latestCommit.author.slice(0, 1).toUpperCase() %></span> |
| 559 | <strong><%= latestCommit.author %></strong> |
| 560 | <span class="latest-message"><%= latestCommit.message.split("\n")[0] %></span> |
| 561 | <code><%= latestCommit.sha.slice(0, 7) %></code> |
| 562 | <time><%= formatDate(latestCommit.date) %></time> |
| 563 | </a> |
| 564 | <% } %> |
| 565 | <nav class="path-bar" aria-label="File path"> |
| 566 | <a href="<%= repoHref() %>"><%= repository.name %></a> |
| 567 | <% let builtPath = ""; path.split("/").filter(Boolean).forEach((part) => { builtPath += (builtPath ? "/" : "") + part; %> |
| 568 | <span>/</span><a href="<%= repoHref({ path: builtPath }) %>"><%= part %></a> |
| 569 | <% }) %> |
| 570 | </nav> |
| 571 | <div class="file-table" role="table" aria-label="Repository files"> |
| 572 | <% if (path) { const parent = path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : ""; %> |
| 573 | <a class="file-row" href="<%= repoHref({ path: parent }) %>"> |
| 574 | <span class="file-symbol back-symbol" aria-hidden="true">←</span> |
| 575 | <strong>..</strong> |
| 576 | <span>Parent folder</span> |
| 577 | </a> |
| 578 | <% } %> |
| 579 | <% browser.folders.forEach((folder) => { const nextPath = path ? path + "/" + folder : folder; %> |
| 580 | <a class="file-row" href="<%= repoHref({ path: nextPath }) %>"> |
| 581 | <span class="file-symbol" aria-hidden="true"><svg class="repo-icon repo-icon-file" width="16" height="16" viewBox="0 0 24 24"><path d="M3 5.75C3 4.78 3.78 4 4.75 4h5.08c.46 0 .9.18 1.23.51L12.55 6H19.25C20.22 6 21 6.78 21 7.75v9.5c0 .97-.78 1.75-1.75 1.75H4.75C3.78 19 3 18.22 3 17.25V5.75Z"/></svg></span> |
| 582 | <strong><%= folder %></strong> |
| 583 | <span>Folder</span> |
| 584 | </a> |
| 585 | <% }) %> |
| 586 | <% browser.files.forEach((item) => { %> |
| 587 | <a class="file-row" href="<%= repoHref({ path, file: item.path }) %>"> |
| 588 | <span class="file-symbol file-symbol-document" aria-hidden="true"><svg class="repo-icon repo-icon-file" width="16" height="16" viewBox="0 0 24 24"><path d="M6 2h8l5 5v13a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2Zm7 2H6v16h11V8h-4V4Z"/></svg></span> |
| 589 | <strong><%= item.path.split("/").pop() %></strong> |
| 590 | <span><%= item.size.toLocaleString() %> bytes</span> |
| 591 | </a> |
| 592 | <% }) %> |
| 593 | <% if (!browser.folders.length && !browser.files.length) { %> |
| 594 | <p class="empty-state">This folder contains no files.</p> |
| 595 | <% } %> |
| 596 | </div> |
| 597 | </section> |
| 598 | |
| 599 | <% if (!path && readme) { %> |
| 600 | <article class="readme-card"> |
| 601 | <header> |
| 602 | <div> |
| 603 | <svg class="repo-icon repo-icon-file" width="16" height="16" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 3h6a3 3 0 0 1 2 .76A3 3 0 0 1 14 3h6a1 1 0 0 1 1 1v15a1 1 0 0 1-1 1h-6a2 2 0 0 0-1.45.62L12 21.2l-.55-.58A2 2 0 0 0 10 20H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1Zm1 2v13h5c.35 0 .69.04 1 .13V6a1 1 0 0 0-1-1H5Zm9 0a1 1 0 0 0-1 1v12.13c.31-.09.65-.13 1-.13h5V5h-5Z"/></svg> |
| 604 | <strong><%= readme.path %></strong> |
| 605 | </div> |
| 606 | <a href="<%= repoHref({ file: readme.path, view: 'source' }) %>">View source</a> |
| 607 | </header> |
| 608 | <div class="markdown-body"><%- renderMarkdown(readme.content, { |
| 609 | baseUrl: "/s/" + share.id + "/repositories/" + repository.id, |
| 610 | readmePath: readme.path, |
| 611 | files: repository.files, |
| 612 | repositoryUrl: repoHref |
| 613 | }) %></div> |
| 614 | </article> |
| 615 | <% } else if (!path) { %> |
| 616 | <section class="readme-empty"> |
| 617 | <strong>No README found</strong> |
| 618 | <p>Browse the repository files above to understand this project.</p> |
| 619 | </section> |
| 620 | <% } %> |
| 621 | </div> |
| 622 | <% if (!path) { %> |
| 623 | <aside class="repository-about"> |
| 624 | <h2>About</h2> |
| 625 | <p><%= repository.description || "No repository description." %></p> |
| 626 | <% if (repository.homepage) { %><span class="about-homepage"><%= repository.homepage %></span><% } %> |
| 627 | <% if ((repository.topics || []).length) { %> |
| 628 | <div class="about-topics"> |
| 629 | <% repository.topics.forEach((topic) => { %><span><%= topic %></span><% }) %> |
| 630 | </div> |
| 631 | <% } %> |
| 632 | <% if (repository.archived) { %><span class="archived-badge">Archived on GitHub</span><% } %> |
| 633 | <ul class="about-stats"> |
| 634 | <li><strong><%= repository.stargazersCount || 0 %></strong> stars</li> |
| 635 | <li><strong><%= repository.watchersCount || 0 %></strong> watching</li> |
| 636 | <li><strong><%= repository.forksCount || 0 %></strong> forks</li> |
| 637 | <li><strong><%= references.branches.length %></strong> branches</li> |
| 638 | <li><strong><%= references.tags.length %></strong> tags</li> |
| 639 | <li><strong><%= repository.releases.length %></strong> releases</li> |
| 640 | </ul> |
| 641 | <% if (repository.license) { %> |
| 642 | <section class="about-section"> |
| 643 | <h3>License</h3> |
| 644 | <p><%= repository.license.spdxId || repository.license.name %></p> |
| 645 | </section> |
| 646 | <% } %> |
| 647 | <% if (repository.languages.length) { %> |
| 648 | <section class="about-section"> |
| 649 | <h3>Languages</h3> |
| 650 | <div class="language-breakdown"> |
| 651 | <% repository.languages.forEach((language) => { %> |
| 652 | <div> |
| 653 | <span><%= language.name %></span> |
| 654 | <strong><%= language.percent.toLocaleString() %>%</strong> |
| 655 | </div> |
| 656 | <% }) %> |
| 657 | </div> |
| 658 | </section> |
| 659 | <% } %> |
| 660 | </aside> |
| 661 | <% } %> |
| 662 | </div> |
| 663 | </section> |
| 664 | <% } %> |
| 665 | </main> |
| 666 | <script src="/assets/app.js" defer></script> |
| 667 | <%- include("partials/foot") %> |
| 668 | |