supervertaler 1.9.204__py3-none-any.whl → 1.9.205__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.

Supervertaler.py CHANGED
@@ -9544,6 +9544,9 @@ class SupervertalerQt(QMainWindow):
9544
9544
  QPushButton:hover {
9545
9545
  background-color: #2980b9;
9546
9546
  }
9547
+ QPushButton:focus {
9548
+ outline: none;
9549
+ }
9547
9550
  """)
9548
9551
  extract_btn.clicked.connect(self._on_extract_images)
9549
9552
  header_layout.addWidget(extract_btn)
@@ -21190,6 +21193,9 @@ class SupervertalerQt(QMainWindow):
21190
21193
  QPushButton:hover:!checked {
21191
21194
  background-color: #858585;
21192
21195
  }
21196
+ QPushButton:focus {
21197
+ outline: none;
21198
+ }
21193
21199
  """)
21194
21200
  wysiwyg_btn.clicked.connect(lambda: self.toggle_tag_view(False, None))
21195
21201
  view_mode_group.addButton(wysiwyg_btn, 0)
@@ -21216,6 +21222,9 @@ class SupervertalerQt(QMainWindow):
21216
21222
  QPushButton:hover:!checked {
21217
21223
  background-color: #858585;
21218
21224
  }
21225
+ QPushButton:focus {
21226
+ outline: none;
21227
+ }
21219
21228
  """)
21220
21229
  tags_btn.clicked.connect(lambda: self.toggle_tag_view(True, None))
21221
21230
  view_mode_group.addButton(tags_btn, 1)
@@ -21273,6 +21282,9 @@ class SupervertalerQt(QMainWindow):
21273
21282
  QPushButton:checked {
21274
21283
  background-color: #2E7D32;
21275
21284
  }
21285
+ QPushButton:focus {
21286
+ outline: none;
21287
+ }
21276
21288
  """)
21277
21289
  alwayson_btn.setToolTip("Toggle always-on voice listening\n\nWhen ON: Listens continuously for voice commands\nNo need to press F9")
21278
21290
  alwayson_btn.clicked.connect(lambda checked: self._toggle_alwayson_from_grid_btn(checked, alwayson_btn))
@@ -22032,6 +22044,9 @@ class SupervertalerQt(QMainWindow):
22032
22044
  QPushButton:pressed {
22033
22045
  background-color: #1565C0;
22034
22046
  }
22047
+ QPushButton:focus {
22048
+ outline: none;
22049
+ }
22035
22050
  """)
22036
22051
  self.scroll_up_btn.setToolTip("Scroll up one row (Precision scroll)")
22037
22052
  self.scroll_up_btn.clicked.connect(lambda: self.precision_scroll(-1))
@@ -22058,6 +22073,9 @@ class SupervertalerQt(QMainWindow):
22058
22073
  QPushButton:pressed {
22059
22074
  background-color: #1565C0;
22060
22075
  }
22076
+ QPushButton:focus {
22077
+ outline: none;
22078
+ }
22061
22079
  """)
22062
22080
  self.scroll_down_btn.setToolTip("Scroll down one row (Precision scroll)")
22063
22081
  self.scroll_down_btn.clicked.connect(lambda: self.precision_scroll(1))
@@ -24678,6 +24696,11 @@ class SupervertalerQt(QMainWindow):
24678
24696
  # Set as current project and load into grid
24679
24697
  self.current_project = project
24680
24698
  self.current_document_path = file_path # Store document path
24699
+
24700
+ # CRITICAL: Update _original_segment_order for new import
24701
+ # This prevents old segments from being saved instead of new ones
24702
+ self._original_segment_order = self.current_project.segments.copy()
24703
+
24681
24704
  self.load_segments_to_grid()
24682
24705
 
24683
24706
  # Initialize TM for this project
@@ -24912,14 +24935,18 @@ class SupervertalerQt(QMainWindow):
24912
24935
  # Set as current project and load into grid
24913
24936
  self.current_project = project
24914
24937
  self.current_document_path = file_path
24938
+
24939
+ # CRITICAL: Update _original_segment_order for new import
24940
+ self._original_segment_order = self.current_project.segments.copy()
24941
+
24915
24942
  self.load_segments_to_grid()
24916
-
24943
+
24917
24944
  # Initialize TM for this project
24918
24945
  self.initialize_tm_database()
24919
-
24946
+
24920
24947
  # Initialize spellcheck for target language
24921
24948
  self._initialize_spellcheck_for_target_language(target_lang)
24922
-
24949
+
24923
24950
  # Update status
24924
24951
  empty_count = sum(1 for seg in segments if not seg.source.strip())
24925
24952
  file_type = "Markdown file" if is_markdown else "text file"
@@ -24979,6 +25006,8 @@ class SupervertalerQt(QMainWindow):
24979
25006
  project.original_txt_path = file_path
24980
25007
  self.current_project = project
24981
25008
  self.current_document_path = file_path
25009
+ # CRITICAL: Update _original_segment_order for new import
25010
+ self._original_segment_order = self.current_project.segments.copy()
24982
25011
  self.load_segments_to_grid()
24983
25012
  self.initialize_tm_database()
24984
25013
  self.log(f"✓ Loaded {len(segments)} lines from text file")
@@ -26307,10 +26336,13 @@ class SupervertalerQt(QMainWindow):
26307
26336
  # Set as current project
26308
26337
  self.current_project = project
26309
26338
  self.current_document_path = folder_path
26310
-
26339
+
26340
+ # CRITICAL: Update _original_segment_order for new import
26341
+ self._original_segment_order = self.current_project.segments.copy()
26342
+
26311
26343
  # Load into grid
26312
26344
  self.load_segments_to_grid()
26313
-
26345
+
26314
26346
  # Initialize TM
26315
26347
  self.initialize_tm_database()
26316
26348
 
@@ -27183,10 +27215,14 @@ class SupervertalerQt(QMainWindow):
27183
27215
  # Update UI
27184
27216
  self.project_file_path = None
27185
27217
  self.project_modified = True
27218
+
27219
+ # CRITICAL: Update _original_segment_order for new import
27220
+ self._original_segment_order = self.current_project.segments.copy()
27221
+
27186
27222
  self.update_window_title()
