pomera-ai-commander 0.1.0

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 (192) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +680 -0
  3. package/bin/pomera-ai-commander.js +62 -0
  4. package/core/__init__.py +66 -0
  5. package/core/__pycache__/__init__.cpython-313.pyc +0 -0
  6. package/core/__pycache__/app_context.cpython-313.pyc +0 -0
  7. package/core/__pycache__/async_text_processor.cpython-313.pyc +0 -0
  8. package/core/__pycache__/backup_manager.cpython-313.pyc +0 -0
  9. package/core/__pycache__/backup_recovery_manager.cpython-313.pyc +0 -0
  10. package/core/__pycache__/content_hash_cache.cpython-313.pyc +0 -0
  11. package/core/__pycache__/context_menu.cpython-313.pyc +0 -0
  12. package/core/__pycache__/data_validator.cpython-313.pyc +0 -0
  13. package/core/__pycache__/database_connection_manager.cpython-313.pyc +0 -0
  14. package/core/__pycache__/database_curl_settings_manager.cpython-313.pyc +0 -0
  15. package/core/__pycache__/database_promera_ai_settings_manager.cpython-313.pyc +0 -0
  16. package/core/__pycache__/database_schema.cpython-313.pyc +0 -0
  17. package/core/__pycache__/database_schema_manager.cpython-313.pyc +0 -0
  18. package/core/__pycache__/database_settings_manager.cpython-313.pyc +0 -0
  19. package/core/__pycache__/database_settings_manager_interface.cpython-313.pyc +0 -0
  20. package/core/__pycache__/dialog_manager.cpython-313.pyc +0 -0
  21. package/core/__pycache__/efficient_line_numbers.cpython-313.pyc +0 -0
  22. package/core/__pycache__/error_handler.cpython-313.pyc +0 -0
  23. package/core/__pycache__/error_service.cpython-313.pyc +0 -0
  24. package/core/__pycache__/event_consolidator.cpython-313.pyc +0 -0
  25. package/core/__pycache__/memory_efficient_text_widget.cpython-313.pyc +0 -0
  26. package/core/__pycache__/migration_manager.cpython-313.pyc +0 -0
  27. package/core/__pycache__/migration_test_suite.cpython-313.pyc +0 -0
  28. package/core/__pycache__/migration_validator.cpython-313.pyc +0 -0
  29. package/core/__pycache__/optimized_find_replace.cpython-313.pyc +0 -0
  30. package/core/__pycache__/optimized_pattern_engine.cpython-313.pyc +0 -0
  31. package/core/__pycache__/optimized_search_highlighter.cpython-313.pyc +0 -0
  32. package/core/__pycache__/performance_monitor.cpython-313.pyc +0 -0
  33. package/core/__pycache__/persistence_manager.cpython-313.pyc +0 -0
  34. package/core/__pycache__/progressive_stats_calculator.cpython-313.pyc +0 -0
  35. package/core/__pycache__/regex_pattern_cache.cpython-313.pyc +0 -0
  36. package/core/__pycache__/regex_pattern_library.cpython-313.pyc +0 -0
  37. package/core/__pycache__/search_operation_manager.cpython-313.pyc +0 -0
  38. package/core/__pycache__/settings_defaults_registry.cpython-313.pyc +0 -0
  39. package/core/__pycache__/settings_integrity_validator.cpython-313.pyc +0 -0
  40. package/core/__pycache__/settings_serializer.cpython-313.pyc +0 -0
  41. package/core/__pycache__/settings_validator.cpython-313.pyc +0 -0
  42. package/core/__pycache__/smart_stats_calculator.cpython-313.pyc +0 -0
  43. package/core/__pycache__/statistics_update_manager.cpython-313.pyc +0 -0
  44. package/core/__pycache__/stats_config_manager.cpython-313.pyc +0 -0
  45. package/core/__pycache__/streaming_text_handler.cpython-313.pyc +0 -0
  46. package/core/__pycache__/task_scheduler.cpython-313.pyc +0 -0
  47. package/core/__pycache__/visibility_monitor.cpython-313.pyc +0 -0
  48. package/core/__pycache__/widget_cache.cpython-313.pyc +0 -0
  49. package/core/app_context.py +482 -0
  50. package/core/async_text_processor.py +422 -0
  51. package/core/backup_manager.py +656 -0
  52. package/core/backup_recovery_manager.py +1034 -0
  53. package/core/content_hash_cache.py +509 -0
  54. package/core/context_menu.py +313 -0
  55. package/core/data_validator.py +1067 -0
  56. package/core/database_connection_manager.py +745 -0
  57. package/core/database_curl_settings_manager.py +609 -0
  58. package/core/database_promera_ai_settings_manager.py +447 -0
  59. package/core/database_schema.py +412 -0
  60. package/core/database_schema_manager.py +396 -0
  61. package/core/database_settings_manager.py +1508 -0
  62. package/core/database_settings_manager_interface.py +457 -0
  63. package/core/dialog_manager.py +735 -0
  64. package/core/efficient_line_numbers.py +511 -0
  65. package/core/error_handler.py +747 -0
  66. package/core/error_service.py +431 -0
  67. package/core/event_consolidator.py +512 -0
  68. package/core/mcp/__init__.py +43 -0
  69. package/core/mcp/__pycache__/__init__.cpython-313.pyc +0 -0
  70. package/core/mcp/__pycache__/protocol.cpython-313.pyc +0 -0
  71. package/core/mcp/__pycache__/schema.cpython-313.pyc +0 -0
  72. package/core/mcp/__pycache__/server_stdio.cpython-313.pyc +0 -0
  73. package/core/mcp/__pycache__/tool_registry.cpython-313.pyc +0 -0
  74. package/core/mcp/protocol.py +288 -0
  75. package/core/mcp/schema.py +251 -0
  76. package/core/mcp/server_stdio.py +299 -0
  77. package/core/mcp/tool_registry.py +2345 -0
  78. package/core/memory_efficient_text_widget.py +712 -0
  79. package/core/migration_manager.py +915 -0
  80. package/core/migration_test_suite.py +1086 -0
  81. package/core/migration_validator.py +1144 -0
  82. package/core/optimized_find_replace.py +715 -0
  83. package/core/optimized_pattern_engine.py +424 -0
  84. package/core/optimized_search_highlighter.py +553 -0
  85. package/core/performance_monitor.py +675 -0
  86. package/core/persistence_manager.py +713 -0
  87. package/core/progressive_stats_calculator.py +632 -0
  88. package/core/regex_pattern_cache.py +530 -0
  89. package/core/regex_pattern_library.py +351 -0
  90. package/core/search_operation_manager.py +435 -0
  91. package/core/settings_defaults_registry.py +1087 -0
  92. package/core/settings_integrity_validator.py +1112 -0
  93. package/core/settings_serializer.py +558 -0
  94. package/core/settings_validator.py +1824 -0
  95. package/core/smart_stats_calculator.py +710 -0
  96. package/core/statistics_update_manager.py +619 -0
  97. package/core/stats_config_manager.py +858 -0
  98. package/core/streaming_text_handler.py +723 -0
  99. package/core/task_scheduler.py +596 -0
  100. package/core/update_pattern_library.py +169 -0
  101. package/core/visibility_monitor.py +596 -0
  102. package/core/widget_cache.py +498 -0
  103. package/mcp.json +61 -0
  104. package/package.json +57 -0
  105. package/pomera.py +7483 -0
  106. package/pomera_mcp_server.py +144 -0
  107. package/tools/__init__.py +5 -0
  108. package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  109. package/tools/__pycache__/ai_tools.cpython-313.pyc +0 -0
  110. package/tools/__pycache__/ascii_art_generator.cpython-313.pyc +0 -0
  111. package/tools/__pycache__/base64_tools.cpython-313.pyc +0 -0
  112. package/tools/__pycache__/base_tool.cpython-313.pyc +0 -0
  113. package/tools/__pycache__/case_tool.cpython-313.pyc +0 -0
  114. package/tools/__pycache__/column_tools.cpython-313.pyc +0 -0
  115. package/tools/__pycache__/cron_tool.cpython-313.pyc +0 -0
  116. package/tools/__pycache__/curl_history.cpython-313.pyc +0 -0
  117. package/tools/__pycache__/curl_processor.cpython-313.pyc +0 -0
  118. package/tools/__pycache__/curl_settings.cpython-313.pyc +0 -0
  119. package/tools/__pycache__/curl_tool.cpython-313.pyc +0 -0
  120. package/tools/__pycache__/diff_viewer.cpython-313.pyc +0 -0
  121. package/tools/__pycache__/email_extraction_tool.cpython-313.pyc +0 -0
  122. package/tools/__pycache__/email_header_analyzer.cpython-313.pyc +0 -0
  123. package/tools/__pycache__/extraction_tools.cpython-313.pyc +0 -0
  124. package/tools/__pycache__/find_replace.cpython-313.pyc +0 -0
  125. package/tools/__pycache__/folder_file_reporter.cpython-313.pyc +0 -0
  126. package/tools/__pycache__/folder_file_reporter_adapter.cpython-313.pyc +0 -0
  127. package/tools/__pycache__/generator_tools.cpython-313.pyc +0 -0
  128. package/tools/__pycache__/hash_generator.cpython-313.pyc +0 -0
  129. package/tools/__pycache__/html_tool.cpython-313.pyc +0 -0
  130. package/tools/__pycache__/huggingface_helper.cpython-313.pyc +0 -0
  131. package/tools/__pycache__/jsonxml_tool.cpython-313.pyc +0 -0
  132. package/tools/__pycache__/line_tools.cpython-313.pyc +0 -0
  133. package/tools/__pycache__/list_comparator.cpython-313.pyc +0 -0
  134. package/tools/__pycache__/markdown_tools.cpython-313.pyc +0 -0
  135. package/tools/__pycache__/mcp_widget.cpython-313.pyc +0 -0
  136. package/tools/__pycache__/notes_widget.cpython-313.pyc +0 -0
  137. package/tools/__pycache__/number_base_converter.cpython-313.pyc +0 -0
  138. package/tools/__pycache__/regex_extractor.cpython-313.pyc +0 -0
  139. package/tools/__pycache__/slug_generator.cpython-313.pyc +0 -0
  140. package/tools/__pycache__/sorter_tools.cpython-313.pyc +0 -0
  141. package/tools/__pycache__/string_escape_tool.cpython-313.pyc +0 -0
  142. package/tools/__pycache__/text_statistics_tool.cpython-313.pyc +0 -0
  143. package/tools/__pycache__/text_wrapper.cpython-313.pyc +0 -0
  144. package/tools/__pycache__/timestamp_converter.cpython-313.pyc +0 -0
  145. package/tools/__pycache__/tool_loader.cpython-313.pyc +0 -0
  146. package/tools/__pycache__/translator_tools.cpython-313.pyc +0 -0
  147. package/tools/__pycache__/url_link_extractor.cpython-313.pyc +0 -0
  148. package/tools/__pycache__/url_parser.cpython-313.pyc +0 -0
  149. package/tools/__pycache__/whitespace_tools.cpython-313.pyc +0 -0
  150. package/tools/__pycache__/word_frequency_counter.cpython-313.pyc +0 -0
  151. package/tools/ai_tools.py +2892 -0
  152. package/tools/ascii_art_generator.py +353 -0
  153. package/tools/base64_tools.py +184 -0
  154. package/tools/base_tool.py +511 -0
  155. package/tools/case_tool.py +309 -0
  156. package/tools/column_tools.py +396 -0
  157. package/tools/cron_tool.py +885 -0
  158. package/tools/curl_history.py +601 -0
  159. package/tools/curl_processor.py +1208 -0
  160. package/tools/curl_settings.py +503 -0
  161. package/tools/curl_tool.py +5467 -0
  162. package/tools/diff_viewer.py +1072 -0
  163. package/tools/email_extraction_tool.py +249 -0
  164. package/tools/email_header_analyzer.py +426 -0
  165. package/tools/extraction_tools.py +250 -0
  166. package/tools/find_replace.py +1751 -0
  167. package/tools/folder_file_reporter.py +1463 -0
  168. package/tools/folder_file_reporter_adapter.py +480 -0
  169. package/tools/generator_tools.py +1217 -0
  170. package/tools/hash_generator.py +256 -0
  171. package/tools/html_tool.py +657 -0
  172. package/tools/huggingface_helper.py +449 -0
  173. package/tools/jsonxml_tool.py +730 -0
  174. package/tools/line_tools.py +419 -0
  175. package/tools/list_comparator.py +720 -0
  176. package/tools/markdown_tools.py +562 -0
  177. package/tools/mcp_widget.py +1417 -0
  178. package/tools/notes_widget.py +973 -0
  179. package/tools/number_base_converter.py +373 -0
  180. package/tools/regex_extractor.py +572 -0
  181. package/tools/slug_generator.py +311 -0
  182. package/tools/sorter_tools.py +459 -0
  183. package/tools/string_escape_tool.py +393 -0
  184. package/tools/text_statistics_tool.py +366 -0
  185. package/tools/text_wrapper.py +431 -0
  186. package/tools/timestamp_converter.py +422 -0
  187. package/tools/tool_loader.py +710 -0
  188. package/tools/translator_tools.py +523 -0
  189. package/tools/url_link_extractor.py +262 -0
  190. package/tools/url_parser.py +205 -0
  191. package/tools/whitespace_tools.py +356 -0
  192. package/tools/word_frequency_counter.py +147 -0
