lemonade-sdk 8.1.7__py3-none-any.whl → 8.1.9__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 (29) hide show
  1. lemonade/cli.py +47 -5
  2. lemonade/profilers/agt_power.py +437 -0
  3. lemonade/profilers/hwinfo_power.py +429 -0
  4. lemonade/tools/llamacpp/utils.py +15 -4
  5. lemonade/tools/oga/load.py +15 -2
  6. lemonade/tools/report/table.py +1 -1
  7. lemonade/tools/server/llamacpp.py +19 -13
  8. lemonade/tools/server/serve.py +39 -9
  9. lemonade/tools/server/static/js/chat.js +545 -242
  10. lemonade/tools/server/static/js/models.js +112 -24
  11. lemonade/tools/server/static/js/shared.js +15 -5
  12. lemonade/tools/server/static/styles.css +145 -75
  13. lemonade/tools/server/static/webapp.html +23 -27
  14. lemonade/tools/server/wrapped_server.py +8 -0
  15. lemonade/version.py +1 -1
  16. lemonade_install/install.py +15 -49
  17. {lemonade_sdk-8.1.7.dist-info → lemonade_sdk-8.1.9.dist-info}/METADATA +16 -64
  18. {lemonade_sdk-8.1.7.dist-info → lemonade_sdk-8.1.9.dist-info}/RECORD +26 -27
  19. lemonade_server/cli.py +12 -9
  20. lemonade_server/model_manager.py +48 -0
  21. lemonade_server/server_models.json +24 -6
  22. lemonade/tools/quark/__init__.py +0 -0
  23. lemonade/tools/quark/quark_load.py +0 -173
  24. lemonade/tools/quark/quark_quantize.py +0 -439
  25. {lemonade_sdk-8.1.7.dist-info → lemonade_sdk-8.1.9.dist-info}/WHEEL +0 -0
  26. {lemonade_sdk-8.1.7.dist-info → lemonade_sdk-8.1.9.dist-info}/entry_points.txt +0 -0
  27. {lemonade_sdk-8.1.7.dist-info → lemonade_sdk-8.1.9.dist-info}/licenses/LICENSE +0 -0
  28. {lemonade_sdk-8.1.7.dist-info → lemonade_sdk-8.1.9.dist-info}/licenses/NOTICE.md +0 -0
  29. {lemonade_sdk-8.1.7.dist-info → lemonade_sdk-8.1.9.dist-info}/top_level.txt +0 -0
@@ -42,48 +42,90 @@ async function checkModelHealth() {
42
42
  }
43
43
  }
44
44
 
45
+ // Populate the model dropdown with all installed models
46
+ function populateModelDropdown() {
47
+ const indicator = document.getElementById('model-status-indicator');
48
+ const select = document.getElementById('model-select');
49
+ select.innerHTML = '';
50
+
51
+ // Add the default option
52
+ const defaultOption = document.createElement('option');
53
+ defaultOption.value = '';
54
+ defaultOption.textContent = 'Click to select a model ▼';
55
+ select.appendChild(defaultOption);
56
+
57
+ // Add the hidden 'Server Offline' option
58
+ const hiddenOption = document.createElement('option');
59
+ hiddenOption.value = 'server-offline';
60
+ hiddenOption.textContent = 'Server Offline';
61
+ hiddenOption.hidden = true;
62
+ select.appendChild(hiddenOption);
63
+
64
+ // Get all installed models from the global set
65
+ const sortedModels = Array.from(installedModels).sort();
66
+
67
+ // Add options for each installed model
68
+ sortedModels.forEach(modelId => {
69
+ const option = document.createElement('option');
70
+ option.value = modelId;
71
+ option.textContent = modelId;
72
+ select.appendChild(option);
73
+ });
74
+ }
75
+
45
76
  // Update model status indicator
