cjm-transcript-segment-align 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,323 @@
1
+ """Shared keyboard navigation configuration for the combined Phase 2 step"""
2
+
3
+ # AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/components/keyboard_config.ipynb.
4
+
5
+ # %% auto #0
6
+ __all__ = ['DEBUG_KB_SYSTEM', 'ZONE_CHANGE_CALLBACK', 'SWITCH_CHROME_BTN_ID', 'render_keyboard_hints_collapsible',
7
+ 'build_combined_kb_system', 'generate_zone_change_js']
8
+
9
+ # %% ../../nbs/components/keyboard_config.ipynb #kb-imports
10
+ from typing import Any, Tuple, Optional
11
+
12
+ from fasthtml.common import Details, Summary, Div, Script
13
+
14
+ # DaisyUI components
15
+ from cjm_fasthtml_daisyui.utilities.semantic_colors import bg_dui
16
+ from cjm_fasthtml_daisyui.components.data_display.collapse import (
17
+ collapse, collapse_title, collapse_content, collapse_modifiers
18
+ )
19
+
20
+ # Tailwind utilities
21
+ from cjm_fasthtml_tailwind.utilities.typography import font_size, font_weight
22
+ from cjm_fasthtml_tailwind.core.base import combine_classes
23
+
24
+ # Keyboard navigation library
25
+ from cjm_fasthtml_keyboard_navigation.core.manager import ZoneManager
26
+ from cjm_fasthtml_keyboard_navigation.components.hints import render_keyboard_hints
27
+ from cjm_fasthtml_keyboard_navigation.components.system import render_keyboard_system
28
+
29
+ # Card stack library
30
+ from cjm_fasthtml_card_stack.keyboard.actions import build_card_stack_url_map
31
+
32
+ # Local HTML IDs
33
+ from ..html_ids import CombinedHtmlIds
34
+
35
+ # Segmentation-specific keyboard config (building blocks)
36
+ from cjm_transcript_segmentation.components.keyboard_config import (
37
+ SD_SEG_ENTER_SPLIT_BTN, SD_SEG_EXIT_SPLIT_BTN,
38
+ SD_SEG_SPLIT_BTN, SD_SEG_MERGE_BTN, SD_SEG_UNDO_BTN,
39
+ create_seg_kb_parts,
40
+ )
41
+ from cjm_transcript_segmentation.components.card_stack_config import (
42
+ SEG_CS_CONFIG, SEG_CS_IDS, SEG_CS_BTN_IDS,
43
+ SEG_TS_IDS,
44
+ )
45
+
46
+ # Alignment-specific keyboard config (building blocks)
47
+ from cjm_transcript_vad_align.components.keyboard_config import (
48
+ create_align_kb_parts,
49
+ )
50
+ from cjm_transcript_vad_align.components.card_stack_config import (
51
+ ALIGN_CS_CONFIG, ALIGN_CS_IDS, ALIGN_CS_BTN_IDS,
52
+ )
53
+
54
+ # URL bundles
55
+ from cjm_transcript_segmentation.models import SegmentationUrls
56
+ from cjm_transcript_vad_align.models import AlignmentUrls
57
+
58
+ # Debug flag for keyboard system tracing (set False in production)
59
+ DEBUG_KB_SYSTEM = True
60
+
61
+ # %% ../../nbs/components/keyboard_config.ipynb #kb-hints
62
+ def render_keyboard_hints_collapsible(
63
+ manager:ZoneManager, # Keyboard zone manager with actions configured
64
+ container_id:str="sd-keyboard-hints", # HTML ID for the hints container
65
+ include_zone_switch:bool=False, # Whether to include zone switch hints
66
+ ) -> Any: # Collapsible keyboard hints component
67
+ """Render keyboard shortcut hints in a collapsible DaisyUI collapse."""
68
+ hints = render_keyboard_hints(
69
+ manager,
70
+ include_navigation=True,
71
+ include_zone_switch=include_zone_switch,
72
+ badge_style="outline",
73
+ container_id=container_id,
74
+ use_icons=False
75
+ )
76
+
77
+ return Details(
78
+ Summary(
79
+ "Keyboard Shortcuts",
80
+ cls=combine_classes(collapse_title, font_size.sm, font_weight.medium)
81
+ ),
82
+ Div(
83
+ hints,
84
+ cls=collapse_content
85
+ ),
86
+ cls=combine_classes(collapse, collapse_modifiers.arrow, bg_dui.base_200)
87
+ )
88
+
89
+ # %% ../../nbs/components/keyboard_config.ipynb #q9f5gd0d0s
90
+ # Zone change callback name — global JS function called when zone switches
91
+ ZONE_CHANGE_CALLBACK = "onCombinedZoneChange"
92
+
93
+
94
+ def build_combined_kb_system(
95
+ seg_urls:SegmentationUrls, # URL bundle for segmentation routes
96
+ align_urls:AlignmentUrls, # URL bundle for alignment routes
97
+ ) -> Tuple[ZoneManager, Any]: # (keyboard manager, rendered keyboard system)
98
+ """Build combined keyboard system with segmentation and alignment zones."""
99
+ if DEBUG_KB_SYSTEM:
100
+ print("[KB_SYSTEM] build_combined_kb_system called")
101
+
102
+ # Get segmentation-specific building blocks
103
+ seg_zone, seg_actions, seg_modes = create_seg_kb_parts(
104
+ ids=SEG_CS_IDS,
105
+ button_ids=SEG_CS_BTN_IDS,
106
+ config=SEG_CS_CONFIG,
107
+ )
108
+
109
+ # Get alignment-specific building blocks
110
+ align_zone, align_actions, align_modes = create_align_kb_parts(
111
+ ids=ALIGN_CS_IDS,
112
+ button_ids=ALIGN_CS_BTN_IDS,
113
+ config=ALIGN_CS_CONFIG,
114
+ )
115
+
116
+ if DEBUG_KB_SYSTEM:
117
+ print(f"[KB_SYSTEM] seg_zone.id: {seg_zone.id}")
118
+ print(f"[KB_SYSTEM] align_zone.id: {align_zone.id}")
119
+ print(f"[KB_SYSTEM] seg_actions count: {len(seg_actions)}")
120
+ print(f"[KB_SYSTEM] align_actions count: {len(align_actions)}")
121
+
122
+ # Combine modes (only segmentation has split mode)
123
+ all_modes = (*seg_modes, *align_modes)
124
+
125
+ # Combine actions from both zones
126
+ all_actions = (*seg_actions, *align_actions)
127
+
128
+ # Assemble into ZoneManager with zone switching enabled
129
+ kb_manager = ZoneManager(
130
+ zones=(seg_zone, align_zone),
131
+ actions=all_actions,
132
+ modes=all_modes,
133
+ initial_zone_id=seg_zone.id,
134
+ prev_zone_key="ArrowLeft",
135
+ next_zone_key="ArrowRight",
136
+ on_zone_change=ZONE_CHANGE_CALLBACK,
137
+ state_hidden_inputs=True,
138
+ )
139
+
140
+ # Build URL maps for both stacks
141
+ seg_include_selector = (
142
+ f"#{SEG_CS_IDS.focused_index_input}, "
143
+ f"#{SEG_TS_IDS.anchor_input}"
144
+ )
145
+ align_include_selector = f"#{ALIGN_CS_IDS.focused_index_input}"
146
+
147
+ # Segmentation URL mappings
148
+ seg_url_map = {
149
+ **build_card_stack_url_map(SEG_CS_BTN_IDS, seg_urls.card_stack),
150
+ SD_SEG_ENTER_SPLIT_BTN: seg_urls.enter_split,
151
+ SD_SEG_EXIT_SPLIT_BTN: seg_urls.exit_split,
152
+ SD_SEG_SPLIT_BTN: seg_urls.split,
153
+ SD_SEG_MERGE_BTN: seg_urls.merge,
154
+ SD_SEG_UNDO_BTN: seg_urls.undo,
155
+ }
156
+
157
+ # Alignment URL mappings (navigation only — no undo for alignment)
158
+ align_url_map = {
159
+ **build_card_stack_url_map(ALIGN_CS_BTN_IDS, align_urls.card_stack),
160
+ }
161
+
162
+ # Combined URL map
163
+ url_map = {**seg_url_map, **align_url_map}
164
+
165
+ # Target maps (each stack targets its own card_stack element)
166
+ seg_target = f"#{SEG_CS_IDS.card_stack}"
167
+ align_target = f"#{ALIGN_CS_IDS.card_stack}"
168
+ target_map = {
169
+ **{btn_id: seg_target for btn_id in seg_url_map},
170
+ **{btn_id: align_target for btn_id in align_url_map},
171
+ }
172
+
173
+ # Include maps (each stack includes its own focused_index input)
174
+ include_map = {
175
+ **{btn_id: seg_include_selector for btn_id in seg_url_map},
176
+ **{btn_id: align_include_selector for btn_id in align_url_map},
177
+ }
178
+
179
+ # Swap map (none for all — OOB swaps handle updates)
180
+ swap_map = {btn_id: "none" for btn_id in url_map}
181
+
182
+ kb_system = render_keyboard_system(
183
+ kb_manager,
184
+ url_map=url_map,
185
+ target_map=target_map,
186
+ include_map=include_map,
187
+ swap_map=swap_map,
188
+ show_hints=False,
189
+ include_state_inputs=True,
190
+ )
191
+
192
+ if DEBUG_KB_SYSTEM:
193
+ print(f"[KB_SYSTEM] combined kb_system built successfully")
194
+
195
+ return kb_manager, kb_system
196
+
197
+ # %% ../../nbs/components/keyboard_config.ipynb #0rck8zsx5u8m
198
+ # Hidden button ID for chrome swap HTMX trigger
199
+ SWITCH_CHROME_BTN_ID = "sd-switch-chrome-btn"
200
+
201
+
202
+ def generate_zone_change_js(
203
+ switch_chrome_url:str="", # URL for chrome swap handler (empty = no swap)
204
+ ) -> Script: # Script element with zone change callback and click handlers
205
+ """Generate JavaScript for zone change handling and column click handlers."""
206
+ # Zone IDs from card stack configs
207
+ seg_zone_id = SEG_CS_IDS.card_stack
208
+ align_zone_id = ALIGN_CS_IDS.card_stack
209
+
210
+ # Card stack prefixes for syncCountDropdown calls
211
+ seg_prefix = SEG_CS_CONFIG.prefix
212
+ align_prefix = ALIGN_CS_CONFIG.prefix
213
+
214
+ # Column container IDs
215
+ seg_col_id = CombinedHtmlIds.SEG_COLUMN
216
+ align_col_id = CombinedHtmlIds.ALIGNMENT_COLUMN
217
+ active_input_id = CombinedHtmlIds.ACTIVE_COLUMN_INPUT
218
+
219
+ # Chrome swap trigger with dropdown sync (optional)
220
+ chrome_swap_js = ""
221
+ if switch_chrome_url:
222
+ chrome_swap_js = f"""
223
+ // Trigger chrome swap via hidden HTMX button
224
+ const chromeSwitchBtn = document.getElementById('{SWITCH_CHROME_BTN_ID}');
225
+ if (chromeSwitchBtn) {{
226
+ // Add one-time listener to sync dropdown after chrome swap settles
227
+ function onChromeSettle(evt) {{
228
+ // Sync the active card stack's dropdown from localStorage
229
+ const activePrefix = (newZoneId === seg_zone_id) ? '{seg_prefix}' : '{align_prefix}';
230
+ if (window.cardStacks?.[activePrefix]?.syncCountDropdown) {{
231
+ window.cardStacks[activePrefix].syncCountDropdown();
232
+ }}
233
+ document.body.removeEventListener('htmx:afterSettle', onChromeSettle);
234
+ }}
235
+ document.body.addEventListener('htmx:afterSettle', onChromeSettle);
236
+ chromeSwitchBtn.click();
237
+ }}
238
+ """
239
+
240
+ js_code = f"""
241
+ (function() {{
242
+ const seg_zone_id = '{seg_zone_id}';
243
+ const align_zone_id = '{align_zone_id}';
244
+ const seg_col_id = '{seg_col_id}';
245
+ const align_col_id = '{align_col_id}';
246
+ const active_input_id = '{active_input_id}';
247
+
248
+ function updateColumnStyles(activeZoneId) {{
249
+ const segCol = document.getElementById(seg_col_id);
250
+ const alignCol = document.getElementById(align_col_id);
251
+ const activeInput = document.getElementById(active_input_id);
252
+
253
+ if (!segCol || !alignCol) return;
254
+
255
+ const isSegActive = activeZoneId === seg_zone_id;
256
+
257
+ // Ring classes for active indicator
258
+ segCol.classList.toggle('ring-1', isSegActive);
259
+ segCol.classList.toggle('ring-primary', isSegActive);
260
+ alignCol.classList.toggle('ring-1', !isSegActive);
261
+ alignCol.classList.toggle('ring-secondary', !isSegActive);
262
+
263
+ // Opacity for inactive column
264
+ segCol.classList.toggle('opacity-70', !isSegActive);
265
+ alignCol.classList.toggle('opacity-70', isSegActive);
266
+
267
+ // Update hidden input
268
+ if (activeInput) {{
269
+ activeInput.value = isSegActive ? 'seg' : 'align';
270
+ }}
271
+ }}
272
+
273
+ // Global callback for ZoneManager (called on keyboard zone switch)
274
+ window.{ZONE_CHANGE_CALLBACK} = function(newZoneId, prevZoneId) {{
275
+ if (window.DEBUG_KB_SYSTEM) {{
276
+ console.log('[KB_SYSTEM] Zone change:', prevZoneId, '->', newZoneId);
277
+ }}
278
+ updateColumnStyles(newZoneId);
279
+ {chrome_swap_js}
280
+ }};
281
+
282
+ // Click handler for column focus switching
283
+ function handleColumnClick(targetZoneId) {{
284
+ // Skip if already active
285
+ const state = window.kbNav?.getState?.();
286
+ if (state && state.activeZoneId === targetZoneId) return;
287
+
288
+ // Skip if in split mode (don't allow accidental zone switch)
289
+ if (state && state.currentMode === 'split') return;
290
+
291
+ // Switch zone using keyboard navigation library API
292
+ if (window.kbNav?.setActiveZone) {{
293
+ window.kbNav.setActiveZone(targetZoneId);
294
+ }}
295
+ }}
296
+
297
+ // Set up click handlers on columns
298
+ function setupClickHandlers() {{
299
+ const segCol = document.getElementById(seg_col_id);
300
+ const alignCol = document.getElementById(align_col_id);
301
+
302
+ if (segCol) {{
303
+ segCol.addEventListener('mousedown', function(e) {{
304
+ handleColumnClick(seg_zone_id);
305
+ }});
306
+ }}
307
+
308
+ if (alignCol) {{
309
+ alignCol.addEventListener('mousedown', function(e) {{
310
+ handleColumnClick(align_zone_id);
311
+ }});
312
+ }}
313
+ }}
314
+
315
+ // Initial update (segmentation zone is active by default)
316
+ updateColumnStyles(seg_zone_id);
317
+
318
+ // Set up click handlers
319
+ setupClickHandlers();
320
+ }})();
321
+ """
322
+
323
+ return Script(js_code)