@cntyclub/agent-react 0.11.0 → 0.12.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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-voice-recorder.d.ts","sourceRoot":"","sources":["../../src/components/agent-voice-recorder.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,WAAW,uBAAuB;IACtC,iFAAiF;IACjF,UAAU,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,UAAU,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,4EAA4E;IAC5E,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,sEAAsE;IACtE,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;
|
|
1
|
+
{"version":3,"file":"agent-voice-recorder.d.ts","sourceRoot":"","sources":["../../src/components/agent-voice-recorder.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,WAAW,uBAAuB;IACtC,iFAAiF;IACjF,UAAU,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,UAAU,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,4EAA4E;IAC5E,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,sEAAsE;IACtE,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAqBD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,uBAAuB,qBAwRhH"}
|
package/dist/index.js
CHANGED
|
@@ -312,6 +312,7 @@ function AgentImageLightbox({ src, alt, onClose }) {
|
|
|
312
312
|
document.body
|
|
313
313
|
);
|
|
314
314
|
}
|
|
315
|
+
var BAR_COUNT = 40;
|
|
315
316
|
function formatClock(totalSeconds) {
|
|
316
317
|
const minutes = Math.floor(totalSeconds / 60);
|
|
317
318
|
const seconds = totalSeconds % 60;
|
|
@@ -335,8 +336,24 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
|
|
|
335
336
|
const audioUrlRef = React11.useRef(null);
|
|
336
337
|
const audioRef = React11.useRef(null);
|
|
337
338
|
const timerRef = React11.useRef(null);
|
|
339
|
+
const canvasRef = React11.useRef(null);
|
|
340
|
+
const audioContextRef = React11.useRef(null);
|
|
341
|
+
const analyserRef = React11.useRef(null);
|
|
342
|
+
const rafRef = React11.useRef(null);
|
|
343
|
+
const levelsRef = React11.useRef(new Array(BAR_COUNT).fill(0));
|
|
338
344
|
const onErrorRef = React11.useRef(onError);
|
|
339
345
|
onErrorRef.current = onError;
|
|
346
|
+
const stopMeter = React11.useCallback(() => {
|
|
347
|
+
if (rafRef.current != null) {
|
|
348
|
+
cancelAnimationFrame(rafRef.current);
|
|
349
|
+
rafRef.current = null;
|
|
350
|
+
}
|
|
351
|
+
analyserRef.current = null;
|
|
352
|
+
const ctx = audioContextRef.current;
|
|
353
|
+
audioContextRef.current = null;
|
|
354
|
+
if (ctx && ctx.state !== "closed") void ctx.close().catch(() => {
|
|
355
|
+
});
|
|
356
|
+
}, []);
|
|
340
357
|
const stopTracks = React11.useCallback(() => {
|
|
341
358
|
streamRef.current?.getTracks().forEach((track) => track.stop());
|
|
342
359
|
streamRef.current = null;
|
|
@@ -344,6 +361,73 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
|
|
|
344
361
|
clearInterval(timerRef.current);
|
|
345
362
|
timerRef.current = null;
|
|
346
363
|
}
|
|
364
|
+
stopMeter();
|
|
365
|
+
}, [stopMeter]);
|
|
366
|
+
const startMeter = React11.useCallback((stream) => {
|
|
367
|
+
const Ctor = typeof window === "undefined" ? void 0 : window.AudioContext ?? window.webkitAudioContext;
|
|
368
|
+
if (!Ctor) return;
|
|
369
|
+
let context;
|
|
370
|
+
try {
|
|
371
|
+
context = new Ctor();
|
|
372
|
+
} catch {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
audioContextRef.current = context;
|
|
376
|
+
const source = context.createMediaStreamSource(stream);
|
|
377
|
+
const analyser = context.createAnalyser();
|
|
378
|
+
analyser.fftSize = 1024;
|
|
379
|
+
analyser.smoothingTimeConstant = 0.75;
|
|
380
|
+
source.connect(analyser);
|
|
381
|
+
analyserRef.current = analyser;
|
|
382
|
+
levelsRef.current = new Array(BAR_COUNT).fill(0);
|
|
383
|
+
const buffer = new Uint8Array(analyser.fftSize);
|
|
384
|
+
const draw = () => {
|
|
385
|
+
const canvas = canvasRef.current;
|
|
386
|
+
const activeAnalyser = analyserRef.current;
|
|
387
|
+
if (!canvas || !activeAnalyser) return;
|
|
388
|
+
const ctx2d = canvas.getContext("2d");
|
|
389
|
+
if (!ctx2d) return;
|
|
390
|
+
activeAnalyser.getByteTimeDomainData(buffer);
|
|
391
|
+
let sumSquares = 0;
|
|
392
|
+
for (let i = 0; i < buffer.length; i += 1) {
|
|
393
|
+
const centered = (buffer[i] - 128) / 128;
|
|
394
|
+
sumSquares += centered * centered;
|
|
395
|
+
}
|
|
396
|
+
const rms = Math.sqrt(sumSquares / buffer.length);
|
|
397
|
+
const level = Math.min(1, rms * 3.2);
|
|
398
|
+
const levels = levelsRef.current;
|
|
399
|
+
levels.push(level);
|
|
400
|
+
levels.shift();
|
|
401
|
+
const dpr = Math.min(2, typeof window === "undefined" ? 1 : window.devicePixelRatio || 1);
|
|
402
|
+
const cssWidth = canvas.clientWidth || 1;
|
|
403
|
+
const cssHeight = canvas.clientHeight || 1;
|
|
404
|
+
const pxWidth = Math.round(cssWidth * dpr);
|
|
405
|
+
const pxHeight = Math.round(cssHeight * dpr);
|
|
406
|
+
if (canvas.width !== pxWidth) canvas.width = pxWidth;
|
|
407
|
+
if (canvas.height !== pxHeight) canvas.height = pxHeight;
|
|
408
|
+
ctx2d.clearRect(0, 0, pxWidth, pxHeight);
|
|
409
|
+
ctx2d.fillStyle = getComputedStyle(canvas).color || "currentColor";
|
|
410
|
+
const gap = Math.max(1, pxWidth / BAR_COUNT / 3);
|
|
411
|
+
const barWidth = Math.max(1, (pxWidth - gap * (BAR_COUNT - 1)) / BAR_COUNT);
|
|
412
|
+
const midY = pxHeight / 2;
|
|
413
|
+
const minBar = Math.max(1, dpr);
|
|
414
|
+
const radius = barWidth / 2;
|
|
415
|
+
for (let i = 0; i < BAR_COUNT; i += 1) {
|
|
416
|
+
const value = levels[i];
|
|
417
|
+
const barHeight = Math.max(minBar, value * pxHeight);
|
|
418
|
+
const x = i * (barWidth + gap);
|
|
419
|
+
const y = midY - barHeight / 2;
|
|
420
|
+
if (typeof ctx2d.roundRect === "function") {
|
|
421
|
+
ctx2d.beginPath();
|
|
422
|
+
ctx2d.roundRect(x, y, barWidth, barHeight, Math.min(radius, barHeight / 2));
|
|
423
|
+
ctx2d.fill();
|
|
424
|
+
} else {
|
|
425
|
+
ctx2d.fillRect(x, y, barWidth, barHeight);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
rafRef.current = requestAnimationFrame(draw);
|
|
429
|
+
};
|
|
430
|
+
rafRef.current = requestAnimationFrame(draw);
|
|
347
431
|
}, []);
|
|
348
432
|
React11.useEffect(() => {
|
|
349
433
|
let cancelled = false;
|
|
@@ -360,6 +444,7 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
|
|
|
360
444
|
return;
|
|
361
445
|
}
|
|
362
446
|
streamRef.current = stream;
|
|
447
|
+
startMeter(stream);
|
|
363
448
|
const mimeType = pickMimeType();
|
|
364
449
|
const recorder = new MediaRecorder(stream, mimeType ? { mimeType } : void 0);
|
|
365
450
|
recorderRef.current = recorder;
|
|
@@ -444,7 +529,7 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
|
|
|
444
529
|
transition: { duration: 0.15 },
|
|
445
530
|
children: [
|
|
446
531
|
phase === "recording" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
447
|
-
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm", children: [
|
|
532
|
+
/* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-2 text-sm", children: [
|
|
448
533
|
/* @__PURE__ */ jsx(
|
|
449
534
|
motion.span,
|
|
450
535
|
{
|
|
@@ -453,13 +538,9 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
|
|
|
453
538
|
transition: { duration: 1.2, repeat: Infinity }
|
|
454
539
|
}
|
|
455
540
|
),
|
|
456
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium tabular-nums", children: formatClock(elapsed) })
|
|
457
|
-
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
458
|
-
"/ ",
|
|
459
|
-
formatClock(maxSeconds)
|
|
460
|
-
] })
|
|
541
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium tabular-nums", children: formatClock(elapsed) })
|
|
461
542
|
] }),
|
|
462
|
-
/* @__PURE__ */ jsx("
|
|
543
|
+
/* @__PURE__ */ jsx("canvas", { "aria-hidden": true, className: "h-6 min-w-0 flex-1 text-foreground/80", ref: canvasRef }),
|
|
463
544
|
/* @__PURE__ */ jsx(Button, { "aria-label": "Discard recording", onClick: onCancel, size: "icon-sm", type: "button", variant: "ghost", children: /* @__PURE__ */ jsx(Trash2Icon, {}) }),
|
|
464
545
|
/* @__PURE__ */ jsx(Button, { "aria-label": "Stop recording", onClick: stopRecording, size: "icon-sm", type: "button", children: /* @__PURE__ */ jsx(SquareIcon, {}) })
|
|
465
546
|
] }) : null,
|