27187
27223
  self.load_segments_to_grid()
27188
27224
  self.initialize_tm_database()
27189
-
27225
+
27190
27226
  # Deactivate all resources for new project, then auto-activate language-matching ones
27191
27227
  self._deactivate_all_resources_for_new_project()
27192
27228
 
@@ -27199,7 +27235,7 @@ class SupervertalerQt(QMainWindow):
27199
27235
  # If smart formatting was used, auto-enable Tags view so user sees the tags
27200
27236
  if self.memoq_smart_formatting:
27201
27237
  self._enable_tag_view_after_import()
27202
-
27238
+
27203
27239
  self.log(f"✓ Imported memoQ bilingual DOCX: {len(source_segments)} segments from {Path(file_path).name}")
27204
27240
 
27205
27241
  # Store current document path for AI Assistant
@@ -27692,10 +27728,14 @@ class SupervertalerQt(QMainWindow):
27692
27728
  # Update UI
27693
27729
  self.project_file_path = None
27694
27730
  self.project_modified = True
27731
+
27732
+ # CRITICAL: Update _original_segment_order for new import
27733
+ self._original_segment_order = self.current_project.segments.copy()
27734
+
27695
27735
  self.update_window_title()
27696
27736
  self.load_segments_to_grid()
27697
27737
  self.initialize_tm_database()
27698
-
27738
+
27699
27739
  # Deactivate all resources for new project, then auto-activate language-matching ones
27700
27740
  self._deactivate_all_resources_for_new_project()
27701
27741
 
@@ -27971,10 +28011,14 @@ class SupervertalerQt(QMainWindow):
27971
28011
  # Update UI
27972
28012
  self.project_file_path = None
27973
28013
  self.project_modified = True
28014
+
28015
+ # CRITICAL: Update _original_segment_order for new import
28016
+ self._original_segment_order = self.current_project.segments.copy()
28017
+
27974
28018
  self.update_window_title()
27975
28019
  self.load_segments_to_grid()
27976
28020
  self.initialize_tm_database()
27977
-
28021
+
27978
28022
  # Deactivate all resources for new project, then auto-activate language-matching ones
27979
28023
  self._deactivate_all_resources_for_new_project()
27980
28024
 
@@ -28192,6 +28236,9 @@ class SupervertalerQt(QMainWindow):
28192
28236
  # Store Trados source path in project for persistence across saves
28193
28237
  self.current_project.trados_source_path = file_path
28194
28238
 
28239
+ # CRITICAL: Update _original_segment_order for new import
28240
+ self._original_segment_order = self.current_project.segments.copy()
28241
+
28195
28242
  # Sync global language settings with imported project languages
28196
28243
  self.source_language = source_lang
28197
28244
  self.target_language = target_lang
@@ -28556,6 +28603,9 @@ class SupervertalerQt(QMainWindow):
28556
28603
  self.sdlppx_source_file = file_path
28557
28604
  self.current_project.sdlppx_source_path = file_path
28558
28605
 
28606
+ # CRITICAL: Update _original_segment_order for new import
28607
+ self._original_segment_order = self.current_project.segments.copy()
28608
+
28559
28609
  # Sync global language settings with imported project languages
28560
28610
  self.source_language = source_lang
28561
28611
  self.target_language = target_lang
@@ -28892,6 +28942,9 @@ class SupervertalerQt(QMainWindow):
28892
28942
  # Store Phrase source path in project for persistence across saves
28893
28943
  self.current_project.phrase_source_path = file_path
28894
28944
 
28945
+ # CRITICAL: Update _original_segment_order for new import
28946
+ self._original_segment_order = self.current_project.segments.copy()
28947
+
28895
28948
  # Sync global language settings with imported project languages
28896
28949
  self.source_language = source_lang
28897
28950
  self.target_language = target_lang
@@ -29194,20 +29247,23 @@ class SupervertalerQt(QMainWindow):
29194
29247
  dejavu_row_index=seg_data.row_index,
29195
29248
  )
29196
29249
  self.current_project.segments.append(segment)
29197
-
29250
+
29251
+ # CRITICAL: Update _original_segment_order for new import
29252
+ self._original_segment_order = self.current_project.segments.copy()
29253
+
29198
29254
  # Update UI
29199
29255
  self.project_file_path = None
29200
29256
  self.project_modified = True
29201
29257
  self.update_window_title()
29202
29258
  self.load_segments_to_grid()
29203
29259
  self.initialize_tm_database()
29204
-
29260
+
29205
29261
  # Auto-resize rows for better initial display
29206
29262
  self.auto_resize_rows()
29207
-
29263
+
29208
29264
  # Initialize spellcheck for target language
29209
29265
  self._initialize_spellcheck_for_target_language(target_lang)
29210
-
29266
+
29211
29267
  self.log(f"✓ Imported Déjà Vu X3 bilingual RTF: {len(segments_data)} segments from {Path(file_path).name}")
29212
29268
 
29213
29269
  # Store current document path for AI Assistant
@@ -44605,12 +44661,25 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
44605
44661
 
44606
44662
  translator = deepl.Translator(api_key)
44607
44663
 
44608
- # Convert source language code (DeepL uses uppercase, no variant needed for source)
44609
- src_code = source_lang.split('-')[0].split('_')[0].upper()
44664
+ # Map full language names to ISO codes
44665
+ lang_name_to_code = {
44666
+ 'english': 'en', 'dutch': 'nl', 'german': 'de', 'french': 'fr',
44667
+ 'spanish': 'es', 'italian': 'it', 'portuguese': 'pt', 'russian': 'ru',
44668
+ 'chinese': 'zh', 'japanese': 'ja', 'korean': 'ko', 'arabic': 'ar',
44669
+ 'polish': 'pl', 'swedish': 'sv', 'norwegian': 'no', 'danish': 'da',
44670
+ 'finnish': 'fi', 'greek': 'el', 'turkish': 'tr', 'czech': 'cs',
44671
+ 'hungarian': 'hu', 'romanian': 'ro', 'bulgarian': 'bg', 'ukrainian': 'uk',
44672
+ }
44673
+
44674
+ # Convert source language - try name mapping first, then code extraction
44675
+ src_lower = source_lang.lower().strip()
44676
+ src_base = lang_name_to_code.get(src_lower, src_lower.split('-')[0].split('_')[0])
44677
+ src_code = src_base.upper()
44610
44678
 
