cjm-transcript-source-select 0.0.15__tar.gz → 0.0.16__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.
Files changed (39) hide show
  1. {cjm_transcript_source_select-0.0.15/cjm_transcript_source_select.egg-info → cjm_transcript_source_select-0.0.16}/PKG-INFO +38 -26
  2. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/README.md +37 -25
  3. cjm_transcript_source_select-0.0.16/cjm_transcript_source_select/__init__.py +1 -0
  4. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/_modidx.py +2 -0
  5. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/models.py +6 -0
  6. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/routes/init.py +1 -0
  7. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/routes/local_files.py +70 -8
  8. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16/cjm_transcript_source_select.egg-info}/PKG-INFO +38 -26
  9. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/settings.ini +1 -1
  10. cjm_transcript_source_select-0.0.15/cjm_transcript_source_select/__init__.py +0 -1
  11. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/LICENSE +0 -0
  12. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/MANIFEST.in +0 -0
  13. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/components/__init__.py +0 -0
  14. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/components/helpers.py +0 -0
  15. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/components/local_files.py +0 -0
  16. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/components/preview_panel.py +0 -0
  17. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/components/selection_queue.py +0 -0
  18. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/components/source_browser.py +0 -0
  19. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/components/step_renderer.py +0 -0
  20. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/html_ids.py +0 -0
  21. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/routes/__init__.py +0 -0
  22. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/routes/core.py +0 -0
  23. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/routes/filtering.py +0 -0
  24. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/routes/queue.py +0 -0
  25. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/routes/source_browser.py +0 -0
  26. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/routes/tabs.py +0 -0
  27. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/services/__init__.py +0 -0
  28. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/services/source.py +0 -0
  29. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/services/source_utils.py +0 -0
  30. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select/utils.py +0 -0
  31. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select.egg-info/SOURCES.txt +0 -0
  32. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select.egg-info/dependency_links.txt +0 -0
  33. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select.egg-info/entry_points.txt +0 -0
  34. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select.egg-info/not-zip-safe +0 -0
  35. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select.egg-info/requires.txt +0 -0
  36. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/cjm_transcript_source_select.egg-info/top_level.txt +0 -0
  37. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/pyproject.toml +0 -0
  38. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/setup.cfg +0 -0
  39. {cjm_transcript_source_select-0.0.15 → cjm_transcript_source_select-0.0.16}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cjm-transcript-source-select
3
- Version: 0.0.15
3
+ Version: 0.0.16
4
4
  Summary: FastHTML source selection component for transcript decomposition workflows, with federated database browsing, drag-drop ordering, and keyboard navigation.
5
5
  Home-page: https://github.com/cj-mills/cjm-transcript-source-select
6
6
  Author: Christian J. Mills
@@ -112,51 +112,51 @@ graph LR
112
112
  components_source_browser --> services_source_utils
113
113
  components_source_browser --> utils
114
114
  components_source_browser --> html_ids
115
- components_step_renderer --> components_preview_panel
116
- components_step_renderer --> components_selection_queue
117
- components_step_renderer --> utils
118
- components_step_renderer --> components_local_files
115
+ components_step_renderer --> components_source_browser
119
116
  components_step_renderer --> html_ids
117
+ components_step_renderer --> utils
118
+ components_step_renderer --> components_selection_queue
120
119
  components_step_renderer --> models
121
- components_step_renderer --> components_source_browser
122
- routes_core --> models
123
- routes_core --> services_source
124
- routes_core --> components_selection_queue
120
+ components_step_renderer --> components_preview_panel
121
+ components_step_renderer --> components_local_files
125
122
  routes_core --> components_step_renderer
123
+ routes_core --> models
126
124
  routes_core --> html_ids
125
+ routes_core --> components_selection_queue
126
+ routes_core --> services_source
127
127
  routes_filtering --> routes_core
128
128
  routes_filtering --> services_source_utils
129
- routes_filtering --> services_source
130
129
  routes_filtering --> models
