pomera-ai-commander 1.1.1 → 1.2.2

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 (213) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +105 -680
  3. package/bin/pomera-ai-commander.js +62 -62
  4. package/core/__init__.py +65 -65
  5. package/core/app_context.py +482 -482
  6. package/core/async_text_processor.py +421 -421
  7. package/core/backup_manager.py +655 -655
  8. package/core/backup_recovery_manager.py +1199 -1033
  9. package/core/content_hash_cache.py +508 -508
  10. package/core/context_menu.py +313 -313
  11. package/core/data_directory.py +549 -0
  12. package/core/data_validator.py +1066 -1066
  13. package/core/database_connection_manager.py +744 -744
  14. package/core/database_curl_settings_manager.py +608 -608
  15. package/core/database_promera_ai_settings_manager.py +446 -446
  16. package/core/database_schema.py +411 -411
  17. package/core/database_schema_manager.py +395 -395
  18. package/core/database_settings_manager.py +1507 -1507
  19. package/core/database_settings_manager_interface.py +456 -456
  20. package/core/dialog_manager.py +734 -734
  21. package/core/diff_utils.py +239 -0
  22. package/core/efficient_line_numbers.py +540 -510
  23. package/core/error_handler.py +746 -746
  24. package/core/error_service.py +431 -431
  25. package/core/event_consolidator.py +511 -511
  26. package/core/mcp/__init__.py +43 -43
  27. package/core/mcp/find_replace_diff.py +334 -0
  28. package/core/mcp/protocol.py +288 -288
  29. package/core/mcp/schema.py +251 -251
  30. package/core/mcp/server_stdio.py +299 -299
  31. package/core/mcp/tool_registry.py +2699 -2345
  32. package/core/memento.py +275 -0
  33. package/core/memory_efficient_text_widget.py +711 -711
  34. package/core/migration_manager.py +914 -914
  35. package/core/migration_test_suite.py +1085 -1085
  36. package/core/migration_validator.py +1143 -1143
  37. package/core/optimized_find_replace.py +714 -714
  38. package/core/optimized_pattern_engine.py +424 -424
  39. package/core/optimized_search_highlighter.py +552 -552
  40. package/core/performance_monitor.py +674 -674
  41. package/core/persistence_manager.py +712 -712
  42. package/core/progressive_stats_calculator.py +632 -632
  43. package/core/regex_pattern_cache.py +529 -529
  44. package/core/regex_pattern_library.py +350 -350
  45. package/core/search_operation_manager.py +434 -434
  46. package/core/settings_defaults_registry.py +1087 -1087
  47. package/core/settings_integrity_validator.py +1111 -1111
  48. package/core/settings_serializer.py +557 -557
  49. package/core/settings_validator.py +1823 -1823
  50. package/core/smart_stats_calculator.py +709 -709
  51. package/core/statistics_update_manager.py +619 -619
  52. package/core/stats_config_manager.py +858 -858
  53. package/core/streaming_text_handler.py +723 -723
  54. package/core/task_scheduler.py +596 -596
  55. package/core/update_pattern_library.py +168 -168
  56. package/core/visibility_monitor.py +596 -596
  57. package/core/widget_cache.py +498 -498
  58. package/mcp.json +51 -61
  59. package/migrate_data.py +127 -0
  60. package/package.json +64 -57
  61. package/pomera.py +7883 -7482
  62. package/pomera_mcp_server.py +183 -144
  63. package/requirements.txt +33 -0
  64. package/scripts/Dockerfile.alpine +43 -0
  65. package/scripts/Dockerfile.gui-test +54 -0
  66. package/scripts/Dockerfile.linux +43 -0
  67. package/scripts/Dockerfile.test-linux +80 -0
  68. package/scripts/Dockerfile.ubuntu +39 -0
  69. package/scripts/README.md +53 -0
  70. package/scripts/build-all.bat +113 -0
  71. package/scripts/build-docker.bat +53 -0
  72. package/scripts/build-docker.sh +55 -0
  73. package/scripts/build-optimized.bat +101 -0
  74. package/scripts/build.sh +78 -0
  75. package/scripts/docker-compose.test.yml +27 -0
  76. package/scripts/docker-compose.yml +32 -0
  77. package/scripts/postinstall.js +62 -0
  78. package/scripts/requirements-minimal.txt +33 -0
  79. package/scripts/test-linux-simple.bat +28 -0
  80. package/scripts/validate-release-workflow.py +450 -0
  81. package/tools/__init__.py +4 -4
  82. package/tools/ai_tools.py +2891 -2891
  83. package/tools/ascii_art_generator.py +352 -352
  84. package/tools/base64_tools.py +183 -183
  85. package/tools/base_tool.py +511 -511
  86. package/tools/case_tool.py +308 -308
  87. package/tools/column_tools.py +395 -395
  88. package/tools/cron_tool.py +884 -884
  89. package/tools/curl_history.py +600 -600
  90. package/tools/curl_processor.py +1207 -1207
  91. package/tools/curl_settings.py +502 -502
  92. package/tools/curl_tool.py +5467 -5467
  93. package/tools/diff_viewer.py +1817 -1072
  94. package/tools/email_extraction_tool.py +248 -248
  95. package/tools/email_header_analyzer.py +425 -425
  96. package/tools/extraction_tools.py +250 -250
  97. package/tools/find_replace.py +2289 -1750
  98. package/tools/folder_file_reporter.py +1463 -1463
  99. package/tools/folder_file_reporter_adapter.py +480 -480
  100. package/tools/generator_tools.py +1216 -1216
  101. package/tools/hash_generator.py +255 -255
  102. package/tools/html_tool.py +656 -656
  103. package/tools/jsonxml_tool.py +729 -729
  104. package/tools/line_tools.py +419 -419
  105. package/tools/markdown_tools.py +561 -561
  106. package/tools/mcp_widget.py +1417 -1417
  107. package/tools/notes_widget.py +978 -973
  108. package/tools/number_base_converter.py +372 -372
  109. package/tools/regex_extractor.py +571 -571
  110. package/tools/slug_generator.py +310 -310
  111. package/tools/sorter_tools.py +458 -458
  112. package/tools/string_escape_tool.py +392 -392
  113. package/tools/text_statistics_tool.py +365 -365
  114. package/tools/text_wrapper.py +430 -430
  115. package/tools/timestamp_converter.py +421 -421
  116. package/tools/tool_loader.py +710 -710
  117. package/tools/translator_tools.py +522 -522
  118. package/tools/url_link_extractor.py +261 -261
  119. package/tools/url_parser.py +204 -204
  120. package/tools/whitespace_tools.py +355 -355
  121. package/tools/word_frequency_counter.py +146 -146
  122. package/core/__pycache__/__init__.cpython-313.pyc +0 -0
  123. package/core/__pycache__/app_context.cpython-313.pyc +0 -0
  124. package/core/__pycache__/async_text_processor.cpython-313.pyc +0 -0
  125. package/core/__pycache__/backup_manager.cpython-313.pyc +0 -0
  126. package/core/__pycache__/backup_recovery_manager.cpython-313.pyc +0 -0
  127. package/core/__pycache__/content_hash_cache.cpython-313.pyc +0 -0
  128. package/core/__pycache__/context_menu.cpython-313.pyc +0 -0
  129. package/core/__pycache__/data_validator.cpython-313.pyc +0 -0
  130. package/core/__pycache__/database_connection_manager.cpython-313.pyc +0 -0
  131. package/core/__pycache__/database_curl_settings_manager.cpython-313.pyc +0 -0
  132. package/core/__pycache__/database_promera_ai_settings_manager.cpython-313.pyc +0 -0
  133. package/core/__pycache__/database_schema.cpython-313.pyc +0 -0
  134. package/core/__pycache__/database_schema_manager.cpython-313.pyc +0 -0
  135. package/core/__pycache__/database_settings_manager.cpython-313.pyc +0 -0
  136. package/core/__pycache__/database_settings_manager_interface.cpython-313.pyc +0 -0
  137. package/core/__pycache__/dialog_manager.cpython-313.pyc +0 -0
  138. package/core/__pycache__/efficient_line_numbers.cpython-313.pyc +0 -0
  139. package/core/__pycache__/error_handler.cpython-313.pyc +0 -0
  140. package/core/__pycache__/error_service.cpython-313.pyc +0 -0
  141. package/core/__pycache__/event_consolidator.cpython-313.pyc +0 -0
  142. package/core/__pycache__/memory_efficient_text_widget.cpython-313.pyc +0 -0
  143. package/core/__pycache__/migration_manager.cpython-313.pyc +0 -0
  144. package/core/__pycache__/migration_test_suite.cpython-313.pyc +0 -0
  145. package/core/__pycache__/migration_validator.cpython-313.pyc +0 -0
  146. package/core/__pycache__/optimized_find_replace.cpython-313.pyc +0 -0
  147. package/core/__pycache__/optimized_pattern_engine.cpython-313.pyc +0 -0
  148. package/core/__pycache__/optimized_search_highlighter.cpython-313.pyc +0 -0
  149. package/core/__pycache__/performance_monitor.cpython-313.pyc +0 -0
  150. package/core/__pycache__/persistence_manager.cpython-313.pyc +0 -0
  151. package/core/__pycache__/progressive_stats_calculator.cpython-313.pyc +0 -0
  152. package/core/__pycache__/regex_pattern_cache.cpython-313.pyc +0 -0
  153. package/core/__pycache__/regex_pattern_library.cpython-313.pyc +0 -0
  154. package/core/__pycache__/search_operation_manager.cpython-313.pyc +0 -0
  155. package/core/__pycache__/settings_defaults_registry.cpython-313.pyc +0 -0
  156. package/core/__pycache__/settings_integrity_validator.cpython-313.pyc +0 -0
  157. package/core/__pycache__/settings_serializer.cpython-313.pyc +0 -0
  158. package/core/__pycache__/settings_validator.cpython-313.pyc +0 -0
  159. package/core/__pycache__/smart_stats_calculator.cpython-313.pyc +0 -0
  160. package/core/__pycache__/statistics_update_manager.cpython-313.pyc +0 -0
  161. package/core/__pycache__/stats_config_manager.cpython-313.pyc +0 -0
  162. package/core/__pycache__/streaming_text_handler.cpython-313.pyc +0 -0
  163. package/core/__pycache__/task_scheduler.cpython-313.pyc +0 -0
  164. package/core/__pycache__/visibility_monitor.cpython-313.pyc +0 -0
  165. package/core/__pycache__/widget_cache.cpython-313.pyc +0 -0
  166. package/core/mcp/__pycache__/__init__.cpython-313.pyc +0 -0
  167. package/core/mcp/__pycache__/protocol.cpython-313.pyc +0 -0
  168. package/core/mcp/__pycache__/schema.cpython-313.pyc +0 -0
  169. package/core/mcp/__pycache__/server_stdio.cpython-313.pyc +0 -0
  170. package/core/mcp/__pycache__/tool_registry.cpython-313.pyc +0 -0
  171. package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  172. package/tools/__pycache__/ai_tools.cpython-313.pyc +0 -0
  173. package/tools/__pycache__/ascii_art_generator.cpython-313.pyc +0 -0
  174. package/tools/__pycache__/base64_tools.cpython-313.pyc +0 -0
  175. package/tools/__pycache__/base_tool.cpython-313.pyc +0 -0
  176. package/tools/__pycache__/case_tool.cpython-313.pyc +0 -0
  177. package/tools/__pycache__/column_tools.cpython-313.pyc +0 -0
  178. package/tools/__pycache__/cron_tool.cpython-313.pyc +0 -0
  179. package/tools/__pycache__/curl_history.cpython-313.pyc +0 -0
  180. package/tools/__pycache__/curl_processor.cpython-313.pyc +0 -0
  181. package/tools/__pycache__/curl_settings.cpython-313.pyc +0 -0
  182. package/tools/__pycache__/curl_tool.cpython-313.pyc +0 -0
  183. package/tools/__pycache__/diff_viewer.cpython-313.pyc +0 -0
  184. package/tools/__pycache__/email_extraction_tool.cpython-313.pyc +0 -0
  185. package/tools/__pycache__/email_header_analyzer.cpython-313.pyc +0 -0
  186. package/tools/__pycache__/extraction_tools.cpython-313.pyc +0 -0
  187. package/tools/__pycache__/find_replace.cpython-313.pyc +0 -0
  188. package/tools/__pycache__/folder_file_reporter.cpython-313.pyc +0 -0
  189. package/tools/__pycache__/folder_file_reporter_adapter.cpython-313.pyc +0 -0
  190. package/tools/__pycache__/generator_tools.cpython-313.pyc +0 -0
  191. package/tools/__pycache__/hash_generator.cpython-313.pyc +0 -0
  192. package/tools/__pycache__/html_tool.cpython-313.pyc +0 -0
  193. package/tools/__pycache__/huggingface_helper.cpython-313.pyc +0 -0
  194. package/tools/__pycache__/jsonxml_tool.cpython-313.pyc +0 -0
  195. package/tools/__pycache__/line_tools.cpython-313.pyc +0 -0
  196. package/tools/__pycache__/list_comparator.cpython-313.pyc +0 -0
  197. package/tools/__pycache__/markdown_tools.cpython-313.pyc +0 -0
  198. package/tools/__pycache__/mcp_widget.cpython-313.pyc +0 -0
  199. package/tools/__pycache__/notes_widget.cpython-313.pyc +0 -0
  200. package/tools/__pycache__/number_base_converter.cpython-313.pyc +0 -0
  201. package/tools/__pycache__/regex_extractor.cpython-313.pyc +0 -0
  202. package/tools/__pycache__/slug_generator.cpython-313.pyc +0 -0
  203. package/tools/__pycache__/sorter_tools.cpython-313.pyc +0 -0
  204. package/tools/__pycache__/string_escape_tool.cpython-313.pyc +0 -0
  205. package/tools/__pycache__/text_statistics_tool.cpython-313.pyc +0 -0
  206. package/tools/__pycache__/text_wrapper.cpython-313.pyc +0 -0
  207. package/tools/__pycache__/timestamp_converter.cpython-313.pyc +0 -0
  208. package/tools/__pycache__/tool_loader.cpython-313.pyc +0 -0
  209. package/tools/__pycache__/translator_tools.cpython-313.pyc +0 -0
  210. package/tools/__pycache__/url_link_extractor.cpython-313.pyc +0 -0
  211. package/tools/__pycache__/url_parser.cpython-313.pyc +0 -0
  212. package/tools/__pycache__/whitespace_tools.cpython-313.pyc +0 -0
  213. package/tools/__pycache__/word_frequency_counter.cpython-313.pyc +0 -0