44611
- # Convert target language code - DeepL requires variants for some languages
44612
- # Handle full codes like "en-US", "en-GB", "pt-BR", "pt-PT"
44613
- tgt_upper = target_lang.upper().replace('_', '-')
44679
+ # Convert target language - try name mapping first, then handle DeepL variants
44680
+ tgt_lower = target_lang.lower().strip()
44681
+ tgt_base = lang_name_to_code.get(tgt_lower, tgt_lower)
44682
+ tgt_upper = tgt_base.upper().replace('_', '-')
44614
44683
 
44615
44684
  # DeepL target language mapping - some require specific variants
44616
44685
  deepl_target_map = {
@@ -44656,19 +44725,33 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
44656
44725
  """Call Microsoft Azure Translator API"""
44657
44726
  try:
44658
44727
  import requests
44659
-
44728
+
44660
44729
  api_keys = self.load_api_keys()
44661
44730
  if not api_key:
44662
44731
  api_key = api_keys.get("microsoft_translate") or api_keys.get("azure_translate")
44663
44732
  if not region:
44664
44733
  region = api_keys.get("microsoft_translate_region") or api_keys.get("azure_region") or "global"
44665
-
44734
+
44666
44735
  if not api_key:
44667
44736
  return "[Microsoft Translator requires API key]"
44668
-
44669
- # Convert language codes (Azure uses standard codes)
44670
- src_code = source_lang.split('-')[0].split('_')[0].lower()
44671
- tgt_code = target_lang.split('-')[0].split('_')[0].lower()
44737
+
44738
+ # Map full language names to ISO codes
44739
+ lang_name_to_code = {
44740
+ 'english': 'en', 'dutch': 'nl', 'german': 'de', 'french': 'fr',
44741
+ 'spanish': 'es', 'italian': 'it', 'portuguese': 'pt', 'russian': 'ru',
44742
+ 'chinese': 'zh', 'japanese': 'ja', 'korean': 'ko', 'arabic': 'ar',
44743
+ 'polish': 'pl', 'swedish': 'sv', 'norwegian': 'no', 'danish': 'da',
44744
+ 'finnish': 'fi', 'greek': 'el', 'turkish': 'tr', 'czech': 'cs',
44745
+ 'hungarian': 'hu', 'romanian': 'ro', 'bulgarian': 'bg', 'ukrainian': 'uk',
44746
+ 'hebrew': 'he', 'thai': 'th', 'vietnamese': 'vi', 'indonesian': 'id',
44747
+ }
44748
+
44749
+ # Convert language - try name mapping first, then code extraction
44750
+ src_lower = source_lang.lower().strip()
44751
+ tgt_lower = target_lang.lower().strip()
44752
+
44753
+ src_code = lang_name_to_code.get(src_lower, src_lower.split('-')[0].split('_')[0])
44754
+ tgt_code = lang_name_to_code.get(tgt_lower, tgt_lower.split('-')[0].split('_')[0])
44672
44755
 
44673
44756
  # Microsoft Translator API v3.0
44674
44757
  endpoint = f"https://api.cognitive.microsofttranslator.com/translate"
@@ -44756,17 +44839,30 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
44756
44839
  """Call ModernMT API"""
44757
44840
  try:
44758
44841
  import requests
44759
-
44842
+
44760
44843
  api_keys = self.load_api_keys()
44761
44844
  if not api_key:
44762
44845
  api_key = api_keys.get("modernmt")
44763
-
44846
+
44764
44847
  if not api_key:
44765
44848
  return "[ModernMT requires API key]"
44766
-
44767
- # Convert language codes
44768
- src_code = source_lang.split('-')[0].split('_')[0]
44769
- tgt_code = target_lang.split('-')[0].split('_')[0]
44849
+
44850
+ # Map full language names to ISO codes
44851
+ lang_name_to_code = {
44852
+ 'english': 'en', 'dutch': 'nl', 'german': 'de', 'french': 'fr',
44853
+ 'spanish': 'es', 'italian': 'it', 'portuguese': 'pt', 'russian': 'ru',
44854
+ 'chinese': 'zh', 'japanese': 'ja', 'korean': 'ko', 'arabic': 'ar',
44855
+ 'polish': 'pl', 'swedish': 'sv', 'norwegian': 'no', 'danish': 'da',
44856
+ 'finnish': 'fi', 'greek': 'el', 'turkish': 'tr', 'czech': 'cs',
44857
+ 'hungarian': 'hu', 'romanian': 'ro', 'bulgarian': 'bg', 'ukrainian': 'uk',
44858
+ }
44859
+
44860
+ # Convert language - try name mapping first, then code extraction
44861
+ src_lower = source_lang.lower().strip()
44862
+ tgt_lower = target_lang.lower().strip()
44863
+
44864
+ src_code = lang_name_to_code.get(src_lower, src_lower.split('-')[0].split('_')[0])
44865
+ tgt_code = lang_name_to_code.get(tgt_lower, tgt_lower.split('-')[0].split('_')[0])
44770
44866
 
44771
44867
  # ModernMT API endpoint
44772
44868
  url = "https://api.modernmt.com/translate"
@@ -44795,14 +44891,29 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
44795
44891
  """Call MyMemory Translation API (free tier available, simple REST API)"""
44796
44892
  try:
44797
44893
  import requests
44798
-
44894
+
44799
44895
  # MyMemory is free, but API key provides higher limits
44800
44896
  api_keys = self.load_api_keys()
44801
44897
  api_key = api_key or api_keys.get("mymemory") # Optional, works without key
44802
-
44803
- # Convert language codes (MyMemory uses 2-letter codes)
44804
- src_code = source_lang.split('-')[0].split('_')[0].lower()
44805
- tgt_code = target_lang.split('-')[0].split('_')[0].lower()
44898
+
44899
+ # Map full language names to ISO codes
44900
+ lang_name_to_code = {
44901
+ 'english': 'en', 'dutch': 'nl', 'german': 'de', 'french': 'fr',
44902
+ 'spanish': 'es', 'italian': 'it', 'portuguese': 'pt', 'russian': 'ru',
44903
+ 'chinese': 'zh', 'japanese': 'ja', 'korean': 'ko', 'arabic': 'ar',
44904
+ 'polish': 'pl', 'swedish': 'sv', 'norwegian': 'no', 'danish': 'da',
44905
+ 'finnish': 'fi', 'greek': 'el', 'turkish': 'tr', 'czech': 'cs',
44906
+ 'hungarian': 'hu', 'romanian': 'ro', 'bulgarian': 'bg', 'ukrainian': 'uk',
44907
+ 'hebrew': 'he', 'thai': 'th', 'vietnamese': 'vi', 'indonesian': 'id',
44908
+ 'malay': 'ms', 'hindi': 'hi', 'bengali': 'bn', 'tamil': 'ta',
44909
+ }
44910
+
44911
+ # Convert language - try name mapping first, then code extraction
44912
+ src_lower = source_lang.lower().strip()
44913
+ tgt_lower = target_lang.lower().strip()
44914
+
44915
+ src_code = lang_name_to_code.get(src_lower, src_lower.split('-')[0].split('_')[0])
44916
+ tgt_code = lang_name_to_code.get(tgt_lower, tgt_lower.split('-')[0].split('_')[0])
44806
44917
 
44807
44918
  # MyMemory API endpoint
44808
44919
  url = "https://api.mymemory.translated.net/get"
modules/ai_actions.py CHANGED
@@ -168,9 +168,10 @@ class AIActionSystem:
168
168
 
169
169
  except json.JSONDecodeError as e:
170
170
  self.log(f"✗ Failed to parse action parameters: {e}")
171
+ self.log(f"[DEBUG] Raw params_str: {params_str[:500]}...") # Log what we tried to parse
171
172
  action_results.append({
172
173
  'action': action_name,
173
- 'params': params_str,
174
+ 'params': params_str[:200], # Truncate for display
174
175
  'success': False,
175
176
  'error': f"Invalid JSON parameters: {e}"
176
177
  })
@@ -325,7 +326,15 @@ class AIActionSystem:
325
326
  content = params.get('content')
326
327
 
327
328
  if not name or not content:
328
- raise ValueError("Missing required parameters: name and content")
329
+ # Log what we actually received for debugging
330
+ self.log(f"[DEBUG] create_prompt received params: {params}")
331
+ self.log(f"[DEBUG] name={repr(name)}, content={repr(content)[:100] if content else None}")
332
+ missing = []
333
+ if not name:
334
+ missing.append("name")
335
+ if not content:
336
+ missing.append("content")
337
+ raise ValueError(f"Missing required parameters: {', '.join(missing)}. Received keys: {list(params.keys())}")
329
338
 
330
339
  # Build relative path
331
340
  folder = params.get('folder', '')
@@ -960,5 +969,8 @@ PARAMS: {
960
969
  output += "\n"
961
970
  else:
962
971
  output += f"\n✗ **{action_name}**: {result['error']}\n"
972
+ # Show hint for common errors
973
+ if 'Missing required parameters' in result['error']:
974
+ output += " _Hint: The AI may not have generated the correct format. Try clicking the button again._\n"
963
975
 
964
976
  return output
modules/quicktrans.py CHANGED
@@ -289,6 +289,9 @@ class MTQuickPopup(QDialog):
289
289
  background-color: #e0e0e0;
290
290
  border-radius: 4px;
291
291
  }
292
+ QPushButton:focus {
293
+ outline: none;
294
+ }
292
295
  """)
293
296
  settings_btn.clicked.connect(self._open_settings)
294
297
  header_layout.addWidget(settings_btn)
@@ -447,6 +447,9 @@ class TermbaseEntryEditor(QDialog):
447
447
  QPushButton:hover {
448
448
  background-color: #d32f2f;
449
449
  }
450
+ QPushButton:focus {
451
+ outline: none;
452
+ }
450
453
  """)
