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

layout.tsx

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

layout.tsxBlame891 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>
699e5c7Claude103 {/* Block I4 — Command palette shell (hidden by default) */}
104 <div
105 id="cmdk-backdrop"
106 style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.5);z-index:9998"
107 />
108 <div
109 id="cmdk-panel"
110 style="display:none;position:fixed;top:10%;left:50%;transform:translateX(-50%);width:min(560px,92vw);background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius);box-shadow:0 12px 32px rgba(0,0,0,0.4);z-index:9999;overflow:hidden"
111 >
112 <input
113 id="cmdk-input"
114 type="text"
115 placeholder="Type a command..."
116 aria-label="Command palette"
117 style="width:100%;padding:12px 16px;background:transparent;color:var(--text);border:0;border-bottom:1px solid var(--border);outline:none;font-size:14px"
118 />
119 <div id="cmdk-list" style="max-height:60vh;overflow-y:auto" />
120 </div>
45e31d0Claude121 <script>{clientJs}</script>
699e5c7Claude122 <script>{pwaRegisterScript}</script>
123 <script>{navScript}</script>
fc1817aClaude124 </body>
125 </html>
126 );
127};
128
6fc53bdClaude129// Runs before paint — reads the theme cookie and flips data-theme so there's
130// no dark-to-light flash on load. SSR default is dark.
131const themeInitScript = `
132 (function(){
133 try {
134 var m = document.cookie.match(/(?:^|; )theme=([^;]+)/);
135 var t = m ? decodeURIComponent(m[1]) : 'dark';
136 if (t !== 'light' && t !== 'dark') t = 'dark';
137 document.documentElement.setAttribute('data-theme', t);
138 } catch(_){}
139 })();
140`;
141
eae38d1Claude142// Block G1 — register service worker for offline / install support.
143// Kept inline (and tiny) so we don't block first paint.
144const pwaRegisterScript = `
145 if ('serviceWorker' in navigator) {
146 window.addEventListener('load', function(){
147 navigator.serviceWorker.register('/sw.js').catch(function(){});
148 });
149 }
150`;
151
3ef4c9dClaude152const navScript = `
153 (function(){
154 var chord = null;
155 var chordTimer = null;
156 function isTyping(t){
157 t = t || {};
158 var tag = (t.tagName || '').toLowerCase();
159 return tag === 'input' || tag === 'textarea' || t.isContentEditable;
160 }
71cd5ecClaude161
162 // ---------- Block I4 — Command palette ----------
163 var COMMANDS = [
164 { label: 'Go to Dashboard', href: '/dashboard', kw: 'home' },
165 { label: 'Go to Explore', href: '/explore', kw: 'browse discover' },
166 { label: 'Go to Notifications', href: '/notifications', kw: 'inbox' },
167 { label: 'Go to Ask AI', href: '/ask', kw: 'chat assistant' },
168 { label: 'Create new repository', href: '/new', kw: 'add create' },
169 { label: 'Marketplace', href: '/marketplace', kw: 'apps store' },
170 { label: 'Installed apps', href: '/settings/apps', kw: 'my apps' },
171 { label: 'Register new app', href: '/developer/apps-new', kw: 'developer create' },
172 { label: 'Keyboard shortcuts', href: '/shortcuts', kw: 'help keys' },
173 { label: 'Settings (profile)', href: '/settings', kw: 'account' },
174 { label: '2FA settings', href: '/settings/2fa', kw: 'two factor security' },
175 { label: 'Passkeys settings', href: '/settings/passkeys', kw: 'webauthn' },
176 { label: 'Personal access tokens', href: '/settings/tokens', kw: 'pat api' },
177 { label: 'Billing + quotas', href: '/settings/billing', kw: 'plans money' },
178 { label: 'Audit log (personal)', href: '/settings/audit', kw: 'history' },
179 { label: 'Gists', href: '/gists', kw: 'snippets' },
180 { label: 'GraphQL explorer', href: '/api/graphql', kw: 'api query' },
181 { label: 'Admin dashboard', href: '/admin', kw: 'superuser' },
182 { label: 'Toggle theme', href: '/theme/toggle', kw: 'dark light mode' }
183 ];
184
185 function fuzzyMatch(item, q){
186 if (!q) return true;
187 var hay = (item.label + ' ' + (item.kw||'') + ' ' + item.href).toLowerCase();
188 q = q.toLowerCase();
189 var qi = 0;
190 for (var i = 0; i < hay.length && qi < q.length; i++) {
191 if (hay[i] === q[qi]) qi++;
192 }
193 return qi === q.length;
194 }
195
196 var backdrop, panel, input, list, selected = 0, filtered = COMMANDS.slice();
197
198 function render(){
199 if (!list) return;
200 var html = '';
201 for (var i = 0; i < filtered.length; i++) {
202 var item = filtered[i];
203 var cls = i === selected ? 'cmdk-item cmdk-active' : 'cmdk-item';
204 var bg = i === selected ? 'background:var(--bg);' : '';
205 html += '<div class="' + cls + '" data-idx="' + i + '" data-href="' + item.href + '"' +
206 ' style="padding:10px 16px;cursor:pointer;border-bottom:1px solid var(--border);' + bg + '">' +
207 '<div>' + item.label + '</div>' +
208 '<div style="font-size:11px;color:var(--text-muted)">' + item.href + '</div>' +
209 '</div>';
210 }
211 if (filtered.length === 0) {
212 html = '<div style="padding:16px;color:var(--text-muted);text-align:center">No matches.</div>';
213 }
214 list.innerHTML = html;
215 }
216
217 function openPalette(){
218 backdrop = document.getElementById('cmdk-backdrop');
219 panel = document.getElementById('cmdk-panel');
220 input = document.getElementById('cmdk-input');
221 list = document.getElementById('cmdk-list');
222 if (!backdrop || !panel) return;
223 backdrop.style.display = 'block';
224 panel.style.display = 'block';
225 input.value = '';
226 selected = 0;
227 filtered = COMMANDS.slice();
228 render();
229 input.focus();
230 }
231 function closePalette(){
232 if (backdrop) backdrop.style.display = 'none';
233 if (panel) panel.style.display = 'none';
234 }
235 function go(href){ closePalette(); window.location.href = href; }
236
237 document.addEventListener('click', function(e){
238 var t = e.target;
239 if (t && t.id === 'cmdk-backdrop') { closePalette(); return; }
240 var item = t && t.closest && t.closest('.cmdk-item');
241 if (item) { go(item.getAttribute('data-href')); }
242 });
243
244 document.addEventListener('input', function(e){
245 if (e.target && e.target.id === 'cmdk-input') {
246 var q = e.target.value;
247 filtered = COMMANDS.filter(function(c){ return fuzzyMatch(c, q); });
248 selected = 0;
249 render();
250 }
251 });
252
3ef4c9dClaude253 document.addEventListener('keydown', function(e){
71cd5ecClaude254 // Palette-scoped keys take priority when open
255 if (panel && panel.style.display === 'block') {
256 if (e.key === 'Escape') { e.preventDefault(); closePalette(); return; }
257 if (e.key === 'ArrowDown') {
258 e.preventDefault();
259 selected = Math.min(filtered.length - 1, selected + 1);
260 render();
261 return;
262 }
263 if (e.key === 'ArrowUp') {
264 e.preventDefault();
265 selected = Math.max(0, selected - 1);
266 render();
267 return;
268 }
269 if (e.key === 'Enter') {
270 e.preventDefault();
271 var item = filtered[selected];
272 if (item) go(item.href);
273 return;
274 }
275 return;
276 }
277
3ef4c9dClaude278 if (isTyping(e.target)) return;
279 // Single key shortcuts
280 if (e.key === '/') {
281 var el = document.querySelector('.nav-search input');
282 if (el) { e.preventDefault(); el.focus(); return; }
283 }
284 if ((e.metaKey||e.ctrlKey) && e.key.toLowerCase() === 'k') {
71cd5ecClaude285 e.preventDefault();
286 openPalette();
287 return;
3ef4c9dClaude288 }
289 if (e.key === '?' && !e.ctrlKey && !e.metaKey) {
290 e.preventDefault(); window.location.href = '/shortcuts'; return;
291 }
292 if (e.key === 'n' && !e.ctrlKey && !e.metaKey) {
293 e.preventDefault(); window.location.href = '/new'; return;
294 }
295 // "g" chord
296 if (e.key === 'g' && !e.ctrlKey && !e.metaKey) {
297 chord = 'g';
298 clearTimeout(chordTimer);
299 chordTimer = setTimeout(function(){ chord = null; }, 1200);
300 return;
301 }
302 if (chord === 'g') {
303 if (e.key === 'd') { e.preventDefault(); window.location.href = '/dashboard'; }
304 else if (e.key === 'n') { e.preventDefault(); window.location.href = '/notifications'; }
305 else if (e.key === 'e') { e.preventDefault(); window.location.href = '/explore'; }
306 else if (e.key === 'a') { e.preventDefault(); window.location.href = '/ask'; }
307 chord = null;
308 }
309 });
310 })();
311`;
312
fc1817aClaude313const css = `
6fc53bdClaude314 :root, :root[data-theme='dark'] {
fc1817aClaude315 --bg: #0d1117;
316 --bg-secondary: #161b22;
317 --bg-tertiary: #21262d;
318 --border: #30363d;
319 --text: #e6edf3;
320 --text-muted: #8b949e;
321 --text-link: #58a6ff;
322 --accent: #1f6feb;
323 --accent-hover: #388bfd;
324 --green: #3fb950;
325 --red: #f85149;
326 --yellow: #d29922;
327 --font-mono: 'SF Mono', 'Cascadia Code', 'Fira Code', monospace;
328 --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
329 --radius: 6px;
330 }
331
6fc53bdClaude332 :root[data-theme='light'] {
333 --bg: #ffffff;
334 --bg-secondary: #f6f8fa;
335 --bg-tertiary: #eaeef2;
336 --border: #d0d7de;
337 --text: #1f2328;
338 --text-muted: #656d76;
339 --text-link: #0969da;
340 --accent: #0969da;
341 --accent-hover: #0550ae;
342 --green: #1a7f37;
343 --red: #cf222e;
344 --yellow: #9a6700;
345 }
346
347 /* Theme toggle — show the icon for the *opposite* theme so users see what they'll switch to. */
348 .nav-theme { display: inline-flex; align-items: center; font-size: 16px; line-height: 1; }
349 :root[data-theme='dark'] .theme-icon-dark { display: none; }
350 :root[data-theme='light'] .theme-icon-light { display: none; }
351
fc1817aClaude352 * { margin: 0; padding: 0; box-sizing: border-box; }
353
354 body {
355 font-family: var(--font-sans);
356 background: var(--bg);
357 color: var(--text);
358 line-height: 1.5;
359 min-height: 100vh;
360 display: flex;
361 flex-direction: column;
362 }
363
364 a { color: var(--text-link); text-decoration: none; }
365 a:hover { text-decoration: underline; }
366
4a52a98Claude367 /* Pre-launch banner - always visible, not dismissible. Amber tone, readable
368 on both light and dark themes via the shared --yellow token. */
369 .prelaunch-banner {
370 background: rgba(210, 153, 34, 0.15);
371 border-bottom: 1px solid var(--yellow);
372 color: var(--yellow);
373 padding: 8px 24px;
374 font-size: 13px;
375 font-weight: 500;
376 text-align: center;
377 line-height: 1.4;
378 }
379
fc1817aClaude380 header {
381 border-bottom: 1px solid var(--border);
382 padding: 12px 24px;
383 background: var(--bg-secondary);
384 }
385
06d5ffeClaude386 header nav {
387 display: flex;
388 align-items: center;
389 justify-content: space-between;
390 max-width: 1200px;
391 margin: 0 auto;
392 }
fc1817aClaude393 .logo { font-size: 20px; font-weight: 700; color: var(--text); }
394 .logo:hover { text-decoration: none; color: var(--text-link); }
395
06d5ffeClaude396 .nav-right { display: flex; align-items: center; gap: 16px; }
397 .nav-link { color: var(--text-muted); font-size: 14px; }
398 .nav-link:hover { color: var(--text); text-decoration: none; }
399 .nav-user { color: var(--text); font-weight: 600; font-size: 14px; }
400 .nav-user:hover { color: var(--text-link); text-decoration: none; }
401
fc1817aClaude402 main { max-width: 1200px; margin: 0 auto; padding: 24px; flex: 1; width: 100%; }
403
404 footer {
405 border-top: 1px solid var(--border);
406 padding: 16px 24px;
407 text-align: center;
408 color: var(--text-muted);
409 font-size: 13px;
410 }
411
06d5ffeClaude412 /* Buttons */
413 .btn {
414 display: inline-flex;
415 align-items: center;
416 gap: 6px;
417 padding: 8px 16px;
418 border-radius: var(--radius);
419 font-size: 14px;
420 font-weight: 500;
421 border: 1px solid var(--border);
422 background: var(--bg-tertiary);
423 color: var(--text);
424 cursor: pointer;
425 text-decoration: none;
426 line-height: 1.4;
427 }
428 .btn:hover { background: var(--border); text-decoration: none; }
429 .btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
430 .btn-primary:hover { background: var(--accent-hover); }
431 .btn-danger { background: transparent; border-color: var(--red); color: var(--red); }
432 .btn-danger:hover { background: rgba(248, 81, 73, 0.15); }
433 .btn-sm { padding: 4px 12px; font-size: 13px; }
434
435 /* Forms */
436 .form-group { margin-bottom: 16px; }
437 .form-group label { display: block; font-size: 14px; font-weight: 500; margin-bottom: 6px; color: var(--text); }
438 .form-group input, .form-group textarea, .form-group select {
439 width: 100%;
440 padding: 8px 12px;
441 background: var(--bg);
442 border: 1px solid var(--border);
443 border-radius: var(--radius);
444 color: var(--text);
445 font-size: 14px;
446 font-family: var(--font-sans);
447 }
448 .form-group input:focus, .form-group textarea:focus, .form-group select:focus {
449 outline: none;
450 border-color: var(--accent);
451 box-shadow: 0 0 0 2px rgba(31, 111, 235, 0.3);
452 }
453 .input-disabled { opacity: 0.5; cursor: not-allowed; }
454
455 /* Auth */
456 .auth-container { max-width: 400px; margin: 40px auto; }
457 .auth-container h2 { margin-bottom: 20px; font-size: 24px; }
458 .auth-error {
459 background: rgba(248, 81, 73, 0.1);
460 border: 1px solid var(--red);
461 color: var(--red);
462 padding: 8px 12px;
463 border-radius: var(--radius);
464 margin-bottom: 16px;
465 font-size: 14px;
466 }
467 .auth-success {
468 background: rgba(63, 185, 80, 0.1);
469 border: 1px solid var(--green);
470 color: var(--green);
471 padding: 8px 12px;
472 border-radius: var(--radius);
473 margin-bottom: 16px;
474 font-size: 14px;
475 }
476 .auth-switch { margin-top: 16px; font-size: 14px; color: var(--text-muted); }
477
478 /* Settings */
479 .settings-container { max-width: 600px; }
480 .settings-container h2 { margin-bottom: 20px; font-size: 24px; }
481 .settings-container h3 { font-size: 18px; margin-bottom: 12px; }
482 .ssh-keys-list { margin-bottom: 24px; }
483 .ssh-key-item {
484 display: flex;
485 justify-content: space-between;
486 align-items: center;
487 padding: 12px 16px;
488 border: 1px solid var(--border);
489 border-radius: var(--radius);
490 margin-bottom: 8px;
491 background: var(--bg-secondary);
492 }
493 .ssh-key-meta { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
494 .ssh-key-meta code { font-size: 11px; background: var(--bg-tertiary); padding: 1px 6px; border-radius: 3px; margin-right: 8px; }
495
496 /* Repo header */
fc1817aClaude497 .repo-header {
498 display: flex;
499 align-items: center;
500 gap: 8px;
501 margin-bottom: 16px;
502 font-size: 20px;
503 }
504 .repo-header .owner { color: var(--text-link); }
505 .repo-header .separator { color: var(--text-muted); }
506 .repo-header .name { color: var(--text-link); font-weight: 600; }
06d5ffeClaude507 .repo-header-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }
fc1817aClaude508
509 .repo-nav {
510 display: flex;
511 gap: 0;
512 border-bottom: 1px solid var(--border);
513 margin-bottom: 20px;
514 }
515 .repo-nav a {
516 padding: 8px 16px;
517 color: var(--text-muted);
518 border-bottom: 2px solid transparent;
519 font-size: 14px;
520 }
521 .repo-nav a:hover { text-decoration: none; color: var(--text); }
522 .repo-nav a.active { color: var(--text); border-bottom-color: var(--accent); }
523
524 .breadcrumb { display: flex; gap: 4px; align-items: center; margin-bottom: 16px; color: var(--text-muted); font-size: 14px; }
525 .breadcrumb a { color: var(--text-link); }
526
527 .file-table { width: 100%; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
528 .file-table tr { border-bottom: 1px solid var(--border); }
529 .file-table tr:last-child { border-bottom: none; }
530 .file-table td { padding: 8px 16px; font-size: 14px; }
531 .file-table tr:hover { background: var(--bg-secondary); }
532 .file-icon { width: 20px; color: var(--text-muted); }
533 .file-name a { color: var(--text); }
534 .file-name a:hover { color: var(--text-link); text-decoration: underline; }
535
536 .blob-view {
537 border: 1px solid var(--border);
538 border-radius: var(--radius);
539 overflow: hidden;
540 }
541 .blob-header {
542 background: var(--bg-secondary);
543 padding: 8px 16px;
544 border-bottom: 1px solid var(--border);
545 font-size: 13px;
546 color: var(--text-muted);
06d5ffeClaude547 display: flex;
548 justify-content: space-between;
549 align-items: center;
fc1817aClaude550 }
551 .blob-code {
552 overflow-x: auto;
553 font-family: var(--font-mono);
554 font-size: 13px;
555 line-height: 1.6;
556 }
557 .blob-code table { width: 100%; border-collapse: collapse; }
558 .blob-code .line-num {
559 width: 1%;
560 min-width: 50px;
561 padding: 0 12px;
562 text-align: right;
563 color: var(--text-muted);
564 user-select: none;
565 white-space: nowrap;
566 border-right: 1px solid var(--border);
567 }
568 .blob-code .line-content { padding: 0 12px; white-space: pre; }
569
570 .commit-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
571 .commit-item {
572 display: flex;
573 justify-content: space-between;
574 align-items: center;
575 padding: 12px 16px;
576 border-bottom: 1px solid var(--border);
577 }
578 .commit-item:last-child { border-bottom: none; }
579 .commit-item:hover { background: var(--bg-secondary); }
580 .commit-message { font-size: 14px; font-weight: 500; }
581 .commit-meta { font-size: 12px; color: var(--text-muted); }
582 .commit-sha {
583 font-family: var(--font-mono);
584 font-size: 12px;
585 padding: 2px 8px;
586 background: var(--bg-tertiary);
587 border: 1px solid var(--border);
588 border-radius: var(--radius);
589 color: var(--text-link);
590 }
591
592 .diff-view { margin-top: 16px; }
593 .diff-file {
594 border: 1px solid var(--border);
595 border-radius: var(--radius);
596 margin-bottom: 16px;
597 overflow: hidden;
598 }
599 .diff-file-header {
600 background: var(--bg-secondary);
601 padding: 8px 16px;
602 border-bottom: 1px solid var(--border);
603 font-family: var(--font-mono);
604 font-size: 13px;
605 }
606 .diff-content {
607 overflow-x: auto;
608 font-family: var(--font-mono);
609 font-size: 13px;
610 line-height: 1.6;
611 }
612 .diff-content .line-add { background: rgba(63, 185, 80, 0.15); color: var(--green); }
613 .diff-content .line-del { background: rgba(248, 81, 73, 0.1); color: var(--red); }
614 .diff-content .line-hunk { background: rgba(56, 139, 253, 0.1); color: var(--text-link); }
615 .diff-content .line { padding: 0 12px; white-space: pre; display: block; }
616
617 .stat-add { color: var(--green); font-weight: 600; }
618 .stat-del { color: var(--red); font-weight: 600; }
619
620 .empty-state {
621 text-align: center;
622 padding: 60px 20px;
623 color: var(--text-muted);
624 }
625 .empty-state h2 { font-size: 24px; margin-bottom: 8px; color: var(--text); }
626 .empty-state pre {
627 text-align: left;
628 display: inline-block;
629 background: var(--bg-secondary);
630 padding: 16px 24px;
631 border-radius: var(--radius);
632 border: 1px solid var(--border);
633 font-family: var(--font-mono);
634 font-size: 13px;
635 margin-top: 16px;
636 line-height: 1.8;
637 }
638
639 .badge {
640 display: inline-block;
641 padding: 2px 8px;
642 border-radius: 12px;
643 font-size: 12px;
644 font-weight: 500;
645 background: var(--bg-tertiary);
646 border: 1px solid var(--border);
647 color: var(--text-muted);
648 }
649
650 .branch-selector {
651 display: inline-flex;
652 align-items: center;
653 gap: 4px;
654 padding: 4px 12px;
655 background: var(--bg-tertiary);
656 border: 1px solid var(--border);
657 border-radius: var(--radius);
658 font-size: 13px;
659 color: var(--text);
660 margin-bottom: 12px;
06d5ffeClaude661 position: relative;
fc1817aClaude662 }
663
06d5ffeClaude664 .branch-dropdown {
665 position: relative;
666 display: inline-block;
667 margin-bottom: 12px;
668 }
669 .branch-dropdown-content {
670 display: none;
671 position: absolute;
672 top: 100%;
673 left: 0;
674 z-index: 10;
675 min-width: 200px;
676 background: var(--bg-secondary);
677 border: 1px solid var(--border);
678 border-radius: var(--radius);
679 margin-top: 4px;
680 box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
681 }
682 .branch-dropdown:hover .branch-dropdown-content,
683 .branch-dropdown:focus-within .branch-dropdown-content { display: block; }
684 .branch-dropdown-content a {
685 display: block;
686 padding: 8px 12px;
687 font-size: 13px;
688 color: var(--text);
689 border-bottom: 1px solid var(--border);
690 }
691 .branch-dropdown-content a:last-child { border-bottom: none; }
692 .branch-dropdown-content a:hover { background: var(--bg-tertiary); text-decoration: none; }
693 .branch-dropdown-content a.active-branch { color: var(--text-link); font-weight: 600; }
694
fc1817aClaude695 .card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; }
696 .card {
697 border: 1px solid var(--border);
698 border-radius: var(--radius);
699 padding: 16px;
700 background: var(--bg-secondary);
06d5ffeClaude701 transition: border-color 0.15s;
fc1817aClaude702 }
06d5ffeClaude703 .card:hover { border-color: var(--text-muted); }
fc1817aClaude704 .card h3 { font-size: 16px; margin-bottom: 4px; }
06d5ffeClaude705 .card h3 a { color: var(--text-link); }
fc1817aClaude706 .card p { font-size: 13px; color: var(--text-muted); }
06d5ffeClaude707 .card-meta { display: flex; gap: 16px; margin-top: 12px; font-size: 12px; color: var(--text-muted); }
708 .card-meta span { display: flex; align-items: center; gap: 4px; }
709
710 .star-btn {
711 display: inline-flex;
712 align-items: center;
713 gap: 6px;
714 padding: 4px 12px;
715 background: var(--bg-tertiary);
716 border: 1px solid var(--border);
717 border-radius: var(--radius);
718 color: var(--text);
719 font-size: 13px;
720 cursor: pointer;
721 }
722 .star-btn:hover { background: var(--border); text-decoration: none; }
723 .star-btn.starred { color: var(--yellow); border-color: var(--yellow); }
724
725 .user-profile {
726 display: flex;
727 gap: 32px;
728 margin-bottom: 32px;
729 }
730 .user-avatar {
731 width: 96px;
732 height: 96px;
733 border-radius: 50%;
734 background: var(--bg-tertiary);
735 border: 1px solid var(--border);
736 display: flex;
737 align-items: center;
738 justify-content: center;
739 font-size: 40px;
740 color: var(--text-muted);
741 flex-shrink: 0;
742 }
743 .user-info h2 { font-size: 24px; margin-bottom: 2px; }
744 .user-info .username { font-size: 16px; color: var(--text-muted); }
745 .user-info .bio { font-size: 14px; color: var(--text-muted); margin-top: 8px; }
746
747 .new-repo-form { max-width: 600px; }
748 .new-repo-form h2 { margin-bottom: 20px; }
749 .visibility-options { display: flex; gap: 12px; margin-bottom: 16px; }
750 .visibility-option {
751 flex: 1;
752 padding: 12px;
753 border: 1px solid var(--border);
754 border-radius: var(--radius);
755 background: var(--bg-secondary);
756 cursor: pointer;
757 text-align: center;
758 }
759 .visibility-option:has(input:checked) { border-color: var(--accent); background: rgba(31, 111, 235, 0.1); }
760 .visibility-option input { display: none; }
761 .visibility-option .vis-label { font-size: 14px; font-weight: 500; }
762 .visibility-option .vis-desc { font-size: 12px; color: var(--text-muted); }
79136bbClaude763
764 /* Issues */
765 .issue-tabs { display: flex; gap: 16px; }
766 .issue-tabs a { color: var(--text-muted); font-size: 14px; font-weight: 500; }
767 .issue-tabs a:hover { color: var(--text); text-decoration: none; }
768 .issue-tabs a.active { color: var(--text); }
769
770 .issue-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
771 .issue-item {
772 display: flex;
773 gap: 12px;
774 align-items: flex-start;
775 padding: 12px 16px;
776 border-bottom: 1px solid var(--border);
777 }
778 .issue-item:last-child { border-bottom: none; }
779 .issue-item:hover { background: var(--bg-secondary); }
780 .issue-state-icon { font-size: 16px; padding-top: 2px; }
781 .state-open { color: var(--green); }
782 .state-closed { color: #986ee2; }
783 .issue-title { font-size: 15px; font-weight: 600; }
784 .issue-title a { color: var(--text); }
785 .issue-title a:hover { color: var(--text-link); }
786 .issue-meta { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
787
788 .issue-badge {
789 display: inline-flex;
790 align-items: center;
791 gap: 4px;
792 padding: 4px 12px;
793 border-radius: 20px;
794 font-size: 13px;
795 font-weight: 500;
796 }
797 .badge-open { background: rgba(63, 185, 80, 0.15); color: var(--green); border: 1px solid var(--green); }
798 .badge-closed { background: rgba(152, 110, 226, 0.15); color: #986ee2; border: 1px solid #986ee2; }
0074234Claude799 .badge-merged { background: rgba(152, 110, 226, 0.15); color: #986ee2; border: 1px solid #986ee2; }
800 .state-merged { color: #986ee2; }
801 .ai-review { border-color: var(--accent); }
79136bbClaude802
803 .issue-detail { max-width: 900px; }
804 .issue-comment-box {
805 border: 1px solid var(--border);
806 border-radius: var(--radius);
807 margin-bottom: 16px;
808 overflow: hidden;
809 }
810 .comment-header {
811 background: var(--bg-secondary);
812 padding: 8px 16px;
813 border-bottom: 1px solid var(--border);
814 font-size: 13px;
815 color: var(--text-muted);
816 }
817
818 /* Search */
819 .search-results .diff-file { margin-bottom: 12px; }
16b325cClaude820
821 /* Timeline */
822 .timeline { position: relative; padding-left: 24px; }
823 .timeline::before {
824 content: '';
825 position: absolute;
826 left: 4px;
827 top: 8px;
828 bottom: 8px;
829 width: 2px;
830 background: var(--border);
831 }
832 .timeline-item {
833 position: relative;
834 padding-bottom: 16px;
835 }
836 .timeline-dot {
837 position: absolute;
838 left: -24px;
839 top: 6px;
840 width: 10px;
841 height: 10px;
842 border-radius: 50%;
843 background: var(--text-muted);
844 border: 2px solid var(--bg);
845 }
846 .timeline-content {
847 background: var(--bg-secondary);
848 border: 1px solid var(--border);
849 border-radius: var(--radius);
850 padding: 12px 16px;
851 }
f1ab587Claude852
853 /* Toggle switch */
854 .toggle-switch {
855 position: relative;
856 display: inline-block;
857 width: 44px;
858 height: 24px;
859 flex-shrink: 0;
860 margin-left: 16px;
861 }
862 .toggle-switch input { opacity: 0; width: 0; height: 0; }
863 .toggle-slider {
864 position: absolute;
865 cursor: pointer;
866 top: 0; left: 0; right: 0; bottom: 0;
867 background: var(--bg-tertiary);
868 border: 1px solid var(--border);
869 border-radius: 12px;
870 transition: 0.2s;
871 }
872 .toggle-slider::before {
873 content: '';
874 position: absolute;
875 height: 18px;
876 width: 18px;
877 left: 2px;
878 bottom: 2px;
879 background: var(--text-muted);
880 border-radius: 50%;
881 transition: 0.2s;
882 }
883 .toggle-switch input:checked + .toggle-slider {
884 background: var(--accent);
885 border-color: var(--accent);
886 }
887 .toggle-switch input:checked + .toggle-slider::before {
888 transform: translateX(20px);
889 background: #fff;
890 }
fc1817aClaude891`;