free-coding-models 0.5.32 → 0.5.35

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 (36) hide show
  1. package/LICENSE +43 -21
  2. package/README.md +2 -1
  3. package/changelog/v0.5.33.md +4 -0
  4. package/changelog/v0.5.34.md +4 -0
  5. package/changelog/v0.5.35.md +4 -0
  6. package/package.json +3 -2
  7. package/sources.js +12 -52
  8. package/src/core/router-daemon.js +56 -49
  9. package/src/core/schema-normalizer.js +172 -0
  10. package/src/core/tool-bootstrap.js +11 -0
  11. package/src/core/tool-launchers.js +37 -2
  12. package/src/core/tool-metadata.js +3 -0
  13. package/web/dist/assets/index-B5tTa7_O.js +40 -0
  14. package/web/dist/assets/index-r8niexgJ.css +1 -0
  15. package/web/dist/index.html +2 -2
  16. package/web/src/App.jsx +93 -105
  17. package/web/src/components/changelog/ChangelogView.module.css +13 -14
  18. package/web/src/components/dashboard/FilterBar.jsx +17 -2
  19. package/web/src/components/dashboard/FilterBar.module.css +68 -21
  20. package/web/src/components/dashboard/ModelTable.jsx +118 -42
  21. package/web/src/components/dashboard/ModelTable.module.css +82 -27
  22. package/web/src/components/help/HelpView.module.css +13 -14
  23. package/web/src/components/install/InstallEndpointsView.module.css +13 -13
  24. package/web/src/components/installed/InstalledModelsView.module.css +13 -13
  25. package/web/src/components/layout/Header.jsx +28 -28
  26. package/web/src/components/layout/Header.module.css +56 -30
  27. package/web/src/components/palette/CommandPalette.jsx +14 -14
  28. package/web/src/components/playground/PlaygroundView.module.css +13 -14
  29. package/web/src/components/recommend/RecommendView.module.css +13 -11
  30. package/web/src/components/router/RouterView.module.css +13 -13
  31. package/web/src/components/tools/ToolPicker.module.css +27 -14
  32. package/web/src/global.css +58 -31
  33. package/web/src/hooks/urlState.constants.js +1 -1
  34. package/web/src/hooks/useUrlState.js +19 -3
  35. package/web/dist/assets/index-C_tCF0A5.js +0 -40
  36. package/web/dist/assets/index-CsFt3qt5.css +0 -1
@@ -13,6 +13,24 @@
13
13
  border-right: 0;
14
14
  border-radius: 0;
15
15
  margin: 0;
16
+ flex: 1;
17
+ min-height: 0;
18
+ /* 📖 The container itself doesn't scroll — it is the relative clip box
19
+ 📖 hosting the scrolling table (.scrollInner). overflow:hidden keeps the
20
+ 📖 sticky header + virtualized rows neatly clipped to the table viewport.
21
+ 📖 isolation:isolate creates a clean stacking context so the sticky
22
+ 📖 <thead> header (z-index 10) reliably layers above body cells. */
23
+ overflow: hidden;
24
+ position: relative;
25
+ isolation: isolate;
26
+ display: flex;
27
+ flex-direction: column;
28
+ }
29
+
30
+ /* 📖 The real scroll surface for the table. It is the scroll element for
31
+ 📖 both the sticky <thead> header and the @tanstack/react-virtual row
32
+ 📖 virtualizer (only ~23 of 189 rows live in the DOM at any time). */
33
+ .scrollInner {
16
34
  flex: 1;
17
35
  min-height: 0;
18
36
  overflow: auto;
@@ -24,27 +42,29 @@
24
42
  width: 100%;
25
43
  min-width: 1200px;
26
44
  border-collapse: collapse;
27
- font-size: 12px;
45
+ font-size: 14px;
28
46
  border: 0;
29
47
  }
30
48
 
31
49
  /* ─── Header ─── */