46
77
  async function updateModelStatusIndicator() {
47
78
  const indicator = document.getElementById('model-status-indicator');
48
- const statusText = document.getElementById('model-status-text');
49
- const unloadBtn = document.getElementById('model-unload-btn');
50
-
79
+ const select = document.getElementById('model-select');
80
+ const buttonIcons = document.querySelectorAll('button');
81
+
51
82
  // Fetch both health and installed models
52
83
  const [health] = await Promise.all([
53
84
  checkModelHealth(),
54
85
  fetchInstalledModels()
55
86
  ]);
56
-
57
- // Refresh model dropdown in chat after fetching installed models
58
- if (window.initializeModelDropdown) {
59
- window.initializeModelDropdown();
60
- }
61
-
87
+
88
+ // Populate the dropdown with the newly fetched installed models
89
+ populateModelDropdown();
90
+
62
91
  // Refresh model management UI if we're on the models tab
63
92
  const modelsTab = document.getElementById('content-models');
64
93
  if (modelsTab && modelsTab.classList.contains('active')) {
65
94
  // Use the display-only version to avoid re-fetching data we just fetched
66
95
  refreshModelMgmtUIDisplay();
67
96
  }
68
-
69
- // Remove any click handlers
70
- indicator.onclick = null;
71
-
97
+
72
98
  if (health && health.model_loaded) {
73
99
  // Model is loaded - show model name with online status
100
+ indicator.classList.remove('online', 'offline', 'loading');
74
101
  currentLoadedModel = health.model_loaded;
75
- updateStatusIndicator(health.model_loaded, 'loaded');
76
- unloadBtn.style.display = 'block';
77
- } else if (health) {
102
+ indicator.classList.add('loaded');
103
+ select.value = currentLoadedModel;
104
+ select.disabled = false;
105
+ buttonIcons.forEach(btn => btn.disabled = false);
106
+ } else if (health !== null) {
78
107
  // Server is online but no model loaded
108
+ indicator.classList.remove('loaded', 'offline', 'loading');
79
109
  currentLoadedModel = null;
80
- updateStatusIndicator('Server Online', 'online');
81
- unloadBtn.style.display = 'none';
110
+ indicator.classList.add('online');
111
+ select.value = ''; // Set to the "Click to select a model ▼" option
112
+ select.disabled = false;
113
+ buttonIcons.forEach(btn => btn.disabled = false);
82
114
  } else {
83
115
  // Server is offline
116
+ indicator.classList.remove('loaded', 'online', 'loading');
84
117
  currentLoadedModel = null;
85
- updateStatusIndicator('Server Offline', 'offline');
86
- unloadBtn.style.display = 'none';
118
+ // Add the hidden 'Server Offline' option
119
+ const hiddenOption = document.createElement('option');
120
+ hiddenOption.value = 'server-offline';
121
+ hiddenOption.textContent = 'Server Offline';
122
+ hiddenOption.hidden = true;
123
+ select.appendChild(hiddenOption);
124
+ indicator.classList.add('offline');
125
+ select.value = 'server-offline';
126
+ select.disabled = true;
127
+ buttonIcons.forEach(btn => btn.disabled = true);
128
+ return;
87
129
  }
88
130
  }
89
131
 
@@ -92,9 +134,18 @@ async function unloadModel() {
92
134
  if (!currentLoadedModel) return;
93
135
 
94
136
  try {
137
+ // Set loading state
138
+ const indicator = document.getElementById('model-status-indicator');
139
+ const select = document.getElementById('model-select');
140
+ indicator.classList.remove('loaded', 'online', 'offline');
141
+ indicator.classList.add('loading');
142
+ select.disabled = true;
143
+ select.value = currentLoadedModel; // Keep the selected model visible during unload
144
+
95
145
  await httpRequest(getServerBaseUrl() + '/api/v1/unload', {
96
146
  method: 'POST'
97
147
  });
148
+
98
149
  await updateModelStatusIndicator();
99
150
 
100
151
  // Refresh model list to show updated button states
@@ -104,6 +155,7 @@ async function unloadModel() {
104
155
  } catch (error) {
105
156
  console.error('Error unloading model:', error);
106
157
  showErrorBanner('Failed to unload model: ' + error.message);
158
+ await updateModelStatusIndicator(); // Revert state on error
107
159
  }
108
160
  }
109
161
 
@@ -321,10 +373,14 @@ function createModelItem(modelId, modelData, container) {
321
373
  actions.appendChild(unloadBtn);
322
374
  } else {
323
375
  const loadBtn = document.createElement('button');
376
+ const modelSelect = document.getElementById('model-select');
324
377
  loadBtn.className = 'model-item-btn load';
325
378
  loadBtn.textContent = '🚀';
326
379
  loadBtn.title = 'Load';
327
- loadBtn.onclick = () => loadModel(modelId);
380
+ loadBtn.onclick = () => {
381
+ modelSelect.value = modelId;
382
+ modelSelect.dispatchEvent(new Event('change', { bubbles: true }));
383
+ };
328
384
  actions.appendChild(loadBtn);
329
385
  }
330
386
 
@@ -394,6 +450,15 @@ async function installModel(modelId) {
394
450
 
395
451
  // Load model
396
452
  async function loadModel(modelId) {
453
+ const indicator = document.getElementById('model-status-indicator');
454
+ const select = document.getElementById('model-select');
455
+
456
+ // Set loading state for indicator
457
+ modelSelect.value = 'loading-model';
458
+ indicator.classList.remove('loaded', 'online', 'offline');
459
+ indicator.classList.add('loading');
460
+ select.disabled = true;
461
+
397
462
  // Find the load button and show loading state
398
463
  const modelItems = document.querySelectorAll('.model-item');
399
464
  let loadBtn = null;
@@ -404,6 +469,12 @@ async function loadModel(modelId) {
404
469
  loadBtn = item.querySelector('.model-item-btn.load');
405
470
  }
406
471
  });
472
+
473
+ if (loadBtn) {
474
+ loadBtn.disabled = true;
475
+ loadBtn.textContent = '⏳';
476
+ loadBtn.classList.add('loading');
477
+ }
407
478
 
408
479
  // Use the standardized load function
409
480
  const success = await loadModelStandardized(modelId, {
@@ -417,6 +488,7 @@ async function loadModel(modelId) {
417
488
  },
418
489
  onError: (error, failedModelId) => {
419
490
  console.error(`Failed to load model ${failedModelId}:`, error);
491
+ showErrorBanner('Failed to load model: ' + error.message);
420
492
  }
421
493
  });
422
494
  }
