cjm-transcript-source-select 0.0.3__tar.gz → 0.0.4__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 (38) hide show
  1. {cjm_transcript_source_select-0.0.3/cjm_transcript_source_select.egg-info → cjm_transcript_source_select-0.0.4}/PKG-INFO +24 -23
  2. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/README.md +23 -22
  3. cjm_transcript_source_select-0.0.4/cjm_transcript_source_select/__init__.py +1 -0
  4. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/components/source_browser.py +11 -7
  5. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/components/step_renderer.py +3 -0
  6. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/html_ids.py +3 -0
  7. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/routes/core.py +35 -15
  8. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4/cjm_transcript_source_select.egg-info}/PKG-INFO +24 -23
  9. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/settings.ini +1 -1
  10. cjm_transcript_source_select-0.0.3/cjm_transcript_source_select/__init__.py +0 -1
  11. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/LICENSE +0 -0
  12. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/MANIFEST.in +0 -0
  13. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/_modidx.py +0 -0
  14. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/components/__init__.py +0 -0
  15. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/components/helpers.py +0 -0
  16. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/components/local_files.py +0 -0
  17. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/components/preview_panel.py +0 -0
  18. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/components/selection_queue.py +0 -0
  19. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/models.py +0 -0
  20. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/routes/__init__.py +0 -0
  21. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/routes/filtering.py +0 -0
  22. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/routes/init.py +0 -0
  23. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/routes/local_files.py +0 -0
  24. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/routes/queue.py +0 -0
  25. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/routes/tabs.py +0 -0
  26. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/services/__init__.py +0 -0
  27. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/services/source.py +0 -0
  28. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/services/source_utils.py +0 -0
  29. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select/utils.py +0 -0
  30. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select.egg-info/SOURCES.txt +0 -0
  31. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select.egg-info/dependency_links.txt +0 -0
  32. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select.egg-info/entry_points.txt +0 -0
  33. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select.egg-info/not-zip-safe +0 -0
  34. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select.egg-info/requires.txt +0 -0
  35. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/cjm_transcript_source_select.egg-info/top_level.txt +0 -0
  36. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/pyproject.toml +0 -0
  37. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/setup.cfg +0 -0
  38. {cjm_transcript_source_select-0.0.3 → cjm_transcript_source_select-0.0.4}/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.3
3
+ Version: 0.0.4
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
@@ -105,52 +105,52 @@ graph LR
105
105
  components_local_files --> html_ids
106
106
  components_preview_panel --> html_ids
107
107
  components_selection_queue --> html_ids
108
- components_source_browser --> utils
109
108
  components_source_browser --> services_source_utils
109
+ components_source_browser --> utils
110
110
  components_source_browser --> html_ids
111
- components_step_renderer --> components_selection_queue
111
+ components_step_renderer --> components_source_browser
112
+ components_step_renderer --> utils
112
113
  components_step_renderer --> components_preview_panel
113
114
  components_step_renderer --> components_local_files
114
- components_step_renderer --> components_source_browser
115
115
  components_step_renderer --> components_helpers
116
- components_step_renderer --> utils
116
+ components_step_renderer --> components_selection_queue
117
117
  components_step_renderer --> html_ids
118
118
  components_step_renderer --> models
119
- routes_core --> components_step_renderer
120
119
  routes_core --> services_source
121
120
  routes_core --> models
122
121
  routes_core --> html_ids
123
- routes_core --> components_selection_queue
124
122
  routes_core --> components_source_browser
125
- routes_filtering --> services_source
123
+ routes_core --> components_selection_queue
124
+ routes_core --> components_step_renderer
126
125
  routes_filtering --> services_source_utils
127
- routes_filtering --> routes_core
128
126
  routes_filtering --> components_source_browser
127
+ routes_filtering --> routes_core
128
+ routes_filtering --> services_source
129
129
  routes_filtering --> models
130
- routes_init --> routes_filtering
131
- routes_init --> routes_local_files
132
130
  routes_init --> services_source
133
- routes_init --> routes_tabs
134
131
  routes_init --> models
135
- routes_init --> routes_core
132
+ routes_init --> routes_tabs
133
+ routes_init --> routes_filtering
134
+ routes_init --> routes_local_files
136
135
  routes_init --> routes_queue
136
+ routes_init --> routes_core
137
137
  routes_local_files --> services_source
138
138
  routes_local_files --> routes_core
139
- routes_local_files --> models
140
139
  routes_local_files --> components_local_files
141
- routes_queue --> routes_core
142
- routes_queue --> components_preview_panel
140
+ routes_local_files --> models
143
141
  routes_queue --> services_source