32
50
  .th {
33
- padding: 9px 8px;
34
- font-size: 10px;
35
- font-weight: 700;
51
+ padding: 10px 8px;
52
+ font-size: 12px;
53
+ font-weight: 800;
54
+ font-family: var(--font-mono);
36
55
  text-transform: uppercase;
37
- letter-spacing: 0.5px;
38
- color: var(--color-text-muted);
39
- background: var(--color-bg-elevated);
40
- border-bottom: 1px solid var(--color-border-hover);
41
- border-right: 1px solid var(--color-border);
56
+ letter-spacing: 0.8px;
57
+ color: var(--color-bg);
58
+ background: var(--color-accent);
59
+ border-bottom: 2px solid var(--color-border);
60
+ border-right: 1px solid rgba(0, 0, 0, 0.08);
42
61
  white-space: nowrap;
43
62
  user-select: none;
44
63
  position: sticky;
45
64
  top: 0;
46
65
  z-index: 10;
47
66
  text-align: center;
67
+ transition: all 120ms var(--ease-out);
48
68
  /* 📖 Disable user-select on the whole header so dragging the resize handle
49
69
  📖 never accidentally selects the cell text on rapid drags. */
50
70
  -webkit-user-select: none;
@@ -52,7 +72,7 @@
52
72
  -ms-user-select: none;
53
73
  }
54
74
  .th:last-child { border-right: 0; }
55
- .th:hover { color: var(--color-accent); background: var(--color-bg-active); }
75
+ .th:hover { color: var(--color-bg); background: var(--color-accent-hover); }
56
76
 
57
77
  /* 📖 Inner flex container: header label on the left, resize handle on the right.
58
78
  📖 Letting the label shrink keeps the column tight when the user makes it narrow. */
@@ -120,8 +140,10 @@
120
140
  user-select: none !important;
121
141
  }
122
142
 
123
- /* 📖 Floating toolbar that appears only when the user has custom column widths.
124
- 📖 Sits at the top-right of the table area without pushing rows down. */
143
+ /* 📖 Floating bar shown only when the user has custom column widths.
144
+ 📖 Rendered OUTSIDE the scroller (a normal flex child at the top of the
145
+ 📖 table container) so it stays put without colliding with the sticky
146
+ 📖 <thead>. It shrinks the scroll area; the sticky header sticks below it. */
125
147
  .resizeToolbar {
126
148
  display: flex;
127
149
  align-items: center;
@@ -130,11 +152,9 @@
130
152
  padding: 6px 10px;
131
153
  background: var(--color-bg-elevated);
132
154
  border-bottom: 1px solid var(--color-border);
133
- font-size: 11px;
155
+ font-size: 13px;
134
156
  color: var(--color-text-muted);
135
- position: sticky;
136
- top: 0;
137
- z-index: 11;
157
+ flex-shrink: 0;
138
158
  }