131
- routes_init --> routes_core
130
+ routes_filtering --> services_source
132
131
  routes_init --> routes_queue
133
- routes_init --> routes_tabs
132
+ routes_init --> routes_core
133
+ routes_init --> models
134
134
  routes_init --> routes_filtering
135
+ routes_init --> routes_tabs
135
136
  routes_init --> services_source
136
137
  routes_init --> routes_source_browser
137
138
  routes_init --> routes_local_files
138
- routes_init --> models
139
- routes_local_files --> models
140
- routes_local_files --> routes_core
141
139
  routes_local_files --> components_local_files
140
+ routes_local_files --> routes_core
142
141
  routes_local_files --> services_source
142
+ routes_local_files --> models
143
143
  routes_queue --> routes_core
144
144
  routes_queue --> services_source_utils
145
+ routes_queue --> models
145
146
  routes_queue --> components_preview_panel
146
147
  routes_queue --> services_source
147
- routes_queue --> models
148
- routes_source_browser --> components_source_browser
149
- routes_source_browser --> components_preview_panel
150
- routes_source_browser --> services_source_utils
151
- routes_source_browser --> services_source
152
148
  routes_source_browser --> routes_core
153
149
  routes_source_browser --> html_ids
154
150
  routes_source_browser --> models
151
+ routes_source_browser --> components_source_browser
152
+ routes_source_browser --> components_preview_panel
153
+ routes_source_browser --> services_source
154
+ routes_source_browser --> services_source_utils
155
+ routes_tabs --> routes_core
156
+ routes_tabs --> services_source_utils
155
157
  routes_tabs --> models
156
158
  routes_tabs --> components_step_renderer
157
159
  routes_tabs --> services_source
158
- routes_tabs --> routes_core
159
- routes_tabs --> services_source_utils
160
160
  ```
161
161
 
162
162
  *52 cross-module dependencies detected*
@@ -528,8 +528,9 @@ def _handle_remove_external_source(
528
528
  external_db_paths_ref: List[str], # Shared external paths list (mutated in place)
529
529
  fb_routers: FileBrowserRouters, # File browser routers (for targeted OOB)
530
530
  remove_url: str, # URL for remove button in external sources list
531
- ): # Tuple of OOB elements (external sources list + checkbox cells)
532
- "Remove an external database source from the Added Sources list."
531
+ urls: SelectionUrls, # Full URL bundle for queue re-rendering
532
+ ): # Tuple of OOB elements (external sources list + checkbox cells + queue + stats)
533
+ "Remove an external database source and clean up orphaned queue items."
533
534
  ```
534
535
 
535
536
  ``` python
@@ -539,7 +540,7 @@ def init_local_files_router(
539
540
  source_service: SourceService, # The source service for external db ops
540
541
  prefix: str, # Route prefix (e.g., "/workflow/selection/local_files")
541
542
  urls: SelectionUrls, # URL bundle for rendering
542
- ) -> LocalFilesResult: # Router result with routers, routes, render, and restore
543
+ ) -> LocalFilesResult: # Router result with routers, routes, render, restore, and reset
543
544
  "Initialize local files browser routes with new file browser API."
544
545
  ```
545
546
 
@@ -571,9 +572,18 @@ def _no_op_restore(session_id: str) -> None:
571
572
  """Default no-op for restore_state."""
572
573
  pass
573
574
 
575
+ def _no_op_reset() -> None
576
+ "Default no-op for restore_state."
577
+ ```
578
+
579
+ ``` python
580
+ def _no_op_reset() -> None:
581
+ """Default no-op for reset_state."""
582
+ pass
583
+
574
584
  @dataclass
575
585
  class LocalFilesResult