@@ -488,6 +560,8 @@ function createModelNameWithLabels(modelId, serverModels) {
488
560
  labelClass = 'reranking';
489
561
  } else if (labelLower === 'coding') {
490
562
  labelClass = 'coding';
563
+ } else if (labelLower === 'tool-calling') {
564
+ labelClass = 'tool-calling';
491
565
  }
492
566
  labelSpan.className = `model-label ${labelClass}`;
493
567
  labelSpan.textContent = label;
@@ -508,11 +582,21 @@ document.addEventListener('DOMContentLoaded', async function() {
508
582
  unloadBtn.onclick = unloadModel;
509
583
  }
510
584
 
585
+ const modelSelect = document.getElementById('model-select');
586
+ if (modelSelect) {
587
+ modelSelect.addEventListener('change', async function() {
588
+ const modelId = this.value;
589
+ if (modelId) {
590
+ await loadModel(modelId);
591
+ }
592
+ });
593
+ }
594
+
511
595
  // Initial fetch of model data - this will populate installedModels
512
596
  await updateModelStatusIndicator();
513
597
 
514
598
  // Set up periodic refresh of model status
515
- setInterval(updateModelStatusIndicator, 5000); // Check every 5 seconds
599
+ setInterval(updateModelStatusIndicator, 1000); // Check every 1 seconds
516
600
 
517
601
  // Initialize model browser with hot models
518
602
  displayHotModels();
@@ -677,7 +761,6 @@ async function refreshModelMgmtUI() {
677
761
  }
678
762
  };
679
763
  tdBtn.appendChild(btn);
680
-
681
764
  tr.appendChild(tdName);
682
765
  tr.appendChild(tdBtn);
683
766
  installedTbody.appendChild(tr);
@@ -699,6 +782,11 @@ async function refreshModelMgmtUI() {
699
782
  if (window.initializeModelDropdown) {
700
783
  window.initializeModelDropdown();
701
784
  }
785
+
786
+ // Update system message when installed models change
787
+ if (window.displaySystemMessage) {
788
+ window.displaySystemMessage();
789
+ }
702
790
  }
703
791
 
704
792
  // Make refreshModelMgmtUI globally accessible
@@ -862,4 +950,4 @@ window.showAddModelForm = showAddModelForm;
862
950
  window.unloadModel = unloadModel;
863
951
  window.installModel = installModel;
864
952
  window.loadModel = loadModel;
865
- window.deleteModel = deleteModel;
953
+ window.deleteModel = deleteModel;
@@ -54,6 +54,14 @@ function renderMarkdown(text) {
54
54
 
55
55
  // Display an error message in the banner
56
56
  function showErrorBanner(msg) {
57
+ // If DOM isn't ready, wait for it
58
+ if (document.readyState === 'loading') {
59
+ document.addEventListener('DOMContentLoaded', () => {
60
+ showErrorBanner(msg);
61
+ });
62
+ return;
63
+ }
64
+
57
65
  const banner = document.getElementById('error-banner');
58
66
  if (!banner) return;
59
67
  const msgEl = document.getElementById('error-banner-msg');
@@ -250,7 +258,7 @@ async function loadModelStandardized(modelId, options = {}) {
250
258
  // Reset the default option text
251
259
  const defaultOption = modelSelect.querySelector('option[value=""]');
252
260
  if (defaultOption) {
253
- defaultOption.textContent = 'Pick a model';
261
+ defaultOption.textContent = 'Click to select a model';
254
262
  }
255
263
  }
256
264
 
@@ -294,7 +302,7 @@ async function loadModelStandardized(modelId, options = {}) {
294
302
  // Reset the default option text
295
303
  const defaultOption = modelSelect.querySelector('option[value=""]');
296
304
  if (defaultOption) {
297
- defaultOption.textContent = 'Pick a model';
305
+ defaultOption.textContent = 'Click to select a model';
298
306
  }
299
307
  }
300
308
 
@@ -303,12 +311,12 @@ async function loadModelStandardized(modelId, options = {}) {
303
311
  onLoadingEnd(modelId, false);
304
312
  }
305
313
 
306
- // Call error callback or show default error
314
+ // Call error callback and always show default error banner as fallback
307
315
  if (onError) {
308
316
  onError(error, modelId);
309
- } else {
310
- showErrorBanner('Failed to load model: ' + error.message);
311
317
  }
318
+ // Always show error banner to ensure user sees the error
319
+ showErrorBanner('Failed to load model: ' + error.message);
312
320
 
313
321
  return false;
314
322
  }
@@ -474,6 +482,8 @@ function createModelNameWithLabels(modelId, allModels) {
474
482
  labelClass = 'reranking';
475
483
  } else if (labelLower === 'coding') {
476
484
  labelClass = 'coding';
485
+ } else if (labelLower === 'tool-calling') {
486
+ labelClass = 'tool-calling';
477
487
  }
478
488
  labelSpan.className = `model-label ${labelClass}`;
479
489
  labelSpan.textContent = label;
@@ -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;
@@ -157,9 +163,8 @@ body::before {
157
163
  margin-bottom: 2em;
158
164
  border-radius: 8px;
159
165
  border: 1px solid #e0e0e0;
160
- max-width: 1000px;
161
166
  min-width: 320px;
162
- width: calc(100% - 2rem); /* Responsive width with margin */
167
+ width: 100%;
163
168
  margin-left: 1rem;
164
169
  margin-right: 1rem;
165
170
  }
@@ -230,49 +235,44 @@ body::before {
230
235
  animation: pulse-glow 2s infinite;
231
236
  }
232
237
 
233
- /* Online status (green) */
238
+ /* Online model unloaded status (yellow) */
234
239
  .model-status-indicator.online .status-light {
235
- background: #28a745;
240
+ background: var(--status-yellow);
236
241
  box-shadow: 0 0 8px rgba(40, 167, 69, 0.6);
237
242
  }
238
243
 
239
244
  .model-status-indicator.online .status-light::before {
240
- background: #28a745;
245
+ background: var(--status-yellow);
241
246
  }
242
247
 
243
- .model-status-indicator.online .model-status-text {
244
- color: #28a745;
245
- font-weight: 600;
248
+ /* Online model loading status (yellow) */
249
+ .model-status-indicator.loading .status-light {
250
+ background: var(--status-yellow);
251
+ box-shadow: 0 0 8px rgba(40, 167, 69, 0.6);
252
+ }
253
+
254
+ .model-status-indicator.loading .status-light::before {
255
+ background: var(--status-yellow);
246
256
  }
247
257
 
248
258
  /* Offline status (red) */
249
259
  .model-status-indicator.offline .status-light {
250
- background: #dc3545;
260
+ background: var(--status-red);
251
261
  box-shadow: 0 0 8px rgba(220, 53, 69, 0.6);
252
262
  }
253
263
 
254
264
  .model-status-indicator.offline .status-light::before {
255
- background: #dc3545;
256
- }
257
-
258
- .model-status-indicator.offline .model-status-text {
259
- color: #dc3545;
260
- font-weight: 600;
265
+ background: var(--status-red);
261
266
  }
262
267
 
263
- /* Model loaded status (same as online but with model name) */
268
+ /* Online model loaded status (with model name) */
264
269
  .model-status-indicator.loaded .status-light {
265
- background: #28a745;
270
+ background: var(--status-green);
266
271
  box-shadow: 0 0 8px rgba(40, 167, 69, 0.6);
267
272
  }
268
273
 
269
274
  .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;
275
+ background: var(--status-green);
276
276
  }
277
277
 
278
278
  @keyframes pulse-glow {
@@ -286,25 +286,99 @@ body::before {
286
286
  }
287
287
  }
288
288
 
289
- .model-status-text {
290
- font-weight: 600;
289
+ /* Base styles for the select element */
290
+ .model-select {
291
+ padding: 0.5rem 0.75rem;
292
+ border: 1px solid #ddd;
293
+ border-radius: 6px;
294
+ background: #fafafa;
291
295
  font-size: 0.9rem;
296
+ min-width: 180px;
297
+ cursor: pointer;
298
+ transition: all var(--transition-fast);
299
+ text-align-last: center;
300
+ -moz-appearance: none;
301
+ -webkit-appearance: none;
302
+ appearance: none;
303
+ }
304
+
305
+ .model-select:focus {
306
+ outline: none;
307
+ border-color: var(--accent-gold);
308
+ box-shadow: 0 0 0 2px rgba(255, 193, 7, 0.2);
309
+ }
310
+
311
+ /* Existing styles for selective coloring based on status */
312
+ .model-status-indicator.loaded .model-select {
313
+ color: var(--status-green); /* Green for loaded model */
314
+ font-weight: bold;
315
+ }
316
+
317
+ .model-status-indicator.loading .model-select {
318
+ color: var(--status-gray); /* Gray for loading */
319
+ font-style: italic;
320
+ cursor: wait;
321
+ }
322
+
323
+ .model-status-indicator.online .model-select {
324
+ color: var(--status-yellow); /* Yellow when online but no model loaded */
325
+ font-weight: bold;
326
+ }
327
+
328
+ .model-status-indicator.offline .model-select {
329
+ color: var(--status-red); /* Red when offline */
330
+ font-weight: bold;
331
+ }
332
+
333
+ /* Ensure options in the list are not affected by the select's styling */
334
+ .model-select option {
335
+ color: var(--text-primary);
336
+ font-weight: normal;
337
+ font-style: normal;
338
+ background-color: white;
339
+ padding: 0.5rem;
340
+ }
341
+
342
+ /* Highlight the selected option ONLY in the open list (if desired) */
343
+ .model-select option:checked {
344
+ background-color: #f0f0f0;
345
+ color: var(--text-primary);
346
+ }
347
+
348
+ .model-select option:checked.server-offline {
349
+ background-color: #f0f0f0;
350
+ color: var(--status-red);
351
+ }
352
+
353
+ .model-select:disabled {
354
+ background: #f5f5f5;
355
+ cursor: not-allowed;
356
+ opacity: 0.7;
357
+ }
358
+ /* When server is offline all buttons are disabled and appear muted */
359
+ button:disabled {
360
+ cursor: not-allowed;
361
+ opacity: 0.6;
362
+ background-color: #cccccc;
363
+ color: #666666;
292
364
  }
293
365
 
294
366
  .model-action-btn {
295
- display: flex;
367
+ display: none; /* Hide by default */
296
368
  align-items: center;
297
369
  justify-content: center;
298
- width: 24px;
299
- height: 24px;
370
+ width: 1.1em;
371
+ height: 1.1em;
300
372
  background: transparent;
301
373
  border: none;
302
374
  border-radius: 50%;
303
375
  cursor: pointer;
304
376
  transition: all var(--transition-fast);
305
- font-size: 0.9rem;
377
+ font-size: 1.5rem;
306
378
  color: #666;
307
- margin-left: 0.2rem;
379
+ margin-left: 0.1rem;
380
+ margin-right: 0.2rem;
381
+ line-height: 1;
308
382
  }
309
383
 
310
384
  .model-action-btn:hover {
@@ -312,6 +386,11 @@ body::before {
312
386
  color: #333;
313
387
  }
314
388
 
389
+ /* Unload button is only visible when a model is loaded */
390
+ .model-status-indicator.loaded .model-action-btn {
391
+ display: flex;
392
+ }
393
+
315
394
  .tab-content {
316
395
  display: none;
317
396
  padding: 2em;
@@ -327,15 +406,16 @@ body::before {
327
406
  .chat-container {
328
407
  display: flex;
329
408
  flex-direction: column;
330
- height: calc(100vh - 650px); /* Subtract space for navbar, title, wall of logos, etc */
331
409
  min-height: 300px;
332
- max-height: 1200px;
333
- max-width: 800px;
410
+ min-width: 300px;
411
+ max-width: 100%;
334
412
  width: 100%;
335
413
  margin: 0 auto;
336
414
  border: 1px solid #e0e0e0;
337
415
  border-radius: 8px;
338
416
  background: #fff;
417
+ resize: both;
418
+ overflow: auto;
339
419
  }
340
420
 
341
421
  .chat-history {
@@ -388,6 +468,21 @@ body::before {
388
468
  align-self: flex-start;
389
469
  }
390
470
 
471
+ .chat-message.system {
472
+ align-items: flex-start;
473
+ }
474
+
475
+ .chat-bubble.system {
476
+ background: linear-gradient(135deg, #f0f8f0 0%, #e8f5e8 100%);
477
+ color: #2d7f47;
478
+ border-bottom-left-radius: 4px;
479
+ align-self: flex-start;
480
+ border: 1px solid #c8e6c9;
481
+ font-style: normal;
482
+ font-weight: 500;
483
+ box-shadow: 0 1px 3px rgba(45, 127, 71, 0.1);
484
+ }
485
+
391
486
  /* Markdown styling within chat bubbles */
392
487
  .chat-bubble h1,
393
488
  .chat-bubble h2,
@@ -570,7 +665,7 @@ body::before {
570
665
  align-items: center;
571
666
  }
572
667
 
573
- .input-with-indicator input[type='text'] {
668
+ #chat-input {
574
669
  flex: 1;
575
670
  padding: 0.5em;
576
671
  border: 1px solid #ddd;
@@ -578,6 +673,16 @@ body::before {
578
673
  background: #fff;
579
674
  color: #222;
580
675
  margin: 0;
676
+ resize: vertical;
677
+ min-height: 40px;
678
+ font-family: inherit;
679
+ }
680
+
681
+ /* Update placeholder style */
682
+ #chat-input::placeholder {
683
+ color: #aaa;
684
+ opacity: 1;
685
+ font-style: italic;
581
686
  }
582
687
 
583
688
  #attachment-indicator {
@@ -849,6 +954,10 @@ body::before {
849
954
  background-color: var(--info-primary);
850
955
  }
851
956
 
957
+ .model-label.tool-calling {
958
+ background-color: #FFB74D;
959
+ }
960
+
852
961
  .model-label.other {
853
962
  background-color: var(--success-primary);
854
963
  }
@@ -1081,6 +1190,7 @@ body::before {
1081
1190
 
1082
1191
  #register-model-name:focus {
1083
1192
  border-color: #e6b800;
1193
+ box-shadow: 0 2px 12px rgba(230,184,0,0.25);
1084
1194
  }
1085
1195
 
1086
1196
  .form-input-wrapper {
@@ -1897,7 +2007,6 @@ body::before {
1897
2007
 
1898
2008
  .category-content {
1899
2009
  display: none;
1900
- padding: 0;
1901
2010
  }
1902
2011
 
1903
2012
  .category-content.expanded {
@@ -2166,43 +2275,4 @@ body::before {
2166
2275
  0 8px 25px rgba(200, 88, 108, 0.2),
2167
2276
  0 3px 10px rgba(0, 0, 0, 0.1),
2168
2277
  inset 0 1px 0 rgba(255, 255, 255, 0.9);
2169
- }
2170
-
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);
2182
- }
2183
-
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);
2188
- }
2189
-
2190
- .model-select:disabled {
2191
- background: #f5f5f5;
2192
- cursor: not-allowed;
2193
- opacity: 0.7;
2194
- }
2195
-
2196
- .model-select option {
2197
- padding: 0.5rem;
2198
- }
2199
-
2200
- .chat-input-row {
2201
- display: flex;
2202
- align-items: center;
2203
- gap: 0.5rem;
2204
- }
2205
-
2206
- .input-with-indicator {
2207
- flex: 1;
2208
- }
2278
+ }