free-coding-models 0.5.9 β 0.5.11
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 +37 -0
- package/bin/free-coding-models.js +6 -1
- package/changelog/v0.5.10.md +7 -0
- package/changelog/v0.5.11.md +56 -0
- package/package.json +1 -1
- package/src/core/config.js +44 -1
- package/src/core/playground.js +502 -0
- package/src/core/router-daemon.js +113 -3
- package/src/core/utils.js +7 -0
- package/src/tui/app.js +9 -1
- package/src/tui/cli-help.js +2 -0
- package/src/tui/command-palette.js +1 -0
- package/src/tui/key-handler.js +22 -0
- package/src/tui/overlays.js +11 -0
- package/src/tui/theme.js +3 -0
- package/web/dist/assets/index-DeEJErFO.js +39 -0
- package/web/dist/assets/{index-DKmmiDip.css β index-Z24EXUEe.css} +1 -1
- package/web/dist/index.html +2 -2
- package/web/server.js +87 -0
- package/web/src/App.jsx +15 -1
- package/web/src/components/install/InstallEndpointsView.jsx +39 -9
- package/web/src/components/install/InstallEndpointsView.module.css +86 -10
- package/web/src/components/layout/Header.jsx +2 -1
- package/web/src/components/palette/CommandPalette.jsx +1 -0
- package/web/src/components/playground/PlaygroundView.jsx +528 -0
- package/web/src/components/playground/PlaygroundView.module.css +413 -0
- package/web/dist/assets/index-27IYGKp2.js +0 -39
|
@@ -88,7 +88,10 @@ export default function InstallEndpointsView({ onClose, onToast }) {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
const handleInstall = async () => {
|
|
91
|
+
// π Avance au step 3 d'abord (animation "installing"), puis lance l'install
|
|
92
|
+
setStep(3)
|
|
91
93
|
setInstalling(true)
|
|
94
|
+
setInstallResult(null)
|
|
92
95
|
try {
|
|
93
96
|
const resp = await fetch('/api/install-endpoints/wizard', {
|
|
94
97
|
method: 'POST',
|
|
@@ -105,10 +108,15 @@ export default function InstallEndpointsView({ onClose, onToast }) {
|
|
|
105
108
|
setInstallResult(data)
|
|
106
109
|
onToast?.(`Installed ${data.modelCount} models for ${selectedProvider.label} into ${TOOLS.find(t => t.id === selectedTool)?.label || selectedTool}.`, 'success')
|
|
107
110
|
} else {
|
|
111
|
+
setInstallResult(null)
|
|
108
112
|
onToast?.(`Install failed: ${data.error || 'unknown'}`, 'error')
|
|
113
|
+
// π Retour au step 2 si l'install Γ©choue pour laisser rΓ©essayer
|
|
114
|
+
setStep(2)
|
|
109
115
|
}
|
|
110
116
|
} catch (err) {
|
|
117
|
+
setInstallResult(null)
|
|
111
118
|
onToast?.(`Install error: ${err.message}`, 'error')
|
|
119
|
+
setStep(2)
|
|
112
120
|
} finally {
|
|
113
121
|
setInstalling(false)
|
|
114
122
|
}
|
|
@@ -224,23 +232,35 @@ export default function InstallEndpointsView({ onClose, onToast }) {
|
|
|
224
232
|
</div>
|
|
225
233
|
)}
|
|
226
234
|
|
|
227
|
-
{/* Step 3: Installing
|
|
235
|
+
{/* Step 3: Installing β Done */}
|
|
228
236
|
{step === 3 && (
|
|
229
237
|
<div className={styles.stepContent}>
|
|
230
238
|
{installing && (
|
|
231
239
|
<div className={styles.installingState}>
|
|
232
|
-
<IconLoader size={
|
|
233
|
-
<span>Installing {scope === 'all' ? 'all' : selectedModels.length}
|
|
240
|
+
<IconLoader size={32} className={styles.spinner} />
|
|
241
|
+
<span className={styles.installingLabel}>Installing {scope === 'all' ? 'all' : selectedModels.length} model{scope === 'all' && catalogModels.length !== 1 ? 's' : selectedModels.length !== 1 ? 's' : ''}β¦</span>
|
|
242
|
+
<span className={styles.installingSub}>Writing config for {TOOLS.find(t => t.id === selectedTool)?.label}</span>
|
|
234
243
|
</div>
|
|
235
244
|
)}
|
|
236
|
-
{installResult && (
|
|
245
|
+
{!installing && installResult && (
|
|
237
246
|
<div className={styles.doneState}>
|
|
238
|
-
<
|
|
239
|
-
|
|
240
|
-
|
|
247
|
+
<div className={styles.checkCircle}>
|
|
248
|
+
<IconCheck size={36} />
|
|
249
|
+
</div>
|
|
250
|
+
<h3 className={styles.doneTitle}>Installed!</h3>
|
|
251
|
+
<p className={styles.doneSub}>
|
|
252
|
+
{installResult.modelCount} model{installResult.modelCount !== 1 ? 's' : ''} from <strong>{selectedProvider?.label}</strong> β <strong>{installResult.toolLabel}</strong>
|
|
253
|
+
</p>
|
|
241
254
|
<code className={styles.installPath}>{installResult.path}</code>
|
|
242
255
|
</div>
|
|
243
256
|
)}
|
|
257
|
+
{!installing && !installResult && (
|
|
258
|
+
<div className={styles.doneState}>
|
|
259
|
+
<span className={styles.errorDot}>β</span>
|
|
260
|
+
<h3 className={styles.doneTitle}>Install failed</h3>
|
|
261
|
+
<p className={styles.doneSub}>Check the toast for details. You can go back and retry.</p>
|
|
262
|
+
</div>
|
|
263
|
+
)}
|
|
244
264
|
</div>
|
|
245
265
|
)}
|
|
246
266
|
</div>
|
|
@@ -253,6 +273,13 @@ export default function InstallEndpointsView({ onClose, onToast }) {
|
|
|
253
273
|
Back
|
|
254
274
|
</button>
|
|
255
275
|
)}
|
|
276
|
+
{/* Step 3 error: show Back to retry */}
|
|
277
|
+
{step === 3 && !installing && !installResult && (
|
|
278
|
+
<button className={styles.backBtn} onClick={() => setStep(2)}>
|
|
279
|
+
<IconChevronLeft size={14} />
|
|
280
|
+
Back
|
|
281
|
+
</button>
|
|
282
|
+
)}
|
|
256
283
|
{step < 3 && (
|
|
257
284
|
<button className={styles.nextBtn} onClick={handleNext} disabled={!canNext()}>
|
|
258
285
|
{step === 2 ? (
|
|
@@ -268,8 +295,11 @@ export default function InstallEndpointsView({ onClose, onToast }) {
|
|
|
268
295
|
)}
|
|
269
296
|
</button>
|
|
270
297
|
)}
|
|
271
|
-
{step === 3 && installResult && (
|
|
272
|
-
<button className={styles.doneBtn} onClick={onClose}>
|
|
298
|
+
{step === 3 && !installing && installResult && (
|
|
299
|
+
<button className={styles.doneBtn} onClick={onClose}>
|
|
300
|
+
<IconCheck size={14} />
|
|
301
|
+
Done
|
|
302
|
+
</button>
|
|
273
303
|
)}
|
|
274
304
|
</div>
|
|
275
305
|
</div>
|
|
@@ -258,23 +258,77 @@
|
|
|
258
258
|
align-items: center;
|
|
259
259
|
justify-content: center;
|
|
260
260
|
gap: 12px;
|
|
261
|
-
padding:
|
|
261
|
+
padding: 50px 0;
|
|
262
262
|
color: var(--text-secondary, #aaa);
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
+
.installingLabel {
|
|
266
|
+
font-size: 16px;
|
|
267
|
+
font-weight: 600;
|
|
268
|
+
color: var(--text-primary, #e0e0e0);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.installingSub {
|
|
272
|
+
font-size: 12px;
|
|
273
|
+
color: var(--text-muted, #888);
|
|
274
|
+
}
|
|
275
|
+
|
|
265
276
|
.spinner {
|
|
266
|
-
animation: spin
|
|
277
|
+
animation: spin 0.8s linear infinite;
|
|
267
278
|
}
|
|
268
279
|
|
|
269
280
|
.doneState {
|
|
270
281
|
display: flex;
|
|
271
282
|
flex-direction: column;
|
|
272
283
|
align-items: center;
|
|
273
|
-
gap:
|
|
274
|
-
padding:
|
|
284
|
+
gap: 10px;
|
|
285
|
+
padding: 40px 0;
|
|
275
286
|
text-align: center;
|
|
276
|
-
|
|
277
|
-
|
|
287
|
+
animation: doneFadeIn 0.3s ease;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.doneTitle {
|
|
291
|
+
margin: 0;
|
|
292
|
+
font-size: 20px;
|
|
293
|
+
font-weight: 700;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.doneSub {
|
|
297
|
+
margin: 0;
|
|
298
|
+
color: var(--text-secondary, #aaa);
|
|
299
|
+
font-size: 14px;
|
|
300
|
+
line-height: 1.5;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.doneSub strong {
|
|
304
|
+
color: var(--text-primary, #e0e0e0);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* π Animated check circle β scales up with a satisfying bounce */
|
|
308
|
+
.checkCircle {
|
|
309
|
+
display: flex;
|
|
310
|
+
align-items: center;
|
|
311
|
+
justify-content: center;
|
|
312
|
+
width: 64px;
|
|
313
|
+
height: 64px;
|
|
314
|
+
border-radius: 50%;
|
|
315
|
+
background: rgba(0, 200, 100, 0.12);
|
|
316
|
+
color: #00c864;
|
|
317
|
+
animation: checkPop 0.5s cubic-bezier(0.16, 1, 0.3, 1);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.errorDot {
|
|
321
|
+
display: flex;
|
|
322
|
+
align-items: center;
|
|
323
|
+
justify-content: center;
|
|
324
|
+
width: 64px;
|
|
325
|
+
height: 64px;
|
|
326
|
+
border-radius: 50%;
|
|
327
|
+
background: rgba(220, 53, 69, 0.12);
|
|
328
|
+
color: #dc3545;
|
|
329
|
+
font-size: 28px;
|
|
330
|
+
font-weight: 700;
|
|
331
|
+
animation: checkPop 0.5s cubic-bezier(0.16, 1, 0.3, 1);
|
|
278
332
|
}
|
|
279
333
|
|
|
280
334
|
.installPath {
|
|
@@ -288,6 +342,23 @@
|
|
|
288
342
|
text-overflow: ellipsis;
|
|
289
343
|
}
|
|
290
344
|
|
|
345
|
+
@keyframes doneFadeIn {
|
|
346
|
+
from { opacity: 0; transform: translateY(8px); }
|
|
347
|
+
to { opacity: 1; transform: translateY(0); }
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
@keyframes donePop {
|
|
351
|
+
0% { transform: scale(0.5); opacity: 0; }
|
|
352
|
+
60% { transform: scale(1.1); opacity: 1; }
|
|
353
|
+
100% { transform: scale(1); opacity: 1; }
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
@keyframes checkPop {
|
|
357
|
+
0% { transform: scale(0); opacity: 0; }
|
|
358
|
+
50% { transform: scale(1.2); }
|
|
359
|
+
100% { transform: scale(1); opacity: 1; }
|
|
360
|
+
}
|
|
361
|
+
|
|
291
362
|
/* Footer */
|
|
292
363
|
.footer {
|
|
293
364
|
display: flex;
|
|
@@ -328,16 +399,21 @@
|
|
|
328
399
|
}
|
|
329
400
|
|
|
330
401
|
.doneBtn {
|
|
331
|
-
|
|
402
|
+
display: flex;
|
|
403
|
+
align-items: center;
|
|
404
|
+
gap: 6px;
|
|
405
|
+
padding: 10px 28px;
|
|
332
406
|
background: #00c864;
|
|
333
407
|
color: #000;
|
|
334
408
|
border: none;
|
|
335
409
|
border-radius: 8px;
|
|
336
|
-
font-size:
|
|
337
|
-
font-weight:
|
|
410
|
+
font-size: 14px;
|
|
411
|
+
font-weight: 700;
|
|
338
412
|
cursor: pointer;
|
|
339
413
|
margin-left: auto;
|
|
340
|
-
|
|
414
|
+
transition: transform 0.15s ease, background 0.15s ease;
|
|
415
|
+
&:hover { background: #00d874; transform: scale(1.02); }
|
|
416
|
+
&:active { transform: scale(0.98); }
|
|
341
417
|
}
|
|
342
418
|
|
|
343
419
|
@keyframes fadeIn {
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
IconBolt, IconSearch, IconDownload, IconSettings, IconMoon, IconSun,
|
|
14
14
|
IconPlayerPlay, IconCommand, IconLayoutDashboard, IconActivity,
|
|
15
15
|
IconSparkles, IconRoute, IconDots, IconQuestionMark, IconHistory,
|
|
16
|
-
IconPlug, IconFolders, IconMenu2,
|
|
16
|
+
IconPlug, IconFolders, IconMenu2, IconMessageChatbot,
|
|
17
17
|
} from '@tabler/icons-react'
|
|
18
18
|
import ToolPicker from '../tools/ToolPicker.jsx'
|
|
19
19
|
import styles from './Header.module.css'
|
|
@@ -27,6 +27,7 @@ const NAV_ITEMS = [
|
|
|
27
27
|
{ id: 'analytics', label: 'Analytics', icon: IconActivity },
|
|
28
28
|
{ id: 'recommend', label: 'Recommend', icon: IconSparkles },
|
|
29
29
|
{ id: 'router', label: 'Router', icon: IconRoute },
|
|
30
|
+
{ id: 'playground', label: 'Playground', icon: IconMessageChatbot },
|
|
30
31
|
]
|
|
31
32
|
|
|
32
33
|
// π Overflow menu items β Help + Changelog shipped in M2; Install Endpoints
|
|
@@ -42,6 +42,7 @@ function buildWebEntries(deps) {
|
|
|
42
42
|
// Pages (open as modals)
|
|
43
43
|
{ id: 'page.help', section: 'page', label: 'Open Help', keywords: ['help', 'shortcuts', 'reference'], run: () => deps.onOpenHelp?.() },
|
|
44
44
|
{ id: 'page.changelog', section: 'page', label: 'Open Changelog', keywords: ['changelog', 'release', 'history', 'version'], run: () => onOpenChangelog?.() },
|
|
45
|
+
{ id: 'page.playground', section: 'page', label: 'Open Playground', keywords: ['playground', 'chat', 'try', 'prompt', 'router'], run: () => deps.onOpenPlayground?.() },
|
|
45
46
|
{ id: 'page.install-endpoints', section: 'page', label: 'Open Install Endpoints (M4)', keywords: ['install', 'endpoint', 'tool', 'configure'], disabled: true },
|
|
46
47
|
{ id: 'page.installed-models', section: 'page', label: 'Open Installed Models (M4)', keywords: ['installed', 'models', 'tools'], disabled: true },
|
|
47
48
|
|