cjm-transcript-verify 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 @@
1
+ __version__ = "0.0.1"
@@ -0,0 +1,127 @@
1
+ # Autogenerated by nbdev
2
+
3
+ d = { 'settings': { 'branch': 'main',
4
+ 'doc_baseurl': '/cjm-transcript-verify',
5
+ 'doc_host': 'https://cj-mills.github.io',
6
+ 'git_url': 'https://github.com/cj-mills/cjm-transcript-verify',
7
+ 'lib_path': 'cjm_transcript_verify'},
8
+ 'syms': { 'cjm_transcript_verify.components.helpers': { 'cjm_transcript_verify.components.helpers._get_document_id': ( 'components/helpers.html#_get_document_id',
9
+ 'cjm_transcript_verify/components/helpers.py'),
10
+ 'cjm_transcript_verify.components.helpers._get_document_id_any': ( 'components/helpers.html#_get_document_id_any',
11
+ 'cjm_transcript_verify/components/helpers.py'),
12
+ 'cjm_transcript_verify.components.helpers._get_document_id_from_review': ( 'components/helpers.html#_get_document_id_from_review',
13
+ 'cjm_transcript_verify/components/helpers.py'),
14
+ 'cjm_transcript_verify.components.helpers._get_media_path': ( 'components/helpers.html#_get_media_path',
15
+ 'cjm_transcript_verify/components/helpers.py'),
16
+ 'cjm_transcript_verify.components.helpers._get_verify_state': ( 'components/helpers.html#_get_verify_state',
17
+ 'cjm_transcript_verify/components/helpers.py')},
18
+ 'cjm_transcript_verify.components.integrity_checks': { 'cjm_transcript_verify.components.integrity_checks._render_check_row': ( 'components/integrity_checks.html#_render_check_row',
19
+ 'cjm_transcript_verify/components/integrity_checks.py'),
20
+ 'cjm_transcript_verify.components.integrity_checks.render_integrity_checks': ( 'components/integrity_checks.html#render_integrity_checks',
21
+ 'cjm_transcript_verify/components/integrity_checks.py')},
22
+ 'cjm_transcript_verify.components.sample_segments': { 'cjm_transcript_verify.components.sample_segments.render_jump_result': ( 'components/sample_segments.html#render_jump_result',
23
+ 'cjm_transcript_verify/components/sample_segments.py'),
24
+ 'cjm_transcript_verify.components.sample_segments.render_jump_to_index': ( 'components/sample_segments.html#render_jump_to_index',
25
+ 'cjm_transcript_verify/components/sample_segments.py'),
26
+ 'cjm_transcript_verify.components.sample_segments.render_sample_list': ( 'components/sample_segments.html#render_sample_list',
27
+ 'cjm_transcript_verify/components/sample_segments.py'),
28
+ 'cjm_transcript_verify.components.sample_segments.render_sample_row': ( 'components/sample_segments.html#render_sample_row',
29
+ 'cjm_transcript_verify/components/sample_segments.py'),
30
+ 'cjm_transcript_verify.components.sample_segments.render_sample_segments': ( 'components/sample_segments.html#render_sample_segments',
31
+ 'cjm_transcript_verify/components/sample_segments.py')},
32
+ 'cjm_transcript_verify.components.step_renderer': { 'cjm_transcript_verify.components.step_renderer.render_verify_error': ( 'components/step_renderer.html#render_verify_error',
33
+ 'cjm_transcript_verify/components/step_renderer.py'),
34
+ 'cjm_transcript_verify.components.step_renderer.render_verify_header': ( 'components/step_renderer.html#render_verify_header',
35
+ 'cjm_transcript_verify/components/step_renderer.py'),
36
+ 'cjm_transcript_verify.components.step_renderer.render_verify_loading': ( 'components/step_renderer.html#render_verify_loading',
37
+ 'cjm_transcript_verify/components/step_renderer.py'),
38
+ 'cjm_transcript_verify.components.step_renderer.render_verify_step': ( 'components/step_renderer.html#render_verify_step',
39
+ 'cjm_transcript_verify/components/step_renderer.py')},
40
+ 'cjm_transcript_verify.components.verification_summary': { 'cjm_transcript_verify.components.verification_summary._render_section_header': ( 'components/verification_summary.html#_render_section_header',
41
+ 'cjm_transcript_verify/components/verification_summary.py'),
42
+ 'cjm_transcript_verify.components.verification_summary._render_stat_row': ( 'components/verification_summary.html#_render_stat_row',
43
+ 'cjm_transcript_verify/components/verification_summary.py'),
44
+ 'cjm_transcript_verify.components.verification_summary.render_document_section': ( 'components/verification_summary.html#render_document_section',
45
+ 'cjm_transcript_verify/components/verification_summary.py'),
46
+ 'cjm_transcript_verify.components.verification_summary.render_segments_section': ( 'components/verification_summary.html#render_segments_section',
47
+ 'cjm_transcript_verify/components/verification_summary.py'),
48
+ 'cjm_transcript_verify.components.verification_summary.render_sources_section': ( 'components/verification_summary.html#render_sources_section',
49
+ 'cjm_transcript_verify/components/verification_summary.py'),
50
+ 'cjm_transcript_verify.components.verification_summary.render_verification_summary': ( 'components/verification_summary.html#render_verification_summary',
51
+ 'cjm_transcript_verify/components/verification_summary.py')},
52
+ 'cjm_transcript_verify.html_ids': { 'cjm_transcript_verify.html_ids.VerifyHtmlIds': ( 'html_ids.html#verifyhtmlids',
53
+ 'cjm_transcript_verify/html_ids.py'),
54
+ 'cjm_transcript_verify.html_ids.VerifyHtmlIds.as_selector': ( 'html_ids.html#verifyhtmlids.as_selector',
55
+ 'cjm_transcript_verify/html_ids.py')},
56
+ 'cjm_transcript_verify.models': { 'cjm_transcript_verify.models.SegmentSample': ( 'models.html#segmentsample',
57
+ 'cjm_transcript_verify/models.py'),
58
+ 'cjm_transcript_verify.models.SegmentSample.duration': ( 'models.html#segmentsample.duration',
59
+ 'cjm_transcript_verify/models.py'),
60
+ 'cjm_transcript_verify.models.VerificationResult': ( 'models.html#verificationresult',
61
+ 'cjm_transcript_verify/models.py'),
62
+ 'cjm_transcript_verify.models.VerificationResult.all_checks_passed': ( 'models.html#verificationresult.all_checks_passed',
63
+ 'cjm_transcript_verify/models.py'),
64
+ 'cjm_transcript_verify.models.VerifyStepState': ( 'models.html#verifystepstate',
65
+ 'cjm_transcript_verify/models.py'),
66
+ 'cjm_transcript_verify.models.VerifyUrls': ( 'models.html#verifyurls',
67
+ 'cjm_transcript_verify/models.py')},
68
+ 'cjm_transcript_verify.routes.core': { 'cjm_transcript_verify.routes.core.VerifyContext': ( 'routes/core.html#verifycontext',
69
+ 'cjm_transcript_verify/routes/core.py'),
70
+ 'cjm_transcript_verify.routes.core._get_review_state': ( 'routes/core.html#_get_review_state',
71
+ 'cjm_transcript_verify/routes/core.py'),
72
+ 'cjm_transcript_verify.routes.core._get_verify_state': ( 'routes/core.html#_get_verify_state',
73
+ 'cjm_transcript_verify/routes/core.py'),
74
+ 'cjm_transcript_verify.routes.core._load_verify_context': ( 'routes/core.html#_load_verify_context',
75
+ 'cjm_transcript_verify/routes/core.py'),
76
+ 'cjm_transcript_verify.routes.core._update_verify_state': ( 'routes/core.html#_update_verify_state',
77
+ 'cjm_transcript_verify/routes/core.py')},
78
+ 'cjm_transcript_verify.routes.init': { 'cjm_transcript_verify.routes.init.init_verify_routers': ( 'routes/init.html#init_verify_routers',
79
+ 'cjm_transcript_verify/routes/init.py')},
80
+ 'cjm_transcript_verify.routes.sample': { 'cjm_transcript_verify.routes.sample.init_sample_router': ( 'routes/sample.html#init_sample_router',
81
+ 'cjm_transcript_verify/routes/sample.py')},
82
+ 'cjm_transcript_verify.routes.verify': { 'cjm_transcript_verify.routes.verify.init_verify_router': ( 'routes/verify.html#init_verify_router',
83
+ 'cjm_transcript_verify/routes/verify.py')},
84
+ 'cjm_transcript_verify.services.verify': { 'cjm_transcript_verify.services.verify.VerifyService': ( 'services/verify.html#verifyservice',
85
+ 'cjm_transcript_verify/services/verify.py'),
86
+ 'cjm_transcript_verify.services.verify.VerifyService.__init__': ( 'services/verify.html#verifyservice.__init__',
87
+ 'cjm_transcript_verify/services/verify.py'),
88
+ 'cjm_transcript_verify.services.verify.VerifyService._compute_source_stats': ( 'services/verify.html#verifyservice._compute_source_stats',
89
+ 'cjm_transcript_verify/services/verify.py'),
90
+ 'cjm_transcript_verify.services.verify.VerifyService._compute_timing_stats': ( 'services/verify.html#verifyservice._compute_timing_stats',
91
+ 'cjm_transcript_verify/services/verify.py'),
92
+ 'cjm_transcript_verify.services.verify.VerifyService._count_edges_by_type': ( 'services/verify.html#verifyservice._count_edges_by_type',
93
+ 'cjm_transcript_verify/services/verify.py'),
94
+ 'cjm_transcript_verify.services.verify.VerifyService._extract_samples': ( 'services/verify.html#verifyservice._extract_samples',
95
+ 'cjm_transcript_verify/services/verify.py'),
96
+ 'cjm_transcript_verify.services.verify.VerifyService._find_document_node': ( 'services/verify.html#verifyservice._find_document_node',
97
+ 'cjm_transcript_verify/services/verify.py'),
98
+ 'cjm_transcript_verify.services.verify.VerifyService._get_context': ( 'services/verify.html#verifyservice._get_context',
99
+ 'cjm_transcript_verify/services/verify.py'),
100
+ 'cjm_transcript_verify.services.verify.VerifyService._get_context_async': ( 'services/verify.html#verifyservice._get_context_async',
101
+ 'cjm_transcript_verify/services/verify.py'),
102
+ 'cjm_transcript_verify.services.verify.VerifyService._get_segment_nodes': ( 'services/verify.html#verifyservice._get_segment_nodes',
103
+ 'cjm_transcript_verify/services/verify.py'),
104
+ 'cjm_transcript_verify.services.verify.VerifyService._node_to_sample': ( 'services/verify.html#verifyservice._node_to_sample',
105
+ 'cjm_transcript_verify/services/verify.py'),
106
+ 'cjm_transcript_verify.services.verify.VerifyService.get_segment_by_index': ( 'services/verify.html#verifyservice.get_segment_by_index',
107
+ 'cjm_transcript_verify/services/verify.py'),
108
+ 'cjm_transcript_verify.services.verify.VerifyService.get_segment_by_index_async': ( 'services/verify.html#verifyservice.get_segment_by_index_async',
109
+ 'cjm_transcript_verify/services/verify.py'),
110
+ 'cjm_transcript_verify.services.verify.VerifyService.get_segment_count': ( 'services/verify.html#verifyservice.get_segment_count',
111
+ 'cjm_transcript_verify/services/verify.py'),
112
+ 'cjm_transcript_verify.services.verify.VerifyService.get_segment_count_async': ( 'services/verify.html#verifyservice.get_segment_count_async',
113
+ 'cjm_transcript_verify/services/verify.py'),
114
+ 'cjm_transcript_verify.services.verify.VerifyService.is_available': ( 'services/verify.html#verifyservice.is_available',
115
+ 'cjm_transcript_verify/services/verify.py'),
116
+ 'cjm_transcript_verify.services.verify.VerifyService.verify_document': ( 'services/verify.html#verifyservice.verify_document',
117
+ 'cjm_transcript_verify/services/verify.py'),
118
+ 'cjm_transcript_verify.services.verify.VerifyService.verify_document_async': ( 'services/verify.html#verifyservice.verify_document_async',
119
+ 'cjm_transcript_verify/services/verify.py')},
120
+ 'cjm_transcript_verify.utils': { 'cjm_transcript_verify.utils.format_duration_mmss': ( 'utils.html#format_duration_mmss',
121
+ 'cjm_transcript_verify/utils.py'),
122
+ 'cjm_transcript_verify.utils.format_duration_seconds': ( 'utils.html#format_duration_seconds',
123
+ 'cjm_transcript_verify/utils.py'),
124
+ 'cjm_transcript_verify.utils.format_time_range': ( 'utils.html#format_time_range',
125
+ 'cjm_transcript_verify/utils.py'),
126
+ 'cjm_transcript_verify.utils.truncate_text': ( 'utils.html#truncate_text',
127
+ 'cjm_transcript_verify/utils.py')}}}
File without changes
@@ -0,0 +1,53 @@
1
+ """State getters for the verify step from InteractionContext"""
2
+
3
+ # AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/components/helpers.ipynb.
4
+
5
+ # %% auto #0
6
+ __all__ = []
7
+
8
+ # %% ../../nbs/components/helpers.ipynb #verify-helpers-exports
9
+ from typing import Optional
10
+
11
+ from cjm_fasthtml_interactions.core.context import InteractionContext
12
+
13
+ from ..models import VerifyStepState
14
+
15
+ # %% ../../nbs/components/helpers.ipynb #verify-helpers-getters
16
+ def _get_verify_state(
17
+ ctx:InteractionContext # Interaction context with state
18
+ ) -> VerifyStepState: # Typed verify step state
19
+ """Get the full verify step state from context."""
20
+ step_states = ctx.state.get("step_states", {})
21
+ return step_states.get("verify", {})
22
+
23
+ def _get_document_id(
24
+ ctx:InteractionContext # Interaction context with state
25
+ ) -> Optional[str]: # Document UUID or None if not set
26
+ """Get the document ID to verify from context."""
27
+ state = _get_verify_state(ctx)
28
+ return state.get("document_id")
29
+
30
+ def _get_media_path(
31
+ ctx:InteractionContext # Interaction context with state
32
+ ) -> Optional[str]: # Media path or None if not set
33
+ """Get the media path for display context."""
34
+ state = _get_verify_state(ctx)
35
+ return state.get("media_path")
36
+
37
+ # %% ../../nbs/components/helpers.ipynb #verify-helpers-cross
38
+ def _get_document_id_from_review(
39
+ ctx:InteractionContext # Interaction context with state
40
+ ) -> Optional[str]: # Document UUID or None if not found
41
+ """Get the document ID from review step state (fallback)."""
42
+ step_states = ctx.state.get("step_states", {})
43
+ review_state = step_states.get("review", {})
44
+ return review_state.get("document_id")
45
+
46
+ def _get_document_id_any(
47
+ ctx:InteractionContext # Interaction context with state
48
+ ) -> Optional[str]: # Document UUID or None if not found anywhere
49
+ """Get document ID from verify state, falling back to review state."""
50
+ doc_id = _get_document_id(ctx)
51
+ if doc_id is None:
52
+ doc_id = _get_document_id_from_review(ctx)
53
+ return doc_id
@@ -0,0 +1,117 @@
1
+ """Integrity check display with pass/fail indicators"""
2
+
3
+ # AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/components/integrity_checks.ipynb.
4
+
5
+ # %% auto #0
6
+ __all__ = ['render_integrity_checks']
7
+
8
+ # %% ../../nbs/components/integrity_checks.ipynb #verify-integrity-exports
9
+ from typing import Any
10
+
11
+ from fasthtml.common import Div, Span, H3
12
+
13
+ # DaisyUI components
14
+ from cjm_fasthtml_daisyui.components.data_display.card import card, card_body
15
+ from cjm_fasthtml_daisyui.utilities.semantic_colors import bg_dui, text_dui
16
+
17
+ # Tailwind utilities
18
+ from cjm_fasthtml_tailwind.utilities.spacing import p, m
19
+ from cjm_fasthtml_tailwind.utilities.typography import font_size, font_weight
20
+ from cjm_fasthtml_tailwind.utilities.flexbox_and_grid import (
21
+ flex_display, flex_direction, items, gap
22
+ )
23
+ from cjm_fasthtml_tailwind.core.base import combine_classes
24
+
25
+ # Lucide icons via factory
26
+ from cjm_fasthtml_lucide_icons.factory import lucide_icon
27
+
28
+ # Local imports
29
+ from ..html_ids import VerifyHtmlIds
30
+ from ..models import VerificationResult
31
+
32
+ # %% ../../nbs/components/integrity_checks.ipynb #verify-integrity-helpers
33
+ def _render_check_row(
34
+ passed:bool, # Whether the check passed
35
+ label:str, # Check description
36
+ detail:str="", # Optional detail (e.g., "246/246")
37
+ ) -> Any: # Check row element
38
+ """Render an integrity check row with pass/fail icon."""
39
+ if passed:
40
+ icon = lucide_icon("circle-check", size=5, cls=str(text_dui.success))
41
+ else:
42
+ icon = lucide_icon("triangle-alert", size=5, cls=str(text_dui.warning))
43
+
44
+ detail_span = Span(
45
+ detail,
46
+ cls=combine_classes(text_dui.base_content.opacity(60), font_size.sm)
47
+ ) if detail else None
48
+
49
+ return Div(
50
+ icon,
51
+ Span(label, cls=str(font_size.sm)),
52
+ detail_span,
53
+ cls=combine_classes(flex_display, items.center, gap(2), p.y(1))
54
+ )
55
+
56
+ # %% ../../nbs/components/integrity_checks.ipynb #verify-integrity-section
57
+ def render_integrity_checks(
58
+ result:VerificationResult, # Verification result with integrity data
59
+ ) -> Any: # Integrity checks card
60
+ """Render the structure integrity checks section."""
61
+ # Compute expected counts for display
62
+ expected_next = max(0, result.segment_count - 1)
63
+
64
+ header = Div(
65
+ lucide_icon("shield-check", size=5),
66
+ H3("Structure Integrity", cls=combine_classes(font_size.lg, font_weight.semibold)),
67
+ cls=combine_classes(flex_display, items.center, gap(2), m.b(3))
68
+ )
69
+
70
+ checks = Div(
71
+ # STARTS_WITH check
72
+ _render_check_row(
73
+ passed=result.has_starts_with and result.starts_with_count == 1,
74
+ label="STARTS_WITH edge",
75
+ detail=str(result.starts_with_count)
76
+ ),
77
+
78
+ # NEXT chain check
79
+ _render_check_row(
80
+ passed=result.next_chain_complete,
81
+ label="NEXT chain",
82
+ detail=f"{result.next_count}/{expected_next}"
83
+ ),
84
+
85
+ # PART_OF check
86
+ _render_check_row(
87
+ passed=result.part_of_complete,
88
+ label="PART_OF edges",
89
+ detail=f"{result.part_of_count}/{result.segment_count}"
90
+ ),
91
+
92
+ # Timing check
93
+ _render_check_row(
94
+ passed=result.all_have_timing,
95
+ label="All segments have timing",
96
+ detail="" if result.all_have_timing else f"{result.segments_missing_timing} missing"
97
+ ),
98
+
99
+ # Sources check
100
+ _render_check_row(
101
+ passed=result.all_have_sources,
102
+ label="All segments have sources",
103
+ detail="" if result.all_have_sources else f"{result.segments_missing_sources} missing"
104
+ ),
105
+
106
+ cls=combine_classes(flex_display, flex_direction.col)
107
+ )
108
+
109
+ return Div(
110
+ Div(
111
+ header,
112
+ checks,
113
+ cls=str(card_body)
114
+ ),
115
+ id=VerifyHtmlIds.INTEGRITY_SECTION,
116
+ cls=combine_classes(card, bg_dui.base_100, "shadow-sm")
117
+ )
@@ -0,0 +1,207 @@
1
+ """Sample segment display with first/last segments and jump-to-index"""
2
+
3
+ # AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/components/sample_segments.ipynb.
4
+
5
+ # %% auto #0
6
+ __all__ = ['render_sample_row', 'render_sample_list', 'render_jump_to_index', 'render_jump_result', 'render_sample_segments']
7
+
8
+ # %% ../../nbs/components/sample_segments.ipynb #verify-samples-exports
9
+ from typing import Any, List, Optional
10
+
11
+ from fasthtml.common import Div, Span, H3, Input, Button, Form
12
+
13
+ # DaisyUI components
14
+ from cjm_fasthtml_daisyui.components.data_display.card import card, card_body
15
+ from cjm_fasthtml_daisyui.components.data_input.text_input import text_input, text_input_sizes
16
+ from cjm_fasthtml_daisyui.components.actions.button import btn, btn_colors, btn_sizes
17
+ from cjm_fasthtml_daisyui.components.feedback.loading import loading, loading_styles, loading_sizes
18
+ from cjm_fasthtml_daisyui.utilities.semantic_colors import bg_dui, text_dui
19
+
20
+ # Tailwind utilities
21
+ from cjm_fasthtml_tailwind.utilities.spacing import p, m
22
+ from cjm_fasthtml_tailwind.utilities.sizing import w
23
+ from cjm_fasthtml_tailwind.utilities.typography import font_size, font_weight, font_family
24
+ from cjm_fasthtml_tailwind.utilities.flexbox_and_grid import (
25
+ flex_display, flex_direction, items, gap
26
+ )
27
+ from cjm_fasthtml_tailwind.core.base import combine_classes
28
+
29
+ # Lucide icons via factory
30
+ from cjm_fasthtml_lucide_icons.factory import lucide_icon
31
+
32
+ # Local imports
33
+ from ..html_ids import VerifyHtmlIds
34
+ from ..models import VerificationResult, SegmentSample, VerifyUrls
35
+ from ..utils import format_time_range
36
+
37
+ # %% ../../nbs/components/sample_segments.ipynb #verify-samples-row
38
+ def render_sample_row(
39
+ sample:SegmentSample, # Segment sample to display
40
+ ) -> Any: # Sample row element
41
+ """Render a single segment sample row."""
42
+ timing = format_time_range(sample.start_time, sample.end_time)
43
+
44
+ return Div(
45
+ # Index badge
46
+ Span(
47
+ f"[{sample.index}]",
48
+ cls=combine_classes(font_family.mono, font_size.sm, text_dui.primary)
49
+ ),
50
+ # Text (truncated)
51
+ Span(
52
+ f'"{sample.text}"',
53
+ cls=combine_classes(font_size.sm, "truncate", "flex-1")
54
+ ),
55
+ # Timing
56
+ Span(
57
+ f"({timing})",
58
+ cls=combine_classes(font_size.xs, text_dui.base_content.opacity(60), font_family.mono)
59
+ ),
60
+ cls=combine_classes(flex_display, items.center, gap(2), p.y(1))
61
+ )
62
+
63
+ # %% ../../nbs/components/sample_segments.ipynb #verify-samples-list
64
+ def render_sample_list(
65
+ samples:List[SegmentSample], # Samples to display
66
+ label:str, # Label for this list (e.g., "First", "Last")
67
+ container_id:str="", # Optional container ID
68
+ ) -> Any: # Sample list element
69
+ """Render a list of segment samples with label."""
70
+ if not samples:
71
+ return Div(
72
+ Span(f"{label}:", cls=combine_classes(font_weight.medium, font_size.sm)),
73
+ Span("No samples", cls=combine_classes(text_dui.base_content.opacity(60), font_size.sm)),
74
+ id=container_id if container_id else None,
75
+ cls=combine_classes(flex_display, flex_direction.col, gap(1))
76
+ )
77
+
78
+ return Div(
79
+ Span(f"{label}:", cls=combine_classes(font_weight.medium, font_size.sm, m.b(1))),
80
+ *[render_sample_row(s) for s in samples],
81
+ id=container_id if container_id else None,
82
+ cls=combine_classes(flex_display, flex_direction.col)
83
+ )
84
+
85
+ # %% ../../nbs/components/sample_segments.ipynb #verify-samples-jump
86
+ def render_jump_to_index(
87
+ urls:VerifyUrls=None, # URL bundle for routes
88
+ max_index:int=0, # Maximum valid index for placeholder
89
+ ) -> Any: # Jump-to-index form with loading indicator
90
+ """Render the jump-to-index input form with loading state."""
91
+ urls = urls or VerifyUrls()
92
+
93
+ # Build form attributes
94
+ form_attrs = {}
95
+ if urls.sample:
96
+ form_attrs.update({
97
+ "hx_post": urls.sample,
98
+ "hx_target": f"#{VerifyHtmlIds.JUMP_RESULT}",
99
+ "hx_swap": "innerHTML",
100
+ "hx_indicator": f"#{VerifyHtmlIds.JUMP_LOADING}",
101
+ })
102
+
103
+ placeholder = f"0-{max_index}" if max_index > 0 else "index"
104
+
105
+ # Loading indicator (hidden by default, shown during HTMX request)
106
+ loading_indicator = Span(
107
+ cls=combine_classes(loading, loading_styles.spinner, loading_sizes.sm, "htmx-indicator"),
108
+ id=VerifyHtmlIds.JUMP_LOADING
109
+ )
110
+
111
+ return Div(
112
+ Span("Jump to index:", cls=combine_classes(font_size.sm, font_weight.medium)),
113
+ Form(
114
+ Input(
115
+ type="number",
116
+ name="index",
117
+ min="0",
118
+ max=str(max_index) if max_index > 0 else None,
119
+ placeholder=placeholder,
120
+ id=VerifyHtmlIds.JUMP_INPUT,
121
+ cls=combine_classes(text_input, text_input_sizes.sm, w(20), font_family.mono)
122
+ ),
123
+ Button(
124
+ lucide_icon("arrow-right", size=4),
125
+ type="submit",
126
+ id=VerifyHtmlIds.JUMP_BUTTON,
127
+ cls=combine_classes(btn, btn_colors.primary, btn_sizes.sm)
128
+ ),
129
+ loading_indicator,
130
+ method="post",
131
+ cls=combine_classes(flex_display, items.center, gap(2)),
132
+ **form_attrs
133
+ ),
134
+ cls=combine_classes(flex_display, items.center, gap(3), m.t(3), p.t(3), "border-t", "border-base-300")
135
+ )
136
+
137
+ # %% ../../nbs/components/sample_segments.ipynb #verify-samples-result
138
+ def render_jump_result(
139
+ sample:Optional[SegmentSample]=None, # Fetched sample or None
140
+ error:str="", # Error message if any
141
+ ) -> Any: # Jump result display
142
+ """Render the jump-to-index result (single segment or error)."""
143
+ if error:
144
+ return Div(
145
+ lucide_icon("circle-alert", size=4, cls=str(text_dui.error)),
146
+ Span(error, cls=combine_classes(font_size.sm, text_dui.error)),
147
+ id=VerifyHtmlIds.JUMP_RESULT,
148
+ cls=combine_classes(flex_display, items.center, gap(2), m.t(2))
149
+ )
150
+
151
+ if sample:
152
+ return Div(
153
+ render_sample_row(sample),
154
+ id=VerifyHtmlIds.JUMP_RESULT,
155
+ cls=str(m.t(2))
156
+ )
157
+
158
+ # Empty placeholder
159
+ return Div(id=VerifyHtmlIds.JUMP_RESULT)
160
+
161
+ # %% ../../nbs/components/sample_segments.ipynb #verify-samples-section
162
+ def render_sample_segments(
163
+ result:VerificationResult, # Verification result with samples
164
+ urls:VerifyUrls=None, # URL bundle for routes
165
+ ) -> Any: # Sample segments card
166
+ """Render the full sample segments section."""
167
+ urls = urls or VerifyUrls()
168
+ max_index = result.segment_count - 1 if result.segment_count > 0 else 0
169
+
170
+ header = Div(
171
+ lucide_icon("scan-search", size=5),
172
+ H3("Sample Segments", cls=combine_classes(font_size.lg, font_weight.semibold)),
173
+ cls=combine_classes(flex_display, items.center, gap(2), m.b(3))
174
+ )
175
+
176
+ return Div(
177
+ Div(
178
+ header,
179
+
180
+ # First samples
181
+ render_sample_list(
182
+ result.first_segments,
183
+ "First",
184
+ VerifyHtmlIds.FIRST_SAMPLES
185
+ ),
186
+
187
+ # Spacer
188
+ Div(cls=str(m.y(3))),
189
+
190
+ # Last samples
191
+ render_sample_list(
192
+ result.last_segments,
193
+ "Last",
194
+ VerifyHtmlIds.LAST_SAMPLES
195
+ ),
196
+
197
+ # Jump-to-index
198
+ render_jump_to_index(urls, max_index),
199
+
200
+ # Result container (empty initially)
201
+ render_jump_result(),
202
+
203
+ cls=str(card_body)
204
+ ),
205
+ id=VerifyHtmlIds.SAMPLES_SECTION,
206
+ cls=combine_classes(card, bg_dui.base_100, "shadow-sm")
207
+ )