576
- "Default no-op for restore_state."
586
+ "Default no-op for reset_state."
577
587
  ```
578
588
 
579
589
  #### Classes
@@ -614,6 +624,7 @@ class LocalFilesResult:
614
624
  routes: Dict[str, Callable] # Named route handlers
615
625
  render_panel: Callable # (error_message?, session_id?) -> rendered panel
616
626
  restore_state: Callable = field(...) # (session_id) -> None, restore persisted state
627
+ reset_state: Callable = field(...) # () -> None, reset in-memory caches
617
628
  ```
618
629
 
619
630
  ``` python
@@ -627,6 +638,7 @@ class SelectionResult:
627
638
  render_local_files_panel: Optional[Callable] # Render fn for local files tab
628
639
  sb_state: Any # SourceBrowserRouterState
629
640
  restore_state: Callable = field(...) # (session_id) -> None, restore persisted state
641
+ reset_state: Callable = field(...) # () -> None, reset in-memory caches
630
642
  ```
631
643
 
632
644
  ### preview_panel (`preview_panel.ipynb`)
@@ -66,51 +66,51 @@ graph LR
66
66
  components_source_browser --> services_source_utils
67
67
  components_source_browser --> utils
68
68
  components_source_browser --> html_ids
69
- components_step_renderer --> components_preview_panel
70
- components_step_renderer --> components_selection_queue
71
- components_step_renderer --> utils
72
- components_step_renderer --> components_local_files
69
+ components_step_renderer --> components_source_browser
73
70
  components_step_renderer --> html_ids
71
+ components_step_renderer --> utils
72
+ components_step_renderer --> components_selection_queue
74
73
  components_step_renderer --> models
75
- components_step_renderer --> components_source_browser
76
- routes_core --> models
77
- routes_core --> services_source
78
- routes_core --> components_selection_queue
74
+ components_step_renderer --> components_preview_panel
75
+ components_step_renderer --> components_local_files
79
76
  routes_core --> components_step_renderer
77
+ routes_core --> models
80
78
  routes_core --> html_ids
79
+ routes_core --> components_selection_queue
80
+ routes_core --> services_source
81
81
  routes_filtering --> routes_core
82
82
  routes_filtering --> services_source_utils
83
- routes_filtering --> services_source
84
83
  routes_filtering --> models
85
- routes_init --> routes_core
84
+ routes_filtering --> services_source
86
85
  routes_init --> routes_queue
87
- routes_init --> routes_tabs
86
+ routes_init --> routes_core
87
+ routes_init --> models
88
88
  routes_init --> routes_filtering
89
+ routes_init --> routes_tabs
89
90
  routes_init --> services_source
90
91
  routes_init --> routes_source_browser
91
92
  routes_init --> routes_local_files
92
- routes_init --> models
93
- routes_local_files --> models
94
- routes_local_files --> routes_core
95
93
  routes_local_files --> components_local_files
94
+ routes_local_files --> routes_core
96
95
  routes_local_files --> services_source
96
+ routes_local_files --> models
97
97
  routes_queue --> routes_core
98
98
  routes_queue --> services_source_utils
99
+ routes_queue --> models
99
100
  routes_queue --> components_preview_panel
100
101
  routes_queue --> services_source
101
- routes_queue --> models
102
- routes_source_browser --> components_source_browser
103
- routes_source_browser --> components_preview_panel
104
- routes_source_browser --> services_source_utils
105
- routes_source_browser --> services_source
106
102
  routes_source_browser --> routes_core
107
103
  routes_source_browser --> html_ids
108
104
  routes_source_browser --> models
105
+ routes_source_browser --> components_source_browser
106
+ routes_source_browser --> components_preview_panel
107
+ routes_source_browser --> services_source
108
+ routes_source_browser --> services_source_utils
109
+ routes_tabs --> routes_core
110
+ routes_tabs --> services_source_utils
109
111
  routes_tabs --> models
110
112
  routes_tabs --> components_step_renderer
111
113
  routes_tabs --> services_source