144
142
  routes_queue --> services_source_utils
143
+ routes_queue --> routes_core
144
+ routes_queue --> components_preview_panel
145
145
  routes_queue --> models
146
- routes_tabs --> routes_local_files
147
146
  routes_tabs --> components_source_browser
148
- routes_tabs --> services_source_utils
149
- routes_tabs --> services_source
147
+ routes_tabs --> components_step_renderer
150
148
  routes_tabs --> routes_core
151
- routes_tabs --> models
149
+ routes_tabs --> services_source
150
+ routes_tabs --> services_source_utils
151
+ routes_tabs --> routes_local_files
152
152
  routes_tabs --> components_local_files
153
- routes_tabs --> components_step_renderer
153
+ routes_tabs --> models
154
154
  ```
155
155
 
156
156
  *51 cross-module dependencies detected*
@@ -203,8 +203,8 @@ def _render_duplicate_flash(
203
203
  candidate_provider_id: str, # Provider ID of the row the user clicked
204
204
  existing_record_id: str, # Record ID of the conflicting selected row
205
205
  existing_provider_id: str, # Provider ID of the conflicting selected row
206
- ) -> Script: # OOB script element for flash animation
207
- "Render a self-removing Script that briefly flashes two source rows with error color."
206
+ ) -> Div: # OOB Div with flash script (replaces previous via innerHTML swap)
207
+ "Render a fixed-ID container with flash script for two source rows."
208
208
  ```
209
209
 
210
210
  ``` python
@@ -1043,6 +1043,7 @@ def _render_source_row(
1043
1043
  remove_url: str, # URL for removing from queue
1044
1044
  preview_url: str, # URL for previewing content
1045
1045
  is_first: bool = False, # Whether this is the first row (gets initial focus)
1046
+ row_index: int = 0, # Index among selectable rows (for keyboard focus sync)
1046
1047
  ) -> Any: # Table row element
1047
1048
  "Render a single source row in the browser table."
1048
1049
  ```
@@ -62,52 +62,52 @@ graph LR
62
62
  components_local_files --> html_ids
63
63
  components_preview_panel --> html_ids
64
64
  components_selection_queue --> html_ids
65
- components_source_browser --> utils
66
65
  components_source_browser --> services_source_utils
66
+ components_source_browser --> utils
67
67
  components_source_browser --> html_ids
68
- components_step_renderer --> components_selection_queue
68
+ components_step_renderer --> components_source_browser
69
+ components_step_renderer --> utils
69
70
  components_step_renderer --> components_preview_panel
70
71
  components_step_renderer --> components_local_files
71
- components_step_renderer --> components_source_browser
72
72
  components_step_renderer --> components_helpers
73
- components_step_renderer --> utils
73
+ components_step_renderer --> components_selection_queue
74
74
  components_step_renderer --> html_ids
75
75
  components_step_renderer --> models
76
- routes_core --> components_step_renderer
77
76
  routes_core --> services_source
78
77
  routes_core --> models
79
78
  routes_core --> html_ids
80
- routes_core --> components_selection_queue
81
79
  routes_core --> components_source_browser
82
- routes_filtering --> services_source
80
+ routes_core --> components_selection_queue
81
+ routes_core --> components_step_renderer
83
82
  routes_filtering --> services_source_utils
84
- routes_filtering --> routes_core
85
83
  routes_filtering --> components_source_browser
84
+ routes_filtering --> routes_core
85
+ routes_filtering --> services_source
86
86
  routes_filtering --> models
87
- routes_init --> routes_filtering
88
- routes_init --> routes_local_files
89
87
  routes_init --> services_source
90
- routes_init --> routes_tabs
91
88
  routes_init --> models
92
- routes_init --> routes_core
89
+ routes_init --> routes_tabs
90
+ routes_init --> routes_filtering
91
+ routes_init --> routes_local_files
93
92
  routes_init --> routes_queue
93
+ routes_init --> routes_core
94
94
  routes_local_files --> services_source
95
95
  routes_local_files --> routes_core
96
- routes_local_files --> models
97
96
  routes_local_files --> components_local_files
98
- routes_queue --> routes_core
99
- routes_queue --> components_preview_panel
97
+ routes_local_files --> models
100
98
  routes_queue --> services_source
101
99
  routes_queue --> services_source_utils
100
+ routes_queue --> routes_core
101
+ routes_queue --> components_preview_panel
102
102
  routes_queue --> models
103
- routes_tabs --> routes_local_files
104
103
  routes_tabs --> components_source_browser
