pomera-ai-commander 1.2.5 → 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/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 +1 -1
- package/pomera.py +403 -69
- 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)
|
|
@@ -2193,11 +2219,17 @@ class PromeraAIApp(tk.Tk):
|
|
|
2193
2219
|
|
|
2194
2220
|
main_frame = ttk.Frame(self, padding="10")
|
|
2195
2221
|
main_frame.pack(fill=tk.BOTH, expand=True)
|
|
2196
|
-
main_frame.rowconfigure(
|
|
2222
|
+
main_frame.rowconfigure(1, weight=1) # Central frame gets the weight
|
|
2197
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)
|
|
2198
2229
|
|
|
2230
|
+
# Row 1: Central frame with Input/Output panels
|
|
2199
2231
|
self.central_frame = ttk.Frame(main_frame, padding="10")
|
|
2200
|
-
self.central_frame.grid(row=
|
|
2232
|
+
self.central_frame.grid(row=1, column=0, sticky="nsew", pady=5)
|
|
2201
2233
|
self.central_frame.grid_columnconfigure(0, weight=1)
|
|
2202
2234
|
self.central_frame.grid_columnconfigure(1, weight=1)
|
|
2203
2235
|
self.central_frame.grid_rowconfigure(1, weight=1)
|
|
@@ -2210,12 +2242,13 @@ class PromeraAIApp(tk.Tk):
|
|
|
2210
2242
|
|
|
2211
2243
|
self.create_diff_viewer(main_frame)
|
|
2212
2244
|
|
|
2213
|
-
#
|
|
2245
|
+
# Row 2: Separator
|
|
2214
2246
|
separator = ttk.Separator(main_frame, orient='horizontal')
|
|
2215
|
-
separator.grid(row=
|
|
2247
|
+
separator.grid(row=2, column=0, sticky="ew", pady=10)
|
|
2216
2248
|
|
|
2249
|
+
# Row 3: Tool options frame (collapsible)
|
|
2217
2250
|
tool_frame = ttk.Frame(main_frame, padding="10")
|
|
2218
|
-
tool_frame.grid(row=
|
|
2251
|
+
tool_frame.grid(row=3, column=0, sticky="ew", pady=5)
|
|
2219
2252
|
self.create_tool_widgets(tool_frame)
|
|
2220
2253
|
|
|
2221
2254
|
# Setup context menus for all text widgets
|
|
@@ -3688,35 +3721,78 @@ class PromeraAIApp(tk.Tk):
|
|
|
3688
3721
|
print(f"Filter text: {repr(self.output_filter_var.get())}")
|
|
3689
3722
|
except Exception as e:
|
|
3690
3723
|
print(f"Debug error: {e}")
|
|
3691
|
-
|
|
3692
|
-
def
|
|
3693
|
-
"""
|
|
3724
|
+
|
|
3725
|
+
def _create_top_search_bar(self, parent):
|
|
3726
|
+
"""Create the top search bar (below menu, above Input/Output panels)."""
|
|
3694
3727
|
self.tool_var = tk.StringVar(value=self.settings.get("selected_tool", "Case Tool"))
|
|
3695
3728
|
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
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
|
+
|
|
3717
3749
|
|
|
3718
|
-
|
|
3719
|
-
|
|
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
|
|
3720
3796
|
|
|
3721
3797
|
# Initialize Widget Cache for tool settings UI
|
|
3722
3798
|
if WIDGET_CACHE_AVAILABLE:
|
|
@@ -3731,9 +3807,262 @@ class PromeraAIApp(tk.Tk):
|
|
|
3731
3807
|
self.widget_cache = None
|
|
3732
3808
|
|
|
3733
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
|
|
3734
4060
|
|
|
3735
4061
|
def update_tool_dropdown_menu(self):
|
|
3736
4062
|
"""Updates the tool dropdown menu with all available tools."""
|
|
4063
|
+
if self.tool_dropdown_menu is None:
|
|
4064
|
+
return # Using ToolSearchPalette instead
|
|
4065
|
+
|
|
3737
4066
|
# Clear existing menu items
|
|
3738
4067
|
self.tool_dropdown_menu.delete(0, tk.END)
|
|
3739
4068
|
|
|
@@ -3827,7 +4156,8 @@ class PromeraAIApp(tk.Tk):
|
|
|
3827
4156
|
self.curl_tool_window,
|
|
3828
4157
|
logger=self.logger,
|
|
3829
4158
|
send_to_input_callback=self.send_content_to_input_tab,
|
|
3830
|
-
dialog_manager=self.dialog_manager
|
|
4159
|
+
dialog_manager=self.dialog_manager,
|
|
4160
|
+
db_settings_manager=getattr(self, 'db_settings_manager', None)
|
|
3831
4161
|
)
|
|
3832
4162
|
|
|
3833
4163
|
# Add context menus to cURL tool widgets
|
|
@@ -4455,8 +4785,8 @@ class PromeraAIApp(tk.Tk):
|
|
|
4455
4785
|
self.after(10, self.diff_viewer_widget.update_tab_labels)
|
|
4456
4786
|
self.diff_viewer_widget.run_comparison()
|
|
4457
4787
|
else:
|
|
4458
|
-
# Show placeholder when module not available
|
|
4459
|
-
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)
|
|
4460
4790
|
self.update_tool_settings_ui()
|
|
4461
4791
|
else:
|
|
4462
4792
|
# Sync diff viewer content before switching away
|
|
@@ -4732,33 +5062,37 @@ class PromeraAIApp(tk.Tk):
|
|
|
4732
5062
|
|
|
4733
5063
|
self._current_tool_ui = tool_name
|
|
4734
5064
|
tool_settings = self.settings["tool_settings"].get(tool_name, {})
|
|
4735
|
-
|
|
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
|
+
|
|
4736
5070
|
if tool_name in ["Google AI", "Anthropic AI", "OpenAI", "Cohere AI", "HuggingFace AI", "Groq AI", "OpenRouterAI"]:
|
|
4737
|
-
self.create_ai_provider_widgets(
|
|
5071
|
+
self.create_ai_provider_widgets(parent_frame, tool_name)
|
|
4738
5072
|
elif tool_name == "AI Tools":
|
|
4739
|
-
self.create_ai_tools_widget(
|
|
5073
|
+
self.create_ai_tools_widget(parent_frame)
|
|
4740
5074
|
elif tool_name == "Case Tool":
|
|
4741
5075
|
if CASE_TOOL_MODULE_AVAILABLE and self.case_tool:
|
|
4742
5076
|
self.case_tool_ui = self.case_tool.create_ui(
|
|
4743
|
-
|
|
5077
|
+
parent_frame,
|
|
4744
5078
|
tool_settings,
|
|
4745
5079
|
on_setting_change_callback=self.on_tool_setting_change,
|
|
4746
5080
|
apply_tool_callback=self.apply_tool
|
|
4747
5081
|
)
|
|
4748
5082
|
else:
|
|
4749
|
-
ttk.Label(
|
|
5083
|
+
ttk.Label(parent_frame, text="Case Tool module not available").pack()
|
|
4750
5084
|
elif tool_name == "Translator Tools":
|
|
4751
|
-
self.create_translator_tools_widget(
|
|
5085
|
+
self.create_translator_tools_widget(parent_frame)
|
|
4752
5086
|
elif tool_name == "Base64 Encoder/Decoder":
|
|
4753
|
-
self.create_base64_tools_widget(
|
|
5087
|
+
self.create_base64_tools_widget(parent_frame)
|
|
4754
5088
|
elif tool_name == "JSON/XML Tool":
|
|
4755
|
-
self.create_jsonxml_tool_widget(
|
|
5089
|
+
self.create_jsonxml_tool_widget(parent_frame)
|
|
4756
5090
|
elif tool_name == "Cron Tool":
|
|
4757
|
-
self.create_cron_tool_widget(
|
|
5091
|
+
self.create_cron_tool_widget(parent_frame)
|
|
4758
5092
|
elif tool_name == "Extraction Tools":
|
|
4759
|
-
self.create_extraction_tools_widget(
|
|
5093
|
+
self.create_extraction_tools_widget(parent_frame)
|
|
4760
5094
|
elif tool_name == "Sorter Tools":
|
|
4761
|
-
self.create_sorter_tools_widget(
|
|
5095
|
+
self.create_sorter_tools_widget(parent_frame)
|
|
4762
5096
|
elif tool_name == "Find & Replace Text":
|
|
4763
5097
|
if FIND_REPLACE_MODULE_AVAILABLE and self.find_replace_widget:
|
|
4764
5098
|
# Set up callback for settings changes
|
|
@@ -4771,36 +5105,36 @@ class PromeraAIApp(tk.Tk):
|
|
|
4771
5105
|
self.input_notebook, self.output_notebook
|
|
4772
5106
|
)
|
|
4773
5107
|
# Create the widgets
|
|
4774
|
-
self.find_replace_widget.create_widgets(
|
|
5108
|
+
self.find_replace_widget.create_widgets(parent_frame, tool_settings)
|
|
4775
5109
|
else:
|
|
4776
5110
|
# Find & Replace module not available
|
|
4777
|
-
ttk.Label(
|
|
5111
|
+
ttk.Label(parent_frame, text="Find & Replace module not available").pack()
|
|
4778
5112
|
elif tool_name == "Generator Tools":
|
|
4779
|
-
self.create_generator_tools_widget(
|
|
5113
|
+
self.create_generator_tools_widget(parent_frame)
|
|
4780
5114
|
elif tool_name == "URL Parser":
|
|
4781
5115
|
if URL_PARSER_MODULE_AVAILABLE and self.url_parser:
|
|
4782
5116
|
tool_settings = self.settings["tool_settings"].get("URL Parser", {
|
|
4783
5117
|
"ascii_decode": True
|
|
4784
5118
|
})
|
|
4785
5119
|
self.url_parser_ui = self.url_parser.create_ui(
|
|
4786
|
-
|
|
5120
|
+
parent_frame,
|
|
4787
5121
|
tool_settings,
|
|
4788
5122
|
on_setting_change_callback=self.on_tool_setting_change,
|
|
4789
5123
|
apply_tool_callback=self.apply_tool
|
|
4790
5124
|
)
|
|
4791
5125
|
else:
|
|
4792
|
-
ttk.Label(
|
|
5126
|
+
ttk.Label(parent_frame, text="URL Parser module not available").pack()
|
|
4793
5127
|
elif tool_name == "Diff Viewer":
|
|
4794
5128
|
if DIFF_VIEWER_MODULE_AVAILABLE:
|
|
4795
5129
|
# Use the new modular settings widget
|
|
4796
5130
|
self.diff_settings_widget = DiffViewerSettingsWidget(
|
|
4797
|
-
|
|
5131
|
+
parent_frame,
|
|
4798
5132
|
self.diff_viewer_widget,
|
|
4799
5133
|
self.on_tool_setting_change
|
|
4800
5134
|
)
|
|
4801
5135
|
else:
|
|
4802
5136
|
# Module not available - show message
|
|
4803
|
-
ttk.Label(
|
|
5137
|
+
ttk.Label(parent_frame, text="Diff Viewer module not available").pack(side=tk.LEFT, padx=10)
|
|
4804
5138
|
elif tool_name == "Email Extraction Tool":
|
|
4805
5139
|
if EMAIL_EXTRACTION_MODULE_AVAILABLE and self.email_extraction_tool:
|
|
4806
5140
|
tool_settings = self.settings["tool_settings"].get("Email Extraction Tool", {
|
|
@@ -4810,13 +5144,13 @@ class PromeraAIApp(tk.Tk):
|
|
|
4810
5144
|
"only_domain": False
|
|
4811
5145
|
})
|
|
4812
5146
|
self.email_extraction_ui = self.email_extraction_tool.create_ui(
|
|
4813
|
-
|
|
5147
|
+
parent_frame,
|
|
4814
5148
|
tool_settings,
|
|
4815
5149
|
on_setting_change_callback=self.on_tool_setting_change,
|
|
4816
5150
|
apply_tool_callback=self.apply_tool
|
|
4817
5151
|
)
|
|
4818
5152
|
else:
|
|
4819
|
-
ttk.Label(
|
|
5153
|
+
ttk.Label(parent_frame, text="Email Extraction Tool module not available").pack()
|
|
4820
5154
|
elif tool_name == "Email Header Analyzer":
|
|
4821
5155
|
if EMAIL_HEADER_ANALYZER_MODULE_AVAILABLE and self.email_header_analyzer:
|
|
4822
5156
|
tool_settings = self.settings["tool_settings"].get("Email Header Analyzer", {
|
|
@@ -4826,13 +5160,13 @@ class PromeraAIApp(tk.Tk):
|
|
|
4826
5160
|
"show_spam_score": True
|
|
4827
5161
|
})
|
|
4828
5162
|
self.email_header_analyzer_ui = self.email_header_analyzer.create_ui(
|
|
4829
|
-
|
|
5163
|
+
parent_frame,
|
|
4830
5164
|
tool_settings,
|
|
4831
5165
|
on_setting_change_callback=self.on_tool_setting_change,
|
|
4832
5166
|
apply_tool_callback=self.apply_tool
|
|
4833
5167
|
)
|
|
4834
5168
|
else:
|
|
4835
|
-
ttk.Label(
|
|
5169
|
+
ttk.Label(parent_frame, text="Email Header Analyzer module not available").pack()
|
|
4836
5170
|
elif tool_name == "Folder File Reporter":
|
|
4837
5171
|
if FOLDER_FILE_REPORTER_MODULE_AVAILABLE and self.folder_file_reporter:
|
|
4838
5172
|
tool_settings = self.settings["tool_settings"].get("Folder File Reporter", {
|
|
@@ -4840,29 +5174,29 @@ class PromeraAIApp(tk.Tk):
|
|
|
4840
5174
|
"last_output_folder": ""
|
|
4841
5175
|
})
|
|
4842
5176
|
self.folder_file_reporter.load_settings(tool_settings)
|
|
4843
|
-
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)
|
|
4844
5178
|
else:
|
|
4845
|
-
ttk.Label(
|
|
5179
|
+
ttk.Label(parent_frame, text="Folder File Reporter module not available").pack()
|
|
4846
5180
|
elif tool_name == "Line Tools":
|
|
4847
|
-
self.create_line_tools_widget(
|
|
5181
|
+
self.create_line_tools_widget(parent_frame)
|
|
4848
5182
|
elif tool_name == "Whitespace Tools":
|
|
4849
|
-
self.create_whitespace_tools_widget(
|
|
5183
|
+
self.create_whitespace_tools_widget(parent_frame)
|
|
4850
5184
|
elif tool_name == "Text Statistics":
|
|
4851
|
-
self.create_text_statistics_widget(
|
|
5185
|
+
self.create_text_statistics_widget(parent_frame)
|
|
4852
5186
|
elif tool_name == "Markdown Tools":
|
|
4853
|
-
self.create_markdown_tools_widget(
|
|
5187
|
+
self.create_markdown_tools_widget(parent_frame)
|
|
4854
5188
|
elif tool_name == "String Escape Tool":
|
|
4855
|
-
self.create_string_escape_tool_widget(
|
|
5189
|
+
self.create_string_escape_tool_widget(parent_frame)
|
|
4856
5190
|
elif tool_name == "Number Base Converter":
|
|
4857
|
-
self.create_number_base_converter_widget(
|
|
5191
|
+
self.create_number_base_converter_widget(parent_frame)
|
|
4858
5192
|
elif tool_name == "Text Wrapper":
|
|
4859
|
-
self.create_text_wrapper_widget(
|
|
5193
|
+
self.create_text_wrapper_widget(parent_frame)
|
|
4860
5194
|
elif tool_name == "Slug Generator":
|
|
4861
|
-
self.create_slug_generator_widget(
|
|
5195
|
+
self.create_slug_generator_widget(parent_frame)
|
|
4862
5196
|
elif tool_name == "Column Tools":
|
|
4863
|
-
self.create_column_tools_widget(
|
|
5197
|
+
self.create_column_tools_widget(parent_frame)
|
|
4864
5198
|
elif tool_name == "Timestamp Converter":
|
|
4865
|
-
self.create_timestamp_converter_widget(
|
|
5199
|
+
self.create_timestamp_converter_widget(parent_frame)
|
|
4866
5200
|
|
|
4867
5201
|
|
|
4868
5202
|
|