112
- routes_tabs --> routes_core
113
- routes_tabs --> services_source_utils
114
114
  ```
115
115
 
116
116
  *52 cross-module dependencies detected*
@@ -482,8 +482,9 @@ def _handle_remove_external_source(
482
482
  external_db_paths_ref: List[str], # Shared external paths list (mutated in place)
483
483
  fb_routers: FileBrowserRouters, # File browser routers (for targeted OOB)
484
484
  remove_url: str, # URL for remove button in external sources list
485
- ): # Tuple of OOB elements (external sources list + checkbox cells)
486
- "Remove an external database source from the Added Sources list."
485
+ urls: SelectionUrls, # Full URL bundle for queue re-rendering
486
+ ): # Tuple of OOB elements (external sources list + checkbox cells + queue + stats)
487
+ "Remove an external database source and clean up orphaned queue items."
487
488
  ```
488
489
 
489
490
  ``` python
@@ -493,7 +494,7 @@ def init_local_files_router(
493
494
  source_service: SourceService, # The source service for external db ops
494
495
  prefix: str, # Route prefix (e.g., "/workflow/selection/local_files")
495
496
  urls: SelectionUrls, # URL bundle for rendering
496
- ) -> LocalFilesResult: # Router result with routers, routes, render, and restore
497
+ ) -> LocalFilesResult: # Router result with routers, routes, render, restore, and reset
497
498
  "Initialize local files browser routes with new file browser API."
498
499
  ```
499
500
 
@@ -525,9 +526,18 @@ def _no_op_restore(session_id: str) -> None:
525
526
  """Default no-op for restore_state."""
526
527
  pass
527
528
 
529
+ def _no_op_reset() -> None
530
+ "Default no-op for restore_state."
531
+ ```
532
+
533
+ ``` python
534
+ def _no_op_reset() -> None:
535
+ """Default no-op for reset_state."""
536
+ pass
537
+
528
538
  @dataclass
529
539
  class LocalFilesResult
530
- "Default no-op for restore_state."
540
+ "Default no-op for reset_state."
531
541
  ```
532
542
 
533
543
  #### Classes
@@ -568,6 +578,7 @@ class LocalFilesResult:
568
578
  routes: Dict[str, Callable] # Named route handlers
569
579
  render_panel: Callable # (error_message?, session_id?) -> rendered panel
570
580
  restore_state: Callable = field(...) # (session_id) -> None, restore persisted state
581
+ reset_state: Callable = field(...) # () -> None, reset in-memory caches
571
582
  ```
572
583
 
573
584
  ``` python
@@ -581,6 +592,7 @@ class SelectionResult:
581
592
  render_local_files_panel: Optional[Callable] # Render fn for local files tab
582
593
  sb_state: Any # SourceBrowserRouterState
583
594
  restore_state: Callable = field(...) # (session_id) -> None, restore persisted state
595
+ reset_state: Callable = field(...) # () -> None, reset in-memory caches
584
596
  ```
585
597
 
586
598
  ### preview_panel (`preview_panel.ipynb`)
@@ -0,0 +1 @@
1
+ __version__ = "0.0.16"
@@ -89,6 +89,8 @@ d = { 'settings': { 'branch': 'main',
89
89
  'cjm_transcript_source_select/models.py'),
90
90
  'cjm_transcript_source_select.models.SelectionUrls': ( 'models.html#selectionurls',
91
91
  'cjm_transcript_source_select/models.py'),
92
+ 'cjm_transcript_source_select.models._no_op_reset': ( 'models.html#_no_op_reset',
93
+ 'cjm_transcript_source_select/models.py'),
92
94
  'cjm_transcript_source_select.models._no_op_restore': ( 'models.html#_no_op_restore',
93
95
  'cjm_transcript_source_select/models.py')},
