cjm-transcript-source-select 0.0.29__tar.gz → 0.0.30__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.
- {cjm_transcript_source_select-0.0.29/cjm_transcript_source_select.egg-info → cjm_transcript_source_select-0.0.30}/PKG-INFO +81 -55
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/README.md +77 -51
- cjm_transcript_source_select-0.0.30/cjm_transcript_source_select/__init__.py +1 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/_modidx.py +2 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/components/source_browser.py +54 -18
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/components/step_renderer.py +46 -22
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/models.py +4 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/routes/init.py +9 -1
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/routes/local_files.py +3 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/routes/source_browser.py +28 -1
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30/cjm_transcript_source_select.egg-info}/PKG-INFO +81 -55
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select.egg-info/requires.txt +3 -3
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/pyproject.toml +1 -1
- cjm_transcript_source_select-0.0.29/cjm_transcript_source_select/__init__.py +0 -1
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/LICENSE +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/MANIFEST.in +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/components/__init__.py +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/components/helpers.py +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/components/local_files.py +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/components/preview_panel.py +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/components/selection_queue.py +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/html_ids.py +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/routes/__init__.py +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/routes/core.py +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/routes/filtering.py +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/routes/queue.py +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/routes/tabs.py +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/services/__init__.py +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/services/source.py +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/services/source_utils.py +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select/utils.py +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select.egg-info/SOURCES.txt +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select.egg-info/dependency_links.txt +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select.egg-info/entry_points.txt +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/cjm_transcript_source_select.egg-info/top_level.txt +0 -0
- {cjm_transcript_source_select-0.0.29 → cjm_transcript_source_select-0.0.30}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cjm-transcript-source-select
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.30
|
|
4
4
|
Summary: FastHTML source selection component for transcript decomposition workflows, with federated database browsing, drag-drop ordering, and keyboard navigation.
|
|
5
5
|
Author-email: "Christian J. Mills" <9126128+cj-mills@users.noreply.github.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -21,8 +21,8 @@ Requires-Dist: cjm-transcription-plugin-system
|
|
|
21
21
|
Requires-Dist: cjm-fasthtml-app-core
|
|
22
22
|
Requires-Dist: cjm-fasthtml-daisyui
|
|
23
23
|
Requires-Dist: cjm_fasthtml_lucide_icons
|
|
24
|
-
Requires-Dist: cjm_fasthtml_file_browser>=0.0.
|
|
25
|
-
Requires-Dist: cjm_fasthtml_keyboard_navigation>=0.0.
|
|
24
|
+
Requires-Dist: cjm_fasthtml_file_browser>=0.0.19
|
|
25
|
+
Requires-Dist: cjm_fasthtml_keyboard_navigation>=0.0.23
|
|
26
26
|
Requires-Dist: duckdb
|
|
27
27
|
Requires-Dist: pandas
|
|
28
28
|
Requires-Dist: cjm_workflow_state
|
|
@@ -30,7 +30,7 @@ Requires-Dist: cjm_source_provider
|
|
|
30
30
|
Requires-Dist: cjm_fasthtml_interactions
|
|
31
31
|
Requires-Dist: cjm_fasthtml_viewport_fit
|
|
32
32
|
Requires-Dist: cjm_fasthtml_virtual_collection
|
|
33
|
-
Requires-Dist: cjm_fasthtml_sortable_queue>=0.0.
|
|
33
|
+
Requires-Dist: cjm_fasthtml_sortable_queue>=0.0.16
|
|
34
34
|
Requires-Dist: cjm_fasthtml_design_system>=0.0.9
|
|
35
35
|
Dynamic: license-file
|
|
36
36
|
|
|
@@ -96,57 +96,57 @@ graph LR
|
|
|
96
96
|
utils[utils<br/>utils]
|
|
97
97
|
|
|
98
98
|
components_helpers --> models
|
|
99
|
-
components_local_files --> components_helpers
|
|
100
99
|
components_local_files --> html_ids
|
|
100
|
+
components_local_files --> components_helpers
|
|
101
101
|
components_preview_panel --> html_ids
|
|
102
102
|
components_source_browser --> services_source_utils
|
|
103
103
|
components_source_browser --> html_ids
|
|
104
104
|
components_source_browser --> utils
|
|
105
|
-
components_step_renderer --> components_preview_panel
|
|
106
|
-
components_step_renderer --> utils
|
|
107
105
|
components_step_renderer --> components_selection_queue
|
|
108
|
-
components_step_renderer --> components_source_browser
|
|
109
|
-
components_step_renderer --> models
|
|
110
106
|
components_step_renderer --> html_ids
|
|
107
|
+
components_step_renderer --> models
|
|
111
108
|
components_step_renderer --> components_local_files
|
|
112
|
-
|
|
109
|
+
components_step_renderer --> components_source_browser
|
|
110
|
+
components_step_renderer --> components_preview_panel
|
|
111
|
+
components_step_renderer --> utils
|
|
113
112
|
routes_core --> models
|
|
114
|
-
routes_core --> components_selection_queue
|
|
115
|
-
routes_core --> services_source
|
|
116
113
|
routes_core --> html_ids
|
|
117
|
-
|
|
114
|
+
routes_core --> components_step_renderer
|
|
115
|
+
routes_core --> services_source
|
|
116
|
+
routes_core --> components_selection_queue
|
|
118
117
|
routes_filtering --> services_source_utils
|
|
119
118
|
routes_filtering --> services_source
|
|
119
|
+
routes_filtering --> routes_core
|
|
120
120
|
routes_filtering --> models
|
|
121
|
-
routes_init --> routes_local_files
|
|
122
|
-
routes_init --> routes_source_browser
|
|
123
121
|
routes_init --> routes_core
|
|
122
|
+
routes_init --> services_source
|
|
123
|
+
routes_init --> routes_local_files
|
|
124
124
|
routes_init --> routes_queue
|
|
125
125
|
routes_init --> routes_tabs
|
|
126
|
-
routes_init --> services_source
|
|
127
|
-
routes_init --> routes_filtering
|
|
128
126
|
routes_init --> models
|
|
129
|
-
|
|
127
|
+
routes_init --> routes_filtering
|
|
128
|
+
routes_init --> routes_source_browser
|
|
130
129
|
routes_local_files --> components_local_files
|
|
131
130
|
routes_local_files --> services_source
|
|
131
|
+
routes_local_files --> routes_core
|
|
132
132
|
routes_local_files --> models
|
|
133
|
-
routes_queue --> components_preview_panel
|
|
134
|
-
routes_queue --> routes_core
|
|
135
|
-
routes_queue --> services_source_utils
|
|
136
133
|
routes_queue --> services_source
|
|
134
|
+
routes_queue --> services_source_utils
|
|
135
|
+
routes_queue --> routes_core
|
|
137
136
|
routes_queue --> models
|
|
138
|
-
|
|
139
|
-
routes_source_browser --> components_source_browser
|
|
137
|
+
routes_queue --> components_preview_panel
|
|
140
138
|
routes_source_browser --> services_source
|
|
141
|
-
routes_source_browser -->
|
|
139
|
+
routes_source_browser --> components_source_browser
|
|
140
|
+
routes_source_browser --> html_ids
|
|
142
141
|
routes_source_browser --> models
|
|
143
142
|
routes_source_browser --> services_source_utils
|
|
144
|
-
routes_source_browser -->
|
|
145
|
-
|
|
143
|
+
routes_source_browser --> routes_core
|
|
144
|
+
routes_source_browser --> components_preview_panel
|
|
145
|
+
routes_tabs --> models
|
|
146
146
|
routes_tabs --> routes_core
|
|
147
|
-
routes_tabs -->
|
|
147
|
+
routes_tabs --> services_source
|
|
148
148
|
routes_tabs --> services_source_utils
|
|
149
|
-
routes_tabs -->
|
|
149
|
+
routes_tabs --> components_step_renderer
|
|
150
150
|
```
|
|
151
151
|
|
|
152
152
|
*52 cross-module dependencies detected*
|
|
@@ -525,12 +525,6 @@ def _handle_remove_external_source(
|
|
|
525
525
|
|
|
526
526
|
``` python
|
|
527
527
|
def init_local_files_router(
|
|
528
|
-
state_store: WorkflowStateStore, # The workflow state store
|
|
529
|
-
workflow_id: str, # The workflow identifier
|
|
530
|
-
source_service: SourceService, # The source service for external db ops
|
|
531
|
-
prefix: str, # Route prefix (e.g., "/workflow/selection/local_files")
|
|
532
|
-
urls: SelectionUrls, # URL bundle for rendering
|
|
533
|
-
) -> LocalFilesResult: # Router result with routers, routes, render, restore, and reset
|
|
534
528
|
"Initialize local files browser routes with new file browser API."
|
|
535
529
|
```
|
|
536
530
|
|
|
@@ -615,6 +609,7 @@ class LocalFilesResult:
|
|
|
615
609
|
render_panel: Callable # (error_message?, session_id?) -> rendered panel
|
|
616
610
|
restore_state: Callable = field(...) # (session_id) -> None, restore persisted state
|
|
617
611
|
reset_state: Callable = field(...) # () -> None, reset in-memory caches
|
|
612
|
+
kb_manager: Optional[ZoneManager] # ZoneManager backing the local file browser's keyboard system — hand to render_keyboard_hints_modal(..., child_managers=[...]) for hierarchical hint display
|
|
618
613
|
```
|
|
619
614
|
|
|
620
615
|
``` python
|
|
@@ -629,6 +624,7 @@ class SelectionResult:
|
|
|
629
624
|
sb_state: Any # SourceBrowserRouterState
|
|
630
625
|
restore_state: Callable = field(...) # (session_id) -> None, restore persisted state
|
|
631
626
|
reset_state: Callable = field(...) # () -> None, reset in-memory caches
|
|
627
|
+
fb_kb_manager: Optional[ZoneManager] # ZoneManager from the local file browser; convenience pointer mirroring LocalFilesResult.kb_manager so step renderers can read it directly from SelectionResult without descending into LocalFilesResult
|
|
632
628
|
```
|
|
633
629
|
|
|
634
630
|
### preview_panel (`preview_panel.ipynb`)
|
|
@@ -1067,7 +1063,8 @@ from cjm_transcript_source_select.components.source_browser import (
|
|
|
1067
1063
|
build_source_items,
|
|
1068
1064
|
is_source_item_skippable,
|
|
1069
1065
|
create_source_cell_renderer,
|
|
1070
|
-
render_source_empty
|
|
1066
|
+
render_source_empty,
|
|
1067
|
+
build_source_browser_keyboard_system
|
|
1071
1068
|
)
|
|
1072
1069
|
```
|
|
1073
1070
|
|
|
@@ -1128,19 +1125,45 @@ def render_source_empty() -> Any: # Empty state component
|
|
|
1128
1125
|
"Render empty state when no transcription sources are available."
|
|
1129
1126
|
```
|
|
1130
1127
|
|
|
1128
|
+
``` python
|
|
1129
|
+
def build_source_browser_keyboard_system(
|
|
1130
|
+
sb_state: Any, # SourceBrowserRouterState (carries vc_ids, vc_btn_ids, vc_urls)
|
|
1131
|
+
manager_label: Optional[str] = None, # Human-readable label for the ZoneManager (consumed by render_keyboard_hints_modal section header when this is a child_managers entry)
|
|
1132
|
+
) -> KeyboardSystem: # Complete rendered KeyboardSystem (carries .manager for hierarchical hints handoff)
|
|
1133
|
+
"""
|
|
1134
|
+
Build the source browser's KeyboardSystem standalone.
|
|
1135
|
+
|
|
1136
|
+
Lifts the manager construction out of `_render_source_browser_vc_content`
|
|
1137
|
+
so the underlying `ZoneManager` is accessible via `KeyboardSystem.manager`
|
|
1138
|
+
for hierarchical hints handoff. Consumers wiring this as a child of the
|
|
1139
|
+
parent selection-step manager pass the manager to
|
|
1140
|
+
`render_keyboard_hints_modal(..., child_managers=[...])` so the modal's
|
|
1141
|
+
section header reads as `manager_label` instead of the technical SB_SYSTEM_ID.
|
|
1142
|
+
"""
|
|
1143
|
+
```
|
|
1144
|
+
|
|
1131
1145
|
``` python
|
|
1132
1146
|
def _render_source_browser_vc_content(
|
|
1133
|
-
sb_state: Any,
|
|
1147
|
+
sb_state: Any, # SourceBrowserRouterState from routes.source_browser
|
|
1148
|
+
keyboard_system: Optional[KeyboardSystem] = None, # Pre-built keyboard system (when None, builds internally via build_source_browser_keyboard_system)
|
|
1134
1149
|
) -> Any: # VC content wrapper (without search/grouping header)
|
|
1135
|
-
"
|
|
1150
|
+
"""
|
|
1151
|
+
Render the VC content portion of the source browser.
|
|
1152
|
+
|
|
1153
|
+
When `keyboard_system` is provided, the caller has built the system upstream
|
|
1154
|
+
(typically `init_source_browser_router`) and owns the underlying ZoneManager.
|
|
1155
|
+
When None, builds the system internally — preserves pre-C1 behavior for any
|
|
1156
|
+
caller that doesn't need the manager handoff.
|
|
1157
|
+
"""
|
|
1136
1158
|
```
|
|
1137
1159
|
|
|
1138
1160
|
``` python
|
|
1139
1161
|
def _render_source_browser_vc(
|
|
1140
|
-
sb_state: Any,
|
|
1141
|
-
filter_url: str = "",
|
|
1142
|
-
grouping_mode: str = "media_path",
|
|
1143
|
-
grouping_change_url: str = "",
|
|
1162
|
+
sb_state: Any, # SourceBrowserRouterState from routes.source_browser
|
|
1163
|
+
filter_url: str = "", # URL for filtering sources
|
|
1164
|
+
grouping_mode: str = "media_path", # Current grouping mode
|
|
1165
|
+
grouping_change_url: str = "", # URL for changing grouping mode
|
|
1166
|
+
keyboard_system: Optional[KeyboardSystem] = None, # Pre-built keyboard system, threaded through to VC content
|
|
1144
1167
|
) -> Any: # Source browser component with virtual collection
|
|
1145
1168
|
"Render the full source browser panel (header + VC content)."
|
|
1146
1169
|
```
|
|
@@ -1187,10 +1210,6 @@ from cjm_transcript_source_select.routes.source_browser import (
|
|
|
1187
1210
|
|
|
1188
1211
|
``` python
|
|
1189
1212
|
def init_source_browser_router(
|
|
1190
|
-
source_service: SourceService, # Source service for querying transcriptions
|
|
1191
|
-
urls: SelectionUrls, # URL bundle (toggle, select_all, filter, grouping_change)
|
|
1192
|
-
prefix: str = "/browser", # Route prefix for VC routes
|
|
1193
|
-
) -> SourceBrowserRouterState: # Router state with all VC objects and helpers
|
|
1194
1213
|
"Initialize the source browser virtual collection router."
|
|
1195
1214
|
```
|
|
1196
1215
|
|
|
@@ -1215,6 +1234,7 @@ class SourceBrowserRouterState:
|
|
|
1215
1234
|
get_visible_checkbox_oobs: Callable # () -> tuple of OOB elements
|
|
1216
1235
|
get_checkbox_oob_for: Callable # (record_id, provider_id) -> OOB element or None
|
|
1217
1236
|
get_vc_row_id_for: Callable # (record_id, provider_id) -> str or None
|
|
1237
|
+
kb_manager: Optional[ZoneManager] # ZoneManager backing the source browser's VC keyboard system — hand to render_keyboard_hints_modal(..., child_managers=[...]) for hierarchical hint display
|
|
1218
1238
|
```
|
|
1219
1239
|
|
|
1220
1240
|
### source_utils (`source_utils.ipynb`)
|
|
@@ -1381,7 +1401,22 @@ from cjm_transcript_source_select.components.step_renderer import (
|
|
|
1381
1401
|
|
|
1382
1402
|
``` python
|
|
1383
1403
|
def _create_parent_keyboard_manager() -> ZoneManager: # Parent keyboard manager for hierarchy
|
|
1384
|
-
"
|
|
1404
|
+
"""
|
|
1405
|
+
Create the parent keyboard manager with two ghost zones for column switching.
|
|
1406
|
+
|
|
1407
|
+
Each ghost zone declares `activate_child_callback` so the library-baked
|
|
1408
|
+
Enter/Space dispatch (`ZoneManager.activate_keys` defaults to `("Enter", " ")`)
|
|
1409
|
+
invokes the consumer-defined JS function — `window.activateBrowserChild`
|
|
1410
|
+
and `window.activateQueueChild` are wired in `_generate_hierarchy_js`. The
|
|
1411
|
+
callback path is used (vs. the declarative `activate_child_id` path) because
|
|
1412
|
+
the browser ghost zone's active child depends on the currently-selected
|
|
1413
|
+
tab (`db` → source-browser child; `files` → file-browser child); this
|
|
1414
|
+
dynamic resolution can't be a static declarative mapping.
|
|
1415
|
+
|
|
1416
|
+
Each ghost zone's `label` surfaces as the section header for that zone's
|
|
1417
|
+
derived rows in the hints modal (e.g., the parent's "Source Browser" /
|
|
1418
|
+
"Selection Queue" — Activate row appears under those labels).
|
|
1419
|
+
"""
|
|
1385
1420
|
```
|
|
1386
1421
|
|
|
1387
1422
|
``` python
|
|
@@ -1428,15 +1463,6 @@ def _generate_hierarchy_js(
|
|
|
1428
1463
|
|
|
1429
1464
|
``` python
|
|
1430
1465
|
def render_selection_step(
|
|
1431
|
-
sources: List[Dict[str, Any]], # Available source plugins
|
|
1432
|
-
transcriptions: List[Dict[str, Any]], # Available transcription records
|
|
1433
|
-
selected_sources: List[Dict[str, str]], # Ordered selection
|
|
1434
|
-
grouping_mode: str, # Grouping mode: "media_path" or "batch_id"
|
|
1435
|
-
active_tab: str, # Active tab: "db" or "files"
|
|
1436
|
-
urls: SelectionUrls, # URL bundle for selection routes
|
|
1437
|
-
render_local_files_panel: Optional[Callable] = None, # Render fn for Files tab content
|
|
1438
|
-
sb_state: Any = None, # SourceBrowserRouterState for DB tab VC rendering
|
|
1439
|
-
) -> Any: # FastHTML component
|
|
1440
1466
|
"Render Phase 1: Source Selection & Ordering step with two-column layout."
|
|
1441
1467
|
```
|
|
1442
1468
|
|
|
@@ -60,57 +60,57 @@ graph LR
|
|
|
60
60
|
utils[utils<br/>utils]
|
|
61
61
|
|
|
62
62
|
components_helpers --> models
|
|
63
|
-
components_local_files --> components_helpers
|
|
64
63
|
components_local_files --> html_ids
|
|
64
|
+
components_local_files --> components_helpers
|
|
65
65
|
components_preview_panel --> html_ids
|
|
66
66
|
components_source_browser --> services_source_utils
|
|
67
67
|
components_source_browser --> html_ids
|
|
68
68
|
components_source_browser --> utils
|
|
69
|
-
components_step_renderer --> components_preview_panel
|
|
70
|
-
components_step_renderer --> utils
|
|
71
69
|
components_step_renderer --> components_selection_queue
|
|
72
|
-
components_step_renderer --> components_source_browser
|
|
73
|
-
components_step_renderer --> models
|
|
74
70
|
components_step_renderer --> html_ids
|
|
71
|
+
components_step_renderer --> models
|
|
75
72
|
components_step_renderer --> components_local_files
|
|
76
|
-
|
|
73
|
+
components_step_renderer --> components_source_browser
|
|
74
|
+
components_step_renderer --> components_preview_panel
|
|
75
|
+
components_step_renderer --> utils
|
|
77
76
|
routes_core --> models
|
|
78
|
-
routes_core --> components_selection_queue
|
|
79
|
-
routes_core --> services_source
|
|
80
77
|
routes_core --> html_ids
|
|
81
|
-
|
|
78
|
+
routes_core --> components_step_renderer
|
|
79
|
+
routes_core --> services_source
|
|
80
|
+
routes_core --> components_selection_queue
|
|
82
81
|
routes_filtering --> services_source_utils
|
|
83
82
|
routes_filtering --> services_source
|
|
83
|
+
routes_filtering --> routes_core
|
|
84
84
|
routes_filtering --> models
|
|
85
|
-
routes_init --> routes_local_files
|
|
86
|
-
routes_init --> routes_source_browser
|
|
87
85
|
routes_init --> routes_core
|
|
86
|
+
routes_init --> services_source
|
|
87
|
+
routes_init --> routes_local_files
|
|
88
88
|
routes_init --> routes_queue
|
|
89
89
|
routes_init --> routes_tabs
|
|
90
|
-
routes_init --> services_source
|
|
91
|
-
routes_init --> routes_filtering
|
|
92
90
|
routes_init --> models
|
|
93
|
-
|
|
91
|
+
routes_init --> routes_filtering
|
|
92
|
+
routes_init --> routes_source_browser
|
|
94
93
|
routes_local_files --> components_local_files
|
|
95
94
|
routes_local_files --> services_source
|
|
95
|
+
routes_local_files --> routes_core
|
|
96
96
|
routes_local_files --> models
|
|
97
|
-
routes_queue --> components_preview_panel
|
|
98
|
-
routes_queue --> routes_core
|
|
99
|
-
routes_queue --> services_source_utils
|
|
100
97
|
routes_queue --> services_source
|
|
98
|
+
routes_queue --> services_source_utils
|
|
99
|
+
routes_queue --> routes_core
|
|
101
100
|
routes_queue --> models
|
|
102
|
-
|
|
103
|
-
routes_source_browser --> components_source_browser
|
|
101
|
+
routes_queue --> components_preview_panel
|
|
104
102
|
routes_source_browser --> services_source
|
|
105
|
-
routes_source_browser -->
|
|
103
|
+
routes_source_browser --> components_source_browser
|
|
104
|
+
routes_source_browser --> html_ids
|
|
106
105
|
routes_source_browser --> models
|
|
107
106
|
routes_source_browser --> services_source_utils
|
|
108
|
-
routes_source_browser -->
|
|
109
|
-
|
|
107
|
+
routes_source_browser --> routes_core
|
|
108
|
+
routes_source_browser --> components_preview_panel
|
|
109
|
+
routes_tabs --> models
|
|
110
110
|
routes_tabs --> routes_core
|
|
111
|
-
routes_tabs -->
|
|
111
|
+
routes_tabs --> services_source
|
|
112
112
|
routes_tabs --> services_source_utils
|
|
113
|
-
routes_tabs -->
|
|
113
|
+
routes_tabs --> components_step_renderer
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
*52 cross-module dependencies detected*
|
|
@@ -489,12 +489,6 @@ def _handle_remove_external_source(
|
|
|
489
489
|
|
|
490
490
|
``` python
|
|
491
491
|
def init_local_files_router(
|
|
492
|
-
state_store: WorkflowStateStore, # The workflow state store
|
|
493
|
-
workflow_id: str, # The workflow identifier
|
|
494
|
-
source_service: SourceService, # The source service for external db ops
|
|
495
|
-
prefix: str, # Route prefix (e.g., "/workflow/selection/local_files")
|
|
496
|
-
urls: SelectionUrls, # URL bundle for rendering
|
|
497
|
-
) -> LocalFilesResult: # Router result with routers, routes, render, restore, and reset
|
|
498
492
|
"Initialize local files browser routes with new file browser API."
|
|
499
493
|
```
|
|
500
494
|
|
|
@@ -579,6 +573,7 @@ class LocalFilesResult:
|
|
|
579
573
|
render_panel: Callable # (error_message?, session_id?) -> rendered panel
|
|
580
574
|
restore_state: Callable = field(...) # (session_id) -> None, restore persisted state
|
|
581
575
|
reset_state: Callable = field(...) # () -> None, reset in-memory caches
|
|
576
|
+
kb_manager: Optional[ZoneManager] # ZoneManager backing the local file browser's keyboard system — hand to render_keyboard_hints_modal(..., child_managers=[...]) for hierarchical hint display
|
|
582
577
|
```
|
|
583
578
|
|
|
584
579
|
``` python
|
|
@@ -593,6 +588,7 @@ class SelectionResult:
|
|
|
593
588
|
sb_state: Any # SourceBrowserRouterState
|
|
594
589
|
restore_state: Callable = field(...) # (session_id) -> None, restore persisted state
|
|
595
590
|
reset_state: Callable = field(...) # () -> None, reset in-memory caches
|
|
591
|
+
fb_kb_manager: Optional[ZoneManager] # ZoneManager from the local file browser; convenience pointer mirroring LocalFilesResult.kb_manager so step renderers can read it directly from SelectionResult without descending into LocalFilesResult
|
|
596
592
|
```
|
|
597
593
|
|
|
598
594
|
### preview_panel (`preview_panel.ipynb`)
|
|
@@ -1031,7 +1027,8 @@ from cjm_transcript_source_select.components.source_browser import (
|
|
|
1031
1027
|
build_source_items,
|
|
1032
1028
|
is_source_item_skippable,
|
|
1033
1029
|
create_source_cell_renderer,
|
|
1034
|
-
render_source_empty
|
|
1030
|
+
render_source_empty,
|
|
1031
|
+
build_source_browser_keyboard_system
|
|
1035
1032
|
)
|
|
1036
1033
|
```
|
|
1037
1034
|
|
|
@@ -1092,19 +1089,45 @@ def render_source_empty() -> Any: # Empty state component
|
|
|
1092
1089
|
"Render empty state when no transcription sources are available."
|
|
1093
1090
|
```
|
|
1094
1091
|
|
|
1092
|
+
``` python
|
|
1093
|
+
def build_source_browser_keyboard_system(
|
|
1094
|
+
sb_state: Any, # SourceBrowserRouterState (carries vc_ids, vc_btn_ids, vc_urls)
|
|
1095
|
+
manager_label: Optional[str] = None, # Human-readable label for the ZoneManager (consumed by render_keyboard_hints_modal section header when this is a child_managers entry)
|
|
1096
|
+
) -> KeyboardSystem: # Complete rendered KeyboardSystem (carries .manager for hierarchical hints handoff)
|
|
1097
|
+
"""
|
|
1098
|
+
Build the source browser's KeyboardSystem standalone.
|
|
1099
|
+
|
|
1100
|
+
Lifts the manager construction out of `_render_source_browser_vc_content`
|
|
1101
|
+
so the underlying `ZoneManager` is accessible via `KeyboardSystem.manager`
|
|
1102
|
+
for hierarchical hints handoff. Consumers wiring this as a child of the
|
|
1103
|
+
parent selection-step manager pass the manager to
|
|
1104
|
+
`render_keyboard_hints_modal(..., child_managers=[...])` so the modal's
|
|
1105
|
+
section header reads as `manager_label` instead of the technical SB_SYSTEM_ID.
|
|
1106
|
+
"""
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1095
1109
|
``` python
|
|
1096
1110
|
def _render_source_browser_vc_content(
|
|
1097
|
-
sb_state: Any,
|
|
1111
|
+
sb_state: Any, # SourceBrowserRouterState from routes.source_browser
|
|
1112
|
+
keyboard_system: Optional[KeyboardSystem] = None, # Pre-built keyboard system (when None, builds internally via build_source_browser_keyboard_system)
|
|
1098
1113
|
) -> Any: # VC content wrapper (without search/grouping header)
|
|
1099
|
-
"
|
|
1114
|
+
"""
|
|
1115
|
+
Render the VC content portion of the source browser.
|
|
1116
|
+
|
|
1117
|
+
When `keyboard_system` is provided, the caller has built the system upstream
|
|
1118
|
+
(typically `init_source_browser_router`) and owns the underlying ZoneManager.
|
|
1119
|
+
When None, builds the system internally — preserves pre-C1 behavior for any
|
|
1120
|
+
caller that doesn't need the manager handoff.
|
|
1121
|
+
"""
|
|
1100
1122
|
```
|
|
1101
1123
|
|
|
1102
1124
|
``` python
|
|
1103
1125
|
def _render_source_browser_vc(
|
|
1104
|
-
sb_state: Any,
|
|
1105
|
-
filter_url: str = "",
|
|
1106
|
-
grouping_mode: str = "media_path",
|
|
1107
|
-
grouping_change_url: str = "",
|
|
1126
|
+
sb_state: Any, # SourceBrowserRouterState from routes.source_browser
|
|
1127
|
+
filter_url: str = "", # URL for filtering sources
|
|
1128
|
+
grouping_mode: str = "media_path", # Current grouping mode
|
|
1129
|
+
grouping_change_url: str = "", # URL for changing grouping mode
|
|
1130
|
+
keyboard_system: Optional[KeyboardSystem] = None, # Pre-built keyboard system, threaded through to VC content
|
|
1108
1131
|
) -> Any: # Source browser component with virtual collection
|
|
1109
1132
|
"Render the full source browser panel (header + VC content)."
|
|
1110
1133
|
```
|
|
@@ -1151,10 +1174,6 @@ from cjm_transcript_source_select.routes.source_browser import (
|
|
|
1151
1174
|
|
|
1152
1175
|
``` python
|
|
1153
1176
|
def init_source_browser_router(
|
|
1154
|
-
source_service: SourceService, # Source service for querying transcriptions
|
|
1155
|
-
urls: SelectionUrls, # URL bundle (toggle, select_all, filter, grouping_change)
|
|
1156
|
-
prefix: str = "/browser", # Route prefix for VC routes
|
|
1157
|
-
) -> SourceBrowserRouterState: # Router state with all VC objects and helpers
|
|
1158
1177
|
"Initialize the source browser virtual collection router."
|
|
1159
1178
|
```
|
|
1160
1179
|
|
|
@@ -1179,6 +1198,7 @@ class SourceBrowserRouterState:
|
|
|
1179
1198
|
get_visible_checkbox_oobs: Callable # () -> tuple of OOB elements
|
|
1180
1199
|
get_checkbox_oob_for: Callable # (record_id, provider_id) -> OOB element or None
|
|
1181
1200
|
get_vc_row_id_for: Callable # (record_id, provider_id) -> str or None
|
|
1201
|
+
kb_manager: Optional[ZoneManager] # ZoneManager backing the source browser's VC keyboard system — hand to render_keyboard_hints_modal(..., child_managers=[...]) for hierarchical hint display
|
|
1182
1202
|
```
|
|
1183
1203
|
|
|
1184
1204
|
### source_utils (`source_utils.ipynb`)
|
|
@@ -1345,7 +1365,22 @@ from cjm_transcript_source_select.components.step_renderer import (
|
|
|
1345
1365
|
|
|
1346
1366
|
``` python
|
|
1347
1367
|
def _create_parent_keyboard_manager() -> ZoneManager: # Parent keyboard manager for hierarchy
|
|
1348
|
-
"
|
|
1368
|
+
"""
|
|
1369
|
+
Create the parent keyboard manager with two ghost zones for column switching.
|
|
1370
|
+
|
|
1371
|
+
Each ghost zone declares `activate_child_callback` so the library-baked
|
|
1372
|
+
Enter/Space dispatch (`ZoneManager.activate_keys` defaults to `("Enter", " ")`)
|
|
1373
|
+
invokes the consumer-defined JS function — `window.activateBrowserChild`
|
|
1374
|
+
and `window.activateQueueChild` are wired in `_generate_hierarchy_js`. The
|
|
1375
|
+
callback path is used (vs. the declarative `activate_child_id` path) because
|
|
1376
|
+
the browser ghost zone's active child depends on the currently-selected
|
|
1377
|
+
tab (`db` → source-browser child; `files` → file-browser child); this
|
|
1378
|
+
dynamic resolution can't be a static declarative mapping.
|
|
1379
|
+
|
|
1380
|
+
Each ghost zone's `label` surfaces as the section header for that zone's
|
|
1381
|
+
derived rows in the hints modal (e.g., the parent's "Source Browser" /
|
|
1382
|
+
"Selection Queue" — Activate row appears under those labels).
|
|
1383
|
+
"""
|
|
1349
1384
|
```
|
|
1350
1385
|
|
|
1351
1386
|
``` python
|
|
@@ -1392,15 +1427,6 @@ def _generate_hierarchy_js(
|
|
|
1392
1427
|
|
|
1393
1428
|
``` python
|
|
1394
1429
|
def render_selection_step(
|
|
1395
|
-
sources: List[Dict[str, Any]], # Available source plugins
|
|
1396
|
-
transcriptions: List[Dict[str, Any]], # Available transcription records
|
|
1397
|
-
selected_sources: List[Dict[str, str]], # Ordered selection
|
|
1398
|
-
grouping_mode: str, # Grouping mode: "media_path" or "batch_id"
|
|
1399
|
-
active_tab: str, # Active tab: "db" or "files"
|
|
1400
|
-
urls: SelectionUrls, # URL bundle for selection routes
|
|
1401
|
-
render_local_files_panel: Optional[Callable] = None, # Render fn for Files tab content
|
|
1402
|
-
sb_state: Any = None, # SourceBrowserRouterState for DB tab VC rendering
|
|
1403
|
-
) -> Any: # FastHTML component
|
|
1404
1430
|
"Render Phase 1: Source Selection & Ordering step with two-column layout."
|
|
1405
1431
|
```
|
|
1406
1432
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.30"
|
|
@@ -45,6 +45,8 @@ d = { 'settings': { 'branch': 'main',
|
|
|
45
45
|
'cjm_transcript_source_select/components/source_browser.py'),
|
|
46
46
|
'cjm_transcript_source_select.components.source_browser._render_source_browser_vc_content': ( 'components/source_browser.html#_render_source_browser_vc_content',
|
|
47
47
|
'cjm_transcript_source_select/components/source_browser.py'),
|
|
48
|
+
'cjm_transcript_source_select.components.source_browser.build_source_browser_keyboard_system': ( 'components/source_browser.html#build_source_browser_keyboard_system',
|
|
49
|
+
'cjm_transcript_source_select/components/source_browser.py'),
|
|
48
50
|
'cjm_transcript_source_select.components.source_browser.build_source_items': ( 'components/source_browser.html#build_source_items',
|
|
49
51
|
'cjm_transcript_source_select/components/source_browser.py'),
|
|
50
52
|
'cjm_transcript_source_select.components.source_browser.create_source_cell_renderer': ( 'components/source_browser.html#create_source_cell_renderer',
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# %% auto #0
|
|
6
6
|
__all__ = ['SOURCE_BROWSER_COLUMNS', 'SB_SYSTEM_ID', 'SourceBrowserItem', 'build_source_items', 'is_source_item_skippable',
|
|
7
|
-
'create_source_cell_renderer', 'render_source_empty']
|
|
7
|
+
'create_source_cell_renderer', 'render_source_empty', 'build_source_browser_keyboard_system']
|
|
8
8
|
|
|
9
9
|
# %% ../../nbs/components/source_browser.ipynb #7b2e89c5
|
|
10
10
|
import json
|
|
@@ -53,7 +53,7 @@ from cjm_fasthtml_virtual_collection.js.scrollbar import generate_scrollbar_js
|
|
|
53
53
|
from cjm_fasthtml_virtual_collection.js.auto_fit import generate_auto_fit_js, auto_fit_callback_name
|
|
54
54
|
|
|
55
55
|
from cjm_fasthtml_keyboard_navigation.core.manager import ZoneManager
|
|
56
|
-
from cjm_fasthtml_keyboard_navigation.components.system import render_keyboard_system
|
|
56
|
+
from cjm_fasthtml_keyboard_navigation.components.system import render_keyboard_system, KeyboardSystem
|
|
57
57
|
|
|
58
58
|
from cjm_fasthtml_viewport_fit.models import ViewportFitConfig
|
|
59
59
|
from cjm_fasthtml_viewport_fit.components import render_viewport_fit_script
|
|
@@ -306,10 +306,52 @@ _SB_VC_WRAPPER_ID = "sb-vc-wrapper"
|
|
|
306
306
|
SB_SYSTEM_ID = "sb-collection"
|
|
307
307
|
|
|
308
308
|
|
|
309
|
+
def build_source_browser_keyboard_system(
|
|
310
|
+
sb_state: Any, # SourceBrowserRouterState (carries vc_ids, vc_btn_ids, vc_urls)
|
|
311
|
+
manager_label: Optional[str] = None, # Human-readable label for the ZoneManager (consumed by render_keyboard_hints_modal section header when this is a child_managers entry)
|
|
312
|
+
) -> KeyboardSystem: # Complete rendered KeyboardSystem (carries .manager for hierarchical hints handoff)
|
|
313
|
+
"""Build the source browser's KeyboardSystem standalone.
|
|
314
|
+
|
|
315
|
+
Lifts the manager construction out of `_render_source_browser_vc_content`
|
|
316
|
+
so the underlying `ZoneManager` is accessible via `KeyboardSystem.manager`
|
|
317
|
+
for hierarchical hints handoff. Consumers wiring this as a child of the
|
|
318
|
+
parent selection-step manager pass the manager to
|
|
319
|
+
`render_keyboard_hints_modal(..., child_managers=[...])` so the modal's
|
|
320
|
+
section header reads as `manager_label` instead of the technical SB_SYSTEM_ID.
|
|
321
|
+
"""
|
|
322
|
+
vc_ids = sb_state.ids
|
|
323
|
+
vc_btn_ids = sb_state.btn_ids
|
|
324
|
+
vc_urls = sb_state.urls
|
|
325
|
+
|
|
326
|
+
zone = create_collection_focus_zone(vc_ids)
|
|
327
|
+
nav_actions = create_collection_nav_actions(zone.id, vc_btn_ids)
|
|
328
|
+
manager = ZoneManager(
|
|
329
|
+
zones=(zone,),
|
|
330
|
+
actions=nav_actions,
|
|
331
|
+
system_id=SB_SYSTEM_ID,
|
|
332
|
+
label=manager_label,
|
|
333
|
+
)
|
|
334
|
+
url_map = build_collection_url_map(vc_btn_ids, vc_urls)
|
|
335
|
+
target_map = {btn_id: f"#{vc_ids.rows}" for btn_id in url_map}
|
|
336
|
+
swap_map = {btn_id: "none" for btn_id in url_map}
|
|
337
|
+
kb_system = render_keyboard_system(
|
|
338
|
+
manager, url_map=url_map, target_map=target_map, swap_map=swap_map,
|
|
339
|
+
)
|
|
340
|
+
apply_nav_sync(kb_system, vc_ids)
|
|
341
|
+
return kb_system
|
|
342
|
+
|
|
343
|
+
|
|
309
344
|
def _render_source_browser_vc_content(
|
|
310
|
-
sb_state: Any,
|
|
345
|
+
sb_state: Any, # SourceBrowserRouterState from routes.source_browser
|
|
346
|
+
keyboard_system: Optional[KeyboardSystem] = None, # Pre-built keyboard system (when None, builds internally via build_source_browser_keyboard_system)
|
|
311
347
|
) -> Any: # VC content wrapper (without search/grouping header)
|
|
312
|
-
"""Render the VC content portion of the source browser.
|
|
348
|
+
"""Render the VC content portion of the source browser.
|
|
349
|
+
|
|
350
|
+
When `keyboard_system` is provided, the caller has built the system upstream
|
|
351
|
+
(typically `init_source_browser_router`) and owns the underlying ZoneManager.
|
|
352
|
+
When None, builds the system internally — preserves pre-C1 behavior for any
|
|
353
|
+
caller that doesn't need the manager handoff.
|
|
354
|
+
"""
|
|
313
355
|
vc_ids = sb_state.ids
|
|
314
356
|
vc_btn_ids = sb_state.btn_ids
|
|
315
357
|
vc_config = sb_state.config
|
|
@@ -323,15 +365,8 @@ def _render_source_browser_vc_content(
|
|
|
323
365
|
render_empty=render_source_empty,
|
|
324
366
|
)
|
|
325
367
|
|
|
326
|
-
# Keyboard system
|
|
327
|
-
|
|
328
|
-
nav_actions = create_collection_nav_actions(zone.id, vc_btn_ids)
|
|
329
|
-
manager = ZoneManager(zones=(zone,), actions=nav_actions, system_id=SB_SYSTEM_ID)
|
|
330
|
-
url_map = build_collection_url_map(vc_btn_ids, vc_urls)
|
|
331
|
-
target_map = {btn_id: f"#{vc_ids.rows}" for btn_id in url_map}
|
|
332
|
-
swap_map = {btn_id: "none" for btn_id in url_map}
|
|
333
|
-
kb_system = render_keyboard_system(manager, url_map=url_map, target_map=target_map, swap_map=swap_map)
|
|
334
|
-
apply_nav_sync(kb_system, vc_ids)
|
|
368
|
+
# Keyboard system — use caller-supplied or build internally.
|
|
369
|
+
kb_system = keyboard_system if keyboard_system is not None else build_source_browser_keyboard_system(sb_state)
|
|
335
370
|
|
|
336
371
|
# Auto-fit JS
|
|
337
372
|
auto_fit_js = generate_auto_fit_js(
|
|
@@ -372,13 +407,14 @@ def _render_source_browser_vc_content(
|
|
|
372
407
|
|
|
373
408
|
|
|
374
409
|
def _render_source_browser_vc(
|
|
375
|
-
sb_state: Any,
|
|
376
|
-
filter_url: str = "",
|
|
377
|
-
grouping_mode: str = "media_path",
|
|
378
|
-
grouping_change_url: str = "",
|
|
410
|
+
sb_state: Any, # SourceBrowserRouterState from routes.source_browser
|
|
411
|
+
filter_url: str = "", # URL for filtering sources
|
|
412
|
+
grouping_mode: str = "media_path", # Current grouping mode
|
|
413
|
+
grouping_change_url: str = "", # URL for changing grouping mode
|
|
414
|
+
keyboard_system: Optional[KeyboardSystem] = None, # Pre-built keyboard system, threaded through to VC content
|
|
379
415
|
) -> Any: # Source browser component with virtual collection
|
|
380
416
|
"""Render the full source browser panel (header + VC content)."""
|
|
381
|
-
vc_content = _render_source_browser_vc_content(sb_state)
|
|
417
|
+
vc_content = _render_source_browser_vc_content(sb_state, keyboard_system=keyboard_system)
|
|
382
418
|
|
|
383
419
|
return Div(
|
|
384
420
|
# Header bar with search and grouping selector
|
|
@@ -87,43 +87,47 @@ _ZONE_FOCUS_CLASSES = (str(shadow.lg), str(shadow_dui.primary))
|
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
def _create_parent_keyboard_manager() -> ZoneManager: # Parent keyboard manager for hierarchy
|
|
90
|
-
"""Create the parent keyboard manager with two ghost zones for column switching.
|
|
90
|
+
"""Create the parent keyboard manager with two ghost zones for column switching.
|
|
91
|
+
|
|
92
|
+
Each ghost zone declares `activate_child_callback` so the library-baked
|
|
93
|
+
Enter/Space dispatch (`ZoneManager.activate_keys` defaults to `("Enter", " ")`)
|
|
94
|
+
invokes the consumer-defined JS function — `window.activateBrowserChild`
|
|
95
|
+
and `window.activateQueueChild` are wired in `_generate_hierarchy_js`. The
|
|
96
|
+
callback path is used (vs. the declarative `activate_child_id` path) because
|
|
97
|
+
the browser ghost zone's active child depends on the currently-selected
|
|
98
|
+
tab (`db` → source-browser child; `files` → file-browser child); this
|
|
99
|
+
dynamic resolution can't be a static declarative mapping.
|
|
100
|
+
|
|
101
|
+
Each ghost zone's `label` surfaces as the section header for that zone's
|
|
102
|
+
derived rows in the hints modal (e.g., the parent's "Source Browser" /
|
|
103
|
+
"Selection Queue" — Activate row appears under those labels).
|
|
104
|
+
"""
|
|
91
105
|
# Ghost zone for browser area (wraps source tabs — child systems live here)
|
|
92
106
|
ghost_browser_zone = FocusZone(
|
|
93
107
|
id=SelectionHtmlIds.GHOST_BROWSER,
|
|
108
|
+
label="Source Browser",
|
|
94
109
|
item_selector=None,
|
|
95
110
|
navigation=ScrollOnly(),
|
|
96
111
|
zone_focus_classes=_ZONE_FOCUS_CLASSES,
|
|
112
|
+
activate_child_callback="activateBrowserChild",
|
|
97
113
|
)
|
|
98
114
|
|
|
99
115
|
# Ghost zone for queue area (wraps queue panel — queue child system lives here)
|
|
100
116
|
ghost_queue_zone = FocusZone(
|
|
101
117
|
id=SelectionHtmlIds.GHOST_QUEUE,
|
|
118
|
+
label="Selection Queue",
|
|
102
119
|
item_selector=None,
|
|
103
120
|
navigation=ScrollOnly(),
|
|
104
121
|
zone_focus_classes=_ZONE_FOCUS_CLASSES,
|
|
122
|
+
activate_child_callback="activateQueueChild",
|
|
105
123
|
)
|
|
106
124
|
|
|
107
|
-
# Parent-level actions (no queue-scoped actions — those live in queue child system)
|
|
125
|
+
# Parent-level actions (no queue-scoped actions — those live in queue child system).
|
|
126
|
+
# The previous per-zone Enter KeyActions for child activation have been removed —
|
|
127
|
+
# library-baked Enter/Space dispatch via FocusZone.activate_child_callback handles
|
|
128
|
+
# activation. Tab switching stays here because Ctrl+Shift+{/} doesn't fit the
|
|
129
|
+
# activation seam (different keys, different dispatch path).
|
|
108
130
|
actions = (
|
|
109
|
-
# Enter activates the browser child when ghost-browser zone is active
|
|
110
|
-
KeyAction(
|
|
111
|
-
key="Enter",
|
|
112
|
-
js_callback="activateBrowserChild",
|
|
113
|
-
zone_ids=(SelectionHtmlIds.GHOST_BROWSER,),
|
|
114
|
-
description="Activate browser",
|
|
115
|
-
hint_group="Navigation",
|
|
116
|
-
),
|
|
117
|
-
|
|
118
|
-
# Enter activates the queue child when ghost-queue zone is active
|
|
119
|
-
KeyAction(
|
|
120
|
-
key="Enter",
|
|
121
|
-
js_callback="activateQueueChild",
|
|
122
|
-
zone_ids=(SelectionHtmlIds.GHOST_QUEUE,),
|
|
123
|
-
description="Activate queue",
|
|
124
|
-
hint_group="Navigation",
|
|
125
|
-
),
|
|
126
|
-
|
|
127
131
|
# Tab switching — no zone_ids so it fires from any parent zone
|
|
128
132
|
# and bubbles up from children (children don't handle Ctrl+Shift+{/})
|
|
129
133
|
KeyAction(
|
|
@@ -146,6 +150,7 @@ def _create_parent_keyboard_manager() -> ZoneManager: # Parent keyboard manager
|
|
|
146
150
|
zones=(ghost_browser_zone, ghost_queue_zone),
|
|
147
151
|
actions=actions,
|
|
148
152
|
system_id=SelectionHtmlIds.PARENT_SYSTEM_ID,
|
|
153
|
+
label="Source Selection",
|
|
149
154
|
prev_zone_key="ArrowLeft",
|
|
150
155
|
next_zone_key="ArrowRight",
|
|
151
156
|
state_hidden_inputs=True,
|
|
@@ -377,6 +382,7 @@ def render_selection_step(
|
|
|
377
382
|
urls: SelectionUrls, # URL bundle for selection routes
|
|
378
383
|
render_local_files_panel: Optional[Callable] = None, # Render fn for Files tab content
|
|
379
384
|
sb_state: Any = None, # SourceBrowserRouterState for DB tab VC rendering
|
|
385
|
+
fb_kb_manager: Optional[Any] = None, # File browser ZoneManager (from LocalFilesResult.kb_manager / SelectionResult.fb_kb_manager) for the hints modal's hierarchical child_managers
|
|
380
386
|
) -> Any: # FastHTML component
|
|
381
387
|
"""Render Phase 1: Source Selection & Ordering step with two-column layout."""
|
|
382
388
|
# Create parent keyboard manager (two ghost zones for column switching)
|
|
@@ -407,6 +413,8 @@ def render_selection_step(
|
|
|
407
413
|
|
|
408
414
|
# Build queue keyboard system (self-contained child system from sortable-queue library)
|
|
409
415
|
# data_attributes includes "key" for remove route + "record-id"/"provider-id" for preview
|
|
416
|
+
# manager_label surfaces as the section header for the queue's kb_manager in
|
|
417
|
+
# the hints modal's hierarchical child_managers section.
|
|
410
418
|
queue_kb = create_queue_keyboard_system(
|
|
411
419
|
config=SD_QUEUE_CONFIG,
|
|
412
420
|
ids=SD_QUEUE_IDS,
|
|
@@ -414,6 +422,7 @@ def render_selection_step(
|
|
|
414
422
|
data_attributes=("key", "record-id", "provider-id"),
|
|
415
423
|
on_focus_change="triggerPreviewUpdate",
|
|
416
424
|
hidden_input_prefix="sd-focused",
|
|
425
|
+
manager_label="Selection Queue",
|
|
417
426
|
)
|
|
418
427
|
|
|
419
428
|
# Build include selector for preview hidden inputs
|
|
@@ -472,8 +481,23 @@ def render_selection_step(
|
|
|
472
481
|
# Hierarchy wiring script (must come after all keyboard systems are rendered)
|
|
473
482
|
hierarchy_js = _generate_hierarchy_js(active_tab)
|
|
474
483
|
|
|
475
|
-
# Keyboard hints modal
|
|
476
|
-
|
|
484
|
+
# Keyboard hints modal — hierarchical mode with all three children.
|
|
485
|
+
# Pass all three regardless of active_tab (modal is a reference, not a
|
|
486
|
+
# "what's active right now" indicator — user benefits from seeing all
|
|
487
|
+
# available actions across DB / Files / Queue). None entries are filtered
|
|
488
|
+
# so the modal handles missing managers gracefully (e.g. before host
|
|
489
|
+
# wiring is complete during tests).
|
|
490
|
+
child_managers = tuple(
|
|
491
|
+
m for m in (
|
|
492
|
+
sb_state.kb_manager if sb_state is not None else None,
|
|
493
|
+
fb_kb_manager,
|
|
494
|
+
queue_kb.manager,
|
|
495
|
+
) if m is not None
|
|
496
|
+
)
|
|
497
|
+
hints_modal, hints_trigger, hints_script = render_keyboard_hints_modal(
|
|
498
|
+
kb_manager,
|
|
499
|
+
child_managers=child_managers if child_managers else None,
|
|
500
|
+
)
|
|
477
501
|
|
|
478
502
|
return Div(
|
|
479
503
|
# Header with keyboard hints trigger
|
|
@@ -12,6 +12,8 @@ from dataclasses import dataclass, field
|
|
|
12
12
|
|
|
13
13
|
from fasthtml.common import APIRouter
|
|
14
14
|
|
|
15
|
+
from cjm_fasthtml_keyboard_navigation.core.manager import ZoneManager
|
|
16
|
+
|
|
15
17
|
from cjm_source_provider.models import SelectedSource
|
|
16
18
|
|
|
17
19
|
# %% ../nbs/models.ipynb #selection-step-state
|
|
@@ -71,6 +73,7 @@ class LocalFilesResult:
|
|
|
71
73
|
render_panel: Callable # (error_message?, session_id?) -> rendered panel
|
|
72
74
|
restore_state: Callable = field(default=_no_op_restore) # (session_id) -> None, restore persisted state
|
|
73
75
|
reset_state: Callable = field(default=_no_op_reset) # () -> None, reset in-memory caches
|
|
76
|
+
kb_manager: Optional[ZoneManager] = None # ZoneManager backing the local file browser's keyboard system — hand to render_keyboard_hints_modal(..., child_managers=[...]) for hierarchical hint display
|
|
74
77
|
|
|
75
78
|
@dataclass
|
|
76
79
|
class SelectionResult:
|
|
@@ -82,3 +85,4 @@ class SelectionResult:
|
|
|
82
85
|
sb_state: Any = None # SourceBrowserRouterState
|
|
83
86
|
restore_state: Callable = field(default=_no_op_restore) # (session_id) -> None, restore persisted state
|
|
84
87
|
reset_state: Callable = field(default=_no_op_reset) # () -> None, reset in-memory caches
|
|
88
|
+
fb_kb_manager: Optional[ZoneManager] = None # ZoneManager from the local file browser; convenience pointer mirroring LocalFilesResult.kb_manager so step renderers can read it directly from SelectionResult without descending into LocalFilesResult
|
|
@@ -42,13 +42,20 @@ def init_selection_routers(
|
|
|
42
42
|
filtering_router, filtering_routes = init_filtering_router(
|
|
43
43
|
state_store, workflow_id, source_service, f"{prefix}/filtering", urls
|
|
44
44
|
)
|
|
45
|
+
# manager_label="Local Files" surfaces as the section header for the file
|
|
46
|
+
# browser's kb_manager in the Phase 1 hints modal (rendered via
|
|
47
|
+
# render_keyboard_hints_modal in step_renderer).
|
|
45
48
|
local_files = init_local_files_router(
|
|
46
|
-
state_store, workflow_id, source_service, f"{prefix}/local_files", urls
|
|
49
|
+
state_store, workflow_id, source_service, f"{prefix}/local_files", urls,
|
|
50
|
+
manager_label="Local Files",
|
|
47
51
|
)
|
|
52
|
+
# manager_label="Source Browser" surfaces as the section header for the
|
|
53
|
+
# source browser's VC kb_manager in the Phase 1 hints modal.
|
|
48
54
|
sb_state = init_source_browser_router(
|
|
49
55
|
source_service=source_service,
|
|
50
56
|
urls=urls,
|
|
51
57
|
prefix=f"{prefix}/browser",
|
|
58
|
+
manager_label="Source Browser",
|
|
52
59
|
)
|
|
53
60
|
|
|
54
61
|
# Wire the module-level refs
|
|
@@ -128,4 +135,5 @@ def init_selection_routers(
|
|
|
128
135
|
sb_state=sb_state,
|
|
129
136
|
restore_state=local_files.restore_state,
|
|
130
137
|
reset_state=local_files.reset_state,
|
|
138
|
+
fb_kb_manager=local_files.kb_manager,
|
|
131
139
|
)
|
|
@@ -103,6 +103,7 @@ def init_local_files_router(
|
|
|
103
103
|
source_service: SourceService, # The source service for external db ops
|
|
104
104
|
prefix: str, # Route prefix (e.g., "/workflow/selection/local_files")
|
|
105
105
|
urls: SelectionUrls, # URL bundle for rendering
|
|
106
|
+
manager_label: Optional[str] = None, # Human-readable label for the local file browser's ZoneManager — surfaces in the hints modal section header when LocalFilesResult.kb_manager is passed as a child_managers entry
|
|
106
107
|
) -> LocalFilesResult: # Router result with routers, routes, render, restore, and reset
|
|
107
108
|
"""Initialize local files browser routes with new file browser API."""
|
|
108
109
|
provider = _get_local_files_provider()
|
|
@@ -253,6 +254,7 @@ def init_local_files_router(
|
|
|
253
254
|
route_prefix=f"{prefix}/browser",
|
|
254
255
|
callbacks=fb_callbacks,
|
|
255
256
|
home_path=home_path,
|
|
257
|
+
manager_label=manager_label,
|
|
256
258
|
)
|
|
257
259
|
|
|
258
260
|
# --- Render function for the full local files panel ---
|
|
@@ -314,4 +316,5 @@ def init_local_files_router(
|
|
|
314
316
|
render_panel=_render_panel,
|
|
315
317
|
restore_state=_restore_state,
|
|
316
318
|
reset_state=_reset_state,
|
|
319
|
+
kb_manager=fb_routers.kb_manager,
|
|
317
320
|
)
|
|
@@ -20,6 +20,8 @@ from cjm_fasthtml_virtual_collection.core.windowing import find_nearest_focusabl
|
|
|
20
20
|
from cjm_fasthtml_virtual_collection.routes.router import init_virtual_collection_router
|
|
21
21
|
from cjm_fasthtml_virtual_collection.components.table import render_cell_oob
|
|
22
22
|
|
|
23
|
+
from cjm_fasthtml_keyboard_navigation.core.manager import ZoneManager
|
|
24
|
+
|
|
23
25
|
from ..html_ids import SelectionHtmlIds
|
|
24
26
|
from ..models import SelectionUrls
|
|
25
27
|
from ..services.source import SourceService
|
|
@@ -29,6 +31,7 @@ from cjm_transcript_source_select.components.source_browser import (
|
|
|
29
31
|
SourceBrowserItem, SOURCE_BROWSER_COLUMNS,
|
|
30
32
|
build_source_items, is_source_item_skippable,
|
|
31
33
|
render_source_empty, _render_header_cell, _render_record_cell,
|
|
34
|
+
build_source_browser_keyboard_system,
|
|
32
35
|
)
|
|
33
36
|
from ..components.preview_panel import _render_preview_panel
|
|
34
37
|
|
|
@@ -50,12 +53,14 @@ class SourceBrowserRouterState:
|
|
|
50
53
|
get_visible_checkbox_oobs: Callable # () -> tuple of OOB elements
|
|
51
54
|
get_checkbox_oob_for: Callable # (record_id, provider_id) -> OOB element or None
|
|
52
55
|
get_vc_row_id_for: Callable # (record_id, provider_id) -> str or None
|
|
56
|
+
kb_manager: Optional[ZoneManager] = None # ZoneManager backing the source browser's VC keyboard system — hand to render_keyboard_hints_modal(..., child_managers=[...]) for hierarchical hint display
|
|
53
57
|
|
|
54
58
|
# %% ../../nbs/routes/source_browser.ipynb #sb-init-router
|
|
55
59
|
def init_source_browser_router(
|
|
56
60
|
source_service: SourceService, # Source service for querying transcriptions
|
|
57
61
|
urls: SelectionUrls, # URL bundle (toggle, select_all, filter, grouping_change)
|
|
58
62
|
prefix: str = "/browser", # Route prefix for VC routes
|
|
63
|
+
manager_label: Optional[str] = None, # Human-readable label for the source browser's ZoneManager (used as the modal section header when wired into a hierarchical hints display via SourceBrowserRouterState.kb_manager)
|
|
59
64
|
) -> SourceBrowserRouterState: # Router state with all VC objects and helpers
|
|
60
65
|
"""Initialize the source browser virtual collection router."""
|
|
61
66
|
# --- VC config and state (closure) ---
|
|
@@ -157,6 +162,12 @@ def init_source_browser_router(
|
|
|
157
162
|
return None
|
|
158
163
|
|
|
159
164
|
# --- Rebuild and render ---
|
|
165
|
+
# `_kb_system_ref` carries the kb_system after it's built at the bottom of
|
|
166
|
+
# this function (built once, reused across renders). rebuild_and_render
|
|
167
|
+
# threads it down to source_browser components so the same kb_system —
|
|
168
|
+
# and thus the same exposed kb_manager — is reused on every render.
|
|
169
|
+
_kb_system_ref: list = [None]
|
|
170
|
+
|
|
160
171
|
def rebuild_and_render(
|
|
161
172
|
transcriptions: List[Dict[str, Any]], # Transcription records
|
|
162
173
|
selected_sources: List[Dict[str, str]], # Current selections
|
|
@@ -167,13 +178,17 @@ def init_source_browser_router(
|
|
|
167
178
|
rebuild_items(transcriptions, selected_sources, grouping_mode)
|
|
168
179
|
if content_only:
|
|
169
180
|
from cjm_transcript_source_select.components.source_browser import _render_source_browser_vc_content
|
|
170
|
-
return _render_source_browser_vc_content(
|
|
181
|
+
return _render_source_browser_vc_content(
|
|
182
|
+
sb_state=_sb_state_ref[0],
|
|
183
|
+
keyboard_system=_kb_system_ref[0],
|
|
184
|
+
)
|
|
171
185
|
from cjm_transcript_source_select.components.source_browser import _render_source_browser_vc
|
|
172
186
|
return _render_source_browser_vc(
|
|
173
187
|
sb_state=_sb_state_ref[0],
|
|
174
188
|
filter_url=urls.filter,
|
|
175
189
|
grouping_mode=grouping_mode,
|
|
176
190
|
grouping_change_url=urls.grouping_change,
|
|
191
|
+
keyboard_system=_kb_system_ref[0],
|
|
177
192
|
)
|
|
178
193
|
|
|
179
194
|
# --- VC callbacks ---
|
|
@@ -228,4 +243,16 @@ def init_source_browser_router(
|
|
|
228
243
|
get_vc_row_id_for=get_vc_row_id_for,
|
|
229
244
|
)
|
|
230
245
|
_sb_state_ref[0] = result
|
|
246
|
+
|
|
247
|
+
# --- Build the kb_system once now that sb_state is wired up ---
|
|
248
|
+
# The kb_system's DOM elements (script, hidden_inputs, action_buttons) are
|
|
249
|
+
# static across renders; only the item list rebuilds. Building once at init
|
|
250
|
+
# ensures kb_manager is identity-stable across renders and gives single
|
|
251
|
+
# source of truth for the manager exposed on SourceBrowserRouterState.
|
|
252
|
+
kb_system = build_source_browser_keyboard_system(
|
|
253
|
+
sb_state=result,
|
|
254
|
+
manager_label=manager_label,
|
|
255
|
+
)
|
|
256
|
+
_kb_system_ref[0] = kb_system
|
|
257
|
+
result.kb_manager = kb_system.manager
|
|
231
258
|
return result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cjm-transcript-source-select
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.30
|
|
4
4
|
Summary: FastHTML source selection component for transcript decomposition workflows, with federated database browsing, drag-drop ordering, and keyboard navigation.
|
|
5
5
|
Author-email: "Christian J. Mills" <9126128+cj-mills@users.noreply.github.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -21,8 +21,8 @@ Requires-Dist: cjm-transcription-plugin-system
|
|
|
21
21
|
Requires-Dist: cjm-fasthtml-app-core
|
|
22
22
|
Requires-Dist: cjm-fasthtml-daisyui
|
|
23
23
|
Requires-Dist: cjm_fasthtml_lucide_icons
|
|
24
|
-
Requires-Dist: cjm_fasthtml_file_browser>=0.0.
|
|
25
|
-
Requires-Dist: cjm_fasthtml_keyboard_navigation>=0.0.
|
|
24
|
+
Requires-Dist: cjm_fasthtml_file_browser>=0.0.19
|
|
25
|
+
Requires-Dist: cjm_fasthtml_keyboard_navigation>=0.0.23
|
|
26
26
|
Requires-Dist: duckdb
|
|
27
27
|
Requires-Dist: pandas
|
|
28
28
|
Requires-Dist: cjm_workflow_state
|
|
@@ -30,7 +30,7 @@ Requires-Dist: cjm_source_provider
|
|
|
30
30
|
Requires-Dist: cjm_fasthtml_interactions
|
|
31
31
|
Requires-Dist: cjm_fasthtml_viewport_fit
|
|
32
32
|
Requires-Dist: cjm_fasthtml_virtual_collection
|
|
33
|
-
Requires-Dist: cjm_fasthtml_sortable_queue>=0.0.
|
|
33
|
+
Requires-Dist: cjm_fasthtml_sortable_queue>=0.0.16
|
|
34
34
|
Requires-Dist: cjm_fasthtml_design_system>=0.0.9
|
|
35
35
|
Dynamic: license-file
|
|
36
36
|
|
|
@@ -96,57 +96,57 @@ graph LR
|
|
|
96
96
|
utils[utils<br/>utils]
|
|
97
97
|
|
|
98
98
|
components_helpers --> models
|
|
99
|
-
components_local_files --> components_helpers
|
|
100
99
|
components_local_files --> html_ids
|
|
100
|
+
components_local_files --> components_helpers
|
|
101
101
|
components_preview_panel --> html_ids
|
|
102
102
|
components_source_browser --> services_source_utils
|
|
103
103
|
components_source_browser --> html_ids
|
|
104
104
|
components_source_browser --> utils
|
|
105
|
-
components_step_renderer --> components_preview_panel
|
|
106
|
-
components_step_renderer --> utils
|
|
107
105
|
components_step_renderer --> components_selection_queue
|
|
108
|
-
components_step_renderer --> components_source_browser
|
|
109
|
-
components_step_renderer --> models
|
|
110
106
|
components_step_renderer --> html_ids
|
|
107
|
+
components_step_renderer --> models
|
|
111
108
|
components_step_renderer --> components_local_files
|
|
112
|
-
|
|
109
|
+
components_step_renderer --> components_source_browser
|
|
110
|
+
components_step_renderer --> components_preview_panel
|
|
111
|
+
components_step_renderer --> utils
|
|
113
112
|
routes_core --> models
|
|
114
|
-
routes_core --> components_selection_queue
|
|
115
|
-
routes_core --> services_source
|
|
116
113
|
routes_core --> html_ids
|
|
117
|
-
|
|
114
|
+
routes_core --> components_step_renderer
|
|
115
|
+
routes_core --> services_source
|
|
116
|
+
routes_core --> components_selection_queue
|
|
118
117
|
routes_filtering --> services_source_utils
|
|
119
118
|
routes_filtering --> services_source
|
|
119
|
+
routes_filtering --> routes_core
|
|
120
120
|
routes_filtering --> models
|
|
121
|
-
routes_init --> routes_local_files
|
|
122
|
-
routes_init --> routes_source_browser
|
|
123
121
|
routes_init --> routes_core
|
|
122
|
+
routes_init --> services_source
|
|
123
|
+
routes_init --> routes_local_files
|
|
124
124
|
routes_init --> routes_queue
|
|
125
125
|
routes_init --> routes_tabs
|
|
126
|
-
routes_init --> services_source
|
|
127
|
-
routes_init --> routes_filtering
|
|
128
126
|
routes_init --> models
|
|
129
|
-
|
|
127
|
+
routes_init --> routes_filtering
|
|
128
|
+
routes_init --> routes_source_browser
|
|
130
129
|
routes_local_files --> components_local_files
|
|
131
130
|
routes_local_files --> services_source
|
|
131
|
+
routes_local_files --> routes_core
|
|
132
132
|
routes_local_files --> models
|
|
133
|
-
routes_queue --> components_preview_panel
|
|
134
|
-
routes_queue --> routes_core
|
|
135
|
-
routes_queue --> services_source_utils
|
|
136
133
|
routes_queue --> services_source
|
|
134
|
+
routes_queue --> services_source_utils
|
|
135
|
+
routes_queue --> routes_core
|
|
137
136
|
routes_queue --> models
|
|
138
|
-
|
|
139
|
-
routes_source_browser --> components_source_browser
|
|
137
|
+
routes_queue --> components_preview_panel
|
|
140
138
|
routes_source_browser --> services_source
|
|
141
|
-
routes_source_browser -->
|
|
139
|
+
routes_source_browser --> components_source_browser
|
|
140
|
+
routes_source_browser --> html_ids
|
|
142
141
|
routes_source_browser --> models
|
|
143
142
|
routes_source_browser --> services_source_utils
|
|
144
|
-
routes_source_browser -->
|
|
145
|
-
|
|
143
|
+
routes_source_browser --> routes_core
|
|
144
|
+
routes_source_browser --> components_preview_panel
|
|
145
|
+
routes_tabs --> models
|
|
146
146
|
routes_tabs --> routes_core
|
|
147
|
-
routes_tabs -->
|
|
147
|
+
routes_tabs --> services_source
|
|
148
148
|
routes_tabs --> services_source_utils
|
|
149
|
-
routes_tabs -->
|
|
149
|
+
routes_tabs --> components_step_renderer
|
|
150
150
|
```
|
|
151
151
|
|
|
152
152
|
*52 cross-module dependencies detected*
|
|
@@ -525,12 +525,6 @@ def _handle_remove_external_source(
|
|
|
525
525
|
|
|
526
526
|
``` python
|
|
527
527
|
def init_local_files_router(
|
|
528
|
-
state_store: WorkflowStateStore, # The workflow state store
|
|
529
|
-
workflow_id: str, # The workflow identifier
|
|
530
|
-
source_service: SourceService, # The source service for external db ops
|
|
531
|
-
prefix: str, # Route prefix (e.g., "/workflow/selection/local_files")
|
|
532
|
-
urls: SelectionUrls, # URL bundle for rendering
|
|
533
|
-
) -> LocalFilesResult: # Router result with routers, routes, render, restore, and reset
|
|
534
528
|
"Initialize local files browser routes with new file browser API."
|
|
535
529
|
```
|
|
536
530
|
|
|
@@ -615,6 +609,7 @@ class LocalFilesResult:
|
|
|
615
609
|
render_panel: Callable # (error_message?, session_id?) -> rendered panel
|
|
616
610
|
restore_state: Callable = field(...) # (session_id) -> None, restore persisted state
|
|
617
611
|
reset_state: Callable = field(...) # () -> None, reset in-memory caches
|
|
612
|
+
kb_manager: Optional[ZoneManager] # ZoneManager backing the local file browser's keyboard system — hand to render_keyboard_hints_modal(..., child_managers=[...]) for hierarchical hint display
|
|
618
613
|
```
|
|
619
614
|
|
|
620
615
|
``` python
|
|
@@ -629,6 +624,7 @@ class SelectionResult:
|
|
|
629
624
|
sb_state: Any # SourceBrowserRouterState
|
|
630
625
|
restore_state: Callable = field(...) # (session_id) -> None, restore persisted state
|
|
631
626
|
reset_state: Callable = field(...) # () -> None, reset in-memory caches
|
|
627
|
+
fb_kb_manager: Optional[ZoneManager] # ZoneManager from the local file browser; convenience pointer mirroring LocalFilesResult.kb_manager so step renderers can read it directly from SelectionResult without descending into LocalFilesResult
|
|
632
628
|
```
|
|
633
629
|
|
|
634
630
|
### preview_panel (`preview_panel.ipynb`)
|
|
@@ -1067,7 +1063,8 @@ from cjm_transcript_source_select.components.source_browser import (
|
|
|
1067
1063
|
build_source_items,
|
|
1068
1064
|
is_source_item_skippable,
|
|
1069
1065
|
create_source_cell_renderer,
|
|
1070
|
-
render_source_empty
|
|
1066
|
+
render_source_empty,
|
|
1067
|
+
build_source_browser_keyboard_system
|
|
1071
1068
|
)
|
|
1072
1069
|
```
|
|
1073
1070
|
|
|
@@ -1128,19 +1125,45 @@ def render_source_empty() -> Any: # Empty state component
|
|
|
1128
1125
|
"Render empty state when no transcription sources are available."
|
|
1129
1126
|
```
|
|
1130
1127
|
|
|
1128
|
+
``` python
|
|
1129
|
+
def build_source_browser_keyboard_system(
|
|
1130
|
+
sb_state: Any, # SourceBrowserRouterState (carries vc_ids, vc_btn_ids, vc_urls)
|
|
1131
|
+
manager_label: Optional[str] = None, # Human-readable label for the ZoneManager (consumed by render_keyboard_hints_modal section header when this is a child_managers entry)
|
|
1132
|
+
) -> KeyboardSystem: # Complete rendered KeyboardSystem (carries .manager for hierarchical hints handoff)
|
|
1133
|
+
"""
|
|
1134
|
+
Build the source browser's KeyboardSystem standalone.
|
|
1135
|
+
|
|
1136
|
+
Lifts the manager construction out of `_render_source_browser_vc_content`
|
|
1137
|
+
so the underlying `ZoneManager` is accessible via `KeyboardSystem.manager`
|
|
1138
|
+
for hierarchical hints handoff. Consumers wiring this as a child of the
|
|
1139
|
+
parent selection-step manager pass the manager to
|
|
1140
|
+
`render_keyboard_hints_modal(..., child_managers=[...])` so the modal's
|
|
1141
|
+
section header reads as `manager_label` instead of the technical SB_SYSTEM_ID.
|
|
1142
|
+
"""
|
|
1143
|
+
```
|
|
1144
|
+
|
|
1131
1145
|
``` python
|
|
1132
1146
|
def _render_source_browser_vc_content(
|
|
1133
|
-
sb_state: Any,
|
|
1147
|
+
sb_state: Any, # SourceBrowserRouterState from routes.source_browser
|
|
1148
|
+
keyboard_system: Optional[KeyboardSystem] = None, # Pre-built keyboard system (when None, builds internally via build_source_browser_keyboard_system)
|
|
1134
1149
|
) -> Any: # VC content wrapper (without search/grouping header)
|
|
1135
|
-
"
|
|
1150
|
+
"""
|
|
1151
|
+
Render the VC content portion of the source browser.
|
|
1152
|
+
|
|
1153
|
+
When `keyboard_system` is provided, the caller has built the system upstream
|
|
1154
|
+
(typically `init_source_browser_router`) and owns the underlying ZoneManager.
|
|
1155
|
+
When None, builds the system internally — preserves pre-C1 behavior for any
|
|
1156
|
+
caller that doesn't need the manager handoff.
|
|
1157
|
+
"""
|
|
1136
1158
|
```
|
|
1137
1159
|
|
|
1138
1160
|
``` python
|
|
1139
1161
|
def _render_source_browser_vc(
|
|
1140
|
-
sb_state: Any,
|
|
1141
|
-
filter_url: str = "",
|
|
1142
|
-
grouping_mode: str = "media_path",
|
|
1143
|
-
grouping_change_url: str = "",
|
|
1162
|
+
sb_state: Any, # SourceBrowserRouterState from routes.source_browser
|
|
1163
|
+
filter_url: str = "", # URL for filtering sources
|
|
1164
|
+
grouping_mode: str = "media_path", # Current grouping mode
|
|
1165
|
+
grouping_change_url: str = "", # URL for changing grouping mode
|
|
1166
|
+
keyboard_system: Optional[KeyboardSystem] = None, # Pre-built keyboard system, threaded through to VC content
|
|
1144
1167
|
) -> Any: # Source browser component with virtual collection
|
|
1145
1168
|
"Render the full source browser panel (header + VC content)."
|
|
1146
1169
|
```
|
|
@@ -1187,10 +1210,6 @@ from cjm_transcript_source_select.routes.source_browser import (
|
|
|
1187
1210
|
|
|
1188
1211
|
``` python
|
|
1189
1212
|
def init_source_browser_router(
|
|
1190
|
-
source_service: SourceService, # Source service for querying transcriptions
|
|
1191
|
-
urls: SelectionUrls, # URL bundle (toggle, select_all, filter, grouping_change)
|
|
1192
|
-
prefix: str = "/browser", # Route prefix for VC routes
|
|
1193
|
-
) -> SourceBrowserRouterState: # Router state with all VC objects and helpers
|
|
1194
1213
|
"Initialize the source browser virtual collection router."
|
|
1195
1214
|
```
|
|
1196
1215
|
|
|
@@ -1215,6 +1234,7 @@ class SourceBrowserRouterState:
|
|
|
1215
1234
|
get_visible_checkbox_oobs: Callable # () -> tuple of OOB elements
|
|
1216
1235
|
get_checkbox_oob_for: Callable # (record_id, provider_id) -> OOB element or None
|
|
1217
1236
|
get_vc_row_id_for: Callable # (record_id, provider_id) -> str or None
|
|
1237
|
+
kb_manager: Optional[ZoneManager] # ZoneManager backing the source browser's VC keyboard system — hand to render_keyboard_hints_modal(..., child_managers=[...]) for hierarchical hint display
|
|
1218
1238
|
```
|
|
1219
1239
|
|
|
1220
1240
|
### source_utils (`source_utils.ipynb`)
|
|
@@ -1381,7 +1401,22 @@ from cjm_transcript_source_select.components.step_renderer import (
|
|
|
1381
1401
|
|
|
1382
1402
|
``` python
|
|
1383
1403
|
def _create_parent_keyboard_manager() -> ZoneManager: # Parent keyboard manager for hierarchy
|
|
1384
|
-
"
|
|
1404
|
+
"""
|
|
1405
|
+
Create the parent keyboard manager with two ghost zones for column switching.
|
|
1406
|
+
|
|
1407
|
+
Each ghost zone declares `activate_child_callback` so the library-baked
|
|
1408
|
+
Enter/Space dispatch (`ZoneManager.activate_keys` defaults to `("Enter", " ")`)
|
|
1409
|
+
invokes the consumer-defined JS function — `window.activateBrowserChild`
|
|
1410
|
+
and `window.activateQueueChild` are wired in `_generate_hierarchy_js`. The
|
|
1411
|
+
callback path is used (vs. the declarative `activate_child_id` path) because
|
|
1412
|
+
the browser ghost zone's active child depends on the currently-selected
|
|
1413
|
+
tab (`db` → source-browser child; `files` → file-browser child); this
|
|
1414
|
+
dynamic resolution can't be a static declarative mapping.
|
|
1415
|
+
|
|
1416
|
+
Each ghost zone's `label` surfaces as the section header for that zone's
|
|
1417
|
+
derived rows in the hints modal (e.g., the parent's "Source Browser" /
|
|
1418
|
+
"Selection Queue" — Activate row appears under those labels).
|
|
1419
|
+
"""
|
|
1385
1420
|
```
|
|
1386
1421
|
|
|
1387
1422
|
``` python
|
|
@@ -1428,15 +1463,6 @@ def _generate_hierarchy_js(
|
|
|
1428
1463
|
|
|
1429
1464
|
``` python
|
|
1430
1465
|
def render_selection_step(
|
|
1431
|
-
sources: List[Dict[str, Any]], # Available source plugins
|
|
1432
|
-
transcriptions: List[Dict[str, Any]], # Available transcription records
|
|
1433
|
-
selected_sources: List[Dict[str, str]], # Ordered selection
|
|
1434
|
-
grouping_mode: str, # Grouping mode: "media_path" or "batch_id"
|
|
1435
|
-
active_tab: str, # Active tab: "db" or "files"
|
|
1436
|
-
urls: SelectionUrls, # URL bundle for selection routes
|
|
1437
|
-
render_local_files_panel: Optional[Callable] = None, # Render fn for Files tab content
|
|
1438
|
-
sb_state: Any = None, # SourceBrowserRouterState for DB tab VC rendering
|
|
1439
|
-
) -> Any: # FastHTML component
|
|
1440
1466
|
"Render Phase 1: Source Selection & Ordering step with two-column layout."
|
|
1441
1467
|
```
|
|
1442
1468
|
|
|
@@ -4,8 +4,8 @@ cjm-transcription-plugin-system
|
|
|
4
4
|
cjm-fasthtml-app-core
|
|
5
5
|
cjm-fasthtml-daisyui
|
|
6
6
|
cjm_fasthtml_lucide_icons
|
|
7
|
-
cjm_fasthtml_file_browser>=0.0.
|
|
8
|
-
cjm_fasthtml_keyboard_navigation>=0.0.
|
|
7
|
+
cjm_fasthtml_file_browser>=0.0.19
|
|
8
|
+
cjm_fasthtml_keyboard_navigation>=0.0.23
|
|
9
9
|
duckdb
|
|
10
10
|
pandas
|
|
11
11
|
cjm_workflow_state
|
|
@@ -13,5 +13,5 @@ cjm_source_provider
|
|
|
13
13
|
cjm_fasthtml_interactions
|
|
14
14
|
cjm_fasthtml_viewport_fit
|
|
15
15
|
cjm_fasthtml_virtual_collection
|
|
16
|
-
cjm_fasthtml_sortable_queue>=0.0.
|
|
16
|
+
cjm_fasthtml_sortable_queue>=0.0.16
|
|
17
17
|
cjm_fasthtml_design_system>=0.0.9
|
|
@@ -12,7 +12,7 @@ license = {text = "Apache-2.0"}
|
|
|
12
12
|
authors = [{name = "Christian J. Mills", email = "9126128+cj-mills@users.noreply.github.com"}]
|
|
13
13
|
keywords = ['nbdev', 'jupyter', 'notebook', 'python']
|
|
14
14
|
classifiers = ["Natural Language :: English", "Intended Audience :: Developers", "Development Status :: 3 - Alpha", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only"]
|
|
15
|
-
dependencies = ['python-fasthtml==0.13.4', 'cjm-plugin-system', 'cjm-transcription-plugin-system', 'cjm-fasthtml-app-core', 'cjm-fasthtml-daisyui', 'cjm_fasthtml_lucide_icons', 'cjm_fasthtml_file_browser>=0.0.
|
|
15
|
+
dependencies = ['python-fasthtml==0.13.4', 'cjm-plugin-system', 'cjm-transcription-plugin-system', 'cjm-fasthtml-app-core', 'cjm-fasthtml-daisyui', 'cjm_fasthtml_lucide_icons', 'cjm_fasthtml_file_browser>=0.0.19', 'cjm_fasthtml_keyboard_navigation>=0.0.23', 'duckdb', 'pandas', 'cjm_workflow_state', 'cjm_source_provider', 'cjm_fasthtml_interactions', 'cjm_fasthtml_viewport_fit', 'cjm_fasthtml_virtual_collection', 'cjm_fasthtml_sortable_queue>=0.0.16', 'cjm_fasthtml_design_system>=0.0.9']
|
|
16
16
|
|
|
17
17
|
[project.urls]
|
|
18
18
|
Repository = "https://github.com/cj-mills/cjm-transcript-source-select"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.0.29"
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|