aboutsummaryrefslogtreecommitdiff
path: root/views
diff options
context:
space:
mode:
authorJoe Mou <dev@mou.fo>2026-09-21 17:55:06 -0400
committerJoe Mou <dev@mou.fo>2026-09-25 01:43:53 -0400
commit499bba129c842c00c1a28d85bc044ab609aee44e (patch)
treebb69fa242a20e3c285fb6c41092b59f207f6855d /views
parent3204ecf1c48e730f02fb83d7c09d2c778a6f6bdd (diff)
Add discussions
Discussions are served from GitHub's older server-rendered HTML, with no embeddedData JSON to read, so the scraper walks the DOM the way the pull request and wiki code does. A discussion's timeline opens with its own post and continues with its comments, each carrying its replies nested inside it, which is where the recursion in _discussionComment.eta comes from. Categories, upvotes, and the answer a Q&A discussion is marked with all come along. Only what GitHub renders up front is shown: neither the next page of discussions nor the replies it hides behind a "Show N previous replies" button are fetched, so a comment says how many replies it is missing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'views')
-rw-r--r--views/_discussionComment.eta37
-rw-r--r--views/discussion.eta29
-rw-r--r--views/discussions.eta39
-rw-r--r--views/layouts/repo.eta2
4 files changed, 107 insertions, 0 deletions
diff --git a/views/_discussionComment.eta b/views/_discussionComment.eta
new file mode 100644
index 0000000..9c2cc07
--- /dev/null
+++ b/views/_discussionComment.eta
@@ -0,0 +1,37 @@
+<div class="box comment<%= it.comment.isAnswer ? " answer" : "" %><%= it.reply ? " reply" : "" %>" id="<%= it.comment.anchor %>">
+<% if (it.comment.hiddenReason) { %>
+ <div class="placeholder"><%= it.comment.hiddenReason %></div>
+<% } else { %>
+ <header>
+ <span>
+ <% if (it.comment.author) { %>
+ <a href="/<%= it.comment.author %>"><%= it.comment.author %></a>
+ <% } else { %>
+ <span class="placeholder">ghost</span>
+ <% } %>
+ <% for (const label of it.comment.labels) { %>
+ <span class="pill"><%= label %></span>
+ <% } %>
+ <% if (it.comment.isAnswer) { %>
+ <span class="answered pill">Answer</span>
+ <% } %>
+ </span>
+ <span class="timestamp">
+ <% if (it.comment.createdAt) { %>
+ <a href="#<%= it.comment.anchor %>"><time title="<%= it.comment.createdAt %>"><%= it.comment.createdAt.split("T")[0] %></time></a>
+ <% } %>
+ </span>
+ </header>
+ <%~ it.comment.bodyHtml %>
+<% } %>
+</div>
+
+<% if (it.comment.hiddenReplies > 0) { %>
+ <div class="box reply placeholder">
+ <%= it.comment.hiddenReplies %> earlier
+ <%= it.comment.hiddenReplies === 1 ? "reply is" : "replies are" %> only on GitHub.
+ </div>
+<% } %>
+<% for (const reply of it.comment.replies) { %>
+ <%~ include("./_discussionComment.eta", { comment: reply, reply: true }) %>
+<% } %>
diff --git a/views/discussion.eta b/views/discussion.eta
new file mode 100644
index 0000000..f87189e
--- /dev/null
+++ b/views/discussion.eta
@@ -0,0 +1,29 @@
+<%
+layout("/layouts/repo.eta")
+it.path = `discussions/${it.number}`
+it.searchType = "discussions"
+%>
+
+<div class="box">
+ <h2><%~ it.titleHtml %></h2>
+
+ #<%= it.number %>
+ <% if (it.category) { %>
+ in <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/discussions/categories/<%= it.category.slug %>"><%= it.category.name %></a>
+ <% } %>
+ <% if (it.answerAnchor) { %>
+ <a class="answered pill" href="#<%= it.answerAnchor %>">Answered</a>
+ <% } %>
+</div>
+
+<%~ include("./_discussionComment.eta", { comment: it.body }) %>
+
+<% if (it.answerAnchor) { %>
+ <a class="answered" href="#<%= it.answerAnchor %>">
+ <div class="box">&#x2193; Show answer</div>
+ </a>
+<% } %>
+
+<% for (const comment of it.comments) { %>
+ <%~ include("./_discussionComment.eta", { comment }) %>
+<% } %>
diff --git a/views/discussions.eta b/views/discussions.eta
new file mode 100644
index 0000000..87a6903
--- /dev/null
+++ b/views/discussions.eta
@@ -0,0 +1,39 @@
+<%
+layout("/layouts/repo.eta")
+it.path = it.category ? `discussions/${it.category.slug}` : "discussions"
+it.searchType = "discussions"
+%>
+
+<nav>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/discussions">All</a>
+ <% for (const category of it.categories) { %>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/discussions/categories/<%= category.slug %>"><% if (category.emoji) { %><%= category.emoji %> <% } %><%= category.name %></a>
+ <% } %>
+</nav>
+
+<% if (it.discussions.length === 0) { %>
+ <div class="box">No discussions found.</div>
+<% } else { %>
+ <table>
+ <% for (const discussion of it.discussions) { %>
+ <tr>
+ <td>
+ <% if (discussion.category) { %>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/discussions/categories/<%= discussion.category.slug %>"><%= discussion.category.name %></a>
+ <% } %>
+ </td>
+ <td>
+ <a href="/<%= it.repo.owner %>/<%= it.repo.name %>/discussions/<%= discussion.number %>"><%~ discussion.titleHtml %></a>
+ <% if (discussion.answered) { %>
+ <span class="answered pill">Answered</span>
+ <% } %>
+ </td>
+ <td class="timestamp">
+ <% if (discussion.createdAt) { %>
+ <time title="<%= discussion.createdAt %>"><%= discussion.createdAt.split("T")[0] %></time>
+ <% } %>
+ </td>
+ </tr>
+ <% } %>
+ </table>
+<% } %>
diff --git a/views/layouts/repo.eta b/views/layouts/repo.eta
index b1fb65c..6381196 100644
--- a/views/layouts/repo.eta
+++ b/views/layouts/repo.eta
@@ -15,6 +15,7 @@ layout("/layouts/base.eta", {
<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 %>/discussions">Discussions</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) { %>
@@ -27,6 +28,7 @@ layout("/layouts/base.eta", {
<option value="code" <%= it.searchType === "code" ? "selected" : "" %>>Code</option>
<option value="issues" <%= it.searchType === "issues" || it.searchType == null ? "selected" : "" %>>Issues</option>
<option value="pullrequests" <%= it.searchType === "pullrequests" ? "selected" : "" %>>Pull requests</option>
+ <option value="discussions" <%= it.searchType === "discussions" ? "selected" : "" %>>Discussions</option>
</select>
<button type="submit">Search</button>
</form>