139
159
  .resizeToolbarHint {
140
160
  display: inline-flex;
@@ -154,7 +174,7 @@
154
174
  color: var(--color-text-muted);
155
175
  padding: 3px 10px;
156
176
  border-radius: 4px;
157
- font-size: 11px;
177
+ font-size: 13px;
158
178
  font-weight: 600;
159
179
  cursor: pointer;
160
180
  transition: background 120ms var(--ease-out, ease), color 120ms var(--ease-out, ease), border-color 120ms var(--ease-out, ease);
@@ -176,8 +196,8 @@
176
196
  .th:nth-child(11) { text-align: left; } /* Verdict */
177
197
 
178
198
  /* Sort icons */
179
- .sortIcon { opacity: 0.3; margin-left: 3px; font-size: 9px; }
180
- .sortIconActive { color: var(--color-accent); margin-left: 3px; font-size: 10px; font-weight: 800; }
199
+ .sortIcon { opacity: 0.4; margin-left: 3px; font-size: 9px; color: var(--color-bg); }
200
+ .sortIconActive { color: var(--color-bg); margin-left: 3px; font-size: 10px; font-weight: 900; }
181
201
 
182
202
  /* ─── Data cells ─── */
183
203
  .td {
@@ -191,6 +211,11 @@
191
211
  .td:nth-child(7) { text-align: left; } /* Provider */
192
212
  .td:nth-child(11) { text-align: left; } /* Verdict */
193
213
 
214
+ /* 📖 Center the BOOKMARKS column (star button) so it sits nicely in its
215
+ 📖 wider header. Other narrow numeric columns are centered by default. */
216
+ .td:nth-child(1),
217
+ .th:nth-child(1) { text-align: center; }
218
+
194
219
  /* ─── Row hover ─── */
195
220
  .table tbody tr {
196
221
  cursor: pointer;
@@ -206,7 +231,7 @@
206
231
  /* ─── Shared cell values ─── */
207
232
  .rankNum {
208
233
  font-family: var(--font-mono);
209
- font-size: 11px;
234
+ font-size: 13px;
210
235
  color: var(--color-text-dim);
211
236
  text-align: center;
212
237
  display: block;
@@ -217,7 +242,7 @@
217
242
  .sweMid { color: #3ddc84; }
218
243
  .sweLow { color: var(--color-text-dim); }
219
244
 
220
- .ctx { font-family: var(--font-mono); font-size: 11px; }
245
+ .ctx { font-family: var(--font-mono); font-size: 13px; }
221
246
 
222
247
  .ping { font-family: var(--font-mono); font-weight: 600; }
223
248
  .pingFast { color: #00ff88; }
@@ -232,14 +257,14 @@
232
257
  :global([data-theme="light"]) .pingMedium { color: #b86b00; }
233
258
  :global([data-theme="light"]) .pingSlow { color: #c8143a; }
234
259
 
235
- .uptime { font-family: var(--font-mono); font-size: 11px; }
260
+ .uptime { font-family: var(--font-mono); font-size: 13px; }
236
261
 
237
262
  /* ─── Model cell ─── */
238
263
  .modelCell { display: flex; align-items: center; gap: 6px; overflow: hidden; }
239
264
  .modelMeta { flex: 1; min-width: 0; }
240
265
  .modelHeader { display: flex; align-items: center; gap: 5px; }
241
- .modelName { font-weight: 600; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
242
- .modelId { font-family: var(--font-mono); font-size: 9px; color: var(--color-text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
266
+ .modelName { font-weight: 600; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
267
+ .modelId { font-family: var(--font-mono); font-size: 11px; color: var(--color-text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
243
268
 
244
269
  /* 📖 Provider cell now uses <ProviderLogo /> (icon + wordmark) — see
245
270
  📖 atoms/ProviderLogo.module.css for the layout. The old bordered pill
@@ -368,13 +393,43 @@
368
393
  }
369
394
 
370
395
  /* ─── Expandable row styles ─── */
371
- .expandedRow {
372
- background: var(--color-bg-active) !important;
396
+ .expandedRow td {
397
+ background: var(--color-accent) !important;
398
+ color: var(--color-bg) !important;
399
+ border-bottom: 1px solid rgba(0, 0, 0, 0.15) !important;
400
+ }
401
+ .expandedRow td * {
402
+ color: var(--color-bg) !important;
403
+ }
404
+ .expandedRow :global(.status-dot) {
405
+ border-color: var(--color-bg) !important;
406
+ }
407
+ .expandedRow :global(.provider-wordmark) {
408
+ filter: brightness(0) saturate(100%) !important; /* Forces logos to render dark/black for contrast */
409
+ }
410
+ .expandedRow .rankNum,
411
+ .expandedRow .modelId {
412
+ color: rgba(0, 0, 0, 0.6) !important;
413
+ }
414
+ .expandedRow .sweLow,
415
+ .expandedRow .sweMid,
416
+ .expandedRow .sweHigh {
417
+ color: var(--color-bg) !important;
418
+ }
419
+ .expandedRow .pingFast,
420
+ .expandedRow .pingMedium,
421
+ .expandedRow .pingSlow {
422
+ color: var(--color-bg) !important;
423
+ font-weight: 800;
373
424
  }
374
425
  .expandRowWrapper {
375
426
  border: none;
376
427
  }
377
428
  .expandRowCell {
378
429
  padding: 0 !important;
379
- border-bottom: 2px solid var(--color-accent) !important;
430
+ border-bottom: 3px solid var(--color-accent) !important;
431
+ background: var(--color-bg-elevated) !important;
432
+ }
433
+ .expandRowCell * {
434
+ color: inherit;
380
435
  }
@@ -4,28 +4,27 @@
4
4
  */
5
5
 
6
6
  .backdrop {
7
- position: fixed;
8
- inset: 0;
9
- background: rgba(0, 0, 0, 0.55);
10
- backdrop-filter: blur(4px);
11
- -webkit-backdrop-filter: blur(4px);
12
- z-index: 500;
13
7
  display: flex;
14
- align-items: center;
15
- justify-content: center;
16
- padding: 5vh 4vw;
8
+ flex-direction: column;
9
+ flex: 1;
10
+ min-height: 0;
11
+ width: 100%;
12
+ padding: 24px;
17
13
  }
18
14
 
19
15
  .modal {
20
- width: min(820px, 95vw);
21
- max-height: 90vh;
22
- background: var(--color-bg-elevated);
23
- border: 1px solid var(--color-border-hover);
16
+ width: 100%;
17
+ max-width: 820px;
18
+ margin: 0 auto;
19
+ background: var(--color-bg-card, #080908);
20
+ border: 1px solid var(--color-border, #161a16);
24
21
  border-radius: 14px;
25
- box-shadow: 0 24px 64px rgba(0, 0, 0, 0.5);
22
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
26
23
  display: flex;
27
24
  flex-direction: column;
28
25
  overflow: hidden;
26
+ flex: 1;
27
+ min-height: 0;
29
28
  }
30
29
 
31
30
  .header {
@@ -1,24 +1,24 @@
1
1
  .overlay {
2
- position: fixed;
3
- inset: 0;
4
- z-index: 200;
5
- background: rgba(0, 0, 0, 0.6);
6
2
  display: flex;
7
- align-items: center;
8
- justify-content: center;
9
- animation: fadeIn 0.15s ease;
3
+ flex-direction: column;
4
+ flex: 1;
5
+ min-height: 0;
6
+ width: 100%;
7
+ padding: 24px;
10
8
  }
11
9
 
12
10
  .modal {
13
- background: var(--bg-primary, #0f0f17);
14
- border: 1px solid var(--border, #1e1e2e);
11
+ background: var(--color-bg-card, #080908);
12
+ border: 1px solid var(--color-border, #161a16);
15
13
  border-radius: 12px;
16
- width: 680px;
17
- max-width: 95vw;
18
- max-height: 85vh;
14
+ width: 100%;
15
+ max-width: 680px;
16
+ margin: 0 auto;
17
+ flex: 1;
19
18
  display: flex;
20
19
  flex-direction: column;
21
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
20
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
21
+ min-height: 0;
22
22
  }
23
23
 
24
24
  .header {
@@ -1,24 +1,24 @@
1
1
  .overlay {
2
- position: fixed;
3
- inset: 0;
4
- z-index: 200;
5
- background: rgba(0, 0, 0, 0.6);
6
2
  display: flex;
7
- align-items: center;
8
- justify-content: center;
9
- animation: fadeIn 0.15s ease;
3
+ flex-direction: column;
4
+ flex: 1;
5
+ min-height: 0;
6
+ width: 100%;
7
+ padding: 24px;
10
8
  }
11
9
 
12
10
  .modal {
13
- background: var(--bg-primary, #0f0f17);
14
- border: 1px solid var(--border, #1e1e2e);
11
+ background: var(--color-bg-card, #080908);
12
+ border: 1px solid var(--color-border, #161a16);
15
13
  border-radius: 12px;
16
- width: 640px;
17
- max-width: 95vw;
18
- max-height: 85vh;
14
+ width: 100%;
15
+ max-width: 640px;
16
+ margin: 0 auto;
17
+ flex: 1;
19
18
  display: flex;
20
19
  flex-direction: column;
21
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
20
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
21
+ min-height: 0;
22
22
  }
23
23
 
24
24
  .header {
@@ -22,20 +22,18 @@ import styles from './Header.module.css'
22
22
  // 📖 order, icon, and "coming soon" milestone are colocated with the
23
23
  // 📖 rendering code. When a view ships, remove the `comingIn` field.
24
24
  const NAV_ITEMS = [
25
- { id: 'dashboard', label: 'Dashboard', icon: IconLayoutDashboard },
26
- { id: 'settings', label: 'Settings', icon: IconSettings },
27
- { id: 'analytics', label: 'Analytics', icon: IconActivity },
28
- { id: 'recommend', label: 'Recommend', icon: IconSparkles },
29
- { id: 'router', label: 'Router', icon: IconRoute },
30
- { id: 'playground', label: 'Playground', icon: IconMessageChatbot },
25
+ { id: 'dashboard', label: 'Dashboard', icon: IconLayoutDashboard },
26
+ { id: 'router', label: 'Router', icon: IconRoute },
27
+ { id: 'playground', label: 'Playground', icon: IconMessageChatbot },
28
+ { id: 'help', label: 'Help', icon: IconQuestionMark },
29
+ { id: 'install-endpoints', label: 'Install Endpoints', icon: IconPlug },
31
30
  ]
32
31
 
33
- // 📖 Overflow menu items — Help + Changelog shipped in M2; Install Endpoints
34
- // 📖 and Installed Models are still M4.
32
+ // 📖 Overflow menu items
35
33
  const MENU_ITEMS = [
36
- { id: 'help', label: 'Help', icon: IconQuestionMark },
34
+ { id: 'analytics', label: 'Analytics', icon: IconActivity },
35
+ { id: 'recommend', label: 'Recommend', icon: IconSparkles },
37
36
  { id: 'changelog', label: 'Changelog', icon: IconHistory },
38
- { id: 'install-endpoints', label: 'Install Endpoints', icon: IconPlug },
39
37
  { id: 'installed-models', label: 'Installed Models', icon: IconFolders },
40
38
  ]
41
39
 
@@ -90,8 +88,11 @@ export default function Header({
90
88
  return (
91
89
  <header className={styles.header}>
92
90
  <div className={styles.left}>
93
- <div className={styles.logo}>
94
- <span className={styles.logoIcon}>&gt;</span>
91
+ <div className={styles.logo} onClick={() => onNavigate('dashboard')} style={{ cursor: 'pointer' }}>
92
+ <svg className={styles.logoIconSvg} width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
93
+ <path d="M4 5L11 12L4 19" stroke="var(--color-brand)" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"/>
94
+ <path d="M14 5V19M14 5H21M14 11H18" stroke="var(--color-brand)" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"/>
95
+ </svg>
95
96
  <span className={styles.logoText}>
96
97
  <span className={styles.logoTextHighlight}>free</span>
97
98
  <span>-coding-models</span>
@@ -168,7 +169,7 @@ export default function Header({
168
169
  {/* Overflow menu (kebab) — hidden features & occasional flows */}
169
170
  <div className={styles.menuWrap} ref={menuRef}>
170
171
  <button
171
- className={`${styles.navBtn} ${styles.menuTrigger}`}
172
+ className={`${styles.navBtn} ${styles.menuTrigger} ${MENU_ITEMS.some((m) => m.id === currentView) ? styles.navBtnActive : ''}`}
172
173
  onClick={() => setMenuOpen((o) => !o)}
173
174
  title="More features"
174
175
  aria-label="More features"
@@ -181,12 +182,14 @@ export default function Header({
181
182
  <div className={styles.menuPopover} role="menu">
182
183
  {MENU_ITEMS.map((item) => {
183
184
  const Icon = item.icon
185
+ const isActive = currentView === item.id
184
186
  return (
185
187
  <button
186
188
  key={item.id}
187
- className={styles.menuItem}
189
+ className={`${styles.menuItem} ${isActive ? styles.menuItemActive : ''}`}
188
190
  onClick={() => handleMenuClick(item)}
189
191
  role="menuitem"
192
+ aria-current={isActive ? 'page' : undefined}
190
193
  >
191
194
  <Icon size={14} stroke={1.5} />
192
195
  <span>{item.label}</span>
@@ -200,21 +203,18 @@ export default function Header({
200
203
  </nav>
201
204
  </div>
202
205
 
203
- <div className={styles.center}>
204
- <div className={styles.searchBar}>
205
- <span className={styles.searchIcon}><IconSearch size={16} stroke={1.5} /></span>
206
- <input
207
- type="text"
208
- className={styles.searchInput}
209
- placeholder="Search models, providers, tiers..."
210
- value={searchQuery}
211
- onChange={(e) => onSearchChange(e.target.value)}
212
- autoComplete="off"
213
- />
214
- </div>
215
- </div>
216
-
217
206
  <div className={styles.right}>
207
+ <button
208
+ className={`${styles.navBtn} ${currentView === 'settings' ? styles.navBtnActive : ''}`}
209
+ onClick={() => onNavigate('settings')}
210
+ title="Settings"
211
+ aria-current={currentView === 'settings' ? 'page' : undefined}
212
+ style={{ marginRight: '2px' }}
213
+ >
214
+ <IconSettings size={14} stroke={1.5} />
215
+ <span>Settings</span>
216
+ </button>
217
+
218
218
  <ToolPicker
219
219
  toolMode={toolMode}
220
220
  onSetToolMode={onSetToolMode}
@@ -46,24 +46,24 @@
46
46
  }
47
47
 
48
48
  /* ─── Brand ─── */
49
- .logo { display: flex; align-items: center; gap: 6px; }
50
- .logoIcon {
49
+ .logo { display: flex; align-items: center; gap: 8px; }
50
+ .logoIconSvg {
51
51
  color: var(--color-brand);
52
- font-family: var(--font-mono);
53
- font-size: 20px;
54
- font-weight: 900;
55
52
  display: flex;
56
53
  align-items: center;
54
+ flex-shrink: 0;
57
55
  }
58
56
  .logoText {
59
- font-size: 14px; font-weight: 700;
57
+ font-size: 16px; font-weight: 750;
58
+ font-family: var(--font-mono);
60
59
  color: var(--color-text);
61
60
  display: flex;
62
61
  align-items: center;
62
+ letter-spacing: -0.2px;
63
63
  }
64
64
  .logoTextHighlight { color: var(--color-brand); }
65
65
  .version {
66
- font-size: 10px; font-weight: 500; font-family: var(--font-mono);
66
+ font-size: 12px; font-weight: 500; font-family: var(--font-mono);
67
67
  color: var(--color-text-muted); background: var(--color-surface);
68
68
  padding: 2px 6px; border-radius: 999px; border: 1px solid var(--color-border);
69
69
  }
@@ -72,7 +72,7 @@
72
72
  .nav {
73
73
  display: flex;
74
74
  align-items: center;
75
- gap: 2px;
75
+ gap: 8px;
76
76
  margin-left: 8px;
77
77
  padding-left: 12px;
78
78
  border-left: 1px solid var(--color-border);
@@ -82,39 +82,44 @@
82
82
  .navBtn {
83
83
  display: inline-flex;
84
84
  align-items: center;
85
- gap: 5px;
85
+ gap: 6px;
86
86
  height: 30px;
87
- padding: 0 10px;
88
- font-size: 12px;
87
+ padding: 0 12px;
88
+ font-size: 13px;
89
89
  font-weight: 600;
90
- font-family: var(--font-sans);
90
+ font-family: var(--font-mono); /* Monospace font looks extremely high-tech */
91
+ text-transform: uppercase;
92
+ letter-spacing: 0.5px;
91
93
  color: var(--color-text-muted);
92
- background: transparent;
93
- border: 1px solid transparent;
94
+ background: var(--color-surface);
95
+ border: 1px solid var(--color-border);
94
96
  border-radius: 6px;
95
97
  cursor: pointer;
96
- transition: background 150ms, color 150ms, border-color 150ms;
98
+ transition: all 150ms var(--ease-out);
97
99
  white-space: nowrap;
98
100
  }
99
101
  .navBtn:hover {
100
102
  background: var(--color-bg-hover);
101
103
  color: var(--color-text);
104
+ border-color: var(--color-border-hover);
102
105
  }
103
106
  .navBtnActive {
104
- background: var(--color-accent-dim);
105
- color: var(--color-accent);
106
- border-color: var(--color-border);
107
- font-weight: 700;
107
+ background: var(--color-accent);
108
+ color: var(--color-bg);
109
+ border-color: var(--color-accent);
110
+ font-weight: 800;
111
+ box-shadow: 0 0 12px var(--color-accent-glow);
108
112
  }
109
113
  .navBtnActive:hover {
110
- background: var(--color-accent-dim);
111
- color: var(--color-accent);
114
+ background: var(--color-accent-hover);
115
+ color: var(--color-bg);
116
+ border-color: var(--color-accent-hover);
112
117
  }
113
118
 
114
119
  /* 📖 "Coming in M2" badge — small inline label that shows the menu structure
115
120
  📖 is honest even before those milestones land. */
116
121
  .comingBadge {
117
- font-size: 9px;
122
+ font-size: 10px;
118
123
  font-weight: 700;
119
124
  font-family: var(--font-mono);
120
125
  color: var(--color-text-dim);
@@ -156,7 +161,7 @@
156
161
  gap: 8px;
157
162
  width: 100%;
158
163
  padding: 7px 10px;
159
- font-size: 12px;
164
+ font-size: 14px;
160
165
  font-weight: 500;
161
166
  color: var(--color-text);
162
167
  background: transparent;
@@ -167,6 +172,11 @@
167
172
  transition: background 100ms;
168
173
  }
169
174
  .menuItem:hover { background: var(--color-bg-hover); }
175
+ .menuItemActive {
176
+ background: var(--color-accent-dim) !important;
177
+ color: var(--color-accent) !important;
178
+ font-weight: 700;
179
+ }
170
180
  .menuItem > span:first-of-type { flex: 1; }
171
181
 
172
182
  /* ─── Search ─── */
@@ -180,10 +190,10 @@
180
190
  border-color: var(--color-accent);
181
191
  box-shadow: 0 0 0 3px var(--color-accent-glow);
182
192
  }
183
- .searchIcon { font-size: 14px; flex-shrink: 0; }
193
+ .searchIcon { font-size: 16px; flex-shrink: 0; }
184
194
  .searchInput {
185
195
  flex: 1; background: none; border: none; outline: none;
186
- color: var(--color-text); font-size: 13px; font-family: var(--font-sans);
196
+ color: var(--color-text); font-size: 15px; font-family: var(--font-sans);
187
197
  min-width: 0;
188
198
  }
189
199
  .searchInput::placeholder { color: var(--color-text-dim); }
@@ -193,7 +203,7 @@
193
203
  display: inline-flex; align-items: center; justify-content: center;
194
204
  padding: 6px 8px; border: 1px solid var(--color-border); border-radius: 6px;
195
205
  background: var(--color-surface); color: var(--color-text);
196
- cursor: pointer; font-size: 16px; transition: all 150ms;
206
+ cursor: pointer; font-size: 18px; transition: all 150ms;
197
207
  }
198
208
  .iconBtn:hover { background: var(--color-bg-hover); border-color: var(--color-border-hover); }
199
209
 
@@ -204,7 +214,7 @@
204
214
  padding: 0 10px; height: 30px;
205
215
  border: 1px solid var(--color-border); border-radius: 6px;
206
216
  background: var(--color-surface); color: var(--color-text);
207
- cursor: pointer; font-size: 12px; font-weight: 600;
217
+ cursor: pointer; font-size: 14px; font-weight: 600;
208
218
  font-family: var(--font-mono);
209
219
  transition: all 150ms;
210
220
  }
@@ -212,12 +222,12 @@
212
222
  border-color: var(--color-accent);
213
223
  background: var(--color-bg-hover);
214
224
  }
215
- .cmdkLabel { font-size: 11px; letter-spacing: 0.5px; }
225
+ .cmdkLabel { font-size: 13px; letter-spacing: 0.5px; }
216
226
 
217
227
  /* ─── AI Latency benchmark button (reused from before) ─── */
218
228
  .benchmarkBtn {
219
229
  display: inline-flex; align-items: center; gap: 6px;
220
- padding: 0 12px; height: 30px; font-size: 12px; font-weight: 700;
230
+ padding: 0 12px; height: 30px; font-size: 14px; font-weight: 700;
221
231
  border: 1px solid var(--color-border); border-radius: 6px;
222
232
  background: var(--color-surface); color: var(--color-text);
223
233
  cursor: pointer; font-family: var(--font-mono); transition: all 150ms;
@@ -307,7 +317,7 @@
307
317
  align-items: center;
308
318
  gap: 10px;
309
319
  padding: 10px 12px;
310
- font-size: 13px;
320
+ font-size: 15px;
311
321
  font-weight: 600;
312
322
  color: var(--color-text-muted);
313
323
  background: transparent;
@@ -329,3 +339,19 @@
329
339
  background: var(--color-border);
330
340
  margin: 6px 0;
331
341
  }
342
+
343
+ /* ─── Keyboard Focus & Micro-interaction Active States ─── */
344
+ .navBtn:focus-visible,
345
+ .iconBtn:focus-visible,
346
+ .cmdkBtn:focus-visible,
347
+ .benchmarkBtn:focus-visible {
348
+ outline: 2px solid var(--color-accent);
349
+ outline-offset: 2px;
350
+ }
351
+
352
+ .navBtn:active,
353
+ .iconBtn:active,
354
+ .cmdkBtn:active,
355
+ .benchmarkBtn:active {
356
+ transform: scale(0.96);
357
+ }