@djangocfg/ui-tools 2.1.382 → 2.1.383
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.
- package/dist/DictationField-U25MEYAL.mjs +4 -0
- package/dist/{DictationField-2ZLQWLYV.mjs.map → DictationField-U25MEYAL.mjs.map} +1 -1
- package/dist/DictationField-XWR5VOID.cjs +13 -0
- package/dist/{DictationField-IPPJ54CU.cjs.map → DictationField-XWR5VOID.cjs.map} +1 -1
- package/dist/{chunk-KMSBGNVC.cjs → chunk-4PFW7MIJ.cjs} +4 -2
- package/dist/chunk-4PFW7MIJ.cjs.map +1 -0
- package/dist/{chunk-4LXG3NBV.mjs → chunk-C2YN6WEO.mjs} +3 -3
- package/dist/chunk-C2YN6WEO.mjs.map +1 -0
- package/dist/index.cjs +139 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -1
- package/dist/index.d.ts +68 -1
- package/dist/index.mjs +141 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -13
- package/src/tools/Chat/index.ts +15 -0
- package/dist/DictationField-2ZLQWLYV.mjs +0 -4
- package/dist/DictationField-IPPJ54CU.cjs +0 -13
- package/dist/chunk-4LXG3NBV.mjs.map +0 -1
- package/dist/chunk-KMSBGNVC.cjs.map +0 -1
- package/src/components/markdown/MarkdownMessage/MarkdownMessage.story.tsx +0 -771
- package/src/stories/index.ts +0 -63
- package/src/tools/AudioPlayer/AudioPlayer.story.tsx +0 -481
- package/src/tools/Chat/stories/01-basic.story.tsx +0 -64
- package/src/tools/Chat/stories/02-bubbles.story.tsx +0 -21
- package/src/tools/Chat/stories/03-tool-calls.story.tsx +0 -59
- package/src/tools/Chat/stories/04-personas.story.tsx +0 -78
- package/src/tools/Chat/stories/05-launcher.story.tsx +0 -321
- package/src/tools/Chat/stories/06-header.story.tsx +0 -147
- package/src/tools/Chat/stories/07-audio-actions.story.tsx +0 -112
- package/src/tools/Chat/stories/shared/Frame.tsx +0 -21
- package/src/tools/Chat/stories/shared/index.ts +0 -5
- package/src/tools/Chat/stories/shared/messages.ts +0 -39
- package/src/tools/Chat/stories/shared/personas.ts +0 -13
- package/src/tools/Chat/stories/shared/seeds.ts +0 -92
- package/src/tools/Chat/stories/shared/transports.ts +0 -36
- package/src/tools/CodeEditor/CodeEditor.story.tsx +0 -202
- package/src/tools/CronScheduler/CronScheduler.story.tsx +0 -300
- package/src/tools/Gallery/Gallery.story.tsx +0 -237
- package/src/tools/ImageViewer/ImageViewer.story.tsx +0 -85
- package/src/tools/JsonForm/JsonForm.story.tsx +0 -350
- package/src/tools/JsonTree/JsonTree.story.tsx +0 -141
- package/src/tools/LottiePlayer/LottiePlayer.story.tsx +0 -95
- package/src/tools/Map/Map.story.tsx +0 -458
- package/src/tools/MarkdownEditor/MarkdownEditor.story.tsx +0 -225
- package/src/tools/Mermaid/Mermaid.story.tsx +0 -251
- package/src/tools/OpenapiViewer/OpenapiViewer.story.tsx +0 -230
- package/src/tools/PrettyCode/PrettyCode.story.tsx +0 -304
- package/src/tools/SpeechRecognition/stories/01-basic.story.tsx +0 -32
- package/src/tools/SpeechRecognition/stories/02-dictation-field.story.tsx +0 -32
- package/src/tools/SpeechRecognition/stories/03-push-to-talk.story.tsx +0 -27
- package/src/tools/SpeechRecognition/stories/04-mic-meter.story.tsx +0 -35
- package/src/tools/SpeechRecognition/stories/05-custom-engine-http.story.tsx +0 -40
- package/src/tools/SpeechRecognition/stories/06-custom-engine-ws.story.tsx +0 -48
- package/src/tools/SpeechRecognition/stories/07-language-device.story.tsx +0 -57
- package/src/tools/SpeechRecognition/stories/08-errors-permissions.story.tsx +0 -25
- package/src/tools/SpeechRecognition/stories/09-chat-voice.story.tsx +0 -90
- package/src/tools/SpeechRecognition/stories/shared.tsx +0 -123
- package/src/tools/Tour/Tour.story.tsx +0 -279
- package/src/tools/Tree/Tree.story.tsx +0 -620
- package/src/tools/Uploader/Uploader.story.tsx +0 -415
- package/src/tools/VideoPlayer/VideoPlayer.story.tsx +0 -87
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"DictationField-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"DictationField-U25MEYAL.mjs"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunk4PFW7MIJ_cjs = require('./chunk-4PFW7MIJ.cjs');
|
|
4
|
+
require('./chunk-OLISEQHS.cjs');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Object.defineProperty(exports, "DictationField", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
get: function () { return chunk4PFW7MIJ_cjs.DictationField; }
|
|
11
|
+
});
|
|
12
|
+
//# sourceMappingURL=DictationField-XWR5VOID.cjs.map
|
|
13
|
+
//# sourceMappingURL=DictationField-XWR5VOID.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"DictationField-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"DictationField-XWR5VOID.cjs"}
|
|
@@ -831,5 +831,7 @@ function DictationField({
|
|
|
831
831
|
chunkOLISEQHS_cjs.__name(DictationField, "DictationField");
|
|
832
832
|
|
|
833
833
|
exports.DictationField = DictationField;
|
|
834
|
-
|
|
835
|
-
|
|
834
|
+
exports.useResolvedLanguage = useResolvedLanguage;
|
|
835
|
+
exports.useSpeechPrefs = useSpeechPrefs;
|
|
836
|
+
//# sourceMappingURL=chunk-4PFW7MIJ.cjs.map
|
|
837
|
+
//# sourceMappingURL=chunk-4PFW7MIJ.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tools/SpeechRecognition/components/DictationButton.tsx","../src/tools/SpeechRecognition/components/ErrorBanner.tsx","../src/tools/SpeechRecognition/components/MicMeter.tsx","../src/tools/SpeechRecognition/components/PushToTalkHint.tsx","../src/tools/SpeechRecognition/core/transcript.ts","../src/tools/SpeechRecognition/core/ids.ts","../src/tools/SpeechRecognition/core/logger.ts","../src/tools/SpeechRecognition/core/reducer.ts","../src/tools/SpeechRecognition/core/engine/index.ts","../src/tools/SpeechRecognition/core/engine/webspeech.ts","../src/tools/SpeechRecognition/store/prefsStore.ts","../src/tools/SpeechRecognition/hooks/useMicLevel.ts","../src/tools/SpeechRecognition/core/language.ts","../src/tools/SpeechRecognition/hooks/useResolvedLanguage.ts","../src/tools/SpeechRecognition/hooks/useSpeechRecognition.ts","../src/tools/SpeechRecognition/hooks/useDictation.ts","../src/tools/SpeechRecognition/hooks/usePushToTalk.ts","../src/tools/SpeechRecognition/widgets/DictationField.tsx"],"names":["jsxs","cn","jsx","Loader2","MicOff","Mic","__name","AlertTriangle","consola","create","persist","createJSONStorage","useState","useRef","useEffect","useLocaleOptional","useMemo","useReducer","useCallback"],"mappings":"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","file":"chunk-4PFW7MIJ.cjs","sourcesContent":["'use client';\n\nimport type * as React from 'react';\n\nimport { Loader2, Mic, MicOff } from 'lucide-react';\nimport type { CSSProperties, ReactNode } from 'react';\n\nimport { cn } from '@djangocfg/ui-core/lib';\n\nimport type { RecognitionStatus } from '../types';\n\nexport interface DictationButtonProps {\n status: RecognitionStatus;\n onClick: () => void;\n isSupported?: boolean;\n size?: 'sm' | 'md' | 'lg';\n className?: string;\n style?: CSSProperties;\n ariaLabel?: string;\n /** Override icon for the idle state. */\n idleIcon?: ReactNode;\n /** Override icon for the listening state. */\n listeningIcon?: ReactNode;\n /** Disable without unmounting. */\n disabled?: boolean;\n}\n\nconst SIZE_CLS: Record<NonNullable<DictationButtonProps['size']>, string> = {\n sm: 'h-8 w-8 [&_svg]:h-4 [&_svg]:w-4',\n md: 'h-10 w-10 [&_svg]:h-5 [&_svg]:w-5',\n lg: 'h-12 w-12 [&_svg]:h-6 [&_svg]:w-6',\n};\n\n/**\n * Round microphone button. Cycles icon by status; shows a soft pulse\n * ring when listening. ARIA-correct so screen readers announce\n * \"recording\" vs \"start dictation\".\n */\nexport function DictationButton({\n status,\n onClick,\n isSupported = true,\n size = 'md',\n className,\n style,\n ariaLabel,\n idleIcon,\n listeningIcon,\n disabled,\n}: DictationButtonProps): React.ReactElement {\n const listening = status === 'listening' || status === 'starting';\n const stopping = status === 'stopping';\n const off = !isSupported;\n\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled || off}\n aria-pressed={listening}\n aria-label={\n ariaLabel ?? (listening ? 'Stop dictation' : off ? 'Dictation not supported' : 'Start dictation')\n }\n className={cn(\n 'relative inline-flex items-center justify-center rounded-full transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n SIZE_CLS[size],\n listening\n ? 'bg-destructive text-destructive-foreground hover:bg-destructive/90'\n : 'bg-primary text-primary-foreground hover:bg-primary/90',\n className,\n )}\n style={style}\n >\n {listening && (\n <span\n aria-hidden\n className=\"absolute inset-0 rounded-full bg-destructive/40 animate-ping\"\n />\n )}\n {stopping ? (\n <Loader2 className=\"animate-spin\" />\n ) : off ? (\n listeningIcon ?? <MicOff />\n ) : listening ? (\n listeningIcon ?? <Mic />\n ) : (\n idleIcon ?? <Mic />\n )}\n </button>\n );\n}\n","'use client';\n\nimport type * as React from 'react';\n\nimport { AlertTriangle } from 'lucide-react';\n\nimport { cn } from '@djangocfg/ui-core/lib';\n\nimport type { RecognitionError } from '../types';\n\nconst FRIENDLY: Record<RecognitionError['code'], string> = {\n unsupported: 'Speech recognition isn\\'t available in this browser.',\n 'permission-denied': 'Microphone access was denied. Allow it in your browser settings to dictate.',\n 'no-microphone': 'No microphone found.',\n network: 'Network error talking to the speech service.',\n aborted: 'Recognition was interrupted.',\n 'no-speech': 'No speech was detected. Try again.',\n language: 'The requested language isn\\'t supported by the engine.',\n engine: 'The speech engine reported an error.',\n unknown: 'Something went wrong with the microphone.',\n};\n\nexport interface ErrorBannerProps {\n error: RecognitionError | null;\n className?: string;\n onDismiss?: () => void;\n}\n\nexport function ErrorBanner({ error, className, onDismiss }: ErrorBannerProps): React.ReactElement | null {\n if (!error) return null;\n return (\n <div\n role=\"alert\"\n className={cn(\n 'flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive',\n className,\n )}\n >\n <AlertTriangle className=\"mt-0.5 h-3.5 w-3.5 shrink-0\" />\n <div className=\"flex-1\">{FRIENDLY[error.code] ?? error.message}</div>\n {onDismiss && (\n <button\n type=\"button\"\n onClick={onDismiss}\n className=\"text-destructive hover:underline\"\n >\n Dismiss\n </button>\n )}\n </div>\n );\n}\n","'use client';\n\nimport type * as React from 'react';\n\nimport { cn } from '@djangocfg/ui-core/lib';\n\nexport interface MicMeterProps {\n /** RMS level 0..1 (use the value returned by `useMicLevel`). */\n level: number;\n /** Number of bars rendered. @default 12 */\n bars?: number;\n /** Bar gap in px. @default 2 */\n gap?: number;\n /** Bar width in px. @default 3 */\n barWidth?: number;\n /** Container height in px. @default 24 */\n height?: number;\n className?: string;\n}\n\n/**\n * Discrete-bar VU meter — small, dependency-free, no canvas. Each bar\n * lights up when the current `level` exceeds its threshold. Centre bars\n * are tallest so the meter reads like a classic mic level indicator.\n */\nexport function MicMeter({\n level,\n bars = 12,\n gap = 2,\n barWidth = 3,\n height = 24,\n className,\n}: MicMeterProps): React.ReactElement {\n const clamped = Math.min(1, Math.max(0, level));\n return (\n <div\n role=\"meter\"\n aria-valuemin={0}\n aria-valuemax={1}\n aria-valuenow={clamped}\n className={cn('inline-flex items-center', className)}\n style={{ gap, height }}\n >\n {Array.from({ length: bars }).map((_, i) => {\n const ratio = (i + 1) / bars;\n const active = clamped >= ratio - 0.5 / bars;\n // taller in the middle, shorter at edges\n const heightRatio = 1 - Math.abs(i - (bars - 1) / 2) / ((bars - 1) / 2 || 1);\n const barH = Math.max(2, height * (0.35 + heightRatio * 0.65));\n return (\n <span\n key={i}\n className={cn(\n 'rounded-sm transition-colors',\n active ? 'bg-primary' : 'bg-border',\n )}\n style={{ width: barWidth, height: barH }}\n />\n );\n })}\n </div>\n );\n}\n","'use client';\n\nimport type * as React from 'react';\n\nimport { cn } from '@djangocfg/ui-core/lib';\n\nexport interface PushToTalkHintProps {\n /** Same chord string passed to `usePushToTalk`. */\n chord: string;\n className?: string;\n}\n\n/**\n * Renders \"Hold ⌥ to talk\" with the chord pretty-printed. Tiny helper\n * meant to live next to a `DictationButton`.\n */\nexport function PushToTalkHint({ chord, className }: PushToTalkHintProps): React.ReactElement {\n const formatted = chord\n .split('+')\n .map((p) => p.trim().toLowerCase())\n .map((p) => {\n switch (p) {\n case 'mod':\n case 'meta':\n return '⌘';\n case 'alt':\n return '⌥';\n case 'shift':\n return '⇧';\n case 'ctrl':\n return '⌃';\n default:\n return p.length === 1 ? p.toUpperCase() : p;\n }\n })\n .join('');\n return (\n <span\n className={cn(\n 'inline-flex items-center gap-1 text-[11px] text-muted-foreground',\n className,\n )}\n >\n Hold\n <kbd className=\"rounded border border-border bg-muted px-1 py-0.5 font-mono text-[10px] text-foreground\">\n {formatted}\n </kbd>\n to talk\n </span>\n );\n}\n","import type { Segment, Transcript } from '../types';\n\nexport const EMPTY_TRANSCRIPT: Transcript = {\n interim: '',\n final: '',\n segments: [],\n};\n\nexport function joinFinal(segments: Segment[]): string {\n let out = '';\n for (const seg of segments) {\n if (!seg.isFinal) continue;\n const text = seg.text.trim();\n if (!text) continue;\n out = out ? `${out} ${text}` : text;\n }\n return out;\n}\n\nexport function buildTranscript(segments: Segment[]): Transcript {\n const last = segments[segments.length - 1];\n const interim = last && !last.isFinal ? last.text : '';\n return {\n interim,\n final: joinFinal(segments),\n segments,\n };\n}\n\n/**\n * Polite text normalisation between concatenated finals — strips double\n * spaces / leading punctuation that some engines emit when the user pauses.\n */\nexport function normaliseFinal(text: string): string {\n return text.replace(/\\s+/g, ' ').replace(/\\s+([,.!?])/g, '$1').trim();\n}\n","let counter = 0;\n\n/**\n * Cheap monotonic id — collisions are fine across sessions, we just need\n * uniqueness within one component lifecycle. Avoids pulling in nanoid for\n * a tool that already keeps the lazy chunk small.\n */\nexport function newSegmentId(): string {\n counter = (counter + 1) % Number.MAX_SAFE_INTEGER;\n return `seg_${Date.now().toString(36)}_${counter.toString(36)}`;\n}\n","import { consola } from 'consola';\n\nexport const sttLogger = consola.withTag('ui-tools:speech');\n","import { newSegmentId } from './ids';\nimport type {\n RecognitionError,\n RecognitionStatus,\n Segment,\n} from '../types';\n\nexport interface RecognitionState {\n status: RecognitionStatus;\n segments: Segment[];\n error: RecognitionError | null;\n startedAt: number | null;\n}\n\nexport const INITIAL_STATE: RecognitionState = {\n status: 'idle',\n segments: [],\n error: null,\n startedAt: null,\n};\n\nexport type RecognitionAction =\n | { type: 'START' }\n | { type: 'STARTED' }\n | { type: 'STOP' }\n | { type: 'STOPPED' }\n | { type: 'ABORT' }\n | {\n type: 'PARTIAL';\n text: string;\n segmentId: string;\n confidence?: number;\n }\n | {\n type: 'FINAL';\n text: string;\n segmentId: string;\n confidence?: number;\n }\n | { type: 'ERROR'; error: RecognitionError }\n | { type: 'RESET' };\n\nfunction nowSinceStart(state: RecognitionState): number {\n return state.startedAt ? Date.now() - state.startedAt : 0;\n}\n\nfunction upsertSegment(\n segments: Segment[],\n patch: Segment,\n): Segment[] {\n const idx = segments.findIndex((s) => s.id === patch.id);\n if (idx === -1) return [...segments, patch];\n const next = segments.slice();\n next[idx] = { ...next[idx], ...patch };\n return next;\n}\n\nexport function reducer(\n state: RecognitionState,\n action: RecognitionAction,\n): RecognitionState {\n switch (action.type) {\n case 'START':\n return {\n ...state,\n status: 'starting',\n error: null,\n startedAt: Date.now(),\n };\n case 'STARTED':\n return { ...state, status: 'listening' };\n case 'STOP':\n return { ...state, status: 'stopping' };\n case 'STOPPED':\n case 'ABORT':\n return { ...state, status: 'idle' };\n case 'PARTIAL': {\n const seg: Segment = {\n id: action.segmentId,\n text: action.text,\n isFinal: false,\n confidence: action.confidence,\n startedAt: nowSinceStart(state),\n };\n return { ...state, segments: upsertSegment(state.segments, seg) };\n }\n case 'FINAL': {\n const seg: Segment = {\n id: action.segmentId || newSegmentId(),\n text: action.text,\n isFinal: true,\n confidence: action.confidence,\n startedAt: nowSinceStart(state),\n endedAt: nowSinceStart(state),\n };\n return { ...state, segments: upsertSegment(state.segments, seg) };\n }\n case 'ERROR':\n return { ...state, status: 'error', error: action.error };\n case 'RESET':\n return { ...INITIAL_STATE };\n default:\n return state;\n }\n}\n","/**\n * Tiny event-bus helper shared by every engine. Lets engine authors avoid\n * re-implementing add/remove listener bookkeeping while keeping the\n * public `RecognitionEngine.on(...)` contract identical across engines.\n */\n\nimport type { EngineEventMap, Unsub } from '../../types';\n\ntype Listeners = {\n [K in keyof EngineEventMap]: Set<EngineEventMap[K]>;\n};\n\nexport function createEngineBus(): {\n on: <K extends keyof EngineEventMap>(event: K, cb: EngineEventMap[K]) => Unsub;\n emit: <K extends keyof EngineEventMap>(\n event: K,\n ...args: Parameters<EngineEventMap[K]>\n ) => void;\n clear: () => void;\n} {\n const listeners: Listeners = {\n partial: new Set(),\n final: new Set(),\n error: new Set(),\n state: new Set(),\n };\n\n return {\n on(event, cb) {\n const set = listeners[event] as Set<typeof cb>;\n set.add(cb);\n return () => {\n set.delete(cb);\n };\n },\n emit(event, ...args) {\n const set = listeners[event];\n for (const cb of set) {\n try {\n (cb as (...a: unknown[]) => void)(...(args as unknown[]));\n } catch {\n // listener errors are isolated — never break the engine loop\n }\n }\n },\n clear() {\n for (const key of Object.keys(listeners) as Array<keyof Listeners>) {\n listeners[key].clear();\n }\n },\n };\n}\n","/**\n * Default engine — wraps the browser's `SpeechRecognition` API.\n *\n * Lives behind the same `RecognitionEngine` contract every other engine\n * implements. When the browser doesn't expose `SpeechRecognition`\n * (Firefox, some mobile WebViews) `isSupported` is `false` and `start()`\n * throws an `unsupported` error.\n */\n\nimport { newSegmentId } from '../ids';\nimport { sttLogger } from '../logger';\nimport { createEngineBus } from './index';\nimport type {\n EngineStartOptions,\n RecognitionEngine,\n RecognitionError,\n RecognitionErrorCode,\n Unsub,\n} from '../../types';\n\n// Minimal subset of the Web Speech API we actually rely on. Browsers\n// expose either `SpeechRecognition` (Edge / Safari new) or the older\n// `webkitSpeechRecognition` (Chrome). Both share the same shape.\ninterface BrowserSpeechRecognition extends EventTarget {\n lang: string;\n interimResults: boolean;\n continuous: boolean;\n maxAlternatives: number;\n start(): void;\n stop(): void;\n abort(): void;\n onresult: ((e: BrowserSpeechRecognitionEvent) => void) | null;\n onerror: ((e: BrowserSpeechRecognitionError) => void) | null;\n onstart: (() => void) | null;\n onend: (() => void) | null;\n}\n\ninterface BrowserSpeechRecognitionResult {\n isFinal: boolean;\n 0: { transcript: string; confidence: number };\n}\n\ninterface BrowserSpeechRecognitionEvent extends Event {\n resultIndex: number;\n results: ArrayLike<BrowserSpeechRecognitionResult>;\n}\n\ninterface BrowserSpeechRecognitionError extends Event {\n error: string;\n message?: string;\n}\n\ntype Ctor = new () => BrowserSpeechRecognition;\n\nfunction resolveCtor(): Ctor | null {\n if (typeof window === 'undefined') return null;\n const w = window as unknown as {\n SpeechRecognition?: Ctor;\n webkitSpeechRecognition?: Ctor;\n };\n return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;\n}\n\nconst ERROR_MAP: Record<string, RecognitionErrorCode> = {\n 'no-speech': 'no-speech',\n aborted: 'aborted',\n 'audio-capture': 'no-microphone',\n network: 'network',\n 'not-allowed': 'permission-denied',\n 'service-not-allowed': 'permission-denied',\n 'bad-grammar': 'engine',\n 'language-not-supported': 'language',\n};\n\nexport interface WebSpeechEngineOptions {\n /** Whether the underlying recognition should be continuous. Default true. */\n continuous?: boolean;\n /** Max alternatives the engine should request. Default 1. */\n maxAlternatives?: number;\n}\n\nexport function createWebSpeechEngine(\n opts: WebSpeechEngineOptions = {},\n): RecognitionEngine {\n const Ctor = resolveCtor();\n const bus = createEngineBus();\n let instance: BrowserSpeechRecognition | null = null;\n let currentSegmentId: string | null = null;\n\n function teardown(): void {\n if (!instance) return;\n instance.onresult = null;\n instance.onerror = null;\n instance.onstart = null;\n instance.onend = null;\n instance = null;\n currentSegmentId = null;\n }\n\n return {\n id: 'webspeech',\n isSupported: Ctor !== null,\n on(event, cb): Unsub {\n return bus.on(event, cb);\n },\n async start(start: EngineStartOptions): Promise<void> {\n if (!Ctor) {\n const err: RecognitionError = {\n code: 'unsupported',\n message: 'Web Speech API is not available in this browser.',\n };\n bus.emit('error', err);\n throw err;\n }\n if (instance) {\n sttLogger.debug('[webspeech] start() called while running — ignoring');\n return;\n }\n\n bus.emit('state', 'connecting');\n\n const rec = new Ctor();\n rec.lang = start.language;\n rec.interimResults = start.interim;\n rec.continuous = opts.continuous ?? true;\n rec.maxAlternatives = opts.maxAlternatives ?? 1;\n\n rec.onstart = () => {\n bus.emit('state', 'listening');\n };\n rec.onend = () => {\n bus.emit('state', 'closed');\n teardown();\n };\n rec.onerror = (e) => {\n const code = ERROR_MAP[e.error] ?? 'engine';\n const err: RecognitionError = {\n code,\n message: e.message || `Web Speech error: ${e.error}`,\n };\n bus.emit('error', err);\n };\n rec.onresult = (e) => {\n for (let i = e.resultIndex; i < e.results.length; i += 1) {\n const res = e.results[i];\n const alt = res[0];\n const text = alt.transcript;\n if (!currentSegmentId) currentSegmentId = newSegmentId();\n if (res.isFinal) {\n bus.emit('final', text, currentSegmentId, alt.confidence);\n currentSegmentId = null;\n } else {\n bus.emit('partial', text, currentSegmentId);\n }\n }\n };\n\n if (start.signal) {\n start.signal.addEventListener('abort', () => {\n rec.abort();\n });\n }\n\n instance = rec;\n try {\n rec.start();\n } catch (cause) {\n const err: RecognitionError = {\n code: 'engine',\n message: 'Failed to start Web Speech recognition.',\n cause,\n };\n bus.emit('error', err);\n teardown();\n throw err;\n }\n },\n async stop(): Promise<void> {\n if (!instance) return;\n bus.emit('state', 'closing');\n instance.stop();\n },\n abort(): void {\n if (!instance) return;\n instance.abort();\n },\n };\n}\n","'use client';\n\nimport { create } from 'zustand';\nimport { persist, createJSONStorage } from 'zustand/middleware';\n\nexport interface SpeechPrefs {\n /**\n * BCP-47 tag the user explicitly picked (via `<LanguagePicker>` or\n * programmatically). `null` means \"no override\" — `useResolvedLanguage`\n * then falls through to the app i18n locale / `navigator.language`.\n * Storing the picker default as `null` is what lets a host's i18n\n * locale take effect when the user never touched the picker.\n */\n language: string | null;\n deviceId: string | null;\n engineId: string | null;\n earcons: boolean;\n}\n\nconst DEFAULTS: SpeechPrefs = {\n language: null,\n deviceId: null,\n engineId: null,\n earcons: false,\n};\n\ninterface PrefsStore extends SpeechPrefs {\n setLanguage: (v: string | null) => void;\n setDeviceId: (v: string | null) => void;\n setEngineId: (v: string | null) => void;\n setEarcons: (v: boolean) => void;\n reset: () => void;\n}\n\nexport const useSpeechPrefs = create<PrefsStore>()(\n persist(\n (set) => ({\n ...DEFAULTS,\n setLanguage: (language) => set({ language }),\n setDeviceId: (deviceId) => set({ deviceId }),\n setEngineId: (engineId) => set({ engineId }),\n setEarcons: (earcons) => set({ earcons }),\n reset: () => set({ ...DEFAULTS }),\n }),\n {\n name: 'djangocfg-stt:prefs',\n storage: createJSONStorage(() =>\n typeof window === 'undefined'\n ? (undefined as unknown as Storage)\n : window.localStorage,\n ),\n },\n ),\n);\n","'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\n/**\n * RMS level meter driven by an `AnalyserNode`. Attach a `MediaStream`\n * (the one returned from `startMicCapture`) and read `level` (0..1) for\n * VU meters / mic-pulse animations. Returns 0 when no stream is bound.\n */\nexport function useMicLevel(stream: MediaStream | null): number {\n const [level, setLevel] = useState(0);\n const raf = useRef<number | null>(null);\n\n useEffect(() => {\n if (!stream) {\n setLevel(0);\n return undefined;\n }\n const AC =\n (window as unknown as { AudioContext?: typeof AudioContext }).AudioContext ??\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!AC) return undefined;\n const ctx = new AC();\n const source = ctx.createMediaStreamSource(stream);\n const analyser = ctx.createAnalyser();\n analyser.fftSize = 1024;\n analyser.smoothingTimeConstant = 0.7;\n source.connect(analyser);\n const buf = new Float32Array(analyser.fftSize);\n\n const tick = (): void => {\n analyser.getFloatTimeDomainData(buf);\n let sum = 0;\n for (let i = 0; i < buf.length; i += 1) sum += buf[i] * buf[i];\n const rms = Math.sqrt(sum / buf.length);\n // soft compression so loud peaks don't dominate the meter\n setLevel(Math.min(1, rms * 2.5));\n raf.current = requestAnimationFrame(tick);\n };\n tick();\n\n return () => {\n if (raf.current != null) cancelAnimationFrame(raf.current);\n raf.current = null;\n source.disconnect();\n analyser.disconnect();\n void ctx.close();\n };\n }, [stream]);\n\n return level;\n}\n","/**\n * Maps 2-letter ISO 639-1 codes (`en`, `ru`, `ko` — what\n * `@djangocfg/i18n` exposes via `useLocale()`) to BCP-47 tags\n * (`en-US`, `ru-RU`, `ko-KR`) that the Web Speech API and most cloud\n * STT services expect.\n *\n * We keep a small built-in table for the locales we ship translations\n * for; everything else falls through to `<code>-<UPPER(code)>`, which\n * works for the majority of regions. The mapping is also re-exported\n * so consumers can extend it.\n */\n\nconst ISO_TO_BCP47: Record<string, string> = {\n en: 'en-US',\n ru: 'ru-RU',\n ko: 'ko-KR',\n ja: 'ja-JP',\n zh: 'zh-CN',\n de: 'de-DE',\n fr: 'fr-FR',\n it: 'it-IT',\n es: 'es-ES',\n nl: 'nl-NL',\n ar: 'ar-SA',\n tr: 'tr-TR',\n pl: 'pl-PL',\n sv: 'sv-SE',\n no: 'nb-NO',\n da: 'da-DK',\n pt: 'pt-BR',\n};\n\nexport const DEFAULT_ISO_TO_BCP47 = ISO_TO_BCP47;\n\n/**\n * Normalise any of:\n * - BCP-47 (\"en-US\", \"ru-RU\") — passed through.\n * - ISO 639-1 (\"en\", \"ru\") — mapped via the table above, or\n * falls back to `<code>-<UPPER(code)>`.\n * - `null`/`undefined`/empty — returns `undefined`.\n */\nexport function toBCP47(\n code: string | null | undefined,\n table: Record<string, string> = ISO_TO_BCP47,\n): string | undefined {\n if (!code) return undefined;\n const trimmed = code.trim();\n if (!trimmed) return undefined;\n if (trimmed.includes('-')) return trimmed; // already BCP-47\n const lower = trimmed.toLowerCase();\n return table[lower] ?? `${lower}-${lower.toUpperCase()}`;\n}\n\n/**\n * Resolve the language tag for a speech session in priority order:\n * 1. `explicit` prop (always wins) — host-supplied override.\n * 2. `prefs` — value stored in `useSpeechPrefs` (user picked it\n * via `<LanguagePicker>` or programmatically).\n * 3. `i18n` — current i18n locale (2-letter ISO).\n * 4. `navigator.language` — browser default.\n * 5. `'en-US'` — last-resort safety net.\n *\n * All inputs may be ISO-2 or BCP-47; the function normalises before\n * returning.\n */\nexport function resolveSpeechLanguage(opts: {\n explicit?: string;\n prefs?: string | null;\n i18n?: string | null;\n}): string {\n return (\n toBCP47(opts.explicit) ??\n toBCP47(opts.prefs) ??\n toBCP47(opts.i18n) ??\n toBCP47(typeof navigator !== 'undefined' ? navigator.language : null) ??\n 'en-US'\n );\n}\n","'use client';\n\nimport { useLocaleOptional } from '@djangocfg/i18n';\n\nimport { resolveSpeechLanguage } from '../core/language';\nimport { useSpeechPrefs } from '../store/prefsStore';\n\n/**\n * Resolves the BCP-47 language tag a speech session should use.\n *\n * Priority: explicit prop → user-picked `useSpeechPrefs.language` →\n * app i18n locale (when an `<I18nProvider>` is mounted) →\n * `navigator.language` → `en-US`.\n *\n * Uses `useLocaleOptional` (not `useLocale`) so that an unmounted\n * provider doesn't silently inject `'en'`. Without that, the i18n\n * default would shadow the user's real browser language — a Russian\n * speaker would always get `en-US` recognition.\n */\nexport function useResolvedLanguage(explicit?: string): string {\n const prefs = useSpeechPrefs();\n const locale = useLocaleOptional();\n return resolveSpeechLanguage({\n explicit,\n prefs: prefs.language,\n i18n: locale,\n });\n}\n","'use client';\n\nimport { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';\n\nimport {\n EMPTY_TRANSCRIPT,\n INITIAL_STATE,\n buildTranscript,\n reducer,\n sttLogger,\n} from '../core';\nimport { createWebSpeechEngine } from '../core/engine/webspeech';\nimport { useSpeechPrefs } from '../store/prefsStore';\nimport type {\n RecognitionEngine,\n Segment,\n UseSpeechRecognitionConfig,\n UseSpeechRecognitionReturn,\n} from '../types';\nimport { useMicLevel } from './useMicLevel';\nimport { useResolvedLanguage } from './useResolvedLanguage';\n\n/**\n * Main entry point. With no config it uses the browser Web Speech API\n * and the persisted language from `useSpeechPrefs`. Pass a custom\n * `engine` to route through Deepgram / Whisper / custom WebSocket.\n */\nexport function useSpeechRecognition(\n config: UseSpeechRecognitionConfig = {},\n): UseSpeechRecognitionReturn {\n const prefs = useSpeechPrefs();\n const language = useResolvedLanguage(config.language);\n const engine = useMemo<RecognitionEngine>(\n () => config.engine ?? createWebSpeechEngine(),\n [config.engine],\n );\n\n const [state, dispatch] = useReducer(reducer, INITIAL_STATE);\n const [stream, setStream] = useState<MediaStream | null>(null);\n const level = useMicLevel(stream);\n\n // Latest-callback refs so engine subscriptions never tear down on\n // every render — same trick the Chat reducer uses.\n const cbRef = useRef(config);\n cbRef.current = config;\n\n // Engine subscription lifecycle.\n useEffect(() => {\n const offs = [\n engine.on('partial', (text, segmentId) => {\n dispatch({ type: 'PARTIAL', text, segmentId });\n const seg: Segment = {\n id: segmentId,\n text,\n isFinal: false,\n startedAt: Date.now(),\n };\n cbRef.current.onPartial?.(text, seg);\n }),\n engine.on('final', (text, segmentId, confidence) => {\n dispatch({ type: 'FINAL', text, segmentId, confidence });\n const seg: Segment = {\n id: segmentId,\n text,\n isFinal: true,\n confidence,\n startedAt: Date.now(),\n endedAt: Date.now(),\n };\n cbRef.current.onFinal?.(text, seg);\n }),\n engine.on('error', (err) => {\n dispatch({ type: 'ERROR', error: err });\n cbRef.current.onError?.(err);\n }),\n engine.on('state', (s) => {\n if (s === 'listening') {\n dispatch({ type: 'STARTED' });\n cbRef.current.onStart?.();\n setStream(engine.getStream?.() ?? null);\n } else if (s === 'closed') {\n dispatch({ type: 'STOPPED' });\n cbRef.current.onStop?.();\n setStream(null);\n }\n }),\n ];\n return () => {\n offs.forEach((off) => off());\n };\n }, [engine]);\n\n // AutoStop driven by silence + maxMs caps.\n const silenceTimer = useRef<number | null>(null);\n const maxTimer = useRef<number | null>(null);\n useEffect(() => {\n if (state.status !== 'listening') return undefined;\n const { silenceMs, maxMs, silenceThreshold = 0.02 } = config.autoStop ?? {};\n if (maxMs) {\n maxTimer.current = window.setTimeout(() => {\n sttLogger.debug('[autoStop] max duration hit');\n void engine.stop();\n }, maxMs);\n }\n if (silenceMs) {\n const checkInterval = window.setInterval(() => {\n if (level < silenceThreshold) {\n if (silenceTimer.current == null) {\n silenceTimer.current = window.setTimeout(() => {\n sttLogger.debug('[autoStop] silence detected');\n void engine.stop();\n }, silenceMs);\n }\n } else if (silenceTimer.current != null) {\n clearTimeout(silenceTimer.current);\n silenceTimer.current = null;\n }\n }, 200);\n return () => {\n clearInterval(checkInterval);\n if (silenceTimer.current != null) clearTimeout(silenceTimer.current);\n silenceTimer.current = null;\n if (maxTimer.current != null) clearTimeout(maxTimer.current);\n maxTimer.current = null;\n };\n }\n return () => {\n if (maxTimer.current != null) clearTimeout(maxTimer.current);\n maxTimer.current = null;\n };\n }, [state.status, config.autoStop, level, engine]);\n\n const start = useCallback(async () => {\n if (state.status === 'listening' || state.status === 'starting') return;\n dispatch({ type: 'START' });\n try {\n await engine.start({\n language,\n interim: config.interim ?? true,\n deviceId: config.deviceId ?? prefs.deviceId ?? undefined,\n });\n } catch (cause) {\n // engine already emitted 'error'; reducer caught it via subscription\n sttLogger.debug('[start] engine threw', cause);\n }\n }, [engine, language, config.interim, config.deviceId, prefs.deviceId, state.status]);\n\n const stop = useCallback(async () => {\n if (state.status === 'idle' || state.status === 'stopping') return;\n dispatch({ type: 'STOP' });\n await engine.stop();\n }, [engine, state.status]);\n\n const abort = useCallback(() => {\n engine.abort();\n dispatch({ type: 'ABORT' });\n }, [engine]);\n\n const toggle = useCallback(async () => {\n if (state.status === 'listening' || state.status === 'starting') {\n await stop();\n } else {\n await start();\n }\n }, [state.status, start, stop]);\n\n const reset = useCallback(() => {\n dispatch({ type: 'RESET' });\n }, []);\n\n const transcript = useMemo(\n () => (state.segments.length === 0 ? EMPTY_TRANSCRIPT : buildTranscript(state.segments)),\n [state.segments],\n );\n\n return {\n status: state.status,\n isSupported: engine.isSupported,\n transcript,\n error: state.error,\n level,\n start,\n stop,\n abort,\n toggle,\n reset,\n };\n}\n","'use client';\n\nimport { useEffect, useRef } from 'react';\n\nimport { normaliseFinal } from '../core/transcript';\nimport type { UseSpeechRecognitionConfig, UseSpeechRecognitionReturn } from '../types';\nimport { useSpeechRecognition } from './useSpeechRecognition';\n\nexport interface UseDictationConfig\n extends Omit<UseSpeechRecognitionConfig, 'onFinal'> {\n /** Controlled value the dictation is appending to. */\n value: string;\n /** Called with the next value after each final segment lands. */\n onChange: (next: string) => void;\n /** Joiner between the previous value and the new segment. Default ' '. */\n separator?: string;\n}\n\nexport interface UseDictationReturn extends UseSpeechRecognitionReturn {\n /** Convenience — same as `toggle`, named for dictation UIs. */\n toggleDictation: () => Promise<void>;\n}\n\n/**\n * Convenience adapter that pipes final transcript segments straight into\n * a controlled string (`<textarea>` / `<input>` / TipTap). Interim text\n * is left alone — bind `transcript.interim` separately if you want to\n * show a live ghost.\n */\nexport function useDictation(config: UseDictationConfig): UseDictationReturn {\n const { value, onChange, separator = ' ', ...rest } = config;\n\n // Stash latest value in a ref so the onFinal closure always sees the\n // freshest text without forcing the underlying hook to resubscribe.\n const valueRef = useRef(value);\n valueRef.current = value;\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n\n const rec = useSpeechRecognition({\n ...rest,\n onFinal: (text) => {\n const clean = normaliseFinal(text);\n if (!clean) return;\n const prev = valueRef.current;\n const next = prev ? `${prev}${separator}${clean}` : clean;\n onChangeRef.current(next);\n },\n });\n\n useEffect(() => {\n valueRef.current = value;\n }, [value]);\n\n return {\n ...rec,\n toggleDictation: rec.toggle,\n };\n}\n","'use client';\n\nimport { useEffect } from 'react';\n\nimport type { UseSpeechRecognitionReturn } from '../types';\n\nexport interface UsePushToTalkOptions {\n /** Key to hold. Combine modifiers with `+`, e.g. `'alt'`, `'mod+alt'`. */\n key: string;\n /** Disable the binding without unmounting. */\n enabled?: boolean;\n}\n\nconst MOD_KEYS = new Set(['shift', 'ctrl', 'alt', 'meta', 'mod']);\n\nfunction parseChord(chord: string): { mods: Set<string>; main: string | null } {\n const parts = chord\n .toLowerCase()\n .split('+')\n .map((s) => s.trim());\n const mods = new Set<string>();\n let main: string | null = null;\n for (const part of parts) {\n if (MOD_KEYS.has(part)) {\n mods.add(part === 'mod' ? 'meta' : part);\n } else {\n main = part;\n }\n }\n return { mods, main };\n}\n\nfunction matches(e: KeyboardEvent, mods: Set<string>, main: string | null): boolean {\n if (mods.has('shift') !== e.shiftKey) return false;\n if (mods.has('ctrl') !== e.ctrlKey) return false;\n if (mods.has('alt') !== e.altKey) return false;\n // 'mod' → meta on mac / ctrl elsewhere; we already normalised to 'meta'.\n if (mods.has('meta') !== (e.metaKey || (!e.metaKey && false))) return false;\n if (main && e.key.toLowerCase() !== main) return false;\n return true;\n}\n\n/**\n * Hold-to-talk wiring. Press → `start()`, release → `stop()`. Ignores\n * repeats and skips keydown inside `<input>` / `<textarea>` unless a\n * modifier is in the chord.\n */\nexport function usePushToTalk(\n recognition: Pick<UseSpeechRecognitionReturn, 'start' | 'stop' | 'status'>,\n opts: UsePushToTalkOptions,\n): void {\n const { key, enabled = true } = opts;\n\n useEffect(() => {\n if (!enabled || typeof window === 'undefined') return undefined;\n const { mods, main } = parseChord(key);\n\n const onDown = (e: KeyboardEvent): void => {\n if (e.repeat) return;\n if (!main && mods.size === 0) return;\n const target = e.target as HTMLElement | null;\n const inField =\n target?.tagName === 'INPUT' ||\n target?.tagName === 'TEXTAREA' ||\n target?.isContentEditable;\n if (inField && mods.size === 0) return;\n if (!matches(e, mods, main)) return;\n if (recognition.status === 'listening' || recognition.status === 'starting') return;\n e.preventDefault();\n void recognition.start();\n };\n const onUp = (e: KeyboardEvent): void => {\n if (!matches(e, mods, main) && main && e.key.toLowerCase() !== main) return;\n if (recognition.status !== 'listening' && recognition.status !== 'starting') return;\n void recognition.stop();\n };\n\n window.addEventListener('keydown', onDown);\n window.addEventListener('keyup', onUp);\n return () => {\n window.removeEventListener('keydown', onDown);\n window.removeEventListener('keyup', onUp);\n };\n }, [enabled, key, recognition]);\n}\n","'use client';\n\nimport type * as React from 'react';\n\nimport { cn } from '@djangocfg/ui-core/lib';\n\nimport { DictationButton } from '../components/DictationButton';\nimport { ErrorBanner } from '../components/ErrorBanner';\nimport { MicMeter } from '../components/MicMeter';\nimport { PushToTalkHint } from '../components/PushToTalkHint';\nimport { useDictation } from '../hooks/useDictation';\nimport { usePushToTalk } from '../hooks/usePushToTalk';\nimport type { RecognitionEngine } from '../types';\n\nexport interface DictationFieldProps {\n value: string;\n onChange: (next: string) => void;\n /** Custom engine. Defaults to Web Speech via `useSpeechRecognition`. */\n engine?: RecognitionEngine;\n /** Override the language stored in `useSpeechPrefs`. */\n language?: string;\n /** Push-to-talk chord (e.g. `'alt'`, `'mod+alt'`). Disabled when omitted. */\n pushToTalk?: { key: string; enabled?: boolean };\n placeholder?: string;\n rows?: number;\n disabled?: boolean;\n /** Show the interim transcript as a ghost overlay below the textarea. */\n showInterim?: boolean;\n /** Show a small RMS meter inside the toolbar. */\n showMeter?: boolean;\n className?: string;\n textareaClassName?: string;\n}\n\n/**\n * Opinionated textarea + dictation button assembly. Final segments are\n * appended to the controlled `value` automatically. Press-and-hold\n * shortcut is optional; the mic button itself works as a toggle.\n */\nexport function DictationField({\n value,\n onChange,\n engine,\n language,\n pushToTalk,\n placeholder = 'Type or press the mic to dictate…',\n rows = 3,\n disabled,\n showInterim = true,\n showMeter = true,\n className,\n textareaClassName,\n}: DictationFieldProps): React.ReactElement {\n const rec = useDictation({\n value,\n onChange,\n engine,\n language,\n });\n\n usePushToTalk(rec, {\n key: pushToTalk?.key ?? 'alt',\n enabled: !!pushToTalk && pushToTalk.enabled !== false,\n });\n\n return (\n <div className={cn('flex flex-col gap-2', className)}>\n <div className=\"relative\">\n <textarea\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n rows={rows}\n disabled={disabled}\n className={cn(\n 'w-full resize-y rounded-md border border-input bg-background px-3 py-2 text-sm',\n 'placeholder:text-muted-foreground',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n textareaClassName,\n )}\n />\n {showInterim && rec.transcript.interim && (\n <div className=\"pointer-events-none mt-1 text-xs italic text-muted-foreground\">\n … {rec.transcript.interim}\n </div>\n )}\n </div>\n\n <div className=\"flex items-center gap-2\">\n <DictationButton\n status={rec.status}\n isSupported={rec.isSupported}\n onClick={() => void rec.toggleDictation()}\n size=\"sm\"\n disabled={disabled}\n />\n {showMeter && <MicMeter level={rec.level} bars={10} height={20} />}\n {pushToTalk && <PushToTalkHint chord={pushToTalk.key} className=\"ml-auto\" />}\n </div>\n\n <ErrorBanner error={rec.error} />\n </div>\n );\n}\n"]}
|
|
@@ -828,6 +828,6 @@ function DictationField({
|
|
|
828
828
|
}
|
|
829
829
|
__name(DictationField, "DictationField");
|
|
830
830
|
|
|
831
|
-
export { DictationField };
|
|
832
|
-
//# sourceMappingURL=chunk-
|
|
833
|
-
//# sourceMappingURL=chunk-
|
|
831
|
+
export { DictationField, useResolvedLanguage, useSpeechPrefs };
|
|
832
|
+
//# sourceMappingURL=chunk-C2YN6WEO.mjs.map
|
|
833
|
+
//# sourceMappingURL=chunk-C2YN6WEO.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tools/SpeechRecognition/components/DictationButton.tsx","../src/tools/SpeechRecognition/components/ErrorBanner.tsx","../src/tools/SpeechRecognition/components/MicMeter.tsx","../src/tools/SpeechRecognition/components/PushToTalkHint.tsx","../src/tools/SpeechRecognition/core/transcript.ts","../src/tools/SpeechRecognition/core/ids.ts","../src/tools/SpeechRecognition/core/logger.ts","../src/tools/SpeechRecognition/core/reducer.ts","../src/tools/SpeechRecognition/core/engine/index.ts","../src/tools/SpeechRecognition/core/engine/webspeech.ts","../src/tools/SpeechRecognition/store/prefsStore.ts","../src/tools/SpeechRecognition/hooks/useMicLevel.ts","../src/tools/SpeechRecognition/core/language.ts","../src/tools/SpeechRecognition/hooks/useResolvedLanguage.ts","../src/tools/SpeechRecognition/hooks/useSpeechRecognition.ts","../src/tools/SpeechRecognition/hooks/useDictation.ts","../src/tools/SpeechRecognition/hooks/usePushToTalk.ts","../src/tools/SpeechRecognition/widgets/DictationField.tsx"],"names":["jsxs","cn","jsx","useState","useRef","useEffect"],"mappings":"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","file":"chunk-C2YN6WEO.mjs","sourcesContent":["'use client';\n\nimport type * as React from 'react';\n\nimport { Loader2, Mic, MicOff } from 'lucide-react';\nimport type { CSSProperties, ReactNode } from 'react';\n\nimport { cn } from '@djangocfg/ui-core/lib';\n\nimport type { RecognitionStatus } from '../types';\n\nexport interface DictationButtonProps {\n status: RecognitionStatus;\n onClick: () => void;\n isSupported?: boolean;\n size?: 'sm' | 'md' | 'lg';\n className?: string;\n style?: CSSProperties;\n ariaLabel?: string;\n /** Override icon for the idle state. */\n idleIcon?: ReactNode;\n /** Override icon for the listening state. */\n listeningIcon?: ReactNode;\n /** Disable without unmounting. */\n disabled?: boolean;\n}\n\nconst SIZE_CLS: Record<NonNullable<DictationButtonProps['size']>, string> = {\n sm: 'h-8 w-8 [&_svg]:h-4 [&_svg]:w-4',\n md: 'h-10 w-10 [&_svg]:h-5 [&_svg]:w-5',\n lg: 'h-12 w-12 [&_svg]:h-6 [&_svg]:w-6',\n};\n\n/**\n * Round microphone button. Cycles icon by status; shows a soft pulse\n * ring when listening. ARIA-correct so screen readers announce\n * \"recording\" vs \"start dictation\".\n */\nexport function DictationButton({\n status,\n onClick,\n isSupported = true,\n size = 'md',\n className,\n style,\n ariaLabel,\n idleIcon,\n listeningIcon,\n disabled,\n}: DictationButtonProps): React.ReactElement {\n const listening = status === 'listening' || status === 'starting';\n const stopping = status === 'stopping';\n const off = !isSupported;\n\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled || off}\n aria-pressed={listening}\n aria-label={\n ariaLabel ?? (listening ? 'Stop dictation' : off ? 'Dictation not supported' : 'Start dictation')\n }\n className={cn(\n 'relative inline-flex items-center justify-center rounded-full transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n SIZE_CLS[size],\n listening\n ? 'bg-destructive text-destructive-foreground hover:bg-destructive/90'\n : 'bg-primary text-primary-foreground hover:bg-primary/90',\n className,\n )}\n style={style}\n >\n {listening && (\n <span\n aria-hidden\n className=\"absolute inset-0 rounded-full bg-destructive/40 animate-ping\"\n />\n )}\n {stopping ? (\n <Loader2 className=\"animate-spin\" />\n ) : off ? (\n listeningIcon ?? <MicOff />\n ) : listening ? (\n listeningIcon ?? <Mic />\n ) : (\n idleIcon ?? <Mic />\n )}\n </button>\n );\n}\n","'use client';\n\nimport type * as React from 'react';\n\nimport { AlertTriangle } from 'lucide-react';\n\nimport { cn } from '@djangocfg/ui-core/lib';\n\nimport type { RecognitionError } from '../types';\n\nconst FRIENDLY: Record<RecognitionError['code'], string> = {\n unsupported: 'Speech recognition isn\\'t available in this browser.',\n 'permission-denied': 'Microphone access was denied. Allow it in your browser settings to dictate.',\n 'no-microphone': 'No microphone found.',\n network: 'Network error talking to the speech service.',\n aborted: 'Recognition was interrupted.',\n 'no-speech': 'No speech was detected. Try again.',\n language: 'The requested language isn\\'t supported by the engine.',\n engine: 'The speech engine reported an error.',\n unknown: 'Something went wrong with the microphone.',\n};\n\nexport interface ErrorBannerProps {\n error: RecognitionError | null;\n className?: string;\n onDismiss?: () => void;\n}\n\nexport function ErrorBanner({ error, className, onDismiss }: ErrorBannerProps): React.ReactElement | null {\n if (!error) return null;\n return (\n <div\n role=\"alert\"\n className={cn(\n 'flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive',\n className,\n )}\n >\n <AlertTriangle className=\"mt-0.5 h-3.5 w-3.5 shrink-0\" />\n <div className=\"flex-1\">{FRIENDLY[error.code] ?? error.message}</div>\n {onDismiss && (\n <button\n type=\"button\"\n onClick={onDismiss}\n className=\"text-destructive hover:underline\"\n >\n Dismiss\n </button>\n )}\n </div>\n );\n}\n","'use client';\n\nimport type * as React from 'react';\n\nimport { cn } from '@djangocfg/ui-core/lib';\n\nexport interface MicMeterProps {\n /** RMS level 0..1 (use the value returned by `useMicLevel`). */\n level: number;\n /** Number of bars rendered. @default 12 */\n bars?: number;\n /** Bar gap in px. @default 2 */\n gap?: number;\n /** Bar width in px. @default 3 */\n barWidth?: number;\n /** Container height in px. @default 24 */\n height?: number;\n className?: string;\n}\n\n/**\n * Discrete-bar VU meter — small, dependency-free, no canvas. Each bar\n * lights up when the current `level` exceeds its threshold. Centre bars\n * are tallest so the meter reads like a classic mic level indicator.\n */\nexport function MicMeter({\n level,\n bars = 12,\n gap = 2,\n barWidth = 3,\n height = 24,\n className,\n}: MicMeterProps): React.ReactElement {\n const clamped = Math.min(1, Math.max(0, level));\n return (\n <div\n role=\"meter\"\n aria-valuemin={0}\n aria-valuemax={1}\n aria-valuenow={clamped}\n className={cn('inline-flex items-center', className)}\n style={{ gap, height }}\n >\n {Array.from({ length: bars }).map((_, i) => {\n const ratio = (i + 1) / bars;\n const active = clamped >= ratio - 0.5 / bars;\n // taller in the middle, shorter at edges\n const heightRatio = 1 - Math.abs(i - (bars - 1) / 2) / ((bars - 1) / 2 || 1);\n const barH = Math.max(2, height * (0.35 + heightRatio * 0.65));\n return (\n <span\n key={i}\n className={cn(\n 'rounded-sm transition-colors',\n active ? 'bg-primary' : 'bg-border',\n )}\n style={{ width: barWidth, height: barH }}\n />\n );\n })}\n </div>\n );\n}\n","'use client';\n\nimport type * as React from 'react';\n\nimport { cn } from '@djangocfg/ui-core/lib';\n\nexport interface PushToTalkHintProps {\n /** Same chord string passed to `usePushToTalk`. */\n chord: string;\n className?: string;\n}\n\n/**\n * Renders \"Hold ⌥ to talk\" with the chord pretty-printed. Tiny helper\n * meant to live next to a `DictationButton`.\n */\nexport function PushToTalkHint({ chord, className }: PushToTalkHintProps): React.ReactElement {\n const formatted = chord\n .split('+')\n .map((p) => p.trim().toLowerCase())\n .map((p) => {\n switch (p) {\n case 'mod':\n case 'meta':\n return '⌘';\n case 'alt':\n return '⌥';\n case 'shift':\n return '⇧';\n case 'ctrl':\n return '⌃';\n default:\n return p.length === 1 ? p.toUpperCase() : p;\n }\n })\n .join('');\n return (\n <span\n className={cn(\n 'inline-flex items-center gap-1 text-[11px] text-muted-foreground',\n className,\n )}\n >\n Hold\n <kbd className=\"rounded border border-border bg-muted px-1 py-0.5 font-mono text-[10px] text-foreground\">\n {formatted}\n </kbd>\n to talk\n </span>\n );\n}\n","import type { Segment, Transcript } from '../types';\n\nexport const EMPTY_TRANSCRIPT: Transcript = {\n interim: '',\n final: '',\n segments: [],\n};\n\nexport function joinFinal(segments: Segment[]): string {\n let out = '';\n for (const seg of segments) {\n if (!seg.isFinal) continue;\n const text = seg.text.trim();\n if (!text) continue;\n out = out ? `${out} ${text}` : text;\n }\n return out;\n}\n\nexport function buildTranscript(segments: Segment[]): Transcript {\n const last = segments[segments.length - 1];\n const interim = last && !last.isFinal ? last.text : '';\n return {\n interim,\n final: joinFinal(segments),\n segments,\n };\n}\n\n/**\n * Polite text normalisation between concatenated finals — strips double\n * spaces / leading punctuation that some engines emit when the user pauses.\n */\nexport function normaliseFinal(text: string): string {\n return text.replace(/\\s+/g, ' ').replace(/\\s+([,.!?])/g, '$1').trim();\n}\n","let counter = 0;\n\n/**\n * Cheap monotonic id — collisions are fine across sessions, we just need\n * uniqueness within one component lifecycle. Avoids pulling in nanoid for\n * a tool that already keeps the lazy chunk small.\n */\nexport function newSegmentId(): string {\n counter = (counter + 1) % Number.MAX_SAFE_INTEGER;\n return `seg_${Date.now().toString(36)}_${counter.toString(36)}`;\n}\n","import { consola } from 'consola';\n\nexport const sttLogger = consola.withTag('ui-tools:speech');\n","import { newSegmentId } from './ids';\nimport type {\n RecognitionError,\n RecognitionStatus,\n Segment,\n} from '../types';\n\nexport interface RecognitionState {\n status: RecognitionStatus;\n segments: Segment[];\n error: RecognitionError | null;\n startedAt: number | null;\n}\n\nexport const INITIAL_STATE: RecognitionState = {\n status: 'idle',\n segments: [],\n error: null,\n startedAt: null,\n};\n\nexport type RecognitionAction =\n | { type: 'START' }\n | { type: 'STARTED' }\n | { type: 'STOP' }\n | { type: 'STOPPED' }\n | { type: 'ABORT' }\n | {\n type: 'PARTIAL';\n text: string;\n segmentId: string;\n confidence?: number;\n }\n | {\n type: 'FINAL';\n text: string;\n segmentId: string;\n confidence?: number;\n }\n | { type: 'ERROR'; error: RecognitionError }\n | { type: 'RESET' };\n\nfunction nowSinceStart(state: RecognitionState): number {\n return state.startedAt ? Date.now() - state.startedAt : 0;\n}\n\nfunction upsertSegment(\n segments: Segment[],\n patch: Segment,\n): Segment[] {\n const idx = segments.findIndex((s) => s.id === patch.id);\n if (idx === -1) return [...segments, patch];\n const next = segments.slice();\n next[idx] = { ...next[idx], ...patch };\n return next;\n}\n\nexport function reducer(\n state: RecognitionState,\n action: RecognitionAction,\n): RecognitionState {\n switch (action.type) {\n case 'START':\n return {\n ...state,\n status: 'starting',\n error: null,\n startedAt: Date.now(),\n };\n case 'STARTED':\n return { ...state, status: 'listening' };\n case 'STOP':\n return { ...state, status: 'stopping' };\n case 'STOPPED':\n case 'ABORT':\n return { ...state, status: 'idle' };\n case 'PARTIAL': {\n const seg: Segment = {\n id: action.segmentId,\n text: action.text,\n isFinal: false,\n confidence: action.confidence,\n startedAt: nowSinceStart(state),\n };\n return { ...state, segments: upsertSegment(state.segments, seg) };\n }\n case 'FINAL': {\n const seg: Segment = {\n id: action.segmentId || newSegmentId(),\n text: action.text,\n isFinal: true,\n confidence: action.confidence,\n startedAt: nowSinceStart(state),\n endedAt: nowSinceStart(state),\n };\n return { ...state, segments: upsertSegment(state.segments, seg) };\n }\n case 'ERROR':\n return { ...state, status: 'error', error: action.error };\n case 'RESET':\n return { ...INITIAL_STATE };\n default:\n return state;\n }\n}\n","/**\n * Tiny event-bus helper shared by every engine. Lets engine authors avoid\n * re-implementing add/remove listener bookkeeping while keeping the\n * public `RecognitionEngine.on(...)` contract identical across engines.\n */\n\nimport type { EngineEventMap, Unsub } from '../../types';\n\ntype Listeners = {\n [K in keyof EngineEventMap]: Set<EngineEventMap[K]>;\n};\n\nexport function createEngineBus(): {\n on: <K extends keyof EngineEventMap>(event: K, cb: EngineEventMap[K]) => Unsub;\n emit: <K extends keyof EngineEventMap>(\n event: K,\n ...args: Parameters<EngineEventMap[K]>\n ) => void;\n clear: () => void;\n} {\n const listeners: Listeners = {\n partial: new Set(),\n final: new Set(),\n error: new Set(),\n state: new Set(),\n };\n\n return {\n on(event, cb) {\n const set = listeners[event] as Set<typeof cb>;\n set.add(cb);\n return () => {\n set.delete(cb);\n };\n },\n emit(event, ...args) {\n const set = listeners[event];\n for (const cb of set) {\n try {\n (cb as (...a: unknown[]) => void)(...(args as unknown[]));\n } catch {\n // listener errors are isolated — never break the engine loop\n }\n }\n },\n clear() {\n for (const key of Object.keys(listeners) as Array<keyof Listeners>) {\n listeners[key].clear();\n }\n },\n };\n}\n","/**\n * Default engine — wraps the browser's `SpeechRecognition` API.\n *\n * Lives behind the same `RecognitionEngine` contract every other engine\n * implements. When the browser doesn't expose `SpeechRecognition`\n * (Firefox, some mobile WebViews) `isSupported` is `false` and `start()`\n * throws an `unsupported` error.\n */\n\nimport { newSegmentId } from '../ids';\nimport { sttLogger } from '../logger';\nimport { createEngineBus } from './index';\nimport type {\n EngineStartOptions,\n RecognitionEngine,\n RecognitionError,\n RecognitionErrorCode,\n Unsub,\n} from '../../types';\n\n// Minimal subset of the Web Speech API we actually rely on. Browsers\n// expose either `SpeechRecognition` (Edge / Safari new) or the older\n// `webkitSpeechRecognition` (Chrome). Both share the same shape.\ninterface BrowserSpeechRecognition extends EventTarget {\n lang: string;\n interimResults: boolean;\n continuous: boolean;\n maxAlternatives: number;\n start(): void;\n stop(): void;\n abort(): void;\n onresult: ((e: BrowserSpeechRecognitionEvent) => void) | null;\n onerror: ((e: BrowserSpeechRecognitionError) => void) | null;\n onstart: (() => void) | null;\n onend: (() => void) | null;\n}\n\ninterface BrowserSpeechRecognitionResult {\n isFinal: boolean;\n 0: { transcript: string; confidence: number };\n}\n\ninterface BrowserSpeechRecognitionEvent extends Event {\n resultIndex: number;\n results: ArrayLike<BrowserSpeechRecognitionResult>;\n}\n\ninterface BrowserSpeechRecognitionError extends Event {\n error: string;\n message?: string;\n}\n\ntype Ctor = new () => BrowserSpeechRecognition;\n\nfunction resolveCtor(): Ctor | null {\n if (typeof window === 'undefined') return null;\n const w = window as unknown as {\n SpeechRecognition?: Ctor;\n webkitSpeechRecognition?: Ctor;\n };\n return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;\n}\n\nconst ERROR_MAP: Record<string, RecognitionErrorCode> = {\n 'no-speech': 'no-speech',\n aborted: 'aborted',\n 'audio-capture': 'no-microphone',\n network: 'network',\n 'not-allowed': 'permission-denied',\n 'service-not-allowed': 'permission-denied',\n 'bad-grammar': 'engine',\n 'language-not-supported': 'language',\n};\n\nexport interface WebSpeechEngineOptions {\n /** Whether the underlying recognition should be continuous. Default true. */\n continuous?: boolean;\n /** Max alternatives the engine should request. Default 1. */\n maxAlternatives?: number;\n}\n\nexport function createWebSpeechEngine(\n opts: WebSpeechEngineOptions = {},\n): RecognitionEngine {\n const Ctor = resolveCtor();\n const bus = createEngineBus();\n let instance: BrowserSpeechRecognition | null = null;\n let currentSegmentId: string | null = null;\n\n function teardown(): void {\n if (!instance) return;\n instance.onresult = null;\n instance.onerror = null;\n instance.onstart = null;\n instance.onend = null;\n instance = null;\n currentSegmentId = null;\n }\n\n return {\n id: 'webspeech',\n isSupported: Ctor !== null,\n on(event, cb): Unsub {\n return bus.on(event, cb);\n },\n async start(start: EngineStartOptions): Promise<void> {\n if (!Ctor) {\n const err: RecognitionError = {\n code: 'unsupported',\n message: 'Web Speech API is not available in this browser.',\n };\n bus.emit('error', err);\n throw err;\n }\n if (instance) {\n sttLogger.debug('[webspeech] start() called while running — ignoring');\n return;\n }\n\n bus.emit('state', 'connecting');\n\n const rec = new Ctor();\n rec.lang = start.language;\n rec.interimResults = start.interim;\n rec.continuous = opts.continuous ?? true;\n rec.maxAlternatives = opts.maxAlternatives ?? 1;\n\n rec.onstart = () => {\n bus.emit('state', 'listening');\n };\n rec.onend = () => {\n bus.emit('state', 'closed');\n teardown();\n };\n rec.onerror = (e) => {\n const code = ERROR_MAP[e.error] ?? 'engine';\n const err: RecognitionError = {\n code,\n message: e.message || `Web Speech error: ${e.error}`,\n };\n bus.emit('error', err);\n };\n rec.onresult = (e) => {\n for (let i = e.resultIndex; i < e.results.length; i += 1) {\n const res = e.results[i];\n const alt = res[0];\n const text = alt.transcript;\n if (!currentSegmentId) currentSegmentId = newSegmentId();\n if (res.isFinal) {\n bus.emit('final', text, currentSegmentId, alt.confidence);\n currentSegmentId = null;\n } else {\n bus.emit('partial', text, currentSegmentId);\n }\n }\n };\n\n if (start.signal) {\n start.signal.addEventListener('abort', () => {\n rec.abort();\n });\n }\n\n instance = rec;\n try {\n rec.start();\n } catch (cause) {\n const err: RecognitionError = {\n code: 'engine',\n message: 'Failed to start Web Speech recognition.',\n cause,\n };\n bus.emit('error', err);\n teardown();\n throw err;\n }\n },\n async stop(): Promise<void> {\n if (!instance) return;\n bus.emit('state', 'closing');\n instance.stop();\n },\n abort(): void {\n if (!instance) return;\n instance.abort();\n },\n };\n}\n","'use client';\n\nimport { create } from 'zustand';\nimport { persist, createJSONStorage } from 'zustand/middleware';\n\nexport interface SpeechPrefs {\n /**\n * BCP-47 tag the user explicitly picked (via `<LanguagePicker>` or\n * programmatically). `null` means \"no override\" — `useResolvedLanguage`\n * then falls through to the app i18n locale / `navigator.language`.\n * Storing the picker default as `null` is what lets a host's i18n\n * locale take effect when the user never touched the picker.\n */\n language: string | null;\n deviceId: string | null;\n engineId: string | null;\n earcons: boolean;\n}\n\nconst DEFAULTS: SpeechPrefs = {\n language: null,\n deviceId: null,\n engineId: null,\n earcons: false,\n};\n\ninterface PrefsStore extends SpeechPrefs {\n setLanguage: (v: string | null) => void;\n setDeviceId: (v: string | null) => void;\n setEngineId: (v: string | null) => void;\n setEarcons: (v: boolean) => void;\n reset: () => void;\n}\n\nexport const useSpeechPrefs = create<PrefsStore>()(\n persist(\n (set) => ({\n ...DEFAULTS,\n setLanguage: (language) => set({ language }),\n setDeviceId: (deviceId) => set({ deviceId }),\n setEngineId: (engineId) => set({ engineId }),\n setEarcons: (earcons) => set({ earcons }),\n reset: () => set({ ...DEFAULTS }),\n }),\n {\n name: 'djangocfg-stt:prefs',\n storage: createJSONStorage(() =>\n typeof window === 'undefined'\n ? (undefined as unknown as Storage)\n : window.localStorage,\n ),\n },\n ),\n);\n","'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\n/**\n * RMS level meter driven by an `AnalyserNode`. Attach a `MediaStream`\n * (the one returned from `startMicCapture`) and read `level` (0..1) for\n * VU meters / mic-pulse animations. Returns 0 when no stream is bound.\n */\nexport function useMicLevel(stream: MediaStream | null): number {\n const [level, setLevel] = useState(0);\n const raf = useRef<number | null>(null);\n\n useEffect(() => {\n if (!stream) {\n setLevel(0);\n return undefined;\n }\n const AC =\n (window as unknown as { AudioContext?: typeof AudioContext }).AudioContext ??\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!AC) return undefined;\n const ctx = new AC();\n const source = ctx.createMediaStreamSource(stream);\n const analyser = ctx.createAnalyser();\n analyser.fftSize = 1024;\n analyser.smoothingTimeConstant = 0.7;\n source.connect(analyser);\n const buf = new Float32Array(analyser.fftSize);\n\n const tick = (): void => {\n analyser.getFloatTimeDomainData(buf);\n let sum = 0;\n for (let i = 0; i < buf.length; i += 1) sum += buf[i] * buf[i];\n const rms = Math.sqrt(sum / buf.length);\n // soft compression so loud peaks don't dominate the meter\n setLevel(Math.min(1, rms * 2.5));\n raf.current = requestAnimationFrame(tick);\n };\n tick();\n\n return () => {\n if (raf.current != null) cancelAnimationFrame(raf.current);\n raf.current = null;\n source.disconnect();\n analyser.disconnect();\n void ctx.close();\n };\n }, [stream]);\n\n return level;\n}\n","/**\n * Maps 2-letter ISO 639-1 codes (`en`, `ru`, `ko` — what\n * `@djangocfg/i18n` exposes via `useLocale()`) to BCP-47 tags\n * (`en-US`, `ru-RU`, `ko-KR`) that the Web Speech API and most cloud\n * STT services expect.\n *\n * We keep a small built-in table for the locales we ship translations\n * for; everything else falls through to `<code>-<UPPER(code)>`, which\n * works for the majority of regions. The mapping is also re-exported\n * so consumers can extend it.\n */\n\nconst ISO_TO_BCP47: Record<string, string> = {\n en: 'en-US',\n ru: 'ru-RU',\n ko: 'ko-KR',\n ja: 'ja-JP',\n zh: 'zh-CN',\n de: 'de-DE',\n fr: 'fr-FR',\n it: 'it-IT',\n es: 'es-ES',\n nl: 'nl-NL',\n ar: 'ar-SA',\n tr: 'tr-TR',\n pl: 'pl-PL',\n sv: 'sv-SE',\n no: 'nb-NO',\n da: 'da-DK',\n pt: 'pt-BR',\n};\n\nexport const DEFAULT_ISO_TO_BCP47 = ISO_TO_BCP47;\n\n/**\n * Normalise any of:\n * - BCP-47 (\"en-US\", \"ru-RU\") — passed through.\n * - ISO 639-1 (\"en\", \"ru\") — mapped via the table above, or\n * falls back to `<code>-<UPPER(code)>`.\n * - `null`/`undefined`/empty — returns `undefined`.\n */\nexport function toBCP47(\n code: string | null | undefined,\n table: Record<string, string> = ISO_TO_BCP47,\n): string | undefined {\n if (!code) return undefined;\n const trimmed = code.trim();\n if (!trimmed) return undefined;\n if (trimmed.includes('-')) return trimmed; // already BCP-47\n const lower = trimmed.toLowerCase();\n return table[lower] ?? `${lower}-${lower.toUpperCase()}`;\n}\n\n/**\n * Resolve the language tag for a speech session in priority order:\n * 1. `explicit` prop (always wins) — host-supplied override.\n * 2. `prefs` — value stored in `useSpeechPrefs` (user picked it\n * via `<LanguagePicker>` or programmatically).\n * 3. `i18n` — current i18n locale (2-letter ISO).\n * 4. `navigator.language` — browser default.\n * 5. `'en-US'` — last-resort safety net.\n *\n * All inputs may be ISO-2 or BCP-47; the function normalises before\n * returning.\n */\nexport function resolveSpeechLanguage(opts: {\n explicit?: string;\n prefs?: string | null;\n i18n?: string | null;\n}): string {\n return (\n toBCP47(opts.explicit) ??\n toBCP47(opts.prefs) ??\n toBCP47(opts.i18n) ??\n toBCP47(typeof navigator !== 'undefined' ? navigator.language : null) ??\n 'en-US'\n );\n}\n","'use client';\n\nimport { useLocaleOptional } from '@djangocfg/i18n';\n\nimport { resolveSpeechLanguage } from '../core/language';\nimport { useSpeechPrefs } from '../store/prefsStore';\n\n/**\n * Resolves the BCP-47 language tag a speech session should use.\n *\n * Priority: explicit prop → user-picked `useSpeechPrefs.language` →\n * app i18n locale (when an `<I18nProvider>` is mounted) →\n * `navigator.language` → `en-US`.\n *\n * Uses `useLocaleOptional` (not `useLocale`) so that an unmounted\n * provider doesn't silently inject `'en'`. Without that, the i18n\n * default would shadow the user's real browser language — a Russian\n * speaker would always get `en-US` recognition.\n */\nexport function useResolvedLanguage(explicit?: string): string {\n const prefs = useSpeechPrefs();\n const locale = useLocaleOptional();\n return resolveSpeechLanguage({\n explicit,\n prefs: prefs.language,\n i18n: locale,\n });\n}\n","'use client';\n\nimport { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';\n\nimport {\n EMPTY_TRANSCRIPT,\n INITIAL_STATE,\n buildTranscript,\n reducer,\n sttLogger,\n} from '../core';\nimport { createWebSpeechEngine } from '../core/engine/webspeech';\nimport { useSpeechPrefs } from '../store/prefsStore';\nimport type {\n RecognitionEngine,\n Segment,\n UseSpeechRecognitionConfig,\n UseSpeechRecognitionReturn,\n} from '../types';\nimport { useMicLevel } from './useMicLevel';\nimport { useResolvedLanguage } from './useResolvedLanguage';\n\n/**\n * Main entry point. With no config it uses the browser Web Speech API\n * and the persisted language from `useSpeechPrefs`. Pass a custom\n * `engine` to route through Deepgram / Whisper / custom WebSocket.\n */\nexport function useSpeechRecognition(\n config: UseSpeechRecognitionConfig = {},\n): UseSpeechRecognitionReturn {\n const prefs = useSpeechPrefs();\n const language = useResolvedLanguage(config.language);\n const engine = useMemo<RecognitionEngine>(\n () => config.engine ?? createWebSpeechEngine(),\n [config.engine],\n );\n\n const [state, dispatch] = useReducer(reducer, INITIAL_STATE);\n const [stream, setStream] = useState<MediaStream | null>(null);\n const level = useMicLevel(stream);\n\n // Latest-callback refs so engine subscriptions never tear down on\n // every render — same trick the Chat reducer uses.\n const cbRef = useRef(config);\n cbRef.current = config;\n\n // Engine subscription lifecycle.\n useEffect(() => {\n const offs = [\n engine.on('partial', (text, segmentId) => {\n dispatch({ type: 'PARTIAL', text, segmentId });\n const seg: Segment = {\n id: segmentId,\n text,\n isFinal: false,\n startedAt: Date.now(),\n };\n cbRef.current.onPartial?.(text, seg);\n }),\n engine.on('final', (text, segmentId, confidence) => {\n dispatch({ type: 'FINAL', text, segmentId, confidence });\n const seg: Segment = {\n id: segmentId,\n text,\n isFinal: true,\n confidence,\n startedAt: Date.now(),\n endedAt: Date.now(),\n };\n cbRef.current.onFinal?.(text, seg);\n }),\n engine.on('error', (err) => {\n dispatch({ type: 'ERROR', error: err });\n cbRef.current.onError?.(err);\n }),\n engine.on('state', (s) => {\n if (s === 'listening') {\n dispatch({ type: 'STARTED' });\n cbRef.current.onStart?.();\n setStream(engine.getStream?.() ?? null);\n } else if (s === 'closed') {\n dispatch({ type: 'STOPPED' });\n cbRef.current.onStop?.();\n setStream(null);\n }\n }),\n ];\n return () => {\n offs.forEach((off) => off());\n };\n }, [engine]);\n\n // AutoStop driven by silence + maxMs caps.\n const silenceTimer = useRef<number | null>(null);\n const maxTimer = useRef<number | null>(null);\n useEffect(() => {\n if (state.status !== 'listening') return undefined;\n const { silenceMs, maxMs, silenceThreshold = 0.02 } = config.autoStop ?? {};\n if (maxMs) {\n maxTimer.current = window.setTimeout(() => {\n sttLogger.debug('[autoStop] max duration hit');\n void engine.stop();\n }, maxMs);\n }\n if (silenceMs) {\n const checkInterval = window.setInterval(() => {\n if (level < silenceThreshold) {\n if (silenceTimer.current == null) {\n silenceTimer.current = window.setTimeout(() => {\n sttLogger.debug('[autoStop] silence detected');\n void engine.stop();\n }, silenceMs);\n }\n } else if (silenceTimer.current != null) {\n clearTimeout(silenceTimer.current);\n silenceTimer.current = null;\n }\n }, 200);\n return () => {\n clearInterval(checkInterval);\n if (silenceTimer.current != null) clearTimeout(silenceTimer.current);\n silenceTimer.current = null;\n if (maxTimer.current != null) clearTimeout(maxTimer.current);\n maxTimer.current = null;\n };\n }\n return () => {\n if (maxTimer.current != null) clearTimeout(maxTimer.current);\n maxTimer.current = null;\n };\n }, [state.status, config.autoStop, level, engine]);\n\n const start = useCallback(async () => {\n if (state.status === 'listening' || state.status === 'starting') return;\n dispatch({ type: 'START' });\n try {\n await engine.start({\n language,\n interim: config.interim ?? true,\n deviceId: config.deviceId ?? prefs.deviceId ?? undefined,\n });\n } catch (cause) {\n // engine already emitted 'error'; reducer caught it via subscription\n sttLogger.debug('[start] engine threw', cause);\n }\n }, [engine, language, config.interim, config.deviceId, prefs.deviceId, state.status]);\n\n const stop = useCallback(async () => {\n if (state.status === 'idle' || state.status === 'stopping') return;\n dispatch({ type: 'STOP' });\n await engine.stop();\n }, [engine, state.status]);\n\n const abort = useCallback(() => {\n engine.abort();\n dispatch({ type: 'ABORT' });\n }, [engine]);\n\n const toggle = useCallback(async () => {\n if (state.status === 'listening' || state.status === 'starting') {\n await stop();\n } else {\n await start();\n }\n }, [state.status, start, stop]);\n\n const reset = useCallback(() => {\n dispatch({ type: 'RESET' });\n }, []);\n\n const transcript = useMemo(\n () => (state.segments.length === 0 ? EMPTY_TRANSCRIPT : buildTranscript(state.segments)),\n [state.segments],\n );\n\n return {\n status: state.status,\n isSupported: engine.isSupported,\n transcript,\n error: state.error,\n level,\n start,\n stop,\n abort,\n toggle,\n reset,\n };\n}\n","'use client';\n\nimport { useEffect, useRef } from 'react';\n\nimport { normaliseFinal } from '../core/transcript';\nimport type { UseSpeechRecognitionConfig, UseSpeechRecognitionReturn } from '../types';\nimport { useSpeechRecognition } from './useSpeechRecognition';\n\nexport interface UseDictationConfig\n extends Omit<UseSpeechRecognitionConfig, 'onFinal'> {\n /** Controlled value the dictation is appending to. */\n value: string;\n /** Called with the next value after each final segment lands. */\n onChange: (next: string) => void;\n /** Joiner between the previous value and the new segment. Default ' '. */\n separator?: string;\n}\n\nexport interface UseDictationReturn extends UseSpeechRecognitionReturn {\n /** Convenience — same as `toggle`, named for dictation UIs. */\n toggleDictation: () => Promise<void>;\n}\n\n/**\n * Convenience adapter that pipes final transcript segments straight into\n * a controlled string (`<textarea>` / `<input>` / TipTap). Interim text\n * is left alone — bind `transcript.interim` separately if you want to\n * show a live ghost.\n */\nexport function useDictation(config: UseDictationConfig): UseDictationReturn {\n const { value, onChange, separator = ' ', ...rest } = config;\n\n // Stash latest value in a ref so the onFinal closure always sees the\n // freshest text without forcing the underlying hook to resubscribe.\n const valueRef = useRef(value);\n valueRef.current = value;\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n\n const rec = useSpeechRecognition({\n ...rest,\n onFinal: (text) => {\n const clean = normaliseFinal(text);\n if (!clean) return;\n const prev = valueRef.current;\n const next = prev ? `${prev}${separator}${clean}` : clean;\n onChangeRef.current(next);\n },\n });\n\n useEffect(() => {\n valueRef.current = value;\n }, [value]);\n\n return {\n ...rec,\n toggleDictation: rec.toggle,\n };\n}\n","'use client';\n\nimport { useEffect } from 'react';\n\nimport type { UseSpeechRecognitionReturn } from '../types';\n\nexport interface UsePushToTalkOptions {\n /** Key to hold. Combine modifiers with `+`, e.g. `'alt'`, `'mod+alt'`. */\n key: string;\n /** Disable the binding without unmounting. */\n enabled?: boolean;\n}\n\nconst MOD_KEYS = new Set(['shift', 'ctrl', 'alt', 'meta', 'mod']);\n\nfunction parseChord(chord: string): { mods: Set<string>; main: string | null } {\n const parts = chord\n .toLowerCase()\n .split('+')\n .map((s) => s.trim());\n const mods = new Set<string>();\n let main: string | null = null;\n for (const part of parts) {\n if (MOD_KEYS.has(part)) {\n mods.add(part === 'mod' ? 'meta' : part);\n } else {\n main = part;\n }\n }\n return { mods, main };\n}\n\nfunction matches(e: KeyboardEvent, mods: Set<string>, main: string | null): boolean {\n if (mods.has('shift') !== e.shiftKey) return false;\n if (mods.has('ctrl') !== e.ctrlKey) return false;\n if (mods.has('alt') !== e.altKey) return false;\n // 'mod' → meta on mac / ctrl elsewhere; we already normalised to 'meta'.\n if (mods.has('meta') !== (e.metaKey || (!e.metaKey && false))) return false;\n if (main && e.key.toLowerCase() !== main) return false;\n return true;\n}\n\n/**\n * Hold-to-talk wiring. Press → `start()`, release → `stop()`. Ignores\n * repeats and skips keydown inside `<input>` / `<textarea>` unless a\n * modifier is in the chord.\n */\nexport function usePushToTalk(\n recognition: Pick<UseSpeechRecognitionReturn, 'start' | 'stop' | 'status'>,\n opts: UsePushToTalkOptions,\n): void {\n const { key, enabled = true } = opts;\n\n useEffect(() => {\n if (!enabled || typeof window === 'undefined') return undefined;\n const { mods, main } = parseChord(key);\n\n const onDown = (e: KeyboardEvent): void => {\n if (e.repeat) return;\n if (!main && mods.size === 0) return;\n const target = e.target as HTMLElement | null;\n const inField =\n target?.tagName === 'INPUT' ||\n target?.tagName === 'TEXTAREA' ||\n target?.isContentEditable;\n if (inField && mods.size === 0) return;\n if (!matches(e, mods, main)) return;\n if (recognition.status === 'listening' || recognition.status === 'starting') return;\n e.preventDefault();\n void recognition.start();\n };\n const onUp = (e: KeyboardEvent): void => {\n if (!matches(e, mods, main) && main && e.key.toLowerCase() !== main) return;\n if (recognition.status !== 'listening' && recognition.status !== 'starting') return;\n void recognition.stop();\n };\n\n window.addEventListener('keydown', onDown);\n window.addEventListener('keyup', onUp);\n return () => {\n window.removeEventListener('keydown', onDown);\n window.removeEventListener('keyup', onUp);\n };\n }, [enabled, key, recognition]);\n}\n","'use client';\n\nimport type * as React from 'react';\n\nimport { cn } from '@djangocfg/ui-core/lib';\n\nimport { DictationButton } from '../components/DictationButton';\nimport { ErrorBanner } from '../components/ErrorBanner';\nimport { MicMeter } from '../components/MicMeter';\nimport { PushToTalkHint } from '../components/PushToTalkHint';\nimport { useDictation } from '../hooks/useDictation';\nimport { usePushToTalk } from '../hooks/usePushToTalk';\nimport type { RecognitionEngine } from '../types';\n\nexport interface DictationFieldProps {\n value: string;\n onChange: (next: string) => void;\n /** Custom engine. Defaults to Web Speech via `useSpeechRecognition`. */\n engine?: RecognitionEngine;\n /** Override the language stored in `useSpeechPrefs`. */\n language?: string;\n /** Push-to-talk chord (e.g. `'alt'`, `'mod+alt'`). Disabled when omitted. */\n pushToTalk?: { key: string; enabled?: boolean };\n placeholder?: string;\n rows?: number;\n disabled?: boolean;\n /** Show the interim transcript as a ghost overlay below the textarea. */\n showInterim?: boolean;\n /** Show a small RMS meter inside the toolbar. */\n showMeter?: boolean;\n className?: string;\n textareaClassName?: string;\n}\n\n/**\n * Opinionated textarea + dictation button assembly. Final segments are\n * appended to the controlled `value` automatically. Press-and-hold\n * shortcut is optional; the mic button itself works as a toggle.\n */\nexport function DictationField({\n value,\n onChange,\n engine,\n language,\n pushToTalk,\n placeholder = 'Type or press the mic to dictate…',\n rows = 3,\n disabled,\n showInterim = true,\n showMeter = true,\n className,\n textareaClassName,\n}: DictationFieldProps): React.ReactElement {\n const rec = useDictation({\n value,\n onChange,\n engine,\n language,\n });\n\n usePushToTalk(rec, {\n key: pushToTalk?.key ?? 'alt',\n enabled: !!pushToTalk && pushToTalk.enabled !== false,\n });\n\n return (\n <div className={cn('flex flex-col gap-2', className)}>\n <div className=\"relative\">\n <textarea\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n rows={rows}\n disabled={disabled}\n className={cn(\n 'w-full resize-y rounded-md border border-input bg-background px-3 py-2 text-sm',\n 'placeholder:text-muted-foreground',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n textareaClassName,\n )}\n />\n {showInterim && rec.transcript.interim && (\n <div className=\"pointer-events-none mt-1 text-xs italic text-muted-foreground\">\n … {rec.transcript.interim}\n </div>\n )}\n </div>\n\n <div className=\"flex items-center gap-2\">\n <DictationButton\n status={rec.status}\n isSupported={rec.isSupported}\n onClick={() => void rec.toggleDictation()}\n size=\"sm\"\n disabled={disabled}\n />\n {showMeter && <MicMeter level={rec.level} bars={10} height={20} />}\n {pushToTalk && <PushToTalkHint chord={pushToTalk.key} className=\"ml-auto\" />}\n </div>\n\n <ErrorBanner error={rec.error} />\n </div>\n );\n}\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
require('./chunk-
|
|
3
|
+
var chunk4PFW7MIJ_cjs = require('./chunk-4PFW7MIJ.cjs');
|
|
4
4
|
var chunkFEN5S772_cjs = require('./chunk-FEN5S772.cjs');
|
|
5
5
|
var chunkQJ6GTUCO_cjs = require('./chunk-QJ6GTUCO.cjs');
|
|
6
6
|
var chunkYDPDTOSP_cjs = require('./chunk-YDPDTOSP.cjs');
|
|
@@ -1632,9 +1632,30 @@ var WEB_SPEECH_LANGUAGES = [
|
|
|
1632
1632
|
WEB_SPEECH_LANGUAGES.flatMap(
|
|
1633
1633
|
(l) => l.dialects.map((d) => d.code)
|
|
1634
1634
|
);
|
|
1635
|
+
function findSpeechLanguage(tag) {
|
|
1636
|
+
if (!tag) return null;
|
|
1637
|
+
const lower = tag.toLowerCase();
|
|
1638
|
+
for (const language of WEB_SPEECH_LANGUAGES) {
|
|
1639
|
+
for (const dialect of language.dialects) {
|
|
1640
|
+
if (dialect.code.toLowerCase() === lower) return { language, dialect };
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
return null;
|
|
1644
|
+
}
|
|
1645
|
+
chunkOLISEQHS_cjs.__name(findSpeechLanguage, "findSpeechLanguage");
|
|
1646
|
+
function countryFromTag(tag) {
|
|
1647
|
+
if (!tag) return null;
|
|
1648
|
+
const parts = tag.split("-");
|
|
1649
|
+
for (let i = parts.length - 1; i >= 0; i -= 1) {
|
|
1650
|
+
const p = parts[i];
|
|
1651
|
+
if (p.length === 2 && /^[A-Za-z]{2}$/.test(p)) return p.toUpperCase();
|
|
1652
|
+
}
|
|
1653
|
+
return null;
|
|
1654
|
+
}
|
|
1655
|
+
chunkOLISEQHS_cjs.__name(countryFromTag, "countryFromTag");
|
|
1635
1656
|
React.createContext(null);
|
|
1636
1657
|
createLazyComponent(
|
|
1637
|
-
() => import('./DictationField-
|
|
1658
|
+
() => import('./DictationField-XWR5VOID.cjs').then((mod) => ({
|
|
1638
1659
|
default: mod.DictationField
|
|
1639
1660
|
})),
|
|
1640
1661
|
{
|
|
@@ -1642,6 +1663,93 @@ createLazyComponent(
|
|
|
1642
1663
|
fallback: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-border/60 bg-card px-3 py-2 text-xs text-muted-foreground", children: "Loading dictation\u2026" })
|
|
1643
1664
|
}
|
|
1644
1665
|
);
|
|
1666
|
+
function ChatHeaderLanguageButton({
|
|
1667
|
+
ariaLabel = "Speech language",
|
|
1668
|
+
allowedTags,
|
|
1669
|
+
hideFallbackIcon,
|
|
1670
|
+
className
|
|
1671
|
+
}) {
|
|
1672
|
+
const prefs = chunk4PFW7MIJ_cjs.useSpeechPrefs();
|
|
1673
|
+
const active = chunk4PFW7MIJ_cjs.useResolvedLanguage();
|
|
1674
|
+
const options = React.useMemo(() => {
|
|
1675
|
+
const allow = allowedTags ? new Set(allowedTags) : null;
|
|
1676
|
+
const out = [];
|
|
1677
|
+
for (const lang of WEB_SPEECH_LANGUAGES) {
|
|
1678
|
+
for (const d of lang.dialects) {
|
|
1679
|
+
if (allow && !allow.has(d.code)) continue;
|
|
1680
|
+
out.push({
|
|
1681
|
+
value: d.code,
|
|
1682
|
+
// "Русский" / "Español — Argentina" / "English — United States"
|
|
1683
|
+
label: lang.dialects.length === 1 ? lang.name : `${lang.name} \u2014 ${d.region}`,
|
|
1684
|
+
// Search-only index: English name, BCP-47 tag, ISO, region.
|
|
1685
|
+
// Lets users type "russian" / "ru-RU" / "ru" / "argentina"
|
|
1686
|
+
// and still find the row regardless of native script.
|
|
1687
|
+
description: `${lang.englishName} ${d.code} ${lang.iso} ${d.region}`.toLowerCase()
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
return out;
|
|
1692
|
+
}, [allowedTags]);
|
|
1693
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1694
|
+
components.Combobox,
|
|
1695
|
+
{
|
|
1696
|
+
options,
|
|
1697
|
+
value: prefs.language ?? active,
|
|
1698
|
+
onValueChange: (v) => prefs.setLanguage(v || null),
|
|
1699
|
+
placeholder: ariaLabel,
|
|
1700
|
+
searchPlaceholder: "Search language\u2026",
|
|
1701
|
+
filterFunction: (opt, search) => {
|
|
1702
|
+
const s = search.toLowerCase();
|
|
1703
|
+
return opt.label.toLowerCase().includes(s) || opt.value.toLowerCase().includes(s) || (opt.description?.includes(s) ?? false);
|
|
1704
|
+
},
|
|
1705
|
+
contentClassName: "w-[280px]",
|
|
1706
|
+
contentStyle: { zIndex: 10001 },
|
|
1707
|
+
renderOption: (option) => {
|
|
1708
|
+
const country = countryFromTag(option.value);
|
|
1709
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
1710
|
+
country ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1711
|
+
components.Flag,
|
|
1712
|
+
{
|
|
1713
|
+
countryCode: country,
|
|
1714
|
+
className: "h-4 w-5 shrink-0 overflow-hidden rounded-[2px] border border-border/60 ring-1 ring-black/5"
|
|
1715
|
+
}
|
|
1716
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": true }),
|
|
1717
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm", children: option.label })
|
|
1718
|
+
] });
|
|
1719
|
+
},
|
|
1720
|
+
renderTrigger: (selected, open) => {
|
|
1721
|
+
const tag = selected?.value ?? active;
|
|
1722
|
+
const country = countryFromTag(tag);
|
|
1723
|
+
const found = findSpeechLanguage(tag);
|
|
1724
|
+
const tooltipLabel = found ? `${found.language.name}${found.language.dialects.length > 1 ? ` \u2014 ${found.dialect.region}` : ""} \xB7 ${tag}` : tag;
|
|
1725
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1726
|
+
"button",
|
|
1727
|
+
{
|
|
1728
|
+
type: "button",
|
|
1729
|
+
"aria-label": `${ariaLabel}: ${tooltipLabel}`,
|
|
1730
|
+
"aria-expanded": open,
|
|
1731
|
+
title: tooltipLabel,
|
|
1732
|
+
className: lib.cn(
|
|
1733
|
+
"inline-flex h-7 w-7 items-center justify-center rounded-md",
|
|
1734
|
+
"text-muted-foreground transition-colors",
|
|
1735
|
+
"hover:bg-accent hover:text-foreground",
|
|
1736
|
+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
1737
|
+
className
|
|
1738
|
+
),
|
|
1739
|
+
children: country ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1740
|
+
components.Flag,
|
|
1741
|
+
{
|
|
1742
|
+
countryCode: country,
|
|
1743
|
+
className: "h-4 w-5 overflow-hidden rounded-[2px] border border-border/60 ring-1 ring-black/5"
|
|
1744
|
+
}
|
|
1745
|
+
) : hideFallbackIcon ? null : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe, { className: "h-3.5 w-3.5", "aria-hidden": true })
|
|
1746
|
+
}
|
|
1747
|
+
);
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
);
|
|
1751
|
+
}
|
|
1752
|
+
chunkOLISEQHS_cjs.__name(ChatHeaderLanguageButton, "ChatHeaderLanguageButton");
|
|
1645
1753
|
function anchorStyle(position, fabOffset, fabClearance) {
|
|
1646
1754
|
const [vert, horiz] = position.split("-");
|
|
1647
1755
|
return { [vert]: fabClearance, [horiz]: fabOffset };
|
|
@@ -2211,6 +2319,33 @@ function useChatDockPrefs(opts = {}) {
|
|
|
2211
2319
|
return { ...prefs, setPrefs, toggleMode, toggleSide, reset };
|
|
2212
2320
|
}
|
|
2213
2321
|
chunkOLISEQHS_cjs.__name(useChatDockPrefs, "useChatDockPrefs");
|
|
2322
|
+
function useChatUnread(opts = {}) {
|
|
2323
|
+
const { open = false, countRoles = ["assistant"] } = opts;
|
|
2324
|
+
const ctx = chunkOZAU3QWD_cjs.useChatContext();
|
|
2325
|
+
const [lastSeenId, setLastSeenId] = React.useState(null);
|
|
2326
|
+
const initialized = React.useRef(false);
|
|
2327
|
+
React.useEffect(() => {
|
|
2328
|
+
if (initialized.current) return;
|
|
2329
|
+
initialized.current = true;
|
|
2330
|
+
const tail = ctx.messages[ctx.messages.length - 1];
|
|
2331
|
+
setLastSeenId(tail?.id ?? null);
|
|
2332
|
+
}, [ctx.messages]);
|
|
2333
|
+
React.useEffect(() => {
|
|
2334
|
+
if (!open) return;
|
|
2335
|
+
const tail = ctx.messages[ctx.messages.length - 1];
|
|
2336
|
+
setLastSeenId(tail?.id ?? null);
|
|
2337
|
+
}, [open, ctx.messages]);
|
|
2338
|
+
const seenIdx = lastSeenId ? ctx.messages.findIndex((m) => m.id === lastSeenId) : -1;
|
|
2339
|
+
const after = seenIdx === -1 ? ctx.messages : ctx.messages.slice(seenIdx + 1);
|
|
2340
|
+
const inbound = after.filter((m) => countRoles.includes(m.role));
|
|
2341
|
+
const unread = inbound.length > 0 ? inbound[inbound.length - 1] : null;
|
|
2342
|
+
const markRead = React.useCallback(() => {
|
|
2343
|
+
const tail = ctx.messages[ctx.messages.length - 1];
|
|
2344
|
+
setLastSeenId(tail?.id ?? null);
|
|
2345
|
+
}, [ctx.messages]);
|
|
2346
|
+
return { unread, count: inbound.length, markRead };
|
|
2347
|
+
}
|
|
2348
|
+
chunkOLISEQHS_cjs.__name(useChatUnread, "useChatUnread");
|
|
2214
2349
|
var STORAGE_KEY = "djangocfg-chat-audio:prefs";
|
|
2215
2350
|
var useChatAudioPrefs = hooks.createAudioPrefsStore(STORAGE_KEY);
|
|
2216
2351
|
|
|
@@ -3995,6 +4130,7 @@ exports.ChatGreeting = ChatGreeting;
|
|
|
3995
4130
|
exports.ChatHeader = ChatHeader;
|
|
3996
4131
|
exports.ChatHeaderActionButton = ChatHeaderActionButton;
|
|
3997
4132
|
exports.ChatHeaderAudioToggle = ChatHeaderAudioToggle;
|
|
4133
|
+
exports.ChatHeaderLanguageButton = ChatHeaderLanguageButton;
|
|
3998
4134
|
exports.ChatHeaderModeToggle = ChatHeaderModeToggle;
|
|
3999
4135
|
exports.ChatHeaderResetButton = ChatHeaderResetButton;
|
|
4000
4136
|
exports.ChatLauncher = ChatLauncher;
|
|
@@ -4048,6 +4184,7 @@ exports.useChatLightbox = useChatLightbox;
|
|
|
4048
4184
|
exports.useChatPresence = useChatPresence;
|
|
4049
4185
|
exports.useChatReset = useChatReset;
|
|
4050
4186
|
exports.useChatScroll = useChatScroll;
|
|
4187
|
+
exports.useChatUnread = useChatUnread;
|
|
4051
4188
|
exports.useEditor = useEditor;
|
|
4052
4189
|
exports.useEditorContext = useEditorContext;
|
|
4053
4190
|
exports.useLanguage = useLanguage;
|