Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

layout.tsx

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

layout.tsxBlame619 lines · 1 contributor
fc1817aClaude1import type { FC, PropsWithChildren } from "hono/jsx";
06d5ffeClaude2import type { User } from "../db/schema";
3import { hljsThemeCss } from "../lib/highlight";
fc1817aClaude4
06d5ffeClaude5export const Layout: FC<
6 PropsWithChildren<{ title?: string; user?: User | null }>
7> = ({ children, title, user }) => {
fc1817aClaude8 return (
9 <html lang="en">
10 <head>
11 <meta charset="UTF-8" />
12 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
13 <title>{title ? `${title} — gluecron` : "gluecron"}</title>
14 <style>{css}</style>
06d5ffeClaude15 <style>{hljsThemeCss}</style>
fc1817aClaude16 </head>
17 <body>
18 <header>
19 <nav>
20 <a href="/" class="logo">
21 gluecron
22 </a>
06d5ffeClaude23 <div class="nav-right">
c81ab7aClaude24 <a href="/explore" class="nav-link">
25 Explore
26 </a>
06d5ffeClaude27 {user ? (
28 <>
f1ab587Claude29 <a href="/dashboard" class="nav-link" style="font-weight: 600">
30 Dashboard
31 </a>
06d5ffeClaude32 <a href="/new" class="btn btn-sm btn-primary">
33 + New
34 </a>
35 <a href={`/${user.username}`} class="nav-user">
36 {user.displayName || user.username}
37 </a>
38 <a href="/settings" class="nav-link">
39 Settings
40 </a>
41 <a href="/logout" class="nav-link">
42 Sign out
43 </a>
44 </>
45 ) : (
46 <>
47 <a href="/login" class="nav-link">
48 Sign in
49 </a>
50 <a href="/register" class="btn btn-sm btn-primary">
51 Register
52 </a>
53 </>
54 )}
55 </div>
fc1817aClaude56 </nav>
57 </header>
58 <main>{children}</main>
59 <footer>
36b4cbdClaude60 <span>
61 &copy; {new Date().getFullYear()} gluecron — AI-native code intelligence
62 </span>
63 <div style="margin-top: 6px; display: flex; gap: 16px; justify-content: center; font-size: 12px">
64 <a href="/terms" style="color: var(--text-muted)">Terms</a>
65 <a href="/privacy" style="color: var(--text-muted)">Privacy</a>
66 <a href="/acceptable-use" style="color: var(--text-muted)">Acceptable Use</a>
67 </div>
fc1817aClaude68 </footer>
69 </body>
70 </html>
71 );
72};
73
74const css = `
75 :root {
76 --bg: #0d1117;
77 --bg-secondary: #161b22;
78 --bg-tertiary: #21262d;
79 --border: #30363d;
80 --text: #e6edf3;
81 --text-muted: #8b949e;
82 --text-link: #58a6ff;
83 --accent: #1f6feb;
84 --accent-hover: #388bfd;
85 --green: #3fb950;
86 --red: #f85149;
87 --yellow: #d29922;
88 --font-mono: 'SF Mono', 'Cascadia Code', 'Fira Code', monospace;
89 --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
90 --radius: 6px;
91 }
92
93 * { margin: 0; padding: 0; box-sizing: border-box; }
94
95 body {
96 font-family: var(--font-sans);
97 background: var(--bg);
98 color: var(--text);
99 line-height: 1.5;
100 min-height: 100vh;
101 display: flex;
102 flex-direction: column;
103 }
104
105 a { color: var(--text-link); text-decoration: none; }
106 a:hover { text-decoration: underline; }
107
108 header {
109 border-bottom: 1px solid var(--border);
110 padding: 12px 24px;
111 background: var(--bg-secondary);
112 }
113
06d5ffeClaude114 header nav {
115 display: flex;
116 align-items: center;
117 justify-content: space-between;
118 max-width: 1200px;
119 margin: 0 auto;
120 }
fc1817aClaude121 .logo { font-size: 20px; font-weight: 700; color: var(--text); }
122 .logo:hover { text-decoration: none; color: var(--text-link); }
123
06d5ffeClaude124 .nav-right { display: flex; align-items: center; gap: 16px; }
125 .nav-link { color: var(--text-muted); font-size: 14px; }
126 .nav-link:hover { color: var(--text); text-decoration: none; }
127 .nav-user { color: var(--text); font-weight: 600; font-size: 14px; }
128 .nav-user:hover { color: var(--text-link); text-decoration: none; }
129
fc1817aClaude130 main { max-width: 1200px; margin: 0 auto; padding: 24px; flex: 1; width: 100%; }
131
132 footer {
133 border-top: 1px solid var(--border);
134 padding: 16px 24px;
135 text-align: center;
136 color: var(--text-muted);
137 font-size: 13px;
138 }
139
06d5ffeClaude140 /* Buttons */
141 .btn {
142 display: inline-flex;
143 align-items: center;
144 gap: 6px;
145 padding: 8px 16px;
146 border-radius: var(--radius);
147 font-size: 14px;
148 font-weight: 500;
149 border: 1px solid var(--border);
150 background: var(--bg-tertiary);
151 color: var(--text);
152 cursor: pointer;
153 text-decoration: none;
154 line-height: 1.4;
155 }
156 .btn:hover { background: var(--border); text-decoration: none; }
157 .btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
158 .btn-primary:hover { background: var(--accent-hover); }
159 .btn-danger { background: transparent; border-color: var(--red); color: var(--red); }
160 .btn-danger:hover { background: rgba(248, 81, 73, 0.15); }
161 .btn-sm { padding: 4px 12px; font-size: 13px; }
162
163 /* Forms */
164 .form-group { margin-bottom: 16px; }
165 .form-group label { display: block; font-size: 14px; font-weight: 500; margin-bottom: 6px; color: var(--text); }
166 .form-group input, .form-group textarea, .form-group select {
167 width: 100%;
168 padding: 8px 12px;
169 background: var(--bg);
170 border: 1px solid var(--border);
171 border-radius: var(--radius);
172 color: var(--text);
173 font-size: 14px;
174 font-family: var(--font-sans);
175 }
176 .form-group input:focus, .form-group textarea:focus, .form-group select:focus {
177 outline: none;
178 border-color: var(--accent);
179 box-shadow: 0 0 0 2px rgba(31, 111, 235, 0.3);
180 }
181 .input-disabled { opacity: 0.5; cursor: not-allowed; }
182
183 /* Auth */
184 .auth-container { max-width: 400px; margin: 40px auto; }
185 .auth-container h2 { margin-bottom: 20px; font-size: 24px; }
186 .auth-error {
187 background: rgba(248, 81, 73, 0.1);
188 border: 1px solid var(--red);
189 color: var(--red);
190 padding: 8px 12px;
191 border-radius: var(--radius);
192 margin-bottom: 16px;
193 font-size: 14px;
194 }
195 .auth-success {
196 background: rgba(63, 185, 80, 0.1);
197 border: 1px solid var(--green);
198 color: var(--green);
199 padding: 8px 12px;
200 border-radius: var(--radius);
201 margin-bottom: 16px;
202 font-size: 14px;
203 }
204 .auth-switch { margin-top: 16px; font-size: 14px; color: var(--text-muted); }
205
206 /* Settings */
207 .settings-container { max-width: 600px; }
208 .settings-container h2 { margin-bottom: 20px; font-size: 24px; }
209 .settings-container h3 { font-size: 18px; margin-bottom: 12px; }
210 .ssh-keys-list { margin-bottom: 24px; }
211 .ssh-key-item {
212 display: flex;
213 justify-content: space-between;
214 align-items: center;
215 padding: 12px 16px;
216 border: 1px solid var(--border);
217 border-radius: var(--radius);
218 margin-bottom: 8px;
219 background: var(--bg-secondary);
220 }
221 .ssh-key-meta { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
222 .ssh-key-meta code { font-size: 11px; background: var(--bg-tertiary); padding: 1px 6px; border-radius: 3px; margin-right: 8px; }
223
224 /* Repo header */
fc1817aClaude225 .repo-header {
226 display: flex;
227 align-items: center;
228 gap: 8px;
229 margin-bottom: 16px;
230 font-size: 20px;
231 }
232 .repo-header .owner { color: var(--text-link); }
233 .repo-header .separator { color: var(--text-muted); }
234 .repo-header .name { color: var(--text-link); font-weight: 600; }
06d5ffeClaude235 .repo-header-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }
fc1817aClaude236
237 .repo-nav {
238 display: flex;
239 gap: 0;
240 border-bottom: 1px solid var(--border);
241 margin-bottom: 20px;
242 }
243 .repo-nav a {
244 padding: 8px 16px;
245 color: var(--text-muted);
246 border-bottom: 2px solid transparent;
247 font-size: 14px;
248 }
249 .repo-nav a:hover { text-decoration: none; color: var(--text); }
250 .repo-nav a.active { color: var(--text); border-bottom-color: var(--accent); }
251
252 .breadcrumb { display: flex; gap: 4px; align-items: center; margin-bottom: 16px; color: var(--text-muted); font-size: 14px; }
253 .breadcrumb a { color: var(--text-link); }
254
255 .file-table { width: 100%; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
256 .file-table tr { border-bottom: 1px solid var(--border); }
257 .file-table tr:last-child { border-bottom: none; }
258 .file-table td { padding: 8px 16px; font-size: 14px; }
259 .file-table tr:hover { background: var(--bg-secondary); }
260 .file-icon { width: 20px; color: var(--text-muted); }
261 .file-name a { color: var(--text); }
262 .file-name a:hover { color: var(--text-link); text-decoration: underline; }
263
264 .blob-view {
265 border: 1px solid var(--border);
266 border-radius: var(--radius);
267 overflow: hidden;
268 }
269 .blob-header {
270 background: var(--bg-secondary);
271 padding: 8px 16px;
272 border-bottom: 1px solid var(--border);
273 font-size: 13px;
274 color: var(--text-muted);
06d5ffeClaude275 display: flex;
276 justify-content: space-between;
277 align-items: center;
fc1817aClaude278 }
279 .blob-code {
280 overflow-x: auto;
281 font-family: var(--font-mono);
282 font-size: 13px;
283 line-height: 1.6;
284 }
285 .blob-code table { width: 100%; border-collapse: collapse; }
286 .blob-code .line-num {
287 width: 1%;
288 min-width: 50px;
289 padding: 0 12px;
290 text-align: right;
291 color: var(--text-muted);
292 user-select: none;
293 white-space: nowrap;
294 border-right: 1px solid var(--border);
295 }
296 .blob-code .line-content { padding: 0 12px; white-space: pre; }
297
298 .commit-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
299 .commit-item {
300 display: flex;
301 justify-content: space-between;
302 align-items: center;
303 padding: 12px 16px;
304 border-bottom: 1px solid var(--border);
305 }
306 .commit-item:last-child { border-bottom: none; }
307 .commit-item:hover { background: var(--bg-secondary); }
308 .commit-message { font-size: 14px; font-weight: 500; }
309 .commit-meta { font-size: 12px; color: var(--text-muted); }
310 .commit-sha {
311 font-family: var(--font-mono);
312 font-size: 12px;
313 padding: 2px 8px;
314 background: var(--bg-tertiary);
315 border: 1px solid var(--border);
316 border-radius: var(--radius);
317 color: var(--text-link);
318 }
319
320 .diff-view { margin-top: 16px; }
321 .diff-file {
322 border: 1px solid var(--border);
323 border-radius: var(--radius);
324 margin-bottom: 16px;
325 overflow: hidden;
326 }
327 .diff-file-header {
328 background: var(--bg-secondary);
329 padding: 8px 16px;
330 border-bottom: 1px solid var(--border);
331 font-family: var(--font-mono);
332 font-size: 13px;
333 }
334 .diff-content {
335 overflow-x: auto;
336 font-family: var(--font-mono);
337 font-size: 13px;
338 line-height: 1.6;
339 }
340 .diff-content .line-add { background: rgba(63, 185, 80, 0.15); color: var(--green); }
341 .diff-content .line-del { background: rgba(248, 81, 73, 0.1); color: var(--red); }
342 .diff-content .line-hunk { background: rgba(56, 139, 253, 0.1); color: var(--text-link); }
343 .diff-content .line { padding: 0 12px; white-space: pre; display: block; }
344
345 .stat-add { color: var(--green); font-weight: 600; }
346 .stat-del { color: var(--red); font-weight: 600; }
347
348 .empty-state {
349 text-align: center;
350 padding: 60px 20px;
351 color: var(--text-muted);
352 }
353 .empty-state h2 { font-size: 24px; margin-bottom: 8px; color: var(--text); }
354 .empty-state pre {
355 text-align: left;
356 display: inline-block;
357 background: var(--bg-secondary);
358 padding: 16px 24px;
359 border-radius: var(--radius);
360 border: 1px solid var(--border);
361 font-family: var(--font-mono);
362 font-size: 13px;
363 margin-top: 16px;
364 line-height: 1.8;
365 }
366
367 .badge {
368 display: inline-block;
369 padding: 2px 8px;
370 border-radius: 12px;
371 font-size: 12px;
372 font-weight: 500;
373 background: var(--bg-tertiary);
374 border: 1px solid var(--border);
375 color: var(--text-muted);
376 }
377
378 .branch-selector {
379 display: inline-flex;
380 align-items: center;
381 gap: 4px;
382 padding: 4px 12px;
383 background: var(--bg-tertiary);
384 border: 1px solid var(--border);
385 border-radius: var(--radius);
386 font-size: 13px;
387 color: var(--text);
388 margin-bottom: 12px;
06d5ffeClaude389 position: relative;
fc1817aClaude390 }
391
06d5ffeClaude392 .branch-dropdown {
393 position: relative;
394 display: inline-block;
395 margin-bottom: 12px;
396 }
397 .branch-dropdown-content {
398 display: none;
399 position: absolute;
400 top: 100%;
401 left: 0;
402 z-index: 10;
403 min-width: 200px;
404 background: var(--bg-secondary);
405 border: 1px solid var(--border);
406 border-radius: var(--radius);
407 margin-top: 4px;
408 box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
409 }
410 .branch-dropdown:hover .branch-dropdown-content,
411 .branch-dropdown:focus-within .branch-dropdown-content { display: block; }
412 .branch-dropdown-content a {
413 display: block;
414 padding: 8px 12px;
415 font-size: 13px;
416 color: var(--text);
417 border-bottom: 1px solid var(--border);
418 }
419 .branch-dropdown-content a:last-child { border-bottom: none; }
420 .branch-dropdown-content a:hover { background: var(--bg-tertiary); text-decoration: none; }
421 .branch-dropdown-content a.active-branch { color: var(--text-link); font-weight: 600; }
422
fc1817aClaude423 .card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; }
424 .card {
425 border: 1px solid var(--border);
426 border-radius: var(--radius);
427 padding: 16px;
428 background: var(--bg-secondary);
06d5ffeClaude429 transition: border-color 0.15s;
fc1817aClaude430 }
06d5ffeClaude431 .card:hover { border-color: var(--text-muted); }
fc1817aClaude432 .card h3 { font-size: 16px; margin-bottom: 4px; }
06d5ffeClaude433 .card h3 a { color: var(--text-link); }
fc1817aClaude434 .card p { font-size: 13px; color: var(--text-muted); }
06d5ffeClaude435 .card-meta { display: flex; gap: 16px; margin-top: 12px; font-size: 12px; color: var(--text-muted); }
436 .card-meta span { display: flex; align-items: center; gap: 4px; }
437
438 .star-btn {
439 display: inline-flex;
440 align-items: center;
441 gap: 6px;
442 padding: 4px 12px;
443 background: var(--bg-tertiary);
444 border: 1px solid var(--border);
445 border-radius: var(--radius);
446 color: var(--text);
447 font-size: 13px;
448 cursor: pointer;
449 }
450 .star-btn:hover { background: var(--border); text-decoration: none; }
451 .star-btn.starred { color: var(--yellow); border-color: var(--yellow); }
452
453 .user-profile {
454 display: flex;
455 gap: 32px;
456 margin-bottom: 32px;
457 }
458 .user-avatar {
459 width: 96px;
460 height: 96px;
461 border-radius: 50%;
462 background: var(--bg-tertiary);
463 border: 1px solid var(--border);
464 display: flex;
465 align-items: center;
466 justify-content: center;
467 font-size: 40px;
468 color: var(--text-muted);
469 flex-shrink: 0;
470 }
471 .user-info h2 { font-size: 24px; margin-bottom: 2px; }
472 .user-info .username { font-size: 16px; color: var(--text-muted); }
473 .user-info .bio { font-size: 14px; color: var(--text-muted); margin-top: 8px; }
474
475 .new-repo-form { max-width: 600px; }
476 .new-repo-form h2 { margin-bottom: 20px; }
477 .visibility-options { display: flex; gap: 12px; margin-bottom: 16px; }
478 .visibility-option {
479 flex: 1;
480 padding: 12px;
481 border: 1px solid var(--border);
482 border-radius: var(--radius);
483 background: var(--bg-secondary);
484 cursor: pointer;
485 text-align: center;
486 }
487 .visibility-option:has(input:checked) { border-color: var(--accent); background: rgba(31, 111, 235, 0.1); }
488 .visibility-option input { display: none; }
489 .visibility-option .vis-label { font-size: 14px; font-weight: 500; }
490 .visibility-option .vis-desc { font-size: 12px; color: var(--text-muted); }
79136bbClaude491
492 /* Issues */
493 .issue-tabs { display: flex; gap: 16px; }
494 .issue-tabs a { color: var(--text-muted); font-size: 14px; font-weight: 500; }
495 .issue-tabs a:hover { color: var(--text); text-decoration: none; }
496 .issue-tabs a.active { color: var(--text); }
497
498 .issue-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
499 .issue-item {
500 display: flex;
501 gap: 12px;
502 align-items: flex-start;
503 padding: 12px 16px;
504 border-bottom: 1px solid var(--border);
505 }
506 .issue-item:last-child { border-bottom: none; }
507 .issue-item:hover { background: var(--bg-secondary); }
508 .issue-state-icon { font-size: 16px; padding-top: 2px; }
509 .state-open { color: var(--green); }
510 .state-closed { color: #986ee2; }
511 .issue-title { font-size: 15px; font-weight: 600; }
512 .issue-title a { color: var(--text); }
513 .issue-title a:hover { color: var(--text-link); }
514 .issue-meta { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
515
516 .issue-badge {
517 display: inline-flex;
518 align-items: center;
519 gap: 4px;
520 padding: 4px 12px;
521 border-radius: 20px;
522 font-size: 13px;
523 font-weight: 500;
524 }
525 .badge-open { background: rgba(63, 185, 80, 0.15); color: var(--green); border: 1px solid var(--green); }
526 .badge-closed { background: rgba(152, 110, 226, 0.15); color: #986ee2; border: 1px solid #986ee2; }
0074234Claude527 .badge-merged { background: rgba(152, 110, 226, 0.15); color: #986ee2; border: 1px solid #986ee2; }
528 .state-merged { color: #986ee2; }
529 .ai-review { border-color: var(--accent); }
79136bbClaude530
531 .issue-detail { max-width: 900px; }
532 .issue-comment-box {
533 border: 1px solid var(--border);
534 border-radius: var(--radius);
535 margin-bottom: 16px;
536 overflow: hidden;
537 }
538 .comment-header {
539 background: var(--bg-secondary);
540 padding: 8px 16px;
541 border-bottom: 1px solid var(--border);
542 font-size: 13px;
543 color: var(--text-muted);
544 }
545
546 /* Search */
547 .search-results .diff-file { margin-bottom: 12px; }
16b325cClaude548
549 /* Timeline */
550 .timeline { position: relative; padding-left: 24px; }
551 .timeline::before {
552 content: '';
553 position: absolute;
554 left: 4px;
555 top: 8px;
556 bottom: 8px;
557 width: 2px;
558 background: var(--border);
559 }
560 .timeline-item {
561 position: relative;
562 padding-bottom: 16px;
563 }
564 .timeline-dot {
565 position: absolute;
566 left: -24px;
567 top: 6px;
568 width: 10px;
569 height: 10px;
570 border-radius: 50%;
571 background: var(--text-muted);
572 border: 2px solid var(--bg);
573 }
574 .timeline-content {
575 background: var(--bg-secondary);
576 border: 1px solid var(--border);
577 border-radius: var(--radius);
578 padding: 12px 16px;
579 }
f1ab587Claude580
581 /* Toggle switch */
582 .toggle-switch {
583 position: relative;
584 display: inline-block;
585 width: 44px;
586 height: 24px;
587 flex-shrink: 0;
588 margin-left: 16px;
589 }
590 .toggle-switch input { opacity: 0; width: 0; height: 0; }
591 .toggle-slider {
592 position: absolute;
593 cursor: pointer;
594 top: 0; left: 0; right: 0; bottom: 0;
595 background: var(--bg-tertiary);
596 border: 1px solid var(--border);
597 border-radius: 12px;
598 transition: 0.2s;
599 }
600 .toggle-slider::before {
601 content: '';
602 position: absolute;
603 height: 18px;
604 width: 18px;
605 left: 2px;
606 bottom: 2px;
607 background: var(--text-muted);
608 border-radius: 50%;
609 transition: 0.2s;
610 }
611 .toggle-switch input:checked + .toggle-slider {
612 background: var(--accent);
613 border-color: var(--accent);
614 }
615 .toggle-switch input:checked + .toggle-slider::before {
616 transform: translateX(20px);
617 background: #fff;
618 }
fc1817aClaude619`;