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.
Files changed (213) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +105 -680
  3. package/bin/pomera-ai-commander.js +62 -62
  4. package/core/__init__.py +65 -65
  5. package/core/app_context.py +482 -482
  6. package/core/async_text_processor.py +421 -421
  7. package/core/backup_manager.py +655 -655
  8. package/core/backup_recovery_manager.py +1199 -1033
  9. package/core/content_hash_cache.py +508 -508
  10. package/core/context_menu.py +313 -313
  11. package/core/data_directory.py +549 -0
  12. package/core/data_validator.py +1066 -1066
  13. package/core/database_connection_manager.py +744 -744
  14. package/core/database_curl_settings_manager.py +608 -608
  15. package/core/database_promera_ai_settings_manager.py +446 -446
  16. package/core/database_schema.py +411 -411
  17. package/core/database_schema_manager.py +395 -395
  18. package/core/database_settings_manager.py +1507 -1507
  19. package/core/database_settings_manager_interface.py +456 -456
  20. package/core/dialog_manager.py +734 -734
  21. package/core/diff_utils.py +239 -0
  22. package/core/efficient_line_numbers.py +540 -510
  23. package/core/error_handler.py +746 -746
  24. package/core/error_service.py +431 -431
  25. package/core/event_consolidator.py +511 -511
  26. package/core/mcp/__init__.py +43 -43
  27. package/core/mcp/find_replace_diff.py +334 -0
  28. package/core/mcp/protocol.py +288 -288
  29. package/core/mcp/schema.py +251 -251
  30. package/core/mcp/server_stdio.py +299 -299
  31. package/core/mcp/tool_registry.py +2699 -2345
  32. package/core/memento.py +275 -0
  33. package/core/memory_efficient_text_widget.py +711 -711
  34. package/core/migration_manager.py +914 -914
  35. package/core/migration_test_suite.py +1085 -1085
  36. package/core/migration_validator.py +1143 -1143
  37. package/core/optimized_find_replace.py +714 -714
  38. package/core/optimized_pattern_engine.py +424 -424
  39. package/core/optimized_search_highlighter.py +552 -552
  40. package/core/performance_monitor.py +674 -674
  41. package/core/persistence_manager.py +712 -712
  42. package/core/progressive_stats_calculator.py +632 -632
  43. package/core/regex_pattern_cache.py +529 -529
  44. package/core/regex_pattern_library.py +350 -350
  45. package/core/search_operation_manager.py +434 -434
  46. package/core/settings_defaults_registry.py +1087 -1087
  47. package/core/settings_integrity_validator.py +1111 -1111
  48. package/core/settings_serializer.py +557 -557
  49. package/core/settings_validator.py +1823 -1823
  50. package/core/smart_stats_calculator.py +709 -709
  51. package/core/statistics_update_manager.py +619 -619
  52. package/core/stats_config_manager.py +858 -858
  53. package/core/streaming_text_handler.py +723 -723
  54. package/core/task_scheduler.py +596 -596
  55. package/core/update_pattern_library.py +168 -168
  56. package/core/visibility_monitor.py +596 -596
  57. package/core/widget_cache.py +498 -498
  58. package/mcp.json +51 -61
  59. package/migrate_data.py +127 -0
  60. package/package.json +64 -57
  61. package/pomera.py +7883 -7482
  62. package/pomera_mcp_server.py +183 -144
  63. package/requirements.txt +33 -0
  64. package/scripts/Dockerfile.alpine +43 -0
  65. package/scripts/Dockerfile.gui-test +54 -0
  66. package/scripts/Dockerfile.linux +43 -0
  67. package/scripts/Dockerfile.test-linux +80 -0
  68. package/scripts/Dockerfile.ubuntu +39 -0
  69. package/scripts/README.md +53 -0
  70. package/scripts/build-all.bat +113 -0
  71. package/scripts/build-docker.bat +53 -0
  72. package/scripts/build-docker.sh +55 -0
  73. package/scripts/build-optimized.bat +101 -0
  74. package/scripts/build.sh +78 -0
  75. package/scripts/docker-compose.test.yml +27 -0
  76. package/scripts/docker-compose.yml +32 -0
  77. package/scripts/postinstall.js +62 -0
  78. package/scripts/requirements-minimal.txt +33 -0
  79. package/scripts/test-linux-simple.bat +28 -0
  80. package/scripts/validate-release-workflow.py +450 -0
  81. package/tools/__init__.py +4 -4
  82. package/tools/ai_tools.py +2891 -2891
  83. package/tools/ascii_art_generator.py +352 -352
  84. package/tools/base64_tools.py +183 -183
  85. package/tools/base_tool.py +511 -511
  86. package/tools/case_tool.py +308 -308
  87. package/tools/column_tools.py +395 -395
  88. package/tools/cron_tool.py +884 -884
  89. package/tools/curl_history.py +600 -600
  90. package/tools/curl_processor.py +1207 -1207
  91. package/tools/curl_settings.py +502 -502
  92. package/tools/curl_tool.py +5467 -5467
  93. package/tools/diff_viewer.py +1817 -1072
  94. package/tools/email_extraction_tool.py +248 -248
  95. package/tools/email_header_analyzer.py +425 -425
  96. package/tools/extraction_tools.py +250 -250
  97. package/tools/find_replace.py +2289 -1750
  98. package/tools/folder_file_reporter.py +1463 -1463
  99. package/tools/folder_file_reporter_adapter.py +480 -480
  100. package/tools/generator_tools.py +1216 -1216
  101. package/tools/hash_generator.py +255 -255
  102. package/tools/html_tool.py +656 -656
  103. package/tools/jsonxml_tool.py +729 -729
  104. package/tools/line_tools.py +419 -419
  105. package/tools/markdown_tools.py +561 -561
  106. package/tools/mcp_widget.py +1417 -1417
  107. package/tools/notes_widget.py +978 -973
  108. package/tools/number_base_converter.py +372 -372
  109. package/tools/regex_extractor.py +571 -571
  110. package/tools/slug_generator.py +310 -310
  111. package/tools/sorter_tools.py +458 -458
  112. package/tools/string_escape_tool.py +392 -392
  113. package/tools/text_statistics_tool.py +365 -365
  114. package/tools/text_wrapper.py +430 -430
  115. package/tools/timestamp_converter.py +421 -421
  116. package/tools/tool_loader.py +710 -710
  117. package/tools/translator_tools.py +522 -522
  118. package/tools/url_link_extractor.py +261 -261
  119. package/tools/url_parser.py +204 -204
  120. package/tools/whitespace_tools.py +355 -355
  121. package/tools/word_frequency_counter.py +146 -146
  122. package/core/__pycache__/__init__.cpython-313.pyc +0 -0
  123. package/core/__pycache__/app_context.cpython-313.pyc +0 -0
  124. package/core/__pycache__/async_text_processor.cpython-313.pyc +0 -0
  125. package/core/__pycache__/backup_manager.cpython-313.pyc +0 -0
  126. package/core/__pycache__/backup_recovery_manager.cpython-313.pyc +0 -0
  127. package/core/__pycache__/content_hash_cache.cpython-313.pyc +0 -0
  128. package/core/__pycache__/context_menu.cpython-313.pyc +0 -0
  129. package/core/__pycache__/data_validator.cpython-313.pyc +0 -0
  130. package/core/__pycache__/database_connection_manager.cpython-313.pyc +0 -0
  131. package/core/__pycache__/database_curl_settings_manager.cpython-313.pyc +0 -0
  132. package/core/__pycache__/database_promera_ai_settings_manager.cpython-313.pyc +0 -0
  133. package/core/__pycache__/database_schema.cpython-313.pyc +0 -0
  134. package/core/__pycache__/database_schema_manager.cpython-313.pyc +0 -0
  135. package/core/__pycache__/database_settings_manager.cpython-313.pyc +0 -0
  136. package/core/__pycache__/database_settings_manager_interface.cpython-313.pyc +0 -0
  137. package/core/__pycache__/dialog_manager.cpython-313.pyc +0 -0
  138. package/core/__pycache__/efficient_line_numbers.cpython-313.pyc +0 -0
  139. package/core/__pycache__/error_handler.cpython-313.pyc +0 -0
  140. package/core/__pycache__/error_service.cpython-313.pyc +0 -0
  141. package/core/__pycache__/event_consolidator.cpython-313.pyc +0 -0
  142. package/core/__pycache__/memory_efficient_text_widget.cpython-313.pyc +0 -0
  143. package/core/__pycache__/migration_manager.cpython-313.pyc +0 -0
  144. package/core/__pycache__/migration_test_suite.cpython-313.pyc +0 -0
  145. package/core/__pycache__/migration_validator.cpython-313.pyc +0 -0
  146. package/core/__pycache__/optimized_find_replace.cpython-313.pyc +0 -0
  147. package/core/__pycache__/optimized_pattern_engine.cpython-313.pyc +0 -0
  148. package/core/__pycache__/optimized_search_highlighter.cpython-313.pyc +0 -0
  149. package/core/__pycache__/performance_monitor.cpython-313.pyc +0 -0
  150. package/core/__pycache__/persistence_manager.cpython-313.pyc +0 -0
  151. package/core/__pycache__/progressive_stats_calculator.cpython-313.pyc +0 -0
  152. package/core/__pycache__/regex_pattern_cache.cpython-313.pyc +0 -0
  153. package/core/__pycache__/regex_pattern_library.cpython-313.pyc +0 -0
  154. package/core/__pycache__/search_operation_manager.cpython-313.pyc +0 -0
  155. package/core/__pycache__/settings_defaults_registry.cpython-313.pyc +0 -0
  156. package/core/__pycache__/settings_integrity_validator.cpython-313.pyc +0 -0
  157. package/core/__pycache__/settings_serializer.cpython-313.pyc +0 -0
  158. package/core/__pycache__/settings_validator.cpython-313.pyc +0 -0
  159. package/core/__pycache__/smart_stats_calculator.cpython-313.pyc +0 -0
  160. package/core/__pycache__/statistics_update_manager.cpython-313.pyc +0 -0
  161. package/core/__pycache__/stats_config_manager.cpython-313.pyc +0 -0
  162. package/core/__pycache__/streaming_text_handler.cpython-313.pyc +0 -0
  163. package/core/__pycache__/task_scheduler.cpython-313.pyc +0 -0
  164. package/core/__pycache__/visibility_monitor.cpython-313.pyc +0 -0
  165. package/core/__pycache__/widget_cache.cpython-313.pyc +0 -0
  166. package/core/mcp/__pycache__/__init__.cpython-313.pyc +0 -0
  167. package/core/mcp/__pycache__/protocol.cpython-313.pyc +0 -0
  168. package/core/mcp/__pycache__/schema.cpython-313.pyc +0 -0
  169. package/core/mcp/__pycache__/server_stdio.cpython-313.pyc +0 -0
  170. package/core/mcp/__pycache__/tool_registry.cpython-313.pyc +0 -0
  171. package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  172. package/tools/__pycache__/ai_tools.cpython-313.pyc +0 -0
  173. package/tools/__pycache__/ascii_art_generator.cpython-313.pyc +0 -0
  174. package/tools/__pycache__/base64_tools.cpython-313.pyc +0 -0
  175. package/tools/__pycache__/base_tool.cpython-313.pyc +0 -0
  176. package/tools/__pycache__/case_tool.cpython-313.pyc +0 -0
  177. package/tools/__pycache__/column_tools.cpython-313.pyc +0 -0
  178. package/tools/__pycache__/cron_tool.cpython-313.pyc +0 -0
  179. package/tools/__pycache__/curl_history.cpython-313.pyc +0 -0
  180. package/tools/__pycache__/curl_processor.cpython-313.pyc +0 -0
  181. package/tools/__pycache__/curl_settings.cpython-313.pyc +0 -0
  182. package/tools/__pycache__/curl_tool.cpython-313.pyc +0 -0
  183. package/tools/__pycache__/diff_viewer.cpython-313.pyc +0 -0
  184. package/tools/__pycache__/email_extraction_tool.cpython-313.pyc +0 -0
  185. package/tools/__pycache__/email_header_analyzer.cpython-313.pyc +0 -0
  186. package/tools/__pycache__/extraction_tools.cpython-313.pyc +0 -0
  187. package/tools/__pycache__/find_replace.cpython-313.pyc +0 -0
  188. package/tools/__pycache__/folder_file_reporter.cpython-313.pyc +0 -0
  189. package/tools/__pycache__/folder_file_reporter_adapter.cpython-313.pyc +0 -0
  190. package/tools/__pycache__/generator_tools.cpython-313.pyc +0 -0
  191. package/tools/__pycache__/hash_generator.cpython-313.pyc +0 -0
  192. package/tools/__pycache__/html_tool.cpython-313.pyc +0 -0
  193. package/tools/__pycache__/huggingface_helper.cpython-313.pyc +0 -0
  194. package/tools/__pycache__/jsonxml_tool.cpython-313.pyc +0 -0
  195. package/tools/__pycache__/line_tools.cpython-313.pyc +0 -0
  196. package/tools/__pycache__/list_comparator.cpython-313.pyc +0 -0
  197. package/tools/__pycache__/markdown_tools.cpython-313.pyc +0 -0
  198. package/tools/__pycache__/mcp_widget.cpython-313.pyc +0 -0
  199. package/tools/__pycache__/notes_widget.cpython-313.pyc +0 -0
  200. package/tools/__pycache__/number_base_converter.cpython-313.pyc +0 -0
  201. package/tools/__pycache__/regex_extractor.cpython-313.pyc +0 -0
  202. package/tools/__pycache__/slug_generator.cpython-313.pyc +0 -0
  203. package/tools/__pycache__/sorter_tools.cpython-313.pyc +0 -0
  204. package/tools/__pycache__/string_escape_tool.cpython-313.pyc +0 -0
  205. package/tools/__pycache__/text_statistics_tool.cpython-313.pyc +0 -0
  206. package/tools/__pycache__/text_wrapper.cpython-313.pyc +0 -0
  207. package/tools/__pycache__/timestamp_converter.cpython-313.pyc +0 -0
  208. package/tools/__pycache__/tool_loader.cpython-313.pyc +0 -0
  209. package/tools/__pycache__/translator_tools.cpython-313.pyc +0 -0
  210. package/tools/__pycache__/url_link_extractor.cpython-313.pyc +0 -0
  211. package/tools/__pycache__/url_parser.cpython-313.pyc +0 -0
  212. package/tools/__pycache__/whitespace_tools.cpython-313.pyc +0 -0
  213. package/tools/__pycache__/word_frequency_counter.cpython-313.pyc +0 -0
