@code-rag/api-server 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -0
  3. package/dist/dashboard/data-collector.d.ts +61 -0
  4. package/dist/dashboard/data-collector.js +244 -0
  5. package/dist/dashboard/data-collector.js.map +1 -0
  6. package/dist/dashboard/data-collector.test.d.ts +1 -0
  7. package/dist/dashboard/data-collector.test.js +334 -0
  8. package/dist/dashboard/data-collector.test.js.map +1 -0
  9. package/dist/dashboard/index.d.ts +10 -0
  10. package/dist/dashboard/index.js +6 -0
  11. package/dist/dashboard/index.js.map +1 -0
  12. package/dist/dashboard/routes.d.ts +18 -0
  13. package/dist/dashboard/routes.js +150 -0
  14. package/dist/dashboard/routes.js.map +1 -0
  15. package/dist/dashboard/templates.d.ts +47 -0
  16. package/dist/dashboard/templates.js +591 -0
  17. package/dist/dashboard/templates.js.map +1 -0
  18. package/dist/dashboard/templates.test.d.ts +1 -0
  19. package/dist/dashboard/templates.test.js +408 -0
  20. package/dist/dashboard/templates.test.js.map +1 -0
  21. package/dist/dashboard/types.d.ts +119 -0
  22. package/dist/dashboard/types.js +4 -0
  23. package/dist/dashboard/types.js.map +1 -0
  24. package/dist/index.d.ts +21 -0
  25. package/dist/index.js +36 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/middleware/auth.d.ts +37 -0
  28. package/dist/middleware/auth.js +92 -0
  29. package/dist/middleware/auth.js.map +1 -0
  30. package/dist/middleware/rate-limit.d.ts +26 -0
  31. package/dist/middleware/rate-limit.js +79 -0
  32. package/dist/middleware/rate-limit.js.map +1 -0
  33. package/dist/openapi.d.ts +14 -0
  34. package/dist/openapi.js +363 -0
  35. package/dist/openapi.js.map +1 -0
  36. package/dist/routes/context.d.ts +15 -0
  37. package/dist/routes/context.js +84 -0
  38. package/dist/routes/context.js.map +1 -0
  39. package/dist/routes/history.d.ts +56 -0
  40. package/dist/routes/history.js +308 -0
  41. package/dist/routes/history.js.map +1 -0
  42. package/dist/routes/index-trigger.d.ts +21 -0
  43. package/dist/routes/index-trigger.js +47 -0
  44. package/dist/routes/index-trigger.js.map +1 -0
  45. package/dist/routes/search.d.ts +24 -0
  46. package/dist/routes/search.js +85 -0
  47. package/dist/routes/search.js.map +1 -0
  48. package/dist/routes/status.d.ts +14 -0
  49. package/dist/routes/status.js +37 -0
  50. package/dist/routes/status.js.map +1 -0
  51. package/dist/routes/team.d.ts +62 -0
  52. package/dist/routes/team.js +129 -0
  53. package/dist/routes/team.js.map +1 -0
  54. package/dist/routes/viewer.d.ts +78 -0
  55. package/dist/routes/viewer.js +387 -0
  56. package/dist/routes/viewer.js.map +1 -0
  57. package/dist/routes/viewer.test.d.ts +1 -0
  58. package/dist/routes/viewer.test.js +509 -0
  59. package/dist/routes/viewer.test.js.map +1 -0
  60. package/dist/server.d.ts +45 -0
  61. package/dist/server.js +227 -0
  62. package/dist/server.js.map +1 -0
  63. package/dist/server.test.d.ts +1 -0
  64. package/dist/server.test.js +620 -0
  65. package/dist/server.test.js.map +1 -0
  66. package/package.json +62 -0
