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.tsxBlame871 lines · 1 contributor
fc1817aClaude1import type { FC, PropsWithChildren } from "hono/jsx";
06d5ffeClaude2import type { User } from "../db/schema";
3import { hljsThemeCss } from "../lib/highlight";
45e31d0Claude4import { clientJs } from "./client-js";
fc1817aClaude5
06d5ffeClaude6export const Layout: FC<
3ef4c9dClaude7 PropsWithChildren<{
8 title?: string;
9 user?: User | null;
10 notificationCount?: number;
6fc53bdClaude11 theme?: "dark" | "light";
3ef4c9dClaude12 }>
6fc53bdClaude13> = ({ children, title, user, notificationCount, theme }) => {
14 const initialTheme = theme === "light" ? "light" : "dark";
fc1817aClaude15 return (
6fc53bdClaude16 <html lang="en" data-theme={initialTheme}>
fc1817aClaude17 <head>
18 <meta charset="UTF-8" />
19 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
eae38d1Claude20 <meta name="theme-color" content="#0d1117" />
21 <link rel="manifest" href="/manifest.webmanifest" />
22 <link rel="icon" type="image/svg+xml" href="/icon.svg" />
fc1817aClaude23 <title>{title ? `${title} — gluecron` : "gluecron"}</title>
6fc53bdClaude24 <script>{themeInitScript}</script>
fc1817aClaude25 <style>{css}</style>
06d5ffeClaude26 <style>{hljsThemeCss}</style>
fc1817aClaude27 </head>
28 <body>
4a52a98Claude29 <div class="prelaunch-banner" role="status" aria-live="polite">
30 Pre-launch &mdash; Gluecron is in final validation. Public signups
31 and git hosting for non-owner users open after launch review.
32 </div>
fc1817aClaude33 <header>
34 <nav>
35 <a href="/" class="logo">
36 gluecron
37 </a>
3ef4c9dClaude38 <div class="nav-search">
001af43Claude39 <form method="get" action="/search">
3ef4c9dClaude40 <input
41 type="search"
42 name="q"
43 placeholder="Search (press /)"
44 aria-label="Search"
45 />
46 </form>
47 </div>
06d5ffeClaude48 <div class="nav-right">
6fc53bdClaude49 <a
50 href="/theme/toggle"
51 class="nav-link nav-theme"
52 title="Toggle theme"
53 aria-label="Toggle theme"
54 >
55 <span class="theme-icon-dark">{"\u263E"}</span>
56 <span class="theme-icon-light">{"\u2600"}</span>
57 </a>
c81ab7aClaude58 <a href="/explore" class="nav-link">
59 Explore
60 </a>
06d5ffeClaude61 {user ? (
62 <>
f1ab587Claude63 <a href="/dashboard" class="nav-link" style="font-weight: 600">
64 Dashboard
65 </a>
06d5ffeClaude66 <a href="/new" class="btn btn-sm btn-primary">
67 + New
68 </a>
69 <a href={`/${user.username}`} class="nav-user">
70 {user.displayName || user.username}
71 </a>
72 <a href="/settings" class="nav-link">
73 Settings
74 </a>
75 <a href="/logout" class="nav-link">
76 Sign out
77 </a>
78 </>
79 ) : (
80 <>
81 <a href="/login" class="nav-link">
82 Sign in
83 </a>
84 <a href="/register" class="btn btn-sm btn-primary">
85 Register
86 </a>
87 </>
88 )}
89 </div>
fc1817aClaude90 </nav>
91 </header>
45e31d0Claude92 <main id="main-content">{children}</main>
fc1817aClaude93 <footer>
36b4cbdClaude94 <span>
95 &copy; {new Date().getFullYear()} gluecron — AI-native code intelligence
96 </span>
97 <div style="margin-top: 6px; display: flex; gap: 16px; justify-content: center; font-size: 12px">
98 <a href="/terms" style="color: var(--text-muted)">Terms</a>
99 <a href="/privacy" style="color: var(--text-muted)">Privacy</a>
100 <a href="/acceptable-use" style="color: var(--text-muted)">Acceptable Use</a>
101 </div>
fc1817aClaude102 </footer>
45e31d0Claude103 <script>{clientJs}</script>
fc1817aClaude104 </body>
105 </html>
106 );
107};
108
6fc53bdClaude109// Runs before paint — reads the theme cookie and flips data-theme so there's
110// no dark-to-light flash on load. SSR default is dark.
111const themeInitScript = `
112 (function(){
113 try {
114 var m = document.cookie.match(/(?:^|; )theme=([^;]+)/);
115 var t = m ? decodeURIComponent(m[1]) : 'dark';
116 if (t !== 'light' && t !== 'dark') t = 'dark';
117 document.documentElement.setAttribute('data-theme', t);
118 } catch(_){}
119 })();
120`;
121
eae38d1Claude122// Block G1 — register service worker for offline / install support.
123// Kept inline (and tiny) so we don't block first paint.
124const pwaRegisterScript = `
125 if ('serviceWorker' in navigator) {
126 window.addEventListener('load', function(){
127 navigator.serviceWorker.register('/sw.js').catch(function(){});
128 });
129 }
130`;
131
3ef4c9dClaude132const navScript = `
133 (function(){
134 var chord = null;
135 var chordTimer = null;
136 function isTyping(t){
137 t = t || {};
138 var tag = (t.tagName || '').toLowerCase();
139 return tag === 'input' || tag === 'textarea' || t.isContentEditable;
140 }
71cd5ecClaude141
142 // ---------- Block I4 — Command palette ----------
143 var COMMANDS = [
144 { label: 'Go to Dashboard', href: '/dashboard', kw: 'home' },
145 { label: 'Go to Explore', href: '/explore', kw: 'browse discover' },
146 { label: 'Go to Notifications', href: '/notifications', kw: 'inbox' },
147 { label: 'Go to Ask AI', href: '/ask', kw: 'chat assistant' },
148 { label: 'Create new repository', href: '/new', kw: 'add create' },
149 { label: 'Marketplace', href: '/marketplace', kw: 'apps store' },
150 { label: 'Installed apps', href: '/settings/apps', kw: 'my apps' },
151 { label: 'Register new app', href: '/developer/apps-new', kw: 'developer create' },
152 { label: 'Keyboard shortcuts', href: '/shortcuts', kw: 'help keys' },
153 { label: 'Settings (profile)', href: '/settings', kw: 'account' },
154 { label: '2FA settings', href: '/settings/2fa', kw: 'two factor security' },
155 { label: 'Passkeys settings', href: '/settings/passkeys', kw: 'webauthn' },
156 { label: 'Personal access tokens', href: '/settings/tokens', kw: 'pat api' },
157 { label: 'Billing + quotas', href: '/settings/billing', kw: 'plans money' },
158 { label: 'Audit log (personal)', href: '/settings/audit', kw: 'history' },
159 { label: 'Gists', href: '/gists', kw: 'snippets' },
160 { label: 'GraphQL explorer', href: '/api/graphql', kw: 'api query' },
161 { label: 'Admin dashboard', href: '/admin', kw: 'superuser' },
162 { label: 'Toggle theme', href: '/theme/toggle', kw: 'dark light mode' }
163 ];
164
165 function fuzzyMatch(item, q){
166 if (!q) return true;
167 var hay = (item.label + ' ' + (item.kw||'') + ' ' + item.href).toLowerCase();
168 q = q.toLowerCase();
169 var qi = 0;
170 for (var i = 0; i < hay.length && qi < q.length; i++) {
171 if (hay[i] === q[qi]) qi++;
172 }
173 return qi === q.length;
174 }
175
176 var backdrop, panel, input, list, selected = 0, filtered = COMMANDS.slice();
177
178 function render(){
179 if (!list) return;
180 var html = '';
181 for (var i = 0; i < filtered.length; i++) {
182 var item = filtered[i];
183 var cls = i === selected ? 'cmdk-item cmdk-active' : 'cmdk-item';
184 var bg = i === selected ? 'background:var(--bg);' : '';
185 html += '<div class="' + cls + '" data-idx="' + i + '" data-href="' + item.href + '"' +
186 ' style="padding:10px 16px;cursor:pointer;border-bottom:1px solid var(--border);' + bg + '">' +
187 '<div>' + item.label + '</div>' +
188 '<div style="font-size:11px;color:var(--text-muted)">' + item.href + '</div>' +
189 '</div>';
190 }
191 if (filtered.length === 0) {
192 html = '<div style="padding:16px;color:var(--text-muted);text-align:center">No matches.</div>';
193 }
194 list.innerHTML = html;
195 }
196
197 function openPalette(){
198 backdrop = document.getElementById('cmdk-backdrop');
199 panel = document.getElementById('cmdk-panel');
200 input = document.getElementById('cmdk-input');
201 list = document.getElementById('cmdk-list');
202 if (!backdrop || !panel) return;
203 backdrop.style.display = 'block';
204 panel.style.display = 'block';
205 input.value = '';
206 selected = 0;
207 filtered = COMMANDS.slice();
208 render();
209 input.focus();
210 }
211 function closePalette(){
212 if (backdrop) backdrop.style.display = 'none';
213 if (panel) panel.style.display = 'none';
214 }
215 function go(href){ closePalette(); window.location.href = href; }
216
217 document.addEventListener('click', function(e){
218 var t = e.target;
219 if (t && t.id === 'cmdk-backdrop') { closePalette(); return; }
220 var item = t && t.closest && t.closest('.cmdk-item');
221 if (item) { go(item.getAttribute('data-href')); }
222 });
223
224 document.addEventListener('input', function(e){
225 if (e.target && e.target.id === 'cmdk-input') {
226 var q = e.target.value;
227 filtered = COMMANDS.filter(function(c){ return fuzzyMatch(c, q); });
228 selected = 0;
229 render();
230 }
231 });
232
3ef4c9dClaude233 document.addEventListener('keydown', function(e){
71cd5ecClaude234 // Palette-scoped keys take priority when open
235 if (panel && panel.style.display === 'block') {
236 if (e.key === 'Escape') { e.preventDefault(); closePalette(); return; }
237 if (e.key === 'ArrowDown') {
238 e.preventDefault();
239 selected = Math.min(filtered.length - 1, selected + 1);
240 render();
241 return;
242 }
243 if (e.key === 'ArrowUp') {
244 e.preventDefault();
245 selected = Math.max(0, selected - 1);
246 render();
247 return;
248 }
249 if (e.key === 'Enter') {
250 e.preventDefault();
251 var item = filtered[selected];
252 if (item) go(item.href);
253 return;
254 }
255 return;
256 }
257
3ef4c9dClaude258 if (isTyping(e.target)) return;
259 // Single key shortcuts
260 if (e.key === '/') {
261 var el = document.querySelector('.nav-search input');
262 if (el) { e.preventDefault(); el.focus(); return; }
263 }
264 if ((e.metaKey||e.ctrlKey) && e.key.toLowerCase() === 'k') {
71cd5ecClaude265 e.preventDefault();
266 openPalette();
267 return;
3ef4c9dClaude268 }
269 if (e.key === '?' && !e.ctrlKey && !e.metaKey) {
270 e.preventDefault(); window.location.href = '/shortcuts'; return;
271 }
272 if (e.key === 'n' && !e.ctrlKey && !e.metaKey) {
273 e.preventDefault(); window.location.href = '/new'; return;
274 }
275 // "g" chord
276 if (e.key === 'g' && !e.ctrlKey && !e.metaKey) {
277 chord = 'g';
278 clearTimeout(chordTimer);
279 chordTimer = setTimeout(function(){ chord = null; }, 1200);
280 return;
281 }
282 if (chord === 'g') {
283 if (e.key === 'd') { e.preventDefault(); window.location.href = '/dashboard'; }
284 else if (e.key === 'n') { e.preventDefault(); window.location.href = '/notifications'; }
285 else if (e.key === 'e') { e.preventDefault(); window.location.href = '/explore'; }
286 else if (e.key === 'a') { e.preventDefault(); window.location.href = '/ask'; }
287 chord = null;
288 }
289 });
290 })();
291`;
292
fc1817aClaude293const css = `
6fc53bdClaude294 :root, :root[data-theme='dark'] {
fc1817aClaude295 --bg: #0d1117;
296 --bg-secondary: #161b22;
297 --bg-tertiary: #21262d;
298 --border: #30363d;
299 --text: #e6edf3;
300 --text-muted: #8b949e;
301 --text-link: #58a6ff;
302 --accent: #1f6feb;
303 --accent-hover: #388bfd;
304 --green: #3fb950;
305 --red: #f85149;
306 --yellow: #d29922;
307 --font-mono: 'SF Mono', 'Cascadia Code', 'Fira Code', monospace;
308 --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
309 --radius: 6px;
310 }
311
6fc53bdClaude312 :root[data-theme='light'] {
313 --bg: #ffffff;
314 --bg-secondary: #f6f8fa;
315 --bg-tertiary: #eaeef2;
316 --border: #d0d7de;
317 --text: #1f2328;
318 --text-muted: #656d76;
319 --text-link: #0969da;
320 --accent: #0969da;
321 --accent-hover: #0550ae;
322 --green: #1a7f37;
323 --red: #cf222e;
324 --yellow: #9a6700;
325 }
326
327 /* Theme toggle — show the icon for the *opposite* theme so users see what they'll switch to. */
328 .nav-theme { display: inline-flex; align-items: center; font-size: 16px; line-height: 1; }
329 :root[data-theme='dark'] .theme-icon-dark { display: none; }
330 :root[data-theme='light'] .theme-icon-light { display: none; }
331
fc1817aClaude332 * { margin: 0; padding: 0; box-sizing: border-box; }
333
334 body {
335 font-family: var(--font-sans);
336 background: var(--bg);
337 color: var(--text);
338 line-height: 1.5;
339 min-height: 100vh;
340 display: flex;
341 flex-direction: column;
342 }
343
344 a { color: var(--text-link); text-decoration: none; }
345 a:hover { text-decoration: underline; }
346
4a52a98Claude347 /* Pre-launch banner - always visible, not dismissible. Amber tone, readable
348 on both light and dark themes via the shared --yellow token. */
349 .prelaunch-banner {
350 background: rgba(210, 153, 34, 0.15);
351 border-bottom: 1px solid var(--yellow);
352 color: var(--yellow);
353 padding: 8px 24px;
354 font-size: 13px;
355 font-weight: 500;
356 text-align: center;
357 line-height: 1.4;
358 }
359
fc1817aClaude360 header {
361 border-bottom: 1px solid var(--border);
362 padding: 12px 24px;
363 background: var(--bg-secondary);
364 }
365
06d5ffeClaude366 header nav {
367 display: flex;
368 align-items: center;
369 justify-content: space-between;
370 max-width: 1200px;
371 margin: 0 auto;
372 }
fc1817aClaude373 .logo { font-size: 20px; font-weight: 700; color: var(--text); }
374 .logo:hover { text-decoration: none; color: var(--text-link); }
375
06d5ffeClaude376 .nav-right { display: flex; align-items: center; gap: 16px; }
377 .nav-link { color: var(--text-muted); font-size: 14px; }
378 .nav-link:hover { color: var(--text); text-decoration: none; }
379 .nav-user { color: var(--text); font-weight: 600; font-size: 14px; }
380 .nav-user:hover { color: var(--text-link); text-decoration: none; }
381
fc1817aClaude382 main { max-width: 1200px; margin: 0 auto; padding: 24px; flex: 1; width: 100%; }
383
384 footer {
385 border-top: 1px solid var(--border);
386 padding: 16px 24px;
387 text-align: center;
388 color: var(--text-muted);
389 font-size: 13px;
390 }
391
06d5ffeClaude392 /* Buttons */
393 .btn {
394 display: inline-flex;
395 align-items: center;
396 gap: 6px;
397 padding: 8px 16px;
398 border-radius: var(--radius);
399 font-size: 14px;
400 font-weight: 500;
401 border: 1px solid var(--border);
402 background: var(--bg-tertiary);
403 color: var(--text);
404 cursor: pointer;
405 text-decoration: none;
406 line-height: 1.4;
407 }
408 .btn:hover { background: var(--border); text-decoration: none; }
409 .btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
410 .btn-primary:hover { background: var(--accent-hover); }
411 .btn-danger { background: transparent; border-color: var(--red); color: var(--red); }
412 .btn-danger:hover { background: rgba(248, 81, 73, 0.15); }
413 .btn-sm { padding: 4px 12px; font-size: 13px; }
414
415 /* Forms */
416 .form-group { margin-bottom: 16px; }
417 .form-group label { display: block; font-size: 14px; font-weight: 500; margin-bottom: 6px; color: var(--text); }
418 .form-group input, .form-group textarea, .form-group select {
419 width: 100%;
420 padding: 8px 12px;
421 background: var(--bg);
422 border: 1px solid var(--border);
423 border-radius: var(--radius);
424 color: var(--text);
425 font-size: 14px;
426 font-family: var(--font-sans);
427 }
428 .form-group input:focus, .form-group textarea:focus, .form-group select:focus {
429 outline: none;
430 border-color: var(--accent);
431 box-shadow: 0 0 0 2px rgba(31, 111, 235, 0.3);
432 }
433 .input-disabled { opacity: 0.5; cursor: not-allowed; }
434
435 /* Auth */
436 .auth-container { max-width: 400px; margin: 40px auto; }
437 .auth-container h2 { margin-bottom: 20px; font-size: 24px; }
438 .auth-error {
439 background: rgba(248, 81, 73, 0.1);
440 border: 1px solid var(--red);
441 color: var(--red);
442 padding: 8px 12px;
443 border-radius: var(--radius);
444 margin-bottom: 16px;
445 font-size: 14px;
446 }
447 .auth-success {
448 background: rgba(63, 185, 80, 0.1);
449 border: 1px solid var(--green);
450 color: var(--green);
451 padding: 8px 12px;
452 border-radius: var(--radius);
453 margin-bottom: 16px;
454 font-size: 14px;
455 }
456 .auth-switch { margin-top: 16px; font-size: 14px; color: var(--text-muted); }
457
458 /* Settings */
459 .settings-container { max-width: 600px; }
460 .settings-container h2 { margin-bottom: 20px; font-size: 24px; }
461 .settings-container h3 { font-size: 18px; margin-bottom: 12px; }
462 .ssh-keys-list { margin-bottom: 24px; }
463 .ssh-key-item {
464 display: flex;
465 justify-content: space-between;
466 align-items: center;
467 padding: 12px 16px;
468 border: 1px solid var(--border);
469 border-radius: var(--radius);
470 margin-bottom: 8px;
471 background: var(--bg-secondary);
472 }
473 .ssh-key-meta { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
474 .ssh-key-meta code { font-size: 11px; background: var(--bg-tertiary); padding: 1px 6px; border-radius: 3px; margin-right: 8px; }
475
476 /* Repo header */
fc1817aClaude477 .repo-header {
478 display: flex;
479 align-items: center;
480 gap: 8px;
481 margin-bottom: 16px;
482 font-size: 20px;
483 }
484 .repo-header .owner { color: var(--text-link); }
485 .repo-header .separator { color: var(--text-muted); }
486 .repo-header .name { color: var(--text-link); font-weight: 600; }
06d5ffeClaude487 .repo-header-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }
fc1817aClaude488
489 .repo-nav {
490 display: flex;
491 gap: 0;
492 border-bottom: 1px solid var(--border);
493 margin-bottom: 20px;
494 }
495 .repo-nav a {
496 padding: 8px 16px;
497 color: var(--text-muted);
498 border-bottom: 2px solid transparent;
499 font-size: 14px;
500 }
501 .repo-nav a:hover { text-decoration: none; color: var(--text); }
502 .repo-nav a.active { color: var(--text); border-bottom-color: var(--accent); }
503
504 .breadcrumb { display: flex; gap: 4px; align-items: center; margin-bottom: 16px; color: var(--text-muted); font-size: 14px; }
505 .breadcrumb a { color: var(--text-link); }
506
507 .file-table { width: 100%; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
508 .file-table tr { border-bottom: 1px solid var(--border); }
509 .file-table tr:last-child { border-bottom: none; }
510 .file-table td { padding: 8px 16px; font-size: 14px; }
511 .file-table tr:hover { background: var(--bg-secondary); }
512 .file-icon { width: 20px; color: var(--text-muted); }
513 .file-name a { color: var(--text); }
514 .file-name a:hover { color: var(--text-link); text-decoration: underline; }
515
516 .blob-view {
517 border: 1px solid var(--border);
518 border-radius: var(--radius);
519 overflow: hidden;
520 }
521 .blob-header {
522 background: var(--bg-secondary);
523 padding: 8px 16px;
524 border-bottom: 1px solid var(--border);
525 font-size: 13px;
526 color: var(--text-muted);
06d5ffeClaude527 display: flex;
528 justify-content: space-between;
529 align-items: center;
fc1817aClaude530 }
531 .blob-code {
532 overflow-x: auto;
533 font-family: var(--font-mono);
534 font-size: 13px;
535 line-height: 1.6;
536 }
537 .blob-code table { width: 100%; border-collapse: collapse; }
538 .blob-code .line-num {
539 width: 1%;
540 min-width: 50px;
541 padding: 0 12px;
542 text-align: right;
543 color: var(--text-muted);
544 user-select: none;
545 white-space: nowrap;
546 border-right: 1px solid var(--border);
547 }
548 .blob-code .line-content { padding: 0 12px; white-space: pre; }
549
550 .commit-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
551 .commit-item {
552 display: flex;
553 justify-content: space-between;
554 align-items: center;
555 padding: 12px 16px;
556 border-bottom: 1px solid var(--border);
557 }
558 .commit-item:last-child { border-bottom: none; }
559 .commit-item:hover { background: var(--bg-secondary); }
560 .commit-message { font-size: 14px; font-weight: 500; }
561 .commit-meta { font-size: 12px; color: var(--text-muted); }
562 .commit-sha {
563 font-family: var(--font-mono);
564 font-size: 12px;
565 padding: 2px 8px;
566 background: var(--bg-tertiary);
567 border: 1px solid var(--border);
568 border-radius: var(--radius);
569 color: var(--text-link);
570 }
571
572 .diff-view { margin-top: 16px; }
573 .diff-file {
574 border: 1px solid var(--border);
575 border-radius: var(--radius);
576 margin-bottom: 16px;
577 overflow: hidden;
578 }
579 .diff-file-header {
580 background: var(--bg-secondary);
581 padding: 8px 16px;
582 border-bottom: 1px solid var(--border);
583 font-family: var(--font-mono);
584 font-size: 13px;
585 }
586 .diff-content {
587 overflow-x: auto;
588 font-family: var(--font-mono);
589 font-size: 13px;
590 line-height: 1.6;
591 }
592 .diff-content .line-add { background: rgba(63, 185, 80, 0.15); color: var(--green); }
593 .diff-content .line-del { background: rgba(248, 81, 73, 0.1); color: var(--red); }
594 .diff-content .line-hunk { background: rgba(56, 139, 253, 0.1); color: var(--text-link); }
595 .diff-content .line { padding: 0 12px; white-space: pre; display: block; }
596
597 .stat-add { color: var(--green); font-weight: 600; }
598 .stat-del { color: var(--red); font-weight: 600; }
599
600 .empty-state {
601 text-align: center;
602 padding: 60px 20px;
603 color: var(--text-muted);
604 }
605 .empty-state h2 { font-size: 24px; margin-bottom: 8px; color: var(--text); }
606 .empty-state pre {
607 text-align: left;
608 display: inline-block;
609 background: var(--bg-secondary);
610 padding: 16px 24px;
611 border-radius: var(--radius);
612 border: 1px solid var(--border);
613 font-family: var(--font-mono);
614 font-size: 13px;
615 margin-top: 16px;
616 line-height: 1.8;
617 }
618
619 .badge {
620 display: inline-block;
621 padding: 2px 8px;
622 border-radius: 12px;
623 font-size: 12px;
624 font-weight: 500;
625 background: var(--bg-tertiary);
626 border: 1px solid var(--border);
627 color: var(--text-muted);
628 }
629
630 .branch-selector {
631 display: inline-flex;
632 align-items: center;
633 gap: 4px;
634 padding: 4px 12px;
635 background: var(--bg-tertiary);
636 border: 1px solid var(--border);
637 border-radius: var(--radius);
638 font-size: 13px;
639 color: var(--text);
640 margin-bottom: 12px;
06d5ffeClaude641 position: relative;
fc1817aClaude642 }
643
06d5ffeClaude644 .branch-dropdown {
645 position: relative;
646 display: inline-block;
647 margin-bottom: 12px;
648 }
649 .branch-dropdown-content {
650 display: none;
651 position: absolute;
652 top: 100%;
653 left: 0;
654 z-index: 10;
655 min-width: 200px;
656 background: var(--bg-secondary);
657 border: 1px solid var(--border);
658 border-radius: var(--radius);
659 margin-top: 4px;
660 box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
661 }
662 .branch-dropdown:hover .branch-dropdown-content,
663 .branch-dropdown:focus-within .branch-dropdown-content { display: block; }
664 .branch-dropdown-content a {
665 display: block;
666 padding: 8px 12px;
667 font-size: 13px;
668 color: var(--text);
669 border-bottom: 1px solid var(--border);
670 }
671 .branch-dropdown-content a:last-child { border-bottom: none; }
672 .branch-dropdown-content a:hover { background: var(--bg-tertiary); text-decoration: none; }
673 .branch-dropdown-content a.active-branch { color: var(--text-link); font-weight: 600; }
674
fc1817aClaude675 .card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; }
676 .card {
677 border: 1px solid var(--border);
678 border-radius: var(--radius);
679 padding: 16px;
680 background: var(--bg-secondary);
06d5ffeClaude681 transition: border-color 0.15s;
fc1817aClaude682 }
06d5ffeClaude683 .card:hover { border-color: var(--text-muted); }
fc1817aClaude684 .card h3 { font-size: 16px; margin-bottom: 4px; }
06d5ffeClaude685 .card h3 a { color: var(--text-link); }
fc1817aClaude686 .card p { font-size: 13px; color: var(--text-muted); }
06d5ffeClaude687 .card-meta { display: flex; gap: 16px; margin-top: 12px; font-size: 12px; color: var(--text-muted); }
688 .card-meta span { display: flex; align-items: center; gap: 4px; }
689
690 .star-btn {
691 display: inline-flex;
692 align-items: center;
693 gap: 6px;
694 padding: 4px 12px;
695 background: var(--bg-tertiary);
696 border: 1px solid var(--border);
697 border-radius: var(--radius);
698 color: var(--text);
699 font-size: 13px;
700 cursor: pointer;
701 }
702 .star-btn:hover { background: var(--border); text-decoration: none; }
703 .star-btn.starred { color: var(--yellow); border-color: var(--yellow); }
704
705 .user-profile {
706 display: flex;
707 gap: 32px;
708 margin-bottom: 32px;
709 }
710 .user-avatar {
711 width: 96px;
712 height: 96px;
713 border-radius: 50%;
714 background: var(--bg-tertiary);
715 border: 1px solid var(--border);
716 display: flex;
717 align-items: center;
718 justify-content: center;
719 font-size: 40px;
720 color: var(--text-muted);
721 flex-shrink: 0;
722 }
723 .user-info h2 { font-size: 24px; margin-bottom: 2px; }
724 .user-info .username { font-size: 16px; color: var(--text-muted); }
725 .user-info .bio { font-size: 14px; color: var(--text-muted); margin-top: 8px; }
726
727 .new-repo-form { max-width: 600px; }
728 .new-repo-form h2 { margin-bottom: 20px; }
729 .visibility-options { display: flex; gap: 12px; margin-bottom: 16px; }
730 .visibility-option {
731 flex: 1;
732 padding: 12px;
733 border: 1px solid var(--border);
734 border-radius: var(--radius);
735 background: var(--bg-secondary);
736 cursor: pointer;
737 text-align: center;
738 }
739 .visibility-option:has(input:checked) { border-color: var(--accent); background: rgba(31, 111, 235, 0.1); }
740 .visibility-option input { display: none; }
741 .visibility-option .vis-label { font-size: 14px; font-weight: 500; }
742 .visibility-option .vis-desc { font-size: 12px; color: var(--text-muted); }
79136bbClaude743
744 /* Issues */
745 .issue-tabs { display: flex; gap: 16px; }
746 .issue-tabs a { color: var(--text-muted); font-size: 14px; font-weight: 500; }
747 .issue-tabs a:hover { color: var(--text); text-decoration: none; }
748 .issue-tabs a.active { color: var(--text); }
749
750 .issue-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
751 .issue-item {
752 display: flex;
753 gap: 12px;
754 align-items: flex-start;
755 padding: 12px 16px;
756 border-bottom: 1px solid var(--border);
757 }
758 .issue-item:last-child { border-bottom: none; }
759 .issue-item:hover { background: var(--bg-secondary); }
760 .issue-state-icon { font-size: 16px; padding-top: 2px; }
761 .state-open { color: var(--green); }
762 .state-closed { color: #986ee2; }
763 .issue-title { font-size: 15px; font-weight: 600; }
764 .issue-title a { color: var(--text); }
765 .issue-title a:hover { color: var(--text-link); }
766 .issue-meta { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
767
768 .issue-badge {
769 display: inline-flex;
770 align-items: center;
771 gap: 4px;
772 padding: 4px 12px;
773 border-radius: 20px;
774 font-size: 13px;
775 font-weight: 500;
776 }
777 .badge-open { background: rgba(63, 185, 80, 0.15); color: var(--green); border: 1px solid var(--green); }
778 .badge-closed { background: rgba(152, 110, 226, 0.15); color: #986ee2; border: 1px solid #986ee2; }
0074234Claude779 .badge-merged { background: rgba(152, 110, 226, 0.15); color: #986ee2; border: 1px solid #986ee2; }
780 .state-merged { color: #986ee2; }
781 .ai-review { border-color: var(--accent); }
79136bbClaude782
783 .issue-detail { max-width: 900px; }
784 .issue-comment-box {
785 border: 1px solid var(--border);
786 border-radius: var(--radius);
787 margin-bottom: 16px;
788 overflow: hidden;
789 }
790 .comment-header {
791 background: var(--bg-secondary);
792 padding: 8px 16px;
793 border-bottom: 1px solid var(--border);
794 font-size: 13px;
795 color: var(--text-muted);
796 }
797
798 /* Search */
799 .search-results .diff-file { margin-bottom: 12px; }
16b325cClaude800
801 /* Timeline */
802 .timeline { position: relative; padding-left: 24px; }
803 .timeline::before {
804 content: '';
805 position: absolute;
806 left: 4px;
807 top: 8px;
808 bottom: 8px;
809 width: 2px;
810 background: var(--border);
811 }
812 .timeline-item {
813 position: relative;
814 padding-bottom: 16px;
815 }
816 .timeline-dot {
817 position: absolute;
818 left: -24px;
819 top: 6px;
820 width: 10px;
821 height: 10px;
822 border-radius: 50%;
823 background: var(--text-muted);
824 border: 2px solid var(--bg);
825 }
826 .timeline-content {
827 background: var(--bg-secondary);
828 border: 1px solid var(--border);
829 border-radius: var(--radius);
830 padding: 12px 16px;
831 }
f1ab587Claude832
833 /* Toggle switch */
834 .toggle-switch {
835 position: relative;
836 display: inline-block;
837 width: 44px;
838 height: 24px;
839 flex-shrink: 0;
840 margin-left: 16px;
841 }
842 .toggle-switch input { opacity: 0; width: 0; height: 0; }
843 .toggle-slider {
844 position: absolute;
845 cursor: pointer;
846 top: 0; left: 0; right: 0; bottom: 0;
847 background: var(--bg-tertiary);
848 border: 1px solid var(--border);
849 border-radius: 12px;
850 transition: 0.2s;
851 }
852 .toggle-slider::before {
853 content: '';
854 position: absolute;
855 height: 18px;
856 width: 18px;
857 left: 2px;
858 bottom: 2px;
859 background: var(--text-muted);
860 border-radius: 50%;
861 transition: 0.2s;
862 }
863 .toggle-switch input:checked + .toggle-slider {
864 background: var(--accent);
865 border-color: var(--accent);
866 }
867 .toggle-switch input:checked + .toggle-slider::before {
868 transform: translateX(20px);
869 background: #fff;
870 }
fc1817aClaude871`;