free-coding-models 0.5.28 → 0.5.30

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 (64) hide show
  1. package/README.md +1 -1
  2. package/bin/free-coding-models.js +10 -2
  3. package/changelog/v0.5.29.md +19 -0
  4. package/changelog/v0.5.30.md +15 -0
  5. package/package.json +1 -1
  6. package/sources.js +3 -2
  7. package/src/core/config.js +1 -14
  8. package/src/core/endpoint-installer.js +5 -9
  9. package/src/core/legacy-proxy-cleanup.js +2 -0
  10. package/src/core/model-merger.js +4 -13
  11. package/src/core/playground.js +6 -4
  12. package/src/core/router-daemon.js +101 -43
  13. package/src/core/router-dashboard.js +63 -25
  14. package/src/core/shared-helpers.js +117 -0
  15. package/src/core/sync-set.js +11 -24
  16. package/src/core/tool-launchers.js +24 -92
  17. package/src/core/utils.js +26 -100
  18. package/src/tui/app.js +2 -2
  19. package/src/tui/command-palette.js +1 -0
  20. package/src/tui/key-handler.js +77 -20
  21. package/src/tui/render-table.js +3 -3
  22. package/web/dist/assets/index-CsFt3qt5.css +1 -0
  23. package/web/dist/assets/index-I6N91rH7.js +40 -0
  24. package/web/dist/favicon.ico +0 -0
  25. package/web/dist/favicons/apple-touch-icon.png +0 -0
  26. package/web/dist/favicons/favicon-16x16.png +0 -0
  27. package/web/dist/favicons/favicon-192x192.png +0 -0
  28. package/web/dist/favicons/favicon-32x32.png +0 -0
  29. package/web/dist/favicons/favicon-48x48.png +0 -0
  30. package/web/dist/favicons/favicon-512x512.png +0 -0
  31. package/web/dist/favicons/favicon-96x96.png +0 -0
  32. package/web/dist/favicons/favicon.ico +0 -0
  33. package/web/dist/favicons/mstile-150x150.png +0 -0
  34. package/web/dist/favicons/mstile-310x150.png +0 -0
  35. package/web/dist/favicons/mstile-310x310.png +0 -0
  36. package/web/dist/favicons/mstile-512x512.png +0 -0
  37. package/web/dist/favicons/mstile-70x70.png +0 -0
  38. package/web/dist/index.html +2 -2
  39. package/web/public/favicon.ico +0 -0
  40. package/web/public/favicons/apple-touch-icon.png +0 -0
  41. package/web/public/favicons/favicon-16x16.png +0 -0
  42. package/web/public/favicons/favicon-192x192.png +0 -0
  43. package/web/public/favicons/favicon-32x32.png +0 -0
  44. package/web/public/favicons/favicon-48x48.png +0 -0
  45. package/web/public/favicons/favicon-512x512.png +0 -0
  46. package/web/public/favicons/favicon-96x96.png +0 -0
  47. package/web/public/favicons/favicon.ico +0 -0
  48. package/web/public/favicons/mstile-150x150.png +0 -0
  49. package/web/public/favicons/mstile-310x150.png +0 -0
  50. package/web/public/favicons/mstile-310x310.png +0 -0
  51. package/web/public/favicons/mstile-512x512.png +0 -0
  52. package/web/public/favicons/mstile-70x70.png +0 -0
  53. package/web/server.js +34 -4
  54. package/web/src/components/dashboard/ExpandedDetailRow.jsx +10 -113
  55. package/web/src/components/dashboard/ExpandedDetailRow.module.css +9 -0
  56. package/web/src/components/playground/PlaygroundChat.jsx +527 -0
  57. package/web/src/components/playground/PlaygroundChat.module.css +382 -0
  58. package/web/src/components/playground/PlaygroundView.jsx +104 -442
  59. package/web/src/components/playground/PlaygroundView.module.css +10 -0
  60. package/web/src/components/router/RouterView.jsx +268 -56
  61. package/web/src/components/router/RouterView.module.css +416 -1
  62. package/src/core/product-flags.js +0 -9
  63. package/web/dist/assets/index-BRFowJv5.css +0 -1
  64. package/web/dist/assets/index-Pr3waI0-.js +0 -41
