pomera-ai-commander 0.1.0 → 1.2.1

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 (191) 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 +1033 -1033
  9. package/core/content_hash_cache.py +508 -508
  10. package/core/context_menu.py +313 -313
  11. package/core/data_validator.py +1066 -1066
  12. package/core/database_connection_manager.py +744 -744
  13. package/core/database_curl_settings_manager.py +608 -608
  14. package/core/database_promera_ai_settings_manager.py +446 -446
  15. package/core/database_schema.py +411 -411
  16. package/core/database_schema_manager.py +395 -395
  17. package/core/database_settings_manager.py +1507 -1507
  18. package/core/database_settings_manager_interface.py +456 -456
  19. package/core/dialog_manager.py +734 -734
  20. package/core/efficient_line_numbers.py +510 -510
  21. package/core/error_handler.py +746 -746
  22. package/core/error_service.py +431 -431
  23. package/core/event_consolidator.py +511 -511
  24. package/core/mcp/__init__.py +43 -43
  25. package/core/mcp/protocol.py +288 -288
  26. package/core/mcp/schema.py +251 -251
  27. package/core/mcp/server_stdio.py +299 -299
  28. package/core/mcp/tool_registry.py +2372 -2345
  29. package/core/memory_efficient_text_widget.py +711 -711
  30. package/core/migration_manager.py +914 -914
  31. package/core/migration_test_suite.py +1085 -1085
  32. package/core/migration_validator.py +1143 -1143
  33. package/core/optimized_find_replace.py +714 -714
  34. package/core/optimized_pattern_engine.py +424 -424
  35. package/core/optimized_search_highlighter.py +552 -552
  36. package/core/performance_monitor.py +674 -674
  37. package/core/persistence_manager.py +712 -712
  38. package/core/progressive_stats_calculator.py +632 -632
  39. package/core/regex_pattern_cache.py +529 -529
  40. package/core/regex_pattern_library.py +350 -350
  41. package/core/search_operation_manager.py +434 -434
  42. package/core/settings_defaults_registry.py +1087 -1087
  43. package/core/settings_integrity_validator.py +1111 -1111
  44. package/core/settings_serializer.py +557 -557
  45. package/core/settings_validator.py +1823 -1823
  46. package/core/smart_stats_calculator.py +709 -709
  47. package/core/statistics_update_manager.py +619 -619
  48. package/core/stats_config_manager.py +858 -858
  49. package/core/streaming_text_handler.py +723 -723
  50. package/core/task_scheduler.py +596 -596
  51. package/core/update_pattern_library.py +168 -168
  52. package/core/visibility_monitor.py +596 -596
  53. package/core/widget_cache.py +498 -498
  54. package/mcp.json +51 -61
  55. package/package.json +61 -57
  56. package/pomera.py +7482 -7482
  57. package/pomera_mcp_server.py +183 -144
  58. package/requirements.txt +32 -0
  59. package/tools/__init__.py +4 -4
  60. package/tools/ai_tools.py +2891 -2891
  61. package/tools/ascii_art_generator.py +352 -352
  62. package/tools/base64_tools.py +183 -183
  63. package/tools/base_tool.py +511 -511
  64. package/tools/case_tool.py +308 -308
  65. package/tools/column_tools.py +395 -395
  66. package/tools/cron_tool.py +884 -884
  67. package/tools/curl_history.py +600 -600
  68. package/tools/curl_processor.py +1207 -1207
  69. package/tools/curl_settings.py +502 -502
  70. package/tools/curl_tool.py +5467 -5467
  71. package/tools/diff_viewer.py +1071 -1071
  72. package/tools/email_extraction_tool.py +248 -248
  73. package/tools/email_header_analyzer.py +425 -425
  74. package/tools/extraction_tools.py +250 -250
  75. package/tools/find_replace.py +1750 -1750
  76. package/tools/folder_file_reporter.py +1463 -1463
  77. package/tools/folder_file_reporter_adapter.py +480 -480
  78. package/tools/generator_tools.py +1216 -1216
  79. package/tools/hash_generator.py +255 -255
  80. package/tools/html_tool.py +656 -656
  81. package/tools/jsonxml_tool.py +729 -729
  82. package/tools/line_tools.py +419 -419
  83. package/tools/markdown_tools.py +561 -561
  84. package/tools/mcp_widget.py +1417 -1417
  85. package/tools/notes_widget.py +973 -973
  86. package/tools/number_base_converter.py +372 -372
  87. package/tools/regex_extractor.py +571 -571
  88. package/tools/slug_generator.py +310 -310
  89. package/tools/sorter_tools.py +458 -458
  90. package/tools/string_escape_tool.py +392 -392
  91. package/tools/text_statistics_tool.py +365 -365
  92. package/tools/text_wrapper.py +430 -430
  93. package/tools/timestamp_converter.py +421 -421
  94. package/tools/tool_loader.py +710 -710
  95. package/tools/translator_tools.py +522 -522
  96. package/tools/url_link_extractor.py +261 -261
  97. package/tools/url_parser.py +204 -204
  98. package/tools/whitespace_tools.py +355 -355
  99. package/tools/word_frequency_counter.py +146 -146
  100. package/core/__pycache__/__init__.cpython-313.pyc +0 -0
  101. package/core/__pycache__/app_context.cpython-313.pyc +0 -0
  102. package/core/__pycache__/async_text_processor.cpython-313.pyc +0 -0
  103. package/core/__pycache__/backup_manager.cpython-313.pyc +0 -0
  104. package/core/__pycache__/backup_recovery_manager.cpython-313.pyc +0 -0
  105. package/core/__pycache__/content_hash_cache.cpython-313.pyc +0 -0
  106. package/core/__pycache__/context_menu.cpython-313.pyc +0 -0
  107. package/core/__pycache__/data_validator.cpython-313.pyc +0 -0
  108. package/core/__pycache__/database_connection_manager.cpython-313.pyc +0 -0
  109. package/core/__pycache__/database_curl_settings_manager.cpython-313.pyc +0 -0
  110. package/core/__pycache__/database_promera_ai_settings_manager.cpython-313.pyc +0 -0
  111. package/core/__pycache__/database_schema.cpython-313.pyc +0 -0
  112. package/core/__pycache__/database_schema_manager.cpython-313.pyc +0 -0
  113. package/core/__pycache__/database_settings_manager.cpython-313.pyc +0 -0
  114. package/core/__pycache__/database_settings_manager_interface.cpython-313.pyc +0 -0
  115. package/core/__pycache__/dialog_manager.cpython-313.pyc +0 -0
  116. package/core/__pycache__/efficient_line_numbers.cpython-313.pyc +0 -0
  117. package/core/__pycache__/error_handler.cpython-313.pyc +0 -0
  118. package/core/__pycache__/error_service.cpython-313.pyc +0 -0
  119. package/core/__pycache__/event_consolidator.cpython-313.pyc +0 -0
  120. package/core/__pycache__/memory_efficient_text_widget.cpython-313.pyc +0 -0
  121. package/core/__pycache__/migration_manager.cpython-313.pyc +0 -0
  122. package/core/__pycache__/migration_test_suite.cpython-313.pyc +0 -0
  123. package/core/__pycache__/migration_validator.cpython-313.pyc +0 -0
  124. package/core/__pycache__/optimized_find_replace.cpython-313.pyc +0 -0
  125. package/core/__pycache__/optimized_pattern_engine.cpython-313.pyc +0 -0
  126. package/core/__pycache__/optimized_search_highlighter.cpython-313.pyc +0 -0
  127. package/core/__pycache__/performance_monitor.cpython-313.pyc +0 -0
  128. package/core/__pycache__/persistence_manager.cpython-313.pyc +0 -0
  129. package/core/__pycache__/progressive_stats_calculator.cpython-313.pyc +0 -0
  130. package/core/__pycache__/regex_pattern_cache.cpython-313.pyc +0 -0
  131. package/core/__pycache__/regex_pattern_library.cpython-313.pyc +0 -0
  132. package/core/__pycache__/search_operation_manager.cpython-313.pyc +0 -0
  133. package/core/__pycache__/settings_defaults_registry.cpython-313.pyc +0 -0
  134. package/core/__pycache__/settings_integrity_validator.cpython-313.pyc +0 -0
  135. package/core/__pycache__/settings_serializer.cpython-313.pyc +0 -0
  136. package/core/__pycache__/settings_validator.cpython-313.pyc +0 -0
  137. package/core/__pycache__/smart_stats_calculator.cpython-313.pyc +0 -0
  138. package/core/__pycache__/statistics_update_manager.cpython-313.pyc +0 -0
  139. package/core/__pycache__/stats_config_manager.cpython-313.pyc +0 -0
  140. package/core/__pycache__/streaming_text_handler.cpython-313.pyc +0 -0
  141. package/core/__pycache__/task_scheduler.cpython-313.pyc +0 -0
  142. package/core/__pycache__/visibility_monitor.cpython-313.pyc +0 -0
  143. package/core/__pycache__/widget_cache.cpython-313.pyc +0 -0
  144. package/core/mcp/__pycache__/__init__.cpython-313.pyc +0 -0
  145. package/core/mcp/__pycache__/protocol.cpython-313.pyc +0 -0
  146. package/core/mcp/__pycache__/schema.cpython-313.pyc +0 -0
  147. package/core/mcp/__pycache__/server_stdio.cpython-313.pyc +0 -0
  148. package/core/mcp/__pycache__/tool_registry.cpython-313.pyc +0 -0
  149. package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  150. package/tools/__pycache__/ai_tools.cpython-313.pyc +0 -0
  151. package/tools/__pycache__/ascii_art_generator.cpython-313.pyc +0 -0
  152. package/tools/__pycache__/base64_tools.cpython-313.pyc +0 -0
  153. package/tools/__pycache__/base_tool.cpython-313.pyc +0 -0
  154. package/tools/__pycache__/case_tool.cpython-313.pyc +0 -0
  155. package/tools/__pycache__/column_tools.cpython-313.pyc +0 -0
  156. package/tools/__pycache__/cron_tool.cpython-313.pyc +0 -0
  157. package/tools/__pycache__/curl_history.cpython-313.pyc +0 -0
  158. package/tools/__pycache__/curl_processor.cpython-313.pyc +0 -0
  159. package/tools/__pycache__/curl_settings.cpython-313.pyc +0 -0
  160. package/tools/__pycache__/curl_tool.cpython-313.pyc +0 -0
  161. package/tools/__pycache__/diff_viewer.cpython-313.pyc +0 -0
  162. package/tools/__pycache__/email_extraction_tool.cpython-313.pyc +0 -0
  163. package/tools/__pycache__/email_header_analyzer.cpython-313.pyc +0 -0
  164. package/tools/__pycache__/extraction_tools.cpython-313.pyc +0 -0
  165. package/tools/__pycache__/find_replace.cpython-313.pyc +0 -0
  166. package/tools/__pycache__/folder_file_reporter.cpython-313.pyc +0 -0
  167. package/tools/__pycache__/folder_file_reporter_adapter.cpython-313.pyc +0 -0
  168. package/tools/__pycache__/generator_tools.cpython-313.pyc +0 -0
  169. package/tools/__pycache__/hash_generator.cpython-313.pyc +0 -0
  170. package/tools/__pycache__/html_tool.cpython-313.pyc +0 -0
  171. package/tools/__pycache__/huggingface_helper.cpython-313.pyc +0 -0
  172. package/tools/__pycache__/jsonxml_tool.cpython-313.pyc +0 -0
  173. package/tools/__pycache__/line_tools.cpython-313.pyc +0 -0
  174. package/tools/__pycache__/list_comparator.cpython-313.pyc +0 -0
  175. package/tools/__pycache__/markdown_tools.cpython-313.pyc +0 -0
  176. package/tools/__pycache__/mcp_widget.cpython-313.pyc +0 -0
  177. package/tools/__pycache__/notes_widget.cpython-313.pyc +0 -0
  178. package/tools/__pycache__/number_base_converter.cpython-313.pyc +0 -0
  179. package/tools/__pycache__/regex_extractor.cpython-313.pyc +0 -0
  180. package/tools/__pycache__/slug_generator.cpython-313.pyc +0 -0
  181. package/tools/__pycache__/sorter_tools.cpython-313.pyc +0 -0
  182. package/tools/__pycache__/string_escape_tool.cpython-313.pyc +0 -0
  183. package/tools/__pycache__/text_statistics_tool.cpython-313.pyc +0 -0
  184. package/tools/__pycache__/text_wrapper.cpython-313.pyc +0 -0
  185. package/tools/__pycache__/timestamp_converter.cpython-313.pyc +0 -0
  186. package/tools/__pycache__/tool_loader.cpython-313.pyc +0 -0
  187. package/tools/__pycache__/translator_tools.cpython-313.pyc +0 -0
  188. package/tools/__pycache__/url_link_extractor.cpython-313.pyc +0 -0
  189. package/tools/__pycache__/url_parser.cpython-313.pyc +0 -0
  190. package/tools/__pycache__/whitespace_tools.cpython-313.pyc +0 -0
  191. 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