patx/hglab
updated the ux for tags, branches, and bookmarks.
Commit d2abb79755dc · Harrison Erd · 2026-05-04 16:54 -0400
Comments
No comments yet.
Diff
diff --git a/app.py b/app.py
--- a/app.py
+++ b/app.py
@@ -12,7 +12,7 @@
import sqlite3
import subprocess
from pathlib import Path, PurePosixPath
-from urllib.parse import quote, unquote, urlencode, urlparse
+from urllib.parse import parse_qsl, quote, unquote, urlencode, urlparse
import bleach
import markdown
@@ -44,6 +44,7 @@
REF_TYPE_BOOKMARK = "bookmark"
REF_TYPE_TIP = "tip"
REF_TYPES = {REF_TYPE_BRANCH, REF_TYPE_BOOKMARK, REF_TYPE_TIP}
+REF_QUERY_KEYS = {"ref", "ref_type", "ref_value"}
REF_VALUE_SEPARATOR = "|"
SCRIPT_STYLE_RE = re.compile(r"(?is)<(script|style)\b[^>]*>.*?</\1>")
RESERVED_USERNAMES = {"dashboard", "favicon.ico", "hg", "login", "logout", "new", "settings", "signup", "static", "harrisonerd"}
@@ -392,6 +393,8 @@
context.setdefault("render_repo_description", render_repo_description)
context.setdefault("format_ref_label", format_ref_label)
context.setdefault("url_with_ref", url_with_ref)
+ context.setdefault("current_url_with_ref", current_url_with_ref)
+ context.setdefault("ref_option_label", ref_option_label)
return template(template_name, **context)
@@ -1199,6 +1202,19 @@
return f"{url}{separator}{query}"
+def current_url_with_ref(ref_info=None, force=False):
+ params = [
+ (key, value)
+ for key, value in request.query.allitems()
+ if key not in REF_QUERY_KEYS
+ ]
+ query = ref_query_string(ref_info, force=force)
+ if query:
+ params.extend(parse_qsl(query, keep_blank_values=True))
+ encoded = urlencode(params)
+ return request.path + (f"?{encoded}" if encoded else "")
+
+
def format_ref_label(ref_type, ref_name=""):
ref_type = (ref_type or REF_TYPE_TIP).strip().lower()
if ref_type == REF_TYPE_BRANCH:
@@ -1760,6 +1776,7 @@
"repo_active_tab": repo_active_tab(repo),
"source_repo": source_repo,
"selected_ref": selected_ref,
+ "selected_ref_label": ref_option_label(selected_ref) if selected_ref else "",
"ref_options": repo_ref_options(path),
"selected_ref_value": ref_option_value(
selected_ref.get("type", REF_TYPE_TIP),
diff --git a/static/styles.css b/static/styles.css
--- a/static/styles.css
+++ b/static/styles.css
@@ -42,13 +42,52 @@
}
.site-header, .panel-heading, .repo-header { justify-content: space-between; }
+.repo-title-row { display: flex; gap: .75rem; align-items: baseline; flex-wrap: wrap; }
+.repo-title-row h1 { margin-bottom: 0; }
.brand { color: #111; font-weight: 700; text-decoration: none; }
.nav form, .repo-tabs form, .inline-form { display: inline; }
.link-button { padding: 0; color: #0645ad; background: none; border: 0; text-decoration: underline; }
.repo-tabs .repo-tab { padding-bottom: .2rem; border-bottom: 2px solid transparent; }
.repo-tabs .repo-tab.active { color: #111; font-weight: 700; text-decoration: none; }
-.ref-selector { display: flex; gap: .5rem; align-items: end; max-width: 28rem; margin-bottom: 1rem; }
-.ref-selector label { flex: 1; }
+.ref-picker { position: relative; display: inline-block; }
+.ref-picker-menu {
+ position: absolute;
+ left: 0;
+ top: calc(100% + .4rem);
+ z-index: 20;
+ width: 20rem;
+ max-width: calc(100vw - 2rem);
+ padding: .5rem;
+ border: 1px solid #bbb;
+ background: #fff;
+ box-shadow: 0 .4rem 1.2rem rgba(0, 0, 0, .12);
+}
+.ref-picker-search { margin-bottom: .4rem; }
+.ref-picker-options { display: grid; gap: .1rem; max-height: 18rem; overflow-y: auto; }
+.ref-picker-option {
+ display: flex;
+ gap: 1rem;
+ justify-content: space-between;
+ padding: .35rem .45rem;
+ color: #111;
+ text-decoration: none;
+}
+.ref-picker-option:hover, .ref-picker-option:focus, .ref-picker-option.active {
+ background: #eef4fb;
+ text-decoration: none;
+}
+.ref-picker-option.active { font-weight: 700; }
+.ref-picker-current { color: #666; font-weight: 400; }
+.ref-picker-empty { padding: .35rem .45rem; color: #666; }
+.ref-picker-footer {
+ display: flex;
+ gap: .75rem;
+ flex-wrap: wrap;
+ margin-top: .5rem;
+ padding-top: .45rem;
+ border-top: 1px solid #ddd;
+}
+.ref-picker-link.active { color: #111; font-weight: 700; text-decoration: none; }
.filters .active, .tabs .active { color: #111; font-weight: 700; text-decoration: none; }
.hero, .repo-tabs { margin-bottom: 1.5rem; }
.eyebrow, .muted, .empty, .nav-user, .repo-card small, .issue-list span, .commit-list span, .file-list span, .clean-list span, .file-kind { color: #666; }
@@ -75,7 +114,8 @@
.notice { margin-bottom: 1rem; color: #060; }
.danger-zone { color: #900; }
.button.danger { color: #900; border-color: #900; }
-.button-link { background: none; border: none; padding: 0; color: #069; text-decoration: underline; cursor: pointer; font: inherit; }
+.button-link { background: none; border: none; padding: 0; color: #069; text-decoration: underline; cursor: pointer; font: inherit; }
+.ref-picker-toggle { display: inline-flex; gap: .25rem; align-items: center; background: none; border: none; padding: 0; color: #666; cursor: pointer; font: inherit; font-size: smaller; }
@media (max-width: 760px) {
body { margin-top: 1rem; }
diff --git a/templates/base.tpl b/templates/base.tpl
--- a/templates/base.tpl
+++ b/templates/base.tpl
@@ -42,6 +42,86 @@
document.querySelectorAll("pre code").forEach((block) => hljs.highlightElement(block));
}
</script>
+ <script>
+ (() => {
+ const pickers = document.querySelectorAll("[data-ref-picker]");
+ if (!pickers.length) return;
+
+ const resetFilter = (picker) => {
+ const search = picker.querySelector("[data-ref-picker-search]");
+ const options = picker.querySelectorAll("[data-ref-picker-option]");
+ const empty = picker.querySelector("[data-ref-picker-empty]");
+ if (search) search.value = "";
+ options.forEach((option) => {
+ option.hidden = false;
+ });
+ if (empty) empty.hidden = true;
+ };
+
+ const closePicker = (picker) => {
+ const button = picker.querySelector(".ref-picker-toggle");
+ const menu = picker.querySelector("[data-ref-picker-menu]");
+ if (!menu || menu.hidden) return;
+ menu.hidden = true;
+ if (button) button.setAttribute("aria-expanded", "false");
+ };
+
+ const openPicker = (picker) => {
+ pickers.forEach((other) => {
+ if (other !== picker) closePicker(other);
+ });
+ const button = picker.querySelector(".ref-picker-toggle");
+ const menu = picker.querySelector("[data-ref-picker-menu]");
+ const search = picker.querySelector("[data-ref-picker-search]");
+ if (!menu) return;
+ resetFilter(picker);
+ menu.hidden = false;
+ if (button) button.setAttribute("aria-expanded", "true");
+ if (search) search.focus();
+ };
+
+ pickers.forEach((picker) => {
+ const button = picker.querySelector(".ref-picker-toggle");
+ const search = picker.querySelector("[data-ref-picker-search]");
+ const options = picker.querySelectorAll("[data-ref-picker-option]");
+ const empty = picker.querySelector("[data-ref-picker-empty]");
+
+ if (button) {
+ button.addEventListener("click", () => {
+ const menu = picker.querySelector("[data-ref-picker-menu]");
+ if (menu && menu.hidden) {
+ openPicker(picker);
+ } else {
+ closePicker(picker);
+ }
+ });
+ }
+
+ if (search) {
+ search.addEventListener("input", () => {
+ const query = search.value.trim().toLowerCase();
+ let visibleCount = 0;
+ options.forEach((option) => {
+ const isVisible = option.dataset.refLabel.includes(query);
+ option.hidden = !isVisible;
+ if (isVisible) visibleCount += 1;
+ });
+ if (empty) empty.hidden = visibleCount > 0;
+ });
+ }
+ });
+
+ document.addEventListener("click", (event) => {
+ pickers.forEach((picker) => {
+ if (!picker.contains(event.target)) closePicker(picker);
+ });
+ });
+
+ document.addEventListener("keydown", (event) => {
+ if (event.key !== "Escape") return;
+ pickers.forEach(closePicker);
+ });
+ })();
+ </script>
</body>
</html>
-
diff --git a/templates/bookmarks.tpl b/templates/bookmarks.tpl
--- a/templates/bookmarks.tpl
+++ b/templates/bookmarks.tpl
@@ -3,7 +3,7 @@
<section class="repo-header slim">
<div>
% include("repo_fork_eyebrow.tpl")
- <h1><a href="/{{repo['owner_username']}}">{{repo["owner_username"]}}</a>/{{repo["name"]}}</h1>
+ % include("repo_title.tpl", repo=repo)
% include("repo_nav.tpl", repo=repo, commit_count=commit_count, issue_counts=issue_counts, pr_counts=pr_counts, star_count=star_count, is_starred=is_starred, is_owner=is_owner, can_maintain=can_maintain)
</div>
diff --git a/templates/branches.tpl b/templates/branches.tpl
--- a/templates/branches.tpl
+++ b/templates/branches.tpl
@@ -3,7 +3,7 @@
<section class="repo-header slim">
<div>
% include("repo_fork_eyebrow.tpl")
- <h1><a href="/{{repo['owner_username']}}">{{repo["owner_username"]}}</a>/{{repo["name"]}}</h1>
+ % include("repo_title.tpl", repo=repo)
% include("repo_nav.tpl", repo=repo, commit_count=commit_count, issue_counts=issue_counts, pr_counts=pr_counts, star_count=star_count, is_starred=is_starred, is_owner=is_owner, can_maintain=can_maintain)
</div>
diff --git a/templates/commit_detail.tpl b/templates/commit_detail.tpl
--- a/templates/commit_detail.tpl
+++ b/templates/commit_detail.tpl
@@ -3,7 +3,7 @@
<section class="repo-header slim">
<div>
% include("repo_fork_eyebrow.tpl")
- <h1><a href="/{{repo['owner_username']}}">{{repo["owner_username"]}}</a>/{{repo["name"]}}</h1>
+ % include("repo_title.tpl", repo=repo)
% include("repo_nav.tpl", repo=repo, commit_count=commit_count, issue_counts=issue_counts, pr_counts=pr_counts, star_count=star_count, is_starred=is_starred, is_owner=is_owner, can_maintain=can_maintain)
diff --git a/templates/commits.tpl b/templates/commits.tpl
--- a/templates/commits.tpl
+++ b/templates/commits.tpl
@@ -4,10 +4,9 @@
<section class="repo-header slim">
<div>
% include("repo_fork_eyebrow.tpl")
- <h1><a href="/{{repo['owner_username']}}">{{repo["owner_username"]}}</a>/{{repo["name"]}}</h1>
+ % include("repo_title.tpl", repo=repo)
% include("repo_nav.tpl", repo=repo, commit_count=commit_count, issue_counts=issue_counts, pr_counts=pr_counts, star_count=star_count, is_starred=is_starred, is_owner=is_owner, can_maintain=can_maintain)
- % include("ref_selector.tpl")
</div>
</section>
diff --git a/templates/file.tpl b/templates/file.tpl
--- a/templates/file.tpl
+++ b/templates/file.tpl
@@ -3,10 +3,9 @@
<section class="repo-header slim">
<div>
% include("repo_fork_eyebrow.tpl")
- <h1><a href="/{{repo['owner_username']}}">{{repo["owner_username"]}}</a>/{{repo["name"]}}</h1>
+ % include("repo_title.tpl", repo=repo)
% include("repo_nav.tpl", repo=repo, commit_count=commit_count, issue_counts=issue_counts, pr_counts=pr_counts, star_count=star_count, is_starred=is_starred, is_owner=is_owner, can_maintain=can_maintain)
- % include("ref_selector.tpl")
<div class="breadcrumb">
<a href="{{url_with_ref('/' + repo['owner_username'] + '/' + repo['name'] + '/src', selected_ref)}}">root</a>
diff --git a/templates/issue_detail.tpl b/templates/issue_detail.tpl
--- a/templates/issue_detail.tpl
+++ b/templates/issue_detail.tpl
@@ -3,7 +3,7 @@
<section class="repo-header slim">
<div>
% include("repo_fork_eyebrow.tpl")
- <h1><a href="/{{repo['owner_username']}}">{{repo["owner_username"]}}</a>/{{repo["name"]}}</h1>
+ % include("repo_title.tpl", repo=repo)
% include("repo_nav.tpl", repo=repo, commit_count=commit_count, issue_counts=issue_counts, pr_counts=pr_counts, star_count=star_count, is_starred=is_starred, is_owner=is_owner, can_maintain=can_maintain)
@@ -62,4 +62,3 @@
<p><a href="/login?next=/{{repo['owner_username']}}/{{repo['name']}}/issues/{{issue['number']}}">Log in to comment</a></p>
% end
</section>
-
diff --git a/templates/issues.tpl b/templates/issues.tpl
--- a/templates/issues.tpl
+++ b/templates/issues.tpl
@@ -3,7 +3,7 @@
<section class="repo-header slim">
<div>
% include("repo_fork_eyebrow.tpl")
- <h1><a href="/{{repo['owner_username']}}">{{repo["owner_username"]}}</a>/{{repo["name"]}}</h1>
+ % include("repo_title.tpl", repo=repo)
% include("repo_nav.tpl", repo=repo, commit_count=commit_count, issue_counts=issue_counts, pr_counts=pr_counts, star_count=star_count, is_starred=is_starred, is_owner=is_owner, can_maintain=can_maintain)
diff --git a/templates/new_issue.tpl b/templates/new_issue.tpl
--- a/templates/new_issue.tpl
+++ b/templates/new_issue.tpl
@@ -3,7 +3,7 @@
<section class="repo-header slim">
<div>
% include("repo_fork_eyebrow.tpl")
- <h1><a href="/{{repo['owner_username']}}">{{repo["owner_username"]}}</a>/{{repo["name"]}}</h1>
+ % include("repo_title.tpl", repo=repo)
% include("repo_nav.tpl", repo=repo, commit_count=commit_count, issue_counts=issue_counts, pr_counts=pr_counts, star_count=star_count, is_starred=is_starred, is_owner=is_owner, can_maintain=can_maintain)
diff --git a/templates/new_pull_request.tpl b/templates/new_pull_request.tpl
--- a/templates/new_pull_request.tpl
+++ b/templates/new_pull_request.tpl
@@ -3,7 +3,7 @@
<section class="repo-header slim">
<div>
% include("repo_fork_eyebrow.tpl")
- <h1><a href="/{{repo['owner_username']}}">{{repo["owner_username"]}}</a>/{{repo["name"]}}</h1>
+ % include("repo_title.tpl", repo=repo)
% include("repo_nav.tpl", repo=repo, commit_count=commit_count, issue_counts=issue_counts, pr_counts=pr_counts, star_count=star_count, is_starred=is_starred, is_owner=is_owner, can_maintain=can_maintain)
diff --git a/templates/pull_request_detail.tpl b/templates/pull_request_detail.tpl
--- a/templates/pull_request_detail.tpl
+++ b/templates/pull_request_detail.tpl
@@ -3,7 +3,7 @@
<section class="repo-header slim">
<div>
% include("repo_fork_eyebrow.tpl")
- <h1><a href="/{{repo['owner_username']}}">{{repo["owner_username"]}}</a>/{{repo["name"]}}</h1>
+ % include("repo_title.tpl", repo=repo)
% include("repo_nav.tpl", repo=repo, commit_count=commit_count, issue_counts=issue_counts, pr_counts=pr_counts, star_count=star_count, is_starred=is_starred, is_owner=is_owner, can_maintain=can_maintain)
diff --git a/templates/pull_requests.tpl b/templates/pull_requests.tpl
--- a/templates/pull_requests.tpl
+++ b/templates/pull_requests.tpl
@@ -3,7 +3,7 @@
<section class="repo-header slim">
<div>
% include("repo_fork_eyebrow.tpl")
- <h1><a href="/{{repo['owner_username']}}">{{repo["owner_username"]}}</a>/{{repo["name"]}}</h1>
+ % include("repo_title.tpl", repo=repo)
% include("repo_nav.tpl", repo=repo, commit_count=commit_count, issue_counts=issue_counts, pr_counts=pr_counts, star_count=star_count, is_starred=is_starred, is_owner=is_owner, can_maintain=can_maintain)
diff --git a/templates/ref_selector.tpl b/templates/ref_selector.tpl
--- a/templates/ref_selector.tpl
+++ b/templates/ref_selector.tpl
@@ -1,16 +1,32 @@
% selected_ref = get("selected_ref", None)
% ref_options = get("ref_options", [])
% selected_ref_value = get("selected_ref_value", "")
+% selected_ref_label = get("selected_ref_label", ref_option_label(selected_ref) if selected_ref else "")
+% active_tab = get("repo_active_tab", "")
% if selected_ref and ref_options:
- <form class="ref-selector" method="get">
- <label>
- Code ref
- <select name="ref_value">
+ <div class="ref-picker" data-ref-picker>
+ <button class="ref-picker-toggle" type="button" aria-haspopup="true" aria-expanded="false">
+ <span>{{selected_ref_label}}</span>
+ </button>
+ <div class="ref-picker-menu" data-ref-picker-menu hidden>
+ <input class="ref-picker-search" type="search" placeholder="Find a ref..." aria-label="Find a ref" autocomplete="off" data-ref-picker-search>
+ <div class="ref-picker-options" role="menu">
% for option in ref_options:
- <option value="{{option['value']}}" {{"selected" if option["value"] == selected_ref_value else ""}}>{{option["label"]}}</option>
+ % is_selected = option["value"] == selected_ref_value
+ <a class="ref-picker-option {{'active' if is_selected else ''}}" href="{{current_url_with_ref(option['ref'])}}" data-ref-picker-option data-ref-label="{{option['label'].lower()}}" role="menuitem" aria-current="{{'page' if is_selected else 'false'}}">
+ <span>{{option["label"]}}</span>
+ % if is_selected:
+ <span class="ref-picker-current">current</span>
+ % end
+ </a>
% end
- </select>
- </label>
- <button class="button small" type="submit">View</button>
- </form>
+ <div class="ref-picker-empty" data-ref-picker-empty hidden>No refs found</div>
+ </div>
+ <div class="ref-picker-footer">
+ <a class="ref-picker-link {{'active' if active_tab == 'tags' else ''}}" href="/{{repo['owner_username']}}/{{repo['name']}}/tags">Tags</a>
+ <a class="ref-picker-link {{'active' if active_tab == 'branches' else ''}}" href="/{{repo['owner_username']}}/{{repo['name']}}/branches">Branches</a>
+ <a class="ref-picker-link {{'active' if active_tab == 'bookmarks' else ''}}" href="/{{repo['owner_username']}}/{{repo['name']}}/bookmarks">Bookmarks</a>
+ </div>
+ </div>
+ </div>
% end
diff --git a/templates/repo.tpl b/templates/repo.tpl
--- a/templates/repo.tpl
+++ b/templates/repo.tpl
@@ -3,10 +3,9 @@
<section class="repo-header">
<div>
% include("repo_fork_eyebrow.tpl")
- <h1><a href="/{{repo['owner_username']}}">{{repo["owner_username"]}}</a>/{{repo["name"]}}</h1>
+ % include("repo_title.tpl", repo=repo)
% include("repo_nav.tpl", repo=repo, commit_count=commit_count, issue_counts=issue_counts, pr_counts=pr_counts, star_count=star_count, is_starred=is_starred, is_owner=is_owner, can_maintain=can_maintain)
- % include("ref_selector.tpl")
<p>{{!render_markdown_links(repo["description"]) or "No description yet."}}</p>
</div>
diff --git a/templates/repo_nav.tpl b/templates/repo_nav.tpl
--- a/templates/repo_nav.tpl
+++ b/templates/repo_nav.tpl
@@ -4,9 +4,6 @@
<a class="repo-tab {{'active' if active_tab == 'overview' else ''}}" href="{{url_with_ref('/' + repo['owner_username'] + '/' + repo['name'], selected_ref)}}">Overview</a>
<a class="repo-tab {{'active' if active_tab == 'source' else ''}}" href="{{url_with_ref('/' + repo['owner_username'] + '/' + repo['name'] + '/src', selected_ref)}}">Source</a>
<a class="repo-tab {{'active' if active_tab == 'commits' else ''}}" href="{{url_with_ref('/' + repo['owner_username'] + '/' + repo['name'] + '/commits', selected_ref)}}">Commits{{" (" + str(commit_count) + ")" if commit_count else ""}}</a>
- <a class="repo-tab {{'active' if active_tab == 'tags' else ''}}" href="/{{repo['owner_username']}}/{{repo['name']}}/tags">Tags</a>
- <a class="repo-tab {{'active' if active_tab == 'branches' else ''}}" href="/{{repo['owner_username']}}/{{repo['name']}}/branches">Branches</a>
- <a class="repo-tab {{'active' if active_tab == 'bookmarks' else ''}}" href="/{{repo['owner_username']}}/{{repo['name']}}/bookmarks">Bookmarks</a>
<a class="repo-tab {{'active' if active_tab == 'issues' else ''}}" href="/{{repo['owner_username']}}/{{repo['name']}}/issues">Issues{{" (" + str(issue_counts["open"]) + ")" if issue_counts["open"] else ""}}</a>
<a class="repo-tab {{'active' if active_tab == 'pulls' else ''}}" href="/{{repo['owner_username']}}/{{repo['name']}}/pulls">Pull requests{{" (" + str(pr_counts["open"]) + ")" if pr_counts["open"] else ""}}</a>
% if user:
diff --git a/templates/repo_settings.tpl b/templates/repo_settings.tpl
--- a/templates/repo_settings.tpl
+++ b/templates/repo_settings.tpl
@@ -3,7 +3,7 @@
<section class="repo-header slim">
<div>
% include("repo_fork_eyebrow.tpl")
- <h1><a href="/{{repo['owner_username']}}">{{repo["owner_username"]}}</a>/{{repo["name"]}}</h1>
+ % include("repo_title.tpl", repo=repo)
% include("repo_nav.tpl", repo=repo, commit_count=commit_count, issue_counts=issue_counts, pr_counts=pr_counts, star_count=star_count, is_starred=is_starred, is_owner=is_owner, can_maintain=can_maintain)
diff --git a/templates/repo_title.tpl b/templates/repo_title.tpl
new file mode 100644
--- /dev/null
+++ b/templates/repo_title.tpl
@@ -0,0 +1,4 @@
+<div class="repo-title-row">
+ <h1><a href="/{{repo['owner_username']}}">{{repo["owner_username"]}}</a>/{{repo["name"]}}</h1>
+ % include("ref_selector.tpl", repo=repo)
+</div>
diff --git a/templates/source.tpl b/templates/source.tpl
--- a/templates/source.tpl
+++ b/templates/source.tpl
@@ -3,10 +3,9 @@
<section class="repo-header slim">
<div>
% include("repo_fork_eyebrow.tpl")
- <h1><a href="/{{repo['owner_username']}}">{{repo["owner_username"]}}</a>/{{repo["name"]}}</h1>
+ % include("repo_title.tpl", repo=repo)
% include("repo_nav.tpl", repo=repo, commit_count=commit_count, issue_counts=issue_counts, pr_counts=pr_counts, star_count=star_count, is_starred=is_starred, is_owner=is_owner, can_maintain=can_maintain)
- % include("ref_selector.tpl")
<div class="breadcrumb">
<a href="{{url_with_ref('/' + repo['owner_username'] + '/' + repo['name'] + '/src', selected_ref)}}">root</a>
diff --git a/templates/tags.tpl b/templates/tags.tpl
--- a/templates/tags.tpl
+++ b/templates/tags.tpl
@@ -4,7 +4,7 @@
<section class="repo-header slim">
<div>
% include("repo_fork_eyebrow.tpl")
- <h1><a href="/{{repo['owner_username']}}">{{repo["owner_username"]}}</a>/{{repo["name"]}}</h1>
+ % include("repo_title.tpl", repo=repo)
% include("repo_nav.tpl", repo=repo, commit_count=commit_count, issue_counts=issue_counts, pr_counts=pr_counts, star_count=star_count, is_starred=is_starred, is_owner=is_owner, can_maintain=can_maintain)
@@ -28,4 +28,3 @@
<p class="empty">No tags yet.</p>
% end
</section>
-