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
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Load Presets Dialog Module
|
|
3
|
+
|
|
4
|
+
Provides a dialog for users to view and reset tool settings to their defaults.
|
|
5
|
+
Shows all registered tools from SettingsDefaultsRegistry and allows selective reset.
|
|
6
|
+
|
|
7
|
+
Author: Pomera AI Commander Team
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import tkinter as tk
|
|
11
|
+
from tkinter import ttk, messagebox
|
|
12
|
+
import logging
|
|
13
|
+
from typing import Dict, Any, List, Optional
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LoadPresetsDialog:
|
|
17
|
+
"""
|
|
18
|
+
Dialog for loading/resetting tool presets to defaults.
|
|
19
|
+
|
|
20
|
+
Features:
|
|
21
|
+
- Lists all registered tools from SettingsDefaultsRegistry
|
|
22
|
+
- Shows current vs default values preview
|
|
23
|
+
- Allows selective reset of individual tools
|
|
24
|
+
- Confirmation dialog before reset
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, parent, settings_manager, logger=None, dialog_manager=None):
|
|
28
|
+
"""
|
|
29
|
+
Initialize the Load Presets Dialog.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
parent: Parent window
|
|
33
|
+
settings_manager: Settings manager instance (database or file-based)
|
|
34
|
+
logger: Logger instance
|
|
35
|
+
dialog_manager: Optional DialogManager for consistent dialogs
|
|
36
|
+
"""
|
|
37
|
+
self.parent = parent
|
|
38
|
+
self.settings_manager = settings_manager
|
|
39
|
+
self.logger = logger or logging.getLogger(__name__)
|
|
40
|
+
self.dialog_manager = dialog_manager
|
|
41
|
+
|
|
42
|
+
# Get registry
|
|
43
|
+
try:
|
|
44
|
+
from core.settings_defaults_registry import get_registry
|
|
45
|
+
self.registry = get_registry()
|
|
46
|
+
except ImportError:
|
|
47
|
+
self.registry = None
|
|
48
|
+
self.logger.error("SettingsDefaultsRegistry not available")
|
|
49
|
+
|
|
50
|
+
# Dialog window
|
|
51
|
+
self.dialog = None
|
|
52
|
+
self.tree = None
|
|
53
|
+
self.preview_text = None
|
|
54
|
+
self.selected_tools = set()
|
|
55
|
+
|
|
56
|
+
def show(self):
|
|
57
|
+
"""Show the Load Presets dialog."""
|
|
58
|
+
if not self.registry:
|
|
59
|
+
self._show_error("Error", "Settings registry not available.")
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
self.dialog = tk.Toplevel(self.parent)
|
|
63
|
+
self.dialog.title("Load Presets - Reset Tool Settings")
|
|
64
|
+
self.dialog.geometry("800x550")
|
|
65
|
+
self.dialog.transient(self.parent)
|
|
66
|
+
self.dialog.grab_set()
|
|
67
|
+
|
|
68
|
+
# Make dialog modal
|
|
69
|
+
self.dialog.focus_set()
|
|
70
|
+
|
|
71
|
+
# Create main layout
|
|
72
|
+
self._create_widgets()
|
|
73
|
+
|
|
74
|
+
# Populate tools list
|
|
75
|
+
self._populate_tools_list()
|
|
76
|
+
|
|
77
|
+
# Center on parent
|
|
78
|
+
self._center_on_parent()
|
|
79
|
+
|
|
80
|
+
def _center_on_parent(self):
|
|
81
|
+
"""Center dialog on parent window."""
|
|
82
|
+
self.dialog.update_idletasks()
|
|
83
|
+
parent_x = self.parent.winfo_x()
|
|
84
|
+
parent_y = self.parent.winfo_y()
|
|
85
|
+
parent_w = self.parent.winfo_width()
|
|
86
|
+
parent_h = self.parent.winfo_height()
|
|
87
|
+
|
|
88
|
+
dialog_w = self.dialog.winfo_width()
|
|
89
|
+
dialog_h = self.dialog.winfo_height()
|
|
90
|
+
|
|
91
|
+
x = parent_x + (parent_w - dialog_w) // 2
|
|
92
|
+
y = parent_y + (parent_h - dialog_h) // 2
|
|
93
|
+
|
|
94
|
+
self.dialog.geometry(f"+{x}+{y}")
|
|
95
|
+
|
|
96
|
+
def _create_widgets(self):
|
|
97
|
+
"""Create the dialog widgets."""
|
|
98
|
+
main_frame = ttk.Frame(self.dialog, padding="10")
|
|
99
|
+
main_frame.pack(fill=tk.BOTH, expand=True)
|
|
100
|
+
|
|
101
|
+
# Title and description
|
|
102
|
+
title_label = ttk.Label(
|
|
103
|
+
main_frame,
|
|
104
|
+
text="Reset Tool Settings to Defaults",
|
|
105
|
+
font=("TkDefaultFont", 12, "bold")
|
|
106
|
+
)
|
|
107
|
+
title_label.pack(anchor="w", pady=(0, 5))
|
|
108
|
+
|
|
109
|
+
desc_label = ttk.Label(
|
|
110
|
+
main_frame,
|
|
111
|
+
text="Select tools to reset to their default settings. Current settings will be replaced.",
|
|
112
|
+
foreground="gray"
|
|
113
|
+
)
|
|
114
|
+
desc_label.pack(anchor="w", pady=(0, 10))
|
|
115
|
+
|
|
116
|
+
# Split pane: left=tools list, right=preview
|
|
117
|
+
paned = ttk.PanedWindow(main_frame, orient=tk.HORIZONTAL)
|
|
118
|
+
paned.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
|
|
119
|
+
|
|
120
|
+
# Left side: Tools list with checkboxes
|
|
121
|
+
left_frame = ttk.LabelFrame(paned, text="Tools with Defaults", padding="5")
|
|
122
|
+
paned.add(left_frame, weight=1)
|
|
123
|
+
|
|
124
|
+
# Select All / Deselect All buttons
|
|
125
|
+
btn_frame = ttk.Frame(left_frame)
|
|
126
|
+
btn_frame.pack(fill=tk.X, pady=(0, 5))
|
|
127
|
+
|
|
128
|
+
ttk.Button(btn_frame, text="Select All", command=self._select_all, width=12).pack(side=tk.LEFT, padx=(0, 5))
|
|
129
|
+
ttk.Button(btn_frame, text="Deselect All", command=self._deselect_all, width=12).pack(side=tk.LEFT)
|
|
130
|
+
|
|
131
|
+
# Treeview for tools
|
|
132
|
+
tree_frame = ttk.Frame(left_frame)
|
|
133
|
+
tree_frame.pack(fill=tk.BOTH, expand=True)
|
|
134
|
+
|
|
135
|
+
self.tree = ttk.Treeview(
|
|
136
|
+
tree_frame,
|
|
137
|
+
columns=("selected", "description"),
|
|
138
|
+
show="tree headings",
|
|
139
|
+
selectmode="browse"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
self.tree.heading("#0", text="Tool Name")
|
|
143
|
+
self.tree.heading("selected", text="Reset?")
|
|
144
|
+
self.tree.heading("description", text="Description")
|
|
145
|
+
|
|
146
|
+
self.tree.column("#0", width=180, minwidth=150)
|
|
147
|
+
self.tree.column("selected", width=60, minwidth=50, anchor="center")
|
|
148
|
+
self.tree.column("description", width=150, minwidth=100)
|
|
149
|
+
|
|
150
|
+
# Scrollbar
|
|
151
|
+
scrollbar = ttk.Scrollbar(tree_frame, orient="vertical", command=self.tree.yview)
|
|
152
|
+
self.tree.configure(yscrollcommand=scrollbar.set)
|
|
153
|
+
|
|
154
|
+
self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
|
155
|
+
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
|
156
|
+
|
|
157
|
+
# Bind selection and toggle
|
|
158
|
+
self.tree.bind("<<TreeviewSelect>>", self._on_tool_select)
|
|
159
|
+
self.tree.bind("<Double-1>", self._toggle_tool_selection)
|
|
160
|
+
self.tree.bind("<space>", self._toggle_tool_selection)
|
|
161
|
+
|
|
162
|
+
# Right side: Preview panel
|
|
163
|
+
right_frame = ttk.LabelFrame(paned, text="Settings Preview", padding="5")
|
|
164
|
+
paned.add(right_frame, weight=1)
|
|
165
|
+
|
|
166
|
+
self.preview_text = tk.Text(
|
|
167
|
+
right_frame,
|
|
168
|
+
wrap=tk.WORD,
|
|
169
|
+
font=("Consolas", 9),
|
|
170
|
+
state="disabled",
|
|
171
|
+
bg="#f5f5f5"
|
|
172
|
+
)
|
|
173
|
+
self.preview_text.pack(fill=tk.BOTH, expand=True)
|
|
174
|
+
|
|
175
|
+
# Bottom buttons
|
|
176
|
+
button_frame = ttk.Frame(main_frame)
|
|
177
|
+
button_frame.pack(fill=tk.X, pady=(10, 0))
|
|
178
|
+
|
|
179
|
+
# Left: Info about selected count
|
|
180
|
+
self.selected_count_label = ttk.Label(button_frame, text="0 tools selected for reset")
|
|
181
|
+
self.selected_count_label.pack(side=tk.LEFT)
|
|
182
|
+
|
|
183
|
+
# Right: Action buttons
|
|
184
|
+
ttk.Button(
|
|
185
|
+
button_frame,
|
|
186
|
+
text="Cancel",
|
|
187
|
+
command=self.dialog.destroy
|
|
188
|
+
).pack(side=tk.RIGHT, padx=(5, 0))
|
|
189
|
+
|
|
190
|
+
self.reset_button = ttk.Button(
|
|
191
|
+
button_frame,
|
|
192
|
+
text="Reset Selected Tools",
|
|
193
|
+
command=self._reset_selected_tools,
|
|
194
|
+
style="Accent.TButton"
|
|
195
|
+
)
|
|
196
|
+
self.reset_button.pack(side=tk.RIGHT)
|
|
197
|
+
self.reset_button.config(state="disabled")
|
|
198
|
+
|
|
199
|
+
# Add Edit Defaults File button
|
|
200
|
+
ttk.Button(
|
|
201
|
+
button_frame,
|
|
202
|
+
text="Edit Defaults File",
|
|
203
|
+
command=self._open_defaults_file
|
|
204
|
+
).pack(side=tk.RIGHT, padx=(0, 10))
|
|
205
|
+
|
|
206
|
+
def _populate_tools_list(self):
|
|
207
|
+
"""Populate the tools treeview from registry."""
|
|
208
|
+
if not self.registry:
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
registered_tools = self.registry.get_registered_tools()
|
|
212
|
+
|
|
213
|
+
for tool_name in sorted(registered_tools):
|
|
214
|
+
spec = self.registry.get_tool_spec(tool_name)
|
|
215
|
+
description = spec.description if spec else ""
|
|
216
|
+
|
|
217
|
+
self.tree.insert(
|
|
218
|
+
"",
|
|
219
|
+
"end",
|
|
220
|
+
iid=tool_name,
|
|
221
|
+
text=tool_name,
|
|
222
|
+
values=("☐", description)
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
def _on_tool_select(self, event=None):
|
|
226
|
+
"""Handle tool selection - show preview."""
|
|
227
|
+
selection = self.tree.selection()
|
|
228
|
+
if not selection:
|
|
229
|
+
return
|
|
230
|
+
|
|
231
|
+
tool_name = selection[0]
|
|
232
|
+
self._show_preview(tool_name)
|
|
233
|
+
|
|
234
|
+
def _toggle_tool_selection(self, event=None):
|
|
235
|
+
"""Toggle the selection checkbox for a tool."""
|
|
236
|
+
selection = self.tree.selection()
|
|
237
|
+
if not selection:
|
|
238
|
+
return
|
|
239
|
+
|
|
240
|
+
tool_name = selection[0]
|
|
241
|
+
|
|
242
|
+
if tool_name in self.selected_tools:
|
|
243
|
+
self.selected_tools.discard(tool_name)
|
|
244
|
+
self.tree.set(tool_name, "selected", "☐")
|
|
245
|
+
else:
|
|
246
|
+
self.selected_tools.add(tool_name)
|
|
247
|
+
self.tree.set(tool_name, "selected", "☑")
|
|
248
|
+
|
|
249
|
+
self._update_selected_count()
|
|
250
|
+
|
|
251
|
+
def _select_all(self):
|
|
252
|
+
"""Select all tools for reset."""
|
|
253
|
+
for item in self.tree.get_children():
|
|
254
|
+
self.selected_tools.add(item)
|
|
255
|
+
self.tree.set(item, "selected", "☑")
|
|
256
|
+
self._update_selected_count()
|
|
257
|
+
|
|
258
|
+
def _deselect_all(self):
|
|
259
|
+
"""Deselect all tools."""
|
|
260
|
+
self.selected_tools.clear()
|
|
261
|
+
for item in self.tree.get_children():
|
|
262
|
+
self.tree.set(item, "selected", "☐")
|
|
263
|
+
self._update_selected_count()
|
|
264
|
+
|
|
265
|
+
def _update_selected_count(self):
|
|
266
|
+
"""Update the selected count label and button state."""
|
|
267
|
+
count = len(self.selected_tools)
|
|
268
|
+
self.selected_count_label.config(text=f"{count} tool(s) selected for reset")
|
|
269
|
+
|
|
270
|
+
if count > 0:
|
|
271
|
+
self.reset_button.config(state="normal")
|
|
272
|
+
else:
|
|
273
|
+
self.reset_button.config(state="disabled")
|
|
274
|
+
|
|
275
|
+
def _show_preview(self, tool_name: str):
|
|
276
|
+
"""Show current vs default settings preview for a tool."""
|
|
277
|
+
self.preview_text.config(state="normal")
|
|
278
|
+
self.preview_text.delete("1.0", tk.END)
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
# Get defaults from registry
|
|
282
|
+
defaults = self.registry.get_tool_defaults(tool_name)
|
|
283
|
+
|
|
284
|
+
# Get current settings
|
|
285
|
+
current = {}
|
|
286
|
+
if hasattr(self.settings_manager, 'get_tool_settings'):
|
|
287
|
+
current = self.settings_manager.get_tool_settings(tool_name)
|
|
288
|
+
elif hasattr(self.settings_manager, 'settings'):
|
|
289
|
+
tool_settings = self.settings_manager.settings.get("tool_settings", {})
|
|
290
|
+
current = tool_settings.get(tool_name, {})
|
|
291
|
+
|
|
292
|
+
# Format preview
|
|
293
|
+
preview = f"=== {tool_name} ===\n\n"
|
|
294
|
+
preview += "--- DEFAULTS (will be applied) ---\n"
|
|
295
|
+
|
|
296
|
+
for key, value in sorted(defaults.items()):
|
|
297
|
+
# Truncate long values
|
|
298
|
+
value_str = str(value)
|
|
299
|
+
if len(value_str) > 50:
|
|
300
|
+
value_str = value_str[:50] + "..."
|
|
301
|
+
preview += f" {key}: {value_str}\n"
|
|
302
|
+
|
|
303
|
+
if current:
|
|
304
|
+
preview += "\n--- CURRENT VALUES ---\n"
|
|
305
|
+
for key, value in sorted(current.items()):
|
|
306
|
+
value_str = str(value)
|
|
307
|
+
if len(value_str) > 50:
|
|
308
|
+
value_str = value_str[:50] + "..."
|
|
309
|
+
# Highlight differences
|
|
310
|
+
if key in defaults and defaults[key] != value:
|
|
311
|
+
preview += f" {key}: {value_str} ⟵ differs\n"
|
|
312
|
+
else:
|
|
313
|
+
preview += f" {key}: {value_str}\n"
|
|
314
|
+
|
|
315
|
+
self.preview_text.insert("1.0", preview)
|
|
316
|
+
|
|
317
|
+
except Exception as e:
|
|
318
|
+
self.preview_text.insert("1.0", f"Error loading preview: {e}")
|
|
319
|
+
self.logger.error(f"Error showing preview for {tool_name}: {e}")
|
|
320
|
+
|
|
321
|
+
self.preview_text.config(state="disabled")
|
|
322
|
+
|
|
323
|
+
def _reset_selected_tools(self):
|
|
324
|
+
"""Reset selected tools to defaults after confirmation."""
|
|
325
|
+
if not self.selected_tools:
|
|
326
|
+
return
|
|
327
|
+
|
|
328
|
+
# Build confirmation message
|
|
329
|
+
tools_list = "\n".join(f" • {name}" for name in sorted(self.selected_tools))
|
|
330
|
+
message = (
|
|
331
|
+
f"The following {len(self.selected_tools)} tool(s) will have their "
|
|
332
|
+
f"settings reset to defaults:\n\n{tools_list}\n\n"
|
|
333
|
+
"This action cannot be undone. Continue?"
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
# Show confirmation
|
|
337
|
+
if not self._ask_yes_no("Confirm Reset", message):
|
|
338
|
+
return
|
|
339
|
+
|
|
340
|
+
# Perform reset
|
|
341
|
+
reset_count = 0
|
|
342
|
+
errors = []
|
|
343
|
+
|
|
344
|
+
for tool_name in self.selected_tools:
|
|
345
|
+
try:
|
|
346
|
+
defaults = self.registry.get_tool_defaults(tool_name)
|
|
347
|
+
|
|
348
|
+
# Apply defaults to settings manager
|
|
349
|
+
if hasattr(self.settings_manager, 'update_tool_settings'):
|
|
350
|
+
self.settings_manager.update_tool_settings(tool_name, defaults)
|
|
351
|
+
elif hasattr(self.settings_manager, 'set_tool_settings'):
|
|
352
|
+
self.settings_manager.set_tool_settings(tool_name, defaults)
|
|
353
|
+
elif hasattr(self.settings_manager, 'settings'):
|
|
354
|
+
if "tool_settings" not in self.settings_manager.settings:
|
|
355
|
+
self.settings_manager.settings["tool_settings"] = {}
|
|
356
|
+
self.settings_manager.settings["tool_settings"][tool_name] = defaults.copy()
|
|
357
|
+
|
|
358
|
+
reset_count += 1
|
|
359
|
+
self.logger.info(f"Reset '{tool_name}' to defaults")
|
|
360
|
+
|
|
361
|
+
except Exception as e:
|
|
362
|
+
errors.append(f"{tool_name}: {e}")
|
|
363
|
+
self.logger.error(f"Error resetting {tool_name}: {e}")
|
|
364
|
+
|
|
365
|
+
# Save settings
|
|
366
|
+
try:
|
|
367
|
+
if hasattr(self.settings_manager, 'save_settings'):
|
|
368
|
+
self.settings_manager.save_settings()
|
|
369
|
+
except Exception as e:
|
|
370
|
+
self.logger.error(f"Error saving settings: {e}")
|
|
371
|
+
|
|
372
|
+
# Show result
|
|
373
|
+
if errors:
|
|
374
|
+
self._show_warning(
|
|
375
|
+
"Partial Reset",
|
|
376
|
+
f"Reset {reset_count} tool(s) successfully.\n\n"
|
|
377
|
+
f"Errors occurred for:\n" + "\n".join(errors)
|
|
378
|
+
)
|
|
379
|
+
else:
|
|
380
|
+
self._show_info(
|
|
381
|
+
"Reset Complete",
|
|
382
|
+
f"Successfully reset {reset_count} tool(s) to defaults.\n\n"
|
|
383
|
+
"Please restart affected tools for changes to take effect."
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
# Close dialog
|
|
387
|
+
self.dialog.destroy()
|
|
388
|
+
|
|
389
|
+
def _show_info(self, title: str, message: str) -> bool:
|
|
390
|
+
"""Show info dialog."""
|
|
391
|
+
if self.dialog_manager:
|
|
392
|
+
return self.dialog_manager.show_info(title, message, parent=self.dialog)
|
|
393
|
+
else:
|
|
394
|
+
messagebox.showinfo(title, message, parent=self.dialog)
|
|
395
|
+
return True
|
|
396
|
+
|
|
397
|
+
def _show_warning(self, title: str, message: str) -> bool:
|
|
398
|
+
"""Show warning dialog."""
|
|
399
|
+
if self.dialog_manager:
|
|
400
|
+
return self.dialog_manager.show_warning(title, message, parent=self.dialog)
|
|
401
|
+
else:
|
|
402
|
+
messagebox.showwarning(title, message, parent=self.dialog)
|
|
403
|
+
return True
|
|
404
|
+
|
|
405
|
+
def _show_error(self, title: str, message: str) -> bool:
|
|
406
|
+
"""Show error dialog."""
|
|
407
|
+
if self.dialog_manager:
|
|
408
|
+
return self.dialog_manager.show_error(title, message, parent=self.parent)
|
|
409
|
+
else:
|
|
410
|
+
messagebox.showerror(title, message, parent=self.parent)
|
|
411
|
+
return True
|
|
412
|
+
|
|
413
|
+
def _ask_yes_no(self, title: str, message: str) -> bool:
|
|
414
|
+
"""Show yes/no confirmation dialog."""
|
|
415
|
+
if self.dialog_manager:
|
|
416
|
+
return self.dialog_manager.ask_yes_no(title, message, parent=self.dialog)
|
|
417
|
+
else:
|
|
418
|
+
return messagebox.askyesno(title, message, parent=self.dialog)
|
|
419
|
+
|
|
420
|
+
def _open_defaults_file(self):
|
|
421
|
+
"""Open the defaults.json file in the system default editor."""
|
|
422
|
+
import os
|
|
423
|
+
import subprocess
|
|
424
|
+
import platform
|
|
425
|
+
|
|
426
|
+
try:
|
|
427
|
+
# Get path from registry
|
|
428
|
+
json_path = self.registry.get_json_defaults_path() if self.registry else None
|
|
429
|
+
|
|
430
|
+
if not json_path:
|
|
431
|
+
# Try default location
|
|
432
|
+
json_path = os.path.join(os.path.dirname(__file__), "..", "defaults.json")
|
|
433
|
+
|
|
434
|
+
if not os.path.exists(json_path):
|
|
435
|
+
self._show_error(
|
|
436
|
+
"File Not Found",
|
|
437
|
+
f"defaults.json not found at:\n{json_path}\n\n"
|
|
438
|
+
"The file will be created when you restart the app."
|
|
439
|
+
)
|
|
440
|
+
return
|
|
441
|
+
|
|
442
|
+
# Open in system default editor
|
|
443
|
+
if platform.system() == "Windows":
|
|
444
|
+
os.startfile(json_path)
|
|
445
|
+
elif platform.system() == "Darwin": # macOS
|
|
446
|
+
subprocess.run(["open", json_path])
|
|
447
|
+
else: # Linux
|
|
448
|
+
subprocess.run(["xdg-open", json_path])
|
|
449
|
+
|
|
450
|
+
self.logger.info(f"Opened defaults.json: {json_path}")
|
|
451
|
+
|
|
452
|
+
except Exception as e:
|
|
453
|
+
self._show_error("Error", f"Could not open defaults.json:\n{e}")
|
|
454
|
+
self.logger.error(f"Error opening defaults.json: {e}")
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def get_registry():
|
|
458
|
+
"""Get the settings defaults registry singleton."""
|
|
459
|
+
from core.settings_defaults_registry import SettingsDefaultsRegistry
|
|
460
|
+
return SettingsDefaultsRegistry()
|