summaryrefslogtreecommitdiff
path: root/cgithub/views
diff options
context:
space:
mode:
Diffstat (limited to 'cgithub/views')
-rw-r--r--cgithub/views/_latestCommit.eta16
-rw-r--r--cgithub/views/_nav.eta34
-rw-r--r--cgithub/views/_ownerRepo.eta15
-rw-r--r--cgithub/views/_refs.eta21
-rw-r--r--cgithub/views/_release.eta32
-rw-r--r--cgithub/views/_sidebar.eta22
-rw-r--r--cgithub/views/_tree.eta38
-rw-r--r--cgithub/views/_wikiNav.eta4
-rw-r--r--cgithub/views/blob.eta35
-rw-r--r--cgithub/views/commits.eta33
-rw-r--r--cgithub/views/error.eta3
-rw-r--r--cgithub/views/home.eta47
-rw-r--r--cgithub/views/issues.eta20
-rw-r--r--cgithub/views/layouts/base.eta17
-rw-r--r--cgithub/views/layouts/repo.eta59
-rw-r--r--cgithub/views/owner.eta54
-rw-r--r--cgithub/views/pulls.eta15
-rw-r--r--cgithub/views/redirect.eta14
-rw-r--r--cgithub/views/release.eta3
-rw-r--r--cgithub/views/releases.eta5
-rw-r--r--cgithub/views/repo.eta31
-rw-r--r--cgithub/views/tree.eta3
-rw-r--r--cgithub/views/wiki.eta37
-rw-r--r--cgithub/views/wikiPages.eta16
24 files changed, 574 insertions, 0 deletions
diff --git a/cgithub/views/_latestCommit.eta b/cgithub/views/_latestCommit.eta
new file mode 100644
index 0000000..18d22a1
--- /dev/null
+++ b/cgithub/views/_latestCommit.eta
@@ -0,0 +1,16 @@
+<div class="commit box">
+ <div>
+ <div>
+ <%~ it.commit.shortMessageHtml %>
+ </div>
+ <div class="commit-author">
+ <% for (const author of it.commit.authors) { %>
+ <a href="/<%= author.login %>"><%= author.login %></a>
+ <% } %>
+ <time class="timestamp" title="<%= it.commit.date %>"><%= it.commit.date.split("T")[0] %></time>
+ </div>
+ </div>
+ <div class="commit-sha">
+ <code><%= it.commit.oid.substring(0, 7) %></code>
+ </div>
+</div>
diff --git a/cgithub/views/_nav.eta b/cgithub/views/_nav.eta
new file mode 100644
index 0000000..a20741b
--- /dev/null
+++ b/cgithub/views/_nav.eta
@@ -0,0 +1,34 @@
+<section>
+ <%
+ const isCommitHash = /^[0-9a-f]{40}$/.test(it.branch);
+ const refLabel = isCommitHash ? it.branch.slice(0, 7) : it.branch;
+ %>
+ <button type="button" class="ref-selector-button branch" command="show-modal" commandfor="ref-selector-menu">
+ <img src="/static/icons/git-branch.svg" alt="branch" class="icon"> <%= refLabel %> ▾
+ </button>
+ <dialog class="ref-selector-menu" id="ref-selector-menu" closedby="any" data-owner="<%= it.repo.owner %>" data-repo="<%= it.repo.name %>" data-view="<%= it.view %>"<% if (it.path && it.path !== "/") { %> data-path="<%= it.path %>"<% } %>>
+ <div class="ref-selector-tabs">
+ <button type="button" class="active" data-ref-type="branches">Branches</button>
+ <button type="button" data-ref-type="tags">Tags</button>
+ </div>
+ <div class="ref-selector-list">Loading…</div>
+ </dialog>
+ <% if (it.path && it.path !== "/") { %>
+ <%
+ const crumbHrefBase = `/${it.repo.owner}/${it.repo.name}/${it.view === "blob" ? "tree" : it.view}/${it.branch}`;
+ %>
+ <a href="<%= crumbHrefBase %>">(top)</a><%
+ const pathParts = it.path.split('/').filter(p => p);
+ let currentPath = '';
+ for (let i = 0; i < pathParts.length; i++) {
+ const part = pathParts[i];
+ currentPath += (currentPath ? '/' : '') + part;
+ const isLast = i === pathParts.length - 1;
+ if (isLast) {
+ %>/<%= part %><%= it.view === "tree" ? "/" : "" %><%
+ } else {
+ %>/<a href="<%= crumbHrefBase %>/<%= currentPath %>"><%= part %></a><%
+ }
+ } %>
+ <% } %>
+</section>
diff --git a/cgithub/views/_ownerRepo.eta b/cgithub/views/_ownerRepo.eta
new file mode 100644
index 0000000..a857aff
--- /dev/null
+++ b/cgithub/views/_ownerRepo.eta
@@ -0,0 +1,15 @@
+<div class="box repo-info">
+ <div><a href="/<%= it.repo.owner %>/<%= it.repo.name %>"><%= it.repo.owner === it.login ? it.repo.name : `${it.repo.owner}/${it.repo.name}` %></a></div>
+ <% if (it.repo.description) { %>
+ <div class="description"><%= it.repo.description %></div>
+ <% } %>
+ <% if (it.repo.language) { %>
+ <span class="detail"><%= it.repo.language %></span>
+ <% } %>
+ <% if (it.repo.stars) { %>
+ <span class="detail"><img src="/static/icons/star.svg" alt="stars" class="icon"> <%= it.repo.stars %></span>
+ <% } %>
+ <% if (it.repo.forks) { %>
+ <span class="detail"><img src="/static/icons/git-fork.svg" alt="forks" class="icon"> <%= it.repo.forks %></span>
+ <% } %>
+</div>
diff --git a/cgithub/views/_refs.eta b/cgithub/views/_refs.eta
new file mode 100644
index 0000000..9d951cc
--- /dev/null
+++ b/cgithub/views/_refs.eta
@@ -0,0 +1,21 @@
+<%
+const linkPrefix = `/${it.repo.owner}/${it.repo.name}/${it.view}/`;
+const linkSuffix = it.path ? `/${it.path}` : "";
+%>
+<% if (it.refs.length === 0) { %>
+ <div class="ref-selector-empty">No <%= it.type %> found.</div>
+<% } else { %>
+ <ul class="ref-selector-items">
+ <% for (const ref of it.refs) { %>
+ <li>
+ <a href="<%= linkPrefix %><%= ref.name %><%= linkSuffix %>"><%= ref.name %></a>
+ <% if (ref.isDefault) { %><span class="ref-selector-badge">default</span><% } %>
+ </li>
+ <% } %>
+ </ul>
+<% } %>
+<% if (it.hasMore) { %>
+ <div class="ref-selector-more">
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/<%= it.type %>">View all <%= it.type %></a>
+ </div>
+<% } %>
diff --git a/cgithub/views/_release.eta b/cgithub/views/_release.eta
new file mode 100644
index 0000000..a8838fb
--- /dev/null
+++ b/cgithub/views/_release.eta
@@ -0,0 +1,32 @@
+<div class="box release">
+ <header>
+ <% if (it.linkToDetail) { %>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/releases/tag/<%= it.release.tagName %>">
+ <%= it.release.title %>
+ </a>
+ <% } else { %>
+ <span><%= it.release.title %></span>
+ <% } %>
+ <% if (it.release.publishedAt) { %>
+ <time class="timestamp" title="<%= it.release.publishedAt %>">
+ <%= it.release.publishedAt.split("T")[0] %>
+ </time>
+ <% } %>
+ </header>
+ <% if (it.release.bodyHtml) { %>
+ <%~ it.release.bodyHtml %>
+ <% } %>
+ <% if (it.release.assets && it.release.assets.length > 0) { %>
+ <footer>
+ <h3>Assets</h3>
+ <% for (const asset of it.release.assets) { %>
+ <div>
+ <a href="<%= asset.url %>"><%= asset.name %></a>
+ <% if (asset.publishedAt) { %>
+ <time class="timestamp" title="<%= asset.publishedAt %>"><%= asset.publishedAt.split("T")[0] %></time>
+ <% } %>
+ </div>
+ <% } %>
+ </footer>
+ <% } %>
+</div>
diff --git a/cgithub/views/_sidebar.eta b/cgithub/views/_sidebar.eta
new file mode 100644
index 0000000..bceef7c
--- /dev/null
+++ b/cgithub/views/_sidebar.eta
@@ -0,0 +1,22 @@
+<% if (it.latestRelease) { %>
+ <span data-target="latest-release">(<a href="/<%= it.repo.owner %>/<%= it.repo.name %>/releases/tag/<%= it.latestRelease.tag %>"><%= it.latestRelease.tag %></a><%
+ if (it.latestRelease.publishedAt) { %> <time class="timestamp" title="<%= it.latestRelease.publishedAt %>"><%= it.latestRelease.publishedAt.split("T")[0] %></time><%
+ } %>)</span>
+<% } %>
+<% if (it.languages.length > 0) { %>
+ <div class="languages box">
+ <div class="language-bar">
+ <% for (const lang of it.languages) { %>
+ <span style="width: <%= lang.percentage %>%; background: <%= lang.color ?? "#ededed" %>" title="<%= lang.name %> <%= lang.percentage %>%"></span>
+ <% } %>
+ </div>
+ <ul class="language-list">
+ <% for (const lang of it.languages) { %>
+ <li>
+ <span class="language-dot" style="background: <%= lang.color ?? "#ededed" %>"></span>
+ <%= lang.name %> <span class="language-percentage"><%= lang.percentage %>%</span>
+ </li>
+ <% } %>
+ </ul>
+ </div>
+<% } %>
diff --git a/cgithub/views/_tree.eta b/cgithub/views/_tree.eta
new file mode 100644
index 0000000..a3c943d
--- /dev/null
+++ b/cgithub/views/_tree.eta
@@ -0,0 +1,38 @@
+<%~ include("./_nav.eta", {...it, view: "tree"}) %>
+
+<% const commitPath = it.path && it.path !== "/" ? it.path : ""; %>
+<div class="placeholder commit box" data-src="/api/<%= it.repo.owner %>/<%= it.repo.name %>/latest-commit/<%= it.branch %>/<%= commitPath %>">
+ <div>
+ <div>…</div>
+ <div class="commit-author">…</div>
+ </div>
+</div>
+
+<table>
+ <% for (const item of it.items) { %>
+ <tr>
+ <td>
+ <% if (item.contentType === 'directory') { %>
+ <img src="/static/icons/folder-simple.svg" alt="tree" class="icon">
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/tree/<%= it.branch %>/<%= item.path %>"><%= item.name %>/</a>
+ <% } else { %>
+ <img src="/static/icons/file.svg" alt="blob" class="icon">
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/blob/<%= it.branch %>/<%= item.path %>"><%= item.name %></a>
+ <% } %>
+ </td>
+ </tr>
+ <% } %>
+</table>
+
+<% if (it.overviewHtml) { %>
+ <% for (const [filename, html] of Object.entries(it.overviewHtml)) { %>
+ <% if (html !== null) { %>
+ <div class="overview box">
+ <header><%= filename %></header>
+ <%~ html %>
+ </div>
+ <% } else { %>
+ <!-- No rendered content for <%= filename %> -->
+ <% } %>
+ <% } %>
+<% } %>
diff --git a/cgithub/views/_wikiNav.eta b/cgithub/views/_wikiNav.eta
new file mode 100644
index 0000000..cc177c7
--- /dev/null
+++ b/cgithub/views/_wikiNav.eta
@@ -0,0 +1,4 @@
+<nav>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/wiki">Wiki home</a>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/wiki/_pages">Pages</a>
+</nav>
diff --git a/cgithub/views/blob.eta b/cgithub/views/blob.eta
new file mode 100644
index 0000000..9f6706e
--- /dev/null
+++ b/cgithub/views/blob.eta
@@ -0,0 +1,35 @@
+<% layout("/layouts/repo.eta"); it.searchType = "code" %>
+
+<%~ include("./_nav.eta", {...it, view: "blob"}) %>
+
+<div>
+ <% if (it.language) { %>
+ <div>language: <%= it.language %></div>
+ <% } %>
+ <div><%= it.size %></div>
+ <div>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/commits/<%= it.branch %>/<%= it.path %>">History</a>
+ </div>
+ <div>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/raw/<%= it.branch %>/<%= it.path %>">View raw</a>
+ </div>
+</div>
+
+<% if (it.image) { %>
+ <div class="box"><img src="/<%= it.repo.owner %>/<%= it.repo.name %>/raw/<%= it.branch %>/<%= it.path %>"></div>
+<% } else if (it.renderFileType === "pdf") { %>
+ <object class="box pdf" type="application/pdf"
+ data="/<%= it.repo.owner %>/<%= it.repo.name %>/embed/<%= it.renderFileType %>/<%= it.branch %>/<%= it.path %>">
+ This file cannot be displayed.
+ </object>
+<% } else if (it.htmlContent) { %>
+ <div class="box"><%~ it.htmlContent %></div>
+<% } else if (it.htmlLines) { %>
+ <pre class="box"><code><% for (const [i, line] of it.htmlLines.entries()) {
+ %><%= i ? "\n" : "" %><span id="L<%= i + 1 %>"><%~ line %></span><% } %></code></pre>
+<% } else if (it.textLines) { %>
+ <pre class="box"><code><% for (const [i, line] of it.textLines.entries()) {
+ %><%= i ? "\n" : "" %><span id="L<%= i + 1 %>"><%= line %></span><% } %></code></pre>
+<% } else { %>
+ <div class="box">This file cannot be displayed.</div>
+<% } %>
diff --git a/cgithub/views/commits.eta b/cgithub/views/commits.eta
new file mode 100644
index 0000000..a80ef07
--- /dev/null
+++ b/cgithub/views/commits.eta
@@ -0,0 +1,33 @@
+<% layout("/layouts/repo.eta") %>
+
+<%~ include("./_nav.eta", {...it, view: "commits"}) %>
+
+<% const pathSuffix = it.path && it.path !== "/" ? "/" + it.path : "" %>
+
+<% if (it.commitGroups.length === 0) { %>
+ <div class="box">No commits found.</div>
+<% } %>
+
+<% for (const group of it.commitGroups) { %>
+ <h3><%= group.title %></h3>
+ <% for (const commit of group.commits) { %>
+ <div class="commit">
+ <div>
+ <div>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/commit/<%= commit.oid %>"><%= commit.shortMessage %></a>
+ </div>
+ <div class="commit-author">
+ by
+ <% for (const author of commit.authors) { %>
+ <a href="/<%= author.login %>"><%= author.login %></a>
+ <% } %>
+ </div>
+ </div>
+ <div class="commit-sha">
+ <code><%= commit.oid.substring(0, 7) %></code>
+ <% /* Like GitHub, blindly link to blob and redirect for trees */ %>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/blob/<%= commit.oid %><%= pathSuffix %>">Browse</a>
+ </div>
+ </div>
+ <% } %>
+<% } %>
diff --git a/cgithub/views/error.eta b/cgithub/views/error.eta
new file mode 100644
index 0000000..a46411e
--- /dev/null
+++ b/cgithub/views/error.eta
@@ -0,0 +1,3 @@
+<% layout("/layouts/base.eta", { title: it.title ?? it.message }) %>
+
+<%= it.message %>
diff --git a/cgithub/views/home.eta b/cgithub/views/home.eta
new file mode 100644
index 0000000..604e4d0
--- /dev/null
+++ b/cgithub/views/home.eta
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>cgithub</title>
+ <link rel="stylesheet" href="/static/style.css">
+</head>
+<body>
+ <header>
+ <h1>cgithub</h1>
+ <p>Lightweight GitHub alternative frontend</p>
+ </header>
+
+ <main>
+ <p>Browse any GitHub repository by navigating to <code>/:owner/:repo</code>, or a user or organization profile at <code>/:owner</code></p>
+ </main>
+
+ <h2>Credits</h2>
+ <p>Icons by <a href="https://phosphoricons.com">Phosphor Icons</a></p>
+ <details>
+ <summary>Phosphor Icons License (MIT)</summary>
+ <pre>
+MIT License
+
+Copyright (c) 2023 Phosphor Icons
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ </pre>
+ </details>
+</body>
+</html>
diff --git a/cgithub/views/issues.eta b/cgithub/views/issues.eta
new file mode 100644
index 0000000..754afb8
--- /dev/null
+++ b/cgithub/views/issues.eta
@@ -0,0 +1,20 @@
+<% layout("/layouts/repo.eta"); it.searchType = "issues" %>
+
+<table>
+ <% for (const issue of it.issues) { %>
+ <tr>
+ <td>#<%= issue.number %></td>
+ <td>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/issues/<%= issue.number %>">
+ <%= issue.title %>
+ </a>
+ </td>
+ <td>
+ <% if (issue.commentCount > 0) { %>
+ <img src="/static/icons/link.svg" alt="comments" class="icon"> <%= issue.commentCount %>
+ <% } %>
+ </td>
+ <td class="timestamp"><time title="<%= issue.createdAt %>"><%= issue.createdAt.split("T")[0] %></time></td>
+ </tr>
+ <% } %>
+</table>
diff --git a/cgithub/views/layouts/base.eta b/cgithub/views/layouts/base.eta
new file mode 100644
index 0000000..fb644f5
--- /dev/null
+++ b/cgithub/views/layouts/base.eta
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title><%= it.title %></title>
+ <link rel="stylesheet" href="/static/style.css">
+ <% for (const src of it.scripts ?? []) { %>
+ <script src="<%= src %>" defer></script>
+ <% } %>
+</head>
+<body>
+ <%~ it.body %>
+
+ <footer>Generated by cgithub commit <code><%= it.buildCommit %></code> · <a href="<%= it.githubUrl %>" rel="noreferrer">View original</a></footer>
+</body>
+</html>
diff --git a/cgithub/views/layouts/repo.eta b/cgithub/views/layouts/repo.eta
new file mode 100644
index 0000000..e4f7880
--- /dev/null
+++ b/cgithub/views/layouts/repo.eta
@@ -0,0 +1,59 @@
+<%
+layout("/layouts/base.eta", {
+ title: `${it.repo.owner}/${it.repo.name} - ${it.path || "/"}`,
+ scripts: ["/static/refs.js"],
+})
+%>
+
+<div class="box">
+ <div class="repo"><a href="/<%= it.repo.owner %>"><%= it.repo.owner %></a>/<a href="/<%= it.repo.owner %>/<%= it.repo.name %>"><%= it.repo.name %></a></div>
+ <pre><code>git clone https://github.com/<%= it.repo.owner %>/<%= it.repo.name %>.git
+git clone git@github.com:<%= it.repo.owner %>/<%= it.repo.name %>.git</code></pre>
+ <nav>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>">Code</a>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/issues">Issues</a>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/pulls">Pull requests</a>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/releases">Releases</a>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/wiki">Wiki</a>
+ <% if (it.branch) { %>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/commits/<%= it.branch %><%= it.path && it.path !== '/' ? '/' + it.path : '' %>">Commits</a>
+ <% } %>
+ </nav>
+ <form action="/<%= it.repo.owner %>/<%= it.repo.name %>/search" method="get">
+ <input id="search-input" type="text" name="q" placeholder="Search…" value="<%= it.q ?? "" %>">
+ <select name="type">
+ <option value="issues" <%= it.searchType === "issues" ? "selected" : "" %>>Issues</option>
+ <option value="pullrequests" <%= it.searchType === "pullrequests" ? "selected" : "" %>>Pull requests</option>
+ <option value="code" <%= it.searchType === "code" || it.searchType == null ? "selected" : "" %>>Code</option>
+ </select>
+ <button type="submit">Search</button>
+ </form>
+</div>
+
+<%~ it.body %>
+
+<script>
+ document.addEventListener("keydown", function(e) {
+ let shortcut = "";
+ if (e.ctrlKey || e.metaKey) shortcut += "C-";
+ if (e.altKey) shortcut += "A-";
+ if (e.shiftKey) shortcut += "S-";
+ shortcut += e.key;
+
+ if (shortcut === "Escape") {
+ document.activeElement.blur();
+ return;
+ }
+
+ if (["INPUT", "TEXTAREA", "SELECT"].includes(document.activeElement.tagName)) {
+ return;
+ }
+
+ if (shortcut === "/") {
+ e.preventDefault();
+ const input = document.getElementById('search-input');
+ input.select();
+ input.scrollIntoView({ block: "nearest" });
+ }
+ });
+</script>
diff --git a/cgithub/views/owner.eta b/cgithub/views/owner.eta
new file mode 100644
index 0000000..b3304fb
--- /dev/null
+++ b/cgithub/views/owner.eta
@@ -0,0 +1,54 @@
+<% layout("/layouts/base.eta", { title: it.name ? `${it.name} (${it.login})` : it.login }) %>
+
+<div class="box">
+ <% if (it.avatarUrl) { %>
+ <img class="avatar" src="<%= it.avatarUrl %>" alt="" width="64" height="64">
+ <% } %>
+ <div class="repo"><%= it.name || it.login %></div>
+ <div><%= it.login %><%= it.type === "organization" ? " (organization)" : "" %></div>
+ <% if (it.location) { %>
+ <div><%= it.location %></div>
+ <% } %>
+ <% if (it.nav.length > 0) { %>
+ <nav>
+ <% for (const item of it.nav) { %>
+ <a href="<%= item.href %>"><%= item.text %></a>
+ <% } %>
+ </nav>
+ <% } %>
+</div>
+
+<% if (it.bio || it.links.length > 0 || it.followers || it.company) { %>
+ <div class="box repo-info">
+ <% if (it.bio) { %>
+ <div class="description"><%= it.bio %></div>
+ <% } %>
+ <% for (const link of it.links) { %>
+ <div class="detail"><img src="/static/icons/link.svg" alt="" class="icon"> <a href="<%= link.href %>" rel="noopener noreferrer"><%= link.text %></a></div>
+ <% } %>
+ <% if (it.followers) { %>
+ <div class="detail"><img src="/static/icons/eye.svg" alt="" class="icon"> <%= it.followers %> followers<%= it.following ? ` · ${it.following} following` : "" %></div>
+ <% } %>
+ <% if (it.company) { %>
+ <div class="detail"><%= it.company %></div>
+ <% } %>
+ </div>
+<% } %>
+
+<% if (it.pinned.length > 0) { %>
+ <section>
+ <h2>Pinned</h2>
+ <% for (const repo of it.pinned) { %>
+ <%~ include("./_ownerRepo", { repo, login: it.login }) %>
+ <% } %>
+ </section>
+<% } %>
+
+<% if (it.repos.length > 0) { %>
+ <section>
+ <h2>Repositories</h2>
+ <% for (const repo of it.repos) { %>
+ <%~ include("./_ownerRepo", { repo, login: it.login }) %>
+ <% } %>
+ </section>
+<% } %>
diff --git a/cgithub/views/pulls.eta b/cgithub/views/pulls.eta
new file mode 100644
index 0000000..697f6b1
--- /dev/null
+++ b/cgithub/views/pulls.eta
@@ -0,0 +1,15 @@
+<% layout("/layouts/repo.eta"); it.searchType = "pullrequests" %>
+
+<table>
+ <% for (const pull of it.pulls) { %>
+ <tr>
+ <td>#<%= pull.number %></td>
+ <td>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/pull/<%= pull.number %>">
+ <%= pull.title %>
+ </a>
+ </td>
+ <td class="timestamp"><time title="<%= pull.createdAt %>"><%= pull.createdAt.split("T")[0] %></time></td>
+ </tr>
+ <% } %>
+</table>
diff --git a/cgithub/views/redirect.eta b/cgithub/views/redirect.eta
new file mode 100644
index 0000000..393a527
--- /dev/null
+++ b/cgithub/views/redirect.eta
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="referrer" content="no-referrer">
+ <meta http-equiv="refresh" content="1; url=<%= it.location %>">
+ <title>Redirecting to GitHub</title>
+ <link rel="stylesheet" href="/static/style.css">
+</head>
+<body>
+ <p>cgithub can't render this page. Redirecting to
+ <a href="<%= it.location %>" rel="noreferrer"><%= it.location %></a>…</p>
+</body>
+</html>
diff --git a/cgithub/views/release.eta b/cgithub/views/release.eta
new file mode 100644
index 0000000..64cca35
--- /dev/null
+++ b/cgithub/views/release.eta
@@ -0,0 +1,3 @@
+<% layout("/layouts/repo.eta") %>
+
+<%~ include("./_release.eta", { repo: it.repo, release: it.release, linkToDetail: false }) %>
diff --git a/cgithub/views/releases.eta b/cgithub/views/releases.eta
new file mode 100644
index 0000000..4ede28b
--- /dev/null
+++ b/cgithub/views/releases.eta
@@ -0,0 +1,5 @@
+<% layout("/layouts/repo.eta") %>
+
+<% for (const release of it.releases) { %>
+ <%~ include("./_release.eta", { repo: it.repo, release, linkToDetail: true }) %>
+<% } %>
diff --git a/cgithub/views/repo.eta b/cgithub/views/repo.eta
new file mode 100644
index 0000000..60746df
--- /dev/null
+++ b/cgithub/views/repo.eta
@@ -0,0 +1,31 @@
+<% layout("/layouts/repo.eta") %>
+
+<% if (it.info) { %>
+ <div class="box repo-info">
+ <% if (it.info.description) { %>
+ <div class="description"><%= it.info.description %></div>
+ <% } %>
+ <% if (it.info.website) { %>
+ <div class="detail"><img src="/static/icons/link.svg" alt="" class="icon"> <a href="<%= it.info.website %>" rel="noopener noreferrer"><%= it.info.website %></a></div>
+ <% } %>
+ <% if (it.info.stars) { %>
+ <span class="detail"><img src="/static/icons/star.svg" alt="stars" class="icon"> <%= it.info.stars %></span>
+ <% } %>
+ <% if (it.info.forks) { %>
+ <span class="detail"><img src="/static/icons/git-fork.svg" alt="forks" class="icon"> <%= it.info.forks %></span>
+ <% } %>
+ <% if (it.info.numReleases) { %>
+ <div class="detail">
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/releases"><%= it.info.numReleases %> releases</a>
+ <span id="latest-release"></span>
+ </div>
+ <% } %>
+ </div>
+<% } %>
+
+<div class="placeholder languages box" data-src="/api/<%= it.repo.owner %>/<%= it.repo.name %>/sidebar">
+ <div class="language-bar"><span style="width: 100%; background: #ededed"></span></div>
+ <ul class="language-list"><li>…</li></ul>
+</div>
+
+<%~ include("./_tree", it) %>
diff --git a/cgithub/views/tree.eta b/cgithub/views/tree.eta
new file mode 100644
index 0000000..fabf5b9
--- /dev/null
+++ b/cgithub/views/tree.eta
@@ -0,0 +1,3 @@
+<% layout("/layouts/repo.eta"); it.searchType = "code" %>
+
+<%~ include("./_tree.eta", it) %>
diff --git a/cgithub/views/wiki.eta b/cgithub/views/wiki.eta
new file mode 100644
index 0000000..781caff
--- /dev/null
+++ b/cgithub/views/wiki.eta
@@ -0,0 +1,37 @@
+<% layout("/layouts/repo.eta"); it.path = "wiki/" + it.title %>
+
+<%~ include("./_wikiNav.eta", it) %>
+
+<div class="box wiki">
+ <header>
+ <span><%= it.title %></span>
+ <% if (it.updatedBy || it.updatedAt) { %>
+ <span class="timestamp">
+ <% if (it.updatedBy) { %><%= it.updatedBy %> edited this page<% } %>
+ <% if (it.updatedAt) { %><time title="<%= it.updatedAt %>"><%= it.updatedAt.split("T")[0] %></time><% } %>
+ </span>
+ <% } %>
+ </header>
+ <%~ it.bodyHtml %>
+ <% if (it.footerHtml) { %>
+ <footer><%~ it.footerHtml %></footer>
+ <% } %>
+</div>
+
+<% if (it.sidebarHtml) { %>
+ <div class="box wiki">
+ <header><span>Sidebar</span></header>
+ <%~ it.sidebarHtml %>
+ </div>
+<% } %>
+
+<% if (it.pages.length > 0) { %>
+ <details class="box wiki-pages">
+ <summary><%= it.pages.length %> page<%= it.pages.length === 1 ? "" : "s" %></summary>
+ <ul>
+ <% for (const page of it.pages) { %>
+ <li><a href="<%= page.href %>"><%= page.name %></a></li>
+ <% } %>
+ </ul>
+ </details>
+<% } %>
diff --git a/cgithub/views/wikiPages.eta b/cgithub/views/wikiPages.eta
new file mode 100644
index 0000000..eb90781
--- /dev/null
+++ b/cgithub/views/wikiPages.eta
@@ -0,0 +1,16 @@
+<% layout("/layouts/repo.eta"); it.path = "wiki/_pages" %>
+
+<%~ include("./_wikiNav.eta", it) %>
+
+<table>
+ <% for (const page of it.pages) { %>
+ <tr>
+ <td><a href="<%= page.href %>"><%= page.name %></a></td>
+ <td class="timestamp">
+ <% if (page.updatedAt) { %>
+ <time title="<%= page.updatedAt %>"><%= page.updatedAt.split("T")[0] %></time>
+ <% } %>
+ </td>
+ </tr>
+ <% } %>
+</table>