451
454
  self.delete_btn.clicked.connect(self.delete_term)
452
455
  buttons_layout.addWidget(self.delete_btn)
@@ -465,6 +468,9 @@ class TermbaseEntryEditor(QDialog):
465
468
  QPushButton:hover {
466
469
  background-color: #e0e0e0;
467
470
  }
471
+ QPushButton:focus {
472
+ outline: none;
473
+ }
468
474
  """)
469
475
  self.cancel_btn.clicked.connect(self.reject)
470
476
  buttons_layout.addWidget(self.cancel_btn)
@@ -483,6 +489,9 @@ class TermbaseEntryEditor(QDialog):
483
489
  QPushButton:hover {
484
490
  background-color: #45a049;
485
491
  }
492
+ QPushButton:focus {
493
+ outline: none;
494
+ }
486
495
  """)
487
496
  self.save_btn.clicked.connect(self.save_term)
488
497
  buttons_layout.addWidget(self.save_btn)
modules/theme_manager.py CHANGED
@@ -370,7 +370,16 @@ class ThemeManager:
370
370
  QPushButton:disabled {{
371
371
  color: {theme.text_disabled};
372
372
  }}
373
-
373
+
374
+ /* Remove focus rectangles from buttons */
375
+ QPushButton:focus {{
376
+ outline: none;
377
+ }}
378
+
379
+ QToolButton:focus {{
380
+ outline: none;
381
+ }}
382
+
374
383
  /* Combo boxes */
375
384
  QComboBox {{
376
385
  background-color: {theme.base};
@@ -1190,6 +1190,9 @@ class TranslationResultsPanel(QWidget):
1190
1190
  QPushButton:hover {
1191
1191
  background-color: #0b7dda;
1192
1192
  }
1193
+ QPushButton:focus {
1194
+ outline: none;
1195
+ }
1193
1196
  """)
1194
1197
  self.termbase_refresh_btn.setFixedHeight(20)
1195
1198
  self.termbase_refresh_btn.setToolTip("Refresh entry from database")
@@ -1210,6 +1213,9 @@ class TranslationResultsPanel(QWidget):
1210
1213
  QPushButton:hover {
1211
1214
  background-color: #45a049;
1212
1215
  }
1216
+ QPushButton:focus {
1217
+ outline: none;
1218
+ }
1213
1219
  """)
1214
1220
  self.termbase_edit_btn.setFixedHeight(20)
1215
1221
  self.termbase_edit_btn.clicked.connect(self._on_edit_termbase_entry)
@@ -595,6 +595,10 @@ class UnifiedPromptManagerQt:
595
595
  log_callback=self.log_message
596
596
  )
