supervertaler 1.9.163__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 (85) hide show
  1. Supervertaler.py +48473 -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 +1911 -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 +351 -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 +1176 -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.163.dist-info/METADATA +906 -0
  81. supervertaler-1.9.163.dist-info/RECORD +85 -0
  82. supervertaler-1.9.163.dist-info/WHEEL +5 -0
  83. supervertaler-1.9.163.dist-info/entry_points.txt +2 -0
  84. supervertaler-1.9.163.dist-info/licenses/LICENSE +21 -0
  85. supervertaler-1.9.163.dist-info/top_level.txt +2 -0
@@ -0,0 +1,447 @@
1
+ """
2
+ Migration Script: 4-Layer to Unified Prompt Library
3
+
4
+ Migrates from old structure:
5
+ 1_System_Prompts/
6
+ 2_Domain_Prompts/
7
+ 3_Project_Prompts/
8
+ 4_Style_Guides/
9
+
10
+ To new unified structure:
11
+ Library/
12
+ Style Guides/
13
+ Domain Expertise/
14
+ Project Prompts/
15
+ Active Projects/
16
+
17
+ System Prompts are moved to settings storage (handled separately).
18
+ """
19
+
20
+ import os
21
+ import shutil
22
+ from pathlib import Path
23
+ from datetime import datetime
24
+
25
+
26
+ class PromptLibraryMigration:
27
+ """Handles one-time migration from 4-layer to unified structure"""
28
+
29
+ def __init__(self, prompt_library_dir: str, log_callback=None):
30
+ """
31
+ Args:
32
+ prompt_library_dir: Path to user_data/prompt_library
33
+ log_callback: Function for logging
34
+ """
35
+ self.prompt_library_dir = Path(prompt_library_dir)
36
+ self.log = log_callback if log_callback else print
37
+
38
+ # Old structure paths
39
+ self.old_system_prompts = self.prompt_library_dir / "1_System_Prompts"
40
+ self.old_domain_prompts = self.prompt_library_dir / "2_Domain_Prompts"
41
+ self.old_project_prompts = self.prompt_library_dir / "3_Project_Prompts"
42
+ self.old_style_guides = self.prompt_library_dir / "4_Style_Guides"
43
+
44
+ # New unified library
45
+ self.new_library = self.prompt_library_dir / "Library"
46
+
47
+ # Migration flag
48
+ self.migration_completed_file = self.prompt_library_dir / ".migration_completed"
49
+
50
+ def needs_migration(self) -> bool:
51
+ """Check if migration is needed"""
52
+ # Already migrated?
53
+ if self.migration_completed_file.exists():
54
+ return False
55
+
56
+ # Old structure exists?
57
+ has_old_structure = (
58
+ self.old_domain_prompts.exists() or
59
+ self.old_project_prompts.exists() or
60
+ self.old_style_guides.exists()
61
+ )
62
+
63
+ return has_old_structure
64
+
65
+ def migrate(self) -> bool:
66
+ """
67
+ Perform migration from old to new structure.
68
+
69
+ Steps:
70
+ 1. Create new Library/ structure
71
+ 2. Copy Domain Prompts → Library/Domain Expertise/
72
+ 3. Copy Project Prompts → Library/Project Prompts/
73
+ 4. Copy Style Guides → Library/Style Guides/
74
+ 5. Backup old folders with .old extension
75
+ 6. Mark migration as completed
76
+
77
+ Note: System Prompts are NOT migrated here (moved to settings separately)
78
+
79
+ Returns:
80
+ True if successful
81
+ """
82
+ try:
83
+ self.log("=" * 60)
84
+ self.log("🔄 Starting Prompt Library Migration")
85
+ self.log("=" * 60)
86
+
87
+ # Create new library structure
88
+ self.log("\n📁 Creating new unified library structure...")
89
+ self.new_library.mkdir(parents=True, exist_ok=True)
90
+
91
+ # Migrate each layer
92
+ migrated_counts = {}
93
+
94
+ # Layer 2: Domain Prompts → Domain Expertise
95
+ if self.old_domain_prompts.exists():
96
+ self.log("\n📚 Migrating Domain Prompts...")
97
+ count = self._migrate_folder(
98
+ self.old_domain_prompts,
99
+ self.new_library / "Domain Expertise",
100
+ add_metadata={'folder': 'Domain Expertise'}
101
+ )
102
+ migrated_counts['domain'] = count
103
+ self.log(f" ✓ Migrated {count} domain prompts")
104
+
105
+ # Layer 3: Project Prompts → Project Prompts
106
+ if self.old_project_prompts.exists():
107
+ self.log("\n📋 Migrating Project Prompts...")
108
+ count = self._migrate_folder(
109
+ self.old_project_prompts,
110
+ self.new_library / "Project Prompts",
111
+ add_metadata={'folder': 'Project Prompts'}
112
+ )
113
+ migrated_counts['project'] = count
114
+ self.log(f" ✓ Migrated {count} project prompts")
115
+
116
+ # Layer 4: Style Guides → Style Guides
117
+ if self.old_style_guides.exists():
118
+ self.log("\n🎨 Migrating Style Guides...")
119
+ count = self._migrate_folder(
120
+ self.old_style_guides,
121
+ self.new_library / "Style Guides",
122
+ add_metadata={'folder': 'Style Guides'}
123
+ )
124
+ migrated_counts['style'] = count
125
+ self.log(f" ✓ Migrated {count} style guides")
126
+
127
+ # Backup old folders
128
+ self.log("\n💾 Creating backups of old folders...")
129
+ self._backup_old_folders()
130
+
131
+ # Create Active Projects folder for user
132
+ active_projects = self.new_library / "Active Projects"
133
+ active_projects.mkdir(exist_ok=True)
134
+ self.log(f" ✓ Created 'Active Projects' folder for user-created prompts")
135
+
136
+ # Mark migration as completed
137
+ self.migration_completed_file.write_text(
138
+ f"Migration completed on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
139
+ f"Migrated: {sum(migrated_counts.values())} prompts\n"
140
+ f"Details: {migrated_counts}\n",
141
+ encoding='utf-8'
142
+ )
143
+
144
+ # Summary
145
+ total = sum(migrated_counts.values())
146
+ self.log("\n" + "=" * 60)
147
+ self.log(f"✅ Migration Complete! Migrated {total} prompts")
148
+ self.log("=" * 60)
149
+ self.log(f"\nDomain Expertise: {migrated_counts.get('domain', 0)} prompts")
150
+ self.log(f"Project Prompts: {migrated_counts.get('project', 0)} prompts")
151
+ self.log(f"Style Guides: {migrated_counts.get('style', 0)} prompts")
152
+ self.log(f"\n📂 New location: {self.new_library}")
153
+ self.log(f"💾 Backups: Old folders renamed with .old extension")
154
+ self.log("\n⚠️ Note: System Prompts moved to Settings (not in Library)")
155
+
156
+ return True
157
+
158
+ except Exception as e:
159
+ self.log(f"\n❌ Migration failed: {e}")
160
+ import traceback
161
+ self.log(traceback.format_exc())
162
+ return False
163
+
164
+ def _migrate_folder(self, source: Path, destination: Path, add_metadata: dict = None) -> int:
165
+ """
166
+ Migrate prompts from source to destination folder.
167
+
168
+ Args:
169
+ source: Source folder path
170
+ destination: Destination folder path
171
+ add_metadata: Additional metadata to add to YAML frontmatter
172
+
173
+ Returns:
174
+ Number of files migrated
175
+ """
176
+ count = 0
177
+
178
+ if not source.exists():
179
+ return count
180
+
181
+ destination.mkdir(parents=True, exist_ok=True)
182
+
183
+ for item in source.iterdir():
184
+ # Skip hidden files, README, and format_examples
185
+ if item.name.startswith('.') or item.name == 'README.md' or item.name == 'format_examples':
186
+ continue
187
+
188
+ # Handle subdirectories (recursive)
189
+ if item.is_dir():
190
+ sub_dest = destination / item.name
191
+ count += self._migrate_folder(item, sub_dest, add_metadata)
192
+ continue
193
+
194
+ # Only process .svprompt, .md, .txt, .json files
195
+ if item.suffix.lower() not in ['.svprompt', '.md', '.txt', '.json']:
196
+ continue
197
+
198
+ try:
199
+ # Convert JSON to Markdown if needed
200
+ if item.suffix.lower() == '.json':
201
+ count += self._migrate_json_prompt(item, destination, add_metadata)
202
+ else:
203
+ # Copy and update metadata if needed (.svprompt, .md, .txt are all markdown-based)
204
+ count += self._migrate_markdown_prompt(item, destination, add_metadata)
205
+
206
+ except Exception as e:
207
+ self.log(f" ⚠ Failed to migrate {item.name}: {e}")
208
+
209
+ return count
210
+
211
+ def _migrate_markdown_prompt(self, source: Path, destination: Path, add_metadata: dict = None) -> int:
212
+ """Migrate a Markdown prompt file, adding metadata if needed"""
213
+ try:
214
+ content = source.read_text(encoding='utf-8')
215
+
216
+ # Parse existing frontmatter if present
217
+ if content.startswith('---'):
218
+ content = content[3:].lstrip('\n')
219
+
220
+ if '---' in content:
221
+ frontmatter_str, prompt_content = content.split('---', 1)
222
+ prompt_content = prompt_content.lstrip('\n')
223
+ else:
224
+ frontmatter_str = ""
225
+ prompt_content = content
226
+ else:
227
+ frontmatter_str = ""
228
+ prompt_content = content
229
+
230
+ # Parse existing metadata
231
+ metadata = self._parse_simple_yaml(frontmatter_str) if frontmatter_str else {}
232
+
233
+ # Add new metadata fields
234
+ if add_metadata:
235
+ metadata.update(add_metadata)
236
+
237
+ # Ensure required fields
238
+ if 'name' not in metadata:
239
+ metadata['name'] = source.stem
240
+
241
+ metadata.setdefault('favorite', False)
242
+ metadata.setdefault('quick_run', False)
243
+ metadata.setdefault('tags', [])
244
+
245
+ if 'created' not in metadata:
246
+ metadata['created'] = datetime.now().strftime("%Y-%m-%d")
247
+
248
+ # Build new file with updated metadata
249
+ new_content = self._build_markdown_with_frontmatter(metadata, prompt_content)
250
+
251
+ # Write to destination
252
+ dest_file = destination / source.name
253
+ dest_file.write_text(new_content, encoding='utf-8')
254
+
255
+ return 1
256
+
257
+ except Exception as e:
258
+ self.log(f" ⚠ Error migrating {source.name}: {e}")
259
+ return 0
260
+
261
+ def _migrate_json_prompt(self, source: Path, destination: Path, add_metadata: dict = None) -> int:
262
+ """Migrate a JSON prompt file, converting to Markdown"""
263
+ try:
264
+ import json
265
+
266
+ with open(source, 'r', encoding='utf-8') as f:
267
+ data = json.load(f)
268
+
269
+ # Extract content (prefer translate_prompt)
270
+ content = data.get('translate_prompt', data.get('content', ''))
271
+
272
+ if not content:
273
+ self.log(f" ⚠ No content in {source.name}, skipping")
274
+ return 0
275
+
276
+ # Build metadata
277
+ metadata = {
278
+ 'name': data.get('name', source.stem),
279
+ 'description': data.get('description', ''),
280
+ 'domain': data.get('domain', ''),
281
+ 'version': data.get('version', '1.0'),
282
+ 'task_type': data.get('task_type', 'Translation'),
283
+ 'favorite': False,
284
+ 'quick_run': False,
285
+ 'tags': [],
286
+ 'created': data.get('created', datetime.now().strftime("%Y-%m-%d"))
287
+ }
288
+
289
+ # Add migration-specific metadata
290
+ if add_metadata:
291
+ metadata.update(add_metadata)
292
+
293
+ # Build Markdown content
294
+ new_content = self._build_markdown_with_frontmatter(metadata, content)
295
+
296
+ # Write as .md file
297
+ dest_file = destination / f"{source.stem}.md"
298
+ dest_file.write_text(new_content, encoding='utf-8')
299
+
300
+ self.log(f" ✓ Converted {source.name} → {dest_file.name}")
301
+ return 1
302
+
303
+ except Exception as e:
304
+ self.log(f" ⚠ Error converting {source.name}: {e}")
305
+ return 0
306
+
307
+ def _build_markdown_with_frontmatter(self, metadata: dict, content: str) -> str:
308
+ """Build Markdown file content with YAML frontmatter"""
309
+ lines = ['---']
310
+
311
+ # Ordered fields
312
+ field_order = [
313
+ 'name', 'description', 'domain', 'version', 'task_type',
314
+ 'favorite', 'quick_run', 'folder', 'tags', 'created', 'modified'
315
+ ]
316
+
317
+ for field in field_order:
318
+ if field in metadata:
319
+ value = metadata[field]
320
+
321
+ if isinstance(value, bool):
322
+ lines.append(f'{field}: {str(value).lower()}')
323
+ elif isinstance(value, list):
324
+ if value: # Non-empty list
325
+ items = ', '.join([f'"{item}"' for item in value])
326
+ lines.append(f'{field}: [{items}]')
327
+ else:
328
+ lines.append(f'{field}: []')
329
+ elif isinstance(value, str):
330
+ lines.append(f'{field}: "{value}"')
331
+ else:
332
+ lines.append(f'{field}: {value}')
333
+
334
+ lines.append('---')
335
+
336
+ return '\n'.join(lines) + '\n\n' + content.strip()
337
+
338
+ def _parse_simple_yaml(self, yaml_str: str) -> dict:
339
+ """Simple YAML parser for frontmatter"""
340
+ data = {}
341
+
342
+ for line in yaml_str.strip().split('\n'):
343
+ line = line.strip()
344
+ if not line or line.startswith('#') or ':' not in line:
345
+ continue
346
+
347
+ key, value = line.split(':', 1)
348
+ key = key.strip()
349
+ value = value.strip()
350
+
351
+ # Handle arrays
352
+ if value.startswith('[') and value.endswith(']'):
353
+ array_str = value[1:-1]
354
+ items = [item.strip().strip('"').strip("'") for item in array_str.split(',')]
355
+ data[key] = [item for item in items if item]
356
+ continue
357
+
358
+ # Handle booleans
359
+ if value.lower() in ['true', 'false']:
360
+ data[key] = value.lower() == 'true'
361
+ continue
362
+
363
+ # Remove quotes
364
+ if (value.startswith('"') and value.endswith('"')) or \
365
+ (value.startswith("'") and value.endswith("'")):
366
+ value = value[1:-1]
367
+
368
+ data[key] = value
369
+
370
+ return data
371
+
372
+ def _backup_old_folders(self):
373
+ """Rename old folders with .old extension"""
374
+ folders_to_backup = [
375
+ self.old_system_prompts,
376
+ self.old_domain_prompts,
377
+ self.old_project_prompts,
378
+ self.old_style_guides
379
+ ]
380
+
381
+ for folder in folders_to_backup:
382
+ if folder.exists():
383
+ backup_path = folder.with_suffix('.old')
384
+
385
+ # If backup already exists, remove it first
386
+ if backup_path.exists():
387
+ shutil.rmtree(backup_path)
388
+
389
+ folder.rename(backup_path)
390
+ self.log(f" ✓ Backed up: {folder.name} → {backup_path.name}")
391
+
392
+ def rollback(self):
393
+ """Rollback migration (restore from .old backups)"""
394
+ try:
395
+ self.log("🔄 Rolling back migration...")
396
+
397
+ # Remove new library
398
+ if self.new_library.exists():
399
+ shutil.rmtree(self.new_library)
400
+ self.log(" ✓ Removed new library structure")
401
+
402
+ # Restore from backups
403
+ backup_folders = [
404
+ (self.prompt_library_dir / "1_System_Prompts.old", self.old_system_prompts),
405
+ (self.prompt_library_dir / "2_Domain_Prompts.old", self.old_domain_prompts),
406
+ (self.prompt_library_dir / "3_Project_Prompts.old", self.old_project_prompts),
407
+ (self.prompt_library_dir / "4_Style_Guides.old", self.old_style_guides)
408
+ ]
409
+
410
+ for backup, original in backup_folders:
411
+ if backup.exists():
412
+ if original.exists():
413
+ shutil.rmtree(original)
414
+ backup.rename(original)
415
+ self.log(f" ✓ Restored: {original.name}")
416
+
417
+ # Remove migration flag
418
+ if self.migration_completed_file.exists():
419
+ self.migration_completed_file.unlink()
420
+
421
+ self.log("✓ Rollback complete")
422
+ return True
423
+
424
+ except Exception as e:
425
+ self.log(f"❌ Rollback failed: {e}")
426
+ return False
427
+
428
+
429
+ def migrate_prompt_library(prompt_library_dir: str, log_callback=None) -> bool:
430
+ """
431
+ Convenience function to perform migration.
432
+
433
+ Args:
434
+ prompt_library_dir: Path to user_data/Prompt_Library
435
+ log_callback: Function for logging
436
+
437
+ Returns:
438
+ True if migration successful or not needed
439
+ """
440
+ migrator = PromptLibraryMigration(prompt_library_dir, log_callback)
441
+
442
+ if not migrator.needs_migration():
443
+ if log_callback:
444
+ log_callback("✓ Prompt library already migrated (or no old structure found)")
445
+ return True
446
+
447
+ return migrator.migrate()