rostree 0.2.1__py3-none-any.whl → 0.2.2__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.
rostree/tui/app.py CHANGED
@@ -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
- by_source = list_known_packages_by_source(
397
- extra_source_roots=self._extra_source_roots or None,
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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rostree
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Explore ROS 2 package dependencies from the command line (CLI, TUI, library)
5
5
  Author: rostree contributors
6
6
  License: MIT
@@ -6,9 +6,9 @@ rostree/core/finder.py,sha256=lmfzjjHX-bDHprs3x-GKPztpCS4hVsZUq2fZOg64DTg,16801
6
6
  rostree/core/parser.py,sha256=_Yhyjrg7Ir-N80KqJnxPYRcejWepDZDFbw_BTKLBySs,3094
7
7
  rostree/core/tree.py,sha256=jBvd7_Pzk4ciO9dWV1r4NyU9q6BaXKiYRimbmK72oD4,4060
8
8
  rostree/tui/__init__.py,sha256=hHtp0ZeBL6drxVxnBi1pm2uRds5O-UbNASQ6xRzOJrs,64
9
- rostree/tui/app.py,sha256=MOKWDaSdQW1eJzkNY9juDoFN2Pu-HHOqtZaqq6-UAvE,24327
10
- rostree-0.2.1.dist-info/METADATA,sha256=KcfJWnZsVyCgKRE2Rj9eBhiGbtU1OqvQEvwqIfQqg_k,4552
11
- rostree-0.2.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
12
- rostree-0.2.1.dist-info/entry_points.txt,sha256=3FqDola110oRFImx5-IF3CEnfCXA3CDSM2fTzbES2Zo,45
13
- rostree-0.2.1.dist-info/licenses/LICENSE,sha256=mcuqLv_cT8O1n1fIsOkUNDMnKlrPgP5LVCMyyVl-tGk,1530
14
- rostree-0.2.1.dist-info/RECORD,,
9
+ rostree/tui/app.py,sha256=Du2bY48uICDkimGLDPiEeNh1ii_lqMeZK9a2kO3Z8sk,29076
10
+ rostree-0.2.2.dist-info/METADATA,sha256=pc2w2vBjfzdMF3hThnFzlML-3IaCGY0K0GTFDww9K_s,4552
11
+ rostree-0.2.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
12
+ rostree-0.2.2.dist-info/entry_points.txt,sha256=3FqDola110oRFImx5-IF3CEnfCXA3CDSM2fTzbES2Zo,45
13
+ rostree-0.2.2.dist-info/licenses/LICENSE,sha256=mcuqLv_cT8O1n1fIsOkUNDMnKlrPgP5LVCMyyVl-tGk,1530
14
+ rostree-0.2.2.dist-info/RECORD,,