lemonade-sdk 9.1.1__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.
Files changed (84) hide show
  1. lemonade/__init__.py +5 -0
  2. lemonade/api.py +180 -0
  3. lemonade/cache.py +92 -0
  4. lemonade/cli.py +173 -0
  5. lemonade/common/__init__.py +0 -0
  6. lemonade/common/build.py +176 -0
  7. lemonade/common/cli_helpers.py +139 -0
  8. lemonade/common/exceptions.py +98 -0
  9. lemonade/common/filesystem.py +368 -0
  10. lemonade/common/inference_engines.py +408 -0
  11. lemonade/common/network.py +93 -0
  12. lemonade/common/printing.py +110 -0
  13. lemonade/common/status.py +471 -0
  14. lemonade/common/system_info.py +1411 -0
  15. lemonade/common/test_helpers.py +28 -0
  16. lemonade/profilers/__init__.py +1 -0
  17. lemonade/profilers/agt_power.py +437 -0
  18. lemonade/profilers/hwinfo_power.py +429 -0
  19. lemonade/profilers/memory_tracker.py +259 -0
  20. lemonade/profilers/profiler.py +58 -0
  21. lemonade/sequence.py +363 -0
  22. lemonade/state.py +159 -0
  23. lemonade/tools/__init__.py +1 -0
  24. lemonade/tools/accuracy.py +432 -0
  25. lemonade/tools/adapter.py +114 -0
  26. lemonade/tools/bench.py +302 -0
  27. lemonade/tools/flm/__init__.py +1 -0
  28. lemonade/tools/flm/utils.py +305 -0
  29. lemonade/tools/huggingface/bench.py +187 -0
  30. lemonade/tools/huggingface/load.py +235 -0
  31. lemonade/tools/huggingface/utils.py +359 -0
  32. lemonade/tools/humaneval.py +264 -0
  33. lemonade/tools/llamacpp/bench.py +255 -0
  34. lemonade/tools/llamacpp/load.py +222 -0
  35. lemonade/tools/llamacpp/utils.py +1260 -0
  36. lemonade/tools/management_tools.py +319 -0
  37. lemonade/tools/mmlu.py +319 -0
  38. lemonade/tools/oga/__init__.py +0 -0
  39. lemonade/tools/oga/bench.py +120 -0
  40. lemonade/tools/oga/load.py +804 -0
  41. lemonade/tools/oga/migration.py +403 -0
  42. lemonade/tools/oga/utils.py +462 -0
  43. lemonade/tools/perplexity.py +147 -0
  44. lemonade/tools/prompt.py +263 -0
  45. lemonade/tools/report/__init__.py +0 -0
  46. lemonade/tools/report/llm_report.py +203 -0
  47. lemonade/tools/report/table.py +899 -0
  48. lemonade/tools/server/__init__.py +0 -0
  49. lemonade/tools/server/flm.py +133 -0
  50. lemonade/tools/server/llamacpp.py +320 -0
  51. lemonade/tools/server/serve.py +2123 -0
  52. lemonade/tools/server/static/favicon.ico +0 -0
  53. lemonade/tools/server/static/index.html +279 -0
  54. lemonade/tools/server/static/js/chat.js +1059 -0
  55. lemonade/tools/server/static/js/model-settings.js +183 -0
  56. lemonade/tools/server/static/js/models.js +1395 -0
  57. lemonade/tools/server/static/js/shared.js +556 -0
  58. lemonade/tools/server/static/logs.html +191 -0
  59. lemonade/tools/server/static/styles.css +2654 -0
  60. lemonade/tools/server/static/webapp.html +321 -0
  61. lemonade/tools/server/tool_calls.py +153 -0
  62. lemonade/tools/server/tray.py +664 -0
  63. lemonade/tools/server/utils/macos_tray.py +226 -0
  64. lemonade/tools/server/utils/port.py +77 -0
  65. lemonade/tools/server/utils/thread.py +85 -0
  66. lemonade/tools/server/utils/windows_tray.py +408 -0
  67. lemonade/tools/server/webapp.py +34 -0
  68. lemonade/tools/server/wrapped_server.py +559 -0
  69. lemonade/tools/tool.py +374 -0
  70. lemonade/version.py +1 -0
  71. lemonade_install/__init__.py +1 -0
  72. lemonade_install/install.py +239 -0
  73. lemonade_sdk-9.1.1.dist-info/METADATA +276 -0
  74. lemonade_sdk-9.1.1.dist-info/RECORD +84 -0
  75. lemonade_sdk-9.1.1.dist-info/WHEEL +5 -0
  76. lemonade_sdk-9.1.1.dist-info/entry_points.txt +5 -0
  77. lemonade_sdk-9.1.1.dist-info/licenses/LICENSE +201 -0
  78. lemonade_sdk-9.1.1.dist-info/licenses/NOTICE.md +47 -0
  79. lemonade_sdk-9.1.1.dist-info/top_level.txt +3 -0
  80. lemonade_server/cli.py +805 -0
  81. lemonade_server/model_manager.py +758 -0
  82. lemonade_server/pydantic_models.py +159 -0
  83. lemonade_server/server_models.json +643 -0
  84. lemonade_server/settings.py +39 -0
