+19
app.js
+19
app.js
···
2
document.addEventListener("alpine:init", () => {
3
Alpine.data("app", () => ({
4
// State
5
serverUrl: "https://knot.srv.rbrt.fr",
6
isConnected: false,
7
status: {
···
34
35
// Initialization
36
init() {
37
// Make this component globally accessible for onclick handlers
38
window.appInstance = this;
39
···
65
66
// Restore from URL on load
67
this.restoreFromURL();
68
},
69
70
// Connection
···
2
document.addEventListener("alpine:init", () => {
3
Alpine.data("app", () => ({
4
// State
5
+
darkTheme: localStorage.getItem("darkTheme") !== "false",
6
serverUrl: "https://knot.srv.rbrt.fr",
7
isConnected: false,
8
status: {
···
35
36
// Initialization
37
init() {
38
+
// Apply saved theme
39
+
this.applyTheme();
40
+
41
// Make this component globally accessible for onclick handlers
42
window.appInstance = this;
43
···
69
70
// Restore from URL on load
71
this.restoreFromURL();
72
+
},
73
+
74
+
// Theme
75
+
toggleTheme() {
76
+
this.darkTheme = !this.darkTheme;
77
+
localStorage.setItem("darkTheme", this.darkTheme);
78
+
this.applyTheme();
79
+
},
80
+
81
+
applyTheme() {
82
+
if (this.darkTheme) {
83
+
document.body.classList.add("dark-theme");
84
+
} else {
85
+
document.body.classList.remove("dark-theme");
86
+
}
87
},
88
89
// Connection
+58
-6
index.html
+58
-6
index.html
···
22
<body>
23
<div class="container" x-data="app" x-init="init()">
24
<header>
25
-
<h1>KnotView</h1>
26
<div class="connection-panel">
27
<input
28
type="text"
···
129
<!-- Empty State -->
130
<div
131
x-show="!loading && !error && !state.currentRepo && view === 'empty'"
132
-
class="empty-state"
133
>
134
<svg
135
xmlns="http://www.w3.org/2000/svg"
···
144
d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"
145
/>
146
</svg>
147
-
<h3>No Repository Selected</h3>
148
-
<p>
149
-
Connect to a server and select a repository to
150
-
browse its contents.
151
</p>
152
</div>
153
154
<!-- Users/Repos List -->
···
22
<body>
23
<div class="container" x-data="app" x-init="init()">
24
<header>
25
+
<div class="header-top">
26
+
<h1>KnotView</h1>
27
+
<button class="theme-toggle" @click="toggleTheme">
28
+
<span x-show="!darkTheme">🌙</span>
29
+
<span x-show="darkTheme">☀️</span>
30
+
<span x-text="darkTheme ? 'Light' : 'Dark'"></span>
31
+
</button>
32
+
</div>
33
<div class="connection-panel">
34
<input
35
type="text"
···
136
<!-- Empty State -->
137
<div
138
x-show="!loading && !error && !state.currentRepo && view === 'empty'"
139
+
class="welcome-hero"
140
>
141
<svg
142
xmlns="http://www.w3.org/2000/svg"
···
151
d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"
152
/>
153
</svg>
154
+
<h2>Welcome to KnotView</h2>
155
+
<p class="subtitle">
156
+
A web-based repository browser for AT Protocol
157
+
repositories. Browse, explore, and download content
158
+
from Knot servers.
159
</p>
160
+
161
+
<div class="feature-list">
162
+
<div class="feature-item">
163
+
<h4>🗂️ Browse Repositories</h4>
164
+
<p>
165
+
Navigate through files and folders with an
166
+
intuitive interface
167
+
</p>
168
+
</div>
169
+
<div class="feature-item">
170
+
<h4>🌿 Branch Support</h4>
171
+
<p>
172
+
Switch between different branches seamlessly
173
+
</p>
174
+
</div>
175
+
<div class="feature-item">
176
+
<h4>📄 File Viewer</h4>
177
+
<p>
178
+
View files with syntax highlighting and
179
+
markdown rendering
180
+
</p>
181
+
</div>
182
+
<div class="feature-item">
183
+
<h4>📦 Download Archives</h4>
184
+
<p>Download entire repositories as archives</p>
185
+
</div>
186
+
</div>
187
+
188
+
<div class="getting-started">
189
+
<h3>Getting Started</h3>
190
+
<ol>
191
+
<li>
192
+
Enter your Knot server URL in the input
193
+
above
194
+
</li>
195
+
<li>
196
+
Click "Connect" to connect to the server
197
+
</li>
198
+
<li>Select a repository from the list</li>
199
+
<li>
200
+
Browse files, switch branches, and explore!
201
+
</li>
202
+
</ol>
203
+
</div>
204
</div>
205
206
<!-- Users/Repos List -->
+2
readme.md
+2
readme.md
···
9
Once PR [#903](https://tangled.org/tangled.org/core/pulls/903) is merged in tangled/core, the UX of browsing a knot will be better.
10
In the meantime, you need to know the did and the repo name of the repository you want to browse.
11
12
+
In case of CORS issues, configure the Knot server to allow CORS requests from the domain hosting KnotView. Or simply use a browser extension to disable CORS check on the domain.
13
+
14
## License
15
16
[MIT](license)
+310
-114
styles.css
+310
-114
styles.css
···
4
box-sizing: border-box;
5
}
6
7
body {
8
-
font-family:
9
-
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
10
-
Cantarell, sans-serif;
11
-
background: #f1f5f9;
12
-
color: #1e293b;
13
font-size: 14px;
14
line-height: 1.5;
15
}
16
17
.container {
···
21
}
22
23
header {
24
-
background: white;
25
padding: 24px;
26
border-radius: 8px;
27
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
28
margin-bottom: 20px;
29
-
border: 1px solid #e2e8f0;
30
}
31
32
h1 {
33
font-size: 24px;
34
-
margin-bottom: 20px;
35
-
color: #0f172a;
36
font-weight: 600;
37
}
38
39
.connection-panel {
40
display: flex;
41
gap: 12px;
···
47
flex: 1;
48
min-width: 300px;
49
padding: 10px 14px;
50
-
border: 1px solid #cbd5e1;
51
border-radius: 6px;
52
font-size: 14px;
53
-
background: white;
54
transition: all 0.15s;
55
}
56
57
.connection-panel input:focus {
58
outline: none;
59
-
border-color: #3b82f6;
60
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
61
}
62
63
button {
64
padding: 10px 20px;
65
-
background: #3b82f6;
66
color: white;
67
border: none;
68
border-radius: 6px;
···
70
font-size: 14px;
71
font-weight: 500;
72
transition: all 0.15s;
73
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
74
}
75
76
button:hover {
77
-
background: #2563eb;
78
}
79
80
button:active {
···
82
}
83
84
button:disabled {
85
-
background: #94a3b8;
86
cursor: not-allowed;
87
transform: none;
88
}
89
90
button.secondary {
91
-
background: white;
92
-
color: #475569;
93
-
border: 1px solid #cbd5e1;
94
}
95
96
button.secondary:hover {
97
-
background: #f8fafc;
98
}
99
100
.status {
···
106
}
107
108
.status.success {
109
-
background: #dcfce7;
110
-
color: #166534;
111
-
border-color: #bbf7d0;
112
}
113
114
.status.error {
115
-
background: #fee2e2;
116
-
color: #991b1b;
117
-
border-color: #fecaca;
118
}
119
120
.main-content {
···
125
126
.sidebar {
127
width: 300px;
128
-
background: white;
129
border-radius: 8px;
130
-
border: 1px solid #e2e8f0;
131
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
132
flex-shrink: 0;
133
}
134
···
136
font-size: 16px;
137
font-weight: 600;
138
padding: 16px 20px;
139
-
border-bottom: 1px solid #e2e8f0;
140
-
color: #0f172a;
141
display: flex;
142
align-items: center;
143
justify-content: space-between;
···
145
146
.repo-info {
147
padding: 20px;
148
-
background: white;
149
-
border-radius: 8px;
150
-
border: 1px solid #e2e8f0;
151
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
152
-
margin-bottom: 20px;
153
}
154
155
.repo-info h3 {
156
font-size: 14px;
157
font-weight: 600;
158
margin-bottom: 12px;
159
-
color: #64748b;
160
}
161
162
.repo-info .label {
163
font-size: 12px;
164
-
color: #64748b;
165
margin-bottom: 4px;
166
font-weight: 500;
167
text-transform: uppercase;
···
170
171
.repo-info .value {
172
font-size: 13px;
173
-
color: #1e293b;
174
margin-bottom: 12px;
175
-
font-family: monospace;
176
}
177
178
.clone-url {
179
display: flex;
180
align-items: center;
181
gap: 8px;
182
-
background: #f8fafc;
183
padding: 8px 12px;
184
border-radius: 6px;
185
-
border: 1px solid #e2e8f0;
186
margin-bottom: 12px;
187
}
188
189
.clone-url code {
190
flex: 1;
191
font-size: 12px;
192
-
color: #475569;
193
overflow: hidden;
194
text-overflow: ellipsis;
195
}
···
201
}
202
203
.copy-btn:hover {
204
-
background: #2563eb;
205
}
206
207
.branches-section {
208
-
border-top: 1px solid #e2e8f0;
209
}
210
211
.branch-list {
···
217
padding: 10px 20px;
218
cursor: pointer;
219
transition: background 0.15s;
220
-
border-bottom: 1px solid #f1f5f9;
221
font-size: 13px;
222
-
color: #475569;
223
display: flex;
224
align-items: center;
225
gap: 8px;
226
}
227
228
.branch-item:hover {
229
-
background: #f8fafc;
230
}
231
232
.branch-item.active {
233
-
background: #eff6ff;
234
-
color: #1e40af;
235
font-weight: 500;
236
}
237
238
.viewer {
239
flex: 1;
240
-
background: white;
241
border-radius: 8px;
242
-
border: 1px solid #e2e8f0;
243
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
244
overflow: hidden;
245
}
246
247
.breadcrumb {
248
padding: 16px 20px;
249
-
border-bottom: 1px solid #e2e8f0;
250
font-size: 13px;
251
-
color: #64748b;
252
-
background: #f8fafc;
253
display: flex;
254
align-items: center;
255
flex-wrap: wrap;
256
}
257
258
.breadcrumb a {
259
-
color: #3b82f6;
260
text-decoration: none;
261
transition: color 0.15s;
262
cursor: pointer;
263
}
264
265
.breadcrumb a:hover {
266
-
color: #2563eb;
267
text-decoration: underline;
268
}
269
···
272
}
273
274
.breadcrumb .current {
275
-
color: #1e293b;
276
font-weight: 500;
277
}
278
···
287
display: flex;
288
align-items: center;
289
gap: 12px;
290
-
border-bottom: 1px solid #f1f5f9;
291
cursor: pointer !important;
292
}
293
···
296
}
297
298
.file-item:hover {
299
-
background: #f8fafc;
300
}
301
302
.file-icon {
303
width: 20px;
304
height: 20px;
305
flex-shrink: 0;
306
-
color: #64748b;
307
cursor: pointer;
308
}
309
310
.file-name {
311
flex: 1;
312
-
color: #1e293b;
313
font-size: 14px;
314
cursor: pointer;
315
}
316
317
.file-size {
318
-
color: #64748b;
319
font-size: 12px;
320
cursor: pointer;
321
}
···
323
.file-content {
324
padding: 0;
325
overflow-x: auto;
326
-
background: #0d1117;
327
-
font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
328
font-size: 13px;
329
}
330
···
347
.line-numbers {
348
display: flex;
349
gap: 0;
350
-
background: #0d1117;
351
}
352
353
.line-numbers .numbers {
354
-
color: #6e7681;
355
text-align: right;
356
user-select: none;
357
min-width: 50px;
358
padding: 20px 16px 20px 20px;
359
-
border-right: 1px solid #30363d;
360
-
background: #0d1117;
361
line-height: 1.5;
362
}
363
···
369
.loading {
370
padding: 40px;
371
text-align: center;
372
-
color: #64748b;
373
}
374
375
.spinner {
376
width: 40px;
377
height: 40px;
378
margin: 0 auto 16px;
379
-
border: 3px solid #e2e8f0;
380
-
border-top-color: #3b82f6;
381
border-radius: 50%;
382
animation: spin 0.8s linear infinite;
383
}
···
397
width: 64px;
398
height: 64px;
399
margin: 0 auto 20px;
400
-
color: #cbd5e1;
401
display: block;
402
}
403
404
.empty-state h3 {
405
font-size: 18px;
406
-
color: #475569;
407
margin-bottom: 8px;
408
}
409
410
.empty-state p {
411
-
color: #64748b;
412
font-size: 14px;
413
}
414
415
.error-message {
416
padding: 40px;
417
text-align: center;
418
-
color: #991b1b;
419
-
background: #fee2e2;
420
margin: 20px;
421
border-radius: 8px;
422
-
border: 1px solid #fecaca;
423
}
424
425
.file-header {
426
padding: 16px 20px;
427
-
border-bottom: 1px solid #e2e8f0;
428
-
background: #f8fafc;
429
display: flex;
430
justify-content: space-between;
431
align-items: center;
···
433
434
.file-header h3 {
435
font-size: 15px;
436
-
color: #1e293b;
437
font-weight: 600;
438
}
439
···
454
.user-header {
455
font-size: 16px;
456
font-weight: 600;
457
-
color: #0f172a;
458
margin-bottom: 12px;
459
padding: 12px;
460
-
background: #f8fafc;
461
border-radius: 6px;
462
}
463
464
.repo-item {
465
padding: 12px;
466
margin-bottom: 8px;
467
-
background: white;
468
-
border: 1px solid #e2e8f0;
469
border-radius: 6px;
470
cursor: pointer;
471
transition: all 0.15s;
472
}
473
474
.repo-item:hover {
475
-
border-color: #3b82f6;
476
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.1);
477
}
478
479
.repo-item strong {
480
display: block;
481
font-size: 14px;
482
-
color: #1e293b;
483
margin-bottom: 4px;
484
}
485
486
.repo-item small {
487
font-size: 12px;
488
-
color: #64748b;
489
-
font-family: monospace;
490
}
491
492
.markdown-content {
493
padding: 20px 40px;
494
-
background: white;
495
-
font-family:
496
-
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
497
-
Cantarell, sans-serif;
498
font-size: 15px;
499
line-height: 1.6;
500
-
color: #1e293b;
501
}
502
503
.markdown-content h1,
···
510
margin-bottom: 16px;
511
font-weight: 600;
512
line-height: 1.25;
513
-
color: #0f172a;
514
}
515
516
.markdown-content h1 {
517
font-size: 2em;
518
padding-bottom: 0.3em;
519
-
border-bottom: 1px solid #e2e8f0;
520
}
521
522
.markdown-content h2 {
523
font-size: 1.5em;
524
padding-bottom: 0.3em;
525
-
border-bottom: 1px solid #e2e8f0;
526
}
527
528
.markdown-content h3 {
···
539
540
.markdown-content h6 {
541
font-size: 0.85em;
542
-
color: #64748b;
543
}
544
545
.markdown-content p {
···
562
padding: 0.2em 0.4em;
563
margin: 0;
564
font-size: 85%;
565
-
background: #f1f5f9;
566
border-radius: 6px;
567
-
font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
568
-
color: #e11d48;
569
}
570
571
.markdown-content pre {
···
573
overflow: auto;
574
font-size: 85%;
575
line-height: 1.45;
576
-
background: #0d1117;
577
border-radius: 6px;
578
margin-bottom: 16px;
579
}
···
587
word-wrap: normal;
588
background: transparent;
589
border: 0;
590
-
color: #c9d1d9;
591
}
592
593
.markdown-content pre code.hljs {
···
596
597
.markdown-content blockquote {
598
padding: 0 1em;
599
-
color: #64748b;
600
-
border-left: 0.25em solid #cbd5e1;
601
margin: 0 0 16px 0;
602
}
603
···
620
.markdown-content table th,
621
.markdown-content table td {
622
padding: 6px 13px;
623
-
border: 1px solid #e2e8f0;
624
}
625
626
.markdown-content table th {
627
font-weight: 600;
628
-
background: #f8fafc;
629
}
630
631
.markdown-content table tr {
632
-
background: white;
633
-
border-top: 1px solid #e2e8f0;
634
}
635
636
.markdown-content table tr:nth-child(2n) {
637
-
background: #f8fafc;
638
}
639
640
.markdown-content img {
···
644
}
645
646
.markdown-content a {
647
-
color: #3b82f6;
648
text-decoration: none;
649
}
650
···
656
height: 0.25em;
657
padding: 0;
658
margin: 24px 0;
659
-
background-color: #e2e8f0;
660
border: 0;
661
}
662
···
665
flex-direction: column;
666
}
667
668
.connection-panel {
669
flex-direction: column;
670
}
···
675
676
.markdown-content {
677
padding: 20px;
678
}
679
}
···
4
box-sizing: border-box;
5
}
6
7
+
:root {
8
+
/* Light theme colors */
9
+
--bg-primary: #f9fafb;
10
+
--bg-secondary: #ffffff;
11
+
--bg-tertiary: #f3f4f6;
12
+
--bg-hover: #f3f4f6;
13
+
--bg-active: #dbeafe;
14
+
15
+
--text-primary: #111827;
16
+
--text-secondary: #4b5563;
17
+
--text-tertiary: #6b7280;
18
+
--text-heading: #111827;
19
+
20
+
--border-primary: #e5e7eb;
21
+
--border-secondary: #d1d5db;
22
+
--border-light: #f3f4f6;
23
+
24
+
--accent-primary: #3b82f6;
25
+
--accent-hover: #2563eb;
26
+
--accent-light: #dbeafe;
27
+
28
+
--success-bg: #dcfce7;
29
+
--success-text: #166534;
30
+
--success-border: #bbf7d0;
31
+
32
+
--error-bg: #fee2e2;
33
+
--error-text: #991b1b;
34
+
--error-border: #fecaca;
35
+
36
+
--code-bg: #f3f4f6;
37
+
--code-text: #111827;
38
+
--code-block-bg: #1f2937;
39
+
--code-block-text: #e5e7eb;
40
+
--code-block-border: #374151;
41
+
--code-block-line-numbers: #9ca3af;
42
+
43
+
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
44
+
--shadow-md:
45
+
0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
46
+
}
47
+
48
+
body.dark-theme {
49
+
/* Dark theme colors */
50
+
--bg-primary: #111827;
51
+
--bg-secondary: #1f2937;
52
+
--bg-tertiary: #374151;
53
+
--bg-hover: #374151;
54
+
--bg-active: #1e3a8a;
55
+
56
+
--text-primary: #f3f4f6;
57
+
--text-secondary: #d1d5db;
58
+
--text-tertiary: #9ca3af;
59
+
--text-heading: #ffffff;
60
+
61
+
--border-primary: #374151;
62
+
--border-secondary: #4b5563;
63
+
--border-light: #374151;
64
+
65
+
--accent-primary: #3b82f6;
66
+
--accent-hover: #60a5fa;
67
+
--accent-light: #1e3a8a;
68
+
69
+
--success-bg: #14532d;
70
+
--success-text: #86efac;
71
+
--success-border: #166534;
72
+
73
+
--error-bg: #7f1d1d;
74
+
--error-text: #fecaca;
75
+
--error-border: #991b1b;
76
+
77
+
--code-bg: #374151;
78
+
--code-text: #d1d5db;
79
+
--code-block-bg: #1f2937;
80
+
--code-block-text: #e5e7eb;
81
+
--code-block-border: #4b5563;
82
+
--code-block-line-numbers: #9ca3af;
83
+
84
+
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.3);
85
+
--shadow-md:
86
+
0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.4);
87
+
}
88
+
89
body {
90
+
font-family: InterVariable, system-ui, sans-serif, ui-sans-serif;
91
+
background: var(--bg-primary);
92
+
color: var(--text-primary);
93
font-size: 14px;
94
line-height: 1.5;
95
+
transition:
96
+
background-color 0.3s ease,
97
+
color 0.3s ease;
98
}
99
100
.container {
···
104
}
105
106
header {
107
+
background: var(--bg-secondary);
108
padding: 24px;
109
border-radius: 8px;
110
+
box-shadow: var(--shadow-md);
111
+
margin-bottom: 20px;
112
+
border: 1px solid var(--border-primary);
113
+
}
114
+
115
+
.header-top {
116
+
display: flex;
117
+
justify-content: space-between;
118
+
align-items: center;
119
margin-bottom: 20px;
120
}
121
122
h1 {
123
font-size: 24px;
124
+
margin: 0;
125
+
color: var(--text-heading);
126
font-weight: 600;
127
}
128
129
+
.theme-toggle {
130
+
background: var(--bg-tertiary);
131
+
border: 1px solid var(--border-primary);
132
+
color: var(--text-primary);
133
+
padding: 8px 16px;
134
+
border-radius: 6px;
135
+
cursor: pointer;
136
+
font-size: 14px;
137
+
display: flex;
138
+
align-items: center;
139
+
gap: 8px;
140
+
transition: all 0.15s;
141
+
}
142
+
143
+
.theme-toggle:hover {
144
+
background: var(--bg-hover);
145
+
border-color: var(--border-secondary);
146
+
}
147
+
148
.connection-panel {
149
display: flex;
150
gap: 12px;
···
156
flex: 1;
157
min-width: 300px;
158
padding: 10px 14px;
159
+
border: 1px solid var(--border-secondary);
160
border-radius: 6px;
161
font-size: 14px;
162
+
background: var(--bg-secondary);
163
+
color: var(--text-primary);
164
transition: all 0.15s;
165
}
166
167
.connection-panel input:focus {
168
outline: none;
169
+
border-color: var(--accent-primary);
170
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
171
}
172
173
button {
174
padding: 10px 20px;
175
+
background: var(--accent-primary);
176
color: white;
177
border: none;
178
border-radius: 6px;
···
180
font-size: 14px;
181
font-weight: 500;
182
transition: all 0.15s;
183
+
box-shadow: var(--shadow-sm);
184
}
185
186
button:hover {
187
+
background: var(--accent-hover);
188
}
189
190
button:active {
···
192
}
193
194
button:disabled {
195
+
background: var(--text-tertiary);
196
cursor: not-allowed;
197
transform: none;
198
}
199
200
button.secondary {
201
+
background: var(--bg-secondary);
202
+
color: var(--text-secondary);
203
+
border: 1px solid var(--border-secondary);
204
}
205
206
button.secondary:hover {
207
+
background: var(--bg-hover);
208
}
209
210
.status {
···
216
}
217
218
.status.success {
219
+
background: var(--success-bg);
220
+
color: var(--success-text);
221
+
border-color: var(--success-border);
222
}
223
224
.status.error {
225
+
background: var(--error-bg);
226
+
color: var(--error-text);
227
+
border-color: var(--error-border);
228
}
229
230
.main-content {
···
235
236
.sidebar {
237
width: 300px;
238
+
background: var(--bg-secondary);
239
border-radius: 8px;
240
+
border: 1px solid var(--border-primary);
241
+
box-shadow: var(--shadow-md);
242
flex-shrink: 0;
243
}
244
···
246
font-size: 16px;
247
font-weight: 600;
248
padding: 16px 20px;
249
+
border-bottom: 1px solid var(--border-primary);
250
+
color: var(--text-heading);
251
display: flex;
252
align-items: center;
253
justify-content: space-between;
···
255
256
.repo-info {
257
padding: 20px;
258
}
259
260
.repo-info h3 {
261
font-size: 14px;
262
font-weight: 600;
263
margin-bottom: 12px;
264
+
color: var(--text-tertiary);
265
}
266
267
.repo-info .label {
268
font-size: 12px;
269
+
color: var(--text-tertiary);
270
margin-bottom: 4px;
271
font-weight: 500;
272
text-transform: uppercase;
···
275
276
.repo-info .value {
277
font-size: 13px;
278
+
color: var(--text-primary);
279
margin-bottom: 12px;
280
+
font-family: IBMPlexMono, ui-monospace, monospace;
281
}
282
283
.clone-url {
284
display: flex;
285
align-items: center;
286
gap: 8px;
287
+
background: var(--bg-tertiary);
288
padding: 8px 12px;
289
border-radius: 6px;
290
+
border: 1px solid var(--border-primary);
291
margin-bottom: 12px;
292
}
293
294
.clone-url code {
295
flex: 1;
296
font-size: 12px;
297
+
color: var(--text-secondary);
298
overflow: hidden;
299
text-overflow: ellipsis;
300
}
···
306
}
307
308
.copy-btn:hover {
309
+
background: var(--accent-hover);
310
}
311
312
.branches-section {
313
+
border-top: 1px solid var(--border-primary);
314
}
315
316
.branch-list {
···
322
padding: 10px 20px;
323
cursor: pointer;
324
transition: background 0.15s;
325
+
border-bottom: 1px solid var(--border-light);
326
font-size: 13px;
327
+
color: var(--text-secondary);
328
display: flex;
329
align-items: center;
330
gap: 8px;
331
}
332
333
.branch-item:hover {
334
+
background: var(--bg-hover);
335
}
336
337
.branch-item.active {
338
+
background: var(--accent-light);
339
+
color: var(--accent-primary);
340
font-weight: 500;
341
}
342
343
.viewer {
344
flex: 1;
345
+
background: var(--bg-secondary);
346
border-radius: 8px;
347
+
border: 1px solid var(--border-primary);
348
+
box-shadow: var(--shadow-md);
349
overflow: hidden;
350
}
351
352
.breadcrumb {
353
padding: 16px 20px;
354
+
border-bottom: 1px solid var(--border-primary);
355
font-size: 13px;
356
+
color: var(--text-tertiary);
357
+
background: var(--bg-tertiary);
358
display: flex;
359
align-items: center;
360
flex-wrap: wrap;
361
}
362
363
.breadcrumb a {
364
+
color: var(--accent-primary);
365
text-decoration: none;
366
transition: color 0.15s;
367
cursor: pointer;
368
}
369
370
.breadcrumb a:hover {
371
+
color: var(--accent-hover);
372
text-decoration: underline;
373
}
374
···
377
}
378
379
.breadcrumb .current {
380
+
color: var(--text-primary);
381
font-weight: 500;
382
}
383
···
392
display: flex;
393
align-items: center;
394
gap: 12px;
395
+
border-bottom: 1px solid var(--border-light);
396
cursor: pointer !important;
397
}
398
···
401
}
402
403
.file-item:hover {
404
+
background: var(--bg-hover);
405
}
406
407
.file-icon {
408
width: 20px;
409
height: 20px;
410
flex-shrink: 0;
411
+
color: var(--text-tertiary);
412
cursor: pointer;
413
}
414
415
.file-name {
416
flex: 1;
417
+
color: var(--text-primary);
418
font-size: 14px;
419
cursor: pointer;
420
}
421
422
.file-size {
423
+
color: var(--text-tertiary);
424
font-size: 12px;
425
cursor: pointer;
426
}
···
428
.file-content {
429
padding: 0;
430
overflow-x: auto;
431
+
background: var(--code-block-bg);
432
+
font-family: IBMPlexMono, Monaco, Menlo, monospace;
433
font-size: 13px;
434
}
435
···
452
.line-numbers {
453
display: flex;
454
gap: 0;
455
+
background: var(--code-block-bg);
456
}
457
458
.line-numbers .numbers {
459
+
color: var(--code-block-line-numbers);
460
text-align: right;
461
user-select: none;
462
min-width: 50px;
463
padding: 20px 16px 20px 20px;
464
+
border-right: 1px solid var(--code-block-border);
465
+
background: var(--code-block-bg);
466
line-height: 1.5;
467
}
468
···
474
.loading {
475
padding: 40px;
476
text-align: center;
477
+
color: var(--text-tertiary);
478
}
479
480
.spinner {
481
width: 40px;
482
height: 40px;
483
margin: 0 auto 16px;
484
+
border: 3px solid var(--border-primary);
485
+
border-top-color: var(--accent-primary);
486
border-radius: 50%;
487
animation: spin 0.8s linear infinite;
488
}
···
502
width: 64px;
503
height: 64px;
504
margin: 0 auto 20px;
505
+
color: var(--text-tertiary);
506
display: block;
507
}
508
509
.empty-state h3 {
510
font-size: 18px;
511
+
color: var(--text-secondary);
512
margin-bottom: 8px;
513
}
514
515
.empty-state p {
516
+
color: var(--text-tertiary);
517
font-size: 14px;
518
}
519
520
+
.welcome-hero {
521
+
padding: 80px 40px;
522
+
text-align: center;
523
+
max-width: 700px;
524
+
margin: 0 auto;
525
+
}
526
+
527
+
.welcome-hero svg {
528
+
width: 96px;
529
+
height: 96px;
530
+
margin: 0 auto 32px;
531
+
color: var(--accent-primary);
532
+
display: block;
533
+
}
534
+
535
+
.welcome-hero h2 {
536
+
font-size: 32px;
537
+
color: var(--text-heading);
538
+
margin-bottom: 16px;
539
+
font-weight: 700;
540
+
}
541
+
542
+
.welcome-hero .subtitle {
543
+
font-size: 18px;
544
+
color: var(--text-secondary);
545
+
margin-bottom: 48px;
546
+
line-height: 1.6;
547
+
}
548
+
549
+
.feature-list {
550
+
display: grid;
551
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
552
+
gap: 24px;
553
+
margin-top: 48px;
554
+
text-align: left;
555
+
}
556
+
557
+
.feature-item {
558
+
padding: 20px;
559
+
background: var(--bg-tertiary);
560
+
border-radius: 8px;
561
+
border: 1px solid var(--border-primary);
562
+
}
563
+
564
+
.feature-item h4 {
565
+
font-size: 16px;
566
+
color: var(--text-heading);
567
+
margin-bottom: 8px;
568
+
display: flex;
569
+
align-items: center;
570
+
gap: 8px;
571
+
}
572
+
573
+
.feature-item p {
574
+
font-size: 14px;
575
+
color: var(--text-tertiary);
576
+
line-height: 1.5;
577
+
margin: 0;
578
+
}
579
+
580
+
.getting-started {
581
+
margin-top: 48px;
582
+
padding: 24px;
583
+
background: var(--bg-tertiary);
584
+
border-radius: 8px;
585
+
border: 1px solid var(--border-primary);
586
+
text-align: left;
587
+
}
588
+
589
+
.getting-started h3 {
590
+
font-size: 18px;
591
+
color: var(--text-heading);
592
+
margin-bottom: 16px;
593
+
}
594
+
595
+
.getting-started ol {
596
+
margin-left: 20px;
597
+
color: var(--text-secondary);
598
+
}
599
+
600
+
.getting-started li {
601
+
margin-bottom: 8px;
602
+
line-height: 1.6;
603
+
}
604
+
605
.error-message {
606
padding: 40px;
607
text-align: center;
608
+
color: var(--error-text);
609
+
background: var(--error-bg);
610
margin: 20px;
611
border-radius: 8px;
612
+
border: 1px solid var(--error-border);
613
}
614
615
.file-header {
616
padding: 16px 20px;
617
+
border-bottom: 1px solid var(--border-primary);
618
+
background: var(--bg-tertiary);
619
display: flex;
620
justify-content: space-between;
621
align-items: center;
···
623
624
.file-header h3 {
625
font-size: 15px;
626
+
color: var(--text-primary);
627
font-weight: 600;
628
}
629
···
644
.user-header {
645
font-size: 16px;
646
font-weight: 600;
647
+
color: var(--text-heading);
648
margin-bottom: 12px;
649
padding: 12px;
650
+
background: var(--bg-tertiary);
651
border-radius: 6px;
652
}
653
654
.repo-item {
655
padding: 12px;
656
margin-bottom: 8px;
657
+
background: var(--bg-secondary);
658
+
border: 1px solid var(--border-primary);
659
border-radius: 6px;
660
cursor: pointer;
661
transition: all 0.15s;
662
}
663
664
.repo-item:hover {
665
+
border-color: var(--accent-primary);
666
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.1);
667
}
668
669
.repo-item strong {
670
display: block;
671
font-size: 14px;
672
+
color: var(--text-primary);
673
margin-bottom: 4px;
674
}
675
676
.repo-item small {
677
font-size: 12px;
678
+
color: var(--text-tertiary);
679
+
font-family: IBMPlexMono, monospace;
680
}
681
682
.markdown-content {
683
padding: 20px 40px;
684
+
background: var(--bg-secondary);
685
+
font-family: InterVariable, system-ui, sans-serif;
686
font-size: 15px;
687
line-height: 1.6;
688
+
color: var(--text-primary);
689
}
690
691
.markdown-content h1,
···
698
margin-bottom: 16px;
699
font-weight: 600;
700
line-height: 1.25;
701
+
color: var(--text-heading);
702
}
703
704
.markdown-content h1 {
705
font-size: 2em;
706
padding-bottom: 0.3em;
707
+
border-bottom: 1px solid var(--border-primary);
708
}
709
710
.markdown-content h2 {
711
font-size: 1.5em;
712
padding-bottom: 0.3em;
713
+
border-bottom: 1px solid var(--border-primary);
714
}
715
716
.markdown-content h3 {
···
727
728
.markdown-content h6 {
729
font-size: 0.85em;
730
+
color: var(--text-tertiary);
731
}
732
733
.markdown-content p {
···
750
padding: 0.2em 0.4em;
751
margin: 0;
752
font-size: 85%;
753
+
background: var(--code-bg);
754
border-radius: 6px;
755
+
font-family: IBMPlexMono, Monaco, Menlo, monospace;
756
+
color: var(--code-text);
757
}
758
759
.markdown-content pre {
···
761
overflow: auto;
762
font-size: 85%;
763
line-height: 1.45;
764
+
background: var(--code-block-bg);
765
border-radius: 6px;
766
margin-bottom: 16px;
767
}
···
775
word-wrap: normal;
776
background: transparent;
777
border: 0;
778
+
color: var(--code-block-text);
779
}
780
781
.markdown-content pre code.hljs {
···
784
785
.markdown-content blockquote {
786
padding: 0 1em;
787
+
color: var(--text-tertiary);
788
+
border-left: 0.25em solid var(--border-secondary);
789
margin: 0 0 16px 0;
790
}
791
···
808
.markdown-content table th,
809
.markdown-content table td {
810
padding: 6px 13px;
811
+
border: 1px solid var(--border-primary);
812
}
813
814
.markdown-content table th {
815
font-weight: 600;
816
+
background: var(--bg-tertiary);
817
}
818
819
.markdown-content table tr {
820
+
background: var(--bg-secondary);
821
+
border-top: 1px solid var(--border-primary);
822
}
823
824
.markdown-content table tr:nth-child(2n) {
825
+
background: var(--bg-tertiary);
826
}
827
828
.markdown-content img {
···
832
}
833
834
.markdown-content a {
835
+
color: var(--accent-primary);
836
text-decoration: none;
837
}
838
···
844
height: 0.25em;
845
padding: 0;
846
margin: 24px 0;
847
+
background-color: var(--border-primary);
848
border: 0;
849
}
850
···
853
flex-direction: column;
854
}
855
856
+
.sidebar {
857
+
width: 100%;
858
+
}
859
+
860
.connection-panel {
861
flex-direction: column;
862
}
···
867
868
.markdown-content {
869
padding: 20px;
870
+
}
871
+
872
+
.welcome-hero {
873
+
padding: 40px 20px;
874
}
875
}