Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesPull RequestsActionsSecurityInsightsSettings
✨ AI
More
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.tsxBlame1003 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<
3ef4c9dClaude6 PropsWithChildren<{
7 title?: string;
8 user?: User | null;
9 notificationCount?: number;
6fc53bdClaude10 theme?: "dark" | "light";
3ef4c9dClaude11 }>
6fc53bdClaude12> = ({ children, title, user, notificationCount, theme }) => {
13 const initialTheme = theme === "light" ? "light" : "dark";
fc1817aClaude14 return (
6fc53bdClaude15 <html lang="en" data-theme={initialTheme}>
fc1817aClaude16 <head>
17 <meta charset="UTF-8" />
18 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
19 <title>{title ? `${title} — gluecron` : "gluecron"}</title>
6fc53bdClaude20 <script>{themeInitScript}</script>
fc1817aClaude21 <style>{css}</style>
06d5ffeClaude22 <style>{hljsThemeCss}</style>
fc1817aClaude23 </head>
24 <body>
25 <header>
26 <nav>
27 <a href="/" class="logo">
28 gluecron
29 </a>
3ef4c9dClaude30 <div class="nav-search">
31 <form method="GET" action="/search">
32 <input
33 type="search"
34 name="q"
35 placeholder="Search (press /)"
36 aria-label="Search"
37 />
38 </form>
39 </div>
06d5ffeClaude40 <div class="nav-right">
6fc53bdClaude41 <a
42 href="/theme/toggle"
43 class="nav-link nav-theme"
44 title="Toggle theme"
45 aria-label="Toggle theme"
46 >
47 <span class="theme-icon-dark">{"\u263E"}</span>
48 <span class="theme-icon-light">{"\u2600"}</span>
49 </a>
c81ab7aClaude50 <a href="/explore" class="nav-link">
51 Explore
52 </a>
06d5ffeClaude53 {user ? (
54 <>
3ef4c9dClaude55 <a href="/ask" class="nav-link" title="Ask AI (Cmd+K)">
56 {"\u2728"} Ask
57 </a>
58 <a
59 href="/notifications"
60 class="nav-link nav-notifications"
61 title="Notifications"
62 >
63 {"\u2709"}
64 {notificationCount !== undefined && notificationCount > 0 && (
65 <span class="nav-badge">
66 {notificationCount > 99 ? "99+" : notificationCount}
67 </span>
68 )}
69 </a>
06d5ffeClaude70 <a href="/new" class="btn btn-sm btn-primary">
71 + New
72 </a>
73 <a href={`/${user.username}`} class="nav-user">
74 {user.displayName || user.username}
75 </a>
76 <a href="/settings" class="nav-link">
77 Settings
78 </a>
79 <a href="/logout" class="nav-link">
80 Sign out
81 </a>
82 </>
83 ) : (
84 <>
85 <a href="/login" class="nav-link">
86 Sign in
87 </a>
88 <a href="/register" class="btn btn-sm btn-primary">
89 Register
90 </a>
91 </>
92 )}
93 </div>
fc1817aClaude94 </nav>
95 </header>
96 <main>{children}</main>
97 <footer>
98 <span>gluecron — AI-native code intelligence</span>
99 </footer>
3ef4c9dClaude100 <script>{navScript}</script>
fc1817aClaude101 </body>
102 </html>
103 );
104};
105
6fc53bdClaude106// Runs before paint — reads the theme cookie and flips data-theme so there's
107// no dark-to-light flash on load. SSR default is dark.
108const themeInitScript = `
109 (function(){
110 try {
111 var m = document.cookie.match(/(?:^|; )theme=([^;]+)/);
112 var t = m ? decodeURIComponent(m[1]) : 'dark';
113 if (t !== 'light' && t !== 'dark') t = 'dark';
114 document.documentElement.setAttribute('data-theme', t);
115 } catch(_){}
116 })();
117`;
118
3ef4c9dClaude119const navScript = `
120 (function(){
121 var chord = null;
122 var chordTimer = null;
123 function isTyping(t){
124 t = t || {};
125 var tag = (t.tagName || '').toLowerCase();
126 return tag === 'input' || tag === 'textarea' || t.isContentEditable;
127 }
128 document.addEventListener('keydown', function(e){
129 if (isTyping(e.target)) return;
130 // Single key shortcuts
131 if (e.key === '/') {
132 var el = document.querySelector('.nav-search input');
133 if (el) { e.preventDefault(); el.focus(); return; }
134 }
135 if ((e.metaKey||e.ctrlKey) && e.key.toLowerCase() === 'k') {
136 e.preventDefault(); window.location.href = '/ask'; return;
137 }
138 if (e.key === '?' && !e.ctrlKey && !e.metaKey) {
139 e.preventDefault(); window.location.href = '/shortcuts'; return;
140 }
141 if (e.key === 'n' && !e.ctrlKey && !e.metaKey) {
142 e.preventDefault(); window.location.href = '/new'; return;
143 }
144 // "g" chord
145 if (e.key === 'g' && !e.ctrlKey && !e.metaKey) {
146 chord = 'g';
147 clearTimeout(chordTimer);
148 chordTimer = setTimeout(function(){ chord = null; }, 1200);
149 return;
150 }
151 if (chord === 'g') {
152 if (e.key === 'd') { e.preventDefault(); window.location.href = '/dashboard'; }
153 else if (e.key === 'n') { e.preventDefault(); window.location.href = '/notifications'; }
154 else if (e.key === 'e') { e.preventDefault(); window.location.href = '/explore'; }
155 else if (e.key === 'a') { e.preventDefault(); window.location.href = '/ask'; }
156 chord = null;
157 }
158 });
159 })();
160`;
161
fc1817aClaude162const css = `
6fc53bdClaude163 :root, :root[data-theme='dark'] {
fc1817aClaude164 --bg: #0d1117;
165 --bg-secondary: #161b22;
166 --bg-tertiary: #21262d;
167 --border: #30363d;
168 --text: #e6edf3;
169 --text-muted: #8b949e;
170 --text-link: #58a6ff;
171 --accent: #1f6feb;
172 --accent-hover: #388bfd;
173 --green: #3fb950;
174 --red: #f85149;
175 --yellow: #d29922;
176 --font-mono: 'SF Mono', 'Cascadia Code', 'Fira Code', monospace;
177 --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
178 --radius: 6px;
179 }
180
6fc53bdClaude181 :root[data-theme='light'] {
182 --bg: #ffffff;
183 --bg-secondary: #f6f8fa;
184 --bg-tertiary: #eaeef2;
185 --border: #d0d7de;
186 --text: #1f2328;
187 --text-muted: #656d76;
188 --text-link: #0969da;
189 --accent: #0969da;
190 --accent-hover: #0550ae;
191 --green: #1a7f37;
192 --red: #cf222e;
193 --yellow: #9a6700;
194 }
195
196 /* Theme toggle — show the icon for the *opposite* theme so users see what they'll switch to. */
197 .nav-theme { display: inline-flex; align-items: center; font-size: 16px; line-height: 1; }
198 :root[data-theme='dark'] .theme-icon-dark { display: none; }
199 :root[data-theme='light'] .theme-icon-light { display: none; }
200
fc1817aClaude201 * { margin: 0; padding: 0; box-sizing: border-box; }
202
203 body {
204 font-family: var(--font-sans);
205 background: var(--bg);
206 color: var(--text);
207 line-height: 1.5;
208 min-height: 100vh;
209 display: flex;
210 flex-direction: column;
211 }
212
213 a { color: var(--text-link); text-decoration: none; }
214 a:hover { text-decoration: underline; }
215
216 header {
217 border-bottom: 1px solid var(--border);
218 padding: 12px 24px;
219 background: var(--bg-secondary);
220 }
221
06d5ffeClaude222 header nav {
223 display: flex;
224 align-items: center;
225 justify-content: space-between;
226 max-width: 1200px;
227 margin: 0 auto;
228 }
fc1817aClaude229 .logo { font-size: 20px; font-weight: 700; color: var(--text); }
230 .logo:hover { text-decoration: none; color: var(--text-link); }
231
06d5ffeClaude232 .nav-right { display: flex; align-items: center; gap: 16px; }
233 .nav-link { color: var(--text-muted); font-size: 14px; }
234 .nav-link:hover { color: var(--text); text-decoration: none; }
235 .nav-user { color: var(--text); font-weight: 600; font-size: 14px; }
236 .nav-user:hover { color: var(--text-link); text-decoration: none; }
237
fc1817aClaude238 main { max-width: 1200px; margin: 0 auto; padding: 24px; flex: 1; width: 100%; }
239
240 footer {
241 border-top: 1px solid var(--border);
242 padding: 16px 24px;
243 text-align: center;
244 color: var(--text-muted);
245 font-size: 13px;
246 }
247
06d5ffeClaude248 /* Buttons */
249 .btn {
250 display: inline-flex;
251 align-items: center;
252 gap: 6px;
253 padding: 8px 16px;
254 border-radius: var(--radius);
255 font-size: 14px;
256 font-weight: 500;
257 border: 1px solid var(--border);
258 background: var(--bg-tertiary);
259 color: var(--text);
260 cursor: pointer;
261 text-decoration: none;
262 line-height: 1.4;
263 }
264 .btn:hover { background: var(--border); text-decoration: none; }
265 .btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
266 .btn-primary:hover { background: var(--accent-hover); }
267 .btn-danger { background: transparent; border-color: var(--red); color: var(--red); }
268 .btn-danger:hover { background: rgba(248, 81, 73, 0.15); }
269 .btn-sm { padding: 4px 12px; font-size: 13px; }
270
271 /* Forms */
272 .form-group { margin-bottom: 16px; }
273 .form-group label { display: block; font-size: 14px; font-weight: 500; margin-bottom: 6px; color: var(--text); }
274 .form-group input, .form-group textarea, .form-group select {
275 width: 100%;
276 padding: 8px 12px;
277 background: var(--bg);
278 border: 1px solid var(--border);
279 border-radius: var(--radius);
280 color: var(--text);
281 font-size: 14px;
282 font-family: var(--font-sans);
283 }
284 .form-group input:focus, .form-group textarea:focus, .form-group select:focus {
285 outline: none;
286 border-color: var(--accent);
287 box-shadow: 0 0 0 2px rgba(31, 111, 235, 0.3);
288 }
289 .input-disabled { opacity: 0.5; cursor: not-allowed; }
290
291 /* Auth */
292 .auth-container { max-width: 400px; margin: 40px auto; }
293 .auth-container h2 { margin-bottom: 20px; font-size: 24px; }
294 .auth-error {
295 background: rgba(248, 81, 73, 0.1);
296 border: 1px solid var(--red);
297 color: var(--red);
298 padding: 8px 12px;
299 border-radius: var(--radius);
300 margin-bottom: 16px;
301 font-size: 14px;
302 }
303 .auth-success {
304 background: rgba(63, 185, 80, 0.1);
305 border: 1px solid var(--green);
306 color: var(--green);
307 padding: 8px 12px;
308 border-radius: var(--radius);
309 margin-bottom: 16px;
310 font-size: 14px;
311 }
312 .auth-switch { margin-top: 16px; font-size: 14px; color: var(--text-muted); }
313
314 /* Settings */
315 .settings-container { max-width: 600px; }
316 .settings-container h2 { margin-bottom: 20px; font-size: 24px; }
317 .settings-container h3 { font-size: 18px; margin-bottom: 12px; }
318 .ssh-keys-list { margin-bottom: 24px; }
319 .ssh-key-item {
320 display: flex;
321 justify-content: space-between;
322 align-items: center;
323 padding: 12px 16px;
324 border: 1px solid var(--border);
325 border-radius: var(--radius);
326 margin-bottom: 8px;
327 background: var(--bg-secondary);
328 }
329 .ssh-key-meta { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
330 .ssh-key-meta code { font-size: 11px; background: var(--bg-tertiary); padding: 1px 6px; border-radius: 3px; margin-right: 8px; }
331
332 /* Repo header */
fc1817aClaude333 .repo-header {
334 display: flex;
335 align-items: center;
336 gap: 8px;
337 margin-bottom: 16px;
338 font-size: 20px;
339 }
340 .repo-header .owner { color: var(--text-link); }
341 .repo-header .separator { color: var(--text-muted); }
342 .repo-header .name { color: var(--text-link); font-weight: 600; }
06d5ffeClaude343 .repo-header-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }
fc1817aClaude344
345 .repo-nav {
346 display: flex;
347 gap: 0;
348 border-bottom: 1px solid var(--border);
349 margin-bottom: 20px;
350 }
351 .repo-nav a {
352 padding: 8px 16px;
353 color: var(--text-muted);
354 border-bottom: 2px solid transparent;
355 font-size: 14px;
356 }
357 .repo-nav a:hover { text-decoration: none; color: var(--text); }
358 .repo-nav a.active { color: var(--text); border-bottom-color: var(--accent); }
359
360 .breadcrumb { display: flex; gap: 4px; align-items: center; margin-bottom: 16px; color: var(--text-muted); font-size: 14px; }
361 .breadcrumb a { color: var(--text-link); }
362
363 .file-table { width: 100%; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
364 .file-table tr { border-bottom: 1px solid var(--border); }
365 .file-table tr:last-child { border-bottom: none; }
366 .file-table td { padding: 8px 16px; font-size: 14px; }
367 .file-table tr:hover { background: var(--bg-secondary); }
368 .file-icon { width: 20px; color: var(--text-muted); }
369 .file-name a { color: var(--text); }
370 .file-name a:hover { color: var(--text-link); text-decoration: underline; }
371
372 .blob-view {
373 border: 1px solid var(--border);
374 border-radius: var(--radius);
375 overflow: hidden;
376 }
377 .blob-header {
378 background: var(--bg-secondary);
379 padding: 8px 16px;
380 border-bottom: 1px solid var(--border);
381 font-size: 13px;
382 color: var(--text-muted);
06d5ffeClaude383 display: flex;
384 justify-content: space-between;
385 align-items: center;
fc1817aClaude386 }
387 .blob-code {
388 overflow-x: auto;
389 font-family: var(--font-mono);
390 font-size: 13px;
391 line-height: 1.6;
392 }
393 .blob-code table { width: 100%; border-collapse: collapse; }
394 .blob-code .line-num {
395 width: 1%;
396 min-width: 50px;
397 padding: 0 12px;
398 text-align: right;
399 color: var(--text-muted);
400 user-select: none;
401 white-space: nowrap;
402 border-right: 1px solid var(--border);
403 }
404 .blob-code .line-content { padding: 0 12px; white-space: pre; }
405
406 .commit-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
407 .commit-item {
408 display: flex;
409 justify-content: space-between;
410 align-items: center;
411 padding: 12px 16px;
412 border-bottom: 1px solid var(--border);
413 }
414 .commit-item:last-child { border-bottom: none; }
415 .commit-item:hover { background: var(--bg-secondary); }
416 .commit-message { font-size: 14px; font-weight: 500; }
417 .commit-meta { font-size: 12px; color: var(--text-muted); }
418 .commit-sha {
419 font-family: var(--font-mono);
420 font-size: 12px;
421 padding: 2px 8px;
422 background: var(--bg-tertiary);
423 border: 1px solid var(--border);
424 border-radius: var(--radius);
425 color: var(--text-link);
426 }
427
428 .diff-view { margin-top: 16px; }
429 .diff-file {
430 border: 1px solid var(--border);
431 border-radius: var(--radius);
432 margin-bottom: 16px;
433 overflow: hidden;
434 }
435 .diff-file-header {
436 background: var(--bg-secondary);
437 padding: 8px 16px;
438 border-bottom: 1px solid var(--border);
439 font-family: var(--font-mono);
440 font-size: 13px;
441 }
442 .diff-content {
443 overflow-x: auto;
444 font-family: var(--font-mono);
445 font-size: 13px;
446 line-height: 1.6;
447 }
448 .diff-content .line-add { background: rgba(63, 185, 80, 0.15); color: var(--green); }
449 .diff-content .line-del { background: rgba(248, 81, 73, 0.1); color: var(--red); }
450 .diff-content .line-hunk { background: rgba(56, 139, 253, 0.1); color: var(--text-link); }
451 .diff-content .line { padding: 0 12px; white-space: pre; display: block; }
452
453 .stat-add { color: var(--green); font-weight: 600; }
454 .stat-del { color: var(--red); font-weight: 600; }
455
456 .empty-state {
457 text-align: center;
458 padding: 60px 20px;
459 color: var(--text-muted);
460 }
461 .empty-state h2 { font-size: 24px; margin-bottom: 8px; color: var(--text); }
462 .empty-state pre {
463 text-align: left;
464 display: inline-block;
465 background: var(--bg-secondary);
466 padding: 16px 24px;
467 border-radius: var(--radius);
468 border: 1px solid var(--border);
469 font-family: var(--font-mono);
470 font-size: 13px;
471 margin-top: 16px;
472 line-height: 1.8;
473 }
474
475 .badge {
476 display: inline-block;
477 padding: 2px 8px;
478 border-radius: 12px;
479 font-size: 12px;
480 font-weight: 500;
481 background: var(--bg-tertiary);
482 border: 1px solid var(--border);
483 color: var(--text-muted);
484 }
485
486 .branch-selector {
487 display: inline-flex;
488 align-items: center;
489 gap: 4px;
490 padding: 4px 12px;
491 background: var(--bg-tertiary);
492 border: 1px solid var(--border);
493 border-radius: var(--radius);
494 font-size: 13px;
495 color: var(--text);
496 margin-bottom: 12px;
06d5ffeClaude497 position: relative;
fc1817aClaude498 }
499
06d5ffeClaude500 .branch-dropdown {
501 position: relative;
502 display: inline-block;
503 margin-bottom: 12px;
504 }
505 .branch-dropdown-content {
506 display: none;
507 position: absolute;
508 top: 100%;
509 left: 0;
510 z-index: 10;
511 min-width: 200px;
512 background: var(--bg-secondary);
513 border: 1px solid var(--border);
514 border-radius: var(--radius);
515 margin-top: 4px;
516 box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
517 }
518 .branch-dropdown:hover .branch-dropdown-content,
519 .branch-dropdown:focus-within .branch-dropdown-content { display: block; }
520 .branch-dropdown-content a {
521 display: block;
522 padding: 8px 12px;
523 font-size: 13px;
524 color: var(--text);
525 border-bottom: 1px solid var(--border);
526 }
527 .branch-dropdown-content a:last-child { border-bottom: none; }
528 .branch-dropdown-content a:hover { background: var(--bg-tertiary); text-decoration: none; }
529 .branch-dropdown-content a.active-branch { color: var(--text-link); font-weight: 600; }
530
fc1817aClaude531 .card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; }
532 .card {
533 border: 1px solid var(--border);
534 border-radius: var(--radius);
535 padding: 16px;
536 background: var(--bg-secondary);
06d5ffeClaude537 transition: border-color 0.15s;
fc1817aClaude538 }
06d5ffeClaude539 .card:hover { border-color: var(--text-muted); }
fc1817aClaude540 .card h3 { font-size: 16px; margin-bottom: 4px; }
06d5ffeClaude541 .card h3 a { color: var(--text-link); }
fc1817aClaude542 .card p { font-size: 13px; color: var(--text-muted); }
06d5ffeClaude543 .card-meta { display: flex; gap: 16px; margin-top: 12px; font-size: 12px; color: var(--text-muted); }
544 .card-meta span { display: flex; align-items: center; gap: 4px; }
545
546 .star-btn {
547 display: inline-flex;
548 align-items: center;
549 gap: 6px;
550 padding: 4px 12px;
551 background: var(--bg-tertiary);
552 border: 1px solid var(--border);
553 border-radius: var(--radius);
554 color: var(--text);
555 font-size: 13px;
556 cursor: pointer;
557 }
558 .star-btn:hover { background: var(--border); text-decoration: none; }
559 .star-btn.starred { color: var(--yellow); border-color: var(--yellow); }
560
561 .user-profile {
562 display: flex;
563 gap: 32px;
564 margin-bottom: 32px;
565 }
566 .user-avatar {
567 width: 96px;
568 height: 96px;
569 border-radius: 50%;
570 background: var(--bg-tertiary);
571 border: 1px solid var(--border);
572 display: flex;
573 align-items: center;
574 justify-content: center;
575 font-size: 40px;
576 color: var(--text-muted);
577 flex-shrink: 0;
578 }
579 .user-info h2 { font-size: 24px; margin-bottom: 2px; }
580 .user-info .username { font-size: 16px; color: var(--text-muted); }
581 .user-info .bio { font-size: 14px; color: var(--text-muted); margin-top: 8px; }
582
583 .new-repo-form { max-width: 600px; }
584 .new-repo-form h2 { margin-bottom: 20px; }
585 .visibility-options { display: flex; gap: 12px; margin-bottom: 16px; }
586 .visibility-option {
587 flex: 1;
588 padding: 12px;
589 border: 1px solid var(--border);
590 border-radius: var(--radius);
591 background: var(--bg-secondary);
592 cursor: pointer;
593 text-align: center;
594 }
595 .visibility-option:has(input:checked) { border-color: var(--accent); background: rgba(31, 111, 235, 0.1); }
596 .visibility-option input { display: none; }
597 .visibility-option .vis-label { font-size: 14px; font-weight: 500; }
598 .visibility-option .vis-desc { font-size: 12px; color: var(--text-muted); }
79136bbClaude599
600 /* Issues */
601 .issue-tabs { display: flex; gap: 16px; }
602 .issue-tabs a { color: var(--text-muted); font-size: 14px; font-weight: 500; }
603 .issue-tabs a:hover { color: var(--text); text-decoration: none; }
604 .issue-tabs a.active { color: var(--text); }
605
606 .issue-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
607 .issue-item {
608 display: flex;
609 gap: 12px;
610 align-items: flex-start;
611 padding: 12px 16px;
612 border-bottom: 1px solid var(--border);
613 }
614 .issue-item:last-child { border-bottom: none; }
615 .issue-item:hover { background: var(--bg-secondary); }
616 .issue-state-icon { font-size: 16px; padding-top: 2px; }
617 .state-open { color: var(--green); }
618 .state-closed { color: #986ee2; }
619 .issue-title { font-size: 15px; font-weight: 600; }
620 .issue-title a { color: var(--text); }
621 .issue-title a:hover { color: var(--text-link); }
622 .issue-meta { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
623
624 .issue-badge {
625 display: inline-flex;
626 align-items: center;
627 gap: 4px;
628 padding: 4px 12px;
629 border-radius: 20px;
630 font-size: 13px;
631 font-weight: 500;
632 }
633 .badge-open { background: rgba(63, 185, 80, 0.15); color: var(--green); border: 1px solid var(--green); }
634 .badge-closed { background: rgba(152, 110, 226, 0.15); color: #986ee2; border: 1px solid #986ee2; }
0074234Claude635 .badge-merged { background: rgba(152, 110, 226, 0.15); color: #986ee2; border: 1px solid #986ee2; }
636 .state-merged { color: #986ee2; }
637 .ai-review { border-color: var(--accent); }
79136bbClaude638
639 .issue-detail { max-width: 900px; }
640 .issue-comment-box {
641 border: 1px solid var(--border);
642 border-radius: var(--radius);
643 margin-bottom: 16px;
644 overflow: hidden;
645 }
646 .comment-header {
647 background: var(--bg-secondary);
648 padding: 8px 16px;
649 border-bottom: 1px solid var(--border);
650 font-size: 13px;
651 color: var(--text-muted);
652 }
653
654 /* Search */
655 .search-results .diff-file { margin-bottom: 12px; }
3ef4c9dClaude656
657 /* Nav — search + ask + notifications badge */
658 .nav-search { flex: 1; max-width: 320px; margin: 0 16px; }
659 .nav-search form { width: 100%; }
660 .nav-search input {
661 width: 100%;
662 padding: 6px 10px;
663 background: var(--bg);
664 border: 1px solid var(--border);
665 border-radius: var(--radius);
666 color: var(--text);
667 font-size: 13px;
668 }
669 .nav-search input::placeholder { color: var(--text-muted); }
670 .nav-search input:focus { outline: none; border-color: var(--accent); }
671 .nav-notifications { position: relative; display: inline-flex; align-items: center; }
672 .nav-badge {
673 position: absolute;
674 top: -6px;
675 right: -10px;
676 min-width: 18px;
677 height: 18px;
678 padding: 0 5px;
679 border-radius: 9px;
680 background: var(--red);
681 color: #fff;
682 font-size: 11px;
683 font-weight: 700;
684 display: flex;
685 align-items: center;
686 justify-content: center;
687 }
688
689 /* Notifications list */
690 .notification-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
691 .notification-item {
692 display: flex;
693 gap: 12px;
694 align-items: flex-start;
695 padding: 12px 16px;
696 border-bottom: 1px solid var(--border);
697 background: var(--bg);
698 }
699 .notification-item.unread { background: rgba(56, 139, 253, 0.06); }
700 .notification-item:last-child { border-bottom: none; }
701 .notification-badge {
702 flex-shrink: 0;
703 padding: 2px 8px;
704 border-radius: 10px;
705 font-size: 11px;
706 font-weight: 600;
707 border: 1px solid;
708 text-transform: lowercase;
709 }
710 .notification-body { flex: 1; min-width: 0; }
711 .notification-title { font-size: 14px; font-weight: 500; margin-bottom: 2px; }
712 .notification-title a { color: var(--text); }
713 .notification-title a:hover { color: var(--text-link); }
714 .notification-desc { font-size: 13px; color: var(--text-muted); margin-bottom: 4px; }
715 .notification-meta { font-size: 12px; color: var(--text-muted); }
716 .notification-meta a { color: var(--text-link); }
717 .notification-actions { display: flex; gap: 4px; align-items: center; }
718 .notification-actions .btn { padding: 2px 8px; font-size: 12px; line-height: 1; }
719
720 /* Dashboard */
721 .dashboard-grid {
722 display: grid;
723 grid-template-columns: 2fr 1fr;
724 gap: 24px;
725 }
726 .dashboard-section { margin-bottom: 24px; }
727 .dashboard-section h3 {
728 font-size: 14px;
729 text-transform: uppercase;
730 letter-spacing: 0.5px;
731 color: var(--text-muted);
732 margin-bottom: 12px;
733 display: flex;
734 justify-content: space-between;
735 align-items: center;
736 }
737 .dashboard-section h3 a { font-size: 12px; font-weight: 400; text-transform: none; letter-spacing: 0; }
738 .panel {
739 border: 1px solid var(--border);
740 border-radius: var(--radius);
741 background: var(--bg-secondary);
742 overflow: hidden;
743 }
744 .panel-item {
745 display: flex;
746 align-items: flex-start;
747 gap: 10px;
748 padding: 10px 14px;
749 border-bottom: 1px solid var(--border);
750 font-size: 13px;
751 }
752 .panel-item:last-child { border-bottom: none; }
753 .panel-item .dot {
754 width: 8px; height: 8px; border-radius: 50%;
755 margin-top: 6px; flex-shrink: 0; background: var(--text-muted);
756 }
757 .panel-item .dot.green { background: var(--green); }
758 .panel-item .dot.red { background: var(--red); }
759 .panel-item .dot.yellow { background: var(--yellow); }
760 .panel-item .dot.blue { background: var(--accent); }
761 .panel-item .meta { color: var(--text-muted); font-size: 12px; margin-top: 2px; }
762 .panel-empty { padding: 24px; text-align: center; color: var(--text-muted); font-size: 13px; }
763
764 /* AI Ask chat */
765 .ask-container { max-width: 900px; margin: 0 auto; }
766 .chat-log {
767 border: 1px solid var(--border);
768 border-radius: var(--radius);
769 background: var(--bg-secondary);
770 padding: 16px;
771 margin-bottom: 16px;
772 min-height: 200px;
773 max-height: 60vh;
774 overflow-y: auto;
775 }
776 .chat-message {
777 padding: 10px 14px;
778 border-radius: var(--radius);
779 margin-bottom: 10px;
780 white-space: pre-wrap;
781 word-wrap: break-word;
782 font-size: 14px;
783 line-height: 1.5;
784 }
785 .chat-message.user { background: var(--bg-tertiary); border: 1px solid var(--border); }
786 .chat-message.assistant { background: rgba(188, 140, 255, 0.08); border: 1px solid rgba(188, 140, 255, 0.3); }
787 .chat-message .role {
788 font-size: 11px;
789 font-weight: 600;
790 text-transform: uppercase;
791 letter-spacing: 0.5px;
792 color: var(--text-muted);
793 margin-bottom: 6px;
794 }
795 .chat-form textarea {
796 width: 100%;
797 min-height: 80px;
798 padding: 10px 12px;
799 background: var(--bg);
800 border: 1px solid var(--border);
801 border-radius: var(--radius);
802 color: var(--text);
803 font-family: var(--font-sans);
804 font-size: 14px;
805 resize: vertical;
806 }
807 .chat-hint { font-size: 12px; color: var(--text-muted); margin-top: 6px; }
808 .chat-cited {
809 display: inline-block;
810 font-family: var(--font-mono);
811 font-size: 11px;
812 padding: 1px 6px;
813 background: var(--bg-tertiary);
814 border: 1px solid var(--border);
815 border-radius: 3px;
816 margin-right: 4px;
817 }
818
819 /* Releases */
820 .release-card {
821 border: 1px solid var(--border);
822 border-radius: var(--radius);
823 background: var(--bg-secondary);
824 padding: 16px;
825 margin-bottom: 16px;
826 }
827 .release-header {
828 display: flex;
829 justify-content: space-between;
830 align-items: baseline;
831 margin-bottom: 8px;
832 gap: 12px;
833 flex-wrap: wrap;
834 }
835 .release-name { font-size: 18px; font-weight: 600; }
836 .release-tag {
837 font-family: var(--font-mono);
838 font-size: 12px;
839 padding: 2px 8px;
840 background: var(--bg-tertiary);
841 border: 1px solid var(--border);
842 border-radius: var(--radius);
843 color: var(--green);
844 }
845 .release-latest { background: var(--green); color: var(--bg); border-color: var(--green); }
846
847 /* Gate runs */
848 .gate-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
849 .gate-run-row {
850 display: flex;
851 align-items: center;
852 gap: 12px;
853 padding: 10px 14px;
854 border-bottom: 1px solid var(--border);
855 font-size: 13px;
856 }
857 .gate-run-row:last-child { border-bottom: none; }
858 .gate-status {
859 display: inline-flex;
860 align-items: center;
861 gap: 4px;
862 padding: 2px 8px;
863 border-radius: 10px;
864 font-size: 11px;
865 font-weight: 600;
866 text-transform: uppercase;
867 }
868 .gate-status.passed { background: rgba(63, 185, 80, 0.15); color: var(--green); border: 1px solid var(--green); }
869 .gate-status.failed { background: rgba(248, 81, 73, 0.15); color: var(--red); border: 1px solid var(--red); }
870 .gate-status.repaired { background: rgba(188, 140, 255, 0.15); color: #bc8cff; border: 1px solid #bc8cff; }
871 .gate-status.skipped { background: var(--bg-tertiary); color: var(--text-muted); border: 1px solid var(--border); }
872 .gate-status.running, .gate-status.pending { background: rgba(210, 153, 34, 0.15); color: var(--yellow); border: 1px solid var(--yellow); }
873
874 /* Search */
875 .search-filters { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
876 .search-hit {
877 border: 1px solid var(--border);
878 border-radius: var(--radius);
879 padding: 12px 16px;
880 background: var(--bg-secondary);
881 margin-bottom: 8px;
882 }
883 .search-hit h4 { font-size: 14px; margin-bottom: 4px; }
884 .search-hit .hit-path { font-size: 12px; color: var(--text-muted); font-family: var(--font-mono); }
885 .search-hit pre { margin-top: 8px; font-size: 12px; }
886
6fc53bdClaude887 /* Audit log */
888 .audit-log {
889 border: 1px solid var(--border);
890 border-radius: var(--radius);
891 overflow-x: auto;
892 background: var(--bg-secondary);
893 }
894 .audit-table { width: 100%; border-collapse: collapse; font-size: 13px; }
895 .audit-table th, .audit-table td {
896 text-align: left;
897 padding: 8px 12px;
898 border-bottom: 1px solid var(--border);
899 vertical-align: top;
900 }
901 .audit-table th {
902 background: var(--bg-tertiary);
903 font-size: 11px;
904 text-transform: uppercase;
905 letter-spacing: 0.5px;
906 color: var(--text-muted);
907 font-weight: 600;
908 }
909 .audit-table tr:last-child td { border-bottom: none; }
910 .audit-when { white-space: nowrap; color: var(--text-muted); }
911 .audit-muted { color: var(--text-muted); font-style: italic; }
912 .audit-action {
913 font-family: var(--font-mono);
914 font-size: 11px;
915 padding: 1px 6px;
916 background: var(--bg-tertiary);
917 border: 1px solid var(--border);
918 border-radius: 3px;
919 color: var(--text-link);
920 }
921 .audit-ip, .audit-meta code {
922 font-family: var(--font-mono);
923 font-size: 11px;
924 color: var(--text-muted);
925 }
926 .audit-target code {
927 font-family: var(--font-mono);
928 font-size: 11px;
929 color: var(--text-muted);
930 }
931
932 /* Reactions */
933 .reactions {
934 display: flex;
935 gap: 6px;
936 flex-wrap: wrap;
937 margin-top: 8px;
938 }
939 .reaction-btn {
940 display: inline-flex;
941 align-items: center;
942 gap: 4px;
943 padding: 2px 8px;
944 border-radius: 12px;
945 background: var(--bg-tertiary);
946 border: 1px solid var(--border);
947 color: var(--text);
948 font-size: 12px;
949 cursor: pointer;
950 font-family: inherit;
951 }
952 .reaction-btn:hover { background: var(--border); }
953 .reaction-btn.active { background: rgba(31, 111, 235, 0.15); border-color: var(--accent); color: var(--accent); }
954 .reaction-picker {
955 display: inline-flex;
956 gap: 4px;
957 align-items: center;
958 }
959 .reaction-picker form { display: inline; }
960 .reaction-count { font-size: 11px; font-weight: 600; }
961
24cf2caClaude962 /* Saved replies */
963 .saved-replies-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
964 .saved-reply-item { border-bottom: 1px solid var(--border); background: var(--bg); }
965 .saved-reply-item:last-child { border-bottom: none; }
966 .saved-reply-item > summary {
967 padding: 10px 16px;
968 cursor: pointer;
969 list-style: none;
970 display: flex;
971 align-items: center;
972 }
973 .saved-reply-item > summary::-webkit-details-marker { display: none; }
974 .saved-reply-item > summary:hover { background: var(--bg-secondary); }
975 .saved-reply-item code { font-family: var(--font-mono); font-size: 12px; color: var(--text-link); }
976
6fc53bdClaude977 /* Draft PR */
978 .draft-badge {
979 background: rgba(139, 148, 158, 0.15);
980 color: var(--text-muted);
981 border: 1px solid var(--text-muted);
982 }
983 .state-draft { color: var(--text-muted); }
984
3ef4c9dClaude985 /* Toasts (prepared for future UI hooks) */
986 .toast-container {
987 position: fixed; bottom: 24px; right: 24px;
988 z-index: 50; display: flex; flex-direction: column; gap: 8px;
989 }
990
991 /* Mobile */
992 @media (max-width: 768px) {
993 header nav { flex-wrap: wrap; gap: 8px; }
994 .nav-search { max-width: 100%; margin: 8px 0; order: 3; width: 100%; }
995 .nav-right { flex-wrap: wrap; gap: 8px; }
996 main { padding: 16px; }
997 .dashboard-grid { grid-template-columns: 1fr; }
998 .card-grid { grid-template-columns: 1fr; }
999 .user-profile { flex-direction: column; gap: 16px; }
1000 .repo-header { flex-wrap: wrap; }
1001 .repo-nav { overflow-x: auto; }
1002 }
fc1817aClaude1003`;