๐ fix: wire pinned-dashboard UI into the new design system
Changes
12 files changed, +370 -195
MODIFY
README.md
+1 -1
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -13,8 +13,15 @@
13
13
<pre class="ascii-row">{ main } ~ >_ log --oneline /\ <shark> ;; switch -c ~~~ 0x1337 </> worktree add >>= /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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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