cjm-transcript-segment-align 0.0.10__tar.gz → 0.0.11__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 (33) hide show
  1. {cjm_transcript_segment_align-0.0.10/cjm_transcript_segment_align.egg-info → cjm_transcript_segment_align-0.0.11}/PKG-INFO +16 -17
  2. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/README.md +15 -16
  3. cjm_transcript_segment_align-0.0.11/cjm_transcript_segment_align/__init__.py +1 -0
  4. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align/components/handlers.py +37 -20
  5. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align/components/step_renderer.py +36 -36
  6. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align/html_ids.py +1 -1
  7. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align/routes/chrome.py +29 -31
  8. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11/cjm_transcript_segment_align.egg-info}/PKG-INFO +16 -17
  9. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/settings.ini +1 -1
  10. cjm_transcript_segment_align-0.0.10/cjm_transcript_segment_align/__init__.py +0 -1
  11. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/LICENSE +0 -0
  12. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/MANIFEST.in +0 -0
  13. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align/_modidx.py +0 -0
  14. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align/components/__init__.py +0 -0
  15. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align/components/helpers.py +0 -0
  16. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align/components/keyboard_config.py +0 -0
  17. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align/components/sync_controls.py +0 -0
  18. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align/components/toolbar_state.py +0 -0
  19. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align/models.py +0 -0
  20. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align/routes/__init__.py +0 -0
  21. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align/routes/forced_alignment.py +0 -0
  22. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align/routes/init.py +0 -0
  23. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align/services/__init__.py +0 -0
  24. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align/services/forced_alignment.py +0 -0
  25. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align.egg-info/SOURCES.txt +0 -0
  26. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align.egg-info/dependency_links.txt +0 -0
  27. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align.egg-info/entry_points.txt +0 -0
  28. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align.egg-info/not-zip-safe +0 -0
  29. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align.egg-info/requires.txt +0 -0
  30. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/cjm_transcript_segment_align.egg-info/top_level.txt +0 -0
  31. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/pyproject.toml +0 -0
  32. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/setup.cfg +0 -0
  33. {cjm_transcript_segment_align-0.0.10 → cjm_transcript_segment_align-0.0.11}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cjm-transcript-segment-align
3
- Version: 0.0.10
3
+ Version: 0.0.11
4
4
  Summary: FastHTML dual-column text segmentation & VAD alignment UI for transcript decomposition workflows with forced alignment-based text splitting for aligning text segments with VAD chunks.
5
5
  Home-page: https://github.com/cj-mills/cjm-transcript-segment-align
6
6
  Author: Christian J. Mills
@@ -83,36 +83,36 @@ graph LR
83
83
  routes_init[routes.init<br/>routes/init]
84
84
  services_forced_alignment[services.forced_alignment<br/>forced_alignment]
85
85
 
86
- components_handlers --> html_ids
86
+ components_handlers --> components_sync_controls
87
87
  components_handlers --> components_keyboard_config
88
+ components_handlers --> html_ids
88
89
  components_handlers --> components_step_renderer
89
90
  components_handlers --> services_forced_alignment
90
- components_handlers --> components_sync_controls
91
91
  components_handlers --> routes_forced_alignment
92
92
  components_keyboard_config --> html_ids
93
93
  components_keyboard_config --> components_sync_controls
94
+ components_step_renderer --> components_keyboard_config
95
+ components_step_renderer --> components_toolbar_state
94
96
  components_step_renderer --> html_ids
95
97
  components_step_renderer --> components_helpers
96
98
  components_step_renderer --> components_sync_controls
97
- components_step_renderer --> components_keyboard_config
98
- components_step_renderer --> components_toolbar_state
99
99
  components_toolbar_state --> components_sync_controls
100
- routes_chrome --> html_ids
101
- routes_chrome --> components_handlers
102
100
  routes_chrome --> components_sync_controls
101
+ routes_chrome --> components_handlers
102
+ routes_chrome --> html_ids
103
103
  routes_chrome --> components_step_renderer
104
+ routes_forced_alignment --> components_sync_controls
104
105
  routes_forced_alignment --> html_ids
105
106
  routes_forced_alignment --> components_step_renderer
106
- routes_forced_alignment --> components_sync_controls
107
107
  routes_init --> components_keyboard_config
108
- routes_init --> html_ids
109
- routes_init --> components_handlers
110
108
  routes_init --> models
109
+ routes_init --> components_handlers
110
+ routes_init --> html_ids
111
111
  routes_init --> services_forced_alignment
112
- routes_init --> components_sync_controls
113
- routes_init --> components_step_renderer
114
- routes_init --> routes_forced_alignment
115
112
  routes_init --> routes_chrome
