kopipasta 0.29.0__tar.gz → 0.30.0__tar.gz
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.
Potentially problematic release.
This version of kopipasta might be problematic. Click here for more details.
- {kopipasta-0.29.0/kopipasta.egg-info → kopipasta-0.30.0}/PKG-INFO +1 -1
- {kopipasta-0.29.0 → kopipasta-0.30.0}/kopipasta/tree_selector.py +76 -27
- {kopipasta-0.29.0 → kopipasta-0.30.0/kopipasta.egg-info}/PKG-INFO +1 -1
- {kopipasta-0.29.0 → kopipasta-0.30.0}/setup.py +1 -1
- {kopipasta-0.29.0 → kopipasta-0.30.0}/LICENSE +0 -0
- {kopipasta-0.29.0 → kopipasta-0.30.0}/MANIFEST.in +0 -0
- {kopipasta-0.29.0 → kopipasta-0.30.0}/README.md +0 -0
- {kopipasta-0.29.0 → kopipasta-0.30.0}/kopipasta/__init__.py +0 -0
- {kopipasta-0.29.0 → kopipasta-0.30.0}/kopipasta/file.py +0 -0
- {kopipasta-0.29.0 → kopipasta-0.30.0}/kopipasta/import_parser.py +0 -0
- {kopipasta-0.29.0 → kopipasta-0.30.0}/kopipasta/main.py +0 -0
- {kopipasta-0.29.0 → kopipasta-0.30.0}/kopipasta/prompt.py +0 -0
- {kopipasta-0.29.0 → kopipasta-0.30.0}/kopipasta.egg-info/SOURCES.txt +0 -0
- {kopipasta-0.29.0 → kopipasta-0.30.0}/kopipasta.egg-info/dependency_links.txt +0 -0
- {kopipasta-0.29.0 → kopipasta-0.30.0}/kopipasta.egg-info/entry_points.txt +0 -0
- {kopipasta-0.29.0 → kopipasta-0.30.0}/kopipasta.egg-info/requires.txt +0 -0
- {kopipasta-0.29.0 → kopipasta-0.30.0}/kopipasta.egg-info/top_level.txt +0 -0
- {kopipasta-0.29.0 → kopipasta-0.30.0}/requirements.txt +0 -0
- {kopipasta-0.29.0 → kopipasta-0.30.0}/setup.cfg +0 -0
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import shutil
|
|
2
3
|
from typing import Dict, List, Optional, Tuple
|
|
3
4
|
from rich.console import Console
|
|
4
5
|
from rich.tree import Tree
|
|
5
6
|
from rich.panel import Panel
|
|
6
|
-
from rich.table import Table
|
|
7
7
|
from rich.text import Text
|
|
8
|
-
from rich.live import Live
|
|
9
|
-
from rich.layout import Layout
|
|
10
8
|
import click
|
|
11
9
|
|
|
12
10
|
from kopipasta.file import FileTuple, is_binary, is_ignored, get_human_readable_size
|
|
@@ -52,6 +50,7 @@ class TreeSelector:
|
|
|
52
50
|
self.visible_nodes: List[FileNode] = []
|
|
53
51
|
self.char_count = 0
|
|
54
52
|
self.quit_selection = False
|
|
53
|
+
self.viewport_offset = 0 # First visible item index
|
|
55
54
|
|
|
56
55
|
def build_tree(self, paths: List[str]) -> FileNode:
|
|
57
56
|
"""Build tree structure from given paths"""
|
|
@@ -154,17 +153,49 @@ class TreeSelector:
|
|
|
154
153
|
return result
|
|
155
154
|
|
|
156
155
|
def _build_display_tree(self) -> Tree:
|
|
157
|
-
"""Build Rich tree for display"""
|
|
158
|
-
|
|
156
|
+
"""Build Rich tree for display with viewport"""
|
|
157
|
+
# Get terminal size
|
|
158
|
+
term_width, term_height = shutil.get_terminal_size()
|
|
159
159
|
|
|
160
|
-
#
|
|
160
|
+
# Reserve space for header, help panel, and status
|
|
161
|
+
available_height = term_height - 15 # Adjust based on your UI
|
|
162
|
+
available_height = max(5, available_height) # Minimum height
|
|
163
|
+
|
|
164
|
+
# Flatten tree to get all visible nodes
|
|
161
165
|
flat_tree = self._flatten_tree(self.root)
|
|
162
166
|
self.visible_nodes = [node for node, _ in flat_tree]
|
|
163
167
|
|
|
164
|
-
#
|
|
168
|
+
# Calculate viewport
|
|
169
|
+
if self.visible_nodes:
|
|
170
|
+
# Ensure current selection is visible
|
|
171
|
+
if self.current_index < self.viewport_offset:
|
|
172
|
+
self.viewport_offset = self.current_index
|
|
173
|
+
elif self.current_index >= self.viewport_offset + available_height:
|
|
174
|
+
self.viewport_offset = self.current_index - available_height + 1
|
|
175
|
+
|
|
176
|
+
# Clamp viewport to valid range
|
|
177
|
+
max_offset = max(0, len(self.visible_nodes) - available_height)
|
|
178
|
+
self.viewport_offset = max(0, min(self.viewport_offset, max_offset))
|
|
179
|
+
else:
|
|
180
|
+
self.viewport_offset = 0
|
|
181
|
+
|
|
182
|
+
# Create tree with scroll indicators
|
|
183
|
+
tree_title = "📁 Project Files"
|
|
184
|
+
if self.viewport_offset > 0:
|
|
185
|
+
tree_title += f" ↑ ({self.viewport_offset} more)"
|
|
186
|
+
|
|
187
|
+
tree = Tree(tree_title, guide_style="dim")
|
|
188
|
+
|
|
189
|
+
# Build tree structure - only for visible portion
|
|
165
190
|
node_map = {}
|
|
191
|
+
viewport_end = min(len(flat_tree), self.viewport_offset + available_height)
|
|
166
192
|
|
|
167
|
-
|
|
193
|
+
# Track what level each visible item is at for proper tree structure
|
|
194
|
+
level_stacks = {} # level -> stack of tree nodes
|
|
195
|
+
|
|
196
|
+
for i in range(self.viewport_offset, viewport_end):
|
|
197
|
+
node, level = flat_tree[i]
|
|
198
|
+
|
|
168
199
|
# Determine style and icon
|
|
169
200
|
is_current = i == self.current_index
|
|
170
201
|
style = "bold cyan" if is_current else ""
|
|
@@ -193,40 +224,44 @@ class TreeSelector:
|
|
|
193
224
|
label.append(f"{selection} ", style="dim")
|
|
194
225
|
label.append(f"{icon} {node.name}{size_str}", style=style)
|
|
195
226
|
|
|
196
|
-
# Add to tree at correct
|
|
197
|
-
|
|
198
|
-
if node.parent and node.parent.path == os.path.abspath("."):
|
|
227
|
+
# Add to tree at correct level
|
|
228
|
+
if level == 0:
|
|
199
229
|
tree_node = tree.add(label)
|
|
200
|
-
|
|
230
|
+
level_stacks[0] = tree_node
|
|
201
231
|
else:
|
|
202
|
-
# Find parent
|
|
203
|
-
|
|
204
|
-
if
|
|
205
|
-
parent_tree =
|
|
232
|
+
# Find parent at previous level
|
|
233
|
+
parent_level = level - 1
|
|
234
|
+
if parent_level in level_stacks:
|
|
235
|
+
parent_tree = level_stacks[parent_level]
|
|
206
236
|
tree_node = parent_tree.add(label)
|
|
207
|
-
|
|
237
|
+
level_stacks[level] = tree_node
|
|
208
238
|
else:
|
|
209
|
-
# Fallback - add to root
|
|
210
|
-
|
|
211
|
-
|
|
239
|
+
# Fallback - add to root with indentation indicator
|
|
240
|
+
indent_label = Text()
|
|
241
|
+
indent_label.append(" " * level + f"{selection} ", style="dim")
|
|
242
|
+
indent_label.append(f"{icon} {node.name}{size_str}", style=style)
|
|
243
|
+
tree_node = tree.add(indent_label)
|
|
244
|
+
level_stacks[level] = tree_node
|
|
245
|
+
|
|
246
|
+
# Add scroll indicator at bottom if needed
|
|
247
|
+
if viewport_end < len(self.visible_nodes):
|
|
248
|
+
remaining = len(self.visible_nodes) - viewport_end
|
|
249
|
+
tree.add(Text(f"↓ ({remaining} more items)", style="dim italic"))
|
|
212
250
|
|
|
213
251
|
return tree
|
|
214
|
-
|
|
252
|
+
|
|
215
253
|
def _show_help(self) -> Panel:
|
|
216
254
|
"""Create help panel"""
|
|
217
255
|
help_text = """[bold]Navigation:[/bold]
|
|
218
|
-
↑/k:
|
|
256
|
+
↑/k: Up ↓/j: Down →/l/Enter: Expand ←/h: Collapse PgUp/PgDn: Page G/End: Bottom
|
|
219
257
|
|
|
220
258
|
[bold]Selection:[/bold]
|
|
221
259
|
Space: Toggle file/dir a: Add all in dir s: Snippet mode
|
|
222
260
|
|
|
223
261
|
[bold]Actions:[/bold]
|
|
224
|
-
g: Grep in directory d: Show dependencies q: Quit selection
|
|
225
|
-
|
|
226
|
-
[bold]Status:[/bold]
|
|
227
|
-
Selected: [green]● Full[/green] [yellow]◐ Snippet[/yellow] ○ Not selected"""
|
|
262
|
+
g: Grep in directory d: Show dependencies q: Quit selection"""
|
|
228
263
|
|
|
229
|
-
return Panel(help_text, title="
|
|
264
|
+
return Panel(help_text, title="Keys", border_style="dim", expand=False)
|
|
230
265
|
|
|
231
266
|
def _get_status_bar(self) -> str:
|
|
232
267
|
"""Create status bar with selection info"""
|
|
@@ -456,6 +491,20 @@ Selected: [green]● Full[/green] [yellow]◐ Snippet[/yellow] ○ Not selecte
|
|
|
456
491
|
self.current_index = max(0, self.current_index - 1)
|
|
457
492
|
elif key in ['\x1b[B', 'j']: # Down arrow or j
|
|
458
493
|
self.current_index = min(len(self.visible_nodes) - 1, self.current_index + 1)
|
|
494
|
+
elif key == '\x1b[5~': # Page Up
|
|
495
|
+
term_width, term_height = shutil.get_terminal_size()
|
|
496
|
+
page_size = max(1, term_height - 15)
|
|
497
|
+
self.current_index = max(0, self.current_index - page_size)
|
|
498
|
+
elif key == '\x1b[6~': # Page Down
|
|
499
|
+
term_width, term_height = shutil.get_terminal_size()
|
|
500
|
+
page_size = max(1, term_height - 15)
|
|
501
|
+
self.current_index = min(len(self.visible_nodes) - 1, self.current_index + page_size)
|
|
502
|
+
elif key == '\x1b[H': # Home - go to top
|
|
503
|
+
self.current_index = 0
|
|
504
|
+
elif key == '\x1b[F': # End - go to bottom
|
|
505
|
+
self.current_index = len(self.visible_nodes) - 1
|
|
506
|
+
elif key == 'G': # Shift+G - go to bottom (vim style)
|
|
507
|
+
self.current_index = len(self.visible_nodes) - 1
|
|
459
508
|
elif key in ['\x1b[C', 'l', '\r']: # Right arrow, l, or Enter
|
|
460
509
|
if current_node.is_dir:
|
|
461
510
|
current_node.expanded = True
|
|
@@ -10,7 +10,7 @@ with open("requirements.txt", "r", encoding="utf-8") as f:
|
|
|
10
10
|
|
|
11
11
|
setup(
|
|
12
12
|
name="kopipasta",
|
|
13
|
-
version="0.
|
|
13
|
+
version="0.30.0",
|
|
14
14
|
author="Mikko Korpela",
|
|
15
15
|
author_email="mikko.korpela@gmail.com",
|
|
16
16
|
description="A CLI tool to generate prompts with project structure and file contents",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|