597
597
 
598
+ # Context inclusion toggles for AI Assistant
599
+ self.include_tm_data = False
600
+ self.include_termbase_data = False
601
+
598
602
  self._init_llm_client()
599
603
  self._load_conversation_history()
600
604
  self._load_persisted_attachments()
@@ -824,6 +828,9 @@ class UnifiedPromptManagerQt:
824
828
  QPushButton:pressed {
825
829
  background-color: #0D47A1;
826
830
  }
831
+ QPushButton:focus {
832
+ outline: none;
833
+ }
827
834
  """)
828
835
  action_btn.clicked.connect(self._analyze_and_generate)
829
836
  layout.addWidget(action_btn, 0)
@@ -1046,14 +1053,16 @@ class UnifiedPromptManagerQt:
1046
1053
  "Click to include TM data"
1047
1054
  )
1048
1055
  self.context_tms.setCursor(Qt.CursorShape.PointingHandCursor)
1056
+ self.context_tms.mousePressEvent = lambda e: self._toggle_tm_inclusion()
1049
1057
  content_layout.addWidget(self.context_tms)
1050
-
1058
+
1051
1059
  # Termbases
1052
1060
  self.context_termbases = self._create_context_section(
1053
1061
  "📚 Termbases",
1054
1062
  "Click to include termbase data"
1055
1063
  )
1056
1064
  self.context_termbases.setCursor(Qt.CursorShape.PointingHandCursor)
1065
+ self.context_termbases.mousePressEvent = lambda e: self._toggle_termbase_inclusion()
1057
1066
  content_layout.addWidget(self.context_termbases)
1058
1067
 
1059
1068
  content_layout.addStretch()
@@ -1094,6 +1103,95 @@ class UnifiedPromptManagerQt:
1094
1103
 
1095
1104
  return frame
1096
1105
 
1106
+ def _toggle_tm_inclusion(self):
1107
+ """Toggle inclusion of TM data in AI context"""
1108
+ self.include_tm_data = not self.include_tm_data
1109
+ self._update_tm_section_display()
1110
+
1111
+ # Show feedback
1112
+ status = "enabled" if self.include_tm_data else "disabled"
1113
+ self._add_chat_message("system", f"💾 Translation Memory inclusion **{status}**")
1114
+
1115
+ def _toggle_termbase_inclusion(self):
1116
+ """Toggle inclusion of termbase data in AI context"""
1117
+ self.include_termbase_data = not self.include_termbase_data
1118
+ self._update_termbase_section_display()
1119
+
1120
+ # Show feedback
1121
+ status = "enabled" if self.include_termbase_data else "disabled"
1122
+ self._add_chat_message("system", f"📚 Termbase inclusion **{status}**")
1123
+
1124
+ def _update_tm_section_display(self):
1125
+ """Update TM section visual state"""
1126
+ if self.include_tm_data:
1127
+ self.context_tms.setStyleSheet("""
1128
+ QFrame {
1129
+ background-color: #E3F2FD;
1130
+ border: 2px solid #1976D2;
1131
+ border-radius: 5px;
1132
+ padding: 8px;
1133
+ }
1134
+ """)
1135
+ # Update description label
1136
+ for child in self.context_tms.findChildren(QLabel):
1137
+ if "Click to" in child.text() or "✓" in child.text():
1138
+ child.setText("✓ TM data will be included")
1139
+ child.setStyleSheet("color: #1976D2; font-size: 8pt; font-weight: bold;")
1140
+ break
1141
+ else:
1142
+ self.context_tms.setStyleSheet("""
1143
+ QFrame {
1144
+ background-color: #F5F5F5;
1145
+ border: 1px solid #E0E0E0;
1146
+ border-radius: 5px;
1147
+ padding: 8px;
1148
+ }
1149
+ QFrame:hover {
1150
+ background-color: #EEEEEE;
1151
+ border: 1px solid #BDBDBD;
1152
+ }
1153
+ """)
1154
+ for child in self.context_tms.findChildren(QLabel):
1155
+ if "✓" in child.text() or "Click to" in child.text():
1156
+ child.setText("Click to include TM data")
1157
+ child.setStyleSheet("color: #666; font-size: 8pt;")
1158
+ break
1159
+
1160
+ def _update_termbase_section_display(self):
1161
+ """Update termbase section visual state"""
1162
+ if self.include_termbase_data:
1163
+ self.context_termbases.setStyleSheet("""
1164
+ QFrame {
1165
+ background-color: #E8F5E9;
1166
+ border: 2px solid #4CAF50;
1167
+ border-radius: 5px;
1168
+ padding: 8px;
1169
+ }
1170
+ """)
1171
+ for child in self.context_termbases.findChildren(QLabel):
1172
+ if "Click to" in child.text() or "✓" in child.text():
1173
+ child.setText("✓ Termbase data will be included")
1174
+ child.setStyleSheet("color: #4CAF50; font-size: 8pt; font-weight: bold;")
1175
+ break
1176
+ else:
1177
+ self.context_termbases.setStyleSheet("""
1178
+ QFrame {
1179
+ background-color: #F5F5F5;
1180
+ border: 1px solid #E0E0E0;
1181
+ border-radius: 5px;
1182
+ padding: 8px;
1183
+ }
1184
+ QFrame:hover {
1185
+ background-color: #EEEEEE;
1186
+ border: 1px solid #BDBDBD;
1187
+ }
1188
+ """)
1189
+ for child in self.context_termbases.findChildren(QLabel):
1190
+ if "✓" in child.text() or "Click to" in child.text():
1191
+ child.setText("Click to include termbase data")
1192
+ child.setStyleSheet("color: #666; font-size: 8pt;")
1193
+ break
1194
+
1097
1195
  def _create_attached_files_section(self) -> QFrame:
1098
1196
  """Create expandable attached files section with view/remove buttons"""
1099
1197
  frame = QFrame()
@@ -1125,6 +1223,9 @@ class UnifiedPromptManagerQt:
1125
1223
  QPushButton:hover {
1126
1224
  background-color: #E0E0E0;
1127
1225
  }
1226
+ QPushButton:focus {
1227
+ outline: none;
1228
+ }
1128
1229
  """)