@@ -0,0 +1,2654 @@
1
+ /* === CSS Variables === */
2
+ :root {
3
+ /* Colors */
4
+ --primary-yellow: #ffe066;
5
+ --primary-yellow-dark: #ffd43b;
6
+ --accent-gold: #e6b800;
7
+ --accent-gold-dark: #bfa100;
8
+
9
+ --text-primary: #222;
10
+ --text-secondary: #555;
11
+ --text-muted: #666;
12
+
13
+ --bg-primary: #fffbe9;
14
+ --bg-secondary: #fff8dd;
15
+ --bg-tertiary: #fff5d1;
16
+
17
+ /* Cohesive Button Color Palette */
18
+ --success-primary: #2d7f47;
19
+ --success-hover: #236338;
20
+ --success-light: #e8f5e8;
21
+
22
+ --danger-primary: #c8586c;
23
+ --danger-hover: #a94858;
24
+ --danger-light: #fdf2f4;
25
+
26
+ --info-primary: #5b8db8;
27
+ --info-hover: #4a7396;
28
+ --info-light: #f0f5fa;
29
+
30
+ --neutral-primary: #6b7280;
31
+ --neutral-hover: #4b5563;
32
+ --neutral-light: #f9fafb;
33
+
34
+ --purple-primary: #8b5a96;
35
+ --purple-hover: #744f7e;
36
+ --purple-light: #f5f1f6;
37
+
38
+ /* Status Colors */
39
+ --status-green: #28a745;
40
+ --status-red: #dc3545;
41
+ --status-yellow: #ffc107;
42
+ --status-gray: #6c757d;
43
+
44
+ /* Transitions */
45
+ --transition-fast: 0.2s ease;
46
+ --transition-medium: 0.3s ease;
47
+ }
48
+
49
+ body {
50
+ margin: 0;
51
+ font-family: 'Segoe UI', 'Arial', sans-serif;
52
+ background: linear-gradient(135deg, var(--bg-primary) 0%, var(--bg-secondary) 50%, var(--bg-tertiary) 100%);
53
+ color: var(--text-primary);
54
+ min-height: 100vh;
55
+ display: flex;
56
+ flex-direction: column;
57
+ }
58
+
59
+ body::before {
60
+ content: '';
61
+ position: fixed;
62
+ top: 0;
63
+ left: 0;
64
+ width: 100%;
65
+ height: 100%;
66
+ background:
67
+ radial-gradient(circle at 20% 20%, rgba(255, 224, 102, 0.1) 0%, transparent 50%),
68
+ radial-gradient(circle at 80% 80%, rgba(255, 212, 59, 0.1) 0%, transparent 50%);
69
+ pointer-events: none;
70
+ z-index: -1;
71
+ }
72
+
73
+ .navbar {
74
+ display: flex;
75
+ justify-content: space-between;
76
+ align-items: center;
77
+ padding: 1rem 3rem 0.5rem 1rem;
78
+ font-size: 1.25rem;
79
+ font-weight: 500;
80
+ background: transparent;
81
+ letter-spacing: 0.02em;
82
+ position: relative;
83
+ transition: var(--transition-medium);
84
+ }
85
+
86
+ .navbar-brand {
87
+ display: flex;
88
+ align-items: center;
89
+ }
90
+
91
+ .brand-title {
92
+ font-size: 1.5rem;
93
+ font-weight: 700;
94
+ color: var(--text-primary);
95
+ text-decoration: none;
96
+ letter-spacing: 0.01em;
97
+ }
98
+
99
+ .brand-title a {
100
+ color: inherit;
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;
111
+ }
112
+
113
+ .navbar-links {
114
+ display: flex;
115
+ gap: 2.5rem;
116
+ }
117
+
118
+ .navbar-links a {
119
+ color: #444;
120
+ text-decoration: none;
121
+ transition: var(--transition-fast);
122
+ }
123
+
124
+ .navbar-links a:hover {
125
+ color: var(--accent-gold);
126
+ }
127
+
128
+ .main {
129
+ flex: 1;
130
+ display: flex;
131
+ flex-direction: column;
132
+ align-items: center;
133
+ justify-content: flex-start;
134
+ min-height: 60vh;
135
+ margin-top: 2rem;
136
+ padding-top: 1rem;
137
+ }
138
+
139
+ .site-footer {
140
+ background: transparent;
141
+ padding-top: 0.5rem;
142
+ margin-top: auto;
143
+ }
144
+
145
+ .dad-joke {
146
+ color: #4ca64c;
147
+ font-size: 1.12rem;
148
+ text-align: center;
149
+ margin-bottom: 0.5rem;
150
+ opacity: 0.98;
151
+ letter-spacing: 0.01em;
152
+ padding: 0.2em 0;
153
+ width: fit-content;
154
+ margin-left: auto;
155
+ margin-right: auto;
156
+ background: none;
157
+ border-radius: 0;
158
+ display: block;
159
+ }
160
+
161
+ .copyright {
162
+ text-align: center;
163
+ font-size: 0.95rem;
164
+ color: #aaa;
165
+ margin-bottom: 0.5rem;
166
+ }
167
+
168
+ /* Tab and Chat UI Styling */
169
+ .tab-container {
170
+ background: #fff;
171
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
172
+ margin-bottom: 2em;
173
+ border-radius: 8px;
174
+ border: 1px solid #e0e0e0;
175
+ min-width: 320px;
176
+ width: 100%;
177
+ margin-left: 1rem;
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;
182
+ }
183
+
184
+ .tabs {
185
+ display: flex;
186
+ border-bottom: 1px solid #e0e0e0;
187
+ align-items: center;
188
+ padding: 0 1rem;
189
+ justify-content: space-between;
190
+ }
191
+
192
+ .tabs .tab-group {
193
+ display: flex;
194
+ align-items: center;
195
+ }
196
+
197
+ .tab {
198
+ padding: 1em 2em;
199
+ cursor: pointer;
200
+ border: none;
201
+ background: none;
202
+ font-size: 1.1em;
203
+ color: #666;
204
+ transition: color 0.2s;
205
+ }
206
+
207
+ .tab.active {
208
+ border-bottom: 2px solid #e6b800;
209
+ color: #e6b800;
210
+ font-weight: bold;
211
+ background: #fafafa;
212
+ }
213
+
214
+ /* Model Status Indicator integrated into tab bar */
215
+ .model-status-indicator {
216
+ display: flex;
217
+ align-items: center;
218
+ gap: 0.5rem;
219
+ padding: 0.6rem 1.2rem;
220
+ background: transparent;
221
+ border: none;
222
+ border-radius: 8px;
223
+ min-height: 38px;
224
+ font-size: 0.9rem;
225
+ position: relative;
226
+ }
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
+
235
+ .status-light {
236
+ width: 8px;
237
+ height: 8px;
238
+ border-radius: 50%;
239
+ position: absolute;
240
+ left: 8px;
241
+ top: 50%;
242
+ transform: translateY(-50%);
243
+ transition: all var(--transition-fast);
244
+ flex-shrink: 0;
245
+ z-index: 10;
246
+ }
247
+
248
+ .status-light::before {
249
+ content: '';
250
+ position: absolute;
251
+ top: -2px;
252
+ left: -2px;
253
+ width: 12px;
254
+ height: 12px;
255
+ border-radius: 50%;
256
+ opacity: 0.3;
257
+ animation: pulse-glow 2s infinite;
258
+ }
259
+
260
+ /* Online model unloaded status (yellow) */
261
+ .model-status-indicator.online .status-light {
262
+ background: var(--status-yellow);
263
+ box-shadow: 0 0 8px rgba(40, 167, 69, 0.6);
264
+ }
265
+
266
+ .model-status-indicator.online .status-light::before {
267
+ background: var(--status-yellow);
268
+ }
269
+
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);
278
+ }
279
+
280
+ /* Offline status (red) */
281
+ .model-status-indicator.offline .status-light {
282
+ background: var(--status-red);
283
+ box-shadow: 0 0 8px rgba(220, 53, 69, 0.6);
284
+ }
285
+
286
+ .model-status-indicator.offline .status-light::before {
287
+ background: var(--status-red);
288
+ }
289
+
290
+ /* Online model loaded status (with model name) */
291
+ .model-status-indicator.loaded .status-light {
292
+ background: var(--status-green);
293
+ box-shadow: 0 0 8px rgba(40, 167, 69, 0.6);
294
+ }
295
+
296
+ .model-status-indicator.loaded .status-light::before {
297
+ background: var(--status-green);
298
+ }
299
+
300
+ @keyframes pulse-glow {
301
+ 0%, 100% {
302
+ transform: scale(1);
303
+ opacity: 0.3;
304
+ }
305
+ 50% {
306
+ transform: scale(1.4);
307
+ opacity: 0.1;
308
+ }
309
+ }
310
+
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;
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;
386
+ }
387
+
388
+ .model-action-btn {
389
+ display: none; /* Hide by default */
390
+ align-items: center;
391
+ justify-content: center;
392
+ width: 1.1em;
393
+ height: 1.1em;
394
+ background: transparent;
395
+ border: none;
396
+ border-radius: 50%;
397
+ cursor: pointer;
398
+ transition: all var(--transition-fast);
399
+ font-size: 1.5rem;
400
+ color: #666;
401
+ margin-left: 0.1rem;
402
+ margin-right: 0.2rem;
403
+ line-height: 1;
404
+ }
405
+
406
+ .model-action-btn:hover {
407
+ background: rgba(0, 0, 0, 0.1);
408
+ color: #333;
409
+ }
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
+
420
+ .tab-content {
421
+ display: none;
422
+ padding: 2em;
423
+ background: #fafafa;
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;
428
+ }
429
+
430
+ .tab-content.active {
431
+ display: block;
432
+ }
433
+
434
+ /* Chat UI */
435
+ .chat-container {
436
+ display: flex;
437
+ flex-direction: column;
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%;
443
+ width: 100%;
444
+ margin: 0 auto;
445
+ border: 1px solid #e0e0e0;
446
+ border-radius: 8px;
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
+ }
464
+ }
465
+
466
+ .chat-history {
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;
470
+ overflow-y: auto;
471
+ -webkit-overflow-scrolling: touch;
472
+ padding: 1em;
473
+ border-bottom: 1px solid #e0e0e0;
474
+ display: flex;
475
+ flex-direction: column;
476
+ gap: 0.5em;
477
+ /* Optional visual hint for scrollable content */
478
+ scrollbar-width: thin;
479
+ }
480
+
481
+ .chat-message {
482
+ display: flex;
483
+ flex-direction: column;
484
+ align-items: flex-end;
485
+ margin-bottom: 0.5em;
486
+ }
487
+
488
+ .chat-message.user {
489
+ align-items: flex-end;
490
+ }
491
+
492
+ .chat-message.llm {
493
+ align-items: flex-start;
494
+ }
495
+
496
+ .chat-bubble {
497
+ max-width: 70%;
498
+ padding: 0.7em 1.1em;
499
+ border-radius: 20px;
500
+ margin-bottom: 2px;
501
+ font-size: 1em;
502
+ word-break: break-word;
503
+ box-shadow: 0 1px 2px rgba(0,0,0,0.10);
504
+ line-height: 1.5;
505
+ }
506
+
507
+ .chat-bubble.user {
508
+ background: linear-gradient(135deg, #ffe066 60%, #ffd43b 100%);
509
+ color: #222;
510
+ border-bottom-right-radius: 4px;
511
+ align-self: flex-end;
512
+ }
513
+
514
+ .chat-bubble.llm {
515
+ background: #f0f0f0;
516
+ color: #222;
517
+ border-bottom-left-radius: 4px;
518
+ align-self: flex-start;
519
+ }
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
+
536
+ /* Markdown styling within chat bubbles */
537
+ .chat-bubble h1,
538
+ .chat-bubble h2,
539
+ .chat-bubble h3,
540
+ .chat-bubble h4,
541
+ .chat-bubble h5,
542
+ .chat-bubble h6 {
543
+ margin: 0.5em 0 0.3em 0;
544
+ color: inherit;
545
+ font-weight: bold;
546
+ }
547
+
548
+ .chat-bubble h1 { font-size: 1.4em; }
549
+ .chat-bubble h2 { font-size: 1.3em; }
550
+ .chat-bubble h3 { font-size: 1.2em; }
551
+ .chat-bubble h4 { font-size: 1.1em; }
552
+ .chat-bubble h5 { font-size: 1.05em; }
553
+ .chat-bubble h6 { font-size: 1em; }
554
+
555
+ .chat-bubble p {
556
+ margin: 0.5em 0;
557
+ line-height: 1.4;
558
+ }
559
+
560
+ .chat-bubble p:first-child {
561
+ margin-top: 0;
562
+ }
563
+
564
+ .chat-bubble p:last-child {
565
+ margin-bottom: 0;
566
+ }
567
+
568
+ .chat-bubble ul,
569
+ .chat-bubble ol {
570
+ margin: 0.5em 0;
571
+ padding-left: 1.5em;
572
+ }
573
+
574
+ .chat-bubble li {
575
+ margin: 0.2em 0;
576
+ }
577
+
578
+ .chat-bubble blockquote {
579
+ margin: 0.5em 0;
580
+ padding: 0.5em 1em;
581
+ border-left: 3px solid #ccc;
582
+ background: rgba(0,0,0,0.05);
583
+ border-radius: 4px;
584
+ font-style: italic;
585
+ }
586
+
587
+ .chat-bubble code {
588
+ background: rgba(0,0,0,0.1);
589
+ padding: 0.2em 0.4em;
590
+ border-radius: 3px;
591
+ font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
592
+ font-size: 0.9em;
593
+ }
594
+
595
+ .chat-bubble pre {
596
+ background: rgba(0,0,0,0.05);
597
+ border: 1px solid rgba(0,0,0,0.1);
598
+ border-radius: 6px;
599
+ padding: 1em;
600
+ margin: 0.5em 0;
601
+ overflow-x: auto;
602
+ font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
603
+ font-size: 0.9em;
604
+ line-height: 1.4;
605
+ }
606
+
607
+ .chat-bubble pre code {
608
+ background: none !important;
609
+ padding: 0 !important;
610
+ border-radius: 0 !important;
611
+ border: none !important;
612
+ font-size: inherit;
613
+ }
614
+
615
+ .chat-bubble table {
616
+ border-collapse: collapse;
617
+ margin: 0.5em 0;
618
+ font-size: 0.9em;
619
+ }
620
+
621
+ .chat-bubble th,
622
+ .chat-bubble td {
623
+ border: 1px solid #ddd;
624
+ padding: 0.3em 0.6em;
625
+ text-align: left;
626
+ }
627
+
628
+ .chat-bubble th {
629
+ background: rgba(0,0,0,0.05);
630
+ font-weight: bold;
631
+ }
632
+
633
+ .chat-bubble a {
634
+ color: var(--info-primary);
635
+ text-decoration: none;
636
+ }
637
+
638
+ .chat-bubble a:hover {
639
+ text-decoration: underline;
640
+ }
641
+
642
+ .chat-bubble hr {
643
+ border: none;
644
+ border-top: 1px solid #ddd;
645
+ margin: 1em 0;
646
+ }
647
+
648
+ .chat-bubble strong {
649
+ font-weight: bold;
650
+ }
651
+
652
+ .chat-bubble em {
653
+ font-style: italic;
654
+ }
655
+
656
+ .chat-bubble del {
657
+ text-decoration: line-through;
658
+ }
659
+
660
+ /* Special styling for code blocks in LLM bubbles */
661
+ .chat-bubble.llm pre {
662
+ background: #f8f8f8;
663
+ border: 1px solid #e0e0e0;
664
+ }
665
+
666
+ /* Apply special styling to inline code elements */
667
+ .chat-bubble.llm code {
668
+ background: #f8f8f8;
669
+ border: 1px solid #e0e0e0;
670
+ }
671
+
672
+ /* Remove styling from code elements inside pre blocks */
673
+ .chat-bubble.llm pre code {
674
+ background: none !important;
675
+ border: none !important;
676
+ padding: 0 !important;
677
+ border-radius: 0 !important;
678
+ }
679
+
680
+ .chat-bubble.llm blockquote {
681
+ background: rgba(0,0,0,0.03);
682
+ border-left-color: #999;
683
+ }
684
+
685
+ .chat-input-row {
686
+ display: flex;
687
+ gap: 0.5em;
688
+ padding: 1em;
689
+ background: #f9f9f9;
690
+ border-radius: 0 0 8px 8px;
691
+ }
692
+
693
+ .chat-input-row select {
694
+ min-width: 120px;
695
+ background: #fff;
696
+ color: #222;
697
+ border: 1px solid #ddd;
698
+ border-radius: 4px;
699
+ padding: 0.5em;
700
+ }
701
+
702
+ .chat-input-row input[type='text'] {
703
+ flex: 1;
704
+ padding: 0.5em;
705
+ border: 1px solid #ddd;
706
+ border-radius: 4px;
707
+ background: #fff;
708
+ color: #222;
709
+ }
710
+
711
+ .input-with-indicator {
712
+ flex: 1;
713
+ position: relative;
714
+ display: flex;
715
+ align-items: center;
716
+ }
717
+
718
+ #chat-input {
719
+ flex: 1;
720
+ padding: 0.5em;
721
+ border: 1px solid #ddd;
722
+ border-radius: 4px;
723
+ background: #fff;
724
+ color: #222;
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;
736
+ }
737
+
738
+ #attachment-indicator {
739
+ position: absolute;
740
+ right: 8px;
741
+ top: 50%;
742
+ transform: translateY(-50%);
743
+ font-size: 14px;
744
+ color: #666;
745
+ pointer-events: none;
746
+ background: rgba(255, 255, 255, 0.9);
747
+ padding: 2px 4px;
748
+ border-radius: 3px;
749
+ border: 1px solid #ddd;
750
+ }
751
+
752
+ .chat-input-row button {
753
+ padding: 0.5em 1.2em;
754
+ background: #e6b800;
755
+ color: #222;
756
+ border: none;
757
+ border-radius: 4px;
758
+ cursor: pointer;
759
+ transition: background 0.2s;
760
+ font-weight: 600;
761
+ }
762
+
763
+ #attachment-btn {
764
+ padding: 0.5em 0.8em;
765
+ background: #f0f0f0;
766
+ color: #222;
767
+ border: 1px solid #ddd;
768
+ }
769
+
770
+ #attachment-btn:hover {
771
+ background: #e0e0e0;
772
+ }
773
+
774
+ #clear-attachments-btn {
775
+ padding: 0.5em 0.6em;
776
+ background: #ff6b6b;
777
+ color: white;
778
+ border: 1px solid #ff5252;
779
+ margin-left: 0.2em;
780
+ }
781
+
782
+ #clear-attachments-btn:hover {
783
+ background: #ff5252;
784
+ }
785
+
786
+ .chat-input-row button:hover {
787
+ background: #d4a500;
788
+ }
789
+
790
+ .chat-input-row button:disabled {
791
+ background: #ccc;
792
+ color: #666;
793
+ cursor: not-allowed;
794
+ }
795
+
796
+ /* Image attachment preview styles */
797
+ .attachments-preview-container {
798
+ padding: 0.5em 1em 0 1em;
799
+ background: #f9f9f9;
800
+ border-top: 1px solid #e0e0e0;
801
+ display: none;
802
+ }
803
+
804
+ .attachments-preview-container.has-attachments {
805
+ display: block;
806
+ }
807
+
808
+ .attachments-preview-row {
809
+ display: flex;
810
+ gap: 8px;
811
+ align-items: center;
812
+ flex-wrap: wrap;
813
+ }
814
+
815
+ .attachment-preview {
816
+ display: flex;
817
+ align-items: center;
818
+ gap: 6px;
819
+ padding: 4px 8px;
820
+ background: #fff;
821
+ border: 1px solid #ddd;
822
+ border-radius: 4px;
823
+ box-shadow: 0 1px 2px rgba(0,0,0,0.05);
824
+ transition: all 0.2s ease;
825
+ font-size: 0.85em;
826
+ position: relative;
827
+ }
828
+
829
+ .attachment-preview:hover {
830
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
831
+ background: #fafafa;
832
+ }
833
+
834
+ .attachment-thumbnail {
835
+ width: 20px;
836
+ height: 20px;
837
+ border-radius: 2px;
838
+ object-fit: cover;
839
+ background: #f8f8f8;
840
+ border: 1px solid #e0e0e0;
841
+ flex-shrink: 0;
842
+ }
843
+
844
+ .attachment-filename {
845
+ color: #666;
846
+ max-width: 120px;
847
+ overflow: hidden;
848
+ text-overflow: ellipsis;
849
+ white-space: nowrap;
850
+ font-size: 0.9em;
851
+ line-height: 1;
852
+ }
853
+
854
+ .attachment-remove-btn {
855
+ background: none;
856
+ border: none;
857
+ color: #999;
858
+ cursor: pointer;
859
+ font-size: 14px;
860
+ padding: 0 2px;
861
+ margin-left: 4px;
862
+ transition: color 0.2s ease;
863
+ flex-shrink: 0;
864
+ }
865
+
866
+ .attachment-remove-btn:hover {
867
+ color: #ff6b6b;
868
+ }
869
+
870
+ .attachment-remove-btn:active {
871
+ transform: scale(0.9);
872
+ }
873
+
874
+ /* Fallback for non-image files or broken images */
875
+ .attachment-preview.no-preview .attachment-thumbnail {
876
+ display: flex;
877
+ align-items: center;
878
+ justify-content: center;
879
+ background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
880
+ border: 1px dashed #dee2e6;
881
+ color: #6c757d;
882
+ font-size: 12px;
883
+ }
884
+
885
+ /* Mobile responsive adjustments */
886
+ @media (max-width: 600px) {
887
+ .attachments-preview-row {
888
+ gap: 6px;
889
+ }
890
+
891
+ .attachment-preview {
892
+ padding: 3px 6px;
893
+ gap: 4px;
894
+ }
895
+
896
+ .attachment-thumbnail {
897
+ width: 18px;
898
+ height: 18px;
899
+ }
900
+
901
+ .attachment-filename {
902
+ max-width: 100px;
903
+ font-size: 0.8em;
904
+ }
905
+
906
+ .attachment-remove-btn {
907
+ font-size: 12px;
908
+ }
909
+ }
910
+
911
+ /* Model Management */
912
+ .model-mgmt-container {
913
+ display: flex;
914
+ gap: 2em;
915
+ align-items: flex-start;
916
+ }
917
+
918
+ .model-mgmt-pane {
919
+ flex: 1 1 0;
920
+ min-width: 0;
921
+ }
922
+
923
+ .model-mgmt-pane h3 {
924
+ margin-top: 0;
925
+ color: #222;
926
+ }
927
+
928
+ .model-table {
929
+ width: 100%;
930
+ border-collapse: collapse;
931
+ background: #fff;
932
+ border: 1px solid #ddd;
933
+ border-radius: 4px;
934
+ }
935
+
936
+ .model-table td {
937
+ padding: 0.5em 0.75em;
938
+ vertical-align: middle;
939
+ border-bottom: 1px solid #eee;
940
+ color: #222;
941
+ }
942
+
943
+ .model-table tr:last-child td {
944
+ border-bottom: none;
945
+ }
946
+
947
+ .model-table button {
948
+ background: var(--accent-gold);
949
+ color: #222;
950
+ border: none;
951
+ border-radius: 6px;
952
+ padding: 0.4em 1em;
953
+ cursor: pointer;
954
+ font-weight: 500;
955
+ transition: all var(--transition-fast);
956
+ box-shadow: 0 2px 4px rgba(230, 184, 0, 0.15);
957
+ }
958
+
959
+ .model-table button:hover {
960
+ background: var(--accent-gold-dark);
961
+ transform: translateY(-1px);
962
+ box-shadow: 0 3px 6px rgba(230, 184, 0, 0.25);
963
+ }
964
+
965
+ .installing-btn {
966
+ font-size: 0.75em !important;
967
+ padding: 0.5em 0.8em !important;
968
+ width: auto !important;
969
+ min-width: 90px !important;
970
+ height: 40px !important;
971
+ background: linear-gradient(145deg, rgba(107, 114, 128, 0.15), rgba(107, 114, 128, 0.08)) !important;
972
+ color: var(--neutral-primary) !important;
973
+ border: 1px solid rgba(107, 114, 128, 0.3) !important;
974
+ cursor: wait !important;
975
+ backdrop-filter: blur(8px) !important;
976
+ box-shadow:
977
+ 0 2px 8px rgba(107, 114, 128, 0.1),
978
+ inset 0 1px 0 rgba(255, 255, 255, 0.6) !important;
979
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif !important;
980
+ font-weight: 500;
981
+ opacity: 1 !important;
982
+ white-space: nowrap !important;
983
+ animation: gentle-pulse 2s ease-in-out infinite !important;
984
+ }
985
+
986
+ /* Model label styles */
987
+ .model-label {
988
+ display: inline-block;
989
+ background-color: var(--info-primary);
990
+ color: white;
991
+ padding: 2px 8px;
992
+ margin-left: 8px;
993
+ border-radius: 4px;
994
+ font-size: 10px;
995
+ font-weight: 500;
996
+ text-transform: uppercase;
997
+ }
998
+
999
+ .model-label.reasoning {
1000
+ background-color: var(--neutral-primary);
1001
+ }
1002
+
1003
+ .model-label.vision {
1004
+ background-color: var(--info-primary);
1005
+ }
1006
+
1007
+ .model-label.tool-calling {
1008
+ background-color: #FFB74D;
1009
+ }
1010
+
1011
+ .model-label.other {
1012
+ background-color: var(--success-primary);
1013
+ }
1014
+
1015
+ .model-label.embeddings {
1016
+ background-color: var(--purple-primary);
1017
+ }
1018
+
1019
+ .model-label.reranking {
1020
+ background-color: #ca4747;
1021
+ }
1022
+
1023
+ .model-label.coding {
1024
+ background-color: #ff6b35;
1025
+ }
1026
+
1027
+ .model-labels-container {
1028
+ display: flex;
1029
+ align-items: center;
1030
+ gap: 4px;
1031
+ }
1032
+
1033
+ /* Model Registration Form */
1034
+ .model-mgmt-register-form {
1035
+ background: linear-gradient(135deg, #ffffff 0%, #fefdfb 100%);
1036
+ border: 1px solid #e8e8e8;
1037
+ border-radius: 12px;
1038
+ margin-bottom: 1.5em;
1039
+ padding: 1.5em 2em 1.2em 2em;
1040
+ max-width: 750px;
1041
+ margin-left: auto;
1042
+ margin-right: auto;
1043
+ box-shadow: 0 4px 20px rgba(0,0,0,0.08), 0 1px 4px rgba(0,0,0,0.04);
1044
+ position: relative;
1045
+ overflow: visible;
1046
+ transition: padding 0.3s ease;
1047
+ }
1048
+
1049
+ /* Collapsed state */
1050
+ .model-mgmt-register-form.collapsed {
1051
+ padding: 1.5em 2em 1.2em 2em;
1052
+ border: 1px solid #e0e0e0;
1053
+ box-shadow: 0 2px 8px rgba(0,0,0,0.04);
1054
+ }
1055
+
1056
+ .model-mgmt-register-form.collapsed .form-content {
1057
+ display: none;
1058
+ }
1059
+
1060
+ .model-mgmt-register-form::before {
1061
+ content: '';
1062
+ position: absolute;
1063
+ top: 0;
1064
+ left: 0;
1065
+ right: 0;
1066
+ height: 4px;
1067
+ background: linear-gradient(90deg, #e6b800, #f4c842, #e6b800);
1068
+ border-radius: 12px 12px 0 0;
1069
+ }
1070
+
1071
+ .model-mgmt-form-title {
1072
+ margin-top: 0;
1073
+ color: #1a1a1a;
1074
+ font-size: 1rem;
1075
+ font-weight: 700;
1076
+ margin-bottom: 1.2em;
1077
+ letter-spacing: 0.01em;
1078
+ display: flex;
1079
+ align-items: center;
1080
+ gap: 0.75em;
1081
+ cursor: pointer;
1082
+ user-select: none;
1083
+ transition: all 0.2s ease;
1084
+ }
1085
+
1086
+ .model-mgmt-form-title:hover {
1087
+ color: #333;
1088
+ }
1089
+
1090
+ /* Collapsed state title margin adjustment */
1091
+ .model-mgmt-register-form.collapsed .model-mgmt-form-title {
1092
+ margin-bottom: 0;
1093
+ }
1094
+
1095
+ .model-mgmt-form-title::before {
1096
+ content: '▶';
1097
+ font-size: 0.8em;
1098
+ color: #666;
1099
+ transition: transform 0.3s ease;
1100
+ transform-origin: center;
1101
+ }
1102
+
1103
+ /* Rotate chevron when expanded */
1104
+ .model-mgmt-register-form:not(.collapsed) .model-mgmt-form-title::before {
1105
+ transform: rotate(90deg);
1106
+ }
1107
+
1108
+ /* Hide the separate toggle indicator since we're using the gear */
1109
+ .form-toggle-indicator {
1110
+ display: none;
1111
+ }
1112
+
1113
+ /* Specific styling for title tooltip */
1114
+ .model-mgmt-form-title .tooltip-icon {
1115
+ margin-left: 12px;
1116
+ font-size: 16px;
1117
+ }
1118
+
1119
+ .model-mgmt-form-title .tooltip-icon::after {
1120
+ width: 250px;
1121
+ font-size: 13px;
1122
+ left: auto;
1123
+ right: 0;
1124
+ transform: translateX(0);
1125
+ }
1126
+
1127
+ .model-mgmt-form-title .tooltip-icon:hover::after {
1128
+ transform: translateX(0) translateY(-2px);
1129
+ }
1130
+
1131
+ .register-form-row {
1132
+ display: flex;
1133
+ align-items: center;
1134
+ gap: 1.2em;
1135
+ margin-bottom: 1em;
1136
+ padding: 0.1em 0;
1137
+ position: relative;
1138
+ }
1139
+
1140
+ .register-form-row-tight {
1141
+ margin-bottom: 0.8em;
1142
+ align-items: center;
1143
+ flex-wrap: wrap;
1144
+ }
1145
+
1146
+ .register-form-row:last-child {
1147
+ margin-bottom: 0;
1148
+ margin-top: 1.4em;
1149
+ padding-top: 1em;
1150
+ border-top: 1px solid #f0f0f0;
1151
+ }
1152
+
1153
+ .register-label {
1154
+ min-width: 120px;
1155
+ color: #333;
1156
+ font-weight: 600;
1157
+ font-size: 0.95em;
1158
+ text-transform: uppercase;
1159
+ letter-spacing: 0.02em;
1160
+ font-size: 0.85em;
1161
+ }
1162
+
1163
+ .register-label.reasoning-inline {
1164
+ margin-left: 1.5em;
1165
+ font-weight: 500;
1166
+ font-size: 0.9em;
1167
+ color: #555;
1168
+ display: flex;
1169
+ align-items: center;
1170
+ gap: 0.5em;
1171
+ white-space: nowrap;
1172
+ text-transform: none;
1173
+ letter-spacing: normal;
1174
+ }
1175
+
1176
+ .register-label.reasoning-inline input[type="checkbox"] {
1177
+ transform: scale(1.1);
1178
+ accent-color: #e6b800;
1179
+ }
1180
+
1181
+ /* Checkboxes row styling */
1182
+ .register-checkboxes-row {
1183
+ display: flex;
1184
+ flex-wrap: nowrap;
1185
+ gap: 1em;
1186
+ padding: 0.5em 0;
1187
+ margin-left: 0;
1188
+ }
1189
+
1190
+ .register-checkboxes-row .register-label.reasoning-inline {
1191
+ margin-left: 0;
1192
+ padding: 0.4em 0.8em;
1193
+ background: #f8f8f8;
1194
+ border-radius: 6px;
1195
+ transition: background-color 0.2s ease;
1196
+ }
1197
+
1198
+ .register-checkboxes-row .register-label.reasoning-inline:hover {
1199
+ background: #f0f0f0;
1200
+ }
1201
+
1202
+ .register-model-name-group {
1203
+ display: flex;
1204
+ align-items: stretch;
1205
+ flex: 1;
1206
+ border-radius: 6px;
1207
+ overflow: hidden;
1208
+ transition: box-shadow 0.2s ease;
1209
+ min-width: 0;
1210
+ }
1211
+
1212
+ .register-model-name-group:focus-within {
1213
+ box-shadow: 0 2px 12px rgba(230,184,0,0.25);
1214
+ }
1215
+
1216
+ .register-model-prefix {
1217
+ background: #e6b800;
1218
+ color: #fff;
1219
+ font-weight: 600;
1220
+ border-radius: 4px 0 0 4px;
1221
+ padding: 0.45em 0.7em;
1222
+ font-size: 1em;
1223
+ border: 1px solid #ddd;
1224
+ border-right: none;
1225
+ }
1226
+
1227
+ .styled-prefix {
1228
+ background: linear-gradient(135deg, #f8f8f8 0%, #f0f0f0 100%);
1229
+ color: #666;
1230
+ border: 1px solid #d5d5d5;
1231
+ border-right: none;
1232
+ border-radius: 6px 0 0 6px;
1233
+ padding: 0.6em 0.8em;
1234
+ font-size: 0.95em;
1235
+ font-family: inherit;
1236
+ font-weight: 500;
1237
+ height: 100%;
1238
+ display: flex;
1239
+ align-items: center;
1240
+ transition: all 0.2s ease;
1241
+ min-width: 50px;
1242
+ justify-content: center;
1243
+ box-shadow: 0 2px 8px rgba(0,0,0,0.06);
1244
+ }
1245
+
1246
+ #register-model-name {
1247
+ border-radius: 0 6px 6px 0;
1248
+ border: 1px solid #d5d5d5;
1249
+ border-left: none;
1250
+ padding: 0.6em 0.8em;
1251
+ font-size: 0.95em;
1252
+ background: #fff;
1253
+ color: #222;
1254
+ flex: 1;
1255
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
1256
+ outline: none;
1257
+ box-shadow: 0 2px 8px rgba(0,0,0,0.06);
1258
+ box-sizing: border-box;
1259
+ min-width: 0;
1260
+ }
1261
+
1262
+ #register-model-name:focus {
1263
+ border-color: #e6b800;
1264
+ box-shadow: 0 2px 12px rgba(230,184,0,0.25);
1265
+ }
1266
+
1267
+ .form-input-wrapper {
1268
+ position: relative;
1269
+ width: 100%;
1270
+ }
1271
+
1272
+ #register-recipe {
1273
+ min-width: 160px;
1274
+ flex: 1;
1275
+ border-radius: 6px;
1276
+ border: 1px solid #d5d5d5;
1277
+ padding: 0.6em 0.8em;
1278
+ font-size: 0.95em;
1279
+ background: #fff;
1280
+ color: #222;
1281
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
1282
+ outline: none;
1283
+ box-shadow: 0 2px 8px rgba(0,0,0,0.06);
1284
+ box-sizing: border-box;
1285
+ }
1286
+
1287
+ #register-recipe:focus {
1288
+ border-color: #e6b800;
1289
+ box-shadow: 0 2px 12px rgba(230,184,0,0.25);
1290
+ }
1291
+
1292
+ .register-doc-link {
1293
+ margin-left: 0.8em;
1294
+ color: var(--info-primary);
1295
+ font-size: 0.9em;
1296
+ text-decoration: none;
1297
+ transition: all 0.2s ease;
1298
+ padding: 0.3em 0.6em;
1299
+ border-radius: 4px;
1300
+ background: rgba(0,122,204,0.1);
1301
+ border: 1px solid rgba(0,122,204,0.2);
1302
+ white-space: nowrap;
1303
+ }
1304
+
1305
+ .register-doc-link:hover {
1306
+ color: #fff;
1307
+ background: var(--info-primary);
1308
+ border-color: var(--info-primary);
1309
+ transform: translateY(-1px);
1310
+ box-shadow: 0 2px 8px rgba(91, 141, 184, 0.3);
1311
+ }
1312
+ .checkpoint-input-group {
1313
+ display: flex;
1314
+ align-items: stretch;
1315
+ flex: 1;
1316
+ border-radius: 6px;
1317
+ overflow: hidden;
1318
+ transition: box-shadow 0.2s ease;
1319
+ min-width: 0;
1320
+ }
1321
+
1322
+ .checkpoint-input-group:focus-within {
1323
+ box-shadow: 0 2px 12px rgba(230,184,0,0.25);
1324
+ }
1325
+
1326
+ .checkpoint-input-group #register-checkpoint {
1327
+ border-radius: 6px 0 0 6px;
1328
+ border: 1px solid #d5d5d5;
1329
+ border-right: none;
1330
+ padding: 0.6em 0.8em;
1331
+ font-size: 0.95em;
1332
+ background: #fff;
1333
+ color: #222;
1334
+ flex: 1;
1335
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
1336
+ outline: none;
1337
+ box-shadow: 0 2px 8px rgba(0,0,0,0.06);
1338
+ box-sizing: border-box;
1339
+ min-width: 0;
1340
+ }
1341
+
1342
+ .checkpoint-input-group #register-checkpoint:focus {
1343
+ border-color: #e6b800;
1344
+ box-shadow: 0 2px 12px rgba(230,184,0,0.25);
1345
+ }
1346
+
1347
+ .folder-select-btn {
1348
+ background: #f8f9fa;
1349
+ border: 1px solid #d5d5d5;
1350
+ border-left: none;
1351
+ border-radius: 0 6px 6px 0;
1352
+ padding: 0.6em 0.8em;
1353
+ cursor: pointer;
1354
+ transition: all 0.2s ease;
1355
+ font-size: 1rem;
1356
+ color: #666;
1357
+ min-width: 48px;
1358
+ display: flex;
1359
+ align-items: center;
1360
+ justify-content: center;
1361
+ }
1362
+
1363
+ .folder-select-btn:hover {
1364
+ background: #e9ecef;
1365
+ color: #333;
1366
+ }
1367
+ /* */
1368
+
1369
+ #register-mmproj, #register-checkpoint {
1370
+ border-radius: 6px;
1371
+ border: 1px solid #d5d5d5;
1372
+ padding: 0.6em 0.8em;
1373
+ font-size: 0.95em;
1374
+ background: #fff;
1375
+ color: #222;
1376
+ flex: 1;
1377
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
1378
+ outline: none;
1379
+ box-shadow: 0 2px 8px rgba(0,0,0,0.06);
1380
+ box-sizing: border-box;
1381
+ min-width: 0;
1382
+ }
1383
+
1384
+ #register-mmproj:focus, #register-checkpoint:focus {
1385
+ border-color: #e6b800;
1386
+ box-shadow: 0 2px 12px rgba(230,184,0,0.25);
1387
+ }
1388
+
1389
+ #register-submit {
1390
+ background: linear-gradient(135deg, #e6b800 0%, #f4c842 100%);
1391
+ color: #222;
1392
+ border: none;
1393
+ border-radius: 8px;
1394
+ padding: 0.8em 2em;
1395
+ font-weight: 700;
1396
+ font-size: 0.95em;
1397
+ cursor: pointer;
1398
+ transition: all 0.2s ease;
1399
+ margin-top: 0;
1400
+ box-shadow: 0 3px 12px rgba(230,184,0,0.3);
1401
+ text-transform: uppercase;
1402
+ letter-spacing: 0.02em;
1403
+ position: relative;
1404
+ overflow: hidden;
1405
+ }
1406
+
1407
+ #register-submit::before {
1408
+ content: '';
1409
+ position: absolute;
1410
+ top: 0;
1411
+ left: -100%;
1412
+ width: 100%;
1413
+ height: 100%;
1414
+ background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
1415
+ transition: left 0.5s ease;
1416
+ }
1417
+
1418
+ #register-submit:hover:enabled {
1419
+ background: linear-gradient(135deg, #d4a500 0%, #e6b800 100%);
1420
+ transform: translateY(-2px);
1421
+ box-shadow: 0 5px 20px rgba(230,184,0,0.4);
1422
+ }
1423
+
1424
+ #register-submit:hover:enabled::before {
1425
+ left: 100%;
1426
+ }
1427
+
1428
+ #register-submit:disabled {
1429
+ background: linear-gradient(135deg, #ccc 0%, #bbb 100%);
1430
+ color: #666;
1431
+ cursor: not-allowed;
1432
+ transform: none;
1433
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
1434
+ }
1435
+
1436
+ .register-status {
1437
+ margin-left: 1.2em;
1438
+ font-size: 0.95em;
1439
+ font-weight: 600;
1440
+ padding: 0.4em 0.8em;
1441
+ border-radius: 6px;
1442
+ transition: all 0.2s ease;
1443
+ }
1444
+
1445
+ /* Make placeholder text in Add a Model form lighter and more elegant */
1446
+ #register-model-form input::placeholder {
1447
+ color: #aaa;
1448
+ opacity: 1;
1449
+ font-style: italic;
1450
+ }
1451
+
1452
+ #register-model-form input:focus::placeholder {
1453
+ color: #ccc;
1454
+ transform: translateX(4px);
1455
+ transition: all 0.2s ease;
1456
+ }
1457
+
1458
+ /* Success status styling */
1459
+ .register-status.success {
1460
+ background: rgba(39, 174, 96, 0.1);
1461
+ color: #27ae60;
1462
+ border: 1px solid rgba(39, 174, 96, 0.3);
1463
+ }
1464
+
1465
+ /* Error status styling */
1466
+ .register-status.error {
1467
+ background: rgba(200, 88, 108, 0.1);
1468
+ color: var(--danger-primary);
1469
+ border: 1px solid rgba(200, 88, 108, 0.3);
1470
+ }
1471
+
1472
+ /* Tooltip styles */
1473
+ .tooltip-icon {
1474
+ display: inline;
1475
+ color: var(--info-primary);
1476
+ font-size: 14px;
1477
+ margin-left: 8px;
1478
+ cursor: help;
1479
+ position: relative;
1480
+ text-transform: none;
1481
+ }
1482
+
1483
+ .tooltip-icon:hover {
1484
+ color: #0086ef;
1485
+ }
1486
+
1487
+ .tooltip-icon::after {
1488
+ content: attr(data-tooltip);
1489
+ position: absolute;
1490
+ bottom: calc(100% + 8px);
1491
+ left: 50%;
1492
+ transform: translateX(-50%);
1493
+ background-color: rgba(28, 28, 30, 0.98);
1494
+ color: white;
1495
+ padding: 10px 14px;
1496
+ border-radius: 8px;
1497
+ font-size: 12px;
1498
+ font-weight: 400;
1499
+ width: 200px;
1500
+ text-align: left;
1501
+ opacity: 0;
1502
+ visibility: hidden;
1503
+ transition: all 0.2s ease;
1504
+ z-index: 1000;
1505
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
1506
+ border: 1px solid rgba(255, 255, 255, 0.15);
1507
+ backdrop-filter: blur(8px);
1508
+ line-height: 1.4;
1509
+ pointer-events: none;
1510
+ text-transform: none;
1511
+ }
1512
+
1513
+ .tooltip-icon:hover::after {
1514
+ opacity: 1;
1515
+ visibility: visible;
1516
+ transform: translateX(-50%) translateY(-2px);
1517
+ }
1518
+
1519
+ /* Smart positioning for tooltips near edges */
1520
+ .tooltip-icon:nth-last-child(-n+2)::after,
1521
+ .register-form-row:last-child .tooltip-icon::after {
1522
+ left: auto;
1523
+ right: 0;
1524
+ transform: translateX(0);
1525
+ width: 180px;
1526
+ }
1527
+
1528
+ .tooltip-icon:nth-last-child(-n+2):hover::after,
1529
+ .register-form-row:last-child .tooltip-icon:hover::after {
1530
+ transform: translateX(0) translateY(-2px);
1531
+ }
1532
+
1533
+ /* Adjust label layout to accommodate tooltip icon */
1534
+ .register-label {
1535
+ display: flex;
1536
+ align-items: center;
1537
+ gap: 0.5em;
1538
+ }
1539
+
1540
+ /* Adjust reasoning inline label for tooltip */
1541
+ .register-label.reasoning-inline {
1542
+ align-items: center;
1543
+ }
1544
+
1545
+ .register-label.reasoning-inline .tooltip-icon {
1546
+ margin-left: 8px;
1547
+ }
1548
+
1549
+ /* Special positioning for reasoning tooltip to prevent overflow */
1550
+ .register-label.reasoning-inline .tooltip-icon::after {
1551
+ left: auto !important;
1552
+ right: 0 !important;
1553
+ transform: translateX(0) !important;
1554
+ width: 160px !important;
1555
+ max-width: 260px !important;
1556
+ white-space: pre-line !important;
1557
+ word-break: break-word !important;
1558
+ }
1559
+
1560
+ .register-label.reasoning-inline .tooltip-icon:hover::after {
1561
+ transform: translateX(0) translateY(-2px) !important;
1562
+ }
1563
+
1564
+ @media (max-width: 800px) {
1565
+ .model-mgmt-container {
1566
+ flex-direction: column;
1567
+ gap: 1.5em;
1568
+ }
1569
+
1570
+ .model-mgmt-pane {
1571
+ flex: none;
1572
+ width: 100%;
1573
+ }
1574
+ }
1575
+
1576
+ @media (max-width: 600px) {
1577
+ .title {
1578
+ font-size: 2rem;
1579
+ }
1580
+ .navbar {
1581
+ font-size: 1rem;
1582
+ gap: 1.2rem;
1583
+ }
1584
+ .main {
1585
+ margin-top: 1rem;
1586
+ }
1587
+ .model-mgmt-container {
1588
+ gap: 1em;
1589
+ }
1590
+
1591
+ .tab-container {
1592
+ margin-left: 0.5rem;
1593
+ margin-right: 0.5rem;
1594
+ width: calc(100% - 1rem);
1595
+ }
1596
+
1597
+ /* Ensure all input fields are properly constrained on very narrow screens */
1598
+ #register-model-name, #register-mmproj, #register-checkpoint, #register-recipe {
1599
+ max-width: 100%;
1600
+ overflow: hidden;
1601
+ text-overflow: ellipsis;
1602
+ }
1603
+ }
1604
+
1605
+ /* === Responsive Navbar === */
1606
+ @media (max-width: 800px) {
1607
+ .navbar {
1608
+ flex-direction: column;
1609
+ gap: 1rem;
1610
+ padding: 1rem 1rem 0.5rem 1rem;
1611
+ align-items: center;
1612
+ }
1613
+
1614
+ .navbar-brand {
1615
+ margin-bottom: 0.5rem;
1616
+ }
1617
+
1618
+ .brand-title {
1619
+ font-size: 1.3rem;
1620
+ }
1621
+
1622
+ .brand-icon {
1623
+ width: 1.3rem;
1624
+ height: 1.3rem;
1625
+ }
1626
+
1627
+ .navbar-links {
1628
+ gap: 1.5rem;
1629
+ font-size: 1rem;
1630
+ }
1631
+ }
1632
+
1633
+ @media (max-width: 600px) {
1634
+ .navbar {
1635
+ padding: 0.5rem 0.5rem 0.25rem 0.5rem;
1636
+ }
1637
+
1638
+ .brand-title {
1639
+ font-size: 1.2rem;
1640
+ }
1641
+
1642
+ .brand-icon {
1643
+ width: 1.2rem;
1644
+ height: 1.2rem;
1645
+ }
1646
+
1647
+ .navbar-links {
1648
+ gap: 1rem;
1649
+ font-size: 0.9rem;
1650
+ flex-wrap: wrap;
1651
+ justify-content: center;
1652
+ }
1653
+
1654
+ .main {
1655
+ margin-top: 0.5rem;
1656
+ }
1657
+ }
1658
+
1659
+ /* Ensure form container allows tooltip overflow */
1660
+ .model-mgmt-register-form {
1661
+ position: relative;
1662
+ overflow: visible;
1663
+ }
1664
+
1665
+ .register-form-row {
1666
+ position: relative;
1667
+ }
1668
+
1669
+ /* Additional positioning rules for better tooltip placement */
1670
+ .register-label .tooltip-icon::after {
1671
+ width: 200px;
1672
+ }
1673
+
1674
+ /* Ensure tooltips for rightmost elements don't get cut off */
1675
+ .register-form-row-tight .tooltip-icon:last-child::after {
1676
+ left: auto;
1677
+ right: 0;
1678
+ transform: translateX(0);
1679
+ width: 180px;
1680
+ }
1681
+
1682
+ .register-form-row-tight .tooltip-icon:last-child:hover::after {
1683
+ transform: translateX(0) translateY(-2px);
1684
+ }
1685
+
1686
+ .register-form-row-tight .tooltip-icon:last-child::before {
1687
+ left: auto;
1688
+ right: 8px;
1689
+ transform: translateX(0);
1690
+ }
1691
+
1692
+ .register-form-row-tight .tooltip-icon:last-child:hover::before {
1693
+ transform: translateX(0) translateY(-2px);
1694
+ }
1695
+
1696
+ /* App Suggestions Section */
1697
+ .app-suggestions-section {
1698
+ margin-top: 2rem;
1699
+ padding: 1.5rem 0;
1700
+ text-align: center;
1701
+ border-top: 1px solid #e0e0e0;
1702
+ background: #fafafa;
1703
+ }
1704
+
1705
+ .suggestion-text {
1706
+ font-size: 1.1rem;
1707
+ color: #555;
1708
+ margin-bottom: 1.5rem;
1709
+ font-weight: 500;
1710
+ letter-spacing: 0.01em;
1711
+ }
1712
+
1713
+ .suggestion-highlight {
1714
+ font-size: 1.2rem;
1715
+ margin-right: 0.5rem;
1716
+ opacity: 0.8;
1717
+ }
1718
+
1719
+ .app-logos-grid {
1720
+ display: flex;
1721
+ justify-content: center;
1722
+ align-items: center;
1723
+ gap: 1rem;
1724
+ margin: 1rem 0;
1725
+ flex-wrap: nowrap;
1726
+ padding: 0 1rem;
1727
+ }
1728
+
1729
+ .app-logo-item {
1730
+ display: flex;
1731
+ align-items: center;
1732
+ justify-content: center;
1733
+ width: 60px;
1734
+ height: 60px;
1735
+ border-radius: 8px;
1736
+ background: #fff;
1737
+ padding: 0.3rem;
1738
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
1739
+ transition: transform 0.2s, box-shadow 0.2s;
1740
+ text-decoration: none;
1741
+ position: relative;
1742
+ }
1743
+
1744
+ .app-logo-img {
1745
+ width: 100%;
1746
+ height: 100%;
1747
+ object-fit: contain;
1748
+ border-radius: 4px;
1749
+ }
1750
+
1751
+ .app-logo-item:hover {
1752
+ transform: translateY(-3px);
1753
+ box-shadow: 0 6px 20px rgba(0,0,0,0.15);
1754
+ background: #fff;
1755
+ }
1756
+
1757
+ .app-name {
1758
+ font-size: 0.9rem;
1759
+ font-weight: 600;
1760
+ text-align: center;
1761
+ line-height: 1.2;
1762
+ transition: color 0.3s ease;
1763
+ }
1764
+
1765
+ /* Fallback styling for broken images */
1766
+ .app-logo-item.image-failed {
1767
+ background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
1768
+ border: 2px dashed #dee2e6;
1769
+ flex-direction: column;
1770
+ gap: 0.2rem;
1771
+ padding: 0.5rem;
1772
+ }
1773
+
1774
+ .app-logo-item.image-failed:hover {
1775
+ background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%);
1776
+ border-color: #adb5bd;
1777
+ }
1778
+
1779
+ .app-logo-item.image-failed .app-logo-img {
1780
+ display: none;
1781
+ }
1782
+
1783
+ .app-logo-item.image-failed .app-name {
1784
+ display: block;
1785
+ font-size: 0.7rem;
1786
+ color: #6c757d;
1787
+ font-weight: 500;
1788
+ text-align: center;
1789
+ line-height: 1.1;
1790
+ word-wrap: break-word;
1791
+ overflow-wrap: break-word;
1792
+ hyphens: auto;
1793
+ }
1794
+
1795
+ /* Default hidden state for app names */
1796
+ .app-logo-item:not(.image-failed) .app-name {
1797
+ display: none;
1798
+ }
1799
+
1800
+ @media (max-width: 800px) {
1801
+ .app-logos-grid {
1802
+ gap: 0.5rem;
1803
+ padding: 0 0.5rem;
1804
+ }
1805
+
1806
+ .app-logo-item {
1807
+ width: 48px;
1808
+ height: 48px;
1809
+ }
1810
+
1811
+ .app-logo-img {
1812
+ border-radius: 3px;
1813
+ }
1814
+
1815
+ .suggestion-text {
1816
+ font-size: 0.9rem;
1817
+ margin-bottom: 0.5rem;
1818
+ }
1819
+
1820
+ .app-logo-item.image-failed .app-name {
1821
+ font-size: 0.6rem;
1822
+ line-height: 1.0;
1823
+ }
1824
+
1825
+ .app-logo-item.image-failed {
1826
+ padding: 0.3rem;
1827
+ }
1828
+ }
1829
+
1830
+ /* Think tokens styling */
1831
+ .think-tokens-container {
1832
+ margin: 0.5em 0;
1833
+ border-radius: 8px;
1834
+ overflow: hidden;
1835
+ border: 1px solid rgba(0,0,0,0.1);
1836
+ background: rgba(0,0,0,0.02);
1837
+ transition: all 0.2s ease;
1838
+ }
1839
+
1840
+ .think-tokens-header {
1841
+ background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
1842
+ padding: 0.6em 0.8em;
1843
+ cursor: pointer;
1844
+ display: flex;
1845
+ align-items: center;
1846
+ gap: 0.5em;
1847
+ border-bottom: 1px solid rgba(0,0,0,0.08);
1848
+ transition: all 0.2s ease;
1849
+ user-select: none;
1850
+ }
1851
+
1852
+ .think-tokens-header:hover {
1853
+ background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%);
1854
+ }
1855
+
1856
+ .think-tokens-chevron {
1857
+ font-size: 0.8em;
1858
+ color: #666;
1859
+ transition: transform 0.2s ease;
1860
+ width: 12px;
1861
+ text-align: center;
1862
+ }
1863
+
1864
+ .think-tokens-label {
1865
+ font-size: 0.9em;
1866
+ font-weight: 600;
1867
+ color: #555;
1868
+ font-style: italic;
1869
+ }
1870
+
1871
+ .think-tokens-content {
1872
+ padding: 1em;
1873
+ background: rgba(0,0,0,0.03);
1874
+ border-top: 1px solid rgba(0,0,0,0.05);
1875
+ display: block;
1876
+ }
1877
+
1878
+ .think-tokens-container.collapsed .think-tokens-content {
1879
+ display: none;
1880
+ }
1881
+
1882
+ .think-tokens-container.collapsed .think-tokens-header {
1883
+ border-bottom: none;
1884
+ }
1885
+
1886
+ .main-response {
1887
+ margin-top: 0.5em;
1888
+ }
1889
+
1890
+ /* Ensure think tokens content inherits proper styling */
1891
+ .think-tokens-content h1,
1892
+ .think-tokens-content h2,
1893
+ .think-tokens-content h3,
1894
+ .think-tokens-content h4,
1895
+ .think-tokens-content h5,
1896
+ .think-tokens-content h6 {
1897
+ margin: 0.5em 0 0.3em 0;
1898
+ color: inherit;
1899
+ font-weight: bold;
1900
+ }
1901
+
1902
+ .think-tokens-content p {
1903
+ margin: 0.5em 0;
1904
+ line-height: 1.4;
1905
+ }
1906
+
1907
+ .think-tokens-content ul,
1908
+ .think-tokens-content ol {
1909
+ margin: 0.5em 0;
1910
+ padding-left: 1.5em;
1911
+ }
1912
+
1913
+ .think-tokens-content li {
1914
+ margin: 0.2em 0;
1915
+ }
1916
+
1917
+ .think-tokens-content blockquote {
1918
+ margin: 0.5em 0;
1919
+ padding: 0.5em 1em;
1920
+ border-left: 3px solid #ccc;
1921
+ background: rgba(0,0,0,0.05);
1922
+ border-radius: 4px;
1923
+ font-style: italic;
1924
+ }
1925
+
1926
+ .think-tokens-content code {
1927
+ background: rgba(0,0,0,0.1);
1928
+ padding: 0.2em 0.4em;
1929
+ border-radius: 3px;
1930
+ font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
1931
+ font-size: 0.9em;
1932
+ }
1933
+
1934
+ .think-tokens-content pre {
1935
+ background: rgba(0,0,0,0.08);
1936
+ border: 1px solid rgba(0,0,0,0.1);
1937
+ border-radius: 6px;
1938
+ padding: 1em;
1939
+ margin: 0.5em 0;
1940
+ overflow-x: auto;
1941
+ font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
1942
+ font-size: 0.9em;
1943
+ line-height: 1.4;
1944
+ }
1945
+
1946
+ .think-tokens-content pre code {
1947
+ background: none !important;
1948
+ padding: 0 !important;
1949
+ border-radius: 0 !important;
1950
+ border: none !important;
1951
+ font-size: inherit;
1952
+ }
1953
+
1954
+ @keyframes fadeIn {
1955
+ from { opacity: 0; transform: translateY(-5px); }
1956
+ to { opacity: 1; transform: translateY(0); }
1957
+ }
1958
+
1959
+ /* Error banner styles */
1960
+ .error-banner {
1961
+ position: fixed;
1962
+ top: 10px;
1963
+ left: 50%;
1964
+ transform: translateX(-50%);
1965
+ background-color: var(--danger-primary);
1966
+ color: #fff;
1967
+ padding: 0.6em 1.2em;
1968
+ border-radius: 6px;
1969
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
1970
+ z-index: 10000;
1971
+ font-weight: 600;
1972
+ white-space: pre-line;
1973
+ display: none;
1974
+ animation: fadeIn 0.2s ease;
1975
+ align-items: center;
1976
+ }
1977
+
1978
+ /* FastFlowLM notice styles */
1979
+ .flm-notice {
1980
+ background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%);
1981
+ border: 1px solid #ffeaa7;
1982
+ border-left: 4px solid #f39c12;
1983
+ border-radius: 8px;
1984
+ margin-bottom: 1.5rem;
1985
+ box-shadow: 0 2px 8px rgba(243, 156, 18, 0.1);
1986
+ animation: fadeIn 0.3s ease;
1987
+ }
1988
+
1989
+ .flm-notice-content {
1990
+ display: flex;
1991
+ align-items: flex-start;
1992
+ gap: 0.75rem;
1993
+ padding: 1rem 1.25rem;
1994
+ }
1995
+
1996
+ .flm-notice-icon {
1997
+ font-size: 1.2rem;
1998
+ flex-shrink: 0;
1999
+ margin-top: 0.1rem;
2000
+ }
2001
+
2002
+ .flm-notice-text {
2003
+ flex: 1;
2004
+ font-size: 0.95rem;
2005
+ line-height: 1.5;
2006
+ color: #856404;
2007
+ }
2008
+
2009
+ .flm-notice-text strong {
2010
+ color: #6c5ce7;
2011
+ font-weight: 600;
2012
+ }
2013
+
2014
+ .flm-notice-text a {
2015
+ color: var(--info-primary);
2016
+ text-decoration: none;
2017
+ font-weight: 500;
2018
+ }
2019
+
2020
+ .flm-notice-text a:hover {
2021
+ text-decoration: underline;
2022
+ color: var(--info-hover);
2023
+ }
2024
+
2025
+ .error-banner .close-btn {
2026
+ background: none;
2027
+ border: none;
2028
+ color: #fff;
2029
+ font-size: 1.2em;
2030
+ margin-left: 0.8em;
2031
+ cursor: pointer;
2032
+ padding: 0;
2033
+ line-height: 1;
2034
+ }
2035
+
2036
+ .error-banner .close-btn:hover {
2037
+ opacity: 0.8;
2038
+ }
2039
+
2040
+ /* Migration banner - reuses error-banner structure with warning color */
2041
+ .migration-banner {
2042
+ position: fixed;
2043
+ top: 10px;
2044
+ left: 50%;
2045
+ transform: translateX(-50%);
2046
+ background-color: #ffa500;
2047
+ color: #222;
2048
+ padding: 0.6em 1.2em;
2049
+ border-radius: 6px;
2050
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
2051
+ z-index: 10000;
2052
+ font-weight: 600;
2053
+ display: none;
2054
+ animation: fadeIn 0.2s ease;
2055
+ align-items: center;
2056
+ gap: 0.8em;
2057
+ }
2058
+
2059
+ .migration-action-btn {
2060
+ background: #222;
2061
+ color: #ffa500;
2062
+ border: none;
2063
+ padding: 0.4em 0.8em;
2064
+ border-radius: 4px;
2065
+ cursor: pointer;
2066
+ font-weight: 600;
2067
+ transition: background 0.2s;
2068
+ }
2069
+
2070
+ .migration-action-btn:hover {
2071
+ background: #444;
2072
+ }
2073
+
2074
+ .migration-banner .close-btn {
2075
+ background: none;
2076
+ border: none;
2077
+ color: #222;
2078
+ font-size: 1.2em;
2079
+ margin-left: 0.8em;
2080
+ cursor: pointer;
2081
+ padding: 0;
2082
+ line-height: 1;
2083
+ }
2084
+
2085
+ /* Modal styles */
2086
+ .modal {
2087
+ position: fixed;
2088
+ z-index: 10001;
2089
+ left: 0;
2090
+ top: 0;
2091
+ width: 100%;
2092
+ height: 100%;
2093
+ background-color: rgba(0,0,0,0.5);
2094
+ display: flex;
2095
+ align-items: center;
2096
+ justify-content: center;
2097
+ }
2098
+
2099
+ .modal-content {
2100
+ background: #fff;
2101
+ border-radius: 8px;
2102
+ max-width: 600px;
2103
+ width: 90%;
2104
+ max-height: 80vh;
2105
+ display: flex;
2106
+ flex-direction: column;
2107
+ box-shadow: 0 4px 20px rgba(0,0,0,0.3);
2108
+ }
2109
+
2110
+ .modal-header {
2111
+ display: flex;
2112
+ justify-content: space-between;
2113
+ align-items: center;
2114
+ padding: 1.2rem 1.5rem;
2115
+ border-bottom: 1px solid #e0e0e0;
2116
+ }
2117
+
2118
+ .modal-header h2 {
2119
+ margin: 0;
2120
+ font-size: 1.3rem;
2121
+ }
2122
+
2123
+ .modal-close {
2124
+ background: none;
2125
+ border: none;
2126
+ font-size: 1.5rem;
2127
+ cursor: pointer;
2128
+ color: #666;
2129
+ padding: 0;
2130
+ }
2131
+
2132
+ .modal-body {
2133
+ padding: 1.5rem;
2134
+ overflow-y: auto;
2135
+ flex: 1;
2136
+ }
2137
+
2138
+ .migration-model-list {
2139
+ margin: 1rem 0;
2140
+ border: 1px solid #e0e0e0;
2141
+ border-radius: 4px;
2142
+ max-height: 300px;
2143
+ overflow-y: auto;
2144
+ }
2145
+
2146
+ .migration-model-item {
2147
+ padding: 0.75rem 1rem;
2148
+ border-bottom: 1px solid #f0f0f0;
2149
+ display: flex;
2150
+ justify-content: space-between;
2151
+ }
2152
+
2153
+ .migration-model-item:last-child {
2154
+ border-bottom: none;
2155
+ }
2156
+
2157
+ .migration-summary {
2158
+ margin-top: 1rem;
2159
+ padding: 0.75rem;
2160
+ background: #f8f9fa;
2161
+ border-radius: 4px;
2162
+ text-align: center;
2163
+ }
2164
+
2165
+ .modal-footer {
2166
+ padding: 1rem 1.5rem;
2167
+ border-top: 1px solid #e0e0e0;
2168
+ display: flex;
2169
+ justify-content: flex-end;
2170
+ gap: 0.75rem;
2171
+ }
2172
+
2173
+ .delete-btn {
2174
+ padding: 0.6em 1.5em;
2175
+ background: var(--danger-primary);
2176
+ color: white;
2177
+ border: none;
2178
+ border-radius: 4px;
2179
+ font-weight: 600;
2180
+ cursor: pointer;
2181
+ transition: background 0.2s;
2182
+ }
2183
+
2184
+ .delete-btn:hover {
2185
+ background: var(--danger-hover);
2186
+ }
2187
+
2188
+ /* === Model Settings === */
2189
+ .model-settings-container {
2190
+ max-width: 600px;
2191
+ margin: 0 auto;
2192
+ padding: 2rem;
2193
+ }
2194
+
2195
+ .settings-form {
2196
+ background: #fff;
2197
+ padding: 2rem;
2198
+ border-radius: 8px;
2199
+ border: 1px solid #e0e0e0;
2200
+ }
2201
+
2202
+ .setting-row {
2203
+ display: flex;
2204
+ flex-direction: column;
2205
+ gap: 0.5rem;
2206
+ margin-bottom: 1.5rem;
2207
+ }
2208
+
2209
+ .setting-row label {
2210
+ font-weight: 600;
2211
+ color: var(--text-primary);
2212
+ font-size: 1rem;
2213
+ }
2214
+
2215
+ .setting-row input {
2216
+ padding: 0.75rem;
2217
+ border: 1px solid #ddd;
2218
+ border-radius: 4px;
2219
+ font-size: 1rem;
2220
+ transition: border-color var(--transition-fast);
2221
+ }
2222
+
2223
+ .setting-row input:focus {
2224
+ outline: none;
2225
+ border-color: var(--accent-gold);
2226
+ box-shadow: 0 0 0 2px rgba(230, 184, 0, 0.2);
2227
+ }
2228
+
2229
+ .setting-description {
2230
+ font-size: 0.9rem;
2231
+ color: var(--text-muted);
2232
+ font-style: italic;
2233
+ }
2234
+
2235
+ .setting-actions {
2236
+ display: flex;
2237
+ gap: 1rem;
2238
+ margin-top: 2rem;
2239
+ justify-content: flex-end;
2240
+ }
2241
+
2242
+ .save-btn {
2243
+ padding: 0.75rem 1.5rem;
2244
+ background: var(--accent-gold);
2245
+ color: #222;
2246
+ border: none;
2247
+ border-radius: 4px;
2248
+ font-weight: 600;
2249
+ cursor: pointer;
2250
+ transition: background var(--transition-fast);
2251
+ }
2252
+
2253
+ .save-btn:hover {
2254
+ background: var(--accent-gold-dark);
2255
+ }
2256
+
2257
+ .reset-btn {
2258
+ padding: 0.75rem 1.5rem;
2259
+ background: #6c757d;
2260
+ color: white;
2261
+ border: none;
2262
+ border-radius: 4px;
2263
+ font-weight: 600;
2264
+ cursor: pointer;
2265
+ transition: background var(--transition-fast);
2266
+ }
2267
+
2268
+ .reset-btn:hover {
2269
+ background: #5a6268;
2270
+ }
2271
+
2272
+ /* === Model Browser === */
2273
+ .model-browser-container {
2274
+ display: flex;
2275
+ max-width: 1200px;
2276
+ margin: 0 auto;
2277
+ min-height: 600px;
2278
+ background: #fff;
2279
+ border-radius: 8px;
2280
+ border: 1px solid #e0e0e0;
2281
+ overflow: visible;
2282
+ }
2283
+
2284
+ .model-browser-sidebar {
2285
+ width: 280px;
2286
+ background: #f8f9fa;
2287
+ border-right: 1px solid #e0e0e0;
2288
+ overflow-y: auto;
2289
+ }
2290
+
2291
+ .model-category {
2292
+ border-bottom: 1px solid #e0e0e0;
2293
+ }
2294
+
2295
+ .model-category-section {
2296
+ border-bottom: 1px solid #e0e0e0;
2297
+ padding: 0.5rem 0;
2298
+ }
2299
+
2300
+ .category-header {
2301
+ display: flex;
2302
+ align-items: center;
2303
+ gap: 0.75rem;
2304
+ padding: 1rem;
2305
+ cursor: pointer;
2306
+ transition: background var(--transition-fast);
2307
+ font-weight: 600;
2308
+ }
2309
+
2310
+ .section-header {
2311
+ display: flex;
2312
+ align-items: center;
2313
+ gap: 0.75rem;
2314
+ padding: 0.75rem 1rem;
2315
+ font-weight: 600;
2316
+ color: #6c757d;
2317
+ font-size: 0.9rem;
2318
+ text-transform: uppercase;
2319
+ letter-spacing: 0.5px;
2320
+ }
2321
+
2322
+ .category-header:hover {
2323
+ background: #e9ecef;
2324
+ }
2325
+
2326
+ .category-header.active {
2327
+ background: var(--primary-yellow);
2328
+ color: #333;
2329
+ font-weight: 600;
2330
+ }
2331
+
2332
+ .category-icon, .section-icon {
2333
+ font-size: 1.1rem;
2334
+ }
2335
+
2336
+ .category-name, .section-name {
2337
+ flex: 1;
2338
+ }
2339
+
2340
+ .category-content {
2341
+ display: none;
2342
+ }
2343
+
2344
+ .category-content.expanded {
2345
+ display: block;
2346
+ }
2347
+
2348
+ .section-content {
2349
+ padding: 0;
2350
+ }
2351
+
2352
+ .subcategory {
2353
+ padding: 0.75rem 1rem 0.75rem 3rem;
2354
+ cursor: pointer;
2355
+ transition: background var(--transition-fast);
2356
+ border-bottom: 1px solid #f0f0f0;
2357
+ }
2358
+
2359
+ .subcategory:hover {
2360
+ background: #e9ecef;
2361
+ }
2362
+
2363
+ .subcategory.active {
2364
+ background: var(--primary-yellow);
2365
+ color: #333;
2366
+ font-weight: 600;
2367
+ }
2368
+
2369
+ /* Add Model Form in Main Area */
2370
+ .add-model-form-main {
2371
+ padding: 1.5rem;
2372
+ max-width: 100%;
2373
+ box-sizing: border-box;
2374
+ }
2375
+
2376
+ .add-model-form-main .form-content {
2377
+ max-width: 800px;
2378
+ width: 100%;
2379
+ }
2380
+
2381
+ .add-model-form-main .register-form-row {
2382
+ display: flex;
2383
+ align-items: center;
2384
+ gap: 1.2em;
2385
+ margin-bottom: 1em;
2386
+ padding: 0.1em 0;
2387
+ position: relative;
2388
+ max-width: 100%;
2389
+ box-sizing: border-box;
2390
+ }
2391
+
2392
+ .add-model-form-main .register-model-name-group {
2393
+ display: flex;
2394
+ align-items: stretch;
2395
+ flex: 1;
2396
+ border-radius: 6px;
2397
+ overflow: hidden;
2398
+ transition: box-shadow 0.2s ease;
2399
+ min-width: 0;
2400
+ max-width: 400px;
2401
+ }
2402
+
2403
+ .add-model-form-main input[type="text"],
2404
+ .add-model-form-main select {
2405
+ max-width: 400px;
2406
+ width: 100%;
2407
+ box-sizing: border-box;
2408
+ }
2409
+
2410
+ .add-model-form-main #register-checkpoint {
2411
+ max-width: 500px;
2412
+ }
2413
+
2414
+ .model-browser-main {
2415
+ flex: 1;
2416
+ display: flex;
2417
+ flex-direction: column;
2418
+ overflow: visible;
2419
+ }
2420
+
2421
+ .model-browser-header {
2422
+ padding: 1.5rem;
2423
+ border-bottom: 1px solid #e0e0e0;
2424
+ background: #f8f9fa;
2425
+ }
2426
+
2427
+ .model-browser-header h3 {
2428
+ margin: 0;
2429
+ color: var(--text-primary);
2430
+ font-size: 1.3rem;
2431
+ }
2432
+
2433
+ .model-list {
2434
+ flex: 1;
2435
+ padding: 1rem;
2436
+ overflow-y: auto;
2437
+ }
2438
+
2439
+ .model-item {
2440
+ display: flex;
2441
+ align-items: center;
2442
+ gap: 1rem;
2443
+ padding: 1rem;
2444
+ border: 1px solid #e0e0e0;
2445
+ border-radius: 6px;
2446
+ margin-bottom: 0.75rem;
2447
+ background: #fff;
2448
+ transition: all var(--transition-fast);
2449
+ }
2450
+
2451
+ .model-item:hover {
2452
+ border-color: var(--accent-gold);
2453
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
2454
+ }
2455
+
2456
+ .model-item-info {
2457
+ flex: 1;
2458
+ }
2459
+
2460
+ .model-item-name {
2461
+ font-weight: 600;
2462
+ font-size: 1rem;
2463
+ margin-bottom: 0.25rem;
2464
+ }
2465
+
2466
+ .model-item-description {
2467
+ font-size: 0.9rem;
2468
+ color: var(--text-muted);
2469
+ }
2470
+
2471
+ .model-item-actions {
2472
+ display: flex;
2473
+ gap: 0.5rem;
2474
+ }
2475
+
2476
+ /* Modern emoji button styling with clean glass effect */
2477
+ .model-item-btn {
2478
+ position: relative;
2479
+ border: none;
2480
+ border-radius: 12px;
2481
+ padding: 0.75em;
2482
+ cursor: pointer;
2483
+ font-weight: 500;
2484
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
2485
+ font-size: 1.3em;
2486
+ width: 48px;
2487
+ height: 48px;
2488
+ display: flex;
2489
+ align-items: center;
2490
+ justify-content: center;
2491
+ line-height: 1;
2492
+ background: linear-gradient(145deg, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.6));
2493
+ backdrop-filter: blur(10px);
2494
+ border: 1px solid rgba(255, 255, 255, 0.3);
2495
+ box-shadow:
2496
+ 0 2px 8px rgba(0, 0, 0, 0.08),
2497
+ 0 1px 3px rgba(0, 0, 0, 0.1),
2498
+ inset 0 1px 0 rgba(255, 255, 255, 0.8);
2499
+ color: #555;
2500
+ text-align: center;
2501
+ overflow: hidden;
2502
+ font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Android Emoji", "EmojiSymbols", sans-serif;
2503
+ }
2504
+
2505
+ /* Elegant hover animation with subtle scaling */
2506
+ .model-item-btn:hover {
2507
+ transform: translateY(-2px) scale(1.05);
2508
+ box-shadow:
2509
+ 0 8px 25px rgba(0, 0, 0, 0.15),
2510
+ 0 3px 10px rgba(0, 0, 0, 0.1),
2511
+ inset 0 1px 0 rgba(255, 255, 255, 0.9);
2512
+ background: linear-gradient(145deg, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.8));
2513
+ border-color: rgba(255, 255, 255, 0.5);
2514
+ }
2515
+
2516
+ /* Satisfying active state */
2517
+ .model-item-btn:active {
2518
+ transform: translateY(-1px) scale(1.02);
2519
+ box-shadow:
2520
+ 0 3px 12px rgba(0, 0, 0, 0.12),
2521
+ 0 1px 5px rgba(0, 0, 0, 0.08),
2522
+ inset 0 1px 0 rgba(255, 255, 255, 0.7);
2523
+ transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
2524
+ }
2525
+
2526
+ /* Disabled state with reduced opacity */
2527
+ .model-item-btn:disabled {
2528
+ opacity: 0.4;
2529
+ cursor: not-allowed;
2530
+ transform: none !important;
2531
+ box-shadow:
2532
+ 0 1px 3px rgba(0, 0, 0, 0.05),
2533
+ inset 0 1px 0 rgba(255, 255, 255, 0.5) !important;
2534
+ background: linear-gradient(145deg, rgba(248, 248, 248, 0.8), rgba(240, 240, 240, 0.6)) !important;
2535
+ }
2536
+
2537
+ /* Special styling for loading/installing state with enhanced design */
2538
+ .model-item-btn.loading,
2539
+ .model-item-btn.installing {
2540
+ font-size: 0.75em;
2541
+ padding: 0.5em 0.8em;
2542
+ width: auto;
2543
+ min-width: 90px;
2544
+ height: 40px;
2545
+ background: linear-gradient(145deg, rgba(107, 114, 128, 0.15), rgba(107, 114, 128, 0.08));
2546
+ color: var(--neutral-primary);
2547
+ border: 1px solid rgba(107, 114, 128, 0.3);
2548
+ cursor: wait;
2549
+ backdrop-filter: blur(8px);
2550
+ box-shadow:
2551
+ 0 2px 8px rgba(107, 114, 128, 0.1),
2552
+ inset 0 1px 0 rgba(255, 255, 255, 0.6);
2553
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
2554
+ font-weight: 500;
2555
+ white-space: nowrap;
2556
+ }
2557
+
2558
+ /* Pulsing animation for loading states */
2559
+ .model-item-btn.loading,
2560
+ .model-item-btn.installing {
2561
+ animation: gentle-pulse 2s ease-in-out infinite;
2562
+ }
2563
+
2564
+ @keyframes gentle-pulse {
2565
+ 0%, 100% {
2566
+ opacity: 1;
2567
+ transform: scale(1);
2568
+ }
2569
+ 50% {
2570
+ opacity: 0.8;
2571
+ transform: scale(1.02);
2572
+ }
2573
+ }
2574
+
2575
+ /* Color-coded hover effects for different action types */
2576
+ .model-item-btn.install:hover {
2577
+ background: linear-gradient(145deg, rgba(230, 184, 0, 0.2), rgba(230, 184, 0, 0.1));
2578
+ border-color: rgba(230, 184, 0, 0.4);
2579
+ box-shadow:
2580
+ 0 8px 25px rgba(230, 184, 0, 0.2),
2581
+ 0 3px 10px rgba(0, 0, 0, 0.1),
2582
+ inset 0 1px 0 rgba(255, 255, 255, 0.9);
2583
+ }
2584
+
2585
+ .model-item-btn.load:hover {
2586
+ background: linear-gradient(145deg, rgba(45, 127, 71, 0.2), rgba(45, 127, 71, 0.1));
2587
+ border-color: rgba(45, 127, 71, 0.4);
2588
+ box-shadow:
2589
+ 0 8px 25px rgba(45, 127, 71, 0.2),
2590
+ 0 3px 10px rgba(0, 0, 0, 0.1),
2591
+ inset 0 1px 0 rgba(255, 255, 255, 0.9);
2592
+ }
2593
+
2594
+ .model-item-btn.unload:hover {
2595
+ background: linear-gradient(145deg, rgba(107, 114, 128, 0.2), rgba(107, 114, 128, 0.1));
2596
+ border-color: rgba(107, 114, 128, 0.4);
2597
+ box-shadow:
2598
+ 0 8px 25px rgba(107, 114, 128, 0.2),
2599
+ 0 3px 10px rgba(0, 0, 0, 0.1),
2600
+ inset 0 1px 0 rgba(255, 255, 255, 0.9);
2601
+ }
2602
+
2603
+ .model-item-btn.delete:hover {
2604
+ background: linear-gradient(145deg, rgba(200, 88, 108, 0.2), rgba(200, 88, 108, 0.1));
2605
+ border-color: rgba(200, 88, 108, 0.4);
2606
+ box-shadow:
2607
+ 0 8px 25px rgba(200, 88, 108, 0.2),
2608
+ 0 3px 10px rgba(0, 0, 0, 0.1),
2609
+ inset 0 1px 0 rgba(255, 255, 255, 0.9);
2610
+ }
2611
+
2612
+
2613
+ /* Dropdown styling */
2614
+ .dropdown {
2615
+ position: relative;
2616
+ display: inline-block;
2617
+ }
2618
+
2619
+ .dropbtn {
2620
+ background-color: transparent;
2621
+ border: none;
2622
+ font-size: 16px;
2623
+ cursor: pointer;
2624
+ }
2625
+
2626
+ .dropdown-content {
2627
+ display: none;
2628
+ position: absolute;
2629
+ right: 0; /* align to the right edge of the parent */
2630
+ left: auto; /* prevent left alignment */
2631
+ top: calc(100% + 1px); /* opens 8px below the button */
2632
+ background-color: #ffe76c;
2633
+ border-radius: 8px;
2634
+ min-width: 140px;
2635
+ box-shadow: 0px 8px 16px rgba(0,0,0,0.2);
2636
+ z-index: 1000;
2637
+ overflow: hidden;
2638
+ }
2639
+
2640
+ .dropdown-content a {
2641
+ color: black;
2642
+ font-size: 12px; /* smaller font */
2643
+ padding: 8px 12px;
2644
+ text-decoration: none;
2645
+ display: block;
2646
+ }
2647
+
2648
+ .dropdown-content a:hover {
2649
+ background-color: #f6c146;
2650
+ }
2651
+
2652
+ .dropdown:hover .dropdown-content {
2653
+ display: block;
2654
+ }