@@ -1,1087 +1,1087 @@
1
- """
2
- Settings Defaults Registry Module
3
-
4
- Centralizes all tool default settings into a single registry system for consistent
5
- first-launch initialization. Provides schema validation, deep merge capability,
6
- and backward compatibility with existing tools.
7
-
8
- Author: Pomera AI Commander Team
9
- """
10
-
11
- from dataclasses import dataclass, field
12
- from typing import Any, Dict, List, Optional, Callable, Set, Tuple
13
- from copy import deepcopy
14
- import logging
15
-
16
-
17
- @dataclass
18
- class ToolDefaultsSpec:
19
- """
20
- Specification for a tool's default settings.
21
-
22
- Attributes:
23
- tool_name: Unique identifier for the tool (must match key in tool_settings)
24
- defaults: Dictionary of default settings for the tool
25
- required_keys: Set of keys that must be present in settings
26
- description: Human-readable description of the tool
27
- version: Version of the settings schema (for future migrations)
28
- """
29
- tool_name: str
30
- defaults: Dict[str, Any]
31
- required_keys: Set[str] = field(default_factory=set)
32
- description: str = ""
33
- version: str = "1.0"
34
-
35
-
36
- class SettingsValidationError(Exception):
37
- """Raised when settings validation fails."""
38
- pass
39
-
40
-
41
- class SettingsDefaultsRegistry:
42
- """
43
- Central registry for all tool default settings.
44
-
45
- This registry provides:
46
- - Centralized storage of all tool defaults
47
- - Schema validation for tool settings
48
- - Deep merge capability for user settings with defaults
49
- - Backward compatibility with existing tools
50
- """
51
-
52
- _instance: Optional['SettingsDefaultsRegistry'] = None
53
-
54
- def __new__(cls) -> 'SettingsDefaultsRegistry':
55
- """Singleton pattern to ensure only one registry exists."""
56
- if cls._instance is None:
57
- cls._instance = super().__new__(cls)
58
- cls._instance._initialized = False
59
- return cls._instance
60
-
61
- def __init__(self):
62
- """Initialize the registry with default tool specifications."""
63
- if self._initialized:
64
- return
65
-
66
- self.logger = logging.getLogger(__name__)
67
- self._tool_specs: Dict[str, ToolDefaultsSpec] = {}
68
- self._app_defaults: Dict[str, Any] = {}
69
- self._initialized = True
70
-
71
- # Register all built-in tool defaults
72
- self._register_builtin_defaults()
73
-
74
- def _register_builtin_defaults(self) -> None:
75
- """Register all built-in tool default settings."""
76
-
77
- # Case Tool - Updated with new exclusions per requirements
78
- self.register_tool(ToolDefaultsSpec(
79
- tool_name="Case Tool",
80
- defaults={
81
- "mode": "Sentence",
82
- "exclusions": "a\nan\nthe\nand\nbut\nor\nfor\nnor\non\nat\nto\nfrom\nby\nwith\nin\nof"
83
- },
84
- required_keys={"mode"},
85
- description="Text case conversion tool"
86
- ))
87
-
88
- # Base64 Encoder/Decoder
89
- self.register_tool(ToolDefaultsSpec(
90
- tool_name="Base64 Encoder/Decoder",
91
- defaults={"mode": "encode"},
92
- required_keys={"mode"},
93
- description="Base64 encoding and decoding tool"
94
- ))
95
-
96
- # JSON/XML Tool
97
- self.register_tool(ToolDefaultsSpec(
98
- tool_name="JSON/XML Tool",
99
- defaults={
100
- "operation": "json_to_xml",
101
- "json_indent": 2,
102
- "xml_indent": 2,
103
- "preserve_attributes": True,
104
- "sort_keys": False,
105
- "array_wrapper": "item",
106
- "root_element": "root",
107
- "jsonpath_query": "$",
108
- "xpath_query": "//*"
109
- },
110
- required_keys={"operation"},
111
- description="JSON and XML conversion tool"
112
- ))
113
-
114
- # Cron Tool
115
- self.register_tool(ToolDefaultsSpec(
116
- tool_name="Cron Tool",
117
- defaults={
118
- "action": "parse_explain",
119
- "preset_category": "Daily Schedules",
120
- "preset_pattern": "Daily at midnight",
121
- "compare_expressions": "",
122
- "next_runs_count": 10
123
- },
124
- required_keys={"action"},
125
- description="Cron expression parser and generator"
126
- ))
127
-
128
- # Find & Replace Text
129
- self.register_tool(ToolDefaultsSpec(
130
- tool_name="Find & Replace Text",
131
- defaults={
132
- "find": "",
133
- "replace": "",
134
- "mode": "Text",
135
- "option": "ignore_case",
136
- "find_history": [],
137
- "replace_history": []
138
- },
139
- required_keys={"mode"},
140
- description="Find and replace text tool"
141
- ))
142
-
143
- # Generator Tools
144
- self.register_tool(ToolDefaultsSpec(
145
- tool_name="Generator Tools",
146
- defaults={
147
- "Strong Password Generator": {
148
- "length": 20,
149
- "numbers": "",
150
- "symbols": ""
151
- },
152
- "Repeating Text Generator": {
153
- "times": 5,
154
- "separator": "+"
155
- }
156
- },
157
- description="Various text and data generators"
158
- ))
159
-
160
- # Sorter Tools
161
- self.register_tool(ToolDefaultsSpec(
162
- tool_name="Sorter Tools",
163
- defaults={
164
- "Number Sorter": {
165
- "order": "ascending"
166
- },
167
- "Alphabetical Sorter": {
168
- "order": "ascending",
169
- "unique_only": False,
170
- "trim": False
171
- }
172
- },
173
- description="Text and number sorting tools"
174
- ))
175
-
176
- # URL and Link Extractor
177
- self.register_tool(ToolDefaultsSpec(
178
- tool_name="URL and Link Extractor",
179
- defaults={
180
- "extract_href": False,
181
- "extract_https": False,
182
- "extract_any_protocol": False,
183
- "extract_markdown": False,
184
- "filter_text": ""
185
- },
186
- description="URL and link extraction tool"
187
- ))
188
-
189
- # Email Extraction Tool
190
- self.register_tool(ToolDefaultsSpec(
191
- tool_name="Email Extraction Tool",
192
- defaults={
193
- "omit_duplicates": False,
194
- "hide_counts": True,
195
- "sort_emails": False,
196
- "only_domain": False
197
- },
198
- description="Email address extraction tool"
199
- ))
200
-
201
- # Regex Extractor
202
- self.register_tool(ToolDefaultsSpec(
203
- tool_name="Regex Extractor",
204
- defaults={
205
- "pattern": "",
206
- "match_mode": "all_per_line",
207
- "omit_duplicates": False,
208
- "hide_counts": True,
209
- "sort_results": False,
210
- "case_sensitive": False
211
- },
212
- required_keys={"match_mode"},
213
- description="Regular expression extraction tool"
214
- ))
215
-
216
- # Email Header Analyzer
217
- self.register_tool(ToolDefaultsSpec(
218
- tool_name="Email Header Analyzer",
219
- defaults={
220
- "show_timestamps": True,
221
- "show_delays": True,
222
- "show_authentication": True,
223
- "show_spam_score": True
224
- },
225
- description="Email header analysis tool"
226
- ))
227
-
228
- # Folder File Reporter
229
- self.register_tool(ToolDefaultsSpec(
230
- tool_name="Folder File Reporter",
231
- defaults={
232
- "last_input_folder": "",
233
- "last_output_folder": "",
234
- "field_selections": {
235
- "path": True,
236
- "name": True,
237
- "size": True,
238
- "date_modified": True
239
- },
240
- "separator": " | ",
241
- "folders_only": False,
242
- "recursion_mode": "full",
243
- "recursion_depth": 2,
244
- "size_format": "human",
245
- "date_format": "%Y-%m-%d %H:%M:%S"
246
- },
247
- description="Folder and file reporting tool"
248
- ))
249
-
250
- # URL Parser
251
- self.register_tool(ToolDefaultsSpec(
252
- tool_name="URL Parser",
253
- defaults={"ascii_decode": True},
254
- description="URL parsing and analysis tool"
255
- ))
256
-
257
- # Word Frequency Counter
258
- self.register_tool(ToolDefaultsSpec(
259
- tool_name="Word Frequency Counter",
260
- defaults={},
261
- description="Word frequency analysis tool"
262
- ))
263
-
264
- # Google AI - Updated December 2025
265
- # Latest: Gemini 2.5 series (Pro, Flash, Flash-Lite), Gemini 2.0 series
266
- self.register_tool(ToolDefaultsSpec(
267
- tool_name="Google AI",
268
- defaults={
269
- "API_KEY": "putinyourkey",
270
- "MODEL": "gemini-2.5-pro",
271
- "MODELS_LIST": [
272
- "gemini-2.5-pro",
273
- "gemini-2.5-flash",
274
- "gemini-2.5-flash-lite",
275
- "gemini-2.0-flash",
276
- "gemini-2.0-flash-lite",
277
- "gemini-1.5-pro-latest",
278
- "gemini-1.5-flash-latest"
279
- ],
280
- "system_prompt": "You are a helpful assistant.",
281
- "temperature": 0.7,
282
- "topK": 40,
283
- "topP": 0.95,
284
- "candidateCount": 1,
285
- "maxOutputTokens": 8192,
286
- "stopSequences": ""
287
- },
288
- required_keys={"API_KEY", "MODEL"},
289
- description="Google AI (Gemini) integration"
290
- ))
291
-
292
- # Azure AI - Updated December 2025
293
- # Latest: GPT-4.1 series (4.1, 4.1-mini, 4.1-nano), GPT-4o being retired Feb 2026
294
- self.register_tool(ToolDefaultsSpec(
295
- tool_name="Azure AI",
296
- defaults={
297
- "API_KEY": "putinyourkey",
298
- "MODEL": "gpt-4.1",
299
- "MODELS_LIST": [
300
- "gpt-4.1",
301
- "gpt-4.1-mini",
302
- "gpt-4.1-nano",
303
- "gpt-4o",
304
- "gpt-4o-mini",
305
- "gpt-4-turbo"
306
- ],
307
- "ENDPOINT": "",
308
- "API_VERSION": "2024-10-21",
309
- "system_prompt": "You are a helpful assistant.",
310
- "temperature": 0.7,
311
- "max_tokens": 4096,
312
- "top_p": 1.0,
313
- "frequency_penalty": 0.0,
314
- "presence_penalty": 0.0,
315
- "seed": "",
316
- "stop": ""
317
- },
318
- required_keys={"API_KEY", "MODEL", "ENDPOINT"},
319
- description="Azure OpenAI integration"
320
- ))
321
-
322
- # Anthropic AI - Updated December 2025
323
- # Latest: Claude 4 series (Opus 4.5, Sonnet 4.5, Sonnet 4, Opus 4)
324
- self.register_tool(ToolDefaultsSpec(
325
- tool_name="Anthropic AI",
326
- defaults={
327
- "API_KEY": "putinyourkey",
328
- "MODEL": "claude-sonnet-4-5-20250929",
329
- "MODELS_LIST": [
330
- "claude-sonnet-4-5-20250929",
331
- "claude-opus-4-5-20251124",
332
- "claude-sonnet-4-20250522",
333
- "claude-opus-4-20250522",
334
- "claude-3-5-sonnet-20241022",
335
- "claude-3-5-haiku-20241022",
336
- "claude-3-opus-20240229"
337
- ],
338
- "system": "You are a helpful assistant.",
339
- "max_tokens": 4096,
340
- "temperature": 0.7,
341
- "top_p": 0.9,
342
- "top_k": 40,
343
- "stop_sequences": ""
344
- },
345
- required_keys={"API_KEY", "MODEL"},
346
- description="Anthropic Claude AI integration"
347
- ))
348
-
349
- # OpenAI - Updated December 2025
350
- # Latest: GPT-4.1 series, GPT-4o being retired Feb 2026
351
- self.register_tool(ToolDefaultsSpec(
352
- tool_name="OpenAI",
353
- defaults={
354
- "API_KEY": "putinyourkey",
355
- "MODEL": "gpt-4.1",
356
- "MODELS_LIST": [
357
- "gpt-4.1",
358
- "gpt-4.1-mini",
359
- "gpt-4.1-nano",
360
- "gpt-4o",
361
- "gpt-4o-mini",
362
- "gpt-4-turbo",
363
- "o1-preview",
364
- "o1-mini"
365
- ],
366
- "system_prompt": "You are a helpful assistant.",
367
- "temperature": 0.7,
368
- "max_tokens": 4096,
369
- "top_p": 1.0,
370
- "frequency_penalty": 0.0,
371
- "presence_penalty": 0.0,
372
- "seed": "",
373
- "response_format": "text",
374
- "stop": ""
375
- },
376
- required_keys={"API_KEY", "MODEL"},
377
- description="OpenAI GPT integration"
378
- ))
379
-
380
- # Cohere AI - Updated December 2025
381
- # Latest: Command A (March 2025), Command R+ (08-2024), Command R (08-2024)
382
- self.register_tool(ToolDefaultsSpec(
383
- tool_name="Cohere AI",
384
- defaults={
385
- "API_KEY": "putinyourkey",
386
- "MODEL": "command-a-03-2025",
387
- "MODELS_LIST": [
388
- "command-a-03-2025",
389
- "command-r-plus-08-2024",
390
- "command-r-08-2024",
391
- "command-r-plus",
392
- "command-r",
393
- "command-light"
394
- ],
395
- "preamble": "You are a helpful assistant.",
396
- "temperature": 0.7,
397
- "max_tokens": 4000,
398
- "k": 50,
399
- "p": 0.75,
400
- "frequency_penalty": 0.0,
401
- "presence_penalty": 0.0,
402
- "stop_sequences": "",
403
- "citation_quality": "accurate"
404
- },
405
- required_keys={"API_KEY", "MODEL"},
406
- description="Cohere AI integration"
407
- ))
408
-
409
- # HuggingFace AI - Updated December 2025
410
- # Latest: Llama 3.3, Mistral Small 3, Qwen 2.5
411
- self.register_tool(ToolDefaultsSpec(
412
- tool_name="HuggingFace AI",
413
- defaults={
414
- "API_KEY": "putinyourkey",
415
- "MODEL": "meta-llama/Llama-3.3-70B-Instruct",
416
- "MODELS_LIST": [
417
- "meta-llama/Llama-3.3-70B-Instruct",
418
- "meta-llama/Meta-Llama-3.1-70B-Instruct",
419
- "meta-llama/Meta-Llama-3.1-8B-Instruct",
420
- "mistralai/Mistral-Small-3-Instruct",
421
- "mistralai/Mistral-7B-Instruct-v0.3",
422
- "Qwen/Qwen2.5-72B-Instruct",
423
- "google/gemma-2-27b-it"
424
- ],
425
- "system_prompt": "You are a helpful assistant.",
426
- "max_tokens": 4096,
427
- "temperature": 0.7,
428
- "top_p": 0.95,
429
- "stop_sequences": "",
430
- "seed": ""
431
- },
432
- required_keys={"API_KEY", "MODEL"},
433
- description="HuggingFace AI integration"
434
- ))
435
-
436
- # Groq AI - Updated December 2025
437
- # Latest: Llama 3.3 70B, Mixtral 8x7B, Gemma 2
438
- self.register_tool(ToolDefaultsSpec(
439
- tool_name="Groq AI",
440
- defaults={
441
- "API_KEY": "putinyourkey",
442
- "MODEL": "llama-3.3-70b-versatile",
443
- "MODELS_LIST": [
444
- "llama-3.3-70b-versatile",
445
- "llama-3.1-70b-versatile",
446
- "llama-3.1-8b-instant",
447
- "mixtral-8x7b-32768",
448
- "gemma2-9b-it",
449
- "llama-guard-3-8b"
450
- ],
451
- "system_prompt": "You are a helpful assistant.",
452
- "temperature": 0.7,
453
- "max_tokens": 8192,
454
- "top_p": 1.0,
455
- "frequency_penalty": 0.0,
456
- "presence_penalty": 0.0,
457
- "stop": "",
458
- "seed": "",
459
- "response_format": "text"
460
- },
461
- required_keys={"API_KEY", "MODEL"},
462
- description="Groq AI integration"
463
- ))
464
-
465
- # OpenRouterAI - Updated December 2025
466
- # Latest: Claude Opus 4.5, GPT-4.1, Gemini 2.5, DeepSeek 3.2
467
- self.register_tool(ToolDefaultsSpec(
468
- tool_name="OpenRouterAI",
469
- defaults={
470
- "API_KEY": "putinyourkey",
471
- "MODEL": "anthropic/claude-sonnet-4.5",
472
- "MODELS_LIST": [
473
- "anthropic/claude-sonnet-4.5",
474
- "anthropic/claude-opus-4.5",
475
- "openai/gpt-4.1",
476
- "google/gemini-2.5-pro",
477
- "google/gemini-2.5-flash",
478
- "deepseek/deepseek-chat",
479
- "meta-llama/llama-3.3-70b-instruct",
480
- "google/gemini-2.0-flash:free",
481
- "meta-llama/llama-3.1-8b-instruct:free"
482
- ],
483
- "system_prompt": "You are a helpful assistant.",
484
- "temperature": 0.7,
485
- "max_tokens": 4096,
486
- "top_p": 1.0,
487
- "top_k": 0,
488
- "frequency_penalty": 0.0,
489
- "presence_penalty": 0.0,
490
- "repetition_penalty": 1.0,
491
- "seed": "",
492
- "stop": ""
493
- },
494
- required_keys={"API_KEY", "MODEL"},
495
- description="OpenRouter AI integration"
496
- ))
497
-
498
- # AWS Bedrock - Updated December 2025
499
- # Latest: Claude 3.5 Sonnet v2, Claude 3.5 Haiku, Llama 3.2, Titan G1
500
- self.register_tool(ToolDefaultsSpec(
501
- tool_name="AWS Bedrock",
502
- defaults={
503
- "ACCESS_KEY": "",
504
- "SECRET_KEY": "",
505
- "REGION": "us-east-1",
506
- "MODEL": "anthropic.claude-3-5-sonnet-20241022-v2:0",
507
- "MODELS_LIST": [
508
- "anthropic.claude-3-5-sonnet-20241022-v2:0",
509
- "anthropic.claude-3-5-haiku-20241022-v1:0",
510
- "anthropic.claude-3-opus-20240229-v1:0",
511
- "anthropic.claude-3-sonnet-20240229-v1:0",
512
- "meta.llama3-2-90b-instruct-v1:0",
513
- "meta.llama3-2-11b-instruct-v1:0",
514
- "meta.llama3-1-70b-instruct-v1:0",
515
- "meta.llama3-1-8b-instruct-v1:0",
516
- "amazon.titan-text-premier-v1:0",
517
- "amazon.titan-text-express-v1",
518
- "mistral.mixtral-8x7b-instruct-v0:1"
519
- ],
520
- "system_prompt": "You are a helpful assistant.",
521
- "temperature": 0.7,
522
- "max_tokens": 4096,
523
- "top_p": 0.9,
524
- "top_k": 40
525
- },
526
- required_keys={"ACCESS_KEY", "SECRET_KEY", "REGION", "MODEL"},
527
- description="AWS Bedrock AI integration"
528
- ))
529
-
530
- # Vertex AI - Updated December 2025
531
- # Latest: Gemini 2.5 series (Pro, Flash, Flash-Lite)
532
- self.register_tool(ToolDefaultsSpec(
533
- tool_name="Vertex AI",
534
- defaults={
535
- "PROJECT_ID": "",
536
- "LOCATION": "us-central1",
537
- "MODEL": "gemini-2.5-pro",
538
- "MODELS_LIST": [
539
- "gemini-2.5-pro",
540
- "gemini-2.5-flash",
541
- "gemini-2.5-flash-lite",
542
- "gemini-2.0-flash",
543
- "gemini-2.0-flash-lite",
544
- "gemini-1.5-pro",
545
- "gemini-1.5-flash"
546
- ],
547
- "system_prompt": "You are a helpful assistant.",
548
- "temperature": 0.7,
549
- "max_tokens": 8192,
550
- "top_p": 0.95,
551
- "top_k": 40
552
- },
553
- required_keys={"PROJECT_ID", "LOCATION", "MODEL"},
554
- description="Google Vertex AI integration"
555
- ))
556
-
557
- # AI Tools (managed by AIToolsWidget)
558
- self.register_tool(ToolDefaultsSpec(
559
- tool_name="AI Tools",
560
- defaults={},
561
- description="AI Tools settings managed by AIToolsWidget"
562
- ))
563
-
564
- # Diff Viewer
565
- self.register_tool(ToolDefaultsSpec(
566
- tool_name="Diff Viewer",
567
- defaults={"option": "ignore_case"},
568
- description="Text diff comparison tool"
569
- ))
570
-
571
- # List Comparator
572
- self.register_tool(ToolDefaultsSpec(
573
- tool_name="List Comparator",
574
- defaults={
575
- "operation": "unique_to_first",
576
- "case_sensitive": False,
577
- "trim_whitespace": True
578
- },
579
- description="List comparison tool"
580
- ))
581
-
582
- # HTML Tool
583
- self.register_tool(ToolDefaultsSpec(
584
- tool_name="HTML Tool",
585
- defaults={
586
- "operation": "strip_tags",
587
- "preserve_links": False,
588
- "preserve_images": False
589
- },
590
- description="HTML processing tool"
591
- ))
592
-
593
- # Line Tools
594
- self.register_tool(ToolDefaultsSpec(
595
- tool_name="Line Tools",
596
- defaults={
597
- "duplicate_mode": "keep_first",
598
- "case_sensitive": True,
599
- "preserve_single": False,
600
- "number_format": "1. ",
601
- "start_number": 1,
602
- "skip_empty": False
603
- },
604
- required_keys={"duplicate_mode"},
605
- description="Line manipulation utilities"
606
- ))
607
-
608
- # Whitespace Tools
609
- self.register_tool(ToolDefaultsSpec(
610
- tool_name="Whitespace Tools",
611
- defaults={
612
- "trim_mode": "both",
613
- "preserve_indent": False,
614
- "tab_size": 4,
615
- "line_ending": "lf"
616
- },
617
- description="Whitespace manipulation utilities"
618
- ))
619
-
620
- # Text Statistics
621
- self.register_tool(ToolDefaultsSpec(
622
- tool_name="Text Statistics",
623
- defaults={
624
- "words_per_minute": 200,
625
- "show_frequency": True,
626
- "frequency_count": 10
627
- },
628
- description="Comprehensive text analysis tool"
629
- ))
630
-
631
- # Hash Generator
632
- self.register_tool(ToolDefaultsSpec(
633
- tool_name="Hash Generator",
634
- defaults={
635
- "algorithms": ["md5", "sha256"],
636
- "uppercase": False
637
- },
638
- description="Cryptographic hash generation tool"
639
- ))
640
-
641
- # Markdown Tools
642
- self.register_tool(ToolDefaultsSpec(
643
- tool_name="Markdown Tools",
644
- defaults={
645
- "preserve_links_text": True,
646
- "include_images": False,
647
- "header_format": "indented",
648
- "csv_delimiter": ","
649
- },
650
- description="Markdown processing utilities"
651
- ))
652
-
653
- # String Escape Tool
654
- self.register_tool(ToolDefaultsSpec(
655
- tool_name="String Escape Tool",
656
- defaults={
657
- "format": "json",
658
- "mode": "escape",
659
- "plus_spaces": False
660
- },
661
- description="String escape/unescape utilities"
662
- ))
663
-
664
- # Number Base Converter
665
- self.register_tool(ToolDefaultsSpec(
666
- tool_name="Number Base Converter",
667
- defaults={
668
- "input_base": "decimal",
669
- "output_base": "hex",
670
- "uppercase": True,
671
- "show_prefix": True
672
- },
673
- description="Number base conversion tool"
674
- ))
675
-
676
- # Text Wrapper
677
- self.register_tool(ToolDefaultsSpec(
678
- tool_name="Text Wrapper",
679
- defaults={
680
- "wrap_width": 80,
681
- "justify_mode": "left",
682
- "justify_width": 80,
683
- "prefix": "",
684
- "suffix": "",
685
- "skip_empty": True,
686
- "indent_size": 4,
687
- "indent_char": "space",
688
- "quote_style": "double"
689
- },
690
- description="Text wrapping and formatting tool"
691
- ))
692
-
693
- # Slug Generator
694
- self.register_tool(ToolDefaultsSpec(
695
- tool_name="Slug Generator",
696
- defaults={
697
- "separator": "-",
698
- "lowercase": True,
699
- "transliterate": True,
700
- "max_length": 0,
701
- "remove_stopwords": False
702
- },
703
- description="URL-friendly slug generation tool"
704
- ))
705
-
706
- # Column Tools
707
- self.register_tool(ToolDefaultsSpec(
708
- tool_name="Column Tools",
709
- defaults={
710
- "delimiter": ",",
711
- "quote_char": "\"",
712
- "has_header": True
713
- },
714
- description="Column and CSV manipulation tool"
715
- ))
716
-
717
- # Timestamp Converter
718
- self.register_tool(ToolDefaultsSpec(
719
- tool_name="Timestamp Converter",
720
- defaults={
721
- "input_format": "unix",
722
- "output_format": "iso",
723
- "use_utc": False,
724
- "custom_format": "%Y-%m-%d %H:%M:%S",
725
- "show_relative": False
726
- },
727
- description="Date/time conversion tool"
728
- ))
729
-
730
- # ASCII Art Generator
731
- self.register_tool(ToolDefaultsSpec(
732
- tool_name="ASCII Art Generator",
733
- defaults={
734
- "font": "standard",
735
- "width": 80
736
- },
737
- description="Text to ASCII art conversion tool"
738
- ))
739
-
740
- # Extraction Tools
741
- self.register_tool(ToolDefaultsSpec(
742
- tool_name="Extraction Tools",
743
- defaults={
744
- "Email Extraction Tool": {"omit_duplicates": False, "hide_counts": True, "sort_emails": False, "only_domain": False},
745
- "HTML Extraction Tool": {},
746
- "Regex Extractor": {"pattern": "", "match_mode": "all_per_line", "omit_duplicates": False, "hide_counts": True, "sort_results": False, "case_sensitive": False},
747
- "URL and Link Extractor": {"extract_href": False, "extract_https": False, "extract_any_protocol": False, "extract_markdown": False, "filter_text": ""}
748
- },
749
- description="Text extraction utilities"
750
- ))
751
-
752
- # Register application-level defaults
753
- self._register_app_defaults()
754
-
755
- def _register_app_defaults(self) -> None:
756
- """Register application-level default settings."""
757
- import os
758
-
759
- default_path = os.path.join(os.path.expanduser('~'), 'Downloads')
760
-
761
- self._app_defaults = {
762
- "export_path": default_path,
763
- "debug_level": "INFO",
764
- "selected_tool": "Case Tool",
765
- "active_input_tab": 0,
766
- "active_output_tab": 0,
767
- "performance_settings": {
768
- "mode": "automatic",
769
- "async_processing": {
770
- "enabled": True,
771
- "threshold_kb": 10,
772
- "max_workers": 2,
773
- "chunk_size_kb": 50
774
- },
775
- "caching": {
776
- "enabled": True,
777
- "stats_cache_size": 1000,
778
- "regex_cache_size": 100,
779
- "content_cache_size_mb": 50,
780
- "processing_cache_size": 500
781
- },
782
- "memory_management": {
783
- "enabled": True,
784
- "gc_optimization": True,
785
- "memory_pool": True,
786
- "leak_detection": True,
787
- "memory_threshold_mb": 500
788
- },
789
- "ui_optimizations": {
790
- "enabled": True,
791
- "efficient_line_numbers": True,
792
- "progressive_search": True,
793
- "debounce_delay_ms": 300,
794
- "lazy_updates": True
795
- }
796
- },
797
- "font_settings": {
798
- "text_font": {
799
- "family": "Source Code Pro",
800
- "size": 11,
801
- "fallback_family": "Consolas",
802
- "fallback_family_mac": "Monaco",
803
- "fallback_family_linux": "DejaVu Sans Mono"
804
- },
805
- "interface_font": {
806
- "family": "Segoe UI",
807
- "size": 9,
808
- "fallback_family": "Arial",
809
- "fallback_family_mac": "Helvetica",
810
- "fallback_family_linux": "Ubuntu"
811
- }
812
- },
813
- "dialog_settings": {
814
- "success": {
815
- "enabled": True,
816
- "description": "Success notifications for completed operations",
817
- "examples": ["File saved successfully", "Settings applied", "Export complete"]
818
- },
819
- "confirmation": {
820
- "enabled": True,
821
- "description": "Confirmation dialogs for destructive actions",
822
- "examples": ["Clear all tabs?", "Delete entry?", "Reset settings?"],
823
- "default_action": "yes"
824
- },
825
- "warning": {
826
- "enabled": True,
827
- "description": "Warning messages for potential issues",
828
- "examples": ["No data specified", "Invalid input detected", "Feature unavailable"]
829
- },
830
- "error": {
831
- "enabled": True,
832
- "locked": True,
833
- "description": "Error messages for critical issues (cannot be disabled)",
834
- "examples": ["File not found", "Network error", "Invalid configuration"]
835
- }
836
- }
837
- }
838
-
839
- def register_tool(self, spec: ToolDefaultsSpec) -> None:
840
- """
841
- Register a tool's default settings.
842
-
843
- Args:
844
- spec: ToolDefaultsSpec containing the tool's defaults
845
- """
846
- self._tool_specs[spec.tool_name] = spec
847
- self.logger.debug(f"Registered defaults for tool: {spec.tool_name}")
848
-
849
- def unregister_tool(self, tool_name: str) -> bool:
850
- """
851
- Unregister a tool's default settings.
852
-
853
- Args:
854
- tool_name: Name of the tool to unregister
855
-
856
- Returns:
857
- True if tool was unregistered, False if not found
858
- """
859
- if tool_name in self._tool_specs:
860
- del self._tool_specs[tool_name]
861
- self.logger.debug(f"Unregistered defaults for tool: {tool_name}")
862
- return True
863
- return False
864
-
865
- def get_tool_defaults(self, tool_name: str) -> Dict[str, Any]:
866
- """
867
- Get default settings for a specific tool.
868
-
869
- Args:
870
- tool_name: Name of the tool
871
-
872
- Returns:
873
- Dictionary of default settings, empty dict if tool not found
874
- """
875
- spec = self._tool_specs.get(tool_name)
876
- if spec:
877
- return deepcopy(spec.defaults)
878
- return {}
879
-
880
- def get_tool_spec(self, tool_name: str) -> Optional[ToolDefaultsSpec]:
881
- """
882
- Get the full specification for a tool.
883
-
884
- Args:
885
- tool_name: Name of the tool
886
-
887
- Returns:
888
- ToolDefaultsSpec or None if not found
889
- """
890
- return self._tool_specs.get(tool_name)
891
-
892
- def get_all_tool_defaults(self) -> Dict[str, Dict[str, Any]]:
893
- """
894
- Get all tool default settings.
895
-
896
- Returns:
897
- Dictionary mapping tool names to their default settings
898
- """
899
- return {
900
- name: deepcopy(spec.defaults)
901
- for name, spec in self._tool_specs.items()
902
- }
903
-
904
- def get_all_defaults(self, tab_count: int = 7) -> Dict[str, Any]:
905
- """
906
- Get complete default settings including app-level and all tool defaults.
907
-
908
- Args:
909
- tab_count: Number of tabs for input/output (default 7)
910
-
911
- Returns:
912
- Complete default settings dictionary
913
- """
914
- defaults = deepcopy(self._app_defaults)
915
- defaults["input_tabs"] = [""] * tab_count
916
- defaults["output_tabs"] = [""] * tab_count
917
- defaults["tool_settings"] = self.get_all_tool_defaults()
918
- return defaults
919
-
920
- def get_registered_tools(self) -> List[str]:
921
- """
922
- Get list of all registered tool names.
923
-
924
- Returns:
925
- List of tool names
926
- """
927
- return list(self._tool_specs.keys())
928
-
929
- def validate_settings(self, settings: Dict[str, Any]) -> Tuple[bool, List[str]]:
930
- """
931
- Validate settings against registered schemas.
932
-
933
- Args:
934
- settings: Settings dictionary to validate
935
-
936
- Returns:
937
- Tuple of (is_valid, list of error messages)
938
- """
939
- errors = []
940
-
941
- # Check for required app-level keys
942
- required_app_keys = {"export_path", "debug_level", "selected_tool"}
943
- for key in required_app_keys:
944
- if key not in settings:
945
- errors.append(f"Missing required app setting: {key}")
946
-
947
- # Validate tool settings
948
- tool_settings = settings.get("tool_settings", {})
949
-
950
- for tool_name, spec in self._tool_specs.items():
951
- if tool_name not in tool_settings:
952
- # Tool settings missing entirely - not necessarily an error
953
- # as they will be populated from defaults
954
- continue
955
-
956
- tool_config = tool_settings[tool_name]
957
-
958
- # Check required keys for this tool
959
- for required_key in spec.required_keys:
960
- if required_key not in tool_config:
961
- errors.append(f"Tool '{tool_name}' missing required key: {required_key}")
962
-
963
- return (len(errors) == 0, errors)
964
-
965
- def validate_tool_settings(self, tool_name: str, settings: Dict[str, Any]) -> Tuple[bool, List[str]]:
966
- """
967
- Validate settings for a specific tool.
968
-
969
- Args:
970
- tool_name: Name of the tool
971
- settings: Tool settings to validate
972
-
973
- Returns:
974
- Tuple of (is_valid, list of error messages)
975
- """
976
- errors = []
977
- spec = self._tool_specs.get(tool_name)
978
-
979
- if not spec:
980
- return (True, []) # Unknown tool, no validation
981
-
982
- for required_key in spec.required_keys:
983
- if required_key not in settings:
984
- errors.append(f"Missing required key: {required_key}")
985
-
986
- return (len(errors) == 0, errors)
987
-
988
- def deep_merge(self, base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
989
- """
990
- Deep merge two dictionaries, with override taking precedence.
991
-
992
- Args:
993
- base: Base dictionary (defaults)
994
- override: Override dictionary (user settings)
995
-
996
- Returns:
997
- Merged dictionary
998
- """
999
- result = deepcopy(base)
1000
-
1001
- for key, value in override.items():
1002
- if key in result and isinstance(result[key], dict) and isinstance(value, dict):
1003
- result[key] = self.deep_merge(result[key], value)
1004
- else:
1005
- result[key] = deepcopy(value)
1006
-
1007
- return result
1008
-
1009
- def merge_with_defaults(self, user_settings: Dict[str, Any], tab_count: int = 7) -> Dict[str, Any]:
1010
- """
1011
- Merge user settings with defaults, filling in any missing values.
1012
-
1013
- Args:
1014
- user_settings: User's current settings
1015
- tab_count: Number of tabs for input/output
1016
-
1017
- Returns:
1018
- Complete settings with defaults filled in
1019
- """
1020
- defaults = self.get_all_defaults(tab_count)
1021
- merged = self.deep_merge(defaults, user_settings)
1022
-
1023
- # Ensure tool_settings has all registered tools
1024
- if "tool_settings" not in merged:
1025
- merged["tool_settings"] = {}
1026
-
1027
- for tool_name in self._tool_specs:
1028
- if tool_name not in merged["tool_settings"]:
1029
- merged["tool_settings"][tool_name] = self.get_tool_defaults(tool_name)
1030
- else:
1031
- # Merge tool-specific settings with defaults
1032
- tool_defaults = self.get_tool_defaults(tool_name)
1033
- merged["tool_settings"][tool_name] = self.deep_merge(
1034
- tool_defaults,
1035
- merged["tool_settings"][tool_name]
1036
- )
1037
-
1038
- return merged
1039
-
1040
- def get_missing_settings(self, settings: Dict[str, Any]) -> Dict[str, Any]:
1041
- """
1042
- Get settings that are missing from the provided settings.
1043
-
1044
- Args:
1045
- settings: Current settings to check
1046
-
1047
- Returns:
1048
- Dictionary of missing settings with their default values
1049
- """
1050
- defaults = self.get_all_defaults()
1051
- missing = {}
1052
-
1053
- def find_missing(default_dict: Dict, current_dict: Dict, path: str = "") -> None:
1054
- for key, value in default_dict.items():
1055
- current_path = f"{path}.{key}" if path else key
1056
-
1057
- if key not in current_dict:
1058
- missing[current_path] = value
1059
- elif isinstance(value, dict) and isinstance(current_dict.get(key), dict):
1060
- find_missing(value, current_dict[key], current_path)
1061
-
1062
- find_missing(defaults, settings)
1063
- return missing
1064
-
1065
-
1066
- # Singleton instance for easy access
1067
- _registry: Optional[SettingsDefaultsRegistry] = None
1068
-
1069
-
1070
- def get_registry() -> SettingsDefaultsRegistry:
1071
- """
1072
- Get the singleton settings defaults registry instance.
1073
-
1074
- Returns:
1075
- SettingsDefaultsRegistry instance
1076
- """
1077
- global _registry
1078
- if _registry is None:
1079
- _registry = SettingsDefaultsRegistry()
1080
- return _registry
1081
-
1082
-
1083
- def reset_registry() -> None:
1084
- """Reset the registry singleton (mainly for testing)."""
1085
- global _registry
1086
- _registry = None
1087
- SettingsDefaultsRegistry._instance = None
1
+ """
2
+ Settings Defaults Registry Module
3
+
4
+ Centralizes all tool default settings into a single registry system for consistent
5
+ first-launch initialization. Provides schema validation, deep merge capability,
6
+ and backward compatibility with existing tools.
7
+
8
+ Author: Pomera AI Commander Team
9
+ """
10
+
11
+ from dataclasses import dataclass, field
12
+ from typing import Any, Dict, List, Optional, Callable, Set, Tuple
13
+ from copy import deepcopy
14
+ import logging
15
+
16
+
17
+ @dataclass
18
+ class ToolDefaultsSpec:
19
+ """
20
+ Specification for a tool's default settings.
21
+
22
+ Attributes:
23
+ tool_name: Unique identifier for the tool (must match key in tool_settings)
24
+ defaults: Dictionary of default settings for the tool
25
+ required_keys: Set of keys that must be present in settings
26
+ description: Human-readable description of the tool
27
+ version: Version of the settings schema (for future migrations)
28
+ """
29
+ tool_name: str
30
+ defaults: Dict[str, Any]
31
+ required_keys: Set[str] = field(default_factory=set)
32
+ description: str = ""
33
+ version: str = "1.0"
34
+
35
+
36
+ class SettingsValidationError(Exception):
37
+ """Raised when settings validation fails."""
38
+ pass
39
+
40
+
41
+ class SettingsDefaultsRegistry:
42
+ """
43
+ Central registry for all tool default settings.
44
+
45
+ This registry provides:
46
+ - Centralized storage of all tool defaults
47
+ - Schema validation for tool settings
48
+ - Deep merge capability for user settings with defaults
49
+ - Backward compatibility with existing tools
50
+ """
51
+
52
+ _instance: Optional['SettingsDefaultsRegistry'] = None
53
+
54
+ def __new__(cls) -> 'SettingsDefaultsRegistry':
55
+ """Singleton pattern to ensure only one registry exists."""
56
+ if cls._instance is None:
57
+ cls._instance = super().__new__(cls)
58
+ cls._instance._initialized = False
59
+ return cls._instance
60
+
61
+ def __init__(self):
62
+ """Initialize the registry with default tool specifications."""
63
+ if self._initialized:
64
+ return
65
+
66
+ self.logger = logging.getLogger(__name__)
67
+ self._tool_specs: Dict[str, ToolDefaultsSpec] = {}
68
+ self._app_defaults: Dict[str, Any] = {}
69
+ self._initialized = True
70
+
71
+ # Register all built-in tool defaults
72
+ self._register_builtin_defaults()
73
+
74
+ def _register_builtin_defaults(self) -> None:
75
+ """Register all built-in tool default settings."""
76
+
77
+ # Case Tool - Updated with new exclusions per requirements
78
+ self.register_tool(ToolDefaultsSpec(
79
+ tool_name="Case Tool",
80
+ defaults={
81
+ "mode": "Sentence",
82
+ "exclusions": "a\nan\nthe\nand\nbut\nor\nfor\nnor\non\nat\nto\nfrom\nby\nwith\nin\nof"
83
+ },
84
+ required_keys={"mode"},
85
+ description="Text case conversion tool"
86
+ ))
87
+
88
+ # Base64 Encoder/Decoder
89
+ self.register_tool(ToolDefaultsSpec(
90
+ tool_name="Base64 Encoder/Decoder",
91
+ defaults={"mode": "encode"},
92
+ required_keys={"mode"},
93
+ description="Base64 encoding and decoding tool"
94
+ ))
95
+
96
+ # JSON/XML Tool
97
+ self.register_tool(ToolDefaultsSpec(
98
+ tool_name="JSON/XML Tool",
99
+ defaults={
100
+ "operation": "json_to_xml",
101
+ "json_indent": 2,
102
+ "xml_indent": 2,
103
+ "preserve_attributes": True,
104
+ "sort_keys": False,
105
+ "array_wrapper": "item",
106
+ "root_element": "root",
107
+ "jsonpath_query": "$",
108
+ "xpath_query": "//*"
109
+ },
110
+ required_keys={"operation"},
111
+ description="JSON and XML conversion tool"
112
+ ))
113
+
114
+ # Cron Tool
115
+ self.register_tool(ToolDefaultsSpec(
116
+ tool_name="Cron Tool",
117
+ defaults={
118
+ "action": "parse_explain",
119
+ "preset_category": "Daily Schedules",
120
+ "preset_pattern": "Daily at midnight",
121
+ "compare_expressions": "",
122
+ "next_runs_count": 10
123
+ },
124
+ required_keys={"action"},
125
+ description="Cron expression parser and generator"
126
+ ))
127
+
128
+ # Find & Replace Text
129
+ self.register_tool(ToolDefaultsSpec(
130
+ tool_name="Find & Replace Text",
131
+ defaults={
132
+ "find": "",
133
+ "replace": "",
134
+ "mode": "Text",
135
+ "option": "ignore_case",
136
+ "find_history": [],
137
+ "replace_history": []
138
+ },
139
+ required_keys={"mode"},
140
+ description="Find and replace text tool"
141
+ ))
142
+
143
+ # Generator Tools
144
+ self.register_tool(ToolDefaultsSpec(
145
+ tool_name="Generator Tools",
146
+ defaults={
147
+ "Strong Password Generator": {
148
+ "length": 20,
149
+ "numbers": "",
150
+ "symbols": ""
151
+ },
152
+ "Repeating Text Generator": {
153
+ "times": 5,
154
+ "separator": "+"
155
+ }
156
+ },
157
+ description="Various text and data generators"
158
+ ))
159
+
160
+ # Sorter Tools
161
+ self.register_tool(ToolDefaultsSpec(
162
+ tool_name="Sorter Tools",
163
+ defaults={
164
+ "Number Sorter": {
165
+ "order": "ascending"
166
+ },
167
+ "Alphabetical Sorter": {
168
+ "order": "ascending",
169
+ "unique_only": False,
170
+ "trim": False
171
+ }
172
+ },
173
+ description="Text and number sorting tools"
174
+ ))
175
+
176
+ # URL and Link Extractor
177
+ self.register_tool(ToolDefaultsSpec(
178
+ tool_name="URL and Link Extractor",
179
+ defaults={
180
+ "extract_href": False,
181
+ "extract_https": False,
182
+ "extract_any_protocol": False,
183
+ "extract_markdown": False,
184
+ "filter_text": ""
185
+ },
186
+ description="URL and link extraction tool"
187
+ ))
188
+
189
+ # Email Extraction Tool
190
+ self.register_tool(ToolDefaultsSpec(
191
+ tool_name="Email Extraction Tool",
192
+ defaults={
193
+ "omit_duplicates": False,
194
+ "hide_counts": True,
195
+ "sort_emails": False,
196
+ "only_domain": False
197
+ },
198
+ description="Email address extraction tool"
199
+ ))
200
+
201
+ # Regex Extractor
202
+ self.register_tool(ToolDefaultsSpec(
203
+ tool_name="Regex Extractor",
204
+ defaults={
205
+ "pattern": "",
206
+ "match_mode": "all_per_line",
207
+ "omit_duplicates": False,
208
+ "hide_counts": True,
209
+ "sort_results": False,
210
+ "case_sensitive": False
211
+ },
212
+ required_keys={"match_mode"},
213
+ description="Regular expression extraction tool"
214
+ ))
215
+
216
+ # Email Header Analyzer
217
+ self.register_tool(ToolDefaultsSpec(
218
+ tool_name="Email Header Analyzer",
219
+ defaults={
220
+ "show_timestamps": True,
221
+ "show_delays": True,
222
+ "show_authentication": True,
223
+ "show_spam_score": True
224
+ },
225
+ description="Email header analysis tool"
226
+ ))
227
+
228
+ # Folder File Reporter
229
+ self.register_tool(ToolDefaultsSpec(
230
+ tool_name="Folder File Reporter",
231
+ defaults={
232
+ "last_input_folder": "",
233
+ "last_output_folder": "",
234
+ "field_selections": {
235
+ "path": True,
236
+ "name": True,
237
+ "size": True,
238
+ "date_modified": True
239
+ },
240
+ "separator": " | ",
241
+ "folders_only": False,
242
+ "recursion_mode": "full",
243
+ "recursion_depth": 2,
244
+ "size_format": "human",
245
+ "date_format": "%Y-%m-%d %H:%M:%S"
246
+ },
247
+ description="Folder and file reporting tool"
248
+ ))
249
+
250
+ # URL Parser
251
+ self.register_tool(ToolDefaultsSpec(
252
+ tool_name="URL Parser",
253
+ defaults={"ascii_decode": True},
254
+ description="URL parsing and analysis tool"
255
+ ))
256
+
257
+ # Word Frequency Counter
258
+ self.register_tool(ToolDefaultsSpec(
259
+ tool_name="Word Frequency Counter",
260
+ defaults={},
261
+ description="Word frequency analysis tool"
262
+ ))
263
+
264
+ # Google AI - Updated December 2025
265
+ # Latest: Gemini 2.5 series (Pro, Flash, Flash-Lite), Gemini 2.0 series
266
+ self.register_tool(ToolDefaultsSpec(
267
+ tool_name="Google AI",
268
+ defaults={
269
+ "API_KEY": "putinyourkey",
270
+ "MODEL": "gemini-2.5-pro",
271
+ "MODELS_LIST": [
272
+ "gemini-2.5-pro",
273
+ "gemini-2.5-flash",
274
+ "gemini-2.5-flash-lite",
275
+ "gemini-2.0-flash",
276
+ "gemini-2.0-flash-lite",
277
+ "gemini-1.5-pro-latest",
278
+ "gemini-1.5-flash-latest"
279
+ ],
280
+ "system_prompt": "You are a helpful assistant.",
281
+ "temperature": 0.7,
282
+ "topK": 40,
283
+ "topP": 0.95,
284
+ "candidateCount": 1,
285
+ "maxOutputTokens": 8192,
286
+ "stopSequences": ""
287
+ },
288
+ required_keys={"API_KEY", "MODEL"},
289
+ description="Google AI (Gemini) integration"
290
+ ))
291
+
292
+ # Azure AI - Updated December 2025
293
+ # Latest: GPT-4.1 series (4.1, 4.1-mini, 4.1-nano), GPT-4o being retired Feb 2026
294
+ self.register_tool(ToolDefaultsSpec(
295
+ tool_name="Azure AI",
296
+ defaults={
297
+ "API_KEY": "putinyourkey",
298
+ "MODEL": "gpt-4.1",
299
+ "MODELS_LIST": [
300
+ "gpt-4.1",
301
+ "gpt-4.1-mini",
302
+ "gpt-4.1-nano",
303
+ "gpt-4o",
304
+ "gpt-4o-mini",
305
+ "gpt-4-turbo"
306
+ ],
307
+ "ENDPOINT": "",
308
+ "API_VERSION": "2024-10-21",
309
+ "system_prompt": "You are a helpful assistant.",
310
+ "temperature": 0.7,
311
+ "max_tokens": 4096,
312
+ "top_p": 1.0,
313
+ "frequency_penalty": 0.0,
314
+ "presence_penalty": 0.0,
315
+ "seed": "",
316
+ "stop": ""
317
+ },
318
+ required_keys={"API_KEY", "MODEL", "ENDPOINT"},
319
+ description="Azure OpenAI integration"
320
+ ))
321
+
322
+ # Anthropic AI - Updated December 2025
323
+ # Latest: Claude 4 series (Opus 4.5, Sonnet 4.5, Sonnet 4, Opus 4)
324
+ self.register_tool(ToolDefaultsSpec(
325
+ tool_name="Anthropic AI",
326
+ defaults={
327
+ "API_KEY": "putinyourkey",
328
+ "MODEL": "claude-sonnet-4-5-20250929",
329
+ "MODELS_LIST": [
330
+ "claude-sonnet-4-5-20250929",
331
+ "claude-opus-4-5-20251124",
332
+ "claude-sonnet-4-20250522",
333
+ "claude-opus-4-20250522",
334
+ "claude-3-5-sonnet-20241022",
335
+ "claude-3-5-haiku-20241022",
336
+ "claude-3-opus-20240229"
337
+ ],
338
+ "system": "You are a helpful assistant.",
339
+ "max_tokens": 4096,
340
+ "temperature": 0.7,
341
+ "top_p": 0.9,
342
+ "top_k": 40,
343
+ "stop_sequences": ""
344
+ },
345
+ required_keys={"API_KEY", "MODEL"},
346
+ description="Anthropic Claude AI integration"
347
+ ))
348
+
349
+ # OpenAI - Updated December 2025
350
+ # Latest: GPT-4.1 series, GPT-4o being retired Feb 2026
351
+ self.register_tool(ToolDefaultsSpec(
352
+ tool_name="OpenAI",
353
+ defaults={
354
+ "API_KEY": "putinyourkey",
355
+ "MODEL": "gpt-4.1",
356
+ "MODELS_LIST": [
357
+ "gpt-4.1",
358
+ "gpt-4.1-mini",
359
+ "gpt-4.1-nano",
360
+ "gpt-4o",
361
+ "gpt-4o-mini",
362
+ "gpt-4-turbo",
363
+ "o1-preview",
364
+ "o1-mini"
365
+ ],
366
+ "system_prompt": "You are a helpful assistant.",
367
+ "temperature": 0.7,
368
+ "max_tokens": 4096,
369
+ "top_p": 1.0,
370
+ "frequency_penalty": 0.0,
371
+ "presence_penalty": 0.0,
372
+ "seed": "",
373
+ "response_format": "text",
374
+ "stop": ""
375
+ },
376
+ required_keys={"API_KEY", "MODEL"},
377
+ description="OpenAI GPT integration"
378
+ ))
379
+
380
+ # Cohere AI - Updated December 2025
381
+ # Latest: Command A (March 2025), Command R+ (08-2024), Command R (08-2024)
382
+ self.register_tool(ToolDefaultsSpec(
383
+ tool_name="Cohere AI",
384
+ defaults={
385
+ "API_KEY": "putinyourkey",
386
+ "MODEL": "command-a-03-2025",
387
+ "MODELS_LIST": [
388
+ "command-a-03-2025",
389
+ "command-r-plus-08-2024",
390
+ "command-r-08-2024",
391
+ "command-r-plus",
392
+ "command-r",
393
+ "command-light"
394
+ ],
395
+ "preamble": "You are a helpful assistant.",
396
+ "temperature": 0.7,
397
+ "max_tokens": 4000,
398
+ "k": 50,
399
+ "p": 0.75,
400
+ "frequency_penalty": 0.0,
401
+ "presence_penalty": 0.0,
402
+ "stop_sequences": "",
403
+ "citation_quality": "accurate"
404
+ },
405
+ required_keys={"API_KEY", "MODEL"},
406
+ description="Cohere AI integration"
407
+ ))
408
+
409
+ # HuggingFace AI - Updated December 2025
410
+ # Latest: Llama 3.3, Mistral Small 3, Qwen 2.5
411
+ self.register_tool(ToolDefaultsSpec(
412
+ tool_name="HuggingFace AI",
413
+ defaults={
414
+ "API_KEY": "putinyourkey",
415
+ "MODEL": "meta-llama/Llama-3.3-70B-Instruct",
416
+ "MODELS_LIST": [
417
+ "meta-llama/Llama-3.3-70B-Instruct",
418
+ "meta-llama/Meta-Llama-3.1-70B-Instruct",
419
+ "meta-llama/Meta-Llama-3.1-8B-Instruct",
420
+ "mistralai/Mistral-Small-3-Instruct",
421
+ "mistralai/Mistral-7B-Instruct-v0.3",
422
+ "Qwen/Qwen2.5-72B-Instruct",
423
+ "google/gemma-2-27b-it"
424
+ ],
425
+ "system_prompt": "You are a helpful assistant.",
426
+ "max_tokens": 4096,
427
+ "temperature": 0.7,
428
+ "top_p": 0.95,
429
+ "stop_sequences": "",
430
+ "seed": ""
431
+ },
432
+ required_keys={"API_KEY", "MODEL"},
433
+ description="HuggingFace AI integration"
434
+ ))
435
+
436
+ # Groq AI - Updated December 2025
437
+ # Latest: Llama 3.3 70B, Mixtral 8x7B, Gemma 2
438
+ self.register_tool(ToolDefaultsSpec(
439
+ tool_name="Groq AI",
440
+ defaults={
441
+ "API_KEY": "putinyourkey",
442
+ "MODEL": "llama-3.3-70b-versatile",
443
+ "MODELS_LIST": [
444
+ "llama-3.3-70b-versatile",
445
+ "llama-3.1-70b-versatile",
446
+ "llama-3.1-8b-instant",
447
+ "mixtral-8x7b-32768",
448
+ "gemma2-9b-it",
449
+ "llama-guard-3-8b"
450
+ ],
451
+ "system_prompt": "You are a helpful assistant.",
452
+ "temperature": 0.7,
453
+ "max_tokens": 8192,
454
+ "top_p": 1.0,
455
+ "frequency_penalty": 0.0,
456
+ "presence_penalty": 0.0,
457
+ "stop": "",
458
+ "seed": "",
459
+ "response_format": "text"
460
+ },
461
+ required_keys={"API_KEY", "MODEL"},
462
+ description="Groq AI integration"
463
+ ))
464
+
465
+ # OpenRouterAI - Updated December 2025
466
+ # Latest: Claude Opus 4.5, GPT-4.1, Gemini 2.5, DeepSeek 3.2
467
+ self.register_tool(ToolDefaultsSpec(
468
+ tool_name="OpenRouterAI",
469
+ defaults={
470
+ "API_KEY": "putinyourkey",
471
+ "MODEL": "anthropic/claude-sonnet-4.5",
472
+ "MODELS_LIST": [
473
+ "anthropic/claude-sonnet-4.5",
474
+ "anthropic/claude-opus-4.5",
475
+ "openai/gpt-4.1",
476
+ "google/gemini-2.5-pro",
477
+ "google/gemini-2.5-flash",
478
+ "deepseek/deepseek-chat",
479
+ "meta-llama/llama-3.3-70b-instruct",
480
+ "google/gemini-2.0-flash:free",
481
+ "meta-llama/llama-3.1-8b-instruct:free"
482
+ ],
483
+ "system_prompt": "You are a helpful assistant.",
484
+ "temperature": 0.7,
485
+ "max_tokens": 4096,
486
+ "top_p": 1.0,
487
+ "top_k": 0,
488
+ "frequency_penalty": 0.0,
489
+ "presence_penalty": 0.0,
490
+ "repetition_penalty": 1.0,
491
+ "seed": "",
492
+ "stop": ""
493
+ },
494
+ required_keys={"API_KEY", "MODEL"},
495
+ description="OpenRouter AI integration"
496
+ ))
497
+
498
+ # AWS Bedrock - Updated December 2025
499
+ # Latest: Claude 3.5 Sonnet v2, Claude 3.5 Haiku, Llama 3.2, Titan G1
500
+ self.register_tool(ToolDefaultsSpec(
501
+ tool_name="AWS Bedrock",
502
+ defaults={
503
+ "ACCESS_KEY": "",
504
+ "SECRET_KEY": "",
505
+ "REGION": "us-east-1",
506
+ "MODEL": "anthropic.claude-3-5-sonnet-20241022-v2:0",
507
+ "MODELS_LIST": [
508
+ "anthropic.claude-3-5-sonnet-20241022-v2:0",
509
+ "anthropic.claude-3-5-haiku-20241022-v1:0",
510
+ "anthropic.claude-3-opus-20240229-v1:0",
511
+ "anthropic.claude-3-sonnet-20240229-v1:0",
512
+ "meta.llama3-2-90b-instruct-v1:0",
513
+ "meta.llama3-2-11b-instruct-v1:0",
514
+ "meta.llama3-1-70b-instruct-v1:0",
515
+ "meta.llama3-1-8b-instruct-v1:0",
516
+ "amazon.titan-text-premier-v1:0",
517
+ "amazon.titan-text-express-v1",
518
+ "mistral.mixtral-8x7b-instruct-v0:1"
519
+ ],
520
+ "system_prompt": "You are a helpful assistant.",
521
+ "temperature": 0.7,
522
+ "max_tokens": 4096,
523
+ "top_p": 0.9,
524
+ "top_k": 40
525
+ },
526
+ required_keys={"ACCESS_KEY", "SECRET_KEY", "REGION", "MODEL"},
527
+ description="AWS Bedrock AI integration"
528
+ ))
529
+
530
+ # Vertex AI - Updated December 2025
531
+ # Latest: Gemini 2.5 series (Pro, Flash, Flash-Lite)
532
+ self.register_tool(ToolDefaultsSpec(
533
+ tool_name="Vertex AI",
534
+ defaults={
535
+ "PROJECT_ID": "",
536
+ "LOCATION": "us-central1",
537
+ "MODEL": "gemini-2.5-pro",
538
+ "MODELS_LIST": [
539
+ "gemini-2.5-pro",
540
+ "gemini-2.5-flash",
541
+ "gemini-2.5-flash-lite",
542
+ "gemini-2.0-flash",
543
+ "gemini-2.0-flash-lite",
544
+ "gemini-1.5-pro",
545
+ "gemini-1.5-flash"
546
+ ],
547
+ "system_prompt": "You are a helpful assistant.",
548
+ "temperature": 0.7,
549
+ "max_tokens": 8192,
550
+ "top_p": 0.95,
551
+ "top_k": 40
552
+ },
553
+ required_keys={"PROJECT_ID", "LOCATION", "MODEL"},
554
+ description="Google Vertex AI integration"
555
+ ))
556
+
557
+ # AI Tools (managed by AIToolsWidget)
558
+ self.register_tool(ToolDefaultsSpec(
559
+ tool_name="AI Tools",
560
+ defaults={},
561
+ description="AI Tools settings managed by AIToolsWidget"
562
+ ))
563
+
564
+ # Diff Viewer
565
+ self.register_tool(ToolDefaultsSpec(
566
+ tool_name="Diff Viewer",
567
+ defaults={"option": "ignore_case"},
568
+ description="Text diff comparison tool"
569
+ ))
570
+
571
+ # List Comparator
572
+ self.register_tool(ToolDefaultsSpec(
573
+ tool_name="List Comparator",
574
+ defaults={
575
+ "operation": "unique_to_first",
576
+ "case_sensitive": False,
577
+ "trim_whitespace": True
578
+ },
579
+ description="List comparison tool"
580
+ ))
581
+
582
+ # HTML Tool
583
+ self.register_tool(ToolDefaultsSpec(
584
+ tool_name="HTML Tool",
585
+ defaults={
586
+ "operation": "strip_tags",
587
+ "preserve_links": False,
588
+ "preserve_images": False
589
+ },
590
+ description="HTML processing tool"
591
+ ))
592
+
593
+ # Line Tools
594
+ self.register_tool(ToolDefaultsSpec(
595
+ tool_name="Line Tools",
596
+ defaults={
597
+ "duplicate_mode": "keep_first",
598
+ "case_sensitive": True,
599
+ "preserve_single": False,
600
+ "number_format": "1. ",
601
+ "start_number": 1,
602
+ "skip_empty": False
603
+ },
604
+ required_keys={"duplicate_mode"},
605
+ description="Line manipulation utilities"
606
+ ))
607
+
608
+ # Whitespace Tools
609
+ self.register_tool(ToolDefaultsSpec(
610
+ tool_name="Whitespace Tools",
611
+ defaults={
612
+ "trim_mode": "both",
613
+ "preserve_indent": False,
614
+ "tab_size": 4,
615
+ "line_ending": "lf"
616
+ },
617
+ description="Whitespace manipulation utilities"
618
+ ))
619
+
620
+ # Text Statistics
621
+ self.register_tool(ToolDefaultsSpec(
622
+ tool_name="Text Statistics",
623
+ defaults={
624
+ "words_per_minute": 200,
625
+ "show_frequency": True,
626
+ "frequency_count": 10
627
+ },
628
+ description="Comprehensive text analysis tool"
629
+ ))
630
+
631
+ # Hash Generator
632
+ self.register_tool(ToolDefaultsSpec(
633
+ tool_name="Hash Generator",
634
+ defaults={
635
+ "algorithms": ["md5", "sha256"],
636
+ "uppercase": False
637
+ },
638
+ description="Cryptographic hash generation tool"
639
+ ))
640
+
641
+ # Markdown Tools
642
+ self.register_tool(ToolDefaultsSpec(
643
+ tool_name="Markdown Tools",
644
+ defaults={
645
+ "preserve_links_text": True,
646
+ "include_images": False,
647
+ "header_format": "indented",
648
+ "csv_delimiter": ","
649
+ },
650
+ description="Markdown processing utilities"
651
+ ))
652
+
653
+ # String Escape Tool
654
+ self.register_tool(ToolDefaultsSpec(
655
+ tool_name="String Escape Tool",
656
+ defaults={
657
+ "format": "json",
658
+ "mode": "escape",
659
+ "plus_spaces": False
660
+ },
661
+ description="String escape/unescape utilities"
662
+ ))
663
+
664
+ # Number Base Converter
665
+ self.register_tool(ToolDefaultsSpec(
666
+ tool_name="Number Base Converter",
667
+ defaults={
668
+ "input_base": "decimal",
669
+ "output_base": "hex",
670
+ "uppercase": True,
671
+ "show_prefix": True
672
+ },
673
+ description="Number base conversion tool"
674
+ ))
675
+
676
+ # Text Wrapper
677
+ self.register_tool(ToolDefaultsSpec(
678
+ tool_name="Text Wrapper",
679
+ defaults={
680
+ "wrap_width": 80,
681
+ "justify_mode": "left",
682
+ "justify_width": 80,
683
+ "prefix": "",
684
+ "suffix": "",
685
+ "skip_empty": True,
686
+ "indent_size": 4,
687
+ "indent_char": "space",
688
+ "quote_style": "double"
689
+ },
690
+ description="Text wrapping and formatting tool"
691
+ ))
692
+
693
+ # Slug Generator
694
+ self.register_tool(ToolDefaultsSpec(
695
+ tool_name="Slug Generator",
696
+ defaults={
697
+ "separator": "-",
698
+ "lowercase": True,
699
+ "transliterate": True,
700
+ "max_length": 0,
701
+ "remove_stopwords": False
702
+ },
703
+ description="URL-friendly slug generation tool"
704
+ ))
705
+
706
+ # Column Tools
707
+ self.register_tool(ToolDefaultsSpec(
708
+ tool_name="Column Tools",
709
+ defaults={
710
+ "delimiter": ",",
711
+ "quote_char": "\"",
712
+ "has_header": True
713
+ },
714
+ description="Column and CSV manipulation tool"
715
+ ))
716
+
717
+ # Timestamp Converter
718
+ self.register_tool(ToolDefaultsSpec(
719
+ tool_name="Timestamp Converter",
720
+ defaults={
721
+ "input_format": "unix",
722
+ "output_format": "iso",
723
+ "use_utc": False,
724
+ "custom_format": "%Y-%m-%d %H:%M:%S",
725
+ "show_relative": False
726
+ },
727
+ description="Date/time conversion tool"
728
+ ))
729
+
730
+ # ASCII Art Generator
731
+ self.register_tool(ToolDefaultsSpec(
732
+ tool_name="ASCII Art Generator",
733
+ defaults={
734
+ "font": "standard",
735
+ "width": 80
736
+ },
737
+ description="Text to ASCII art conversion tool"
738
+ ))
739
+
740
+ # Extraction Tools
741
+ self.register_tool(ToolDefaultsSpec(
742
+ tool_name="Extraction Tools",
743
+ defaults={
744
+ "Email Extraction Tool": {"omit_duplicates": False, "hide_counts": True, "sort_emails": False, "only_domain": False},
745
+ "HTML Extraction Tool": {},
746
+ "Regex Extractor": {"pattern": "", "match_mode": "all_per_line", "omit_duplicates": False, "hide_counts": True, "sort_results": False, "case_sensitive": False},
747
+ "URL and Link Extractor": {"extract_href": False, "extract_https": False, "extract_any_protocol": False, "extract_markdown": False, "filter_text": ""}
748
+ },
749
+ description="Text extraction utilities"
750
+ ))
751
+
752
+ # Register application-level defaults
753
+ self._register_app_defaults()
754
+
755
+ def _register_app_defaults(self) -> None:
756
+ """Register application-level default settings."""
757
+ import os
758
+
759
+ default_path = os.path.join(os.path.expanduser('~'), 'Downloads')
760
+
761
+ self._app_defaults = {
762
+ "export_path": default_path,
763
+ "debug_level": "INFO",
764
+ "selected_tool": "Case Tool",
765
+ "active_input_tab": 0,
766
+ "active_output_tab": 0,
767
+ "performance_settings": {
768
+ "mode": "automatic",
769
+ "async_processing": {
770
+ "enabled": True,
771
+ "threshold_kb": 10,
772
+ "max_workers": 2,
773
+ "chunk_size_kb": 50
774
+ },
775
+ "caching": {
776
+ "enabled": True,
777
+ "stats_cache_size": 1000,
778
+ "regex_cache_size": 100,
779
+ "content_cache_size_mb": 50,
780
+ "processing_cache_size": 500
781
+ },
782
+ "memory_management": {
783
+ "enabled": True,
784
+ "gc_optimization": True,
785
+ "memory_pool": True,
786
+ "leak_detection": True,
787
+ "memory_threshold_mb": 500
788
+ },
789
+ "ui_optimizations": {
790
+ "enabled": True,
791
+ "efficient_line_numbers": True,
792
+ "progressive_search": True,
793
+ "debounce_delay_ms": 300,
794
+ "lazy_updates": True
795
+ }
796
+ },
797
+ "font_settings": {
798
+ "text_font": {
799
+ "family": "Source Code Pro",
800
+ "size": 11,
801
+ "fallback_family": "Consolas",
802
+ "fallback_family_mac": "Monaco",
803
+ "fallback_family_linux": "DejaVu Sans Mono"
804
+ },
805
+ "interface_font": {
806
+ "family": "Segoe UI",
807
+ "size": 9,
808
+ "fallback_family": "Arial",
809
+ "fallback_family_mac": "Helvetica",
810
+ "fallback_family_linux": "Ubuntu"
811
+ }
812
+ },
813
+ "dialog_settings": {
814
+ "success": {
815
+ "enabled": True,
816
+ "description": "Success notifications for completed operations",
817
+ "examples": ["File saved successfully", "Settings applied", "Export complete"]
818
+ },
819
+ "confirmation": {
820
+ "enabled": True,
821
+ "description": "Confirmation dialogs for destructive actions",
822
+ "examples": ["Clear all tabs?", "Delete entry?", "Reset settings?"],
823
+ "default_action": "yes"
824
+ },
825
+ "warning": {
826
+ "enabled": True,
827
+ "description": "Warning messages for potential issues",
828
+ "examples": ["No data specified", "Invalid input detected", "Feature unavailable"]
829
+ },
830
+ "error": {
831
+ "enabled": True,
832
+ "locked": True,
833
+ "description": "Error messages for critical issues (cannot be disabled)",
834
+ "examples": ["File not found", "Network error", "Invalid configuration"]
835
+ }
836
+ }
837
+ }
838
+
839
+ def register_tool(self, spec: ToolDefaultsSpec) -> None:
840
+ """
841
+ Register a tool's default settings.
842
+
843
+ Args:
844
+ spec: ToolDefaultsSpec containing the tool's defaults
845
+ """
846
+ self._tool_specs[spec.tool_name] = spec
847
+ self.logger.debug(f"Registered defaults for tool: {spec.tool_name}")
848
+
849
+ def unregister_tool(self, tool_name: str) -> bool:
850
+ """
851
+ Unregister a tool's default settings.
852
+
853
+ Args:
854
+ tool_name: Name of the tool to unregister
855
+
856
+ Returns:
857
+ True if tool was unregistered, False if not found
858
+ """
859
+ if tool_name in self._tool_specs:
860
+ del self._tool_specs[tool_name]
861
+ self.logger.debug(f"Unregistered defaults for tool: {tool_name}")
862
+ return True
863
+ return False
864
+
865
+ def get_tool_defaults(self, tool_name: str) -> Dict[str, Any]:
866
+ """
867
+ Get default settings for a specific tool.
868
+
869
+ Args:
870
+ tool_name: Name of the tool
871
+
872
+ Returns:
873
+ Dictionary of default settings, empty dict if tool not found
874
+ """
875
+ spec = self._tool_specs.get(tool_name)
876
+ if spec:
877
+ return deepcopy(spec.defaults)
878
+ return {}
879
+
880
+ def get_tool_spec(self, tool_name: str) -> Optional[ToolDefaultsSpec]:
881
+ """
882
+ Get the full specification for a tool.
883
+
884
+ Args:
885
+ tool_name: Name of the tool
886
+
887
+ Returns:
888
+ ToolDefaultsSpec or None if not found
889
+ """
890
+ return self._tool_specs.get(tool_name)
891
+
892
+ def get_all_tool_defaults(self) -> Dict[str, Dict[str, Any]]:
893
+ """
894
+ Get all tool default settings.
895
+
896
+ Returns:
897
+ Dictionary mapping tool names to their default settings
898
+ """
899
+ return {
900
+ name: deepcopy(spec.defaults)
901
+ for name, spec in self._tool_specs.items()
902
+ }
903
+
904
+ def get_all_defaults(self, tab_count: int = 7) -> Dict[str, Any]:
905
+ """
906
+ Get complete default settings including app-level and all tool defaults.
907
+
908
+ Args:
909
+ tab_count: Number of tabs for input/output (default 7)
910
+
911
+ Returns:
912
+ Complete default settings dictionary
913
+ """
914
+ defaults = deepcopy(self._app_defaults)
915
+ defaults["input_tabs"] = [""] * tab_count
916
+ defaults["output_tabs"] = [""] * tab_count
917
+ defaults["tool_settings"] = self.get_all_tool_defaults()
918
+ return defaults
919
+
920
+ def get_registered_tools(self) -> List[str]:
921
+ """
922
+ Get list of all registered tool names.
923
+
924
+ Returns:
925
+ List of tool names
926
+ """
927
+ return list(self._tool_specs.keys())
928
+
929
+ def validate_settings(self, settings: Dict[str, Any]) -> Tuple[bool, List[str]]:
930
+ """
931
+ Validate settings against registered schemas.
932
+
933
+ Args:
934
+ settings: Settings dictionary to validate
935
+
936
+ Returns:
937
+ Tuple of (is_valid, list of error messages)
938
+ """
939
+ errors = []
940
+
941
+ # Check for required app-level keys
942
+ required_app_keys = {"export_path", "debug_level", "selected_tool"}
943
+ for key in required_app_keys:
944
+ if key not in settings:
945
+ errors.append(f"Missing required app setting: {key}")
946
+
947
+ # Validate tool settings
948
+ tool_settings = settings.get("tool_settings", {})
949
+
950
+ for tool_name, spec in self._tool_specs.items():
951
+ if tool_name not in tool_settings:
952
+ # Tool settings missing entirely - not necessarily an error
953
+ # as they will be populated from defaults
954
+ continue
955
+
956
+ tool_config = tool_settings[tool_name]
957
+
958
+ # Check required keys for this tool
959
+ for required_key in spec.required_keys:
960
+ if required_key not in tool_config:
961
+ errors.append(f"Tool '{tool_name}' missing required key: {required_key}")
962
+
963
+ return (len(errors) == 0, errors)
964
+
965
+ def validate_tool_settings(self, tool_name: str, settings: Dict[str, Any]) -> Tuple[bool, List[str]]:
966
+ """
967
+ Validate settings for a specific tool.
968
+
969
+ Args:
970
+ tool_name: Name of the tool
971
+ settings: Tool settings to validate
972
+
973
+ Returns:
974
+ Tuple of (is_valid, list of error messages)
975
+ """
976
+ errors = []
977
+ spec = self._tool_specs.get(tool_name)
978
+
979
+ if not spec:
980
+ return (True, []) # Unknown tool, no validation
981
+
982
+ for required_key in spec.required_keys:
983
+ if required_key not in settings:
984
+ errors.append(f"Missing required key: {required_key}")
985
+
986
+ return (len(errors) == 0, errors)
987
+
988
+ def deep_merge(self, base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
989
+ """
990
+ Deep merge two dictionaries, with override taking precedence.
991
+
992
+ Args:
993
+ base: Base dictionary (defaults)
994
+ override: Override dictionary (user settings)
995
+
996
+ Returns:
997
+ Merged dictionary
998
+ """
999
+ result = deepcopy(base)
1000
+
1001
+ for key, value in override.items():
1002
+ if key in result and isinstance(result[key], dict) and isinstance(value, dict):
1003
+ result[key] = self.deep_merge(result[key], value)
1004
+ else:
1005
+ result[key] = deepcopy(value)
1006
+
1007
+ return result
1008
+
1009
+ def merge_with_defaults(self, user_settings: Dict[str, Any], tab_count: int = 7) -> Dict[str, Any]:
1010
+ """
1011
+ Merge user settings with defaults, filling in any missing values.
1012
+
1013
+ Args:
1014
+ user_settings: User's current settings
1015
+ tab_count: Number of tabs for input/output
1016
+
1017
+ Returns:
1018
+ Complete settings with defaults filled in
1019
+ """
1020
+ defaults = self.get_all_defaults(tab_count)
1021
+ merged = self.deep_merge(defaults, user_settings)
1022
+
1023
+ # Ensure tool_settings has all registered tools
1024
+ if "tool_settings" not in merged:
1025
+ merged["tool_settings"] = {}
1026
+
1027
+ for tool_name in self._tool_specs:
1028
+ if tool_name not in merged["tool_settings"]:
1029
+ merged["tool_settings"][tool_name] = self.get_tool_defaults(tool_name)
1030
+ else:
1031
+ # Merge tool-specific settings with defaults
1032
+ tool_defaults = self.get_tool_defaults(tool_name)
1033
+ merged["tool_settings"][tool_name] = self.deep_merge(
1034
+ tool_defaults,
1035
+ merged["tool_settings"][tool_name]
1036
+ )
1037
+
1038
+ return merged
1039
+
1040
+ def get_missing_settings(self, settings: Dict[str, Any]) -> Dict[str, Any]:
1041
+ """
1042
+ Get settings that are missing from the provided settings.
1043
+
1044
+ Args:
1045
+ settings: Current settings to check
1046
+
1047
+ Returns:
1048
+ Dictionary of missing settings with their default values
1049
+ """
1050
+ defaults = self.get_all_defaults()
1051
+ missing = {}
1052
+
1053
+ def find_missing(default_dict: Dict, current_dict: Dict, path: str = "") -> None:
1054
+ for key, value in default_dict.items():
1055
+ current_path = f"{path}.{key}" if path else key
1056
+
1057
+ if key not in current_dict:
1058
+ missing[current_path] = value
1059
+ elif isinstance(value, dict) and isinstance(current_dict.get(key), dict):
1060
+ find_missing(value, current_dict[key], current_path)
1061
+
1062
+ find_missing(defaults, settings)
1063
+ return missing
1064
+
1065
+
1066
+ # Singleton instance for easy access
1067
+ _registry: Optional[SettingsDefaultsRegistry] = None
1068
+
1069
+
1070
+ def get_registry() -> SettingsDefaultsRegistry:
1071
+ """
1072
+ Get the singleton settings defaults registry instance.
1073
+
1074
+ Returns:
1075
+ SettingsDefaultsRegistry instance
1076
+ """
1077
+ global _registry
1078
+ if _registry is None:
1079
+ _registry = SettingsDefaultsRegistry()
1080
+ return _registry
1081
+
1082
+
1083
+ def reset_registry() -> None:
1084
+ """Reset the registry singleton (mainly for testing)."""
1085
+ global _registry
1086
+ _registry = None
1087
+ SettingsDefaultsRegistry._instance = None