supervertaler 1.9.153__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 supervertaler might be problematic. Click here for more details.

Files changed (85) hide show
  1. Supervertaler.py +47886 -0
  2. modules/__init__.py +10 -0
  3. modules/ai_actions.py +964 -0
  4. modules/ai_attachment_manager.py +343 -0
  5. modules/ai_file_viewer_dialog.py +210 -0
  6. modules/autofingers_engine.py +466 -0
  7. modules/cafetran_docx_handler.py +379 -0
  8. modules/config_manager.py +469 -0
  9. modules/database_manager.py +1878 -0
  10. modules/database_migrations.py +417 -0
  11. modules/dejavurtf_handler.py +779 -0
  12. modules/document_analyzer.py +427 -0
  13. modules/docx_handler.py +689 -0
  14. modules/encoding_repair.py +319 -0
  15. modules/encoding_repair_Qt.py +393 -0
  16. modules/encoding_repair_ui.py +481 -0
  17. modules/feature_manager.py +350 -0
  18. modules/figure_context_manager.py +340 -0
  19. modules/file_dialog_helper.py +148 -0
  20. modules/find_replace.py +164 -0
  21. modules/find_replace_qt.py +457 -0
  22. modules/glossary_manager.py +433 -0
  23. modules/image_extractor.py +188 -0
  24. modules/keyboard_shortcuts_widget.py +571 -0
  25. modules/llm_clients.py +1211 -0
  26. modules/llm_leaderboard.py +737 -0
  27. modules/llm_superbench_ui.py +1401 -0
  28. modules/local_llm_setup.py +1104 -0
  29. modules/model_update_dialog.py +381 -0
  30. modules/model_version_checker.py +373 -0
  31. modules/mqxliff_handler.py +638 -0
  32. modules/non_translatables_manager.py +743 -0
  33. modules/pdf_rescue_Qt.py +1822 -0
  34. modules/pdf_rescue_tkinter.py +909 -0
  35. modules/phrase_docx_handler.py +516 -0
  36. modules/project_home_panel.py +209 -0
  37. modules/prompt_assistant.py +357 -0
  38. modules/prompt_library.py +689 -0
  39. modules/prompt_library_migration.py +447 -0
  40. modules/quick_access_sidebar.py +282 -0
  41. modules/ribbon_widget.py +597 -0
  42. modules/sdlppx_handler.py +874 -0
  43. modules/setup_wizard.py +353 -0
  44. modules/shortcut_manager.py +932 -0
  45. modules/simple_segmenter.py +128 -0
  46. modules/spellcheck_manager.py +727 -0
  47. modules/statuses.py +207 -0
  48. modules/style_guide_manager.py +315 -0
  49. modules/superbench_ui.py +1319 -0
  50. modules/superbrowser.py +329 -0
  51. modules/supercleaner.py +600 -0
  52. modules/supercleaner_ui.py +444 -0
  53. modules/superdocs.py +19 -0
  54. modules/superdocs_viewer_qt.py +382 -0
  55. modules/superlookup.py +252 -0
  56. modules/tag_cleaner.py +260 -0
  57. modules/tag_manager.py +333 -0
  58. modules/term_extractor.py +270 -0
  59. modules/termbase_entry_editor.py +842 -0
  60. modules/termbase_import_export.py +488 -0
  61. modules/termbase_manager.py +1060 -0
  62. modules/termview_widget.py +1172 -0
  63. modules/theme_manager.py +499 -0
  64. modules/tm_editor_dialog.py +99 -0
  65. modules/tm_manager_qt.py +1280 -0
  66. modules/tm_metadata_manager.py +545 -0
  67. modules/tmx_editor.py +1461 -0
  68. modules/tmx_editor_qt.py +2784 -0
  69. modules/tmx_generator.py +284 -0
  70. modules/tracked_changes.py +900 -0
  71. modules/trados_docx_handler.py +430 -0
  72. modules/translation_memory.py +715 -0
  73. modules/translation_results_panel.py +2134 -0
  74. modules/translation_services.py +282 -0
  75. modules/unified_prompt_library.py +659 -0
  76. modules/unified_prompt_manager_qt.py +3951 -0
  77. modules/voice_commands.py +920 -0
  78. modules/voice_dictation.py +477 -0
  79. modules/voice_dictation_lite.py +249 -0
  80. supervertaler-1.9.153.dist-info/METADATA +896 -0
  81. supervertaler-1.9.153.dist-info/RECORD +85 -0
  82. supervertaler-1.9.153.dist-info/WHEEL +5 -0
  83. supervertaler-1.9.153.dist-info/entry_points.txt +2 -0
  84. supervertaler-1.9.153.dist-info/licenses/LICENSE +21 -0
  85. supervertaler-1.9.153.dist-info/top_level.txt +2 -0
