@alfadocs/ui-kit-debug 0.20.0 → 0.21.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.
- package/dist/_chunks/{agenda-card-CsCm7hQv.js → agenda-card-BmpaUG1h.js} +2 -2
- package/dist/_chunks/{agenda-card-CsCm7hQv.js.map → agenda-card-BmpaUG1h.js.map} +1 -1
- package/dist/_chunks/{agenda-tray-B8QZ3wEp.js → agenda-tray-DiInnVqk.js} +2 -2
- package/dist/_chunks/{agenda-tray-B8QZ3wEp.js.map → agenda-tray-DiInnVqk.js.map} +1 -1
- package/dist/_chunks/{alert-DBnawbmf.js → alert-ywPR59NE.js} +14 -1
- package/dist/_chunks/alert-ywPR59NE.js.map +1 -0
- package/dist/_chunks/{alia-sidebar-Bof6TlFx.js → alia-sidebar-CUi2UCbY.js} +3 -3
- package/dist/_chunks/{alia-sidebar-Bof6TlFx.js.map → alia-sidebar-CUi2UCbY.js.map} +1 -1
- package/dist/_chunks/{audio-recorder-DNkQLW1G.js → audio-recorder-D4xM3H5i.js} +2 -2
- package/dist/_chunks/{audio-recorder-DNkQLW1G.js.map → audio-recorder-D4xM3H5i.js.map} +1 -1
- package/dist/_chunks/{avatar-I10iCDs8.js → avatar-BpFohgWG.js} +24 -16
- package/dist/_chunks/avatar-BpFohgWG.js.map +1 -0
- package/dist/_chunks/{chat-message-ChOnwqf_.js → chat-message-W3k8rLOA.js} +2 -2
- package/dist/_chunks/{chat-message-ChOnwqf_.js.map → chat-message-W3k8rLOA.js.map} +1 -1
- package/dist/_chunks/editable-currency-cell-renderer-1t42gENX.js +1972 -0
- package/dist/_chunks/editable-currency-cell-renderer-1t42gENX.js.map +1 -0
- package/dist/_chunks/{freemium-paywall-DXc7XlGE.js → freemium-paywall-CM6V1zNf.js} +2 -2
- package/dist/_chunks/{freemium-paywall-DXc7XlGE.js.map → freemium-paywall-CM6V1zNf.js.map} +1 -1
- package/dist/_chunks/grip-vertical-Dlg0_k5C.js +19 -0
- package/dist/_chunks/grip-vertical-Dlg0_k5C.js.map +1 -0
- package/dist/_chunks/key-value-pair-DBuOCtIc.js +98 -0
- package/dist/_chunks/key-value-pair-DBuOCtIc.js.map +1 -0
- package/dist/_chunks/{message-card-c6R0-qXq.js → message-card-i61k1TGc.js} +2 -2
- package/dist/_chunks/{message-card-c6R0-qXq.js.map → message-card-i61k1TGc.js.map} +1 -1
- package/dist/_chunks/{message-tray-BNAS8al4.js → message-tray-DrV7G-wR.js} +2 -2
- package/dist/_chunks/{message-tray-BNAS8al4.js.map → message-tray-DrV7G-wR.js.map} +1 -1
- package/dist/_chunks/{patient-shell-Dr64lBp_.js → patient-shell-DavGODt9.js} +2 -2
- package/dist/_chunks/{patient-shell-Dr64lBp_.js.map → patient-shell-DavGODt9.js.map} +1 -1
- package/dist/_chunks/{payment-form-BjkuQeqR.js → payment-form-F7uh0Rqr.js} +2 -2
- package/dist/_chunks/{payment-form-BjkuQeqR.js.map → payment-form-F7uh0Rqr.js.map} +1 -1
- package/dist/_chunks/{pdf-viewer-BG_nsFT5.js → pdf-viewer-CuYaVR1I.js} +2 -2
- package/dist/_chunks/{pdf-viewer-BG_nsFT5.js.map → pdf-viewer-CuYaVR1I.js.map} +1 -1
- package/dist/_chunks/{sidebar-BqzlRBvC.js → sidebar-DkyC6GvS.js} +167 -180
- package/dist/_chunks/sidebar-DkyC6GvS.js.map +1 -0
- package/dist/_chunks/{timeline-rmls7dIh.js → timeline-Czeqr3HF.js} +2 -2
- package/dist/_chunks/{timeline-rmls7dIh.js.map → timeline-Czeqr3HF.js.map} +1 -1
- package/dist/_chunks/{warning-stack-CDH9TudY.js → warning-stack-bDicCvxs.js} +2 -2
- package/dist/_chunks/{warning-stack-CDH9TudY.js.map → warning-stack-bDicCvxs.js.map} +1 -1
- package/dist/_chunks/{whatsapp-button-Bj5FIhpC.js → whatsapp-button-CtlLwM3M.js} +14 -3
- package/dist/_chunks/whatsapp-button-CtlLwM3M.js.map +1 -0
- package/dist/_chunks/{workflow-map-BeKe23uw.js → workflow-map-712GL-8u.js} +3 -3
- package/dist/_chunks/{workflow-map-BeKe23uw.js.map → workflow-map-712GL-8u.js.map} +1 -1
- package/dist/agent-catalog.json +1 -1
- package/dist/components/agenda-card/index.js +1 -1
- package/dist/components/agenda-tray/index.js +1 -1
- package/dist/components/alert/alert.d.ts.map +1 -1
- package/dist/components/alert/index.js +1 -1
- package/dist/components/audio-recorder/index.js +1 -1
- package/dist/components/avatar/avatar.d.ts +1 -1
- package/dist/components/avatar/avatar.d.ts.map +1 -1
- package/dist/components/avatar/index.js +1 -1
- package/dist/components/chat-message/index.js +1 -1
- package/dist/components/data-table/cell-renderers/balance-cell-renderer.d.ts +37 -0
- package/dist/components/data-table/cell-renderers/balance-cell-renderer.d.ts.map +1 -1
- package/dist/components/data-table/filters/date-range-filter.d.ts +12 -1
- package/dist/components/data-table/filters/date-range-filter.d.ts.map +1 -1
- package/dist/components/data-table/filters/select-filter.d.ts +58 -0
- package/dist/components/data-table/filters/select-filter.d.ts.map +1 -0
- package/dist/components/data-table/filters/typeahead-filter.d.ts +52 -0
- package/dist/components/data-table/filters/typeahead-filter.d.ts.map +1 -0
- package/dist/components/data-table/index.d.ts +5 -1
- package/dist/components/data-table/index.d.ts.map +1 -1
- package/dist/components/data-table/index.js +23 -18
- package/dist/components/data-table/toolbar.d.ts.map +1 -1
- package/dist/components/freemium-paywall/index.js +1 -1
- package/dist/components/key-value-pair/index.js +1 -1
- package/dist/components/key-value-pair/key-value-pair.d.ts +19 -1
- package/dist/components/key-value-pair/key-value-pair.d.ts.map +1 -1
- package/dist/components/message-card/index.js +1 -1
- package/dist/components/message-tray/index.js +1 -1
- package/dist/components/payment-form/index.js +1 -1
- package/dist/components/pdf-viewer/index.js +1 -1
- package/dist/components/sidebar/index.js +1 -1
- package/dist/components/timeline/index.js +1 -1
- package/dist/components/warning-stack/index.js +1 -1
- package/dist/components/whatsapp-button/index.js +1 -1
- package/dist/components/whatsapp-button/whatsapp-button.d.ts.map +1 -1
- package/dist/components/workflow/index.js +1 -1
- package/dist/i18n/config.js +34 -4
- package/dist/i18n/config.js.map +1 -1
- package/dist/i18n/resources.d.ts +30 -0
- package/dist/i18n/resources.d.ts.map +1 -1
- package/dist/index.js +399 -394
- package/dist/locales/de.json +10 -0
- package/dist/locales/en.json +10 -0
- package/dist/locales/it.json +10 -0
- package/dist/patterns/alia-assistant/index.js +1 -1
- package/dist/patterns/patient-shell/index.js +1 -1
- package/dist/tokens.css +2 -2
- package/package.json +1 -1
- package/dist/_chunks/alert-DBnawbmf.js.map +0 -1
- package/dist/_chunks/avatar-I10iCDs8.js.map +0 -1
- package/dist/_chunks/editable-currency-cell-renderer-CqMs0Y8L.js +0 -1590
- package/dist/_chunks/editable-currency-cell-renderer-CqMs0Y8L.js.map +0 -1
- package/dist/_chunks/key-value-pair-AbrRwesr.js +0 -76
- package/dist/_chunks/key-value-pair-AbrRwesr.js.map +0 -1
- package/dist/_chunks/sidebar-BqzlRBvC.js.map +0 -1
- package/dist/_chunks/whatsapp-button-Bj5FIhpC.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio-recorder-DNkQLW1G.js","sources":["../../node_modules/lucide-react/dist/esm/icons/mic.js","../../node_modules/lucide-react/dist/esm/icons/pause.js","../../node_modules/lucide-react/dist/esm/icons/play.js","../../src/components/audio-recorder/audio-recorder.agent.ts","../../src/components/audio-recorder/audio-recorder.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"path\", { d: \"M12 19v3\", key: \"npa21l\" }],\n [\"path\", { d: \"M19 10v2a7 7 0 0 1-14 0v-2\", key: \"1vc78b\" }],\n [\"rect\", { x: \"9\", y: \"2\", width: \"6\", height: \"13\", rx: \"3\", key: \"s6n7sd\" }]\n];\nconst Mic = createLucideIcon(\"mic\", __iconNode);\n\nexport { __iconNode, Mic as default };\n//# sourceMappingURL=mic.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"rect\", { x: \"14\", y: \"3\", width: \"5\", height: \"18\", rx: \"1\", key: \"kaeet6\" }],\n [\"rect\", { x: \"5\", y: \"3\", width: \"5\", height: \"18\", rx: \"1\", key: \"1wsw3u\" }]\n];\nconst Pause = createLucideIcon(\"pause\", __iconNode);\n\nexport { __iconNode, Pause as default };\n//# sourceMappingURL=pause.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\",\n key: \"10ikf1\"\n }\n ]\n];\nconst Play = createLucideIcon(\"play\", __iconNode);\n\nexport { __iconNode, Play as default };\n//# sourceMappingURL=play.js.map\n","/* -------------------------------------------------------------------- */\n/* Agent adapter — AudioRecorder. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { AudioRecorderHandle } from './audio-recorder';\n\nexport const audioRecorderAgent: AgentAdapter<AudioRecorderHandle> = {\n id: 'audio-recorder',\n capabilities: ['submit'],\n state: {\n isRecording: {\n type: 'boolean',\n descriptionKey: 'ui.agent.audioRecorder.state.isRecording',\n description: 'True while actively capturing audio (not paused).',\n read: (handle) => handle.isRecording(),\n },\n duration: {\n type: 'number',\n descriptionKey: 'ui.agent.audioRecorder.state.duration',\n description:\n 'Elapsed recording time in milliseconds (paused time excluded).',\n read: (handle) => handle.getDuration(),\n },\n hasRecording: {\n type: 'boolean',\n descriptionKey: 'ui.agent.audioRecorder.state.hasRecording',\n description:\n 'True when a completed recording is available for submission.',\n read: (handle) => handle.hasRecording(),\n },\n },\n actions: {\n start_recording: {\n safety: 'write',\n descriptionKey: 'ui.agent.audioRecorder.actions.startRecording',\n description: 'Request microphone permission and begin recording.',\n invoke: (handle) => handle.startRecording(),\n },\n stop_recording: {\n safety: 'write',\n descriptionKey: 'ui.agent.audioRecorder.actions.stopRecording',\n description: 'Stop the current recording and finalise the blob.',\n invoke: (handle) => {\n handle.stopRecording();\n },\n },\n discard: {\n safety: 'destructive',\n descriptionKey: 'ui.agent.audioRecorder.actions.discard',\n description:\n 'Cancel the recording. Irreversible — the captured audio is dropped.',\n invoke: (handle) => {\n handle.discard();\n },\n },\n submit: {\n safety: 'write',\n descriptionKey: 'ui.agent.audioRecorder.actions.submit',\n description:\n 'Stop the active recording, finalising it via onRecordingComplete.',\n invoke: (handle) => {\n handle.stopRecording();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'audio-recorder',\n description: 'Marks the AudioRecorder wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useReducer,\n useRef,\n useState,\n type HTMLAttributes,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { Mic, Pause, Play, Square, X } from 'lucide-react';\nimport { IconButton, Button } from '../button';\nimport { Select, type SelectOption } from '../select/select';\nimport { AudioVisualiser } from '../audio-visualiser';\nimport { Alert } from '../alert';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { audioRecorderAgent } from './audio-recorder.agent';\n\ntype State =\n | { kind: 'idle' }\n | { kind: 'requesting' }\n | { kind: 'recording'; startedAt: number; pausedMs: number }\n | { kind: 'paused'; startedAt: number; pausedMs: number; pausedAt: number }\n | { kind: 'stopped'; duration: number }\n | {\n kind: 'error';\n type:\n | 'permission-denied'\n | 'no-device'\n | 'unsupported'\n | 'capture-failed';\n };\n\ntype Action =\n | { type: 'request' }\n | { type: 'start' }\n | { type: 'pause' }\n | { type: 'resume' }\n | { type: 'stop'; duration: number }\n | { type: 'cancel' }\n | {\n type: 'error';\n kind:\n | 'permission-denied'\n | 'no-device'\n | 'unsupported'\n | 'capture-failed';\n }\n | { type: 'reset' };\n\nfunction reducer(state: State, action: Action): State {\n switch (action.type) {\n case 'request':\n return { kind: 'requesting' };\n case 'start':\n return { kind: 'recording', startedAt: Date.now(), pausedMs: 0 };\n case 'pause':\n if (state.kind !== 'recording') return state;\n return {\n kind: 'paused',\n startedAt: state.startedAt,\n pausedMs: state.pausedMs,\n pausedAt: Date.now(),\n };\n case 'resume':\n if (state.kind !== 'paused') return state;\n return {\n kind: 'recording',\n startedAt: state.startedAt,\n pausedMs: state.pausedMs + (Date.now() - state.pausedAt),\n };\n case 'stop':\n return { kind: 'stopped', duration: action.duration };\n case 'cancel':\n return { kind: 'idle' };\n case 'error':\n return { kind: 'error', type: action.kind };\n case 'reset':\n return { kind: 'idle' };\n default:\n return state;\n }\n}\n\nconst rootVariants = cva(\n [\n 'ds:inline-flex ds:flex-col ds:items-stretch ds:gap-[var(--spacing-sm)]',\n 'ds:rounded-[var(--radius-md)] ds:border ds:border-border',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]',\n 'ds:bg-background',\n ].join(' '),\n {\n variants: {\n size: {\n sm: '',\n md: '',\n lg: '',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nconst MIME_PREFERENCES = [\n 'audio/webm;codecs=opus',\n 'audio/webm',\n 'audio/mp4',\n 'audio/ogg;codecs=opus',\n];\n\nfunction pickMimeType(): string | undefined {\n if (typeof MediaRecorder === 'undefined') return undefined;\n for (const mime of MIME_PREFERENCES) {\n if (MediaRecorder.isTypeSupported(mime)) return mime;\n }\n return undefined;\n}\n\nfunction formatTimer(ms: number, locale: string): string {\n const total = Math.max(0, Math.floor(ms / 1000));\n const minutes = Math.floor(total / 60);\n const seconds = total % 60;\n const fmt = (n: number) =>\n new Intl.NumberFormat(locale, { minimumIntegerDigits: 2 }).format(n);\n return `${fmt(minutes)}:${fmt(seconds)}`;\n}\n\n/** Curated imperative handle for agent / external automation. */\nexport interface AudioRecorderHandle {\n isRecording: () => boolean;\n getDuration: () => number;\n hasRecording: () => boolean;\n startRecording: () => Promise<void> | void;\n stopRecording: () => void;\n discard: () => void;\n}\n\nexport interface AudioRecorderProps\n extends\n Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'onError'>,\n VariantProps<typeof rootVariants> {\n /** Called on stop with the final blob + duration in ms. */\n onRecordingComplete?: (blob: Blob, durationMs: number) => void;\n /** Called when recording is cancelled. */\n onCancel?: () => void;\n /** Called when an error occurs. */\n onError?: (\n error:\n | 'permission-denied'\n | 'no-device'\n | 'unsupported'\n | 'capture-failed'\n | 'max-duration'\n | 'max-size',\n ) => void;\n /**\n * Auto-stop after this many milliseconds of active recording. Default\n * 30 minutes. Set `null` to disable.\n */\n maxDurationMs?: number | null;\n /**\n * Auto-stop when the accumulated blob chunks exceed this many bytes.\n * Default 250 MB. Set `null` to disable.\n */\n maxBytes?: number | null;\n}\n\nexport const AudioRecorder = forwardRef<\n AudioRecorderHandle,\n AudioRecorderProps\n>(\n (\n {\n size = 'md',\n onRecordingComplete,\n onCancel,\n onError,\n maxDurationMs = 30 * 60 * 1000,\n maxBytes = 250 * 1024 * 1024,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const [state, dispatch] = useReducer(reducer, { kind: 'idle' });\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [now, setNow] = useState<number>(Date.now());\n const [devices, setDevices] = useState<MediaDeviceInfo[]>([]);\n const [selectedDeviceId, setSelectedDeviceId] = useState<string>('');\n\n const recorderRef = useRef<MediaRecorder | null>(null);\n const chunksRef = useRef<Blob[]>([]);\n // Running total of bytes accumulated in `chunksRef` — reading\n // `chunksRef.current.reduce(...)` on every ondataavailable scales\n // poorly; maintain the sum incrementally instead.\n const byteCountRef = useRef<number>(0);\n const durationAtStopRef = useRef<number>(0);\n // Tracks the *currently-held* MediaStream in a ref so the unmount\n // cleanup effect (which closes over [] deps) sees the latest value.\n // Without this, a unmount while getUserMedia is pending would never\n // stop() tracks and the browser mic indicator stays on.\n const streamRef = useRef<MediaStream | null>(null);\n // Flips to true on unmount; consulted inside the getUserMedia promise\n // resolution so we don't try to setStream() on a dead component and\n // so the freshly-acquired tracks get released immediately.\n const unmountedRef = useRef<boolean>(false);\n // Flips to true when cancel() is called; the onstop handler reads it\n // so `onRecordingComplete` is NOT invoked on a cancel path with a blank\n // blob. Reset on each recording start.\n const cancelledRef = useRef<boolean>(false);\n\n const supported = typeof MediaRecorder !== 'undefined';\n\n /* ── Enumerate devices once (after any prior permission grant) ── */\n useEffect(() => {\n if (!supported || !navigator.mediaDevices?.enumerateDevices) return;\n navigator.mediaDevices\n .enumerateDevices()\n .then((list) => {\n const mics = list.filter((d) => d.kind === 'audioinput');\n setDevices(mics);\n })\n .catch(() => {\n /* ignore */\n });\n }, [supported]);\n\n /* ── Timer tick ── */\n useEffect(() => {\n if (state.kind !== 'recording') return;\n const handle = window.setInterval(() => setNow(Date.now()), 250);\n return () => window.clearInterval(handle);\n }, [state.kind]);\n\n const cleanupStream = useCallback(() => {\n const held = streamRef.current ?? stream;\n held?.getTracks().forEach((t) => t.stop());\n streamRef.current = null;\n setStream(null);\n }, [stream]);\n\n const requestAndStart = useCallback(async () => {\n if (!supported) {\n dispatch({ type: 'error', kind: 'unsupported' });\n onError?.('unsupported');\n return;\n }\n dispatch({ type: 'request' });\n cancelledRef.current = false;\n try {\n const nextStream = await navigator.mediaDevices.getUserMedia({\n audio: selectedDeviceId\n ? { deviceId: { exact: selectedDeviceId } }\n : true,\n });\n // Guard against the component unmounting between the permission\n // prompt and the promise resolution — otherwise the freshly-acquired\n // tracks leak and the browser's mic indicator stays live.\n if (unmountedRef.current) {\n nextStream.getTracks().forEach((tr) => tr.stop());\n return;\n }\n streamRef.current = nextStream;\n setStream(nextStream);\n const mimeType = pickMimeType();\n const recorder = new MediaRecorder(\n nextStream,\n mimeType ? { mimeType } : undefined,\n );\n recorderRef.current = recorder;\n chunksRef.current = [];\n byteCountRef.current = 0;\n recorder.ondataavailable = (event) => {\n if (event.data && event.data.size > 0) {\n chunksRef.current.push(event.data);\n byteCountRef.current += event.data.size;\n // Byte-cap: auto-stop if the recording would exceed maxBytes.\n // Cheaper than summing on every tick; stop once and bail.\n if (\n typeof maxBytes === 'number' &&\n byteCountRef.current >= maxBytes &&\n recorderRef.current?.state === 'recording'\n ) {\n onError?.('max-size');\n try {\n recorderRef.current.stop();\n } catch {\n /* ignore */\n }\n }\n }\n };\n recorder.onstop = () => {\n // Cancel path: drop the blob on the floor — consumers who called\n // cancel() must not receive a silent empty recording.\n if (!cancelledRef.current) {\n const blob = new Blob(chunksRef.current, {\n type: mimeType ?? 'audio/webm',\n });\n onRecordingComplete?.(blob, durationAtStopRef.current);\n }\n chunksRef.current = [];\n recorderRef.current = null;\n nextStream.getTracks().forEach((tr) => tr.stop());\n streamRef.current = null;\n setStream(null);\n cancelledRef.current = false;\n };\n recorder.start(1000);\n dispatch({ type: 'start' });\n } catch (err: unknown) {\n const errorName = err instanceof Error ? err.name : '';\n if (errorName === 'NotAllowedError' || errorName === 'SecurityError') {\n dispatch({ type: 'error', kind: 'permission-denied' });\n onError?.('permission-denied');\n } else if (\n errorName === 'NotFoundError' ||\n errorName === 'OverconstrainedError'\n ) {\n dispatch({ type: 'error', kind: 'no-device' });\n onError?.('no-device');\n } else {\n dispatch({ type: 'error', kind: 'capture-failed' });\n onError?.('capture-failed');\n }\n }\n }, [onError, onRecordingComplete, selectedDeviceId, supported]);\n\n const pause = useCallback(() => {\n recorderRef.current?.pause();\n dispatch({ type: 'pause' });\n }, []);\n\n const resume = useCallback(() => {\n recorderRef.current?.resume();\n dispatch({ type: 'resume' });\n }, []);\n\n const stop = useCallback(() => {\n if (state.kind === 'recording' || state.kind === 'paused') {\n const started = state.startedAt;\n const pausedMs = state.pausedMs;\n const duration = Date.now() - started - pausedMs;\n durationAtStopRef.current = Math.max(0, duration);\n dispatch({ type: 'stop', duration: duration });\n }\n try {\n recorderRef.current?.stop();\n } catch {\n /* stop() throws on non-recording state; safe to swallow */\n }\n }, [state]);\n\n const cancel = useCallback(() => {\n // Flip the cancelled flag BEFORE asking MediaRecorder to stop so the\n // async onstop handler sees it and skips onRecordingComplete.\n cancelledRef.current = true;\n try {\n recorderRef.current?.stop();\n } catch {\n /* ignore */\n }\n chunksRef.current = [];\n recorderRef.current = null;\n cleanupStream();\n dispatch({ type: 'cancel' });\n onCancel?.();\n }, [cleanupStream, onCancel]);\n\n /* ── Max-duration auto-stop ── */\n useEffect(() => {\n if (state.kind !== 'recording') return;\n if (typeof maxDurationMs !== 'number' || maxDurationMs <= 0) return;\n const alreadyElapsed = Date.now() - state.startedAt - state.pausedMs;\n const remaining = Math.max(0, maxDurationMs - alreadyElapsed);\n const handle = window.setTimeout(() => {\n if (recorderRef.current?.state === 'recording') {\n onError?.('max-duration');\n try {\n recorderRef.current.stop();\n } catch {\n /* ignore */\n }\n }\n }, remaining);\n return () => window.clearTimeout(handle);\n }, [state, maxDurationMs, onError]);\n\n /* ── Unmount cleanup: release mic if still held ── */\n useEffect(() => {\n return () => {\n unmountedRef.current = true;\n try {\n recorderRef.current?.stop();\n } catch {\n /* ignore */\n }\n // Use streamRef so we see a stream that was acquired by an\n // in-flight getUserMedia resolution — not just the one captured\n // by the closure at mount.\n streamRef.current?.getTracks().forEach((tr) => tr.stop());\n streamRef.current = null;\n };\n }, []);\n\n const elapsedMs =\n state.kind === 'recording'\n ? now - state.startedAt - state.pausedMs\n : state.kind === 'paused'\n ? state.pausedAt - state.startedAt - state.pausedMs\n : state.kind === 'stopped'\n ? state.duration\n : 0;\n\n const agentHandle = useMemo<AudioRecorderHandle>(\n () => ({\n isRecording: () => state.kind === 'recording',\n getDuration: () => elapsedMs,\n hasRecording: () => state.kind === 'stopped',\n startRecording: () => requestAndStart(),\n stopRecording: () => stop(),\n discard: () => cancel(),\n }),\n [state, elapsedMs, requestAndStart, stop, cancel],\n );\n useImperativeHandle(ref, () => agentHandle, [agentHandle]);\n useAgentRegistration(audioRecorderAgent, agentHandle, rest.id);\n\n const deviceOptions: SelectOption<string>[] = devices.map((d) => ({\n value: d.deviceId,\n label: d.label || t('chat.audio.selectDevice'),\n }));\n\n const statusText = (() => {\n switch (state.kind) {\n case 'idle':\n return t('chat.audio.idle');\n case 'requesting':\n return t('common.loading');\n case 'recording':\n return t('chat.audio.recording');\n case 'paused':\n return t('chat.audio.paused');\n case 'stopped':\n return t('chat.audio.idle');\n case 'error':\n if (state.type === 'permission-denied')\n return t('chat.audio.permissionDenied');\n if (state.type === 'unsupported') return t('chat.audio.unsupported');\n return t('chat.audio.idle');\n }\n })();\n\n const isRecording = state.kind === 'recording';\n const isPaused = state.kind === 'paused';\n const hasError = state.kind === 'error';\n\n return (\n <div\n data-component=\"audio-recorder\"\n data-component-id={rest.id}\n className={rootVariants({ size, className })}\n {...rest}\n >\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-sm)]\">\n <span\n aria-hidden=\"true\"\n className={[\n 'ds:relative ds:inline-flex ds:size-3 ds:items-center ds:justify-center ds:rounded-[var(--radius-full)]',\n isRecording\n ? 'ds:bg-[color:var(--destructive)]'\n : 'ds:bg-[color:var(--muted)]',\n ].join(' ')}\n >\n {isRecording ? (\n <span\n className={[\n 'ds:absolute ds:inset-0 ds:rounded-[var(--radius-full)]',\n 'ds:bg-[color:var(--destructive)]',\n 'ds:motion-safe:animate-[recorder-pulse_1.2s_ease-out_infinite]',\n 'ds:[.theme-accessible_&]:animate-none',\n ].join(' ')}\n />\n ) : null}\n </span>\n <AudioVisualiser\n stream={stream}\n size=\"sm\"\n aria-hidden=\"true\"\n className=\"ds:flex-1\"\n />\n <time\n dir=\"ltr\"\n aria-hidden=\"true\"\n className=\"ds:tabular-nums type-meta ds:text-[color:var(--muted-foreground)]\"\n >\n {formatTimer(elapsedMs, i18n.language)}\n </time>\n </div>\n\n <span role=\"status\" aria-live=\"polite\" className=\"ds:sr-only\">\n {statusText}\n </span>\n\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n {state.kind === 'idle' || state.kind === 'stopped' || hasError ? (\n <Button\n intent=\"primary\"\n size=\"sm\"\n startIcon={<Mic />}\n onClick={requestAndStart}\n disabled={!supported}\n >\n {t('chat.audio.record')}\n </Button>\n ) : null}\n {isRecording ? (\n <IconButton\n icon={<Pause />}\n aria-label={t('chat.audio.pause')}\n intent=\"secondary\"\n size=\"sm\"\n onClick={pause}\n />\n ) : null}\n {isPaused ? (\n <IconButton\n icon={<Play />}\n aria-label={t('chat.audio.resume')}\n intent=\"secondary\"\n size=\"sm\"\n onClick={resume}\n />\n ) : null}\n {isRecording || isPaused ? (\n <>\n <IconButton\n icon={<Square />}\n aria-label={t('chat.audio.stop')}\n intent=\"primary\"\n size=\"sm\"\n onClick={stop}\n />\n <IconButton\n icon={<X />}\n aria-label={t('chat.audio.cancel')}\n intent=\"ghost\"\n size=\"sm\"\n onClick={cancel}\n />\n </>\n ) : null}\n {state.kind === 'idle' && deviceOptions.length > 1 ? (\n <div className=\"ds:ms-auto ds:min-w-[160px]\">\n <Select\n aria-label={t('chat.audio.selectDevice')}\n options={deviceOptions}\n value={selectedDeviceId}\n onValueChange={(v) => setSelectedDeviceId(v)}\n size=\"sm\"\n clearable\n />\n </div>\n ) : null}\n </div>\n\n {hasError && state.kind === 'error' ? (\n <Alert variant=\"error\" live=\"polite\">\n <Alert.Description>{statusText}</Alert.Description>\n {state.type === 'permission-denied' ? (\n <Alert.Action>\n <Button intent=\"ghost\" size=\"sm\" onClick={requestAndStart}>\n {t('chat.audio.retry')}\n </Button>\n </Alert.Action>\n ) : null}\n </Alert>\n ) : null}\n </div>\n );\n },\n);\n\nAudioRecorder.displayName = 'AudioRecorder';\n"],"names":["__iconNode","Mic","createLucideIcon","Pause","Play","audioRecorderAgent","handle","reducer","state","action","rootVariants","cva","MIME_PREFERENCES","pickMimeType","mime","formatTimer","ms","locale","total","minutes","seconds","fmt","n","AudioRecorder","forwardRef","size","onRecordingComplete","onCancel","onError","maxDurationMs","maxBytes","className","rest","ref","t","i18n","useTranslation","dispatch","useReducer","stream","setStream","useState","now","setNow","devices","setDevices","selectedDeviceId","setSelectedDeviceId","recorderRef","useRef","chunksRef","byteCountRef","durationAtStopRef","streamRef","unmountedRef","cancelledRef","supported","useEffect","_a","list","mics","d","cleanupStream","useCallback","held","requestAndStart","nextStream","tr","mimeType","recorder","event","blob","err","errorName","pause","resume","stop","started","pausedMs","duration","cancel","alreadyElapsed","remaining","_b","elapsedMs","agentHandle","useMemo","useImperativeHandle","useAgentRegistration","deviceOptions","statusText","isRecording","isPaused","hasError","jsxs","jsx","AudioVisualiser","Button","IconButton","Fragment","Square","X","Select","v","Alert"],"mappings":"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","x_google_ignoreList":[0,1,2]}
|
|
1
|
+
{"version":3,"file":"audio-recorder-D4xM3H5i.js","sources":["../../node_modules/lucide-react/dist/esm/icons/mic.js","../../node_modules/lucide-react/dist/esm/icons/pause.js","../../node_modules/lucide-react/dist/esm/icons/play.js","../../src/components/audio-recorder/audio-recorder.agent.ts","../../src/components/audio-recorder/audio-recorder.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"path\", { d: \"M12 19v3\", key: \"npa21l\" }],\n [\"path\", { d: \"M19 10v2a7 7 0 0 1-14 0v-2\", key: \"1vc78b\" }],\n [\"rect\", { x: \"9\", y: \"2\", width: \"6\", height: \"13\", rx: \"3\", key: \"s6n7sd\" }]\n];\nconst Mic = createLucideIcon(\"mic\", __iconNode);\n\nexport { __iconNode, Mic as default };\n//# sourceMappingURL=mic.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"rect\", { x: \"14\", y: \"3\", width: \"5\", height: \"18\", rx: \"1\", key: \"kaeet6\" }],\n [\"rect\", { x: \"5\", y: \"3\", width: \"5\", height: \"18\", rx: \"1\", key: \"1wsw3u\" }]\n];\nconst Pause = createLucideIcon(\"pause\", __iconNode);\n\nexport { __iconNode, Pause as default };\n//# sourceMappingURL=pause.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\",\n key: \"10ikf1\"\n }\n ]\n];\nconst Play = createLucideIcon(\"play\", __iconNode);\n\nexport { __iconNode, Play as default };\n//# sourceMappingURL=play.js.map\n","/* -------------------------------------------------------------------- */\n/* Agent adapter — AudioRecorder. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { AudioRecorderHandle } from './audio-recorder';\n\nexport const audioRecorderAgent: AgentAdapter<AudioRecorderHandle> = {\n id: 'audio-recorder',\n capabilities: ['submit'],\n state: {\n isRecording: {\n type: 'boolean',\n descriptionKey: 'ui.agent.audioRecorder.state.isRecording',\n description: 'True while actively capturing audio (not paused).',\n read: (handle) => handle.isRecording(),\n },\n duration: {\n type: 'number',\n descriptionKey: 'ui.agent.audioRecorder.state.duration',\n description:\n 'Elapsed recording time in milliseconds (paused time excluded).',\n read: (handle) => handle.getDuration(),\n },\n hasRecording: {\n type: 'boolean',\n descriptionKey: 'ui.agent.audioRecorder.state.hasRecording',\n description:\n 'True when a completed recording is available for submission.',\n read: (handle) => handle.hasRecording(),\n },\n },\n actions: {\n start_recording: {\n safety: 'write',\n descriptionKey: 'ui.agent.audioRecorder.actions.startRecording',\n description: 'Request microphone permission and begin recording.',\n invoke: (handle) => handle.startRecording(),\n },\n stop_recording: {\n safety: 'write',\n descriptionKey: 'ui.agent.audioRecorder.actions.stopRecording',\n description: 'Stop the current recording and finalise the blob.',\n invoke: (handle) => {\n handle.stopRecording();\n },\n },\n discard: {\n safety: 'destructive',\n descriptionKey: 'ui.agent.audioRecorder.actions.discard',\n description:\n 'Cancel the recording. Irreversible — the captured audio is dropped.',\n invoke: (handle) => {\n handle.discard();\n },\n },\n submit: {\n safety: 'write',\n descriptionKey: 'ui.agent.audioRecorder.actions.submit',\n description:\n 'Stop the active recording, finalising it via onRecordingComplete.',\n invoke: (handle) => {\n handle.stopRecording();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'audio-recorder',\n description: 'Marks the AudioRecorder wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useReducer,\n useRef,\n useState,\n type HTMLAttributes,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { Mic, Pause, Play, Square, X } from 'lucide-react';\nimport { IconButton, Button } from '../button';\nimport { Select, type SelectOption } from '../select/select';\nimport { AudioVisualiser } from '../audio-visualiser';\nimport { Alert } from '../alert';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { audioRecorderAgent } from './audio-recorder.agent';\n\ntype State =\n | { kind: 'idle' }\n | { kind: 'requesting' }\n | { kind: 'recording'; startedAt: number; pausedMs: number }\n | { kind: 'paused'; startedAt: number; pausedMs: number; pausedAt: number }\n | { kind: 'stopped'; duration: number }\n | {\n kind: 'error';\n type:\n | 'permission-denied'\n | 'no-device'\n | 'unsupported'\n | 'capture-failed';\n };\n\ntype Action =\n | { type: 'request' }\n | { type: 'start' }\n | { type: 'pause' }\n | { type: 'resume' }\n | { type: 'stop'; duration: number }\n | { type: 'cancel' }\n | {\n type: 'error';\n kind:\n | 'permission-denied'\n | 'no-device'\n | 'unsupported'\n | 'capture-failed';\n }\n | { type: 'reset' };\n\nfunction reducer(state: State, action: Action): State {\n switch (action.type) {\n case 'request':\n return { kind: 'requesting' };\n case 'start':\n return { kind: 'recording', startedAt: Date.now(), pausedMs: 0 };\n case 'pause':\n if (state.kind !== 'recording') return state;\n return {\n kind: 'paused',\n startedAt: state.startedAt,\n pausedMs: state.pausedMs,\n pausedAt: Date.now(),\n };\n case 'resume':\n if (state.kind !== 'paused') return state;\n return {\n kind: 'recording',\n startedAt: state.startedAt,\n pausedMs: state.pausedMs + (Date.now() - state.pausedAt),\n };\n case 'stop':\n return { kind: 'stopped', duration: action.duration };\n case 'cancel':\n return { kind: 'idle' };\n case 'error':\n return { kind: 'error', type: action.kind };\n case 'reset':\n return { kind: 'idle' };\n default:\n return state;\n }\n}\n\nconst rootVariants = cva(\n [\n 'ds:inline-flex ds:flex-col ds:items-stretch ds:gap-[var(--spacing-sm)]',\n 'ds:rounded-[var(--radius-md)] ds:border ds:border-border',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]',\n 'ds:bg-background',\n ].join(' '),\n {\n variants: {\n size: {\n sm: '',\n md: '',\n lg: '',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nconst MIME_PREFERENCES = [\n 'audio/webm;codecs=opus',\n 'audio/webm',\n 'audio/mp4',\n 'audio/ogg;codecs=opus',\n];\n\nfunction pickMimeType(): string | undefined {\n if (typeof MediaRecorder === 'undefined') return undefined;\n for (const mime of MIME_PREFERENCES) {\n if (MediaRecorder.isTypeSupported(mime)) return mime;\n }\n return undefined;\n}\n\nfunction formatTimer(ms: number, locale: string): string {\n const total = Math.max(0, Math.floor(ms / 1000));\n const minutes = Math.floor(total / 60);\n const seconds = total % 60;\n const fmt = (n: number) =>\n new Intl.NumberFormat(locale, { minimumIntegerDigits: 2 }).format(n);\n return `${fmt(minutes)}:${fmt(seconds)}`;\n}\n\n/** Curated imperative handle for agent / external automation. */\nexport interface AudioRecorderHandle {\n isRecording: () => boolean;\n getDuration: () => number;\n hasRecording: () => boolean;\n startRecording: () => Promise<void> | void;\n stopRecording: () => void;\n discard: () => void;\n}\n\nexport interface AudioRecorderProps\n extends\n Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'onError'>,\n VariantProps<typeof rootVariants> {\n /** Called on stop with the final blob + duration in ms. */\n onRecordingComplete?: (blob: Blob, durationMs: number) => void;\n /** Called when recording is cancelled. */\n onCancel?: () => void;\n /** Called when an error occurs. */\n onError?: (\n error:\n | 'permission-denied'\n | 'no-device'\n | 'unsupported'\n | 'capture-failed'\n | 'max-duration'\n | 'max-size',\n ) => void;\n /**\n * Auto-stop after this many milliseconds of active recording. Default\n * 30 minutes. Set `null` to disable.\n */\n maxDurationMs?: number | null;\n /**\n * Auto-stop when the accumulated blob chunks exceed this many bytes.\n * Default 250 MB. Set `null` to disable.\n */\n maxBytes?: number | null;\n}\n\nexport const AudioRecorder = forwardRef<\n AudioRecorderHandle,\n AudioRecorderProps\n>(\n (\n {\n size = 'md',\n onRecordingComplete,\n onCancel,\n onError,\n maxDurationMs = 30 * 60 * 1000,\n maxBytes = 250 * 1024 * 1024,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const [state, dispatch] = useReducer(reducer, { kind: 'idle' });\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [now, setNow] = useState<number>(Date.now());\n const [devices, setDevices] = useState<MediaDeviceInfo[]>([]);\n const [selectedDeviceId, setSelectedDeviceId] = useState<string>('');\n\n const recorderRef = useRef<MediaRecorder | null>(null);\n const chunksRef = useRef<Blob[]>([]);\n // Running total of bytes accumulated in `chunksRef` — reading\n // `chunksRef.current.reduce(...)` on every ondataavailable scales\n // poorly; maintain the sum incrementally instead.\n const byteCountRef = useRef<number>(0);\n const durationAtStopRef = useRef<number>(0);\n // Tracks the *currently-held* MediaStream in a ref so the unmount\n // cleanup effect (which closes over [] deps) sees the latest value.\n // Without this, a unmount while getUserMedia is pending would never\n // stop() tracks and the browser mic indicator stays on.\n const streamRef = useRef<MediaStream | null>(null);\n // Flips to true on unmount; consulted inside the getUserMedia promise\n // resolution so we don't try to setStream() on a dead component and\n // so the freshly-acquired tracks get released immediately.\n const unmountedRef = useRef<boolean>(false);\n // Flips to true when cancel() is called; the onstop handler reads it\n // so `onRecordingComplete` is NOT invoked on a cancel path with a blank\n // blob. Reset on each recording start.\n const cancelledRef = useRef<boolean>(false);\n\n const supported = typeof MediaRecorder !== 'undefined';\n\n /* ── Enumerate devices once (after any prior permission grant) ── */\n useEffect(() => {\n if (!supported || !navigator.mediaDevices?.enumerateDevices) return;\n navigator.mediaDevices\n .enumerateDevices()\n .then((list) => {\n const mics = list.filter((d) => d.kind === 'audioinput');\n setDevices(mics);\n })\n .catch(() => {\n /* ignore */\n });\n }, [supported]);\n\n /* ── Timer tick ── */\n useEffect(() => {\n if (state.kind !== 'recording') return;\n const handle = window.setInterval(() => setNow(Date.now()), 250);\n return () => window.clearInterval(handle);\n }, [state.kind]);\n\n const cleanupStream = useCallback(() => {\n const held = streamRef.current ?? stream;\n held?.getTracks().forEach((t) => t.stop());\n streamRef.current = null;\n setStream(null);\n }, [stream]);\n\n const requestAndStart = useCallback(async () => {\n if (!supported) {\n dispatch({ type: 'error', kind: 'unsupported' });\n onError?.('unsupported');\n return;\n }\n dispatch({ type: 'request' });\n cancelledRef.current = false;\n try {\n const nextStream = await navigator.mediaDevices.getUserMedia({\n audio: selectedDeviceId\n ? { deviceId: { exact: selectedDeviceId } }\n : true,\n });\n // Guard against the component unmounting between the permission\n // prompt and the promise resolution — otherwise the freshly-acquired\n // tracks leak and the browser's mic indicator stays live.\n if (unmountedRef.current) {\n nextStream.getTracks().forEach((tr) => tr.stop());\n return;\n }\n streamRef.current = nextStream;\n setStream(nextStream);\n const mimeType = pickMimeType();\n const recorder = new MediaRecorder(\n nextStream,\n mimeType ? { mimeType } : undefined,\n );\n recorderRef.current = recorder;\n chunksRef.current = [];\n byteCountRef.current = 0;\n recorder.ondataavailable = (event) => {\n if (event.data && event.data.size > 0) {\n chunksRef.current.push(event.data);\n byteCountRef.current += event.data.size;\n // Byte-cap: auto-stop if the recording would exceed maxBytes.\n // Cheaper than summing on every tick; stop once and bail.\n if (\n typeof maxBytes === 'number' &&\n byteCountRef.current >= maxBytes &&\n recorderRef.current?.state === 'recording'\n ) {\n onError?.('max-size');\n try {\n recorderRef.current.stop();\n } catch {\n /* ignore */\n }\n }\n }\n };\n recorder.onstop = () => {\n // Cancel path: drop the blob on the floor — consumers who called\n // cancel() must not receive a silent empty recording.\n if (!cancelledRef.current) {\n const blob = new Blob(chunksRef.current, {\n type: mimeType ?? 'audio/webm',\n });\n onRecordingComplete?.(blob, durationAtStopRef.current);\n }\n chunksRef.current = [];\n recorderRef.current = null;\n nextStream.getTracks().forEach((tr) => tr.stop());\n streamRef.current = null;\n setStream(null);\n cancelledRef.current = false;\n };\n recorder.start(1000);\n dispatch({ type: 'start' });\n } catch (err: unknown) {\n const errorName = err instanceof Error ? err.name : '';\n if (errorName === 'NotAllowedError' || errorName === 'SecurityError') {\n dispatch({ type: 'error', kind: 'permission-denied' });\n onError?.('permission-denied');\n } else if (\n errorName === 'NotFoundError' ||\n errorName === 'OverconstrainedError'\n ) {\n dispatch({ type: 'error', kind: 'no-device' });\n onError?.('no-device');\n } else {\n dispatch({ type: 'error', kind: 'capture-failed' });\n onError?.('capture-failed');\n }\n }\n }, [onError, onRecordingComplete, selectedDeviceId, supported]);\n\n const pause = useCallback(() => {\n recorderRef.current?.pause();\n dispatch({ type: 'pause' });\n }, []);\n\n const resume = useCallback(() => {\n recorderRef.current?.resume();\n dispatch({ type: 'resume' });\n }, []);\n\n const stop = useCallback(() => {\n if (state.kind === 'recording' || state.kind === 'paused') {\n const started = state.startedAt;\n const pausedMs = state.pausedMs;\n const duration = Date.now() - started - pausedMs;\n durationAtStopRef.current = Math.max(0, duration);\n dispatch({ type: 'stop', duration: duration });\n }\n try {\n recorderRef.current?.stop();\n } catch {\n /* stop() throws on non-recording state; safe to swallow */\n }\n }, [state]);\n\n const cancel = useCallback(() => {\n // Flip the cancelled flag BEFORE asking MediaRecorder to stop so the\n // async onstop handler sees it and skips onRecordingComplete.\n cancelledRef.current = true;\n try {\n recorderRef.current?.stop();\n } catch {\n /* ignore */\n }\n chunksRef.current = [];\n recorderRef.current = null;\n cleanupStream();\n dispatch({ type: 'cancel' });\n onCancel?.();\n }, [cleanupStream, onCancel]);\n\n /* ── Max-duration auto-stop ── */\n useEffect(() => {\n if (state.kind !== 'recording') return;\n if (typeof maxDurationMs !== 'number' || maxDurationMs <= 0) return;\n const alreadyElapsed = Date.now() - state.startedAt - state.pausedMs;\n const remaining = Math.max(0, maxDurationMs - alreadyElapsed);\n const handle = window.setTimeout(() => {\n if (recorderRef.current?.state === 'recording') {\n onError?.('max-duration');\n try {\n recorderRef.current.stop();\n } catch {\n /* ignore */\n }\n }\n }, remaining);\n return () => window.clearTimeout(handle);\n }, [state, maxDurationMs, onError]);\n\n /* ── Unmount cleanup: release mic if still held ── */\n useEffect(() => {\n return () => {\n unmountedRef.current = true;\n try {\n recorderRef.current?.stop();\n } catch {\n /* ignore */\n }\n // Use streamRef so we see a stream that was acquired by an\n // in-flight getUserMedia resolution — not just the one captured\n // by the closure at mount.\n streamRef.current?.getTracks().forEach((tr) => tr.stop());\n streamRef.current = null;\n };\n }, []);\n\n const elapsedMs =\n state.kind === 'recording'\n ? now - state.startedAt - state.pausedMs\n : state.kind === 'paused'\n ? state.pausedAt - state.startedAt - state.pausedMs\n : state.kind === 'stopped'\n ? state.duration\n : 0;\n\n const agentHandle = useMemo<AudioRecorderHandle>(\n () => ({\n isRecording: () => state.kind === 'recording',\n getDuration: () => elapsedMs,\n hasRecording: () => state.kind === 'stopped',\n startRecording: () => requestAndStart(),\n stopRecording: () => stop(),\n discard: () => cancel(),\n }),\n [state, elapsedMs, requestAndStart, stop, cancel],\n );\n useImperativeHandle(ref, () => agentHandle, [agentHandle]);\n useAgentRegistration(audioRecorderAgent, agentHandle, rest.id);\n\n const deviceOptions: SelectOption<string>[] = devices.map((d) => ({\n value: d.deviceId,\n label: d.label || t('chat.audio.selectDevice'),\n }));\n\n const statusText = (() => {\n switch (state.kind) {\n case 'idle':\n return t('chat.audio.idle');\n case 'requesting':\n return t('common.loading');\n case 'recording':\n return t('chat.audio.recording');\n case 'paused':\n return t('chat.audio.paused');\n case 'stopped':\n return t('chat.audio.idle');\n case 'error':\n if (state.type === 'permission-denied')\n return t('chat.audio.permissionDenied');\n if (state.type === 'unsupported') return t('chat.audio.unsupported');\n return t('chat.audio.idle');\n }\n })();\n\n const isRecording = state.kind === 'recording';\n const isPaused = state.kind === 'paused';\n const hasError = state.kind === 'error';\n\n return (\n <div\n data-component=\"audio-recorder\"\n data-component-id={rest.id}\n className={rootVariants({ size, className })}\n {...rest}\n >\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-sm)]\">\n <span\n aria-hidden=\"true\"\n className={[\n 'ds:relative ds:inline-flex ds:size-3 ds:items-center ds:justify-center ds:rounded-[var(--radius-full)]',\n isRecording\n ? 'ds:bg-[color:var(--destructive)]'\n : 'ds:bg-[color:var(--muted)]',\n ].join(' ')}\n >\n {isRecording ? (\n <span\n className={[\n 'ds:absolute ds:inset-0 ds:rounded-[var(--radius-full)]',\n 'ds:bg-[color:var(--destructive)]',\n 'ds:motion-safe:animate-[recorder-pulse_1.2s_ease-out_infinite]',\n 'ds:[.theme-accessible_&]:animate-none',\n ].join(' ')}\n />\n ) : null}\n </span>\n <AudioVisualiser\n stream={stream}\n size=\"sm\"\n aria-hidden=\"true\"\n className=\"ds:flex-1\"\n />\n <time\n dir=\"ltr\"\n aria-hidden=\"true\"\n className=\"ds:tabular-nums type-meta ds:text-[color:var(--muted-foreground)]\"\n >\n {formatTimer(elapsedMs, i18n.language)}\n </time>\n </div>\n\n <span role=\"status\" aria-live=\"polite\" className=\"ds:sr-only\">\n {statusText}\n </span>\n\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n {state.kind === 'idle' || state.kind === 'stopped' || hasError ? (\n <Button\n intent=\"primary\"\n size=\"sm\"\n startIcon={<Mic />}\n onClick={requestAndStart}\n disabled={!supported}\n >\n {t('chat.audio.record')}\n </Button>\n ) : null}\n {isRecording ? (\n <IconButton\n icon={<Pause />}\n aria-label={t('chat.audio.pause')}\n intent=\"secondary\"\n size=\"sm\"\n onClick={pause}\n />\n ) : null}\n {isPaused ? (\n <IconButton\n icon={<Play />}\n aria-label={t('chat.audio.resume')}\n intent=\"secondary\"\n size=\"sm\"\n onClick={resume}\n />\n ) : null}\n {isRecording || isPaused ? (\n <>\n <IconButton\n icon={<Square />}\n aria-label={t('chat.audio.stop')}\n intent=\"primary\"\n size=\"sm\"\n onClick={stop}\n />\n <IconButton\n icon={<X />}\n aria-label={t('chat.audio.cancel')}\n intent=\"ghost\"\n size=\"sm\"\n onClick={cancel}\n />\n </>\n ) : null}\n {state.kind === 'idle' && deviceOptions.length > 1 ? (\n <div className=\"ds:ms-auto ds:min-w-[160px]\">\n <Select\n aria-label={t('chat.audio.selectDevice')}\n options={deviceOptions}\n value={selectedDeviceId}\n onValueChange={(v) => setSelectedDeviceId(v)}\n size=\"sm\"\n clearable\n />\n </div>\n ) : null}\n </div>\n\n {hasError && state.kind === 'error' ? (\n <Alert variant=\"error\" live=\"polite\">\n <Alert.Description>{statusText}</Alert.Description>\n {state.type === 'permission-denied' ? (\n <Alert.Action>\n <Button intent=\"ghost\" size=\"sm\" onClick={requestAndStart}>\n {t('chat.audio.retry')}\n </Button>\n </Alert.Action>\n ) : null}\n </Alert>\n ) : null}\n </div>\n );\n },\n);\n\nAudioRecorder.displayName = 'AudioRecorder';\n"],"names":["__iconNode","Mic","createLucideIcon","Pause","Play","audioRecorderAgent","handle","reducer","state","action","rootVariants","cva","MIME_PREFERENCES","pickMimeType","mime","formatTimer","ms","locale","total","minutes","seconds","fmt","n","AudioRecorder","forwardRef","size","onRecordingComplete","onCancel","onError","maxDurationMs","maxBytes","className","rest","ref","t","i18n","useTranslation","dispatch","useReducer","stream","setStream","useState","now","setNow","devices","setDevices","selectedDeviceId","setSelectedDeviceId","recorderRef","useRef","chunksRef","byteCountRef","durationAtStopRef","streamRef","unmountedRef","cancelledRef","supported","useEffect","_a","list","mics","d","cleanupStream","useCallback","held","requestAndStart","nextStream","tr","mimeType","recorder","event","blob","err","errorName","pause","resume","stop","started","pausedMs","duration","cancel","alreadyElapsed","remaining","_b","elapsedMs","agentHandle","useMemo","useImperativeHandle","useAgentRegistration","deviceOptions","statusText","isRecording","isPaused","hasError","jsxs","jsx","AudioVisualiser","Button","IconButton","Fragment","Square","X","Select","v","Alert"],"mappings":"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","x_google_ignoreList":[0,1,2]}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { jsxs as C, jsx as
|
|
1
|
+
import { jsxs as C, jsx as r } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef as y, useRef as j, useState as w, useEffect as A, useLayoutEffect as I } from "react";
|
|
3
3
|
import { c as N } from "./index-D2ZczOXr.js";
|
|
4
4
|
import { useTranslation as E } from "react-i18next";
|
|
5
5
|
import { c as B } from "./compose-refs-C0k0tdqF.js";
|
|
6
6
|
function R(s) {
|
|
7
7
|
let e = 5381;
|
|
8
|
-
for (let
|
|
9
|
-
e = (e << 5) + e ^ s.charCodeAt(
|
|
8
|
+
for (let t = 0; t < s.length; t++)
|
|
9
|
+
e = (e << 5) + e ^ s.charCodeAt(t), e = e >>> 0;
|
|
10
10
|
return e;
|
|
11
11
|
}
|
|
12
12
|
const i = [
|
|
@@ -52,23 +52,31 @@ const T = N(
|
|
|
52
52
|
sm: "ds:size-8 ds:text-[length:var(--font-size-xs)] ds:font-semibold ds:leading-none",
|
|
53
53
|
md: "ds:size-10 ds:text-[length:var(--font-size-xs)] ds:font-semibold ds:leading-none",
|
|
54
54
|
lg: "ds:size-12 ds:text-[length:var(--font-size-sm)] ds:font-semibold ds:leading-none",
|
|
55
|
-
xl: "ds:size-16 ds:text-[length:var(--font-size-base)] ds:font-semibold ds:leading-none"
|
|
55
|
+
xl: "ds:size-16 ds:text-[length:var(--font-size-base)] ds:font-semibold ds:leading-none",
|
|
56
|
+
// 2xl / 3xl are hero / display crops (operator cards, practice-
|
|
57
|
+
// profile pages). They token-reference `--avatar-size-*` so
|
|
58
|
+
// consumer themes can override the dimensions per-context, and
|
|
59
|
+
// `--avatar-text-size-*` so initials scale proportionally to
|
|
60
|
+
// the circle (otherwise --font-size-base at 192px reads as a
|
|
61
|
+
// tiny letter in a vast disc).
|
|
62
|
+
"2xl": "ds:size-[var(--avatar-size-2xl)] ds:text-[length:var(--avatar-text-size-2xl)] ds:font-semibold ds:leading-none",
|
|
63
|
+
"3xl": "ds:size-[var(--avatar-size-3xl)] ds:text-[length:var(--avatar-text-size-3xl)] ds:font-semibold ds:leading-none"
|
|
56
64
|
}
|
|
57
65
|
},
|
|
58
66
|
defaultVariants: { size: "md" }
|
|
59
67
|
}
|
|
60
68
|
), U = y(
|
|
61
|
-
({ src: s, name: e, color:
|
|
62
|
-
const { t: g } = E(), l = j(null), u = B(
|
|
69
|
+
({ src: s, name: e, color: t, size: d, className: c, ...v }, f) => {
|
|
70
|
+
const { t: g } = E(), l = j(null), u = B(f, l), [b, n] = w(!1);
|
|
63
71
|
A(() => {
|
|
64
72
|
n(!1);
|
|
65
73
|
}, [s]), I(() => {
|
|
66
74
|
const a = l.current;
|
|
67
|
-
a && (
|
|
68
|
-
}, [
|
|
69
|
-
const
|
|
75
|
+
a && (t ? a.style.setProperty("--avatar-color-override", t) : a.style.removeProperty("--avatar-color-override"));
|
|
76
|
+
}, [t]);
|
|
77
|
+
const o = !!s && !b, m = !o && !!e, h = !o && !e, p = t ? "ds:bg-[var(--avatar-color-override)]" : e ? S(e) : "ds:bg-muted", x = T({
|
|
70
78
|
size: d,
|
|
71
|
-
className: [p,
|
|
79
|
+
className: [p, t || e ? "ds:text-[var(--primary-foreground)]" : "ds:text-muted-foreground", c].filter(Boolean).join(" ")
|
|
72
80
|
}), z = e ?? g("avatar.placeholder", "User avatar");
|
|
73
81
|
return /* @__PURE__ */ C(
|
|
74
82
|
"span",
|
|
@@ -78,19 +86,19 @@ const T = N(
|
|
|
78
86
|
"aria-label": z,
|
|
79
87
|
"data-component": "avatar",
|
|
80
88
|
className: x,
|
|
81
|
-
...
|
|
89
|
+
...v,
|
|
82
90
|
children: [
|
|
83
|
-
(m ||
|
|
84
|
-
h && /* @__PURE__ */
|
|
91
|
+
(m || o) && !!e && /* @__PURE__ */ r("span", { "aria-hidden": "true", children: L(e) }),
|
|
92
|
+
h && /* @__PURE__ */ r(
|
|
85
93
|
"svg",
|
|
86
94
|
{
|
|
87
95
|
"aria-hidden": "true",
|
|
88
96
|
viewBox: "0 0 24 24",
|
|
89
97
|
className: "ds:size-[58%] ds:fill-current",
|
|
90
|
-
children: /* @__PURE__ */
|
|
98
|
+
children: /* @__PURE__ */ r("path", { d: "M12 12c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm0 2c-3.33 0-10 1.67-10 5v2h20v-2c0-3.33-6.67-5-10-5z" })
|
|
91
99
|
}
|
|
92
100
|
),
|
|
93
|
-
|
|
101
|
+
o && /* @__PURE__ */ r(
|
|
94
102
|
"img",
|
|
95
103
|
{
|
|
96
104
|
src: s,
|
|
@@ -111,4 +119,4 @@ export {
|
|
|
111
119
|
L as g,
|
|
112
120
|
S as n
|
|
113
121
|
};
|
|
114
|
-
//# sourceMappingURL=avatar-
|
|
122
|
+
//# sourceMappingURL=avatar-BpFohgWG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"avatar-BpFohgWG.js","sources":["../../src/components/avatar/avatar.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type HTMLAttributes,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { composeRefs } from '../_shared/compose-refs';\n\n// ── Hash ──────────────────────────────────────────────────────────────────────\n\n/** djb2 hash — fast, unsigned 32-bit, stable across calls. */\nfunction djb2(str: string): number {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = ((hash << 5) + hash) ^ str.charCodeAt(i);\n hash = hash >>> 0; // coerce to unsigned 32-bit integer\n }\n return hash;\n}\n\n/**\n * 16 brand hues — all verified ≥ 4.5 : 1 contrast against white\n * (var(--primary-foreground)) using the WCAG 2.x relative-luminance formula.\n * Spans four distinct hue families for good visual distribution.\n *\n * Index | Token | Contrast | Visual character\n * 0 | --color-violet-500 | 4.74 : 1 | bright violet\n * 1 | --color-violet-600 | 5.53 : 1 | medium violet\n * 2 | --color-violet-700 | 7.29 : 1 | dark violet\n * 3 | --color-violet-800 | 9.57 : 1 | deep violet\n * 4 | --color-violet-900 | 12.06 : 1 | midnight violet\n * 5 | --color-purple-700 | 5.21 : 1 | medium purple\n * 6 | --color-purple-800 | 7.52 : 1 | dark purple\n * 7 | --color-purple-900 | 10.40 : 1 | deep purple\n * 8 | --color-magenta-700 | 6.01 : 1 | vivid magenta\n * 9 | --color-magenta-800 | 8.05 : 1 | dark magenta\n * 10 | --color-magenta-900 | 11.20 : 1 | deep magenta\n * 11 | --color-blue-300 | 4.64 : 1 | slate / steel blue\n * 12 | --color-blue-400 | 6.95 : 1 | medium slate\n * 13 | --color-blue-500 | 13.00 : 1 | deep navy\n * 14 | --color-blue-700 | 17.82 : 1 | midnight navy\n * 15 | --color-grey-900 | 5.78 : 1 | warm charcoal\n */\nconst INITIALS_BG_CLASSES = [\n 'ds:bg-[var(--color-violet-500)]',\n 'ds:bg-[var(--color-violet-600)]',\n 'ds:bg-[var(--color-violet-700)]',\n 'ds:bg-[var(--color-violet-800)]',\n 'ds:bg-[var(--color-violet-900)]',\n 'ds:bg-[var(--color-purple-700)]',\n 'ds:bg-[var(--color-purple-800)]',\n 'ds:bg-[var(--color-purple-900)]',\n 'ds:bg-[var(--color-magenta-700)]',\n 'ds:bg-[var(--color-magenta-800)]',\n 'ds:bg-[var(--color-magenta-900)]',\n 'ds:bg-[var(--color-blue-300)]',\n 'ds:bg-[var(--color-blue-400)]',\n 'ds:bg-[var(--color-blue-500)]',\n 'ds:bg-[var(--color-blue-700)]',\n 'ds:bg-[var(--color-grey-900)]',\n] as const;\n\n/** Deterministically maps a name string to one of 16 brand-hue Tailwind classes. Same name always produces the same class. */\nexport function nameToColorClass(name: string): string {\n return INITIALS_BG_CLASSES[djb2(name) % INITIALS_BG_CLASSES.length];\n}\n\n/**\n * Extracts 1–2 uppercase initials:\n * - Single-word name: first two characters (\"John\" → \"JO\")\n * - Multi-word name: first char of first word + first char of last word (\"John Doe\" → \"JD\")\n */\nexport function getInitials(name: string): string {\n const words = name.trim().split(/\\s+/).filter(Boolean);\n if (words.length === 0) return '';\n if (words.length === 1) return words[0].slice(0, 2).toUpperCase();\n return (words[0][0] + words[words.length - 1][0]).toUpperCase();\n}\n\n// ── CVA ───────────────────────────────────────────────────────────────────────\n\nconst avatarVariants = cva(\n [\n 'ds:relative ds:inline-flex ds:shrink-0 ds:select-none ds:items-center ds:justify-center ds:overflow-hidden ds:rounded-[var(--radius-full)]',\n // Focus ring — uses design-system tokens so theme overrides (accessible: 3px) apply automatically.\n // The ring renders when Avatar is placed inside a focusable ancestor (e.g. a <button>).\n 'ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)]',\n 'ds:focus-visible:outline-ring',\n 'ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n ].join(' '),\n {\n variants: {\n size: {\n // text-[var(--font-size-xs)] = 0.75rem (12px) — smallest available token\n xs: 'ds:size-6 ds:text-[length:var(--font-size-xs)] ds:font-semibold ds:leading-none',\n sm: 'ds:size-8 ds:text-[length:var(--font-size-xs)] ds:font-semibold ds:leading-none',\n md: 'ds:size-10 ds:text-[length:var(--font-size-xs)] ds:font-semibold ds:leading-none',\n lg: 'ds:size-12 ds:text-[length:var(--font-size-sm)] ds:font-semibold ds:leading-none',\n xl: 'ds:size-16 ds:text-[length:var(--font-size-base)] ds:font-semibold ds:leading-none',\n // 2xl / 3xl are hero / display crops (operator cards, practice-\n // profile pages). They token-reference `--avatar-size-*` so\n // consumer themes can override the dimensions per-context, and\n // `--avatar-text-size-*` so initials scale proportionally to\n // the circle (otherwise --font-size-base at 192px reads as a\n // tiny letter in a vast disc).\n '2xl':\n 'ds:size-[var(--avatar-size-2xl)] ds:text-[length:var(--avatar-text-size-2xl)] ds:font-semibold ds:leading-none',\n '3xl':\n 'ds:size-[var(--avatar-size-3xl)] ds:text-[length:var(--avatar-text-size-3xl)] ds:font-semibold ds:leading-none',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\n// ── Props ─────────────────────────────────────────────────────────────────────\n\nexport interface AvatarProps\n extends\n Omit<HTMLAttributes<HTMLSpanElement>, 'color'>,\n VariantProps<typeof avatarVariants> {\n /** Image source URL. When provided and loads successfully, the image is displayed. */\n src?: string;\n /** Display name — drives initials extraction and deterministic background colour. */\n name?: string;\n /**\n * Override the auto-generated background with any CSS colour string\n * (e.g. `\"var(--color-violet-700)\"`).\n * Applied via a CSS custom property so no `style` attribute is added to the JSX element.\n * Caller is responsible for WCAG AA contrast (≥4.5:1) against white text.\n */\n color?: string;\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport const Avatar = forwardRef<HTMLSpanElement, AvatarProps>(\n ({ src, name, color, size, className, ...props }, ref) => {\n const { t } = useTranslation();\n\n const innerRef = useRef<HTMLSpanElement>(null);\n const composedRef = composeRefs(ref, innerRef);\n\n // Track only image errors. The image renders immediately as an overlay;\n // before it loads the browser paints nothing (transparent), showing the\n // background colour + initials behind it. On error we remove the img and\n // the initials/fallback remain permanently visible.\n const [imgError, setImgError] = useState(false);\n\n // Reset error flag whenever src changes\n useEffect(() => {\n setImgError(false);\n }, [src]);\n\n // Apply color override via CSS custom property — avoids style={{…}} on JSX.\n // Mirrors the pattern used by the Progress component for imperative CSS property injection.\n useLayoutEffect(() => {\n const el = innerRef.current;\n if (!el) return;\n if (color) {\n el.style.setProperty('--avatar-color-override', color);\n } else {\n el.style.removeProperty('--avatar-color-override');\n }\n }, [color]);\n\n // Image is \"active\" when provided and not errored\n const imgActive = !!src && !imgError;\n\n // Initials and fallback icon are shown when no usable image\n const showInitials = !imgActive && !!name;\n const showIcon = !imgActive && !name;\n\n // Background — always rendered (serves as loading placeholder when image is fetching)\n const fallbackBgClass = color\n ? 'ds:bg-[var(--avatar-color-override)]'\n : name\n ? nameToColorClass(name)\n : 'ds:bg-muted';\n\n // Always white text against brand hues / color override; muted-foreground for icon\n const contentColorClass =\n color || name\n ? 'ds:text-[var(--primary-foreground)]'\n : 'ds:text-muted-foreground';\n\n const rootClassName = avatarVariants({\n size,\n className: [fallbackBgClass, contentColorClass, className]\n .filter(Boolean)\n .join(' '),\n });\n\n // Accessible label — name takes priority, then the localised placeholder\n const ariaLabel = name ?? t('avatar.placeholder', 'User avatar');\n\n return (\n <span\n ref={composedRef}\n role=\"img\"\n aria-label={ariaLabel}\n data-component=\"avatar\"\n className={rootClassName}\n {...props}\n >\n {/* Initials — rendered behind the image; visible while image is loading\n or permanently when no src / src errors */}\n {(showInitials || imgActive) && !!name && (\n <span aria-hidden=\"true\">{getInitials(name)}</span>\n )}\n\n {/* Fallback icon — shown when neither image nor name is available */}\n {showIcon && (\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n className=\"ds:size-[58%] ds:fill-current\"\n >\n <path d=\"M12 12c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm0 2c-3.33 0-10 1.67-10 5v2h20v-2c0-3.33-6.67-5-10-5z\" />\n </svg>\n )}\n\n {/* Image — absolutely positioned overlay. Renders transparently until the\n browser paints the decoded pixels, then covers the initials behind it.\n Removed from DOM on error so initials remain permanently visible. */}\n {imgActive && (\n <img\n src={src}\n alt=\"\"\n aria-hidden=\"true\"\n className=\"ds:absolute ds:inset-0 ds:size-full ds:object-cover\"\n onError={() => setImgError(true)}\n />\n )}\n </span>\n );\n },\n);\n\nAvatar.displayName = 'Avatar';\n"],"names":["djb2","str","hash","i","INITIALS_BG_CLASSES","nameToColorClass","name","getInitials","words","avatarVariants","cva","Avatar","forwardRef","src","color","size","className","props","ref","t","useTranslation","innerRef","useRef","composedRef","composeRefs","imgError","setImgError","useState","useEffect","useLayoutEffect","el","imgActive","showInitials","showIcon","fallbackBgClass","rootClassName","ariaLabel","jsxs","jsx"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { forwardRef as C, useMemo as j, Fragment as g } from "react";
|
|
|
3
3
|
import { c as h } from "./index-D2ZczOXr.js";
|
|
4
4
|
import { useTranslation as T } from "react-i18next";
|
|
5
5
|
import { B as z } from "./button-DD_0Xdmr.js";
|
|
6
|
-
import { A as M } from "./avatar-
|
|
6
|
+
import { A as M } from "./avatar-BpFohgWG.js";
|
|
7
7
|
import { T as I } from "./timestamp-BV2lC-wV.js";
|
|
8
8
|
import { s as A } from "./safe-image-src-DstKgCo7.js";
|
|
9
9
|
import { c as F } from "./createLucideIcon-CrFbzy84.js";
|
|
@@ -236,4 +236,4 @@ H.displayName = "ChatMessage";
|
|
|
236
236
|
export {
|
|
237
237
|
H as C
|
|
238
238
|
};
|
|
239
|
-
//# sourceMappingURL=chat-message-
|
|
239
|
+
//# sourceMappingURL=chat-message-W3k8rLOA.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-message-ChOnwqf_.js","sources":["../../node_modules/lucide-react/dist/esm/icons/rotate-ccw.js","../../src/components/chat-message/chat-message.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"path\", { d: \"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\", key: \"1357e3\" }],\n [\"path\", { d: \"M3 3v5h5\", key: \"1xhq8a\" }]\n];\nconst RotateCcw = createLucideIcon(\"rotate-ccw\", __iconNode);\n\nexport { __iconNode, RotateCcw as default };\n//# sourceMappingURL=rotate-ccw.js.map\n","import {\n forwardRef,\n Fragment,\n useMemo,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { AlertCircle, Check, Clock, RotateCcw } from 'lucide-react';\nimport { Button } from '../button';\nimport { Avatar } from '../avatar';\nimport { Timestamp } from '../timestamp';\nimport { safeImageSrc } from '../_shared';\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport type ChatMessageRole = 'user' | 'assistant' | 'system';\nexport type ChatMessageStatus = 'sending' | 'sent' | 'error' | 'edited';\n\nexport interface ChatMessageAvatar {\n name?: string;\n src?: string;\n /**\n * Optional custom avatar node. When provided, it REPLACES the built-in\n * `<Avatar>` (initials / image) for this message. Used by the Alia\n * pattern to render a Sparkles identity tile instead of an avatar, and\n * by consumers that want to swap in a user-icon chip, brand glyph, etc.\n * The caller is responsible for accessible labelling on the custom node\n * — ChatMessage's own `aria-label` already announces the role/time, so\n * purely decorative nodes can be `aria-hidden`.\n */\n slot?: ReactNode;\n}\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst rowVariants = cva(\n 'ds:flex ds:w-full ds:items-start ds:gap-[var(--spacing-sm)]',\n {\n variants: {\n role: {\n user: 'ds:flex-row-reverse',\n assistant: 'ds:flex-row',\n system: 'ds:flex-col ds:items-center ds:text-center',\n },\n },\n defaultVariants: { role: 'assistant' },\n },\n);\n\nconst bubbleVariants = cva(\n [\n // Cap at 42rem on wide surfaces; on narrow docks (Alia sidebar ≈22rem)\n // leave room for the avatar column (size-8 = 32px = --spacing-xl) +\n // `gap-sm` (8px = --spacing-sm) so avatars don't clip off the inline\n // edge when the row is width-constrained.\n 'ds:relative ds:max-w-[min(42rem,calc(100%-var(--spacing-xl)-var(--spacing-sm)))]',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)]',\n 'ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]',\n 'type-body',\n 'ds:break-words',\n ].join(' '),\n {\n variants: {\n role: {\n user: 'ds:bg-[color:var(--primary)] ds:text-[color:var(--primary-foreground)] ds:rounded-[var(--radius-md)] ds:rounded-ee-[var(--radius-sm)]',\n assistant:\n 'ds:bg-muted/40 ds:text-foreground ds:rounded-[var(--radius-md)] ds:rounded-es-[var(--radius-sm)]',\n system:\n 'ds:bg-transparent ds:text-[color:var(--muted-foreground)] type-body-sm ds:italic',\n },\n },\n defaultVariants: { role: 'assistant' },\n },\n);\n\n/* ------------------------------------------------------------------ */\n/* Safe markdown-ish (bold, inline code, line-breaks, links as text) */\n/* No dangerouslySetInnerHTML. */\n/* ------------------------------------------------------------------ */\n\ntype Token =\n | { type: 'text'; value: string }\n | { type: 'bold'; value: string }\n | { type: 'code'; value: string }\n | { type: 'br' }\n | { type: 'link'; label: string; href: string };\n\nconst SAFE_SCHEMES = /^(https?:|mailto:)/i;\n\nfunction tokenize(input: string): Token[] {\n const tokens: Token[] = [];\n let remaining = input;\n const pattern = /\\*\\*([^*]+)\\*\\*|`([^`]+)`|\\[([^\\]]+)\\]\\(([^)]+)\\)|\\n/;\n while (remaining.length > 0) {\n const match = pattern.exec(remaining);\n if (!match) {\n tokens.push({ type: 'text', value: remaining });\n break;\n }\n if (match.index > 0) {\n tokens.push({ type: 'text', value: remaining.slice(0, match.index) });\n }\n if (match[1] !== undefined) {\n tokens.push({ type: 'bold', value: match[1] });\n } else if (match[2] !== undefined) {\n tokens.push({ type: 'code', value: match[2] });\n } else if (match[3] !== undefined && match[4] !== undefined) {\n tokens.push({ type: 'link', label: match[3], href: match[4] });\n } else {\n tokens.push({ type: 'br' });\n }\n remaining = remaining.slice(match.index + match[0].length);\n }\n return tokens;\n}\n\nfunction renderBody(input: string): ReactNode {\n return tokenize(input).map((tok, i) => {\n if (tok.type === 'bold') return <strong key={i}>{tok.value}</strong>;\n if (tok.type === 'code') {\n return (\n <code\n key={i}\n dir=\"ltr\"\n className=\"ds:rounded-[var(--radius-sm)] ds:bg-muted/30 ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:font-[family-name:var(--font-mono)]\"\n >\n {tok.value}\n </code>\n );\n }\n if (tok.type === 'br') return <br key={i} />;\n if (tok.type === 'link') {\n if (!SAFE_SCHEMES.test(tok.href)) {\n // Unsafe scheme (javascript:, data:) — render as inert text.\n return <Fragment key={i}>{tok.label}</Fragment>;\n }\n return (\n <a\n key={i}\n href={tok.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"ds:underline ds:underline-offset-2 ds:hover:opacity-80\"\n >\n {tok.label}\n </a>\n );\n }\n return <Fragment key={i}>{tok.value}</Fragment>;\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* ChatMessage */\n/* ------------------------------------------------------------------ */\n\ntype NativeProps = Omit<HTMLAttributes<HTMLElement>, 'content' | 'role'>;\n\nexport interface ChatMessageProps\n extends NativeProps, VariantProps<typeof bubbleVariants> {\n role: ChatMessageRole;\n content: string;\n avatar?: ChatMessageAvatar;\n timestamp?: Date | number | string;\n status?: ChatMessageStatus;\n /** Parse a small, safe subset of markdown. Never renders raw HTML. */\n renderMarkdown?: boolean;\n /** Invoked when the retry button is activated (error status only). */\n onRetry?: () => void;\n}\n\nexport const ChatMessage = forwardRef<HTMLElement, ChatMessageProps>(\n (\n {\n role,\n content,\n avatar,\n timestamp,\n status,\n renderMarkdown = false,\n onRetry,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n\n // Local format — used for the aria-label interpolation only. Visible\n // label is rendered by `<Timestamp>` below, which computes the same\n // locale-aware HH:MM internally.\n const formattedTime = useMemo(() => {\n if (!timestamp) return null;\n const date = timestamp instanceof Date ? timestamp : new Date(timestamp);\n if (Number.isNaN(date.getTime())) return null;\n return new Intl.DateTimeFormat(i18n.language, {\n hour: '2-digit',\n minute: '2-digit',\n }).format(date);\n }, [timestamp, i18n.language]);\n\n const roleLabel = t(`chat.message.role.${role}`);\n const messageLabel = formattedTime\n ? t('chat.message.label', { role: roleLabel, time: formattedTime })\n : t('chat.message.labelNoTime', { role: roleLabel });\n\n const body = renderMarkdown ? renderBody(content) : content;\n\n const statusIcon = (() => {\n if (!status || status === 'edited') return null;\n if (status === 'sending')\n return <Clock aria-hidden=\"true\" className=\"ds:size-3.5\" />;\n if (status === 'sent')\n return <Check aria-hidden=\"true\" className=\"ds:size-3.5\" />;\n return (\n <AlertCircle\n aria-hidden=\"true\"\n className=\"ds:size-3.5 ds:text-[color:var(--destructive)]\"\n />\n );\n })();\n\n const statusText = status ? t(`chat.message.status.${status}`) : null;\n\n return (\n <article\n ref={ref}\n aria-label={messageLabel}\n data-component=\"chat-message\"\n className={[\n rowVariants({ role, className }),\n // Entrance: fade + slide up on mount. `--animation-duration` is 0ms\n // under `prefers-reduced-motion` and `.theme-accessible`, so both\n // collapse the animation to an instant state change automatically.\n 'ds:motion-safe:animate-in ds:motion-safe:fade-in-0 ds:motion-safe:slide-in-from-bottom-2',\n 'ds:duration-[var(--animation-duration)] ds:ease-[var(--ease-out)]',\n ].join(' ')}\n {...rest}\n >\n {role !== 'system' && avatar ? (\n avatar.slot ? (\n // Consumer-supplied avatar node (e.g. Alia's Sparkles tile).\n avatar.slot\n ) : (\n // Allow-list the src: LLM-supplied values can carry\n // `javascript:` / `data:text/html` / `data:image/svg+xml` and so\n // are scrubbed here before reaching <Avatar>. Fallback to initials.\n <Avatar\n name={avatar.name}\n src={safeImageSrc(avatar.src)}\n size=\"sm\"\n />\n )\n ) : null}\n\n <div\n className={[\n // min-w-0 allows this flex item to shrink below its content's\n // natural width — without it, a single unbreakable token (URL,\n // CJK run, hash) can force the bubble wider than the parent\n // and spill outside narrow containers like the Alia sidebar.\n 'ds:flex ds:flex-col ds:min-w-0',\n role === 'user'\n ? 'ds:items-end'\n : role === 'assistant'\n ? 'ds:items-start'\n : 'ds:items-center',\n ].join(' ')}\n >\n <div\n dir=\"auto\"\n className={[\n bubbleVariants({ role }),\n // `break-words` (from bubbleVariants) handles soft hyphens;\n // `overflow-wrap: anywhere` is the last-resort break for\n // URLs, hashes, or CJK runs that would otherwise push the\n // bubble wider than its parent in narrow docks like Alia.\n 'ds:[overflow-wrap:anywhere]',\n ].join(' ')}\n >\n {body}\n </div>\n\n {(formattedTime || statusText) && role !== 'system' ? (\n <div\n className={[\n 'ds:mt-[var(--spacing-xs)] ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'type-meta ds:text-[color:var(--muted-foreground)]',\n ].join(' ')}\n >\n {timestamp ? (\n <Timestamp\n value={timestamp}\n format=\"absolute\"\n absoluteFormat={{ hour: '2-digit', minute: '2-digit' }}\n dir=\"ltr\"\n />\n ) : null}\n {statusIcon ? (\n // `key={status}` re-mounts the span on every status change so\n // `animate-in` re-runs — gives the sending → sent → error\n // swap a subtle cross-fade instead of an abrupt pop.\n <span\n key={status}\n className={[\n 'ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'ds:motion-safe:animate-in ds:motion-safe:fade-in-0',\n 'ds:duration-[var(--animation-duration)] ds:ease-[var(--ease-out)]',\n ].join(' ')}\n >\n {statusIcon}\n <span className=\"ds:sr-only\">{statusText}</span>\n </span>\n ) : null}\n {status === 'error' && onRetry ? (\n <Button\n intent=\"ghost\"\n size=\"sm\"\n onClick={onRetry}\n startIcon={<RotateCcw />}\n >\n {t('chat.message.retry')}\n </Button>\n ) : null}\n </div>\n ) : null}\n </div>\n </article>\n );\n },\n);\n\nChatMessage.displayName = 'ChatMessage';\n"],"names":["__iconNode","RotateCcw","createLucideIcon","rowVariants","cva","bubbleVariants","SAFE_SCHEMES","tokenize","input","tokens","remaining","pattern","match","renderBody","tok","i","jsx","Fragment","ChatMessage","forwardRef","role","content","avatar","timestamp","status","renderMarkdown","onRetry","className","rest","ref","t","i18n","useTranslation","formattedTime","useMemo","date","roleLabel","messageLabel","body","statusIcon","Clock","Check","AlertCircle","statusText","jsxs","Avatar","safeImageSrc","Timestamp","Button"],"mappings":"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","x_google_ignoreList":[0]}
|
|
1
|
+
{"version":3,"file":"chat-message-W3k8rLOA.js","sources":["../../node_modules/lucide-react/dist/esm/icons/rotate-ccw.js","../../src/components/chat-message/chat-message.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"path\", { d: \"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\", key: \"1357e3\" }],\n [\"path\", { d: \"M3 3v5h5\", key: \"1xhq8a\" }]\n];\nconst RotateCcw = createLucideIcon(\"rotate-ccw\", __iconNode);\n\nexport { __iconNode, RotateCcw as default };\n//# sourceMappingURL=rotate-ccw.js.map\n","import {\n forwardRef,\n Fragment,\n useMemo,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { AlertCircle, Check, Clock, RotateCcw } from 'lucide-react';\nimport { Button } from '../button';\nimport { Avatar } from '../avatar';\nimport { Timestamp } from '../timestamp';\nimport { safeImageSrc } from '../_shared';\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport type ChatMessageRole = 'user' | 'assistant' | 'system';\nexport type ChatMessageStatus = 'sending' | 'sent' | 'error' | 'edited';\n\nexport interface ChatMessageAvatar {\n name?: string;\n src?: string;\n /**\n * Optional custom avatar node. When provided, it REPLACES the built-in\n * `<Avatar>` (initials / image) for this message. Used by the Alia\n * pattern to render a Sparkles identity tile instead of an avatar, and\n * by consumers that want to swap in a user-icon chip, brand glyph, etc.\n * The caller is responsible for accessible labelling on the custom node\n * — ChatMessage's own `aria-label` already announces the role/time, so\n * purely decorative nodes can be `aria-hidden`.\n */\n slot?: ReactNode;\n}\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst rowVariants = cva(\n 'ds:flex ds:w-full ds:items-start ds:gap-[var(--spacing-sm)]',\n {\n variants: {\n role: {\n user: 'ds:flex-row-reverse',\n assistant: 'ds:flex-row',\n system: 'ds:flex-col ds:items-center ds:text-center',\n },\n },\n defaultVariants: { role: 'assistant' },\n },\n);\n\nconst bubbleVariants = cva(\n [\n // Cap at 42rem on wide surfaces; on narrow docks (Alia sidebar ≈22rem)\n // leave room for the avatar column (size-8 = 32px = --spacing-xl) +\n // `gap-sm` (8px = --spacing-sm) so avatars don't clip off the inline\n // edge when the row is width-constrained.\n 'ds:relative ds:max-w-[min(42rem,calc(100%-var(--spacing-xl)-var(--spacing-sm)))]',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)]',\n 'ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]',\n 'type-body',\n 'ds:break-words',\n ].join(' '),\n {\n variants: {\n role: {\n user: 'ds:bg-[color:var(--primary)] ds:text-[color:var(--primary-foreground)] ds:rounded-[var(--radius-md)] ds:rounded-ee-[var(--radius-sm)]',\n assistant:\n 'ds:bg-muted/40 ds:text-foreground ds:rounded-[var(--radius-md)] ds:rounded-es-[var(--radius-sm)]',\n system:\n 'ds:bg-transparent ds:text-[color:var(--muted-foreground)] type-body-sm ds:italic',\n },\n },\n defaultVariants: { role: 'assistant' },\n },\n);\n\n/* ------------------------------------------------------------------ */\n/* Safe markdown-ish (bold, inline code, line-breaks, links as text) */\n/* No dangerouslySetInnerHTML. */\n/* ------------------------------------------------------------------ */\n\ntype Token =\n | { type: 'text'; value: string }\n | { type: 'bold'; value: string }\n | { type: 'code'; value: string }\n | { type: 'br' }\n | { type: 'link'; label: string; href: string };\n\nconst SAFE_SCHEMES = /^(https?:|mailto:)/i;\n\nfunction tokenize(input: string): Token[] {\n const tokens: Token[] = [];\n let remaining = input;\n const pattern = /\\*\\*([^*]+)\\*\\*|`([^`]+)`|\\[([^\\]]+)\\]\\(([^)]+)\\)|\\n/;\n while (remaining.length > 0) {\n const match = pattern.exec(remaining);\n if (!match) {\n tokens.push({ type: 'text', value: remaining });\n break;\n }\n if (match.index > 0) {\n tokens.push({ type: 'text', value: remaining.slice(0, match.index) });\n }\n if (match[1] !== undefined) {\n tokens.push({ type: 'bold', value: match[1] });\n } else if (match[2] !== undefined) {\n tokens.push({ type: 'code', value: match[2] });\n } else if (match[3] !== undefined && match[4] !== undefined) {\n tokens.push({ type: 'link', label: match[3], href: match[4] });\n } else {\n tokens.push({ type: 'br' });\n }\n remaining = remaining.slice(match.index + match[0].length);\n }\n return tokens;\n}\n\nfunction renderBody(input: string): ReactNode {\n return tokenize(input).map((tok, i) => {\n if (tok.type === 'bold') return <strong key={i}>{tok.value}</strong>;\n if (tok.type === 'code') {\n return (\n <code\n key={i}\n dir=\"ltr\"\n className=\"ds:rounded-[var(--radius-sm)] ds:bg-muted/30 ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:font-[family-name:var(--font-mono)]\"\n >\n {tok.value}\n </code>\n );\n }\n if (tok.type === 'br') return <br key={i} />;\n if (tok.type === 'link') {\n if (!SAFE_SCHEMES.test(tok.href)) {\n // Unsafe scheme (javascript:, data:) — render as inert text.\n return <Fragment key={i}>{tok.label}</Fragment>;\n }\n return (\n <a\n key={i}\n href={tok.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"ds:underline ds:underline-offset-2 ds:hover:opacity-80\"\n >\n {tok.label}\n </a>\n );\n }\n return <Fragment key={i}>{tok.value}</Fragment>;\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* ChatMessage */\n/* ------------------------------------------------------------------ */\n\ntype NativeProps = Omit<HTMLAttributes<HTMLElement>, 'content' | 'role'>;\n\nexport interface ChatMessageProps\n extends NativeProps, VariantProps<typeof bubbleVariants> {\n role: ChatMessageRole;\n content: string;\n avatar?: ChatMessageAvatar;\n timestamp?: Date | number | string;\n status?: ChatMessageStatus;\n /** Parse a small, safe subset of markdown. Never renders raw HTML. */\n renderMarkdown?: boolean;\n /** Invoked when the retry button is activated (error status only). */\n onRetry?: () => void;\n}\n\nexport const ChatMessage = forwardRef<HTMLElement, ChatMessageProps>(\n (\n {\n role,\n content,\n avatar,\n timestamp,\n status,\n renderMarkdown = false,\n onRetry,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n\n // Local format — used for the aria-label interpolation only. Visible\n // label is rendered by `<Timestamp>` below, which computes the same\n // locale-aware HH:MM internally.\n const formattedTime = useMemo(() => {\n if (!timestamp) return null;\n const date = timestamp instanceof Date ? timestamp : new Date(timestamp);\n if (Number.isNaN(date.getTime())) return null;\n return new Intl.DateTimeFormat(i18n.language, {\n hour: '2-digit',\n minute: '2-digit',\n }).format(date);\n }, [timestamp, i18n.language]);\n\n const roleLabel = t(`chat.message.role.${role}`);\n const messageLabel = formattedTime\n ? t('chat.message.label', { role: roleLabel, time: formattedTime })\n : t('chat.message.labelNoTime', { role: roleLabel });\n\n const body = renderMarkdown ? renderBody(content) : content;\n\n const statusIcon = (() => {\n if (!status || status === 'edited') return null;\n if (status === 'sending')\n return <Clock aria-hidden=\"true\" className=\"ds:size-3.5\" />;\n if (status === 'sent')\n return <Check aria-hidden=\"true\" className=\"ds:size-3.5\" />;\n return (\n <AlertCircle\n aria-hidden=\"true\"\n className=\"ds:size-3.5 ds:text-[color:var(--destructive)]\"\n />\n );\n })();\n\n const statusText = status ? t(`chat.message.status.${status}`) : null;\n\n return (\n <article\n ref={ref}\n aria-label={messageLabel}\n data-component=\"chat-message\"\n className={[\n rowVariants({ role, className }),\n // Entrance: fade + slide up on mount. `--animation-duration` is 0ms\n // under `prefers-reduced-motion` and `.theme-accessible`, so both\n // collapse the animation to an instant state change automatically.\n 'ds:motion-safe:animate-in ds:motion-safe:fade-in-0 ds:motion-safe:slide-in-from-bottom-2',\n 'ds:duration-[var(--animation-duration)] ds:ease-[var(--ease-out)]',\n ].join(' ')}\n {...rest}\n >\n {role !== 'system' && avatar ? (\n avatar.slot ? (\n // Consumer-supplied avatar node (e.g. Alia's Sparkles tile).\n avatar.slot\n ) : (\n // Allow-list the src: LLM-supplied values can carry\n // `javascript:` / `data:text/html` / `data:image/svg+xml` and so\n // are scrubbed here before reaching <Avatar>. Fallback to initials.\n <Avatar\n name={avatar.name}\n src={safeImageSrc(avatar.src)}\n size=\"sm\"\n />\n )\n ) : null}\n\n <div\n className={[\n // min-w-0 allows this flex item to shrink below its content's\n // natural width — without it, a single unbreakable token (URL,\n // CJK run, hash) can force the bubble wider than the parent\n // and spill outside narrow containers like the Alia sidebar.\n 'ds:flex ds:flex-col ds:min-w-0',\n role === 'user'\n ? 'ds:items-end'\n : role === 'assistant'\n ? 'ds:items-start'\n : 'ds:items-center',\n ].join(' ')}\n >\n <div\n dir=\"auto\"\n className={[\n bubbleVariants({ role }),\n // `break-words` (from bubbleVariants) handles soft hyphens;\n // `overflow-wrap: anywhere` is the last-resort break for\n // URLs, hashes, or CJK runs that would otherwise push the\n // bubble wider than its parent in narrow docks like Alia.\n 'ds:[overflow-wrap:anywhere]',\n ].join(' ')}\n >\n {body}\n </div>\n\n {(formattedTime || statusText) && role !== 'system' ? (\n <div\n className={[\n 'ds:mt-[var(--spacing-xs)] ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'type-meta ds:text-[color:var(--muted-foreground)]',\n ].join(' ')}\n >\n {timestamp ? (\n <Timestamp\n value={timestamp}\n format=\"absolute\"\n absoluteFormat={{ hour: '2-digit', minute: '2-digit' }}\n dir=\"ltr\"\n />\n ) : null}\n {statusIcon ? (\n // `key={status}` re-mounts the span on every status change so\n // `animate-in` re-runs — gives the sending → sent → error\n // swap a subtle cross-fade instead of an abrupt pop.\n <span\n key={status}\n className={[\n 'ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'ds:motion-safe:animate-in ds:motion-safe:fade-in-0',\n 'ds:duration-[var(--animation-duration)] ds:ease-[var(--ease-out)]',\n ].join(' ')}\n >\n {statusIcon}\n <span className=\"ds:sr-only\">{statusText}</span>\n </span>\n ) : null}\n {status === 'error' && onRetry ? (\n <Button\n intent=\"ghost\"\n size=\"sm\"\n onClick={onRetry}\n startIcon={<RotateCcw />}\n >\n {t('chat.message.retry')}\n </Button>\n ) : null}\n </div>\n ) : null}\n </div>\n </article>\n );\n },\n);\n\nChatMessage.displayName = 'ChatMessage';\n"],"names":["__iconNode","RotateCcw","createLucideIcon","rowVariants","cva","bubbleVariants","SAFE_SCHEMES","tokenize","input","tokens","remaining","pattern","match","renderBody","tok","i","jsx","Fragment","ChatMessage","forwardRef","role","content","avatar","timestamp","status","renderMarkdown","onRetry","className","rest","ref","t","i18n","useTranslation","formattedTime","useMemo","date","roleLabel","messageLabel","body","statusIcon","Clock","Check","AlertCircle","statusText","jsxs","Avatar","safeImageSrc","Timestamp","Button"],"mappings":"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","x_google_ignoreList":[0]}
|