@adonis-agora/agent
Version:
Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.
69 lines • 3.74 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useEffect, useState } from 'react';
import { useSpeechRecognition } from './use-speech-recognition.js';
/**
* Styling-agnostic chat composer. Enter submits, Shift+Enter inserts a
* newline. Optional dictation streams interim transcripts into the draft
* and commits finalized chunks, and `onStop` surfaces the backend's
* cancel next to send. No design-system or icon-library deps —
* style it entirely through the `*ClassName` props.
*/
export function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = true, speechLang = 'en-US', sendLabel = 'Send', onStop, isStreaming = false, stopLabel = 'Stop generating', className, textareaClassName, sendButtonClassName, micButtonClassName, stopButtonClassName, renderMic, }) {
const [value, setValue] = useState('');
// Interim dictation transcript shown appended to the draft until the
// engine finalizes it — kept out of `value` so editing the rest of the
// text doesn't persist a partial guess.
const [interim, setInterim] = useState('');
const speech = useSpeechRecognition({
lang: speechLang,
onFinalTranscript: (text) => {
const trimmed = text.trim();
if (!trimmed)
return;
setValue((prev) => (prev.length === 0 ? trimmed : `${prev.trimEnd()} ${trimmed}`));
setInterim('');
},
onInterimTranscript: (text) => setInterim(text),
});
useEffect(() => {
if (!speech.isListening)
setInterim('');
}, [speech.isListening]);
function submit() {
const trimmed = value.trim();
if (!trimmed)
return;
onSubmit(trimmed);
setValue('');
setInterim('');
}
function handleKeyDown(event) {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
submit();
}
}
function toggleMic() {
if (speech.isListening)
speech.stop();
else
speech.start();
}
const displayValue = speech.isListening && interim
? `${value}${value && !value.endsWith(' ') ? ' ' : ''}${interim}`
: value;
const showMic = enableSpeech && speech.isSupported;
const showStop = isStreaming && onStop !== undefined;
return (_jsxs("div", { className: className, children: [_jsx("textarea", { className: textareaClassName, rows: rows, value: displayValue, disabled: disabled, onChange: (event) => {
// Ignore changes while interim text is injected — the engine
// overwrites it on the next result. Editing works when idle.
if (speech.isListening && interim)
return;
setValue(event.target.value);
}, onKeyDown: handleKeyDown, placeholder: placeholder ?? 'Ask something (Enter to send, Shift+Enter for newline)' }), _jsx("button", { type: "button", className: sendButtonClassName, onClick: submit, disabled: disabled || !value.trim(), children: sendLabel }), showStop ? (_jsx("button", { type: "button", className: stopButtonClassName, onClick: () => void onStop(), "aria-label": "Stop generating", children: stopLabel })) : null, showMic ? (_jsx("button", { type: "button", className: micButtonClassName, onClick: toggleMic, disabled: disabled, "aria-label": speech.isListening ? 'Stop dictating' : 'Start dictating', children: renderMic
? renderMic({ isListening: speech.isListening })
: speech.isListening
? 'Stop'
: 'Speak' })) : null] }));
}
//# sourceMappingURL=chat-input.js.map