kopipasta 0.29.0__py3-none-any.whl → 0.30.0__py3-none-any.whl

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.
@@ -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
- tree = Tree("📁 Project Files", guide_style="dim")
156
+ """Build Rich tree for display with viewport"""
157
+ # Get terminal size
158
+ term_width, term_height = shutil.get_terminal_size()
159
159
 
160
- # Flatten tree and rebuild visible nodes list
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
- # Build tree structure - we'll map absolute paths to tree nodes
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
- for i, (node, level) in enumerate(flat_tree):
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 position
197
- # For root-level items, add directly to tree
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
- node_map[abs_path] = tree_node
230
+ level_stacks[0] = tree_node
201
231
  else:
202
- # Find parent node in map
203
- parent_abs_path = os.path.abspath(node.parent.path) if node.parent else None
204
- if parent_abs_path and parent_abs_path in node_map:
205
- parent_tree = node_map[parent_abs_path]
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
- node_map[abs_path] = tree_node
237
+ level_stacks[level] = tree_node
208
238
  else:
209
- # Fallback - add to root
210
- tree_node = tree.add(label)
211
- node_map[abs_path] = tree_node
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: Move up ↓/j: Move down →/l/Enter: Expand dir ←/h: Collapse dir
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="Keyboard Shortcuts", border_style="dim", expand=False)
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kopipasta
3
- Version: 0.29.0
3
+ Version: 0.30.0
4
4
  Summary: A CLI tool to generate prompts with project structure and file contents
5
5
  Home-page: https://github.com/mkorpela/kopipasta
6
6
  Author: Mikko Korpela
@@ -3,10 +3,10 @@ kopipasta/file.py,sha256=2HEoNczbKH3TtLO0zYUcZfUoHqb0-o83xFUOgY9rG-Y,1244
3
3
  kopipasta/import_parser.py,sha256=yLzkMlQm2avKjfqcpMY0PxbA_2ihV9gSYJplreWIPEQ,12424
4
4
  kopipasta/main.py,sha256=MlfmP_eG0ZyvdhxdGyPsN2TxqnM4hq9pa2ToQEbbNP4,48894
5
5
  kopipasta/prompt.py,sha256=fOCuJVTLUfR0fjKf5qIlnl_3pNsKNKsvt3C8f4tsmxk,6889
6
- kopipasta/tree_selector.py,sha256=Dr7R4d19NzxWMwfQI3GioaD2i1ho84hMRxCzVp4sfCg,21342
7
- kopipasta-0.29.0.dist-info/LICENSE,sha256=xw4C9TAU7LFu4r_MwSbky90uzkzNtRwAo3c51IWR8lk,1091
8
- kopipasta-0.29.0.dist-info/METADATA,sha256=4vQpgi-XLKavkyQulJTbEscOUX5uAe1n6n-PJZwKTck,4894
9
- kopipasta-0.29.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
10
- kopipasta-0.29.0.dist-info/entry_points.txt,sha256=but54qDNz1-F8fVvGstq_QID5tHjczP7bO7rWLFkc6Y,50
11
- kopipasta-0.29.0.dist-info/top_level.txt,sha256=iXohixMuCdw8UjGDUp0ouICLYBDrx207sgZIJ9lxn0o,10
12
- kopipasta-0.29.0.dist-info/RECORD,,
6
+ kopipasta/tree_selector.py,sha256=hoS2yENrI6cLJ8s3ZXesctZkn3kAx4LwpD2cnp_ilSc,23900
7
+ kopipasta-0.30.0.dist-info/LICENSE,sha256=xw4C9TAU7LFu4r_MwSbky90uzkzNtRwAo3c51IWR8lk,1091
8
+ kopipasta-0.30.0.dist-info/METADATA,sha256=Ol5GwqwEazcTaH_dsMc18We53YLIeSWRlgNT2CeLfWo,4894
9
+ kopipasta-0.30.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
10
+ kopipasta-0.30.0.dist-info/entry_points.txt,sha256=but54qDNz1-F8fVvGstq_QID5tHjczP7bO7rWLFkc6Y,50
11
+ kopipasta-0.30.0.dist-info/top_level.txt,sha256=iXohixMuCdw8UjGDUp0ouICLYBDrx207sgZIJ9lxn0o,10
12
+ kopipasta-0.30.0.dist-info/RECORD,,