pomera-ai-commander 0.1.0

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 (192) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +680 -0
  3. package/bin/pomera-ai-commander.js +62 -0
  4. package/core/__init__.py +66 -0
  5. package/core/__pycache__/__init__.cpython-313.pyc +0 -0
  6. package/core/__pycache__/app_context.cpython-313.pyc +0 -0
  7. package/core/__pycache__/async_text_processor.cpython-313.pyc +0 -0
  8. package/core/__pycache__/backup_manager.cpython-313.pyc +0 -0
  9. package/core/__pycache__/backup_recovery_manager.cpython-313.pyc +0 -0
  10. package/core/__pycache__/content_hash_cache.cpython-313.pyc +0 -0
  11. package/core/__pycache__/context_menu.cpython-313.pyc +0 -0
  12. package/core/__pycache__/data_validator.cpython-313.pyc +0 -0
  13. package/core/__pycache__/database_connection_manager.cpython-313.pyc +0 -0
  14. package/core/__pycache__/database_curl_settings_manager.cpython-313.pyc +0 -0
  15. package/core/__pycache__/database_promera_ai_settings_manager.cpython-313.pyc +0 -0
  16. package/core/__pycache__/database_schema.cpython-313.pyc +0 -0
  17. package/core/__pycache__/database_schema_manager.cpython-313.pyc +0 -0
  18. package/core/__pycache__/database_settings_manager.cpython-313.pyc +0 -0
  19. package/core/__pycache__/database_settings_manager_interface.cpython-313.pyc +0 -0
  20. package/core/__pycache__/dialog_manager.cpython-313.pyc +0 -0
  21. package/core/__pycache__/efficient_line_numbers.cpython-313.pyc +0 -0
  22. package/core/__pycache__/error_handler.cpython-313.pyc +0 -0
  23. package/core/__pycache__/error_service.cpython-313.pyc +0 -0
  24. package/core/__pycache__/event_consolidator.cpython-313.pyc +0 -0
  25. package/core/__pycache__/memory_efficient_text_widget.cpython-313.pyc +0 -0
  26. package/core/__pycache__/migration_manager.cpython-313.pyc +0 -0
  27. package/core/__pycache__/migration_test_suite.cpython-313.pyc +0 -0
  28. package/core/__pycache__/migration_validator.cpython-313.pyc +0 -0
  29. package/core/__pycache__/optimized_find_replace.cpython-313.pyc +0 -0
  30. package/core/__pycache__/optimized_pattern_engine.cpython-313.pyc +0 -0
  31. package/core/__pycache__/optimized_search_highlighter.cpython-313.pyc +0 -0
  32. package/core/__pycache__/performance_monitor.cpython-313.pyc +0 -0
  33. package/core/__pycache__/persistence_manager.cpython-313.pyc +0 -0
  34. package/core/__pycache__/progressive_stats_calculator.cpython-313.pyc +0 -0
  35. package/core/__pycache__/regex_pattern_cache.cpython-313.pyc +0 -0
  36. package/core/__pycache__/regex_pattern_library.cpython-313.pyc +0 -0
  37. package/core/__pycache__/search_operation_manager.cpython-313.pyc +0 -0
  38. package/core/__pycache__/settings_defaults_registry.cpython-313.pyc +0 -0
  39. package/core/__pycache__/settings_integrity_validator.cpython-313.pyc +0 -0
  40. package/core/__pycache__/settings_serializer.cpython-313.pyc +0 -0
  41. package/core/__pycache__/settings_validator.cpython-313.pyc +0 -0
  42. package/core/__pycache__/smart_stats_calculator.cpython-313.pyc +0 -0
  43. package/core/__pycache__/statistics_update_manager.cpython-313.pyc +0 -0
  44. package/core/__pycache__/stats_config_manager.cpython-313.pyc +0 -0
  45. package/core/__pycache__/streaming_text_handler.cpython-313.pyc +0 -0
  46. package/core/__pycache__/task_scheduler.cpython-313.pyc +0 -0
  47. package/core/__pycache__/visibility_monitor.cpython-313.pyc +0 -0
  48. package/core/__pycache__/widget_cache.cpython-313.pyc +0 -0
  49. package/core/app_context.py +482 -0
  50. package/core/async_text_processor.py +422 -0
  51. package/core/backup_manager.py +656 -0
  52. package/core/backup_recovery_manager.py +1034 -0
  53. package/core/content_hash_cache.py +509 -0
  54. package/core/context_menu.py +313 -0
  55. package/core/data_validator.py +1067 -0
  56. package/core/database_connection_manager.py +745 -0
  57. package/core/database_curl_settings_manager.py +609 -0
  58. package/core/database_promera_ai_settings_manager.py +447 -0
  59. package/core/database_schema.py +412 -0
  60. package/core/database_schema_manager.py +396 -0
  61. package/core/database_settings_manager.py +1508 -0
  62. package/core/database_settings_manager_interface.py +457 -0
  63. package/core/dialog_manager.py +735 -0
  64. package/core/efficient_line_numbers.py +511 -0
  65. package/core/error_handler.py +747 -0
  66. package/core/error_service.py +431 -0
  67. package/core/event_consolidator.py +512 -0
  68. package/core/mcp/__init__.py +43 -0
  69. package/core/mcp/__pycache__/__init__.cpython-313.pyc +0 -0
  70. package/core/mcp/__pycache__/protocol.cpython-313.pyc +0 -0
  71. package/core/mcp/__pycache__/schema.cpython-313.pyc +0 -0
  72. package/core/mcp/__pycache__/server_stdio.cpython-313.pyc +0 -0
  73. package/core/mcp/__pycache__/tool_registry.cpython-313.pyc +0 -0
  74. package/core/mcp/protocol.py +288 -0
  75. package/core/mcp/schema.py +251 -0
  76. package/core/mcp/server_stdio.py +299 -0
  77. package/core/mcp/tool_registry.py +2345 -0
  78. package/core/memory_efficient_text_widget.py +712 -0
  79. package/core/migration_manager.py +915 -0
  80. package/core/migration_test_suite.py +1086 -0
  81. package/core/migration_validator.py +1144 -0
  82. package/core/optimized_find_replace.py +715 -0
  83. package/core/optimized_pattern_engine.py +424 -0
  84. package/core/optimized_search_highlighter.py +553 -0
  85. package/core/performance_monitor.py +675 -0
  86. package/core/persistence_manager.py +713 -0
  87. package/core/progressive_stats_calculator.py +632 -0
  88. package/core/regex_pattern_cache.py +530 -0
  89. package/core/regex_pattern_library.py +351 -0
  90. package/core/search_operation_manager.py +435 -0
  91. package/core/settings_defaults_registry.py +1087 -0
  92. package/core/settings_integrity_validator.py +1112 -0
  93. package/core/settings_serializer.py +558 -0
  94. package/core/settings_validator.py +1824 -0
  95. package/core/smart_stats_calculator.py +710 -0
  96. package/core/statistics_update_manager.py +619 -0
  97. package/core/stats_config_manager.py +858 -0
  98. package/core/streaming_text_handler.py +723 -0
  99. package/core/task_scheduler.py +596 -0
  100. package/core/update_pattern_library.py +169 -0
  101. package/core/visibility_monitor.py +596 -0
  102. package/core/widget_cache.py +498 -0
  103. package/mcp.json +61 -0
  104. package/package.json +57 -0
  105. package/pomera.py +7483 -0
  106. package/pomera_mcp_server.py +144 -0
  107. package/tools/__init__.py +5 -0
  108. package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  109. package/tools/__pycache__/ai_tools.cpython-313.pyc +0 -0
  110. package/tools/__pycache__/ascii_art_generator.cpython-313.pyc +0 -0
  111. package/tools/__pycache__/base64_tools.cpython-313.pyc +0 -0
  112. package/tools/__pycache__/base_tool.cpython-313.pyc +0 -0
  113. package/tools/__pycache__/case_tool.cpython-313.pyc +0 -0
  114. package/tools/__pycache__/column_tools.cpython-313.pyc +0 -0
  115. package/tools/__pycache__/cron_tool.cpython-313.pyc +0 -0
  116. package/tools/__pycache__/curl_history.cpython-313.pyc +0 -0
  117. package/tools/__pycache__/curl_processor.cpython-313.pyc +0 -0
  118. package/tools/__pycache__/curl_settings.cpython-313.pyc +0 -0
  119. package/tools/__pycache__/curl_tool.cpython-313.pyc +0 -0
  120. package/tools/__pycache__/diff_viewer.cpython-313.pyc +0 -0
  121. package/tools/__pycache__/email_extraction_tool.cpython-313.pyc +0 -0
  122. package/tools/__pycache__/email_header_analyzer.cpython-313.pyc +0 -0
  123. package/tools/__pycache__/extraction_tools.cpython-313.pyc +0 -0
  124. package/tools/__pycache__/find_replace.cpython-313.pyc +0 -0
  125. package/tools/__pycache__/folder_file_reporter.cpython-313.pyc +0 -0
  126. package/tools/__pycache__/folder_file_reporter_adapter.cpython-313.pyc +0 -0
  127. package/tools/__pycache__/generator_tools.cpython-313.pyc +0 -0
  128. package/tools/__pycache__/hash_generator.cpython-313.pyc +0 -0
  129. package/tools/__pycache__/html_tool.cpython-313.pyc +0 -0
  130. package/tools/__pycache__/huggingface_helper.cpython-313.pyc +0 -0
  131. package/tools/__pycache__/jsonxml_tool.cpython-313.pyc +0 -0
  132. package/tools/__pycache__/line_tools.cpython-313.pyc +0 -0
  133. package/tools/__pycache__/list_comparator.cpython-313.pyc +0 -0
  134. package/tools/__pycache__/markdown_tools.cpython-313.pyc +0 -0
  135. package/tools/__pycache__/mcp_widget.cpython-313.pyc +0 -0
  136. package/tools/__pycache__/notes_widget.cpython-313.pyc +0 -0
  137. package/tools/__pycache__/number_base_converter.cpython-313.pyc +0 -0
  138. package/tools/__pycache__/regex_extractor.cpython-313.pyc +0 -0
  139. package/tools/__pycache__/slug_generator.cpython-313.pyc +0 -0
  140. package/tools/__pycache__/sorter_tools.cpython-313.pyc +0 -0
  141. package/tools/__pycache__/string_escape_tool.cpython-313.pyc +0 -0
  142. package/tools/__pycache__/text_statistics_tool.cpython-313.pyc +0 -0
  143. package/tools/__pycache__/text_wrapper.cpython-313.pyc +0 -0
  144. package/tools/__pycache__/timestamp_converter.cpython-313.pyc +0 -0
  145. package/tools/__pycache__/tool_loader.cpython-313.pyc +0 -0
  146. package/tools/__pycache__/translator_tools.cpython-313.pyc +0 -0
  147. package/tools/__pycache__/url_link_extractor.cpython-313.pyc +0 -0
  148. package/tools/__pycache__/url_parser.cpython-313.pyc +0 -0
  149. package/tools/__pycache__/whitespace_tools.cpython-313.pyc +0 -0
  150. package/tools/__pycache__/word_frequency_counter.cpython-313.pyc +0 -0
  151. package/tools/ai_tools.py +2892 -0
  152. package/tools/ascii_art_generator.py +353 -0
  153. package/tools/base64_tools.py +184 -0
  154. package/tools/base_tool.py +511 -0
  155. package/tools/case_tool.py +309 -0
  156. package/tools/column_tools.py +396 -0
  157. package/tools/cron_tool.py +885 -0
  158. package/tools/curl_history.py +601 -0
  159. package/tools/curl_processor.py +1208 -0
  160. package/tools/curl_settings.py +503 -0
  161. package/tools/curl_tool.py +5467 -0
  162. package/tools/diff_viewer.py +1072 -0
  163. package/tools/email_extraction_tool.py +249 -0
  164. package/tools/email_header_analyzer.py +426 -0
  165. package/tools/extraction_tools.py +250 -0
  166. package/tools/find_replace.py +1751 -0
  167. package/tools/folder_file_reporter.py +1463 -0
  168. package/tools/folder_file_reporter_adapter.py +480 -0
  169. package/tools/generator_tools.py +1217 -0
  170. package/tools/hash_generator.py +256 -0
  171. package/tools/html_tool.py +657 -0
  172. package/tools/huggingface_helper.py +449 -0
  173. package/tools/jsonxml_tool.py +730 -0
  174. package/tools/line_tools.py +419 -0
  175. package/tools/list_comparator.py +720 -0
  176. package/tools/markdown_tools.py +562 -0
  177. package/tools/mcp_widget.py +1417 -0
  178. package/tools/notes_widget.py +973 -0
  179. package/tools/number_base_converter.py +373 -0
  180. package/tools/regex_extractor.py +572 -0
  181. package/tools/slug_generator.py +311 -0
  182. package/tools/sorter_tools.py +459 -0
  183. package/tools/string_escape_tool.py +393 -0
  184. package/tools/text_statistics_tool.py +366 -0
  185. package/tools/text_wrapper.py +431 -0
  186. package/tools/timestamp_converter.py +422 -0
  187. package/tools/tool_loader.py +710 -0
  188. package/tools/translator_tools.py +523 -0
  189. package/tools/url_link_extractor.py +262 -0
  190. package/tools/url_parser.py +205 -0
  191. package/tools/whitespace_tools.py +356 -0
  192. package/tools/word_frequency_counter.py +147 -0