@@ -0,0 +1,419 @@
1
+ """
2
+ Line Tools Module - Line manipulation utilities
3
+
4
+ This module provides comprehensive line manipulation functionality with a tabbed UI interface
5
+ for the Pomera AI Commander application.
6
+
7
+ Features:
8
+ - Remove Duplicates: Remove duplicate lines (keep first/last, case-sensitive option)
9
+ - Remove Empty Lines: Remove blank/whitespace-only lines
10
+ - Add Line Numbers: Prefix lines with numbers
11
+ - Remove Line Numbers: Strip leading numbers from lines
12
+ - Reverse Lines: Reverse line order
13
+ - Shuffle Lines: Randomize line order
14
+ """
15
+
16
+ import tkinter as tk
17
+ from tkinter import ttk
18
+ import random
19
+ import re
20
+
21
+
22
+ class LineToolsProcessor:
23
+ """Line tools processor with various line manipulation capabilities."""
24
+
25
+ @staticmethod
26
+ def remove_duplicates(text, mode="keep_first", case_sensitive=True):
27
+ """Remove duplicate lines from text."""
28
+ lines = text.splitlines()
29
+
30
+ if mode == "keep_first":
31
+ seen = set()
32
+ result = []
33
+ for line in lines:
34
+ key = line if case_sensitive else line.lower()
35
+ if key not in seen:
36
+ seen.add(key)
37
+ result.append(line)
38
+ else: # keep_last
39
+ seen = {}
40
+ for i, line in enumerate(lines):
41
+ key = line if case_sensitive else line.lower()
42
+ seen[key] = (i, line)
43
+ result = [v[1] for v in sorted(seen.values(), key=lambda x: x[0])]
44
+
45
+ return '\n'.join(result)
46
+
47
+ @staticmethod
48
+ def remove_empty_lines(text, preserve_single=False):
49
+ """Remove empty or whitespace-only lines."""
50
+ lines = text.splitlines()
51
+
52
+ if preserve_single:
53
+ result = []
54
+ prev_empty = False
55
+ for line in lines:
56
+ is_empty = not line.strip()
57
+ if is_empty:
58
+ if not prev_empty:
59
+ result.append('')
60
+ prev_empty = True
61
+ else:
62
+ result.append(line)
63
+ prev_empty = False
64
+ else:
65
+ result = [line for line in lines if line.strip()]
66
+
67
+ return '\n'.join(result)
68
+
69
+ @staticmethod
70
+ def add_line_numbers(text, format_style="1. ", start_number=1, skip_empty=False):
71
+ """Add line numbers to each line."""
72
+ lines = text.splitlines()
73
+ result = []
74
+ num = start_number
75
+
76
+ for line in lines:
77
+ if skip_empty and not line.strip():
78
+ result.append(line)
79
+ else:
80
+ if format_style == "1. ":
81
+ prefix = f"{num}. "
82
+ elif format_style == "1) ":
83
+ prefix = f"{num}) "
84
+ elif format_style == "[1] ":
85
+ prefix = f"[{num}] "
86
+ elif format_style == "1: ":
87
+ prefix = f"{num}: "
88
+ else:
89
+ prefix = f"{num}. "
90
+ result.append(f"{prefix}{line}")
91
+ num += 1
92
+
93
+ return '\n'.join(result)
94
+
95
+ @staticmethod
96
+ def remove_line_numbers(text):
97
+ """Remove line numbers from the beginning of each line."""
98
+ lines = text.splitlines()
99
+ result = []
100
+ pattern = r'^(\d+[\.\)\:]?\s*|\[\d+\]\s*)'
101
+
102
+ for line in lines:
103
+ result.append(re.sub(pattern, '', line))
104
+
105
+ return '\n'.join(result)
106
+
107
+ @staticmethod
108
+ def reverse_lines(text):
109
+ """Reverse the order of lines."""
110
+ lines = text.splitlines()
111
+ return '\n'.join(reversed(lines))
112
+
113
+ @staticmethod
114
+ def shuffle_lines(text):
115
+ """Randomly shuffle the order of lines."""
116
+ lines = text.splitlines()
117
+ random.shuffle(lines)
118
+ return '\n'.join(lines)
119
+
120
+ @staticmethod
121
+ def process_text(input_text, tool_type, settings):
122
+ """Process text using the specified line tool and settings."""
123
+ if tool_type == "Remove Duplicates":
124
+ return LineToolsProcessor.remove_duplicates(
125
+ input_text,
126
+ settings.get("duplicate_mode", "keep_first"),
127
+ settings.get("case_sensitive", True)
128
+ )
129
+ elif tool_type == "Remove Empty Lines":
130
+ return LineToolsProcessor.remove_empty_lines(
131
+ input_text,
132
+ settings.get("preserve_single", False)
133
+ )
134
+ elif tool_type == "Add Line Numbers":
135
+ return LineToolsProcessor.add_line_numbers(
136
+ input_text,
137
+ settings.get("number_format", "1. "),
138
+ settings.get("start_number", 1),
139
+ settings.get("skip_empty", False)
140
+ )
141
+ elif tool_type == "Remove Line Numbers":
142
+ return LineToolsProcessor.remove_line_numbers(input_text)
143
+ elif tool_type == "Reverse Lines":
144
+ return LineToolsProcessor.reverse_lines(input_text)
145
+ elif tool_type == "Shuffle Lines":
146
+ return LineToolsProcessor.shuffle_lines(input_text)
147
+ else:
148
+ return f"Unknown line tool: {tool_type}"
149
+
150
+
151
+ class LineToolsWidget(ttk.Frame):
152
+ """Tabbed interface widget for line tools."""
153
+
154
+ def __init__(self, parent, app):
155
+ super().__init__(parent)
156
+ self.app = app
157
+ self.processor = LineToolsProcessor()
158
+
159
+ self.duplicate_mode = tk.StringVar(value="keep_first")
160
+ self.case_sensitive = tk.BooleanVar(value=True)
161
+ self.preserve_single = tk.BooleanVar(value=False)
162
+ self.number_format = tk.StringVar(value="1. ")
163
+ self.start_number = tk.IntVar(value=1)
164
+ self.skip_empty = tk.BooleanVar(value=False)
165
+
166
+ self.create_widgets()
167
+ self.load_settings()
168
+
169
+ def create_widgets(self):
170
+ """Creates the tabbed interface for line tools."""
171
+ self.notebook = ttk.Notebook(self)
172
+ self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
173
+
174
+ self.create_remove_duplicates_tab()
175
+ self.create_remove_empty_tab()
176
+ self.create_add_numbers_tab()
177
+ self.create_remove_numbers_tab()
178
+ self.create_reverse_tab()
179
+ self.create_shuffle_tab()
180
+
181
+ def create_remove_duplicates_tab(self):
182
+ """Creates the Remove Duplicates tab."""
183
+ frame = ttk.Frame(self.notebook)
184
+ self.notebook.add(frame, text="Remove Duplicates")
185
+
186
+ mode_frame = ttk.LabelFrame(frame, text="Duplicate Handling", padding=10)
187
+ mode_frame.pack(fill=tk.X, padx=5, pady=5)
188
+
189
+ ttk.Radiobutton(mode_frame, text="Keep First Occurrence",
190
+ variable=self.duplicate_mode, value="keep_first",
191
+ command=self.on_setting_change).pack(anchor=tk.W)
192
+ ttk.Radiobutton(mode_frame, text="Keep Last Occurrence",
193
+ variable=self.duplicate_mode, value="keep_last",
194
+ command=self.on_setting_change).pack(anchor=tk.W)
195
+
196
+ ttk.Checkbutton(frame, text="Case Sensitive",
197
+ variable=self.case_sensitive,
198
+ command=self.on_setting_change).pack(anchor=tk.W, padx=5, pady=5)
199
+
200
+ ttk.Button(frame, text="Remove Duplicates",
201
+ command=lambda: self.process("Remove Duplicates")).pack(pady=10)
202
+
203
+ def create_remove_empty_tab(self):
204
+ """Creates the Remove Empty Lines tab."""
205
+ frame = ttk.Frame(self.notebook)
206
+ self.notebook.add(frame, text="Remove Empty")
207
+
208
+ options_frame = ttk.LabelFrame(frame, text="Options", padding=10)
209
+ options_frame.pack(fill=tk.X, padx=5, pady=5)
210
+
211
+ ttk.Checkbutton(options_frame, text="Preserve Single Empty Lines (collapse multiple)",
212
+ variable=self.preserve_single,
213
+ command=self.on_setting_change).pack(anchor=tk.W)
214
+
215
+ ttk.Button(frame, text="Remove Empty Lines",
216
+ command=lambda: self.process("Remove Empty Lines")).pack(pady=10)
217
+
218
+ def create_add_numbers_tab(self):
219
+ """Creates the Add Line Numbers tab."""
220
+ frame = ttk.Frame(self.notebook)
221
+ self.notebook.add(frame, text="Add Numbers")
222
+
223
+ format_frame = ttk.LabelFrame(frame, text="Number Format", padding=10)
224
+ format_frame.pack(fill=tk.X, padx=5, pady=5)
225
+
226
+ formats = [("1. (dot)", "1. "), ("1) (parenthesis)", "1) "),
227
+ ("[1] (brackets)", "[1] "), ("1: (colon)", "1: ")]
228
+ for text, value in formats:
229
+ ttk.Radiobutton(format_frame, text=text,
230
+ variable=self.number_format, value=value,
231
+ command=self.on_setting_change).pack(anchor=tk.W)
232
+
233
+ start_frame = ttk.Frame(frame)
234
+ start_frame.pack(fill=tk.X, padx=5, pady=5)
235
+ ttk.Label(start_frame, text="Start Number:").pack(side=tk.LEFT)
236
+ ttk.Spinbox(start_frame, from_=0, to=9999, width=6,
237
+ textvariable=self.start_number,
238
+ command=self.on_setting_change).pack(side=tk.LEFT, padx=5)
239
+
240
+ ttk.Checkbutton(frame, text="Skip Empty Lines",
241
+ variable=self.skip_empty,
242
+ command=self.on_setting_change).pack(anchor=tk.W, padx=5)
243
+
244
+ ttk.Button(frame, text="Add Line Numbers",
245
+ command=lambda: self.process("Add Line Numbers")).pack(pady=10)
246
+
247
+ def create_remove_numbers_tab(self):
248
+ """Creates the Remove Line Numbers tab."""
249
+ frame = ttk.Frame(self.notebook)
250
+ self.notebook.add(frame, text="Remove Numbers")
251
+
252
+ info = ttk.Label(frame, text="Removes line numbers from the beginning of each line.\n"
253
+ "Supports formats: 1. , 1) , [1] , 1: , 1 ",
254
+ justify=tk.CENTER)
255
+ info.pack(pady=20)
256
+
257
+ ttk.Button(frame, text="Remove Line Numbers",
258
+ command=lambda: self.process("Remove Line Numbers")).pack(pady=10)
259
+
260
+ def create_reverse_tab(self):
261
+ """Creates the Reverse Lines tab."""
262
+ frame = ttk.Frame(self.notebook)
263
+ self.notebook.add(frame, text="Reverse")
264
+
265
+ info = ttk.Label(frame, text="Reverses the order of all lines in the text.",
266
+ justify=tk.CENTER)
267
+ info.pack(pady=20)
268
+
269
+ ttk.Button(frame, text="Reverse Lines",
270
+ command=lambda: self.process("Reverse Lines")).pack(pady=10)
271
+
272
+ def create_shuffle_tab(self):
273
+ """Creates the Shuffle Lines tab."""
274
+ frame = ttk.Frame(self.notebook)
275
+ self.notebook.add(frame, text="Shuffle")
276
+
277
+ info = ttk.Label(frame, text="Randomly shuffles the order of all lines.",
278
+ justify=tk.CENTER)
279
+ info.pack(pady=20)
280
+
281
+ ttk.Button(frame, text="Shuffle Lines",
282
+ command=lambda: self.process("Shuffle Lines")).pack(pady=10)
283
+
284
+ def load_settings(self):
285
+ """Load settings from the application."""
286
+ settings = self.app.settings.get("tool_settings", {}).get("Line Tools", {})
287
+
288
+ self.duplicate_mode.set(settings.get("duplicate_mode", "keep_first"))
289
+ self.case_sensitive.set(settings.get("case_sensitive", True))
290
+ self.preserve_single.set(settings.get("preserve_single", False))
291
+ self.number_format.set(settings.get("number_format", "1. "))
292
+ self.start_number.set(settings.get("start_number", 1))
293
+ self.skip_empty.set(settings.get("skip_empty", False))
294
+
295
+ def save_settings(self):
296
+ """Save current settings to the application."""
297
+ if "Line Tools" not in self.app.settings["tool_settings"]:
298
+ self.app.settings["tool_settings"]["Line Tools"] = {}
299
+
300
+ self.app.settings["tool_settings"]["Line Tools"].update({
301
+ "duplicate_mode": self.duplicate_mode.get(),
302
+ "case_sensitive": self.case_sensitive.get(),
303
+ "preserve_single": self.preserve_single.get(),
304
+ "number_format": self.number_format.get(),
305
+ "start_number": self.start_number.get(),
306
+ "skip_empty": self.skip_empty.get()
307
+ })
308
+
309
+ self.app.save_settings()
310
+
311
+ def on_setting_change(self, *args):
312
+ """Handle setting changes."""
313
+ self.save_settings()
314
+
315
+ def process(self, tool_type):
316
+ """Process the input text with the selected tool."""
317
+ active_input_tab = self.app.input_tabs[self.app.input_notebook.index(self.app.input_notebook.select())]
318
+ input_text = active_input_tab.text.get("1.0", tk.END).rstrip('\n')
319
+
320
+ if not input_text.strip():
321
+ return
322
+
323
+ settings = {
324
+ "duplicate_mode": self.duplicate_mode.get(),
325
+ "case_sensitive": self.case_sensitive.get(),
326
+ "preserve_single": self.preserve_single.get(),
327
+ "number_format": self.number_format.get(),
328
+ "start_number": self.start_number.get(),
329
+ "skip_empty": self.skip_empty.get()
330
+ }
331
+
332
+ result = LineToolsProcessor.process_text(input_text, tool_type, settings)
333
+
334
+ active_output_tab = self.app.output_tabs[self.app.output_notebook.index(self.app.output_notebook.select())]
335
+ active_output_tab.text.config(state="normal")
336
+ active_output_tab.text.delete("1.0", tk.END)
337
+ active_output_tab.text.insert("1.0", result)
338
+ active_output_tab.text.config(state="disabled")
339
+
340
+ self.app.update_all_stats()
341
+
342
+
343
+ class LineTools:
344
+ """Main class for Line Tools integration."""
345
+
346
+ def __init__(self):
347
+ self.processor = LineToolsProcessor()
348
+
349
+ def create_widget(self, parent, app):
350
+ """Create and return the Line Tools widget."""
351
+ return LineToolsWidget(parent, app)
352
+
353
+ def get_default_settings(self):
354
+ """Return default settings for Line Tools."""
355
+ return {
356
+ "duplicate_mode": "keep_first",
357
+ "case_sensitive": True,
358
+ "preserve_single": False,
359
+ "number_format": "1. ",
360
+ "start_number": 1,
361
+ "skip_empty": False
362
+ }
363
+
364
+ def process_text(self, input_text, tool_type, settings):
365
+ """Process text using the specified tool and settings."""
366
+ return LineToolsProcessor.process_text(input_text, tool_type, settings)
367
+
368
+
369
+ # BaseTool-compatible wrapper
370
+ try:
371
+ from tools.base_tool import ToolWithOptions
372
+ from typing import Dict, Any
373
+
374
+ class LineToolsV2(ToolWithOptions):
375
+ """
376
+ BaseTool-compatible version of LineTools.
377
+ """
378
+
379
+ TOOL_NAME = "Line Tools"
380
+ TOOL_DESCRIPTION = "Line manipulation: remove duplicates, add numbers, reverse, shuffle"
381
+ TOOL_VERSION = "2.0.0"
382
+
383
+ OPTIONS = [
384
+ ("Remove Duplicates", "remove_duplicates"),
385
+ ("Remove Empty Lines", "remove_empty"),
386
+ ("Add Line Numbers", "add_numbers"),
387
+ ("Remove Line Numbers", "remove_numbers"),
388
+ ("Reverse Lines", "reverse"),
389
+ ("Shuffle Lines", "shuffle"),
390
+ ]
391
+ OPTIONS_LABEL = "Operation"
392
+ USE_DROPDOWN = True
393
+ DEFAULT_OPTION = "remove_duplicates"
394
+
395
+ def __init__(self):
396
+ super().__init__()
397
+ self._processor = LineToolsProcessor()
398
+
399
+ def process_text(self, input_text: str, settings: Dict[str, Any]) -> str:
400
+ """Process text using the specified line operation."""
401
+ mode = settings.get("mode", "remove_duplicates")
402
+
403
+ if mode == "remove_duplicates":
404
+ return LineToolsProcessor.remove_duplicates(input_text)
405
+ elif mode == "remove_empty":
406
+ return LineToolsProcessor.remove_empty_lines(input_text)
407
+ elif mode == "add_numbers":
408
+ return LineToolsProcessor.add_line_numbers(input_text)
409
+ elif mode == "remove_numbers":
410
+ return LineToolsProcessor.remove_line_numbers(input_text)
411
+ elif mode == "reverse":
412
+ return LineToolsProcessor.reverse_lines(input_text)
413
+ elif mode == "shuffle":
414
+ return LineToolsProcessor.shuffle_lines(input_text)
415
+ else:
416
+ return input_text
417
+
418
+ except ImportError:
419
+ pass