105
- routes_tabs --> services_source_utils
106
- routes_tabs --> services_source
104
+ routes_tabs --> components_step_renderer
107
105
  routes_tabs --> routes_core
108
- routes_tabs --> models
106
+ routes_tabs --> services_source
107
+ routes_tabs --> services_source_utils
108
+ routes_tabs --> routes_local_files
109
109
  routes_tabs --> components_local_files
110
- routes_tabs --> components_step_renderer
110
+ routes_tabs --> models
111
111
  ```
112
112
 
113
113
  *51 cross-module dependencies detected*
@@ -160,8 +160,8 @@ def _render_duplicate_flash(
160
160
  candidate_provider_id: str, # Provider ID of the row the user clicked
161
161
  existing_record_id: str, # Record ID of the conflicting selected row
162
162
  existing_provider_id: str, # Provider ID of the conflicting selected row
163
- ) -> Script: # OOB script element for flash animation
164
- "Render a self-removing Script that briefly flashes two source rows with error color."
163
+ ) -> Div: # OOB Div with flash script (replaces previous via innerHTML swap)
164
+ "Render a fixed-ID container with flash script for two source rows."
165
165
  ```
166
166
 
167
167
  ``` python
@@ -1000,6 +1000,7 @@ def _render_source_row(
1000
1000
  remove_url: str, # URL for removing from queue
1001
1001
  preview_url: str, # URL for previewing content
1002
1002
  is_first: bool = False, # Whether this is the first row (gets initial focus)
1003
+ row_index: int = 0, # Index among selectable rows (for keyboard focus sync)
1003
1004
  ) -> Any: # Table row element
1004
1005
  "Render a single source row in the browser table."
1005
1006
  ```
@@ -0,0 +1 @@
1
+ __version__ = "0.0.4"
@@ -39,7 +39,6 @@ from cjm_fasthtml_tailwind.utilities.interactivity import cursor
39
39
  from cjm_fasthtml_tailwind.utilities.flexbox_and_grid import (
40
40
  flex_display, flex_direction, justify, items, gap, grow
41
41
  )
42
- from cjm_fasthtml_tailwind.utilities.transitions_and_animation import transition, duration
43
42
  from cjm_fasthtml_tailwind.core.base import combine_classes
44
43
 
45
44
  # Local imports
@@ -84,6 +83,7 @@ def _render_source_row(
84
83
  remove_url: str, # URL for removing from queue
85
84
  preview_url: str, # URL for previewing content
86
85
  is_first: bool = False, # Whether this is the first row (gets initial focus)
86
+ row_index: int = 0, # Index among selectable rows (for keyboard focus sync)
87
87
  ) -> Any: # Table row element
88
88
  """Render a single source row in the browser table."""
89
89
  record_id = record.get("record_id", "")
@@ -139,6 +139,10 @@ def _render_source_row(
139
139
  cls=combine_classes(tooltip, tooltip_placement.top)
140
140
  )
141
141
 
