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