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.tsxBlame478 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">
24 {user ? (
25 <>
26 <a href="/new" class="btn btn-sm btn-primary">
27 + New
28 </a>
29 <a href={`/${user.username}`} class="nav-user">
30 {user.displayName || user.username}
31 </a>
32 <a href="/settings" class="nav-link">
33 Settings
34 </a>
35 <a href="/logout" class="nav-link">
36 Sign out
37 </a>
38 </>
39 ) : (
40 <>
41 <a href="/login" class="nav-link">
42 Sign in
43 </a>
44 <a href="/register" class="btn btn-sm btn-primary">
45 Register
46 </a>
47 </>
48 )}
49 </div>
fc1817aClaude50 </nav>
51 </header>
52 <main>{children}</main>
53 <footer>
54 <span>gluecron — AI-native code intelligence</span>
55 </footer>
56 </body>
57 </html>
58 );
59};
60
61const css = `
62 :root {
63 --bg: #0d1117;
64 --bg-secondary: #161b22;
65 --bg-tertiary: #21262d;
66 --border: #30363d;
67 --text: #e6edf3;
68 --text-muted: #8b949e;
69 --text-link: #58a6ff;
70 --accent: #1f6feb;
71 --accent-hover: #388bfd;
72 --green: #3fb950;
73 --red: #f85149;
74 --yellow: #d29922;
75 --font-mono: 'SF Mono', 'Cascadia Code', 'Fira Code', monospace;
76 --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
77 --radius: 6px;
78 }
79
80 * { margin: 0; padding: 0; box-sizing: border-box; }
81
82 body {
83 font-family: var(--font-sans);
84 background: var(--bg);
85 color: var(--text);
86 line-height: 1.5;
87 min-height: 100vh;
88 display: flex;
89 flex-direction: column;
90 }
91
92 a { color: var(--text-link); text-decoration: none; }
93 a:hover { text-decoration: underline; }
94
95 header {
96 border-bottom: 1px solid var(--border);
97 padding: 12px 24px;
98 background: var(--bg-secondary);
99 }
100
06d5ffeClaude101 header nav {
102 display: flex;
103 align-items: center;
104 justify-content: space-between;
105 max-width: 1200px;
106 margin: 0 auto;
107 }
fc1817aClaude108 .logo { font-size: 20px; font-weight: 700; color: var(--text); }
109 .logo:hover { text-decoration: none; color: var(--text-link); }
110
06d5ffeClaude111 .nav-right { display: flex; align-items: center; gap: 16px; }
112 .nav-link { color: var(--text-muted); font-size: 14px; }
113 .nav-link:hover { color: var(--text); text-decoration: none; }
114 .nav-user { color: var(--text); font-weight: 600; font-size: 14px; }
115 .nav-user:hover { color: var(--text-link); text-decoration: none; }
116
fc1817aClaude117 main { max-width: 1200px; margin: 0 auto; padding: 24px; flex: 1; width: 100%; }
118
119 footer {
120 border-top: 1px solid var(--border);
121 padding: 16px 24px;
122 text-align: center;
123 color: var(--text-muted);
124 font-size: 13px;
125 }
126
06d5ffeClaude127 /* Buttons */
128 .btn {
129 display: inline-flex;
130 align-items: center;
131 gap: 6px;
132 padding: 8px 16px;
133 border-radius: var(--radius);
134 font-size: 14px;
135 font-weight: 500;
136 border: 1px solid var(--border);
137 background: var(--bg-tertiary);
138 color: var(--text);
139 cursor: pointer;
140 text-decoration: none;
141 line-height: 1.4;
142 }
143 .btn:hover { background: var(--border); text-decoration: none; }
144 .btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
145 .btn-primary:hover { background: var(--accent-hover); }
146 .btn-danger { background: transparent; border-color: var(--red); color: var(--red); }
147 .btn-danger:hover { background: rgba(248, 81, 73, 0.15); }
148 .btn-sm { padding: 4px 12px; font-size: 13px; }
149
150 /* Forms */
151 .form-group { margin-bottom: 16px; }
152 .form-group label { display: block; font-size: 14px; font-weight: 500; margin-bottom: 6px; color: var(--text); }
153 .form-group input, .form-group textarea, .form-group select {
154 width: 100%;
155 padding: 8px 12px;
156 background: var(--bg);
157 border: 1px solid var(--border);
158 border-radius: var(--radius);
159 color: var(--text);
160 font-size: 14px;
161 font-family: var(--font-sans);
162 }
163 .form-group input:focus, .form-group textarea:focus, .form-group select:focus {
164 outline: none;
165 border-color: var(--accent);
166 box-shadow: 0 0 0 2px rgba(31, 111, 235, 0.3);
167 }
168 .input-disabled { opacity: 0.5; cursor: not-allowed; }
169
170 /* Auth */
171 .auth-container { max-width: 400px; margin: 40px auto; }
172 .auth-container h2 { margin-bottom: 20px; font-size: 24px; }
173 .auth-error {
174 background: rgba(248, 81, 73, 0.1);
175 border: 1px solid var(--red);
176 color: var(--red);
177 padding: 8px 12px;
178 border-radius: var(--radius);
179 margin-bottom: 16px;
180 font-size: 14px;
181 }
182 .auth-success {
183 background: rgba(63, 185, 80, 0.1);
184 border: 1px solid var(--green);
185 color: var(--green);
186 padding: 8px 12px;
187 border-radius: var(--radius);
188 margin-bottom: 16px;
189 font-size: 14px;
190 }
191 .auth-switch { margin-top: 16px; font-size: 14px; color: var(--text-muted); }
192
193 /* Settings */
194 .settings-container { max-width: 600px; }
195 .settings-container h2 { margin-bottom: 20px; font-size: 24px; }
196 .settings-container h3 { font-size: 18px; margin-bottom: 12px; }
197 .ssh-keys-list { margin-bottom: 24px; }
198 .ssh-key-item {
199 display: flex;
200 justify-content: space-between;
201 align-items: center;
202 padding: 12px 16px;
203 border: 1px solid var(--border);
204 border-radius: var(--radius);
205 margin-bottom: 8px;
206 background: var(--bg-secondary);
207 }
208 .ssh-key-meta { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
209 .ssh-key-meta code { font-size: 11px; background: var(--bg-tertiary); padding: 1px 6px; border-radius: 3px; margin-right: 8px; }
210
211 /* Repo header */
fc1817aClaude212 .repo-header {
213 display: flex;
214 align-items: center;
215 gap: 8px;
216 margin-bottom: 16px;
217 font-size: 20px;
218 }
219 .repo-header .owner { color: var(--text-link); }
220 .repo-header .separator { color: var(--text-muted); }
221 .repo-header .name { color: var(--text-link); font-weight: 600; }
06d5ffeClaude222 .repo-header-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }
fc1817aClaude223
224 .repo-nav {
225 display: flex;
226 gap: 0;
227 border-bottom: 1px solid var(--border);
228 margin-bottom: 20px;
229 }
230 .repo-nav a {
231 padding: 8px 16px;
232 color: var(--text-muted);
233 border-bottom: 2px solid transparent;
234 font-size: 14px;
235 }
236 .repo-nav a:hover { text-decoration: none; color: var(--text); }
237 .repo-nav a.active { color: var(--text); border-bottom-color: var(--accent); }
238
239 .breadcrumb { display: flex; gap: 4px; align-items: center; margin-bottom: 16px; color: var(--text-muted); font-size: 14px; }
240 .breadcrumb a { color: var(--text-link); }
241
242 .file-table { width: 100%; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
243 .file-table tr { border-bottom: 1px solid var(--border); }
244 .file-table tr:last-child { border-bottom: none; }
245 .file-table td { padding: 8px 16px; font-size: 14px; }
246 .file-table tr:hover { background: var(--bg-secondary); }
247 .file-icon { width: 20px; color: var(--text-muted); }
248 .file-name a { color: var(--text); }
249 .file-name a:hover { color: var(--text-link); text-decoration: underline; }
250
251 .blob-view {
252 border: 1px solid var(--border);
253 border-radius: var(--radius);
254 overflow: hidden;
255 }
256 .blob-header {
257 background: var(--bg-secondary);
258 padding: 8px 16px;
259 border-bottom: 1px solid var(--border);
260 font-size: 13px;
261 color: var(--text-muted);
06d5ffeClaude262 display: flex;
263 justify-content: space-between;
264 align-items: center;
fc1817aClaude265 }
266 .blob-code {
267 overflow-x: auto;
268 font-family: var(--font-mono);
269 font-size: 13px;
270 line-height: 1.6;
271 }
272 .blob-code table { width: 100%; border-collapse: collapse; }
273 .blob-code .line-num {
274 width: 1%;
275 min-width: 50px;
276 padding: 0 12px;
277 text-align: right;
278 color: var(--text-muted);
279 user-select: none;
280 white-space: nowrap;
281 border-right: 1px solid var(--border);
282 }
283 .blob-code .line-content { padding: 0 12px; white-space: pre; }
284
285 .commit-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
286 .commit-item {
287 display: flex;
288 justify-content: space-between;
289 align-items: center;
290 padding: 12px 16px;
291 border-bottom: 1px solid var(--border);
292 }
293 .commit-item:last-child { border-bottom: none; }
294 .commit-item:hover { background: var(--bg-secondary); }
295 .commit-message { font-size: 14px; font-weight: 500; }
296 .commit-meta { font-size: 12px; color: var(--text-muted); }
297 .commit-sha {
298 font-family: var(--font-mono);
299 font-size: 12px;
300 padding: 2px 8px;
301 background: var(--bg-tertiary);
302 border: 1px solid var(--border);
303 border-radius: var(--radius);
304 color: var(--text-link);
305 }
306
307 .diff-view { margin-top: 16px; }
308 .diff-file {
309 border: 1px solid var(--border);
310 border-radius: var(--radius);
311 margin-bottom: 16px;
312 overflow: hidden;
313 }
314 .diff-file-header {
315 background: var(--bg-secondary);
316 padding: 8px 16px;
317 border-bottom: 1px solid var(--border);
318 font-family: var(--font-mono);
319 font-size: 13px;
320 }
321 .diff-content {
322 overflow-x: auto;
323 font-family: var(--font-mono);
324 font-size: 13px;
325 line-height: 1.6;
326 }
327 .diff-content .line-add { background: rgba(63, 185, 80, 0.15); color: var(--green); }
328 .diff-content .line-del { background: rgba(248, 81, 73, 0.1); color: var(--red); }
329 .diff-content .line-hunk { background: rgba(56, 139, 253, 0.1); color: var(--text-link); }
330 .diff-content .line { padding: 0 12px; white-space: pre; display: block; }
331
332 .stat-add { color: var(--green); font-weight: 600; }
333 .stat-del { color: var(--red); font-weight: 600; }
334
335 .empty-state {
336 text-align: center;
337 padding: 60px 20px;
338 color: var(--text-muted);
339 }
340 .empty-state h2 { font-size: 24px; margin-bottom: 8px; color: var(--text); }
341 .empty-state pre {
342 text-align: left;
343 display: inline-block;
344 background: var(--bg-secondary);
345 padding: 16px 24px;
346 border-radius: var(--radius);
347 border: 1px solid var(--border);
348 font-family: var(--font-mono);
349 font-size: 13px;
350 margin-top: 16px;
351 line-height: 1.8;
352 }
353
354 .badge {
355 display: inline-block;
356 padding: 2px 8px;
357 border-radius: 12px;
358 font-size: 12px;
359 font-weight: 500;
360 background: var(--bg-tertiary);
361 border: 1px solid var(--border);
362 color: var(--text-muted);
363 }
364
365 .branch-selector {
366 display: inline-flex;
367 align-items: center;
368 gap: 4px;
369 padding: 4px 12px;
370 background: var(--bg-tertiary);
371 border: 1px solid var(--border);
372 border-radius: var(--radius);
373 font-size: 13px;
374 color: var(--text);
375 margin-bottom: 12px;
06d5ffeClaude376 position: relative;
fc1817aClaude377 }
378
06d5ffeClaude379 .branch-dropdown {
380 position: relative;
381 display: inline-block;
382 margin-bottom: 12px;
383 }
384 .branch-dropdown-content {
385 display: none;
386 position: absolute;
387 top: 100%;
388 left: 0;
389 z-index: 10;
390 min-width: 200px;
391 background: var(--bg-secondary);
392 border: 1px solid var(--border);
393 border-radius: var(--radius);
394 margin-top: 4px;
395 box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
396 }
397 .branch-dropdown:hover .branch-dropdown-content,
398 .branch-dropdown:focus-within .branch-dropdown-content { display: block; }
399 .branch-dropdown-content a {
400 display: block;
401 padding: 8px 12px;
402 font-size: 13px;
403 color: var(--text);
404 border-bottom: 1px solid var(--border);
405 }
406 .branch-dropdown-content a:last-child { border-bottom: none; }
407 .branch-dropdown-content a:hover { background: var(--bg-tertiary); text-decoration: none; }
408 .branch-dropdown-content a.active-branch { color: var(--text-link); font-weight: 600; }
409
fc1817aClaude410 .card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; }
411 .card {
412 border: 1px solid var(--border);
413 border-radius: var(--radius);
414 padding: 16px;
415 background: var(--bg-secondary);
06d5ffeClaude416 transition: border-color 0.15s;
fc1817aClaude417 }
06d5ffeClaude418 .card:hover { border-color: var(--text-muted); }
fc1817aClaude419 .card h3 { font-size: 16px; margin-bottom: 4px; }
06d5ffeClaude420 .card h3 a { color: var(--text-link); }
fc1817aClaude421 .card p { font-size: 13px; color: var(--text-muted); }
06d5ffeClaude422 .card-meta { display: flex; gap: 16px; margin-top: 12px; font-size: 12px; color: var(--text-muted); }
423 .card-meta span { display: flex; align-items: center; gap: 4px; }
424
425 .star-btn {
426 display: inline-flex;
427 align-items: center;
428 gap: 6px;
429 padding: 4px 12px;
430 background: var(--bg-tertiary);
431 border: 1px solid var(--border);
432 border-radius: var(--radius);
433 color: var(--text);
434 font-size: 13px;
435 cursor: pointer;
436 }
437 .star-btn:hover { background: var(--border); text-decoration: none; }
438 .star-btn.starred { color: var(--yellow); border-color: var(--yellow); }
439
440 .user-profile {
441 display: flex;
442 gap: 32px;
443 margin-bottom: 32px;
444 }
445 .user-avatar {
446 width: 96px;
447 height: 96px;
448 border-radius: 50%;
449 background: var(--bg-tertiary);
450 border: 1px solid var(--border);
451 display: flex;
452 align-items: center;
453 justify-content: center;
454 font-size: 40px;
455 color: var(--text-muted);
456 flex-shrink: 0;
457 }
458 .user-info h2 { font-size: 24px; margin-bottom: 2px; }
459 .user-info .username { font-size: 16px; color: var(--text-muted); }
460 .user-info .bio { font-size: 14px; color: var(--text-muted); margin-top: 8px; }
461
462 .new-repo-form { max-width: 600px; }
463 .new-repo-form h2 { margin-bottom: 20px; }
464 .visibility-options { display: flex; gap: 12px; margin-bottom: 16px; }
465 .visibility-option {
466 flex: 1;
467 padding: 12px;
468 border: 1px solid var(--border);
469 border-radius: var(--radius);
470 background: var(--bg-secondary);
471 cursor: pointer;
472 text-align: center;
473 }
474 .visibility-option:has(input:checked) { border-color: var(--accent); background: rgba(31, 111, 235, 0.1); }
475 .visibility-option input { display: none; }
476 .visibility-option .vis-label { font-size: 14px; font-weight: 500; }
477 .visibility-option .vis-desc { font-size: 12px; color: var(--text-muted); }
fc1817aClaude478`;