gitshark

Clone repository

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

← Commits

๐Ÿ’„ fix: wire pinned-dashboard UI into the new design system

bde0d72b9ff37101cb4f683e5e35de9406bd0cf6 ยท Michael Hainz ยท 2026-06-26T12:28:57Z

Changes

12 files changed, +370 -195

MODIFY README.md +1 -1
diff --git a/README.md b/README.md
index 0cbdc54..45fa376 100644
--- a/README.md
+++ b/README.md
@@ -90,7 +90,7 @@
90 90
91 91 | Store | What |
92 92 |---|---|
93 -| PostgreSQL | `users`, `repositories` (metadata), `ssh_keys` (public keys + fingerprints), `access_tokens` (SHA-256 hashes, labels, last-used), federation tables (`federation_keys`, `remote_actors`, `repository_followers`, `federation_outbox`, `federation_inbox`, `federation_delivery`) |
93 +| PostgreSQL | `users`, `repositories` (metadata), `repository_pins` (per-user pinned repositories), `ssh_keys` (public keys + fingerprints), `access_tokens` (SHA-256 hashes, labels, last-used), federation tables (`federation_keys`, `remote_actors`, `repository_followers`, `federation_outbox`, `federation_inbox`, `federation_delivery`) |
94 94 | Filesystem (`GITSHARK_STORAGE_ROOT`) | Bare Git repositories |
95 95 | Filesystem (`GITSHARK_SSH_HOST_KEY`) | SSH host key |
96 96
ADD src/main/resources/META-INF/resources/favicon.svg +7 -0
diff --git a/src/main/resources/META-INF/resources/favicon.svg b/src/main/resources/META-INF/resources/favicon.svg
new file mode 100644
index 0000000..f84ac09
--- /dev/null
+++ b/src/main/resources/META-INF/resources/favicon.svg
@@ -0,0 +1,7 @@
1 +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
2 +<rect width="64" height="64" rx="14" fill="#168b85"></rect>
3 +<path d="M12 42 H52 M24 42 C29 42 31 50 38 50" fill="none" stroke="#fff" stroke-width="3" stroke-linecap="round"></path>
4 +<circle cx="16" cy="42" r="3.2" fill="#fff"></circle>
5 +<circle cx="38" cy="50" r="3.2" fill="#fff"></circle>
6 +<path d="M26 42 C28 32 32 24 39 14 C40 22 42 33 45 42 Z" fill="#fff"></path>
7 +</svg>
MODIFY src/main/resources/META-INF/resources/shark.css +320 -170
diff --git a/src/main/resources/META-INF/resources/shark.css b/src/main/resources/META-INF/resources/shark.css
index 27622b1..bc7720e 100644
--- a/src/main/resources/META-INF/resources/shark.css
+++ b/src/main/resources/META-INF/resources/shark.css
@@ -1,28 +1,52 @@
1 -/* git-shark design system โ€” light theme, shark blue & white */
1 +/* git-shark design system โ€” warm canvas, deep-teal accent, Space Grotesk + JetBrains Mono */
2 2
3 3 :root {
4 - /* palette */
5 - --shark-blue: #1565c0;
6 - --shark-deep: #0d3b66;
7 - --shark-fin: #4f9fe0;
8 - --shark-belly: #f4f8fb;
4 + /* surfaces */
5 + --canvas: oklch(0.985 0.004 80);
9 6 --surface: #ffffff;
10 - --ink: #16242f;
11 - --ink-muted: #5b7282;
12 - --border: #d3e0ea;
13 - --danger: #c62828;
14 7
15 - /* typography */
16 - --font-body: system-ui, -apple-system, "Segoe UI", sans-serif;
17 - --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Code", Menlo, Consolas, monospace;
8 + /* ink */
9 + --ink: oklch(0.24 0.018 205);
10 + --muted: oklch(0.53 0.018 210);
11 + --faint: oklch(0.66 0.012 210);
18 12
19 - /* shape & rhythm */
20 - --radius: 6px;
21 - --space-1: 0.25rem;
22 - --space-2: 0.5rem;
23 - --space-3: 1rem;
24 - --space-4: 1.5rem;
25 - --space-5: 2.5rem;
13 + /* lines */
14 + --border: oklch(0.9 0.01 205);
15 + --border-soft: oklch(0.945 0.007 205);
16 + --border-strong: oklch(0.82 0.012 205);
17 +
18 + /* accent โ€” deep teal */
19 + --accent: oklch(0.55 0.11 195);
20 + --accent-deep: oklch(0.46 0.1 198);
21 + --accent-soft: oklch(0.96 0.028 195);
22 + --accent-bright: oklch(0.72 0.12 195);
23 +
24 + /* status */
25 + --coral: oklch(0.63 0.13 35);
26 + --success: oklch(0.55 0.12 150);
27 + --success-deep: oklch(0.47 0.11 150);
28 + --danger: oklch(0.55 0.17 27);
29 + --danger-bg: oklch(0.965 0.02 27);
30 + --danger-border: oklch(0.85 0.08 27);
31 + --attention: oklch(0.58 0.1 75);
32 + --attention-bg: oklch(0.965 0.035 85);
33 + --attention-border: oklch(0.84 0.09 80);
34 +
35 + /* shape */
36 + --radius: 12px;
37 + --radius-sm: 8px;
38 +
39 + /* spacing (4px base) */
40 + --s1: 4px;
41 + --s2: 8px;
42 + --s3: 12px;
43 + --s4: 16px;
44 + --s5: 24px;
45 + --s6: 32px;
46 +
47 + /* type */
48 + --font: "Space Grotesk", system-ui, -apple-system, "Segoe UI", sans-serif;
49 + --mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
26 50 }
27 51
28 52 /* base */
@@ -33,23 +57,36 @@
33 57
34 58 body {
35 59 margin: 0;
36 - background: var(--shark-belly);
60 + background: var(--canvas);
37 61 color: var(--ink);
38 - font-family: var(--font-body);
39 - line-height: 1.5;
62 + font: 15px/1.55 var(--font);
63 + -webkit-font-smoothing: antialiased;
40 64 }
41 65
42 66 h1, h2, h3 {
43 - color: var(--shark-deep);
67 + color: var(--ink);
44 68 line-height: 1.2;
69 + letter-spacing: -0.02em;
45 70 }
46 71
47 72 h1 {
48 - font-size: 1.5rem;
73 + font-size: 26px;
74 + font-weight: 700;
75 + letter-spacing: -0.03em;
76 +}
77 +
78 +h2 {
79 + font-size: 19px;
80 + font-weight: 600;
81 +}
82 +
83 +h3 {
84 + font-size: 15px;
85 + font-weight: 600;
49 86 }
50 87
51 88 a {
52 - color: var(--shark-blue);
89 + color: var(--accent-deep);
53 90 text-decoration: none;
54 91 }
55 92
@@ -57,120 +94,134 @@
57 94 text-decoration: underline;
58 95 }
59 96
97 +small {
98 + color: var(--muted);
99 + font: 500 12px/1 var(--mono);
100 +}
101 +
60 102 .mono {
61 - font-family: var(--font-mono);
103 + font-family: var(--mono);
62 104 }
63 105
64 106 .muted {
65 - color: var(--ink-muted);
107 + color: var(--muted);
66 108 }
67 109
68 110 .error {
69 111 color: var(--danger);
112 + background: var(--danger-bg);
113 + border: 1px solid var(--danger-border);
114 + border-radius: var(--radius-sm);
115 + padding: var(--s2) var(--s3);
116 + font-size: 13px;
70 117 }
71 118
72 119 /* header */
73 120
74 121 header.site {
75 122 display: flex;
76 - gap: var(--space-4);
77 123 align-items: center;
78 - padding: var(--space-2) var(--space-4);
79 - background: var(--shark-deep);
124 + gap: var(--s4);
125 + padding: var(--s3) var(--s5);
126 + background: var(--surface);
127 + border-bottom: 1px solid var(--border);
80 128 }
81 129
82 130 header.site .brand {
83 131 display: flex;
84 - gap: var(--space-2);
85 132 align-items: center;
86 - color: #fff;
87 - font-family: var(--font-mono);
88 - font-weight: 700;
89 - font-size: 1.05rem;
133 + gap: 9px;
134 + font: 700 17px/1 var(--font);
135 + letter-spacing: -0.02em;
136 + color: var(--ink);
137 + text-decoration: none;
90 138 }
91 139
92 140 header.site .brand:hover {
93 141 text-decoration: none;
94 - color: var(--shark-fin);
95 142 }
96 143
97 -header.site .brand img {
98 - width: 28px;
99 - height: 28px;
100 - border-radius: 50%;
101 - border: 1px solid var(--shark-fin);
144 +header.site .brand .bmark {
145 + display: block;
146 + flex: none;
147 +}
148 +
149 +header.site .brand .g {
150 + color: var(--ink);
151 +}
152 +
153 +header.site .brand .s {
154 + color: var(--accent);
102 155 }
103 156
104 157 header.site nav {
105 158 display: flex;
106 - gap: var(--space-3);
159 + align-items: center;
160 + gap: var(--s4);
107 161 margin-left: auto;
108 162 }
109 163
110 164 header.site nav a {
111 - color: #d9e8f5;
112 - font-size: 0.9rem;
165 + color: var(--ink);
166 + font-size: 14px;
167 + font-weight: 500;
113 168 }
114 169
115 170 header.site nav a:hover {
116 - color: #fff;
171 + color: var(--accent-deep);
172 + text-decoration: none;
117 173 }
118 174
119 -/* buttons in the header nav must keep their own readable text colour,
120 - not the light nav-link colour */
121 175 header.site nav a.btn-primary {
122 176 color: #fff;
123 177 }
124 178
125 179 header.site nav a.btn-secondary {
126 - color: var(--shark-blue);
180 + color: var(--ink);
127 181 }
128 182
129 183 header.site nav a.btn-secondary:hover {
130 - color: var(--shark-blue);
184 + color: var(--accent-deep);
131 185 }
132 186
133 187 main {
134 - max-width: 960px;
135 - margin: var(--space-5) auto;
136 - padding: 0 var(--space-4);
188 + max-width: 1120px;
189 + margin: var(--s6) auto;
190 + padding: 0 var(--s5);
137 191 }
138 192
139 -/* hero (landing page) */
193 +/* in-app repo hero */
140 194
141 195 .hero {
142 196 display: flex;
143 - gap: var(--space-4);
197 + gap: var(--s4);
144 198 align-items: center;
145 - margin-bottom: var(--space-5);
199 + margin-bottom: var(--s6);
146 200 }
147 201
148 -.hero img {
149 - width: 96px;
150 - height: 96px;
151 - border-radius: 50%;
152 - border: 2px solid var(--border);
202 +.hero .hero-mark {
203 + flex: none;
153 204 }
154 205
155 206 .hero h1 {
156 - font-family: var(--font-mono);
157 - margin: 0 0 var(--space-1);
207 + margin: 0 0 var(--s1);
158 208 }
159 209
160 210 .hero .tagline {
161 - font-family: var(--font-mono);
162 - color: var(--ink-muted);
211 + font-family: var(--mono);
212 + color: var(--muted);
213 + font-size: 14px;
163 214 margin: 0;
164 215 }
165 216
166 217 .hero .tagline::before {
167 218 content: "$ ";
168 - color: var(--shark-fin);
219 + color: var(--accent);
169 220 }
170 221
171 222 /* landing page */
172 223
173 -/* animated glowing symbol backdrop โ€” light theme, sits behind all content */
224 +/* animated glowing symbol backdrop โ€” sits behind all content */
174 225 .ascii-bg {
175 226 position: fixed;
176 227 inset: 0;
@@ -180,8 +231,8 @@
180 231 display: flex;
181 232 flex-direction: column;
182 233 justify-content: space-around;
183 - font-family: var(--font-mono);
184 - color: var(--shark-fin);
234 + font-family: var(--mono);
235 + color: var(--accent);
185 236 -webkit-user-select: none;
186 237 user-select: none;
187 238 }
@@ -194,7 +245,7 @@
194 245 white-space: nowrap;
195 246 font-size: clamp(1.1rem, 2.6vw, 1.9rem);
196 247 letter-spacing: 0.15em;
197 - opacity: 0.16;
248 + opacity: 0.14;
198 249 animation: ascii-glow 7s ease-in-out infinite, ascii-drift 48s linear infinite alternate;
199 250 will-change: transform, opacity, text-shadow;
200 251 }
@@ -206,8 +257,8 @@
206 257 .ascii-bg .ascii-row:nth-child(6) { animation-delay: -7s, -30s; font-size: clamp(1rem, 2.2vw, 1.6rem); }
207 258
208 259 @keyframes ascii-glow {
209 - 0%, 100% { opacity: 0.08; text-shadow: 0 0 4px var(--shark-fin); }
210 - 50% { opacity: 0.30; text-shadow: 0 0 10px var(--shark-blue), 0 0 22px var(--shark-fin); }
260 + 0%, 100% { opacity: 0.06; text-shadow: 0 0 4px var(--accent-bright); }
261 + 50% { opacity: 0.26; text-shadow: 0 0 10px var(--accent), 0 0 22px var(--accent-bright); }
211 262 }
212 263
213 264 @keyframes ascii-drift {
@@ -216,112 +267,151 @@
216 267 }
217 268
218 269 @media (prefers-reduced-motion: reduce) {
219 - .ascii-bg .ascii-row { animation: none; opacity: 0.12; }
270 + .ascii-bg .ascii-row { animation: none; opacity: 0.1; }
220 271 }
221 272
222 273 /* keep header and content above the backdrop */
223 274 header.site, main { position: relative; z-index: 1; }
224 275
225 -/* centered hero distinct from the in-app repo hero */
276 +/* centered landing hero distinct from the in-app repo hero */
226 277 .landing {
227 278 text-align: center;
228 279 max-width: 44rem;
229 - margin: var(--space-5) auto 0;
280 + margin: var(--s6) auto 0;
281 + padding: var(--s6) var(--s4) 0;
282 + background: radial-gradient(1200px 260px at 50% -40px, oklch(0.55 0.11 195 / .12), transparent);
230 283 }
231 284
232 -.landing .landing-logo {
233 - width: 120px;
234 - height: 120px;
235 - border-radius: 50%;
236 - border: 2px solid var(--shark-fin);
237 - box-shadow: 0 0 0 6px rgba(79, 159, 224, 0.12), 0 0 24px rgba(21, 101, 192, 0.25);
285 +.landing .landing-mark {
286 + display: block;
287 + margin: 0 auto var(--s4);
238 288 }
239 289
240 290 .landing h1 {
241 - font-family: var(--font-mono);
242 - font-size: 2.2rem;
243 - margin: var(--space-3) 0 var(--space-1);
291 + font-size: 38px;
292 + margin: 0 0 var(--s3);
244 293 }
245 294
295 +.landing h1 .g { color: var(--ink); }
296 +.landing h1 .s { color: var(--accent); }
297 +
246 298 .landing .tagline {
247 - font-family: var(--font-mono);
248 - color: var(--ink-muted);
249 - margin: 0 0 var(--space-5);
299 + font-family: var(--mono);
300 + color: var(--muted);
301 + font-size: 14px;
302 + margin: 0 0 var(--s5);
250 303 }
251 304
252 305 .landing .tagline::before {
253 306 content: "$ ";
254 - color: var(--shark-fin);
307 + color: var(--accent);
255 308 }
256 309
257 310 .landing-cta {
258 311 display: flex;
259 - gap: var(--space-3);
312 + gap: 10px;
260 313 justify-content: center;
261 314 flex-wrap: wrap;
262 315 }
263 316
264 317 ul.values {
265 318 list-style: none;
266 - padding: 0;
267 - margin: 0 auto var(--space-5);
268 - max-width: 40rem;
319 + padding: 0 var(--s4);
320 + margin: var(--s6) auto;
321 + max-width: 900px;
322 + display: grid;
323 + grid-template-columns: repeat(3, 1fr);
324 + gap: var(--s4);
269 325 text-align: left;
270 326 }
271 327
272 328 ul.values li {
273 - padding: var(--space-3);
274 - margin-bottom: var(--space-2);
275 329 background: var(--surface);
276 330 border: 1px solid var(--border);
277 - border-left: 4px solid var(--shark-blue);
278 331 border-radius: var(--radius);
332 + padding: 20px;
279 333 }
280 334
281 335 ul.values li strong {
282 - color: var(--shark-deep);
336 + display: block;
337 + margin-bottom: 5px;
338 + color: var(--ink);
339 + font-weight: 600;
340 +}
341 +
342 +@media (max-width: 640px) {
343 + ul.values { grid-template-columns: 1fr; }
283 344 }
284 345
285 346 /* buttons */
286 347
287 348 .btn {
288 - display: inline-block;
289 - padding: var(--space-1) var(--space-3);
349 + display: inline-flex;
350 + align-items: center;
351 + gap: 7px;
352 + padding: 8px 16px;
290 353 border: 1px solid transparent;
291 - border-radius: var(--radius);
292 - font: inherit;
293 - font-size: 0.9rem;
354 + border-radius: var(--radius-sm);
355 + font: 600 14px/1 var(--font);
294 356 cursor: pointer;
295 357 text-decoration: none;
358 + white-space: nowrap;
359 + transition: background .12s, border-color .12s, box-shadow .12s;
296 360 }
297 361
298 362 .btn:hover {
299 363 text-decoration: none;
300 364 }
301 365
366 +.btn:focus-visible {
367 + outline: 2px solid var(--accent);
368 + outline-offset: 2px;
369 +}
370 +
302 371 .btn-primary {
303 - background: var(--shark-blue);
372 + background: var(--accent);
304 373 color: #fff;
305 374 }
306 375
307 376 .btn-primary:hover {
308 - background: var(--shark-deep);
377 + background: var(--accent-deep);
378 +}
379 +
380 +.btn-success {
381 + background: var(--success);
382 + color: #fff;
383 +}
384 +
385 +.btn-success:hover {
386 + background: var(--success-deep);
309 387 }
310 388
311 389 .btn-secondary {
312 390 background: var(--surface);
313 - color: var(--shark-blue);
391 + color: var(--ink);
314 392 border-color: var(--border);
393 + box-shadow: 0 1px 0 rgba(20, 32, 30, .03);
315 394 }
316 395
317 396 .btn-secondary:hover {
318 - border-color: var(--shark-blue);
397 + background: var(--canvas);
398 + border-color: var(--border-strong);
399 + color: var(--accent-deep);
400 +}
401 +
402 +.btn-ghost {
403 + background: transparent;
404 + color: var(--accent-deep);
405 +}
406 +
407 +.btn-ghost:hover {
408 + background: var(--accent-soft);
319 409 }
320 410
321 411 .btn-danger {
322 412 background: var(--surface);
323 413 color: var(--danger);
324 - border-color: var(--border);
414 + border-color: var(--danger-border);
325 415 }
326 416
327 417 .btn-danger:hover {
@@ -330,36 +420,68 @@
330 420 color: #fff;
331 421 }
332 422
423 +.btn[disabled], .btn.is-disabled {
424 + opacity: .45;
425 + cursor: not-allowed;
426 + pointer-events: none;
427 +}
428 +
429 +.btn-sm { padding: 5px 11px; font-size: 12.5px; }
430 +.btn-lg { padding: 11px 20px; font-size: 15px; }
431 +
333 432 /* tabs */
334 433
335 434 .tabs {
336 435 display: flex;
337 - gap: var(--space-2);
338 - border-bottom: 2px solid var(--border);
339 - margin-bottom: var(--space-3);
436 + gap: 4px;
437 + border-bottom: 1px solid var(--border);
438 + margin-bottom: var(--s4);
340 439 }
341 440
342 441 .tab {
343 - padding: var(--space-1) var(--space-3);
344 - color: var(--ink-muted);
442 + display: flex;
443 + align-items: center;
444 + gap: 7px;
445 + padding: 9px 14px;
446 + color: var(--muted);
345 447 border-bottom: 2px solid transparent;
346 - margin-bottom: -2px;
347 - font-family: var(--font-mono);
348 - font-size: 0.9rem;
448 + margin-bottom: -1px;
449 + font-size: 14px;
450 + font-weight: 500;
349 451 }
350 452
351 453 .tab:hover {
352 - color: var(--shark-blue);
454 + color: var(--accent-deep);
353 455 text-decoration: none;
354 456 }
355 457
356 458 .tab.active {
357 - color: var(--shark-deep);
358 - border-bottom-color: var(--shark-blue);
359 459 font-weight: 600;
460 + color: var(--ink);
461 + border-bottom-color: var(--accent);
360 462 }
361 463
362 -/* tables */
464 +.tab .ct {
465 + font: 500 11px/1 var(--mono);
466 + color: var(--faint);
467 + background: var(--border-soft);
468 + border-radius: 999px;
469 + padding: 2px 7px;
470 +}
471 +
472 +.tab.active .ct {
473 + color: var(--accent-deep);
474 + background: var(--accent-soft);
475 +}
476 +
477 +/* panels & tables */
478 +
479 +.panel {
480 + background: var(--surface);
481 + border: 1px solid var(--border);
482 + border-radius: var(--radius);
483 + overflow: hidden;
484 +}
363 485
364 486 table {
365 487 border-collapse: collapse;
@@ -367,19 +489,21 @@
367 489 background: var(--surface);
368 490 border: 1px solid var(--border);
369 491 border-radius: var(--radius);
492 + overflow: hidden;
370 493 }
371 494
372 495 td, th {
373 - border-bottom: 1px solid var(--border);
374 - padding: var(--space-2) var(--space-3);
496 + border-bottom: 1px solid var(--border-soft);
497 + padding: var(--s3) var(--s4);
375 498 text-align: left;
376 499 }
377 500
378 501 th {
379 - background: var(--shark-belly);
380 - color: var(--shark-deep);
381 - font-family: var(--font-mono);
382 - font-size: 0.85rem;
502 + background: var(--accent-soft);
503 + color: var(--accent-deep);
504 + font: 600 11px/1 var(--mono);
505 + text-transform: uppercase;
506 + letter-spacing: .06em;
383 507 }
384 508
385 509 tr:last-child td {
@@ -387,39 +511,41 @@
387 511 }
388 512
389 513 tbody tr:hover, table tr:hover td {
390 - background: var(--shark-belly);
514 + background: var(--accent-soft);
391 515 }
392 516
393 517 /* forms */
394 518
395 519 label {
396 520 display: block;
397 - color: var(--shark-deep);
398 - font-size: 0.9rem;
399 - font-weight: 600;
521 + color: var(--ink);
522 + font: 600 13px/1.4 var(--font);
523 + margin-bottom: 7px;
400 524 }
401 525
402 526 input, textarea, select {
403 - font: inherit;
527 + width: 100%;
528 + font: 14px/1.4 var(--font);
404 529 color: var(--ink);
405 530 background: var(--surface);
406 531 border: 1px solid var(--border);
407 - border-radius: var(--radius);
408 - padding: var(--space-1) var(--space-2);
409 - margin-top: var(--space-1);
532 + border-radius: var(--radius-sm);
533 + padding: 8px 12px;
534 + margin-top: var(--s1);
535 + transition: border-color .12s, box-shadow .12s;
410 536 }
411 537
412 538 input:focus, textarea:focus, select:focus {
413 - outline: 2px solid var(--shark-fin);
414 - outline-offset: 0;
415 - border-color: var(--shark-blue);
539 + outline: none;
540 + border-color: var(--accent);
541 + box-shadow: 0 0 0 3px var(--accent-soft);
416 542 }
417 543
418 544 textarea {
419 - width: 100%;
420 - min-height: 6rem;
421 - font-family: var(--font-mono);
422 - font-size: 0.85rem;
545 + font-family: var(--mono);
546 + font-size: 13px;
547 + min-height: 64px;
548 + resize: vertical;
423 549 }
424 550
425 551 form.inline {
@@ -429,67 +555,90 @@
429 555 /* code */
430 556
431 557 code, pre {
432 - font-family: var(--font-mono);
433 - font-size: 0.85rem;
434 - background: var(--shark-belly);
435 - border-radius: 4px;
558 + font-family: var(--mono);
559 + font-size: 13px;
436 560 }
437 561
438 562 code {
439 - padding: 0.1rem 0.3rem;
563 + background: var(--accent-soft);
564 + color: var(--accent-deep);
565 + padding: 2px 7px;
566 + border-radius: 6px;
440 567 }
441 568
442 569 pre {
570 + margin: 0;
571 + background: var(--surface);
443 572 border: 1px solid var(--border);
444 - padding: var(--space-3);
573 + border-radius: var(--radius);
574 + padding: 14px 16px;
575 + line-height: 1.6;
445 576 overflow-x: auto;
446 577 }
447 578
448 579 pre code {
449 580 padding: 0;
450 581 background: none;
582 + color: inherit;
451 583 }
452 584
453 585 /* badges */
454 586
455 587 .badge {
456 588 display: inline-block;
457 - padding: 0 var(--space-2);
589 + font: 500 11px/1 var(--mono);
590 + text-transform: uppercase;
591 + letter-spacing: .05em;
592 + padding: 4px 9px;
458 593 border: 1px solid var(--border);
459 594 border-radius: 999px;
460 - font-family: var(--font-mono);
461 - font-size: 0.75rem;
462 - color: var(--ink-muted);
595 + color: var(--muted);
463 596 }
464 597
465 598 .badge-public {
466 - color: var(--shark-blue);
467 - border-color: var(--shark-fin);
599 + color: var(--accent-deep);
600 + border-color: transparent;
601 + background: var(--accent-soft);
468 602 }
469 603
470 604 .badge-private {
471 - color: var(--shark-deep);
472 - background: var(--shark-belly);
605 + color: var(--attention);
606 + border-color: var(--attention-border);
607 + background: var(--attention-bg);
473 608 }
474 609
475 610 .badge-default {
476 611 color: #fff;
477 - background: var(--shark-blue);
478 - border-color: var(--shark-blue);
612 + background: var(--accent);
613 + border-color: var(--accent);
614 +}
615 +
616 +/* clone command pill */
617 +
618 +.clonecmd {
619 + display: inline-flex;
620 + align-items: center;
621 + gap: 10px;
622 + font: 500 12.5px/1 var(--mono);
623 + color: var(--muted);
624 + background: var(--surface);
625 + border: 1px solid var(--border);
626 + border-radius: 999px;
627 + padding: 8px 14px;
479 628 }
480 629
481 630 /* dashboard */
482 631
483 632 .dashboard-section {
484 - margin-bottom: var(--space-5);
633 + margin-bottom: var(--s5);
485 634 }
486 635
487 636 .dashboard-section h2 {
488 - margin-bottom: var(--space-2);
637 + margin-bottom: var(--s2);
489 638 }
490 639
491 640 .empty-state {
492 - padding: var(--space-3);
641 + padding: var(--s3);
493 642 background: var(--surface);
494 643 border: 1px dashed var(--border);
495 644 border-radius: var(--radius);
@@ -510,22 +659,22 @@
510 659 display: inline-flex;
511 660 align-items: center;
512 661 justify-content: center;
513 - padding: var(--space-1);
662 + padding: var(--s1);
514 663 border: none;
515 664 background: none;
516 - color: var(--ink-muted);
665 + color: var(--muted);
517 666 cursor: pointer;
518 667 border-radius: var(--radius);
519 668 line-height: 0;
520 669 }
521 670
522 671 .btn-icon:hover {
523 - color: var(--shark-blue);
524 - background: var(--shark-belly);
672 + color: var(--accent);
673 + background: var(--accent-soft);
525 674 }
526 675
527 676 .btn-icon.pinned {
528 - color: var(--shark-blue);
677 + color: var(--accent);
529 678 }
530 679
531 680 .btn-icon svg {
@@ -539,19 +688,20 @@
539 688 dialog#hotkey-help {
540 689 border: 1px solid var(--border);
541 690 border-radius: var(--radius);
542 - padding: var(--space-4);
691 + padding: var(--s5);
543 692 min-width: 20rem;
693 + background: var(--surface);
694 + color: var(--ink);
544 695 }
545 696
546 697 dialog#hotkey-help::backdrop {
547 - background: rgba(13, 59, 102, 0.4);
698 + background: oklch(0.24 0.018 205 / .4);
548 699 }
549 700
550 701 dialog#hotkey-help kbd {
551 - font-family: var(--font-mono);
552 - font-size: 0.8rem;
553 - background: var(--shark-belly);
702 + font: 11px/1 var(--mono);
703 + background: var(--surface);
554 704 border: 1px solid var(--border);
555 - border-radius: 4px;
556 - padding: 0 var(--space-1);
705 + border-radius: 5px;
706 + padding: 2px 6px;
557 707 }
DELETE src/main/resources/db/migration/V2__repository_pins.sql +0 -8
diff --git a/src/main/resources/db/migration/V2__repository_pins.sql b/src/main/resources/db/migration/V2__repository_pins.sql
deleted file mode 100644
index ffd2c62..0000000
--- a/src/main/resources/db/migration/V2__repository_pins.sql
+++ /dev/null
@@ -1,8 +0,0 @@
1 -create table repository_pins
2 -(
3 - id uuid primary key,
4 - user_id uuid not null references users (id) on delete cascade,
5 - repository_id uuid not null references repositories (id) on delete cascade,
6 - created_at timestamptz not null default now(),
7 - unique (user_id, repository_id)
8 -);
ADD src/main/resources/db/migration/V3__repository_pins.sql +8 -0
diff --git a/src/main/resources/db/migration/V3__repository_pins.sql b/src/main/resources/db/migration/V3__repository_pins.sql
new file mode 100644
index 0000000..ffd2c62
--- /dev/null
+++ b/src/main/resources/db/migration/V3__repository_pins.sql
@@ -0,0 +1,8 @@
1 +create table repository_pins
2 +(
3 + id uuid primary key,
4 + user_id uuid not null references users (id) on delete cascade,
5 + repository_id uuid not null references repositories (id) on delete cascade,
6 + created_at timestamptz not null default now(),
7 + unique (user_id, repository_id)
8 +);
MODIFY src/main/resources/templates/HomeResource/home.html +8 -1
diff --git a/src/main/resources/templates/HomeResource/home.html b/src/main/resources/templates/HomeResource/home.html
index 723cada..7b7c8ec 100644
--- a/src/main/resources/templates/HomeResource/home.html
+++ b/src/main/resources/templates/HomeResource/home.html
@@ -1,7 +1,14 @@
1 1 {#include layout}
2 2 {#title}git-shark{/title}
3 3 <section class="hero">
4 - <img src="/img/shark-logo.png" alt="git-shark logo">
4 + <svg class="hero-mark" viewBox="0 0 64 64" width="64" height="64" aria-hidden="true">
5 + <path d="M8 40 H56 M22 40 C28 40 30 50 40 50" fill="none" stroke="#11716c" stroke-width="3" stroke-linecap="round"/>
6 + <circle cx="12" cy="40" r="3.4" fill="#11716c"/>
7 + <circle cx="22" cy="40" r="3.4" fill="#11716c"/>
8 + <circle cx="40" cy="50" r="3.4" fill="#df7a5c"/>
9 + <circle cx="52" cy="40" r="3.4" fill="#fff" stroke="#11716c" stroke-width="3"/>
10 + <path d="M24 40 C26 30 31 22 38 12 C39 20 41 31 44 40 Z" fill="#168b85"/>
11 + </svg>
5 12 <div>
6 13 <h1>git-shark</h1>
7 14 <p class="tagline">self-hosted git hosting โ€” natively compiled, bite-sized</p>
MODIFY src/main/resources/templates/HomeResource/landing.html +9 -2
diff --git a/src/main/resources/templates/HomeResource/landing.html b/src/main/resources/templates/HomeResource/landing.html
index 1da1759..d938b63 100644
--- a/src/main/resources/templates/HomeResource/landing.html
+++ b/src/main/resources/templates/HomeResource/landing.html
@@ -13,8 +13,15 @@
13 13 <pre class="ascii-row">{ main } ~ &gt;_ log --oneline /\ &lt;shark&gt; ;; switch -c ~~~ 0x1337 &lt;/&gt; worktree add &gt;&gt;= /push</pre>
14 14 </div>
15 15 <section class="landing">
16 - <img class="landing-logo" src="/img/shark-logo.png" alt="git-shark logo">
17 - <h1>git-shark</h1>
16 + <svg class="landing-mark" viewBox="0 0 64 64" width="112" height="112" aria-hidden="true">
17 + <path d="M8 40 H56 M22 40 C28 40 30 50 40 50" fill="none" stroke="#11716c" stroke-width="3" stroke-linecap="round"/>
18 + <circle cx="12" cy="40" r="3.4" fill="#11716c"/>
19 + <circle cx="22" cy="40" r="3.4" fill="#11716c"/>
20 + <circle cx="40" cy="50" r="3.4" fill="#df7a5c"/>
21 + <circle cx="52" cy="40" r="3.4" fill="#fff" stroke="#11716c" stroke-width="3"/>
22 + <path d="M24 40 C26 30 31 22 38 12 C39 20 41 31 44 40 Z" fill="#168b85"/>
23 + </svg>
24 + <h1><span class="g">git</span><span class="s">shark</span></h1>
18 25 <p class="tagline">self-hosted git hosting for people who still write the code</p>
19 26
20 27 <ul class="values">
MODIFY src/main/resources/templates/RepositoryResource/overview.html +1 -1
diff --git a/src/main/resources/templates/RepositoryResource/overview.html b/src/main/resources/templates/RepositoryResource/overview.html
index adf36b7..fce4088 100644
--- a/src/main/resources/templates/RepositoryResource/overview.html
+++ b/src/main/resources/templates/RepositoryResource/overview.html
@@ -41,7 +41,7 @@
41 41 <h2>Danger zone</h2>
42 42 <form method="post" action="/repos/{repo.owner.username}/{repo.name}/delete">
43 43 <p><label>Type the repository name to confirm deletion <input name="confirm"></label></p>
44 - <button>Delete repository</button>
44 + <button class="btn btn-danger">Delete repository</button>
45 45 </form>
46 46 {/if}
47 47 {/include}
MODIFY src/main/resources/templates/SettingsResource/keys.html +2 -2
diff --git a/src/main/resources/templates/SettingsResource/keys.html b/src/main/resources/templates/SettingsResource/keys.html
index 3d3866e..8ecac2e 100644
--- a/src/main/resources/templates/SettingsResource/keys.html
+++ b/src/main/resources/templates/SettingsResource/keys.html
@@ -12,7 +12,7 @@
12 12 <td><code>{key.fingerprint}</code></td>
13 13 <td>
14 14 <form class="inline" method="post" action="/settings/keys/{key.id}/delete">
15 - <button>Remove</button>
15 + <button class="btn btn-danger btn-sm">Remove</button>
16 16 </form>
17 17 </td>
18 18 </tr>
@@ -22,6 +22,6 @@
22 22 <form method="post" action="/settings/keys">
23 23 <p><label>Title <input name="title" required></label></p>
24 24 <p><label>Public key (OpenSSH format)<br><textarea name="key" required></textarea></label></p>
25 - <button>Add key</button>
25 + <button class="btn btn-primary">Add key</button>
26 26 </form>
27 27 {/include}
MODIFY src/main/resources/templates/SettingsResource/tokens.html +2 -2
diff --git a/src/main/resources/templates/SettingsResource/tokens.html b/src/main/resources/templates/SettingsResource/tokens.html
index 862ead9..70f1886 100644
--- a/src/main/resources/templates/SettingsResource/tokens.html
+++ b/src/main/resources/templates/SettingsResource/tokens.html
@@ -10,7 +10,7 @@
10 10 <td>{token.lastUsed ?: 'never'}</td>
11 11 <td>
12 12 <form class="inline" method="post" action="/settings/tokens/{token.id}/revoke">
13 - <button>Revoke</button>
13 + <button class="btn btn-danger btn-sm">Revoke</button>
14 14 </form>
15 15 </td>
16 16 </tr>
@@ -19,6 +19,6 @@
19 19 <h2>Create token</h2>
20 20 <form method="post" action="/settings/tokens">
21 21 <p><label>Label <input name="label" required></label></p>
22 - <button>Create token</button>
22 + <button class="btn btn-primary">Create token</button>
23 23 </form>
24 24 {/include}
MODIFY src/main/resources/templates/layout.html +5 -2
diff --git a/src/main/resources/templates/layout.html b/src/main/resources/templates/layout.html
index a6cd0a6..ba83ad0 100644
--- a/src/main/resources/templates/layout.html
+++ b/src/main/resources/templates/layout.html
@@ -4,13 +4,16 @@
4 4 <meta charset="utf-8">
5 5 <meta name="viewport" content="width=device-width, initial-scale=1">
6 6 <title>{#insert title}git-shark{/}</title>
7 - <link rel="icon" type="image/png" href="/img/shark-logo.png">
7 + <link rel="icon" type="image/svg+xml" href="/favicon.svg">
8 + <link rel="preconnect" href="https://fonts.googleapis.com">
9 + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10 + <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
8 11 <link rel="stylesheet" href="/shark.css">
9 12 <script defer src="/shark-hotkeys.js"></script>
10 13 </head>
11 14 <body>
12 15 <header class="site">
13 - <a class="brand" href="/"><img src="/img/shark-logo.png" alt="git-shark logo">git-shark</a>
16 + <a class="brand" href="/"><svg class="bmark" viewBox="0 0 64 64" width="26" height="26" aria-hidden="true"><rect width="64" height="64" rx="15" fill="#168b85"></rect><path d="M12 42 H52 M24 42 C29 42 31 50 38 50" fill="none" stroke="#fff" stroke-width="3" stroke-linecap="round"></path><circle cx="16" cy="42" r="3.2" fill="#fff"></circle><circle cx="38" cy="50" r="3.2" fill="#fff"></circle><path d="M26 42 C28 32 32 24 39 14 C40 22 42 33 45 42 Z" fill="#fff"></path></svg><span class="g">git</span><span class="s">shark</span></a>
14 17 <nav>
15 18 {#insert nav}
16 19 <a href="/settings/keys">SSH keys</a>
MODIFY src/test/java/de/workaround/web/DesignSystemTest.java +7 -6
diff --git a/src/test/java/de/workaround/web/DesignSystemTest.java b/src/test/java/de/workaround/web/DesignSystemTest.java
index f1d7253..3d83e95 100644
--- a/src/test/java/de/workaround/web/DesignSystemTest.java
+++ b/src/test/java/de/workaround/web/DesignSystemTest.java
@@ -36,8 +36,9 @@
36 36 given().when().get("/shark.css")
37 37 .then().statusCode(200)
38 38 .body(containsString(":root"))
39 - .body(containsString("--shark-blue"))
40 - .body(containsString("--shark-deep"));
39 + .body(containsString("--accent"))
40 + .body(containsString("--ink"))
41 + .body(containsString("--canvas"));
41 42 }
42 43
43 44 @Test
@@ -45,16 +46,16 @@
45 46 {
46 47 given().when().get("/")
47 48 .then().statusCode(200)
48 - .body(containsString("/img/shark-logo.png"))
49 + .body(containsString("landing-mark"))
49 50 .body(containsString("git-shark"));
50 51 }
51 52
52 53 @Test
53 - void logoIsServedAsStaticResource()
54 + void faviconIsServedAsSvgResource()
54 55 {
55 - given().when().get("/img/shark-logo.png")
56 + given().when().get("/favicon.svg")
56 57 .then().statusCode(200)
57 - .header("Content-Type", containsString("image/png"));
58 + .header("Content-Type", containsString("image/svg+xml"));
58 59 }
59 60
60 61 @Test

Keyboard shortcuts

?Show this help
g hGo home
EscClose dialog