1129
1230
  self.attached_files_expand_btn.clicked.connect(self._toggle_attached_files)
1130
1231
  header_layout.addWidget(self.attached_files_expand_btn)
@@ -1149,6 +1250,9 @@ class UnifiedPromptManagerQt:
1149
1250
  QPushButton:hover {
1150
1251
  background-color: #1565C0;
1151
1252
  }
1253
+ QPushButton:focus {
1254
+ outline: none;
1255
+ }
1152
1256
  """)
1153
1257
  attach_btn.clicked.connect(self._attach_file)
1154
1258
  header_layout.addWidget(attach_btn)
@@ -1253,6 +1357,9 @@ class UnifiedPromptManagerQt:
1253
1357
  QPushButton:hover {
1254
1358
  background-color: #1565C0;
1255
1359
  }
1360
+ QPushButton:focus {
1361
+ outline: none;
1362
+ }
1256
1363
  """)
1257
1364
  view_btn.clicked.connect(lambda: self._view_file(file_data))
1258
1365
  btn_layout.addWidget(view_btn)
@@ -1270,6 +1377,9 @@ class UnifiedPromptManagerQt:
1270
1377
  QPushButton:hover {
1271
1378
  background-color: #b71c1c;
1272
1379
  }
1380
+ QPushButton:focus {
1381
+ outline: none;
1382
+ }
1273
1383
  """)
1274
1384
  remove_btn.clicked.connect(lambda: self._remove_file(file_data))
1275
1385
  btn_layout.addWidget(remove_btn)
@@ -1405,6 +1515,9 @@ class UnifiedPromptManagerQt:
1405
1515
  QPushButton:pressed {
1406
1516
  background-color: #424242;
1407
1517
  }
1518
+ QPushButton:focus {
1519
+ outline: none;
1520
+ }
1408
1521
  """)
1409
1522
  clear_btn.clicked.connect(self._clear_chat)
1410
1523
  toolbar_layout.addWidget(clear_btn)
@@ -1456,6 +1569,9 @@ class UnifiedPromptManagerQt:
1456
1569
  QPushButton:pressed {
1457
1570
  background-color: #0D47A1;
1458
1571
  }
1572
+ QPushButton:focus {
1573
+ outline: none;
1574
+ }
1459
1575
  """)
1460
1576
  send_btn.clicked.connect(self._send_chat_message)
1461
1577
  input_layout.addWidget(send_btn)
@@ -3217,9 +3333,17 @@ If the text refers to figures (e.g., 'Figure 1A'), relevant images may be provid
3217
3333
  PROJECT CONTEXT:
3218
3334
  {context}
3219
3335
 
3220
- Create a translation prompt. Output ONE complete ACTION block:
3336
+ **CRITICAL INSTRUCTIONS:**
3337
+ 1. You MUST output exactly ONE ACTION block in the following format
3338
+ 2. The JSON MUST be valid and complete
3339
+ 3. Both "name" and "content" are REQUIRED fields
3340
+
3341
+ **EXACT FORMAT TO USE:**
3221
3342
 
3222
- ACTION:create_prompt PARAMS:{{"name": "[Name]", "content": "[Prompt]", "folder": "Project Prompts", "description": "Auto-generated", "activate": true}}
3343
+ ACTION:create_prompt PARAMS:{{"name": "Your Prompt Name Here", "content": "Your full prompt content here", "folder": "Project Prompts", "description": "Auto-generated prompt", "activate": true}}
3344
+
3345
+ **EXAMPLE:**
3346
+ ACTION:create_prompt PARAMS:{{"name": "Legal Translation EN-NL", "content": "You are an expert legal translator...\\n\\n# TERMINOLOGY\\n| Source | Target |\\n...", "folder": "Project Prompts", "description": "Auto-generated", "activate": true}}
3223
3347
 
3224
3348
  Prompt must include:
3225
3349
 
@@ -3317,6 +3441,18 @@ Output complete ACTION."""
3317
3441
  preview = file['content'][:200].replace('\n', ' ')
3318
3442
  context_parts.append(f" Preview: {preview}...")
3319
3443
 
3444
+ # Translation Memory data (if enabled)
3445
+ if self.include_tm_data:
3446
+ tm_data = self._get_tm_context_data()
3447
+ if tm_data:
3448
+ context_parts.append(f"\n**Translation Memory Matches:**\n{tm_data}")
3449
+
3450
+ # Termbase data (if enabled)
3451
+ if self.include_termbase_data:
3452
+ tb_data = self._get_termbase_context_data()
3453
+ if tb_data:
3454
+ context_parts.append(f"\n**Termbase Entries:**\n{tb_data}")
3455
+
3320
3456
  return "\n".join(context_parts) if context_parts else "No context available"
3321
3457
 
3322
3458
  def _list_available_prompts(self) -> str:
