86 lines
3.1 KiB
TypeScript
86 lines
3.1 KiB
TypeScript
import { useEffect, useRef, useState } from 'react';
|
||
import { ArrowUp, Play, RefreshCw, Square } from './Icons';
|
||
import type { Task } from '../types';
|
||
|
||
export function Composer({ task, busy, onSend, onResume, onTrackNow, onTrackStop }: {
|
||
task: Task | null;
|
||
busy: boolean;
|
||
onSend: (text: string) => void;
|
||
onResume: () => void;
|
||
onTrackNow: () => void;
|
||
onTrackStop: () => void;
|
||
}) {
|
||
const [text, setText] = useState('');
|
||
const composing = useRef(false);
|
||
const area = useRef<HTMLTextAreaElement>(null);
|
||
|
||
useEffect(() => {
|
||
const element = area.current;
|
||
if (!element) return;
|
||
element.style.height = 'auto';
|
||
element.style.height = `${Math.min(200, Math.max(48, element.scrollHeight))}px`;
|
||
}, [text]);
|
||
|
||
const submit = (): void => {
|
||
const value = text.trim();
|
||
if (!value || busy) return;
|
||
setText('');
|
||
onSend(value);
|
||
};
|
||
|
||
const running = task?.status === 'running';
|
||
const placeholder = task?.status === 'paused'
|
||
? '在这里回答助手的问题…'
|
||
: task?.status === 'collecting'
|
||
? '补充助手需要的资料…'
|
||
: '例如:帮我填今天的查课日志,班级计科2301班,听课记录还好';
|
||
|
||
return (
|
||
<div className="composer-area">
|
||
<div className="composer">
|
||
<textarea
|
||
ref={area}
|
||
value={text}
|
||
placeholder={placeholder}
|
||
rows={1}
|
||
disabled={busy}
|
||
onChange={event => setText(event.target.value)}
|
||
onCompositionStart={() => { composing.current = true; }}
|
||
onCompositionEnd={() => { composing.current = false; }}
|
||
onKeyDown={event => {
|
||
const isEnter = event.key === 'Enter' || event.keyCode === 13;
|
||
if (isEnter && !event.shiftKey && !composing.current && !event.nativeEvent.isComposing) {
|
||
event.preventDefault();
|
||
submit();
|
||
}
|
||
}}
|
||
aria-label="输入消息"
|
||
data-testid="composer-input"
|
||
/>
|
||
<button type="button" className="send-button" aria-label="发送" disabled={busy || !text.trim()} onClick={submit} data-testid="composer-send">
|
||
{running ? <Square size={16} /> : <ArrowUp size={16} />}
|
||
</button>
|
||
</div>
|
||
<div className="composer-toolbar">
|
||
<span className="composer-hint">Enter 发送,Shift + Enter 换行</span>
|
||
<span className="spacer" />
|
||
{task && ['paused', 'failed', 'blocked'].includes(task.status) && (
|
||
<button type="button" className="secondary-button small" onClick={onResume} disabled={busy} data-testid="resume">
|
||
<Play size={14} />继续执行
|
||
</button>
|
||
)}
|
||
{task?.status === 'submitted' && (
|
||
<>
|
||
<button type="button" className="secondary-button small" onClick={onTrackNow} disabled={busy} data-testid="track-now">
|
||
<RefreshCw size={14} />立即回查
|
||
</button>
|
||
{task.next_check_at && (
|
||
<button type="button" className="text-action" onClick={onTrackStop} disabled={busy} data-testid="track-stop">停止回查</button>
|
||
)}
|
||
</>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|