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.
- package/LICENSE +21 -0
- package/README.md +680 -0
- package/bin/pomera-ai-commander.js +62 -0
- package/core/__init__.py +66 -0
- package/core/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/__pycache__/app_context.cpython-313.pyc +0 -0
- package/core/__pycache__/async_text_processor.cpython-313.pyc +0 -0
- package/core/__pycache__/backup_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/backup_recovery_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/content_hash_cache.cpython-313.pyc +0 -0
- package/core/__pycache__/context_menu.cpython-313.pyc +0 -0
- package/core/__pycache__/data_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/database_connection_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_curl_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_promera_ai_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_schema.cpython-313.pyc +0 -0
- package/core/__pycache__/database_schema_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_settings_manager_interface.cpython-313.pyc +0 -0
- package/core/__pycache__/dialog_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/efficient_line_numbers.cpython-313.pyc +0 -0
- package/core/__pycache__/error_handler.cpython-313.pyc +0 -0
- package/core/__pycache__/error_service.cpython-313.pyc +0 -0
- package/core/__pycache__/event_consolidator.cpython-313.pyc +0 -0
- package/core/__pycache__/memory_efficient_text_widget.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_test_suite.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_find_replace.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_pattern_engine.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_search_highlighter.cpython-313.pyc +0 -0
- package/core/__pycache__/performance_monitor.cpython-313.pyc +0 -0
- package/core/__pycache__/persistence_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/progressive_stats_calculator.cpython-313.pyc +0 -0
- package/core/__pycache__/regex_pattern_cache.cpython-313.pyc +0 -0
- package/core/__pycache__/regex_pattern_library.cpython-313.pyc +0 -0
- package/core/__pycache__/search_operation_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_defaults_registry.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_integrity_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_serializer.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/smart_stats_calculator.cpython-313.pyc +0 -0
- package/core/__pycache__/statistics_update_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/stats_config_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/streaming_text_handler.cpython-313.pyc +0 -0
- package/core/__pycache__/task_scheduler.cpython-313.pyc +0 -0
- package/core/__pycache__/visibility_monitor.cpython-313.pyc +0 -0
- package/core/__pycache__/widget_cache.cpython-313.pyc +0 -0
- package/core/app_context.py +482 -0
- package/core/async_text_processor.py +422 -0
- package/core/backup_manager.py +656 -0
- package/core/backup_recovery_manager.py +1034 -0
- package/core/content_hash_cache.py +509 -0
- package/core/context_menu.py +313 -0
- package/core/data_validator.py +1067 -0
- package/core/database_connection_manager.py +745 -0
- package/core/database_curl_settings_manager.py +609 -0
- package/core/database_promera_ai_settings_manager.py +447 -0
- package/core/database_schema.py +412 -0
- package/core/database_schema_manager.py +396 -0
- package/core/database_settings_manager.py +1508 -0
- package/core/database_settings_manager_interface.py +457 -0
- package/core/dialog_manager.py +735 -0
- package/core/efficient_line_numbers.py +511 -0
- package/core/error_handler.py +747 -0
- package/core/error_service.py +431 -0
- package/core/event_consolidator.py +512 -0
- package/core/mcp/__init__.py +43 -0
- package/core/mcp/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/protocol.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/server_stdio.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/tool_registry.cpython-313.pyc +0 -0
- package/core/mcp/protocol.py +288 -0
- package/core/mcp/schema.py +251 -0
- package/core/mcp/server_stdio.py +299 -0
- package/core/mcp/tool_registry.py +2345 -0
- package/core/memory_efficient_text_widget.py +712 -0
- package/core/migration_manager.py +915 -0
- package/core/migration_test_suite.py +1086 -0
- package/core/migration_validator.py +1144 -0
- package/core/optimized_find_replace.py +715 -0
- package/core/optimized_pattern_engine.py +424 -0
- package/core/optimized_search_highlighter.py +553 -0
- package/core/performance_monitor.py +675 -0
- package/core/persistence_manager.py +713 -0
- package/core/progressive_stats_calculator.py +632 -0
- package/core/regex_pattern_cache.py +530 -0
- package/core/regex_pattern_library.py +351 -0
- package/core/search_operation_manager.py +435 -0
- package/core/settings_defaults_registry.py +1087 -0
- package/core/settings_integrity_validator.py +1112 -0
- package/core/settings_serializer.py +558 -0
- package/core/settings_validator.py +1824 -0
- package/core/smart_stats_calculator.py +710 -0
- package/core/statistics_update_manager.py +619 -0
- package/core/stats_config_manager.py +858 -0
- package/core/streaming_text_handler.py +723 -0
- package/core/task_scheduler.py +596 -0
- package/core/update_pattern_library.py +169 -0
- package/core/visibility_monitor.py +596 -0
- package/core/widget_cache.py +498 -0
- package/mcp.json +61 -0
- package/package.json +57 -0
- package/pomera.py +7483 -0
- package/pomera_mcp_server.py +144 -0
- package/tools/__init__.py +5 -0
- package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- package/tools/__pycache__/ai_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/ascii_art_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/base64_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/base_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/case_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/column_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/cron_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_history.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_processor.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_settings.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/diff_viewer.cpython-313.pyc +0 -0
- package/tools/__pycache__/email_extraction_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/email_header_analyzer.cpython-313.pyc +0 -0
- package/tools/__pycache__/extraction_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/find_replace.cpython-313.pyc +0 -0
- package/tools/__pycache__/folder_file_reporter.cpython-313.pyc +0 -0
- package/tools/__pycache__/folder_file_reporter_adapter.cpython-313.pyc +0 -0
- package/tools/__pycache__/generator_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/hash_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/html_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/huggingface_helper.cpython-313.pyc +0 -0
- package/tools/__pycache__/jsonxml_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/line_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/list_comparator.cpython-313.pyc +0 -0
- package/tools/__pycache__/markdown_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/mcp_widget.cpython-313.pyc +0 -0
- package/tools/__pycache__/notes_widget.cpython-313.pyc +0 -0
- package/tools/__pycache__/number_base_converter.cpython-313.pyc +0 -0
- package/tools/__pycache__/regex_extractor.cpython-313.pyc +0 -0
- package/tools/__pycache__/slug_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/sorter_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/string_escape_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/text_statistics_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/text_wrapper.cpython-313.pyc +0 -0
- package/tools/__pycache__/timestamp_converter.cpython-313.pyc +0 -0
- package/tools/__pycache__/tool_loader.cpython-313.pyc +0 -0
- package/tools/__pycache__/translator_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/url_link_extractor.cpython-313.pyc +0 -0
- package/tools/__pycache__/url_parser.cpython-313.pyc +0 -0
- package/tools/__pycache__/whitespace_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/word_frequency_counter.cpython-313.pyc +0 -0
- package/tools/ai_tools.py +2892 -0
- package/tools/ascii_art_generator.py +353 -0
- package/tools/base64_tools.py +184 -0
- package/tools/base_tool.py +511 -0
- package/tools/case_tool.py +309 -0
- package/tools/column_tools.py +396 -0
- package/tools/cron_tool.py +885 -0
- package/tools/curl_history.py +601 -0
- package/tools/curl_processor.py +1208 -0
- package/tools/curl_settings.py +503 -0
- package/tools/curl_tool.py +5467 -0
- package/tools/diff_viewer.py +1072 -0
- package/tools/email_extraction_tool.py +249 -0
- package/tools/email_header_analyzer.py +426 -0
- package/tools/extraction_tools.py +250 -0
- package/tools/find_replace.py +1751 -0
- package/tools/folder_file_reporter.py +1463 -0
- package/tools/folder_file_reporter_adapter.py +480 -0
- package/tools/generator_tools.py +1217 -0
- package/tools/hash_generator.py +256 -0
- package/tools/html_tool.py +657 -0
- package/tools/huggingface_helper.py +449 -0
- package/tools/jsonxml_tool.py +730 -0
- package/tools/line_tools.py +419 -0
- package/tools/list_comparator.py +720 -0
- package/tools/markdown_tools.py +562 -0
- package/tools/mcp_widget.py +1417 -0
- package/tools/notes_widget.py +973 -0
- package/tools/number_base_converter.py +373 -0
- package/tools/regex_extractor.py +572 -0
- package/tools/slug_generator.py +311 -0
- package/tools/sorter_tools.py +459 -0
- package/tools/string_escape_tool.py +393 -0
- package/tools/text_statistics_tool.py +366 -0
- package/tools/text_wrapper.py +431 -0
- package/tools/timestamp_converter.py +422 -0
- package/tools/tool_loader.py +710 -0
- package/tools/translator_tools.py +523 -0
- package/tools/url_link_extractor.py +262 -0
- package/tools/url_parser.py +205 -0
- package/tools/whitespace_tools.py +356 -0
- package/tools/word_frequency_counter.py +147 -0
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base Tool - Abstract base class for all tools.
|
|
3
|
+
|
|
4
|
+
This module provides a standardized interface that all tools should implement
|
|
5
|
+
for consistent behavior across the application.
|
|
6
|
+
|
|
7
|
+
Author: Pomera AI Commander Team
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from abc import ABC, abstractmethod
|
|
11
|
+
from typing import Dict, Any, Optional, Callable, List, Tuple, Type
|
|
12
|
+
import tkinter as tk
|
|
13
|
+
from tkinter import ttk
|
|
14
|
+
import logging
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BaseTool(ABC):
|
|
21
|
+
"""
|
|
22
|
+
Abstract base class that all tools should inherit from.
|
|
23
|
+
|
|
24
|
+
This ensures a consistent interface across all tools for:
|
|
25
|
+
- UI creation
|
|
26
|
+
- Text processing
|
|
27
|
+
- Settings management
|
|
28
|
+
- Font application
|
|
29
|
+
|
|
30
|
+
Subclasses must implement:
|
|
31
|
+
- process_text(): The main text processing logic
|
|
32
|
+
- create_ui(): Create the tool's settings UI
|
|
33
|
+
- get_default_settings(): Return default settings
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
class MyTool(BaseTool):
|
|
37
|
+
TOOL_NAME = "My Tool"
|
|
38
|
+
TOOL_DESCRIPTION = "Does something useful"
|
|
39
|
+
|
|
40
|
+
def process_text(self, input_text: str, settings: Dict[str, Any]) -> str:
|
|
41
|
+
return input_text.upper()
|
|
42
|
+
|
|
43
|
+
def create_ui(self, parent, settings, on_change=None, apply=None):
|
|
44
|
+
# Create UI widgets
|
|
45
|
+
return self._ui_frame
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def get_default_settings(cls) -> Dict[str, Any]:
|
|
49
|
+
return {"option": "default"}
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
# Tool metadata - override in subclasses
|
|
53
|
+
TOOL_NAME: str = "Base Tool"
|
|
54
|
+
TOOL_DESCRIPTION: str = ""
|
|
55
|
+
TOOL_VERSION: str = "1.0.0"
|
|
56
|
+
|
|
57
|
+
# Tool capabilities
|
|
58
|
+
REQUIRES_INPUT: bool = True # Whether tool needs input text
|
|
59
|
+
SUPPORTS_STREAMING: bool = False # Whether tool supports streaming output
|
|
60
|
+
SUPPORTS_ASYNC: bool = False # Whether tool supports async processing
|
|
61
|
+
|
|
62
|
+
def __init__(self):
|
|
63
|
+
"""Initialize the tool."""
|
|
64
|
+
self._settings: Dict[str, Any] = {}
|
|
65
|
+
self._ui_frame: Optional[tk.Frame] = None
|
|
66
|
+
self._on_setting_change: Optional[Callable] = None
|
|
67
|
+
self._apply_callback: Optional[Callable] = None
|
|
68
|
+
self._logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
|
|
69
|
+
self._initializing: bool = False
|
|
70
|
+
|
|
71
|
+
@abstractmethod
|
|
72
|
+
def process_text(self, input_text: str, settings: Dict[str, Any]) -> str:
|
|
73
|
+
"""
|
|
74
|
+
Process input text and return result.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
input_text: The text to process
|
|
78
|
+
settings: Current tool settings
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Processed text result
|
|
82
|
+
"""
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
@abstractmethod
|
|
86
|
+
def create_ui(self,
|
|
87
|
+
parent: tk.Frame,
|
|
88
|
+
settings: Dict[str, Any],
|
|
89
|
+
on_setting_change_callback: Optional[Callable] = None,
|
|
90
|
+
apply_tool_callback: Optional[Callable] = None) -> Any:
|
|
91
|
+
"""
|
|
92
|
+
Create the tool's settings UI.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
parent: Parent frame for the UI
|
|
96
|
+
settings: Current tool settings
|
|
97
|
+
on_setting_change_callback: Called when settings change
|
|
98
|
+
apply_tool_callback: Called to apply the tool
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
The created UI component (frame or widget)
|
|
102
|
+
"""
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
@classmethod
|
|
106
|
+
@abstractmethod
|
|
107
|
+
def get_default_settings(cls) -> Dict[str, Any]:
|
|
108
|
+
"""
|
|
109
|
+
Get default settings for this tool.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Dictionary of default settings
|
|
113
|
+
"""
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
def validate_settings(self, settings: Dict[str, Any]) -> Tuple[bool, str]:
|
|
117
|
+
"""
|
|
118
|
+
Validate settings for this tool.
|
|
119
|
+
|
|
120
|
+
Override this method to add custom validation.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
settings: Settings to validate
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Tuple of (is_valid, error_message)
|
|
127
|
+
"""
|
|
128
|
+
return True, ""
|
|
129
|
+
|
|
130
|
+
def get_current_settings(self) -> Dict[str, Any]:
|
|
131
|
+
"""
|
|
132
|
+
Get current settings from UI state.
|
|
133
|
+
|
|
134
|
+
Override this to extract settings from UI widgets.
|
|
135
|
+
"""
|
|
136
|
+
return self._settings.copy()
|
|
137
|
+
|
|
138
|
+
def update_settings(self, settings: Dict[str, Any]) -> None:
|
|
139
|
+
"""
|
|
140
|
+
Update tool settings.
|
|
141
|
+
|
|
142
|
+
Override this to update UI widgets when settings change.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
settings: New settings to apply
|
|
146
|
+
"""
|
|
147
|
+
self._settings.update(settings)
|
|
148
|
+
|
|
149
|
+
def apply_font_to_widgets(self, font_tuple: Tuple[str, int]) -> None:
|
|
150
|
+
"""
|
|
151
|
+
Apply font settings to tool widgets.
|
|
152
|
+
|
|
153
|
+
Override this if your tool has text widgets that need font updates.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
font_tuple: Tuple of (font_family, font_size)
|
|
157
|
+
"""
|
|
158
|
+
pass
|
|
159
|
+
|
|
160
|
+
def cleanup(self) -> None:
|
|
161
|
+
"""
|
|
162
|
+
Clean up resources when tool is destroyed.
|
|
163
|
+
|
|
164
|
+
Override this to clean up any resources (threads, connections, etc.)
|
|
165
|
+
"""
|
|
166
|
+
pass
|
|
167
|
+
|
|
168
|
+
def _notify_setting_change(self, key: Optional[str] = None, value: Any = None) -> None:
|
|
169
|
+
"""
|
|
170
|
+
Notify that a setting has changed.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
key: Setting key that changed (optional)
|
|
174
|
+
value: New value (optional)
|
|
175
|
+
"""
|
|
176
|
+
if key is not None:
|
|
177
|
+
self._settings[key] = value
|
|
178
|
+
|
|
179
|
+
if not self._initializing and self._on_setting_change:
|
|
180
|
+
self._on_setting_change()
|
|
181
|
+
|
|
182
|
+
# UI Helper methods
|
|
183
|
+
def _create_labeled_entry(self,
|
|
184
|
+
parent: tk.Frame,
|
|
185
|
+
label: str,
|
|
186
|
+
var: tk.Variable,
|
|
187
|
+
width: int = 30,
|
|
188
|
+
label_width: int = 15) -> ttk.Entry:
|
|
189
|
+
"""
|
|
190
|
+
Helper to create a labeled entry field.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
parent: Parent widget
|
|
194
|
+
label: Label text
|
|
195
|
+
var: Tkinter variable for the entry
|
|
196
|
+
width: Entry width
|
|
197
|
+
label_width: Label width
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
The created Entry widget
|
|
201
|
+
"""
|
|
202
|
+
frame = ttk.Frame(parent)
|
|
203
|
+
frame.pack(fill=tk.X, pady=2)
|
|
204
|
+
ttk.Label(frame, text=label, width=label_width).pack(side=tk.LEFT)
|
|
205
|
+
entry = ttk.Entry(frame, textvariable=var, width=width)
|
|
206
|
+
entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
|
207
|
+
return entry
|
|
208
|
+
|
|
209
|
+
def _create_labeled_combo(self,
|
|
210
|
+
parent: tk.Frame,
|
|
211
|
+
label: str,
|
|
212
|
+
var: tk.Variable,
|
|
213
|
+
values: List[str],
|
|
214
|
+
width: int = 27,
|
|
215
|
+
label_width: int = 15,
|
|
216
|
+
on_change: Optional[Callable] = None) -> ttk.Combobox:
|
|
217
|
+
"""
|
|
218
|
+
Helper to create a labeled combobox.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
parent: Parent widget
|
|
222
|
+
label: Label text
|
|
223
|
+
var: Tkinter variable for the combobox
|
|
224
|
+
values: List of values
|
|
225
|
+
width: Combobox width
|
|
226
|
+
label_width: Label width
|
|
227
|
+
on_change: Callback for selection change
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
The created Combobox widget
|
|
231
|
+
"""
|
|
232
|
+
frame = ttk.Frame(parent)
|
|
233
|
+
frame.pack(fill=tk.X, pady=2)
|
|
234
|
+
ttk.Label(frame, text=label, width=label_width).pack(side=tk.LEFT)
|
|
235
|
+
combo = ttk.Combobox(frame, textvariable=var, values=values, width=width, state="readonly")
|
|
236
|
+
combo.pack(side=tk.LEFT)
|
|
237
|
+
if on_change:
|
|
238
|
+
combo.bind("<<ComboboxSelected>>", lambda e: on_change())
|
|
239
|
+
return combo
|
|
240
|
+
|
|
241
|
+
def _create_labeled_spinbox(self,
|
|
242
|
+
parent: tk.Frame,
|
|
243
|
+
label: str,
|
|
244
|
+
var: tk.Variable,
|
|
245
|
+
from_: int = 0,
|
|
246
|
+
to: int = 100,
|
|
247
|
+
width: int = 10,
|
|
248
|
+
label_width: int = 15) -> ttk.Spinbox:
|
|
249
|
+
"""
|
|
250
|
+
Helper to create a labeled spinbox.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
parent: Parent widget
|
|
254
|
+
label: Label text
|
|
255
|
+
var: Tkinter variable
|
|
256
|
+
from_: Minimum value
|
|
257
|
+
to: Maximum value
|
|
258
|
+
width: Spinbox width
|
|
259
|
+
label_width: Label width
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
The created Spinbox widget
|
|
263
|
+
"""
|
|
264
|
+
frame = ttk.Frame(parent)
|
|
265
|
+
frame.pack(fill=tk.X, pady=2)
|
|
266
|
+
ttk.Label(frame, text=label, width=label_width).pack(side=tk.LEFT)
|
|
267
|
+
spinbox = ttk.Spinbox(frame, textvariable=var, from_=from_, to=to, width=width)
|
|
268
|
+
spinbox.pack(side=tk.LEFT)
|
|
269
|
+
return spinbox
|
|
270
|
+
|
|
271
|
+
def _create_checkbox(self,
|
|
272
|
+
parent: tk.Frame,
|
|
273
|
+
label: str,
|
|
274
|
+
var: tk.BooleanVar,
|
|
275
|
+
on_change: Optional[Callable] = None) -> ttk.Checkbutton:
|
|
276
|
+
"""
|
|
277
|
+
Helper to create a checkbox.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
parent: Parent widget
|
|
281
|
+
label: Checkbox label
|
|
282
|
+
var: BooleanVar for the checkbox
|
|
283
|
+
on_change: Callback for state change
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
The created Checkbutton widget
|
|
287
|
+
"""
|
|
288
|
+
cb = ttk.Checkbutton(parent, text=label, variable=var)
|
|
289
|
+
cb.pack(anchor="w", pady=2)
|
|
290
|
+
if on_change:
|
|
291
|
+
cb.configure(command=on_change)
|
|
292
|
+
return cb
|
|
293
|
+
|
|
294
|
+
def _create_radio_group(self,
|
|
295
|
+
parent: tk.Frame,
|
|
296
|
+
label: str,
|
|
297
|
+
var: tk.Variable,
|
|
298
|
+
options: List[Tuple[str, str]],
|
|
299
|
+
on_change: Optional[Callable] = None) -> ttk.Frame:
|
|
300
|
+
"""
|
|
301
|
+
Helper to create a radio button group.
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
parent: Parent widget
|
|
305
|
+
label: Group label
|
|
306
|
+
var: Variable for the selection
|
|
307
|
+
options: List of (text, value) tuples
|
|
308
|
+
on_change: Callback for selection change
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
Frame containing the radio buttons
|
|
312
|
+
"""
|
|
313
|
+
frame = ttk.LabelFrame(parent, text=label)
|
|
314
|
+
frame.pack(fill=tk.X, pady=5)
|
|
315
|
+
|
|
316
|
+
for text, value in options:
|
|
317
|
+
rb = ttk.Radiobutton(
|
|
318
|
+
frame,
|
|
319
|
+
text=text,
|
|
320
|
+
variable=var,
|
|
321
|
+
value=value,
|
|
322
|
+
command=on_change if on_change else None
|
|
323
|
+
)
|
|
324
|
+
rb.pack(anchor="w")
|
|
325
|
+
|
|
326
|
+
return frame
|
|
327
|
+
|
|
328
|
+
def _create_apply_button(self,
|
|
329
|
+
parent: tk.Frame,
|
|
330
|
+
text: str = "Apply",
|
|
331
|
+
callback: Optional[Callable] = None) -> ttk.Button:
|
|
332
|
+
"""
|
|
333
|
+
Helper to create an apply/process button.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
parent: Parent widget
|
|
337
|
+
text: Button text
|
|
338
|
+
callback: Button callback (uses apply_tool_callback if None)
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
The created Button widget
|
|
342
|
+
"""
|
|
343
|
+
cmd = callback or self._apply_callback
|
|
344
|
+
if cmd:
|
|
345
|
+
btn = ttk.Button(parent, text=text, command=cmd)
|
|
346
|
+
btn.pack(side=tk.LEFT, padx=10, pady=10)
|
|
347
|
+
return btn
|
|
348
|
+
return None
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
class SimpleProcessingTool(BaseTool):
|
|
352
|
+
"""
|
|
353
|
+
Base class for simple text processing tools that don't need complex UI.
|
|
354
|
+
|
|
355
|
+
Subclasses only need to implement:
|
|
356
|
+
- process_text(): The text processing logic
|
|
357
|
+
- get_default_settings(): Default settings
|
|
358
|
+
|
|
359
|
+
The UI will show a simple description and apply button.
|
|
360
|
+
"""
|
|
361
|
+
|
|
362
|
+
def create_ui(self,
|
|
363
|
+
parent: tk.Frame,
|
|
364
|
+
settings: Dict[str, Any],
|
|
365
|
+
on_setting_change_callback: Optional[Callable] = None,
|
|
366
|
+
apply_tool_callback: Optional[Callable] = None) -> tk.Frame:
|
|
367
|
+
"""Create a simple UI showing tool description and apply button."""
|
|
368
|
+
self._settings = settings.copy()
|
|
369
|
+
self._on_setting_change = on_setting_change_callback
|
|
370
|
+
self._apply_callback = apply_tool_callback
|
|
371
|
+
|
|
372
|
+
frame = ttk.Frame(parent)
|
|
373
|
+
frame.pack(fill=tk.BOTH, expand=True)
|
|
374
|
+
|
|
375
|
+
# Show description if available
|
|
376
|
+
if self.TOOL_DESCRIPTION:
|
|
377
|
+
desc_label = ttk.Label(frame, text=self.TOOL_DESCRIPTION, wraplength=400)
|
|
378
|
+
desc_label.pack(pady=10)
|
|
379
|
+
|
|
380
|
+
# Add apply button
|
|
381
|
+
self._create_apply_button(frame)
|
|
382
|
+
|
|
383
|
+
self._ui_frame = frame
|
|
384
|
+
return frame
|
|
385
|
+
|
|
386
|
+
@classmethod
|
|
387
|
+
def get_default_settings(cls) -> Dict[str, Any]:
|
|
388
|
+
"""Simple tools have no settings by default."""
|
|
389
|
+
return {}
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
class ToolWithOptions(BaseTool):
|
|
393
|
+
"""
|
|
394
|
+
Base class for tools with selectable options/modes.
|
|
395
|
+
|
|
396
|
+
Provides automatic UI generation for tools that have:
|
|
397
|
+
- A mode/option selector (radio buttons or dropdown)
|
|
398
|
+
- Optional additional settings per mode
|
|
399
|
+
|
|
400
|
+
Subclasses should define:
|
|
401
|
+
- OPTIONS: List of (display_name, value) tuples
|
|
402
|
+
- OPTIONS_LABEL: Label for the options (default: "Mode")
|
|
403
|
+
- USE_DROPDOWN: True for dropdown, False for radio buttons
|
|
404
|
+
"""
|
|
405
|
+
|
|
406
|
+
OPTIONS: List[Tuple[str, str]] = []
|
|
407
|
+
OPTIONS_LABEL: str = "Mode"
|
|
408
|
+
USE_DROPDOWN: bool = False
|
|
409
|
+
DEFAULT_OPTION: str = ""
|
|
410
|
+
|
|
411
|
+
def __init__(self):
|
|
412
|
+
super().__init__()
|
|
413
|
+
self._option_var: Optional[tk.StringVar] = None
|
|
414
|
+
|
|
415
|
+
def create_ui(self,
|
|
416
|
+
parent: tk.Frame,
|
|
417
|
+
settings: Dict[str, Any],
|
|
418
|
+
on_setting_change_callback: Optional[Callable] = None,
|
|
419
|
+
apply_tool_callback: Optional[Callable] = None) -> tk.Frame:
|
|
420
|
+
"""Create UI with option selector."""
|
|
421
|
+
self._settings = settings.copy()
|
|
422
|
+
self._on_setting_change = on_setting_change_callback
|
|
423
|
+
self._apply_callback = apply_tool_callback
|
|
424
|
+
self._initializing = True
|
|
425
|
+
|
|
426
|
+
frame = ttk.Frame(parent)
|
|
427
|
+
frame.pack(fill=tk.BOTH, expand=True)
|
|
428
|
+
|
|
429
|
+
# Option selector
|
|
430
|
+
current_value = settings.get("mode", self.DEFAULT_OPTION or
|
|
431
|
+
(self.OPTIONS[0][1] if self.OPTIONS else ""))
|
|
432
|
+
self._option_var = tk.StringVar(value=current_value)
|
|
433
|
+
|
|
434
|
+
if self.USE_DROPDOWN:
|
|
435
|
+
values = [opt[0] for opt in self.OPTIONS]
|
|
436
|
+
self._create_labeled_combo(
|
|
437
|
+
frame,
|
|
438
|
+
self.OPTIONS_LABEL + ":",
|
|
439
|
+
self._option_var,
|
|
440
|
+
values,
|
|
441
|
+
on_change=self._on_option_change
|
|
442
|
+
)
|
|
443
|
+
else:
|
|
444
|
+
self._create_radio_group(
|
|
445
|
+
frame,
|
|
446
|
+
self.OPTIONS_LABEL,
|
|
447
|
+
self._option_var,
|
|
448
|
+
self.OPTIONS,
|
|
449
|
+
on_change=self._on_option_change
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
# Create additional UI (override in subclass)
|
|
453
|
+
self._create_additional_ui(frame, settings)
|
|
454
|
+
|
|
455
|
+
# Apply button
|
|
456
|
+
self._create_apply_button(frame)
|
|
457
|
+
|
|
458
|
+
self._ui_frame = frame
|
|
459
|
+
self._initializing = False
|
|
460
|
+
return frame
|
|
461
|
+
|
|
462
|
+
def _create_additional_ui(self, parent: tk.Frame, settings: Dict[str, Any]) -> None:
|
|
463
|
+
"""
|
|
464
|
+
Override to add additional UI elements.
|
|
465
|
+
|
|
466
|
+
Args:
|
|
467
|
+
parent: Parent frame
|
|
468
|
+
settings: Current settings
|
|
469
|
+
"""
|
|
470
|
+
pass
|
|
471
|
+
|
|
472
|
+
def _on_option_change(self) -> None:
|
|
473
|
+
"""Handle option change."""
|
|
474
|
+
self._notify_setting_change("mode", self._option_var.get())
|
|
475
|
+
|
|
476
|
+
def get_current_settings(self) -> Dict[str, Any]:
|
|
477
|
+
"""Get current settings including selected option."""
|
|
478
|
+
settings = self._settings.copy()
|
|
479
|
+
if self._option_var:
|
|
480
|
+
settings["mode"] = self._option_var.get()
|
|
481
|
+
return settings
|
|
482
|
+
|
|
483
|
+
@classmethod
|
|
484
|
+
def get_default_settings(cls) -> Dict[str, Any]:
|
|
485
|
+
"""Default settings with first option selected."""
|
|
486
|
+
default_mode = cls.DEFAULT_OPTION or (cls.OPTIONS[0][1] if cls.OPTIONS else "")
|
|
487
|
+
return {"mode": default_mode}
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def get_tool_registry_defaults(tool_name: str, fallback: Dict[str, Any]) -> Dict[str, Any]:
|
|
491
|
+
"""
|
|
492
|
+
Helper to get defaults from registry with fallback.
|
|
493
|
+
|
|
494
|
+
Args:
|
|
495
|
+
tool_name: Name of the tool
|
|
496
|
+
fallback: Fallback defaults if registry unavailable
|
|
497
|
+
|
|
498
|
+
Returns:
|
|
499
|
+
Tool default settings
|
|
500
|
+
"""
|
|
501
|
+
try:
|
|
502
|
+
from core.settings_defaults_registry import get_registry
|
|
503
|
+
registry = get_registry()
|
|
504
|
+
return registry.get_tool_defaults(tool_name)
|
|
505
|
+
except ImportError:
|
|
506
|
+
pass
|
|
507
|
+
except Exception as e:
|
|
508
|
+
logger.debug(f"Could not get registry defaults for {tool_name}: {e}")
|
|
509
|
+
|
|
510
|
+
return fallback
|
|
511
|
+
|