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,444 @@
1
+ """
2
+ Supercleaner UI Module for Supervertaler
3
+ ========================================
4
+
5
+ Interactive UI for document cleaning with selectable operations.
6
+ Inspired by TransTools Document Cleaner, Unbreaker, and CodeZapper.
7
+
8
+ Author: Michael Beijer / Supervertaler
9
+ """
10
+
11
+ from PyQt6.QtWidgets import (
12
+ QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QCheckBox,
13
+ QPushButton, QLabel, QFileDialog, QMessageBox, QTextEdit, QScrollArea
14
+ )
15
+ from PyQt6.QtCore import Qt
16
+ from PyQt6.QtGui import QFont
17
+ import os
18
+ from typing import Optional, Dict
19
+
20
+
21
+ class SupercleanerUI(QWidget):
22
+ """Interactive UI for document cleaning with selectable operations"""
23
+
24
+ def __init__(self, parent=None):
25
+ super().__init__(parent)
26
+ self.parent_window = parent
27
+ self.init_ui()
28
+
29
+ def init_ui(self):
30
+ """Initialize the user interface"""
31
+ main_layout = QVBoxLayout(self)
32
+ main_layout.setContentsMargins(10, 10, 10, 10)
33
+ main_layout.setSpacing(10)
34
+
35
+ # === FILE SELECTION ===
36
+ file_group = QGroupBox("📄 Document Selection")
37
+ file_layout = QVBoxLayout()
38
+
39
+ # File path display and browse button
40
+ file_row = QHBoxLayout()
41
+ self.file_label = QLabel("No file selected")
42
+ self.file_label.setStyleSheet("color: #666; padding: 5px; background-color: #f0f0f0; border-radius: 3px;")
43
+ browse_btn = QPushButton("📂 Browse...")
44
+ browse_btn.clicked.connect(self.browse_file)
45
+ browse_btn.setMaximumWidth(150)
46
+
47
+ file_row.addWidget(self.file_label, 1)
48
+ file_row.addWidget(browse_btn, 0)
49
+ file_layout.addLayout(file_row)
50
+
51
+ file_group.setLayout(file_layout)
52
+ main_layout.addWidget(file_group)
53
+
54
+ # === CLEANING OPTIONS ===
55
+ # Create scrollable area for options
56
+ scroll_area = QScrollArea()
57
+ scroll_area.setWidgetResizable(True)
58
+ scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
59
+
60
+ options_container = QWidget()
61
+ options_layout = QVBoxLayout(options_container)
62
+
63
+ # Document Cleaner Operations
64
+ cleaner_group = QGroupBox("🧹 Document Cleaner Operations")
65
+ cleaner_layout = QVBoxLayout()
66
+
67
+ self.cb_remove_shading = CheckmarkCheckBox("Remove text shading")
68
+ self.cb_remove_highlighting = CheckmarkCheckBox("Remove text highlighting")
69
+ self.cb_font_color_automatic = CheckmarkCheckBox("Change font color from Black to Automatic")
70
+ self.cb_normalize_font_color = CheckmarkCheckBox("Normalize font color in each paragraph")
71
+ self.cb_normalize_font_size = CheckmarkCheckBox("Normalize font size in each paragraph")
72
+ self.cb_normalize_font = CheckmarkCheckBox("Normalize font in each paragraph")
73
+ self.cb_set_default_spacing = CheckmarkCheckBox("Set default text spacing")
74
+ self.cb_remove_hyphens = CheckmarkCheckBox("Remove manual hyphens")
75
+ self.cb_replace_symbols = CheckmarkCheckBox("Replace special symbols (non-breaking spaces, ellipsis)")
76
+ self.cb_replace_symbols.setToolTip("Replaces non-breaking spaces and ellipsis characters that can cause TM matching issues")
77
+ self.cb_simplify_quotes = CheckmarkCheckBox("Simplify quotes & dashes to ASCII (optional)")
78
+ self.cb_simplify_quotes.setToolTip("Convert curly quotes ("") and em-dashes (—) to straight quotes (\") and hyphens (-)")
79
+ self.cb_remove_styles = CheckmarkCheckBox("Remove character styles (aggressive)")
80
+
81
+ # Set defaults (checked)
82
+ for cb in [self.cb_remove_shading, self.cb_remove_highlighting,
83
+ self.cb_font_color_automatic, self.cb_normalize_font_color,
84
+ self.cb_normalize_font_size, self.cb_normalize_font,
85
+ self.cb_set_default_spacing, self.cb_remove_hyphens,
86
+ self.cb_replace_symbols]:
87
+ cb.setChecked(True)
88
+ cleaner_layout.addWidget(cb)
89
+
90
+ # Optional operations unchecked by default
91
+ cleaner_layout.addWidget(self.cb_simplify_quotes) # New optional operation
92
+ cleaner_layout.addWidget(self.cb_remove_styles)
93
+
94
+ cleaner_group.setLayout(cleaner_layout)
95
+ options_layout.addWidget(cleaner_group)
96
+
97
+ # Unbreaker Operations
98
+ unbreaker_group = QGroupBox("🔗 Unbreaker Operations")
99
+ unbreaker_layout = QVBoxLayout()
100
+
101
+ self.cb_fix_line_breaks = CheckmarkCheckBox("Fix incorrect line breaks (within paragraphs)")
102
+ self.cb_join_sentences = CheckmarkCheckBox("Join broken sentences (across paragraphs) - EXPERIMENTAL")
103
+
104
+ self.cb_fix_line_breaks.setChecked(True)
105
+ # join_sentences disabled by default due to word spacing issues
106
+ self.cb_join_sentences.setChecked(False)
107
+ self.cb_join_sentences.setStyleSheet("color: #d32f2f;")
108
+ self.cb_join_sentences.setToolTip(
109
+ "⚠️ WARNING: This feature is experimental and may cause words to stick together.\n"
110
+ "Only enable if you need to fix severely broken paragraph structure."
111
+ )
112
+
113
+ unbreaker_layout.addWidget(self.cb_fix_line_breaks)
114
+ unbreaker_layout.addWidget(self.cb_join_sentences)
115
+
116
+ unbreaker_group.setLayout(unbreaker_layout)
117
+ options_layout.addWidget(unbreaker_group)
118
+
119
+ # Space Removal Operations
120
+ spaces_group = QGroupBox("␣ Remove Excessive Spaces")
121
+ spaces_layout = QVBoxLayout()
122
+
123
+ self.cb_remove_spaces = CheckmarkCheckBox("Remove excessive spaces (2+ spaces become 1)")
124
+ self.cb_remove_spaces.setChecked(True)
125
+
126
+ spaces_layout.addWidget(self.cb_remove_spaces)
127
+ spaces_group.setLayout(spaces_layout)
128
+ options_layout.addWidget(spaces_group)
129
+
130
+ # Quick presets
131
+ presets_layout = QHBoxLayout()
132
+ quick_clean_btn = QPushButton("✨ Quick Clean (Recommended)")
133
+ quick_clean_btn.clicked.connect(self.apply_quick_clean_preset)
134
+ quick_clean_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold; padding: 5px;")
135
+
136
+ aggressive_btn = QPushButton("⚡ Aggressive Clean")
137
+ aggressive_btn.clicked.connect(self.apply_aggressive_preset)
138
+ aggressive_btn.setStyleSheet("background-color: #FF9800; color: white; font-weight: bold; padding: 5px;")
139
+
140
+ clear_all_btn = QPushButton("❌ Clear All")
141
+ clear_all_btn.clicked.connect(self.clear_all_options)
142
+
143
+ presets_layout.addWidget(quick_clean_btn)
144
+ presets_layout.addWidget(aggressive_btn)
145
+ presets_layout.addWidget(clear_all_btn)
146
+ options_layout.addLayout(presets_layout)
147
+
148
+ scroll_area.setWidget(options_container)
149
+ main_layout.addWidget(scroll_area, 1)
150
+
151
+ # === CLEAN BUTTON ===
152
+ clean_btn = QPushButton("🧹 Clean Document")
153
+ clean_btn.clicked.connect(self.clean_document)
154
+ clean_btn.setStyleSheet(
155
+ "background-color: #2196F3; color: white; font-weight: bold; "
156
+ "font-size: 14pt; padding: 10px; border-radius: 5px;"
157
+ )
158
+ main_layout.addWidget(clean_btn)
159
+
160
+ # === RESULTS LOG ===
161
+ log_group = QGroupBox("📊 Cleaning Results")
162
+ log_layout = QVBoxLayout()
163
+
164
+ self.log_text = QTextEdit()
165
+ self.log_text.setReadOnly(True)
166
+ self.log_text.setMaximumHeight(150)
167
+ self.log_text.setStyleSheet("background-color: #f5f5f5; font-family: Consolas, monospace;")
168
+
169
+ log_layout.addWidget(self.log_text)
170
+ log_group.setLayout(log_layout)
171
+ main_layout.addWidget(log_group)
172
+
173
+ # Initial message
174
+ self.log("👋 Welcome to Supercleaner! Select a DOCX file and choose cleaning operations.")
175
+ self.log("💡 Tip: Use 'Quick Clean' preset for most OCR/PDF documents.")
176
+
177
+ def browse_file(self):
178
+ """Open file browser to select DOCX file"""
179
+ file_path, _ = QFileDialog.getOpenFileName(
180
+ self,
181
+ "Select DOCX File to Clean",
182
+ "",
183
+ "Word Documents (*.docx);;All Files (*.*)"
184
+ )
185
+
186
+ if file_path:
187
+ self.file_label.setText(file_path)
188
+ self.log(f"📄 Selected: {os.path.basename(file_path)}")
189
+
190
+ def apply_quick_clean_preset(self):
191
+ """Apply recommended quick clean settings"""
192
+ # Document Cleaner
193
+ self.cb_remove_shading.setChecked(True)
194
+ self.cb_remove_highlighting.setChecked(True)
195
+ self.cb_font_color_automatic.setChecked(True)
196
+ self.cb_normalize_font_color.setChecked(True)
197
+ self.cb_normalize_font_size.setChecked(True)
198
+ self.cb_normalize_font.setChecked(True)
199
+ self.cb_set_default_spacing.setChecked(True)
200
+ self.cb_remove_hyphens.setChecked(True)
201
+ self.cb_replace_symbols.setChecked(True)
202
+ self.cb_simplify_quotes.setChecked(False) # Optional - preserves curly quotes by default
203
+ self.cb_remove_styles.setChecked(False)
204
+
205
+ # Unbreaker
206
+ self.cb_fix_line_breaks.setChecked(True)
207
+ self.cb_join_sentences.setChecked(False) # Disabled due to spacing issues
208
+
209
+ # Spaces
210
+ self.cb_remove_spaces.setChecked(True)
211
+
212
+ self.log("✨ Applied 'Quick Clean' preset (recommended for OCR/PDF documents)")
213
+
214
+ def apply_aggressive_preset(self):
215
+ """Apply aggressive cleaning settings"""
216
+ # Check everything
217
+ for cb in [self.cb_remove_shading, self.cb_remove_highlighting,
218
+ self.cb_font_color_automatic, self.cb_normalize_font_color,
219
+ self.cb_normalize_font_size, self.cb_normalize_font,
220
+ self.cb_set_default_spacing, self.cb_remove_hyphens,
221
+ self.cb_replace_symbols, self.cb_simplify_quotes,
222
+ self.cb_remove_styles, self.cb_fix_line_breaks,
223
+ self.cb_remove_spaces]:
224
+ cb.setChecked(True)
225
+
226
+ # Keep sentence joining disabled (known issues)
227
+ self.cb_join_sentences.setChecked(False)
228
+
229
+ self.log("⚡ Applied 'Aggressive Clean' preset")
230
+ self.log("⚠️ Note: 'Join broken sentences' remains disabled due to known spacing issues")
231
+
232
+ def clear_all_options(self):
233
+ """Clear all cleaning options"""
234
+ for cb in [self.cb_remove_shading, self.cb_remove_highlighting,
235
+ self.cb_font_color_automatic, self.cb_normalize_font_color,
236
+ self.cb_normalize_font_size, self.cb_normalize_font,
237
+ self.cb_set_default_spacing, self.cb_remove_hyphens,
238
+ self.cb_replace_symbols, self.cb_simplify_quotes,
239
+ self.cb_remove_styles, self.cb_fix_line_breaks,
240
+ self.cb_join_sentences, self.cb_remove_spaces]:
241
+ cb.setChecked(False)
242
+
243
+ self.log("❌ Cleared all cleaning options")
244
+
245
+ def get_selected_operations(self) -> Dict[str, bool]:
246
+ """Get dictionary of selected cleaning operations"""
247
+ return {
248
+ 'remove_text_shading': self.cb_remove_shading.isChecked(),
249
+ 'remove_highlighting': self.cb_remove_highlighting.isChecked(),
250
+ 'font_color_to_automatic': self.cb_font_color_automatic.isChecked(),
251
+ 'normalize_font_color': self.cb_normalize_font_color.isChecked(),
252
+ 'normalize_font_size': self.cb_normalize_font_size.isChecked(),
253
+ 'normalize_font': self.cb_normalize_font.isChecked(),
254
+ 'set_default_spacing': self.cb_set_default_spacing.isChecked(),
255
+ 'remove_manual_hyphens': self.cb_remove_hyphens.isChecked(),
256
+ 'replace_special_symbols': self.cb_replace_symbols.isChecked(),
257
+ 'simplify_quotes_and_dashes': self.cb_simplify_quotes.isChecked(),
258
+ 'remove_character_styles': self.cb_remove_styles.isChecked(),
259
+ 'fix_line_breaks': self.cb_fix_line_breaks.isChecked(),
260
+ 'join_broken_sentences': self.cb_join_sentences.isChecked(),
261
+ 'remove_excessive_spaces': self.cb_remove_spaces.isChecked(),
262
+ 'accept_tracked_changes': False, # Not yet implemented
263
+ }
264
+
265
+ def clean_document(self):
266
+ """Perform document cleaning with selected operations"""
267
+ # Validate file selection
268
+ file_path = self.file_label.text()
269
+ if file_path == "No file selected" or not os.path.exists(file_path):
270
+ QMessageBox.warning(
271
+ self,
272
+ "No File Selected",
273
+ "Please select a DOCX file to clean."
274
+ )
275
+ return
276
+
277
+ # Get selected operations
278
+ operations = self.get_selected_operations()
279
+
280
+ # Check if any operations are selected
281
+ if not any(operations.values()):
282
+ QMessageBox.warning(
283
+ self,
284
+ "No Operations Selected",
285
+ "Please select at least one cleaning operation."
286
+ )
287
+ return
288
+
289
+ # Ask for output file
290
+ default_output = file_path.replace('.docx', '_cleaned.docx')
291
+ output_path, _ = QFileDialog.getSaveFileName(
292
+ self,
293
+ "Save Cleaned Document As",
294
+ default_output,
295
+ "Word Documents (*.docx);;All Files (*.*)"
296
+ )
297
+
298
+ if not output_path:
299
+ return
300
+
301
+ # Perform cleaning
302
+ self.log("━" * 60)
303
+ self.log(f"🧹 Starting Supercleaner...")
304
+ self.log(f"📄 Input: {os.path.basename(file_path)}")
305
+ self.log(f"💾 Output: {os.path.basename(output_path)}")
306
+ self.log("")
307
+
308
+ try:
309
+ from modules.supercleaner import DocumentCleaner
310
+
311
+ cleaner = DocumentCleaner()
312
+ stats = cleaner.clean_document(file_path, output_path, operations)
313
+
314
+ self.log("✅ Cleaning Complete!")
315
+ self.log(f" 📊 Paragraphs processed: {stats['paragraphs_processed']}")
316
+ self.log(f" 🔧 Total changes made: {stats['changes_made']}")
317
+ self.log("")
318
+
319
+ if stats.get('operations'):
320
+ self.log(" 📝 Operations performed:")
321
+ for op in stats['operations']:
322
+ self.log(f" • {op}")
323
+
324
+ self.log("")
325
+ self.log(f"✅ Saved to: {output_path}")
326
+ self.log("━" * 60)
327
+
328
+ QMessageBox.information(
329
+ self,
330
+ "Success",
331
+ f"Document cleaned successfully!\n\n"
332
+ f"Paragraphs processed: {stats['paragraphs_processed']}\n"
333
+ f"Changes made: {stats['changes_made']}\n\n"
334
+ f"Saved to:\n{output_path}"
335
+ )
336
+
337
+ except Exception as e:
338
+ self.log(f"❌ Error: {str(e)}")
339
+ self.log("━" * 60)
340
+ QMessageBox.critical(
341
+ self,
342
+ "Error",
343
+ f"Failed to clean document:\n\n{str(e)}"
344
+ )
345
+
346
+ def log(self, message: str):
347
+ """Add message to log"""
348
+ self.log_text.append(message)
349
+ # Scroll to bottom
350
+ scrollbar = self.log_text.verticalScrollBar()
351
+ scrollbar.setValue(scrollbar.maximum())
352
+
353
+
354
+ class CheckmarkCheckBox(QCheckBox):
355
+ """Custom checkbox with green background and white checkmark when checked"""
356
+
357
+ def __init__(self, text="", parent=None):
358
+ super().__init__(text, parent)
359
+ self.setCheckable(True)
360
+ self.setEnabled(True)
361
+ self.setStyleSheet("""
362
+ QCheckBox {
363
+ font-size: 9pt;
364
+ spacing: 6px;
365
+ }
366
+ QCheckBox::indicator {
367
+ width: 16px;
368
+ height: 16px;
369
+ border: 2px solid #999;
370
+ border-radius: 3px;
371
+ background-color: white;
372
+ }
373
+ QCheckBox::indicator:checked {
374
+ background-color: #4CAF50;
375
+ border-color: #4CAF50;
376
+ }
377
+ QCheckBox::indicator:hover {
378
+ border-color: #666;
379
+ }
380
+ QCheckBox::indicator:checked:hover {
381
+ background-color: #45a049;
382
+ border-color: #45a049;
383
+ }
384
+ """)
385
+
386
+ def paintEvent(self, event):
387
+ """Override paint event to draw white checkmark when checked"""
388
+ super().paintEvent(event)
389
+
390
+ if self.isChecked():
391
+ from PyQt6.QtWidgets import QStyleOptionButton
392
+ from PyQt6.QtGui import QPainter, QPen, QColor
393
+ from PyQt6.QtCore import QPointF, Qt
394
+
395
+ opt = QStyleOptionButton()
396
+ self.initStyleOption(opt)
397
+ indicator_rect = self.style().subElementRect(
398
+ self.style().SubElement.SE_CheckBoxIndicator,
399
+ opt,
400
+ self
401
+ )
402
+
403
+ if indicator_rect.isValid():
404
+ painter = QPainter(self)
405
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
406
+ pen_width = max(2.0, min(indicator_rect.width(), indicator_rect.height()) * 0.12)
407
+ painter.setPen(QPen(QColor(255, 255, 255), pen_width, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap, Qt.PenJoinStyle.RoundJoin))
408
+ painter.setBrush(QColor(255, 255, 255))
409
+
410
+ x = indicator_rect.x()
411
+ y = indicator_rect.y()
412
+ w = indicator_rect.width()
413
+ h = indicator_rect.height()
414
+
415
+ padding = min(w, h) * 0.15
416
+ x += padding
417
+ y += padding
418
+ w -= padding * 2
419
+ h -= padding * 2
420
+
421
+ check_x1 = x + w * 0.10
422
+ check_y1 = y + h * 0.50
423
+ check_x2 = x + w * 0.35
424
+ check_y2 = y + h * 0.70
425
+ check_x3 = x + w * 0.90
426
+ check_y3 = y + h * 0.25
427
+
428
+ painter.drawLine(QPointF(check_x2, check_y2), QPointF(check_x3, check_y3))
429
+ painter.drawLine(QPointF(check_x1, check_y1), QPointF(check_x2, check_y2))
430
+
431
+ painter.end()
432
+
433
+
434
+ if __name__ == "__main__":
435
+ """Standalone mode for testing"""
436
+ import sys
437
+ from PyQt6.QtWidgets import QApplication
438
+
439
+ app = QApplication(sys.argv)
440
+ window = SupercleanerUI()
441
+ window.setWindowTitle("Supercleaner - Document Cleaner")
442
+ window.resize(800, 900)
443
+ window.show()
444
+ sys.exit(app.exec())
modules/superdocs.py ADDED
@@ -0,0 +1,19 @@
1
+ """modules.superdocs (deprecated)
2
+
3
+ The in-app Superdocs generator and viewer were removed in favor of the online GitBook
4
+ documentation:
5
+
6
+ https://supervertaler.gitbook.io/superdocs/
7
+
8
+ This module remains as a tiny shim to prevent accidental imports in older code paths.
9
+ """
10
+
11
+
12
+ def __getattr__(name: str):
13
+ raise ImportError(
14
+ "The 'modules.superdocs' module has been removed. Use the online Superdocs at "
15
+ "https://supervertaler.gitbook.io/superdocs/"
16
+ )
17
+
18
+
19
+ __all__: list[str] = []