@@ -3334,7 +3470,73 @@ Output complete ACTION."""
3334
3470
  lines.append(f"... and {len(self.library.prompts) - 20} more")
3335
3471
 
3336
3472
  return "\n".join(lines)
3337
-
3473
+
3474
+ def _get_tm_context_data(self) -> str:
3475
+ """Get Translation Memory data for AI context"""
3476
+ try:
3477
+ if not hasattr(self.parent_app, 'tm_databases') or not self.parent_app.tm_databases:
3478
+ return "No translation memories loaded"
3479
+
3480
+ lines = []
3481
+ total_entries = 0
3482
+
3483
+ for tm_name, tm_db in self.parent_app.tm_databases.items():
3484
+ if hasattr(tm_db, 'entries'):
3485
+ count = len(tm_db.entries)
3486
+ total_entries += count
3487
+ lines.append(f"- **{tm_name}**: {count} entries")
3488
+
3489
+ # Show sample entries (first 10)
3490
+ for i, entry in enumerate(list(tm_db.entries.values())[:10]):
3491
+ if hasattr(entry, 'source') and hasattr(entry, 'target'):
3492
+ lines.append(f" {i+1}. {entry.source[:50]}... → {entry.target[:50]}...")
3493
+
3494
+ if not lines:
3495
+ return "Translation memories are empty"
3496
+
3497
+ return f"Total: {total_entries} TM entries\n" + "\n".join(lines)
3498
+
3499
+ except Exception as e:
3500
+ return f"Error loading TM data: {e}"
3501
+
3502
+ def _get_termbase_context_data(self) -> str:
3503
+ """Get Termbase data for AI context"""
3504
+ try:
3505
+ if not hasattr(self.parent_app, 'termbases') or not self.parent_app.termbases:
3506
+ # Try to get termbase entries from the termbase manager
3507
+ if hasattr(self.parent_app, 'termbase_manager'):
3508
+ terms = self.parent_app.termbase_manager.get_all_terms()
3509
+ if terms:
3510
+ lines = [f"Total: {len(terms)} termbase entries\n"]
3511
+ for i, term in enumerate(terms[:50]): # First 50 terms
3512
+ source = term.get('source_term', term.get('source', ''))
3513
+ target = term.get('target_term', term.get('target', ''))
3514
+ if source and target:
3515
+ lines.append(f"| {source} | {target} |")
3516
+ return "\n".join(lines)
3517
+ return "No termbases loaded"
3518
+
3519
+ lines = []
3520
+ total_terms = 0
3521
+
3522
+ for tb_name, tb in self.parent_app.termbases.items():
3523
+ if hasattr(tb, 'terms'):
3524
+ count = len(tb.terms)
3525
+ total_terms += count
3526
+ lines.append(f"- **{tb_name}**: {count} terms")
3527
+
3528
+ # Show sample terms (first 20)
3529
+ for i, (source, target) in enumerate(list(tb.terms.items())[:20]):
3530
+ lines.append(f" | {source} | {target} |")
3531
+
3532
+ if not lines:
3533
+ return "Termbases are empty"
3534
+
3535
+ return f"Total: {total_terms} terms\n" + "\n".join(lines)
3536
+
3537
+ except Exception as e:
3538
+ return f"Error loading termbase data: {e}"
3539
+
3338
3540
  def _attach_file(self):
3339
3541
  """Attach a file to the conversation"""
3340
3542
  file_path, _ = QFileDialog.getOpenFileName(
@@ -240,6 +240,9 @@ class VoiceDictationWidget(QWidget):
240
240
  QPushButton:disabled {
241
241
  background-color: #BDBDBD;
242
242
  }
243
+ QPushButton:focus {
244
+ outline: none;
245
+ }
243
246
  """)
244
247
  self.record_btn.clicked.connect(self.toggle_recording)
245
248
  controls_layout.addWidget(self.record_btn)
@@ -345,6 +348,9 @@ class VoiceDictationWidget(QWidget):
345
348
  QPushButton:hover {
346
349
  background-color: #C62828;
347
350
  }
351
+ QPushButton:focus {
352
+ outline: none;
353
+ }
348
354
  """)
349
355
 
350
356
  # Disable controls
@@ -380,6 +386,9 @@ class VoiceDictationWidget(QWidget):
380
386
  QPushButton:hover {
381
387
  background-color: #1976D2;
382
388
  }
389
+ QPushButton:focus {
390
+ outline: none;
391
+ }
383
392
  """)
384
393
 
385
394
  # Start transcription
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supervertaler
3
- Version: 1.9.204
3
+ Version: 1.9.205
4
4
  Summary: Professional AI-enhanced translation workbench with multi-LLM support, glossary system, TM, spellcheck, voice commands, and PyQt6 interface. Batteries included (core).
5
5
  Home-page: https://supervertaler.com
6
6
  Author: Michael Beijer
@@ -1,6 +1,6 @@
1
- Supervertaler.py,sha256=ZCV8QknOZhHpUGJ_t4ONW_wq7zS1L_0nlHAuw8x0ybo,2398568
1
+ Supervertaler.py,sha256=Q4pIpw6ZFn6DcIsX7oPk0iyZKRLg2HQFnIvhzZ98MrY,2403906
2
2
  modules/__init__.py,sha256=G58XleS-EJ2sX4Kehm-3N2m618_W2Es0Kg8CW_eBG7g,327
3
- modules/ai_actions.py,sha256=i5MJcM-7Y6CAvKUwxmxrVHeoZAVtAP7aRDdWM5KLkO0,33877
3
+ modules/ai_actions.py,sha256=rU7h1TOkhPk_UaO6sxqj7V3FFUfo5Aj03Q6kSEllXyk,34688
4
4
  modules/ai_attachment_manager.py,sha256=juZlrW3UPkIkcnj0SREgOQkQROLf0fcu3ShZcKXMxsI,11361
5
5
  modules/ai_file_viewer_dialog.py,sha256=lKKqUUlOEVgHmmu6aRxqH7P6ds-7dRLk4ltDyjCwGxs,6246
6
6
  modules/autofingers_engine.py,sha256=eJ7tBi7YJvTToe5hYTfnyGXB-qme_cHrOPZibaoR2Xw,17061
@@ -40,7 +40,7 @@ modules/prompt_assistant.py,sha256=shkZqNTvyQKNDO_9aFEu1_gN0zQq0fR5krXkWfnTR2Y,1
40
40
  modules/prompt_library.py,sha256=t5w4cqB6_Sin4BQDVNALKpfB1EN_oaDeHFwlHxILLSY,26894
41
41
  modules/prompt_library_migration.py,sha256=fv3RHhe2-EnH50XW5tyTWy0YP_KJ2EsESuTxR8klfmI,17639
42
42
  modules/quick_access_sidebar.py,sha256=RPn5ssvYXlitNMWFZN9Yxv7So8u_z5RGNpHN6N-SFDI,10151
43
- modules/quicktrans.py,sha256=jnCwaX8tSwaGlM5BxX2NEz5XqkHu9Pnxd8XBRX3OUFM,26291
43
+ modules/quicktrans.py,sha256=uoEWb3MALfSR2nn07-hpBIsaQookqzcX9vAlKLGtOrE,26371
44
44
  modules/ribbon_widget.py,sha256=QNGKxmit_oM5C5nJViadYYEzeRlIdIsla8Bzu_RNGO0,21990
45
45
  modules/sdlppx_handler.py,sha256=o6Rj_T0B94toiYlvDDwMYLSz4q6kANgegFaDK5i3yhs,33538