142
+ # Click handler to sync keyboard focus with clicked row
143
+ zone_id = SelectionHtmlIds.SOURCE_LIST
144
+ focus_onclick = f"if(window.kbNav)window.kbNav.setItemFocus('{zone_id}',{row_index},true)"
145
+
142
146
  return Tr(
143
147
  # Hidden cell for keyboard action data (must be in TD for valid HTML)
144
148
  Td(
@@ -178,15 +182,14 @@ def _render_source_row(
178
182
  cls=combine_classes(
179
183
  bg_dui.primary.opacity(10) if is_selected else "",
180
184
  bg_dui.base_200.hover,
181
- cursor.pointer,
182
- transition.colors,
183
- duration(200)
185
+ cursor.pointer
184
186
  ),
185
187
  tabindex="0",
186
188
  data_selectable="true",
187
189
  data_focused="true" if is_first else "false",
188
190
  data_record_id=record_id,
189
191
  data_provider_id=provider_id,
192
+ onclick=focus_onclick,
190
193
  hx_get=preview_url,
191
194
  hx_vals=json.dumps({"record_id": record_id, "provider_id": provider_id}),
192
195
  hx_target=SelectionHtmlIds.as_selector(SelectionHtmlIds.PREVIEW_PANEL),
@@ -254,7 +257,7 @@ def _render_source_list(
254
257
 
255
258
  # Build table rows
256
259
  table_rows = []
257
- is_first_record = True
260
+ row_index = 0
258
261
  for group_key, records in grouped.items():
259
262
  # Add group header
260
263
  table_rows.append(_render_group_header(
@@ -268,9 +271,10 @@ def _render_source_list(
268
271
  )
269
272
  table_rows.append(_render_source_row(
270
273
  record, is_selected, add_url, remove_url, preview_url,
271
- is_first=is_first_record
274
+ is_first=(row_index == 0),
275
+ row_index=row_index,
272
276
  ))
273
- is_first_record = False
277
+ row_index += 1
274
278
 
275
279
  return Div(
276
280
  Table(
@@ -570,6 +570,9 @@ def render_selection_step(
570
570
  # Sortable initialization script (htmx integration)
571
571
  sortable_script,
572
572
 
573
+ # Script runner for OOB-triggered JS (duplicate rejection flash)
574
+ Div(id=SelectionHtmlIds.SCRIPT_RUNNER),
575
+
573
576
  id=SelectionHtmlIds.SOURCE_SELECTOR,
574
577
  cls=combine_classes(
575
578
  w.full,
@@ -62,6 +62,9 @@ class SelectionHtmlIds:
62
62
  SELECTION_FOOTER = "sd-selection-footer"
63
63
  SELECTION_STATS = "sd-selection-stats"
64
64
 
65
+ # Script runner (OOB container for flash feedback scripts)
66
+ SCRIPT_RUNNER = "sd-selection-script-runner"
67
+
65
68
  @staticmethod
66
69
  def _safe_id(
67
70
  value:str # Raw value to sanitize for HTML ID use
@@ -8,7 +8,7 @@ __all__ = ['DEBUG_SELECTION_STATE', 'WorkflowStateStore']
8
8
  # %% ../../nbs/routes/core.ipynb #sel-core-imports
9
9
  from typing import List, Dict, Any, Optional, Tuple, Union
10
10
 
11
- from fasthtml.common import Script
11
+ from fasthtml.common import Div, Script
12
12
 
13
13
  from cjm_workflow_state.state_store import SQLiteWorkflowStateStore
14
14
 
@@ -63,26 +63,46 @@ def _render_duplicate_flash(
63
63
  candidate_provider_id: str, # Provider ID of the row the user clicked
64
64
  existing_record_id: str, # Record ID of the conflicting selected row
65
65
  existing_provider_id: str, # Provider ID of the conflicting selected row
66
- ) -> Script: # OOB script element for flash animation
67
- """Render a self-removing Script that briefly flashes two source rows with error color."""
66
+ ) -> Div: # OOB Div with flash script (replaces previous via innerHTML swap)
67
+ """Render a fixed-ID container with flash script for two source rows."""
68
68
  row1 = SelectionHtmlIds.source_row(candidate_record_id, candidate_provider_id)
69
69
  row2 = SelectionHtmlIds.source_row(existing_record_id, existing_provider_id)
70
- return Script(f"""
70
+ return Div(
71
+ Script(f"""
71
72
  (function() {{
72
- var s = document.currentScript;
73
- var r1 = document.getElementById('{row1}');
74
- var r2 = document.getElementById('{row2}');
75
- [r1, r2].forEach(function(r) {{
76
- if (r) {{ r.classList.add('bg-error'); }}
77
- }});
78
- setTimeout(function() {{
73
+ function flash() {{
74
+ var r1 = document.getElementById('{row1}');
75
+ var r2 = document.getElementById('{row2}');
79
76
  [r1, r2].forEach(function(r) {{
80
- if (r) {{ r.classList.remove('bg-error'); }}
77
+ if (r) {{
78
+ r.style.transition = 'background-color 0.05s ease-in';
79
+ r.classList.add('bg-error');
80
+ }}
81
81
  }});
82
- if (s) {{ s.remove(); }}
83
- }}, 400);
82
+ setTimeout(function() {{
83
+ [r1, r2].forEach(function(r) {{
84
+ if (r) {{
85
+ r.style.transition = 'background-color 0.3s ease-out';
86
+ r.classList.remove('bg-error');
87
+ }}
88
+ }});
89
+ setTimeout(function() {{
90
+ [r1, r2].forEach(function(r) {{
91
+ if (r) {{ r.style.transition = ''; }}
92
+ }});
93
+ }}, 300);
94
+ }}, 600);
95
+ }}
96
+ // Defer until after HTMX finishes all OOB swaps
97
+ document.body.addEventListener('htmx:afterSettle', function handler() {{
98
+ document.body.removeEventListener('htmx:afterSettle', handler);
99
+ flash();
100
+ }});
84
101
  }})();
85
- """)
102
+ """),
103
+ id=SelectionHtmlIds.SCRIPT_RUNNER,
104
+ hx_swap_oob="innerHTML",
105
+ )
86
106
 
87
107
  # %% ../../nbs/routes/core.ipynb #3zll5oy1hsc
88
108
  def _get_active_source_tab(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cjm-transcript-source-select
3
- Version: 0.0.3
3
+ Version: 0.0.4
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
@@ -105,52 +105,52 @@ graph LR
105
105
  components_local_files --> html_ids
106
106
  components_preview_panel --> html_ids
107
107
  components_selection_queue --> html_ids
108
- components_source_browser --> utils
109
108
  components_source_browser --> services_source_utils
109
+ components_source_browser --> utils
110
110
  components_source_browser --> html_ids
111
- components_step_renderer --> components_selection_queue
111
+ components_step_renderer --> components_source_browser
112
+ components_step_renderer --> utils
112
113
  components_step_renderer --> components_preview_panel
113
114
  components_step_renderer --> components_local_files
114
- components_step_renderer --> components_source_browser
115
115
  components_step_renderer --> components_helpers
116
- components_step_renderer --> utils
116
+ components_step_renderer --> components_selection_queue
117
117
  components_step_renderer --> html_ids
118
118
  components_step_renderer --> models
119
- routes_core --> components_step_renderer
120
119
  routes_core --> services_source
121
120
  routes_core --> models
122
121
  routes_core --> html_ids
123
- routes_core --> components_selection_queue
124
122
  routes_core --> components_source_browser
125
- routes_filtering --> services_source
123
+ routes_core --> components_selection_queue
124
+ routes_core --> components_step_renderer
126
125
  routes_filtering --> services_source_utils
127
- routes_filtering --> routes_core
128
126
  routes_filtering --> components_source_browser
127
+ routes_filtering --> routes_core
128
+ routes_filtering --> services_source
129
129
  routes_filtering --> models
130
- routes_init --> routes_filtering
131
- routes_init --> routes_local_files
132
130
  routes_init --> services_source
133
- routes_init --> routes_tabs
134
131
  routes_init --> models
135
- routes_init --> routes_core
132
+ routes_init --> routes_tabs
133
+ routes_init --> routes_filtering
134
+ routes_init --> routes_local_files
136
135
  routes_init --> routes_queue
136
+ routes_init --> routes_core
137
137
  routes_local_files --> services_source
138
138
  routes_local_files --> routes_core
139
- routes_local_files --> models
140
139
  routes_local_files --> components_local_files
141
- routes_queue --> routes_core
142
- routes_queue --> components_preview_panel
140
+ routes_local_files --> models
143
141
  routes_queue --> services_source
144
142
  routes_queue --> services_source_utils
143
+ routes_queue --> routes_core
144
+ routes_queue --> components_preview_panel
145
145
  routes_queue --> models
146
- routes_tabs --> routes_local_files
147
146
  routes_tabs --> components_source_browser
148
- routes_tabs --> services_source_utils
149
- routes_tabs --> services_source
147
+ routes_tabs --> components_step_renderer
150
148
  routes_tabs --> routes_core
151
- routes_tabs --> models
149
+ routes_tabs --> services_source
150
+ routes_tabs --> services_source_utils
151
+ routes_tabs --> routes_local_files
152
152
  routes_tabs --> components_local_files
153
- routes_tabs --> components_step_renderer
153
+ routes_tabs --> models
154
154
  ```
155
155
 
156
156
  *51 cross-module dependencies detected*
@@ -203,8 +203,8 @@ def _render_duplicate_flash(
203
203
  candidate_provider_id: str, # Provider ID of the row the user clicked
204
204
  existing_record_id: str, # Record ID of the conflicting selected row
205
205
  existing_provider_id: str, # Provider ID of the conflicting selected row
206
- ) -> Script: # OOB script element for flash animation
207
- "Render a self-removing Script that briefly flashes two source rows with error color."
206
+ ) -> Div: # OOB Div with flash script (replaces previous via innerHTML swap)
207
+ "Render a fixed-ID container with flash script for two source rows."
208
208
  ```
209
209
 
210
210
  ``` python
@@ -1043,6 +1043,7 @@ def _render_source_row(
1043
1043
  remove_url: str, # URL for removing from queue
1044
1044
  preview_url: str, # URL for previewing content
1045
1045
  is_first: bool = False, # Whether this is the first row (gets initial focus)
1046
+ row_index: int = 0, # Index among selectable rows (for keyboard focus sync)
1046
1047
  ) -> Any: # Table row element
1047
1048
  "Render a single source row in the browser table."
1048
1049
  ```
@@ -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.3
4
+ version = 0.0.4
5
5
  min_python = 3.12
6
6
  license = apache2
7
7
  black_formatting = False
@@ -1 +0,0 @@
1
- __version__ = "0.0.3"