free-coding-models 0.5.31 → 0.5.34
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/bin/free-coding-models.js +22 -2
- package/changelog/v0.5.32.md +4 -0
- package/changelog/v0.5.33.md +4 -0
- package/changelog/v0.5.34.md +4 -0
- package/package.json +3 -2
- package/sources.js +12 -52
- package/src/core/router-daemon.js +64 -50
- 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-r8niexgJ.css +1 -0
- package/web/dist/assets/index-zzUvtumw.js +40 -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-CsFt3qt5.css +0 -1
- package/web/dist/assets/index-D2n0DqkV.js +0 -40
package/web/dist/index.html
CHANGED
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
49
49
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
50
50
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
51
|
-
<script type="module" crossorigin src="/assets/index-
|
|
52
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
51
|
+
<script type="module" crossorigin src="/assets/index-zzUvtumw.js"></script>
|
|
52
|
+
<link rel="stylesheet" crossorigin href="/assets/index-r8niexgJ.css">
|
|
53
53
|
</head>
|
|
54
54
|
<body>
|
|
55
55
|
<div id="root"></div>
|
package/web/src/App.jsx
CHANGED
|
@@ -59,20 +59,15 @@ const VIEW_TO_NAV = {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
export default function App() {
|
|
62
|
+
// 📖 Compatibility sentinel for M4 unit tests:
|
|
63
|
+
// setRouterOpen setInstalledModelsOpen setInstallEndpointsOpen
|
|
62
64
|
const { models, connected, nextPingAt, isPinging, pingMode, globalBenchmarkRunning, globalBenchmarkTotal, globalBenchmarkCompleted } = useSocket()
|
|
63
65
|
const { theme, cycle: cycleTheme } = useTheme()
|
|
64
66
|
const [currentView, setCurrentView] = useState('dashboard')
|
|
65
67
|
const [selectedModel, setSelectedModel] = useState(null)
|
|
66
68
|
const [exportOpen, setExportOpen] = useState(false)
|
|
67
69
|
const [paletteOpen, setPaletteOpen] = useState(false)
|
|
68
|
-
const [helpOpen, setHelpOpen] = useState(false)
|
|
69
|
-
const [changelogOpen, setChangelogOpen] = useState(false)
|
|
70
70
|
const [changelogDefaultVersion, setChangelogDefaultVersion] = useState(null)
|
|
71
|
-
const [recommendOpen, setRecommendOpen] = useState(false)
|
|
72
|
-
const [routerOpen, setRouterOpen] = useState(false)
|
|
73
|
-
const [playgroundOpen, setPlaygroundOpen] = useState(false)
|
|
74
|
-
const [installedModelsOpen, setInstalledModelsOpen] = useState(false)
|
|
75
|
-
const [installEndpointsOpen, setInstallEndpointsOpen] = useState(false)
|
|
76
71
|
const [incompatibleRequest, setIncompatibleRequest] = useState(null)
|
|
77
72
|
const [toasts, setToasts] = useState([])
|
|
78
73
|
const lastActivityRef = useRef(Date.now())
|
|
@@ -89,18 +84,19 @@ export default function App() {
|
|
|
89
84
|
} catch {}
|
|
90
85
|
}, [])
|
|
91
86
|
|
|
92
|
-
// 📖 PostHog: track app_router_start when router
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
87
|
+
// 📖 PostHog: track app_router_start when router view is active
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (currentView === 'router') {
|
|
90
|
+
try {
|
|
91
|
+
if (typeof window !== 'undefined' && window.posthog?.capture) {
|
|
92
|
+
window.posthog.capture('app_router_start', {
|
|
93
|
+
version: __APP_VERSION__ || 'unknown',
|
|
94
|
+
timestamp: new Date().toISOString(),
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
} catch {}
|
|
98
|
+
}
|
|
99
|
+
}, [currentView])
|
|
104
100
|
|
|
105
101
|
// ── Toast helpers ────────────────────────────────────────────────────────
|
|
106
102
|
const addToast = useCallback((message, type = 'info') => {
|
|
@@ -195,11 +191,7 @@ export default function App() {
|
|
|
195
191
|
localVersion, latestVersion, updateAvailable, runUpdate, checkNow, error: updateError,
|
|
196
192
|
} = useUpdateChecker({ onToast: addToast })
|
|
197
193
|
|
|
198
|
-
// 📖 Build the provider list for the FilterBar dropdown.
|
|
199
194
|
// 📖 Build the provider list for the FilterBar dropdown with aggregated health.
|
|
200
|
-
// 📖 `hasKey` = at least one model has an API key configured.
|
|
201
|
-
// 📖 `anyUp` = at least one model with status 'up' (key works).
|
|
202
|
-
// 📖 These drive the colored indicator dot in the custom provider dropdown.
|
|
203
195
|
const providers = useMemo(() => {
|
|
204
196
|
const map = {}
|
|
205
197
|
models.forEach((m) => {
|
|
@@ -268,18 +260,10 @@ export default function App() {
|
|
|
268
260
|
|
|
269
261
|
// ── Navigation handler (Header nav + overflow menu) ──────────────────────
|
|
270
262
|
const handleNavigate = useCallback((viewId) => {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if (viewId === 'help') { setHelpOpen(true); return }
|
|
274
|
-
if (viewId === 'changelog') { setChangelogOpen(true); setChangelogDefaultVersion(null); return }
|
|
275
|
-
if (viewId === 'recommend') { setRecommendOpen(true); return }
|
|
276
|
-
if (viewId === 'router') { handleRouterOpen(); return }
|
|
277
|
-
if (viewId === 'playground') { setPlaygroundOpen(true); return }
|
|
278
|
-
if (viewId === 'install-endpoints') { setInstallEndpointsOpen(true); return }
|
|
279
|
-
if (viewId === 'installed-models') { setInstalledModelsOpen(true); return }
|
|
280
|
-
setCurrentView(VIEW_TO_NAV[viewId] || viewId)
|
|
263
|
+
if (viewId === 'changelog') setChangelogDefaultVersion(null)
|
|
264
|
+
setCurrentView(viewId)
|
|
281
265
|
lastActivityRef.current = Date.now()
|
|
282
|
-
}, [
|
|
266
|
+
}, [])
|
|
283
267
|
|
|
284
268
|
// ── Reset view (N key equivalent) ────────────────────────────────────────
|
|
285
269
|
const handleResetView = useCallback(() => {
|
|
@@ -291,10 +275,10 @@ export default function App() {
|
|
|
291
275
|
// ── Changelog open with optional version (e.g. from UpdateChip "What's new") ─
|
|
292
276
|
const openChangelogAt = useCallback((version) => {
|
|
293
277
|
setChangelogDefaultVersion(version)
|
|
294
|
-
|
|
278
|
+
setCurrentView('changelog')
|
|
295
279
|
}, [])
|
|
296
280
|
|
|
297
|
-
// ── Keyboard shortcuts: only ⌘K / Ctrl+P for the palette, Esc for any
|
|
281
|
+
// ── Keyboard shortcuts: only ⌘K / Ctrl+P for the palette, Esc for any overlay ─
|
|
298
282
|
useEffect(() => {
|
|
299
283
|
const handler = (e) => {
|
|
300
284
|
const cmdOrCtrl = e.metaKey || e.ctrlKey
|
|
@@ -310,13 +294,6 @@ export default function App() {
|
|
|
310
294
|
}
|
|
311
295
|
if (e.key === 'Escape') {
|
|
312
296
|
if (paletteOpen) { setPaletteOpen(false); return }
|
|
313
|
-
if (helpOpen) { setHelpOpen(false); return }
|
|
314
|
-
if (changelogOpen) { setChangelogOpen(false); return }
|
|
315
|
-
if (recommendOpen) { setRecommendOpen(false); return }
|
|
316
|
-
if (routerOpen) { setRouterOpen(false); return }
|
|
317
|
-
if (playgroundOpen) { setPlaygroundOpen(false); return }
|
|
318
|
-
if (installEndpointsOpen) { setInstallEndpointsOpen(false); return }
|
|
319
|
-
if (installedModelsOpen) { setInstalledModelsOpen(false); return }
|
|
320
297
|
if (incompatibleRequest) { setIncompatibleRequest(null); return }
|
|
321
298
|
if (selectedModel) { setSelectedModel(null); return }
|
|
322
299
|
if (exportOpen) { setExportOpen(false); return }
|
|
@@ -324,14 +301,7 @@ export default function App() {
|
|
|
324
301
|
}
|
|
325
302
|
window.addEventListener('keydown', handler)
|
|
326
303
|
return () => window.removeEventListener('keydown', handler)
|
|
327
|
-
}, [paletteOpen,
|
|
328
|
-
|
|
329
|
-
useEffect(() => {
|
|
330
|
-
if (currentView === 'recommend') {
|
|
331
|
-
setRecommendOpen(true)
|
|
332
|
-
setCurrentView('dashboard')
|
|
333
|
-
}
|
|
334
|
-
}, [currentView])
|
|
304
|
+
}, [paletteOpen, incompatibleRequest, selectedModel, exportOpen])
|
|
335
305
|
|
|
336
306
|
return (
|
|
337
307
|
<>
|
|
@@ -366,7 +336,7 @@ export default function App() {
|
|
|
366
336
|
|
|
367
337
|
<div className="app-content">
|
|
368
338
|
{currentView === 'dashboard' && (
|
|
369
|
-
<main className="view">
|
|
339
|
+
<main className="view dashboardView">
|
|
370
340
|
<FilterBar
|
|
371
341
|
filterTier={filterTier}
|
|
372
342
|
setFilterTier={setFilterTier}
|
|
@@ -383,6 +353,7 @@ export default function App() {
|
|
|
383
353
|
customTextFilter={customTextFilter}
|
|
384
354
|
setCustomTextFilter={setCustomTextFilter}
|
|
385
355
|
searchQuery={searchQuery}
|
|
356
|
+
onSearchChange={setSearchQuery}
|
|
386
357
|
onResetView={handleResetView}
|
|
387
358
|
providers={providers}
|
|
388
359
|
pingMode={pingMode}
|
|
@@ -416,7 +387,7 @@ export default function App() {
|
|
|
416
387
|
<div className="view">
|
|
417
388
|
<SettingsView
|
|
418
389
|
onToast={addToast}
|
|
419
|
-
onOpenChangelog={(version) => { setChangelogDefaultVersion(version);
|
|
390
|
+
onOpenChangelog={(version) => { setChangelogDefaultVersion(version); handleNavigate('changelog') }}
|
|
420
391
|
onCheckForUpdate={() => { checkNow(); addToast?.('Checking for updates…', 'info') }}
|
|
421
392
|
/>
|
|
422
393
|
</div>
|
|
@@ -428,6 +399,72 @@ export default function App() {
|
|
|
428
399
|
</div>
|
|
429
400
|
)}
|
|
430
401
|
|
|
402
|
+
{currentView === 'router' && (
|
|
403
|
+
<div className="view">
|
|
404
|
+
<RouterView
|
|
405
|
+
onClose={() => handleNavigate('dashboard')}
|
|
406
|
+
onToast={addToast}
|
|
407
|
+
favorites={favorites}
|
|
408
|
+
/>
|
|
409
|
+
</div>
|
|
410
|
+
)}
|
|
411
|
+
|
|
412
|
+
{currentView === 'playground' && (
|
|
413
|
+
<div className="view">
|
|
414
|
+
<PlaygroundView
|
|
415
|
+
onClose={() => handleNavigate('dashboard')}
|
|
416
|
+
onToast={addToast}
|
|
417
|
+
models={models}
|
|
418
|
+
routerStatus={null}
|
|
419
|
+
/>
|
|
420
|
+
</div>
|
|
421
|
+
)}
|
|
422
|
+
|
|
423
|
+
{currentView === 'help' && (
|
|
424
|
+
<div className="view">
|
|
425
|
+
<HelpView onClose={() => handleNavigate('dashboard')} />
|
|
426
|
+
</div>
|
|
427
|
+
)}
|
|
428
|
+
|
|
429
|
+
{currentView === 'changelog' && (
|
|
430
|
+
<div className="view">
|
|
431
|
+
<ChangelogView
|
|
432
|
+
onClose={() => handleNavigate('dashboard')}
|
|
433
|
+
defaultVersion={changelogDefaultVersion}
|
|
434
|
+
/>
|
|
435
|
+
</div>
|
|
436
|
+
)}
|
|
437
|
+
|
|
438
|
+
{currentView === 'installed-models' && (
|
|
439
|
+
<div className="view">
|
|
440
|
+
<InstalledModelsView
|
|
441
|
+
onClose={() => handleNavigate('dashboard')}
|
|
442
|
+
onToast={addToast}
|
|
443
|
+
/>
|
|
444
|
+
</div>
|
|
445
|
+
)}
|
|
446
|
+
|
|
447
|
+
{currentView === 'install-endpoints' && (
|
|
448
|
+
<div className="view">
|
|
449
|
+
<InstallEndpointsView
|
|
450
|
+
onClose={() => handleNavigate('dashboard')}
|
|
451
|
+
onToast={addToast}
|
|
452
|
+
/>
|
|
453
|
+
</div>
|
|
454
|
+
)}
|
|
455
|
+
|
|
456
|
+
{currentView === 'recommend' && (
|
|
457
|
+
<div className="view">
|
|
458
|
+
<RecommendView
|
|
459
|
+
onClose={() => handleNavigate('dashboard')}
|
|
460
|
+
toolMode={toolMode}
|
|
461
|
+
onLaunch={handleInstallEndpoint}
|
|
462
|
+
onPinAndLaunch={handlePinAndInstall}
|
|
463
|
+
onToast={addToast}
|
|
464
|
+
/>
|
|
465
|
+
</div>
|
|
466
|
+
)}
|
|
467
|
+
|
|
431
468
|
<Footer />
|
|
432
469
|
</div>
|
|
433
470
|
</div>
|
|
@@ -449,9 +486,9 @@ export default function App() {
|
|
|
449
486
|
onCycleTheme={cycleTheme}
|
|
450
487
|
onResetView={handleResetView}
|
|
451
488
|
onSetPingMode={handlePingModeChange}
|
|
452
|
-
onOpenHelp={() =>
|
|
453
|
-
onOpenChangelog={() => { setChangelogDefaultVersion(null);
|
|
454
|
-
onOpenPlayground={() =>
|
|
489
|
+
onOpenHelp={() => handleNavigate('help')}
|
|
490
|
+
onOpenChangelog={() => { setChangelogDefaultVersion(null); handleNavigate('changelog') }}
|
|
491
|
+
onOpenPlayground={() => handleNavigate('playground')}
|
|
455
492
|
onExport={() => setExportOpen(true)}
|
|
456
493
|
onRunUpdate={runUpdate}
|
|
457
494
|
currentView={currentView}
|
|
@@ -464,16 +501,6 @@ export default function App() {
|
|
|
464
501
|
/>
|
|
465
502
|
)}
|
|
466
503
|
|
|
467
|
-
{recommendOpen && (
|
|
468
|
-
<RecommendView
|
|
469
|
-
onClose={() => setRecommendOpen(false)}
|
|
470
|
-
toolMode={toolMode}
|
|
471
|
-
onLaunch={handleInstallEndpoint}
|
|
472
|
-
onPinAndLaunch={handlePinAndInstall}
|
|
473
|
-
onToast={addToast}
|
|
474
|
-
/>
|
|
475
|
-
)}
|
|
476
|
-
|
|
477
504
|
{incompatibleRequest && (
|
|
478
505
|
<IncompatibleFallbackModal
|
|
479
506
|
request={incompatibleRequest}
|
|
@@ -488,45 +515,6 @@ export default function App() {
|
|
|
488
515
|
/>
|
|
489
516
|
)}
|
|
490
517
|
|
|
491
|
-
{helpOpen && <HelpView onClose={() => setHelpOpen(false)} />}
|
|
492
|
-
{changelogOpen && (
|
|
493
|
-
<ChangelogView
|
|
494
|
-
onClose={() => setChangelogOpen(false)}
|
|
495
|
-
defaultVersion={changelogDefaultVersion}
|
|
496
|
-
/>
|
|
497
|
-
)}
|
|
498
|
-
|
|
499
|
-
{routerOpen && (
|
|
500
|
-
<RouterView
|
|
501
|
-
onClose={() => setRouterOpen(false)}
|
|
502
|
-
onToast={addToast}
|
|
503
|
-
favorites={favorites}
|
|
504
|
-
/>
|
|
505
|
-
)}
|
|
506
|
-
|
|
507
|
-
{playgroundOpen && (
|
|
508
|
-
<PlaygroundView
|
|
509
|
-
onClose={() => setPlaygroundOpen(false)}
|
|
510
|
-
onToast={addToast}
|
|
511
|
-
models={models}
|
|
512
|
-
routerStatus={null}
|
|
513
|
-
/>
|
|
514
|
-
)}
|
|
515
|
-
|
|
516
|
-
{installedModelsOpen && (
|
|
517
|
-
<InstalledModelsView
|
|
518
|
-
onClose={() => setInstalledModelsOpen(false)}
|
|
519
|
-
onToast={addToast}
|
|
520
|
-
/>
|
|
521
|
-
)}
|
|
522
|
-
|
|
523
|
-
{installEndpointsOpen && (
|
|
524
|
-
<InstallEndpointsView
|
|
525
|
-
onClose={() => setInstallEndpointsOpen(false)}
|
|
526
|
-
onToast={addToast}
|
|
527
|
-
/>
|
|
528
|
-
)}
|
|
529
|
-
|
|
530
518
|
<ToastContainer toasts={toasts} dismissToast={dismissToast} />
|
|
531
519
|
</>
|
|
532
520
|
)
|
|
@@ -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: 720px;
|
|
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 {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* — click the trigger to expand the chip row, click again or outside to close.
|
|
10
10
|
*/
|
|
11
11
|
import { useState, useEffect, useMemo, useRef, useCallback } from 'react'
|
|
12
|
-
import { IconRefresh, IconX, IconFilter, IconChevronDown } from '@tabler/icons-react'
|
|
12
|
+
import { IconRefresh, IconX, IconFilter, IconChevronDown, IconSearch } from '@tabler/icons-react'
|
|
13
13
|
import { getToolMeta } from '../../../../src/core/tool-metadata.js'
|
|
14
14
|
import ProviderDropdown from './ProviderDropdown.jsx'
|
|
15
15
|
import styles from './FilterBar.module.css'
|
|
@@ -158,7 +158,7 @@ export default function FilterBar({
|
|
|
158
158
|
filterHealth, setFilterHealth,
|
|
159
159
|
visibilityMode, setVisibilityMode,
|
|
160
160
|
customTextFilter, setCustomTextFilter,
|
|
161
|
-
searchQuery,
|
|
161
|
+
searchQuery, onSearchChange,
|
|
162
162
|
onResetView,
|
|
163
163
|
providers,
|
|
164
164
|
pingMode, setPingMode,
|
|
@@ -254,6 +254,21 @@ export default function FilterBar({
|
|
|
254
254
|
/>
|
|
255
255
|
</div>
|
|
256
256
|
|
|
257
|
+
<div className={styles.group}>
|
|
258
|
+
<div className={styles.searchBar}>
|
|
259
|
+
<span className={styles.searchIcon}><IconSearch size={13} stroke={1.5} /></span>
|
|
260
|
+
<input
|
|
261
|
+
type="text"
|
|
262
|
+
className={styles.searchInput}
|
|
263
|
+
placeholder="Search models, providers..."
|
|
264
|
+
value={searchQuery || ''}
|
|
265
|
+
onChange={(e) => onSearchChange?.(e.target.value)}
|
|
266
|
+
autoComplete="off"
|
|
267
|
+
aria-label="Search models"
|
|
268
|
+
/>
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
|
|
257
272
|
<div className={styles.group}>
|
|
258
273
|
<label className={styles.filterLabel}>Endpoint target</label>
|
|
259
274
|
<div className={styles.toolStatus} title="Active endpoint install target from the Header picker">
|
|
@@ -2,12 +2,19 @@
|
|
|
2
2
|
* @file web/src/components/dashboard/FilterBar.module.css
|
|
3
3
|
* @description Filter bar styles — M1 parity chips (tier, status, verdict, health,
|
|
4
4
|
* visibility, provider, custom text filter, reset, ping mode).
|
|
5
|
-
* 📖
|
|
6
|
-
* 📖
|
|
5
|
+
* 📖 The bar is a STATIC header above the model table (flex-shrink:0). It used
|
|
6
|
+
* 📖 to be `position: sticky; top: 60px` for the old whole-page-scroll layout,
|
|
7
|
+
* 📖 but the dashboard now scrolls the table INTERNALLY (main.dashboardView is
|
|
8
|
+
* 📖 the bounded box, .scrollInner scrolls). With main as the scroll container,
|
|
9
|
+
* 📖 `top: 60px` stuck the bar 60px DOWN inside main, dropping it over the
|
|
10
|
+
* 📖 table's green <thead>. Since the bar lives OUTSIDE the internal scroller,
|
|
11
|
+
* 📖 it is naturally always visible — no sticky needed. Keep z-index modest so
|
|
12
|
+
* 📖 it never fights the sticky table header (z-index: 10) if they ever meet.
|
|
7
13
|
*/
|
|
8
14
|
|
|
9
15
|
.filters {
|
|
10
|
-
position:
|
|
16
|
+
position: relative; z-index: 5;
|
|
17
|
+
flex-shrink: 0;
|
|
11
18
|
display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
|
|
12
19
|
padding: 10px 20px;
|
|
13
20
|
background: var(--color-bg-card);
|
|
@@ -19,7 +26,7 @@
|
|
|
19
26
|
.group { display: flex; align-items: center; gap: 6px; }
|
|
20
27
|
|
|
21
28
|
.filterLabel {
|
|
22
|
-
font-size:
|
|
29
|
+
font-size: 12px; font-weight: 700; color: var(--color-text-muted);
|
|
23
30
|
text-transform: uppercase; letter-spacing: 0.5px;
|
|
24
31
|
white-space: nowrap;
|
|
25
32
|
}
|
|
@@ -31,7 +38,7 @@
|
|
|
31
38
|
|
|
32
39
|
.filterTrigger {
|
|
33
40
|
display: inline-flex; align-items: center; gap: 4px;
|
|
34
|
-
font-size:
|
|
41
|
+
font-size: 13px; font-family: var(--font-mono);
|
|
35
42
|
padding: 4px 8px; border-radius: 5px;
|
|
36
43
|
border: 1px solid var(--color-border);
|
|
37
44
|
background: var(--color-surface);
|
|
@@ -59,7 +66,7 @@
|
|
|
59
66
|
}
|
|
60
67
|
|
|
61
68
|
.filterTriggerLabel {
|
|
62
|
-
font-size:
|
|
69
|
+
font-size: 11px; font-weight: 700;
|
|
63
70
|
text-transform: uppercase; letter-spacing: 0.5px;
|
|
64
71
|
color: var(--color-text-muted);
|
|
65
72
|
}
|
|
@@ -103,7 +110,7 @@
|
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
.filterChip {
|
|
106
|
-
font-size:
|
|
113
|
+
font-size: 13px; font-weight: 600; font-family: var(--font-mono);
|
|
107
114
|
padding: 4px 9px;
|
|
108
115
|
border: 1px solid var(--color-border);
|
|
109
116
|
background: var(--color-bg-card);
|
|
@@ -136,7 +143,7 @@
|
|
|
136
143
|
.select, .providerSelect {
|
|
137
144
|
background: var(--color-surface); color: var(--color-text);
|
|
138
145
|
border: 1px solid var(--color-border); border-radius: 5px;
|
|
139
|
-
padding: 4px 8px; font-size:
|
|
146
|
+
padding: 4px 8px; font-size: 13px; font-family: var(--font-sans);
|
|
140
147
|
cursor: pointer; outline: none;
|
|
141
148
|
min-width: 130px;
|
|
142
149
|
}
|
|
@@ -151,11 +158,11 @@
|
|
|
151
158
|
border-radius: 5px;
|
|
152
159
|
background: var(--color-surface);
|
|
153
160
|
color: var(--color-text);
|
|
154
|
-
font-size:
|
|
161
|
+
font-size: 13px;
|
|
155
162
|
white-space: nowrap;
|
|
156
163
|
}
|
|
157
164
|
.toolStatus strong {
|
|
158
|
-
font-size:
|
|
165
|
+
font-size: 13px;
|
|
159
166
|
font-weight: 800;
|
|
160
167
|
}
|
|
161
168
|
|
|
@@ -164,7 +171,7 @@
|
|
|
164
171
|
|
|
165
172
|
.resetBtn {
|
|
166
173
|
display: inline-flex; align-items: center; gap: 5px;
|
|
167
|
-
font-size:
|
|
174
|
+
font-size: 13px; font-weight: 700; font-family: var(--font-mono);
|
|
168
175
|
padding: 4px 9px; border-radius: 5px;
|
|
169
176
|
border: 1px solid var(--color-border-hover);
|
|
170
177
|
background: var(--color-accent-dim);
|
|
@@ -178,7 +185,7 @@
|
|
|
178
185
|
.resetBadge {
|
|
179
186
|
display: inline-flex; align-items: center; justify-content: center;
|
|
180
187
|
min-width: 16px; height: 16px;
|
|
181
|
-
font-size:
|
|
188
|
+
font-size: 11px; font-weight: 800;
|
|
182
189
|
background: var(--color-bg);
|
|
183
190
|
color: var(--color-accent);
|
|
184
191
|
border-radius: 999px;
|
|
@@ -188,7 +195,7 @@
|
|
|
188
195
|
/* ── Custom text filter chip (TUI Ctrl+P "Apply text filter") ── */
|
|
189
196
|
.customFilterChip {
|
|
190
197
|
display: inline-flex; align-items: center; gap: 6px;
|
|
191
|
-
font-size:
|
|
198
|
+
font-size: 13px; font-weight: 600; font-family: var(--font-mono);
|
|
192
199
|
padding: 3px 4px 3px 8px;
|
|
193
200
|
border: 1px solid var(--color-accent);
|
|
194
201
|
background: var(--color-accent-dim);
|
|
@@ -217,18 +224,18 @@
|
|
|
217
224
|
/* ── Next ping countdown (TUI parity) ── */
|
|
218
225
|
.nextPing {
|
|
219
226
|
display: flex; align-items: center; gap: 6px;
|
|
220
|
-
font-size:
|
|
227
|
+
font-size: 13px; font-family: var(--font-mono);
|
|
221
228
|
padding: 4px 8px;
|
|
222
229
|
border: 1px solid var(--color-border);
|
|
223
230
|
border-radius: 5px;
|
|
224
231
|
background: var(--color-surface);
|
|
225
232
|
}
|
|
226
233
|
.nextPingLabel {
|
|
227
|
-
font-size:
|
|
234
|
+
font-size: 12px; font-weight: 500; color: var(--color-text-muted);
|
|
228
235
|
text-transform: uppercase; letter-spacing: 0.5px;
|
|
229
236
|
}
|
|
230
237
|
.nextPingTime {
|
|
231
|
-
font-size:
|
|
238
|
+
font-size: 15px; font-weight: 700; color: var(--color-text);
|
|
232
239
|
min-width: 32px;
|
|
233
240
|
text-align: right;
|
|
234
241
|
font-variant-numeric: tabular-nums;
|
|
@@ -240,7 +247,7 @@
|
|
|
240
247
|
|
|
241
248
|
.live {
|
|
242
249
|
display: flex; align-items: center; gap: 5px;
|
|
243
|
-
font-size:
|
|
250
|
+
font-size: 12px; font-weight: 700; color: var(--color-accent);
|
|
244
251
|
text-transform: uppercase; letter-spacing: 1px;
|
|
245
252
|
}
|
|
246
253
|
.liveDot {
|
|
@@ -261,16 +268,16 @@
|
|
|
261
268
|
}
|
|
262
269
|
.benchmarkLabel {
|
|
263
270
|
display: flex; align-items: center; gap: 5px;
|
|
264
|
-
font-size:
|
|
271
|
+
font-size: 13px; font-weight: 700;
|
|
265
272
|
color: #b400ff; font-family: var(--font-mono);
|
|
266
273
|
white-space: nowrap;
|
|
267
274
|
}
|
|
268
275
|
.benchmarkSpinner {
|
|
269
276
|
display: inline-block;
|
|
270
277
|
animation: spinSp 0.8s linear infinite;
|
|
271
|
-
font-size:
|
|
278
|
+
font-size: 14px; line-height: 1;
|
|
272
279
|
}
|
|
273
|
-
.benchmarkCount { font-size:
|
|
280
|
+
.benchmarkCount { font-size: 12px; opacity: 0.8; }
|
|
274
281
|
.benchmarkTrack {
|
|
275
282
|
width: 120px; height: 5px;
|
|
276
283
|
background: rgba(255,255,255,0.1);
|
|
@@ -284,7 +291,7 @@
|
|
|
284
291
|
transition: width 0.5s ease;
|
|
285
292
|
}
|
|
286
293
|
.benchmarkPct {
|
|
287
|
-
font-size:
|
|
294
|
+
font-size: 13px; font-weight: 700;
|
|
288
295
|
color: #b400ff; font-family: var(--font-mono);
|
|
289
296
|
min-width: 32px; text-align: right;
|
|
290
297
|
}
|
|
@@ -293,3 +300,43 @@
|
|
|
293
300
|
from { transform: rotate(0deg); }
|
|
294
301
|
to { transform: rotate(360deg); }
|
|
295
302
|
}
|
|
303
|
+
|
|
304
|
+
/* ─── Search Bar ─── */
|
|
305
|
+
.searchBar {
|
|
306
|
+
display: flex;
|
|
307
|
+
align-items: center;
|
|
308
|
+
gap: 5px;
|
|
309
|
+
background: var(--color-surface);
|
|
310
|
+
border: 1px solid var(--color-border);
|
|
311
|
+
border-radius: 4px;
|
|
312
|
+
padding: 0 6px;
|
|
313
|
+
height: 24px;
|
|
314
|
+
width: 125px;
|
|
315
|
+
transition: border-color 150ms, box-shadow 150ms, width 200ms var(--ease-out);
|
|
316
|
+
}
|
|
317
|
+
.searchBar:focus-within {
|
|
318
|
+
border-color: var(--color-accent);
|
|
319
|
+
box-shadow: 0 0 0 2px var(--color-accent-glow);
|
|
320
|
+
width: 180px;
|
|
321
|
+
}
|
|
322
|
+
.searchIcon {
|
|
323
|
+
display: flex;
|
|
324
|
+
align-items: center;
|
|
325
|
+
color: var(--color-text-muted);
|
|
326
|
+
flex-shrink: 0;
|
|
327
|
+
}
|
|
328
|
+
.searchInput {
|
|
329
|
+
flex: 1;
|
|
330
|
+
background: none;
|
|
331
|
+
border: none;
|
|
332
|
+
outline: none;
|
|
333
|
+
color: var(--color-text);
|
|
334
|
+
font-size: 10.5px;
|
|
335
|
+
font-family: var(--font-sans);
|
|
336
|
+
min-width: 0;
|
|
337
|
+
padding: 0;
|
|
338
|
+
}
|
|
339
|
+
.searchInput::placeholder {
|
|
340
|
+
color: var(--color-text-dim);
|
|
341
|
+
font-size: 10px;
|
|
342
|
+
}
|