@@ -0,0 +1,382 @@
1
+ /**
2
+ * @file web/src/components/playground/PlaygroundChat.module.css
3
+ * @description Styles for the shared PlaygroundChat core. Two density variants
4
+ * (`full` for the modal playground, `mini` for the router + model-row
5
+ * playgrounds) share the same visual language so every playground looks
6
+ * identical — same bubbles, same metadata chips, same input bar.
7
+ *
8
+ * 📖 Design tokens come from the app's CSS variables (set by the theme), with
9
+ * 📖 sensible hard-coded fallbacks so the component degrades gracefully.
10
+ */
11
+
12
+ .root {
13
+ display: flex;
14
+ flex-direction: column;
15
+ height: 100%;
16
+ min-height: 0;
17
+ background: transparent;
18
+ }
19
+
20
+ /* ── Transcript ─────────────────────────────────────────────────────────── */
21
+ .transcript {
22
+ flex: 1;
23
+ min-height: 0;
24
+ overflow-y: auto;
25
+ display: flex;
26
+ flex-direction: column;
27
+ gap: 12px;
28
+ }
29
+
30
+ .root.full .transcript {
31
+ padding: 16px;
32
+ }
33
+
34
+ .root.mini .transcript {
35
+ padding: 8px;
36
+ gap: 8px;
37
+ }
38
+
39
+ /* ── Empty state ────────────────────────────────────────────────────────── */
40
+ .empty {
41
+ flex: 1;
42
+ display: flex;
43
+ flex-direction: column;
44
+ align-items: center;
45
+ justify-content: center;
46
+ text-align: center;
47
+ gap: 6px;
48
+ color: var(--text-muted, #888);
49
+ padding: 24px 16px;
50
+ }
51
+
52
+ .root.mini .empty {
53
+ padding: 18px 12px;
54
+ justify-content: flex-start;
55
+ }
56
+
57
+ .emptyIcon {
58
+ opacity: 0.55;
59
+ margin-bottom: 4px;
60
+ }
61
+
62
+ .emptyTitle {
63
+ font-size: 15px;
64
+ font-weight: 600;
65
+ color: var(--text, #eee);
66
+ }
67
+
68
+ .root.mini .emptyTitle {
69
+ font-size: 13px;
70
+ }
71
+
72
+ .emptyHint {
73
+ font-size: 12px;
74
+ line-height: 1.5;
75
+ max-width: 420px;
76
+ opacity: 0.8;
77
+ }
78
+
79
+ .root.mini .emptyHint {
80
+ font-size: 11px;
81
+ max-width: 100%;
82
+ }
83
+
84
+ .emptyHint code {
85
+ background: var(--bg-secondary, #16161e);
86
+ border: 1px solid var(--border, #1e1e2e);
87
+ border-radius: 4px;
88
+ padding: 1px 5px;
89
+ font-family: 'Menlo', 'Monaco', monospace;
90
+ font-size: 11px;
91
+ }
92
+
93
+ .suggestions {
94
+ display: flex;
95
+ flex-wrap: wrap;
96
+ gap: 6px;
97
+ justify-content: center;
98
+ margin-top: 10px;
99
+ }
100
+
101
+ /* ── Message bubble ─────────────────────────────────────────────────────── */
102
+ .message {
103
+ display: flex;
104
+ flex-direction: column;
105
+ gap: 4px;
106
+ max-width: 88%;
107
+ animation: msgIn 0.15s ease;
108
+ }
109
+
110
+ .root.mini .message {
111
+ max-width: 92%;
112
+ gap: 3px;
113
+ }
114
+
115
+ @keyframes msgIn {
116
+ from { opacity: 0; transform: translateY(2px); }
117
+ to { opacity: 1; transform: translateY(0); }
118
+ }
119
+
120
+ .message.user {
121
+ align-self: flex-end;
122
+ align-items: flex-end;
123
+ }
124
+
125
+ .message.assistant {
126
+ align-self: flex-start;
127
+ align-items: flex-start;
128
+ }
129
+
130
+ .bubbleRow {
131
+ display: flex;
132
+ align-items: center;
133
+ gap: 6px;
134
+ padding: 0 4px;
135
+ }
136
+
137
+ .role {
138
+ font-size: 10px;
139
+ text-transform: uppercase;
140
+ letter-spacing: 0.05em;
141
+ color: var(--text-muted, #888);
142
+ font-weight: 600;
143
+ }
144
+
145
+ .copyBtn {
146
+ display: inline-flex;
147
+ align-items: center;
148
+ background: transparent;
149
+ border: none;
150
+ color: var(--text-muted, #888);
151
+ cursor: pointer;
152
+ padding: 2px;
153
+ border-radius: 4px;
154
+ opacity: 0;
155
+ transition: opacity 0.12s;
156
+ }
157
+
158
+ .message.assistant:hover .copyBtn,
159
+ .copyBtn:focus-visible {
160
+ opacity: 0.8;
161
+ }
162
+
163
+ .bubble {
164
+ padding: 10px 14px;
165
+ border-radius: 12px;
166
+ font-size: 13px;
167
+ line-height: 1.5;
168
+ white-space: pre-wrap;
169
+ word-wrap: break-word;
170
+ overflow-wrap: break-word;
171
+ }
172
+
173
+ .root.mini .bubble {
174
+ padding: 8px 11px;
175
+ font-size: 12.5px;
176
+ border-radius: 10px;
177
+ }
178
+
179
+ .message.user .bubble {
180
+ background: var(--accent, #22c55e);
181
+ color: #061a08;
182
+ border-bottom-right-radius: 4px;
183
+ }
184
+
185
+ .message.assistant .bubble {
186
+ background: var(--bg-secondary, #16161e);
187
+ border: 1px solid var(--border, #1e1e2e);
188
+ color: var(--text, #eee);
189
+ border-bottom-left-radius: 4px;
190
+ }
191
+
192
+ .typing {
193
+ color: var(--text-muted, #888);
194
+ font-style: italic;
195
+ font-size: 12px;
196
+ }
197
+
198
+ .codeBlock {
199
+ background: rgba(0, 0, 0, 0.35);
200
+ border: 1px solid var(--border, #1e1e2e);
201
+ border-radius: 6px;
202
+ padding: 8px 10px;
203
+ font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
204
+ font-size: 12px;
205
+ overflow-x: auto;
206
+ margin: 6px 0;
207
+ white-space: pre;
208
+ }
209
+
210
+ /* ── Streaming cursor ───────────────────────────────────────────────────── */
211
+ .cursor {
212
+ display: inline-block;
213
+ width: 7px;
214
+ height: 14px;
215
+ background: var(--accent, #22c55e);
216
+ margin-left: 2px;
217
+ vertical-align: text-bottom;
218
+ animation: blink 0.9s infinite;
219
+ }
220
+
221
+ @keyframes blink {
222
+ 0%, 50% { opacity: 1; }
223
+ 51%, 100% { opacity: 0; }
224
+ }
225
+
226
+ /* ── Metadata chips (the harmonized stats row) ──────────────────────────── */
227
+ .meta {
228
+ display: flex;
229
+ align-items: center;
230
+ flex-wrap: wrap;
231
+ gap: 6px;
232
+ font-size: 11px;
233
+ color: var(--text-muted, #888);
234
+ padding: 0 4px;
235
+ }
236
+
237
+ .root.mini .meta {
238
+ gap: 5px;
239
+ font-size: 10.5px;
240
+ }
241
+
242
+ .metaChip {
243
+ display: inline-flex;
244
+ align-items: center;
245
+ gap: 4px;
246
+ background: var(--bg-secondary, #16161e);
247
+ border: 1px solid var(--border, #1e1e2e);
248
+ border-radius: 4px;
249
+ padding: 2px 6px;
250
+ font-family: 'Menlo', 'Monaco', monospace;
251
+ line-height: 1.4;
252
+ }
253
+
254
+ /* addressed model under the user bubble */
255
+ .target {
256
+ color: var(--text-muted, #aaa);
257
+ opacity: 0.85;
258
+ }
259
+
260
+ /* served model chip — accent-tinted so it stands out */
261
+ .modelChip {
262
+ color: var(--accent, #22c55e);
263
+ border-color: rgba(34, 197, 94, 0.3);
264
+ }
265
+
266
+ .latencyChip {
267
+ color: #fbbf24;
268
+ border-color: rgba(251, 191, 36, 0.3);
269
+ }
270
+
271
+ .tpsChip {
272
+ color: #60a5fa;
273
+ border-color: rgba(96, 165, 250, 0.3);
274
+ }
275
+
276
+ .errorChip {
277
+ color: #f87171;
278
+ border-color: rgba(248, 113, 113, 0.3);
279
+ }
280
+
281
+ /* ── Input bar ──────────────────────────────────────────────────────────── */
282
+ .inputBar {
283
+ display: flex;
284
+ gap: 8px;
285
+ align-items: flex-end;
286
+ flex-shrink: 0;
287
+ background: var(--bg-secondary, #16161e);
288
+ }
289
+
290
+ .root.full .inputBar {
291
+ padding: 12px 16px;
292
+ border-top: 1px solid var(--border, #1e1e2e);
293
+ }
294
+
295
+ .root.mini .inputBar {
296
+ padding: 8px 0 0;
297
+ gap: 6px;
298
+ }
299
+
300
+ .input {
301
+ flex: 1;
302
+ resize: none;
303
+ min-height: 40px;
304
+ max-height: 180px;
305
+ background: var(--bg-primary, #0f0f17);
306
+ border: 1px solid var(--border, #1e1e2e);
307
+ border-radius: 8px;
308
+ color: var(--text, #eee);
309
+ padding: 9px 12px;
310
+ font-size: 13px;
311
+ font-family: inherit;
312
+ line-height: 1.4;
313
+ transition: border-color 0.12s;
314
+ }
315
+
316
+ .root.mini .input {
317
+ min-height: 34px;
318
+ padding: 7px 10px;
319
+ font-size: 12.5px;
320
+ border-radius: 7px;
321
+ }
322
+
323
+ .input:focus {
324
+ outline: none;
325
+ border-color: var(--accent, #22c55e);
326
+ }
327
+
328
+ .input:disabled {
329
+ opacity: 0.5;
330
+ cursor: not-allowed;
331
+ }
332
+
333
+ .sendBtn,
334
+ .stopBtn {
335
+ background: var(--accent, #22c55e);
336
+ color: #061a08;
337
+ border: none;
338
+ border-radius: 8px;
339
+ padding: 9px 14px;
340
+ font-size: 13px;
341
+ font-weight: 600;
342
+ cursor: pointer;
343
+ display: inline-flex;
344
+ align-items: center;
345
+ gap: 6px;
346
+ height: 40px;
347
+ font-family: inherit;
348
+ transition: filter 0.12s, opacity 0.12s;
349
+ flex-shrink: 0;
350
+ }
351
+
352
+ .stopBtn {
353
+ background: #ef4444;
354
+ color: #fff;
355
+ }
356
+
357
+ .root.mini .sendBtn,
358
+ .root.mini .stopBtn {
359
+ height: 34px;
360
+ width: 34px;
361
+ padding: 0;
362
+ justify-content: center;
363
+ border-radius: 7px;
364
+ }
365
+
366
+ .sendBtn:disabled {
367
+ opacity: 0.5;
368
+ cursor: not-allowed;
369
+ }
370
+
371
+ .sendBtn:hover:not(:disabled),
372
+ .stopBtn:hover {
373
+ filter: brightness(1.1);
374
+ }
375
+
376
+ .spin {
377
+ animation: spin 0.9s linear infinite;
378
+ }
379
+
380
+ @keyframes spin {
381
+ to { transform: rotate(360deg); }
382
+ }