mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-16 18:25:17 +02:00
Fix the copy button not working
Fix copy button not working
This commit is contained in:
parent
0d775003bf
commit
767584dbbc
2 changed files with 53 additions and 35 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
'use client';
|
'use client';
|
||||||
import { useState } from "react";
|
import { useState, useCallback, useRef } from "react";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { MCPServer, PlaygroundChat } from "@/app/lib/types/types";
|
import { MCPServer, PlaygroundChat } from "@/app/lib/types/types";
|
||||||
import { Workflow } from "@/app/lib/types/workflow_types";
|
import { Workflow } from "@/app/lib/types/workflow_types";
|
||||||
|
|
@ -30,7 +30,6 @@ export function App({
|
||||||
mcpServerUrls: Array<z.infer<typeof MCPServer>>;
|
mcpServerUrls: Array<z.infer<typeof MCPServer>>;
|
||||||
toolWebhookUrl: string;
|
toolWebhookUrl: string;
|
||||||
}) {
|
}) {
|
||||||
const [counter, setCounter] = useState<number>(0);
|
|
||||||
const [testProfile, setTestProfile] = useState<WithStringId<z.infer<typeof TestProfile>> | null>(null);
|
const [testProfile, setTestProfile] = useState<WithStringId<z.infer<typeof TestProfile>> | null>(null);
|
||||||
const [systemMessage, setSystemMessage] = useState<string>(defaultSystemMessage);
|
const [systemMessage, setSystemMessage] = useState<string>(defaultSystemMessage);
|
||||||
const [chat, setChat] = useState<z.infer<typeof PlaygroundChat>>({
|
const [chat, setChat] = useState<z.infer<typeof PlaygroundChat>>({
|
||||||
|
|
@ -42,19 +41,17 @@ export function App({
|
||||||
});
|
});
|
||||||
const [isProfileSelectorOpen, setIsProfileSelectorOpen] = useState(false);
|
const [isProfileSelectorOpen, setIsProfileSelectorOpen] = useState(false);
|
||||||
const [showCopySuccess, setShowCopySuccess] = useState(false);
|
const [showCopySuccess, setShowCopySuccess] = useState(false);
|
||||||
|
const getCopyContentRef = useRef<(() => string) | null>(null);
|
||||||
|
|
||||||
function handleSystemMessageChange(message: string) {
|
function handleSystemMessageChange(message: string) {
|
||||||
setSystemMessage(message);
|
setSystemMessage(message);
|
||||||
setCounter(counter + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTestProfileChange(profile: WithStringId<z.infer<typeof TestProfile>> | null) {
|
function handleTestProfileChange(profile: WithStringId<z.infer<typeof TestProfile>> | null) {
|
||||||
setTestProfile(profile);
|
setTestProfile(profile);
|
||||||
setCounter(counter + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNewChatButtonClick() {
|
function handleNewChatButtonClick() {
|
||||||
setCounter(counter + 1);
|
|
||||||
setChat({
|
setChat({
|
||||||
projectId,
|
projectId,
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
|
|
@ -62,21 +59,23 @@ export function App({
|
||||||
simulated: false,
|
simulated: false,
|
||||||
systemMessage: defaultSystemMessage,
|
systemMessage: defaultSystemMessage,
|
||||||
});
|
});
|
||||||
|
setSystemMessage(defaultSystemMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCopyJson = () => {
|
const handleCopyJson = useCallback(() => {
|
||||||
const jsonString = JSON.stringify({
|
if (getCopyContentRef.current) {
|
||||||
messages: [{
|
try {
|
||||||
role: 'system',
|
const data = getCopyContentRef.current();
|
||||||
content: systemMessage,
|
navigator.clipboard.writeText(data);
|
||||||
}, ...chat.messages],
|
setShowCopySuccess(true);
|
||||||
}, null, 2);
|
setTimeout(() => {
|
||||||
navigator.clipboard.writeText(jsonString);
|
setShowCopySuccess(false);
|
||||||
setShowCopySuccess(true);
|
}, 2000);
|
||||||
setTimeout(() => {
|
} catch (error) {
|
||||||
setShowCopySuccess(false);
|
console.error('Error copying:', error);
|
||||||
}, 2000);
|
}
|
||||||
};
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (hidden) {
|
if (hidden) {
|
||||||
return <></>;
|
return <></>;
|
||||||
|
|
@ -140,7 +139,6 @@ export function App({
|
||||||
/>
|
/>
|
||||||
<div className="h-full overflow-auto px-4 py-4">
|
<div className="h-full overflow-auto px-4 py-4">
|
||||||
<Chat
|
<Chat
|
||||||
key={`chat-${counter}`}
|
|
||||||
chat={chat}
|
chat={chat}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
workflow={workflow}
|
workflow={workflow}
|
||||||
|
|
@ -151,6 +149,7 @@ export function App({
|
||||||
onSystemMessageChange={handleSystemMessageChange}
|
onSystemMessageChange={handleSystemMessageChange}
|
||||||
mcpServerUrls={mcpServerUrls}
|
mcpServerUrls={mcpServerUrls}
|
||||||
toolWebhookUrl={toolWebhookUrl}
|
toolWebhookUrl={toolWebhookUrl}
|
||||||
|
onCopyClick={(fn) => { getCopyContentRef.current = fn; }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
'use client';
|
'use client';
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState, useCallback } from "react";
|
||||||
import { getAssistantResponseStreamId } from "@/app/actions/actions";
|
import { getAssistantResponseStreamId } from "@/app/actions/actions";
|
||||||
import { Messages } from "./messages";
|
import { Messages } from "./messages";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
@ -27,6 +27,7 @@ export function Chat({
|
||||||
onSystemMessageChange,
|
onSystemMessageChange,
|
||||||
mcpServerUrls,
|
mcpServerUrls,
|
||||||
toolWebhookUrl,
|
toolWebhookUrl,
|
||||||
|
onCopyClick,
|
||||||
}: {
|
}: {
|
||||||
chat: z.infer<typeof PlaygroundChat>;
|
chat: z.infer<typeof PlaygroundChat>;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
|
@ -38,6 +39,7 @@ export function Chat({
|
||||||
onSystemMessageChange: (message: string) => void;
|
onSystemMessageChange: (message: string) => void;
|
||||||
mcpServerUrls: Array<z.infer<typeof MCPServer>>;
|
mcpServerUrls: Array<z.infer<typeof MCPServer>>;
|
||||||
toolWebhookUrl: string;
|
toolWebhookUrl: string;
|
||||||
|
onCopyClick: (fn: () => string) => void;
|
||||||
}) {
|
}) {
|
||||||
const [messages, setMessages] = useState<z.infer<typeof apiV1.ChatMessage>[]>(chat.messages);
|
const [messages, setMessages] = useState<z.infer<typeof apiV1.ChatMessage>[]>(chat.messages);
|
||||||
const [loadingAssistantResponse, setLoadingAssistantResponse] = useState<boolean>(false);
|
const [loadingAssistantResponse, setLoadingAssistantResponse] = useState<boolean>(false);
|
||||||
|
|
@ -47,9 +49,23 @@ export function Chat({
|
||||||
const [fetchResponseError, setFetchResponseError] = useState<string | null>(null);
|
const [fetchResponseError, setFetchResponseError] = useState<string | null>(null);
|
||||||
const [lastAgenticRequest, setLastAgenticRequest] = useState<unknown | null>(null);
|
const [lastAgenticRequest, setLastAgenticRequest] = useState<unknown | null>(null);
|
||||||
const [lastAgenticResponse, setLastAgenticResponse] = useState<unknown | null>(null);
|
const [lastAgenticResponse, setLastAgenticResponse] = useState<unknown | null>(null);
|
||||||
const [isProfileSelectorOpen, setIsProfileSelectorOpen] = useState(false);
|
|
||||||
const [optimisticMessages, setOptimisticMessages] = useState<z.infer<typeof apiV1.ChatMessage>[]>(chat.messages);
|
const [optimisticMessages, setOptimisticMessages] = useState<z.infer<typeof apiV1.ChatMessage>[]>(chat.messages);
|
||||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
const getCopyContent = useCallback(() => {
|
||||||
|
return JSON.stringify({
|
||||||
|
messages: [{
|
||||||
|
role: 'system',
|
||||||
|
content: systemMessage,
|
||||||
|
}, ...messages],
|
||||||
|
lastRequest: lastAgenticRequest,
|
||||||
|
lastResponse: lastAgenticResponse,
|
||||||
|
}, null, 2);
|
||||||
|
}, [messages, systemMessage, lastAgenticRequest, lastAgenticResponse]);
|
||||||
|
|
||||||
|
// Expose copy function to parent
|
||||||
|
useEffect(() => {
|
||||||
|
onCopyClick(getCopyContent);
|
||||||
|
}, [getCopyContent, onCopyClick]);
|
||||||
|
|
||||||
// reset optimistic messages when messages change
|
// reset optimistic messages when messages change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -63,7 +79,6 @@ export function Chat({
|
||||||
.forEach((message) => {
|
.forEach((message) => {
|
||||||
toolCallResults[message.tool_call_id] = message;
|
toolCallResults[message.tool_call_id] = message;
|
||||||
});
|
});
|
||||||
console.log('toolCallResults', toolCallResults);
|
|
||||||
|
|
||||||
function handleUserMessage(prompt: string) {
|
function handleUserMessage(prompt: string) {
|
||||||
const updatedMessages: z.infer<typeof apiV1.ChatMessage>[] = [...messages, {
|
const updatedMessages: z.infer<typeof apiV1.ChatMessage>[] = [...messages, {
|
||||||
|
|
@ -94,7 +109,6 @@ export function Chat({
|
||||||
|
|
||||||
// get assistant response
|
// get assistant response
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('stream useEffect called');
|
|
||||||
let ignore = false;
|
let ignore = false;
|
||||||
let eventSource: EventSource | null = null;
|
let eventSource: EventSource | null = null;
|
||||||
let msgs: z.infer<typeof apiV1.ChatMessage>[] = [];
|
let msgs: z.infer<typeof apiV1.ChatMessage>[] = [];
|
||||||
|
|
@ -102,6 +116,11 @@ export function Chat({
|
||||||
async function process() {
|
async function process() {
|
||||||
setLoadingAssistantResponse(true);
|
setLoadingAssistantResponse(true);
|
||||||
setFetchResponseError(null);
|
setFetchResponseError(null);
|
||||||
|
|
||||||
|
// Reset request/response state before making new request
|
||||||
|
setLastAgenticRequest(null);
|
||||||
|
setLastAgenticResponse(null);
|
||||||
|
|
||||||
const { agents, tools, prompts, startAgent } = convertWorkflowToAgenticAPI(workflow);
|
const { agents, tools, prompts, startAgent } = convertWorkflowToAgenticAPI(workflow);
|
||||||
const request: z.infer<typeof AgenticAPIChatRequest> = {
|
const request: z.infer<typeof AgenticAPIChatRequest> = {
|
||||||
projectId,
|
projectId,
|
||||||
|
|
@ -121,8 +140,9 @@ export function Chat({
|
||||||
toolWebhookUrl: toolWebhookUrl,
|
toolWebhookUrl: toolWebhookUrl,
|
||||||
testProfile: testProfile ?? undefined,
|
testProfile: testProfile ?? undefined,
|
||||||
};
|
};
|
||||||
setLastAgenticRequest(null);
|
|
||||||
setLastAgenticResponse(null);
|
// Store the full request object
|
||||||
|
setLastAgenticRequest(request);
|
||||||
|
|
||||||
let streamId: string | null = null;
|
let streamId: string | null = null;
|
||||||
try {
|
try {
|
||||||
|
|
@ -139,14 +159,9 @@ export function Chat({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ignore || !streamId) {
|
if (ignore || !streamId) {
|
||||||
console.log('almost there', ignore, streamId);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// log the stream id
|
|
||||||
console.log('🔄 got assistant response', streamId);
|
|
||||||
|
|
||||||
// read from SSE stream
|
|
||||||
eventSource = new EventSource(`/api/v1/stream-response/${streamId}`);
|
eventSource = new EventSource(`/api/v1/stream-response/${streamId}`);
|
||||||
|
|
||||||
eventSource.addEventListener("message", (event) => {
|
eventSource.addEventListener("message", (event) => {
|
||||||
|
|
@ -158,7 +173,6 @@ export function Chat({
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
const msg = AgenticAPIChatMessage.parse(data);
|
const msg = AgenticAPIChatMessage.parse(data);
|
||||||
const parsedMsg = convertFromAgenticAPIChatMessages([msg])[0];
|
const parsedMsg = convertFromAgenticAPIChatMessages([msg])[0];
|
||||||
console.log('🔄 got assistant response chunk', parsedMsg);
|
|
||||||
msgs.push(parsedMsg);
|
msgs.push(parsedMsg);
|
||||||
setOptimisticMessages(prev => [...prev, parsedMsg]);
|
setOptimisticMessages(prev => [...prev, parsedMsg]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
@ -173,9 +187,15 @@ export function Chat({
|
||||||
eventSource.close();
|
eventSource.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('🔄 got assistant response done', event.data);
|
const parsed = JSON.parse(event.data);
|
||||||
const parsed: {state: unknown} = JSON.parse(event.data);
|
|
||||||
setAgenticState(parsed.state);
|
setAgenticState(parsed.state);
|
||||||
|
|
||||||
|
// Combine state and collected messages in the response
|
||||||
|
setLastAgenticResponse({
|
||||||
|
...parsed,
|
||||||
|
messages: msgs
|
||||||
|
});
|
||||||
|
|
||||||
setMessages([...messages, ...msgs]);
|
setMessages([...messages, ...msgs]);
|
||||||
setLoadingAssistantResponse(false);
|
setLoadingAssistantResponse(false);
|
||||||
});
|
});
|
||||||
|
|
@ -207,7 +227,6 @@ export function Chat({
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
ignore = true;
|
ignore = true;
|
||||||
console.log('stream useEffect cleanup called');
|
|
||||||
if (eventSource) {
|
if (eventSource) {
|
||||||
eventSource.close();
|
eventSource.close();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue