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.
- package/LICENSE +43 -21
- package/README.md +2 -1
- package/changelog/v0.5.33.md +4 -0
- package/changelog/v0.5.34.md +4 -0
- package/changelog/v0.5.35.md +4 -0
- package/package.json +3 -2
- package/sources.js +12 -52
- package/src/core/router-daemon.js +56 -49
- package/src/core/schema-normalizer.js +172 -0
- package/src/core/tool-bootstrap.js +11 -0
- package/src/core/tool-launchers.js +37 -2
- package/src/core/tool-metadata.js +3 -0
- package/web/dist/assets/index-B5tTa7_O.js +40 -0
- package/web/dist/assets/index-r8niexgJ.css +1 -0
- package/web/dist/index.html +2 -2
- package/web/src/App.jsx +93 -105
- package/web/src/components/changelog/ChangelogView.module.css +13 -14
- package/web/src/components/dashboard/FilterBar.jsx +17 -2
- package/web/src/components/dashboard/FilterBar.module.css +68 -21
- package/web/src/components/dashboard/ModelTable.jsx +118 -42
- package/web/src/components/dashboard/ModelTable.module.css +82 -27
- package/web/src/components/help/HelpView.module.css +13 -14
- package/web/src/components/install/InstallEndpointsView.module.css +13 -13
- package/web/src/components/installed/InstalledModelsView.module.css +13 -13
- package/web/src/components/layout/Header.jsx +28 -28
- package/web/src/components/layout/Header.module.css +56 -30
- package/web/src/components/palette/CommandPalette.jsx +14 -14
- package/web/src/components/playground/PlaygroundView.module.css +13 -14
- package/web/src/components/recommend/RecommendView.module.css +13 -11
- package/web/src/components/router/RouterView.module.css +13 -13
- package/web/src/components/tools/ToolPicker.module.css +27 -14
- package/web/src/global.css +58 -31
- package/web/src/hooks/urlState.constants.js +1 -1
- package/web/src/hooks/useUrlState.js +19 -3
- package/web/dist/assets/index-C_tCF0A5.js +0 -40
- 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:
|
|
45
|
+
font-size: 14px;
|
|
28
46
|
border: 0;
|
|
29
47
|
}
|
|
30
48
|
|
|
31
49
|
/* ─── Header ─── */
|
|
32
50
|
.th {
|
|
33
|
-
padding:
|
|
34
|
-
font-size:
|
|
35
|
-
font-weight:
|
|
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.
|
|
38
|
-
color: var(--color-
|
|
39
|
-
background: var(--color-
|
|
40
|
-
border-bottom:
|
|
41
|
-
border-right: 1px solid
|
|
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-
|
|
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
|
|
124
|
-
📖
|
|
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:
|
|
155
|
+
font-size: 13px;
|
|
134
156
|
color: var(--color-text-muted);
|
|
135
|
-
|
|
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:
|
|
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.
|
|
180
|
-
.sortIconActive { color: var(--color-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
242
|
-
.modelId { font-family: var(--font-mono); font-size:
|
|
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-
|
|
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:
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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:
|
|
21
|
-
max-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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-
|
|
14
|
-
border: 1px solid var(--border, #
|
|
11
|
+
background: var(--color-bg-card, #080908);
|
|
12
|
+
border: 1px solid var(--color-border, #161a16);
|
|
15
13
|
border-radius: 12px;
|
|
16
|
-
width:
|
|
17
|
-
max-width:
|
|
18
|
-
|
|
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
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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-
|
|
14
|
-
border: 1px solid var(--border, #
|
|
11
|
+
background: var(--color-bg-card, #080908);
|
|
12
|
+
border: 1px solid var(--color-border, #161a16);
|
|
15
13
|
border-radius: 12px;
|
|
16
|
-
width:
|
|
17
|
-
max-width:
|
|
18
|
-
|
|
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
|
|
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',
|
|
26
|
-
{ id: '
|
|
27
|
-
{ id: '
|
|
28
|
-
{ id: '
|
|
29
|
-
{ id: '
|
|
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
|
|
34
|
-
// 📖 and Installed Models are still M4.
|
|
32
|
+
// 📖 Overflow menu items
|
|
35
33
|
const MENU_ITEMS = [
|
|
36
|
-
{ id: '
|
|
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
|
-
<
|
|
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:
|
|
50
|
-
.
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
85
|
+
gap: 6px;
|
|
86
86
|
height: 30px;
|
|
87
|
-
padding: 0
|
|
88
|
-
font-size:
|
|
87
|
+
padding: 0 12px;
|
|
88
|
+
font-size: 13px;
|
|
89
89
|
font-weight: 600;
|
|
90
|
-
font-family: var(--font-
|
|
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:
|
|
93
|
-
border: 1px solid
|
|
94
|
+
background: var(--color-surface);
|
|
95
|
+
border: 1px solid var(--color-border);
|
|
94
96
|
border-radius: 6px;
|
|
95
97
|
cursor: pointer;
|
|
96
|
-
transition:
|
|
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
|
|
105
|
-
color: var(--color-
|
|
106
|
-
border-color: var(--color-
|
|
107
|
-
font-weight:
|
|
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-
|
|
111
|
-
color: var(--color-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
+
}
|