@@ -0,0 +1,381 @@
1
+ """
2
+ Model Update Dialog for Supervertaler
3
+ ======================================
4
+
5
+ Dialog window that displays new LLM models detected by the version checker.
6
+ Allows users to easily add new models to their configuration.
7
+
8
+ Features:
9
+ - Shows new models grouped by provider
10
+ - Click to select models to add
11
+ - One-click "Add Selected" button
12
+ - Shows model details when available
13
+ """
14
+
15
+ from PyQt6.QtWidgets import (
16
+ QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
17
+ QCheckBox, QGroupBox, QScrollArea, QWidget, QTextEdit
18
+ )
19
+ from PyQt6.QtCore import Qt, pyqtSignal
20
+ from PyQt6.QtGui import QFont
21
+ from typing import Dict, List
22
+
23
+
24
+ class ModelUpdateDialog(QDialog):
25
+ """Dialog for showing and adding new models"""
26
+
27
+ models_selected = pyqtSignal(dict) # Emits selected models by provider
28
+
29
+ def __init__(self, results: Dict, parent=None):
30
+ """
31
+ Initialize the dialog
32
+
33
+ Args:
34
+ results: Results from ModelVersionChecker.check_all_providers()
35
+ parent: Parent widget
36
+ """
37
+ super().__init__(parent)
38
+ self.results = results
39
+ self.selected_models = {
40
+ "openai": [],
41
+ "claude": [],
42
+ "gemini": []
43
+ }
44
+
45
+ self.init_ui()
46
+
47
+ def init_ui(self):
48
+ """Initialize the user interface"""
49
+ self.setWindowTitle("🆕 New LLM Models Available")
50
+ self.setMinimumWidth(700)
51
+ self.setMinimumHeight(500)
52
+
53
+ layout = QVBoxLayout()
54
+
55
+ # Header
56
+ header_label = QLabel("New models have been detected from LLM providers!")
57
+ header_font = QFont()
58
+ header_font.setPointSize(12)
59
+ header_font.setBold(True)
60
+ header_label.setFont(header_font)
61
+ layout.addWidget(header_label)
62
+
63
+ info_label = QLabel(
64
+ "Select the models you want to add to your Supervertaler configuration.\n"
65
+ "These models will be added to the respective dropdowns in Settings."
66
+ )
67
+ info_label.setWordWrap(True)
68
+ layout.addWidget(info_label)
69
+
70
+ # Scroll area for model groups
71
+ scroll = QScrollArea()
72
+ scroll.setWidgetResizable(True)
73
+ scroll_widget = QWidget()
74
+ scroll_layout = QVBoxLayout(scroll_widget)
75
+
76
+ # Track if we have any new models
77
+ has_new_models = False
78
+
79
+ # OpenAI models
80
+ if self.results.get("openai", {}).get("new_models"):
81
+ has_new_models = True
82
+ openai_group = self._create_provider_group(
83
+ "OpenAI",
84
+ self.results["openai"]["new_models"],
85
+ "openai"
86
+ )
87
+ scroll_layout.addWidget(openai_group)
88
+
89
+ # Claude models
90
+ if self.results.get("claude", {}).get("new_models"):
91
+ has_new_models = True
92
+ claude_group = self._create_provider_group(
93
+ "Anthropic Claude",
94
+ self.results["claude"]["new_models"],
95
+ "claude"
96
+ )
97
+ scroll_layout.addWidget(claude_group)
98
+
99
+ # Gemini models
100
+ if self.results.get("gemini", {}).get("new_models"):
101
+ has_new_models = True
102
+ gemini_group = self._create_provider_group(
103
+ "Google Gemini",
104
+ self.results["gemini"]["new_models"],
105
+ "gemini"
106
+ )
107
+ scroll_layout.addWidget(gemini_group)
108
+
109
+ # Show errors if any
110
+ error_text = []
111
+ for provider in ["openai", "claude", "gemini"]:
112
+ error = self.results.get(provider, {}).get("error")
113
+ if error and "No API key" not in error:
114
+ error_text.append(f"❌ {provider.capitalize()}: {error}")
115
+
116
+ if error_text:
117
+ error_box = QGroupBox("⚠️ Errors")
118
+ error_layout = QVBoxLayout()
119
+ error_label = QLabel("\n".join(error_text))
120
+ error_label.setWordWrap(True)
121
+ error_label.setStyleSheet("color: #d32f2f;")
122
+ error_layout.addWidget(error_label)
123
+ error_box.setLayout(error_layout)
124
+ scroll_layout.addWidget(error_box)
125
+
126
+ scroll_layout.addStretch()
127
+ scroll.setWidget(scroll_widget)
128
+ layout.addWidget(scroll)
129
+
130
+ # Buttons
131
+ button_layout = QHBoxLayout()
132
+
133
+ if has_new_models:
134
+ select_all_btn = QPushButton("Select All")
135
+ select_all_btn.clicked.connect(self._select_all)
136
+ button_layout.addWidget(select_all_btn)
137
+
138
+ deselect_all_btn = QPushButton("Deselect All")
139
+ deselect_all_btn.clicked.connect(self._deselect_all)
140
+ button_layout.addWidget(deselect_all_btn)
141
+
142
+ button_layout.addStretch()
143
+
144
+ if has_new_models:
145
+ add_btn = QPushButton("Add Selected Models")
146
+ add_btn.setStyleSheet("""
147
+ QPushButton {
148
+ background-color: #4CAF50;
149
+ color: white;
150
+ font-weight: bold;
151
+ padding: 8px 16px;
152
+ border-radius: 4px;
153
+ }
154
+ QPushButton:hover {
155
+ background-color: #45a049;
156
+ }
157
+ """)
158
+ add_btn.clicked.connect(self._add_selected)
159
+ button_layout.addWidget(add_btn)
160
+
161
+ close_btn = QPushButton("Close")
162
+ close_btn.clicked.connect(self.reject)
163
+ button_layout.addWidget(close_btn)
164
+
165
+ layout.addLayout(button_layout)
166
+
167
+ self.setLayout(layout)
168
+
169
+ def _create_provider_group(self, provider_name: str, models: List[str], provider_key: str) -> QGroupBox:
170
+ """
171
+ Create a group box for a provider's models
172
+
173
+ Args:
174
+ provider_name: Display name (e.g., "OpenAI")
175
+ models: List of model IDs
176
+ provider_key: Key for tracking selections ("openai", "claude", "gemini")
177
+
178
+ Returns:
179
+ QGroupBox with checkboxes for each model
180
+ """
181
+ group = QGroupBox(f"🤖 {provider_name} ({len(models)} new model{'s' if len(models) != 1 else ''})")
182
+ layout = QVBoxLayout()
183
+
184
+ # Create checkbox for each model
185
+ for model in models:
186
+ checkbox = CheckmarkCheckBox(model)
187
+ checkbox.setChecked(True) # Pre-select all by default
188
+ checkbox.stateChanged.connect(
189
+ lambda state, m=model, p=provider_key: self._on_model_toggled(p, m, state)
190
+ )
191
+ layout.addWidget(checkbox)
192
+
193
+ # Add model to selected by default
194
+ if model not in self.selected_models[provider_key]:
195
+ self.selected_models[provider_key].append(model)
196
+
197
+ group.setLayout(layout)
198
+ return group
199
+
200
+ def _on_model_toggled(self, provider: str, model: str, state: int):
201
+ """Handle checkbox toggle"""
202
+ if state == Qt.CheckState.Checked.value:
203
+ if model not in self.selected_models[provider]:
204
+ self.selected_models[provider].append(model)
205
+ else:
206
+ if model in self.selected_models[provider]:
207
+ self.selected_models[provider].remove(model)
208
+
209
+ def _select_all(self):
210
+ """Select all checkboxes"""
211
+ for checkbox in self.findChildren(QCheckBox):
212
+ checkbox.setChecked(True)
213
+
214
+ def _deselect_all(self):
215
+ """Deselect all checkboxes"""
216
+ for checkbox in self.findChildren(QCheckBox):
217
+ checkbox.setChecked(False)
218
+
219
+ def _add_selected(self):
220
+ """Emit signal with selected models and close dialog"""
221
+ # Only include providers with selected models
222
+ selected = {
223
+ provider: models
224
+ for provider, models in self.selected_models.items()
225
+ if models
226
+ }
227
+
228
+ if selected:
229
+ self.models_selected.emit(selected)
230
+ self.accept()
231
+ else:
232
+ # No models selected
233
+ self.reject()
234
+
235
+ def get_selected_models(self) -> Dict[str, List[str]]:
236
+ """Get the selected models"""
237
+ return {
238
+ provider: models
239
+ for provider, models in self.selected_models.items()
240
+ if models
241
+ }
242
+
243
+
244
+ class NoNewModelsDialog(QDialog):
245
+ """Simple dialog shown when no new models are found"""
246
+
247
+ def __init__(self, last_check: str = None, parent=None):
248
+ """
249
+ Initialize dialog
250
+
251
+ Args:
252
+ last_check: ISO format timestamp of last check
253
+ parent: Parent widget
254
+ """
255
+ super().__init__(parent)
256
+ self.last_check = last_check
257
+ self.init_ui()
258
+
259
+ def init_ui(self):
260
+ """Initialize UI"""
261
+ self.setWindowTitle("✅ No New Models")
262
+ self.setMinimumWidth(400)
263
+
264
+ layout = QVBoxLayout()
265
+
266
+ # Icon and message
267
+ label = QLabel("✅ No new LLM models detected")
268
+ font = QFont()
269
+ font.setPointSize(12)
270
+ font.setBold(True)
271
+ label.setFont(font)
272
+ layout.addWidget(label)
273
+
274
+ info_label = QLabel(
275
+ "Your Supervertaler is up to date with the latest models from:\n"
276
+ "• OpenAI (GPT-4, GPT-5, o1, o3)\n"
277
+ "• Anthropic (Claude Sonnet, Haiku, Opus)\n"
278
+ "• Google (Gemini 2.5, Gemini 3)"
279
+ )
280
+ info_label.setWordWrap(True)
281
+ layout.addWidget(info_label)
282
+
283
+ if self.last_check:
284
+ from datetime import datetime
285
+ try:
286
+ check_time = datetime.fromisoformat(self.last_check)
287
+ time_str = check_time.strftime("%Y-%m-%d %H:%M")
288
+ last_check_label = QLabel(f"Last checked: {time_str}")
289
+ last_check_label.setStyleSheet("color: #666; font-style: italic;")
290
+ layout.addWidget(last_check_label)
291
+ except:
292
+ pass
293
+
294
+ layout.addStretch()
295
+
296
+ # Close button
297
+ close_btn = QPushButton("OK")
298
+ close_btn.clicked.connect(self.accept)
299
+ layout.addWidget(close_btn)
300
+
301
+ self.setLayout(layout)
302
+
303
+
304
+ class CheckmarkCheckBox(QCheckBox):
305
+ """Custom checkbox with green background and white checkmark when checked"""
306
+
307
+ def __init__(self, text="", parent=None):
308
+ super().__init__(text, parent)
309
+ self.setCheckable(True)
310
+ self.setEnabled(True)
311
+ self.setStyleSheet("""
312
+ QCheckBox {
313
+ font-size: 9pt;
314
+ spacing: 6px;
315
+ }
316
+ QCheckBox::indicator {
317
+ width: 16px;
318
+ height: 16px;
319
+ border: 2px solid #999;
320
+ border-radius: 3px;
321
+ background-color: white;
322
+ }
323
+ QCheckBox::indicator:checked {
324
+ background-color: #4CAF50;
325
+ border-color: #4CAF50;
326
+ }
327
+ QCheckBox::indicator:hover {
328
+ border-color: #666;
329
+ }
330
+ QCheckBox::indicator:checked:hover {
331
+ background-color: #45a049;
332
+ border-color: #45a049;
333
+ }
334
+ """)
335
+
336
+ def paintEvent(self, event):
337
+ """Override paint event to draw white checkmark when checked"""
338
+ super().paintEvent(event)
339
+
340
+ if self.isChecked():
341
+ from PyQt6.QtWidgets import QStyleOptionButton
342
+ from PyQt6.QtGui import QPainter, QPen, QColor
343
+ from PyQt6.QtCore import QPointF, Qt
344
+
345
+ opt = QStyleOptionButton()
346
+ self.initStyleOption(opt)
347
+ indicator_rect = self.style().subElementRect(
348
+ self.style().SubElement.SE_CheckBoxIndicator,
349
+ opt,
350
+ self
351
+ )
352
+
353
+ if indicator_rect.isValid():
354
+ painter = QPainter(self)
355
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
356
+ pen_width = max(2.0, min(indicator_rect.width(), indicator_rect.height()) * 0.12)
357
+ painter.setPen(QPen(QColor(255, 255, 255), pen_width, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap, Qt.PenJoinStyle.RoundJoin))
358
+ painter.setBrush(QColor(255, 255, 255))
359
+
360
+ x = indicator_rect.x()
361
+ y = indicator_rect.y()
362
+ w = indicator_rect.width()
363
+ h = indicator_rect.height()
364
+
365
+ padding = min(w, h) * 0.15
366
+ x += padding
367
+ y += padding
368
+ w -= padding * 2
369
+ h -= padding * 2
370
+
371
+ check_x1 = x + w * 0.10
372
+ check_y1 = y + h * 0.50
373
+ check_x2 = x + w * 0.35
374
+ check_y2 = y + h * 0.70
375
+ check_x3 = x + w * 0.90
376
+ check_y3 = y + h * 0.25
377
+
378
+ painter.drawLine(QPointF(check_x2, check_y2), QPointF(check_x3, check_y3))
379
+ painter.drawLine(QPointF(check_x1, check_y1), QPointF(check_x2, check_y2))
380
+
381
+ painter.end()