@neuronection/assistant-ui 0.19.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/CHANGELOG.md +17 -0
- package/README.md +3 -0
- package/dist/ai-text-transform.d.ts +44 -0
- package/dist/ai-text-transform.js +3 -0
- package/dist/ai-text-transform.js.map +1 -0
- package/dist/chunk-2PR6POP6.js +543 -0
- package/dist/chunk-2PR6POP6.js.map +1 -0
- package/dist/chunk-BKAO7BTU.js +156 -0
- package/dist/chunk-BKAO7BTU.js.map +1 -0
- package/dist/chunk-KAC2DOLH.js +362 -0
- package/dist/chunk-KAC2DOLH.js.map +1 -0
- package/dist/{chunk-AHLNXCP2.js → chunk-UJVNCZ5Z.js} +5 -4
- package/dist/chunk-UJVNCZ5Z.js.map +1 -0
- package/dist/chunk-XUDCCQRN.js +253 -0
- package/dist/chunk-XUDCCQRN.js.map +1 -0
- package/dist/dictation.d.ts +64 -0
- package/dist/dictation.js +3 -0
- package/dist/dictation.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +16 -12
- package/dist/index.js.map +1 -1
- package/dist/rich-text-editor.d.ts +51 -0
- package/dist/rich-text-editor.js +3 -0
- package/dist/rich-text-editor.js.map +1 -0
- package/dist/settings-shell.d.ts +2 -0
- package/dist/settings-shell.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/text-diff-view.d.ts +61 -0
- package/dist/text-diff-view.js +3 -0
- package/dist/text-diff-view.js.map +1 -0
- package/package.json +24 -2
- package/dist/chunk-AHLNXCP2.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/text-diff-view/lineDiff.ts","../src/components/text-diff-view/TextDiffView.tsx"],"names":["i","j","index","TextDiffView"],"mappings":"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","file":"chunk-2PR6POP6.js","sourcesContent":["export interface DiffSegment {\n text: string\n changed: boolean\n}\n\nexport interface DiffCell {\n kind: 'add' | 'del' | 'context'\n text: string\n no?: number\n segments?: DiffSegment[]\n}\n\nexport type DiffRow =\n | { kind: 'pair'; left: DiffCell | null; right: DiffCell | null }\n | { kind: 'fold'; count: number; hidden: DiffRow[] }\n\nexport interface LineDiffOptions {\n contextLines?: number\n}\n\nexport interface LineDiffResult {\n rows: DiffRow[]\n added: number\n removed: number\n}\n\nconst MAX_LCS_CELLS = 4_000_000\nconst MAX_WORD_CELLS = 40_000\n\ntype Op = { type: 'equal' | 'del' | 'add'; text: string }\n\nfunction at<T>(list: readonly T[], index: number): T {\n const value = list[index]\n if (value === undefined) {\n throw new Error(`lineDiff: index ${index} out of bounds`)\n }\n return value\n}\n\nfunction atNum(list: Int32Array, index: number): number {\n const value = list[index]\n if (value === undefined) {\n throw new Error(`lineDiff: index ${index} out of bounds`)\n }\n return value\n}\n\nfunction splitLines(text: string): string[] {\n if (text === '') {\n return []\n }\n return text.replace(/\\r\\n?/g, '\\n').split('\\n')\n}\n\nfunction diffOps(a: string[], b: string[], maxCells = MAX_LCS_CELLS): Op[] {\n const n = a.length\n const m = b.length\n const ops: Op[] = []\n if (n * m > maxCells) {\n for (const text of a) {\n ops.push({ type: 'del', text })\n }\n for (const text of b) {\n ops.push({ type: 'add', text })\n }\n return ops\n }\n const width = m + 1\n const lcs = new Int32Array((n + 1) * width)\n for (let i = n - 1; i >= 0; i -= 1) {\n for (let j = m - 1; j >= 0; j -= 1) {\n lcs[i * width + j] =\n at(a, i) === at(b, j)\n ? atNum(lcs, (i + 1) * width + j + 1) + 1\n : Math.max(atNum(lcs, (i + 1) * width + j), atNum(lcs, i * width + j + 1))\n }\n }\n let i = 0\n let j = 0\n while (i < n && j < m) {\n if (at(a, i) === at(b, j)) {\n ops.push({ type: 'equal', text: at(a, i) })\n i += 1\n j += 1\n } else if (atNum(lcs, (i + 1) * width + j) >= atNum(lcs, i * width + j + 1)) {\n ops.push({ type: 'del', text: at(a, i) })\n i += 1\n } else {\n ops.push({ type: 'add', text: at(b, j) })\n j += 1\n }\n }\n while (i < n) {\n ops.push({ type: 'del', text: at(a, i) })\n i += 1\n }\n while (j < m) {\n ops.push({ type: 'add', text: at(b, j) })\n j += 1\n }\n return ops\n}\n\nfunction tokenize(text: string): string[] {\n return text.match(/\\S+|\\s+/g) ?? []\n}\n\nfunction isMeaningful(token: string): boolean {\n return /\\S/.test(token)\n}\n\nfunction mergeSegments(segments: DiffSegment[]): DiffSegment[] {\n const merged: DiffSegment[] = []\n for (const segment of segments) {\n const last = merged[merged.length - 1]\n if (last !== undefined && last.changed === segment.changed) {\n last.text += segment.text\n } else {\n merged.push({ ...segment })\n }\n }\n return merged\n}\n\nexport function wordSegments(\n original: string,\n suggested: string,\n): { left: DiffSegment[]; right: DiffSegment[] } {\n const a = tokenize(original)\n const b = tokenize(suggested)\n const ops = diffOps(a, b, MAX_WORD_CELLS)\n const left: DiffSegment[] = []\n const right: DiffSegment[] = []\n for (const op of ops) {\n if (op.type === 'equal') {\n left.push({ text: op.text, changed: false })\n right.push({ text: op.text, changed: false })\n } else if (op.type === 'del') {\n left.push({ text: op.text, changed: isMeaningful(op.text) })\n } else {\n right.push({ text: op.text, changed: isMeaningful(op.text) })\n }\n }\n return { left: mergeSegments(left), right: mergeSegments(right) }\n}\n\nfunction pairRows(ops: Op[]): DiffRow[] {\n const rows: DiffRow[] = []\n let index = 0\n while (index < ops.length) {\n const op = at(ops, index)\n if (op.type === 'equal') {\n rows.push({\n kind: 'pair',\n left: { kind: 'context', text: op.text },\n right: { kind: 'context', text: op.text },\n })\n index += 1\n continue\n }\n const dels: string[] = []\n const adds: string[] = []\n while (index < ops.length && at(ops, index).type !== 'equal') {\n const current = at(ops, index)\n if (current.type === 'del') {\n dels.push(current.text)\n } else {\n adds.push(current.text)\n }\n index += 1\n }\n const pairCount = Math.max(dels.length, adds.length)\n for (let k = 0; k < pairCount; k += 1) {\n const leftText = k < dels.length ? at(dels, k) : null\n const rightText = k < adds.length ? at(adds, k) : null\n const leftCell: DiffCell | null =\n leftText !== null ? { kind: 'del', text: leftText } : null\n const rightCell: DiffCell | null =\n rightText !== null ? { kind: 'add', text: rightText } : null\n if (\n leftCell !== null &&\n rightCell !== null &&\n leftText !== null &&\n rightText !== null &&\n leftText !== rightText\n ) {\n const words = wordSegments(leftText, rightText)\n leftCell.segments = words.left\n rightCell.segments = words.right\n }\n rows.push({ kind: 'pair', left: leftCell, right: rightCell })\n }\n }\n return rows\n}\n\nfunction numberRows(rows: DiffRow[]): void {\n let leftNo = 0\n let rightNo = 0\n for (const row of rows) {\n if (row.kind !== 'pair') {\n continue\n }\n if (row.left !== null) {\n leftNo += 1\n row.left.no = leftNo\n }\n if (row.right !== null) {\n rightNo += 1\n row.right.no = rightNo\n }\n }\n}\n\nfunction foldRows(rows: DiffRow[], contextLines: number): DiffRow[] {\n const hasChange = rows.some((row) => {\n if (row.kind !== 'pair') {\n return false\n }\n return row.left?.kind !== 'context' || row.right?.kind !== 'context'\n })\n if (!hasChange || contextLines < 0) {\n return rows\n }\n const runs: { start: number; end: number }[] = []\n let start = -1\n for (let index = 0; index <= rows.length; index += 1) {\n const row = rows[index]\n const isContext =\n index < rows.length &&\n row !== undefined &&\n row.kind === 'pair' &&\n row.left?.kind === 'context' &&\n row.right?.kind === 'context'\n if (isContext) {\n if (start === -1) {\n start = index\n }\n } else if (start !== -1) {\n runs.push({ start, end: index })\n start = -1\n }\n }\n const hiddenAt = new Map<number, DiffRow[]>()\n for (const run of runs) {\n const length = run.end - run.start\n const isFirstRun = run.start === 0\n const isLastRun = run.end === rows.length\n const keepBefore = isFirstRun ? 0 : contextLines\n const keepAfter = isLastRun ? 0 : contextLines\n const foldCount = length - keepBefore - keepAfter\n if (foldCount <= 0) {\n continue\n }\n const hidden = rows.slice(run.start + keepBefore, run.start + keepBefore + foldCount)\n hiddenAt.set(run.start + keepBefore, hidden)\n }\n const folded: DiffRow[] = []\n let index = 0\n while (index < rows.length) {\n const hidden = hiddenAt.get(index)\n if (hidden === undefined) {\n folded.push(at(rows, index))\n index += 1\n } else {\n folded.push({ kind: 'fold', count: hidden.length, hidden })\n index += hidden.length\n }\n }\n return folded\n}\n\nexport function computeLineDiff(\n original: string,\n suggested: string,\n options: LineDiffOptions = {},\n): LineDiffResult {\n const contextLines = options.contextLines ?? 2\n const ops = diffOps(splitLines(original), splitLines(suggested))\n const flatRows = pairRows(ops)\n numberRows(flatRows)\n const rows = foldRows(flatRows, contextLines)\n let added = 0\n let removed = 0\n for (const row of flatRows) {\n if (row.kind !== 'pair') {\n continue\n }\n if (row.right?.kind === 'add') {\n added += 1\n }\n if (row.left?.kind === 'del') {\n removed += 1\n }\n }\n return { rows, added, removed }\n}\n","import * as React from 'react'\nimport { useEffect, useMemo, useState } from 'react'\nimport { useVirtualizer } from '@tanstack/react-virtual'\nimport {\n ChevronDown,\n ChevronRight,\n ChevronUp,\n Minus,\n Plus,\n} from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\n\nimport { computeLineDiff, type DiffCell, type DiffRow } from './lineDiff'\n\nexport interface TextDiffViewLabels {\n original?: string\n suggested?: string\n /** Fold summary for a run of hidden unchanged lines. */\n unchangedLines?: (count: number) => string\n showLess?: string\n prevChange?: string\n nextChange?: string\n changePosition?: (index: number, total: number) => string\n}\n\nconst DEFAULT_LABELS: Required<TextDiffViewLabels> = {\n original: 'Original',\n suggested: 'Suggested',\n unchangedLines: (count) =>\n count === 1 ? '1 unchanged line' : `${count} unchanged lines`,\n showLess: 'Show less',\n prevChange: 'Previous change',\n nextChange: 'Next change',\n changePosition: (index, total) => `${index}/${total}`,\n}\n\nexport interface TextDiffViewProps {\n original: string\n suggested: string\n /** Unchanged context lines kept visible around each changed block. Default 2. */\n contextLines?: number\n showHeader?: boolean\n showNav?: boolean\n labels?: Partial<TextDiffViewLabels>\n /** Applied to the outer box. */\n className?: string\n /** Applied to the scrolling body — size the view here (e.g. `max-h-64`). */\n bodyClassName?: string\n}\n\nfunction isChangedRow(row: DiffRow): boolean {\n return (\n row.kind === 'pair' &&\n (row.left?.kind === 'del' || row.right?.kind === 'add')\n )\n}\n\ninterface FlatRow {\n key: string\n row: DiffRow\n foldOf?: number\n collapseOf?: number\n}\n\nfunction CellContent({ cell }: { cell: DiffCell }) {\n if (cell.segments === undefined) {\n return <span className=\"whitespace-pre-wrap break-words\">{cell.text}</span>\n }\n return (\n <span className=\"whitespace-pre-wrap break-words\">\n {cell.segments.map((segment, index) =>\n segment.changed ? (\n <mark\n key={index}\n className={cn(\n 'rounded-[2px] text-inherit',\n cell.kind === 'add' ? 'bg-[var(--as-success)]/30' : 'bg-[var(--as-danger)]/30',\n )}\n >\n {segment.text}\n </mark>\n ) : (\n <span key={index}>{segment.text}</span>\n ),\n )}\n </span>\n )\n}\n\nexport const TextDiffView = React.forwardRef<HTMLDivElement, TextDiffViewProps>(\n function TextDiffView(\n {\n original,\n suggested,\n contextLines = 2,\n showHeader = true,\n showNav = true,\n labels,\n className,\n bodyClassName,\n },\n ref,\n ) {\n const mergedLabels = { ...DEFAULT_LABELS, ...labels }\n const [expanded, setExpanded] = useState<Set<number>>(new Set())\n const [activeGroup, setActiveGroup] = useState(0)\n const scrollRef = React.useRef<HTMLDivElement | null>(null)\n const diff = useMemo(\n () => computeLineDiff(original, suggested, { contextLines }),\n [original, suggested, contextLines],\n )\n\n const flatRows = useMemo<FlatRow[]>(() => {\n const out: FlatRow[] = []\n diff.rows.forEach((row, index) => {\n if (row.kind === 'fold' && expanded.has(index)) {\n row.hidden.forEach((hiddenRow, hiddenIndex) => {\n out.push({ key: `${index}.${hiddenIndex}`, row: hiddenRow })\n })\n out.push({ key: `${index}.collapse`, row, collapseOf: index })\n } else {\n out.push({\n key: String(index),\n row,\n foldOf: row.kind === 'fold' ? index : undefined,\n })\n }\n })\n return out\n }, [diff.rows, expanded])\n\n const changeGroups = useMemo<number[]>(() => {\n const groups: number[] = []\n let inGroup = false\n flatRows.forEach((entry, index) => {\n const changed = isChangedRow(entry.row)\n if (changed && !inGroup) {\n groups.push(index)\n }\n inGroup = changed\n })\n return groups\n }, [flatRows])\n\n const virtualizer = useVirtualizer({\n count: flatRows.length,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => 22,\n overscan: 12,\n getItemKey: (index) => flatRows[index]?.key ?? String(index),\n })\n\n useEffect(() => {\n setExpanded(new Set())\n setActiveGroup(0)\n }, [original, suggested])\n\n useEffect(() => {\n if (activeGroup >= changeGroups.length) {\n setActiveGroup(0)\n }\n }, [activeGroup, changeGroups.length])\n\n useEffect(() => {\n const target = changeGroups[activeGroup]\n if (target !== undefined) {\n virtualizer.scrollToIndex(target, { align: 'center' })\n }\n }, [activeGroup, changeGroups, virtualizer])\n\n if (diff.added === 0 && diff.removed === 0) {\n return (\n <p\n ref={ref}\n className={cn(\n 'text-[var(--as-muted-fg)] border-[var(--as-border)] bg-[var(--as-subtle)] rounded-md border p-3 text-xs',\n className,\n )}\n >\n {'No changes'}\n </p>\n )\n }\n\n const activeGroupStart = changeGroups[activeGroup]\n const activeGroupEnd =\n activeGroupStart === undefined\n ? undefined\n : (() => {\n for (let index = activeGroupStart + 1; index < flatRows.length; index += 1) {\n const entry = flatRows[index]\n if (entry !== undefined && isChangedRow(entry.row)) {\n return index\n }\n }\n return flatRows.length\n })()\n\n const renderRow = (entry: FlatRow, index: number) => {\n const row = entry.row\n if (row.kind === 'fold') {\n if (entry.collapseOf !== undefined) {\n const collapseOf = entry.collapseOf\n return (\n <button\n key={entry.key}\n type=\"button\"\n onClick={() =>\n setExpanded((current) => {\n const next = new Set(current)\n next.delete(collapseOf)\n return next\n })\n }\n className=\"text-[var(--as-muted-fg)] hover:bg-[var(--as-subtle)] hover:text-[var(--as-fg)] flex w-full items-center gap-1 px-3 py-0.5 text-left text-[11px]\"\n >\n <ChevronDown className=\"size-3\" aria-hidden />\n {mergedLabels.showLess}\n </button>\n )\n }\n const foldOf = entry.foldOf\n return (\n <button\n key={entry.key}\n type=\"button\"\n onClick={() => {\n if (foldOf !== undefined) {\n setExpanded((current) => {\n const next = new Set(current)\n next.add(foldOf)\n return next\n })\n }\n }}\n className=\"text-[var(--as-muted-fg)] hover:bg-[var(--as-subtle)] hover:text-[var(--as-fg)] flex w-full items-center gap-1 px-3 py-0.5 text-left text-[11px]\"\n >\n <ChevronRight className=\"size-3\" aria-hidden />\n {mergedLabels.unchangedLines(row.count)}\n </button>\n )\n }\n const changed = isChangedRow(row)\n const active =\n activeGroupStart !== undefined &&\n activeGroupEnd !== undefined &&\n index >= activeGroupStart &&\n index < activeGroupEnd\n return (\n <div\n key={entry.key}\n className={cn('grid grid-cols-2', active && 'bg-[var(--as-primary)]/5')}\n data-changed={changed || undefined}\n data-active={active || undefined}\n >\n {(['left', 'right'] as const).map((side) => {\n const cell = row[side]\n const isAdd = cell?.kind === 'add'\n const isDel = cell?.kind === 'del'\n return (\n <pre\n key={side}\n className={cn(\n 'flex min-w-0 gap-1.5 px-2 py-0.5',\n isAdd && 'bg-[var(--as-success)]/10 text-[var(--as-success)]',\n isDel && 'bg-[var(--as-danger)]/10 text-[var(--as-danger)]',\n )}\n >\n <span className=\"w-7 shrink-0 select-none text-right text-[var(--as-muted-fg)] tabular-nums opacity-70\">\n {cell?.no ?? ''}\n </span>\n <span className=\"w-2 shrink-0 select-none text-center\" aria-hidden>\n {isAdd ? '+' : isDel ? '-' : ''}\n </span>\n <span className=\"min-w-0 flex-1\">\n {cell === null ? null : <CellContent cell={cell} />}\n </span>\n </pre>\n )\n })}\n </div>\n )\n }\n\n return (\n <div\n ref={ref}\n data-as=\"text-diff-view\"\n className={cn(\n 'bg-[var(--as-subtle)] border-[var(--as-border)] flex flex-col overflow-hidden rounded-md border font-mono text-xs leading-relaxed',\n className,\n )}\n >\n {showHeader ? (\n <div className=\"border-[var(--as-border)] text-[var(--as-muted-fg)] grid grid-cols-[1fr_auto_1fr] items-center gap-2 border-b px-3 py-1.5 text-[11px] font-medium tracking-wide uppercase\">\n <span className=\"flex items-center gap-1\">\n <Minus className=\"size-3 text-[var(--as-danger)]\" aria-hidden />\n {mergedLabels.original}\n </span>\n <span className=\"flex items-center gap-2\">\n <span>\n <span className=\"text-[var(--as-success)]\">+{diff.added}</span>{' '}\n <span className=\"text-[var(--as-danger)]\">−{diff.removed}</span>\n </span>\n {showNav && changeGroups.length > 0 ? (\n <span className=\"flex items-center gap-0.5\">\n <button\n type=\"button\"\n aria-label={mergedLabels.prevChange}\n title={mergedLabels.prevChange}\n disabled={activeGroup === 0}\n onClick={() => setActiveGroup((value) => Math.max(0, value - 1))}\n className=\"hover:bg-[var(--as-border)] hover:text-[var(--as-fg)] rounded p-0.5 disabled:opacity-40\"\n >\n <ChevronUp className=\"size-3.5\" aria-hidden />\n </button>\n <span className=\"tabular-nums\">\n {mergedLabels.changePosition(activeGroup + 1, changeGroups.length)}\n </span>\n <button\n type=\"button\"\n aria-label={mergedLabels.nextChange}\n title={mergedLabels.nextChange}\n disabled={activeGroup >= changeGroups.length - 1}\n onClick={() =>\n setActiveGroup((value) => Math.min(changeGroups.length - 1, value + 1))\n }\n className=\"hover:bg-[var(--as-border)] hover:text-[var(--as-fg)] rounded p-0.5 disabled:opacity-40\"\n >\n <ChevronDown className=\"size-3.5\" aria-hidden />\n </button>\n </span>\n ) : null}\n </span>\n <span className=\"flex items-center justify-end gap-1\">\n {mergedLabels.suggested}\n <Plus className=\"size-3 text-[var(--as-success)]\" aria-hidden />\n </span>\n </div>\n ) : null}\n <div ref={scrollRef} className={cn('min-h-0 flex-1 overflow-y-auto', bodyClassName)}>\n <div\n style={{\n height: virtualizer.getTotalSize(),\n position: 'relative',\n width: '100%',\n }}\n >\n {virtualizer.getVirtualItems().map((virtualItem) => (\n <div\n key={virtualItem.key}\n data-index={virtualItem.index}\n ref={virtualizer.measureElement}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n transform: `translateY(${virtualItem.start}px)`,\n }}\n >\n {(() => {\n const entry = flatRows[virtualItem.index]\n return entry === undefined ? null : renderRow(entry, virtualItem.index)\n })()}\n </div>\n ))}\n </div>\n </div>\n </div>\n )\n },\n)\n"]}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
// src/components/ai-text-transform/useAiTextTransform.ts
|
|
4
|
+
function useAiTextTransform(options) {
|
|
5
|
+
const {
|
|
6
|
+
transport,
|
|
7
|
+
pollIntervalMs = 800,
|
|
8
|
+
timeoutMs = 9e4,
|
|
9
|
+
flushMs = 50
|
|
10
|
+
} = options;
|
|
11
|
+
const [status, setStatus] = useState("idle");
|
|
12
|
+
const [jobId, setJobId] = useState(null);
|
|
13
|
+
const [result, setResult] = useState("");
|
|
14
|
+
const [error, setError] = useState(null);
|
|
15
|
+
const transportRef = useRef(transport);
|
|
16
|
+
transportRef.current = transport;
|
|
17
|
+
const unsubscribeRef = useRef(null);
|
|
18
|
+
const bufferRef = useRef("");
|
|
19
|
+
const flushTimerRef = useRef(null);
|
|
20
|
+
const lastFlushRef = useRef(0);
|
|
21
|
+
const terminalRef = useRef(false);
|
|
22
|
+
const cancelFlush = useCallback(() => {
|
|
23
|
+
if (flushTimerRef.current !== null) {
|
|
24
|
+
window.clearTimeout(flushTimerRef.current);
|
|
25
|
+
flushTimerRef.current = null;
|
|
26
|
+
}
|
|
27
|
+
}, []);
|
|
28
|
+
const flush = useCallback(() => {
|
|
29
|
+
cancelFlush();
|
|
30
|
+
lastFlushRef.current = Date.now();
|
|
31
|
+
if (bufferRef.current !== "") {
|
|
32
|
+
const chunk = bufferRef.current;
|
|
33
|
+
bufferRef.current = "";
|
|
34
|
+
setResult((current) => current + chunk);
|
|
35
|
+
}
|
|
36
|
+
}, [cancelFlush]);
|
|
37
|
+
const scheduleFlush = useCallback(() => {
|
|
38
|
+
if (flushTimerRef.current !== null) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const elapsed = Date.now() - lastFlushRef.current;
|
|
42
|
+
const delay = Math.max(0, flushMs - elapsed);
|
|
43
|
+
flushTimerRef.current = window.setTimeout(() => {
|
|
44
|
+
flushTimerRef.current = null;
|
|
45
|
+
flush();
|
|
46
|
+
}, delay);
|
|
47
|
+
}, [flush, flushMs]);
|
|
48
|
+
const detach = useCallback(() => {
|
|
49
|
+
unsubscribeRef.current?.();
|
|
50
|
+
unsubscribeRef.current = null;
|
|
51
|
+
cancelFlush();
|
|
52
|
+
}, [cancelFlush]);
|
|
53
|
+
const start = useCallback(async () => {
|
|
54
|
+
detach();
|
|
55
|
+
bufferRef.current = "";
|
|
56
|
+
lastFlushRef.current = 0;
|
|
57
|
+
terminalRef.current = false;
|
|
58
|
+
setStatus("running");
|
|
59
|
+
setResult("");
|
|
60
|
+
setError(null);
|
|
61
|
+
setJobId(null);
|
|
62
|
+
let jobIdResolved;
|
|
63
|
+
try {
|
|
64
|
+
jobIdResolved = await transportRef.current.start();
|
|
65
|
+
} catch (startError) {
|
|
66
|
+
setError(startError instanceof Error ? startError.message : String(startError));
|
|
67
|
+
setStatus("error");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
setJobId(jobIdResolved);
|
|
71
|
+
unsubscribeRef.current = transportRef.current.subscribe(jobIdResolved, {
|
|
72
|
+
onDelta: (text) => {
|
|
73
|
+
if (terminalRef.current) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
bufferRef.current += text;
|
|
77
|
+
scheduleFlush();
|
|
78
|
+
},
|
|
79
|
+
onDone: (finalResult) => {
|
|
80
|
+
if (terminalRef.current) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
terminalRef.current = true;
|
|
84
|
+
bufferRef.current = "";
|
|
85
|
+
flush();
|
|
86
|
+
setResult(finalResult);
|
|
87
|
+
setStatus("done");
|
|
88
|
+
},
|
|
89
|
+
onError: (message) => {
|
|
90
|
+
if (terminalRef.current) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
terminalRef.current = true;
|
|
94
|
+
bufferRef.current = "";
|
|
95
|
+
flush();
|
|
96
|
+
setError(message);
|
|
97
|
+
setStatus("error");
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}, [detach, flush, scheduleFlush]);
|
|
101
|
+
const stop = useCallback(async () => {
|
|
102
|
+
const currentJobId = jobId;
|
|
103
|
+
terminalRef.current = true;
|
|
104
|
+
if (currentJobId !== null) {
|
|
105
|
+
detach();
|
|
106
|
+
await transportRef.current.cancel?.(currentJobId).catch(() => void 0);
|
|
107
|
+
}
|
|
108
|
+
setStatus("cancelled");
|
|
109
|
+
}, [detach, jobId]);
|
|
110
|
+
const reset = useCallback(() => {
|
|
111
|
+
terminalRef.current = false;
|
|
112
|
+
detach();
|
|
113
|
+
setJobId(null);
|
|
114
|
+
setResult("");
|
|
115
|
+
setError(null);
|
|
116
|
+
setStatus("idle");
|
|
117
|
+
}, [detach]);
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
if (status !== "running" || jobId === null || transportRef.current.poll === void 0) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const poll = () => {
|
|
123
|
+
transportRef.current.poll(jobId).then((job) => {
|
|
124
|
+
if (job.status === "done") {
|
|
125
|
+
terminalRef.current = true;
|
|
126
|
+
bufferRef.current = "";
|
|
127
|
+
cancelFlush();
|
|
128
|
+
setResult(job.result ?? "");
|
|
129
|
+
setStatus("done");
|
|
130
|
+
} else if (job.status === "error") {
|
|
131
|
+
terminalRef.current = true;
|
|
132
|
+
setError(job.error ?? "AI transform failed");
|
|
133
|
+
setStatus("error");
|
|
134
|
+
} else if (job.status === "cancelled") {
|
|
135
|
+
terminalRef.current = true;
|
|
136
|
+
setStatus("cancelled");
|
|
137
|
+
}
|
|
138
|
+
}).catch(() => void 0);
|
|
139
|
+
};
|
|
140
|
+
const timer = window.setInterval(poll, pollIntervalMs);
|
|
141
|
+
const timeout = window.setTimeout(() => {
|
|
142
|
+
setError("AI transform timed out");
|
|
143
|
+
setStatus("error");
|
|
144
|
+
}, timeoutMs);
|
|
145
|
+
return () => {
|
|
146
|
+
window.clearInterval(timer);
|
|
147
|
+
window.clearTimeout(timeout);
|
|
148
|
+
};
|
|
149
|
+
}, [status, jobId, pollIntervalMs, timeoutMs, cancelFlush]);
|
|
150
|
+
useEffect(() => detach, [detach]);
|
|
151
|
+
return { status, result, error, start, stop, reset };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export { useAiTextTransform };
|
|
155
|
+
//# sourceMappingURL=chunk-BKAO7BTU.js.map
|
|
156
|
+
//# sourceMappingURL=chunk-BKAO7BTU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ai-text-transform/useAiTextTransform.ts"],"names":[],"mappings":"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","file":"chunk-BKAO7BTU.js","sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react'\n\nexport type AiTextTransformStatus = 'idle' | 'running' | 'done' | 'error' | 'cancelled'\n\nexport interface AiTextTransformEventHandlers {\n onDelta: (text: string) => void\n onDone: (result: string) => void\n onError: (message: string) => void\n}\n\nexport interface AiTextTransformTransport {\n /** Begin the job and resolve with its handle (e.g. a job id). */\n start: () => Promise<string>\n /** Subscribe to a job's events; returns an unsubscribe function. */\n subscribe: (jobId: string, handlers: AiTextTransformEventHandlers) => () => void\n cancel?: (jobId: string) => Promise<void>\n /** Optional status fallback when events may be missed. */\n poll?: (\n jobId: string,\n ) => Promise<{\n status: 'running' | 'done' | 'error' | 'cancelled'\n result?: string\n error?: string\n }>\n}\n\nexport interface UseAiTextTransformOptions {\n transport: AiTextTransformTransport\n /** Status-fallback interval when `poll` is provided. Default 800 ms. */\n pollIntervalMs?: number\n /** Hard timeout for a running job. Default 90 000 ms. */\n timeoutMs?: number\n /** Streaming delta coalescing window. Default 50 ms. */\n flushMs?: number\n}\n\n/**\n * Streaming text-transform state machine (idle → running → done/error/\n * cancelled). Transport-injected: the app provides start/subscribe/cancel/\n * poll closures over its own gateway; this hook owns status transitions,\n * delta coalescing, the poll fallback and the hard timeout.\n */\nexport function useAiTextTransform(options: UseAiTextTransformOptions) {\n const {\n transport,\n pollIntervalMs = 800,\n timeoutMs = 90_000,\n flushMs = 50,\n } = options\n const [status, setStatus] = useState<AiTextTransformStatus>('idle')\n const [jobId, setJobId] = useState<string | null>(null)\n const [result, setResult] = useState('')\n const [error, setError] = useState<string | null>(null)\n\n const transportRef = useRef(transport)\n transportRef.current = transport\n\n const unsubscribeRef = useRef<(() => void) | null>(null)\n const bufferRef = useRef('')\n const flushTimerRef = useRef<number | null>(null)\n const lastFlushRef = useRef(0)\n const terminalRef = useRef(false)\n\n const cancelFlush = useCallback(() => {\n if (flushTimerRef.current !== null) {\n window.clearTimeout(flushTimerRef.current)\n flushTimerRef.current = null\n }\n }, [])\n\n const flush = useCallback(() => {\n cancelFlush()\n lastFlushRef.current = Date.now()\n if (bufferRef.current !== '') {\n const chunk = bufferRef.current\n bufferRef.current = ''\n setResult((current) => current + chunk)\n }\n }, [cancelFlush])\n\n const scheduleFlush = useCallback(() => {\n if (flushTimerRef.current !== null) {\n return\n }\n const elapsed = Date.now() - lastFlushRef.current\n const delay = Math.max(0, flushMs - elapsed)\n flushTimerRef.current = window.setTimeout(() => {\n flushTimerRef.current = null\n flush()\n }, delay)\n }, [flush, flushMs])\n\n const detach = useCallback(() => {\n unsubscribeRef.current?.()\n unsubscribeRef.current = null\n cancelFlush()\n }, [cancelFlush])\n\n const start = useCallback(async () => {\n detach()\n bufferRef.current = ''\n lastFlushRef.current = 0\n terminalRef.current = false\n setStatus('running')\n setResult('')\n setError(null)\n setJobId(null)\n let jobIdResolved: string\n try {\n jobIdResolved = await transportRef.current.start()\n } catch (startError) {\n setError(startError instanceof Error ? startError.message : String(startError))\n setStatus('error')\n return\n }\n setJobId(jobIdResolved)\n unsubscribeRef.current = transportRef.current.subscribe(jobIdResolved, {\n onDelta: (text) => {\n if (terminalRef.current) {\n return\n }\n bufferRef.current += text\n scheduleFlush()\n },\n onDone: (finalResult) => {\n if (terminalRef.current) {\n return\n }\n terminalRef.current = true\n bufferRef.current = ''\n flush()\n setResult(finalResult)\n setStatus('done')\n },\n onError: (message) => {\n if (terminalRef.current) {\n return\n }\n terminalRef.current = true\n bufferRef.current = ''\n flush()\n setError(message)\n setStatus('error')\n },\n })\n }, [detach, flush, scheduleFlush])\n\n const stop = useCallback(async () => {\n const currentJobId = jobId\n terminalRef.current = true\n if (currentJobId !== null) {\n detach()\n await transportRef.current.cancel?.(currentJobId).catch(() => undefined)\n }\n setStatus('cancelled')\n }, [detach, jobId])\n\n const reset = useCallback(() => {\n terminalRef.current = false\n detach()\n setJobId(null)\n setResult('')\n setError(null)\n setStatus('idle')\n }, [detach])\n\n useEffect(() => {\n if (status !== 'running' || jobId === null || transportRef.current.poll === undefined) {\n return\n }\n const poll = () => {\n transportRef.current\n .poll!(jobId)\n .then((job) => {\n if (job.status === 'done') {\n terminalRef.current = true\n bufferRef.current = ''\n cancelFlush()\n setResult(job.result ?? '')\n setStatus('done')\n } else if (job.status === 'error') {\n terminalRef.current = true\n setError(job.error ?? 'AI transform failed')\n setStatus('error')\n } else if (job.status === 'cancelled') {\n terminalRef.current = true\n setStatus('cancelled')\n }\n })\n .catch(() => undefined)\n }\n const timer = window.setInterval(poll, pollIntervalMs)\n const timeout = window.setTimeout(() => {\n setError('AI transform timed out')\n setStatus('error')\n }, timeoutMs)\n return () => {\n window.clearInterval(timer)\n window.clearTimeout(timeout)\n }\n }, [status, jobId, pollIntervalMs, timeoutMs, cancelFlush])\n\n useEffect(() => detach, [detach])\n\n return { status, result, error, start, stop, reset }\n}\n"]}
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { cn } from './chunk-BLJWR4ZV.js';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
4
|
+
import { Mic, X, Square, Loader2 } from 'lucide-react';
|
|
5
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var BAR_WEIGHTS = [0.45, 0.62, 0.8, 0.92, 1.05];
|
|
8
|
+
function DictationButton({
|
|
9
|
+
status,
|
|
10
|
+
onStart,
|
|
11
|
+
label = "Dictate",
|
|
12
|
+
recordingLabel,
|
|
13
|
+
className
|
|
14
|
+
}) {
|
|
15
|
+
return /* @__PURE__ */ jsxs(
|
|
16
|
+
"button",
|
|
17
|
+
{
|
|
18
|
+
type: "button",
|
|
19
|
+
title: label,
|
|
20
|
+
"aria-label": label,
|
|
21
|
+
"aria-pressed": status === "recording" ? true : status === "transcribing" ? void 0 : false,
|
|
22
|
+
disabled: status === "transcribing",
|
|
23
|
+
"data-as": "dictation-button",
|
|
24
|
+
className: cn(
|
|
25
|
+
"rounded p-1.5 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--as-focus-ring)]",
|
|
26
|
+
status === "recording" ? "animate-pulse bg-[var(--as-danger)]/10 text-[var(--as-danger)]" : "text-[var(--as-muted-fg)] hover:text-[var(--as-fg)]",
|
|
27
|
+
className
|
|
28
|
+
),
|
|
29
|
+
onClick: onStart,
|
|
30
|
+
children: [
|
|
31
|
+
/* @__PURE__ */ jsx(Mic, { className: "size-4", "aria-hidden": true }),
|
|
32
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: recordingLabel ?? label })
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
function LevelBars({
|
|
38
|
+
levelRef,
|
|
39
|
+
active
|
|
40
|
+
}) {
|
|
41
|
+
const barsRef = React.useRef([]);
|
|
42
|
+
React.useEffect(() => {
|
|
43
|
+
if (!active) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
let frame = requestAnimationFrame(function tick() {
|
|
47
|
+
const level = Math.min(1, levelRef.current ?? 0);
|
|
48
|
+
barsRef.current.forEach((bar, index) => {
|
|
49
|
+
if (bar !== null) {
|
|
50
|
+
const weight = BAR_WEIGHTS[index] ?? 1;
|
|
51
|
+
const height = Math.max(0.15, Math.min(1, level * weight));
|
|
52
|
+
bar.style.transform = `scaleY(${height})`;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
frame = requestAnimationFrame(tick);
|
|
56
|
+
});
|
|
57
|
+
return () => cancelAnimationFrame(frame);
|
|
58
|
+
}, [active, levelRef]);
|
|
59
|
+
return /* @__PURE__ */ jsx("span", { className: "flex h-4 items-end gap-0.5", "aria-hidden": true, children: BAR_WEIGHTS.map((_, index) => /* @__PURE__ */ jsx(
|
|
60
|
+
"span",
|
|
61
|
+
{
|
|
62
|
+
ref: (element) => {
|
|
63
|
+
barsRef.current[index] = element;
|
|
64
|
+
},
|
|
65
|
+
className: "h-4 w-1 origin-bottom rounded-full bg-[var(--as-danger)] opacity-40"
|
|
66
|
+
},
|
|
67
|
+
index
|
|
68
|
+
)) });
|
|
69
|
+
}
|
|
70
|
+
var DEFAULT_STRIP_LABELS = {
|
|
71
|
+
recording: "Recording\u2026",
|
|
72
|
+
transcribing: "Transcribing\u2026",
|
|
73
|
+
stop: "Stop",
|
|
74
|
+
cancel: "Cancel",
|
|
75
|
+
dismissError: "Dismiss",
|
|
76
|
+
unsupported: "Speech input is not supported in this browser.",
|
|
77
|
+
denied: "Microphone access was denied.",
|
|
78
|
+
unassigned: "No speech-to-text model is assigned.",
|
|
79
|
+
failed: "Transcription failed. {detail}"
|
|
80
|
+
};
|
|
81
|
+
function DictationStrip({
|
|
82
|
+
status,
|
|
83
|
+
seconds,
|
|
84
|
+
levelRef,
|
|
85
|
+
error,
|
|
86
|
+
labels,
|
|
87
|
+
onStop,
|
|
88
|
+
onCancel,
|
|
89
|
+
onDismissError,
|
|
90
|
+
className
|
|
91
|
+
}) {
|
|
92
|
+
const merged = { ...DEFAULT_STRIP_LABELS, ...labels };
|
|
93
|
+
const formatTime = (total) => {
|
|
94
|
+
const minutes = Math.floor(total / 60);
|
|
95
|
+
const padded = String(total % 60).padStart(2, "0");
|
|
96
|
+
return `${String(minutes).padStart(2, "0")}:${padded}`;
|
|
97
|
+
};
|
|
98
|
+
const errorText = error === null ? "" : error.kind === "unsupported" ? merged.unsupported : error.kind === "denied" ? merged.denied : error.kind === "unassigned" ? merged.unassigned : merged.failed.replace("{detail}", error.detail ?? "");
|
|
99
|
+
if (error !== null) {
|
|
100
|
+
return /* @__PURE__ */ jsxs(
|
|
101
|
+
"div",
|
|
102
|
+
{
|
|
103
|
+
"data-as": "dictation-strip",
|
|
104
|
+
className: cn(
|
|
105
|
+
"flex items-center gap-2 rounded-md border border-dashed border-[var(--as-warning)] px-2.5 py-1.5 text-xs text-[var(--as-warning)]",
|
|
106
|
+
className
|
|
107
|
+
),
|
|
108
|
+
role: "alert",
|
|
109
|
+
children: [
|
|
110
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 break-words", children: errorText }),
|
|
111
|
+
/* @__PURE__ */ jsx(
|
|
112
|
+
"button",
|
|
113
|
+
{
|
|
114
|
+
type: "button",
|
|
115
|
+
"aria-label": merged.dismissError,
|
|
116
|
+
title: merged.dismissError,
|
|
117
|
+
className: "rounded-full p-0.5 hover:opacity-70",
|
|
118
|
+
onClick: onDismissError,
|
|
119
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-3.5", "aria-hidden": true })
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
if (status === "idle") {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
return /* @__PURE__ */ jsx(
|
|
130
|
+
"div",
|
|
131
|
+
{
|
|
132
|
+
"data-as": "dictation-strip",
|
|
133
|
+
className: cn(
|
|
134
|
+
"flex items-center gap-2 rounded-md border border-[var(--as-border)] bg-[var(--as-surface)] px-2.5 py-1.5 text-xs",
|
|
135
|
+
className
|
|
136
|
+
),
|
|
137
|
+
role: "status",
|
|
138
|
+
"aria-label": merged.recording,
|
|
139
|
+
children: status === "recording" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
140
|
+
/* @__PURE__ */ jsx(
|
|
141
|
+
"span",
|
|
142
|
+
{
|
|
143
|
+
className: "size-2 shrink-0 animate-pulse rounded-full bg-[var(--as-danger)]",
|
|
144
|
+
"aria-hidden": true
|
|
145
|
+
}
|
|
146
|
+
),
|
|
147
|
+
/* @__PURE__ */ jsx("span", { className: "tabular-nums font-medium text-[var(--as-fg)]", children: formatTime(seconds) }),
|
|
148
|
+
/* @__PURE__ */ jsx(LevelBars, { levelRef, active: true }),
|
|
149
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-[var(--as-muted-fg)]", children: merged.recording }),
|
|
150
|
+
/* @__PURE__ */ jsx(
|
|
151
|
+
"button",
|
|
152
|
+
{
|
|
153
|
+
type: "button",
|
|
154
|
+
"aria-label": merged.cancel,
|
|
155
|
+
title: merged.cancel,
|
|
156
|
+
className: "rounded p-1 text-[var(--as-muted-fg)] hover:text-[var(--as-fg)]",
|
|
157
|
+
onClick: onCancel,
|
|
158
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-3.5", "aria-hidden": true })
|
|
159
|
+
}
|
|
160
|
+
),
|
|
161
|
+
/* @__PURE__ */ jsxs(
|
|
162
|
+
"button",
|
|
163
|
+
{
|
|
164
|
+
type: "button",
|
|
165
|
+
title: merged.stop,
|
|
166
|
+
"aria-label": merged.stop,
|
|
167
|
+
className: "flex items-center gap-1 rounded bg-[var(--as-danger)] px-2 py-1 font-medium",
|
|
168
|
+
onClick: onStop,
|
|
169
|
+
children: [
|
|
170
|
+
/* @__PURE__ */ jsx(Square, { className: "size-3", "aria-hidden": true }),
|
|
171
|
+
merged.stop
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
)
|
|
175
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
176
|
+
/* @__PURE__ */ jsx(
|
|
177
|
+
Loader2,
|
|
178
|
+
{
|
|
179
|
+
className: "size-3.5 shrink-0 animate-spin text-[var(--as-muted-fg)]",
|
|
180
|
+
"aria-hidden": true
|
|
181
|
+
}
|
|
182
|
+
),
|
|
183
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 text-[var(--as-muted-fg)]", children: merged.transcribing })
|
|
184
|
+
] })
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
var PREFERRED_MIME_TYPES = [
|
|
189
|
+
"audio/webm;codecs=opus",
|
|
190
|
+
"audio/webm",
|
|
191
|
+
"audio/ogg;codecs=opus",
|
|
192
|
+
"audio/mp4"
|
|
193
|
+
];
|
|
194
|
+
var MAX_LEVEL = 1;
|
|
195
|
+
var LEVEL_BOOST = 4;
|
|
196
|
+
var LEVEL_DECAY = 0.85;
|
|
197
|
+
function pickMimeType() {
|
|
198
|
+
if (typeof MediaRecorder === "undefined") {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
for (const mimeType of PREFERRED_MIME_TYPES) {
|
|
202
|
+
if (MediaRecorder.isTypeSupported(mimeType)) {
|
|
203
|
+
return mimeType;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
function useDictation(options) {
|
|
209
|
+
const [status, setStatus] = useState("idle");
|
|
210
|
+
const [seconds, setSeconds] = useState(0);
|
|
211
|
+
const [error, setError] = useState(null);
|
|
212
|
+
const optionsRef = useRef(options);
|
|
213
|
+
optionsRef.current = options;
|
|
214
|
+
const recorderRef = useRef(null);
|
|
215
|
+
const streamRef = useRef(null);
|
|
216
|
+
const chunksRef = useRef([]);
|
|
217
|
+
const audioContextRef = useRef(null);
|
|
218
|
+
const rafRef = useRef(null);
|
|
219
|
+
const timerRef = useRef(null);
|
|
220
|
+
const levelRef = useRef(0);
|
|
221
|
+
const teardown = useCallback(() => {
|
|
222
|
+
if (rafRef.current !== null) {
|
|
223
|
+
cancelAnimationFrame(rafRef.current);
|
|
224
|
+
rafRef.current = null;
|
|
225
|
+
}
|
|
226
|
+
if (timerRef.current !== null) {
|
|
227
|
+
window.clearInterval(timerRef.current);
|
|
228
|
+
timerRef.current = null;
|
|
229
|
+
}
|
|
230
|
+
audioContextRef.current?.close().catch(() => void 0);
|
|
231
|
+
audioContextRef.current = null;
|
|
232
|
+
streamRef.current?.getTracks().forEach((track) => track.stop());
|
|
233
|
+
streamRef.current = null;
|
|
234
|
+
recorderRef.current = null;
|
|
235
|
+
levelRef.current = 0;
|
|
236
|
+
}, []);
|
|
237
|
+
const startMeter = useCallback((stream) => {
|
|
238
|
+
if (typeof AudioContext === "undefined") {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
const context = new AudioContext();
|
|
242
|
+
audioContextRef.current = context;
|
|
243
|
+
const analyser = context.createAnalyser();
|
|
244
|
+
analyser.fftSize = 512;
|
|
245
|
+
context.createMediaStreamSource(stream).connect(analyser);
|
|
246
|
+
const buffer = new Uint8Array(analyser.fftSize);
|
|
247
|
+
const tick = () => {
|
|
248
|
+
analyser.getByteTimeDomainData(buffer);
|
|
249
|
+
let sum = 0;
|
|
250
|
+
for (const value of buffer) {
|
|
251
|
+
const centered = (value - 128) / 128;
|
|
252
|
+
sum += centered * centered;
|
|
253
|
+
}
|
|
254
|
+
const rms = Math.sqrt(sum / buffer.length);
|
|
255
|
+
levelRef.current = Math.min(
|
|
256
|
+
MAX_LEVEL,
|
|
257
|
+
Math.max(rms * LEVEL_BOOST, levelRef.current * LEVEL_DECAY)
|
|
258
|
+
);
|
|
259
|
+
rafRef.current = requestAnimationFrame(tick);
|
|
260
|
+
};
|
|
261
|
+
rafRef.current = requestAnimationFrame(tick);
|
|
262
|
+
}, []);
|
|
263
|
+
const start = useCallback(async () => {
|
|
264
|
+
if (status !== "idle") {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
setError(null);
|
|
268
|
+
if (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia || typeof MediaRecorder === "undefined") {
|
|
269
|
+
setError({ kind: "unsupported" });
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
let stream;
|
|
273
|
+
try {
|
|
274
|
+
stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
275
|
+
} catch (requestError) {
|
|
276
|
+
const name = requestError instanceof DOMException ? requestError.name : "";
|
|
277
|
+
setError(
|
|
278
|
+
name === "NotAllowedError" || name === "SecurityError" || name === "NotFoundError" ? { kind: "denied" } : {
|
|
279
|
+
kind: "failed",
|
|
280
|
+
detail: requestError instanceof Error ? requestError.message : String(requestError)
|
|
281
|
+
}
|
|
282
|
+
);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const mimeType = pickMimeType();
|
|
286
|
+
const recorder = new MediaRecorder(stream, mimeType ? { mimeType } : void 0);
|
|
287
|
+
const chunks = [];
|
|
288
|
+
recorder.ondataavailable = (event) => {
|
|
289
|
+
if (event.data.size > 0) {
|
|
290
|
+
chunks.push(event.data);
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
recorderRef.current = recorder;
|
|
294
|
+
streamRef.current = stream;
|
|
295
|
+
chunksRef.current = chunks;
|
|
296
|
+
recorder.start(250);
|
|
297
|
+
setSeconds(0);
|
|
298
|
+
setStatus("recording");
|
|
299
|
+
startMeter(stream);
|
|
300
|
+
timerRef.current = window.setInterval(() => {
|
|
301
|
+
setSeconds((current) => current + 1);
|
|
302
|
+
}, 1e3);
|
|
303
|
+
}, [startMeter, status]);
|
|
304
|
+
const cancel = useCallback(() => {
|
|
305
|
+
const recorder = recorderRef.current;
|
|
306
|
+
if (recorder !== null && recorder.state !== "inactive") {
|
|
307
|
+
recorder.onstop = null;
|
|
308
|
+
recorder.stop();
|
|
309
|
+
}
|
|
310
|
+
teardown();
|
|
311
|
+
setSeconds(0);
|
|
312
|
+
setStatus("idle");
|
|
313
|
+
}, [teardown]);
|
|
314
|
+
const stop = useCallback(async () => {
|
|
315
|
+
const recorder = recorderRef.current;
|
|
316
|
+
if (recorder === null || recorder.state === "inactive") {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const mimeType = recorder.mimeType || "audio/webm";
|
|
320
|
+
const chunks = chunksRef.current;
|
|
321
|
+
const stopped = new Promise((resolve) => {
|
|
322
|
+
recorder.onstop = () => resolve();
|
|
323
|
+
});
|
|
324
|
+
recorder.stop();
|
|
325
|
+
await stopped;
|
|
326
|
+
teardown();
|
|
327
|
+
setSeconds(0);
|
|
328
|
+
setStatus("transcribing");
|
|
329
|
+
try {
|
|
330
|
+
const blob = new Blob(chunks, { type: mimeType });
|
|
331
|
+
const result = await optionsRef.current.transcribe(blob);
|
|
332
|
+
setStatus("idle");
|
|
333
|
+
optionsRef.current.onResult?.(result.text);
|
|
334
|
+
} catch (transcribeError) {
|
|
335
|
+
const classified = optionsRef.current.classifyError?.(transcribeError) ?? {
|
|
336
|
+
kind: "failed",
|
|
337
|
+
detail: transcribeError instanceof Error ? transcribeError.message : String(transcribeError)
|
|
338
|
+
};
|
|
339
|
+
setError(classified);
|
|
340
|
+
setStatus("idle");
|
|
341
|
+
}
|
|
342
|
+
}, [teardown]);
|
|
343
|
+
const dismissError = useCallback(() => {
|
|
344
|
+
setError(null);
|
|
345
|
+
}, []);
|
|
346
|
+
useEffect(
|
|
347
|
+
() => () => {
|
|
348
|
+
const recorder = recorderRef.current;
|
|
349
|
+
if (recorder !== null && recorder.state !== "inactive") {
|
|
350
|
+
recorder.onstop = null;
|
|
351
|
+
recorder.stop();
|
|
352
|
+
}
|
|
353
|
+
teardown();
|
|
354
|
+
},
|
|
355
|
+
[teardown]
|
|
356
|
+
);
|
|
357
|
+
return { status, seconds, error, levelRef, start, stop, cancel, dismissError };
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export { DictationButton, DictationStrip, useDictation };
|
|
361
|
+
//# sourceMappingURL=chunk-KAC2DOLH.js.map
|
|
362
|
+
//# sourceMappingURL=chunk-KAC2DOLH.js.map
|