@seedvault/server 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.html +724 -302
- package/dist/server.js +10 -4
- package/package.json +2 -1
package/dist/index.html
CHANGED
|
@@ -6,34 +6,83 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
7
|
<title>Seedvault</title>
|
|
8
8
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
9
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
10
|
-
<link
|
|
9
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
|
|
10
|
+
<link
|
|
11
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;1,400&display=swap"
|
|
11
12
|
rel="stylesheet" />
|
|
12
13
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@phosphor-icons/web@2.1.2/src/regular/style.css" />
|
|
13
14
|
<style>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
/* ── Variables ── */
|
|
16
|
+
|
|
17
|
+
:root {
|
|
18
|
+
--bg: #F5F3EE;
|
|
19
|
+
--surface: #FFFFFF;
|
|
20
|
+
--surface-hover: #F0EDE6;
|
|
21
|
+
--surface-active: rgba(62, 124, 83, 0.07);
|
|
22
|
+
--border: #E2DED6;
|
|
23
|
+
--border-subtle: #ECEAE4;
|
|
24
|
+
--text: #1D1D1B;
|
|
25
|
+
--text-secondary: #7D7870;
|
|
26
|
+
--text-tertiary: #B0A99E;
|
|
27
|
+
--accent: #3E7C53;
|
|
28
|
+
--accent-hover: #346A47;
|
|
29
|
+
--accent-text: #FFFFFF;
|
|
30
|
+
--highlight: #C4903D;
|
|
31
|
+
--highlight-dim: rgba(196, 144, 61, 0.10);
|
|
32
|
+
--diff-add: #2E7D42;
|
|
33
|
+
--diff-del: #C62828;
|
|
34
|
+
--diff-hunk: #6A42C1;
|
|
35
|
+
--code-bg: rgba(0, 0, 0, 0.035);
|
|
36
|
+
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
37
|
+
--shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
|
38
|
+
--radius: 3px;
|
|
39
|
+
--radius-sm: 2px;
|
|
40
|
+
--font-body: system-ui, -apple-system, sans-serif;
|
|
41
|
+
--font-mono: 'IBM Plex Mono', ui-monospace, monospace;
|
|
42
|
+
--transition: 150ms ease;
|
|
23
43
|
}
|
|
24
44
|
|
|
25
45
|
@media (prefers-color-scheme: dark) {
|
|
26
|
-
|
|
27
|
-
|
|
46
|
+
:root {
|
|
47
|
+
--bg: #0A0A09;
|
|
48
|
+
--surface: #111110;
|
|
49
|
+
--surface-hover: #1A1A18;
|
|
50
|
+
--surface-active: rgba(107, 175, 126, 0.08);
|
|
51
|
+
--border: #222220;
|
|
52
|
+
--border-subtle: #1A1A18;
|
|
53
|
+
--text: #D4D0C8;
|
|
54
|
+
--text-secondary: #8E887E;
|
|
55
|
+
--text-tertiary: #5A554D;
|
|
56
|
+
--accent: #6BAF7E;
|
|
57
|
+
--accent-hover: #82C494;
|
|
58
|
+
--accent-text: #0A0A09;
|
|
59
|
+
--highlight: #D4A853;
|
|
60
|
+
--highlight-dim: rgba(212, 168, 83, 0.12);
|
|
61
|
+
--diff-add: #81C784;
|
|
62
|
+
--diff-del: #EF9A9A;
|
|
63
|
+
--diff-hunk: #B39DDB;
|
|
64
|
+
--code-bg: rgba(255, 255, 255, 0.04);
|
|
65
|
+
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
66
|
+
--shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
28
67
|
}
|
|
68
|
+
}
|
|
29
69
|
|
|
30
|
-
|
|
31
|
-
color: rgb(204, 204, 204);
|
|
32
|
-
}
|
|
70
|
+
/* ── Reset & Base ── */
|
|
33
71
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
72
|
+
*,
|
|
73
|
+
*::before,
|
|
74
|
+
*::after {
|
|
75
|
+
box-sizing: border-box;
|
|
76
|
+
margin: 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
html {
|
|
80
|
+
font-family: var(--font-body);
|
|
81
|
+
font-size: 13px;
|
|
82
|
+
background: var(--bg);
|
|
83
|
+
color: var(--text);
|
|
84
|
+
-webkit-font-smoothing: antialiased;
|
|
85
|
+
-moz-osx-font-smoothing: grayscale;
|
|
37
86
|
}
|
|
38
87
|
|
|
39
88
|
html,
|
|
@@ -42,133 +91,194 @@
|
|
|
42
91
|
}
|
|
43
92
|
|
|
44
93
|
body {
|
|
45
|
-
padding: 12px;
|
|
46
94
|
display: flex;
|
|
47
95
|
flex-direction: column;
|
|
96
|
+
overflow: hidden;
|
|
48
97
|
}
|
|
49
98
|
|
|
50
|
-
|
|
99
|
+
/* ── Top Bar ── */
|
|
100
|
+
|
|
101
|
+
.top-bar {
|
|
51
102
|
display: flex;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
103
|
+
align-items: center;
|
|
104
|
+
justify-content: space-between;
|
|
105
|
+
gap: 12px;
|
|
106
|
+
padding: 0 16px;
|
|
107
|
+
height: 52px;
|
|
55
108
|
flex-shrink: 0;
|
|
109
|
+
border-bottom: 1px solid var(--border);
|
|
110
|
+
background: var(--surface);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.top-bar-left {
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
gap: 12px;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#contributor-select {
|
|
120
|
+
font-family: var(--font-body);
|
|
121
|
+
font-size: 13px;
|
|
122
|
+
font-weight: 500;
|
|
123
|
+
padding: 6px 28px 6px 10px;
|
|
124
|
+
min-width: 120px;
|
|
125
|
+
cursor: pointer;
|
|
126
|
+
appearance: none;
|
|
127
|
+
-webkit-appearance: none;
|
|
128
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%237D7870'/%3E%3C/svg%3E");
|
|
129
|
+
background-repeat: no-repeat;
|
|
130
|
+
background-position: right 10px center;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
#contributor-select:focus {
|
|
134
|
+
border-color: var(--accent);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#contributor-select:disabled {
|
|
138
|
+
opacity: 0.5;
|
|
139
|
+
cursor: default;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@media (prefers-color-scheme: dark) {
|
|
143
|
+
#contributor-select {
|
|
144
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%238E887E'/%3E%3C/svg%3E");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.top-bar-right {
|
|
149
|
+
display: flex;
|
|
150
|
+
align-items: center;
|
|
151
|
+
gap: 8px;
|
|
56
152
|
}
|
|
57
153
|
|
|
58
154
|
input,
|
|
59
|
-
select
|
|
60
|
-
|
|
61
|
-
font:
|
|
62
|
-
padding:
|
|
63
|
-
background:
|
|
64
|
-
color:
|
|
65
|
-
border: 1px solid
|
|
155
|
+
select {
|
|
156
|
+
font-family: var(--font-mono);
|
|
157
|
+
font-size: 12px;
|
|
158
|
+
padding: 6px 10px;
|
|
159
|
+
background: var(--bg);
|
|
160
|
+
color: var(--text);
|
|
161
|
+
border: 1px solid var(--border);
|
|
162
|
+
border-radius: var(--radius-sm);
|
|
163
|
+
outline: none;
|
|
164
|
+
transition: border-color var(--transition);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
input:focus {
|
|
168
|
+
border-color: var(--accent);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
input::placeholder {
|
|
172
|
+
color: var(--text-tertiary);
|
|
66
173
|
}
|
|
67
174
|
|
|
68
175
|
#token {
|
|
69
|
-
|
|
70
|
-
flex: 1;
|
|
176
|
+
width: 240px;
|
|
71
177
|
}
|
|
72
178
|
|
|
73
179
|
button {
|
|
180
|
+
font-family: var(--font-mono);
|
|
181
|
+
font-size: 11px;
|
|
182
|
+
font-weight: 500;
|
|
74
183
|
cursor: pointer;
|
|
184
|
+
border: none;
|
|
185
|
+
background: transparent;
|
|
186
|
+
color: var(--text);
|
|
187
|
+
padding: 0;
|
|
188
|
+
transition: color var(--transition), background var(--transition), opacity var(--transition);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.btn-primary {
|
|
192
|
+
padding: 6px 14px;
|
|
193
|
+
background: var(--accent);
|
|
194
|
+
color: var(--accent-text);
|
|
195
|
+
border-radius: var(--radius-sm);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.btn-primary:hover {
|
|
199
|
+
background: var(--accent-hover);
|
|
75
200
|
}
|
|
76
201
|
|
|
77
202
|
#menu-toggle {
|
|
78
203
|
display: none;
|
|
204
|
+
padding: 6px 8px;
|
|
79
205
|
font-size: 18px;
|
|
80
|
-
|
|
206
|
+
border-radius: var(--radius-sm);
|
|
81
207
|
}
|
|
82
208
|
|
|
209
|
+
#menu-toggle:hover {
|
|
210
|
+
background: var(--surface-hover);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* ── Mobile Nav ── */
|
|
214
|
+
|
|
83
215
|
#nav-close {
|
|
84
216
|
display: none;
|
|
85
|
-
|
|
86
|
-
border: none;
|
|
217
|
+
padding: 4px;
|
|
87
218
|
font-size: 16px;
|
|
88
|
-
|
|
89
|
-
|
|
219
|
+
border-radius: var(--radius-sm);
|
|
220
|
+
opacity: 0.5;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
#nav-close:hover {
|
|
224
|
+
opacity: 1;
|
|
225
|
+
background: var(--surface-hover);
|
|
90
226
|
}
|
|
91
227
|
|
|
92
228
|
#backdrop {
|
|
93
229
|
display: none;
|
|
94
230
|
position: fixed;
|
|
95
231
|
inset: 0;
|
|
96
|
-
background: rgba(0, 0, 0, 0.
|
|
232
|
+
background: rgba(0, 0, 0, 0.35);
|
|
97
233
|
z-index: 90;
|
|
234
|
+
backdrop-filter: blur(2px);
|
|
235
|
+
-webkit-backdrop-filter: blur(2px);
|
|
98
236
|
}
|
|
99
237
|
|
|
100
238
|
#backdrop.visible {
|
|
101
239
|
display: block;
|
|
102
240
|
}
|
|
103
241
|
|
|
242
|
+
/* ── Grid Layout ── */
|
|
243
|
+
|
|
104
244
|
.grid {
|
|
105
245
|
display: flex;
|
|
106
|
-
gap: 0;
|
|
107
246
|
flex: 1;
|
|
108
247
|
min-height: 0;
|
|
109
248
|
}
|
|
110
249
|
|
|
111
250
|
.grid>.panel:first-child {
|
|
112
251
|
flex-shrink: 0;
|
|
252
|
+
background: var(--surface);
|
|
253
|
+
border-right: 1px solid var(--border);
|
|
113
254
|
}
|
|
114
255
|
|
|
115
256
|
.grid>.panel:last-child {
|
|
116
257
|
flex: 1;
|
|
117
258
|
min-width: 0;
|
|
259
|
+
background: var(--bg);
|
|
118
260
|
}
|
|
119
261
|
|
|
120
262
|
.divider {
|
|
121
|
-
width:
|
|
263
|
+
width: 4px;
|
|
122
264
|
cursor: col-resize;
|
|
123
265
|
background: transparent;
|
|
124
266
|
flex-shrink: 0;
|
|
267
|
+
position: relative;
|
|
268
|
+
z-index: 2;
|
|
269
|
+
margin-left: -2px;
|
|
270
|
+
margin-right: -2px;
|
|
125
271
|
}
|
|
126
272
|
|
|
127
273
|
.divider:hover,
|
|
128
274
|
.divider.dragging {
|
|
129
|
-
background:
|
|
275
|
+
background: var(--accent);
|
|
276
|
+
opacity: 0.3;
|
|
130
277
|
}
|
|
131
278
|
|
|
132
|
-
|
|
133
|
-
#menu-toggle {
|
|
134
|
-
display: block;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
#nav-close {
|
|
138
|
-
display: block;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
.grid {
|
|
142
|
-
flex-direction: column;
|
|
143
|
-
position: relative;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
#nav {
|
|
147
|
-
position: fixed;
|
|
148
|
-
top: 0;
|
|
149
|
-
left: -100vw;
|
|
150
|
-
width: 100vw !important;
|
|
151
|
-
height: 100%;
|
|
152
|
-
z-index: 100;
|
|
153
|
-
background: Canvas;
|
|
154
|
-
transition: transform 0.2s ease;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
#nav.open {
|
|
158
|
-
transform: translateX(100vw);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
.grid>.panel:first-child {
|
|
162
|
-
width: auto;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
.divider {
|
|
166
|
-
display: none;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
279
|
+
/* ── Panels ── */
|
|
169
280
|
|
|
170
281
|
.panel {
|
|
171
|
-
border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
|
|
172
282
|
display: flex;
|
|
173
283
|
flex-direction: column;
|
|
174
284
|
min-height: 0;
|
|
@@ -176,13 +286,18 @@
|
|
|
176
286
|
}
|
|
177
287
|
|
|
178
288
|
.panel-head {
|
|
179
|
-
padding:
|
|
180
|
-
border-bottom: 1px solid
|
|
181
|
-
font-
|
|
289
|
+
padding: 10px 14px;
|
|
290
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
291
|
+
font-family: var(--font-mono);
|
|
182
292
|
font-size: 11px;
|
|
293
|
+
font-weight: 600;
|
|
183
294
|
text-transform: uppercase;
|
|
184
|
-
letter-spacing: 0.
|
|
295
|
+
letter-spacing: 0.06em;
|
|
296
|
+
color: var(--text-secondary);
|
|
185
297
|
flex-shrink: 0;
|
|
298
|
+
display: flex;
|
|
299
|
+
align-items: center;
|
|
300
|
+
justify-content: space-between;
|
|
186
301
|
}
|
|
187
302
|
|
|
188
303
|
#nav-body:focus {
|
|
@@ -193,34 +308,46 @@
|
|
|
193
308
|
flex: 1;
|
|
194
309
|
overflow: auto;
|
|
195
310
|
scrollbar-width: thin;
|
|
196
|
-
scrollbar-color:
|
|
311
|
+
scrollbar-color: var(--border) transparent;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.grid>.panel:last-child>.panel-body {
|
|
315
|
+
overflow: auto;
|
|
197
316
|
}
|
|
198
317
|
|
|
318
|
+
/* ── Scrollbars ── */
|
|
319
|
+
|
|
199
320
|
.panel-body::-webkit-scrollbar,
|
|
200
|
-
#content::-webkit-scrollbar
|
|
321
|
+
#content::-webkit-scrollbar,
|
|
322
|
+
#activity-dialog-body::-webkit-scrollbar {
|
|
201
323
|
width: 6px;
|
|
202
324
|
height: 6px;
|
|
203
325
|
}
|
|
204
326
|
|
|
205
327
|
.panel-body::-webkit-scrollbar-track,
|
|
206
|
-
#content::-webkit-scrollbar-track
|
|
328
|
+
#content::-webkit-scrollbar-track,
|
|
329
|
+
#activity-dialog-body::-webkit-scrollbar-track {
|
|
207
330
|
background: transparent;
|
|
208
331
|
}
|
|
209
332
|
|
|
210
333
|
.panel-body::-webkit-scrollbar-thumb,
|
|
211
|
-
#content::-webkit-scrollbar-thumb
|
|
212
|
-
|
|
334
|
+
#content::-webkit-scrollbar-thumb,
|
|
335
|
+
#activity-dialog-body::-webkit-scrollbar-thumb {
|
|
336
|
+
background: var(--border);
|
|
213
337
|
border-radius: 3px;
|
|
214
338
|
}
|
|
215
339
|
|
|
216
340
|
.panel-body::-webkit-scrollbar-thumb:hover,
|
|
217
|
-
#content::-webkit-scrollbar-thumb:hover
|
|
218
|
-
|
|
341
|
+
#content::-webkit-scrollbar-thumb:hover,
|
|
342
|
+
#activity-dialog-body::-webkit-scrollbar-thumb:hover {
|
|
343
|
+
background: var(--text-tertiary);
|
|
219
344
|
}
|
|
220
345
|
|
|
346
|
+
/* ── File Tree ── */
|
|
347
|
+
|
|
221
348
|
.tree {
|
|
222
349
|
list-style: none;
|
|
223
|
-
padding: 0;
|
|
350
|
+
padding: 4px 0;
|
|
224
351
|
}
|
|
225
352
|
|
|
226
353
|
.tree ul {
|
|
@@ -233,11 +360,15 @@
|
|
|
233
360
|
align-items: baseline;
|
|
234
361
|
width: 100%;
|
|
235
362
|
text-align: left;
|
|
236
|
-
|
|
237
|
-
padding: 4px 8px;
|
|
363
|
+
padding: 5px 12px;
|
|
238
364
|
white-space: nowrap;
|
|
239
365
|
overflow: hidden;
|
|
240
366
|
cursor: pointer;
|
|
367
|
+
border-left: 2px solid transparent;
|
|
368
|
+
border-radius: 0;
|
|
369
|
+
font-family: var(--font-mono);
|
|
370
|
+
font-size: 11px;
|
|
371
|
+
transition: background var(--transition), border-color var(--transition), color var(--transition);
|
|
241
372
|
}
|
|
242
373
|
|
|
243
374
|
.tree-row .name {
|
|
@@ -250,172 +381,332 @@
|
|
|
250
381
|
.tree-row .ctime {
|
|
251
382
|
flex-shrink: 0;
|
|
252
383
|
margin-left: 8px;
|
|
253
|
-
|
|
384
|
+
color: var(--text-tertiary);
|
|
254
385
|
font-size: 10px;
|
|
386
|
+
font-family: var(--font-mono);
|
|
255
387
|
}
|
|
256
388
|
|
|
257
389
|
.tree-row:hover {
|
|
258
|
-
background:
|
|
390
|
+
background: var(--surface-hover);
|
|
259
391
|
}
|
|
260
392
|
|
|
261
393
|
.tree-row.active {
|
|
262
|
-
background:
|
|
263
|
-
color:
|
|
394
|
+
background: var(--text);
|
|
395
|
+
border-left-color: transparent;
|
|
396
|
+
color: var(--bg);
|
|
397
|
+
font-weight: 500;
|
|
264
398
|
}
|
|
265
399
|
|
|
266
400
|
.tree-row .arrow {
|
|
267
401
|
display: inline-block;
|
|
268
402
|
width: 1em;
|
|
269
403
|
text-align: center;
|
|
404
|
+
font-size: 9px;
|
|
405
|
+
color: var(--text-tertiary);
|
|
406
|
+
transition: color var(--transition);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.tree-row:hover .arrow {
|
|
410
|
+
color: var(--text-secondary);
|
|
270
411
|
}
|
|
271
412
|
|
|
272
413
|
.tree-row .ph {
|
|
273
414
|
display: inline-block;
|
|
274
415
|
font-size: 14px;
|
|
275
|
-
margin-left:
|
|
276
|
-
margin-right:
|
|
277
|
-
vertical-align: -0.
|
|
416
|
+
margin-left: 6px;
|
|
417
|
+
margin-right: 4px;
|
|
418
|
+
vertical-align: -0.12em;
|
|
419
|
+
color: var(--text-tertiary);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.tree-row.active .ph {
|
|
423
|
+
color: var(--bg);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.tree-row.active .ctime {
|
|
427
|
+
color: var(--bg);
|
|
428
|
+
opacity: 0.6;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.tree-row.active .arrow {
|
|
432
|
+
color: var(--bg);
|
|
278
433
|
}
|
|
279
434
|
|
|
280
435
|
.tree ul.collapsed {
|
|
281
436
|
display: none;
|
|
282
437
|
}
|
|
283
438
|
|
|
439
|
+
/* ── Item Header ── */
|
|
440
|
+
|
|
441
|
+
.item-header {
|
|
442
|
+
padding: 24px 0 20px;
|
|
443
|
+
margin-bottom: 20px;
|
|
444
|
+
border-bottom: 1px solid var(--border);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.item-header-title {
|
|
448
|
+
font-family: var(--font-body);
|
|
449
|
+
font-size: 5rem;
|
|
450
|
+
font-weight: 500;
|
|
451
|
+
line-height: 1.05;
|
|
452
|
+
letter-spacing: -0.01em;
|
|
453
|
+
color: var(--text);
|
|
454
|
+
overflow-wrap: break-word;
|
|
455
|
+
word-break: break-word;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.item-header-path {
|
|
459
|
+
font-size: 11px;
|
|
460
|
+
font-family: var(--font-mono);
|
|
461
|
+
color: var(--text-tertiary);
|
|
462
|
+
margin-bottom: 6px;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.item-header-meta {
|
|
466
|
+
display: flex;
|
|
467
|
+
flex-direction: column;
|
|
468
|
+
gap: 6px;
|
|
469
|
+
margin-top: 14px;
|
|
470
|
+
font-size: 12px;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.item-header-meta .meta-field {
|
|
474
|
+
display: flex;
|
|
475
|
+
align-items: center;
|
|
476
|
+
gap: 6px;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.item-header-meta .meta-label {
|
|
480
|
+
font-weight: 600;
|
|
481
|
+
text-transform: uppercase;
|
|
482
|
+
font-size: 10px;
|
|
483
|
+
letter-spacing: 0.04em;
|
|
484
|
+
color: var(--text-tertiary);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.item-header-meta .meta-value {
|
|
488
|
+
font-family: var(--font-mono);
|
|
489
|
+
font-size: 11px;
|
|
490
|
+
color: var(--text-secondary);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.item-header-meta .meta-tag {
|
|
494
|
+
display: inline-block;
|
|
495
|
+
padding: 2px 8px;
|
|
496
|
+
border-radius: 99px;
|
|
497
|
+
background: var(--surface-hover);
|
|
498
|
+
border: 1px solid var(--border-subtle);
|
|
499
|
+
font-size: 11px;
|
|
500
|
+
color: var(--text-secondary);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/* ── Empty State ── */
|
|
504
|
+
|
|
505
|
+
.empty-state {
|
|
506
|
+
display: flex;
|
|
507
|
+
flex-direction: column;
|
|
508
|
+
align-items: center;
|
|
509
|
+
justify-content: center;
|
|
510
|
+
height: 100%;
|
|
511
|
+
color: var(--text-tertiary);
|
|
512
|
+
gap: 12px;
|
|
513
|
+
user-select: none;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
.empty-state .ph {
|
|
517
|
+
font-size: 48px;
|
|
518
|
+
opacity: 0.4;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.empty-state p {
|
|
522
|
+
font-size: 14px;
|
|
523
|
+
font-weight: 500;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/* ── Content / Markdown ── */
|
|
527
|
+
|
|
284
528
|
#content {
|
|
285
|
-
font-family:
|
|
529
|
+
font-family: var(--font-body);
|
|
286
530
|
font-size: 16px;
|
|
287
|
-
padding:
|
|
288
|
-
|
|
289
|
-
|
|
531
|
+
padding: 28px 32px;
|
|
532
|
+
line-height: 1.5;
|
|
533
|
+
color: var(--text);
|
|
534
|
+
max-width: 544px;
|
|
535
|
+
margin: 0 auto;
|
|
290
536
|
}
|
|
291
537
|
|
|
292
538
|
#content h1 {
|
|
293
|
-
font-size:
|
|
294
|
-
font-weight:
|
|
295
|
-
margin:
|
|
296
|
-
line-height:
|
|
539
|
+
font-size: 2rem;
|
|
540
|
+
font-weight: 600;
|
|
541
|
+
margin: 1rem 0 0.5rem;
|
|
542
|
+
line-height: 2.5rem;
|
|
543
|
+
letter-spacing: 0.02rem;
|
|
297
544
|
}
|
|
298
545
|
|
|
299
546
|
#content h2 {
|
|
300
|
-
font-size: 1.
|
|
547
|
+
font-size: 1.6rem;
|
|
301
548
|
font-weight: 600;
|
|
302
|
-
margin:
|
|
303
|
-
line-height:
|
|
549
|
+
margin: 1rem 0 0.3rem;
|
|
550
|
+
line-height: 2.5rem;
|
|
551
|
+
letter-spacing: 0.02rem;
|
|
304
552
|
}
|
|
305
553
|
|
|
306
554
|
#content h3 {
|
|
307
|
-
font-size: 1.
|
|
555
|
+
font-size: 1.2rem;
|
|
308
556
|
font-weight: 600;
|
|
309
|
-
margin:
|
|
310
|
-
line-height:
|
|
557
|
+
margin: 0.2rem 0 0;
|
|
558
|
+
line-height: 2rem;
|
|
559
|
+
letter-spacing: 0.02rem;
|
|
311
560
|
}
|
|
312
561
|
|
|
313
562
|
#content h4,
|
|
314
563
|
#content h5,
|
|
315
564
|
#content h6 {
|
|
316
|
-
font-size:
|
|
565
|
+
font-size: 1em;
|
|
317
566
|
font-weight: 600;
|
|
318
|
-
margin: 0.
|
|
567
|
+
margin: 0.8em 0 0.4em;
|
|
319
568
|
}
|
|
320
569
|
|
|
321
570
|
#content p {
|
|
322
|
-
margin: 0 0 0.
|
|
571
|
+
margin: 0 0 0.8em;
|
|
323
572
|
}
|
|
324
573
|
|
|
325
574
|
#content img {
|
|
326
575
|
max-width: 100%;
|
|
327
576
|
height: auto;
|
|
328
577
|
display: block;
|
|
578
|
+
border-radius: var(--radius-sm);
|
|
329
579
|
}
|
|
330
580
|
|
|
331
581
|
#content ul,
|
|
332
582
|
#content ol {
|
|
333
|
-
margin: 0 0 0.
|
|
334
|
-
padding-left: 1.
|
|
583
|
+
margin: 0 0 0.8em;
|
|
584
|
+
padding-left: 1.4em;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
#content li {
|
|
588
|
+
margin-bottom: 0.2em;
|
|
335
589
|
}
|
|
336
590
|
|
|
337
591
|
#content pre,
|
|
338
592
|
#content code {
|
|
339
|
-
font-family:
|
|
340
|
-
font-size: 0.
|
|
593
|
+
font-family: var(--font-mono);
|
|
594
|
+
font-size: 0.88em;
|
|
341
595
|
}
|
|
342
596
|
|
|
343
597
|
#content pre {
|
|
344
|
-
background:
|
|
345
|
-
padding:
|
|
346
|
-
border-radius:
|
|
598
|
+
background: var(--code-bg);
|
|
599
|
+
padding: 14px 16px;
|
|
600
|
+
border-radius: var(--radius);
|
|
347
601
|
overflow-x: auto;
|
|
348
|
-
margin: 0.
|
|
602
|
+
margin: 0.6em 0 1em;
|
|
603
|
+
border: 1px solid var(--border-subtle);
|
|
604
|
+
line-height: 1.5;
|
|
349
605
|
}
|
|
350
606
|
|
|
351
607
|
#content code {
|
|
352
|
-
padding: 0.15em 0.
|
|
353
|
-
border-radius:
|
|
354
|
-
background:
|
|
608
|
+
padding: 0.15em 0.35em;
|
|
609
|
+
border-radius: var(--radius-sm);
|
|
610
|
+
background: var(--code-bg);
|
|
355
611
|
}
|
|
356
612
|
|
|
357
613
|
#content pre code {
|
|
358
614
|
padding: 0;
|
|
359
615
|
background: none;
|
|
616
|
+
border: none;
|
|
360
617
|
}
|
|
361
618
|
|
|
362
619
|
#content blockquote {
|
|
363
|
-
border-left:
|
|
364
|
-
margin: 0.
|
|
365
|
-
padding
|
|
366
|
-
color:
|
|
620
|
+
border-left: 3px solid var(--accent);
|
|
621
|
+
margin: 0.6em 0 1em;
|
|
622
|
+
padding: 0.3em 0 0.3em 1em;
|
|
623
|
+
color: var(--text-secondary);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
#content blockquote p:last-child {
|
|
627
|
+
margin-bottom: 0;
|
|
367
628
|
}
|
|
368
629
|
|
|
369
630
|
#content a {
|
|
370
|
-
color:
|
|
631
|
+
color: var(--accent);
|
|
371
632
|
text-decoration: underline;
|
|
633
|
+
text-decoration-color: rgba(62, 124, 83, 0.3);
|
|
634
|
+
text-underline-offset: 2px;
|
|
635
|
+
transition: text-decoration-color var(--transition);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
#content a:hover {
|
|
639
|
+
text-decoration-color: var(--accent);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
@media (prefers-color-scheme: dark) {
|
|
643
|
+
#content a {
|
|
644
|
+
text-decoration-color: rgba(107, 175, 126, 0.3);
|
|
645
|
+
}
|
|
372
646
|
}
|
|
373
647
|
|
|
374
648
|
#content hr {
|
|
375
649
|
border: none;
|
|
376
|
-
border-top: 1px solid
|
|
377
|
-
margin:
|
|
650
|
+
border-top: 1px solid var(--border);
|
|
651
|
+
margin: 2em 0;
|
|
378
652
|
}
|
|
379
653
|
|
|
380
654
|
#content table {
|
|
381
655
|
border-collapse: collapse;
|
|
382
|
-
margin: 0.
|
|
656
|
+
margin: 0.6em 0 1em;
|
|
657
|
+
font-size: 0.92em;
|
|
383
658
|
}
|
|
384
659
|
|
|
385
660
|
#content th,
|
|
386
661
|
#content td {
|
|
387
|
-
border: 1px solid
|
|
388
|
-
padding:
|
|
662
|
+
border: 1px solid var(--border);
|
|
663
|
+
padding: 8px 12px;
|
|
389
664
|
text-align: left;
|
|
390
665
|
}
|
|
391
666
|
|
|
667
|
+
#content th {
|
|
668
|
+
background: var(--code-bg);
|
|
669
|
+
font-weight: 600;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/* ── Status Bar ── */
|
|
673
|
+
|
|
392
674
|
#status {
|
|
393
|
-
|
|
675
|
+
padding: 0 16px;
|
|
676
|
+
height: 32px;
|
|
394
677
|
font-size: 11px;
|
|
395
|
-
|
|
678
|
+
color: var(--text-tertiary);
|
|
396
679
|
flex-shrink: 0;
|
|
397
680
|
display: flex;
|
|
398
681
|
align-items: center;
|
|
682
|
+
border-top: 1px solid var(--border-subtle);
|
|
683
|
+
background: var(--surface);
|
|
399
684
|
}
|
|
400
685
|
|
|
401
686
|
#status-text {
|
|
402
687
|
flex: 1;
|
|
688
|
+
font-family: var(--font-mono);
|
|
689
|
+
font-size: 11px;
|
|
403
690
|
}
|
|
404
691
|
|
|
405
692
|
#activity-btn {
|
|
406
|
-
border: none;
|
|
407
693
|
font-size: 11px;
|
|
408
|
-
|
|
409
|
-
|
|
694
|
+
font-weight: 500;
|
|
695
|
+
padding: 4px 8px;
|
|
696
|
+
border-radius: var(--radius-sm);
|
|
697
|
+
color: var(--text-secondary);
|
|
410
698
|
display: flex;
|
|
411
699
|
align-items: center;
|
|
412
|
-
gap:
|
|
700
|
+
gap: 4px;
|
|
413
701
|
}
|
|
414
702
|
|
|
415
703
|
#activity-btn:hover {
|
|
416
|
-
|
|
704
|
+
color: var(--text);
|
|
705
|
+
background: var(--surface-hover);
|
|
417
706
|
}
|
|
418
707
|
|
|
708
|
+
/* ── Activity Panel ── */
|
|
709
|
+
|
|
419
710
|
#activity-modal {
|
|
420
711
|
display: none;
|
|
421
712
|
position: fixed;
|
|
@@ -430,49 +721,79 @@
|
|
|
430
721
|
#activity-overlay {
|
|
431
722
|
position: absolute;
|
|
432
723
|
inset: 0;
|
|
433
|
-
background: rgba(0, 0, 0, 0.
|
|
724
|
+
background: rgba(0, 0, 0, 0.3);
|
|
725
|
+
backdrop-filter: blur(2px);
|
|
726
|
+
-webkit-backdrop-filter: blur(2px);
|
|
727
|
+
animation: fadeIn 0.2s ease;
|
|
434
728
|
}
|
|
435
729
|
|
|
436
730
|
#activity-dialog {
|
|
437
731
|
position: absolute;
|
|
438
732
|
inset: 0;
|
|
439
|
-
background:
|
|
440
|
-
color:
|
|
733
|
+
background: var(--surface);
|
|
734
|
+
color: var(--text);
|
|
441
735
|
display: flex;
|
|
442
736
|
flex-direction: column;
|
|
443
737
|
overflow: hidden;
|
|
444
738
|
z-index: 1;
|
|
739
|
+
animation: fadeIn 0.2s ease;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
@keyframes fadeIn {
|
|
743
|
+
from {
|
|
744
|
+
opacity: 0;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
to {
|
|
748
|
+
opacity: 1;
|
|
749
|
+
}
|
|
445
750
|
}
|
|
446
751
|
|
|
752
|
+
|
|
447
753
|
#activity-dialog-head {
|
|
448
|
-
padding:
|
|
449
|
-
border-bottom: 1px solid
|
|
450
|
-
font-
|
|
451
|
-
font-
|
|
452
|
-
text-transform: uppercase;
|
|
453
|
-
letter-spacing: 0.05em;
|
|
754
|
+
padding: 10px 16px;
|
|
755
|
+
border-bottom: 1px solid var(--border);
|
|
756
|
+
font-size: 12px;
|
|
757
|
+
font-weight: 600;
|
|
454
758
|
display: flex;
|
|
455
759
|
align-items: center;
|
|
456
760
|
flex-shrink: 0;
|
|
761
|
+
color: var(--text-secondary);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
#activity-dialog-head span {
|
|
765
|
+
display: flex;
|
|
766
|
+
align-items: center;
|
|
767
|
+
gap: 6px;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
#activity-dialog-head .ph {
|
|
771
|
+
font-size: 16px;
|
|
772
|
+
color: var(--accent);
|
|
457
773
|
}
|
|
458
774
|
|
|
459
775
|
#activity-dialog-head button {
|
|
460
776
|
margin-left: auto;
|
|
461
|
-
|
|
777
|
+
padding: 4px 6px;
|
|
462
778
|
font-size: 16px;
|
|
463
|
-
|
|
464
|
-
|
|
779
|
+
border-radius: var(--radius-sm);
|
|
780
|
+
color: var(--text-tertiary);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
#activity-dialog-head button:hover {
|
|
784
|
+
color: var(--text);
|
|
785
|
+
background: var(--surface-hover);
|
|
465
786
|
}
|
|
466
787
|
|
|
467
788
|
#activity-dialog-body {
|
|
468
789
|
flex: 1;
|
|
469
790
|
overflow-y: auto;
|
|
470
791
|
scrollbar-width: thin;
|
|
471
|
-
scrollbar-color:
|
|
792
|
+
scrollbar-color: var(--border) transparent;
|
|
472
793
|
}
|
|
473
794
|
|
|
474
795
|
.activity-list {
|
|
475
|
-
padding:
|
|
796
|
+
padding: 12px 0;
|
|
476
797
|
margin: 0;
|
|
477
798
|
list-style: none;
|
|
478
799
|
}
|
|
@@ -491,13 +812,14 @@
|
|
|
491
812
|
|
|
492
813
|
.activity-time {
|
|
493
814
|
font-size: 11px;
|
|
494
|
-
|
|
815
|
+
font-family: var(--font-mono);
|
|
816
|
+
color: var(--text-secondary);
|
|
495
817
|
white-space: nowrap;
|
|
496
818
|
}
|
|
497
819
|
|
|
498
820
|
.activity-date {
|
|
499
821
|
font-size: 10px;
|
|
500
|
-
|
|
822
|
+
color: var(--text-tertiary);
|
|
501
823
|
}
|
|
502
824
|
|
|
503
825
|
.activity-dot-col {
|
|
@@ -515,7 +837,7 @@
|
|
|
515
837
|
bottom: 0;
|
|
516
838
|
width: 1px;
|
|
517
839
|
transform: translateX(-50%);
|
|
518
|
-
background:
|
|
840
|
+
background: var(--border);
|
|
519
841
|
}
|
|
520
842
|
|
|
521
843
|
.activity-item:first-child .activity-dot-col::after {
|
|
@@ -527,13 +849,13 @@
|
|
|
527
849
|
}
|
|
528
850
|
|
|
529
851
|
.activity-dot {
|
|
530
|
-
width:
|
|
531
|
-
height:
|
|
852
|
+
width: 6px;
|
|
853
|
+
height: 6px;
|
|
532
854
|
border-radius: 50%;
|
|
533
|
-
background:
|
|
534
|
-
opacity: 0.
|
|
855
|
+
background: var(--accent);
|
|
856
|
+
opacity: 0.5;
|
|
535
857
|
flex-shrink: 0;
|
|
536
|
-
margin-top:
|
|
858
|
+
margin-top: 10px;
|
|
537
859
|
position: relative;
|
|
538
860
|
z-index: 1;
|
|
539
861
|
}
|
|
@@ -543,156 +865,244 @@
|
|
|
543
865
|
}
|
|
544
866
|
|
|
545
867
|
.activity-content {
|
|
546
|
-
padding: 6px
|
|
868
|
+
padding: 6px 16px 8px 0;
|
|
547
869
|
min-width: 0;
|
|
548
870
|
}
|
|
549
871
|
|
|
550
872
|
.activity-action {
|
|
551
|
-
font-family:
|
|
873
|
+
font-family: var(--font-mono);
|
|
552
874
|
font-size: 11px;
|
|
875
|
+
font-weight: 500;
|
|
553
876
|
}
|
|
554
877
|
|
|
555
878
|
.activity-by {
|
|
556
879
|
font-size: 11px;
|
|
557
|
-
|
|
880
|
+
color: var(--text-tertiary);
|
|
558
881
|
}
|
|
559
882
|
|
|
560
883
|
.activity-contributor {
|
|
561
884
|
font-weight: 600;
|
|
562
|
-
|
|
885
|
+
color: var(--text-secondary);
|
|
563
886
|
}
|
|
564
887
|
|
|
565
888
|
.activity-detail {
|
|
566
|
-
margin-top:
|
|
889
|
+
margin-top: 3px;
|
|
567
890
|
font-size: 11px;
|
|
568
|
-
|
|
569
|
-
font-family:
|
|
891
|
+
color: var(--text-tertiary);
|
|
892
|
+
font-family: var(--font-mono);
|
|
570
893
|
word-break: break-all;
|
|
571
894
|
}
|
|
572
895
|
|
|
573
896
|
.activity-diff {
|
|
574
|
-
margin-top:
|
|
575
|
-
font-family:
|
|
897
|
+
margin-top: 6px;
|
|
898
|
+
font-family: var(--font-mono);
|
|
576
899
|
font-size: 11px;
|
|
577
|
-
line-height: 1.
|
|
900
|
+
line-height: 1.45;
|
|
578
901
|
white-space: pre-wrap;
|
|
579
902
|
word-break: break-all;
|
|
580
|
-
padding:
|
|
581
|
-
background:
|
|
582
|
-
border-radius:
|
|
903
|
+
padding: 8px 10px;
|
|
904
|
+
background: var(--code-bg);
|
|
905
|
+
border-radius: var(--radius-sm);
|
|
906
|
+
border: 1px solid var(--border-subtle);
|
|
583
907
|
overflow-x: auto;
|
|
584
908
|
}
|
|
585
909
|
|
|
586
910
|
.activity-diff .diff-add {
|
|
587
|
-
color:
|
|
911
|
+
color: var(--diff-add);
|
|
588
912
|
}
|
|
589
913
|
|
|
590
914
|
.activity-diff .diff-del {
|
|
591
|
-
color:
|
|
915
|
+
color: var(--diff-del);
|
|
592
916
|
}
|
|
593
917
|
|
|
594
918
|
.activity-diff .diff-hunk {
|
|
595
|
-
color:
|
|
919
|
+
color: var(--diff-hunk);
|
|
596
920
|
opacity: 0.7;
|
|
597
921
|
}
|
|
598
922
|
|
|
599
|
-
@media (prefers-color-scheme: dark) {
|
|
600
|
-
.activity-diff .diff-add {
|
|
601
|
-
color: #85e89d;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
.activity-diff .diff-del {
|
|
605
|
-
color: #f97583;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
.activity-diff .diff-hunk {
|
|
609
|
-
color: #b392f0;
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
|
|
613
923
|
.activity-diff-truncated {
|
|
614
924
|
font-size: 10px;
|
|
615
|
-
|
|
925
|
+
color: var(--text-tertiary);
|
|
616
926
|
font-style: italic;
|
|
927
|
+
margin-top: 2px;
|
|
617
928
|
}
|
|
618
929
|
|
|
619
930
|
#activity-load-more {
|
|
620
931
|
width: 100%;
|
|
621
|
-
padding:
|
|
622
|
-
border:
|
|
623
|
-
border-top: 1px solid color-mix(in srgb, currentColor 10%, transparent);
|
|
932
|
+
padding: 10px;
|
|
933
|
+
border-top: 1px solid var(--border-subtle);
|
|
624
934
|
font-size: 11px;
|
|
935
|
+
font-weight: 500;
|
|
625
936
|
text-transform: uppercase;
|
|
626
|
-
letter-spacing: 0.
|
|
627
|
-
|
|
937
|
+
letter-spacing: 0.06em;
|
|
938
|
+
color: var(--text-tertiary);
|
|
628
939
|
}
|
|
629
940
|
|
|
630
941
|
#activity-load-more:hover {
|
|
631
|
-
|
|
632
|
-
background:
|
|
942
|
+
color: var(--text);
|
|
943
|
+
background: var(--surface-hover);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/* ── Responsive ── */
|
|
947
|
+
|
|
948
|
+
@media (max-width: 600px) {
|
|
949
|
+
.top-bar {
|
|
950
|
+
padding: 0 12px;
|
|
951
|
+
height: 48px;
|
|
952
|
+
gap: 8px;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
.top-bar-right {
|
|
956
|
+
flex: 1;
|
|
957
|
+
min-width: 0;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
#contributor-select {
|
|
961
|
+
min-width: 100px;
|
|
962
|
+
flex-shrink: 1;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
#token {
|
|
966
|
+
width: auto;
|
|
967
|
+
flex: 1;
|
|
968
|
+
min-width: 80px;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
#menu-toggle {
|
|
972
|
+
display: flex;
|
|
973
|
+
align-items: center;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
#nav-close {
|
|
977
|
+
display: flex;
|
|
978
|
+
align-items: center;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
.grid {
|
|
982
|
+
flex-direction: column;
|
|
983
|
+
position: relative;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
#nav {
|
|
987
|
+
position: fixed;
|
|
988
|
+
top: 0;
|
|
989
|
+
left: -100vw;
|
|
990
|
+
width: 100vw !important;
|
|
991
|
+
height: 100%;
|
|
992
|
+
z-index: 100;
|
|
993
|
+
background: var(--surface);
|
|
994
|
+
transition: transform 0.2s ease;
|
|
995
|
+
border-right: none !important;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
#nav.open {
|
|
999
|
+
transform: translateX(100vw);
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
.grid>.panel:first-child {
|
|
1003
|
+
width: auto;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
.divider {
|
|
1007
|
+
display: none;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
#content {
|
|
1011
|
+
padding: 20px 16px;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
.item-header-title {
|
|
1015
|
+
font-size: 3rem;
|
|
1016
|
+
}
|
|
633
1017
|
}
|
|
634
1018
|
</style>
|
|
635
1019
|
</head>
|
|
636
1020
|
|
|
637
1021
|
<body>
|
|
638
|
-
<
|
|
639
|
-
<
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
1022
|
+
<header class="top-bar">
|
|
1023
|
+
<div class="top-bar-left">
|
|
1024
|
+
<button id="menu-toggle" aria-label="Toggle navigation"><i class="ph ph-list"></i></button>
|
|
1025
|
+
<select id="contributor-select" disabled>
|
|
1026
|
+
<option value="">Contributors</option>
|
|
1027
|
+
</select>
|
|
1028
|
+
</div>
|
|
1029
|
+
<div class="top-bar-right">
|
|
1030
|
+
<input id="token" type="password" placeholder="API key" />
|
|
1031
|
+
<button id="connect" class="btn-primary">Connect</button>
|
|
1032
|
+
</div>
|
|
1033
|
+
</header>
|
|
643
1034
|
<div id="backdrop"></div>
|
|
644
1035
|
<div class="grid">
|
|
645
|
-
<section id="nav" class="panel" style="width:
|
|
646
|
-
<div class="panel-head">Files
|
|
1036
|
+
<section id="nav" class="panel" style="width:260px">
|
|
1037
|
+
<div class="panel-head"><span>Files</span><button id="nav-close" aria-label="Close navigation"><i
|
|
1038
|
+
class="ph ph-x"></i></button></div>
|
|
647
1039
|
<div class="panel-body" id="nav-body" tabindex="0">
|
|
648
1040
|
<ul id="files" class="tree"></ul>
|
|
649
1041
|
</div>
|
|
650
1042
|
</section>
|
|
651
1043
|
<div id="divider" class="divider"></div>
|
|
652
1044
|
<section class="panel">
|
|
653
|
-
<div class="panel-head">Content</div>
|
|
654
1045
|
<div class="panel-body">
|
|
655
|
-
<div id="content"
|
|
1046
|
+
<div id="content">
|
|
1047
|
+
<div id="item-header" class="item-header" hidden>
|
|
1048
|
+
<div class="item-header-path" id="item-path"></div>
|
|
1049
|
+
<div class="item-header-title" id="item-title"></div>
|
|
1050
|
+
<div class="item-header-meta" id="item-meta"></div>
|
|
1051
|
+
</div>
|
|
1052
|
+
<div id="content-body">
|
|
1053
|
+
<div class="empty-state">
|
|
1054
|
+
<i class="ph ph-plant"></i>
|
|
1055
|
+
<p>Select a file to view</p>
|
|
1056
|
+
</div>
|
|
1057
|
+
</div>
|
|
1058
|
+
</div>
|
|
656
1059
|
</div>
|
|
657
1060
|
</section>
|
|
658
1061
|
</div>
|
|
659
1062
|
<div id="activity-modal">
|
|
660
1063
|
<div id="activity-overlay"></div>
|
|
661
1064
|
<div id="activity-dialog">
|
|
662
|
-
<div id="activity-dialog-head">Activity
|
|
1065
|
+
<div id="activity-dialog-head"><span><i class="ph ph-pulse"></i> Activity</span><button id="activity-close"
|
|
1066
|
+
aria-label="Close"><i class="ph ph-x"></i></button></div>
|
|
663
1067
|
<div id="activity-dialog-body"></div>
|
|
664
1068
|
</div>
|
|
665
1069
|
</div>
|
|
666
|
-
<
|
|
1070
|
+
<footer id="status">
|
|
667
1071
|
<span id="status-text"></span>
|
|
668
|
-
<button id="activity-btn" aria-label="Activity log"><i class="ph ph-
|
|
669
|
-
|
|
1072
|
+
<button id="activity-btn" aria-label="Activity log"><i class="ph ph-clock-counter-clockwise"></i>
|
|
1073
|
+
Activity</button>
|
|
1074
|
+
</footer>
|
|
670
1075
|
<script type="module">
|
|
671
1076
|
const { marked } = await import("https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js");
|
|
672
1077
|
const matter = (await import("https://cdn.jsdelivr.net/npm/gray-matter@4.0.3/+esm")).default;
|
|
673
1078
|
const DOMPurify = (await import("https://cdn.jsdelivr.net/npm/dompurify@3.0.9/+esm")).default;
|
|
674
1079
|
const morphdom = (await import("https://cdn.jsdelivr.net/npm/morphdom@2.7.4/+esm")).default;
|
|
675
1080
|
|
|
1081
|
+
const emptyStateHTML = '<div class="empty-state"><i class="ph ph-plant"></i><p>Select a file to view</p></div>';
|
|
1082
|
+
|
|
676
1083
|
function renderMarkdown(raw) {
|
|
677
|
-
if (typeof raw !== "string") return "";
|
|
1084
|
+
if (typeof raw !== "string") return { html: "", meta: {} };
|
|
678
1085
|
try {
|
|
679
|
-
const { content } = matter(raw);
|
|
1086
|
+
const { content, data } = matter(raw);
|
|
680
1087
|
const html = marked.parse(content);
|
|
681
|
-
return
|
|
1088
|
+
return {
|
|
1089
|
+
html: DOMPurify.sanitize(html, { USE_PROFILES: { html: true } }),
|
|
1090
|
+
meta: data || {},
|
|
1091
|
+
};
|
|
682
1092
|
} catch {
|
|
683
1093
|
const escaped = raw
|
|
684
1094
|
.replace(/&/g, "&")
|
|
685
1095
|
.replace(/</g, "<")
|
|
686
1096
|
.replace(/>/g, ">")
|
|
687
1097
|
.replace(/"/g, """);
|
|
688
|
-
return "<pre>" + escaped + "</pre>";
|
|
1098
|
+
return { html: "<pre>" + escaped + "</pre>", meta: {} };
|
|
689
1099
|
}
|
|
690
1100
|
}
|
|
691
1101
|
|
|
692
1102
|
const $ = (id) => document.getElementById(id);
|
|
693
1103
|
const tokenEl = $("token");
|
|
694
1104
|
const filesEl = $("files");
|
|
695
|
-
const contentEl = $("content");
|
|
1105
|
+
const contentEl = $("content-body");
|
|
696
1106
|
const statusTextEl = $("status-text");
|
|
697
1107
|
const backdropEl = $("backdrop");
|
|
698
1108
|
const menuToggleEl = $("menu-toggle");
|
|
@@ -731,8 +1141,7 @@
|
|
|
731
1141
|
return res;
|
|
732
1142
|
}
|
|
733
1143
|
|
|
734
|
-
const
|
|
735
|
-
const savedFile = localStorage.getItem("sv-file") || "";
|
|
1144
|
+
const selectEl = $("contributor-select");
|
|
736
1145
|
|
|
737
1146
|
function getExpandedKeys() {
|
|
738
1147
|
try {
|
|
@@ -805,7 +1214,7 @@
|
|
|
805
1214
|
li.dataset.key = nodePath;
|
|
806
1215
|
const row = document.createElement("div");
|
|
807
1216
|
row.className = "tree-row";
|
|
808
|
-
row.style.paddingLeft = (depth *
|
|
1217
|
+
row.style.paddingLeft = (depth * 14 + 12) + "px";
|
|
809
1218
|
|
|
810
1219
|
if (isFile) {
|
|
811
1220
|
const ctime = child.__file.createdAt || "";
|
|
@@ -819,7 +1228,7 @@
|
|
|
819
1228
|
const expanded = getExpandedKeys().has(username + ":" + nodePath);
|
|
820
1229
|
const arrow = expanded ? "▼" : "▶";
|
|
821
1230
|
if (!expanded) sub.classList.add("collapsed");
|
|
822
|
-
row.innerHTML = '<span class="name"><span class="arrow">' + arrow + '</span><i class="ph ph-folder"></i> ' + key + ' <span style="opacity:0.
|
|
1231
|
+
row.innerHTML = '<span class="name"><span class="arrow">' + arrow + '</span><i class="ph ph-folder"></i> ' + key + ' <span style="opacity:0.45;font-size:11px">' + fileCount + '</span></span>';
|
|
823
1232
|
row.dataset.action = "toggle";
|
|
824
1233
|
row.dataset.toggleKey = username + ":" + nodePath;
|
|
825
1234
|
renderTree(child, sub, username, depth + 1, nodePath);
|
|
@@ -877,12 +1286,6 @@
|
|
|
877
1286
|
rows[idx].click();
|
|
878
1287
|
}
|
|
879
1288
|
|
|
880
|
-
function getLoadedContributorList(username) {
|
|
881
|
-
return filesEl.querySelector(
|
|
882
|
-
'ul[data-contributor="' + CSS.escape(username) + '"][data-loaded="true"]'
|
|
883
|
-
);
|
|
884
|
-
}
|
|
885
|
-
|
|
886
1289
|
filesEl.addEventListener("click", (e) => {
|
|
887
1290
|
const row = e.target.closest(".tree-row");
|
|
888
1291
|
if (!row) return;
|
|
@@ -902,26 +1305,13 @@
|
|
|
902
1305
|
else keys.add(toggleKey);
|
|
903
1306
|
saveExpandedKeys(keys);
|
|
904
1307
|
}
|
|
905
|
-
} else if (action === "contributor") {
|
|
906
|
-
const sub = row.nextElementSibling;
|
|
907
|
-
if (!sub) return;
|
|
908
|
-
const collapsed = sub.classList.contains("collapsed");
|
|
909
|
-
const keys = getExpandedKeys();
|
|
910
|
-
const toggleKey = "contributor:" + row.dataset.contributor;
|
|
911
|
-
if (collapsed) {
|
|
912
|
-
sub.classList.remove("collapsed");
|
|
913
|
-
row.querySelector(".arrow").innerHTML = "▼";
|
|
914
|
-
keys.add(toggleKey);
|
|
915
|
-
} else {
|
|
916
|
-
sub.classList.add("collapsed");
|
|
917
|
-
row.querySelector(".arrow").innerHTML = "▶";
|
|
918
|
-
keys.delete(toggleKey);
|
|
919
|
-
}
|
|
920
|
-
saveExpandedKeys(keys);
|
|
921
1308
|
}
|
|
922
1309
|
});
|
|
923
1310
|
|
|
924
|
-
async function
|
|
1311
|
+
async function loadSelectedContributor(opts = {}) {
|
|
1312
|
+
const username = selectEl.value;
|
|
1313
|
+
if (!username) return;
|
|
1314
|
+
localStorage.setItem("sv-contributor", username);
|
|
925
1315
|
const silent = !!opts.silent;
|
|
926
1316
|
if (!silent) status("Loading files...");
|
|
927
1317
|
const { files } = await (await api("/v1/files?prefix=" + encodeURIComponent(username + "/"))).json();
|
|
@@ -931,63 +1321,79 @@
|
|
|
931
1321
|
path: f.path.startsWith(prefix) ? f.path.slice(prefix.length) : f.path,
|
|
932
1322
|
})));
|
|
933
1323
|
const tmp = document.createElement("ul");
|
|
934
|
-
renderTree(tree, tmp, username,
|
|
935
|
-
if (
|
|
936
|
-
morphdom(
|
|
1324
|
+
renderTree(tree, tmp, username, 0);
|
|
1325
|
+
if (filesEl.hasChildNodes()) {
|
|
1326
|
+
morphdom(filesEl, tmp, {
|
|
937
1327
|
childrenOnly: true,
|
|
938
1328
|
getNodeKey(node) {
|
|
939
1329
|
return node.dataset?.key || "";
|
|
940
1330
|
},
|
|
941
1331
|
});
|
|
942
1332
|
} else {
|
|
943
|
-
|
|
1333
|
+
filesEl.append(...tmp.childNodes);
|
|
944
1334
|
}
|
|
945
1335
|
markActiveRow();
|
|
946
|
-
|
|
947
|
-
const row = sub.parentElement && sub.parentElement.querySelector(".tree-row");
|
|
948
|
-
if (row) {
|
|
949
|
-
const arrow = row.querySelector(".arrow").outerHTML;
|
|
950
|
-
row.innerHTML = '<span class="name">' + arrow + '<i class="ph ph-user"></i> ' + username + ' <span style="opacity:0.5">(' + files.length + ')</span></span>';
|
|
951
|
-
}
|
|
1336
|
+
status(files.length + " file(s)");
|
|
952
1337
|
}
|
|
953
1338
|
|
|
954
1339
|
async function loadContributors() {
|
|
955
1340
|
filesEl.innerHTML = "";
|
|
956
|
-
contentEl.innerHTML =
|
|
1341
|
+
contentEl.innerHTML = emptyStateHTML;
|
|
1342
|
+
$("item-header").hidden = true;
|
|
957
1343
|
status("Loading contributors...");
|
|
958
1344
|
const { contributors } = await (await api("/v1/contributors")).json();
|
|
959
|
-
const expandedKeys = getExpandedKeys();
|
|
960
1345
|
|
|
1346
|
+
selectEl.innerHTML = "";
|
|
961
1347
|
for (const b of contributors) {
|
|
962
|
-
const
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
const sub = document.createElement("ul");
|
|
967
|
-
sub.dataset.contributor = b.username;
|
|
968
|
-
sub.dataset.loaded = "true";
|
|
969
|
-
const expanded = expandedKeys.has("contributor:" + b.username);
|
|
970
|
-
if (!expanded) sub.classList.add("collapsed");
|
|
971
|
-
const arrow = expanded ? "▼" : "▶";
|
|
972
|
-
row.innerHTML = '<span class="name"><span class="arrow">' + arrow + '</span><i class="ph ph-user"></i> ' + b.username + '</span>';
|
|
973
|
-
row.dataset.action = "contributor";
|
|
974
|
-
row.dataset.contributor = b.username;
|
|
975
|
-
li.appendChild(row);
|
|
976
|
-
li.appendChild(sub);
|
|
977
|
-
filesEl.appendChild(li);
|
|
1348
|
+
const opt = document.createElement("option");
|
|
1349
|
+
opt.value = b.username;
|
|
1350
|
+
opt.textContent = b.username;
|
|
1351
|
+
selectEl.appendChild(opt);
|
|
978
1352
|
}
|
|
1353
|
+
selectEl.disabled = contributors.length === 0;
|
|
979
1354
|
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
})
|
|
1355
|
+
const saved = localStorage.getItem("sv-contributor");
|
|
1356
|
+
if (saved && contributors.some((c) => c.username === saved)) {
|
|
1357
|
+
selectEl.value = saved;
|
|
1358
|
+
} else if (contributors.length > 0) {
|
|
1359
|
+
selectEl.value = contributors[0].username;
|
|
1360
|
+
}
|
|
984
1361
|
|
|
985
|
-
|
|
1362
|
+
await loadSelectedContributor();
|
|
1363
|
+
|
|
1364
|
+
const savedFile = localStorage.getItem("sv-file");
|
|
1365
|
+
if (savedFile) {
|
|
986
1366
|
const match = filesEl.querySelector('[data-path="' + CSS.escape(savedFile) + '"]');
|
|
987
|
-
if (match) await loadContent(
|
|
1367
|
+
if (match) await loadContent(selectEl.value, savedFile, match);
|
|
988
1368
|
}
|
|
1369
|
+
}
|
|
989
1370
|
|
|
990
|
-
|
|
1371
|
+
selectEl.addEventListener("change", () => {
|
|
1372
|
+
contentEl.innerHTML = emptyStateHTML;
|
|
1373
|
+
$("item-header").hidden = true;
|
|
1374
|
+
localStorage.removeItem("sv-file");
|
|
1375
|
+
loadSelectedContributor().catch((e) => status(e.message));
|
|
1376
|
+
});
|
|
1377
|
+
|
|
1378
|
+
function escapeHtml(str) {
|
|
1379
|
+
return String(str).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
function renderMeta(meta) {
|
|
1383
|
+
const skip = new Set(["title"]);
|
|
1384
|
+
const parts = [];
|
|
1385
|
+
for (const [key, val] of Object.entries(meta)) {
|
|
1386
|
+
if (skip.has(key) || val == null) continue;
|
|
1387
|
+
const label = '<span class="meta-label">' + escapeHtml(key) + '</span>';
|
|
1388
|
+
let value;
|
|
1389
|
+
if (Array.isArray(val)) {
|
|
1390
|
+
value = val.map((v) => '<span class="meta-tag">' + escapeHtml(v) + "</span>").join(" ");
|
|
1391
|
+
} else {
|
|
1392
|
+
value = '<span class="meta-value">' + escapeHtml(val) + "</span>";
|
|
1393
|
+
}
|
|
1394
|
+
parts.push('<span class="meta-field">' + label + " " + value + "</span>");
|
|
1395
|
+
}
|
|
1396
|
+
return parts.join("");
|
|
991
1397
|
}
|
|
992
1398
|
|
|
993
1399
|
async function loadContent(username, path, row) {
|
|
@@ -1001,8 +1407,18 @@
|
|
|
1001
1407
|
const encodedPath = path.split("/").map(encodeURIComponent).join("/");
|
|
1002
1408
|
const res = await api("/v1/files/" + encodeURIComponent(username) + "/" + encodedPath);
|
|
1003
1409
|
const text = await res.text();
|
|
1004
|
-
|
|
1005
|
-
|
|
1410
|
+
const { html, meta } = renderMarkdown(text);
|
|
1411
|
+
const headerEl = $("item-header");
|
|
1412
|
+
const titleEl = $("item-title");
|
|
1413
|
+
const pathEl = $("item-path");
|
|
1414
|
+
const metaEl = $("item-meta");
|
|
1415
|
+
const fileName = path.split("/").pop().replace(/\.md$/, "");
|
|
1416
|
+
titleEl.textContent = meta.title || fileName;
|
|
1417
|
+
pathEl.textContent = username + ":" + path;
|
|
1418
|
+
metaEl.innerHTML = renderMeta(meta);
|
|
1419
|
+
headerEl.hidden = false;
|
|
1420
|
+
contentEl.innerHTML = html;
|
|
1421
|
+
$("content").scrollTop = 0;
|
|
1006
1422
|
status(path);
|
|
1007
1423
|
}
|
|
1008
1424
|
|
|
@@ -1017,16 +1433,15 @@
|
|
|
1017
1433
|
const contributorReloadTimers = new Map();
|
|
1018
1434
|
|
|
1019
1435
|
function scheduleContributorReload(username) {
|
|
1020
|
-
if (
|
|
1436
|
+
if (username !== selectEl.value) return;
|
|
1021
1437
|
|
|
1022
1438
|
const existing = contributorReloadTimers.get(username);
|
|
1023
1439
|
if (existing) clearTimeout(existing);
|
|
1024
1440
|
|
|
1025
1441
|
const timer = setTimeout(() => {
|
|
1026
1442
|
contributorReloadTimers.delete(username);
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
loadContributorFiles(username, targetUl, { silent: true }).catch((e) => status(e.message));
|
|
1443
|
+
if (username !== selectEl.value) return;
|
|
1444
|
+
loadSelectedContributor({ silent: true }).catch((e) => status(e.message));
|
|
1030
1445
|
}, 100);
|
|
1031
1446
|
|
|
1032
1447
|
contributorReloadTimers.set(username, timer);
|
|
@@ -1041,21 +1456,28 @@
|
|
|
1041
1456
|
evtSource.addEventListener("file_updated", (e) => {
|
|
1042
1457
|
const { contributor, path } = JSON.parse(e.data);
|
|
1043
1458
|
scheduleContributorReload(contributor);
|
|
1044
|
-
// If this file is currently open, reload its content
|
|
1045
1459
|
if (localStorage.getItem("sv-file") === path && localStorage.getItem("sv-contributor") === contributor) {
|
|
1046
1460
|
const encodedPath = path.split("/").map(encodeURIComponent).join("/");
|
|
1047
1461
|
api("/v1/files/" + encodeURIComponent(contributor) + "/" + encodedPath)
|
|
1048
1462
|
.then((res) => res.text())
|
|
1049
|
-
.then((text) => {
|
|
1463
|
+
.then((text) => {
|
|
1464
|
+
const { html, meta } = renderMarkdown(text);
|
|
1465
|
+
const fileName = path.split("/").pop().replace(/\.md$/, "");
|
|
1466
|
+
$("item-title").textContent = meta.title || fileName;
|
|
1467
|
+
$("item-path").textContent = contributor + ":" + path;
|
|
1468
|
+
$("item-meta").innerHTML = renderMeta(meta);
|
|
1469
|
+
$("item-header").hidden = false;
|
|
1470
|
+
contentEl.innerHTML = html;
|
|
1471
|
+
});
|
|
1050
1472
|
}
|
|
1051
1473
|
});
|
|
1052
1474
|
|
|
1053
1475
|
evtSource.addEventListener("file_deleted", (e) => {
|
|
1054
1476
|
const { contributor, path } = JSON.parse(e.data);
|
|
1055
1477
|
scheduleContributorReload(contributor);
|
|
1056
|
-
// If this file was being viewed, clear the content
|
|
1057
1478
|
if (localStorage.getItem("sv-file") === path && localStorage.getItem("sv-contributor") === contributor) {
|
|
1058
|
-
|
|
1479
|
+
$("item-header").hidden = true;
|
|
1480
|
+
contentEl.innerHTML = emptyStateHTML;
|
|
1059
1481
|
status("File deleted: " + path);
|
|
1060
1482
|
}
|
|
1061
1483
|
});
|
|
@@ -1227,12 +1649,12 @@
|
|
|
1227
1649
|
|
|
1228
1650
|
return '<li class="activity-item">' +
|
|
1229
1651
|
'<div class="activity-time-col">' +
|
|
1230
|
-
|
|
1231
|
-
|
|
1652
|
+
'<div class="activity-time">' + time + '</div>' +
|
|
1653
|
+
'<div class="activity-date">' + date + '</div>' +
|
|
1232
1654
|
'</div>' +
|
|
1233
1655
|
'<div class="activity-dot-col">' +
|
|
1234
|
-
|
|
1235
|
-
|
|
1656
|
+
'<div class="activity-dot"></div>' +
|
|
1657
|
+
'<div class="activity-line"></div>' +
|
|
1236
1658
|
'</div>' +
|
|
1237
1659
|
'<div class="activity-content">' + content + '</div>' +
|
|
1238
1660
|
'</li>';
|
|
@@ -1261,7 +1683,7 @@
|
|
|
1261
1683
|
}
|
|
1262
1684
|
|
|
1263
1685
|
if (activitySeenIds.size === 0) {
|
|
1264
|
-
activityBody.innerHTML = '<p style="padding:
|
|
1686
|
+
activityBody.innerHTML = '<p style="padding:16px;color:var(--text-tertiary);font-size:13px">No activity yet.</p>';
|
|
1265
1687
|
return;
|
|
1266
1688
|
}
|
|
1267
1689
|
|
|
@@ -1278,4 +1700,4 @@
|
|
|
1278
1700
|
</script>
|
|
1279
1701
|
</body>
|
|
1280
1702
|
|
|
1281
|
-
</html>
|
|
1703
|
+
</html>
|