46
46
  modules/setup_wizard.py,sha256=7apNkeTcmMyw8pzJOWAmTOncxFvt3klOtmg-kKjQNgQ,13091
@@ -59,11 +59,11 @@ modules/superlookup.py,sha256=0SnIv-L8xNhAS5JuvoFPdp4BoScgACLFUB1fVwFe36Y,8980
59
59
  modules/tag_cleaner.py,sha256=u7VOchIWzD4sAhFs3X1Vuo3hX6X72zESQ0AGZE83cYc,8703
60
60
  modules/tag_manager.py,sha256=g66S0JSxdguN9AhWzZG3hsIz87Ul51wQ3c2wOCTZVSk,12789
61
61
  modules/term_extractor.py,sha256=qPvKNCVXFTGEGwXNvvC0cfCmdb5c3WhzE38EOgKdKUI,11253
62
- modules/termbase_entry_editor.py,sha256=iWO9CgLjMomGAqBXDsGAX7TFJvDOp2s_taS4gBL1rZY,35818
62
+ modules/termbase_entry_editor.py,sha256=V2IzVar8noU_SDhqkcZLg2NOtyAIhalSa5E6bLGcGUc,36070
63
63
  modules/termbase_import_export.py,sha256=16IAY04IS_rgt0GH5UOUzUI5NoqAli4JMfMquxmFBm0,23552
64
64
  modules/termbase_manager.py,sha256=XAVrz-wt8jKcjoD6ocHoXewY5PN0A0GeqFEctsv0jS8,48697
65
65
  modules/termview_widget.py,sha256=FsNnSWh86PCnmKcC3fFJS8MJNdVvRpgE5e8-u4jAosY,55742
66
- modules/theme_manager.py,sha256=Qk_jfCmfm7fjdMAOyBHpD18w3MiRfWBZk0cHTw6yAAg,18639
66
+ modules/theme_manager.py,sha256=EkiAviQ31ewzhMM0XaP4NkDJ2LZwRg8oXCy7BLQhc0k,18842
67
67
  modules/tm_editor_dialog.py,sha256=AzGwq4QW641uFJdF8DljLTRRp4FLoYX3Pe4rlTjQWNg,3517
68
68
  modules/tm_manager_qt.py,sha256=h2bvXkRuboHf_RRz9-5FX35GVRlpXgRDWeXyj1QWtPs,54406
69
69
  modules/tm_metadata_manager.py,sha256=NTsaI_YjQnVOpU_scAwK9uR1Tcl9pzKD1GwLVy7sx2g,23590
@@ -73,16 +73,16 @@ modules/tmx_generator.py,sha256=pNkxwdMLvSRMMru0lkB1gvViIpg9BQy1EVhRbwoef3k,9426
73
73
  modules/tracked_changes.py,sha256=S_BIEC6r7wVAwjG42aSy_RgH4KaMAC8GS5thEvqrYdE,39480
74
74
  modules/trados_docx_handler.py,sha256=VPRAQ73cUHs_SEj6x81z1PmSxfjnwPBp9P4fXeK3KpQ,16363
75
75
  modules/translation_memory.py,sha256=LnG8csZNL2GTHXT4zk0uecJEtvRc-MKwv7Pt7EX3s7s,28002
76
- modules/translation_results_panel.py,sha256=OWqzV9xmJOi8NGCi3h42nq-qE7-v6WStjQWRsghCVbQ,92044
76
+ modules/translation_results_panel.py,sha256=HbYYjvRNqaJrJNLYLX-YanamMm6aRdLzLjn5rWIfk3U,92198
77
77
  modules/translation_services.py,sha256=lyVpWuZK1wtVtYZMDMdLoq1DHBoSaeAnp-Yejb0TlVQ,10530
78
78
  modules/unified_prompt_library.py,sha256=96u4WlMwnmmhD4uNJHZ-qVQj8v9_8dA2AVCWpBcwTrg,26006
79
- modules/unified_prompt_manager_qt.py,sha256=y7xAIM9X7f1afXGE1tOcAc5EY7BKFy53Rgqi3fFzKmQ,173374
79
+ modules/unified_prompt_manager_qt.py,sha256=L96tgYpSLI0ZjD-R8eyxbwNYo_Vi1fyREFYriAR-Vlo,181992
80
80
  modules/voice_commands.py,sha256=iBb-gjWxRMLhFH7-InSRjYJz1EIDBNA2Pog8V7TtJaY,38516
81
- modules/voice_dictation.py,sha256=QmitXfkG-vRt5hIQATjphHdhXfqmwhzcQcbXB6aRzIg,16386
81
+ modules/voice_dictation.py,sha256=7TlTTILSW4KwpTi1rDeKfvTwd5z3_XtNSzNIo35VAWU,16617
82
82
  modules/voice_dictation_lite.py,sha256=jorY0BmWE-8VczbtGrWwt1zbnOctMoSlWOsQrcufBcc,9423
83
- supervertaler-1.9.204.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
84
- supervertaler-1.9.204.dist-info/METADATA,sha256=zcv2fcgWU60pLclQn-I23GvrLuXf-z9vVFzElk6hIXw,5725
85
- supervertaler-1.9.204.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
86
- supervertaler-1.9.204.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
87
- supervertaler-1.9.204.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
88
- supervertaler-1.9.204.dist-info/RECORD,,
83
+ supervertaler-1.9.205.dist-info/licenses/LICENSE,sha256=m28u-4qL5nXIWnJ6xlQVw__H30rWFtRK3pCOais2OuY,1092
84
+ supervertaler-1.9.205.dist-info/METADATA,sha256=hX2ysmA8ur00DXX7Ua21H9t4Ct620QB1vAH7iwqVILs,5725
85
+ supervertaler-1.9.205.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
86
+ supervertaler-1.9.205.dist-info/entry_points.txt,sha256=NP4hiCvx-_30YYKqgr-jfJYQvHr1qTYBMfoVmKIXSM8,53
87
+ supervertaler-1.9.205.dist-info/top_level.txt,sha256=9tUHBYUSfaE4S2E4W3eavJsDyYymkwLfeWAHHAPT6Dk,22
88
+ supervertaler-1.9.205.dist-info/RECORD,,