lemonade-sdk 8.1.4__py3-none-any.whl → 8.2.2__py3-none-any.whl

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.

Potentially problematic release.


This version of lemonade-sdk might be problematic. Click here for more details.

Files changed (53) hide show
  1. lemonade/cache.py +6 -1
  2. lemonade/cli.py +47 -5
  3. lemonade/common/inference_engines.py +13 -4
  4. lemonade/common/status.py +4 -4
  5. lemonade/common/system_info.py +544 -1
  6. lemonade/profilers/agt_power.py +437 -0
  7. lemonade/profilers/hwinfo_power.py +429 -0
  8. lemonade/tools/accuracy.py +143 -48
  9. lemonade/tools/adapter.py +6 -1
  10. lemonade/tools/bench.py +26 -8
  11. lemonade/tools/flm/__init__.py +1 -0
  12. lemonade/tools/flm/utils.py +303 -0
  13. lemonade/tools/huggingface/bench.py +6 -1
  14. lemonade/tools/llamacpp/bench.py +146 -27
  15. lemonade/tools/llamacpp/load.py +30 -2
  16. lemonade/tools/llamacpp/utils.py +393 -33
  17. lemonade/tools/oga/bench.py +5 -26
  18. lemonade/tools/oga/load.py +60 -121
  19. lemonade/tools/oga/migration.py +403 -0
  20. lemonade/tools/report/table.py +76 -8
  21. lemonade/tools/server/flm.py +133 -0
  22. lemonade/tools/server/llamacpp.py +220 -553
  23. lemonade/tools/server/serve.py +684 -168
  24. lemonade/tools/server/static/js/chat.js +666 -342
  25. lemonade/tools/server/static/js/model-settings.js +24 -3
  26. lemonade/tools/server/static/js/models.js +597 -73
  27. lemonade/tools/server/static/js/shared.js +79 -14
  28. lemonade/tools/server/static/logs.html +191 -0
  29. lemonade/tools/server/static/styles.css +491 -66
  30. lemonade/tools/server/static/webapp.html +83 -31
  31. lemonade/tools/server/tray.py +158 -38
  32. lemonade/tools/server/utils/macos_tray.py +226 -0
  33. lemonade/tools/server/utils/{system_tray.py → windows_tray.py} +13 -0
  34. lemonade/tools/server/webapp.py +4 -1
  35. lemonade/tools/server/wrapped_server.py +559 -0
  36. lemonade/version.py +1 -1
  37. lemonade_install/install.py +54 -611
  38. {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/METADATA +29 -72
  39. lemonade_sdk-8.2.2.dist-info/RECORD +83 -0
  40. lemonade_server/cli.py +145 -37
  41. lemonade_server/model_manager.py +521 -37
  42. lemonade_server/pydantic_models.py +28 -1
  43. lemonade_server/server_models.json +246 -92
  44. lemonade_server/settings.py +39 -39
  45. lemonade/tools/quark/__init__.py +0 -0
  46. lemonade/tools/quark/quark_load.py +0 -173
  47. lemonade/tools/quark/quark_quantize.py +0 -439
  48. lemonade_sdk-8.1.4.dist-info/RECORD +0 -77
  49. {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/WHEEL +0 -0
  50. {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/entry_points.txt +0 -0
  51. {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/licenses/LICENSE +0 -0
  52. {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/licenses/NOTICE.md +0 -0
  53. {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/top_level.txt +0 -0
@@ -35,6 +35,12 @@
35
35
  --purple-hover: #744f7e;
36
36
  --purple-light: #f5f1f6;
37
37
 
38
+ /* Status Colors */
39
+ --status-green: #28a745;
40
+ --status-red: #dc3545;
41
+ --status-yellow: #ffc107;
42
+ --status-gray: #6c757d;
43
+
38
44
  /* Transitions */
39
45
  --transition-fast: 0.2s ease;
40
46
  --transition-medium: 0.3s ease;
@@ -93,6 +99,15 @@ body::before {
93
99
  .brand-title a {
94
100
  color: inherit;
95
101
  text-decoration: none;
102
+ display: flex;
103
+ align-items: center;
104
+ gap: 0.5rem;
105
+ }
106
+
107
+ .brand-icon {
108
+ width: 1.5rem;
109
+ height: 1.5rem;
110
+ vertical-align: middle;
96
111
  }
97
112
 
98
113
  .navbar-links {
@@ -157,11 +172,13 @@ body::before {
157
172
  margin-bottom: 2em;
158
173
  border-radius: 8px;
159
174
  border: 1px solid #e0e0e0;
160
- max-width: 1000px;
161
175
  min-width: 320px;
162
- width: calc(100% - 2rem); /* Responsive width with margin */
176
+ width: 100%;
163
177
  margin-left: 1rem;
164
178
  margin-right: 1rem;
179
+ /* Removing only the bottom border and shadow for the content tab gap to look nicer */
180
+ box-shadow: 0 -2px 8px rgba(0, 0, 0, 0);
181
+ border-bottom: none;
165
182
  }
166
183
 
167
184
  .tabs {
@@ -208,14 +225,24 @@ body::before {
208
225
  position: relative;
209
226
  }
210
227
 
228
+ /* Wrapper for the select element with embedded status light */
229
+ .model-select-wrapper {
230
+ position: relative;
231
+ display: flex;
232
+ align-items: center;
233
+ }
234
+
211
235
  .status-light {
212
236
  width: 8px;
213
237
  height: 8px;
214
238
  border-radius: 50%;
215
- position: relative;
239
+ position: absolute;
240
+ left: 8px;
241
+ top: 50%;
242
+ transform: translateY(-50%);
216
243
  transition: all var(--transition-fast);
217
244
  flex-shrink: 0;
218
- align-self: center;
245
+ z-index: 10;
219
246
  }
220
247
 
221
248
  .status-light::before {
@@ -230,49 +257,44 @@ body::before {
230
257
  animation: pulse-glow 2s infinite;
231
258
  }
232
259
 
233
- /* Online status (green) */
260
+ /* Online model unloaded status (yellow) */
234
261
  .model-status-indicator.online .status-light {
235
- background: #28a745;
262
+ background: var(--status-yellow);
236
263
  box-shadow: 0 0 8px rgba(40, 167, 69, 0.6);
237
264
  }
238
265
 
239
266
  .model-status-indicator.online .status-light::before {
240
- background: #28a745;
267
+ background: var(--status-yellow);
241
268
  }
242
269
 
243
- .model-status-indicator.online .model-status-text {
244
- color: #28a745;
245
- font-weight: 600;
270
+ /* Online model loading status (yellow) */
271
+ .model-status-indicator.loading .status-light {
272
+ background: var(--status-yellow);
273
+ box-shadow: 0 0 8px rgba(40, 167, 69, 0.6);
274
+ }
275
+
276
+ .model-status-indicator.loading .status-light::before {
277
+ background: var(--status-yellow);
246
278
  }
247
279
 
248
280
  /* Offline status (red) */
249
281
  .model-status-indicator.offline .status-light {
250
- background: #dc3545;
282
+ background: var(--status-red);
251
283
  box-shadow: 0 0 8px rgba(220, 53, 69, 0.6);
252
284
  }
253
285
 
254
286
  .model-status-indicator.offline .status-light::before {
255
- background: #dc3545;
287
+ background: var(--status-red);
256
288
  }
257
289
 
258
- .model-status-indicator.offline .model-status-text {
259
- color: #dc3545;
260
- font-weight: 600;
261
- }
262
-
263
- /* Model loaded status (same as online but with model name) */
290
+ /* Online model loaded status (with model name) */
264
291
  .model-status-indicator.loaded .status-light {
265
- background: #28a745;
292
+ background: var(--status-green);
266
293
  box-shadow: 0 0 8px rgba(40, 167, 69, 0.6);
267
294
  }
268
295
 
269
296
  .model-status-indicator.loaded .status-light::before {
270
- background: #28a745;
271
- }
272
-
273
- .model-status-indicator.loaded .model-status-text {
274
- color: #28a745;
275
- font-weight: 600;
297
+ background: var(--status-green);
276
298
  }
277
299
 
278
300
  @keyframes pulse-glow {
@@ -286,25 +308,99 @@ body::before {
286
308
  }
287
309
  }
288
310
 
289
- .model-status-text {
290
- font-weight: 600;
311
+ /* Base styles for the select element */
312
+ .model-select {
313
+ padding: 0.5rem 0.75rem 0.5rem 1.5rem;
314
+ border: 1px solid #ddd;
315
+ border-radius: 6px;
316
+ background: #fafafa;
291
317
  font-size: 0.9rem;
318
+ min-width: 180px;
319
+ cursor: pointer;
320
+ transition: all var(--transition-fast);
321
+ text-align-last: center;
322
+ -moz-appearance: none;
323
+ -webkit-appearance: none;
324
+ appearance: none;
325
+ }
326
+
327
+ .model-select:focus {
328
+ outline: none;
329
+ border-color: var(--accent-gold);
330
+ box-shadow: 0 0 0 2px rgba(255, 193, 7, 0.2);
331
+ }
332
+
333
+ /* Existing styles for selective coloring based on status */
334
+ .model-status-indicator.loaded .model-select {
335
+ color: var(--status-green); /* Green for loaded model */
336
+ font-weight: bold;
337
+ }
338
+
339
+ .model-status-indicator.loading .model-select {
340
+ color: var(--status-gray); /* Gray for loading */
341
+ font-style: italic;
342
+ cursor: wait;
343
+ }
344
+
345
+ .model-status-indicator.online .model-select {
346
+ color: var(--status-yellow); /* Yellow when online but no model loaded */
347
+ font-weight: bold;
348
+ }
349
+
350
+ .model-status-indicator.offline .model-select {
351
+ color: var(--status-red); /* Red when offline */
352
+ font-weight: bold;
353
+ }
354
+
355
+ /* Ensure options in the list are not affected by the select's styling */
356
+ .model-select option {
357
+ color: var(--text-primary);
358
+ font-weight: normal;
359
+ font-style: normal;
360
+ background-color: white;
361
+ padding: 0.5rem;
362
+ }
363
+
364
+ /* Highlight the selected option ONLY in the open list (if desired) */
365
+ .model-select option:checked {
366
+ background-color: #f0f0f0;
367
+ color: var(--text-primary);
368
+ }
369
+
370
+ .model-select option:checked.server-offline {
371
+ background-color: #f0f0f0;
372
+ color: var(--status-red);
373
+ }
374
+
375
+ .model-select:disabled {
376
+ background: #f5f5f5;
377
+ cursor: not-allowed;
378
+ opacity: 0.7;
379
+ }
380
+ /* When server is offline all buttons are disabled and appear muted */
381
+ button:disabled {
382
+ cursor: not-allowed;
383
+ opacity: 0.6;
384
+ background-color: #cccccc;
385
+ color: #666666;
292
386
  }
293
387
 
294
388
  .model-action-btn {
295
- display: flex;
389
+ display: none; /* Hide by default */
296
390
  align-items: center;
297
391
  justify-content: center;
298
- width: 24px;
299
- height: 24px;
392
+ width: 1.1em;
393
+ height: 1.1em;
300
394
  background: transparent;
301
395
  border: none;
302
396
  border-radius: 50%;
303
397
  cursor: pointer;
304
398
  transition: all var(--transition-fast);
305
- font-size: 0.9rem;
399
+ font-size: 1.5rem;
306
400
  color: #666;
307
- margin-left: 0.2rem;
401
+ margin-left: 0.1rem;
402
+ margin-right: 0.2rem;
403
+ line-height: 1;
308
404
  }
309
405
 
310
406
  .model-action-btn:hover {
@@ -312,11 +408,23 @@ body::before {
312
408
  color: #333;
313
409
  }
314
410
 
411
+ /* Unload button is only visible when a model is loaded */
412
+ .model-status-indicator.loaded .model-action-btn {
413
+ display: flex;
414
+ }
415
+
416
+ .tab-content-wrapper {
417
+ width: 85%;
418
+ }
419
+
315
420
  .tab-content {
316
421
  display: none;
317
422
  padding: 2em;
318
423
  background: #fafafa;
319
424
  border-radius: 0 0 8px 8px;
425
+ /* adding border and shadow that was removed for the gap look from higher div */
426
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
427
+ border: 1px solid #e0e0e0;
320
428
  }
321
429
 
322
430
  .tab-content.active {
@@ -327,25 +435,47 @@ body::before {
327
435
  .chat-container {
328
436
  display: flex;
329
437
  flex-direction: column;
330
- height: calc(100vh - 650px); /* Subtract space for navbar, title, wall of logos, etc */
331
- min-height: 300px;
332
- max-height: 1200px;
333
- max-width: 800px;
438
+ /* Max space available in viewport
439
+ This also prevents the chat-history section to resize lower than a certain point */
440
+ min-height: calc(100vh - 550px);
441
+ min-width: 300px;
442
+ max-width: 100%;
334
443
  width: 100%;
335
444
  margin: 0 auto;
336
445
  border: 1px solid #e0e0e0;
337
446
  border-radius: 8px;
338
447
  background: #fff;
448
+ /* Use a semi-fixed chat 'window' height so streaming content never expands the page.
449
+ The chat-history area inside will scroll. The CSS variable allows easy tuning. */
450
+ --chat-height: 520px;
451
+ /* Allow vertical resizing by the user while keeping horizontal size fixed.
452
+ Constrain the resize with min/max heights so it stays usable and doesn't overflow the viewport. */
453
+ resize: vertical;
454
+ height: var(--chat-height);
455
+ max-height: calc(100vh - 120px);
456
+ overflow: hidden; /* hide overflow at container level; chat-history will scroll */
457
+ }
458
+
459
+ /* Responsive fallback: if the viewport height is small, cap the chat window to fit */
460
+ @media (max-height: 700px) {
461
+ .chat-container {
462
+ height: calc(100vh - 180px); /* leave space for navbar/footer */
463
+ }
339
464
  }
340
465
 
341
466
  .chat-history {
342
- flex: 1;
467
+ /* Make chat history take remaining space in the chat container and scroll when content
468
+ exceeds this space. This prevents streaming text from expanding the overall layout. */
469
+ flex: 1 1 auto;
343
470
  overflow-y: auto;
471
+ -webkit-overflow-scrolling: touch;
344
472
  padding: 1em;
345
473
  border-bottom: 1px solid #e0e0e0;
346
474
  display: flex;
347
475
  flex-direction: column;
348
476
  gap: 0.5em;
477
+ /* Optional visual hint for scrollable content */
478
+ scrollbar-width: thin;
349
479
  }
350
480
 
351
481
  .chat-message {
@@ -388,6 +518,21 @@ body::before {
388
518
  align-self: flex-start;
389
519
  }
390
520
 
521
+ .chat-message.system {
522
+ align-items: flex-start;
523
+ }
524
+
525
+ .chat-bubble.system {
526
+ background: linear-gradient(135deg, #f0f8f0 0%, #e8f5e8 100%);
527
+ color: #2d7f47;
528
+ border-bottom-left-radius: 4px;
529
+ align-self: flex-start;
530
+ border: 1px solid #c8e6c9;
531
+ font-style: normal;
532
+ font-weight: 500;
533
+ box-shadow: 0 1px 3px rgba(45, 127, 71, 0.1);
534
+ }
535
+
391
536
  /* Markdown styling within chat bubbles */
392
537
  .chat-bubble h1,
393
538
  .chat-bubble h2,
@@ -570,7 +715,7 @@ body::before {
570
715
  align-items: center;
571
716
  }
572
717
 
573
- .input-with-indicator input[type='text'] {
718
+ #chat-input {
574
719
  flex: 1;
575
720
  padding: 0.5em;
576
721
  border: 1px solid #ddd;
@@ -578,6 +723,16 @@ body::before {
578
723
  background: #fff;
579
724
  color: #222;
580
725
  margin: 0;
726
+ resize: vertical;
727
+ min-height: 40px;
728
+ font-family: inherit;
729
+ }
730
+
731
+ /* Update placeholder style */
732
+ #chat-input::placeholder {
733
+ color: #aaa;
734
+ opacity: 1;
735
+ font-style: italic;
581
736
  }
582
737
 
583
738
  #attachment-indicator {
@@ -849,6 +1004,10 @@ body::before {
849
1004
  background-color: var(--info-primary);
850
1005
  }
851
1006
 
1007
+ .model-label.tool-calling {
1008
+ background-color: #FFB74D;
1009
+ }
1010
+
852
1011
  .model-label.other {
853
1012
  background-color: var(--success-primary);
854
1013
  }
@@ -1081,6 +1240,7 @@ body::before {
1081
1240
 
1082
1241
  #register-model-name:focus {
1083
1242
  border-color: #e6b800;
1243
+ box-shadow: 0 2px 12px rgba(230,184,0,0.25);
1084
1244
  }
1085
1245
 
1086
1246
  .form-input-wrapper {
@@ -1128,6 +1288,62 @@ body::before {
1128
1288
  transform: translateY(-1px);
1129
1289
  box-shadow: 0 2px 8px rgba(91, 141, 184, 0.3);
1130
1290
  }
1291
+ .checkpoint-input-group {
1292
+ display: flex;
1293
+ align-items: stretch;
1294
+ flex: 1;
1295
+ border-radius: 6px;
1296
+ overflow: hidden;
1297
+ transition: box-shadow 0.2s ease;
1298
+ min-width: 0;
1299
+ }
1300
+
1301
+ .checkpoint-input-group:focus-within {
1302
+ box-shadow: 0 2px 12px rgba(230,184,0,0.25);
1303
+ }
1304
+
1305
+ .checkpoint-input-group #register-checkpoint {
1306
+ border-radius: 6px 0 0 6px;
1307
+ border: 1px solid #d5d5d5;
1308
+ border-right: none;
1309
+ padding: 0.6em 0.8em;
1310
+ font-size: 0.95em;
1311
+ background: #fff;
1312
+ color: #222;
1313
+ flex: 1;
1314
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
1315
+ outline: none;
1316
+ box-shadow: 0 2px 8px rgba(0,0,0,0.06);
1317
+ box-sizing: border-box;
1318
+ min-width: 0;
1319
+ }
1320
+
1321
+ .checkpoint-input-group #register-checkpoint:focus {
1322
+ border-color: #e6b800;
1323
+ box-shadow: 0 2px 12px rgba(230,184,0,0.25);
1324
+ }
1325
+
1326
+ .folder-select-btn {
1327
+ background: #f8f9fa;
1328
+ border: 1px solid #d5d5d5;
1329
+ border-left: none;
1330
+ border-radius: 0 6px 6px 0;
1331
+ padding: 0.6em 0.8em;
1332
+ cursor: pointer;
1333
+ transition: all 0.2s ease;
1334
+ font-size: 1rem;
1335
+ color: #666;
1336
+ min-width: 48px;
1337
+ display: flex;
1338
+ align-items: center;
1339
+ justify-content: center;
1340
+ }
1341
+
1342
+ .folder-select-btn:hover {
1343
+ background: #e9ecef;
1344
+ color: #333;
1345
+ }
1346
+ /* */
1131
1347
 
1132
1348
  #register-mmproj, #register-checkpoint {
1133
1349
  border-radius: 6px;
@@ -1382,6 +1598,11 @@ body::before {
1382
1598
  font-size: 1.3rem;
1383
1599
  }
1384
1600
 
1601
+ .brand-icon {
1602
+ width: 1.3rem;
1603
+ height: 1.3rem;
1604
+ }
1605
+
1385
1606
  .navbar-links {
1386
1607
  gap: 1.5rem;
1387
1608
  font-size: 1rem;
@@ -1397,6 +1618,11 @@ body::before {
1397
1618
  font-size: 1.2rem;
1398
1619
  }
1399
1620
 
1621
+ .brand-icon {
1622
+ width: 1.2rem;
1623
+ height: 1.2rem;
1624
+ }
1625
+
1400
1626
  .navbar-links {
1401
1627
  gap: 1rem;
1402
1628
  font-size: 0.9rem;
@@ -1728,6 +1954,53 @@ body::before {
1728
1954
  align-items: center;
1729
1955
  }
1730
1956
 
1957
+ /* FastFlowLM notice styles */
1958
+ .flm-notice {
1959
+ background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%);
1960
+ border: 1px solid #ffeaa7;
1961
+ border-left: 4px solid #f39c12;
1962
+ border-radius: 8px;
1963
+ margin-bottom: 1.5rem;
1964
+ box-shadow: 0 2px 8px rgba(243, 156, 18, 0.1);
1965
+ animation: fadeIn 0.3s ease;
1966
+ }
1967
+
1968
+ .flm-notice-content {
1969
+ display: flex;
1970
+ align-items: flex-start;
1971
+ gap: 0.75rem;
1972
+ padding: 1rem 1.25rem;
1973
+ }
1974
+
1975
+ .flm-notice-icon {
1976
+ font-size: 1.2rem;
1977
+ flex-shrink: 0;
1978
+ margin-top: 0.1rem;
1979
+ }
1980
+
1981
+ .flm-notice-text {
1982
+ flex: 1;
1983
+ font-size: 0.95rem;
1984
+ line-height: 1.5;
1985
+ color: #856404;
1986
+ }
1987
+
1988
+ .flm-notice-text strong {
1989
+ color: #6c5ce7;
1990
+ font-weight: 600;
1991
+ }
1992
+
1993
+ .flm-notice-text a {
1994
+ color: var(--info-primary);
1995
+ text-decoration: none;
1996
+ font-weight: 500;
1997
+ }
1998
+
1999
+ .flm-notice-text a:hover {
2000
+ text-decoration: underline;
2001
+ color: var(--info-hover);
2002
+ }
2003
+
1731
2004
  .error-banner .close-btn {
1732
2005
  background: none;
1733
2006
  border: none;
@@ -1743,6 +2016,154 @@ body::before {
1743
2016
  opacity: 0.8;
1744
2017
  }
1745
2018
 
2019
+ /* Migration banner - reuses error-banner structure with warning color */
2020
+ .migration-banner {
2021
+ position: fixed;
2022
+ top: 10px;
2023
+ left: 50%;
2024
+ transform: translateX(-50%);
2025
+ background-color: #ffa500;
2026
+ color: #222;
2027
+ padding: 0.6em 1.2em;
2028
+ border-radius: 6px;
2029
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
2030
+ z-index: 10000;
2031
+ font-weight: 600;
2032
+ display: none;
2033
+ animation: fadeIn 0.2s ease;
2034
+ align-items: center;
2035
+ gap: 0.8em;
2036
+ }
2037
+
2038
+ .migration-action-btn {
2039
+ background: #222;
2040
+ color: #ffa500;
2041
+ border: none;
2042
+ padding: 0.4em 0.8em;
2043
+ border-radius: 4px;
2044
+ cursor: pointer;
2045
+ font-weight: 600;
2046
+ transition: background 0.2s;
2047
+ }
2048
+
2049
+ .migration-action-btn:hover {
2050
+ background: #444;
2051
+ }
2052
+
2053
+ .migration-banner .close-btn {
2054
+ background: none;
2055
+ border: none;
2056
+ color: #222;
2057
+ font-size: 1.2em;
2058
+ margin-left: 0.8em;
2059
+ cursor: pointer;
2060
+ padding: 0;
2061
+ line-height: 1;
2062
+ }
2063
+
2064
+ /* Modal styles */
2065
+ .modal {
2066
+ position: fixed;
2067
+ z-index: 10001;
2068
+ left: 0;
2069
+ top: 0;
2070
+ width: 100%;
2071
+ height: 100%;
2072
+ background-color: rgba(0,0,0,0.5);
2073
+ display: flex;
2074
+ align-items: center;
2075
+ justify-content: center;
2076
+ }
2077
+
2078
+ .modal-content {
2079
+ background: #fff;
2080
+ border-radius: 8px;
2081
+ max-width: 600px;
2082
+ width: 90%;
2083
+ max-height: 80vh;
2084
+ display: flex;
2085
+ flex-direction: column;
2086
+ box-shadow: 0 4px 20px rgba(0,0,0,0.3);
2087
+ }
2088
+
2089
+ .modal-header {
2090
+ display: flex;
2091
+ justify-content: space-between;
2092
+ align-items: center;
2093
+ padding: 1.2rem 1.5rem;
2094
+ border-bottom: 1px solid #e0e0e0;
2095
+ }
2096
+
2097
+ .modal-header h2 {
2098
+ margin: 0;
2099
+ font-size: 1.3rem;
2100
+ }
2101
+
2102
+ .modal-close {
2103
+ background: none;
2104
+ border: none;
2105
+ font-size: 1.5rem;
2106
+ cursor: pointer;
2107
+ color: #666;
2108
+ padding: 0;
2109
+ }
2110
+
2111
+ .modal-body {
2112
+ padding: 1.5rem;
2113
+ overflow-y: auto;
2114
+ flex: 1;
2115
+ }
2116
+
2117
+ .migration-model-list {
2118
+ margin: 1rem 0;
2119
+ border: 1px solid #e0e0e0;
2120
+ border-radius: 4px;
2121
+ max-height: 300px;
2122
+ overflow-y: auto;
2123
+ }
2124
+
2125
+ .migration-model-item {
2126
+ padding: 0.75rem 1rem;
2127
+ border-bottom: 1px solid #f0f0f0;
2128
+ display: flex;
2129
+ justify-content: space-between;
2130
+ }
2131
+
2132
+ .migration-model-item:last-child {
2133
+ border-bottom: none;
2134
+ }
2135
+
2136
+ .migration-summary {
2137
+ margin-top: 1rem;
2138
+ padding: 0.75rem;
2139
+ background: #f8f9fa;
2140
+ border-radius: 4px;
2141
+ text-align: center;
2142
+ }
2143
+
2144
+ .modal-footer {
2145
+ padding: 1rem 1.5rem;
2146
+ border-top: 1px solid #e0e0e0;
2147
+ display: flex;
2148
+ justify-content: flex-end;
2149
+ gap: 0.75rem;
2150
+ }
2151
+
2152
+ .delete-btn {
2153
+ padding: 0.6em 1.5em;
2154
+ background: var(--danger-primary);
2155
+ color: white;
2156
+ border: none;
2157
+ border-radius: 4px;
2158
+ font-weight: 600;
2159
+ cursor: pointer;
2160
+ transition: background 0.2s;
2161
+ }
2162
+
2163
+ .delete-btn:hover {
2164
+ background: var(--danger-hover);
2165
+ }
2166
+
1746
2167
  /* === Model Settings === */
1747
2168
  .model-settings-container {
1748
2169
  max-width: 600px;
@@ -1897,7 +2318,6 @@ body::before {
1897
2318
 
1898
2319
  .category-content {
1899
2320
  display: none;
1900
- padding: 0;
1901
2321
  }
1902
2322
 
1903
2323
  .category-content.expanded {
@@ -2168,41 +2588,46 @@ body::before {
2168
2588
  inset 0 1px 0 rgba(255, 255, 255, 0.9);
2169
2589
  }
2170
2590
 
2171
- /* Model select dropdown in chat */
2172
- .model-select {
2173
- padding: 0.5rem 0.75rem;
2174
- border: 1px solid #ddd;
2175
- border-radius: 6px;
2176
- background: white;
2177
- font-size: 0.9rem;
2178
- min-width: 180px;
2179
- margin-right: 0.75rem;
2180
- cursor: pointer;
2181
- transition: all var(--transition-fast);
2591
+
2592
+ /* Dropdown styling */
2593
+ .dropdown {
2594
+ position: relative;
2595
+ display: inline-block;
2182
2596
  }
2183
2597
 
2184
- .model-select:focus {
2185
- outline: none;
2186
- border-color: var(--accent-gold);
2187
- box-shadow: 0 0 0 2px rgba(255, 193, 7, 0.2);
2598
+ .dropbtn {
2599
+ background-color: transparent;
2600
+ border: none;
2601
+ font-size: 16px;
2602
+ cursor: pointer;
2188
2603
  }
2189
2604
 
2190
- .model-select:disabled {
2191
- background: #f5f5f5;
2192
- cursor: not-allowed;
2193
- opacity: 0.7;
2605
+ .dropdown-content {
2606
+ display: none;
2607
+ position: absolute;
2608
+ right: 0; /* align to the right edge of the parent */
2609
+ left: auto; /* prevent left alignment */
2610
+ top: calc(100% + 1px); /* opens 8px below the button */
2611
+ background-color: #ffe76c;
2612
+ border-radius: 8px;
2613
+ min-width: 140px;
2614
+ box-shadow: 0px 8px 16px rgba(0,0,0,0.2);
2615
+ z-index: 1000;
2616
+ overflow: hidden;
2194
2617
  }
2195
2618
 
2196
- .model-select option {
2197
- padding: 0.5rem;
2619
+ .dropdown-content a {
2620
+ color: black;
2621
+ font-size: 12px; /* smaller font */
2622
+ padding: 8px 12px;
2623
+ text-decoration: none;
2624
+ display: block;
2198
2625
  }
2199
2626
 
2200
- .chat-input-row {
2201
- display: flex;
2202
- align-items: center;
2203
- gap: 0.5rem;
2627
+ .dropdown-content a:hover {
2628
+ background-color: #f6c146;
2204
2629
  }
2205
2630
 
2206
- .input-with-indicator {
2207
- flex: 1;
2631
+ .dropdown:hover .dropdown-content {
2632
+ display: block;
2208
2633
  }