pomera-ai-commander 1.2.4 → 1.2.7
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/README.md +23 -4
- package/core/backup_recovery_manager.py +223 -60
- package/core/collapsible_panel.py +197 -0
- package/core/database_settings_manager.py +20 -9
- package/core/migration_manager.py +12 -11
- package/core/settings_defaults_registry.py +8 -0
- package/core/tool_search_widget.py +417 -0
- package/mcp.json +1 -1
- package/package.json +2 -1
- package/pomera.py +414 -90
- package/pomera_mcp_server.py +8 -2
- package/tools/curl_history.py +45 -5
- package/tools/curl_tool.py +42 -12
- package/tools/diff_viewer.py +2 -9
- package/tools/tool_loader.py +263 -15
package/pomera.py
CHANGED
|
@@ -494,6 +494,22 @@ except ImportError as e:
|
|
|
494
494
|
WIDGET_CACHE_AVAILABLE = False
|
|
495
495
|
print(f"Widget Cache not available: {e}")
|
|
496
496
|
|
|
497
|
+
# Collapsible Panel import (UI redesign)
|
|
498
|
+
try:
|
|
499
|
+
from core.collapsible_panel import CollapsiblePanel
|
|
500
|
+
COLLAPSIBLE_PANEL_AVAILABLE = True
|
|
501
|
+
except ImportError as e:
|
|
502
|
+
COLLAPSIBLE_PANEL_AVAILABLE = False
|
|
503
|
+
print(f"Collapsible Panel not available: {e}")
|
|
504
|
+
|
|
505
|
+
# Tool Search Widget import (UI redesign)
|
|
506
|
+
try:
|
|
507
|
+
from core.tool_search_widget import ToolSearchPalette
|
|
508
|
+
TOOL_SEARCH_WIDGET_AVAILABLE = True
|
|
509
|
+
except ImportError as e:
|
|
510
|
+
TOOL_SEARCH_WIDGET_AVAILABLE = False
|
|
511
|
+
print(f"Tool Search Widget not available: {e}")
|
|
512
|
+
|
|
497
513
|
class AppConfig:
|
|
498
514
|
"""Configuration constants for the application."""
|
|
499
515
|
DEFAULT_WINDOW_SIZE = "1200x900"
|
|
@@ -938,7 +954,8 @@ class PromeraAIApp(tk.Tk):
|
|
|
938
954
|
self.logger.info("Existing content found - skipping apply_tool to preserve loaded content")
|
|
939
955
|
elif self.tool_var.get() == "Diff Viewer":
|
|
940
956
|
self.central_frame.grid_remove()
|
|
941
|
-
|
|
957
|
+
# Use row=1 (same as central_frame) to not cover search bar in row=0
|
|
958
|
+
self.diff_frame.grid(row=1, column=0, sticky="nsew", pady=5)
|
|
942
959
|
self.update_tool_settings_ui()
|
|
943
960
|
self.load_diff_viewer_content()
|
|
944
961
|
self.run_diff_viewer()
|
|
@@ -961,6 +978,15 @@ class PromeraAIApp(tk.Tk):
|
|
|
961
978
|
if MCP_MANAGER_MODULE_AVAILABLE:
|
|
962
979
|
self.bind_all("<Control-m>", lambda e: self.open_mcp_manager())
|
|
963
980
|
|
|
981
|
+
# Set up Tool Search keyboard shortcut (Ctrl+K)
|
|
982
|
+
if TOOL_SEARCH_WIDGET_AVAILABLE:
|
|
983
|
+
self.bind_all("<Control-k>", self.focus_tool_search)
|
|
984
|
+
|
|
985
|
+
# Set up Options Panel toggle shortcut (Ctrl+Shift+H)
|
|
986
|
+
if COLLAPSIBLE_PANEL_AVAILABLE:
|
|
987
|
+
self.bind_all("<Control-Shift-h>", self.toggle_options_panel)
|
|
988
|
+
self.bind_all("<Control-Shift-H>", self.toggle_options_panel) # Windows needs uppercase
|
|
989
|
+
|
|
964
990
|
# Set up window focus and minimize event handlers for visibility-aware updates
|
|
965
991
|
if hasattr(self, 'statistics_update_manager') and self.statistics_update_manager:
|
|
966
992
|
self.bind("<FocusIn>", self._on_window_focus_in)
|
|
@@ -1196,26 +1222,12 @@ class PromeraAIApp(tk.Tk):
|
|
|
1196
1222
|
|
|
1197
1223
|
ttk.Label(main_frame, text="Pomera AI Commander", font=('TkDefaultFont', 14, 'bold')).pack(pady=(0, 10))
|
|
1198
1224
|
|
|
1199
|
-
# Get current version
|
|
1200
|
-
current_version = None
|
|
1225
|
+
# Get current version from unified version module
|
|
1201
1226
|
try:
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
content = pyproject_path.read_text()
|
|
1207
|
-
match = re.search(r'version = "1.2.4"]+)"', content)
|
|
1208
|
-
if match:
|
|
1209
|
-
current_version = match.group(1)
|
|
1210
|
-
except Exception:
|
|
1211
|
-
pass
|
|
1212
|
-
|
|
1213
|
-
if not current_version:
|
|
1214
|
-
try:
|
|
1215
|
-
import importlib.metadata
|
|
1216
|
-
current_version = importlib.metadata.version("pomera-ai-commander")
|
|
1217
|
-
except Exception:
|
|
1218
|
-
current_version = "1.2.4"
|
|
1227
|
+
from pomera.version import __version__
|
|
1228
|
+
current_version = __version__
|
|
1229
|
+
except ImportError:
|
|
1230
|
+
current_version = "unknown"
|
|
1219
1231
|
|
|
1220
1232
|
# Detect OS for download link
|
|
1221
1233
|
system = platform.system()
|
|
@@ -1353,8 +1365,12 @@ class PromeraAIApp(tk.Tk):
|
|
|
1353
1365
|
|
|
1354
1366
|
def _show_about_dialog(self):
|
|
1355
1367
|
"""Show About dialog."""
|
|
1356
|
-
# Version managed by
|
|
1357
|
-
|
|
1368
|
+
# Version from unified version module (managed by setuptools_scm)
|
|
1369
|
+
try:
|
|
1370
|
+
from pomera.version import __version__
|
|
1371
|
+
version = __version__
|
|
1372
|
+
except ImportError:
|
|
1373
|
+
version = "unknown"
|
|
1358
1374
|
|
|
1359
1375
|
messagebox.showinfo(
|
|
1360
1376
|
"About Pomera AI Commander",
|
|
@@ -2203,11 +2219,17 @@ class PromeraAIApp(tk.Tk):
|
|
|
2203
2219
|
|
|
2204
2220
|
main_frame = ttk.Frame(self, padding="10")
|
|
2205
2221
|
main_frame.pack(fill=tk.BOTH, expand=True)
|
|
2206
|
-
main_frame.rowconfigure(
|
|
2222
|
+
main_frame.rowconfigure(1, weight=1) # Central frame gets the weight
|
|
2207
2223
|
main_frame.columnconfigure(0, weight=1)
|
|
2224
|
+
|
|
2225
|
+
# Row 0: Search bar (below menu, above Input/Output)
|
|
2226
|
+
self.top_search_frame = ttk.Frame(main_frame)
|
|
2227
|
+
self.top_search_frame.grid(row=0, column=0, sticky="ew", pady=(0, 5))
|
|
2228
|
+
self._create_top_search_bar(self.top_search_frame)
|
|
2208
2229
|
|
|
2230
|
+
# Row 1: Central frame with Input/Output panels
|
|
2209
2231
|
self.central_frame = ttk.Frame(main_frame, padding="10")
|
|
2210
|
-
self.central_frame.grid(row=
|
|
2232
|
+
self.central_frame.grid(row=1, column=0, sticky="nsew", pady=5)
|
|
2211
2233
|
self.central_frame.grid_columnconfigure(0, weight=1)
|
|
2212
2234
|
self.central_frame.grid_columnconfigure(1, weight=1)
|
|
2213
2235
|
self.central_frame.grid_rowconfigure(1, weight=1)
|
|
@@ -2220,12 +2242,13 @@ class PromeraAIApp(tk.Tk):
|
|
|
2220
2242
|
|
|
2221
2243
|
self.create_diff_viewer(main_frame)
|
|
2222
2244
|
|
|
2223
|
-
#
|
|
2245
|
+
# Row 2: Separator
|
|
2224
2246
|
separator = ttk.Separator(main_frame, orient='horizontal')
|
|
2225
|
-
separator.grid(row=
|
|
2247
|
+
separator.grid(row=2, column=0, sticky="ew", pady=10)
|
|
2226
2248
|
|
|
2249
|
+
# Row 3: Tool options frame (collapsible)
|
|
2227
2250
|
tool_frame = ttk.Frame(main_frame, padding="10")
|
|
2228
|
-
tool_frame.grid(row=
|
|
2251
|
+
tool_frame.grid(row=3, column=0, sticky="ew", pady=5)
|
|
2229
2252
|
self.create_tool_widgets(tool_frame)
|
|
2230
2253
|
|
|
2231
2254
|
# Setup context menus for all text widgets
|
|
@@ -3698,35 +3721,78 @@ class PromeraAIApp(tk.Tk):
|
|
|
3698
3721
|
print(f"Filter text: {repr(self.output_filter_var.get())}")
|
|
3699
3722
|
except Exception as e:
|
|
3700
3723
|
print(f"Debug error: {e}")
|
|
3701
|
-
|
|
3702
|
-
def
|
|
3703
|
-
"""
|
|
3724
|
+
|
|
3725
|
+
def _create_top_search_bar(self, parent):
|
|
3726
|
+
"""Create the top search bar (below menu, above Input/Output panels)."""
|
|
3704
3727
|
self.tool_var = tk.StringVar(value=self.settings.get("selected_tool", "Case Tool"))
|
|
3705
3728
|
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
self.update_tool_dropdown_menu()
|
|
3729
|
+
# Use ToolSearchPalette if available
|
|
3730
|
+
if TOOL_SEARCH_WIDGET_AVAILABLE and TOOL_LOADER_AVAILABLE and self.tool_loader:
|
|
3731
|
+
self.tool_search_palette = ToolSearchPalette(
|
|
3732
|
+
parent,
|
|
3733
|
+
tool_loader=self.tool_loader,
|
|
3734
|
+
on_tool_selected=self._on_palette_tool_selected,
|
|
3735
|
+
settings=self.settings,
|
|
3736
|
+
on_settings_change=self._on_ui_settings_change
|
|
3737
|
+
)
|
|
3738
|
+
self.tool_search_palette.pack(fill=tk.X, expand=True, padx=5)
|
|
3739
|
+
self.tool_menu = self.tool_search_palette
|
|
3740
|
+
self.tool_dropdown_menu = None
|
|
3741
|
+
self.logger.info("Top search bar created with ToolSearchPalette")
|
|
3742
|
+
else:
|
|
3743
|
+
# Fallback: simple label
|
|
3744
|
+
self.tool_search_palette = None
|
|
3745
|
+
self.tool_menu = None
|
|
3746
|
+
self.tool_dropdown_menu = None
|
|
3747
|
+
ttk.Label(parent, text="Tool search not available").pack()
|
|
3748
|
+
|
|
3727
3749
|
|
|
3728
|
-
|
|
3729
|
-
|
|
3750
|
+
def create_tool_widgets(self, parent):
|
|
3751
|
+
"""Creates widgets for the tool options/settings section (bottom panel).
|
|
3752
|
+
|
|
3753
|
+
Note: The search bar is now created separately by _create_top_search_bar()
|
|
3754
|
+
at the top of the main layout.
|
|
3755
|
+
"""
|
|
3756
|
+
# Get tool list for fallback
|
|
3757
|
+
if TOOL_LOADER_AVAILABLE and self.tool_loader:
|
|
3758
|
+
self.tool_options = self.tool_loader.get_available_tools()
|
|
3759
|
+
else:
|
|
3760
|
+
self.tool_options = [
|
|
3761
|
+
"AI Tools", "Base64 Encoder/Decoder", "Case Tool", "Column Tools",
|
|
3762
|
+
"Cron Tool", "Diff Viewer", "Email Header Analyzer", "Extraction Tools",
|
|
3763
|
+
"Find & Replace Text", "Folder File Reporter", "Generator Tools",
|
|
3764
|
+
"JSON/XML Tool", "Line Tools", "Markdown Tools",
|
|
3765
|
+
"Number Base Converter", "Sorter Tools",
|
|
3766
|
+
"String Escape Tool", "Text Statistics", "Text Wrapper", "Timestamp Converter",
|
|
3767
|
+
"Translator Tools", "URL Parser", "Whitespace Tools"
|
|
3768
|
+
]
|
|
3769
|
+
self.filtered_tool_options = list(self.tool_options)
|
|
3770
|
+
|
|
3771
|
+
# Tool settings panel (with collapsible wrapper if available)
|
|
3772
|
+
if COLLAPSIBLE_PANEL_AVAILABLE:
|
|
3773
|
+
# Get collapsed state from settings
|
|
3774
|
+
ui_layout = self.settings.get("ui_layout", {})
|
|
3775
|
+
initial_collapsed = ui_layout.get("options_panel_collapsed", False)
|
|
3776
|
+
|
|
3777
|
+
# Create CollapsiblePanel to wrap tool settings
|
|
3778
|
+
self.options_collapsible = CollapsiblePanel(
|
|
3779
|
+
parent,
|
|
3780
|
+
title="Tool Options",
|
|
3781
|
+
collapsed=initial_collapsed,
|
|
3782
|
+
on_state_change=self._on_options_panel_toggle
|
|
3783
|
+
)
|
|
3784
|
+
self.options_collapsible.pack(fill=tk.BOTH, expand=True, padx=5)
|
|
3785
|
+
|
|
3786
|
+
# Tool settings go inside the collapsible panel's content
|
|
3787
|
+
self.tool_settings_frame = ttk.Frame(self.options_collapsible.content_frame)
|
|
3788
|
+
self.tool_settings_frame.pack(fill=tk.BOTH, expand=True)
|
|
3789
|
+
|
|
3790
|
+
self.logger.info(f"Options panel wrapped in CollapsiblePanel (collapsed={initial_collapsed})")
|
|
3791
|
+
else:
|
|
3792
|
+
# Fallback: Tool settings frame without collapsible wrapper
|
|
3793
|
+
self.tool_settings_frame = ttk.Frame(parent)
|
|
3794
|
+
self.tool_settings_frame.pack(fill=tk.BOTH, expand=True, padx=5)
|
|
3795
|
+
self.options_collapsible = None
|
|
3730
3796
|
|
|
3731
3797
|
# Initialize Widget Cache for tool settings UI
|
|
3732
3798
|
if WIDGET_CACHE_AVAILABLE:
|
|
@@ -3741,9 +3807,262 @@ class PromeraAIApp(tk.Tk):
|
|
|
3741
3807
|
self.widget_cache = None
|
|
3742
3808
|
|
|
3743
3809
|
self.update_tool_settings_ui()
|
|
3810
|
+
|
|
3811
|
+
def _create_tool_search_palette(self, parent):
|
|
3812
|
+
"""Create the new ToolSearchPalette for tool selection."""
|
|
3813
|
+
# Get UI layout settings
|
|
3814
|
+
ui_layout = self.settings.get("ui_layout", {})
|
|
3815
|
+
|
|
3816
|
+
self.tool_search_palette = ToolSearchPalette(
|
|
3817
|
+
parent,
|
|
3818
|
+
tool_loader=self.tool_loader,
|
|
3819
|
+
on_tool_selected=self._on_palette_tool_selected,
|
|
3820
|
+
settings=self.settings,
|
|
3821
|
+
on_settings_change=self._on_ui_settings_change
|
|
3822
|
+
)
|
|
3823
|
+
# Pack to fill horizontally (top-center layout)
|
|
3824
|
+
self.tool_search_palette.pack(fill=tk.BOTH, expand=True, padx=5)
|
|
3825
|
+
|
|
3826
|
+
# For backwards compatibility, also keep a reference as tool_menu
|
|
3827
|
+
self.tool_menu = self.tool_search_palette
|
|
3828
|
+
self.tool_dropdown_menu = None # Not used with palette
|
|
3829
|
+
|
|
3830
|
+
self.logger.info("Tool Search Palette initialized with fuzzy search")
|
|
3831
|
+
|
|
3832
|
+
def _create_legacy_tool_dropdown(self, parent):
|
|
3833
|
+
"""Create the legacy dropdown menu for tool selection (fallback)."""
|
|
3834
|
+
# Create custom dropdown that opens upwards
|
|
3835
|
+
self.tool_menu = ttk.Menubutton(parent, textvariable=self.tool_var, direction="above", width=25)
|
|
3836
|
+
self.tool_menu.pack(side=tk.LEFT, padx=5)
|
|
3837
|
+
|
|
3838
|
+
# Create the dropdown menu
|
|
3839
|
+
self.tool_dropdown_menu = tk.Menu(self.tool_menu, tearoff=0)
|
|
3840
|
+
self.tool_menu.config(menu=self.tool_dropdown_menu)
|
|
3841
|
+
|
|
3842
|
+
# Populate the dropdown menu with all tools
|
|
3843
|
+
self.update_tool_dropdown_menu()
|
|
3844
|
+
|
|
3845
|
+
self.tool_search_palette = None # Not available in legacy mode
|
|
3846
|
+
self.logger.info("Using legacy tool dropdown (ToolSearchPalette not available)")
|
|
3847
|
+
|
|
3848
|
+
# Sub-tools that should redirect to their parent category
|
|
3849
|
+
SUB_TOOL_TO_PARENT = {
|
|
3850
|
+
# AI Tools (11 sub-tools)
|
|
3851
|
+
"Google AI": "AI Tools",
|
|
3852
|
+
"Vertex AI": "AI Tools",
|
|
3853
|
+
"Azure AI": "AI Tools",
|
|
3854
|
+
"Anthropic AI": "AI Tools",
|
|
3855
|
+
"OpenAI": "AI Tools",
|
|
3856
|
+
"Cohere AI": "AI Tools",
|
|
3857
|
+
"HuggingFace AI": "AI Tools",
|
|
3858
|
+
"Groq AI": "AI Tools",
|
|
3859
|
+
"OpenRouterAI": "AI Tools",
|
|
3860
|
+
"LM Studio": "AI Tools",
|
|
3861
|
+
"AWS Bedrock": "AI Tools",
|
|
3862
|
+
# Generator Tools (8 sub-tools)
|
|
3863
|
+
"Strong Password Generator": "Generator Tools",
|
|
3864
|
+
"Repeating Text Generator": "Generator Tools",
|
|
3865
|
+
"Lorem Ipsum Generator": "Generator Tools",
|
|
3866
|
+
"UUID/GUID Generator": "Generator Tools",
|
|
3867
|
+
"Random Email Generator": "Generator Tools",
|
|
3868
|
+
"ASCII Art Generator": "Generator Tools",
|
|
3869
|
+
"Hash Generator": "Generator Tools",
|
|
3870
|
+
"Slug Generator": "Generator Tools",
|
|
3871
|
+
# Extraction Tools (4 sub-tools)
|
|
3872
|
+
"Email Extraction": "Extraction Tools",
|
|
3873
|
+
"HTML Tool": "Extraction Tools",
|
|
3874
|
+
"Regex Extractor": "Extraction Tools",
|
|
3875
|
+
"URL Link Extractor": "Extraction Tools",
|
|
3876
|
+
# Line Tools (6 sub-tools)
|
|
3877
|
+
"Remove Duplicates": "Line Tools",
|
|
3878
|
+
"Remove Empty Lines": "Line Tools",
|
|
3879
|
+
"Add Line Numbers": "Line Tools",
|
|
3880
|
+
"Remove Line Numbers": "Line Tools",
|
|
3881
|
+
"Reverse Lines": "Line Tools",
|
|
3882
|
+
"Shuffle Lines": "Line Tools",
|
|
3883
|
+
# Markdown Tools (5 sub-tools)
|
|
3884
|
+
"Strip Markdown": "Markdown Tools",
|
|
3885
|
+
"Extract Links": "Markdown Tools",
|
|
3886
|
+
"Extract Headers": "Markdown Tools",
|
|
3887
|
+
"Table to CSV": "Markdown Tools",
|
|
3888
|
+
"Format Table": "Markdown Tools",
|
|
3889
|
+
# Sorter Tools (2 sub-tools)
|
|
3890
|
+
"Number Sorter": "Sorter Tools",
|
|
3891
|
+
"Alphabetical Sorter": "Sorter Tools",
|
|
3892
|
+
# Text Wrapper (5 sub-tools)
|
|
3893
|
+
"Word Wrap": "Text Wrapper",
|
|
3894
|
+
"Justify Text": "Text Wrapper",
|
|
3895
|
+
"Prefix/Suffix": "Text Wrapper",
|
|
3896
|
+
"Indent Text": "Text Wrapper",
|
|
3897
|
+
"Quote Text": "Text Wrapper",
|
|
3898
|
+
# Translator Tools (2 sub-tools)
|
|
3899
|
+
"Morse Code Translator": "Translator Tools",
|
|
3900
|
+
"Binary Code Translator": "Translator Tools",
|
|
3901
|
+
# Whitespace Tools (4 sub-tools)
|
|
3902
|
+
"Trim Lines": "Whitespace Tools",
|
|
3903
|
+
"Remove Extra Spaces": "Whitespace Tools",
|
|
3904
|
+
"Tabs/Spaces Converter": "Whitespace Tools",
|
|
3905
|
+
"Normalize Line Endings": "Whitespace Tools",
|
|
3906
|
+
# Other sub-tools
|
|
3907
|
+
"Word Frequency Counter": "Text Statistics",
|
|
3908
|
+
}
|
|
3909
|
+
|
|
3910
|
+
# Tab index within parent category for sub-tools (0-indexed)
|
|
3911
|
+
# Based on actual tab order in UI from screenshot
|
|
3912
|
+
SUB_TOOL_TAB_INDEX = {
|
|
3913
|
+
# AI Tools tabs (11 tabs):
|
|
3914
|
+
"Google AI": 0,
|
|
3915
|
+
"Vertex AI": 1,
|
|
3916
|
+
"Azure AI": 2,
|
|
3917
|
+
"Anthropic AI": 3,
|
|
3918
|
+
"OpenAI": 4,
|
|
3919
|
+
"Cohere AI": 5,
|
|
3920
|
+
"HuggingFace AI": 6,
|
|
3921
|
+
"Groq AI": 7,
|
|
3922
|
+
"OpenRouterAI": 8,
|
|
3923
|
+
"LM Studio": 9,
|
|
3924
|
+
"AWS Bedrock": 10,
|
|
3925
|
+
# Generator Tools tabs (8 tabs):
|
|
3926
|
+
"Strong Password Generator": 0,
|
|
3927
|
+
"Repeating Text Generator": 1,
|
|
3928
|
+
"Lorem Ipsum Generator": 2,
|
|
3929
|
+
"UUID/GUID Generator": 3,
|
|
3930
|
+
"Random Email Generator": 4,
|
|
3931
|
+
"ASCII Art Generator": 5,
|
|
3932
|
+
"Hash Generator": 6,
|
|
3933
|
+
"Slug Generator": 7,
|
|
3934
|
+
# Extraction Tools tabs (4 tabs):
|
|
3935
|
+
"Email Extraction": 0,
|
|
3936
|
+
"HTML Tool": 1,
|
|
3937
|
+
"Regex Extractor": 2,
|
|
3938
|
+
"URL Link Extractor": 3,
|
|
3939
|
+
# Line Tools tabs (6 tabs):
|
|
3940
|
+
"Remove Duplicates": 0,
|
|
3941
|
+
"Remove Empty Lines": 1,
|
|
3942
|
+
"Add Line Numbers": 2,
|
|
3943
|
+
"Remove Line Numbers": 3,
|
|
3944
|
+
"Reverse Lines": 4,
|
|
3945
|
+
"Shuffle Lines": 5,
|
|
3946
|
+
# Markdown Tools tabs (5 tabs):
|
|
3947
|
+
"Strip Markdown": 0,
|
|
3948
|
+
"Extract Links": 1,
|
|
3949
|
+
"Extract Headers": 2,
|
|
3950
|
+
"Table to CSV": 3,
|
|
3951
|
+
"Format Table": 4,
|
|
3952
|
+
# Sorter Tools tabs (2 tabs):
|
|
3953
|
+
"Number Sorter": 0,
|
|
3954
|
+
"Alphabetical Sorter": 1,
|
|
3955
|
+
# Text Wrapper tabs (5 tabs):
|
|
3956
|
+
"Word Wrap": 0,
|
|
3957
|
+
"Justify Text": 1,
|
|
3958
|
+
"Prefix/Suffix": 2,
|
|
3959
|
+
"Indent Text": 3,
|
|
3960
|
+
"Quote Text": 4,
|
|
3961
|
+
# Translator Tools tabs (2 tabs):
|
|
3962
|
+
"Morse Code Translator": 0,
|
|
3963
|
+
"Binary Code Translator": 1,
|
|
3964
|
+
# Whitespace Tools tabs (4 tabs):
|
|
3965
|
+
"Trim Lines": 0,
|
|
3966
|
+
"Remove Extra Spaces": 1,
|
|
3967
|
+
"Tabs/Spaces Converter": 2,
|
|
3968
|
+
"Normalize Line Endings": 3,
|
|
3969
|
+
}
|
|
3970
|
+
|
|
3971
|
+
def _on_palette_tool_selected(self, tool_name):
|
|
3972
|
+
"""Handle tool selection from the ToolSearchPalette."""
|
|
3973
|
+
# Map sub-tools to their parent category
|
|
3974
|
+
actual_tool = self.SUB_TOOL_TO_PARENT.get(tool_name, tool_name)
|
|
3975
|
+
sub_tool_tab = self.SUB_TOOL_TAB_INDEX.get(tool_name)
|
|
3976
|
+
|
|
3977
|
+
if actual_tool != tool_name:
|
|
3978
|
+
self.logger.debug(f"Mapped sub-tool '{tool_name}' to parent '{actual_tool}' (tab {sub_tool_tab})")
|
|
3979
|
+
|
|
3980
|
+
self.tool_var.set(actual_tool)
|
|
3981
|
+
|
|
3982
|
+
# Auto-expand options panel if collapsed
|
|
3983
|
+
if hasattr(self, 'options_collapsible') and self.options_collapsible:
|
|
3984
|
+
if self.options_collapsible.collapsed:
|
|
3985
|
+
self.options_collapsible.expand()
|
|
3986
|
+
self.logger.debug("Auto-expanded options panel on tool select")
|
|
3987
|
+
|
|
3988
|
+
self.on_tool_selected()
|
|
3989
|
+
|
|
3990
|
+
# Schedule tab selection after UI updates (if sub-tool with tab index)
|
|
3991
|
+
if sub_tool_tab is not None:
|
|
3992
|
+
self.after(100, lambda: self._select_tool_tab(actual_tool, sub_tool_tab))
|
|
3993
|
+
|
|
3994
|
+
def _select_tool_tab(self, tool_name: str, tab_index: int):
|
|
3995
|
+
"""Select a specific tab within a tool's tabbed interface."""
|
|
3996
|
+
try:
|
|
3997
|
+
# Recursively find notebooks in tool_settings_frame
|
|
3998
|
+
def find_notebook(widget):
|
|
3999
|
+
if isinstance(widget, ttk.Notebook):
|
|
4000
|
+
return widget
|
|
4001
|
+
for child in widget.winfo_children():
|
|
4002
|
+
result = find_notebook(child)
|
|
4003
|
+
if result:
|
|
4004
|
+
return result
|
|
4005
|
+
return None
|
|
4006
|
+
|
|
4007
|
+
notebook = find_notebook(self.tool_settings_frame)
|
|
4008
|
+
if notebook:
|
|
4009
|
+
if 0 <= tab_index < notebook.index('end'):
|
|
4010
|
+
notebook.select(tab_index)
|
|
4011
|
+
self.logger.debug(f"Selected tab {tab_index} for {tool_name}")
|
|
4012
|
+
else:
|
|
4013
|
+
self.logger.warning(f"Tab index {tab_index} out of range for {tool_name}")
|
|
4014
|
+
except Exception as e:
|
|
4015
|
+
self.logger.warning(f"Could not select tab {tab_index} for {tool_name}: {e}")
|
|
4016
|
+
|
|
4017
|
+
def _on_ui_settings_change(self, settings_update):
|
|
4018
|
+
"""Handle UI settings change from ToolSearchPalette."""
|
|
4019
|
+
try:
|
|
4020
|
+
# Merge with existing settings
|
|
4021
|
+
if "ui_layout" in settings_update:
|
|
4022
|
+
current_layout = self.settings.get("ui_layout", {})
|
|
4023
|
+
current_layout.update(settings_update["ui_layout"])
|
|
4024
|
+
self.settings["ui_layout"] = current_layout
|
|
4025
|
+
|
|
4026
|
+
# Save settings
|
|
4027
|
+
if hasattr(self, 'db_settings_manager'):
|
|
4028
|
+
self.db_settings_manager.save_settings(self.settings)
|
|
4029
|
+
except Exception as e:
|
|
4030
|
+
self.logger.warning(f"Error saving UI settings: {e}")
|
|
4031
|
+
|
|
4032
|
+
def _on_options_panel_toggle(self, collapsed: bool):
|
|
4033
|
+
"""Handle options panel toggle (save collapsed state to settings)."""
|
|
4034
|
+
try:
|
|
4035
|
+
current_layout = self.settings.get("ui_layout", {})
|
|
4036
|
+
current_layout["options_panel_collapsed"] = collapsed
|
|
4037
|
+
self.settings["ui_layout"] = current_layout
|
|
4038
|
+
|
|
4039
|
+
# Save settings
|
|
4040
|
+
if hasattr(self, 'db_settings_manager'):
|
|
4041
|
+
self.db_settings_manager.save_settings(self.settings)
|
|
4042
|
+
|
|
4043
|
+
self.logger.debug(f"Options panel collapsed: {collapsed}")
|
|
4044
|
+
except Exception as e:
|
|
4045
|
+
self.logger.warning(f"Error saving options panel state: {e}")
|
|
4046
|
+
|
|
4047
|
+
def toggle_options_panel(self, event=None):
|
|
4048
|
+
"""Toggle the options panel visibility (Ctrl+Shift+H shortcut)."""
|
|
4049
|
+
if hasattr(self, 'options_collapsible') and self.options_collapsible:
|
|
4050
|
+
self.options_collapsible.toggle()
|
|
4051
|
+
return "break"
|
|
4052
|
+
return None
|
|
4053
|
+
|
|
4054
|
+
def focus_tool_search(self, event=None):
|
|
4055
|
+
"""Focus the tool search entry (Ctrl+K shortcut)."""
|
|
4056
|
+
if self.tool_search_palette:
|
|
4057
|
+
self.tool_search_palette.focus_search()
|
|
4058
|
+
return "break"
|
|
4059
|
+
return None
|
|
3744
4060
|
|
|
3745
4061
|
def update_tool_dropdown_menu(self):
|
|
3746
4062
|
"""Updates the tool dropdown menu with all available tools."""
|
|
4063
|
+
if self.tool_dropdown_menu is None:
|
|
4064
|
+
return # Using ToolSearchPalette instead
|
|
4065
|
+
|
|
3747
4066
|
# Clear existing menu items
|
|
3748
4067
|
self.tool_dropdown_menu.delete(0, tk.END)
|
|
3749
4068
|
|
|
@@ -3837,7 +4156,8 @@ class PromeraAIApp(tk.Tk):
|
|
|
3837
4156
|
self.curl_tool_window,
|
|
3838
4157
|
logger=self.logger,
|
|
3839
4158
|
send_to_input_callback=self.send_content_to_input_tab,
|
|
3840
|
-
dialog_manager=self.dialog_manager
|
|
4159
|
+
dialog_manager=self.dialog_manager,
|
|
4160
|
+
db_settings_manager=getattr(self, 'db_settings_manager', None)
|
|
3841
4161
|
)
|
|
3842
4162
|
|
|
3843
4163
|
# Add context menus to cURL tool widgets
|
|
@@ -4465,8 +4785,8 @@ class PromeraAIApp(tk.Tk):
|
|
|
4465
4785
|
self.after(10, self.diff_viewer_widget.update_tab_labels)
|
|
4466
4786
|
self.diff_viewer_widget.run_comparison()
|
|
4467
4787
|
else:
|
|
4468
|
-
# Show placeholder when module not available
|
|
4469
|
-
self.diff_frame.grid(row=
|
|
4788
|
+
# Show placeholder when module not available (row=1 same as central_frame)
|
|
4789
|
+
self.diff_frame.grid(row=1, column=0, sticky="nsew", pady=5)
|
|
4470
4790
|
self.update_tool_settings_ui()
|
|
4471
4791
|
else:
|
|
4472
4792
|
# Sync diff viewer content before switching away
|
|
@@ -4742,33 +5062,37 @@ class PromeraAIApp(tk.Tk):
|
|
|
4742
5062
|
|
|
4743
5063
|
self._current_tool_ui = tool_name
|
|
4744
5064
|
tool_settings = self.settings["tool_settings"].get(tool_name, {})
|
|
4745
|
-
|
|
5065
|
+
|
|
5066
|
+
# Use tool_settings_frame directly as parent
|
|
5067
|
+
# Note: Centering was causing layout issues (e.g. Folder File Reporter cut off)
|
|
5068
|
+
parent_frame = self.tool_settings_frame
|
|
5069
|
+
|
|
4746
5070
|
if tool_name in ["Google AI", "Anthropic AI", "OpenAI", "Cohere AI", "HuggingFace AI", "Groq AI", "OpenRouterAI"]:
|
|
4747
|
-
self.create_ai_provider_widgets(
|
|
5071
|
+
self.create_ai_provider_widgets(parent_frame, tool_name)
|
|
4748
5072
|
elif tool_name == "AI Tools":
|
|
4749
|
-
self.create_ai_tools_widget(
|
|
5073
|
+
self.create_ai_tools_widget(parent_frame)
|
|
4750
5074
|
elif tool_name == "Case Tool":
|
|
4751
5075
|
if CASE_TOOL_MODULE_AVAILABLE and self.case_tool:
|
|
4752
5076
|
self.case_tool_ui = self.case_tool.create_ui(
|
|
4753
|
-
|
|
5077
|
+
parent_frame,
|
|
4754
5078
|
tool_settings,
|
|
4755
5079
|
on_setting_change_callback=self.on_tool_setting_change,
|
|
4756
5080
|
apply_tool_callback=self.apply_tool
|
|
4757
5081
|
)
|
|
4758
5082
|
else:
|
|
4759
|
-
ttk.Label(
|
|
5083
|
+
ttk.Label(parent_frame, text="Case Tool module not available").pack()
|
|
4760
5084
|
elif tool_name == "Translator Tools":
|
|
4761
|
-
self.create_translator_tools_widget(
|
|
5085
|
+
self.create_translator_tools_widget(parent_frame)
|
|
4762
5086
|
elif tool_name == "Base64 Encoder/Decoder":
|
|
4763
|
-
self.create_base64_tools_widget(
|
|
5087
|
+
self.create_base64_tools_widget(parent_frame)
|
|
4764
5088
|
elif tool_name == "JSON/XML Tool":
|
|
4765
|
-
self.create_jsonxml_tool_widget(
|
|
5089
|
+
self.create_jsonxml_tool_widget(parent_frame)
|
|
4766
5090
|
elif tool_name == "Cron Tool":
|
|
4767
|
-
self.create_cron_tool_widget(
|
|
5091
|
+
self.create_cron_tool_widget(parent_frame)
|
|
4768
5092
|
elif tool_name == "Extraction Tools":
|
|
4769
|
-
self.create_extraction_tools_widget(
|
|
5093
|
+
self.create_extraction_tools_widget(parent_frame)
|
|
4770
5094
|
elif tool_name == "Sorter Tools":
|
|
4771
|
-
self.create_sorter_tools_widget(
|
|
5095
|
+
self.create_sorter_tools_widget(parent_frame)
|
|
4772
5096
|
elif tool_name == "Find & Replace Text":
|
|
4773
5097
|
if FIND_REPLACE_MODULE_AVAILABLE and self.find_replace_widget:
|
|
4774
5098
|
# Set up callback for settings changes
|
|
@@ -4781,36 +5105,36 @@ class PromeraAIApp(tk.Tk):
|
|
|
4781
5105
|
self.input_notebook, self.output_notebook
|
|
4782
5106
|
)
|
|
4783
5107
|
# Create the widgets
|
|
4784
|
-
self.find_replace_widget.create_widgets(
|
|
5108
|
+
self.find_replace_widget.create_widgets(parent_frame, tool_settings)
|
|
4785
5109
|
else:
|
|
4786
5110
|
# Find & Replace module not available
|
|
4787
|
-
ttk.Label(
|
|
5111
|
+
ttk.Label(parent_frame, text="Find & Replace module not available").pack()
|
|
4788
5112
|
elif tool_name == "Generator Tools":
|
|
4789
|
-
self.create_generator_tools_widget(
|
|
5113
|
+
self.create_generator_tools_widget(parent_frame)
|
|
4790
5114
|
elif tool_name == "URL Parser":
|
|
4791
5115
|
if URL_PARSER_MODULE_AVAILABLE and self.url_parser:
|
|
4792
5116
|
tool_settings = self.settings["tool_settings"].get("URL Parser", {
|
|
4793
5117
|
"ascii_decode": True
|
|
4794
5118
|
})
|
|
4795
5119
|
self.url_parser_ui = self.url_parser.create_ui(
|
|
4796
|
-
|
|
5120
|
+
parent_frame,
|
|
4797
5121
|
tool_settings,
|
|
4798
5122
|
on_setting_change_callback=self.on_tool_setting_change,
|
|
4799
5123
|
apply_tool_callback=self.apply_tool
|
|
4800
5124
|
)
|
|
4801
5125
|
else:
|
|
4802
|
-
ttk.Label(
|
|
5126
|
+
ttk.Label(parent_frame, text="URL Parser module not available").pack()
|
|
4803
5127
|
elif tool_name == "Diff Viewer":
|
|
4804
5128
|
if DIFF_VIEWER_MODULE_AVAILABLE:
|
|
4805
5129
|
# Use the new modular settings widget
|
|
4806
5130
|
self.diff_settings_widget = DiffViewerSettingsWidget(
|
|
4807
|
-
|
|
5131
|
+
parent_frame,
|
|
4808
5132
|
self.diff_viewer_widget,
|
|
4809
5133
|
self.on_tool_setting_change
|
|
4810
5134
|
)
|
|
4811
5135
|
else:
|
|
4812
5136
|
# Module not available - show message
|
|
4813
|
-
ttk.Label(
|
|
5137
|
+
ttk.Label(parent_frame, text="Diff Viewer module not available").pack(side=tk.LEFT, padx=10)
|
|
4814
5138
|
elif tool_name == "Email Extraction Tool":
|
|
4815
5139
|
if EMAIL_EXTRACTION_MODULE_AVAILABLE and self.email_extraction_tool:
|
|
4816
5140
|
tool_settings = self.settings["tool_settings"].get("Email Extraction Tool", {
|
|
@@ -4820,13 +5144,13 @@ class PromeraAIApp(tk.Tk):
|
|
|
4820
5144
|
"only_domain": False
|
|
4821
5145
|
})
|
|
4822
5146
|
self.email_extraction_ui = self.email_extraction_tool.create_ui(
|
|
4823
|
-
|
|
5147
|
+
parent_frame,
|
|
4824
5148
|
tool_settings,
|
|
4825
5149
|
on_setting_change_callback=self.on_tool_setting_change,
|
|
4826
5150
|
apply_tool_callback=self.apply_tool
|
|
4827
5151
|
)
|
|
4828
5152
|
else:
|
|
4829
|
-
ttk.Label(
|
|
5153
|
+
ttk.Label(parent_frame, text="Email Extraction Tool module not available").pack()
|
|
4830
5154
|
elif tool_name == "Email Header Analyzer":
|
|
4831
5155
|
if EMAIL_HEADER_ANALYZER_MODULE_AVAILABLE and self.email_header_analyzer:
|
|
4832
5156
|
tool_settings = self.settings["tool_settings"].get("Email Header Analyzer", {
|
|
@@ -4836,13 +5160,13 @@ class PromeraAIApp(tk.Tk):
|
|
|
4836
5160
|
"show_spam_score": True
|
|
4837
5161
|
})
|
|
4838
5162
|
self.email_header_analyzer_ui = self.email_header_analyzer.create_ui(
|
|
4839
|
-
|
|
5163
|
+
parent_frame,
|
|
4840
5164
|
tool_settings,
|
|
4841
5165
|
on_setting_change_callback=self.on_tool_setting_change,
|
|
4842
5166
|
apply_tool_callback=self.apply_tool
|
|
4843
5167
|
)
|
|
4844
5168
|
else:
|
|
4845
|
-
ttk.Label(
|
|
5169
|
+
ttk.Label(parent_frame, text="Email Header Analyzer module not available").pack()
|
|
4846
5170
|
elif tool_name == "Folder File Reporter":
|
|
4847
5171
|
if FOLDER_FILE_REPORTER_MODULE_AVAILABLE and self.folder_file_reporter:
|
|
4848
5172
|
tool_settings = self.settings["tool_settings"].get("Folder File Reporter", {
|
|
@@ -4850,29 +5174,29 @@ class PromeraAIApp(tk.Tk):
|
|
|
4850
5174
|
"last_output_folder": ""
|
|
4851
5175
|
})
|
|
4852
5176
|
self.folder_file_reporter.load_settings(tool_settings)
|
|
4853
|
-
self.folder_file_reporter_ui = self.folder_file_reporter.create_ui(
|
|
5177
|
+
self.folder_file_reporter_ui = self.folder_file_reporter.create_ui(parent_frame)
|
|
4854
5178
|
else:
|
|
4855
|
-
ttk.Label(
|
|
5179
|
+
ttk.Label(parent_frame, text="Folder File Reporter module not available").pack()
|
|
4856
5180
|
elif tool_name == "Line Tools":
|
|
4857
|
-
self.create_line_tools_widget(
|
|
5181
|
+
self.create_line_tools_widget(parent_frame)
|
|
4858
5182
|
elif tool_name == "Whitespace Tools":
|
|
4859
|
-
self.create_whitespace_tools_widget(
|
|
5183
|
+
self.create_whitespace_tools_widget(parent_frame)
|
|
4860
5184
|
elif tool_name == "Text Statistics":
|
|
4861
|
-
self.create_text_statistics_widget(
|
|
5185
|
+
self.create_text_statistics_widget(parent_frame)
|
|
4862
5186
|
elif tool_name == "Markdown Tools":
|
|
4863
|
-
self.create_markdown_tools_widget(
|
|
5187
|
+
self.create_markdown_tools_widget(parent_frame)
|
|
4864
5188
|
elif tool_name == "String Escape Tool":
|
|
4865
|
-
self.create_string_escape_tool_widget(
|
|
5189
|
+
self.create_string_escape_tool_widget(parent_frame)
|
|
4866
5190
|
elif tool_name == "Number Base Converter":
|
|
4867
|
-
self.create_number_base_converter_widget(
|
|
5191
|
+
self.create_number_base_converter_widget(parent_frame)
|
|
4868
5192
|
elif tool_name == "Text Wrapper":
|
|
4869
|
-
self.create_text_wrapper_widget(
|
|
5193
|
+
self.create_text_wrapper_widget(parent_frame)
|
|
4870
5194
|
elif tool_name == "Slug Generator":
|
|
4871
|
-
self.create_slug_generator_widget(
|
|
5195
|
+
self.create_slug_generator_widget(parent_frame)
|
|
4872
5196
|
elif tool_name == "Column Tools":
|
|
4873
|
-
self.create_column_tools_widget(
|
|
5197
|
+
self.create_column_tools_widget(parent_frame)
|
|
4874
5198
|
elif tool_name == "Timestamp Converter":
|
|
4875
|
-
self.create_timestamp_converter_widget(
|
|
5199
|
+
self.create_timestamp_converter_widget(parent_frame)
|
|
4876
5200
|
|
|
4877
5201
|
|
|
4878
5202
|
|