94
96
  'cjm_transcript_source_select.routes.core': { 'cjm_transcript_source_select.routes.core._build_queue_response': ( 'routes/core.html#_build_queue_response',
@@ -59,6 +59,10 @@ def _no_op_restore(session_id: str) -> None:
59
59
  """Default no-op for restore_state."""
60
60
  pass
61
61
 
62
+ def _no_op_reset() -> None:
63
+ """Default no-op for reset_state."""
64
+ pass
65
+
62
66
  @dataclass
63
67
  class LocalFilesResult:
64
68
  """Return type from init_local_files_router."""
@@ -66,6 +70,7 @@ class LocalFilesResult:
66
70
  routes: Dict[str, Callable] # Named route handlers
67
71
  render_panel: Callable # (error_message?, session_id?) -> rendered panel
68
72
  restore_state: Callable = field(default=_no_op_restore) # (session_id) -> None, restore persisted state
73
+ reset_state: Callable = field(default=_no_op_reset) # () -> None, reset in-memory caches
69
74
 
70
75
  @dataclass
71
76
  class SelectionResult:
@@ -76,3 +81,4 @@ class SelectionResult:
76
81
  render_local_files_panel: Optional[Callable] = None # Render fn for local files tab
77
82
  sb_state: Any = None # SourceBrowserRouterState
78
83
  restore_state: Callable = field(default=_no_op_restore) # (session_id) -> None, restore persisted state
84
+ reset_state: Callable = field(default=_no_op_reset) # () -> None, reset in-memory caches
@@ -127,4 +127,5 @@ def init_selection_routers(
127
127
  render_local_files_panel=local_files.render_panel,
128
128
  sb_state=sb_state,
129
129
  restore_state=local_files.restore_state,
130
+ reset_state=local_files.reset_state,
130
131
  )
@@ -19,7 +19,8 @@ from cjm_fasthtml_interactions.core.state_store import get_session_id
19
19
 
20
20
  from ..models import SelectionUrls, LocalFilesResult
21
21
  from cjm_transcript_source_select.routes.core import (
22
- WorkflowStateStore, _get_step_state, _update_step_state
22
+ WorkflowStateStore, _get_step_state, _update_step_state,
23
+ _build_queue_response,
23
24
  )
24
25
  from cjm_transcript_source_select.components.local_files import (
25
26
  _render_local_files_browser,
@@ -54,12 +55,15 @@ def _handle_remove_external_source(
54
55
  external_db_paths_ref: List[str], # Shared external paths list (mutated in place)
55
56
  fb_routers: FileBrowserRouters, # File browser routers (for targeted OOB)
56
57
  remove_url: str, # URL for remove button in external sources list
57
- ): # Tuple of OOB elements (external sources list + checkbox cells)
58
- """Remove an external database source from the Added Sources list."""
58
+ urls: SelectionUrls, # Full URL bundle for queue re-rendering
59
+ ): # Tuple of OOB elements (external sources list + checkbox cells + queue + stats)
60
+ """Remove an external database source and clean up orphaned queue items."""
59
61
  session_id = get_session_id(sess)
60
62
 
61
63
  # Remove the path if it exists
64
+ removed_provider_id = None
62
65
  if db_path in external_db_paths_ref:
66
+ removed_provider_id = f"external:{db_path}"
63
67
  external_db_paths_ref.remove(db_path)
64
68
  _update_step_state(state_store, workflow_id, session_id, external_db_paths=list(external_db_paths_ref))
65
69
  source_service.set_external_paths(list(external_db_paths_ref))
@@ -69,7 +73,28 @@ def _handle_remove_external_source(
69
73
  external_list_oob = _render_external_sources_list(
70
74
  list(external_db_paths_ref), remove_url, oob=True,
71
75
  )
72
- return (external_list_oob, *checkbox_oobs)
76
+
77
+ parts = [external_list_oob, *checkbox_oobs]
78
+
79
+ # Remove orphaned queue items from the deleted provider
80
+ if removed_provider_id is not None:
81
+ step_state = _get_step_state(state_store, workflow_id, session_id)
82
+ selected_sources = step_state.get("selected_sources", [])
83
+ filtered = [s for s in selected_sources if s.get("provider_id") != removed_provider_id]
84
+ if len(filtered) != len(selected_sources):
85
+ _update_step_state(state_store, workflow_id, session_id, selected_sources=filtered)
86
+ # Re-render queue + stats as OOB
87
+ queue_response = _build_queue_response(
88
+ state_store, workflow_id, source_service, session_id,
89
+ filtered, urls, include_checkbox_oobs=False,
90
+ )
91
+ queue_parts = queue_response if isinstance(queue_response, tuple) else (queue_response,)
92
+ for part in queue_parts:
93
+ if hasattr(part, 'attrs') and 'hx-swap-oob' not in part.attrs and 'id' in part.attrs:
94
+ part.attrs['hx-swap-oob'] = 'outerHTML'
95
+ parts.extend(queue_parts)
96
+
97
+ return tuple(parts)
73
98
 
74
99
  # %% ../../nbs/routes/local_files.ipynb #rdrpw4jrxpc
75
100
  def init_local_files_router(
@@ -78,7 +103,7 @@ def init_local_files_router(
78
103
  source_service: SourceService, # The source service for external db ops
79
104
  prefix: str, # Route prefix (e.g., "/workflow/selection/local_files")
80
105
  urls: SelectionUrls, # URL bundle for rendering
81
- ) -> LocalFilesResult: # Router result with routers, routes, render, and restore
106
+ ) -> LocalFilesResult: # Router result with routers, routes, render, restore, and reset
82
107
  """Initialize local files browser routes with new file browser API."""
83
108
  provider = _get_local_files_provider()
84
109
  config = _create_db_browser_config()
@@ -156,8 +181,10 @@ def init_local_files_router(
156
181
  if request is not None and not _state_restored[0]:
157
182
  _restore_from_persisted(get_session_id(request.session))
158
183
 
159
- # Validate any newly added paths
184
+ # Detect removed paths before validation
160
185
  old_set = set(_external_db_paths)
186
+
187
+ # Validate any newly added paths
161
188
  validated_paths = []
162
189
  error_message = None
163
190
  for path in selected_paths:
@@ -185,10 +212,32 @@ def init_local_files_router(
185
212
  external_db_paths=list(_external_db_paths),
186
213
  )
187
214
 
188
- return (
215
+ parts = [
189
216
  _render_external_sources_list(list(_external_db_paths), urls.remove_external, oob=True),
190
217
  _render_error_alert(error_message, oob=True),
191
- )
218
+ ]
219
+
220
+ # Clean up orphaned queue items for removed external DBs
221
+ removed_paths = old_set - set(validated_paths)
222
+ if removed_paths and request is not None:
223
+ session_id = get_session_id(request.session)
224
+ removed_provider_ids = {f"external:{p}" for p in removed_paths}
225
+ step_state = _get_step_state(state_store, workflow_id, session_id)
226
+ selected_sources = step_state.get("selected_sources", [])
227
+ filtered = [s for s in selected_sources if s.get("provider_id") not in removed_provider_ids]
228
+ if len(filtered) != len(selected_sources):
229
+ _update_step_state(state_store, workflow_id, session_id, selected_sources=filtered)
230
+ queue_response = _build_queue_response(
231
+ state_store, workflow_id, source_service, session_id,
232
+ filtered, urls, include_checkbox_oobs=False,
233
+ )
234
+ queue_parts = queue_response if isinstance(queue_response, tuple) else (queue_response,)
235
+ for part in queue_parts:
236
+ if hasattr(part, 'attrs') and 'hx-swap-oob' not in part.attrs and 'id' in part.attrs:
237
+ part.attrs['hx-swap-oob'] = 'outerHTML'
238
+ parts.extend(queue_parts)
239
+
240
+ return tuple(parts)
192
241
 
193
242
  fb_callbacks = FileBrowserCallbacks(
194
243
  validate_selection=_validate_selection,
@@ -226,6 +275,17 @@ def init_local_files_router(
226
275
  _restore_from_persisted(session_id)
227
276
  fb_routers.sync_items()
228
277
 
278
+ # --- Reset callable for clearing in-memory caches ---
279
+ def _reset_state() -> None:
280
+ """Reset in-memory state so next restore re-reads from DB."""
281
+ _external_db_paths[:] = []
282
+ _state_restored[0] = False
283
+ _browser_state.current_path = home_path
284
+ _browser_state.sort_by = "name"
285
+ _browser_state.sort_descending = False
286
+ _browser_state.selection.selected_paths = []
287
+ source_service.set_external_paths([])
288
+
229
289
  # --- Custom router for remove_external (needs session context) ---
230
290
  custom_router = APIRouter(prefix=prefix)
231
291
 
@@ -238,6 +298,7 @@ def init_local_files_router(
238
298
  external_db_paths_ref=_external_db_paths,
239
299
  fb_routers=fb_routers,
240
300
  remove_url=urls.remove_external,
301
+ urls=urls,
241
302
  )
242
303
 
243
304
  # All routers to register
@@ -252,4 +313,5 @@ def init_local_files_router(
252
313
  routes=routes,
253
314
  render_panel=_render_panel,
254
315
  restore_state=_restore_state,
316
+ reset_state=_reset_state,
255
317
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cjm-transcript-source-select
3
- Version: 0.0.15
3
+ Version: 0.0.16
4
4
  Summary: FastHTML source selection component for transcript decomposition workflows, with federated database browsing, drag-drop ordering, and keyboard navigation.
5
5
  Home-page: https://github.com/cj-mills/cjm-transcript-source-select
6
6
  Author: Christian J. Mills
@@ -112,51 +112,51 @@ graph LR
112
112
  components_source_browser --> services_source_utils
113
113
  components_source_browser --> utils
114
114
  components_source_browser --> html_ids
115
- components_step_renderer --> components_preview_panel
116
- components_step_renderer --> components_selection_queue
117
- components_step_renderer --> utils
118
- components_step_renderer --> components_local_files
115
+ components_step_renderer --> components_source_browser
119
116
  components_step_renderer --> html_ids
117
+ components_step_renderer --> utils
118
+ components_step_renderer --> components_selection_queue
120
119
  components_step_renderer --> models
121
- components_step_renderer --> components_source_browser
122
- routes_core --> models
123
- routes_core --> services_source
124
- routes_core --> components_selection_queue
120
+ components_step_renderer --> components_preview_panel
121
+ components_step_renderer --> components_local_files
125
122
  routes_core --> components_step_renderer
123
+ routes_core --> models
126
124
  routes_core --> html_ids
125
+ routes_core --> components_selection_queue
126
+ routes_core --> services_source
127
127
  routes_filtering --> routes_core
128
128
  routes_filtering --> services_source_utils
129
- routes_filtering --> services_source
130
129
  routes_filtering --> models
131
- routes_init --> routes_core
130
+ routes_filtering --> services_source
132
131
  routes_init --> routes_queue
133
- routes_init --> routes_tabs
132
+ routes_init --> routes_core
133
+ routes_init --> models
134
134
  routes_init --> routes_filtering
135
+ routes_init --> routes_tabs
135
136
  routes_init --> services_source
136
137
  routes_init --> routes_source_browser
137
138
  routes_init --> routes_local_files
138
- routes_init --> models
139
- routes_local_files --> models
140
- routes_local_files --> routes_core
141
139
  routes_local_files --> components_local_files
140
+ routes_local_files --> routes_core
142
141
  routes_local_files --> services_source
142
+ routes_local_files --> models
143
143
  routes_queue --> routes_core
144
144
  routes_queue --> services_source_utils
145
+ routes_queue --> models
145
146
  routes_queue --> components_preview_panel
146
147
  routes_queue --> services_source
147
- routes_queue --> models
148
- routes_source_browser --> components_source_browser
149
- routes_source_browser --> components_preview_panel
150
- routes_source_browser --> services_source_utils
151
- routes_source_browser --> services_source
152
148
  routes_source_browser --> routes_core
153
149
  routes_source_browser --> html_ids
154
150
  routes_source_browser --> models
151
+ routes_source_browser --> components_source_browser
152
+ routes_source_browser --> components_preview_panel
153
+ routes_source_browser --> services_source
154
+ routes_source_browser --> services_source_utils
155
+ routes_tabs --> routes_core
156
+ routes_tabs --> services_source_utils
155
157
  routes_tabs --> models
156
158
  routes_tabs --> components_step_renderer
157
159
  routes_tabs --> services_source
158
- routes_tabs --> routes_core
159
- routes_tabs --> services_source_utils
160
160
  ```
161
161
 
162
162
  *52 cross-module dependencies detected*
@@ -528,8 +528,9 @@ def _handle_remove_external_source(
528
528
  external_db_paths_ref: List[str], # Shared external paths list (mutated in place)
529
529
  fb_routers: FileBrowserRouters, # File browser routers (for targeted OOB)
530
530
  remove_url: str, # URL for remove button in external sources list
531
- ): # Tuple of OOB elements (external sources list + checkbox cells)
532
- "Remove an external database source from the Added Sources list."
531
+ urls: SelectionUrls, # Full URL bundle for queue re-rendering
532
+ ): # Tuple of OOB elements (external sources list + checkbox cells + queue + stats)
533
+ "Remove an external database source and clean up orphaned queue items."
533
534
  ```
534
535
 
535
536
  ``` python
@@ -539,7 +540,7 @@ def init_local_files_router(
539
540
  source_service: SourceService, # The source service for external db ops
540
541
  prefix: str, # Route prefix (e.g., "/workflow/selection/local_files")
541
542
  urls: SelectionUrls, # URL bundle for rendering
542
- ) -> LocalFilesResult: # Router result with routers, routes, render, and restore
543
+ ) -> LocalFilesResult: # Router result with routers, routes, render, restore, and reset
543
544
  "Initialize local files browser routes with new file browser API."
544
545
  ```
545
546
 
@@ -571,9 +572,18 @@ def _no_op_restore(session_id: str) -> None:
571
572
  """Default no-op for restore_state."""
572
573
  pass
573
574
 
575
+ def _no_op_reset() -> None
576
+ "Default no-op for restore_state."
577
+ ```
578
+
579
+ ``` python
580
+ def _no_op_reset() -> None:
581
+ """Default no-op for reset_state."""
582
+ pass
583
+
574
584
  @dataclass
575
585
  class LocalFilesResult
576
- "Default no-op for restore_state."
586
+ "Default no-op for reset_state."
577
587
  ```
578
588
 
579
589
  #### Classes
@@ -614,6 +624,7 @@ class LocalFilesResult:
614
624
  routes: Dict[str, Callable] # Named route handlers
615
625
  render_panel: Callable # (error_message?, session_id?) -> rendered panel
616
626
  restore_state: Callable = field(...) # (session_id) -> None, restore persisted state
627
+ reset_state: Callable = field(...) # () -> None, reset in-memory caches
617
628
  ```
618
629
 
619
630
  ``` python
@@ -627,6 +638,7 @@ class SelectionResult:
627
638
  render_local_files_panel: Optional[Callable] # Render fn for local files tab
628
639
  sb_state: Any # SourceBrowserRouterState
629
640
  restore_state: Callable = field(...) # (session_id) -> None, restore persisted state
641
+ reset_state: Callable = field(...) # () -> None, reset in-memory caches
630
642
  ```
631
643
 
632
644
  ### preview_panel (`preview_panel.ipynb`)
@@ -1,7 +1,7 @@
1
1
  [DEFAULT]
2
2
  repo = cjm-transcript-source-select
3
3
  lib_name = cjm-transcript-source-select
4
- version = 0.0.15
4
+ version = 0.0.16
5
5
  min_python = 3.12
6
6
  license = apache2
7
7
  black_formatting = False
@@ -1 +0,0 @@
1
- __version__ = "0.0.15"