free-coding-models 0.5.0 → 0.5.4
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/README.md +40 -6
- package/bin/free-coding-models.js +10 -0
- package/changelog/v0.5.1.md +24 -0
- package/changelog/v0.5.4.md +24 -0
- package/package.json +11 -3
- package/sources.js +2 -0
- package/src/core/router-daemon.js +166 -1
- package/src/core/utils.js +2 -0
- package/src/tui/cli-help.js +2 -0
- package/src/tui/key-handler.js +1 -1
- package/src/tui/render-table.js +1 -1
- package/web/README.md +8 -5
- package/web/assets/providers/aistudio/aistudio-text.svg +1 -0
- package/web/assets/providers/aistudio/aistudio.svg +1 -0
- package/web/assets/providers/cerebras/cerebras-brand-color.svg +1 -0
- package/web/assets/providers/cerebras/cerebras-brand.svg +1 -0
- package/web/assets/providers/cerebras/cerebras-color.svg +1 -0
- package/web/assets/providers/cerebras/cerebras-text.svg +1 -0
- package/web/assets/providers/cerebras/cerebras.svg +1 -0
- package/web/assets/providers/cloudflare/cloudflare-color.svg +1 -0
- package/web/assets/providers/cloudflare/cloudflare-text.svg +1 -0
- package/web/assets/providers/cloudflare/cloudflare.svg +1 -0
- package/web/assets/providers/gemini/gemini-color.svg +1 -0
- package/web/assets/providers/gemini/gemini-text.svg +1 -0
- package/web/assets/providers/gemini/gemini.svg +1 -0
- package/web/assets/providers/github/github-text.svg +1 -0
- package/web/assets/providers/github/github.svg +1 -0
- package/web/assets/providers/groq/groq-text.svg +1 -0
- package/web/assets/providers/groq/groq.svg +1 -0
- package/web/assets/providers/mistral/mistral-color.svg +1 -0
- package/web/assets/providers/mistral/mistral-text.svg +1 -0
- package/web/assets/providers/mistral/mistral.svg +1 -0
- package/web/assets/providers/nvidia/nvidia-color.svg +1 -0
- package/web/assets/providers/nvidia/nvidia-text.svg +1 -0
- package/web/assets/providers/nvidia/nvidia.svg +1 -0
- package/web/assets/providers/opencode/opencode-text.svg +1 -0
- package/web/assets/providers/opencode/opencode.svg +1 -0
- package/web/assets/providers/openrouter/openrouter (1).svg +1 -0
- package/web/assets/providers/openrouter/openrouter-text.svg +1 -0
- package/web/assets/providers/openrouter/openrouter.svg +1 -0
- package/web/assets/providers/ovhcloud/ovhcloud.svg +12 -0
- package/web/assets/providers/qwen/qwen-color.svg +1 -0
- package/web/assets/providers/qwen/qwen-text.svg +1 -0
- package/web/assets/providers/qwen/qwen.svg +1 -0
- package/web/assets/providers/sambanova/sambanova-color.svg +1 -0
- package/web/assets/providers/sambanova/sambanova-text.svg +1 -0
- package/web/assets/providers/sambanova/sambanova.svg +1 -0
- package/web/assets/providers/scalewaylogo/ScalewayLogo.svg +13 -0
- package/web/assets/providers/zai/zai-text.svg +1 -0
- package/web/assets/providers/zai/zai.svg +1 -0
- package/web/dist/assets/index-BoWmUveV.js +39 -0
- package/web/dist/assets/index-BrpHevg4.css +1 -0
- package/web/dist/favicon.ico +0 -0
- package/web/dist/favicons/apple-touch-icon.png +0 -0
- package/web/dist/favicons/browserconfig.xml +12 -0
- package/web/dist/favicons/favicon-16x16.png +0 -0
- package/web/dist/favicons/favicon-192x192.png +0 -0
- package/web/dist/favicons/favicon-32x32.png +0 -0
- package/web/dist/favicons/favicon-48x48.png +0 -0
- package/web/dist/favicons/favicon-512x512.png +0 -0
- package/web/dist/favicons/favicon-96x96.png +0 -0
- package/web/dist/favicons/favicon.ico +0 -0
- package/web/dist/favicons/mstile-150x150.png +0 -0
- package/web/dist/favicons/mstile-310x150.png +0 -0
- package/web/dist/favicons/mstile-310x310.png +0 -0
- package/web/dist/favicons/mstile-512x512.png +0 -0
- package/web/dist/favicons/mstile-70x70.png +0 -0
- package/web/dist/favicons/site.webmanifest +25 -0
- package/web/dist/index.html +42 -6
- package/web/index.html +40 -4
- package/web/package.json +11 -0
- package/web/public/favicon.ico +0 -0
- package/web/public/favicons/apple-touch-icon.png +0 -0
- package/web/public/favicons/browserconfig.xml +12 -0
- package/web/public/favicons/favicon-16x16.png +0 -0
- package/web/public/favicons/favicon-192x192.png +0 -0
- package/web/public/favicons/favicon-32x32.png +0 -0
- package/web/public/favicons/favicon-48x48.png +0 -0
- package/web/public/favicons/favicon-512x512.png +0 -0
- package/web/public/favicons/favicon-96x96.png +0 -0
- package/web/public/favicons/favicon.ico +0 -0
- package/web/public/favicons/mstile-150x150.png +0 -0
- package/web/public/favicons/mstile-310x150.png +0 -0
- package/web/public/favicons/mstile-310x310.png +0 -0
- package/web/public/favicons/mstile-512x512.png +0 -0
- package/web/public/favicons/mstile-70x70.png +0 -0
- package/web/public/favicons/site.webmanifest +25 -0
- package/web/server.js +676 -211
- package/web/src/App.jsx +233 -71
- package/web/src/components/analytics/AnalyticsView.jsx +40 -6
- package/web/src/components/analytics/AnalyticsView.module.css +4 -0
- package/web/src/components/atoms/AILatencyCell.jsx +38 -0
- package/web/src/components/atoms/AILatencyCell.module.css +51 -0
- package/web/src/components/atoms/HealthCell.jsx +138 -0
- package/web/src/components/atoms/HealthCell.module.css +56 -0
- package/web/src/components/atoms/LastPingCell.jsx +35 -0
- package/web/src/components/atoms/LastPingCell.module.css +45 -0
- package/web/src/components/atoms/MoodCell.jsx +25 -0
- package/web/src/components/atoms/MoodCell.module.css +6 -0
- package/web/src/components/atoms/NoKeyIcon.jsx +65 -0
- package/web/src/components/atoms/NoKeyIcon.module.css +30 -0
- package/web/src/components/atoms/ProviderLogo.jsx +188 -0
- package/web/src/components/atoms/ProviderLogo.module.css +103 -0
- package/web/src/components/atoms/RankCell.jsx +9 -0
- package/web/src/components/atoms/RankCell.module.css +9 -0
- package/web/src/components/atoms/Sparkline.jsx +21 -2
- package/web/src/components/atoms/StabilityCell.module.css +5 -0
- package/web/src/components/atoms/StatusDot.module.css +7 -0
- package/web/src/components/atoms/TPSCell.jsx +36 -0
- package/web/src/components/atoms/TPSCell.module.css +44 -0
- package/web/src/components/atoms/TierBadge.module.css +11 -0
- package/web/src/components/atoms/Toast.module.css +6 -0
- package/web/src/components/atoms/VerdictBadge.jsx +30 -7
- package/web/src/components/atoms/VerdictBadge.module.css +36 -15
- package/web/src/components/dashboard/DetailPanel.jsx +63 -2
- package/web/src/components/dashboard/DetailPanel.module.css +130 -0
- package/web/src/components/dashboard/ExportModal.jsx +9 -4
- package/web/src/components/dashboard/FilterBar.jsx +228 -33
- package/web/src/components/dashboard/FilterBar.module.css +187 -22
- package/web/src/components/dashboard/ModelTable.jsx +500 -54
- package/web/src/components/dashboard/ModelTable.module.css +318 -31
- package/web/src/components/layout/Footer.jsx +1 -1
- package/web/src/components/layout/Header.jsx +171 -9
- package/web/src/components/layout/Header.module.css +223 -25
- package/web/src/components/palette/CommandPalette.jsx +146 -0
- package/web/src/components/palette/CommandPalette.module.css +121 -0
- package/web/src/components/settings/SettingsView.jsx +24 -6
- package/web/src/components/settings/SettingsView.module.css +0 -1
- package/web/src/global.css +118 -84
- package/web/src/hooks/useColumnSizing.js +211 -0
- package/web/src/hooks/useFavorites.js +150 -0
- package/web/src/hooks/useFilter.js +262 -37
- package/web/src/hooks/useSSE.js +33 -9
- package/web/src/hooks/useSocket.js +200 -0
- package/web/src/hooks/useTheme.js +72 -16
- package/web/src/hooks/useUrlState.js +93 -0
- package/web/vite.config.js +41 -0
- package/web/dist/assets/index-CGN-0_A0.css +0 -1
- package/web/dist/assets/index-CvMUM9Jr.js +0 -11
- package/web/src/components/dashboard/StatsBar.jsx +0 -40
- package/web/src/components/dashboard/StatsBar.module.css +0 -28
- package/web/src/components/layout/Sidebar.jsx +0 -42
- package/web/src/components/layout/Sidebar.module.css +0 -76
- package/web/src/components/map/MapView.jsx +0 -17
- package/web/src/components/map/MapView.module.css +0 -25
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file web/src/components/dashboard/StatsBar.jsx
|
|
3
|
-
* @description Stats cards row showing total models, online count, avg latency, fastest model, providers.
|
|
4
|
-
*/
|
|
5
|
-
import { useMemo } from 'react'
|
|
6
|
-
import styles from './StatsBar.module.css'
|
|
7
|
-
|
|
8
|
-
export default function StatsBar({ models }) {
|
|
9
|
-
const stats = useMemo(() => {
|
|
10
|
-
const total = models.length
|
|
11
|
-
const online = models.filter(m => m.status === 'up').length
|
|
12
|
-
const onlineWithPing = models.filter(m => m.status === 'up' && m.avg !== Infinity && m.avg < 99000)
|
|
13
|
-
const avgLatency = onlineWithPing.length > 0
|
|
14
|
-
? Math.round(onlineWithPing.reduce((s, m) => s + m.avg, 0) / onlineWithPing.length)
|
|
15
|
-
: null
|
|
16
|
-
const fastest = [...onlineWithPing].sort((a, b) => a.avg - b.avg)[0]
|
|
17
|
-
const providers = new Set(models.map(m => m.providerKey)).size
|
|
18
|
-
return [
|
|
19
|
-
{ icon: '📊', value: total, label: 'Total Models' },
|
|
20
|
-
{ icon: '🟢', value: online, label: 'Online' },
|
|
21
|
-
{ icon: '⚡', value: avgLatency != null ? `${avgLatency}ms` : '—', label: 'Avg Latency' },
|
|
22
|
-
{ icon: '🏆', value: fastest ? fastest.label : '—', label: 'Fastest Model' },
|
|
23
|
-
{ icon: '🌐', value: providers, label: 'Providers' },
|
|
24
|
-
]
|
|
25
|
-
}, [models])
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<section className={styles.statsBar}>
|
|
29
|
-
{stats.map(s => (
|
|
30
|
-
<div key={s.label} className={styles.card}>
|
|
31
|
-
<div className={styles.icon}>{s.icon}</div>
|
|
32
|
-
<div className={styles.body}>
|
|
33
|
-
<div className={styles.value}>{s.value}</div>
|
|
34
|
-
<div className={styles.label}>{s.label}</div>
|
|
35
|
-
</div>
|
|
36
|
-
</div>
|
|
37
|
-
))}
|
|
38
|
-
</section>
|
|
39
|
-
)
|
|
40
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
.statsBar {
|
|
2
|
-
position: relative; z-index: 1;
|
|
3
|
-
display: grid; grid-template-columns: repeat(5, 1fr); gap: 14px;
|
|
4
|
-
padding: 20px 24px 0;
|
|
5
|
-
}
|
|
6
|
-
.card {
|
|
7
|
-
display: flex; align-items: center; gap: 12px;
|
|
8
|
-
background: var(--color-bg-card);
|
|
9
|
-
backdrop-filter: blur(12px);
|
|
10
|
-
-webkit-backdrop-filter: blur(12px);
|
|
11
|
-
border: 1px solid var(--color-border);
|
|
12
|
-
border-radius: 16px;
|
|
13
|
-
padding: 16px 18px;
|
|
14
|
-
transition: all 250ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
15
|
-
}
|
|
16
|
-
.card:hover {
|
|
17
|
-
border-color: var(--color-border-hover);
|
|
18
|
-
transform: translateY(-2px);
|
|
19
|
-
box-shadow: var(--shadow-md);
|
|
20
|
-
}
|
|
21
|
-
.icon { font-size: 28px; }
|
|
22
|
-
.body { flex: 1; }
|
|
23
|
-
.value { font-size: 22px; font-weight: 800; font-family: var(--font-mono); line-height: 1; }
|
|
24
|
-
.label { font-size: 11px; color: var(--color-text-muted); text-transform: uppercase; letter-spacing: 0.5px; font-weight: 500; margin-top: 2px; }
|
|
25
|
-
|
|
26
|
-
@media (max-width: 1024px) { .statsBar { grid-template-columns: repeat(3, 1fr); } }
|
|
27
|
-
@media (max-width: 768px) { .statsBar { grid-template-columns: repeat(2, 1fr); } }
|
|
28
|
-
@media (max-width: 480px) { .statsBar { grid-template-columns: 1fr; } }
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file web/src/components/layout/Sidebar.jsx
|
|
3
|
-
* @description Collapsible sidebar navigation with Dashboard / Settings / Analytics links + theme toggle.
|
|
4
|
-
*/
|
|
5
|
-
import styles from './Sidebar.module.css'
|
|
6
|
-
|
|
7
|
-
const NAV_ITEMS = [
|
|
8
|
-
{ id: 'dashboard', icon: '▤', label: 'Dashboard' },
|
|
9
|
-
{ id: 'settings', icon: '⚙', label: 'Settings' },
|
|
10
|
-
{ id: 'analytics', icon: '▌▌', label: 'Analytics' },
|
|
11
|
-
{ id: 'map', icon: '🌍', label: 'Map' },
|
|
12
|
-
]
|
|
13
|
-
|
|
14
|
-
export default function Sidebar({ currentView, onNavigate, onToggleTheme }) {
|
|
15
|
-
return (
|
|
16
|
-
<aside className={styles.sidebar}>
|
|
17
|
-
<div className={styles.logo}>
|
|
18
|
-
<span className={styles.logoIcon}>⚡</span>
|
|
19
|
-
<span className={styles.logoText}>FCM</span>
|
|
20
|
-
</div>
|
|
21
|
-
<nav className={styles.nav}>
|
|
22
|
-
{NAV_ITEMS.map(({ id, icon, label }) => (
|
|
23
|
-
<button
|
|
24
|
-
key={id}
|
|
25
|
-
className={`${styles.navItem} ${currentView === id ? styles.active : ''}`}
|
|
26
|
-
onClick={() => onNavigate(id)}
|
|
27
|
-
title={label}
|
|
28
|
-
>
|
|
29
|
-
<span className={styles.navIcon}>{icon}</span>
|
|
30
|
-
<span className={styles.navLabel}>{label}</span>
|
|
31
|
-
</button>
|
|
32
|
-
))}
|
|
33
|
-
</nav>
|
|
34
|
-
<div className={styles.bottom}>
|
|
35
|
-
<button className={styles.navItem} onClick={onToggleTheme} title="Toggle Theme">
|
|
36
|
-
<span className={styles.navIcon}>☽</span>
|
|
37
|
-
<span className={styles.navLabel}>Theme</span>
|
|
38
|
-
</button>
|
|
39
|
-
</div>
|
|
40
|
-
</aside>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
.sidebar {
|
|
2
|
-
position: fixed;
|
|
3
|
-
left: 0; top: 0; bottom: 0;
|
|
4
|
-
width: 64px;
|
|
5
|
-
z-index: 200;
|
|
6
|
-
background: var(--color-bg-elevated);
|
|
7
|
-
border-right: 1px solid var(--color-border);
|
|
8
|
-
display: flex;
|
|
9
|
-
flex-direction: column;
|
|
10
|
-
padding: 12px 0;
|
|
11
|
-
transition: width 250ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
12
|
-
overflow: hidden;
|
|
13
|
-
}
|
|
14
|
-
.sidebar:hover { width: 200px; }
|
|
15
|
-
|
|
16
|
-
.logo {
|
|
17
|
-
display: flex;
|
|
18
|
-
align-items: center;
|
|
19
|
-
gap: 10px;
|
|
20
|
-
padding: 8px 18px;
|
|
21
|
-
margin-bottom: 16px;
|
|
22
|
-
white-space: nowrap;
|
|
23
|
-
overflow: hidden;
|
|
24
|
-
}
|
|
25
|
-
.logoIcon { font-size: 24px; flex-shrink: 0; }
|
|
26
|
-
.logoText {
|
|
27
|
-
font-size: 16px;
|
|
28
|
-
font-weight: 800;
|
|
29
|
-
background: linear-gradient(135deg, var(--color-accent), #06b6d4);
|
|
30
|
-
-webkit-background-clip: text;
|
|
31
|
-
-webkit-text-fill-color: transparent;
|
|
32
|
-
background-clip: text;
|
|
33
|
-
opacity: 0;
|
|
34
|
-
transition: opacity 250ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
35
|
-
}
|
|
36
|
-
.sidebar:hover .logoText { opacity: 1; }
|
|
37
|
-
|
|
38
|
-
.nav { flex: 1; display: flex; flex-direction: column; gap: 4px; }
|
|
39
|
-
.bottom { display: flex; flex-direction: column; gap: 4px; }
|
|
40
|
-
|
|
41
|
-
.navItem {
|
|
42
|
-
display: flex;
|
|
43
|
-
align-items: center;
|
|
44
|
-
gap: 12px;
|
|
45
|
-
padding: 10px 20px;
|
|
46
|
-
border: none;
|
|
47
|
-
background: none;
|
|
48
|
-
color: var(--color-text-muted);
|
|
49
|
-
cursor: pointer;
|
|
50
|
-
white-space: nowrap;
|
|
51
|
-
overflow: hidden;
|
|
52
|
-
font-family: var(--font-sans);
|
|
53
|
-
font-size: 13px;
|
|
54
|
-
font-weight: 500;
|
|
55
|
-
transition: all 150ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
56
|
-
}
|
|
57
|
-
.navItem:hover {
|
|
58
|
-
color: var(--color-text);
|
|
59
|
-
background: var(--color-bg-hover);
|
|
60
|
-
}
|
|
61
|
-
.navIcon {
|
|
62
|
-
width: 20px;
|
|
63
|
-
text-align: center;
|
|
64
|
-
flex-shrink: 0;
|
|
65
|
-
font-size: 16px;
|
|
66
|
-
}
|
|
67
|
-
.navLabel {
|
|
68
|
-
opacity: 0;
|
|
69
|
-
transition: opacity 250ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
70
|
-
}
|
|
71
|
-
.sidebar:hover .navLabel { opacity: 1; }
|
|
72
|
-
.active {
|
|
73
|
-
color: var(--color-accent) !important;
|
|
74
|
-
background: var(--color-accent-dim) !important;
|
|
75
|
-
border-right: 3px solid var(--color-accent);
|
|
76
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file web/src/components/map/MapView.jsx
|
|
3
|
-
* @description WIP - Live map view showing users location and activity.
|
|
4
|
-
* @exports MapView → placeholder component for user map visualization
|
|
5
|
-
*/
|
|
6
|
-
import styles from './MapView.module.css'
|
|
7
|
-
|
|
8
|
-
export default function MapView() {
|
|
9
|
-
return (
|
|
10
|
-
<div className={styles.container}>
|
|
11
|
-
<div className={styles.wip}>
|
|
12
|
-
<h1>WIP : map users live</h1>
|
|
13
|
-
<p>Coming soon...</p>
|
|
14
|
-
</div>
|
|
15
|
-
</div>
|
|
16
|
-
)
|
|
17
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
.container {
|
|
2
|
-
padding: 2rem;
|
|
3
|
-
width: 100%;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
.wip {
|
|
7
|
-
display: flex;
|
|
8
|
-
flex-direction: column;
|
|
9
|
-
align-items: center;
|
|
10
|
-
justify-content: center;
|
|
11
|
-
min-height: 60vh;
|
|
12
|
-
text-align: center;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.wip h1 {
|
|
16
|
-
font-size: 2rem;
|
|
17
|
-
font-weight: 700;
|
|
18
|
-
color: var(--text-primary);
|
|
19
|
-
margin-bottom: 1rem;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
.wip p {
|
|
23
|
-
font-size: 1.125rem;
|
|
24
|
-
color: var(--text-secondary);
|
|
25
|
-
}
|