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.
@@ -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
- self.base64_tools = base64_tools
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
- # Auto-process if there's input text
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."""
@@ -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 hardcoded defaults.
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
- # Fallback to hardcoded defaults with updated exclusions
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": "a\nan\nthe\nand\nbut\nor\nfor\nnor\non\nat\nto\nfrom\nby\nwith\nin\nof"
206
+ "exclusions": ""
207
207
  }
208
208
 
209
209
 
@@ -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)
@@ -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
- def _on_closing(self):
4236
- """Handle application closing - save settings before exit."""
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
@@ -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."""
@@ -301,20 +301,13 @@ TOOL_SPECS: Dict[str, ToolSpec] = {
301
301
  ),
302
302
 
303
303
  # Analysis Tools
304
- "Word Frequency Counter": ToolSpec(
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="Calculate text statistics (chars, words, lines)",
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