mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-13 17:22:37 +02:00
Add stop button in playground
This commit is contained in:
parent
cb23be8e16
commit
a1e4eddb72
2 changed files with 42 additions and 7 deletions
|
|
@ -57,6 +57,7 @@ export function Chat({
|
||||||
|
|
||||||
// --- Scroll/auto-scroll/unread bubble logic ---
|
// --- Scroll/auto-scroll/unread bubble logic ---
|
||||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const eventSourceRef = useRef<EventSource | null>(null);
|
||||||
const [autoScroll, setAutoScroll] = useState(true);
|
const [autoScroll, setAutoScroll] = useState(true);
|
||||||
const [showUnreadBubble, setShowUnreadBubble] = useState(false);
|
const [showUnreadBubble, setShowUnreadBubble] = useState(false);
|
||||||
|
|
||||||
|
|
@ -215,6 +216,7 @@ export function Chat({
|
||||||
|
|
||||||
console.log(`chat.tsx: got streamid: ${streamId}`);
|
console.log(`chat.tsx: got streamid: ${streamId}`);
|
||||||
eventSource = new EventSource(`/api/stream-response/${streamId}`);
|
eventSource = new EventSource(`/api/stream-response/${streamId}`);
|
||||||
|
eventSourceRef.current = eventSource;
|
||||||
|
|
||||||
eventSource.addEventListener("message", (event) => {
|
eventSource.addEventListener("message", (event) => {
|
||||||
console.log(`chat.tsx: got message: ${event.data}`);
|
console.log(`chat.tsx: got message: ${event.data}`);
|
||||||
|
|
@ -238,6 +240,7 @@ export function Chat({
|
||||||
console.log(`chat.tsx: got done event: ${event.data}`);
|
console.log(`chat.tsx: got done event: ${event.data}`);
|
||||||
if (eventSource) {
|
if (eventSource) {
|
||||||
eventSource.close();
|
eventSource.close();
|
||||||
|
eventSourceRef.current = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsed = JSON.parse(event.data);
|
const parsed = JSON.parse(event.data);
|
||||||
|
|
@ -256,6 +259,7 @@ export function Chat({
|
||||||
console.log(`chat.tsx: got stream_error event: ${event.data}`);
|
console.log(`chat.tsx: got stream_error event: ${event.data}`);
|
||||||
if (eventSource) {
|
if (eventSource) {
|
||||||
eventSource.close();
|
eventSource.close();
|
||||||
|
eventSourceRef.current = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error('SSE Error:', event);
|
console.error('SSE Error:', event);
|
||||||
|
|
@ -296,6 +300,7 @@ export function Chat({
|
||||||
ignore = true;
|
ignore = true;
|
||||||
if (eventSource) {
|
if (eventSource) {
|
||||||
eventSource.close();
|
eventSource.close();
|
||||||
|
eventSourceRef.current = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
|
|
@ -309,6 +314,15 @@ export function Chat({
|
||||||
projectTools,
|
projectTools,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Add a stop handler function
|
||||||
|
const handleStop = useCallback(() => {
|
||||||
|
if (eventSourceRef.current) {
|
||||||
|
eventSourceRef.current.close();
|
||||||
|
eventSourceRef.current = null;
|
||||||
|
setLoadingAssistantResponse(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
return <div className="w-11/12 max-w-6xl mx-auto h-full flex flex-col relative">
|
return <div className="w-11/12 max-w-6xl mx-auto h-full flex flex-col relative">
|
||||||
<div className="sticky top-0 z-10 bg-white dark:bg-zinc-900 pt-4 pb-4">
|
<div className="sticky top-0 z-10 bg-white dark:bg-zinc-900 pt-4 pb-4">
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -387,6 +401,7 @@ export function Chat({
|
||||||
loading={loadingAssistantResponse}
|
loading={loadingAssistantResponse}
|
||||||
shouldAutoFocus={isLastInteracted}
|
shouldAutoFocus={isLastInteracted}
|
||||||
onFocus={() => setIsLastInteracted(true)}
|
onFocus={() => setIsLastInteracted(true)}
|
||||||
|
onCancel={handleStop}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ interface ComposeBoxPlaygroundProps {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
shouldAutoFocus?: boolean;
|
shouldAutoFocus?: boolean;
|
||||||
onFocus?: () => void;
|
onFocus?: () => void;
|
||||||
|
onCancel?: () => void; // Add this prop
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ComposeBoxPlayground({
|
export function ComposeBoxPlayground({
|
||||||
|
|
@ -18,6 +19,7 @@ export function ComposeBoxPlayground({
|
||||||
disabled = false,
|
disabled = false,
|
||||||
shouldAutoFocus = false,
|
shouldAutoFocus = false,
|
||||||
onFocus,
|
onFocus,
|
||||||
|
onCancel,
|
||||||
}: ComposeBoxPlaygroundProps) {
|
}: ComposeBoxPlaygroundProps) {
|
||||||
const [input, setInput] = useState('');
|
const [input, setInput] = useState('');
|
||||||
const [isFocused, setIsFocused] = useState(false);
|
const [isFocused, setIsFocused] = useState(false);
|
||||||
|
|
@ -93,17 +95,19 @@ export function ComposeBoxPlayground({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Send button */}
|
{/* Send/Stop button */}
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
isIconOnly
|
isIconOnly
|
||||||
disabled={disabled || loading || !input.trim()}
|
disabled={disabled || (loading ? false : !input.trim())}
|
||||||
onPress={handleInput}
|
onPress={loading ? onCancel : handleInput}
|
||||||
className={`
|
className={`
|
||||||
transition-all duration-200
|
transition-all duration-200
|
||||||
${input.trim()
|
${loading
|
||||||
? 'bg-indigo-50 hover:bg-indigo-100 text-indigo-700 dark:bg-indigo-900/50 dark:hover:bg-indigo-800/60 dark:text-indigo-300'
|
? 'bg-red-50 hover:bg-red-100 text-red-700 dark:bg-red-900/50 dark:hover:bg-red-800/60 dark:text-red-300'
|
||||||
: 'bg-gray-100 dark:bg-gray-800 text-gray-400 dark:text-gray-500'
|
: input.trim()
|
||||||
|
? 'bg-indigo-50 hover:bg-indigo-100 text-indigo-700 dark:bg-indigo-900/50 dark:hover:bg-indigo-800/60 dark:text-indigo-300'
|
||||||
|
: 'bg-gray-100 dark:bg-gray-800 text-gray-400 dark:text-gray-500'
|
||||||
}
|
}
|
||||||
scale-100 hover:scale-105 active:scale-95
|
scale-100 hover:scale-105 active:scale-95
|
||||||
disabled:opacity-50 disabled:scale-95
|
disabled:opacity-50 disabled:scale-95
|
||||||
|
|
@ -112,7 +116,7 @@ export function ComposeBoxPlayground({
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Spinner size="sm" color={input.trim() ? "primary" : "default"} />
|
<StopIcon size={16} />
|
||||||
) : (
|
) : (
|
||||||
<SendIcon
|
<SendIcon
|
||||||
size={16}
|
size={16}
|
||||||
|
|
@ -143,4 +147,20 @@ function SendIcon({ size, className }: { size: number, className?: string }) {
|
||||||
<path d="M22 2L15 22L11 13L2 9L22 2Z" />
|
<path d="M22 2L15 22L11 13L2 9L22 2Z" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add StopIcon component (copy from ComposeBoxCopilot)
|
||||||
|
function StopIcon({ size, className }: { size: number, className?: string }) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
stroke="none"
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
<rect x="6" y="6" width="12" height="12" rx="1" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue