abstractassistant 0.2.0__py3-none-any.whl → 0.2.6__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.
@@ -145,7 +145,7 @@ class TTSToggle(QPushButton):
145
145
  border-radius: 12px;
146
146
  font-size: 12px;
147
147
  color: {text_color};
148
- font-family: -apple-system, system-ui, sans-serif;
148
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
149
149
  font-weight: 600;
150
150
  }}
151
151
  QPushButton:hover {{
@@ -212,7 +212,7 @@ class FullVoiceToggle(QPushButton):
212
212
  border-radius: 12px;
213
213
  font-size: 12px;
214
214
  color: {text_color};
215
- font-family: -apple-system, system-ui, sans-serif;
215
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
216
216
  font-weight: 600;
217
217
  }}
218
218
  QPushButton:hover {{
@@ -228,28 +228,34 @@ class FullVoiceToggle(QPushButton):
228
228
 
229
229
  class LLMWorker(QThread):
230
230
  """Worker thread for LLM processing."""
231
-
231
+
232
232
  response_ready = pyqtSignal(str)
233
233
  error_occurred = pyqtSignal(str)
234
-
235
- def __init__(self, llm_manager, message, provider, model):
234
+
235
+ def __init__(self, llm_manager, message, provider, model, media=None):
236
236
  super().__init__()
237
237
  self.llm_manager = llm_manager
238
238
  self.message = message
239
239
  self.provider = provider
240
240
  self.model = model
241
-
241
+ self.media = media or []
242
+
242
243
  def run(self):
243
244
  """Run LLM processing in background."""
244
245
  try:
245
- # Use LLMManager session for context persistence
246
- response = self.llm_manager.generate_response(self.message, self.provider, self.model)
247
-
246
+ # Use LLMManager session for context persistence with optional media files
247
+ response = self.llm_manager.generate_response(
248
+ self.message,
249
+ self.provider,
250
+ self.model,
251
+ media=self.media if self.media else None
252
+ )
253
+
248
254
  # Response is already a string from LLMManager
249
255
  response_text = str(response)
250
-
256
+
251
257
  self.response_ready.emit(response_text)
252
-
258
+
253
259
  except Exception as e:
254
260
  print(f"❌ LLM Error: {e}")
255
261
  import traceback
@@ -278,6 +284,9 @@ class QtChatBubble(QWidget):
278
284
 
279
285
  # History dialog instance for toggle behavior
280
286
  self.history_dialog = None
287
+
288
+ # Attached files for media handling (AbstractCore 2.4.5+)
289
+ self.attached_files: List[str] = []
281
290
 
282
291
  # Initialize new manager classes
283
292
  self.provider_manager = None
@@ -357,7 +366,7 @@ class QtChatBubble(QWidget):
357
366
  font-size: 14px;
358
367
  font-weight: 600;
359
368
  color: rgba(255, 255, 255, 0.9);
360
- font-family: -apple-system, system-ui, sans-serif;
369
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
361
370
  }
362
371
  QPushButton:hover {
363
372
  background: rgba(255, 60, 60, 0.8);
@@ -389,7 +398,7 @@ class QtChatBubble(QWidget):
389
398
  border-radius: 11px;
390
399
  font-size: 10px;
391
400
  color: rgba(255, 255, 255, 0.7);
392
- font-family: -apple-system, system-ui, sans-serif;
401
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
393
402
  padding: 0 10px;
394
403
  }
395
404
  QPushButton:hover {
@@ -431,7 +440,7 @@ class QtChatBubble(QWidget):
431
440
  font-size: 10px;
432
441
  font-weight: 600;
433
442
  color: #ffffff;
434
- font-family: -apple-system, system-ui, sans-serif;
443
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
435
444
  }
436
445
  """)
437
446
  header_layout.addWidget(self.status_label)
@@ -442,7 +451,7 @@ class QtChatBubble(QWidget):
442
451
  self.input_container = QFrame()
443
452
  self.input_container.setStyleSheet("""
444
453
  QFrame {
445
- background: #1e1e1e;
454
+ background: #2a2a2a;
446
455
  border: 1px solid #404040;
447
456
  border-radius: 8px;
448
457
  padding: 4px;
@@ -455,14 +464,42 @@ class QtChatBubble(QWidget):
455
464
  # Input field with inline send button
456
465
  input_row = QHBoxLayout()
457
466
  input_row.setSpacing(4)
458
-
467
+
468
+ # File attachment button - modern paperclip icon
469
+ self.attach_button = QPushButton("📎")
470
+ self.attach_button.clicked.connect(self.attach_files)
471
+ self.attach_button.setFixedSize(36, 36)
472
+ self.attach_button.setToolTip("Attach files (images, PDFs, Office docs, etc.)")
473
+ self.attach_button.setStyleSheet("""
474
+ QPushButton {
475
+ background: rgba(255, 255, 255, 0.08);
476
+ border: 1px solid #404040;
477
+ border-radius: 18px;
478
+ font-size: 14px;
479
+ color: rgba(255, 255, 255, 0.7);
480
+ text-align: center;
481
+ padding: 0px;
482
+ }
483
+
484
+ QPushButton:hover {
485
+ background: rgba(255, 255, 255, 0.12);
486
+ border: 1px solid #0066cc;
487
+ color: rgba(255, 255, 255, 0.9);
488
+ }
489
+
490
+ QPushButton:pressed {
491
+ background: rgba(255, 255, 255, 0.06);
492
+ }
493
+ """)
494
+ input_row.addWidget(self.attach_button)
495
+
459
496
  # Text input - larger, primary focus
460
497
  self.input_text = QTextEdit()
461
498
  self.input_text.setPlaceholderText("Ask me anything... (Shift+Enter to send)")
462
499
  self.input_text.setMaximumHeight(100) # Increased to better use available space
463
500
  self.input_text.setMinimumHeight(70) # Increased to better use available space
464
501
  input_row.addWidget(self.input_text)
465
-
502
+
466
503
  # Send button - primary action with special styling
467
504
  self.send_button = QPushButton("→")
468
505
  self.send_button.clicked.connect(self.send_message)
@@ -478,16 +515,16 @@ class QtChatBubble(QWidget):
478
515
  text-align: center;
479
516
  padding: 0px;
480
517
  }
481
-
518
+
482
519
  QPushButton:hover {
483
520
  background: #0080ff;
484
521
  border: 1px solid #0099ff;
485
522
  }
486
-
523
+
487
524
  QPushButton:pressed {
488
525
  background: #0052a3;
489
526
  }
490
-
527
+
491
528
  QPushButton:disabled {
492
529
  background: #404040;
493
530
  color: #666666;
@@ -495,14 +532,30 @@ class QtChatBubble(QWidget):
495
532
  }
496
533
  """)
497
534
  input_row.addWidget(self.send_button)
498
-
535
+
499
536
  input_layout.addLayout(input_row)
537
+
538
+ # Attached files display area (initially hidden)
539
+ self.attached_files_container = QFrame()
540
+ self.attached_files_container.setStyleSheet("""
541
+ QFrame {
542
+ background: rgba(255, 255, 255, 0.04);
543
+ border: 1px solid #404040;
544
+ border-radius: 6px;
545
+ padding: 4px;
546
+ }
547
+ """)
548
+ self.attached_files_layout = QHBoxLayout(self.attached_files_container)
549
+ self.attached_files_layout.setContentsMargins(4, 4, 4, 4)
550
+ self.attached_files_layout.setSpacing(4)
551
+ self.attached_files_container.hide() # Initially hidden
552
+ input_layout.addWidget(self.attached_files_container)
500
553
  layout.addWidget(self.input_container)
501
554
 
502
555
  # Bottom controls - Cursor style (minimal, clean)
503
556
  controls_layout = QHBoxLayout()
504
- controls_layout.setContentsMargins(12, 3, 12, 4)
505
- controls_layout.setSpacing(8)
557
+ controls_layout.setContentsMargins(8, 2, 8, 2)
558
+ controls_layout.setSpacing(4)
506
559
 
507
560
  # Provider dropdown (rounded, clean)
508
561
  self.provider_combo = QComboBox()
@@ -514,10 +567,10 @@ class QtChatBubble(QWidget):
514
567
  background: rgba(255, 255, 255, 0.08);
515
568
  border: none;
516
569
  border-radius: 14px;
517
- padding: 0 12px;
570
+ padding: 0 8px;
518
571
  font-size: 11px;
519
572
  color: rgba(255, 255, 255, 0.9);
520
- font-family: -apple-system, system-ui, sans-serif;
573
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
521
574
  }
522
575
  QComboBox:hover {
523
576
  background: rgba(255, 255, 255, 0.12);
@@ -544,10 +597,10 @@ class QtChatBubble(QWidget):
544
597
  background: rgba(255, 255, 255, 0.08);
545
598
  border: none;
546
599
  border-radius: 14px;
547
- padding: 0 12px;
600
+ padding: 0 8px;
548
601
  font-size: 11px;
549
602
  color: rgba(255, 255, 255, 0.9);
550
- font-family: -apple-system, system-ui, sans-serif;
603
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
551
604
  }
552
605
  QComboBox:hover {
553
606
  background: rgba(255, 255, 255, 0.12);
@@ -568,7 +621,7 @@ class QtChatBubble(QWidget):
568
621
 
569
622
  # Token counter (minimal)
570
623
  self.token_label = QLabel("0 / 128k")
571
- self.token_label.setFixedHeight(36) # Increased by 30% (28 * 1.3 = 36.4 ≈ 36)
624
+ self.token_label.setFixedHeight(28) # Match provider and model dropdown height
572
625
  self.token_label.setMinimumWidth(104) # Increased by 30% (80 * 1.3 = 104)
573
626
  self.token_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
574
627
  self.token_label.setStyleSheet("""
@@ -578,7 +631,7 @@ class QtChatBubble(QWidget):
578
631
  border-radius: 14px;
579
632
  font-size: 12px;
580
633
  color: rgba(255, 255, 255, 0.6);
581
- font-family: -apple-system, system-ui, sans-serif;
634
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
582
635
  }
583
636
  """)
584
637
  controls_layout.addWidget(self.token_label)
@@ -612,21 +665,21 @@ class QtChatBubble(QWidget):
612
665
 
613
666
  /* Input Field - Modern Grey Design */
614
667
  QTextEdit {
615
- background: #1e1e1e;
668
+ background: #2a2a2a;
616
669
  border: 1px solid #404040;
617
670
  border-radius: 8px;
618
671
  padding: 12px 16px;
619
672
  font-size: 14px;
620
673
  font-weight: 400;
621
674
  color: #ffffff;
622
- font-family: system-ui, -apple-system, sans-serif;
675
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
623
676
  selection-background-color: #0066cc;
624
677
  line-height: 1.4;
625
678
  }
626
679
 
627
680
  QTextEdit:focus {
628
681
  border: 1px solid #0066cc;
629
- background: #252525;
682
+ background: #333333;
630
683
  }
631
684
 
632
685
  QTextEdit::placeholder {
@@ -642,7 +695,7 @@ class QtChatBubble(QWidget):
642
695
  font-size: 11px;
643
696
  font-weight: 500;
644
697
  color: #ffffff;
645
- font-family: system-ui, -apple-system, sans-serif;
698
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
646
699
  }
647
700
 
648
701
  QPushButton:hover {
@@ -670,7 +723,7 @@ class QtChatBubble(QWidget):
670
723
  font-size: 12px;
671
724
  font-weight: 400;
672
725
  color: #ffffff;
673
- font-family: system-ui, -apple-system, sans-serif;
726
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
674
727
  letter-spacing: 0.01em;
675
728
  }
676
729
 
@@ -700,7 +753,7 @@ class QtChatBubble(QWidget):
700
753
  selection-background-color: #4299e1;
701
754
  color: #e2e8f0;
702
755
  padding: 4px;
703
- font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", Roboto, sans-serif;
756
+ font-family: "SF Pro Text", "Helvetica Neue", "Segoe UI", Roboto, sans-serif;
704
757
  }
705
758
 
706
759
  QComboBox QAbstractItemView::item {
@@ -732,7 +785,7 @@ class QtChatBubble(QWidget):
732
785
  color: rgba(255, 255, 255, 0.8);
733
786
  font-size: 12px;
734
787
  font-weight: 500;
735
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
788
+ font-family: "SF Pro Text", "Helvetica Neue", 'Segoe UI', Roboto, sans-serif;
736
789
  letter-spacing: 0.3px;
737
790
  }
738
791
 
@@ -747,7 +800,7 @@ class QtChatBubble(QWidget):
747
800
  text-transform: uppercase;
748
801
  letter-spacing: 0.5px;
749
802
  color: #a6e3a1;
750
- font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", Roboto, sans-serif;
803
+ font-family: "SF Pro Text", "Helvetica Neue", "Segoe UI", Roboto, sans-serif;
751
804
  }
752
805
 
753
806
  QLabel#status_generating {
@@ -760,7 +813,7 @@ class QtChatBubble(QWidget):
760
813
  text-transform: uppercase;
761
814
  letter-spacing: 0.5px;
762
815
  color: #fab387;
763
- font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", Roboto, sans-serif;
816
+ font-family: "SF Pro Text", "Helvetica Neue", "Segoe UI", Roboto, sans-serif;
764
817
  }
765
818
 
766
819
  QLabel#status_error {
@@ -773,7 +826,7 @@ class QtChatBubble(QWidget):
773
826
  text-transform: uppercase;
774
827
  letter-spacing: 0.5px;
775
828
  color: #f38ba8;
776
- font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", Roboto, sans-serif;
829
+ font-family: "SF Pro Text", "Helvetica Neue", "Segoe UI", Roboto, sans-serif;
777
830
  }
778
831
 
779
832
  QLabel#token_label {
@@ -781,7 +834,7 @@ class QtChatBubble(QWidget):
781
834
  border: 1px solid #4a5568;
782
835
  border-radius: 8px;
783
836
  padding: 10px 12px;
784
- font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", Roboto, sans-serif;
837
+ font-family: "SF Pro Text", "Helvetica Neue", "Segoe UI", Roboto, sans-serif;
785
838
  font-size: 11px;
786
839
  font-weight: 500;
787
840
  color: #cbd5e0;
@@ -1023,22 +1076,136 @@ class QtChatBubble(QWidget):
1023
1076
  print(f"Model changed to: {self.current_model}")
1024
1077
 
1025
1078
 
1079
+ def attach_files(self):
1080
+ """Open file dialog to attach files (AbstractCore 2.4.5+ media handling)."""
1081
+ file_dialog = QFileDialog(self)
1082
+ file_dialog.setFileMode(QFileDialog.FileMode.ExistingFiles)
1083
+ file_dialog.setNameFilter(
1084
+ "All supported files (*.png *.jpg *.jpeg *.gif *.webp *.bmp *.tiff "
1085
+ "*.pdf *.docx *.xlsx *.pptx *.txt *.md *.csv *.tsv *.json);;"
1086
+ "Images (*.png *.jpg *.jpeg *.gif *.webp *.bmp *.tiff);;"
1087
+ "Documents (*.pdf *.docx *.xlsx *.pptx *.txt *.md);;"
1088
+ "Data files (*.csv *.tsv *.json);;"
1089
+ "All files (*.*)"
1090
+ )
1091
+
1092
+ if file_dialog.exec():
1093
+ selected_files = file_dialog.selectedFiles()
1094
+ for file_path in selected_files:
1095
+ if file_path not in self.attached_files:
1096
+ self.attached_files.append(file_path)
1097
+ if self.debug:
1098
+ print(f"📎 Attached file: {file_path}")
1099
+
1100
+ self.update_attached_files_display()
1101
+
1102
+ def update_attached_files_display(self):
1103
+ """Update the visual display of attached files."""
1104
+ # Clear existing file chips
1105
+ while self.attached_files_layout.count():
1106
+ child = self.attached_files_layout.takeAt(0)
1107
+ if child.widget():
1108
+ child.widget().deleteLater()
1109
+
1110
+ if not self.attached_files:
1111
+ self.attached_files_container.hide()
1112
+ return
1113
+
1114
+ # Show container and add file chips
1115
+ self.attached_files_container.show()
1116
+
1117
+ for file_path in self.attached_files:
1118
+ import os
1119
+ file_name = os.path.basename(file_path)
1120
+
1121
+ # Create file chip
1122
+ file_chip = QFrame()
1123
+ file_chip.setStyleSheet("""
1124
+ QFrame {
1125
+ background: rgba(0, 102, 204, 0.2);
1126
+ border: 1px solid rgba(0, 102, 204, 0.4);
1127
+ border-radius: 10px;
1128
+ padding: 2px 8px;
1129
+ }
1130
+ """)
1131
+
1132
+ chip_layout = QHBoxLayout(file_chip)
1133
+ chip_layout.setContentsMargins(4, 2, 4, 2)
1134
+ chip_layout.setSpacing(4)
1135
+
1136
+ # File icon based on type
1137
+ ext = os.path.splitext(file_name)[1].lower()
1138
+ if ext in ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.tiff']:
1139
+ icon = "🖼️"
1140
+ elif ext == '.pdf':
1141
+ icon = "📄"
1142
+ elif ext in ['.docx', '.doc']:
1143
+ icon = "📝"
1144
+ elif ext in ['.xlsx', '.xls']:
1145
+ icon = "📊"
1146
+ elif ext in ['.pptx', '.ppt']:
1147
+ icon = "📊"
1148
+ elif ext in ['.csv', '.tsv']:
1149
+ icon = "📋"
1150
+ else:
1151
+ icon = "📎"
1152
+
1153
+ file_label = QLabel(f"{icon} {file_name[:20]}{'...' if len(file_name) > 20 else ''}")
1154
+ file_label.setStyleSheet("background: transparent; border: none; color: rgba(255, 255, 255, 0.9); font-size: 10px;")
1155
+ chip_layout.addWidget(file_label)
1156
+
1157
+ # Remove button
1158
+ remove_btn = QPushButton("✕")
1159
+ remove_btn.setFixedSize(16, 16)
1160
+ remove_btn.setStyleSheet("""
1161
+ QPushButton {
1162
+ background: transparent;
1163
+ border: none;
1164
+ color: rgba(255, 255, 255, 0.6);
1165
+ font-size: 10px;
1166
+ padding: 0px;
1167
+ }
1168
+ QPushButton:hover {
1169
+ color: rgba(255, 60, 60, 0.9);
1170
+ }
1171
+ """)
1172
+ remove_btn.clicked.connect(lambda checked, fp=file_path: self.remove_attached_file(fp))
1173
+ chip_layout.addWidget(remove_btn)
1174
+
1175
+ self.attached_files_layout.addWidget(file_chip)
1176
+
1177
+ self.attached_files_layout.addStretch()
1178
+
1179
+ def remove_attached_file(self, file_path):
1180
+ """Remove a file from the attached files list."""
1181
+ if file_path in self.attached_files:
1182
+ self.attached_files.remove(file_path)
1183
+ if self.debug:
1184
+ print(f"🗑️ Removed attached file: {file_path}")
1185
+ self.update_attached_files_display()
1186
+
1026
1187
  def send_message(self):
1027
- """Send message to LLM."""
1188
+ """Send message to LLM with optional media attachments."""
1028
1189
  message = self.input_text.toPlainText().strip()
1029
1190
  if not message:
1030
1191
  return
1031
-
1192
+
1032
1193
  if self.debug:
1033
1194
  print(f"💬 Sending message: '{message[:50]}...' to {self.current_provider}/{self.current_model}")
1034
-
1195
+ if self.attached_files:
1196
+ print(f"📎 With {len(self.attached_files)} attached file(s)")
1197
+
1035
1198
  # 1. Clear input immediately
1036
1199
  self.input_text.clear()
1037
-
1038
- # 2. Don't manually add to history - let AbstractCore handle it via session.generate()
1039
- # The LLMManager will automatically add the message when generate_response is called
1040
-
1041
- # 3. Update UI for sending state
1200
+
1201
+ # 2. Capture attached files before clearing
1202
+ media_files = self.attached_files.copy()
1203
+
1204
+ # 3. Clear attached files display
1205
+ self.attached_files.clear()
1206
+ self.update_attached_files_display()
1207
+
1208
+ # 4. Update UI for sending state
1042
1209
  self.send_button.setEnabled(False)
1043
1210
  self.send_button.setText("⏳")
1044
1211
  self.status_label.setText("generating")
@@ -1056,21 +1223,27 @@ class QtChatBubble(QWidget):
1056
1223
  color: #fab387;
1057
1224
  }
1058
1225
  """)
1059
-
1226
+
1060
1227
  # Notify main app about status change (for icon animation)
1061
1228
  if self.status_callback:
1062
1229
  self.status_callback("generating")
1063
-
1230
+
1064
1231
  print("🔄 QtChatBubble: UI updated, creating worker thread...")
1065
-
1066
- # 4. Start worker thread to send request
1067
- self.worker = LLMWorker(self.llm_manager, message, self.current_provider, self.current_model)
1232
+
1233
+ # 5. Start worker thread to send request with optional media files
1234
+ self.worker = LLMWorker(
1235
+ self.llm_manager,
1236
+ message,
1237
+ self.current_provider,
1238
+ self.current_model,
1239
+ media=media_files if media_files else None
1240
+ )
1068
1241
  self.worker.response_ready.connect(self.on_response_ready)
1069
1242
  self.worker.error_occurred.connect(self.on_error_occurred)
1070
-
1243
+
1071
1244
  print("🔄 QtChatBubble: Starting worker thread...")
1072
1245
  self.worker.start()
1073
-
1246
+
1074
1247
  print("🔄 QtChatBubble: Worker thread started, hiding bubble...")
1075
1248
  # Hide bubble after sending (like the original design)
1076
1249
  QTimer.singleShot(500, self.hide)
@@ -1450,7 +1623,7 @@ class QtChatBubble(QWidget):
1450
1623
  font-size: 10px;
1451
1624
  font-weight: 600;
1452
1625
  color: #ffffff;
1453
- font-family: -apple-system, system-ui, sans-serif;
1626
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
1454
1627
  }}
1455
1628
  """)
1456
1629
 
@@ -1952,7 +2125,7 @@ class QtChatBubble(QWidget):
1952
2125
  border-radius: 11px;
1953
2126
  font-size: 10px;
1954
2127
  color: #ffffff;
1955
- font-family: -apple-system, system-ui, sans-serif;
2128
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
1956
2129
  padding: 0 10px;
1957
2130
  font-weight: 600;
1958
2131
  }
@@ -1969,7 +2142,7 @@ class QtChatBubble(QWidget):
1969
2142
  border-radius: 11px;
1970
2143
  font-size: 10px;
1971
2144
  color: rgba(255, 255, 255, 0.7);
1972
- font-family: -apple-system, system-ui, sans-serif;
2145
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
1973
2146
  padding: 0 10px;
1974
2147
  }
1975
2148
  QPushButton:hover {
@@ -107,7 +107,7 @@ class ToastWindow(QWidget):
107
107
  color: rgba(255, 255, 255, 0.9);
108
108
  background: transparent;
109
109
  border: none;
110
- font-family: -apple-system, system-ui, sans-serif;
110
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
111
111
  }
112
112
  """)
113
113
  header_layout.addWidget(title_label)
@@ -128,7 +128,7 @@ class ToastWindow(QWidget):
128
128
  border-radius: 12px;
129
129
  font-size: 11px;
130
130
  color: rgba(255, 255, 255, 0.7);
131
- font-family: -apple-system, system-ui, sans-serif;
131
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
132
132
  }
133
133
  QPushButton:hover {
134
134
  background: rgba(255, 255, 255, 0.15);
@@ -149,7 +149,7 @@ class ToastWindow(QWidget):
149
149
  border-radius: 12px;
150
150
  font-size: 11px;
151
151
  color: rgba(255, 255, 255, 0.7);
152
- font-family: -apple-system, system-ui, sans-serif;
152
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
153
153
  }
154
154
  QPushButton:hover {
155
155
  background: rgba(255, 255, 255, 0.15);
@@ -173,7 +173,7 @@ class ToastWindow(QWidget):
173
173
  border-radius: 12px;
174
174
  font-size: 11px;
175
175
  color: rgba(255, 255, 255, 0.7);
176
- font-family: -apple-system, system-ui, sans-serif;
176
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
177
177
  }
178
178
  QPushButton:hover {
179
179
  background: rgba(255, 255, 255, 0.15);
@@ -194,7 +194,7 @@ class ToastWindow(QWidget):
194
194
  border-radius: 12px;
195
195
  font-size: 11px;
196
196
  color: rgba(255, 255, 255, 0.7);
197
- font-family: -apple-system, system-ui, sans-serif;
197
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
198
198
  }
199
199
  QPushButton:hover {
200
200
  background: rgba(255, 255, 255, 0.15);
@@ -260,7 +260,7 @@ class ToastWindow(QWidget):
260
260
  color: rgba(255, 255, 255, 0.9);
261
261
  background: transparent;
262
262
  border: none;
263
- font-family: -apple-system, system-ui, sans-serif;
263
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
264
264
  font-size: 11px;
265
265
  font-weight: 500;
266
266
  }
@@ -274,7 +274,7 @@ class ToastWindow(QWidget):
274
274
  font-size: 10px;
275
275
  font-weight: 500;
276
276
  color: rgba(255, 255, 255, 0.8);
277
- font-family: -apple-system, system-ui, sans-serif;
277
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
278
278
  }
279
279
 
280
280
  QPushButton:hover {
@@ -295,7 +295,7 @@ class ToastWindow(QWidget):
295
295
  font-size: 13px;
296
296
  font-weight: 400;
297
297
  color: rgba(255, 255, 255, 0.95);
298
- font-family: -apple-system, system-ui, sans-serif;
298
+ font-family: "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
299
299
  selection-background-color: rgba(34, 197, 94, 0.3);
300
300
  line-height: 1.5;
301
301
  }
@@ -276,7 +276,7 @@ class UIStyles:
276
276
  padding: 8px;
277
277
  background: {COLORS['surface']};
278
278
  font-size: 13px;
279
- font-family: 'SF Pro Text', -apple-system, BlinkMacSystemFont, sans-serif;
279
+ font-family: 'SF Pro Text', "Helvetica Neue", BlinkMacSystemFont, sans-serif;
280
280
  }}
281
281
  QTextEdit:focus {{
282
282
  border-color: {COLORS['primary']};
@@ -291,7 +291,7 @@ class UIStyles:
291
291
  padding: 12px 16px;
292
292
  background: {COLORS['surface']};
293
293
  font-size: 14px;
294
- font-family: 'SF Pro Text', -apple-system, BlinkMacSystemFont, sans-serif;
294
+ font-family: 'SF Pro Text', "Helvetica Neue", BlinkMacSystemFont, sans-serif;
295
295
  max-height: 120px;
296
296
  min-height: 40px;
297
297
  }}
@@ -103,7 +103,7 @@ class MarkdownRenderer:
103
103
  """Get base CSS styles for markdown content."""
104
104
  return """
105
105
  .markdown-content {
106
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
106
+ font-family: "SF Pro Text", "Helvetica Neue", BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
107
107
  font-size: 14px; /* Base font size */
108
108
  line-height: 1.6;
109
109
  color: #e2e8f0;