@neobyzantine/series-player 0.2.0

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":3,"sources":["../src/index.ts","../src/SeriesPlayer.tsx","../src/useSeriesPlayer.ts","../src/SeriesStepList.tsx","../src/AnimatorControls.tsx","../src/AnimatedEventMap.tsx","../src/EventAnimator.ts","../src/SeriesEventPanel.tsx"],"sourcesContent":["export { SeriesPlayer } from './SeriesPlayer';\nexport type { SeriesPlayerProps } from './SeriesPlayer';\n\nexport { SeriesStepList } from './SeriesStepList';\nexport type { SeriesStepListProps } from './SeriesStepList';\n\nexport { SeriesEventPanel } from './SeriesEventPanel';\nexport type { SeriesEventPanelProps } from './SeriesEventPanel';\n\nexport { AnimatorControls } from './AnimatorControls';\nexport type { AnimatorControlsProps } from './AnimatorControls';\n\nexport { AnimatedEventMap } from './AnimatedEventMap';\nexport type { AnimatedEventMapProps } from './AnimatedEventMap';\n\nexport { EventAnimator } from './EventAnimator';\nexport type { EventAnimatorOptions } from './EventAnimator';\n\nexport { useSeriesPlayer } from './useSeriesPlayer';\nexport type { SeriesPlayerState, SeriesPlayerActions } from './useSeriesPlayer';\n\nexport type {\n SeriesEntry,\n AnimationFeature,\n AnimationFeatureCollection,\n AnimationFeatureProperties,\n AnimatorEvents,\n AnimatorEventName,\n} from './types';\n","import React, { useCallback, useRef, useState } from 'react';\nimport type { HistoricalEvent } from '@neobyzantine/types';\nimport type { AnimatorEvents } from './types';\nimport { useSeriesPlayer } from './useSeriesPlayer';\nimport { SeriesStepList } from './SeriesStepList';\nimport { AnimatorControls } from './AnimatorControls';\nimport { AnimatedEventMap } from './AnimatedEventMap';\nimport { SeriesEventPanel } from './SeriesEventPanel';\nimport type { SeriesEntry } from './types';\nimport type { EventAnimator } from './EventAnimator';\n\nexport interface SeriesPlayerProps {\n entries: SeriesEntry[];\n onEventLoad: (slug: string) => Promise<HistoricalEvent>;\n apiBase?: string;\n /** Color for map animation pulses. Defaults to active entry color or gold. */\n accentColor?: string;\n ttsEnabled?: boolean;\n autoPlayInterval?: number;\n resolvePortrait?: (path: string) => string;\n sanitizeHtml?: (html: string) => string;\n onEventChange?: (index: number, event: HistoricalEvent) => void;\n height?: string;\n className?: string;\n}\n\nexport function SeriesPlayer({\n entries,\n onEventLoad,\n accentColor,\n ttsEnabled = false,\n autoPlayInterval = 12000,\n resolvePortrait,\n sanitizeHtml,\n onEventChange,\n height = '600px',\n className,\n}: SeriesPlayerProps) {\n const [animatorState, setAnimatorState] = useState<{\n playing: boolean;\n current: number;\n currentProps: import('./types').AnimationFeatureProperties | null;\n }>({ playing: false, current: -1, currentProps: null });\n\n const animatorRef = useRef<EventAnimator | null>(null);\n\n const [state, actions] = useSeriesPlayer(entries, onEventLoad, {\n onEventChange,\n autoPlayInterval,\n });\n\n const currentEntry = entries[state.currentIndex];\n const resolvedColor = accentColor ?? currentEntry?.color ?? '#CFB53B';\n\n const geojson = state.event?.locations\n ? {\n type: 'FeatureCollection' as const,\n features: state.event.locations.map((loc) => ({\n type: 'Feature' as const,\n geometry: { type: 'Point' as const, coordinates: [loc.lng, loc.lat] as [number, number] },\n properties: {\n sort_order: loc.sort_order,\n label: loc.label,\n narration: loc.narration_text,\n zoom: undefined,\n animation_pause_ms: undefined,\n },\n })),\n }\n : null;\n\n const handleAnimatorReady = useCallback((animator: EventAnimator) => {\n animatorRef.current = animator;\n setAnimatorState({ playing: false, current: -1, currentProps: null });\n }, []);\n\n const handleStep = useCallback((data: AnimatorEvents['step']) => {\n setAnimatorState((prev) => ({ ...prev, current: data.index, currentProps: data.props }));\n }, []);\n\n const handleComplete = useCallback(() => {\n setAnimatorState((prev) => ({ ...prev, playing: false }));\n }, []);\n\n const handlePlay = useCallback(() => {\n animatorRef.current?.play();\n setAnimatorState((prev) => ({ ...prev, playing: true }));\n }, []);\n\n const handlePause = useCallback(() => {\n animatorRef.current?.pause();\n setAnimatorState((prev) => ({ ...prev, playing: false }));\n }, []);\n\n const handleReset = useCallback(() => {\n animatorRef.current?.reset();\n setAnimatorState({ playing: false, current: -1, currentProps: null });\n }, []);\n\n const handleStepForward = useCallback(() => {\n animatorRef.current?.stepForward();\n }, []);\n\n const handleStepBack = useCallback(() => {\n animatorRef.current?.stepBack();\n }, []);\n\n return (\n <div\n className={`nb-sp-root${className ? ` ${className}` : ''}`}\n style={{ '--nb-sp-accent': resolvedColor } as React.CSSProperties}\n >\n {/* Left: step list */}\n <aside className=\"nb-sp-sidebar\">\n <SeriesStepList\n entries={entries}\n currentIndex={state.currentIndex}\n jumpHistory={state.jumpHistory}\n onSelect={actions.goTo}\n />\n </aside>\n\n {/* Center: map + controls */}\n <main className=\"nb-sp-main\" style={{ height }}>\n <AnimatedEventMap\n geojson={geojson}\n color={resolvedColor}\n ttsEnabled={ttsEnabled}\n onAnimatorReady={handleAnimatorReady}\n onStep={handleStep}\n onComplete={handleComplete}\n height=\"100%\"\n className=\"nb-sp-map\"\n />\n <AnimatorControls\n playing={animatorState.playing}\n current={animatorState.current}\n total={geojson?.features.length ?? 0}\n currentStep={animatorState.currentProps}\n accentColor={resolvedColor}\n onPlay={handlePlay}\n onPause={handlePause}\n onReset={handleReset}\n onStepForward={handleStepForward}\n onStepBack={handleStepBack}\n />\n <div className=\"nb-sp-nav-row\">\n <button\n type=\"button\"\n className=\"nb-sp-nav-btn\"\n onClick={actions.prev}\n disabled={state.currentIndex === 0 && state.jumpHistory.length === 0}\n >\n ← Previous\n </button>\n <span className=\"nb-sp-nav-label\">\n {state.currentIndex + 1} / {entries.length}\n </span>\n <button\n type=\"button\"\n className=\"nb-sp-nav-btn\"\n onClick={actions.next}\n disabled={state.currentIndex >= entries.length - 1}\n >\n Next →\n </button>\n </div>\n </main>\n\n {/* Right: event detail */}\n <aside className=\"nb-sp-detail\">\n <SeriesEventPanel\n event={state.event}\n loading={state.loading}\n error={state.error}\n accentColor={resolvedColor}\n resolvePortrait={resolvePortrait}\n sanitizeHtml={sanitizeHtml}\n onRelatedClick={actions.jumpToRelated}\n />\n </aside>\n </div>\n );\n}\n","import { useState, useCallback, useRef, useEffect } from 'react';\nimport type { HistoricalEvent } from '@neobyzantine/types';\nimport type { SeriesEntry } from './types';\n\nexport interface SeriesPlayerState {\n currentIndex: number;\n event: HistoricalEvent | null;\n loading: boolean;\n error: string | null;\n jumpHistory: number[];\n autoPlaying: boolean;\n}\n\nexport interface SeriesPlayerActions {\n next(): void;\n prev(): void;\n goTo(index: number): void;\n jumpToRelated(slug: string): void;\n backFromJump(): void;\n toggleAutoPlay(): void;\n}\n\nexport function useSeriesPlayer(\n entries: SeriesEntry[],\n onEventLoad: (slug: string) => Promise<HistoricalEvent>,\n opts?: {\n onEventChange?: (index: number, event: HistoricalEvent) => void;\n autoPlayInterval?: number;\n },\n): [SeriesPlayerState, SeriesPlayerActions] {\n const [currentIndex, setCurrentIndex] = useState(0);\n const [event, setEvent] = useState<HistoricalEvent | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [jumpHistory, setJumpHistory] = useState<number[]>([]);\n const [autoPlaying, setAutoPlaying] = useState(false);\n\n const autoTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const loadingSlugRef = useRef<string | null>(null);\n const onEventChangeRef = useRef(opts?.onEventChange);\n onEventChangeRef.current = opts?.onEventChange;\n const autoPlayIntervalRef = useRef(opts?.autoPlayInterval ?? 12000);\n autoPlayIntervalRef.current = opts?.autoPlayInterval ?? 12000;\n\n const clearAutoTimer = useCallback(() => {\n if (autoTimerRef.current) {\n clearTimeout(autoTimerRef.current);\n autoTimerRef.current = null;\n }\n }, []);\n\n const loadSlug = useCallback(\n async (slug: string, index: number) => {\n loadingSlugRef.current = slug;\n clearAutoTimer();\n setLoading(true);\n setError(null);\n try {\n const ev = await onEventLoad(slug);\n if (loadingSlugRef.current !== slug) return; // superseded\n setEvent(ev);\n if (index >= 0) onEventChangeRef.current?.(index, ev);\n } catch (err) {\n if (loadingSlugRef.current === slug) {\n setError(err instanceof Error ? err.message : 'Failed to load event');\n }\n } finally {\n if (loadingSlugRef.current === slug) setLoading(false);\n }\n },\n [onEventLoad, clearAutoTimer],\n );\n\n // Load initial event on mount\n useEffect(() => {\n const entry = entries[0];\n if (entry) void loadSlug(entry.slug, 0);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Load event when currentIndex changes (driven by nav actions)\n const loadAtIndex = useCallback(\n (index: number) => {\n const entry = entries[index];\n if (entry) void loadSlug(entry.slug, index);\n },\n [entries, loadSlug],\n );\n\n const next = useCallback(() => {\n setCurrentIndex((prev) => {\n if (prev >= entries.length - 1) {\n setAutoPlaying(false);\n clearAutoTimer();\n return prev;\n }\n const next = prev + 1;\n setJumpHistory([]);\n loadAtIndex(next);\n return next;\n });\n }, [entries.length, loadAtIndex, clearAutoTimer]);\n\n const prev = useCallback(() => {\n setJumpHistory((hist) => {\n if (hist.length > 0) {\n // back from jump\n const restored = hist[hist.length - 1];\n const newHist = hist.slice(0, -1);\n setCurrentIndex(restored);\n loadAtIndex(restored);\n return newHist;\n }\n setCurrentIndex((idx) => {\n if (idx <= 0) return idx;\n const newIdx = idx - 1;\n loadAtIndex(newIdx);\n return newIdx;\n });\n return hist;\n });\n }, [loadAtIndex]);\n\n const goTo = useCallback(\n (index: number) => {\n if (index < 0 || index >= entries.length) return;\n setJumpHistory([]);\n setAutoPlaying(false);\n clearAutoTimer();\n setCurrentIndex(index);\n loadAtIndex(index);\n },\n [entries.length, loadAtIndex, clearAutoTimer],\n );\n\n const jumpToRelated = useCallback(\n (slug: string) => {\n setJumpHistory((hist) => [...hist, currentIndex]);\n loadingSlugRef.current = slug;\n clearAutoTimer();\n setLoading(true);\n setError(null);\n onEventLoad(slug)\n .then((ev) => {\n if (loadingSlugRef.current !== slug) return;\n setEvent(ev);\n setLoading(false);\n })\n .catch((err) => {\n if (loadingSlugRef.current === slug) {\n setError(err instanceof Error ? err.message : 'Failed to load event');\n setLoading(false);\n }\n });\n },\n [currentIndex, onEventLoad, clearAutoTimer],\n );\n\n const backFromJump = useCallback(() => {\n setJumpHistory((hist) => {\n if (!hist.length) return hist;\n const restored = hist[hist.length - 1];\n const newHist = hist.slice(0, -1);\n setCurrentIndex(restored);\n loadAtIndex(restored);\n return newHist;\n });\n }, [loadAtIndex]);\n\n const toggleAutoPlay = useCallback(() => {\n setAutoPlaying((prev) => {\n if (prev) {\n clearAutoTimer();\n return false;\n }\n // Start auto-play timer\n autoTimerRef.current = setTimeout(() => {\n next();\n }, autoPlayIntervalRef.current);\n return true;\n });\n }, [clearAutoTimer, next]);\n\n return [\n { currentIndex, event, loading, error, jumpHistory, autoPlaying },\n { next, prev, goTo, jumpToRelated, backFromJump, toggleAutoPlay },\n ];\n}\n","import React from 'react';\nimport type { SeriesEntry } from './types';\n\nexport interface SeriesStepListProps {\n entries: SeriesEntry[];\n currentIndex: number;\n jumpHistory: number[];\n onSelect: (index: number) => void;\n}\n\nexport function SeriesStepList({ entries, currentIndex, jumpHistory, onSelect }: SeriesStepListProps) {\n const isJumped = jumpHistory.length > 0;\n\n return (\n <ol className=\"nb-sp-steps\">\n {entries.map((entry, i) => {\n const isActive = !isJumped && i === currentIndex;\n const isDone = !isJumped && i < currentIndex;\n return (\n <li\n key={entry.slug}\n className={`nb-sp-step${isActive ? ' nb-sp-step--active' : ''}${isDone ? ' nb-sp-step--done' : ''}`}\n >\n <button\n type=\"button\"\n className=\"nb-sp-step-btn\"\n onClick={() => onSelect(i)}\n aria-current={isActive ? 'step' : undefined}\n >\n <span className=\"nb-sp-step-num\">{i + 1}</span>\n <span className=\"nb-sp-step-body\">\n <span className=\"nb-sp-step-title\">{entry.title}</span>\n {entry.date_display && (\n <span className=\"nb-sp-step-date\">{entry.date_display}</span>\n )}\n </span>\n </button>\n </li>\n );\n })}\n </ol>\n );\n}\n","import React from 'react';\nimport type { AnimationFeatureProperties } from './types';\n\nexport interface AnimatorControlsProps {\n playing: boolean;\n current: number;\n total: number;\n currentStep: AnimationFeatureProperties | null;\n accentColor?: string;\n onPlay: () => void;\n onPause: () => void;\n onReset: () => void;\n onStepForward: () => void;\n onStepBack: () => void;\n}\n\nexport function AnimatorControls({\n playing,\n current,\n total,\n currentStep,\n accentColor = '#CFB53B',\n onPlay,\n onPause,\n onReset,\n onStepForward,\n onStepBack,\n}: AnimatorControlsProps) {\n if (total === 0) return null;\n\n return (\n <div className=\"nb-sp-anim-controls\">\n <div className=\"nb-sp-anim-btns\">\n <button\n type=\"button\"\n className=\"nb-sp-anim-btn\"\n onClick={onReset}\n title=\"Reset animation\"\n disabled={current < 0}\n >⏮</button>\n <button\n type=\"button\"\n className=\"nb-sp-anim-btn\"\n onClick={onStepBack}\n title=\"Step back\"\n disabled={current <= 0}\n >⏪</button>\n <button\n type=\"button\"\n className={`nb-sp-anim-btn nb-sp-anim-btn--play${playing ? ' nb-sp-anim-btn--active' : ''}`}\n onClick={playing ? onPause : onPlay}\n style={{ borderColor: `${accentColor}66`, color: accentColor }}\n >\n {playing ? '⏸' : '▶'}\n </button>\n <button\n type=\"button\"\n className=\"nb-sp-anim-btn\"\n onClick={onStepForward}\n title=\"Step forward\"\n disabled={current >= total - 1}\n >⏩</button>\n <span className=\"nb-sp-anim-counter\">\n {current < 0 ? `0 / ${total}` : `${current + 1} / ${total}`}\n </span>\n </div>\n\n {currentStep && (currentStep.label || currentStep.narration) && (\n <div className=\"nb-sp-anim-narration\">\n {currentStep.label && (\n <span className=\"nb-sp-anim-label\" style={{ color: accentColor }}>\n {currentStep.label}\n </span>\n )}\n {currentStep.narration && (\n <span className=\"nb-sp-anim-text\">{currentStep.narration}</span>\n )}\n </div>\n )}\n </div>\n );\n}\n","import React, { useEffect, useRef } from 'react';\nimport { MapContainer, TileLayer, useMap } from 'react-leaflet';\nimport type { Map as LMap } from 'leaflet';\nimport { EventAnimator } from './EventAnimator';\nimport type { AnimationFeatureCollection, AnimatorEvents } from './types';\n\nexport interface AnimatedEventMapProps {\n geojson: AnimationFeatureCollection | null;\n color?: string;\n stepMs?: number;\n ttsEnabled?: boolean;\n onAnimatorReady?: (animator: EventAnimator) => void;\n onStep?: (data: AnimatorEvents['step']) => void;\n onComplete?: () => void;\n center?: [number, number];\n zoom?: number;\n height?: string;\n className?: string;\n}\n\n// Zero-render inner component: lives inside MapContainer, bridges useMap() to EventAnimator\ninterface DriverProps {\n geojson: AnimationFeatureCollection;\n color: string;\n stepMs: number;\n ttsEnabled: boolean;\n onAnimatorReady?: (animator: EventAnimator) => void;\n onStep?: (data: AnimatorEvents['step']) => void;\n onComplete?: () => void;\n}\n\nfunction AnimatorDriver({ geojson, color, stepMs, ttsEnabled, onAnimatorReady, onStep, onComplete }: DriverProps) {\n const map = useMap() as LMap;\n const animatorRef = useRef<EventAnimator | null>(null);\n\n useEffect(() => {\n const animator = new EventAnimator(map, geojson, color, { stepMs, ttsEnabled });\n if (onStep) animator.on('step', onStep);\n if (onComplete) animator.on('complete', onComplete);\n animatorRef.current = animator;\n onAnimatorReady?.(animator);\n\n return () => {\n animator.reset();\n animatorRef.current = null;\n };\n // Rebuild animator when geojson or key visual settings change.\n // Stable refs used for callbacks — they don't need to be deps.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [geojson, color, stepMs, ttsEnabled]);\n\n return null;\n}\n\nexport function AnimatedEventMap({\n geojson,\n color = '#CFB53B',\n stepMs = 7500,\n ttsEnabled = false,\n onAnimatorReady,\n onStep,\n onComplete,\n center = [41.0, 29.0], // Constantinople\n zoom = 5,\n height = '400px',\n className,\n}: AnimatedEventMapProps) {\n return (\n <div className={`nb-sp-map-wrap${className ? ` ${className}` : ''}`} style={{ height }}>\n <MapContainer\n center={center}\n zoom={zoom}\n style={{ height: '100%', width: '100%' }}\n zoomControl\n scrollWheelZoom={false}\n >\n <TileLayer\n url=\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\"\n attribution='&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a>'\n maxZoom={18}\n />\n {geojson && geojson.features.length > 0 && (\n <AnimatorDriver\n geojson={geojson}\n color={color}\n stepMs={stepMs}\n ttsEnabled={ttsEnabled}\n onAnimatorReady={onAnimatorReady}\n onStep={onStep}\n onComplete={onComplete}\n />\n )}\n </MapContainer>\n </div>\n );\n}\n","import L from 'leaflet';\nimport type {\n AnimationFeature,\n AnimationFeatureCollection,\n AnimationFeatureProperties,\n AnimatorEvents,\n AnimatorEventName,\n} from './types';\n\ntype Listener<K extends AnimatorEventName> = (data: AnimatorEvents[K]) => void;\n\nexport interface EventAnimatorOptions {\n stepMs?: number;\n ttsEnabled?: boolean;\n}\n\n/**\n * Step-by-step playback of a GeoJSON feature collection on a Leaflet map.\n * Each Point feature represents one animation step: the map flies to it,\n * a pulsing marker is shown, and optional TTS narration is spoken.\n *\n * Pure TypeScript class — no React. Wire into React via AnimatorDriver (uses useMap()).\n */\nexport class EventAnimator {\n private readonly map: L.Map;\n private readonly color: string;\n private readonly stepMs: number;\n private ttsEnabled: boolean;\n\n current = -1;\n playing = false;\n\n private timer: ReturnType<typeof setTimeout> | null = null;\n private pulseMarker: L.Marker | null = null;\n private listeners = new Map<AnimatorEventName, Array<Listener<AnimatorEventName>>>();\n\n readonly steps: AnimationFeature[];\n\n constructor(\n map: L.Map,\n geojson: AnimationFeatureCollection,\n color: string,\n options?: EventAnimatorOptions,\n ) {\n this.map = map;\n this.color = color;\n this.stepMs = options?.stepMs ?? 7500;\n this.ttsEnabled = options?.ttsEnabled ?? false;\n\n this.steps = (geojson.features ?? [])\n .filter((f) => f.properties.sort_order != null && f.geometry?.type === 'Point')\n .sort((a, b) => a.properties.sort_order - b.properties.sort_order);\n }\n\n get total(): number { return this.steps.length; }\n\n on<K extends AnimatorEventName>(event: K, fn: Listener<K>): this {\n if (!this.listeners.has(event)) this.listeners.set(event, []);\n this.listeners.get(event)!.push(fn as Listener<AnimatorEventName>);\n return this;\n }\n\n private emit<K extends AnimatorEventName>(event: K, data?: AnimatorEvents[K]): void {\n this.listeners.get(event)?.forEach((fn) => (fn as Listener<K>)(data as AnimatorEvents[K]));\n }\n\n play(): void {\n if (this.playing || this.total === 0) return;\n this.playing = true;\n this.emit('play');\n if (this.current < 0) {\n this.goto(0);\n } else {\n this.schedule();\n }\n }\n\n pause(): void {\n if (!this.playing) return;\n this.playing = false;\n if (this.timer) { clearTimeout(this.timer); this.timer = null; }\n window.speechSynthesis?.cancel();\n this.emit('pause');\n }\n\n reset(): void {\n this.pause();\n this.current = -1;\n this.clearPulse();\n window.speechSynthesis?.cancel();\n this.emit('reset');\n }\n\n stepForward(): void {\n this.pause();\n if (this.current + 1 < this.total) this.goto(this.current + 1);\n }\n\n stepBack(): void {\n this.pause();\n if (this.current - 1 >= 0) this.goto(this.current - 1);\n }\n\n setTTS(enabled: boolean): void {\n this.ttsEnabled = enabled;\n if (!enabled) window.speechSynthesis?.cancel();\n }\n\n private goto(index: number): void {\n if (index < 0 || index >= this.total) return;\n this.current = index;\n\n const feat = this.steps[index];\n const props = feat.properties;\n const [lng, lat] = feat.geometry.coordinates;\n\n this.map.flyTo([lat, lng], props.zoom ?? 8, { duration: 1.4, easeLinearity: 0.4 });\n\n this.clearPulse();\n this.pulseMarker = L.marker([lat, lng], {\n icon: L.divIcon({\n className: '',\n html: `<div class=\"nb-anim-pulse\" style=\"--anim-color:${this.color}\"></div>`,\n iconSize: [48, 48],\n iconAnchor: [24, 24],\n }),\n zIndexOffset: 1000,\n interactive: false,\n }).addTo(this.map);\n\n if (this.ttsEnabled) this.speak(props.narration ?? props.label ?? '');\n\n this.emit('step', { index, total: this.total, props });\n\n if (this.playing) this.schedule();\n }\n\n private schedule(): void {\n const props = this.steps[this.current]?.properties;\n const delay = props?.animation_pause_ms ?? this.stepMs;\n this.timer = setTimeout(() => {\n if (this.current + 1 >= this.total) {\n this.playing = false;\n window.speechSynthesis?.cancel();\n this.emit('complete');\n } else {\n this.goto(this.current + 1);\n }\n }, delay);\n }\n\n private clearPulse(): void {\n if (this.pulseMarker) {\n this.map.removeLayer(this.pulseMarker);\n this.pulseMarker = null;\n }\n }\n\n private speak(text: string): void {\n const ss = window.speechSynthesis;\n if (!ss || !text) return;\n ss.cancel();\n setTimeout(() => {\n if (ss.paused) ss.resume();\n const u = new SpeechSynthesisUtterance(text);\n u.lang = 'en-GB';\n u.rate = 0.70;\n u.pitch = 0.95;\n u.volume = 1;\n ss.speak(u);\n }, 80);\n }\n}\n","import React from 'react';\nimport type { HistoricalEvent } from '@neobyzantine/types';\nimport { ActorGrid } from '@neobyzantine/actor';\n\nexport interface SeriesEventPanelProps {\n event: HistoricalEvent | null;\n loading?: boolean;\n error?: string | null;\n accentColor?: string;\n /** Convert storage path to URL — consumer knows the URL scheme. */\n resolvePortrait?: (path: string) => string;\n /** Sanitize HTML before dangerouslySetInnerHTML — consumer provides sanitizer. */\n sanitizeHtml?: (html: string) => string;\n onActorClick?: (actor: import('@neobyzantine/types').Actor) => void;\n onRelatedClick?: (slug: string) => void;\n className?: string;\n}\n\n/** Default strips all tags — safe fallback. Pass DOMPurify.sanitize for full HTML rendering. */\nfunction defaultSanitize(html: string) { return html.replace(/<[^>]*>/g, ''); }\n\nexport function SeriesEventPanel({\n event,\n loading,\n error,\n accentColor = '#CFB53B',\n resolvePortrait,\n sanitizeHtml = defaultSanitize,\n onActorClick,\n onRelatedClick,\n className,\n}: SeriesEventPanelProps) {\n if (loading) {\n return (\n <div className={`nb-sp-panel nb-sp-panel--loading${className ? ` ${className}` : ''}`}>\n <div className=\"nb-sp-panel-spinner\" />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className={`nb-sp-panel nb-sp-panel--error${className ? ` ${className}` : ''}`}>\n <p className=\"nb-sp-panel-error\">{error}</p>\n </div>\n );\n }\n\n if (!event) return null;\n\n const hasActors = (event.actors?.length ?? 0) > 0;\n const hasGallery = (event.gallery_images?.length ?? 0) > 0;\n const hasRelations = (event.relations?.length ?? 0) > 0;\n\n return (\n <article className={`nb-sp-panel${className ? ` ${className}` : ''}`}>\n <header className=\"nb-sp-panel-header\">\n <h2 className=\"nb-sp-panel-title\" style={{ color: accentColor }}>{event.title}</h2>\n {event.date_display && (\n <p className=\"nb-sp-panel-date\">{event.date_display}</p>\n )}\n </header>\n\n {event.description && (\n <div\n className=\"nb-sp-panel-body\"\n dangerouslySetInnerHTML={{ __html: sanitizeHtml(event.description) }}\n />\n )}\n\n {hasActors && (\n <section className=\"nb-sp-panel-section\">\n <h3 className=\"nb-sp-panel-section-title\">Key Figures</h3>\n <ActorGrid\n actors={event.actors!}\n resolvePortrait={resolvePortrait}\n onActorClick={onActorClick}\n columns={2}\n compact\n />\n </section>\n )}\n\n {hasGallery && (\n <section className=\"nb-sp-panel-section\">\n <h3 className=\"nb-sp-panel-section-title\">Gallery</h3>\n <div className=\"nb-sp-panel-gallery\">\n {event.gallery_images!.map((src, i) => (\n <img\n key={i}\n src={src}\n alt=\"\"\n className=\"nb-sp-panel-gallery-img\"\n loading=\"lazy\"\n />\n ))}\n </div>\n </section>\n )}\n\n {hasRelations && (\n <section className=\"nb-sp-panel-section\">\n <h3 className=\"nb-sp-panel-section-title\">Related Events</h3>\n <ul className=\"nb-sp-panel-relations\">\n {event.relations!.map((rel) => (\n <li key={rel.slug} className={`nb-sp-panel-rel nb-sp-panel-rel--${rel.type}`}>\n <button\n type=\"button\"\n className=\"nb-sp-panel-rel-btn\"\n onClick={() => onRelatedClick?.(rel.slug)}\n >\n {rel.title}\n {rel.date && <span className=\"nb-sp-panel-rel-date\"> ({rel.date})</span>}\n </button>\n </li>\n ))}\n </ul>\n </section>\n )}\n </article>\n );\n}\n"],"mappings":"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","names":["import_react","prev","next","import_jsx_runtime","import_react","L","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime"]}