@@ -0,0 +1,591 @@
1
+ /**
2
+ * Server-rendered HTML templates for the CodeRAG admin dashboard.
3
+ *
4
+ * All HTML/CSS is inline — no external assets or client-side JS frameworks.
5
+ * Works without JavaScript in the browser (progressive enhancement).
6
+ */
7
+ /**
8
+ * Render a complete dashboard HTML page.
9
+ */
10
+ export function renderDashboardPage(pageData) {
11
+ let content;
12
+ switch (pageData.page) {
13
+ case 'overview':
14
+ content = renderOverviewContent(pageData.data);
15
+ break;
16
+ case 'analytics':
17
+ content = renderAnalyticsContent(pageData.data);
18
+ break;
19
+ case 'users':
20
+ content = renderUsersContent(pageData.data);
21
+ break;
22
+ case 'settings':
23
+ content = renderSettingsContent(pageData.data);
24
+ break;
25
+ }
26
+ return renderLayout(pageData.page, content);
27
+ }
28
+ // ---------------------------------------------------------------------------
29
+ // Layout
30
+ // ---------------------------------------------------------------------------
31
+ function renderLayout(activePage, content) {
32
+ return `<!DOCTYPE html>
33
+ <html lang="en">
34
+ <head>
35
+ <meta charset="utf-8">
36
+ <meta name="viewport" content="width=device-width, initial-scale=1">
37
+ <title>CodeRAG Dashboard — ${capitalize(activePage)}</title>
38
+ <style>${CSS}</style>
39
+ </head>
40
+ <body>
41
+ <div class="layout">
42
+ <nav class="sidebar" aria-label="Dashboard navigation">
43
+ <div class="sidebar-header">
44
+ <h1 class="logo">CodeRAG</h1>
45
+ <span class="subtitle">Admin Dashboard</span>
46
+ </div>
47
+ <ul class="nav-list">
48
+ ${navItem('overview', 'Overview', activePage)}
49
+ ${navItem('analytics', 'Analytics', activePage)}
50
+ ${navItem('users', 'Users', activePage)}
51
+ ${navItem('settings', 'Settings', activePage)}
52
+ </ul>
53
+ </nav>
54
+ <main class="content">
55
+ ${content}
56
+ </main>
57
+ </div>
58
+ </body>
59
+ </html>`;
60
+ }
61
+ function navItem(page, label, activePage) {
62
+ const activeClass = page === activePage ? ' class="active"' : '';
63
+ return `<li><a href="/dashboard/${page}"${activeClass}>${esc(label)}</a></li>`;
64
+ }
65
+ // ---------------------------------------------------------------------------
66
+ // Overview Page
67
+ // ---------------------------------------------------------------------------
68
+ function renderOverviewContent(data) {
69
+ const { overview, usageStats, flash } = data;
70
+ const flashHtml = flash ? renderFlash(flash) : '';
71
+ const healthBadge = renderHealthBadge(overview.health);
72
+ const lastIndexed = overview.lastIndexed
73
+ ? formatTimestamp(overview.lastIndexed)
74
+ : 'Never';
75
+ const languages = overview.languages.length > 0
76
+ ? overview.languages.join(', ')
77
+ : 'None detected';
78
+ return `
79
+ <h2>Index Overview</h2>
80
+ ${flashHtml}
81
+ <div class="card-grid">
82
+ <div class="card">
83
+ <h3>Index Health</h3>
84
+ <div class="card-body">
85
+ ${healthBadge}
86
+ <dl>
87
+ <dt>Chunks</dt><dd>${formatNumber(overview.chunkCount)}</dd>
88
+ <dt>Files</dt><dd>${formatNumber(overview.fileCount)}</dd>
89
+ <dt>Languages</dt><dd>${esc(languages)}</dd>
90
+ <dt>Last Indexed</dt><dd>${esc(lastIndexed)}</dd>
91
+ <dt>Storage</dt><dd>${formatBytes(overview.storageBytes)}</dd>
92
+ </dl>
93
+ </div>
94
+ </div>
95
+ <div class="card">
96
+ <h3>Usage Summary</h3>
97
+ <div class="card-body">
98
+ <dl>
99
+ <dt>API Calls Today</dt><dd>${formatNumber(usageStats.apiCallsToday)}</dd>
100
+ <dt>API Calls (Week)</dt><dd>${formatNumber(usageStats.apiCallsWeek)}</dd>
101
+ <dt>API Calls (Month)</dt><dd>${formatNumber(usageStats.apiCallsMonth)}</dd>
102
+ <dt>Cost Estimate</dt><dd>${esc(usageStats.costEstimate)}</dd>
103
+ </dl>
104
+ </div>
105
+ </div>
106
+ <div class="card">
107
+ <h3>Quick Actions</h3>
108
+ <div class="card-body">
109
+ <form method="POST" action="/dashboard/actions/reindex">
110
+ <button type="submit" class="btn btn-primary">Re-index Now</button>
111
+ </form>
112
+ <p class="hint">Triggers a full re-indexing of the project.</p>
113
+ </div>
114
+ </div>
115
+ </div>`;
116
+ }
117
+ // ---------------------------------------------------------------------------
118
+ // Analytics Page
119
+ // ---------------------------------------------------------------------------
120
+ function renderAnalyticsContent(data) {
121
+ const { analytics } = data;
122
+ const chartHtml = renderQueryChart(analytics.queriesPerDay);
123
+ const topQueriesRows = analytics.topQueries.length > 0
124
+ ? analytics.topQueries
125
+ .map((tq, i) => `<tr><td>${i + 1}</td><td>${esc(tq.query)}</td><td>${formatNumber(tq.count)}</td></tr>`)
126
+ .join('\n')
127
+ : '<tr><td colspan="3" class="empty-state">No queries recorded yet.</td></tr>';
128
+ return `
129
+ <h2>Search Analytics</h2>
130
+ <div class="card-grid">
131
+ <div class="card card-wide">
132
+ <h3>Queries Per Day</h3>
133
+ <div class="card-body">
134
+ ${chartHtml}
135
+ </div>
136
+ </div>
137
+ <div class="card">
138
+ <h3>Summary</h3>
139
+ <div class="card-body">
140
+ <dl>
141
+ <dt>Total Queries</dt><dd>${formatNumber(analytics.totalQueries)}</dd>
142
+ <dt>Avg Response Time</dt><dd>${analytics.avgResponseTimeMs.toFixed(1)} ms</dd>
143
+ <dt>Error Rate</dt><dd>${(analytics.errorRate * 100).toFixed(2)}%</dd>
144
+ </dl>
145
+ </div>
146
+ </div>
147
+ </div>
148
+ <div class="card card-wide">
149
+ <h3>Top Queries</h3>
150
+ <div class="card-body">
151
+ <table class="data-table">
152
+ <thead><tr><th>#</th><th>Query</th><th>Count</th></tr></thead>
153
+ <tbody>${topQueriesRows}</tbody>
154
+ </table>
155
+ </div>
156
+ </div>`;
157
+ }
158
+ function renderQueryChart(queriesPerDay) {
159
+ if (queriesPerDay.length === 0) {
160
+ return '<p class="empty-state">No query data available.</p>';
161
+ }
162
+ const maxCount = Math.max(...queriesPerDay.map((d) => d.count), 1);
163
+ const bars = queriesPerDay
164
+ .map((day) => {
165
+ const pct = Math.round((day.count / maxCount) * 100);
166
+ return `<div class="bar-group">
167
+ <div class="bar" style="height:${pct}%" title="${esc(day.date)}: ${day.count} queries"></div>
168
+ <span class="bar-label">${esc(day.date.slice(5))}</span>
169
+ </div>`;
170
+ })
171
+ .join('\n');
172
+ return `<div class="chart">${bars}</div>`;
173
+ }
174
+ // ---------------------------------------------------------------------------
175
+ // Users Page
176
+ // ---------------------------------------------------------------------------
177
+ function renderUsersContent(data) {
178
+ const { users } = data;
179
+ if (users.length === 0) {
180
+ return `
181
+ <h2>User Management</h2>
182
+ <div class="card card-wide">
183
+ <div class="card-body">
184
+ <p class="empty-state">No API keys configured. Authentication is disabled.</p>
185
+ </div>
186
+ </div>`;
187
+ }
188
+ const rows = users
189
+ .map((u) => `<tr>
190
+ <td>${esc(u.userId)}</td>
191
+ <td>${renderRoleBadge(u.role)}</td>
192
+ <td>${u.lastActive ? formatTimestamp(u.lastActive) : 'Never'}</td>
193
+ <td>${formatNumber(u.queryCount)}</td>
194
+ </tr>`)
195
+ .join('\n');
196
+ return `
197
+ <h2>User Management</h2>
198
+ <div class="card card-wide">
199
+ <div class="card-body">
200
+ <table class="data-table">
201
+ <thead><tr><th>User ID</th><th>Role</th><th>Last Active</th><th>Queries</th></tr></thead>
202
+ <tbody>${rows}</tbody>
203
+ </table>
204
+ </div>
205
+ </div>`;
206
+ }
207
+ // ---------------------------------------------------------------------------
208
+ // Settings Page
209
+ // ---------------------------------------------------------------------------
210
+ function renderSettingsContent(data) {
211
+ const { config, projectConfig } = data;
212
+ const projectSection = projectConfig
213
+ ? `<dl>
214
+ <dt>Project Name</dt><dd>${esc(projectConfig.name)}</dd>
215
+ <dt>Embedding Model</dt><dd>${esc(projectConfig.embeddingModel)}</dd>
216
+ <dt>Storage Path</dt><dd>${esc(projectConfig.storagePath)}</dd>
217
+ </dl>`
218
+ : '<p class="empty-state">Project configuration not loaded.</p>';
219
+ return `
220
+ <h2>Settings</h2>
221
+ <div class="card-grid">
222
+ <div class="card">
223
+ <h3>Project Configuration</h3>
224
+ <div class="card-body">
225
+ ${projectSection}
226
+ </div>
227
+ </div>
228
+ <div class="card">
229
+ <h3>Dashboard Configuration</h3>
230
+ <div class="card-body">
231
+ <dl>
232
+ <dt>Max Search Records</dt><dd>${formatNumber(config.maxSearchRecords)}</dd>
233
+ <dt>Max Request Records</dt><dd>${formatNumber(config.maxRequestRecords)}</dd>
234
+ <dt>Top Queries Limit</dt><dd>${formatNumber(config.topQueriesLimit)}</dd>
235
+ </dl>
236
+ </div>
237
+ </div>
238
+ <div class="card">
239
+ <h3>Re-index</h3>
240
+ <div class="card-body">
241
+ <form method="POST" action="/dashboard/actions/reindex">
242
+ <button type="submit" class="btn btn-primary">Trigger Re-index</button>
243
+ </form>
244
+ <p class="hint">Triggers a full re-indexing of the project.</p>
245
+ </div>
246
+ </div>
247
+ </div>`;
248
+ }
249
+ // ---------------------------------------------------------------------------
250
+ // Shared Helpers
251
+ // ---------------------------------------------------------------------------
252
+ function renderFlash(flash) {
253
+ return `<div class="flash flash-${esc(flash.type)}" role="alert">${esc(flash.text)}</div>`;
254
+ }
255
+ function renderHealthBadge(health) {
256
+ const colorClass = health === 'healthy'
257
+ ? 'badge-green'
258
+ : health === 'degraded'
259
+ ? 'badge-yellow'
260
+ : 'badge-red';
261
+ return `<span class="badge ${colorClass}">${esc(health)}</span>`;
262
+ }
263
+ function renderRoleBadge(role) {
264
+ const colorClass = role === 'admin' ? 'badge-purple' : 'badge-blue';
265
+ return `<span class="badge ${colorClass}">${esc(role)}</span>`;
266
+ }
267
+ function capitalize(s) {
268
+ return s.charAt(0).toUpperCase() + s.slice(1);
269
+ }
270
+ function formatNumber(n) {
271
+ return n.toLocaleString('en-US');
272
+ }
273
+ function formatBytes(bytes) {
274
+ if (bytes === 0)
275
+ return '0 B';
276
+ const units = ['B', 'KB', 'MB', 'GB'];
277
+ const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
278
+ const value = bytes / Math.pow(1024, i);
279
+ return `${value.toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
280
+ }
281
+ function formatTimestamp(iso) {
282
+ try {
283
+ const d = new Date(iso);
284
+ return d.toLocaleString('en-US', {
285
+ year: 'numeric',
286
+ month: 'short',
287
+ day: 'numeric',
288
+ hour: '2-digit',
289
+ minute: '2-digit',
290
+ });
291
+ }
292
+ catch {
293
+ return iso;
294
+ }
295
+ }
296
+ /**
297
+ * Escape HTML special characters to prevent XSS.
298
+ */
299
+ export function esc(s) {
300
+ return s
301
+ .replace(/&/g, '&amp;')
302
+ .replace(/</g, '&lt;')
303
+ .replace(/>/g, '&gt;')
304
+ .replace(/"/g, '&quot;')
305
+ .replace(/'/g, '&#39;');
306
+ }
307
+ // ---------------------------------------------------------------------------
308
+ // Inline CSS
309
+ // ---------------------------------------------------------------------------
310
+ const CSS = `
311
+ :root {
312
+ --bg: #0f1117;
313
+ --surface: #1a1d27;
314
+ --surface-hover: #242736;
315
+ --border: #2e3142;
316
+ --text: #e4e4e7;
317
+ --text-muted: #9ca3af;
318
+ --primary: #6366f1;
319
+ --primary-hover: #818cf8;
320
+ --green: #22c55e;
321
+ --yellow: #eab308;
322
+ --red: #ef4444;
323
+ --purple: #a855f7;
324
+ --blue: #3b82f6;
325
+ --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
326
+ --mono: 'SF Mono', 'Fira Code', monospace;
327
+ }
328
+
329
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
330
+
331
+ body {
332
+ font-family: var(--font);
333
+ background: var(--bg);
334
+ color: var(--text);
335
+ line-height: 1.6;
336
+ }
337
+
338
+ .layout {
339
+ display: flex;
340
+ min-height: 100vh;
341
+ }
342
+
343
+ /* Sidebar */
344
+ .sidebar {
345
+ width: 240px;
346
+ background: var(--surface);
347
+ border-right: 1px solid var(--border);
348
+ padding: 1.5rem 0;
349
+ flex-shrink: 0;
350
+ }
351
+
352
+ .sidebar-header {
353
+ padding: 0 1.25rem 1.5rem;
354
+ border-bottom: 1px solid var(--border);
355
+ }
356
+
357
+ .logo {
358
+ font-size: 1.25rem;
359
+ font-weight: 700;
360
+ color: var(--primary);
361
+ }
362
+
363
+ .subtitle {
364
+ display: block;
365
+ font-size: 0.75rem;
366
+ color: var(--text-muted);
367
+ margin-top: 0.25rem;
368
+ }
369
+
370
+ .nav-list {
371
+ list-style: none;
372
+ padding: 1rem 0;
373
+ }
374
+
375
+ .nav-list li a {
376
+ display: block;
377
+ padding: 0.6rem 1.25rem;
378
+ color: var(--text-muted);
379
+ text-decoration: none;
380
+ font-size: 0.9rem;
381
+ transition: background 0.15s, color 0.15s;
382
+ }
383
+
384
+ .nav-list li a:hover {
385
+ background: var(--surface-hover);
386
+ color: var(--text);
387
+ }
388
+
389
+ .nav-list li a.active {
390
+ color: var(--primary);
391
+ background: var(--surface-hover);
392
+ border-left: 3px solid var(--primary);
393
+ font-weight: 600;
394
+ }
395
+
396
+ /* Content */
397
+ .content {
398
+ flex: 1;
399
+ padding: 2rem;
400
+ max-width: 1200px;
401
+ }
402
+
403
+ .content h2 {
404
+ font-size: 1.5rem;
405
+ margin-bottom: 1.5rem;
406
+ }
407
+
408
+ /* Cards */
409
+ .card-grid {
410
+ display: grid;
411
+ grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
412
+ gap: 1.25rem;
413
+ margin-bottom: 1.5rem;
414
+ }
415
+
416
+ .card {
417
+ background: var(--surface);
418
+ border: 1px solid var(--border);
419
+ border-radius: 8px;
420
+ overflow: hidden;
421
+ }
422
+
423
+ .card-wide {
424
+ grid-column: 1 / -1;
425
+ }
426
+
427
+ .card h3 {
428
+ font-size: 0.9rem;
429
+ text-transform: uppercase;
430
+ letter-spacing: 0.05em;
431
+ color: var(--text-muted);
432
+ padding: 1rem 1.25rem;
433
+ border-bottom: 1px solid var(--border);
434
+ }
435
+
436
+ .card-body {
437
+ padding: 1.25rem;
438
+ }
439
+
440
+ /* Definition lists */
441
+ dl {
442
+ display: grid;
443
+ grid-template-columns: auto 1fr;
444
+ gap: 0.5rem 1rem;
445
+ }
446
+
447
+ dt {
448
+ color: var(--text-muted);
449
+ font-size: 0.85rem;
450
+ }
451
+
452
+ dd {
453
+ font-weight: 500;
454
+ }
455
+
456
+ /* Badges */
457
+ .badge {
458
+ display: inline-block;
459
+ padding: 0.2rem 0.6rem;
460
+ border-radius: 9999px;
461
+ font-size: 0.75rem;
462
+ font-weight: 600;
463
+ text-transform: uppercase;
464
+ letter-spacing: 0.05em;
465
+ }
466
+
467
+ .badge-green { background: rgba(34,197,94,0.15); color: var(--green); }
468
+ .badge-yellow { background: rgba(234,179,8,0.15); color: var(--yellow); }
469
+ .badge-red { background: rgba(239,68,68,0.15); color: var(--red); }
470
+ .badge-purple { background: rgba(168,85,247,0.15); color: var(--purple); }
471
+ .badge-blue { background: rgba(59,130,246,0.15); color: var(--blue); }
472
+
473
+ /* Tables */
474
+ .data-table {
475
+ width: 100%;
476
+ border-collapse: collapse;
477
+ }
478
+
479
+ .data-table th, .data-table td {
480
+ text-align: left;
481
+ padding: 0.6rem 0.75rem;
482
+ border-bottom: 1px solid var(--border);
483
+ }
484
+
485
+ .data-table th {
486
+ font-size: 0.8rem;
487
+ text-transform: uppercase;
488
+ color: var(--text-muted);
489
+ font-weight: 600;
490
+ }
491
+
492
+ .data-table tbody tr:hover {
493
+ background: var(--surface-hover);
494
+ }
495
+
496
+ /* Chart */
497
+ .chart {
498
+ display: flex;
499
+ align-items: flex-end;
500
+ gap: 4px;
501
+ height: 160px;
502
+ padding-top: 1rem;
503
+ }
504
+
505
+ .bar-group {
506
+ flex: 1;
507
+ display: flex;
508
+ flex-direction: column;
509
+ align-items: center;
510
+ min-width: 0;
511
+ }
512
+
513
+ .bar {
514
+ width: 100%;
515
+ max-width: 32px;
516
+ background: var(--primary);
517
+ border-radius: 3px 3px 0 0;
518
+ min-height: 2px;
519
+ transition: background 0.15s;
520
+ }
521
+
522
+ .bar:hover {
523
+ background: var(--primary-hover);
524
+ }
525
+
526
+ .bar-label {
527
+ font-size: 0.65rem;
528
+ color: var(--text-muted);
529
+ margin-top: 0.3rem;
530
+ white-space: nowrap;
531
+ overflow: hidden;
532
+ text-overflow: ellipsis;
533
+ max-width: 100%;
534
+ }
535
+
536
+ /* Buttons */
537
+ .btn {
538
+ display: inline-block;
539
+ padding: 0.5rem 1rem;
540
+ border: none;
541
+ border-radius: 6px;
542
+ font-size: 0.9rem;
543
+ font-weight: 500;
544
+ cursor: pointer;
545
+ transition: background 0.15s;
546
+ }
547
+
548
+ .btn-primary {
549
+ background: var(--primary);
550
+ color: #fff;
551
+ }
552
+
553
+ .btn-primary:hover {
554
+ background: var(--primary-hover);
555
+ }
556
+
557
+ /* Flash messages */
558
+ .flash {
559
+ padding: 0.75rem 1rem;
560
+ border-radius: 6px;
561
+ margin-bottom: 1.25rem;
562
+ font-size: 0.9rem;
563
+ }
564
+
565
+ .flash-success { background: rgba(34,197,94,0.15); color: var(--green); border: 1px solid rgba(34,197,94,0.3); }
566
+ .flash-error { background: rgba(239,68,68,0.15); color: var(--red); border: 1px solid rgba(239,68,68,0.3); }
567
+ .flash-info { background: rgba(59,130,246,0.15); color: var(--blue); border: 1px solid rgba(59,130,246,0.3); }
568
+
569
+ /* Misc */
570
+ .empty-state {
571
+ color: var(--text-muted);
572
+ font-style: italic;
573
+ padding: 1rem 0;
574
+ }
575
+
576
+ .hint {
577
+ font-size: 0.8rem;
578
+ color: var(--text-muted);
579
+ margin-top: 0.75rem;
580
+ }
581
+
582
+ /* Responsive */
583
+ @media (max-width: 768px) {
584
+ .layout { flex-direction: column; }
585
+ .sidebar { width: 100%; border-right: none; border-bottom: 1px solid var(--border); }
586
+ .nav-list { display: flex; flex-wrap: wrap; padding: 0.5rem; }
587
+ .nav-list li a { padding: 0.4rem 0.75rem; }
588
+ .content { padding: 1rem; }
589
+ .card-grid { grid-template-columns: 1fr; }
590
+ }
591
+ `;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/dashboard/templates.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};