rostree 0.2.1__tar.gz → 0.2.2__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.
- {rostree-0.2.1 → rostree-0.2.2}/.gitignore +21 -0
- {rostree-0.2.1 → rostree-0.2.2}/PKG-INFO +1 -1
- {rostree-0.2.1 → rostree-0.2.2}/pyproject.toml +1 -1
- {rostree-0.2.1 → rostree-0.2.2}/src/rostree/tui/app.py +111 -4
- {rostree-0.2.1 → rostree-0.2.2}/LICENSE +0 -0
- {rostree-0.2.1 → rostree-0.2.2}/README.md +0 -0
- {rostree-0.2.1 → rostree-0.2.2}/src/rostree/__init__.py +0 -0
- {rostree-0.2.1 → rostree-0.2.2}/src/rostree/api.py +0 -0
- {rostree-0.2.1 → rostree-0.2.2}/src/rostree/cli.py +0 -0
- {rostree-0.2.1 → rostree-0.2.2}/src/rostree/core/__init__.py +0 -0
- {rostree-0.2.1 → rostree-0.2.2}/src/rostree/core/finder.py +0 -0
- {rostree-0.2.1 → rostree-0.2.2}/src/rostree/core/parser.py +0 -0
- {rostree-0.2.1 → rostree-0.2.2}/src/rostree/core/tree.py +0 -0
- {rostree-0.2.1 → rostree-0.2.2}/src/rostree/tui/__init__.py +0 -0
|
@@ -20,6 +20,27 @@ wheels/
|
|
|
20
20
|
.installed.cfg
|
|
21
21
|
*.egg
|
|
22
22
|
|
|
23
|
+
# Images
|
|
24
|
+
*.png
|
|
25
|
+
*.jpg
|
|
26
|
+
*.jpeg
|
|
27
|
+
*.gif
|
|
28
|
+
*.bmp
|
|
29
|
+
*.tiff
|
|
30
|
+
*.ico
|
|
31
|
+
*.webp
|
|
32
|
+
*.svg
|
|
33
|
+
*.eps
|
|
34
|
+
*.pdf
|
|
35
|
+
*.docx
|
|
36
|
+
*.doc
|
|
37
|
+
*.xls
|
|
38
|
+
*.xlsx
|
|
39
|
+
*.pptx
|
|
40
|
+
*.ppt
|
|
41
|
+
*.dot
|
|
42
|
+
*.mermaid
|
|
43
|
+
|
|
23
44
|
# Virtual environments
|
|
24
45
|
.venv/
|
|
25
46
|
venv/
|
|
@@ -10,8 +10,9 @@ from textual.app import App, ComposeResult
|
|
|
10
10
|
from textual.binding import Binding
|
|
11
11
|
from textual.containers import Container, Vertical
|
|
12
12
|
from textual.screen import ModalScreen
|
|
13
|
-
from textual.widgets import Footer, Header, Input, Static, Tree
|
|
13
|
+
from textual.widgets import Footer, Header, Input, LoadingIndicator, Static, Tree
|
|
14
14
|
from textual.widgets.tree import TreeNode
|
|
15
|
+
from textual.worker import Worker, WorkerState
|
|
15
16
|
|
|
16
17
|
from rostree.api import build_tree, list_known_packages_by_source
|
|
17
18
|
|
|
@@ -285,6 +286,10 @@ class DepTreeApp(App[None]):
|
|
|
285
286
|
self._search_matches: list[TreeNode] = []
|
|
286
287
|
self._search_index: int = 0
|
|
287
288
|
self._details_visible: bool = True
|
|
289
|
+
# Background loading state
|
|
290
|
+
self._packages_cache: dict[str, list[str]] | None = None
|
|
291
|
+
self._packages_loading: bool = False
|
|
292
|
+
self._packages_error: str | None = None
|
|
288
293
|
|
|
289
294
|
DEFAULT_CSS = """
|
|
290
295
|
/* Welcome screen styles */
|
|
@@ -306,6 +311,17 @@ class DepTreeApp(App[None]):
|
|
|
306
311
|
text-align: center;
|
|
307
312
|
padding-top: 1;
|
|
308
313
|
}
|
|
314
|
+
#welcome_loading {
|
|
315
|
+
text-align: center;
|
|
316
|
+
padding-top: 1;
|
|
317
|
+
display: none;
|
|
318
|
+
}
|
|
319
|
+
#welcome_loading.loading {
|
|
320
|
+
display: block;
|
|
321
|
+
}
|
|
322
|
+
#welcome_loading LoadingIndicator {
|
|
323
|
+
background: transparent;
|
|
324
|
+
}
|
|
309
325
|
/* Main view styles */
|
|
310
326
|
#main_container {
|
|
311
327
|
display: none;
|
|
@@ -336,6 +352,10 @@ class DepTreeApp(App[None]):
|
|
|
336
352
|
id="welcome_hint",
|
|
337
353
|
markup=True,
|
|
338
354
|
)
|
|
355
|
+
# Loading indicator (shown while scanning)
|
|
356
|
+
with Container(id="welcome_loading"):
|
|
357
|
+
yield LoadingIndicator()
|
|
358
|
+
yield Static("[dim]Scanning for packages...[/]", id="loading_text", markup=True)
|
|
339
359
|
# Main view (hidden initially)
|
|
340
360
|
with Container(id="main_container"):
|
|
341
361
|
yield Static(
|
|
@@ -351,6 +371,8 @@ class DepTreeApp(App[None]):
|
|
|
351
371
|
|
|
352
372
|
def on_mount(self) -> None:
|
|
353
373
|
self.sub_title = "Dependency Tree Explorer"
|
|
374
|
+
# Start loading packages in the background immediately
|
|
375
|
+
self._start_package_scan()
|
|
354
376
|
|
|
355
377
|
def on_key(self, event: Any) -> None:
|
|
356
378
|
"""Handle key events - specifically Enter on welcome screen."""
|
|
@@ -359,6 +381,69 @@ class DepTreeApp(App[None]):
|
|
|
359
381
|
event.stop()
|
|
360
382
|
self.action_start_main()
|
|
361
383
|
|
|
384
|
+
def _start_package_scan(self) -> None:
|
|
385
|
+
"""Start scanning for packages in the background."""
|
|
386
|
+
if self._packages_cache is not None or self._packages_loading:
|
|
387
|
+
return # Already loaded or loading
|
|
388
|
+
self._packages_loading = True
|
|
389
|
+
# Show loading indicator
|
|
390
|
+
try:
|
|
391
|
+
loading_container = self.query_one("#welcome_loading")
|
|
392
|
+
loading_container.add_class("loading")
|
|
393
|
+
except Exception:
|
|
394
|
+
pass
|
|
395
|
+
# Start background worker
|
|
396
|
+
self.run_worker(self._scan_packages_worker, thread=True)
|
|
397
|
+
|
|
398
|
+
def _scan_packages_worker(self) -> dict[str, list[str]]:
|
|
399
|
+
"""Worker that scans for packages in a background thread."""
|
|
400
|
+
return list_known_packages_by_source(
|
|
401
|
+
extra_source_roots=self._extra_source_roots or None,
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
def on_worker_state_changed(self, event: Worker.StateChanged) -> None:
|
|
405
|
+
"""Handle worker completion."""
|
|
406
|
+
if event.state == WorkerState.SUCCESS:
|
|
407
|
+
self._packages_cache = event.worker.result
|
|
408
|
+
self._packages_loading = False
|
|
409
|
+
self._packages_error = None
|
|
410
|
+
# Update loading indicator
|
|
411
|
+
self._update_loading_status()
|
|
412
|
+
elif event.state == WorkerState.ERROR:
|
|
413
|
+
self._packages_loading = False
|
|
414
|
+
self._packages_error = str(event.worker.error)
|
|
415
|
+
self._update_loading_status()
|
|
416
|
+
|
|
417
|
+
def _update_loading_status(self) -> None:
|
|
418
|
+
"""Update loading indicator status."""
|
|
419
|
+
try:
|
|
420
|
+
loading_container = self.query_one("#welcome_loading")
|
|
421
|
+
loading_text = self.query_one("#loading_text", Static)
|
|
422
|
+
if self._packages_cache is not None:
|
|
423
|
+
total = sum(len(v) for v in self._packages_cache.values())
|
|
424
|
+
loading_container.remove_class("loading")
|
|
425
|
+
# Update hint to show ready status
|
|
426
|
+
hint = self.query_one("#welcome_hint", Static)
|
|
427
|
+
hint.update(
|
|
428
|
+
f"[green]✓[/] {total} packages found · [cyan]Enter[/] to explore · [dim]q[/] to quit"
|
|
429
|
+
)
|
|
430
|
+
elif self._packages_error:
|
|
431
|
+
loading_text.update(f"[red]Error: {self._packages_error}[/]")
|
|
432
|
+
except Exception:
|
|
433
|
+
pass
|
|
434
|
+
|
|
435
|
+
def _check_loading_complete(self) -> None:
|
|
436
|
+
"""Check if background loading is complete and update main view."""
|
|
437
|
+
if self._packages_loading:
|
|
438
|
+
# Still loading, check again later
|
|
439
|
+
self.set_timer(0.3, self._check_loading_complete)
|
|
440
|
+
return
|
|
441
|
+
# Loading complete, refresh main view
|
|
442
|
+
if self._main_started:
|
|
443
|
+
tree = self.query_one("#dep_tree", Tree)
|
|
444
|
+
self._clear_tree(tree)
|
|
445
|
+
self._load_main_view()
|
|
446
|
+
|
|
362
447
|
def action_start_main(self) -> None:
|
|
363
448
|
"""Transition from welcome screen to main view."""
|
|
364
449
|
if self._main_started:
|
|
@@ -393,9 +478,26 @@ class DepTreeApp(App[None]):
|
|
|
393
478
|
if self._root_package:
|
|
394
479
|
self._load_tree(self._root_package)
|
|
395
480
|
else:
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
481
|
+
# Use cached packages if available, otherwise load (fallback)
|
|
482
|
+
if self._packages_loading:
|
|
483
|
+
# Still loading - show loading message and wait
|
|
484
|
+
tree.root.label = f"[{COLOR_HEADER}]Loading packages...[/]"
|
|
485
|
+
tree.root.add_leaf("[dim]Scanning for packages, please wait...[/]")
|
|
486
|
+
self._set_details("[dim]Scanning for ROS 2 packages in background...[/]")
|
|
487
|
+
try:
|
|
488
|
+
tree.focus()
|
|
489
|
+
except Exception:
|
|
490
|
+
pass
|
|
491
|
+
# Set a timer to check again
|
|
492
|
+
self.set_timer(0.5, self._check_loading_complete)
|
|
493
|
+
return
|
|
494
|
+
by_source = self._packages_cache
|
|
495
|
+
if by_source is None:
|
|
496
|
+
# Cache not available, load synchronously (fallback)
|
|
497
|
+
by_source = list_known_packages_by_source(
|
|
498
|
+
extra_source_roots=self._extra_source_roots or None,
|
|
499
|
+
)
|
|
500
|
+
self._packages_cache = by_source
|
|
399
501
|
if not by_source:
|
|
400
502
|
self._set_details(
|
|
401
503
|
"No ROS 2 packages found. Set AMENT_PREFIX_PATH or run from a workspace.\n\n"
|
|
@@ -547,8 +649,13 @@ class DepTreeApp(App[None]):
|
|
|
547
649
|
if self._root_package:
|
|
548
650
|
self._load_tree(self._root_package)
|
|
549
651
|
else:
|
|
652
|
+
# Clear cache to force rescan
|
|
653
|
+
self._packages_cache = None
|
|
654
|
+
self._packages_loading = False
|
|
550
655
|
tree = self.query_one("#dep_tree", Tree)
|
|
551
656
|
self._clear_tree(tree)
|
|
657
|
+
# Start background scan and show loading
|
|
658
|
+
self._start_package_scan()
|
|
552
659
|
self._load_main_view()
|
|
553
660
|
|
|
554
661
|
def action_expand_all(self) -> None:
|
|
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
|