@@ -1,1217 +1,1217 @@
1
- """
2
- Generator Tools Module for Promera AI Commander
3
-
4
- This module provides various text generation tools including:
5
- - Strong Password Generator
6
- - Repeating Text Generator
7
- - Lorem Ipsum Generator
8
- - UUID/GUID Generator
9
-
10
- Author: Promera AI Commander
11
- """
12
-
13
- import tkinter as tk
14
- from tkinter import ttk
15
- import string
16
- import random
17
- import json
18
- import uuid
19
- import base64
20
- import hashlib
21
-
22
-
23
- class GeneratorTools:
24
- """A class containing various text generation tools."""
25
-
26
- def __init__(self):
27
- """Initialize the GeneratorTools class."""
28
- self.tools = {
29
- "Strong Password Generator": self.strong_password,
30
- "Repeating Text Generator": self.repeating_text,
31
- "Lorem Ipsum Generator": self.lorem_ipsum,
32
- "UUID/GUID Generator": self.uuid_generator,
33
- "Random Email Generator": self.random_email_generator
34
- }
35
-
36
- def get_available_tools(self):
37
- """Returns a list of available generator tools."""
38
- return list(self.tools.keys())
39
-
40
- def get_default_settings(self):
41
- """Returns default settings for all generator tools."""
42
- return {
43
- "Strong Password Generator": {"length": 20, "numbers": "", "symbols": "", "letters_percent": 70, "numbers_percent": 20, "symbols_percent": 10},
44
- "Repeating Text Generator": {"times": 5, "separator": "+"},
45
- "Lorem Ipsum Generator": {"count": 5, "type": "paragraphs", "format": "plain", "ordered": False},
46
- "UUID/GUID Generator": {"version": 4, "format": "standard", "case": "lowercase", "count": 1, "namespace": "dns", "name": ""},
47
- "Random Email Generator": {"count": 5, "separator_type": "list", "separator": ",", "domain_type": "random", "domain": "example.com"},
48
- "ASCII Art Generator": {"font": "standard", "width": 80},
49
- "Hash Generator": {"algorithms": ["md5", "sha256"], "uppercase": False},
50
- "Slug Generator": {"separator": "-", "lowercase": True, "transliterate": True, "max_length": 0, "remove_stopwords": False}
51
- }
52
-
53
- def process_text(self, input_text, tool_name, settings):
54
- """Process text using the specified generator tool."""
55
- if tool_name not in self.tools:
56
- return f"Error: Unknown tool '{tool_name}'"
57
-
58
- return self.tools[tool_name](input_text, settings)
59
-
60
- @staticmethod
61
- def strong_password(input_text, settings):
62
- """Generates a strong, random password with specified character distribution."""
63
- length = settings.get("length", 20)
64
- numbers = settings.get("numbers", "")
65
- symbols = settings.get("symbols", "")
66
- letters_percent = settings.get("letters_percent", 70)
67
- numbers_percent = settings.get("numbers_percent", 20)
68
- symbols_percent = settings.get("symbols_percent", 10)
69
-
70
- if not isinstance(length, int) or length <= 0:
71
- return "Error: Password length must be a positive number."
72
-
73
- # Validate percentages
74
- total_percent = letters_percent + numbers_percent + symbols_percent
75
- if total_percent != 100:
76
- return "Error: Percentages must add up to 100%."
77
-
78
- # Character sets
79
- letters = string.ascii_letters
80
- digits = string.digits
81
- special_chars = string.punctuation
82
-
83
- # Calculate character counts with ±5% approximation
84
- def calculate_count(percent, total_length):
85
- base_count = int((percent / 100) * total_length)
86
- # Allow ±5% variation (rounded to nearest integer)
87
- variation = max(1, int(0.05 * total_length))
88
- min_count = max(0, base_count - variation)
89
- max_count = min(total_length, base_count + variation)
90
- return random.randint(min_count, max_count)
91
-
92
- letters_count = calculate_count(letters_percent, length)
93
- numbers_count = calculate_count(numbers_percent, length)
94
- symbols_count = length - letters_count - numbers_count
95
-
96
- # Ensure we don't exceed total length
97
- if symbols_count < 0:
98
- # Adjust if we went over
99
- excess = abs(symbols_count)
100
- if letters_count >= excess:
101
- letters_count -= excess
102
- symbols_count = 0
103
- else:
104
- numbers_count -= (excess - letters_count)
105
- letters_count = 0
106
- symbols_count = 0
107
-
108
- # Generate password parts
109
- password_chars = []
110
-
111
- # Add letters
112
- password_chars.extend(random.choices(letters, k=letters_count))
113
-
114
- # Add numbers
115
- password_chars.extend(random.choices(digits, k=numbers_count))
116
-
117
- # Add symbols
118
- password_chars.extend(random.choices(special_chars, k=symbols_count))
119
-
120
- # Ensure we have the exact length
121
- while len(password_chars) < length:
122
- password_chars.append(random.choice(letters + digits + special_chars))
123
-
124
- # Shuffle the password
125
- random.shuffle(password_chars)
126
- password = ''.join(password_chars)
127
-
128
- # Ensure included numbers and symbols are present
129
- must_include = numbers + symbols
130
- if must_include and len(must_include) <= length:
131
- password_list = list(password)
132
- for i, char in enumerate(must_include):
133
- if i < len(password_list):
134
- password_list[i] = char
135
- random.shuffle(password_list)
136
- password = "".join(password_list)
137
-
138
- return password
139
-
140
- @staticmethod
141
- def repeating_text(input_text, settings):
142
- """Repeats the input text a specified number of times."""
143
- times = settings.get("times", 5)
144
- separator = settings.get("separator", "+")
145
-
146
- if not isinstance(times, int) or times < 0:
147
- return "Error: 'Times' must be a non-negative number."
148
- return separator.join([input_text] * times)
149
-
150
- @staticmethod
151
- def lorem_ipsum(input_text, settings):
152
- """Generates Lorem Ipsum text in various formats."""
153
- count = settings.get("count", 5)
154
- text_type = settings.get("type", "paragraphs")
155
- format_type = settings.get("format", "plain")
156
- ordered = settings.get("ordered", False)
157
-
158
- # Validation
159
- if not isinstance(count, int) or count <= 0:
160
- return "Error: Count must be a positive integer."
161
-
162
- if format_type not in ['plain', 'html', 'markdown', 'json']:
163
- return "Error: Format must be one of: 'plain', 'html', 'markdown', 'json'."
164
-
165
- # Lorem ipsum word bank
166
- lorem_words = [
167
- "lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit",
168
- "sed", "do", "eiusmod", "tempor", "incididunt", "ut", "labore", "et", "dolore",
169
- "magna", "aliqua", "enim", "ad", "minim", "veniam", "quis", "nostrud",
170
- "exercitation", "ullamco", "laboris", "nisi", "aliquip", "ex", "ea", "commodo",
171
- "consequat", "duis", "aute", "irure", "in", "reprehenderit", "voluptate",
172
- "velit", "esse", "cillum", "fugiat", "nulla", "pariatur", "excepteur", "sint",
173
- "occaecat", "cupidatat", "non", "proident", "sunt", "culpa", "qui", "officia",
174
- "deserunt", "mollit", "anim", "id", "est", "laborum", "at", "vero", "eos",
175
- "accusamus", "accusantium", "doloremque", "laudantium", "totam", "rem",
176
- "aperiam", "eaque", "ipsa", "quae", "ab", "illo", "inventore", "veritatis"
177
- ]
178
-
179
- def generate_sentence():
180
- """Generate a single sentence."""
181
- length = random.randint(8, 20)
182
- words = random.choices(lorem_words, k=length)
183
- words[0] = words[0].capitalize()
184
- return " ".join(words) + "."
185
-
186
- def generate_paragraph():
187
- """Generate a single paragraph."""
188
- sentences = [generate_sentence() for _ in range(random.randint(3, 8))]
189
- return " ".join(sentences)
190
-
191
- # Generate content based on type
192
- if text_type == "words":
193
- content = random.choices(lorem_words, k=count)
194
- elif text_type == "sentences":
195
- content = [generate_sentence() for _ in range(count)]
196
- elif text_type == "paragraphs":
197
- content = [generate_paragraph() for _ in range(count)]
198
- elif text_type == "bytes":
199
- # Generate text up to specified byte count
200
- text = ""
201
- while len(text.encode('utf-8')) < count:
202
- text += generate_sentence() + " "
203
- content = [text[:count]]
204
- else:
205
- return "Error: Invalid text type."
206
-
207
- # Format output
208
- if format_type == "plain":
209
- if text_type == "words":
210
- return " ".join(content)
211
- elif text_type == "sentences":
212
- return " ".join(content)
213
- elif text_type == "paragraphs":
214
- return "\n\n".join(content)
215
- else: # bytes
216
- return content[0]
217
-
218
- elif format_type == "html":
219
- if text_type == "words":
220
- return "<span>" + " ".join(content) + "</span>"
221
- elif text_type == "sentences":
222
- if ordered:
223
- items = "".join([f"<li>{sentence}</li>" for sentence in content])
224
- return f"<ol>{items}</ol>"
225
- else:
226
- items = "".join([f"<li>{sentence}</li>" for sentence in content])
227
- return f"<ul>{items}</ul>"
228
- elif text_type == "paragraphs":
229
- paragraphs = "".join([f"<p>{para}</p>" for para in content])
230
- return paragraphs
231
- else: # bytes
232
- return f"<div>{content[0]}</div>"
233
-
234
- elif format_type == "markdown":
235
- if text_type == "words":
236
- return " ".join(content)
237
- elif text_type == "sentences":
238
- if ordered:
239
- items = "\n".join([f"{i+1}. {sentence}" for i, sentence in enumerate(content)])
240
- else:
241
- items = "\n".join([f"- {sentence}" for sentence in content])
242
- return items
243
- elif text_type == "paragraphs":
244
- return "\n\n".join(content)
245
- else: # bytes
246
- return content[0]
247
-
248
- elif format_type == "json":
249
- return json.dumps({
250
- "type": text_type,
251
- "count": len(content),
252
- "content": content
253
- }, indent=2)
254
-
255
- return "Error: Unknown format type."
256
-
257
- @staticmethod
258
- def uuid_generator(input_text, settings):
259
- """Generates UUID/GUID in various formats and versions."""
260
- version = settings.get("version", 4)
261
- format_type = settings.get("format", "standard")
262
- case = settings.get("case", "lowercase")
263
- count = settings.get("count", 1)
264
- namespace_name = settings.get("namespace", "dns")
265
- name = settings.get("name", "")
266
-
267
- # Validation
268
- if not isinstance(count, int) or count <= 0:
269
- return "Error: Count must be a positive integer."
270
-
271
- if version not in [1, 3, 4, 5]:
272
- return "Error: UUID version must be 1, 3, 4, or 5."
273
-
274
- if version in [3, 5] and not name:
275
- return "Error: Name is required for UUID versions 3 and 5."
276
-
277
- # Predefined namespaces
278
- namespaces = {
279
- "dns": uuid.NAMESPACE_DNS,
280
- "url": uuid.NAMESPACE_URL,
281
- "oid": uuid.NAMESPACE_OID,
282
- "x500": uuid.NAMESPACE_X500
283
- }
284
-
285
- def generate_single_uuid():
286
- """Generate a single UUID based on version."""
287
- if version == 1:
288
- return uuid.uuid1()
289
- elif version == 3:
290
- if namespace_name not in namespaces:
291
- return None
292
- return uuid.uuid3(namespaces[namespace_name], name)
293
- elif version == 4:
294
- return uuid.uuid4()
295
- elif version == 5:
296
- if namespace_name not in namespaces:
297
- return None
298
- return uuid.uuid5(namespaces[namespace_name], name)
299
-
300
- def format_uuid(uuid_obj):
301
- """Format UUID according to specified format and case."""
302
- if uuid_obj is None:
303
- return "Error: Invalid namespace."
304
-
305
- uuid_str = str(uuid_obj)
306
-
307
- # Apply case formatting
308
- if case == "uppercase":
309
- uuid_str = uuid_str.upper()
310
- elif case == "lowercase":
311
- uuid_str = uuid_str.lower()
312
-
313
- # Apply format
314
- if format_type == "standard":
315
- return uuid_str
316
- elif format_type == "hex":
317
- return uuid_str.replace("-", "")
318
- elif format_type == "microsoft":
319
- return "{" + uuid_str + "}"
320
- elif format_type == "urn":
321
- return "urn:uuid:" + uuid_str
322
- elif format_type == "base64":
323
- return base64.b64encode(uuid_obj.bytes).decode('ascii')
324
- elif format_type == "c_array":
325
- bytes_list = list(uuid_obj.bytes)
326
- hex_values = [f"0x{b:02x}" for b in bytes_list]
327
- # Format as C array with proper line breaks
328
- formatted = "{ " + ", ".join(hex_values[:8]) + ",\n " + ", ".join(hex_values[8:]) + " }"
329
- return formatted
330
- elif format_type == "nil":
331
- return "00000000-0000-0000-0000-000000000000"
332
- else:
333
- return uuid_str
334
-
335
- # Generate UUIDs
336
- results = []
337
- for _ in range(count):
338
- if format_type == "nil":
339
- # Special case for nil UUID
340
- nil_uuid = uuid.UUID('00000000-0000-0000-0000-000000000000')
341
- results.append(format_uuid(nil_uuid))
342
- else:
343
- uuid_obj = generate_single_uuid()
344
- if uuid_obj is None:
345
- return "Error: Invalid namespace for name-based UUID."
346
- results.append(format_uuid(uuid_obj))
347
-
348
- # Return results
349
- if count == 1:
350
- return results[0]
351
- else:
352
- return "\n".join(results)
353
-
354
- @staticmethod
355
- def random_email_generator(input_text, settings):
356
- """Generates random email addresses."""
357
- count = settings.get("count", 5)
358
- separator_type = settings.get("separator_type", "list")
359
- separator = settings.get("separator", ",")
360
- domain_type = settings.get("domain_type", "random")
361
- domain = settings.get("domain", "example.com")
362
-
363
- # Validation
364
- if not isinstance(count, int) or count <= 0:
365
- return "Error: Count must be a positive integer."
366
-
367
- # Common first names and last names for generating realistic emails
368
- first_names = [
369
- "john", "jane", "mike", "sarah", "david", "lisa", "chris", "anna", "james", "mary",
370
- "robert", "jennifer", "michael", "linda", "william", "elizabeth", "richard", "barbara",
371
- "joseph", "susan", "thomas", "jessica", "charles", "karen", "daniel", "nancy",
372
- "matthew", "betty", "anthony", "helen", "mark", "sandra", "donald", "donna",
373
- "steven", "carol", "paul", "ruth", "andrew", "sharon", "joshua", "michelle",
374
- "kenneth", "laura", "kevin", "sarah", "brian", "kimberly", "george", "deborah",
375
- "edward", "dorothy", "ronald", "lisa", "timothy", "nancy", "jason", "karen"
376
- ]
377
-
378
- last_names = [
379
- "smith", "johnson", "williams", "brown", "jones", "garcia", "miller", "davis",
380
- "rodriguez", "martinez", "hernandez", "lopez", "gonzalez", "wilson", "anderson",
381
- "thomas", "taylor", "moore", "jackson", "martin", "lee", "perez", "thompson",
382
- "white", "harris", "sanchez", "clark", "ramirez", "lewis", "robinson", "walker",
383
- "young", "allen", "king", "wright", "scott", "torres", "nguyen", "hill",
384
- "flores", "green", "adams", "nelson", "baker", "hall", "rivera", "campbell",
385
- "mitchell", "carter", "roberts", "gomez", "phillips", "evans", "turner", "diaz"
386
- ]
387
-
388
- # Common domain names for random domain generation
389
- random_domains = [
390
- "gmail.com", "yahoo.com", "hotmail.com", "outlook.com", "aol.com",
391
- "icloud.com", "protonmail.com", "mail.com", "zoho.com", "fastmail.com",
392
- "example.com", "test.com", "demo.org", "sample.net", "placeholder.io"
393
- ]
394
-
395
- def generate_single_email():
396
- """Generate a single random email address."""
397
- # Generate username part
398
- first = random.choice(first_names)
399
- last = random.choice(last_names)
400
-
401
- # Various username patterns
402
- patterns = [
403
- f"{first}.{last}",
404
- f"{first}{last}",
405
- f"{first}_{last}",
406
- f"{first}{random.randint(1, 999)}",
407
- f"{first}.{last}{random.randint(1, 99)}",
408
- f"{first[0]}{last}",
409
- f"{first}{last[0]}",
410
- f"{first}.{last[0]}",
411
- f"{first[0]}.{last}"
412
- ]
413
-
414
- username = random.choice(patterns)
415
-
416
- # Generate domain part
417
- if domain_type == "random":
418
- email_domain = random.choice(random_domains)
419
- else:
420
- email_domain = domain
421
-
422
- return f"{username}@{email_domain}"
423
-
424
- # Generate emails
425
- emails = []
426
- for _ in range(count):
427
- emails.append(generate_single_email())
428
-
429
- # Format output based on separator type
430
- if separator_type == "list":
431
- return "\n".join(emails)
432
- else:
433
- return separator.join(emails)
434
-
435
-
436
- class GeneratorToolsWidget:
437
- """Widget for the Generator Tools tabbed interface."""
438
-
439
- def __init__(self, generator_tools):
440
- """Initialize the GeneratorToolsWidget."""
441
- self.generator_tools = generator_tools
442
- self.main_app = None
443
-
444
- # Variables for Strong Password Generator
445
- self.pw_len_var = None
446
- self.pw_num_var = None
447
- self.pw_sym_var = None
448
- self.pw_letters_percent_var = None
449
- self.pw_numbers_percent_var = None
450
- self.pw_symbols_percent_var = None
451
-
452
- # Variables for Repeating Text Generator
453
- self.repeat_times_var = None
454
- self.repeat_sep_var = None
455
-
456
- # Variables for Lorem Ipsum Generator
457
- self.lorem_count_var = None
458
- self.lorem_type_var = None
459
- self.lorem_format_var = None
460
- self.lorem_ordered_var = None
461
-
462
- # Variables for UUID/GUID Generator
463
- self.uuid_version_var = None
464
- self.uuid_format_var = None
465
- self.uuid_case_var = None
466
- self.uuid_count_var = None
467
- self.uuid_namespace_var = None
468
- self.uuid_name_var = None
469
-
470
- # Variables for Random Email Generator
471
- self.email_count_var = None
472
- self.email_separator_type_var = None
473
- self.email_separator_var = None
474
- self.email_domain_type_var = None
475
- self.email_domain_var = None
476
-
477
- def create_widget(self, parent, main_app):
478
- """Create and return the main widget."""
479
- self.main_app = main_app
480
-
481
- # Create main frame
482
- main_frame = ttk.Frame(parent)
483
-
484
- # Create notebook for tabs
485
- self.notebook = ttk.Notebook(main_frame)
486
- self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
487
-
488
- # Create tabs
489
- self.create_password_generator_tab()
490
- self.create_repeating_text_tab()
491
- self.create_lorem_ipsum_tab()
492
- self.create_uuid_generator_tab()
493
- self.create_email_generator_tab()
494
- self.create_ascii_art_generator_tab()
495
- self.create_hash_generator_tab()
496
- self.create_slug_generator_tab()
497
-
498
- return main_frame
499
-
500
- def create_password_generator_tab(self):
501
- """Create the Strong Password Generator tab."""
502
- tab_frame = ttk.Frame(self.notebook)
503
- self.notebook.add(tab_frame, text="Strong Password Generator")
504
-
505
- # Get current settings
506
- settings = self.main_app.settings["tool_settings"].get("Generator Tools", {}).get("Strong Password Generator", {})
507
-
508
- # Length setting
509
- length_frame = ttk.Frame(tab_frame)
510
- length_frame.pack(fill=tk.X, padx=10, pady=5)
511
-
512
- ttk.Label(length_frame, text="Password Length:").pack(side=tk.LEFT)
513
- self.pw_len_var = tk.StringVar(value=str(settings.get("length", 20)))
514
- length_entry = ttk.Entry(length_frame, textvariable=self.pw_len_var, width=10)
515
- length_entry.pack(side=tk.LEFT, padx=(5, 0))
516
-
517
- # Character distribution sliders
518
- distribution_frame = ttk.LabelFrame(tab_frame, text="Character Distribution (%)")
519
- distribution_frame.pack(fill=tk.X, padx=10, pady=5)
520
-
521
- # Letters percentage slider
522
- letters_frame = ttk.Frame(distribution_frame)
523
- letters_frame.pack(fill=tk.X, padx=5, pady=2)
524
-
525
- ttk.Label(letters_frame, text="Letters:", width=12).pack(side=tk.LEFT)
526
- self.pw_letters_percent_var = tk.IntVar(value=settings.get("letters_percent", 70))
527
- letters_slider = ttk.Scale(letters_frame, from_=0, to=100, orient=tk.HORIZONTAL,
528
- variable=self.pw_letters_percent_var, command=self.update_percentages)
529
- letters_slider.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(5, 5))
530
- letters_label = ttk.Label(letters_frame, text="70%", width=5)
531
- letters_label.pack(side=tk.RIGHT)
532
- self.pw_letters_label = letters_label
533
-
534
- # Numbers percentage slider
535
- numbers_frame = ttk.Frame(distribution_frame)
536
- numbers_frame.pack(fill=tk.X, padx=5, pady=2)
537
-
538
- ttk.Label(numbers_frame, text="Numbers:", width=12).pack(side=tk.LEFT)
539
- self.pw_numbers_percent_var = tk.IntVar(value=settings.get("numbers_percent", 20))
540
- numbers_slider = ttk.Scale(numbers_frame, from_=0, to=100, orient=tk.HORIZONTAL,
541
- variable=self.pw_numbers_percent_var, command=self.update_percentages)
542
- numbers_slider.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(5, 5))
543
- numbers_label = ttk.Label(numbers_frame, text="20%", width=5)
544
- numbers_label.pack(side=tk.RIGHT)
545
- self.pw_numbers_label = numbers_label
546
-
547
- # Symbols percentage slider
548
- symbols_frame = ttk.Frame(distribution_frame)
549
- symbols_frame.pack(fill=tk.X, padx=5, pady=2)
550
-
551
- ttk.Label(symbols_frame, text="Symbols:", width=12).pack(side=tk.LEFT)
552
- self.pw_symbols_percent_var = tk.IntVar(value=settings.get("symbols_percent", 10))
553
- symbols_slider = ttk.Scale(symbols_frame, from_=0, to=100, orient=tk.HORIZONTAL,
554
- variable=self.pw_symbols_percent_var, command=self.update_percentages)
555
- symbols_slider.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(5, 5))
556
- symbols_label = ttk.Label(symbols_frame, text="10%", width=5)
557
- symbols_label.pack(side=tk.RIGHT)
558
- self.pw_symbols_label = symbols_label
559
-
560
- # Total percentage display
561
- total_frame = ttk.Frame(distribution_frame)
562
- total_frame.pack(fill=tk.X, padx=5, pady=2)
563
-
564
- ttk.Label(total_frame, text="Total:", width=12, font=('TkDefaultFont', 9, 'bold')).pack(side=tk.LEFT)
565
- self.pw_total_label = ttk.Label(total_frame, text="100%", width=5, font=('TkDefaultFont', 9, 'bold'))
566
- self.pw_total_label.pack(side=tk.RIGHT)
567
-
568
- # Numbers setting
569
- numbers_include_frame = ttk.Frame(tab_frame)
570
- numbers_include_frame.pack(fill=tk.X, padx=10, pady=5)
571
-
572
- ttk.Label(numbers_include_frame, text="Must Include Numbers:").pack(side=tk.LEFT)
573
- self.pw_num_var = tk.StringVar(value=settings.get("numbers", ""))
574
- numbers_entry = ttk.Entry(numbers_include_frame, textvariable=self.pw_num_var, width=20)
575
- numbers_entry.pack(side=tk.LEFT, padx=(5, 0))
576
-
577
- # Symbols setting
578
- symbols_include_frame = ttk.Frame(tab_frame)
579
- symbols_include_frame.pack(fill=tk.X, padx=10, pady=5)
580
-
581
- ttk.Label(symbols_include_frame, text="Must Include Symbols:").pack(side=tk.LEFT)
582
- self.pw_sym_var = tk.StringVar(value=settings.get("symbols", ""))
583
- symbols_entry = ttk.Entry(symbols_include_frame, textvariable=self.pw_sym_var, width=20)
584
- symbols_entry.pack(side=tk.LEFT, padx=(5, 0))
585
-
586
- # Generate button
587
- button_frame = ttk.Frame(tab_frame)
588
- button_frame.pack(fill=tk.X, padx=10, pady=10)
589
-
590
- generate_btn = ttk.Button(button_frame, text="Generate Password",
591
- command=lambda: self.generate_password())
592
- generate_btn.pack(side=tk.LEFT)
593
-
594
- # Initialize percentage display
595
- self.update_percentages()
596
-
597
- def create_repeating_text_tab(self):
598
- """Create the Repeating Text Generator tab."""
599
- tab_frame = ttk.Frame(self.notebook)
600
- self.notebook.add(tab_frame, text="Repeating Text Generator")
601
-
602
- # Get current settings
603
- settings = self.main_app.settings["tool_settings"].get("Generator Tools", {}).get("Repeating Text Generator", {})
604
-
605
- # Times setting
606
- times_frame = ttk.Frame(tab_frame)
607
- times_frame.pack(fill=tk.X, padx=10, pady=5)
608
-
609
- ttk.Label(times_frame, text="Repeat Times:").pack(side=tk.LEFT)
610
- self.repeat_times_var = tk.StringVar(value=str(settings.get("times", 5)))
611
- times_entry = ttk.Entry(times_frame, textvariable=self.repeat_times_var, width=10)
612
- times_entry.pack(side=tk.LEFT, padx=(5, 0))
613
-
614
- # Separator setting
615
- separator_frame = ttk.Frame(tab_frame)
616
- separator_frame.pack(fill=tk.X, padx=10, pady=5)
617
-
618
- ttk.Label(separator_frame, text="Separator:").pack(side=tk.LEFT)
619
- self.repeat_sep_var = tk.StringVar(value=settings.get("separator", "+"))
620
- separator_entry = ttk.Entry(separator_frame, textvariable=self.repeat_sep_var, width=20)
621
- separator_entry.pack(side=tk.LEFT, padx=(5, 0))
622
-
623
- # Generate button
624
- button_frame = ttk.Frame(tab_frame)
625
- button_frame.pack(fill=tk.X, padx=10, pady=10)
626
-
627
- repeat_btn = ttk.Button(button_frame, text="Generate Repeated Text",
628
- command=lambda: self.generate_repeated_text())
629
- repeat_btn.pack(side=tk.LEFT)
630
-
631
- def create_lorem_ipsum_tab(self):
632
- """Create the Lorem Ipsum Generator tab."""
633
- tab_frame = ttk.Frame(self.notebook)
634
- self.notebook.add(tab_frame, text="Lorem Ipsum Generator")
635
-
636
- # Get current settings
637
- settings = self.main_app.settings["tool_settings"].get("Generator Tools", {}).get("Lorem Ipsum Generator", {})
638
-
639
- # Count setting
640
- count_frame = ttk.Frame(tab_frame)
641
- count_frame.pack(fill=tk.X, padx=10, pady=5)
642
-
643
- ttk.Label(count_frame, text="Count:").pack(side=tk.LEFT)
644
- self.lorem_count_var = tk.StringVar(value=str(settings.get("count", 5)))
645
- count_entry = ttk.Entry(count_frame, textvariable=self.lorem_count_var, width=10)
646
- count_entry.pack(side=tk.LEFT, padx=(5, 0))
647
-
648
- # Type selection (radio buttons)
649
- type_frame = ttk.LabelFrame(tab_frame, text="Type")
650
- type_frame.pack(fill=tk.X, padx=10, pady=5)
651
-
652
- self.lorem_type_var = tk.StringVar(value=settings.get("type", "paragraphs"))
653
-
654
- type_options = [
655
- ("Words", "words"),
656
- ("Sentences", "sentences"),
657
- ("Paragraphs", "paragraphs"),
658
- ("Bytes", "bytes")
659
- ]
660
-
661
- for text, value in type_options:
662
- ttk.Radiobutton(type_frame, text=text, variable=self.lorem_type_var,
663
- value=value).pack(side=tk.LEFT, padx=5)
664
-
665
- # Format selection (radio buttons)
666
- format_frame = ttk.LabelFrame(tab_frame, text="Format")
667
- format_frame.pack(fill=tk.X, padx=10, pady=5)
668
-
669
- self.lorem_format_var = tk.StringVar(value=settings.get("format", "plain"))
670
-
671
- format_options = [
672
- ("Plain", "plain"),
673
- ("HTML", "html"),
674
- ("Markdown", "markdown"),
675
- ("JSON", "json")
676
- ]
677
-
678
- for text, value in format_options:
679
- ttk.Radiobutton(format_frame, text=text, variable=self.lorem_format_var,
680
- value=value).pack(side=tk.LEFT, padx=5)
681
-
682
- # Ordered checkbox (for lists)
683
- ordered_frame = ttk.Frame(tab_frame)
684
- ordered_frame.pack(fill=tk.X, padx=10, pady=5)
685
-
686
- self.lorem_ordered_var = tk.BooleanVar(value=settings.get("ordered", False))
687
- ordered_check = ttk.Checkbutton(ordered_frame, text="Ordered (for lists)",
688
- variable=self.lorem_ordered_var)
689
- ordered_check.pack(side=tk.LEFT)
690
-
691
- # Generate button
692
- button_frame = ttk.Frame(tab_frame)
693
- button_frame.pack(fill=tk.X, padx=10, pady=10)
694
-
695
- lorem_btn = ttk.Button(button_frame, text="Generate",
696
- command=lambda: self.generate_lorem_ipsum())
697
- lorem_btn.pack(side=tk.LEFT)
698
-
699
- def create_uuid_generator_tab(self):
700
- """Create the UUID/GUID Generator tab."""
701
- tab_frame = ttk.Frame(self.notebook)
702
- self.notebook.add(tab_frame, text="UUID/GUID Generator")
703
-
704
- # Get current settings
705
- settings = self.main_app.settings["tool_settings"].get("Generator Tools", {}).get("UUID/GUID Generator", {})
706
-
707
- # Create top row frame for UUID Version and Name-based settings
708
- top_row_frame = ttk.Frame(tab_frame)
709
- top_row_frame.pack(fill=tk.X, padx=10, pady=5)
710
-
711
- # UUID Version selection (left side)
712
- version_frame = ttk.LabelFrame(top_row_frame, text="UUID Version")
713
- version_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5))
714
-
715
- self.uuid_version_var = tk.IntVar(value=settings.get("version", 4))
716
-
717
- version_options = [
718
- ("Version 1 (Time-based)", 1),
719
- ("Version 3 (MD5 Name-based)", 3),
720
- ("Version 4 (Random)", 4),
721
- ("Version 5 (SHA-1 Name-based)", 5)
722
- ]
723
-
724
- for text, value in version_options:
725
- ttk.Radiobutton(version_frame, text=text, variable=self.uuid_version_var,
726
- value=value, command=self.update_uuid_fields).pack(anchor=tk.W, padx=5, pady=2)
727
-
728
- # Name-based UUID settings (right side, for versions 3 and 5)
729
- self.uuid_namebased_frame = ttk.LabelFrame(top_row_frame, text="Name-based Settings")
730
- self.uuid_namebased_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0))
731
-
732
- # Namespace selection
733
- namespace_frame = ttk.Frame(self.uuid_namebased_frame)
734
- namespace_frame.pack(fill=tk.X, padx=5, pady=2)
735
-
736
- ttk.Label(namespace_frame, text="Namespace:").pack(side=tk.LEFT)
737
- self.uuid_namespace_var = tk.StringVar(value=settings.get("namespace", "dns"))
738
- namespace_combo = ttk.Combobox(namespace_frame, textvariable=self.uuid_namespace_var,
739
- values=["dns", "url", "oid", "x500"], state="readonly", width=15)
740
- namespace_combo.pack(side=tk.LEFT, padx=(5, 0))
741
-
742
- # Name input
743
- name_frame = ttk.Frame(self.uuid_namebased_frame)
744
- name_frame.pack(fill=tk.X, padx=5, pady=2)
745
-
746
- ttk.Label(name_frame, text="Name:").pack(side=tk.LEFT)
747
- self.uuid_name_var = tk.StringVar(value=settings.get("name", ""))
748
- name_entry = ttk.Entry(name_frame, textvariable=self.uuid_name_var, width=25)
749
- name_entry.pack(side=tk.LEFT, padx=(5, 0), fill=tk.X, expand=True)
750
-
751
- # Output Format selection
752
- format_frame = ttk.LabelFrame(tab_frame, text="Output Format")
753
- format_frame.pack(fill=tk.X, padx=10, pady=5)
754
-
755
- self.uuid_format_var = tk.StringVar(value=settings.get("format", "standard"))
756
-
757
- format_options = [
758
- ("Standard (8-4-4-4-12)", "standard"),
759
- ("Hex (32 chars)", "hex"),
760
- ("Microsoft GUID {}", "microsoft"),
761
- ("URN format", "urn"),
762
- ("Base64", "base64"),
763
- ("C Array", "c_array"),
764
- ("Nil UUID", "nil")
765
- ]
766
-
767
- # Create two columns for format options
768
- format_left = ttk.Frame(format_frame)
769
- format_left.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
770
- format_right = ttk.Frame(format_frame)
771
- format_right.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
772
-
773
- for i, (text, value) in enumerate(format_options):
774
- parent = format_left if i < 4 else format_right
775
- ttk.Radiobutton(parent, text=text, variable=self.uuid_format_var,
776
- value=value).pack(anchor=tk.W, padx=5, pady=1)
777
-
778
- # Case selection
779
- case_frame = ttk.LabelFrame(tab_frame, text="Case")
780
- case_frame.pack(fill=tk.X, padx=10, pady=5)
781
-
782
- self.uuid_case_var = tk.StringVar(value=settings.get("case", "lowercase"))
783
-
784
- case_options = [
785
- ("Lowercase", "lowercase"),
786
- ("Uppercase", "uppercase")
787
- ]
788
-
789
- for text, value in case_options:
790
- ttk.Radiobutton(case_frame, text=text, variable=self.uuid_case_var,
791
- value=value).pack(side=tk.LEFT, padx=5)
792
-
793
- # Count setting
794
- count_frame = ttk.Frame(tab_frame)
795
- count_frame.pack(fill=tk.X, padx=10, pady=5)
796
-
797
- ttk.Label(count_frame, text="Count:").pack(side=tk.LEFT)
798
- self.uuid_count_var = tk.StringVar(value=str(settings.get("count", 1)))
799
- count_entry = ttk.Entry(count_frame, textvariable=self.uuid_count_var, width=10)
800
- count_entry.pack(side=tk.LEFT, padx=(5, 0))
801
-
802
- # Generate button
803
- button_frame = ttk.Frame(tab_frame)
804
- button_frame.pack(fill=tk.X, padx=10, pady=10)
805
-
806
- uuid_btn = ttk.Button(button_frame, text="Generate",
807
- command=lambda: self.generate_uuid())
808
- uuid_btn.pack(side=tk.LEFT)
809
-
810
- # Initialize field visibility
811
- self.update_uuid_fields()
812
-
813
- def create_email_generator_tab(self):
814
- """Create the Random Email Generator tab."""
815
- tab_frame = ttk.Frame(self.notebook)
816
- self.notebook.add(tab_frame, text="Random Email Generator")
817
-
818
- # Get current settings
819
- settings = self.main_app.settings["tool_settings"].get("Generator Tools", {}).get("Random Email Generator", {})
820
-
821
- # Count setting
822
- count_frame = ttk.Frame(tab_frame)
823
- count_frame.pack(fill=tk.X, padx=10, pady=5)
824
-
825
- ttk.Label(count_frame, text="Count:").pack(side=tk.LEFT)
826
- self.email_count_var = tk.StringVar(value=str(settings.get("count", 5)))
827
- count_entry = ttk.Entry(count_frame, textvariable=self.email_count_var, width=10)
828
- count_entry.pack(side=tk.LEFT, padx=(5, 0))
829
-
830
- # Separator type selection (radio buttons)
831
- separator_type_frame = ttk.LabelFrame(tab_frame, text="Output Format")
832
- separator_type_frame.pack(fill=tk.X, padx=10, pady=5)
833
-
834
- self.email_separator_type_var = tk.StringVar(value=settings.get("separator_type", "list"))
835
-
836
- separator_type_options = [
837
- ("List (\\n separator)", "list"),
838
- ("Custom Separator", "custom")
839
- ]
840
-
841
- for text, value in separator_type_options:
842
- ttk.Radiobutton(separator_type_frame, text=text, variable=self.email_separator_type_var,
843
- value=value, command=self.update_email_separator_field).pack(side=tk.LEFT, padx=5)
844
-
845
- # Custom separator field
846
- self.email_separator_frame = ttk.Frame(tab_frame)
847
- self.email_separator_frame.pack(fill=tk.X, padx=10, pady=5)
848
-
849
- ttk.Label(self.email_separator_frame, text="Separator:").pack(side=tk.LEFT)
850
- self.email_separator_var = tk.StringVar(value=settings.get("separator", ","))
851
- separator_entry = ttk.Entry(self.email_separator_frame, textvariable=self.email_separator_var, width=10)
852
- separator_entry.pack(side=tk.LEFT, padx=(5, 0))
853
-
854
- # Domain type selection (radio buttons)
855
- domain_type_frame = ttk.LabelFrame(tab_frame, text="Domain")
856
- domain_type_frame.pack(fill=tk.X, padx=10, pady=5)
857
-
858
- self.email_domain_type_var = tk.StringVar(value=settings.get("domain_type", "random"))
859
-
860
- domain_type_options = [
861
- ("Random", "random"),
862
- ("Custom Domain", "custom")
863
- ]
864
-
865
- for text, value in domain_type_options:
866
- ttk.Radiobutton(domain_type_frame, text=text, variable=self.email_domain_type_var,
867
- value=value, command=self.update_email_domain_field).pack(side=tk.LEFT, padx=5)
868
-
869
- # Custom domain field
870
- self.email_domain_frame = ttk.Frame(tab_frame)
871
- self.email_domain_frame.pack(fill=tk.X, padx=10, pady=5)
872
-
873
- ttk.Label(self.email_domain_frame, text="Domain:").pack(side=tk.LEFT)
874
- self.email_domain_var = tk.StringVar(value=settings.get("domain", "example.com"))
875
- domain_entry = ttk.Entry(self.email_domain_frame, textvariable=self.email_domain_var, width=30)
876
- domain_entry.pack(side=tk.LEFT, padx=(5, 0))
877
-
878
- # Generate button
879
- button_frame = ttk.Frame(tab_frame)
880
- button_frame.pack(fill=tk.X, padx=10, pady=10)
881
-
882
- email_btn = ttk.Button(button_frame, text="Generate",
883
- command=lambda: self.generate_emails())
884
- email_btn.pack(side=tk.LEFT)
885
-
886
- # Initialize field visibility
887
- self.update_email_separator_field()
888
- self.update_email_domain_field()
889
-
890
- def update_uuid_fields(self):
891
- """Update UUID field visibility based on selected version."""
892
- if hasattr(self, 'uuid_version_var') and hasattr(self, 'uuid_namebased_frame'):
893
- version = self.uuid_version_var.get()
894
- if version in [3, 5]:
895
- # Show name-based settings for versions 3 and 5
896
- self.uuid_namebased_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0))
897
- else:
898
- # Hide name-based settings for versions 1 and 4
899
- self.uuid_namebased_frame.pack_forget()
900
-
901
- def update_email_separator_field(self):
902
- """Update email separator field visibility based on selected type."""
903
- if hasattr(self, 'email_separator_type_var') and hasattr(self, 'email_separator_frame'):
904
- separator_type = self.email_separator_type_var.get()
905
- if separator_type == "custom":
906
- # Show separator field for custom separator
907
- self.email_separator_frame.pack(fill=tk.X, padx=10, pady=5)
908
- else:
909
- # Hide separator field for list format
910
- self.email_separator_frame.pack_forget()
911
-
912
- def update_email_domain_field(self):
913
- """Update email domain field visibility based on selected type."""
914
- if hasattr(self, 'email_domain_type_var') and hasattr(self, 'email_domain_frame'):
915
- domain_type = self.email_domain_type_var.get()
916
- if domain_type == "custom":
917
- # Show domain field for custom domain
918
- self.email_domain_frame.pack(fill=tk.X, padx=10, pady=5)
919
- else:
920
- # Hide domain field for random domain
921
- self.email_domain_frame.pack_forget()
922
-
923
- def update_percentages(self, *args):
924
- """Update percentage labels and ensure they add up to 100%."""
925
- if hasattr(self, 'pw_letters_percent_var'):
926
- letters_percent = self.pw_letters_percent_var.get()
927
- numbers_percent = self.pw_numbers_percent_var.get()
928
- symbols_percent = self.pw_symbols_percent_var.get()
929
-
930
- total = letters_percent + numbers_percent + symbols_percent
931
-
932
- # Update labels
933
- self.pw_letters_label.config(text=f"{letters_percent}%")
934
- self.pw_numbers_label.config(text=f"{numbers_percent}%")
935
- self.pw_symbols_label.config(text=f"{symbols_percent}%")
936
-
937
- # Update total label with color coding
938
- if total == 100:
939
- self.pw_total_label.config(text=f"{total}%", foreground="green")
940
- else:
941
- self.pw_total_label.config(text=f"{total}%", foreground="red")
942
-
943
- def generate_password(self):
944
- """Generate a password and update the output."""
945
- try:
946
- length = int(self.pw_len_var.get())
947
- numbers = self.pw_num_var.get()
948
- symbols = self.pw_sym_var.get()
949
- letters_percent = self.pw_letters_percent_var.get()
950
- numbers_percent = self.pw_numbers_percent_var.get()
951
- symbols_percent = self.pw_symbols_percent_var.get()
952
-
953
- settings = {
954
- "length": length,
955
- "numbers": numbers,
956
- "symbols": symbols,
957
- "letters_percent": letters_percent,
958
- "numbers_percent": numbers_percent,
959
- "symbols_percent": symbols_percent
960
- }
961
-
962
- result = self.generator_tools.strong_password("", settings)
963
- self.main_app.update_output_text(result)
964
-
965
- # Save settings
966
- self.save_settings()
967
-
968
- except ValueError:
969
- self.main_app.update_output_text("Error: Invalid password length")
970
-
971
- def generate_repeated_text(self):
972
- """Generate repeated text and update the output."""
973
- try:
974
- # Get input text
975
- active_input_tab = self.main_app.input_tabs[self.main_app.input_notebook.index(self.main_app.input_notebook.select())]
976
- input_text = active_input_tab.text.get("1.0", tk.END).strip()
977
-
978
- if not input_text:
979
- self.main_app.update_output_text("Error: No input text provided")
980
- return
981
-
982
- times = int(self.repeat_times_var.get())
983
- separator = self.repeat_sep_var.get()
984
-
985
- settings = {
986
- "times": times,
987
- "separator": separator
988
- }
989
-
990
- result = self.generator_tools.repeating_text(input_text, settings)
991
- self.main_app.update_output_text(result)
992
-
993
- # Save settings
994
- self.save_settings()
995
-
996
- except ValueError:
997
- self.main_app.update_output_text("Error: Invalid repeat times value")
998
-
999
- def generate_lorem_ipsum(self):
1000
- """Generate Lorem Ipsum text and update the output."""
1001
- try:
1002
- count = int(self.lorem_count_var.get())
1003
- text_type = self.lorem_type_var.get()
1004
- format_type = self.lorem_format_var.get()
1005
- ordered = self.lorem_ordered_var.get()
1006
-
1007
- settings = {
1008
- "count": count,
1009
- "type": text_type,
1010
- "format": format_type,
1011
- "ordered": ordered
1012
- }
1013
-
1014
- result = self.generator_tools.lorem_ipsum("", settings)
1015
- self.main_app.update_output_text(result)
1016
-
1017
- # Save settings
1018
- self.save_settings()
1019
-
1020
- except ValueError:
1021
- self.main_app.update_output_text("Error: Invalid count value")
1022
-
1023
- def generate_uuid(self):
1024
- """Generate UUID/GUID and update the output."""
1025
- try:
1026
- version = self.uuid_version_var.get()
1027
- format_type = self.uuid_format_var.get()
1028
- case = self.uuid_case_var.get()
1029
- count = int(self.uuid_count_var.get())
1030
- namespace = self.uuid_namespace_var.get()
1031
- name = self.uuid_name_var.get()
1032
-
1033
- settings = {
1034
- "version": version,
1035
- "format": format_type,
1036
- "case": case,
1037
- "count": count,
1038
- "namespace": namespace,
1039
- "name": name
1040
- }
1041
-
1042
- result = self.generator_tools.uuid_generator("", settings)
1043
- self.main_app.update_output_text(result)
1044
-
1045
- # Save settings
1046
- self.save_settings()
1047
-
1048
- except ValueError:
1049
- self.main_app.update_output_text("Error: Invalid count value")
1050
-
1051
- def generate_emails(self):
1052
- """Generate random emails and update the output."""
1053
- try:
1054
- count = int(self.email_count_var.get())
1055
- separator_type = self.email_separator_type_var.get()
1056
- separator = self.email_separator_var.get()
1057
- domain_type = self.email_domain_type_var.get()
1058
- domain = self.email_domain_var.get()
1059
-
1060
- settings = {
1061
- "count": count,
1062
- "separator_type": separator_type,
1063
- "separator": separator,
1064
- "domain_type": domain_type,
1065
- "domain": domain
1066
- }
1067
-
1068
- result = self.generator_tools.random_email_generator("", settings)
1069
- self.main_app.update_output_text(result)
1070
-
1071
- # Save settings
1072
- self.save_settings()
1073
-
1074
- except ValueError:
1075
- self.main_app.update_output_text("Error: Invalid count value")
1076
-
1077
- def save_settings(self):
1078
- """Save current settings to the main app."""
1079
- if not self.main_app:
1080
- return
1081
-
1082
- # Ensure the Generator Tools settings exist
1083
- if "Generator Tools" not in self.main_app.settings["tool_settings"]:
1084
- self.main_app.settings["tool_settings"]["Generator Tools"] = {}
1085
-
1086
- # Save Strong Password Generator settings
1087
- try:
1088
- self.main_app.settings["tool_settings"]["Generator Tools"]["Strong Password Generator"] = {
1089
- "length": int(self.pw_len_var.get()),
1090
- "numbers": self.pw_num_var.get(),
1091
- "symbols": self.pw_sym_var.get(),
1092
- "letters_percent": self.pw_letters_percent_var.get(),
1093
- "numbers_percent": self.pw_numbers_percent_var.get(),
1094
- "symbols_percent": self.pw_symbols_percent_var.get()
1095
- }
1096
- except (ValueError, AttributeError):
1097
- pass
1098
-
1099
- # Save Repeating Text Generator settings
1100
- try:
1101
- self.main_app.settings["tool_settings"]["Generator Tools"]["Repeating Text Generator"] = {
1102
- "times": int(self.repeat_times_var.get()),
1103
- "separator": self.repeat_sep_var.get()
1104
- }
1105
- except (ValueError, AttributeError):
1106
- pass
1107
-
1108
- # Save Lorem Ipsum Generator settings
1109
- try:
1110
- self.main_app.settings["tool_settings"]["Generator Tools"]["Lorem Ipsum Generator"] = {
1111
- "count": int(self.lorem_count_var.get()),
1112
- "type": self.lorem_type_var.get(),
1113
- "format": self.lorem_format_var.get(),
1114
- "ordered": self.lorem_ordered_var.get()
1115
- }
1116
- except (ValueError, AttributeError):
1117
- pass
1118
-
1119
- # Save UUID/GUID Generator settings
1120
- try:
1121
- self.main_app.settings["tool_settings"]["Generator Tools"]["UUID/GUID Generator"] = {
1122
- "version": self.uuid_version_var.get(),
1123
- "format": self.uuid_format_var.get(),
1124
- "case": self.uuid_case_var.get(),
1125
- "count": int(self.uuid_count_var.get()),
1126
- "namespace": self.uuid_namespace_var.get(),
1127
- "name": self.uuid_name_var.get()
1128
- }
1129
- except (ValueError, AttributeError):
1130
- pass
1131
-
1132
- # Save Random Email Generator settings
1133
- try:
1134
- self.main_app.settings["tool_settings"]["Generator Tools"]["Random Email Generator"] = {
1135
- "count": int(self.email_count_var.get()),
1136
- "separator_type": self.email_separator_type_var.get(),
1137
- "separator": self.email_separator_var.get(),
1138
- "domain_type": self.email_domain_type_var.get(),
1139
- "domain": self.email_domain_var.get()
1140
- }
1141
- except (ValueError, AttributeError):
1142
- pass
1143
-
1144
- # Save ASCII Art Generator settings
1145
- try:
1146
- if hasattr(self.main_app, 'ascii_art_generator') and self.main_app.ascii_art_generator:
1147
- if "ASCII Art Generator" not in self.main_app.settings["tool_settings"]:
1148
- self.main_app.settings["tool_settings"]["ASCII Art Generator"] = {}
1149
- # Settings are saved by the widget itself
1150
- except (ValueError, AttributeError):
1151
- pass
1152
-
1153
- # Save Hash Generator settings
1154
- try:
1155
- if hasattr(self.main_app, 'hash_generator') and self.main_app.hash_generator:
1156
- if "Hash Generator" not in self.main_app.settings["tool_settings"]:
1157
- self.main_app.settings["tool_settings"]["Hash Generator"] = {}
1158
- # Settings are saved by the widget itself
1159
- except (ValueError, AttributeError):
1160
- pass
1161
-
1162
- # Save Slug Generator settings
1163
- try:
1164
- if hasattr(self.main_app, 'slug_generator') and self.main_app.slug_generator:
1165
- if "Slug Generator" not in self.main_app.settings["tool_settings"]:
1166
- self.main_app.settings["tool_settings"]["Slug Generator"] = {}
1167
- # Settings are saved by the widget itself
1168
- except (ValueError, AttributeError):
1169
- pass
1170
-
1171
- # Save settings to file
1172
- self.main_app.save_settings()
1173
-
1174
- def create_ascii_art_generator_tab(self):
1175
- """Create the ASCII Art Generator tab."""
1176
- tab_frame = ttk.Frame(self.notebook)
1177
- self.notebook.add(tab_frame, text="ASCII Art Generator")
1178
-
1179
- try:
1180
- from tools.ascii_art_generator import ASCIIArtGenerator
1181
- if hasattr(self.main_app, 'ascii_art_generator') and self.main_app.ascii_art_generator:
1182
- widget = self.main_app.ascii_art_generator.create_widget(tab_frame, self.main_app)
1183
- widget.pack(fill=tk.BOTH, expand=True)
1184
- else:
1185
- ttk.Label(tab_frame, text="ASCII Art Generator module not available").pack(padx=10, pady=10)
1186
- except ImportError:
1187
- ttk.Label(tab_frame, text="ASCII Art Generator module not available").pack(padx=10, pady=10)
1188
-
1189
- def create_hash_generator_tab(self):
1190
- """Create the Hash Generator tab."""
1191
- tab_frame = ttk.Frame(self.notebook)
1192
- self.notebook.add(tab_frame, text="Hash Generator")
1193
-
1194
- try:
1195
- from tools.hash_generator import HashGenerator
1196
- if hasattr(self.main_app, 'hash_generator') and self.main_app.hash_generator:
1197
- widget = self.main_app.hash_generator.create_widget(tab_frame, self.main_app)
1198
- widget.pack(fill=tk.BOTH, expand=True)
1199
- else:
1200
- ttk.Label(tab_frame, text="Hash Generator module not available").pack(padx=10, pady=10)
1201
- except ImportError:
1202
- ttk.Label(tab_frame, text="Hash Generator module not available").pack(padx=10, pady=10)
1203
-
1204
- def create_slug_generator_tab(self):
1205
- """Create the Slug Generator tab."""
1206
- tab_frame = ttk.Frame(self.notebook)
1207
- self.notebook.add(tab_frame, text="Slug Generator")
1208
-
1209
- try:
1210
- from tools.slug_generator import SlugGenerator
1211
- if hasattr(self.main_app, 'slug_generator') and self.main_app.slug_generator:
1212
- widget = self.main_app.slug_generator.create_widget(tab_frame, self.main_app)
1213
- widget.pack(fill=tk.BOTH, expand=True)
1214
- else:
1215
- ttk.Label(tab_frame, text="Slug Generator module not available").pack(padx=10, pady=10)
1216
- except ImportError:
1
+ """
2
+ Generator Tools Module for Promera AI Commander
3
+
4
+ This module provides various text generation tools including:
5
+ - Strong Password Generator
6
+ - Repeating Text Generator
7
+ - Lorem Ipsum Generator
8
+ - UUID/GUID Generator
9
+
10
+ Author: Promera AI Commander
11
+ """
12
+
13
+ import tkinter as tk
14
+ from tkinter import ttk
15
+ import string
16
+ import random
17
+ import json
18
+ import uuid
19
+ import base64
20
+ import hashlib
21
+
22
+
23
+ class GeneratorTools:
24
+ """A class containing various text generation tools."""
25
+
26
+ def __init__(self):
27
+ """Initialize the GeneratorTools class."""
28
+ self.tools = {
29
+ "Strong Password Generator": self.strong_password,
30
+ "Repeating Text Generator": self.repeating_text,
31
+ "Lorem Ipsum Generator": self.lorem_ipsum,
32
+ "UUID/GUID Generator": self.uuid_generator,
33
+ "Random Email Generator": self.random_email_generator
34
+ }
35
+
36
+ def get_available_tools(self):
37
+ """Returns a list of available generator tools."""
38
+ return list(self.tools.keys())
39
+
40
+ def get_default_settings(self):
41
+ """Returns default settings for all generator tools."""
42
+ return {
43
+ "Strong Password Generator": {"length": 20, "numbers": "", "symbols": "", "letters_percent": 70, "numbers_percent": 20, "symbols_percent": 10},
44
+ "Repeating Text Generator": {"times": 5, "separator": "+"},
45
+ "Lorem Ipsum Generator": {"count": 5, "type": "paragraphs", "format": "plain", "ordered": False},
46
+ "UUID/GUID Generator": {"version": 4, "format": "standard", "case": "lowercase", "count": 1, "namespace": "dns", "name": ""},
47
+ "Random Email Generator": {"count": 5, "separator_type": "list", "separator": ",", "domain_type": "random", "domain": "example.com"},
48
+ "ASCII Art Generator": {"font": "standard", "width": 80},
49
+ "Hash Generator": {"algorithms": ["md5", "sha256"], "uppercase": False},
50
+ "Slug Generator": {"separator": "-", "lowercase": True, "transliterate": True, "max_length": 0, "remove_stopwords": False}
51
+ }
52
+
53
+ def process_text(self, input_text, tool_name, settings):
54
+ """Process text using the specified generator tool."""
55
+ if tool_name not in self.tools:
56
+ return f"Error: Unknown tool '{tool_name}'"
57
+
58
+ return self.tools[tool_name](input_text, settings)
59
+
60
+ @staticmethod
61
+ def strong_password(input_text, settings):
62
+ """Generates a strong, random password with specified character distribution."""
63
+ length = settings.get("length", 20)
64
+ numbers = settings.get("numbers", "")
65
+ symbols = settings.get("symbols", "")
66
+ letters_percent = settings.get("letters_percent", 70)
67
+ numbers_percent = settings.get("numbers_percent", 20)
68
+ symbols_percent = settings.get("symbols_percent", 10)
69
+
70
+ if not isinstance(length, int) or length <= 0:
71
+ return "Error: Password length must be a positive number."
72
+
73
+ # Validate percentages
74
+ total_percent = letters_percent + numbers_percent + symbols_percent
75
+ if total_percent != 100:
76
+ return "Error: Percentages must add up to 100%."
77
+
78
+ # Character sets
79
+ letters = string.ascii_letters
80
+ digits = string.digits
81
+ special_chars = string.punctuation
82
+
83
+ # Calculate character counts with ±5% approximation
84
+ def calculate_count(percent, total_length):
85
+ base_count = int((percent / 100) * total_length)
86
+ # Allow ±5% variation (rounded to nearest integer)
87
+ variation = max(1, int(0.05 * total_length))
88
+ min_count = max(0, base_count - variation)
89
+ max_count = min(total_length, base_count + variation)
90
+ return random.randint(min_count, max_count)
91
+
92
+ letters_count = calculate_count(letters_percent, length)
93
+ numbers_count = calculate_count(numbers_percent, length)
94
+ symbols_count = length - letters_count - numbers_count
95
+
96
+ # Ensure we don't exceed total length
97
+ if symbols_count < 0:
98
+ # Adjust if we went over
99
+ excess = abs(symbols_count)
100
+ if letters_count >= excess:
101
+ letters_count -= excess
102
+ symbols_count = 0
103
+ else:
104
+ numbers_count -= (excess - letters_count)
105
+ letters_count = 0
106
+ symbols_count = 0
107
+
108
+ # Generate password parts
109
+ password_chars = []
110
+
111
+ # Add letters
112
+ password_chars.extend(random.choices(letters, k=letters_count))
113
+
114
+ # Add numbers
115
+ password_chars.extend(random.choices(digits, k=numbers_count))
116
+
117
+ # Add symbols
118
+ password_chars.extend(random.choices(special_chars, k=symbols_count))
119
+
120
+ # Ensure we have the exact length
121
+ while len(password_chars) < length:
122
+ password_chars.append(random.choice(letters + digits + special_chars))
123
+
124
+ # Shuffle the password
125
+ random.shuffle(password_chars)
126
+ password = ''.join(password_chars)
127
+
128
+ # Ensure included numbers and symbols are present
129
+ must_include = numbers + symbols
130
+ if must_include and len(must_include) <= length:
131
+ password_list = list(password)
132
+ for i, char in enumerate(must_include):
133
+ if i < len(password_list):
134
+ password_list[i] = char
135
+ random.shuffle(password_list)
136
+ password = "".join(password_list)
137
+
138
+ return password
139
+
140
+ @staticmethod
141
+ def repeating_text(input_text, settings):
142
+ """Repeats the input text a specified number of times."""
143
+ times = settings.get("times", 5)
144
+ separator = settings.get("separator", "+")
145
+
146
+ if not isinstance(times, int) or times < 0:
147
+ return "Error: 'Times' must be a non-negative number."
148
+ return separator.join([input_text] * times)
149
+
150
+ @staticmethod
151
+ def lorem_ipsum(input_text, settings):
152
+ """Generates Lorem Ipsum text in various formats."""
153
+ count = settings.get("count", 5)
154
+ text_type = settings.get("type", "paragraphs")
155
+ format_type = settings.get("format", "plain")
156
+ ordered = settings.get("ordered", False)
157
+
158
+ # Validation
159
+ if not isinstance(count, int) or count <= 0:
160
+ return "Error: Count must be a positive integer."
161
+
162
+ if format_type not in ['plain', 'html', 'markdown', 'json']:
163
+ return "Error: Format must be one of: 'plain', 'html', 'markdown', 'json'."
164
+
165
+ # Lorem ipsum word bank
166
+ lorem_words = [
167
+ "lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit",
168
+ "sed", "do", "eiusmod", "tempor", "incididunt", "ut", "labore", "et", "dolore",
169
+ "magna", "aliqua", "enim", "ad", "minim", "veniam", "quis", "nostrud",
170
+ "exercitation", "ullamco", "laboris", "nisi", "aliquip", "ex", "ea", "commodo",
171
+ "consequat", "duis", "aute", "irure", "in", "reprehenderit", "voluptate",
172
+ "velit", "esse", "cillum", "fugiat", "nulla", "pariatur", "excepteur", "sint",
173
+ "occaecat", "cupidatat", "non", "proident", "sunt", "culpa", "qui", "officia",
174
+ "deserunt", "mollit", "anim", "id", "est", "laborum", "at", "vero", "eos",
175
+ "accusamus", "accusantium", "doloremque", "laudantium", "totam", "rem",
176
+ "aperiam", "eaque", "ipsa", "quae", "ab", "illo", "inventore", "veritatis"
177
+ ]
178
+
179
+ def generate_sentence():
180
+ """Generate a single sentence."""
181
+ length = random.randint(8, 20)
182
+ words = random.choices(lorem_words, k=length)
183
+ words[0] = words[0].capitalize()
184
+ return " ".join(words) + "."
185
+
186
+ def generate_paragraph():
187
+ """Generate a single paragraph."""
188
+ sentences = [generate_sentence() for _ in range(random.randint(3, 8))]
189
+ return " ".join(sentences)
190
+
191
+ # Generate content based on type
192
+ if text_type == "words":
193
+ content = random.choices(lorem_words, k=count)
194
+ elif text_type == "sentences":
195
+ content = [generate_sentence() for _ in range(count)]
196
+ elif text_type == "paragraphs":
197
+ content = [generate_paragraph() for _ in range(count)]
198
+ elif text_type == "bytes":
199
+ # Generate text up to specified byte count
200
+ text = ""
201
+ while len(text.encode('utf-8')) < count:
202
+ text += generate_sentence() + " "
203
+ content = [text[:count]]
204
+ else:
205
+ return "Error: Invalid text type."
206
+
207
+ # Format output
208
+ if format_type == "plain":
209
+ if text_type == "words":
210
+ return " ".join(content)
211
+ elif text_type == "sentences":
212
+ return " ".join(content)
213
+ elif text_type == "paragraphs":
214
+ return "\n\n".join(content)
215
+ else: # bytes
216
+ return content[0]
217
+
218
+ elif format_type == "html":
219
+ if text_type == "words":
220
+ return "<span>" + " ".join(content) + "</span>"
221
+ elif text_type == "sentences":
222
+ if ordered:
223
+ items = "".join([f"<li>{sentence}</li>" for sentence in content])
224
+ return f"<ol>{items}</ol>"
225
+ else:
226
+ items = "".join([f"<li>{sentence}</li>" for sentence in content])
227
+ return f"<ul>{items}</ul>"
228
+ elif text_type == "paragraphs":
229
+ paragraphs = "".join([f"<p>{para}</p>" for para in content])
230
+ return paragraphs
231
+ else: # bytes
232
+ return f"<div>{content[0]}</div>"
233
+
234
+ elif format_type == "markdown":
235
+ if text_type == "words":
236
+ return " ".join(content)
237
+ elif text_type == "sentences":
238
+ if ordered:
239
+ items = "\n".join([f"{i+1}. {sentence}" for i, sentence in enumerate(content)])
240
+ else:
241
+ items = "\n".join([f"- {sentence}" for sentence in content])
242
+ return items
243
+ elif text_type == "paragraphs":
244
+ return "\n\n".join(content)
245
+ else: # bytes
246
+ return content[0]
247
+
248
+ elif format_type == "json":
249
+ return json.dumps({
250
+ "type": text_type,
251
+ "count": len(content),
252
+ "content": content
253
+ }, indent=2)
254
+
255
+ return "Error: Unknown format type."
256
+
257
+ @staticmethod
258
+ def uuid_generator(input_text, settings):
259
+ """Generates UUID/GUID in various formats and versions."""
260
+ version = settings.get("version", 4)
261
+ format_type = settings.get("format", "standard")
262
+ case = settings.get("case", "lowercase")
263
+ count = settings.get("count", 1)
264
+ namespace_name = settings.get("namespace", "dns")
265
+ name = settings.get("name", "")
266
+
267
+ # Validation
268
+ if not isinstance(count, int) or count <= 0:
269
+ return "Error: Count must be a positive integer."
270
+
271
+ if version not in [1, 3, 4, 5]:
272
+ return "Error: UUID version must be 1, 3, 4, or 5."
273
+
274
+ if version in [3, 5] and not name:
275
+ return "Error: Name is required for UUID versions 3 and 5."
276
+
277
+ # Predefined namespaces
278
+ namespaces = {
279
+ "dns": uuid.NAMESPACE_DNS,
280
+ "url": uuid.NAMESPACE_URL,
281
+ "oid": uuid.NAMESPACE_OID,
282
+ "x500": uuid.NAMESPACE_X500
283
+ }
284
+
285
+ def generate_single_uuid():
286
+ """Generate a single UUID based on version."""
287
+ if version == 1:
288
+ return uuid.uuid1()
289
+ elif version == 3:
290
+ if namespace_name not in namespaces:
291
+ return None
292
+ return uuid.uuid3(namespaces[namespace_name], name)
293
+ elif version == 4:
294
+ return uuid.uuid4()
295
+ elif version == 5:
296
+ if namespace_name not in namespaces:
297
+ return None
298
+ return uuid.uuid5(namespaces[namespace_name], name)
299
+
300
+ def format_uuid(uuid_obj):
301
+ """Format UUID according to specified format and case."""
302
+ if uuid_obj is None:
303
+ return "Error: Invalid namespace."
304
+
305
+ uuid_str = str(uuid_obj)
306
+
307
+ # Apply case formatting
308
+ if case == "uppercase":
309
+ uuid_str = uuid_str.upper()
310
+ elif case == "lowercase":
311
+ uuid_str = uuid_str.lower()
312
+
313
+ # Apply format
314
+ if format_type == "standard":
315
+ return uuid_str
316
+ elif format_type == "hex":
317
+ return uuid_str.replace("-", "")
318
+ elif format_type == "microsoft":
319
+ return "{" + uuid_str + "}"
320
+ elif format_type == "urn":
321
+ return "urn:uuid:" + uuid_str
322
+ elif format_type == "base64":
323
+ return base64.b64encode(uuid_obj.bytes).decode('ascii')
324
+ elif format_type == "c_array":
325
+ bytes_list = list(uuid_obj.bytes)
326
+ hex_values = [f"0x{b:02x}" for b in bytes_list]
327
+ # Format as C array with proper line breaks
328
+ formatted = "{ " + ", ".join(hex_values[:8]) + ",\n " + ", ".join(hex_values[8:]) + " }"
329
+ return formatted
330
+ elif format_type == "nil":
331
+ return "00000000-0000-0000-0000-000000000000"
332
+ else:
333
+ return uuid_str
334
+
335
+ # Generate UUIDs
336
+ results = []
337
+ for _ in range(count):
338
+ if format_type == "nil":
339
+ # Special case for nil UUID
340
+ nil_uuid = uuid.UUID('00000000-0000-0000-0000-000000000000')
341
+ results.append(format_uuid(nil_uuid))
342
+ else:
343
+ uuid_obj = generate_single_uuid()
344
+ if uuid_obj is None:
345
+ return "Error: Invalid namespace for name-based UUID."
346
+ results.append(format_uuid(uuid_obj))
347
+
348
+ # Return results
349
+ if count == 1:
350
+ return results[0]
351
+ else:
352
+ return "\n".join(results)
353
+
354
+ @staticmethod
355
+ def random_email_generator(input_text, settings):
356
+ """Generates random email addresses."""
357
+ count = settings.get("count", 5)
358
+ separator_type = settings.get("separator_type", "list")
359
+ separator = settings.get("separator", ",")
360
+ domain_type = settings.get("domain_type", "random")
361
+ domain = settings.get("domain", "example.com")
362
+
363
+ # Validation
364
+ if not isinstance(count, int) or count <= 0:
365
+ return "Error: Count must be a positive integer."
366
+
367
+ # Common first names and last names for generating realistic emails
368
+ first_names = [
369
+ "john", "jane", "mike", "sarah", "david", "lisa", "chris", "anna", "james", "mary",
370
+ "robert", "jennifer", "michael", "linda", "william", "elizabeth", "richard", "barbara",
371
+ "joseph", "susan", "thomas", "jessica", "charles", "karen", "daniel", "nancy",
372
+ "matthew", "betty", "anthony", "helen", "mark", "sandra", "donald", "donna",
373
+ "steven", "carol", "paul", "ruth", "andrew", "sharon", "joshua", "michelle",
374
+ "kenneth", "laura", "kevin", "sarah", "brian", "kimberly", "george", "deborah",
375
+ "edward", "dorothy", "ronald", "lisa", "timothy", "nancy", "jason", "karen"
376
+ ]
377
+
378
+ last_names = [
379
+ "smith", "johnson", "williams", "brown", "jones", "garcia", "miller", "davis",
380
+ "rodriguez", "martinez", "hernandez", "lopez", "gonzalez", "wilson", "anderson",
381
+ "thomas", "taylor", "moore", "jackson", "martin", "lee", "perez", "thompson",
382
+ "white", "harris", "sanchez", "clark", "ramirez", "lewis", "robinson", "walker",
383
+ "young", "allen", "king", "wright", "scott", "torres", "nguyen", "hill",
384
+ "flores", "green", "adams", "nelson", "baker", "hall", "rivera", "campbell",
385
+ "mitchell", "carter", "roberts", "gomez", "phillips", "evans", "turner", "diaz"
386
+ ]
387
+
388
+ # Common domain names for random domain generation
389
+ random_domains = [
390
+ "gmail.com", "yahoo.com", "hotmail.com", "outlook.com", "aol.com",
391
+ "icloud.com", "protonmail.com", "mail.com", "zoho.com", "fastmail.com",
392
+ "example.com", "test.com", "demo.org", "sample.net", "placeholder.io"
393
+ ]
394
+
395
+ def generate_single_email():
396
+ """Generate a single random email address."""
397
+ # Generate username part
398
+ first = random.choice(first_names)
399
+ last = random.choice(last_names)
400
+
401
+ # Various username patterns
402
+ patterns = [
403
+ f"{first}.{last}",
404
+ f"{first}{last}",
405
+ f"{first}_{last}",
406
+ f"{first}{random.randint(1, 999)}",
407
+ f"{first}.{last}{random.randint(1, 99)}",
408
+ f"{first[0]}{last}",
409
+ f"{first}{last[0]}",
410
+ f"{first}.{last[0]}",
411
+ f"{first[0]}.{last}"
412
+ ]
413
+
414
+ username = random.choice(patterns)
415
+
416
+ # Generate domain part
417
+ if domain_type == "random":
418
+ email_domain = random.choice(random_domains)
419
+ else:
420
+ email_domain = domain
421
+
422
+ return f"{username}@{email_domain}"
423
+
424
+ # Generate emails
425
+ emails = []
426
+ for _ in range(count):
427
+ emails.append(generate_single_email())
428
+
429
+ # Format output based on separator type
430
+ if separator_type == "list":
431
+ return "\n".join(emails)
432
+ else:
433
+ return separator.join(emails)
434
+
435
+
436
+ class GeneratorToolsWidget:
437
+ """Widget for the Generator Tools tabbed interface."""
438
+
439
+ def __init__(self, generator_tools):
440
+ """Initialize the GeneratorToolsWidget."""
441
+ self.generator_tools = generator_tools
442
+ self.main_app = None
443
+
444
+ # Variables for Strong Password Generator
445
+ self.pw_len_var = None
446
+ self.pw_num_var = None
447
+ self.pw_sym_var = None
448
+ self.pw_letters_percent_var = None
449
+ self.pw_numbers_percent_var = None
450
+ self.pw_symbols_percent_var = None
451
+
452
+ # Variables for Repeating Text Generator
453
+ self.repeat_times_var = None
454
+ self.repeat_sep_var = None
455
+
456
+ # Variables for Lorem Ipsum Generator
457
+ self.lorem_count_var = None
458
+ self.lorem_type_var = None
459
+ self.lorem_format_var = None
460
+ self.lorem_ordered_var = None
461
+
462
+ # Variables for UUID/GUID Generator
463
+ self.uuid_version_var = None
464
+ self.uuid_format_var = None
465
+ self.uuid_case_var = None
466
+ self.uuid_count_var = None
467
+ self.uuid_namespace_var = None
468
+ self.uuid_name_var = None
469
+
470
+ # Variables for Random Email Generator
471
+ self.email_count_var = None
472
+ self.email_separator_type_var = None
473
+ self.email_separator_var = None
474
+ self.email_domain_type_var = None
475
+ self.email_domain_var = None
476
+
477
+ def create_widget(self, parent, main_app):
478
+ """Create and return the main widget."""
479
+ self.main_app = main_app
480
+
481
+ # Create main frame
482
+ main_frame = ttk.Frame(parent)
483
+
484
+ # Create notebook for tabs
485
+ self.notebook = ttk.Notebook(main_frame)
486
+ self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
487
+
488
+ # Create tabs
489
+ self.create_password_generator_tab()
490
+ self.create_repeating_text_tab()
491
+ self.create_lorem_ipsum_tab()
492
+ self.create_uuid_generator_tab()
493
+ self.create_email_generator_tab()
494
+ self.create_ascii_art_generator_tab()
495
+ self.create_hash_generator_tab()
496
+ self.create_slug_generator_tab()
497
+
498
+ return main_frame
499
+
500
+ def create_password_generator_tab(self):
501
+ """Create the Strong Password Generator tab."""
502
+ tab_frame = ttk.Frame(self.notebook)
503
+ self.notebook.add(tab_frame, text="Strong Password Generator")
504
+
505
+ # Get current settings
506
+ settings = self.main_app.settings["tool_settings"].get("Generator Tools", {}).get("Strong Password Generator", {})
507
+
508
+ # Length setting
509
+ length_frame = ttk.Frame(tab_frame)
510
+ length_frame.pack(fill=tk.X, padx=10, pady=5)
511
+
512
+ ttk.Label(length_frame, text="Password Length:").pack(side=tk.LEFT)
513
+ self.pw_len_var = tk.StringVar(value=str(settings.get("length", 20)))
514
+ length_entry = ttk.Entry(length_frame, textvariable=self.pw_len_var, width=10)
515
+ length_entry.pack(side=tk.LEFT, padx=(5, 0))
516
+
517
+ # Character distribution sliders
518
+ distribution_frame = ttk.LabelFrame(tab_frame, text="Character Distribution (%)")
519
+ distribution_frame.pack(fill=tk.X, padx=10, pady=5)
520
+
521
+ # Letters percentage slider
522
+ letters_frame = ttk.Frame(distribution_frame)
523
+ letters_frame.pack(fill=tk.X, padx=5, pady=2)
524
+
525
+ ttk.Label(letters_frame, text="Letters:", width=12).pack(side=tk.LEFT)
526
+ self.pw_letters_percent_var = tk.IntVar(value=settings.get("letters_percent", 70))
527
+ letters_slider = ttk.Scale(letters_frame, from_=0, to=100, orient=tk.HORIZONTAL,
528
+ variable=self.pw_letters_percent_var, command=self.update_percentages)
529
+ letters_slider.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(5, 5))
530
+ letters_label = ttk.Label(letters_frame, text="70%", width=5)
531
+ letters_label.pack(side=tk.RIGHT)
532
+ self.pw_letters_label = letters_label
533
+
534
+ # Numbers percentage slider
535
+ numbers_frame = ttk.Frame(distribution_frame)
536
+ numbers_frame.pack(fill=tk.X, padx=5, pady=2)
537
+
538
+ ttk.Label(numbers_frame, text="Numbers:", width=12).pack(side=tk.LEFT)
539
+ self.pw_numbers_percent_var = tk.IntVar(value=settings.get("numbers_percent", 20))
540
+ numbers_slider = ttk.Scale(numbers_frame, from_=0, to=100, orient=tk.HORIZONTAL,
541
+ variable=self.pw_numbers_percent_var, command=self.update_percentages)
542
+ numbers_slider.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(5, 5))
543
+ numbers_label = ttk.Label(numbers_frame, text="20%", width=5)
544
+ numbers_label.pack(side=tk.RIGHT)
545
+ self.pw_numbers_label = numbers_label
546
+
547
+ # Symbols percentage slider
548
+ symbols_frame = ttk.Frame(distribution_frame)
549
+ symbols_frame.pack(fill=tk.X, padx=5, pady=2)
550
+
551
+ ttk.Label(symbols_frame, text="Symbols:", width=12).pack(side=tk.LEFT)
552
+ self.pw_symbols_percent_var = tk.IntVar(value=settings.get("symbols_percent", 10))
553
+ symbols_slider = ttk.Scale(symbols_frame, from_=0, to=100, orient=tk.HORIZONTAL,
554
+ variable=self.pw_symbols_percent_var, command=self.update_percentages)
555
+ symbols_slider.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(5, 5))
556
+ symbols_label = ttk.Label(symbols_frame, text="10%", width=5)
557
+ symbols_label.pack(side=tk.RIGHT)
558
+ self.pw_symbols_label = symbols_label
559
+
560
+ # Total percentage display
561
+ total_frame = ttk.Frame(distribution_frame)
562
+ total_frame.pack(fill=tk.X, padx=5, pady=2)
563
+
564
+ ttk.Label(total_frame, text="Total:", width=12, font=('TkDefaultFont', 9, 'bold')).pack(side=tk.LEFT)
565
+ self.pw_total_label = ttk.Label(total_frame, text="100%", width=5, font=('TkDefaultFont', 9, 'bold'))
566
+ self.pw_total_label.pack(side=tk.RIGHT)
567
+
568
+ # Numbers setting
569
+ numbers_include_frame = ttk.Frame(tab_frame)
570
+ numbers_include_frame.pack(fill=tk.X, padx=10, pady=5)
571
+
572
+ ttk.Label(numbers_include_frame, text="Must Include Numbers:").pack(side=tk.LEFT)
573
+ self.pw_num_var = tk.StringVar(value=settings.get("numbers", ""))
574
+ numbers_entry = ttk.Entry(numbers_include_frame, textvariable=self.pw_num_var, width=20)
575
+ numbers_entry.pack(side=tk.LEFT, padx=(5, 0))
576
+
577
+ # Symbols setting
578
+ symbols_include_frame = ttk.Frame(tab_frame)
579
+ symbols_include_frame.pack(fill=tk.X, padx=10, pady=5)
580
+
581
+ ttk.Label(symbols_include_frame, text="Must Include Symbols:").pack(side=tk.LEFT)
582
+ self.pw_sym_var = tk.StringVar(value=settings.get("symbols", ""))
583
+ symbols_entry = ttk.Entry(symbols_include_frame, textvariable=self.pw_sym_var, width=20)
584
+ symbols_entry.pack(side=tk.LEFT, padx=(5, 0))
585
+
586
+ # Generate button
587
+ button_frame = ttk.Frame(tab_frame)
588
+ button_frame.pack(fill=tk.X, padx=10, pady=10)
589
+
590
+ generate_btn = ttk.Button(button_frame, text="Generate Password",
591
+ command=lambda: self.generate_password())
592
+ generate_btn.pack(side=tk.LEFT)
593
+
594
+ # Initialize percentage display
595
+ self.update_percentages()
596
+
597
+ def create_repeating_text_tab(self):
598
+ """Create the Repeating Text Generator tab."""
599
+ tab_frame = ttk.Frame(self.notebook)
600
+ self.notebook.add(tab_frame, text="Repeating Text Generator")
601
+
602
+ # Get current settings
603
+ settings = self.main_app.settings["tool_settings"].get("Generator Tools", {}).get("Repeating Text Generator", {})
604
+
605
+ # Times setting
606
+ times_frame = ttk.Frame(tab_frame)
607
+ times_frame.pack(fill=tk.X, padx=10, pady=5)
608
+
609
+ ttk.Label(times_frame, text="Repeat Times:").pack(side=tk.LEFT)
610
+ self.repeat_times_var = tk.StringVar(value=str(settings.get("times", 5)))
611
+ times_entry = ttk.Entry(times_frame, textvariable=self.repeat_times_var, width=10)
612
+ times_entry.pack(side=tk.LEFT, padx=(5, 0))
613
+
614
+ # Separator setting
615
+ separator_frame = ttk.Frame(tab_frame)
616
+ separator_frame.pack(fill=tk.X, padx=10, pady=5)
617
+
618
+ ttk.Label(separator_frame, text="Separator:").pack(side=tk.LEFT)
619
+ self.repeat_sep_var = tk.StringVar(value=settings.get("separator", "+"))
620
+ separator_entry = ttk.Entry(separator_frame, textvariable=self.repeat_sep_var, width=20)
621
+ separator_entry.pack(side=tk.LEFT, padx=(5, 0))
622
+
623
+ # Generate button
624
+ button_frame = ttk.Frame(tab_frame)
625
+ button_frame.pack(fill=tk.X, padx=10, pady=10)
626
+
627
+ repeat_btn = ttk.Button(button_frame, text="Generate Repeated Text",
628
+ command=lambda: self.generate_repeated_text())
629
+ repeat_btn.pack(side=tk.LEFT)
630
+
631
+ def create_lorem_ipsum_tab(self):
632
+ """Create the Lorem Ipsum Generator tab."""
633
+ tab_frame = ttk.Frame(self.notebook)
634
+ self.notebook.add(tab_frame, text="Lorem Ipsum Generator")
635
+
636
+ # Get current settings
637
+ settings = self.main_app.settings["tool_settings"].get("Generator Tools", {}).get("Lorem Ipsum Generator", {})
638
+
639
+ # Count setting
640
+ count_frame = ttk.Frame(tab_frame)
641
+ count_frame.pack(fill=tk.X, padx=10, pady=5)
642
+
643
+ ttk.Label(count_frame, text="Count:").pack(side=tk.LEFT)
644
+ self.lorem_count_var = tk.StringVar(value=str(settings.get("count", 5)))
645
+ count_entry = ttk.Entry(count_frame, textvariable=self.lorem_count_var, width=10)
646
+ count_entry.pack(side=tk.LEFT, padx=(5, 0))
647
+
648
+ # Type selection (radio buttons)
649
+ type_frame = ttk.LabelFrame(tab_frame, text="Type")
650
+ type_frame.pack(fill=tk.X, padx=10, pady=5)
651
+
652
+ self.lorem_type_var = tk.StringVar(value=settings.get("type", "paragraphs"))
653
+
654
+ type_options = [
655
+ ("Words", "words"),
656
+ ("Sentences", "sentences"),
657
+ ("Paragraphs", "paragraphs"),
658
+ ("Bytes", "bytes")
659
+ ]
660
+
661
+ for text, value in type_options:
662
+ ttk.Radiobutton(type_frame, text=text, variable=self.lorem_type_var,
663
+ value=value).pack(side=tk.LEFT, padx=5)
664
+
665
+ # Format selection (radio buttons)
666
+ format_frame = ttk.LabelFrame(tab_frame, text="Format")
667
+ format_frame.pack(fill=tk.X, padx=10, pady=5)
668
+
669
+ self.lorem_format_var = tk.StringVar(value=settings.get("format", "plain"))
670
+
671
+ format_options = [
672
+ ("Plain", "plain"),
673
+ ("HTML", "html"),
674
+ ("Markdown", "markdown"),
675
+ ("JSON", "json")
676
+ ]
677
+
678
+ for text, value in format_options:
679
+ ttk.Radiobutton(format_frame, text=text, variable=self.lorem_format_var,
680
+ value=value).pack(side=tk.LEFT, padx=5)
681
+
682
+ # Ordered checkbox (for lists)
683
+ ordered_frame = ttk.Frame(tab_frame)
684
+ ordered_frame.pack(fill=tk.X, padx=10, pady=5)
685
+
686
+ self.lorem_ordered_var = tk.BooleanVar(value=settings.get("ordered", False))
687
+ ordered_check = ttk.Checkbutton(ordered_frame, text="Ordered (for lists)",
688
+ variable=self.lorem_ordered_var)
689
+ ordered_check.pack(side=tk.LEFT)
690
+
691
+ # Generate button
692
+ button_frame = ttk.Frame(tab_frame)
693
+ button_frame.pack(fill=tk.X, padx=10, pady=10)
694
+
695
+ lorem_btn = ttk.Button(button_frame, text="Generate",
696
+ command=lambda: self.generate_lorem_ipsum())
697
+ lorem_btn.pack(side=tk.LEFT)
698
+
699
+ def create_uuid_generator_tab(self):
700
+ """Create the UUID/GUID Generator tab."""
701
+ tab_frame = ttk.Frame(self.notebook)
702
+ self.notebook.add(tab_frame, text="UUID/GUID Generator")
703
+
704
+ # Get current settings
705
+ settings = self.main_app.settings["tool_settings"].get("Generator Tools", {}).get("UUID/GUID Generator", {})
706
+
707
+ # Create top row frame for UUID Version and Name-based settings
708
+ top_row_frame = ttk.Frame(tab_frame)
709
+ top_row_frame.pack(fill=tk.X, padx=10, pady=5)
710
+
711
+ # UUID Version selection (left side)
712
+ version_frame = ttk.LabelFrame(top_row_frame, text="UUID Version")
713
+ version_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5))
714
+
715
+ self.uuid_version_var = tk.IntVar(value=settings.get("version", 4))
716
+
717
+ version_options = [
718
+ ("Version 1 (Time-based)", 1),
719
+ ("Version 3 (MD5 Name-based)", 3),
720
+ ("Version 4 (Random)", 4),
721
+ ("Version 5 (SHA-1 Name-based)", 5)
722
+ ]
723
+
724
+ for text, value in version_options:
725
+ ttk.Radiobutton(version_frame, text=text, variable=self.uuid_version_var,
726
+ value=value, command=self.update_uuid_fields).pack(anchor=tk.W, padx=5, pady=2)
727
+
728
+ # Name-based UUID settings (right side, for versions 3 and 5)
729
+ self.uuid_namebased_frame = ttk.LabelFrame(top_row_frame, text="Name-based Settings")
730
+ self.uuid_namebased_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0))
731
+
732
+ # Namespace selection
733
+ namespace_frame = ttk.Frame(self.uuid_namebased_frame)
734
+ namespace_frame.pack(fill=tk.X, padx=5, pady=2)
735
+
736
+ ttk.Label(namespace_frame, text="Namespace:").pack(side=tk.LEFT)
737
+ self.uuid_namespace_var = tk.StringVar(value=settings.get("namespace", "dns"))
738
+ namespace_combo = ttk.Combobox(namespace_frame, textvariable=self.uuid_namespace_var,
739
+ values=["dns", "url", "oid", "x500"], state="readonly", width=15)
740
+ namespace_combo.pack(side=tk.LEFT, padx=(5, 0))
741
+
742
+ # Name input
743
+ name_frame = ttk.Frame(self.uuid_namebased_frame)
744
+ name_frame.pack(fill=tk.X, padx=5, pady=2)
745
+
746
+ ttk.Label(name_frame, text="Name:").pack(side=tk.LEFT)
747
+ self.uuid_name_var = tk.StringVar(value=settings.get("name", ""))
748
+ name_entry = ttk.Entry(name_frame, textvariable=self.uuid_name_var, width=25)
749
+ name_entry.pack(side=tk.LEFT, padx=(5, 0), fill=tk.X, expand=True)
750
+
751
+ # Output Format selection
752
+ format_frame = ttk.LabelFrame(tab_frame, text="Output Format")
753
+ format_frame.pack(fill=tk.X, padx=10, pady=5)
754
+
755
+ self.uuid_format_var = tk.StringVar(value=settings.get("format", "standard"))
756
+
757
+ format_options = [
758
+ ("Standard (8-4-4-4-12)", "standard"),
759
+ ("Hex (32 chars)", "hex"),
760
+ ("Microsoft GUID {}", "microsoft"),
761
+ ("URN format", "urn"),
762
+ ("Base64", "base64"),
763
+ ("C Array", "c_array"),
764
+ ("Nil UUID", "nil")
765
+ ]
766
+
767
+ # Create two columns for format options
768
+ format_left = ttk.Frame(format_frame)
769
+ format_left.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
770
+ format_right = ttk.Frame(format_frame)
771
+ format_right.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
772
+
773
+ for i, (text, value) in enumerate(format_options):
774
+ parent = format_left if i < 4 else format_right
775
+ ttk.Radiobutton(parent, text=text, variable=self.uuid_format_var,
776
+ value=value).pack(anchor=tk.W, padx=5, pady=1)
777
+
778
+ # Case selection
779
+ case_frame = ttk.LabelFrame(tab_frame, text="Case")
780
+ case_frame.pack(fill=tk.X, padx=10, pady=5)
781
+
782
+ self.uuid_case_var = tk.StringVar(value=settings.get("case", "lowercase"))
783
+
784
+ case_options = [
785
+ ("Lowercase", "lowercase"),
786
+ ("Uppercase", "uppercase")
787
+ ]
788
+
789
+ for text, value in case_options:
790
+ ttk.Radiobutton(case_frame, text=text, variable=self.uuid_case_var,
791
+ value=value).pack(side=tk.LEFT, padx=5)
792
+
793
+ # Count setting
794
+ count_frame = ttk.Frame(tab_frame)
795
+ count_frame.pack(fill=tk.X, padx=10, pady=5)
796
+
797
+ ttk.Label(count_frame, text="Count:").pack(side=tk.LEFT)
798
+ self.uuid_count_var = tk.StringVar(value=str(settings.get("count", 1)))
799
+ count_entry = ttk.Entry(count_frame, textvariable=self.uuid_count_var, width=10)
800
+ count_entry.pack(side=tk.LEFT, padx=(5, 0))
801
+
802
+ # Generate button
803
+ button_frame = ttk.Frame(tab_frame)
804
+ button_frame.pack(fill=tk.X, padx=10, pady=10)
805
+
806
+ uuid_btn = ttk.Button(button_frame, text="Generate",
807
+ command=lambda: self.generate_uuid())
808
+ uuid_btn.pack(side=tk.LEFT)
809
+
810
+ # Initialize field visibility
811
+ self.update_uuid_fields()
812
+
813
+ def create_email_generator_tab(self):
814
+ """Create the Random Email Generator tab."""
815
+ tab_frame = ttk.Frame(self.notebook)
816
+ self.notebook.add(tab_frame, text="Random Email Generator")
817
+
818
+ # Get current settings
819
+ settings = self.main_app.settings["tool_settings"].get("Generator Tools", {}).get("Random Email Generator", {})
820
+
821
+ # Count setting
822
+ count_frame = ttk.Frame(tab_frame)
823
+ count_frame.pack(fill=tk.X, padx=10, pady=5)
824
+
825
+ ttk.Label(count_frame, text="Count:").pack(side=tk.LEFT)
826
+ self.email_count_var = tk.StringVar(value=str(settings.get("count", 5)))
827
+ count_entry = ttk.Entry(count_frame, textvariable=self.email_count_var, width=10)
828
+ count_entry.pack(side=tk.LEFT, padx=(5, 0))
829
+
830
+ # Separator type selection (radio buttons)
831
+ separator_type_frame = ttk.LabelFrame(tab_frame, text="Output Format")
832
+ separator_type_frame.pack(fill=tk.X, padx=10, pady=5)
833
+
834
+ self.email_separator_type_var = tk.StringVar(value=settings.get("separator_type", "list"))
835
+
836
+ separator_type_options = [
837
+ ("List (\\n separator)", "list"),
838
+ ("Custom Separator", "custom")
839
+ ]
840
+
841
+ for text, value in separator_type_options:
842
+ ttk.Radiobutton(separator_type_frame, text=text, variable=self.email_separator_type_var,
843
+ value=value, command=self.update_email_separator_field).pack(side=tk.LEFT, padx=5)
844
+
845
+ # Custom separator field
846
+ self.email_separator_frame = ttk.Frame(tab_frame)
847
+ self.email_separator_frame.pack(fill=tk.X, padx=10, pady=5)
848
+
849
+ ttk.Label(self.email_separator_frame, text="Separator:").pack(side=tk.LEFT)
850
+ self.email_separator_var = tk.StringVar(value=settings.get("separator", ","))
851
+ separator_entry = ttk.Entry(self.email_separator_frame, textvariable=self.email_separator_var, width=10)
852
+ separator_entry.pack(side=tk.LEFT, padx=(5, 0))
853
+
854
+ # Domain type selection (radio buttons)
855
+ domain_type_frame = ttk.LabelFrame(tab_frame, text="Domain")
856
+ domain_type_frame.pack(fill=tk.X, padx=10, pady=5)
857
+
858
+ self.email_domain_type_var = tk.StringVar(value=settings.get("domain_type", "random"))
859
+
860
+ domain_type_options = [
861
+ ("Random", "random"),
862
+ ("Custom Domain", "custom")
863
+ ]
864
+
865
+ for text, value in domain_type_options:
866
+ ttk.Radiobutton(domain_type_frame, text=text, variable=self.email_domain_type_var,
867
+ value=value, command=self.update_email_domain_field).pack(side=tk.LEFT, padx=5)
868
+
869
+ # Custom domain field
870
+ self.email_domain_frame = ttk.Frame(tab_frame)
871
+ self.email_domain_frame.pack(fill=tk.X, padx=10, pady=5)
872
+
873
+ ttk.Label(self.email_domain_frame, text="Domain:").pack(side=tk.LEFT)
874
+ self.email_domain_var = tk.StringVar(value=settings.get("domain", "example.com"))
875
+ domain_entry = ttk.Entry(self.email_domain_frame, textvariable=self.email_domain_var, width=30)
876
+ domain_entry.pack(side=tk.LEFT, padx=(5, 0))
877
+
878
+ # Generate button
879
+ button_frame = ttk.Frame(tab_frame)
880
+ button_frame.pack(fill=tk.X, padx=10, pady=10)
881
+
882
+ email_btn = ttk.Button(button_frame, text="Generate",
883
+ command=lambda: self.generate_emails())
884
+ email_btn.pack(side=tk.LEFT)
885
+
886
+ # Initialize field visibility
887
+ self.update_email_separator_field()
888
+ self.update_email_domain_field()
889
+
890
+ def update_uuid_fields(self):
891
+ """Update UUID field visibility based on selected version."""
892
+ if hasattr(self, 'uuid_version_var') and hasattr(self, 'uuid_namebased_frame'):
893
+ version = self.uuid_version_var.get()
894
+ if version in [3, 5]:
895
+ # Show name-based settings for versions 3 and 5
896
+ self.uuid_namebased_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0))
897
+ else:
898
+ # Hide name-based settings for versions 1 and 4
899
+ self.uuid_namebased_frame.pack_forget()
900
+
901
+ def update_email_separator_field(self):
902
+ """Update email separator field visibility based on selected type."""
903
+ if hasattr(self, 'email_separator_type_var') and hasattr(self, 'email_separator_frame'):
904
+ separator_type = self.email_separator_type_var.get()
905
+ if separator_type == "custom":
906
+ # Show separator field for custom separator
907
+ self.email_separator_frame.pack(fill=tk.X, padx=10, pady=5)
908
+ else:
909
+ # Hide separator field for list format
910
+ self.email_separator_frame.pack_forget()
911
+
912
+ def update_email_domain_field(self):
913
+ """Update email domain field visibility based on selected type."""
914
+ if hasattr(self, 'email_domain_type_var') and hasattr(self, 'email_domain_frame'):
915
+ domain_type = self.email_domain_type_var.get()
916
+ if domain_type == "custom":
917
+ # Show domain field for custom domain
918
+ self.email_domain_frame.pack(fill=tk.X, padx=10, pady=5)
919
+ else:
920
+ # Hide domain field for random domain
921
+ self.email_domain_frame.pack_forget()
922
+
923
+ def update_percentages(self, *args):
924
+ """Update percentage labels and ensure they add up to 100%."""
925
+ if hasattr(self, 'pw_letters_percent_var'):
926
+ letters_percent = self.pw_letters_percent_var.get()
927
+ numbers_percent = self.pw_numbers_percent_var.get()
928
+ symbols_percent = self.pw_symbols_percent_var.get()
929
+
930
+ total = letters_percent + numbers_percent + symbols_percent
931
+
932
+ # Update labels
933
+ self.pw_letters_label.config(text=f"{letters_percent}%")
934
+ self.pw_numbers_label.config(text=f"{numbers_percent}%")
935
+ self.pw_symbols_label.config(text=f"{symbols_percent}%")
936
+
937
+ # Update total label with color coding
938
+ if total == 100:
939
+ self.pw_total_label.config(text=f"{total}%", foreground="green")
940
+ else:
941
+ self.pw_total_label.config(text=f"{total}%", foreground="red")
942
+
943
+ def generate_password(self):
944
+ """Generate a password and update the output."""
945
+ try:
946
+ length = int(self.pw_len_var.get())
947
+ numbers = self.pw_num_var.get()
948
+ symbols = self.pw_sym_var.get()
949
+ letters_percent = self.pw_letters_percent_var.get()
950
+ numbers_percent = self.pw_numbers_percent_var.get()
951
+ symbols_percent = self.pw_symbols_percent_var.get()
952
+
953
+ settings = {
954
+ "length": length,
955
+ "numbers": numbers,
956
+ "symbols": symbols,
957
+ "letters_percent": letters_percent,
958
+ "numbers_percent": numbers_percent,
959
+ "symbols_percent": symbols_percent
960
+ }
961
+
962
+ result = self.generator_tools.strong_password("", settings)
963
+ self.main_app.update_output_text(result)
964
+
965
+ # Save settings
966
+ self.save_settings()
967
+
968
+ except ValueError:
969
+ self.main_app.update_output_text("Error: Invalid password length")
970
+
971
+ def generate_repeated_text(self):
972
+ """Generate repeated text and update the output."""
973
+ try:
974
+ # Get input text
975
+ active_input_tab = self.main_app.input_tabs[self.main_app.input_notebook.index(self.main_app.input_notebook.select())]
976
+ input_text = active_input_tab.text.get("1.0", tk.END).strip()
977
+
978
+ if not input_text:
979
+ self.main_app.update_output_text("Error: No input text provided")
980
+ return
981
+
982
+ times = int(self.repeat_times_var.get())
983
+ separator = self.repeat_sep_var.get()
984
+
985
+ settings = {
986
+ "times": times,
987
+ "separator": separator
988
+ }
989
+
990
+ result = self.generator_tools.repeating_text(input_text, settings)
991
+ self.main_app.update_output_text(result)
992
+
993
+ # Save settings
994
+ self.save_settings()
995
+
996
+ except ValueError:
997
+ self.main_app.update_output_text("Error: Invalid repeat times value")
998
+
999
+ def generate_lorem_ipsum(self):
1000
+ """Generate Lorem Ipsum text and update the output."""
1001
+ try:
1002
+ count = int(self.lorem_count_var.get())
1003
+ text_type = self.lorem_type_var.get()
1004
+ format_type = self.lorem_format_var.get()
1005
+ ordered = self.lorem_ordered_var.get()
1006
+
1007
+ settings = {
1008
+ "count": count,
1009
+ "type": text_type,
1010
+ "format": format_type,
1011
+ "ordered": ordered
1012
+ }
1013
+
1014
+ result = self.generator_tools.lorem_ipsum("", settings)
1015
+ self.main_app.update_output_text(result)
1016
+
1017
+ # Save settings
1018
+ self.save_settings()
1019
+
1020
+ except ValueError:
1021
+ self.main_app.update_output_text("Error: Invalid count value")
1022
+
1023
+ def generate_uuid(self):
1024
+ """Generate UUID/GUID and update the output."""
1025
+ try:
1026
+ version = self.uuid_version_var.get()
1027
+ format_type = self.uuid_format_var.get()
1028
+ case = self.uuid_case_var.get()
1029
+ count = int(self.uuid_count_var.get())
1030
+ namespace = self.uuid_namespace_var.get()
1031
+ name = self.uuid_name_var.get()
1032
+
1033
+ settings = {
1034
+ "version": version,
1035
+ "format": format_type,
1036
+ "case": case,
1037
+ "count": count,
1038
+ "namespace": namespace,
1039
+ "name": name
1040
+ }
1041
+
1042
+ result = self.generator_tools.uuid_generator("", settings)
1043
+ self.main_app.update_output_text(result)
1044
+
1045
+ # Save settings
1046
+ self.save_settings()
1047
+
1048
+ except ValueError:
1049
+ self.main_app.update_output_text("Error: Invalid count value")
1050
+
1051
+ def generate_emails(self):
1052
+ """Generate random emails and update the output."""
1053
+ try:
1054
+ count = int(self.email_count_var.get())
1055
+ separator_type = self.email_separator_type_var.get()
1056
+ separator = self.email_separator_var.get()
1057
+ domain_type = self.email_domain_type_var.get()
1058
+ domain = self.email_domain_var.get()
1059
+
1060
+ settings = {
1061
+ "count": count,
1062
+ "separator_type": separator_type,
1063
+ "separator": separator,
1064
+ "domain_type": domain_type,
1065
+ "domain": domain
1066
+ }
1067
+
1068
+ result = self.generator_tools.random_email_generator("", settings)
1069
+ self.main_app.update_output_text(result)
1070
+
1071
+ # Save settings
1072
+ self.save_settings()
1073
+
1074
+ except ValueError:
1075
+ self.main_app.update_output_text("Error: Invalid count value")
1076
+
1077
+ def save_settings(self):
1078
+ """Save current settings to the main app."""
1079
+ if not self.main_app:
1080
+ return
1081
+
1082
+ # Ensure the Generator Tools settings exist
1083
+ if "Generator Tools" not in self.main_app.settings["tool_settings"]:
1084
+ self.main_app.settings["tool_settings"]["Generator Tools"] = {}
1085
+
1086
+ # Save Strong Password Generator settings
1087
+ try:
1088
+ self.main_app.settings["tool_settings"]["Generator Tools"]["Strong Password Generator"] = {
1089
+ "length": int(self.pw_len_var.get()),
1090
+ "numbers": self.pw_num_var.get(),
1091
+ "symbols": self.pw_sym_var.get(),
1092
+ "letters_percent": self.pw_letters_percent_var.get(),
1093
+ "numbers_percent": self.pw_numbers_percent_var.get(),
1094
+ "symbols_percent": self.pw_symbols_percent_var.get()
1095
+ }
1096
+ except (ValueError, AttributeError):
1097
+ pass
1098
+
1099
+ # Save Repeating Text Generator settings
1100
+ try:
1101
+ self.main_app.settings["tool_settings"]["Generator Tools"]["Repeating Text Generator"] = {
1102
+ "times": int(self.repeat_times_var.get()),
1103
+ "separator": self.repeat_sep_var.get()
1104
+ }
1105
+ except (ValueError, AttributeError):
1106
+ pass
1107
+
1108
+ # Save Lorem Ipsum Generator settings
1109
+ try:
1110
+ self.main_app.settings["tool_settings"]["Generator Tools"]["Lorem Ipsum Generator"] = {
1111
+ "count": int(self.lorem_count_var.get()),
1112
+ "type": self.lorem_type_var.get(),
1113
+ "format": self.lorem_format_var.get(),
1114
+ "ordered": self.lorem_ordered_var.get()
1115
+ }
1116
+ except (ValueError, AttributeError):
1117
+ pass
1118
+
1119
+ # Save UUID/GUID Generator settings
1120
+ try:
1121
+ self.main_app.settings["tool_settings"]["Generator Tools"]["UUID/GUID Generator"] = {
1122
+ "version": self.uuid_version_var.get(),
1123
+ "format": self.uuid_format_var.get(),
1124
+ "case": self.uuid_case_var.get(),
1125
+ "count": int(self.uuid_count_var.get()),
1126
+ "namespace": self.uuid_namespace_var.get(),
1127
+ "name": self.uuid_name_var.get()
1128
+ }
1129
+ except (ValueError, AttributeError):
1130
+ pass
1131
+
1132
+ # Save Random Email Generator settings
1133
+ try:
1134
+ self.main_app.settings["tool_settings"]["Generator Tools"]["Random Email Generator"] = {
1135
+ "count": int(self.email_count_var.get()),
1136
+ "separator_type": self.email_separator_type_var.get(),
1137
+ "separator": self.email_separator_var.get(),
1138
+ "domain_type": self.email_domain_type_var.get(),
1139
+ "domain": self.email_domain_var.get()
1140
+ }
1141
+ except (ValueError, AttributeError):
1142
+ pass
1143
+
1144
+ # Save ASCII Art Generator settings
1145
+ try:
1146
+ if hasattr(self.main_app, 'ascii_art_generator') and self.main_app.ascii_art_generator:
1147
+ if "ASCII Art Generator" not in self.main_app.settings["tool_settings"]:
1148
+ self.main_app.settings["tool_settings"]["ASCII Art Generator"] = {}
1149
+ # Settings are saved by the widget itself
1150
+ except (ValueError, AttributeError):
1151
+ pass
1152
+
1153
+ # Save Hash Generator settings
1154
+ try:
1155
+ if hasattr(self.main_app, 'hash_generator') and self.main_app.hash_generator:
1156
+ if "Hash Generator" not in self.main_app.settings["tool_settings"]:
1157
+ self.main_app.settings["tool_settings"]["Hash Generator"] = {}
1158
+ # Settings are saved by the widget itself
1159
+ except (ValueError, AttributeError):
1160
+ pass
1161
+
1162
+ # Save Slug Generator settings
1163
+ try:
1164
+ if hasattr(self.main_app, 'slug_generator') and self.main_app.slug_generator:
1165
+ if "Slug Generator" not in self.main_app.settings["tool_settings"]:
1166
+ self.main_app.settings["tool_settings"]["Slug Generator"] = {}
1167
+ # Settings are saved by the widget itself
1168
+ except (ValueError, AttributeError):
1169
+ pass
1170
+
1171
+ # Save settings to file
1172
+ self.main_app.save_settings()
1173
+
1174
+ def create_ascii_art_generator_tab(self):
1175
+ """Create the ASCII Art Generator tab."""
1176
+ tab_frame = ttk.Frame(self.notebook)
1177
+ self.notebook.add(tab_frame, text="ASCII Art Generator")
1178
+
1179
+ try:
1180
+ from tools.ascii_art_generator import ASCIIArtGenerator
1181
+ if hasattr(self.main_app, 'ascii_art_generator') and self.main_app.ascii_art_generator:
1182
+ widget = self.main_app.ascii_art_generator.create_widget(tab_frame, self.main_app)
1183
+ widget.pack(fill=tk.BOTH, expand=True)
1184
+ else:
1185
+ ttk.Label(tab_frame, text="ASCII Art Generator module not available").pack(padx=10, pady=10)
1186
+ except ImportError:
1187
+ ttk.Label(tab_frame, text="ASCII Art Generator module not available").pack(padx=10, pady=10)
1188
+
1189
+ def create_hash_generator_tab(self):
1190
+ """Create the Hash Generator tab."""
1191
+ tab_frame = ttk.Frame(self.notebook)
1192
+ self.notebook.add(tab_frame, text="Hash Generator")
1193
+
1194
+ try:
1195
+ from tools.hash_generator import HashGenerator
1196
+ if hasattr(self.main_app, 'hash_generator') and self.main_app.hash_generator:
1197
+ widget = self.main_app.hash_generator.create_widget(tab_frame, self.main_app)
1198
+ widget.pack(fill=tk.BOTH, expand=True)
1199
+ else:
1200
+ ttk.Label(tab_frame, text="Hash Generator module not available").pack(padx=10, pady=10)
1201
+ except ImportError:
1202
+ ttk.Label(tab_frame, text="Hash Generator module not available").pack(padx=10, pady=10)
1203
+
1204
+ def create_slug_generator_tab(self):
1205
+ """Create the Slug Generator tab."""
1206
+ tab_frame = ttk.Frame(self.notebook)
1207
+ self.notebook.add(tab_frame, text="Slug Generator")
1208
+
1209
+ try:
1210
+ from tools.slug_generator import SlugGenerator
1211
+ if hasattr(self.main_app, 'slug_generator') and self.main_app.slug_generator:
1212
+ widget = self.main_app.slug_generator.create_widget(tab_frame, self.main_app)
1213
+ widget.pack(fill=tk.BOTH, expand=True)
1214
+ else:
1215
+ ttk.Label(tab_frame, text="Slug Generator module not available").pack(padx=10, pady=10)
1216
+ except ImportError:
1217
1217
  ttk.Label(tab_frame, text="Slug Generator module not available").pack(padx=10, pady=10)