pomera-ai-commander 1.2.7 → 1.2.9
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/bin/pomera-create-shortcut.js +51 -0
- package/bin/pomera.js +68 -0
- package/core/database_schema.py +24 -1
- package/core/database_schema_manager.py +4 -2
- package/core/database_settings_manager.py +25 -2
- package/core/dialog_manager.py +4 -4
- package/core/efficient_line_numbers.py +5 -4
- package/core/load_presets_dialog.py +460 -0
- package/core/mcp/tool_registry.py +327 -0
- package/core/settings_defaults_registry.py +159 -15
- package/core/tool_search_widget.py +85 -5
- package/create_shortcut.py +12 -4
- package/mcp.json +1 -1
- package/package.json +4 -2
- package/pomera.py +760 -25
- package/tools/base64_tools.py +4 -4
- package/tools/case_tool.py +4 -4
- package/tools/curl_settings.py +12 -1
- package/tools/curl_tool.py +176 -11
- package/tools/notes_widget.py +8 -1
- package/tools/tool_loader.py +20 -9
- package/tools/url_content_reader.py +402 -0
- package/tools/web_search.py +522 -0
package/tools/base64_tools.py
CHANGED
|
@@ -53,9 +53,10 @@ class Base64Tools:
|
|
|
53
53
|
class Base64ToolsWidget:
|
|
54
54
|
"""Widget for the Base64 Tools interface."""
|
|
55
55
|
|
|
56
|
-
def __init__(self, base64_tools):
|
|
56
|
+
def __init__(self, base64_tools=None):
|
|
57
57
|
"""Initialize the Base64ToolsWidget."""
|
|
58
|
-
|
|
58
|
+
# Create Base64Tools instance if not provided
|
|
59
|
+
self.base64_tools = base64_tools if base64_tools else Base64Tools()
|
|
59
60
|
self.main_app = None
|
|
60
61
|
|
|
61
62
|
# Variables for Base64 mode
|
|
@@ -102,8 +103,7 @@ class Base64ToolsWidget:
|
|
|
102
103
|
def on_mode_change(self):
|
|
103
104
|
"""Handle mode change and save settings."""
|
|
104
105
|
self.save_settings()
|
|
105
|
-
#
|
|
106
|
-
self.process_base64()
|
|
106
|
+
# Don't auto-process - wait for Process button click
|
|
107
107
|
|
|
108
108
|
def process_base64(self):
|
|
109
109
|
"""Process the input text with Base64 encoding/decoding."""
|
package/tools/case_tool.py
CHANGED
|
@@ -188,7 +188,8 @@ class CaseTool:
|
|
|
188
188
|
"""Get default settings for the Case Tool.
|
|
189
189
|
|
|
190
190
|
Uses the centralized Settings Defaults Registry if available,
|
|
191
|
-
otherwise falls back to
|
|
191
|
+
otherwise falls back to minimal defaults. Full exclusions list
|
|
192
|
+
is maintained only in the registry.
|
|
192
193
|
"""
|
|
193
194
|
try:
|
|
194
195
|
from core.settings_defaults_registry import get_registry
|
|
@@ -199,11 +200,10 @@ class CaseTool:
|
|
|
199
200
|
except Exception:
|
|
200
201
|
pass
|
|
201
202
|
|
|
202
|
-
#
|
|
203
|
-
# Exclusions: a, an, the, and, but, or, for, nor, on, at, to, from, by, with, in, of
|
|
203
|
+
# Minimal fallback - registry has the full exclusions list
|
|
204
204
|
return {
|
|
205
205
|
"mode": "Sentence",
|
|
206
|
-
"exclusions": "
|
|
206
|
+
"exclusions": ""
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
|
package/tools/curl_settings.py
CHANGED
|
@@ -91,7 +91,18 @@ class CurlSettingsManager:
|
|
|
91
91
|
# Version and metadata
|
|
92
92
|
"settings_version": "1.0",
|
|
93
93
|
"last_updated": None,
|
|
94
|
-
"created_date": None
|
|
94
|
+
"created_date": None,
|
|
95
|
+
|
|
96
|
+
# UI State Persistence (NEW - persist between restarts)
|
|
97
|
+
"last_url": "",
|
|
98
|
+
"last_method": "GET",
|
|
99
|
+
"last_headers": "",
|
|
100
|
+
"last_body": "",
|
|
101
|
+
"last_body_type": "None",
|
|
102
|
+
"last_auth_type": "None",
|
|
103
|
+
"last_auth_data": {}, # Encrypted auth tokens stored here
|
|
104
|
+
"last_complex_options": "",
|
|
105
|
+
"persist_ui_state": True # User preference to persist UI state
|
|
95
106
|
}
|
|
96
107
|
|
|
97
108
|
# Current settings (loaded from file or defaults)
|
package/tools/curl_tool.py
CHANGED
|
@@ -284,6 +284,9 @@ class CurlToolWidget:
|
|
|
284
284
|
# Create the UI
|
|
285
285
|
self.create_widgets()
|
|
286
286
|
|
|
287
|
+
# Restore saved UI state (URL, method, headers, body, auth)
|
|
288
|
+
self._restore_ui_state()
|
|
289
|
+
|
|
287
290
|
# Save settings when the window is closed
|
|
288
291
|
if hasattr(self.parent, 'protocol'):
|
|
289
292
|
self.parent.protocol("WM_DELETE_WINDOW", self._on_closing)
|
|
@@ -4232,17 +4235,8 @@ curl -X POST https://api.example.com/users \\
|
|
|
4232
4235
|
if self.logger:
|
|
4233
4236
|
self.logger.error(f"Error saving settings: {e}")
|
|
4234
4237
|
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
try:
|
|
4238
|
-
self._save_current_settings()
|
|
4239
|
-
except Exception as e:
|
|
4240
|
-
if self.logger:
|
|
4241
|
-
self.logger.error(f"Error saving settings on close: {e}")
|
|
4242
|
-
|
|
4243
|
-
# Continue with normal closing
|
|
4244
|
-
if hasattr(self.parent, 'destroy'):
|
|
4245
|
-
self.parent.destroy()
|
|
4238
|
+
# NOTE: _on_closing method has been moved to end of class (line ~5490)
|
|
4239
|
+
# to consolidate with UI state persistence logic
|
|
4246
4240
|
|
|
4247
4241
|
def _export_curl(self):
|
|
4248
4242
|
"""Export current request as cURL command."""
|
|
@@ -5483,6 +5477,177 @@ Timestamp: {timestamp}{additional_details}"""
|
|
|
5483
5477
|
if item:
|
|
5484
5478
|
self.history_tree.selection_set(item)
|
|
5485
5479
|
self.history_context_menu.post(event.x_root, event.y_root)
|
|
5480
|
+
|
|
5481
|
+
def _on_closing(self):
|
|
5482
|
+
"""Handle widget/window closing - save all settings and UI state."""
|
|
5483
|
+
# Save original settings (from the original method at line 4238)
|
|
5484
|
+
try:
|
|
5485
|
+
self._save_current_settings()
|
|
5486
|
+
except Exception as e:
|
|
5487
|
+
if self.logger:
|
|
5488
|
+
self.logger.error(f"Error saving settings on close: {e}")
|
|
5489
|
+
|
|
5490
|
+
# Also save UI state for full persistence (new functionality)
|
|
5491
|
+
try:
|
|
5492
|
+
self._save_ui_state()
|
|
5493
|
+
self.logger.info("cURL Tool UI state saved on close")
|
|
5494
|
+
except Exception as e:
|
|
5495
|
+
self.logger.error(f"Error saving UI state on close: {e}")
|
|
5496
|
+
|
|
5497
|
+
# If parent has destroy, call it
|
|
5498
|
+
if hasattr(self.parent, 'destroy'):
|
|
5499
|
+
self.parent.destroy()
|
|
5500
|
+
|
|
5501
|
+
def _save_ui_state(self):
|
|
5502
|
+
"""Save current UI state to settings for persistence."""
|
|
5503
|
+
if not self.settings_manager:
|
|
5504
|
+
return
|
|
5505
|
+
|
|
5506
|
+
# Check if UI state persistence is enabled
|
|
5507
|
+
if not self.settings.get("persist_ui_state", True):
|
|
5508
|
+
return
|
|
5509
|
+
|
|
5510
|
+
try:
|
|
5511
|
+
# Get current URL from text widget
|
|
5512
|
+
url = ""
|
|
5513
|
+
if self.url_text and self.url_text.winfo_exists():
|
|
5514
|
+
url = self.url_text.get("1.0", tk.END).strip()
|
|
5515
|
+
|
|
5516
|
+
# Get current method
|
|
5517
|
+
method = self.method_var.get() if self.method_var else "GET"
|
|
5518
|
+
|
|
5519
|
+
# Get headers from text widget
|
|
5520
|
+
headers = ""
|
|
5521
|
+
if hasattr(self, 'headers_text') and self.headers_text and self.headers_text.winfo_exists():
|
|
5522
|
+
headers = self.headers_text.get("1.0", tk.END).strip()
|
|
5523
|
+
|
|
5524
|
+
# Get body from text widget
|
|
5525
|
+
body = ""
|
|
5526
|
+
if hasattr(self, 'body_text') and self.body_text and self.body_text.winfo_exists():
|
|
5527
|
+
body = self.body_text.get("1.0", tk.END).strip()
|
|
5528
|
+
|
|
5529
|
+
# Get body type
|
|
5530
|
+
body_type = self.body_type_var.get() if self.body_type_var else "None"
|
|
5531
|
+
|
|
5532
|
+
# Get auth type
|
|
5533
|
+
auth_type = self.auth_type_var.get() if self.auth_type_var else "None"
|
|
5534
|
+
|
|
5535
|
+
# Get auth data (encrypted)
|
|
5536
|
+
auth_data = {}
|
|
5537
|
+
if auth_type == "Bearer":
|
|
5538
|
+
token = self.bearer_token_var.get() if hasattr(self, 'bearer_token_var') else ""
|
|
5539
|
+
if token:
|
|
5540
|
+
auth_data["bearer_token"] = encrypt_auth_value(token)
|
|
5541
|
+
elif auth_type == "Basic":
|
|
5542
|
+
username = self.basic_username_var.get() if hasattr(self, 'basic_username_var') else ""
|
|
5543
|
+
password = self.basic_password_var.get() if hasattr(self, 'basic_password_var') else ""
|
|
5544
|
+
if username or password:
|
|
5545
|
+
auth_data["basic_username"] = username
|
|
5546
|
+
auth_data["basic_password"] = encrypt_auth_value(password)
|
|
5547
|
+
elif auth_type == "API Key":
|
|
5548
|
+
key_name = self.api_key_name_var.get() if hasattr(self, 'api_key_name_var') else ""
|
|
5549
|
+
key_value = self.api_key_value_var.get() if hasattr(self, 'api_key_value_var') else ""
|
|
5550
|
+
key_location = self.api_key_location_var.get() if hasattr(self, 'api_key_location_var') else "header"
|
|
5551
|
+
if key_value:
|
|
5552
|
+
auth_data["api_key_name"] = key_name
|
|
5553
|
+
auth_data["api_key_value"] = encrypt_auth_value(key_value)
|
|
5554
|
+
auth_data["api_key_location"] = key_location
|
|
5555
|
+
|
|
5556
|
+
# Get complex options
|
|
5557
|
+
complex_options = self.complex_options_var.get() if hasattr(self, 'complex_options_var') else ""
|
|
5558
|
+
|
|
5559
|
+
# Update settings
|
|
5560
|
+
self.settings_manager.set_setting("last_url", url)
|
|
5561
|
+
self.settings_manager.set_setting("last_method", method)
|
|
5562
|
+
self.settings_manager.set_setting("last_headers", headers)
|
|
5563
|
+
self.settings_manager.set_setting("last_body", body)
|
|
5564
|
+
self.settings_manager.set_setting("last_body_type", body_type)
|
|
5565
|
+
self.settings_manager.set_setting("last_auth_type", auth_type)
|
|
5566
|
+
self.settings_manager.set_setting("last_auth_data", auth_data)
|
|
5567
|
+
self.settings_manager.set_setting("last_complex_options", complex_options)
|
|
5568
|
+
|
|
5569
|
+
# Save to persistent storage
|
|
5570
|
+
self.settings_manager.save_settings()
|
|
5571
|
+
self.logger.debug("UI state saved successfully")
|
|
5572
|
+
|
|
5573
|
+
except Exception as e:
|
|
5574
|
+
self.logger.error(f"Error saving UI state: {e}")
|
|
5575
|
+
|
|
5576
|
+
def _restore_ui_state(self):
|
|
5577
|
+
"""Restore UI state from saved settings."""
|
|
5578
|
+
if not self.settings_manager:
|
|
5579
|
+
return
|
|
5580
|
+
|
|
5581
|
+
# Check if UI state persistence is enabled
|
|
5582
|
+
if not self.settings.get("persist_ui_state", True):
|
|
5583
|
+
return
|
|
5584
|
+
|
|
5585
|
+
try:
|
|
5586
|
+
# Restore URL
|
|
5587
|
+
last_url = self.settings.get("last_url", "")
|
|
5588
|
+
if last_url and self.url_text and self.url_text.winfo_exists():
|
|
5589
|
+
self.url_text.delete("1.0", tk.END)
|
|
5590
|
+
self.url_text.insert("1.0", last_url)
|
|
5591
|
+
|
|
5592
|
+
# Restore method
|
|
5593
|
+
last_method = self.settings.get("last_method", "GET")
|
|
5594
|
+
if self.method_var:
|
|
5595
|
+
self.method_var.set(last_method)
|
|
5596
|
+
|
|
5597
|
+
# Restore headers
|
|
5598
|
+
last_headers = self.settings.get("last_headers", "")
|
|
5599
|
+
if last_headers and hasattr(self, 'headers_text') and self.headers_text:
|
|
5600
|
+
if self.headers_text.winfo_exists():
|
|
5601
|
+
self.headers_text.delete("1.0", tk.END)
|
|
5602
|
+
self.headers_text.insert("1.0", last_headers)
|
|
5603
|
+
|
|
5604
|
+
# Restore body
|
|
5605
|
+
last_body = self.settings.get("last_body", "")
|
|
5606
|
+
if last_body and hasattr(self, 'body_text') and self.body_text:
|
|
5607
|
+
if self.body_text.winfo_exists():
|
|
5608
|
+
self.body_text.delete("1.0", tk.END)
|
|
5609
|
+
self.body_text.insert("1.0", last_body)
|
|
5610
|
+
|
|
5611
|
+
# Restore body type
|
|
5612
|
+
last_body_type = self.settings.get("last_body_type", "None")
|
|
5613
|
+
if self.body_type_var:
|
|
5614
|
+
self.body_type_var.set(last_body_type)
|
|
5615
|
+
|
|
5616
|
+
# Restore auth type
|
|
5617
|
+
last_auth_type = self.settings.get("last_auth_type", "None")
|
|
5618
|
+
if self.auth_type_var:
|
|
5619
|
+
self.auth_type_var.set(last_auth_type)
|
|
5620
|
+
|
|
5621
|
+
# Restore auth data (decrypted)
|
|
5622
|
+
last_auth_data = self.settings.get("last_auth_data", {})
|
|
5623
|
+
if last_auth_data:
|
|
5624
|
+
if last_auth_type == "Bearer" and hasattr(self, 'bearer_token_var'):
|
|
5625
|
+
token = last_auth_data.get("bearer_token", "")
|
|
5626
|
+
self.bearer_token_var.set(decrypt_auth_value(token))
|
|
5627
|
+
elif last_auth_type == "Basic":
|
|
5628
|
+
if hasattr(self, 'basic_username_var'):
|
|
5629
|
+
self.basic_username_var.set(last_auth_data.get("basic_username", ""))
|
|
5630
|
+
if hasattr(self, 'basic_password_var'):
|
|
5631
|
+
password = last_auth_data.get("basic_password", "")
|
|
5632
|
+
self.basic_password_var.set(decrypt_auth_value(password))
|
|
5633
|
+
elif last_auth_type == "API Key":
|
|
5634
|
+
if hasattr(self, 'api_key_name_var'):
|
|
5635
|
+
self.api_key_name_var.set(last_auth_data.get("api_key_name", ""))
|
|
5636
|
+
if hasattr(self, 'api_key_value_var'):
|
|
5637
|
+
key_value = last_auth_data.get("api_key_value", "")
|
|
5638
|
+
self.api_key_value_var.set(decrypt_auth_value(key_value))
|
|
5639
|
+
if hasattr(self, 'api_key_location_var'):
|
|
5640
|
+
self.api_key_location_var.set(last_auth_data.get("api_key_location", "header"))
|
|
5641
|
+
|
|
5642
|
+
# Restore complex options
|
|
5643
|
+
last_complex_options = self.settings.get("last_complex_options", "")
|
|
5644
|
+
if last_complex_options and hasattr(self, 'complex_options_var'):
|
|
5645
|
+
self.complex_options_var.set(last_complex_options)
|
|
5646
|
+
|
|
5647
|
+
self.logger.debug("UI state restored successfully")
|
|
5648
|
+
|
|
5649
|
+
except Exception as e:
|
|
5650
|
+
self.logger.error(f"Error restoring UI state: {e}")
|
|
5486
5651
|
|
|
5487
5652
|
|
|
5488
5653
|
# For standalone testing
|
package/tools/notes_widget.py
CHANGED
|
@@ -716,9 +716,16 @@ class NotesWidget:
|
|
|
716
716
|
• Search specific columns: Title:refactor OR Input:code.
|
|
717
717
|
• Leave empty to show all records."""
|
|
718
718
|
if self.dialog_manager:
|
|
719
|
-
self.dialog_manager.show_info("Search Help", help_text)
|
|
719
|
+
self.dialog_manager.show_info("Search Help", help_text, parent=self.parent)
|
|
720
720
|
else:
|
|
721
721
|
messagebox.showinfo("Search Help", help_text, parent=self.parent)
|
|
722
|
+
|
|
723
|
+
# Return focus to Notes window after dialog closes
|
|
724
|
+
try:
|
|
725
|
+
self.parent.focus_force()
|
|
726
|
+
self.search_entry.focus_set()
|
|
727
|
+
except Exception:
|
|
728
|
+
pass # Widget may not exist
|
|
722
729
|
|
|
723
730
|
def new_note(self) -> None:
|
|
724
731
|
"""Create a new note."""
|
package/tools/tool_loader.py
CHANGED
|
@@ -301,20 +301,13 @@ TOOL_SPECS: Dict[str, ToolSpec] = {
|
|
|
301
301
|
),
|
|
302
302
|
|
|
303
303
|
# Analysis Tools
|
|
304
|
-
"Word Frequency Counter"
|
|
305
|
-
name="Word Frequency Counter",
|
|
306
|
-
module_path="tools.word_frequency_counter",
|
|
307
|
-
class_name="WordFrequencyCounter",
|
|
308
|
-
category=ToolCategory.ANALYSIS,
|
|
309
|
-
description="Count word frequencies in text",
|
|
310
|
-
available_flag="WORD_FREQUENCY_COUNTER_MODULE_AVAILABLE"
|
|
311
|
-
),
|
|
304
|
+
# NOTE: Word Frequency Counter merged into Text Statistics (has "Word Frequency Counter" button)
|
|
312
305
|
"Text Statistics": ToolSpec(
|
|
313
306
|
name="Text Statistics",
|
|
314
307
|
module_path="tools.text_statistics_tool",
|
|
315
308
|
class_name="TextStatistics",
|
|
316
309
|
category=ToolCategory.ANALYSIS,
|
|
317
|
-
description="
|
|
310
|
+
description="Text stats, character/word/line counts, word frequency",
|
|
318
311
|
available_flag="TEXT_STATISTICS_MODULE_AVAILABLE"
|
|
319
312
|
),
|
|
320
313
|
"Cron Tool": ToolSpec(
|
|
@@ -373,6 +366,24 @@ TOOL_SPECS: Dict[str, ToolSpec] = {
|
|
|
373
366
|
description="Model Context Protocol server management",
|
|
374
367
|
available_flag="MCP_WIDGET_MODULE_AVAILABLE"
|
|
375
368
|
),
|
|
369
|
+
|
|
370
|
+
# Web Tools - handled inline in pomera.py (tabbed interface like AI Tools)
|
|
371
|
+
"Web Search": ToolSpec(
|
|
372
|
+
name="Web Search",
|
|
373
|
+
module_path="tools.web_search", # Core module, UI created inline
|
|
374
|
+
class_name="search", # Function, not class
|
|
375
|
+
category=ToolCategory.UTILITY,
|
|
376
|
+
description="Search the web using DuckDuckGo, Tavily, Google, Brave, SerpApi, Serper",
|
|
377
|
+
available_flag="" # Always available
|
|
378
|
+
),
|
|
379
|
+
"URL Reader": ToolSpec(
|
|
380
|
+
name="URL Reader",
|
|
381
|
+
module_path="tools.url_content_reader",
|
|
382
|
+
class_name="URLContentReader",
|
|
383
|
+
category=ToolCategory.UTILITY,
|
|
384
|
+
description="Fetch URL content and convert to HTML, JSON, or Markdown",
|
|
385
|
+
available_flag="" # Always available
|
|
386
|
+
),
|
|
376
387
|
}
|
|
377
388
|
|
|
378
389
|
# These sub-tools appear as tabs within their parent tool
|