pomera-ai-commander 1.1.1 → 1.2.2
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/LICENSE +21 -21
- package/README.md +105 -680
- package/bin/pomera-ai-commander.js +62 -62
- package/core/__init__.py +65 -65
- package/core/app_context.py +482 -482
- package/core/async_text_processor.py +421 -421
- package/core/backup_manager.py +655 -655
- package/core/backup_recovery_manager.py +1199 -1033
- package/core/content_hash_cache.py +508 -508
- package/core/context_menu.py +313 -313
- package/core/data_directory.py +549 -0
- package/core/data_validator.py +1066 -1066
- package/core/database_connection_manager.py +744 -744
- package/core/database_curl_settings_manager.py +608 -608
- package/core/database_promera_ai_settings_manager.py +446 -446
- package/core/database_schema.py +411 -411
- package/core/database_schema_manager.py +395 -395
- package/core/database_settings_manager.py +1507 -1507
- package/core/database_settings_manager_interface.py +456 -456
- package/core/dialog_manager.py +734 -734
- package/core/diff_utils.py +239 -0
- package/core/efficient_line_numbers.py +540 -510
- package/core/error_handler.py +746 -746
- package/core/error_service.py +431 -431
- package/core/event_consolidator.py +511 -511
- package/core/mcp/__init__.py +43 -43
- package/core/mcp/find_replace_diff.py +334 -0
- package/core/mcp/protocol.py +288 -288
- package/core/mcp/schema.py +251 -251
- package/core/mcp/server_stdio.py +299 -299
- package/core/mcp/tool_registry.py +2699 -2345
- package/core/memento.py +275 -0
- package/core/memory_efficient_text_widget.py +711 -711
- package/core/migration_manager.py +914 -914
- package/core/migration_test_suite.py +1085 -1085
- package/core/migration_validator.py +1143 -1143
- package/core/optimized_find_replace.py +714 -714
- package/core/optimized_pattern_engine.py +424 -424
- package/core/optimized_search_highlighter.py +552 -552
- package/core/performance_monitor.py +674 -674
- package/core/persistence_manager.py +712 -712
- package/core/progressive_stats_calculator.py +632 -632
- package/core/regex_pattern_cache.py +529 -529
- package/core/regex_pattern_library.py +350 -350
- package/core/search_operation_manager.py +434 -434
- package/core/settings_defaults_registry.py +1087 -1087
- package/core/settings_integrity_validator.py +1111 -1111
- package/core/settings_serializer.py +557 -557
- package/core/settings_validator.py +1823 -1823
- package/core/smart_stats_calculator.py +709 -709
- package/core/statistics_update_manager.py +619 -619
- package/core/stats_config_manager.py +858 -858
- package/core/streaming_text_handler.py +723 -723
- package/core/task_scheduler.py +596 -596
- package/core/update_pattern_library.py +168 -168
- package/core/visibility_monitor.py +596 -596
- package/core/widget_cache.py +498 -498
- package/mcp.json +51 -61
- package/migrate_data.py +127 -0
- package/package.json +64 -57
- package/pomera.py +7883 -7482
- package/pomera_mcp_server.py +183 -144
- package/requirements.txt +33 -0
- package/scripts/Dockerfile.alpine +43 -0
- package/scripts/Dockerfile.gui-test +54 -0
- package/scripts/Dockerfile.linux +43 -0
- package/scripts/Dockerfile.test-linux +80 -0
- package/scripts/Dockerfile.ubuntu +39 -0
- package/scripts/README.md +53 -0
- package/scripts/build-all.bat +113 -0
- package/scripts/build-docker.bat +53 -0
- package/scripts/build-docker.sh +55 -0
- package/scripts/build-optimized.bat +101 -0
- package/scripts/build.sh +78 -0
- package/scripts/docker-compose.test.yml +27 -0
- package/scripts/docker-compose.yml +32 -0
- package/scripts/postinstall.js +62 -0
- package/scripts/requirements-minimal.txt +33 -0
- package/scripts/test-linux-simple.bat +28 -0
- package/scripts/validate-release-workflow.py +450 -0
- package/tools/__init__.py +4 -4
- package/tools/ai_tools.py +2891 -2891
- package/tools/ascii_art_generator.py +352 -352
- package/tools/base64_tools.py +183 -183
- package/tools/base_tool.py +511 -511
- package/tools/case_tool.py +308 -308
- package/tools/column_tools.py +395 -395
- package/tools/cron_tool.py +884 -884
- package/tools/curl_history.py +600 -600
- package/tools/curl_processor.py +1207 -1207
- package/tools/curl_settings.py +502 -502
- package/tools/curl_tool.py +5467 -5467
- package/tools/diff_viewer.py +1817 -1072
- package/tools/email_extraction_tool.py +248 -248
- package/tools/email_header_analyzer.py +425 -425
- package/tools/extraction_tools.py +250 -250
- package/tools/find_replace.py +2289 -1750
- package/tools/folder_file_reporter.py +1463 -1463
- package/tools/folder_file_reporter_adapter.py +480 -480
- package/tools/generator_tools.py +1216 -1216
- package/tools/hash_generator.py +255 -255
- package/tools/html_tool.py +656 -656
- package/tools/jsonxml_tool.py +729 -729
- package/tools/line_tools.py +419 -419
- package/tools/markdown_tools.py +561 -561
- package/tools/mcp_widget.py +1417 -1417
- package/tools/notes_widget.py +978 -973
- package/tools/number_base_converter.py +372 -372
- package/tools/regex_extractor.py +571 -571
- package/tools/slug_generator.py +310 -310
- package/tools/sorter_tools.py +458 -458
- package/tools/string_escape_tool.py +392 -392
- package/tools/text_statistics_tool.py +365 -365
- package/tools/text_wrapper.py +430 -430
- package/tools/timestamp_converter.py +421 -421
- package/tools/tool_loader.py +710 -710
- package/tools/translator_tools.py +522 -522
- package/tools/url_link_extractor.py +261 -261
- package/tools/url_parser.py +204 -204
- package/tools/whitespace_tools.py +355 -355
- package/tools/word_frequency_counter.py +146 -146
- package/core/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/__pycache__/app_context.cpython-313.pyc +0 -0
- package/core/__pycache__/async_text_processor.cpython-313.pyc +0 -0
- package/core/__pycache__/backup_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/backup_recovery_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/content_hash_cache.cpython-313.pyc +0 -0
- package/core/__pycache__/context_menu.cpython-313.pyc +0 -0
- package/core/__pycache__/data_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/database_connection_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_curl_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_promera_ai_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_schema.cpython-313.pyc +0 -0
- package/core/__pycache__/database_schema_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_settings_manager_interface.cpython-313.pyc +0 -0
- package/core/__pycache__/dialog_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/efficient_line_numbers.cpython-313.pyc +0 -0
- package/core/__pycache__/error_handler.cpython-313.pyc +0 -0
- package/core/__pycache__/error_service.cpython-313.pyc +0 -0
- package/core/__pycache__/event_consolidator.cpython-313.pyc +0 -0
- package/core/__pycache__/memory_efficient_text_widget.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_test_suite.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_find_replace.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_pattern_engine.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_search_highlighter.cpython-313.pyc +0 -0
- package/core/__pycache__/performance_monitor.cpython-313.pyc +0 -0
- package/core/__pycache__/persistence_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/progressive_stats_calculator.cpython-313.pyc +0 -0
- package/core/__pycache__/regex_pattern_cache.cpython-313.pyc +0 -0
- package/core/__pycache__/regex_pattern_library.cpython-313.pyc +0 -0
- package/core/__pycache__/search_operation_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_defaults_registry.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_integrity_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_serializer.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/smart_stats_calculator.cpython-313.pyc +0 -0
- package/core/__pycache__/statistics_update_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/stats_config_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/streaming_text_handler.cpython-313.pyc +0 -0
- package/core/__pycache__/task_scheduler.cpython-313.pyc +0 -0
- package/core/__pycache__/visibility_monitor.cpython-313.pyc +0 -0
- package/core/__pycache__/widget_cache.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/protocol.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/server_stdio.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/tool_registry.cpython-313.pyc +0 -0
- package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- package/tools/__pycache__/ai_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/ascii_art_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/base64_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/base_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/case_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/column_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/cron_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_history.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_processor.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_settings.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/diff_viewer.cpython-313.pyc +0 -0
- package/tools/__pycache__/email_extraction_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/email_header_analyzer.cpython-313.pyc +0 -0
- package/tools/__pycache__/extraction_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/find_replace.cpython-313.pyc +0 -0
- package/tools/__pycache__/folder_file_reporter.cpython-313.pyc +0 -0
- package/tools/__pycache__/folder_file_reporter_adapter.cpython-313.pyc +0 -0
- package/tools/__pycache__/generator_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/hash_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/html_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/huggingface_helper.cpython-313.pyc +0 -0
- package/tools/__pycache__/jsonxml_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/line_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/list_comparator.cpython-313.pyc +0 -0
- package/tools/__pycache__/markdown_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/mcp_widget.cpython-313.pyc +0 -0
- package/tools/__pycache__/notes_widget.cpython-313.pyc +0 -0
- package/tools/__pycache__/number_base_converter.cpython-313.pyc +0 -0
- package/tools/__pycache__/regex_extractor.cpython-313.pyc +0 -0
- package/tools/__pycache__/slug_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/sorter_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/string_escape_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/text_statistics_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/text_wrapper.cpython-313.pyc +0 -0
- package/tools/__pycache__/timestamp_converter.cpython-313.pyc +0 -0
- package/tools/__pycache__/tool_loader.cpython-313.pyc +0 -0
- package/tools/__pycache__/translator_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/url_link_extractor.cpython-313.pyc +0 -0
- package/tools/__pycache__/url_parser.cpython-313.pyc +0 -0
- package/tools/__pycache__/whitespace_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/word_frequency_counter.cpython-313.pyc +0 -0
package/tools/slug_generator.py
CHANGED
|
@@ -1,311 +1,311 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Slug/URL Generator Module - URL-friendly slug generation
|
|
3
|
-
|
|
4
|
-
This module provides slug generation functionality
|
|
5
|
-
for the Pomera AI Commander application.
|
|
6
|
-
|
|
7
|
-
Features:
|
|
8
|
-
- Convert text to URL-friendly slugs
|
|
9
|
-
- Multiple separator options
|
|
10
|
-
- Transliteration of accented characters
|
|
11
|
-
- Max length option
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
import tkinter as tk
|
|
15
|
-
from tkinter import ttk
|
|
16
|
-
import re
|
|
17
|
-
import unicodedata
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class SlugGeneratorProcessor:
|
|
21
|
-
"""Slug generator processor."""
|
|
22
|
-
|
|
23
|
-
# Common transliteration mappings
|
|
24
|
-
TRANSLITERATION_MAP = {
|
|
25
|
-
'ä': 'ae', 'ö': 'oe', 'ü': 'ue', 'ß': 'ss',
|
|
26
|
-
'à': 'a', 'á': 'a', 'â': 'a', 'ã': 'a', 'å': 'a',
|
|
27
|
-
'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e',
|
|
28
|
-
'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i',
|
|
29
|
-
'ò': 'o', 'ó': 'o', 'ô': 'o', 'õ': 'o', 'ø': 'o',
|
|
30
|
-
'ù': 'u', 'ú': 'u', 'û': 'u',
|
|
31
|
-
'ñ': 'n', 'ç': 'c', 'ý': 'y', 'ÿ': 'y',
|
|
32
|
-
'æ': 'ae', 'œ': 'oe', 'ð': 'd', 'þ': 'th',
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
# Common stop words to optionally remove
|
|
36
|
-
STOP_WORDS = {
|
|
37
|
-
'a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',
|
|
38
|
-
'of', 'with', 'by', 'from', 'is', 'are', 'was', 'were', 'be', 'been',
|
|
39
|
-
'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would',
|
|
40
|
-
'could', 'should', 'may', 'might', 'must', 'shall', 'can'
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
@staticmethod
|
|
44
|
-
def transliterate(text):
|
|
45
|
-
"""Transliterate accented characters to ASCII equivalents."""
|
|
46
|
-
result = []
|
|
47
|
-
for char in text:
|
|
48
|
-
lower_char = char.lower()
|
|
49
|
-
if lower_char in SlugGeneratorProcessor.TRANSLITERATION_MAP:
|
|
50
|
-
replacement = SlugGeneratorProcessor.TRANSLITERATION_MAP[lower_char]
|
|
51
|
-
if char.isupper():
|
|
52
|
-
replacement = replacement.capitalize()
|
|
53
|
-
result.append(replacement)
|
|
54
|
-
else:
|
|
55
|
-
# Use unicodedata for other characters
|
|
56
|
-
normalized = unicodedata.normalize('NFKD', char)
|
|
57
|
-
ascii_char = normalized.encode('ascii', 'ignore').decode('ascii')
|
|
58
|
-
result.append(ascii_char if ascii_char else '')
|
|
59
|
-
|
|
60
|
-
return ''.join(result)
|
|
61
|
-
|
|
62
|
-
@staticmethod
|
|
63
|
-
def generate_slug(text, separator="-", lowercase=True, transliterate=True,
|
|
64
|
-
max_length=0, remove_stopwords=False):
|
|
65
|
-
"""Generate a URL-friendly slug from text."""
|
|
66
|
-
result = text
|
|
67
|
-
|
|
68
|
-
# Transliterate if requested
|
|
69
|
-
if transliterate:
|
|
70
|
-
result = SlugGeneratorProcessor.transliterate(result)
|
|
71
|
-
|
|
72
|
-
# Convert to lowercase if requested
|
|
73
|
-
if lowercase:
|
|
74
|
-
result = result.lower()
|
|
75
|
-
|
|
76
|
-
# Remove stop words if requested
|
|
77
|
-
if remove_stopwords:
|
|
78
|
-
words = result.split()
|
|
79
|
-
words = [w for w in words if w.lower() not in SlugGeneratorProcessor.STOP_WORDS]
|
|
80
|
-
result = ' '.join(words)
|
|
81
|
-
|
|
82
|
-
# Replace non-alphanumeric characters with separator
|
|
83
|
-
if separator:
|
|
84
|
-
result = re.sub(r'[^a-zA-Z0-9]+', separator, result)
|
|
85
|
-
# Remove leading/trailing separators
|
|
86
|
-
result = result.strip(separator)
|
|
87
|
-
# Collapse multiple separators
|
|
88
|
-
result = re.sub(re.escape(separator) + '+', separator, result)
|
|
89
|
-
else:
|
|
90
|
-
# No separator - just remove non-alphanumeric
|
|
91
|
-
result = re.sub(r'[^a-zA-Z0-9]', '', result)
|
|
92
|
-
|
|
93
|
-
# Apply max length if specified
|
|
94
|
-
if max_length > 0 and len(result) > max_length:
|
|
95
|
-
result = result[:max_length]
|
|
96
|
-
# Don't end with separator
|
|
97
|
-
if separator:
|
|
98
|
-
result = result.rstrip(separator)
|
|
99
|
-
|
|
100
|
-
return result
|
|
101
|
-
|
|
102
|
-
@staticmethod
|
|
103
|
-
def generate_batch(text, separator="-", lowercase=True, transliterate=True,
|
|
104
|
-
max_length=0, remove_stopwords=False):
|
|
105
|
-
"""Generate slugs for multiple lines."""
|
|
106
|
-
lines = text.strip().split('\n')
|
|
107
|
-
results = []
|
|
108
|
-
|
|
109
|
-
for line in lines:
|
|
110
|
-
line = line.strip()
|
|
111
|
-
if line:
|
|
112
|
-
slug = SlugGeneratorProcessor.generate_slug(
|
|
113
|
-
line, separator, lowercase, transliterate, max_length, remove_stopwords
|
|
114
|
-
)
|
|
115
|
-
results.append(slug)
|
|
116
|
-
else:
|
|
117
|
-
results.append('')
|
|
118
|
-
|
|
119
|
-
return '\n'.join(results)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
class SlugGeneratorWidget(ttk.Frame):
|
|
123
|
-
"""Widget for slug generator tool."""
|
|
124
|
-
|
|
125
|
-
def __init__(self, parent, app):
|
|
126
|
-
super().__init__(parent)
|
|
127
|
-
self.app = app
|
|
128
|
-
self.processor = SlugGeneratorProcessor()
|
|
129
|
-
|
|
130
|
-
self.separator = tk.StringVar(value="-")
|
|
131
|
-
self.lowercase = tk.BooleanVar(value=True)
|
|
132
|
-
self.transliterate = tk.BooleanVar(value=True)
|
|
133
|
-
self.max_length = tk.IntVar(value=0)
|
|
134
|
-
self.remove_stopwords = tk.BooleanVar(value=False)
|
|
135
|
-
|
|
136
|
-
self.create_widgets()
|
|
137
|
-
self.load_settings()
|
|
138
|
-
|
|
139
|
-
def create_widgets(self):
|
|
140
|
-
"""Creates the widget interface."""
|
|
141
|
-
# Separator selection
|
|
142
|
-
sep_frame = ttk.LabelFrame(self, text="Separator", padding=10)
|
|
143
|
-
sep_frame.pack(fill=tk.X, padx=5, pady=5)
|
|
144
|
-
|
|
145
|
-
separators = [("Hyphen (-)", "-"), ("Underscore (_)", "_"), ("None", "")]
|
|
146
|
-
for text, value in separators:
|
|
147
|
-
ttk.Radiobutton(sep_frame, text=text,
|
|
148
|
-
variable=self.separator, value=value,
|
|
149
|
-
command=self.on_setting_change).pack(side=tk.LEFT, padx=10)
|
|
150
|
-
|
|
151
|
-
# Options
|
|
152
|
-
options_frame = ttk.LabelFrame(self, text="Options", padding=10)
|
|
153
|
-
options_frame.pack(fill=tk.X, padx=5, pady=5)
|
|
154
|
-
|
|
155
|
-
ttk.Checkbutton(options_frame, text="Lowercase",
|
|
156
|
-
variable=self.lowercase,
|
|
157
|
-
command=self.on_setting_change).pack(anchor=tk.W)
|
|
158
|
-
ttk.Checkbutton(options_frame, text="Transliterate Accents (é → e)",
|
|
159
|
-
variable=self.transliterate,
|
|
160
|
-
command=self.on_setting_change).pack(anchor=tk.W)
|
|
161
|
-
ttk.Checkbutton(options_frame, text="Remove Stop Words (a, the, and, etc.)",
|
|
162
|
-
variable=self.remove_stopwords,
|
|
163
|
-
command=self.on_setting_change).pack(anchor=tk.W)
|
|
164
|
-
|
|
165
|
-
# Max length
|
|
166
|
-
length_frame = ttk.Frame(self)
|
|
167
|
-
length_frame.pack(fill=tk.X, padx=5, pady=5)
|
|
168
|
-
|
|
169
|
-
ttk.Label(length_frame, text="Max Length (0 = no limit):").pack(side=tk.LEFT)
|
|
170
|
-
ttk.Spinbox(length_frame, from_=0, to=500, width=5,
|
|
171
|
-
textvariable=self.max_length,
|
|
172
|
-
command=self.on_setting_change).pack(side=tk.LEFT, padx=5)
|
|
173
|
-
|
|
174
|
-
# Generate button
|
|
175
|
-
ttk.Button(self, text="Generate Slug(s)",
|
|
176
|
-
command=self.generate).pack(pady=10)
|
|
177
|
-
|
|
178
|
-
# Info
|
|
179
|
-
info = ttk.Label(self, text="Enter text on each line to generate multiple slugs",
|
|
180
|
-
font=('TkDefaultFont', 8))
|
|
181
|
-
info.pack(pady=5)
|
|
182
|
-
|
|
183
|
-
def load_settings(self):
|
|
184
|
-
"""Load settings from the application."""
|
|
185
|
-
settings = self.app.settings.get("tool_settings", {}).get("Slug Generator", {})
|
|
186
|
-
|
|
187
|
-
self.separator.set(settings.get("separator", "-"))
|
|
188
|
-
self.lowercase.set(settings.get("lowercase", True))
|
|
189
|
-
self.transliterate.set(settings.get("transliterate", True))
|
|
190
|
-
self.max_length.set(settings.get("max_length", 0))
|
|
191
|
-
self.remove_stopwords.set(settings.get("remove_stopwords", False))
|
|
192
|
-
|
|
193
|
-
def save_settings(self):
|
|
194
|
-
"""Save current settings to the application."""
|
|
195
|
-
if "Slug Generator" not in self.app.settings["tool_settings"]:
|
|
196
|
-
self.app.settings["tool_settings"]["Slug Generator"] = {}
|
|
197
|
-
|
|
198
|
-
self.app.settings["tool_settings"]["Slug Generator"].update({
|
|
199
|
-
"separator": self.separator.get(),
|
|
200
|
-
"lowercase": self.lowercase.get(),
|
|
201
|
-
"transliterate": self.transliterate.get(),
|
|
202
|
-
"max_length": self.max_length.get(),
|
|
203
|
-
"remove_stopwords": self.remove_stopwords.get()
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
self.app.save_settings()
|
|
207
|
-
|
|
208
|
-
def on_setting_change(self, *args):
|
|
209
|
-
"""Handle setting changes."""
|
|
210
|
-
self.save_settings()
|
|
211
|
-
|
|
212
|
-
def generate(self):
|
|
213
|
-
"""Generate slugs."""
|
|
214
|
-
active_input_tab = self.app.input_tabs[self.app.input_notebook.index(self.app.input_notebook.select())]
|
|
215
|
-
input_text = active_input_tab.text.get("1.0", tk.END).rstrip('\n')
|
|
216
|
-
|
|
217
|
-
if not input_text.strip():
|
|
218
|
-
return
|
|
219
|
-
|
|
220
|
-
result = SlugGeneratorProcessor.generate_batch(
|
|
221
|
-
input_text,
|
|
222
|
-
self.separator.get(),
|
|
223
|
-
self.lowercase.get(),
|
|
224
|
-
self.transliterate.get(),
|
|
225
|
-
self.max_length.get(),
|
|
226
|
-
self.remove_stopwords.get()
|
|
227
|
-
)
|
|
228
|
-
|
|
229
|
-
active_output_tab = self.app.output_tabs[self.app.output_notebook.index(self.app.output_notebook.select())]
|
|
230
|
-
active_output_tab.text.config(state="normal")
|
|
231
|
-
active_output_tab.text.delete("1.0", tk.END)
|
|
232
|
-
active_output_tab.text.insert("1.0", result)
|
|
233
|
-
active_output_tab.text.config(state="disabled")
|
|
234
|
-
|
|
235
|
-
self.app.update_all_stats()
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
class SlugGenerator:
|
|
239
|
-
"""Main class for Slug Generator integration."""
|
|
240
|
-
|
|
241
|
-
def __init__(self):
|
|
242
|
-
self.processor = SlugGeneratorProcessor()
|
|
243
|
-
|
|
244
|
-
def create_widget(self, parent, app):
|
|
245
|
-
"""Create and return the Slug Generator widget."""
|
|
246
|
-
return SlugGeneratorWidget(parent, app)
|
|
247
|
-
|
|
248
|
-
def get_default_settings(self):
|
|
249
|
-
"""Return default settings for Slug Generator."""
|
|
250
|
-
return {
|
|
251
|
-
"separator": "-",
|
|
252
|
-
"lowercase": True,
|
|
253
|
-
"transliterate": True,
|
|
254
|
-
"max_length": 0,
|
|
255
|
-
"remove_stopwords": False
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
# BaseTool-compatible wrapper
|
|
260
|
-
try:
|
|
261
|
-
from tools.base_tool import BaseTool
|
|
262
|
-
from typing import Dict, Any, Optional, Callable
|
|
263
|
-
|
|
264
|
-
class SlugGeneratorV2(BaseTool):
|
|
265
|
-
"""
|
|
266
|
-
BaseTool-compatible version of SlugGenerator.
|
|
267
|
-
"""
|
|
268
|
-
|
|
269
|
-
TOOL_NAME = "Slug Generator"
|
|
270
|
-
TOOL_DESCRIPTION = "Generate URL-friendly slugs from text"
|
|
271
|
-
TOOL_VERSION = "2.0.0"
|
|
272
|
-
|
|
273
|
-
def __init__(self):
|
|
274
|
-
super().__init__()
|
|
275
|
-
self._processor = SlugGeneratorProcessor()
|
|
276
|
-
|
|
277
|
-
def process_text(self, input_text: str, settings: Dict[str, Any]) -> str:
|
|
278
|
-
"""Generate slugs from input text."""
|
|
279
|
-
return SlugGeneratorProcessor.generate_batch(
|
|
280
|
-
input_text,
|
|
281
|
-
settings.get("separator", "-"),
|
|
282
|
-
settings.get("lowercase", True),
|
|
283
|
-
settings.get("transliterate", True),
|
|
284
|
-
settings.get("max_length", 0),
|
|
285
|
-
settings.get("remove_stopwords", False)
|
|
286
|
-
)
|
|
287
|
-
|
|
288
|
-
def create_ui(self,
|
|
289
|
-
parent,
|
|
290
|
-
settings: Dict[str, Any],
|
|
291
|
-
on_setting_change_callback: Optional[Callable] = None,
|
|
292
|
-
apply_tool_callback: Optional[Callable] = None):
|
|
293
|
-
"""Create minimal UI - full widget used separately."""
|
|
294
|
-
self._settings = settings.copy()
|
|
295
|
-
self._on_setting_change = on_setting_change_callback
|
|
296
|
-
self._apply_callback = apply_tool_callback
|
|
297
|
-
return None
|
|
298
|
-
|
|
299
|
-
@classmethod
|
|
300
|
-
def get_default_settings(cls) -> Dict[str, Any]:
|
|
301
|
-
"""Return default settings."""
|
|
302
|
-
return {
|
|
303
|
-
"separator": "-",
|
|
304
|
-
"lowercase": True,
|
|
305
|
-
"transliterate": True,
|
|
306
|
-
"max_length": 0,
|
|
307
|
-
"remove_stopwords": False
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
except ImportError:
|
|
1
|
+
"""
|
|
2
|
+
Slug/URL Generator Module - URL-friendly slug generation
|
|
3
|
+
|
|
4
|
+
This module provides slug generation functionality
|
|
5
|
+
for the Pomera AI Commander application.
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
- Convert text to URL-friendly slugs
|
|
9
|
+
- Multiple separator options
|
|
10
|
+
- Transliteration of accented characters
|
|
11
|
+
- Max length option
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import tkinter as tk
|
|
15
|
+
from tkinter import ttk
|
|
16
|
+
import re
|
|
17
|
+
import unicodedata
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SlugGeneratorProcessor:
|
|
21
|
+
"""Slug generator processor."""
|
|
22
|
+
|
|
23
|
+
# Common transliteration mappings
|
|
24
|
+
TRANSLITERATION_MAP = {
|
|
25
|
+
'ä': 'ae', 'ö': 'oe', 'ü': 'ue', 'ß': 'ss',
|
|
26
|
+
'à': 'a', 'á': 'a', 'â': 'a', 'ã': 'a', 'å': 'a',
|
|
27
|
+
'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e',
|
|
28
|
+
'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i',
|
|
29
|
+
'ò': 'o', 'ó': 'o', 'ô': 'o', 'õ': 'o', 'ø': 'o',
|
|
30
|
+
'ù': 'u', 'ú': 'u', 'û': 'u',
|
|
31
|
+
'ñ': 'n', 'ç': 'c', 'ý': 'y', 'ÿ': 'y',
|
|
32
|
+
'æ': 'ae', 'œ': 'oe', 'ð': 'd', 'þ': 'th',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Common stop words to optionally remove
|
|
36
|
+
STOP_WORDS = {
|
|
37
|
+
'a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',
|
|
38
|
+
'of', 'with', 'by', 'from', 'is', 'are', 'was', 'were', 'be', 'been',
|
|
39
|
+
'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would',
|
|
40
|
+
'could', 'should', 'may', 'might', 'must', 'shall', 'can'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def transliterate(text):
|
|
45
|
+
"""Transliterate accented characters to ASCII equivalents."""
|
|
46
|
+
result = []
|
|
47
|
+
for char in text:
|
|
48
|
+
lower_char = char.lower()
|
|
49
|
+
if lower_char in SlugGeneratorProcessor.TRANSLITERATION_MAP:
|
|
50
|
+
replacement = SlugGeneratorProcessor.TRANSLITERATION_MAP[lower_char]
|
|
51
|
+
if char.isupper():
|
|
52
|
+
replacement = replacement.capitalize()
|
|
53
|
+
result.append(replacement)
|
|
54
|
+
else:
|
|
55
|
+
# Use unicodedata for other characters
|
|
56
|
+
normalized = unicodedata.normalize('NFKD', char)
|
|
57
|
+
ascii_char = normalized.encode('ascii', 'ignore').decode('ascii')
|
|
58
|
+
result.append(ascii_char if ascii_char else '')
|
|
59
|
+
|
|
60
|
+
return ''.join(result)
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def generate_slug(text, separator="-", lowercase=True, transliterate=True,
|
|
64
|
+
max_length=0, remove_stopwords=False):
|
|
65
|
+
"""Generate a URL-friendly slug from text."""
|
|
66
|
+
result = text
|
|
67
|
+
|
|
68
|
+
# Transliterate if requested
|
|
69
|
+
if transliterate:
|
|
70
|
+
result = SlugGeneratorProcessor.transliterate(result)
|
|
71
|
+
|
|
72
|
+
# Convert to lowercase if requested
|
|
73
|
+
if lowercase:
|
|
74
|
+
result = result.lower()
|
|
75
|
+
|
|
76
|
+
# Remove stop words if requested
|
|
77
|
+
if remove_stopwords:
|
|
78
|
+
words = result.split()
|
|
79
|
+
words = [w for w in words if w.lower() not in SlugGeneratorProcessor.STOP_WORDS]
|
|
80
|
+
result = ' '.join(words)
|
|
81
|
+
|
|
82
|
+
# Replace non-alphanumeric characters with separator
|
|
83
|
+
if separator:
|
|
84
|
+
result = re.sub(r'[^a-zA-Z0-9]+', separator, result)
|
|
85
|
+
# Remove leading/trailing separators
|
|
86
|
+
result = result.strip(separator)
|
|
87
|
+
# Collapse multiple separators
|
|
88
|
+
result = re.sub(re.escape(separator) + '+', separator, result)
|
|
89
|
+
else:
|
|
90
|
+
# No separator - just remove non-alphanumeric
|
|
91
|
+
result = re.sub(r'[^a-zA-Z0-9]', '', result)
|
|
92
|
+
|
|
93
|
+
# Apply max length if specified
|
|
94
|
+
if max_length > 0 and len(result) > max_length:
|
|
95
|
+
result = result[:max_length]
|
|
96
|
+
# Don't end with separator
|
|
97
|
+
if separator:
|
|
98
|
+
result = result.rstrip(separator)
|
|
99
|
+
|
|
100
|
+
return result
|
|
101
|
+
|
|
102
|
+
@staticmethod
|
|
103
|
+
def generate_batch(text, separator="-", lowercase=True, transliterate=True,
|
|
104
|
+
max_length=0, remove_stopwords=False):
|
|
105
|
+
"""Generate slugs for multiple lines."""
|
|
106
|
+
lines = text.strip().split('\n')
|
|
107
|
+
results = []
|
|
108
|
+
|
|
109
|
+
for line in lines:
|
|
110
|
+
line = line.strip()
|
|
111
|
+
if line:
|
|
112
|
+
slug = SlugGeneratorProcessor.generate_slug(
|
|
113
|
+
line, separator, lowercase, transliterate, max_length, remove_stopwords
|
|
114
|
+
)
|
|
115
|
+
results.append(slug)
|
|
116
|
+
else:
|
|
117
|
+
results.append('')
|
|
118
|
+
|
|
119
|
+
return '\n'.join(results)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class SlugGeneratorWidget(ttk.Frame):
|
|
123
|
+
"""Widget for slug generator tool."""
|
|
124
|
+
|
|
125
|
+
def __init__(self, parent, app):
|
|
126
|
+
super().__init__(parent)
|
|
127
|
+
self.app = app
|
|
128
|
+
self.processor = SlugGeneratorProcessor()
|
|
129
|
+
|
|
130
|
+
self.separator = tk.StringVar(value="-")
|
|
131
|
+
self.lowercase = tk.BooleanVar(value=True)
|
|
132
|
+
self.transliterate = tk.BooleanVar(value=True)
|
|
133
|
+
self.max_length = tk.IntVar(value=0)
|
|
134
|
+
self.remove_stopwords = tk.BooleanVar(value=False)
|
|
135
|
+
|
|
136
|
+
self.create_widgets()
|
|
137
|
+
self.load_settings()
|
|
138
|
+
|
|
139
|
+
def create_widgets(self):
|
|
140
|
+
"""Creates the widget interface."""
|
|
141
|
+
# Separator selection
|
|
142
|
+
sep_frame = ttk.LabelFrame(self, text="Separator", padding=10)
|
|
143
|
+
sep_frame.pack(fill=tk.X, padx=5, pady=5)
|
|
144
|
+
|
|
145
|
+
separators = [("Hyphen (-)", "-"), ("Underscore (_)", "_"), ("None", "")]
|
|
146
|
+
for text, value in separators:
|
|
147
|
+
ttk.Radiobutton(sep_frame, text=text,
|
|
148
|
+
variable=self.separator, value=value,
|
|
149
|
+
command=self.on_setting_change).pack(side=tk.LEFT, padx=10)
|
|
150
|
+
|
|
151
|
+
# Options
|
|
152
|
+
options_frame = ttk.LabelFrame(self, text="Options", padding=10)
|
|
153
|
+
options_frame.pack(fill=tk.X, padx=5, pady=5)
|
|
154
|
+
|
|
155
|
+
ttk.Checkbutton(options_frame, text="Lowercase",
|
|
156
|
+
variable=self.lowercase,
|
|
157
|
+
command=self.on_setting_change).pack(anchor=tk.W)
|
|
158
|
+
ttk.Checkbutton(options_frame, text="Transliterate Accents (é → e)",
|
|
159
|
+
variable=self.transliterate,
|
|
160
|
+
command=self.on_setting_change).pack(anchor=tk.W)
|
|
161
|
+
ttk.Checkbutton(options_frame, text="Remove Stop Words (a, the, and, etc.)",
|
|
162
|
+
variable=self.remove_stopwords,
|
|
163
|
+
command=self.on_setting_change).pack(anchor=tk.W)
|
|
164
|
+
|
|
165
|
+
# Max length
|
|
166
|
+
length_frame = ttk.Frame(self)
|
|
167
|
+
length_frame.pack(fill=tk.X, padx=5, pady=5)
|
|
168
|
+
|
|
169
|
+
ttk.Label(length_frame, text="Max Length (0 = no limit):").pack(side=tk.LEFT)
|
|
170
|
+
ttk.Spinbox(length_frame, from_=0, to=500, width=5,
|
|
171
|
+
textvariable=self.max_length,
|
|
172
|
+
command=self.on_setting_change).pack(side=tk.LEFT, padx=5)
|
|
173
|
+
|
|
174
|
+
# Generate button
|
|
175
|
+
ttk.Button(self, text="Generate Slug(s)",
|
|
176
|
+
command=self.generate).pack(pady=10)
|
|
177
|
+
|
|
178
|
+
# Info
|
|
179
|
+
info = ttk.Label(self, text="Enter text on each line to generate multiple slugs",
|
|
180
|
+
font=('TkDefaultFont', 8))
|
|
181
|
+
info.pack(pady=5)
|
|
182
|
+
|
|
183
|
+
def load_settings(self):
|
|
184
|
+
"""Load settings from the application."""
|
|
185
|
+
settings = self.app.settings.get("tool_settings", {}).get("Slug Generator", {})
|
|
186
|
+
|
|
187
|
+
self.separator.set(settings.get("separator", "-"))
|
|
188
|
+
self.lowercase.set(settings.get("lowercase", True))
|
|
189
|
+
self.transliterate.set(settings.get("transliterate", True))
|
|
190
|
+
self.max_length.set(settings.get("max_length", 0))
|
|
191
|
+
self.remove_stopwords.set(settings.get("remove_stopwords", False))
|
|
192
|
+
|
|
193
|
+
def save_settings(self):
|
|
194
|
+
"""Save current settings to the application."""
|
|
195
|
+
if "Slug Generator" not in self.app.settings["tool_settings"]:
|
|
196
|
+
self.app.settings["tool_settings"]["Slug Generator"] = {}
|
|
197
|
+
|
|
198
|
+
self.app.settings["tool_settings"]["Slug Generator"].update({
|
|
199
|
+
"separator": self.separator.get(),
|
|
200
|
+
"lowercase": self.lowercase.get(),
|
|
201
|
+
"transliterate": self.transliterate.get(),
|
|
202
|
+
"max_length": self.max_length.get(),
|
|
203
|
+
"remove_stopwords": self.remove_stopwords.get()
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
self.app.save_settings()
|
|
207
|
+
|
|
208
|
+
def on_setting_change(self, *args):
|
|
209
|
+
"""Handle setting changes."""
|
|
210
|
+
self.save_settings()
|
|
211
|
+
|
|
212
|
+
def generate(self):
|
|
213
|
+
"""Generate slugs."""
|
|
214
|
+
active_input_tab = self.app.input_tabs[self.app.input_notebook.index(self.app.input_notebook.select())]
|
|
215
|
+
input_text = active_input_tab.text.get("1.0", tk.END).rstrip('\n')
|
|
216
|
+
|
|
217
|
+
if not input_text.strip():
|
|
218
|
+
return
|
|
219
|
+
|
|
220
|
+
result = SlugGeneratorProcessor.generate_batch(
|
|
221
|
+
input_text,
|
|
222
|
+
self.separator.get(),
|
|
223
|
+
self.lowercase.get(),
|
|
224
|
+
self.transliterate.get(),
|
|
225
|
+
self.max_length.get(),
|
|
226
|
+
self.remove_stopwords.get()
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
active_output_tab = self.app.output_tabs[self.app.output_notebook.index(self.app.output_notebook.select())]
|
|
230
|
+
active_output_tab.text.config(state="normal")
|
|
231
|
+
active_output_tab.text.delete("1.0", tk.END)
|
|
232
|
+
active_output_tab.text.insert("1.0", result)
|
|
233
|
+
active_output_tab.text.config(state="disabled")
|
|
234
|
+
|
|
235
|
+
self.app.update_all_stats()
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class SlugGenerator:
|
|
239
|
+
"""Main class for Slug Generator integration."""
|
|
240
|
+
|
|
241
|
+
def __init__(self):
|
|
242
|
+
self.processor = SlugGeneratorProcessor()
|
|
243
|
+
|
|
244
|
+
def create_widget(self, parent, app):
|
|
245
|
+
"""Create and return the Slug Generator widget."""
|
|
246
|
+
return SlugGeneratorWidget(parent, app)
|
|
247
|
+
|
|
248
|
+
def get_default_settings(self):
|
|
249
|
+
"""Return default settings for Slug Generator."""
|
|
250
|
+
return {
|
|
251
|
+
"separator": "-",
|
|
252
|
+
"lowercase": True,
|
|
253
|
+
"transliterate": True,
|
|
254
|
+
"max_length": 0,
|
|
255
|
+
"remove_stopwords": False
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
# BaseTool-compatible wrapper
|
|
260
|
+
try:
|
|
261
|
+
from tools.base_tool import BaseTool
|
|
262
|
+
from typing import Dict, Any, Optional, Callable
|
|
263
|
+
|
|
264
|
+
class SlugGeneratorV2(BaseTool):
|
|
265
|
+
"""
|
|
266
|
+
BaseTool-compatible version of SlugGenerator.
|
|
267
|
+
"""
|
|
268
|
+
|
|
269
|
+
TOOL_NAME = "Slug Generator"
|
|
270
|
+
TOOL_DESCRIPTION = "Generate URL-friendly slugs from text"
|
|
271
|
+
TOOL_VERSION = "2.0.0"
|
|
272
|
+
|
|
273
|
+
def __init__(self):
|
|
274
|
+
super().__init__()
|
|
275
|
+
self._processor = SlugGeneratorProcessor()
|
|
276
|
+
|
|
277
|
+
def process_text(self, input_text: str, settings: Dict[str, Any]) -> str:
|
|
278
|
+
"""Generate slugs from input text."""
|
|
279
|
+
return SlugGeneratorProcessor.generate_batch(
|
|
280
|
+
input_text,
|
|
281
|
+
settings.get("separator", "-"),
|
|
282
|
+
settings.get("lowercase", True),
|
|
283
|
+
settings.get("transliterate", True),
|
|
284
|
+
settings.get("max_length", 0),
|
|
285
|
+
settings.get("remove_stopwords", False)
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
def create_ui(self,
|
|
289
|
+
parent,
|
|
290
|
+
settings: Dict[str, Any],
|
|
291
|
+
on_setting_change_callback: Optional[Callable] = None,
|
|
292
|
+
apply_tool_callback: Optional[Callable] = None):
|
|
293
|
+
"""Create minimal UI - full widget used separately."""
|
|
294
|
+
self._settings = settings.copy()
|
|
295
|
+
self._on_setting_change = on_setting_change_callback
|
|
296
|
+
self._apply_callback = apply_tool_callback
|
|
297
|
+
return None
|
|
298
|
+
|
|
299
|
+
@classmethod
|
|
300
|
+
def get_default_settings(cls) -> Dict[str, Any]:
|
|
301
|
+
"""Return default settings."""
|
|
302
|
+
return {
|
|
303
|
+
"separator": "-",
|
|
304
|
+
"lowercase": True,
|
|
305
|
+
"transliterate": True,
|
|
306
|
+
"max_length": 0,
|
|
307
|
+
"remove_stopwords": False
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
except ImportError:
|
|
311
311
|
pass
|