113
+ routes_init --> routes_forced_alignment
114
+ routes_init --> components_step_renderer
115
+ routes_init --> components_sync_controls
116
116
  ```
117
117
 
118
118
  *30 cross-module dependencies detected*
@@ -157,6 +157,7 @@ async def _handle_switch_chrome(
157
157
 
158
158
  Client-side toolbar state restoration (sync button, auto-play toggle)
159
159
  is handled by the centralized settle handler from toolbar_state.py.
160
+ Settings modals persist in the DOM — only the trigger buttons swap.
160
161
  """
161
162
  ```
162
163
 
@@ -479,9 +480,6 @@ def create_seg_mutation_wrapper(
479
480
  Calls the _result handler variant, builds targeted OOB response with FA
480
481
  controls in toolbar, and appends alignment status + mini-stats OOB.
481
482
  Computes nltk_split_disabled from state for toolbar rendering.
482
-
483
- When clear_fa_presplit=True (used for NLTK Split), clears the FA pre-split
484
- snapshot so the toggle is replaced with the Force Align button.
485
483
  """
486
484
  ```
487
485
 
@@ -509,6 +507,7 @@ def create_seg_init_chrome_wrapper(
509
507
 
510
508
  Saves nltk_presplit snapshot at init time for match detection.
511
509
  FA controls are rendered in the toolbar via extra_actions.
510
+ Settings modals are rendered in a persistent container (both seg + align).
512
511
  """
513
512
  ```
514
513
 
@@ -842,7 +841,7 @@ def _render_shared_chrome(
842
841
  urls:SegmentationUrls=None, # Segmentation URL bundle (required when seg_state provided)
843
842
  extra_actions:tuple=(), # Extra toolbar elements (FA controls, sync toggle, etc.)
844
843
  nltk_split_disabled:bool=False, # Whether NLTK Split button is disabled
845
- ) -> tuple: # (toolbar, controls, footer)
844
+ ) -> tuple: # (toolbar, footer, settings_modals_container)
846
845
  """
847
846
  Render shared chrome containers, populated with segmentation content when initialized.
848
847
 
@@ -47,36 +47,36 @@ graph LR
47
47
  routes_init[routes.init<br/>routes/init]
48
48
  services_forced_alignment[services.forced_alignment<br/>forced_alignment]
49
49
 
50
- components_handlers --> html_ids
50
+ components_handlers --> components_sync_controls
51
51
  components_handlers --> components_keyboard_config
52
+ components_handlers --> html_ids
52
53
  components_handlers --> components_step_renderer
53
54
  components_handlers --> services_forced_alignment
54
- components_handlers --> components_sync_controls
55
55
  components_handlers --> routes_forced_alignment
56
56
  components_keyboard_config --> html_ids
57
57
  components_keyboard_config --> components_sync_controls
58
+ components_step_renderer --> components_keyboard_config
59
+ components_step_renderer --> components_toolbar_state
58
60
  components_step_renderer --> html_ids
59
61
  components_step_renderer --> components_helpers
60
62
  components_step_renderer --> components_sync_controls
61
- components_step_renderer --> components_keyboard_config
62
- components_step_renderer --> components_toolbar_state
63
63
  components_toolbar_state --> components_sync_controls
64
- routes_chrome --> html_ids
65
- routes_chrome --> components_handlers
66
64
  routes_chrome --> components_sync_controls
65
+ routes_chrome --> components_handlers
66
+ routes_chrome --> html_ids
67
67
  routes_chrome --> components_step_renderer
68
+ routes_forced_alignment --> components_sync_controls
68
69
  routes_forced_alignment --> html_ids
69
70
  routes_forced_alignment --> components_step_renderer
70
- routes_forced_alignment --> components_sync_controls
71
71
  routes_init --> components_keyboard_config
72
- routes_init --> html_ids
73
- routes_init --> components_handlers
74
72
  routes_init --> models
73
+ routes_init --> components_handlers
74
+ routes_init --> html_ids
75
75
  routes_init --> services_forced_alignment
76
- routes_init --> components_sync_controls
77
- routes_init --> components_step_renderer
78
- routes_init --> routes_forced_alignment
79
76
  routes_init --> routes_chrome
77
+ routes_init --> routes_forced_alignment
78
+ routes_init --> components_step_renderer
79
+ routes_init --> components_sync_controls
80
80
  ```
81
81
 
82
82
  *30 cross-module dependencies detected*
@@ -121,6 +121,7 @@ async def _handle_switch_chrome(
121
121
 
122
122
  Client-side toolbar state restoration (sync button, auto-play toggle)
123
123
  is handled by the centralized settle handler from toolbar_state.py.
124
+ Settings modals persist in the DOM — only the trigger buttons swap.
124
125
  """
125
126
  ```
126
127
 
@@ -443,9 +444,6 @@ def create_seg_mutation_wrapper(
443
444
  Calls the _result handler variant, builds targeted OOB response with FA
444
445
  controls in toolbar, and appends alignment status + mini-stats OOB.
445
446
  Computes nltk_split_disabled from state for toolbar rendering.
446
-
447
- When clear_fa_presplit=True (used for NLTK Split), clears the FA pre-split
448
- snapshot so the toggle is replaced with the Force Align button.
449
447
  """
450
448
  ```
451
449
 
@@ -473,6 +471,7 @@ def create_seg_init_chrome_wrapper(
473
471
 
474
472
  Saves nltk_presplit snapshot at init time for match detection.
475
473
  FA controls are rendered in the toolbar via extra_actions.
474
+ Settings modals are rendered in a persistent container (both seg + align).
476
475
  """
477
476
  ```
478
477
 
@@ -806,7 +805,7 @@ def _render_shared_chrome(
806
805
  urls:SegmentationUrls=None, # Segmentation URL bundle (required when seg_state provided)
807
806
  extra_actions:tuple=(), # Extra toolbar elements (FA controls, sync toggle, etc.)
808
807
  nltk_split_disabled:bool=False, # Whether NLTK Split button is disabled
809
- ) -> tuple: # (toolbar, controls, footer)
808
+ ) -> tuple: # (toolbar, footer, settings_modals_container)
810
809
  """
811
810
  Render shared chrome containers, populated with segmentation content when initialized.
812
811
 
@@ -0,0 +1 @@
1
+ __version__ = "0.0.11"
@@ -16,10 +16,12 @@ from typing import Callable, Any, Dict, List, Optional, Tuple
16
16
  from fasthtml.common import Div, Span, Button, FT
17
17
 
18
18
  from cjm_fasthtml_interactions.core.state_store import get_session_id
19
- from cjm_fasthtml_card_stack.components.controls import render_width_slider
19
+ from cjm_fasthtml_card_stack.components.settings_modal import render_card_stack_settings_modal, render_settings_trigger
20
20
  from cjm_fasthtml_card_stack.core.constants import DEFAULT_VISIBLE_COUNT
21
21
  from cjm_fasthtml_daisyui.components.data_display.badge import badge, badge_styles, badge_sizes
22
+ from cjm_fasthtml_tailwind.utilities.sizing import w
22
23
  from cjm_fasthtml_tailwind.utilities.layout import display_tw
24
+ from cjm_fasthtml_tailwind.utilities.flexbox_and_grid import flex_display, items, gap
23
25
  from cjm_fasthtml_tailwind.core.base import combine_classes
24
26
 
25
27
  from ..html_ids import CombinedHtmlIds
@@ -277,9 +279,6 @@ def create_seg_mutation_wrapper(
277
279
  Calls the _result handler variant, builds targeted OOB response with FA
278
280
  controls in toolbar, and appends alignment status + mini-stats OOB.
279
281
  Computes nltk_split_disabled from state for toolbar rendering.
280
-
281
- When clear_fa_presplit=True (used for NLTK Split), clears the FA pre-split
282
- snapshot so the toggle is replaced with the Force Align button.
283
282
  """
284
283
  @wraps(handler_result)
285
284
  async def wrapped(
@@ -333,7 +332,7 @@ def create_seg_mutation_wrapper(
333
332
  urls = kwargs.get("urls")
334
333
  seg_oob = build_mutation_response(
335
334
  result.segment_dicts, result.focused_index, result.visible_count,
336
- result.history_depth, urls, is_auto_mode=result.is_auto_mode,
335
+ result.history_depth, urls,
337
336
  extra_actions=build_extra_actions(fa_extra), nltk_split_disabled=nltk_disabled,
338
337
  )
339
338
 
@@ -341,7 +340,6 @@ def create_seg_mutation_wrapper(
341
340
 
342
341
  return wrapped
343
342
 
344
-
345
343
  # %% ../../nbs/components/handlers.ipynb #f6a7b8c9
346
344
  def wrap_align_mutation_handler(
347
345
  handler: Callable, # Handler function to wrap
@@ -388,6 +386,7 @@ def create_seg_init_chrome_wrapper(
388
386
 
389
387
  Saves nltk_presplit snapshot at init time for match detection.
390
388
  FA controls are rendered in the toolbar via extra_actions.
389
+ Settings modals are rendered in a persistent container (both seg + align).
391
390
  """
392
391
  async def wrapped_seg_init(
393
392
  state_store:WorkflowStateStore,
@@ -455,24 +454,43 @@ def create_seg_init_chrome_wrapper(
455
454
  seg_state, jm_trigger, fa_toggle_url, fa_available,
456
455
  )
457
456
 
458
- # Toolbar OOB (with FA controls in extra_actions, NLTK Split disabled at init)
457
+ # Settings modal trigger for seg column
458
+ settings_trigger = render_settings_trigger(modal_id=SEG_CS_IDS.settings_modal)
459
+
460
+ # Toolbar OOB (settings trigger + toolbar with FA controls, NLTK Split disabled at init)
459
461
  toolbar_oob = Div(
460
- render_toolbar(
461
- reset_url=urls.reset, ai_split_url=urls.ai_split, undo_url=urls.undo,
462
- can_undo=(result.history_depth > 0),
463
- visible_count=result.visible_count,
464
- is_auto_mode=result.is_auto_mode,
465
- extra_actions=build_extra_actions(fa_extra),
466
- nltk_split_disabled=True,
462
+ Div(
463
+ settings_trigger,
464
+ render_toolbar(
465
+ reset_url=urls.reset, ai_split_url=urls.ai_split, undo_url=urls.undo,
466
+ can_undo=(result.history_depth > 0),
467
+ extra_actions=build_extra_actions(fa_extra),
468
+ nltk_split_disabled=True,
469
+ ),
470
+ cls=combine_classes(flex_display, items.center, gap(2), w.full),
467
471
  ),
468
472
  id=CombinedHtmlIds.SHARED_TOOLBAR,
469
473
  hx_swap_oob="innerHTML"
470
474
  )
471
475
 
472
- # Controls OOB (width slider)
473
- controls_oob = Div(
474
- render_width_slider(SEG_CS_CONFIG, SEG_CS_IDS, card_width=result.card_width),
475
- id=CombinedHtmlIds.SHARED_CONTROLS,
476
+ # Settings modals OOB (both seg + align persist in DOM)
477
+ from cjm_transcript_vad_align.components.card_stack_config import (
478
+ ALIGN_CS_CONFIG, ALIGN_CS_IDS,
479
+ )
480
+ seg_modal, _ = render_card_stack_settings_modal(
481
+ SEG_CS_CONFIG, SEG_CS_IDS,
482
+ current_count=result.visible_count,
483
+ card_width=result.card_width,
484
+ )
485
+ align_modal, _ = render_card_stack_settings_modal(
486
+ ALIGN_CS_CONFIG, ALIGN_CS_IDS,
487
+ current_count=align_state.get("visible_count", 5),
488
+ card_width=align_state.get("card_width", 40),
489
+ )
490
+ settings_modals_oob = Div(
491
+ seg_modal,
492
+ align_modal,
493
+ id=CombinedHtmlIds.SETTINGS_MODALS,
476
494
  hx_swap_oob="innerHTML"
477
495
  )
478
496
 
@@ -499,12 +517,11 @@ def create_seg_init_chrome_wrapper(
499
517
 
500
518
  return (
501
519
  result.column_body, kb_system_oob, zone_change_js, chrome_switch_btn,
502
- toolbar_oob, controls_oob, footer_oob, mini_stats_oob,
520
+ toolbar_oob, settings_modals_oob, footer_oob, mini_stats_oob,
503
521
  )
504
522
 
505
523
  return wrapped_seg_init
506
524
 
507
-
508
525
  # %% ../../nbs/components/handlers.ipynb #ecvyiypdxk
509
526
  def create_align_init_chrome_wrapper(
510
527
  should_play_fn:str="", # Consumer-defined play guard function name
@@ -35,8 +35,8 @@ from cjm_fasthtml_tailwind.core.base import combine_classes
35
35
  from cjm_fasthtml_interactions.core.context import InteractionContext
36
36
 
37
37
  # Card stack library
38
- from cjm_fasthtml_card_stack.components.controls import render_width_slider
39
38
  from cjm_fasthtml_card_stack.components.states import render_loading_state
39
+ from cjm_fasthtml_card_stack.components.settings_modal import render_card_stack_settings_modal
40
40
  from cjm_fasthtml_card_stack.core.constants import DEFAULT_VISIBLE_COUNT, DEFAULT_CARD_WIDTH
41
41
 
42
42
  # Local imports
@@ -63,7 +63,7 @@ from cjm_transcript_vad_align.components.step_renderer import (
63
63
  render_align_footer_content,
64
64
  )
65
65
  from cjm_transcript_vad_align.components.card_stack_config import (
66
- ALIGN_CS_IDS,
66
+ ALIGN_CS_CONFIG, ALIGN_CS_IDS,
67
67
  )
68
68
 
69
69
  # Shared keyboard config (combined-level)
@@ -88,7 +88,6 @@ from .toolbar_state import generate_toolbar_restore_js
88
88
  # Debug flag for combined step rendering tracing (set False in production)
89
89
  DEBUG_COMBINED_RENDER = True
90
90
 
91
-
92
91
  # %% ../../nbs/components/step_renderer.ipynb #e5f6a7b8
93
92
  def _render_column_header(
94
93
  title:str, # Column title (e.g., "Text Decomposition")
@@ -227,7 +226,7 @@ def _render_shared_chrome(
227
226
  urls:SegmentationUrls=None, # Segmentation URL bundle (required when seg_state provided)
228
227
  extra_actions:tuple=(), # Extra toolbar elements (FA controls, sync toggle, etc.)
229
228
  nltk_split_disabled:bool=False, # Whether NLTK Split button is disabled
230
- ) -> tuple: # (toolbar, controls, footer)
229
+ ) -> tuple: # (toolbar, footer, settings_modals_container)
231
230
  """Render shared chrome containers, populated with segmentation content when initialized.
232
231
 
233
232
  Takes extracted state dicts from `extract_seg_state()` and `extract_alignment_state()`
@@ -235,19 +234,37 @@ def _render_shared_chrome(
235
234
  """
236
235
  is_init = seg_state is not None and seg_state.get("is_initialized", False)
237
236
 
237
+ # --- Settings modals (both persist in DOM, triggers swap with toolbar) ---
238
+ seg_modal, seg_trigger = render_card_stack_settings_modal(
239
+ SEG_CS_CONFIG, SEG_CS_IDS,
240
+ current_count=seg_state.get("visible_count", DEFAULT_VISIBLE_COUNT) if seg_state else DEFAULT_VISIBLE_COUNT,
241
+ card_width=seg_state.get("card_width", DEFAULT_CARD_WIDTH) if seg_state else DEFAULT_CARD_WIDTH,
242
+ )
243
+ align_modal, align_trigger = render_card_stack_settings_modal(
244
+ ALIGN_CS_CONFIG, ALIGN_CS_IDS,
245
+ current_count=align_state.get("visible_count", 5) if align_state else 5,
246
+ card_width=align_state.get("card_width", 40) if align_state else 40,
247
+ )
248
+
249
+ settings_modals_container = Div(
250
+ seg_modal,
251
+ align_modal,
252
+ id=CombinedHtmlIds.SETTINGS_MODALS,
253
+ )
254
+
238
255
  # --- Toolbar ---
239
256
  if is_init and urls:
240
- segments = seg_state.get("segments", [])
241
- history = seg_state.get("history", [])
242
- visible_count = seg_state.get("visible_count", DEFAULT_VISIBLE_COUNT)
243
- toolbar_content = render_toolbar(
244
- reset_url=urls.reset,
245
- ai_split_url=urls.ai_split,
246
- undo_url=urls.undo,
247
- can_undo=(len(history) > 0),
248
- visible_count=visible_count,
249
- extra_actions=extra_actions,
250
- nltk_split_disabled=nltk_split_disabled,
257
+ toolbar_content = Div(
258
+ seg_trigger,
259
+ render_toolbar(
260
+ reset_url=urls.reset,
261
+ ai_split_url=urls.ai_split,
262
+ undo_url=urls.undo,
263
+ can_undo=(len(seg_state.get("history", [])) > 0),
264
+ extra_actions=extra_actions,
265
+ nltk_split_disabled=nltk_split_disabled,
266
+ ),
267
+ cls=combine_classes(flex_display, items.center, gap(2), w.full),
251
268
  )
252
269
  else:
253
270
  toolbar_content = _placeholder("Toolbar actions will appear here based on the active column.")
@@ -258,22 +275,7 @@ def _render_shared_chrome(
258
275
  cls=str(p(2))
259
276
  )
260
277
 
261
- # --- Controls ---
262
- if is_init:
263
- card_width = seg_state.get("card_width", DEFAULT_CARD_WIDTH)
264
- controls_content = render_width_slider(SEG_CS_CONFIG, SEG_CS_IDS, card_width=card_width)
265
- else:
266
- controls_content = _placeholder("Column-specific controls will appear here.")
267
-
268
- controls = Div(
269
- controls_content,
270
- id=CombinedHtmlIds.SHARED_CONTROLS,
271
- cls=str(p(2))
272
- )
273
-
274
278
  # --- Footer with both column footers + alignment status ---
275
- # Note: seg_state["segments"] and align_state["vad_chunks"] are already deserialized
276
- # objects from extract_seg_state/extract_alignment_state — don't call from_dict() again
277
279
  seg_segments = seg_state.get("segments", []) if seg_state else []
278
280
  segment_count = len(seg_segments)
279
281
  align_chunks = align_state.get("vad_chunks", []) if align_state else []
@@ -306,8 +308,7 @@ def _render_shared_chrome(
306
308
  )
307
309
  )
308
310
 
309
- return toolbar, controls, footer
310
-
311
+ return toolbar, footer, settings_modals_container
311
312
 
312
313
  # %% ../../nbs/components/step_renderer.ipynb #c9d0e1f2
313
314
  # Shared column styling (reused by init handler for outerHTML swap)
@@ -528,7 +529,7 @@ def render_combined_step(
528
529
  )
529
530
  mini_stats_text = render_seg_mini_stats_text(segments)
530
531
 
531
- toolbar, controls, footer = _render_shared_chrome(
532
+ toolbar, footer, settings_modals_container = _render_shared_chrome(
532
533
  seg_state=seg_state,
533
534
  align_state=align_state,
534
535
  urls=seg_urls,
@@ -542,7 +543,7 @@ def render_combined_step(
542
543
  mini_stats_text=mini_stats_text,
543
544
  )
544
545
  else:
545
- toolbar, controls, footer = _render_shared_chrome(
546
+ toolbar, footer, settings_modals_container = _render_shared_chrome(
546
547
  align_state=align_state,
547
548
  )
548
549
  seg_col = _render_seg_column(
@@ -630,7 +631,6 @@ def render_combined_step(
630
631
  cls=combine_classes(flex_display, items.start, justify.between),
631
632
  ),
632
633
  toolbar,
633
- controls,
634
634
  Div(
635
635
  *columns_children,
636
636
  id=CombinedHtmlIds.COLUMNS,
@@ -649,6 +649,7 @@ def render_combined_step(
649
649
  toolbar_restore_script,
650
650
  chrome_switch_btn,
651
651
  active_column_input,
652
+ settings_modals_container, # Both settings modals persist in DOM
652
653
  jm_modal_el, # Job monitor modal (page-level, outside columns)
653
654
  jm_kb_script_el, # Job monitor keyboard script placeholder (for OOB pause/resume)
654
655
  hints_modal, # Keyboard hints modal dialog
@@ -660,4 +661,3 @@ def render_combined_step(
660
661
  p(4), p.x(2), p.b(0)
661
662
  )
662
663
  )
663
-
@@ -21,9 +21,9 @@ class CombinedHtmlIds:
21
21
  # Shared Chrome
22
22
  SHARED_HINTS = "sd-shared-hints"
23
23
  SHARED_TOOLBAR = "sd-shared-toolbar"
24
- SHARED_CONTROLS = "sd-shared-controls"
25
24
  SHARED_FOOTER = "sd-shared-footer"
26
25
  ALIGNMENT_STATUS = "sd-alignment-status"
26
+ SETTINGS_MODALS = "sd-settings-modals"
27
27
 
28
28
  # Segmentation Column
29
29
  SEG_COLUMN = "sd-seg-column"
@@ -10,7 +10,7 @@ from typing import Tuple, Dict, Callable
10
10
 
11
11
  from fasthtml.common import APIRouter, Div
12
12
 
13
- from cjm_fasthtml_card_stack.components.controls import render_width_slider
13
+ from cjm_fasthtml_card_stack.components.settings_modal import render_settings_trigger
14
14
  from cjm_fasthtml_card_stack.core.constants import DEFAULT_VISIBLE_COUNT, DEFAULT_CARD_WIDTH
15
15
 
16
16
  from cjm_fasthtml_interactions.core.state_store import get_session_id
@@ -38,6 +38,12 @@ from cjm_transcript_vad_align.components.card_stack_config import (
38
38
  ALIGN_CS_CONFIG, ALIGN_CS_IDS,
39
39
  )
40
40
 
41
+ from cjm_fasthtml_tailwind.utilities.sizing import w
42
+ from cjm_fasthtml_tailwind.utilities.flexbox_and_grid import (
43
+ flex_display, items, gap,
44
+ )
45
+ from cjm_fasthtml_tailwind.core.base import combine_classes
46
+
41
47
  # Footer helper + FA extra_actions builder + match detection
42
48
  from cjm_transcript_segment_align.components.step_renderer import (
43
49
  render_footer_inner_content,
@@ -66,6 +72,7 @@ async def _handle_switch_chrome(
66
72
 
67
73
  Client-side toolbar state restoration (sync button, auto-play toggle)
68
74
  is handled by the centralized settle handler from toolbar_state.py.
75
+ Settings modals persist in the DOM — only the trigger buttons swap.
69
76
  """
70
77
  form = await request.form()
71
78
  active_column = form.get("active_column", "seg")
@@ -84,13 +91,10 @@ async def _handle_switch_chrome(
84
91
  chunk_count = len(align_state.get("vad_chunks", []))
85
92
 
86
93
  if active_column == "seg":
87
- # Segmentation chrome (toolbar + controls)
94
+ # Segmentation chrome (settings trigger + toolbar)
88
95
  segments = [TextSegment.from_dict(s) for s in seg_state.get("segments", [])]
89
96
  history = seg_state.get("history", [])
90
97
  focused_index = seg_state.get("focused_index", 0)
91
- visible_count = seg_state.get("visible_count", DEFAULT_VISIBLE_COUNT)
92
- is_auto_mode = seg_state.get("is_auto_mode", False)
93
- card_width = seg_state.get("card_width", DEFAULT_CARD_WIDTH)
94
98
 
95
99
  # Build FA extra_actions and NLTK Split disabled state
96
100
  fa_extra = build_fa_extra_actions(
@@ -101,28 +105,27 @@ async def _handle_switch_chrome(
101
105
  seg_state.get("segments", []), nltk_presplit,
102
106
  )
103
107
 
104
- toolbar_content = render_seg_toolbar(
105
- reset_url=seg_urls.reset,
106
- ai_split_url=seg_urls.ai_split,
107
- undo_url=seg_urls.undo,
108
- can_undo=(len(history) > 0),
109
- visible_count=visible_count,
110
- is_auto_mode=is_auto_mode,
111
- extra_actions=build_extra_actions(fa_extra),
112
- nltk_split_disabled=nltk_disabled,
108
+ settings_trigger = render_settings_trigger(modal_id=SEG_CS_IDS.settings_modal)
109
+ toolbar_content = Div(
110
+ settings_trigger,
111
+ render_seg_toolbar(
112
+ reset_url=seg_urls.reset,
113
+ ai_split_url=seg_urls.ai_split,
114
+ undo_url=seg_urls.undo,
115
+ can_undo=(len(history) > 0),
116
+ extra_actions=build_extra_actions(fa_extra),
117
+ nltk_split_disabled=nltk_disabled,
118
+ ),
119
+ cls=combine_classes(flex_display, items.center, gap(2), w.full),
113
120
  )
114
- controls_content = render_width_slider(SEG_CS_CONFIG, SEG_CS_IDS, card_width=card_width)
115
121
  else:
116
- # Alignment chrome (toolbar with auto-play toggle + controls)
117
- visible_count = align_state.get("visible_count", 5)
118
- is_auto_mode = align_state.get("is_auto_mode", False)
119
- card_width = align_state.get("card_width", 40)
120
-
121
- toolbar_content = render_align_toolbar(
122
- visible_count=visible_count,
123
- is_auto_mode=is_auto_mode,
122
+ # Alignment chrome (settings trigger + auto-play toggle)
123
+ settings_trigger = render_settings_trigger(modal_id=ALIGN_CS_IDS.settings_modal)
124
+ toolbar_content = Div(
125
+ settings_trigger,
126
+ render_align_toolbar(),
127
+ cls=combine_classes(flex_display, items.center, gap(2), w.full),
124
128
  )
125
- controls_content = render_width_slider(ALIGN_CS_CONFIG, ALIGN_CS_IDS, card_width=card_width)
126
129
 
127
130
  # --- Footer: both column footers always present ---
128
131
  seg_segments = [TextSegment.from_dict(s) for s in seg_state.get("segments", [])]
@@ -136,24 +139,19 @@ async def _handle_switch_chrome(
136
139
  if DEBUG_SWITCH_CHROME:
137
140
  print(f"[SWITCH_CHROME] returning OOB swaps for {active_column}")
138
141
 
139
- # Return OOB swaps
142
+ # Return OOB swaps (toolbar + footer — settings modals persist in DOM)
140
143
  toolbar_oob = Div(
141
144
  toolbar_content,
142
145
  id=CombinedHtmlIds.SHARED_TOOLBAR,
143
146
  hx_swap_oob="innerHTML"
144
147
  )
145
- controls_oob = Div(
146
- controls_content,
147
- id=CombinedHtmlIds.SHARED_CONTROLS,
148
- hx_swap_oob="innerHTML"
149
- )
150
148
  footer_oob = Div(
151
149
  render_footer_inner_content(seg_footer, align_footer, segment_count, chunk_count),
152
150
  id=CombinedHtmlIds.SHARED_FOOTER,
153
151
  hx_swap_oob="innerHTML"
154
152
  )
155
153
 
156
- return (toolbar_oob, controls_oob, footer_oob)
154
+ return (toolbar_oob, footer_oob)
157
155
 
158
156
  # %% ../../nbs/routes/chrome.ipynb #g7b8c9d0
159
157
  def init_chrome_router(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cjm-transcript-segment-align
3
- Version: 0.0.10
3
+ Version: 0.0.11
4
4
  Summary: FastHTML dual-column text segmentation & VAD alignment UI for transcript decomposition workflows with forced alignment-based text splitting for aligning text segments with VAD chunks.
5
5
  Home-page: https://github.com/cj-mills/cjm-transcript-segment-align
6
6
  Author: Christian J. Mills
@@ -83,36 +83,36 @@ graph LR
83
83
  routes_init[routes.init<br/>routes/init]
84
84
  services_forced_alignment[services.forced_alignment<br/>forced_alignment]
85
85
 
86
- components_handlers --> html_ids
86
+ components_handlers --> components_sync_controls
87
87
  components_handlers --> components_keyboard_config
88
+ components_handlers --> html_ids
88
89
  components_handlers --> components_step_renderer
89
90
  components_handlers --> services_forced_alignment
90
- components_handlers --> components_sync_controls
91
91
  components_handlers --> routes_forced_alignment
92
92
  components_keyboard_config --> html_ids
93
93
  components_keyboard_config --> components_sync_controls
94
+ components_step_renderer --> components_keyboard_config
95
+ components_step_renderer --> components_toolbar_state
94
96
  components_step_renderer --> html_ids
95
97
  components_step_renderer --> components_helpers
96
98
  components_step_renderer --> components_sync_controls
97
- components_step_renderer --> components_keyboard_config
98
- components_step_renderer --> components_toolbar_state
99
99
  components_toolbar_state --> components_sync_controls
100
- routes_chrome --> html_ids
101
- routes_chrome --> components_handlers
102
100
  routes_chrome --> components_sync_controls
101
+ routes_chrome --> components_handlers
102
+ routes_chrome --> html_ids
103
103
  routes_chrome --> components_step_renderer
104
+ routes_forced_alignment --> components_sync_controls
104
105
  routes_forced_alignment --> html_ids
105
106
  routes_forced_alignment --> components_step_renderer
106
- routes_forced_alignment --> components_sync_controls
107
107
  routes_init --> components_keyboard_config
108
- routes_init --> html_ids
109
- routes_init --> components_handlers
110
108
  routes_init --> models
109
+ routes_init --> components_handlers
110
+ routes_init --> html_ids
111
111
  routes_init --> services_forced_alignment
112
- routes_init --> components_sync_controls
113
- routes_init --> components_step_renderer
114
- routes_init --> routes_forced_alignment
115
112
  routes_init --> routes_chrome
113
+ routes_init --> routes_forced_alignment
114
+ routes_init --> components_step_renderer
115
+ routes_init --> components_sync_controls
116
116
  ```
117
117
 
118
118
  *30 cross-module dependencies detected*
@@ -157,6 +157,7 @@ async def _handle_switch_chrome(
157
157
 
158
158
  Client-side toolbar state restoration (sync button, auto-play toggle)
159
159
  is handled by the centralized settle handler from toolbar_state.py.
160
+ Settings modals persist in the DOM — only the trigger buttons swap.
160
161
  """
161
162
  ```
162
163
 
@@ -479,9 +480,6 @@ def create_seg_mutation_wrapper(
479
480
  Calls the _result handler variant, builds targeted OOB response with FA
480
481
  controls in toolbar, and appends alignment status + mini-stats OOB.
481
482
  Computes nltk_split_disabled from state for toolbar rendering.
482
-
483
- When clear_fa_presplit=True (used for NLTK Split), clears the FA pre-split
484
- snapshot so the toggle is replaced with the Force Align button.
485
483
  """
486
484
  ```
487
485
 
@@ -509,6 +507,7 @@ def create_seg_init_chrome_wrapper(
509
507
 
510
508
  Saves nltk_presplit snapshot at init time for match detection.
511
509
  FA controls are rendered in the toolbar via extra_actions.
510
+ Settings modals are rendered in a persistent container (both seg + align).
512
511
  """
513
512
  ```
514
513
 
@@ -842,7 +841,7 @@ def _render_shared_chrome(
842
841
  urls:SegmentationUrls=None, # Segmentation URL bundle (required when seg_state provided)
843
842
  extra_actions:tuple=(), # Extra toolbar elements (FA controls, sync toggle, etc.)
844
843
  nltk_split_disabled:bool=False, # Whether NLTK Split button is disabled
845
- ) -> tuple: # (toolbar, controls, footer)
844
+ ) -> tuple: # (toolbar, footer, settings_modals_container)
846
845
  """
847
846
  Render shared chrome containers, populated with segmentation content when initialized.
848
847
 
@@ -1,7 +1,7 @@
1
1
  [DEFAULT]
2
2
  repo = cjm-transcript-segment-align
3
3
  lib_name = cjm-transcript-segment-align
4
- version = 0.0.10
4
+ version = 0.0.11
5
5
  min_python = 3.12
6
6
  license = apache2
7
7
  black_formatting = False
@@ -1 +0,0 @@
1
- __version__ = "0.0.10"