free-coding-models 0.5.85 → 0.5.87

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 (164) hide show
  1. package/README.md +110 -881
  2. package/bin/free-coding-models.js +4 -1
  3. package/changelog/v0.5.86.md +10 -0
  4. package/changelog/v0.5.87.md +48 -0
  5. package/package.json +8 -5
  6. package/sources.js +4 -4
  7. package/src/core/cache.js +3 -1
  8. package/src/core/config.js +43 -5
  9. package/src/core/endpoint-installer.js +23 -12
  10. package/src/core/installed-models-manager.js +43 -20
  11. package/src/core/kilo-config.js +51 -5
  12. package/src/core/kilo.js +5 -3
  13. package/src/core/legacy-proxy-cleanup.js +22 -5
  14. package/src/core/model-merger.js +4 -0
  15. package/src/core/models-drift.js +9 -0
  16. package/src/core/opencode-config.js +57 -5
  17. package/src/core/opencode.js +67 -41
  18. package/src/core/playground.js +7 -0
  19. package/src/core/probe-cache.js +35 -4
  20. package/src/core/provider-key-tester.js +10 -2
  21. package/src/core/provider-metadata.js +10 -0
  22. package/src/core/provider-quota-fetchers.js +14 -7
  23. package/src/core/router-daemon.js +208 -26
  24. package/src/core/runtime-telemetry.js +27 -1
  25. package/src/core/shared-helpers.js +12 -1
  26. package/src/core/shell-env.js +3 -1
  27. package/src/core/telemetry.js +18 -5
  28. package/src/core/tool-launchers.js +9 -6
  29. package/src/core/updater.js +28 -2
  30. package/src/core/utils.js +76 -71
  31. package/src/tui/app.js +47 -16
  32. package/src/tui/command-palette.js +22 -2
  33. package/src/tui/key-handler.js +123 -179
  34. package/src/tui/overlays.js +35 -75
  35. package/src/tui/render-helpers.js +62 -4
  36. package/src/tui/render-table.js +46 -5
  37. package/src/tui/theme.js +36 -5
  38. package/src/tui/tui-filters.js +28 -8
  39. package/src/tui/tui-state.js +22 -6
  40. package/web/dist/assets/index-BpTpPS0g.js +44 -0
  41. package/web/dist/assets/index-CAzFIt8P.css +1 -0
  42. package/web/dist/index.html +5 -9
  43. package/web/index.html +3 -7
  44. package/web/server.js +97 -23
  45. package/web/vite.config.js +8 -0
  46. package/web/app.legacy.js +0 -900
  47. package/web/dist/assets/index-C5-Ywvv3.css +0 -1
  48. package/web/dist/assets/index-DWUely84.js +0 -40
  49. package/web/public/favicon.ico +0 -0
  50. package/web/public/favicons/apple-touch-icon.png +0 -0
  51. package/web/public/favicons/browserconfig.xml +0 -12
  52. package/web/public/favicons/favicon-16x16.png +0 -0
  53. package/web/public/favicons/favicon-192x192.png +0 -0
  54. package/web/public/favicons/favicon-32x32.png +0 -0
  55. package/web/public/favicons/favicon-48x48.png +0 -0
  56. package/web/public/favicons/favicon-512x512.png +0 -0
  57. package/web/public/favicons/favicon-96x96.png +0 -0
  58. package/web/public/favicons/favicon.ico +0 -0
  59. package/web/public/favicons/mstile-150x150.png +0 -0
  60. package/web/public/favicons/mstile-310x150.png +0 -0
  61. package/web/public/favicons/mstile-310x310.png +0 -0
  62. package/web/public/favicons/mstile-512x512.png +0 -0
  63. package/web/public/favicons/mstile-70x70.png +0 -0
  64. package/web/public/favicons/site.webmanifest +0 -25
  65. package/web/src/App.jsx +0 -521
  66. package/web/src/components/analytics/AnalyticsView.jsx +0 -147
  67. package/web/src/components/analytics/AnalyticsView.module.css +0 -190
  68. package/web/src/components/analytics/TokenUsagePanel.jsx +0 -105
  69. package/web/src/components/analytics/TokenUsagePanel.module.css +0 -126
  70. package/web/src/components/atoms/AILatencyCell.jsx +0 -38
  71. package/web/src/components/atoms/AILatencyCell.module.css +0 -51
  72. package/web/src/components/atoms/HealthCell.jsx +0 -143
  73. package/web/src/components/atoms/HealthCell.module.css +0 -60
  74. package/web/src/components/atoms/LastPingCell.jsx +0 -35
  75. package/web/src/components/atoms/LastPingCell.module.css +0 -45
  76. package/web/src/components/atoms/MoodCell.jsx +0 -25
  77. package/web/src/components/atoms/MoodCell.module.css +0 -6
  78. package/web/src/components/atoms/NoKeyIcon.jsx +0 -65
  79. package/web/src/components/atoms/NoKeyIcon.module.css +0 -30
  80. package/web/src/components/atoms/ProviderLogo.jsx +0 -187
  81. package/web/src/components/atoms/ProviderLogo.module.css +0 -103
  82. package/web/src/components/atoms/RankCell.jsx +0 -9
  83. package/web/src/components/atoms/RankCell.module.css +0 -9
  84. package/web/src/components/atoms/Sparkline.jsx +0 -63
  85. package/web/src/components/atoms/StabilityCell.jsx +0 -18
  86. package/web/src/components/atoms/StabilityCell.module.css +0 -13
  87. package/web/src/components/atoms/StatusDot.jsx +0 -13
  88. package/web/src/components/atoms/StatusDot.module.css +0 -29
  89. package/web/src/components/atoms/TPSCell.jsx +0 -36
  90. package/web/src/components/atoms/TPSCell.module.css +0 -44
  91. package/web/src/components/atoms/TierBadge.jsx +0 -10
  92. package/web/src/components/atoms/TierBadge.module.css +0 -29
  93. package/web/src/components/atoms/Toast.jsx +0 -25
  94. package/web/src/components/atoms/Toast.module.css +0 -41
  95. package/web/src/components/atoms/ToastContainer.jsx +0 -16
  96. package/web/src/components/atoms/ToastContainer.module.css +0 -10
  97. package/web/src/components/atoms/VerdictBadge.jsx +0 -36
  98. package/web/src/components/atoms/VerdictBadge.module.css +0 -40
  99. package/web/src/components/changelog/ChangelogView.jsx +0 -135
  100. package/web/src/components/changelog/ChangelogView.module.css +0 -159
  101. package/web/src/components/dashboard/DetailPanel.jsx +0 -217
  102. package/web/src/components/dashboard/DetailPanel.module.css +0 -260
  103. package/web/src/components/dashboard/ExpandedDetailRow.jsx +0 -361
  104. package/web/src/components/dashboard/ExpandedDetailRow.module.css +0 -356
  105. package/web/src/components/dashboard/ExportModal.jsx +0 -84
  106. package/web/src/components/dashboard/ExportModal.module.css +0 -99
  107. package/web/src/components/dashboard/FilterBar.jsx +0 -338
  108. package/web/src/components/dashboard/FilterBar.module.css +0 -342
  109. package/web/src/components/dashboard/ModelTable.jsx +0 -672
  110. package/web/src/components/dashboard/ModelTable.module.css +0 -446
  111. package/web/src/components/dashboard/ProviderDropdown.jsx +0 -156
  112. package/web/src/components/dashboard/ProviderDropdown.module.css +0 -248
  113. package/web/src/components/help/HelpView.jsx +0 -201
  114. package/web/src/components/help/HelpView.module.css +0 -156
  115. package/web/src/components/install/InstallEndpointsView.jsx +0 -309
  116. package/web/src/components/install/InstallEndpointsView.module.css +0 -427
  117. package/web/src/components/installed/InstalledModelsView.jsx +0 -89
  118. package/web/src/components/installed/InstalledModelsView.module.css +0 -200
  119. package/web/src/components/launch/IncompatibleFallbackModal.jsx +0 -69
  120. package/web/src/components/launch/LaunchButton.jsx +0 -30
  121. package/web/src/components/launch/LaunchButton.module.css +0 -35
  122. package/web/src/components/launch/LaunchModal.module.css +0 -125
  123. package/web/src/components/layout/Footer.jsx +0 -19
  124. package/web/src/components/layout/Footer.module.css +0 -10
  125. package/web/src/components/layout/Header.jsx +0 -265
  126. package/web/src/components/layout/Header.module.css +0 -357
  127. package/web/src/components/palette/CommandPalette.jsx +0 -301
  128. package/web/src/components/palette/CommandPalette.module.css +0 -121
  129. package/web/src/components/playground/PlaygroundChat.jsx +0 -527
  130. package/web/src/components/playground/PlaygroundChat.module.css +0 -382
  131. package/web/src/components/playground/PlaygroundView.jsx +0 -371
  132. package/web/src/components/playground/PlaygroundView.module.css +0 -504
  133. package/web/src/components/recommend/RecommendView.jsx +0 -121
  134. package/web/src/components/recommend/RecommendView.module.css +0 -57
  135. package/web/src/components/router/RouterView.jsx +0 -1212
  136. package/web/src/components/router/RouterView.module.css +0 -1289
  137. package/web/src/components/settings/SettingsView.jsx +0 -555
  138. package/web/src/components/settings/SettingsView.module.css +0 -550
  139. package/web/src/components/tools/ToolPicker.jsx +0 -86
  140. package/web/src/components/tools/ToolPicker.module.css +0 -97
  141. package/web/src/components/update/UpdateChip.jsx +0 -104
  142. package/web/src/components/update/UpdateChip.module.css +0 -146
  143. package/web/src/global.css +0 -298
  144. package/web/src/hooks/urlState.constants.js +0 -28
  145. package/web/src/hooks/useChangelog.js +0 -51
  146. package/web/src/hooks/useColumnSizing.js +0 -211
  147. package/web/src/hooks/useFavorites.js +0 -150
  148. package/web/src/hooks/useFilter.js +0 -310
  149. package/web/src/hooks/useInstalledModels.js +0 -42
  150. package/web/src/hooks/useRecommend.js +0 -67
  151. package/web/src/hooks/useRouterDashboard.js +0 -133
  152. package/web/src/hooks/useSSE.js +0 -73
  153. package/web/src/hooks/useSocket.js +0 -203
  154. package/web/src/hooks/useTheme.js +0 -83
  155. package/web/src/hooks/useTokenUsage.js +0 -103
  156. package/web/src/hooks/useToolMode.js +0 -77
  157. package/web/src/hooks/useUpdateChecker.js +0 -91
  158. package/web/src/hooks/useUrlState.js +0 -170
  159. package/web/src/main.jsx +0 -15
  160. package/web/src/utils/download.js +0 -15
  161. package/web/src/utils/format.js +0 -42
  162. package/web/src/utils/m3.js +0 -57
  163. package/web/src/utils/ranks.js +0 -37
  164. package/web/styles.legacy.css +0 -963
