gitshark

Clone repository

git clone https://gitshark.de/git/workaround/Gitshark.git
git clone git@gitshark.de:workaround/Gitshark.git

← Commits

๐Ÿ’„ (ui): Collapse account links into an Account header dropdown

185d322b78b2a377a00ea76c6d81a11cd87c22d8 ยท Michael Hainz ยท 2026-07-09T10:53:35Z

Changes

4 files changed, +73 -8

MODIFY README.md +1 -1
diff --git a/README.md b/README.md
index df39d39..1b0e382 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@
14 14 - push and private read authenticate with **personal access tokens** (HTTP Basic password)
15 15 - Clone/fetch/push over `ssh://git@<host>:2222/<owner>/<repo>.git`
16 16 - public-key authentication only; keys managed per user in the UI
17 -- Web UI: an auth-aware header nav (a "Log in" button for visitors; Profile, SSH keys, Access tokens, and Logout links only for signed-in users), landing page with login CTA for visitors (`/`), repository list for authenticated users (`/`), public repository browse at `/explore`, file/tree browser with self-hosted syntax highlighting (extension-based language detection, falls back to plain text for unknown extensions and binary files), a rendered README (commonmark-java, XSS-safe) shown below the file list on the repository overview page, commit log (paginated), branches, tags (own dedicated page, separate from branches), one-time handle selection (`/onboarding`), profile settings (`/settings/profile`). Every repository sub-page shows a persistent left sidebar with repo identity, a Clone button opening the clone dialog, a pin toggle, and section navigation (Code, Commits, Branches, Tags, Issues, Merge requests, plus Collaborators for the owner) with per-section counts and active-section highlighting, and the clone panel has copy-to-clipboard buttons for the HTTP and SSH `git clone` commands. Keyboard shortcuts are an optional, progressive enhancement (`?` opens a help overlay, `Escape` closes it, `g h` goes home) โ€” every page works fully without JavaScript
17 +- Web UI: an auth-aware header nav (a "Log in" button for visitors; for signed-in users a top-level Following link plus an Account dropdown holding Profile, SSH keys, Access tokens, and Logout โ€” a JS-free `<details>` menu), landing page with login CTA for visitors (`/`), repository list for authenticated users (`/`), public repository browse at `/explore`, file/tree browser with self-hosted syntax highlighting (extension-based language detection, falls back to plain text for unknown extensions and binary files), a rendered README (commonmark-java, XSS-safe) shown below the file list on the repository overview page, commit log (paginated), branches, tags (own dedicated page, separate from branches), one-time handle selection (`/onboarding`), profile settings (`/settings/profile`). Every repository sub-page shows a persistent left sidebar with repo identity, a Clone button opening the clone dialog, a pin toggle, and section navigation (Code, Commits, Branches, Tags, Issues, Merge requests, plus Collaborators for the owner) with per-section counts and active-section highlighting, and the clone panel has copy-to-clipboard buttons for the HTTP and SSH `git clone` commands. Keyboard shortcuts are an optional, progressive enhancement (`?` opens a help overlay, `Escape` closes it, `g h` goes home) โ€” every page works fully without JavaScript
18 18 - Per-repository issues: title, optional description, per-repo sequential number (`#1`, `#2`, โ€ฆ), and author; created and managed by the repo owner and collaborators, readable by anyone who can read the repo, via a dedicated "New issue" page
19 19 - Issues move through a fixed lifecycle (Planned โ†’ In development โ†’ Done); the repo navigation shows the open (Planned + In development) issue count, and Done issues collapse into an "Archive" section on the issues page
20 20 - Issues auto-close from pushed commit messages, GitHub-style (`close(s|d)`/`fix(es|ed)`/`resolve(s|d)` + `#<number>`, e.g. `fixes #12`), over both HTTP and SSH pushes
MODIFY src/main/resources/META-INF/resources/shark.css +59 -3
diff --git a/src/main/resources/META-INF/resources/shark.css b/src/main/resources/META-INF/resources/shark.css
index b6a5e17..7b8331c 100644
--- a/src/main/resources/META-INF/resources/shark.css
+++ b/src/main/resources/META-INF/resources/shark.css
@@ -63,6 +63,7 @@
63 63 --success: oklch(0.55 0.12 150);
64 64 --success-deep: oklch(0.47 0.11 150);
65 65 --danger: oklch(0.55 0.17 27);
66 + --danger-deep: oklch(0.45 0.17 27);
66 67 --danger-bg: oklch(0.965 0.02 27);
67 68 --danger-border: oklch(0.85 0.08 27);
68 69 --attention: oklch(0.58 0.1 75);
@@ -221,23 +222,77 @@
221 222 color: var(--accent-deep);
222 223 }
223 224
225 +header.site nav .user-menu {
226 + position: relative;
227 +}
228 +
229 +header.site nav .user-menu summary {
230 + list-style: none;
231 + color: var(--ink);
232 + font-size: 14px;
233 + font-weight: 500;
234 + cursor: pointer;
235 + transition: color .12s;
236 +}
237 +
238 +header.site nav .user-menu summary::-webkit-details-marker {
239 + display: none;
240 +}
241 +
242 +header.site nav .user-menu summary::after {
243 + content: " \25BE";
244 + font-size: 11px;
245 +}
246 +
247 +header.site nav .user-menu summary:hover {
248 + color: var(--accent-deep);
249 +}
250 +
251 +header.site nav .user-menu summary:focus-visible {
252 + outline: 2px solid var(--accent);
253 + outline-offset: 2px;
254 + border-radius: var(--radius-sm);
255 +}
256 +
257 +header.site nav .user-menu[open] summary {
258 + color: var(--accent-deep);
259 +}
260 +
261 +header.site nav .user-menu .user-menu-items {
262 + position: absolute;
263 + right: 0;
264 + top: calc(100% + var(--s2));
265 + display: flex;
266 + flex-direction: column;
267 + gap: var(--s2);
268 + min-width: 160px;
269 + padding: var(--s3);
270 + background: var(--surface);
271 + border: 1px solid var(--border);
272 + border-radius: var(--radius-sm);
273 + box-shadow: 0 4px 12px rgba(0, 0, 0, .08);
274 + z-index: 10;
275 +}
276 +
224 277 header.site nav .logout {
225 278 display: flex;
226 279 margin: 0;
280 + border-top: 1px solid var(--border-soft);
281 + padding-top: var(--s2);
227 282 }
228 283
229 284 header.site nav .logout button {
230 285 padding: 0;
231 286 border: none;
232 287 background: none;
233 - color: var(--ink);
288 + color: var(--danger);
234 289 font: 500 14px/1 var(--font);
235 290 cursor: pointer;
236 291 transition: color .12s;
237 292 }
238 293
239 294 header.site nav .logout button:hover {
240 - color: var(--accent-deep);
295 + color: var(--danger-deep);
241 296 }
242 297
243 298 header.site nav .logout button:focus-visible {
@@ -332,8 +387,9 @@
332 387 .ascii-bg .ascii-row { animation: none; opacity: 0.1; }
333 388 }
334 389
335 -/* keep header and content above the backdrop */
390 +/* keep header and content above the backdrop; header above main so its dropdown overlays content */
336 391 header.site, main { position: relative; z-index: 1; }
392 +header.site { z-index: 2; }
337 393
338 394 /* centered landing hero distinct from the in-app repo hero */
339 395 .landing {
MODIFY src/main/resources/templates/layout.html +9 -4
diff --git a/src/main/resources/templates/layout.html b/src/main/resources/templates/layout.html
index 0577d08..bee460f 100644
--- a/src/main/resources/templates/layout.html
+++ b/src/main/resources/templates/layout.html
@@ -15,10 +15,15 @@
15 15 {#insert nav}
16 16 {#if cdi:currentUser.loggedIn}
17 17 <a href="/following">Following</a>
18 - <a href="/settings/profile">{#avatar user=cdi:currentUser.get /} Profile</a>
19 - <a href="/settings/keys">SSH keys</a>
20 - <a href="/settings/tokens">Access tokens</a>
21 - <form class="logout" method="post" action="/logout"><button type="submit">Logout</button></form>
18 + <details class="user-menu">
19 + <summary>Account</summary>
20 + <div class="user-menu-items">
21 + <a href="/settings/profile">{#avatar user=cdi:currentUser.get /} Profile</a>
22 + <a href="/settings/keys">SSH keys</a>
23 + <a href="/settings/tokens">Access tokens</a>
24 + <form class="logout" method="post" action="/logout"><button type="submit">Logout</button></form>
25 + </div>
26 + </details>
22 27 {#else}
23 28 <a class="btn btn-primary" href="/login">Log in</a>
24 29 {/if}
MODIFY src/test/java/de/workaround/web/WebUiTest.java +4 -0
diff --git a/src/test/java/de/workaround/web/WebUiTest.java b/src/test/java/de/workaround/web/WebUiTest.java
index c955684..f7aa67e 100644
--- a/src/test/java/de/workaround/web/WebUiTest.java
+++ b/src/test/java/de/workaround/web/WebUiTest.java
@@ -522,8 +522,12 @@
522 522 .when().get("/explore")
523 523 .then()
524 524 .statusCode(200)
525 + // account links are collapsed into a single dropdown menu...
526 + .body(containsString("class=\"user-menu\""))
525 527 .body(containsString("Access tokens"))
526 528 .body(containsString("Logout"))
529 + // ...while Following stays a top-level nav link outside the menu
530 + .body(containsString("href=\"/following\""))
527 531 .body(not(containsString("href=\"/login\"")));
528 532 }
529 533

Keyboard shortcuts

?Show this help
g hGo home
EscClose dialog