@@ -0,0 +1,572 @@
1
+ """
2
+ Regex Extractor Module - Regex pattern extraction utility
3
+
4
+ This module provides regex pattern extraction functionality with UI components
5
+ for the Promera AI Commander application.
6
+ """
7
+
8
+ import tkinter as tk
9
+ from tkinter import ttk
10
+ import re
11
+ from collections import Counter
12
+
13
+
14
+ class RegexExtractorProcessor:
15
+ """Regex extractor processor that extracts matches from text using regex patterns."""
16
+
17
+ @staticmethod
18
+ def extract_matches(text, pattern, match_mode="all_per_line", omit_duplicates=False, hide_counts=True, sort_results=False, case_sensitive=False):
19
+ """
20
+ Extract matches from text using a regex pattern.
21
+
22
+ Args:
23
+ text: Input text to search
24
+ pattern: Regex pattern to search for
25
+ match_mode: "first_per_line" to match only first occurrence per line, "all_per_line" to match all occurrences per line
26
+ omit_duplicates: If True, only return unique matches
27
+ hide_counts: If True, don't show match counts
28
+ sort_results: If True, sort the results
29
+ case_sensitive: If True, perform case-sensitive matching
30
+
31
+ Returns:
32
+ String containing extracted matches (one per line) or error message
33
+ """
34
+ if not pattern or not pattern.strip():
35
+ return "Please enter a regex pattern in the Find field."
36
+
37
+ try:
38
+ # Compile the regex pattern
39
+ flags = 0 if case_sensitive else re.IGNORECASE
40
+ regex = re.compile(pattern, flags)
41
+
42
+ processed_matches = []
43
+
44
+ # Process based on match mode
45
+ if match_mode == "first_per_line":
46
+ # Process line by line, taking only the first match per line
47
+ lines = text.split('\n')
48
+ for line in lines:
49
+ matches = regex.findall(line)
50
+ if matches:
51
+ # Take only the first match from this line
52
+ match = matches[0]
53
+ if isinstance(match, tuple):
54
+ # Join tuple elements with a separator
55
+ processed_matches.append(' | '.join(str(m) if m else '' for m in match))
56
+ else:
57
+ processed_matches.append(str(match))
58
+ else:
59
+ # Match all occurrences (original behavior)
60
+ matches = regex.findall(text)
61
+
62
+ if not matches:
63
+ return "No matches found for the regex pattern."
64
+
65
+ # Handle different match types (strings vs tuples)
66
+ # If pattern has groups, findall returns tuples, otherwise strings
67
+ for match in matches:
68
+ if isinstance(match, tuple):
69
+ # Join tuple elements with a separator
70
+ processed_matches.append(' | '.join(str(m) if m else '' for m in match))
71
+ else:
72
+ processed_matches.append(str(match))
73
+
74
+ if not processed_matches:
75
+ return "No matches found for the regex pattern."
76
+
77
+ # Count occurrences
78
+ match_counts = Counter(processed_matches)
79
+
80
+ # Get unique matches if omit_duplicates is True
81
+ if omit_duplicates:
82
+ unique_matches = list(match_counts.keys())
83
+ if sort_results:
84
+ unique_matches.sort()
85
+
86
+ # Format output
87
+ if hide_counts:
88
+ return '\n'.join(unique_matches)
89
+ else:
90
+ # When omit_duplicates=True, show count as (1) for all
91
+ return '\n'.join([f"{match} (1)" for match in unique_matches])
92
+ else:
93
+ # Keep all matches including duplicates
94
+ if sort_results:
95
+ processed_matches.sort()
96
+
97
+ if hide_counts:
98
+ return '\n'.join(processed_matches)
99
+ else:
100
+ # Show actual counts for each unique match
101
+ result = []
102
+ processed = set()
103
+ for match in processed_matches:
104
+ if match not in processed:
105
+ result.append(f"{match} ({match_counts[match]})")
106
+ processed.add(match)
107
+
108
+ if sort_results:
109
+ result.sort()
110
+
111
+ return '\n'.join(result)
112
+
113
+ except re.error as e:
114
+ return f"Regex Error: {str(e)}\n\nPlease check your regex pattern syntax."
115
+ except Exception as e:
116
+ return f"Error: {str(e)}"
117
+
118
+ @staticmethod
119
+ def process_text(input_text, settings):
120
+ """Process text using the current settings."""
121
+ return RegexExtractorProcessor.extract_matches(
122
+ input_text,
123
+ settings.get("pattern", ""),
124
+ settings.get("match_mode", "all_per_line"),
125
+ settings.get("omit_duplicates", False),
126
+ settings.get("hide_counts", True),
127
+ settings.get("sort_results", False),
128
+ settings.get("case_sensitive", False)
129
+ )
130
+
131
+
132
+ class RegexExtractorUI:
133
+ """UI components for the Regex Extractor."""
134
+
135
+ def __init__(self, parent, settings, on_setting_change_callback=None, apply_tool_callback=None, settings_manager=None):
136
+ """
137
+ Initialize the Regex Extractor UI.
138
+
139
+ Args:
140
+ parent: Parent widget
141
+ settings: Dictionary containing tool settings
142
+ on_setting_change_callback: Callback function for setting changes
143
+ apply_tool_callback: Callback function for applying the tool
144
+ settings_manager: Settings manager for accessing pattern library
145
+ """
146
+ self.parent = parent
147
+ self.settings = settings
148
+ self.on_setting_change_callback = on_setting_change_callback
149
+ self.apply_tool_callback = apply_tool_callback
150
+ self.settings_manager = settings_manager
151
+
152
+ # Initialize UI variables
153
+ self.pattern_var = tk.StringVar(value=settings.get("pattern", ""))
154
+ self.match_mode_var = tk.StringVar(value=settings.get("match_mode", "all_per_line"))
155
+ self.regex_omit_duplicates_var = tk.BooleanVar(value=settings.get("omit_duplicates", False))
156
+ self.regex_hide_counts_var = tk.BooleanVar(value=settings.get("hide_counts", True))
157
+ self.regex_sort_results_var = tk.BooleanVar(value=settings.get("sort_results", False))
158
+ self.regex_case_sensitive_var = tk.BooleanVar(value=settings.get("case_sensitive", False))
159
+
160
+ self.create_widgets()
161
+
162
+ def create_widgets(self):
163
+ """Creates the UI widgets for the Regex Extractor."""
164
+ # Find field (Regex pattern)
165
+ find_frame = ttk.Frame(self.parent)
166
+ find_frame.pack(fill=tk.X, padx=5, pady=5)
167
+ ttk.Label(find_frame, text="Find:").pack(side=tk.LEFT, padx=(0, 5))
168
+ find_entry = ttk.Entry(find_frame, textvariable=self.pattern_var, width=40)
169
+ find_entry.pack(side=tk.LEFT, expand=True, fill=tk.X, padx=(0, 5))
170
+ self.pattern_var.trace_add("write", self._on_pattern_change)
171
+
172
+ # Pattern Library button
173
+ if self.settings_manager:
174
+ ttk.Button(
175
+ find_frame,
176
+ text="Pattern Library",
177
+ command=self.show_pattern_library
178
+ ).pack(side=tk.LEFT, padx=(5, 0))
179
+
180
+ # Match mode option (first per line or all per line)
181
+ match_mode_frame = ttk.Frame(self.parent)
182
+ match_mode_frame.pack(fill=tk.X, padx=5, pady=(5, 2))
183
+ ttk.Label(match_mode_frame, text="Match mode:").pack(side=tk.LEFT, padx=(0, 5))
184
+ ttk.Radiobutton(
185
+ match_mode_frame,
186
+ text="First match per line",
187
+ variable=self.match_mode_var,
188
+ value="first_per_line",
189
+ command=self._on_setting_change
190
+ ).pack(side=tk.LEFT, padx=5)
191
+ ttk.Radiobutton(
192
+ match_mode_frame,
193
+ text="All occurrences",
194
+ variable=self.match_mode_var,
195
+ value="all_per_line",
196
+ command=self._on_setting_change
197
+ ).pack(side=tk.LEFT, padx=5)
198
+
199
+ # Checkboxes for various options
200
+ options_frame = ttk.Frame(self.parent)
201
+ options_frame.pack(fill=tk.X, padx=5, pady=5)
202
+
203
+ ttk.Checkbutton(
204
+ options_frame,
205
+ text="Omit duplicates",
206
+ variable=self.regex_omit_duplicates_var,
207
+ command=self._on_setting_change
208
+ ).pack(side=tk.LEFT, padx=5)
209
+
210
+ ttk.Checkbutton(
211
+ options_frame,
212
+ text="Hide counts",
213
+ variable=self.regex_hide_counts_var,
214
+ command=self._on_setting_change
215
+ ).pack(side=tk.LEFT, padx=5)
216
+
217
+ ttk.Checkbutton(
218
+ options_frame,
219
+ text="Sort results",
220
+ variable=self.regex_sort_results_var,
221
+ command=self._on_setting_change
222
+ ).pack(side=tk.LEFT, padx=5)
223
+
224
+ ttk.Checkbutton(
225
+ options_frame,
226
+ text="Case sensitive",
227
+ variable=self.regex_case_sensitive_var,
228
+ command=self._on_setting_change
229
+ ).pack(side=tk.LEFT, padx=5)
230
+
231
+ # Extract button
232
+ button_frame = ttk.Frame(self.parent)
233
+ button_frame.pack(fill=tk.X, padx=5, pady=5)
234
+
235
+ if self.apply_tool_callback:
236
+ ttk.Button(
237
+ button_frame,
238
+ text="Extract",
239
+ command=self.apply_tool_callback
240
+ ).pack(side=tk.LEFT, padx=5)
241
+
242
+ def _on_setting_change(self):
243
+ """Handle setting changes."""
244
+ if self.on_setting_change_callback:
245
+ self.on_setting_change_callback()
246
+
247
+ def _on_pattern_change(self, *args):
248
+ """Handle pattern text changes."""
249
+ if self.on_setting_change_callback:
250
+ self.on_setting_change_callback()
251
+
252
+ def get_current_settings(self):
253
+ """Get the current settings from the UI."""
254
+ return {
255
+ "pattern": self.pattern_var.get(),
256
+ "match_mode": self.match_mode_var.get(),
257
+ "omit_duplicates": self.regex_omit_duplicates_var.get(),
258
+ "hide_counts": self.regex_hide_counts_var.get(),
259
+ "sort_results": self.regex_sort_results_var.get(),
260
+ "case_sensitive": self.regex_case_sensitive_var.get()
261
+ }
262
+
263
+ def update_settings(self, settings):
264
+ """Update the UI with new settings."""
265
+ self.pattern_var.set(settings.get("pattern", ""))
266
+ self.match_mode_var.set(settings.get("match_mode", "all_per_line"))
267
+ self.regex_omit_duplicates_var.set(settings.get("omit_duplicates", False))
268
+ self.regex_hide_counts_var.set(settings.get("hide_counts", True))
269
+ self.regex_sort_results_var.set(settings.get("sort_results", False))
270
+ self.regex_case_sensitive_var.set(settings.get("case_sensitive", False))
271
+
272
+ def show_pattern_library(self):
273
+ """Shows the Pattern Library window with regex patterns."""
274
+ if not self.settings_manager:
275
+ return
276
+
277
+ # Get pattern library from settings
278
+ pattern_library = self.settings_manager.get_pattern_library()
279
+
280
+ popup = tk.Toplevel(self.parent)
281
+ popup.title("Regex Pattern Library")
282
+ popup.geometry("800x500")
283
+ popup.transient(self.parent)
284
+ popup.grab_set()
285
+
286
+ # Center the popup
287
+ popup.update_idletasks()
288
+ x = (popup.winfo_screenwidth() // 2) - (popup.winfo_width() // 2)
289
+ y = (popup.winfo_screenheight() // 2) - (popup.winfo_height() // 2)
290
+ popup.geometry(f"+{x}+{y}")
291
+
292
+ # Main frame
293
+ main_frame = ttk.Frame(popup)
294
+ main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
295
+
296
+ # Title
297
+ ttk.Label(main_frame, text="Regex Pattern Library", font=("Arial", 12, "bold")).pack(anchor="w", pady=(0,10))
298
+
299
+ # Treeview for the table
300
+ tree_frame = ttk.Frame(main_frame)
301
+ tree_frame.pack(fill=tk.BOTH, expand=True)
302
+
303
+ # Create Treeview with scrollbars
304
+ tree_scroll_y = ttk.Scrollbar(tree_frame)
305
+ tree_scroll_y.pack(side=tk.RIGHT, fill=tk.Y)
306
+
307
+ tree_scroll_x = ttk.Scrollbar(tree_frame, orient=tk.HORIZONTAL)
308
+ tree_scroll_x.pack(side=tk.BOTTOM, fill=tk.X)
309
+
310
+ tree = ttk.Treeview(tree_frame,
311
+ columns=("Pattern", "Purpose"),
312
+ show="headings",
313
+ yscrollcommand=tree_scroll_y.set,
314
+ xscrollcommand=tree_scroll_x.set)
315
+ tree.pack(fill=tk.BOTH, expand=True)
316
+
317
+ tree_scroll_y.config(command=tree.yview)
318
+ tree_scroll_x.config(command=tree.xview)
319
+
320
+ # Configure columns
321
+ tree.heading("Pattern", text="Pattern")
322
+ tree.heading("Purpose", text="Purpose")
323
+
324
+ tree.column("Pattern", width=300, minwidth=200)
325
+ tree.column("Purpose", width=450, minwidth=300)
326
+
327
+ # Populate tree with patterns
328
+ def refresh_tree():
329
+ tree.delete(*tree.get_children())
330
+ for i, pattern in enumerate(pattern_library):
331
+ # For Regex Extractor, we only show the find pattern and purpose
332
+ find_pattern = pattern.get("find", "")
333
+ purpose = pattern.get("purpose", "")
334
+ tree.insert("", tk.END, iid=i, values=(find_pattern, purpose))
335
+
336
+ refresh_tree()
337
+
338
+ # Management buttons frame
339
+ button_frame = ttk.Frame(main_frame)
340
+ button_frame.pack(fill=tk.X, pady=(10,0))
341
+
342
+ # Left side buttons (management)
343
+ left_buttons = ttk.Frame(button_frame)
344
+ left_buttons.pack(side=tk.LEFT)
345
+
346
+ def add_pattern():
347
+ pattern_library.append({"find": "", "replace": "", "purpose": ""})
348
+ refresh_tree()
349
+ # Select the new item for editing
350
+ new_item_id = len(pattern_library) - 1
351
+ tree.selection_set(str(new_item_id))
352
+ tree.focus(str(new_item_id))
353
+ self.settings_manager.save_settings()
354
+
355
+ def delete_pattern():
356
+ selection = tree.selection()
357
+ if selection:
358
+ item_id = int(selection[0])
359
+ del pattern_library[item_id]
360
+ refresh_tree()
361
+ self.settings_manager.save_settings()
362
+
363
+ def move_up():
364
+ selection = tree.selection()
365
+ if selection:
366
+ item_id = int(selection[0])
367
+ if item_id > 0:
368
+ # Swap with previous item
369
+ pattern_library[item_id], pattern_library[item_id-1] = \
370
+ pattern_library[item_id-1], pattern_library[item_id]
371
+ refresh_tree()
372
+ tree.selection_set(str(item_id-1))
373
+ tree.focus(str(item_id-1))
374
+ self.settings_manager.save_settings()
375
+
376
+ def move_down():
377
+ selection = tree.selection()
378
+ if selection:
379
+ item_id = int(selection[0])
380
+ if item_id < len(pattern_library) - 1:
381
+ # Swap with next item
382
+ pattern_library[item_id], pattern_library[item_id+1] = \
383
+ pattern_library[item_id+1], pattern_library[item_id]
384
+ refresh_tree()
385
+ tree.selection_set(str(item_id+1))
386
+ tree.focus(str(item_id+1))
387
+ self.settings_manager.save_settings()
388
+
389
+ ttk.Button(left_buttons, text="Add", command=add_pattern).pack(side=tk.LEFT, padx=(0,5))
390
+ ttk.Button(left_buttons, text="Delete", command=delete_pattern).pack(side=tk.LEFT, padx=5)
391
+ ttk.Button(left_buttons, text="Move Up", command=move_up).pack(side=tk.LEFT, padx=5)
392
+ ttk.Button(left_buttons, text="Move Down", command=move_down).pack(side=tk.LEFT, padx=5)
393
+
394
+ # Right side buttons (use/close)
395
+ right_buttons = ttk.Frame(button_frame)
396
+ right_buttons.pack(side=tk.RIGHT)
397
+
398
+ def use_pattern():
399
+ selection = tree.selection()
400
+ if selection:
401
+ item_id = int(selection[0])
402
+ pattern = pattern_library[item_id]
403
+ # Only fill the Find field for Regex Extractor
404
+ self.pattern_var.set(pattern.get("find", ""))
405
+ popup.destroy()
406
+ self._on_pattern_change()
407
+
408
+ use_pattern_button = ttk.Button(right_buttons, text="Use Pattern", command=use_pattern)
409
+ use_pattern_button.pack(side=tk.LEFT, padx=5)
410
+ ttk.Button(right_buttons, text="Close", command=popup.destroy).pack(side=tk.LEFT, padx=(5,0))
411
+
412
+ # Function to update button states based on selection
413
+ def update_button_states():
414
+ selection = tree.selection()
415
+ state = "normal" if selection else "disabled"
416
+ use_pattern_button.config(state=state)
417
+
418
+ # Bind selection change to update button states
419
+ tree.bind('<<TreeviewSelect>>', lambda e: update_button_states())
420
+
421
+ # Initial button state update
422
+ update_button_states()
423
+
424
+ # Double-click to use pattern
425
+ tree.bind('<Double-Button-1>', lambda e: use_pattern())
426
+
427
+ # Cell editing functionality
428
+ def on_cell_click(event):
429
+ item = tree.selection()[0] if tree.selection() else None
430
+ if item:
431
+ column = tree.identify_column(event.x)
432
+ if column in ['#1', '#2']: # Pattern, Purpose columns
433
+ self._edit_cell(tree, item, column, popup, pattern_library)
434
+
435
+ tree.bind('<Button-1>', on_cell_click)
436
+
437
+ def _edit_cell(self, tree, item, column, parent_window, pattern_library):
438
+ """Edit a cell in the pattern library tree."""
439
+ # Get current value
440
+ item_id = int(item)
441
+ pattern = pattern_library[item_id]
442
+
443
+ column_map = {'#1': 'find', '#2': 'purpose'}
444
+ field_name = column_map[column]
445
+
446
+ # For Pattern column, edit the 'find' field
447
+ if field_name == 'find':
448
+ current_value = pattern.get("find", "")
449
+ else:
450
+ current_value = pattern.get("purpose", "")
451
+
452
+ # Get cell position
453
+ bbox = tree.bbox(item, column)
454
+ if not bbox:
455
+ return
456
+
457
+ # Create entry widget for editing
458
+ entry = tk.Entry(tree)
459
+ entry.place(x=bbox[0], y=bbox[1], width=bbox[2], height=bbox[3])
460
+ entry.insert(0, current_value)
461
+ entry.select_range(0, tk.END)
462
+ entry.focus()
463
+
464
+ def save_edit():
465
+ new_value = entry.get()
466
+ if field_name == 'find':
467
+ pattern["find"] = new_value
468
+ tree.set(item, column, new_value)
469
+ else:
470
+ pattern["purpose"] = new_value
471
+ tree.set(item, column, new_value)
472
+ entry.destroy()
473
+ self.settings_manager.save_settings()
474
+
475
+ def cancel_edit():
476
+ entry.destroy()
477
+
478
+ entry.bind('<Return>', lambda e: save_edit())
479
+ entry.bind('<Escape>', lambda e: cancel_edit())
480
+ entry.bind('<FocusOut>', lambda e: save_edit())
481
+
482
+
483
+ class RegexExtractor:
484
+ """Main Regex Extractor class that combines processor and UI functionality."""
485
+
486
+ def __init__(self):
487
+ self.processor = RegexExtractorProcessor()
488
+ self.ui = None
489
+
490
+ def create_ui(self, parent, settings, on_setting_change_callback=None, apply_tool_callback=None, settings_manager=None):
491
+ """Create and return the UI component."""
492
+ self.ui = RegexExtractorUI(parent, settings, on_setting_change_callback, apply_tool_callback, settings_manager)
493
+ return self.ui
494
+
495
+ def process_text(self, input_text, settings):
496
+ """Process text using the current settings."""
497
+ return self.processor.process_text(input_text, settings)
498
+
499
+ def get_default_settings(self):
500
+ """Get default settings for the Regex Extractor."""
501
+ return {
502
+ "pattern": "",
503
+ "match_mode": "all_per_line",
504
+ "omit_duplicates": False,
505
+ "hide_counts": True,
506
+ "sort_results": False,
507
+ "case_sensitive": False
508
+ }
509
+
510
+
511
+ # Convenience functions for backward compatibility
512
+ def extract_regex_matches(text, pattern, match_mode="all_per_line", omit_duplicates=False, hide_counts=True, sort_results=False, case_sensitive=False):
513
+ """Extract matches with specified options."""
514
+ return RegexExtractorProcessor.extract_matches(
515
+ text, pattern, match_mode, omit_duplicates, hide_counts, sort_results, case_sensitive
516
+ )
517
+
518
+
519
+ def process_regex_extraction(input_text, settings):
520
+ """Process regex extraction with the specified settings."""
521
+ return RegexExtractorProcessor.process_text(input_text, settings)
522
+
523
+
524
+ # BaseTool-compatible wrapper
525
+ try:
526
+ from tools.base_tool import BaseTool
527
+ from typing import Dict, Any
528
+ import tkinter as tk
529
+ from tkinter import ttk
530
+
531
+ class RegexExtractorV2(BaseTool):
532
+ """
533
+ BaseTool-compatible version of RegexExtractor.
534
+ """
535
+
536
+ TOOL_NAME = "Regex Extractor"
537
+ TOOL_DESCRIPTION = "Extract text matches using regular expressions"
538
+ TOOL_VERSION = "2.0.0"
539
+
540
+ def process_text(self, input_text: str, settings: Dict[str, Any]) -> str:
541
+ """Extract regex matches from text."""
542
+ return RegexExtractorProcessor.extract_matches(
543
+ input_text,
544
+ settings.get("pattern", ""),
545
+ settings.get("match_mode", "all_per_line"),
546
+ settings.get("omit_duplicates", False),
547
+ settings.get("hide_counts", True),
548
+ settings.get("sort_results", False),
549
+ settings.get("case_sensitive", False)
550
+ )
551
+
552
+ def get_default_settings(self) -> Dict[str, Any]:
553
+ return {
554
+ "pattern": "",
555
+ "match_mode": "all_per_line",
556
+ "omit_duplicates": False,
557
+ "hide_counts": True,
558
+ "sort_results": False,
559
+ "case_sensitive": False
560
+ }
561
+
562
+ def create_ui(self, parent: tk.Widget, settings: Dict[str, Any],
563
+ on_change=None, on_apply=None) -> tk.Widget:
564
+ """Create a simple UI for Regex Extractor."""
565
+ frame = ttk.Frame(parent)
566
+ ttk.Label(frame, text="Extract regex matches").pack(side=tk.LEFT, padx=5)
567
+ if on_apply:
568
+ ttk.Button(frame, text="Extract", command=on_apply).pack(side=tk.LEFT, padx=5)
569
+ return frame
570
+
571
+ except ImportError:
572
+ pass