@@ -1,1289 +0,0 @@
1
- .overlay {
2
- display: flex;
3
- flex-direction: column;
4
- flex: 1;
5
- min-height: 0;
6
- width: 100%;
7
- padding: 24px;
8
- }
9
-
10
- .modal {
11
- background: var(--color-bg-card, #080908);
12
- border: 1px solid var(--color-border, #161a16);
13
- border-radius: 12px;
14
- width: 100%;
15
- max-width: 1000px;
16
- margin: 0 auto;
17
- flex: 1;
18
- display: flex;
19
- flex-direction: column;
20
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
21
- min-height: 0;
22
- }
23
-
24
- .header {
25
- display: flex;
26
- align-items: center;
27
- justify-content: space-between;
28
- padding: 16px 20px;
29
- border-bottom: 1px solid var(--border, #1e1e2e);
30
- }
31
-
32
- .title {
33
- margin: 0;
34
- font-size: 16px;
35
- font-weight: 600;
36
- display: flex;
37
- align-items: center;
38
- gap: 8px;
39
- color: var(--text-primary, #e0e0e0);
40
- }
41
-
42
- .closeBtn {
43
- background: none;
44
- border: none;
45
- color: var(--text-muted, #888);
46
- font-size: 18px;
47
- cursor: pointer;
48
- padding: 4px 8px;
49
- border-radius: 6px;
50
- &:hover { background: var(--bg-hover, #1a1a2e); color: var(--text-primary, #e0e0e0); }
51
- }
52
-
53
- .body {
54
- padding: 20px;
55
- overflow-y: auto;
56
- flex: 1;
57
- }
58
-
59
- /* Hero Card */
60
- .heroCard {
61
- display: flex;
62
- align-items: center;
63
- justify-content: space-between;
64
- padding: 16px;
65
- border-radius: 10px;
66
- margin-bottom: 16px;
67
- }
68
-
69
- .heroRunning {
70
- background: linear-gradient(135deg, rgba(0, 200, 100, 0.08), rgba(0, 200, 100, 0.02));
71
- border: 1px solid rgba(0, 200, 100, 0.2);
72
- }
73
-
74
- .heroStopped {
75
- background: var(--bg-secondary, #14141f);
76
- border: 1px solid var(--border, #1e1e2e);
77
- }
78
-
79
- .heroLeft {
80
- display: flex;
81
- flex-direction: column;
82
- gap: 4px;
83
- }
84
-
85
- .heroStatus {
86
- display: flex;
87
- align-items: center;
88
- gap: 8px;
89
- }
90
-
91
- .statusDot {
92
- width: 10px;
93
- height: 10px;
94
- border-radius: 50%;
95
- display: inline-block;
96
- }
97
-
98
- .dotGreen {
99
- background: #00c864;
100
- box-shadow: 0 0 6px rgba(0, 200, 100, 0.4);
101
- }
102
-
103
- .dotGray {
104
- background: #555;
105
- }
106
-
107
- .heroLabel {
108
- font-size: 15px;
109
- font-weight: 600;
110
- color: var(--text-primary, #e0e0e0);
111
- }
112
-
113
- .heroMeta {
114
- font-size: 12px;
115
- color: var(--text-muted, #888);
116
- display: flex;
117
- align-items: center;
118
- gap: 6px;
119
- }
120
-
121
- .heroActions {
122
- display: flex;
123
- align-items: center;
124
- gap: 8px;
125
- }
126
-
127
- .startBtn {
128
- display: flex;
129
- align-items: center;
130
- gap: 6px;
131
- padding: 8px 16px;
132
- background: #00c864;
133
- color: #000;
134
- border: none;
135
- border-radius: 8px;
136
- font-size: 13px;
137
- font-weight: 600;
138
- cursor: pointer;
139
- &:hover { background: #00d874; }
140
- &:disabled { opacity: 0.5; cursor: not-allowed; }
141
- }
142
-
143
- /* 📖 Bigger start button for the stopped state — more prominent CTA. */
144
- .startBtnBig {
145
- padding: 10px 24px;
146
- font-size: 15px;
147
- border-radius: 10px;
148
- }
149
-
150
- .stopBtn {
151
- display: flex;
152
- align-items: center;
153
- gap: 6px;
154
- padding: 8px 16px;
155
- background: #dc3545;
156
- color: #fff;
157
- border: none;
158
- border-radius: 8px;
159
- font-size: 13px;
160
- font-weight: 600;
161
- cursor: pointer;
162
- &:hover { background: #e04555; }
163
- &:disabled { opacity: 0.5; cursor: not-allowed; }
164
- }
165
-
166
- .refreshBtn {
167
- display: flex;
168
- align-items: center;
169
- justify-content: center;
170
- width: 32px;
171
- height: 32px;
172
- background: var(--bg-secondary, #14141f);
173
- border: 1px solid var(--border, #1e1e2e);
174
- border-radius: 8px;
175
- color: var(--text-muted, #888);
176
- cursor: pointer;
177
- &:hover { color: var(--text-primary, #e0e0e0); background: var(--bg-hover, #1a1a2e); }
178
- }
179
-
180
- /* Quick Setup */
181
- .quickSetup {
182
- margin-bottom: 16px;
183
- }
184
-
185
- /* 📖 Hero glow when running — makes the quick setup card visually prominent. */
186
- .quickSetupHero {
187
- border: 1px solid rgba(0, 200, 100, 0.25);
188
- box-shadow: 0 0 16px rgba(0, 200, 100, 0.08);
189
- border-radius: 10px;
190
- padding: 12px;
191
- background: linear-gradient(135deg, rgba(0, 200, 100, 0.05), transparent);
192
- }
193
-
194
- /* 📖 "Copy all" button — sits at the right of the Quick Setup title. */
195
- .copyAllBtn {
196
- display: inline-flex;
197
- align-items: center;
198
- gap: 4px;
199
- margin-left: auto;
200
- padding: 3px 10px;
201
- background: var(--bg-secondary, #14141f);
202
- border: 1px solid var(--border, #1e1e2e);
203
- border-radius: 6px;
204
- color: var(--text-secondary, #aaa);
205
- font-size: 11px;
206
- font-weight: 500;
207
- cursor: pointer;
208
- font-family: inherit;
209
- &:hover { color: var(--text-primary, #e0e0e0); border-color: var(--accent, #00c864); }
210
- }
211
-
212
- .sectionTitle {
213
- display: flex;
214
- align-items: center;
215
- gap: 6px;
216
- font-size: 13px;
217
- font-weight: 600;
218
- color: var(--text-secondary, #aaa);
219
- margin: 0 0 8px 0;
220
- text-transform: uppercase;
221
- letter-spacing: 0.5px;
222
- }
223
-
224
- .quickRows {
225
- display: flex;
226
- flex-direction: column;
227
- gap: 6px;
228
- background: var(--bg-secondary, #14141f);
229
- border: 1px solid var(--border, #1e1e2e);
230
- border-radius: 8px;
231
- padding: 10px 12px;
232
- }
233
-
234
- .quickRow {
235
- display: flex;
236
- align-items: center;
237
- gap: 8px;
238
- }
239
-
240
- .quickLabel {
241
- font-size: 11px;
242
- font-weight: 600;
243
- color: var(--text-muted, #888);
244
- min-width: 60px;
245
- text-transform: uppercase;
246
- }
247
-
248
- .quickValue {
249
- flex: 1;
250
- font-size: 13px;
251
- color: var(--text-primary, #e0e0e0);
252
- background: var(--bg-primary, #0f0f17);
253
- padding: 4px 8px;
254
- border-radius: 4px;
255
- font-family: 'SF Mono', monospace;
256
- }
257
-
258
- .copyBtn {
259
- background: none;
260
- border: none;
261
- color: var(--text-muted, #888);
262
- cursor: pointer;
263
- padding: 4px;
264
- border-radius: 4px;
265
- &:hover { color: var(--text-primary, #e0e0e0); background: var(--bg-hover, #1a1a2e); }
266
- }
267
-
268
- /* Probe Mode */
269
- .section {
270
- margin-bottom: 16px;
271
- }
272
-
273
- .probeModes {
274
- display: flex;
275
- gap: 8px;
276
- }
277
-
278
- .probeBtn {
279
- padding: 6px 14px;
280
- border: 1px solid var(--border, #1e1e2e);
281
- border-radius: 6px;
282
- background: var(--bg-secondary, #14141f);
283
- color: var(--text-secondary, #aaa);
284
- font-size: 12px;
285
- cursor: pointer;
286
- text-transform: capitalize;
287
- &:hover { border-color: var(--text-muted, #888); }
288
- }
289
-
290
- .probeActive {
291
- background: rgba(0, 200, 100, 0.1);
292
- border-color: rgba(0, 200, 100, 0.3);
293
- color: #00c864;
294
- font-weight: 600;
295
- }
296
-
297
- /* Model Health */
298
- .modelList {
299
- display: flex;
300
- flex-direction: column;
301
- gap: 4px;
302
- }
303
-
304
- .modelRow {
305
- display: flex;
306
- align-items: center;
307
- gap: 8px;
308
- padding: 6px 10px;
309
- background: var(--bg-secondary, #14141f);
310
- border-radius: 6px;
311
- font-size: 12px;
312
- }
313
-
314
- .modelPriority {
315
- color: var(--text-muted, #888);
316
- min-width: 22px;
317
- font-weight: 600;
318
- }
319
-
320
- .modelName {
321
- flex: 1;
322
- color: var(--text-primary, #e0e0e0);
323
- font-family: 'SF Mono', monospace;
324
- overflow: hidden;
325
- text-overflow: ellipsis;
326
- white-space: nowrap;
327
- }
328
-
329
- .modelScore {
330
- color: var(--text-muted, #888);
331
- min-width: 40px;
332
- text-align: right;
333
- }
334
-
335
- .modelLatency {
336
- color: var(--text-muted, #888);
337
- min-width: 50px;
338
- text-align: right;
339
- }
340
-
341
- .circuitBadge {
342
- font-size: 10px;
343
- font-weight: 700;
344
- padding: 2px 6px;
345
- border-radius: 4px;
346
- text-transform: uppercase;
347
- letter-spacing: 0.5px;
348
- min-width: 70px;
349
- text-align: center;
350
- }
351
-
352
- .circuitClosed { background: rgba(0, 200, 100, 0.15); color: #00c864; }
353
- .circuitOpen { background: rgba(220, 53, 69, 0.15); color: #dc3545; }
354
- .circuitHalfOpen { background: rgba(255, 193, 7, 0.15); color: #ffc107; }
355
- .circuitAuth { background: rgba(255, 140, 0, 0.15); color: #ff8c00; }
356
- .circuitUnknown { background: rgba(128, 128, 128, 0.15); color: #888; }
357
-
358
- .pulse {
359
- color: #fbbf24;
360
- animation: pulse 2s infinite;
361
- }
362
-
363
- @keyframes pulse {
364
- 0% { opacity: 0.4; }
365
- 50% { opacity: 1; }
366
- 100% { opacity: 0.4; }
367
- }
368
-
369
- .activeList {
370
- display: flex;
371
- flex-direction: column;
372
- gap: 8px;
373
- margin-top: 8px;
374
- }
375
-
376
- .activeRow {
377
- background: rgba(251, 191, 36, 0.05);
378
- border: 1px solid rgba(251, 191, 36, 0.2);
379
- border-radius: 8px;
380
- padding: 10px 12px;
381
- }
382
-
383
- .activeMain {
384
- display: flex;
385
- justify-content: space-between;
386
- align-items: center;
387
- margin-bottom: 4px;
388
- }
389
-
390
- .activeModel {
391
- font-weight: 600;
392
- font-size: 14px;
393
- color: var(--text-primary, #e0e0e0);
394
- display: flex;
395
- align-items: center;
396
- gap: 8px;
397
- }
398
-
399
- .activeId {
400
- color: var(--text-muted, #666);
401
- font-size: 10px;
402
- background: rgba(255, 255, 255, 0.05);
403
- padding: 1px 4px;
404
- border-radius: 4px;
405
- font-family: 'SF Mono', monospace;
406
- font-weight: normal;
407
- }
408
-
409
- .activeStatus {
410
- font-size: 12px;
411
- }
412
-
413
- .processingText {
414
- color: #10b981;
415
- font-weight: 500;
416
- }
417
-
418
- .stalledText {
419
- color: #ef4444;
420
- font-weight: 700;
421
- animation: flash 1s infinite;
422
- }
423
-
424
- @keyframes flash {
425
- 0% { opacity: 1; }
426
- 50% { opacity: 0.5; }
427
- 100% { opacity: 1; }
428
- }
429
-
430
- .activeDetails {
431
- display: flex;
432
- gap: 12px;
433
- font-size: 11px;
434
- color: var(--text-muted, #888);
435
- }
436
-
437
- .activeCurrentModel {
438
- color: var(--text-secondary, #b0b0b0);
439
- }
440
-
441
- .activeTokens {
442
- background: rgba(255, 255, 255, 0.05);
443
- padding: 0 4px;
444
- border-radius: 3px;
445
- }
446
-
447
- /* Request Log */
448
- .logList {
449
- display: flex;
450
- flex-direction: column;
451
- gap: 2px;
452
- }
453
-
454
- .logRow {
455
- display: flex;
456
- align-items: center;
457
- gap: 8px;
458
- padding: 4px 8px;
459
- font-size: 11px;
460
- font-family: 'SF Mono', monospace;
461
- }
462
-
463
- .logTime { color: var(--text-muted, #555); min-width: 60px; }
464
- .logId {
465
- color: var(--text-muted, #666);
466
- font-size: 9px;
467
- min-width: 32px;
468
- background: rgba(255, 255, 255, 0.03);
469
- padding: 1px 3px;
470
- border-radius: 3px;
471
- text-align: center;
472
- font-family: 'SF Mono', monospace;
473
- }
474
- .logOk { color: #00c864; min-width: 28px; }
475
- .logErr { color: #dc3545; min-width: 28px; }
476
- .logModel { color: var(--text-secondary, #aaa); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
477
- .logLatency { color: var(--text-muted, #888); min-width: 50px; text-align: right; }
478
- .logTokens { color: var(--text-muted, #888); min-width: 60px; text-align: right; }
479
- .logDetail {
480
- color: var(--text-muted, #666);
481
- font-size: 10px;
482
- flex: 1;
483
- text-align: right;
484
- overflow: hidden;
485
- text-overflow: ellipsis;
486
- white-space: nowrap;
487
- }
488
- .logFailover {
489
- font-size: 9px;
490
- padding: 1px 4px;
491
- background: rgba(255, 193, 7, 0.15);
492
- color: #ffc107;
493
- border-radius: 3px;
494
- text-transform: uppercase;
495
- }
496
-
497
- .logEmpty {
498
- padding: 16px;
499
- text-align: center;
500
- color: var(--text-muted, #888);
501
- font-size: 12px;
502
- background: var(--bg-secondary, #14141f);
503
- border: 1px dashed var(--border, #1e1e2e);
504
- border-radius: 8px;
505
- }
506
-
507
- @keyframes fadeIn {
508
- from { opacity: 0; }
509
- to { opacity: 1; }
510
- }
511
-
512
- /* ── Set Manager (add / remove / drag-and-drop) ─────────────────────── */
513
-
514
- .setMeta {
515
- display: flex;
516
- align-items: center;
517
- justify-content: space-between;
518
- font-size: 12px;
519
- color: var(--text-muted, #888);
520
- margin-bottom: 8px;
521
- }
522
-
523
- .setMetaName {
524
- font-weight: 600;
525
- color: var(--text-secondary, #aaa);
526
- font-family: 'SF Mono', monospace;
527
- }
528
-
529
- .setActions {
530
- display: flex;
531
- gap: 6px;
532
- align-items: center;
533
- }
534
-
535
- .smallBtn {
536
- display: inline-flex;
537
- align-items: center;
538
- gap: 4px;
539
- padding: 4px 10px;
540
- background: var(--bg-secondary, #14141f);
541
- border: 1px solid var(--border, #1e1e2e);
542
- border-radius: 6px;
543
- color: var(--text-secondary, #aaa);
544
- font-size: 11px;
545
- font-weight: 500;
546
- cursor: pointer;
547
- font-family: inherit;
548
- &:hover { color: var(--text-primary, #e0e0e0); border-color: var(--text-muted, #888); }
549
- &:disabled { opacity: 0.4; cursor: not-allowed; }
550
- }
551
-
552
- .primaryBtn {
553
- display: inline-flex;
554
- align-items: center;
555
- gap: 4px;
556
- padding: 4px 10px;
557
- background: #00c864;
558
- color: #000;
559
- border: 1px solid #00c864;
560
- border-radius: 6px;
561
- font-size: 11px;
562
- font-weight: 600;
563
- cursor: pointer;
564
- font-family: inherit;
565
- &:hover { background: #00d874; border-color: #00d874; }
566
- &:disabled { opacity: 0.4; cursor: not-allowed; }
567
- }
568
-
569
- .setList {
570
- display: flex;
571
- flex-direction: column;
572
- gap: 4px;
573
- min-height: 60px;
574
- }
575
-
576
- .setEmpty {
577
- padding: 20px 16px;
578
- text-align: center;
579
- color: var(--text-muted, #888);
580
- font-size: 12px;
581
- background: var(--bg-secondary, #14141f);
582
- border: 1px dashed var(--border, #1e1e2e);
583
- border-radius: 8px;
584
- }
585
-
586
- .setEmptyTitle {
587
- font-size: 13px;
588
- font-weight: 600;
589
- color: var(--text-secondary, #aaa);
590
- margin-bottom: 4px;
591
- }
592
-
593
- .setEmptyHint {
594
- font-size: 11px;
595
- color: var(--text-muted, #888);
596
- line-height: 1.5;
597
- }
598
-
599
- .setEmptyHint strong {
600
- color: var(--text-secondary, #aaa);
601
- }
602
-
603
- .setRow {
604
- display: flex;
605
- align-items: center;
606
- gap: 8px;
607
- padding: 6px 10px;
608
- background: var(--bg-secondary, #14141f);
609
- border: 1px solid var(--border, #1e1e2e);
610
- border-radius: 6px;
611
- font-size: 12px;
612
- cursor: grab;
613
- user-select: none;
614
- transition: background 0.1s, border-color 0.1s, transform 0.1s;
615
- }
616
-
617
- .setRow:hover {
618
- background: var(--bg-hover, #1a1a2e);
619
- border-color: var(--text-muted, #888);
620
- }
621
-
622
- .setRowDragging {
623
- opacity: 0.5;
624
- transform: scale(0.99);
625
- cursor: grabbing;
626
- }
627
-
628
- .setRowDropTarget {
629
- border-color: #00c864;
630
- background: rgba(0, 200, 100, 0.06);
631
- }
632
-
633
- .setRowDropTargetAbove {
634
- border-top: 2px solid #00c864;
635
- }
636
-
637
- .setRowDropTargetBelow {
638
- border-bottom: 2px solid #00c864;
639
- }
640
-
641
- .setDragHandle {
642
- color: var(--text-muted, #888);
643
- cursor: grab;
644
- display: flex;
645
- align-items: center;
646
- font-size: 14px;
647
- line-height: 1;
648
- user-select: none;
649
- &:hover { color: var(--text-secondary, #aaa); }
650
- }
651
-
652
- .setPriority {
653
- min-width: 28px;
654
- font-weight: 700;
655
- font-size: 11px;
656
- color: var(--text-muted, #888);
657
- text-align: right;
658
- font-family: 'SF Mono', monospace;
659
- }
660
-
661
- .setKey {
662
- flex: 1;
663
- color: var(--text-primary, #e0e0e0);
664
- font-family: 'SF Mono', monospace;
665
- font-size: 11px;
666
- overflow: hidden;
667
- text-overflow: ellipsis;
668
- white-space: nowrap;
669
- }
670
-
671
- .setTier {
672
- font-size: 10px;
673
- font-weight: 700;
674
- padding: 2px 6px;
675
- border-radius: 3px;
676
- background: var(--bg-primary, #0f0f17);
677
- color: var(--text-muted, #888);
678
- min-width: 32px;
679
- text-align: center;
680
- text-transform: uppercase;
681
- }
682
-
683
- /* 📖 Priority badge variants — make the fallback chain semantics explicit
684
- (issue #120). Primary gets the accent color so users instantly see which
685
- model serves every request; fallbacks stay muted. */
686
- .setPriorityPrimary {
687
- min-width: 52px;
688
- padding: 2px 8px;
689
- border-radius: 4px;
690
- background: rgba(0, 200, 100, 0.14);
691
- color: var(--accent, #00c864);
692
- border: 1px solid rgba(0, 200, 100, 0.3);
693
- text-transform: uppercase;
694
- letter-spacing: 0.3px;
695
- text-align: center;
696
- }
697
-
698
- .setPriorityFallback {
699
- text-transform: none;
700
- opacity: 0.85;
701
- }
702
-
703
- /* 📖 Next-to-serve row — subtle accent left border + tint so the user can
704
- see at a glance which model will handle the next chat completion.
705
- Only applied when the daemon is running and routingOrder is known. */
706
- .setRowNext {
707
- background: linear-gradient(90deg, rgba(0, 200, 100, 0.07), transparent 60%);
708
- border-left: 2px solid var(--accent, #00c864);
709
- padding-left: 8px;
710
- }
711
-
712
- /* 📖 Priority legend — one-line explainer above the set list. */
713
- .priorityLegend {
714
- display: flex;
715
- align-items: center;
716
- gap: 8px;
717
- flex-wrap: wrap;
718
- padding: 6px 10px;
719
- margin-bottom: 8px;
720
- font-size: 11px;
721
- color: var(--text-muted, #888);
722
- background: var(--bg-secondary, #14141f);
723
- border: 1px solid var(--border, #1e1e2e);
724
- border-radius: 6px;
725
- }
726
-
727
- .legendPrimary {
728
- display: inline-flex;
729
- align-items: center;
730
- gap: 3px;
731
- font-weight: 600;
732
- color: var(--accent, #00c864);
733
- }
734
-
735
- .legendFallback {
736
- font-weight: 600;
737
- color: var(--text-secondary, #aaa);
738
- }
739
-
740
- .legendSeparator {
741
- color: var(--text-muted, #888);
742
- opacity: 0.7;
743
- }
744
-
745
- .legendNext {
746
- margin-left: auto;
747
- color: var(--text-secondary, #aaa);
748
- }
749
-
750
- .legendNext code {
751
- color: var(--accent, #00c864);
752
- font-family: 'SF Mono', monospace;
753
- font-size: 10px;
754
- }
755
-
756
- .setRowBtns {
757
- display: flex;
758
- gap: 2px;
759
- }
760
-
761
- .iconBtn {
762
- background: none;
763
- border: none;
764
- color: var(--text-muted, #888);
765
- cursor: pointer;
766
- padding: 4px;
767
- border-radius: 4px;
768
- display: flex;
769
- align-items: center;
770
- justify-content: center;
771
- &:hover { color: var(--text-primary, #e0e0e0); background: var(--bg-hover, #1a1a2e); }
772
- &:disabled { opacity: 0.3; cursor: not-allowed; }
773
- }
774
-
775
- .removeBtn:hover { color: #ff6b6b; background: rgba(220, 53, 69, 0.1); }
776
-
777
- /* Add picker */
778
- .pickerPanel {
779
- margin-top: 10px;
780
- background: var(--bg-secondary, #14141f);
781
- border: 1px solid var(--border, #1e1e2e);
782
- border-radius: 8px;
783
- padding: 10px 12px;
784
- display: flex;
785
- flex-direction: column;
786
- gap: 8px;
787
- }
788
-
789
- .pickerHeader {
790
- display: flex;
791
- align-items: center;
792
- justify-content: space-between;
793
- font-size: 12px;
794
- font-weight: 600;
795
- color: var(--text-secondary, #aaa);
796
- }
797
-
798
- .pickerSearch {
799
- display: flex;
800
- gap: 6px;
801
- }
802
-
803
- .pickerInput {
804
- flex: 1;
805
- padding: 6px 10px;
806
- background: var(--bg-primary, #0f0f17);
807
- border: 1px solid var(--border, #1e1e2e);
808
- border-radius: 6px;
809
- color: var(--text-primary, #e0e0e0);
810
- font-size: 12px;
811
- font-family: inherit;
812
- outline: none;
813
- &:focus { border-color: var(--accent, #00c864); }
814
- }
815
-
816
- .pickerSelect {
817
- padding: 6px 10px;
818
- background: var(--bg-primary, #0f0f17);
819
- border: 1px solid var(--border, #1e1e2e);
820
- border-radius: 6px;
821
- color: var(--text-primary, #e0e0e0);
822
- font-size: 12px;
823
- font-family: inherit;
824
- outline: none;
825
- cursor: pointer;
826
- max-width: 140px;
827
- }
828
-
829
- .pickerList {
830
- max-height: 220px;
831
- overflow-y: auto;
832
- display: flex;
833
- flex-direction: column;
834
- gap: 2px;
835
- }
836
-
837
- .pickerItem {
838
- display: flex;
839
- align-items: center;
840
- gap: 8px;
841
- padding: 4px 8px;
842
- font-size: 11px;
843
- font-family: 'SF Mono', monospace;
844
- color: var(--text-primary, #e0e0e0);
845
- border-radius: 4px;
846
- cursor: pointer;
847
- &:hover { background: var(--bg-hover, #1a1a2e); }
848
- }
849
-
850
- .pickerItemAdded {
851
- opacity: 0.4;
852
- cursor: not-allowed;
853
- text-decoration: line-through;
854
- }
855
-
856
- .pickerProvider {
857
- color: var(--text-muted, #888);
858
- min-width: 90px;
859
- }
860
-
861
- .pickerModel {
862
- flex: 1;
863
- overflow: hidden;
864
- text-overflow: ellipsis;
865
- white-space: nowrap;
866
- }
867
-
868
- .pickerBadge {
869
- font-size: 9px;
870
- padding: 1px 4px;
871
- border-radius: 3px;
872
- background: rgba(255, 140, 0, 0.15);
873
- color: #ff8c00;
874
- text-transform: uppercase;
875
- font-weight: 700;
876
- }
877
-
878
- .pickerBadgeOk {
879
- background: rgba(0, 200, 100, 0.15);
880
- color: #00c864;
881
- }
882
-
883
- .pickerEmpty {
884
- padding: 10px;
885
- text-align: center;
886
- color: var(--text-muted, #888);
887
- font-size: 12px;
888
- }
889
-
890
- .saveStatus {
891
- display: inline-flex;
892
- align-items: center;
893
- gap: 4px;
894
- font-size: 10px;
895
- color: var(--text-muted, #888);
896
- text-transform: uppercase;
897
- font-weight: 600;
898
- }
899
-
900
- .saveStatusOk { color: #00c864; }
901
- .saveStatusErr { color: #dc3545; }
902
-
903
- /* ── Auto-heal banner ────────────────────────────────────────── */
904
-
905
- .autoHealBanner {
906
- display: flex;
907
- align-items: center;
908
- justify-content: space-between;
909
- gap: 12px;
910
- padding: 10px 14px;
911
- margin-bottom: 12px;
912
- background: rgba(255, 193, 7, 0.08);
913
- border: 1px solid rgba(255, 193, 7, 0.3);
914
- border-radius: 8px;
915
- }
916
-
917
- .autoHealLeft {
918
- display: flex;
919
- align-items: flex-start;
920
- gap: 10px;
921
- min-width: 0;
922
- flex: 1;
923
- }
924
-
925
- .autoHealIcon {
926
- font-size: 18px;
927
- color: #ffc107;
928
- flex-shrink: 0;
929
- line-height: 1.4;
930
- }
931
-
932
- .autoHealTitle {
933
- font-size: 13px;
934
- font-weight: 600;
935
- color: var(--text-primary, #e0e0e0);
936
- margin-bottom: 2px;
937
- }
938
-
939
- .autoHealHint {
940
- font-size: 11px;
941
- color: var(--text-muted, #888);
942
- line-height: 1.4;
943
- }
944
-
945
- .autoHealHint strong {
946
- color: var(--text-secondary, #aaa);
947
- }
948
-
949
- .autoHealActions {
950
- display: flex;
951
- align-items: center;
952
- gap: 6px;
953
- flex-shrink: 0;
954
- }
955
-
956
- /* ════════════════════════════════════════════════════════════════════════
957
- 📖 Probe-all button + progress bar (AI Latency benchmarks on the set).
958
- ════════════════════════════════════════════════════════════════════════ */
959
-
960
- /* 📖 Active state for the Probe button while benchmarks run. */
961
- .probeBtnActive {
962
- border-color: var(--accent, #00c864);
963
- color: var(--accent, #00c864);
964
- cursor: progress;
965
- }
966
-
967
- /* 📖 Progress card shown below the set actions while probing. */
968
- .probeProgress {
969
- display: flex;
970
- flex-direction: column;
971
- gap: 6px;
972
- padding: 8px 10px;
973
- margin-bottom: 8px;
974
- background: var(--bg-secondary, #14141f);
975
- border: 1px solid rgba(0, 200, 100, 0.2);
976
- border-radius: 6px;
977
- }
978
-
979
- .probeProgressLabel {
980
- display: flex;
981
- align-items: center;
982
- gap: 5px;
983
- font-size: 11px;
984
- color: var(--accent, #00c864);
985
- font-weight: 600;
986
- }
987
-
988
- .probeProgressCount {
989
- margin-left: auto;
990
- color: var(--text-secondary, #aaa);
991
- font-weight: 500;
992
- font-family: 'SF Mono', monospace;
993
- }
994
-
995
- .probeProgressBar {
996
- height: 4px;
997
- background: var(--bg-primary, #0f0f17);
998
- border-radius: 2px;
999
- overflow: hidden;
1000
- }
1001
-
1002
- .probeProgressFill {
1003
- height: 100%;
1004
- background: linear-gradient(90deg, var(--accent, #00c864), rgba(0, 200, 100, 0.6));
1005
- border-radius: 2px;
1006
- transition: width 0.4s ease;
1007
- }
1008
-
1009
- /* ════════════════════════════════════════════════════════════════════════
1010
- 📖 AI Latency cell in each set row — populated by Probe all.
1011
- ════════════════════════════════════════════════════════════════════════ */
1012
-
1013
- .aiLatencyCell {
1014
- display: inline-flex;
1015
- align-items: center;
1016
- gap: 4px;
1017
- min-width: 78px;
1018
- font-size: 10px;
1019
- font-family: 'SF Mono', monospace;
1020
- justify-content: flex-end;
1021
- }
1022
-
1023
- .aiLatencyMs {
1024
- font-weight: 700;
1025
- color: #b400ff;
1026
- }
1027
-
1028
- .aiLatencyTps {
1029
- color: var(--text-muted, #888);
1030
- font-size: 9px;
1031
- }
1032
-
1033
- .aiLatencySpin {
1034
- color: var(--accent, #00c864);
1035
- letter-spacing: 2px;
1036
- animation: probeSpin 0.8s steps(3, end) infinite;
1037
- }
1038
-
1039
- @keyframes probeSpin {
1040
- 0% { opacity: 0.3; }
1041
- 50% { opacity: 1; }
1042
- 100% { opacity: 0.3; }
1043
- }
1044
-
1045
- .aiLatencyErr {
1046
- color: var(--error, #ff5a5a);
1047
- font-size: 10px;
1048
- }
1049
-
1050
- .aiLatencyNone {
1051
- color: var(--text-muted, #888);
1052
- opacity: 0.4;
1053
- }
1054
-
1055
- /* ════════════════════════════════════════════════════════════════════════
1056
- 📖 Mini Playground — live router test inside the Router view.
1057
- ════════════════════════════════════════════════════════════════════════ */
1058
-
1059
- .miniPgHint {
1060
- margin-left: auto;
1061
- font-size: 10px;
1062
- font-weight: 400;
1063
- color: var(--text-muted, #888);
1064
- }
1065
-
1066
- .miniPgHint code {
1067
- color: var(--accent, #00c864);
1068
- font-family: 'SF Mono', monospace;
1069
- }
1070
-
1071
- .miniPg {
1072
- max-height: 220px;
1073
- overflow-y: auto;
1074
- padding: 8px;
1075
- margin-bottom: 8px;
1076
- background: var(--bg-primary, #0f0f17);
1077
- border: 1px solid var(--border, #1e1e2e);
1078
- border-radius: 8px;
1079
- display: flex;
1080
- flex-direction: column;
1081
- gap: 8px;
1082
- scroll-behavior: smooth;
1083
- }
1084
-
1085
- .miniPgEmpty {
1086
- text-align: center;
1087
- color: var(--text-muted, #888);
1088
- font-size: 11px;
1089
- padding: 18px 8px;
1090
- }
1091
-
1092
- .miniPgMsg {
1093
- display: flex;
1094
- flex-direction: column;
1095
- gap: 3px;
1096
- max-width: 88%;
1097
- }
1098
-
1099
- .miniPgUser {
1100
- align-self: flex-end;
1101
- align-items: flex-end;
1102
- }
1103
-
1104
- .miniPgAssistant {
1105
- align-self: flex-start;
1106
- align-items: flex-start;
1107
- }
1108
-
1109
- .miniPgBubble {
1110
- padding: 7px 11px;
1111
- border-radius: 12px;
1112
- font-size: 12px;
1113
- line-height: 1.5;
1114
- white-space: pre-wrap;
1115
- word-break: break-word;
1116
- color: var(--text-primary, #e0e0e0);
1117
- }
1118
-
1119
- .miniPgUser .miniPgBubble {
1120
- background: rgba(0, 200, 100, 0.12);
1121
- border: 1px solid rgba(0, 200, 100, 0.2);
1122
- border-bottom-right-radius: 4px;
1123
- }
1124
-
1125
- .miniPgAssistant .miniPgBubble {
1126
- background: var(--bg-secondary, #14141f);
1127
- border: 1px solid var(--border, #1e1e2e);
1128
- border-bottom-left-radius: 4px;
1129
- }
1130
-
1131
- .miniPgTyping {
1132
- color: var(--accent, #00c864);
1133
- letter-spacing: 3px;
1134
- animation: probeSpin 0.9s steps(3, end) infinite;
1135
- }
1136
-
1137
- .miniPgError {
1138
- display: block;
1139
- margin-top: 4px;
1140
- color: var(--error, #ff5a5a);
1141
- font-size: 11px;
1142
- }
1143
-
1144
- .miniPgAborted {
1145
- display: block;
1146
- margin-top: 4px;
1147
- color: var(--text-muted, #888);
1148
- font-size: 10px;
1149
- font-style: italic;
1150
- }
1151
-
1152
- /* 📖 Target badge under each user bubble — shows the Primary model the
1153
- router will try first for this message (priority-first routing, #120).
1154
- If failover kicks in, the reply's .miniPgRouted badge shows a different
1155
- model, making the fallback chain visible at a glance. */
1156
- .miniPgTarget {
1157
- font-size: 10px;
1158
- color: var(--text-muted, #888);
1159
- font-family: 'SF Mono', monospace;
1160
- padding: 0 4px;
1161
- display: inline-flex;
1162
- align-items: center;
1163
- gap: 3px;
1164
- }
1165
-
1166
- .miniPgTarget svg {
1167
- color: var(--accent, #00c864);
1168
- }
1169
-
1170
- /* 📖 Routed-via badge under each assistant reply — shows WHICH model the
1171
- priority-first router actually picked for this message (issue #120). */
1172
- .miniPgRouted {
1173
- font-size: 10px;
1174
- color: var(--text-muted, #888);
1175
- font-family: 'SF Mono', monospace;
1176
- padding: 0 4px;
1177
- }
1178
-
1179
- .miniPgRouted span {
1180
- color: var(--accent, #00c864);
1181
- }
1182
-
1183
- .miniPgInputRow {
1184
- display: flex;
1185
- gap: 6px;
1186
- align-items: stretch;
1187
- }
1188
-
1189
- .miniPgInput {
1190
- flex: 1;
1191
- padding: 8px 12px;
1192
- background: var(--bg-primary, #0f0f17);
1193
- border: 1px solid var(--border, #1e1e2e);
1194
- border-radius: 8px;
1195
- color: var(--text-primary, #e0e0e0);
1196
- font-size: 12px;
1197
- font-family: inherit;
1198
- outline: none;
1199
- transition: border-color 0.15s;
1200
- }
1201
-
1202
- .miniPgInput:focus {
1203
- border-color: var(--accent, #00c864);
1204
- }
1205
-
1206
- .miniPgInput::placeholder {
1207
- color: var(--text-muted, #888);
1208
- }
1209
-
1210
- .miniPgSendBtn,
1211
- .miniPgStopBtn {
1212
- display: flex;
1213
- align-items: center;
1214
- justify-content: center;
1215
- width: 38px;
1216
- border: none;
1217
- border-radius: 8px;
1218
- cursor: pointer;
1219
- transition: opacity 0.15s, background 0.15s;
1220
- flex-shrink: 0;
1221
- }
1222
-
1223
- .miniPgSendBtn {
1224
- background: var(--accent, #00c864);
1225
- color: #000;
1226
- }
1227
-
1228
- .miniPgSendBtn:disabled {
1229
- opacity: 0.4;
1230
- cursor: not-allowed;
1231
- }
1232
-
1233
- .miniPgStopBtn {
1234
- background: var(--error, #ff5a5a);
1235
- color: #fff;
1236
- }
1237
-
1238
- /* ── Passive quota (t2) ───────────────────────────────────────────────── */
1239
-
1240
- .quotaGrid {
1241
- display: grid;
1242
- grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
1243
- gap: 8px;
1244
- margin-top: 8px;
1245
- }
1246
-
1247
- .quotaCell {
1248
- display: grid;
1249
- grid-template-columns: 18px 1fr auto;
1250
- align-items: center;
1251
- gap: 4px 8px;
1252
- padding: 6px 10px;
1253
- border-radius: 6px;
1254
- background: rgba(255, 255, 255, 0.04);
1255
- border: 1px solid rgba(255, 255, 255, 0.08);
1256
- }
1257
-
1258
- .quotaSource {
1259
- font-size: 14px;
1260
- text-align: center;
1261
- }
1262
-
1263
- .quotaName {
1264
- font-size: 12px;
1265
- font-weight: 600;
1266
- opacity: 0.9;
1267
- text-transform: lowercase;
1268
- font-family: var(--mono, ui-monospace, SFMono-Regular, monospace);
1269
- }
1270
-
1271
- .quotaBar {
1272
- grid-column: 1 / -1;
1273
- height: 4px;
1274
- background: rgba(255, 255, 255, 0.08);
1275
- border-radius: 2px;
1276
- overflow: hidden;
1277
- }
1278
-
1279
- .quotaFill {
1280
- height: 100%;
1281
- transition: width 0.4s ease-out;
1282
- }
1283
-
1284
- .quotaPct {
1285
- font-size: 12px;
1286
- font-weight: 700;
1287
- font-variant-numeric: tabular-nums;
1288
- text-align: right;
1289
- }