mirror of https://github.com/kortix-ai/suna.git
fix: refactor playback controls and consolidate XML tag constants
- Remove commented-out playback logic from share page - Simplify tool call handling in PlaybackControls by removing toolPlaybackIndex state - Fix tool navigation initialization and synchronization issues - Consolidate HIDE_STREAMING_XML_TAGS constant from multiple files into utils.ts - Add 'create-tasks' to hidden streaming XML tags list - Improve tool call side panel index synchronization - Add cursor pointer styling to slider components for better UX - Remove unused toolName tracking in chunk processing - Fix tool index initialization to start at 0 instead of -1 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
a1ef96b245
commit
176182e2e9
|
@ -288,33 +288,6 @@ export default function ThreadPage({
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// if (!isPlaying || messages.length === 0) return;
|
|
||||||
|
|
||||||
// let playbackTimeout: NodeJS.Timeout;
|
|
||||||
|
|
||||||
// const playbackNextMessage = async () => {
|
|
||||||
// if (currentMessageIndex >= messages.length) {
|
|
||||||
// setIsPlaying(false);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const currentMessage = messages[currentMessageIndex];
|
|
||||||
// console.log(
|
|
||||||
// `Playing message ${currentMessageIndex}:`,
|
|
||||||
// currentMessage.type,
|
|
||||||
// currentMessage.message_id,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// setCurrentMessageIndex((prevIndex) => prevIndex + 1);
|
|
||||||
// };
|
|
||||||
// playbackTimeout = setTimeout(playbackNextMessage, 500);
|
|
||||||
|
|
||||||
// return () => {
|
|
||||||
// clearTimeout(playbackTimeout);
|
|
||||||
// };
|
|
||||||
// }, [isPlaying, currentMessageIndex, messages]);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
status: streamHookStatus,
|
status: streamHookStatus,
|
||||||
toolCall: streamingToolCall,
|
toolCall: streamingToolCall,
|
||||||
|
|
|
@ -11,42 +11,15 @@ import {
|
||||||
import { UnifiedMessage } from '@/components/thread/types';
|
import { UnifiedMessage } from '@/components/thread/types';
|
||||||
import { safeJsonParse } from '@/components/thread/utils';
|
import { safeJsonParse } from '@/components/thread/utils';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import { parseXmlToolCalls } from '../tool-views/xml-parser';
|
||||||
// Define the set of tags whose raw XML should be hidden during streaming
|
import { HIDE_STREAMING_XML_TAGS } from '@/components/thread/utils';
|
||||||
const HIDE_STREAMING_XML_TAGS = new Set([
|
|
||||||
'execute-command',
|
|
||||||
'create-file',
|
|
||||||
'delete-file',
|
|
||||||
'full-file-rewrite',
|
|
||||||
'edit-file',
|
|
||||||
'str-replace',
|
|
||||||
'browser-click-element',
|
|
||||||
'browser-close-tab',
|
|
||||||
'browser-drag-drop',
|
|
||||||
'browser-get-dropdown-options',
|
|
||||||
'browser-go-back',
|
|
||||||
'browser-input-text',
|
|
||||||
'browser-navigate-to',
|
|
||||||
'browser-scroll-down',
|
|
||||||
'browser-scroll-to-text',
|
|
||||||
'browser-scroll-up',
|
|
||||||
'browser-select-dropdown-option',
|
|
||||||
'browser-send-keys',
|
|
||||||
'browser-switch-tab',
|
|
||||||
'browser-wait',
|
|
||||||
'deploy',
|
|
||||||
'ask',
|
|
||||||
'complete',
|
|
||||||
'crawl-webpage',
|
|
||||||
'web-search',
|
|
||||||
]);
|
|
||||||
|
|
||||||
export interface PlaybackControlsProps {
|
export interface PlaybackControlsProps {
|
||||||
messages: UnifiedMessage[];
|
messages: UnifiedMessage[];
|
||||||
isSidePanelOpen: boolean;
|
isSidePanelOpen: boolean;
|
||||||
onToggleSidePanel: () => void;
|
onToggleSidePanel: () => void;
|
||||||
toolCalls: any[];
|
toolCalls: any[];
|
||||||
setCurrentToolIndex: (index: number) => void;
|
setCurrentToolIndex: React.Dispatch<React.SetStateAction<number>>;
|
||||||
onFileViewerOpen: () => void;
|
onFileViewerOpen: () => void;
|
||||||
projectName?: string;
|
projectName?: string;
|
||||||
}
|
}
|
||||||
|
@ -58,7 +31,6 @@ export interface PlaybackState {
|
||||||
streamingText: string;
|
streamingText: string;
|
||||||
isStreamingText: boolean;
|
isStreamingText: boolean;
|
||||||
currentToolCall: any | null;
|
currentToolCall: any | null;
|
||||||
toolPlaybackIndex: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PlaybackController {
|
export interface PlaybackController {
|
||||||
|
@ -88,7 +60,6 @@ export const PlaybackControls = ({
|
||||||
streamingText: '',
|
streamingText: '',
|
||||||
isStreamingText: false,
|
isStreamingText: false,
|
||||||
currentToolCall: null,
|
currentToolCall: null,
|
||||||
toolPlaybackIndex: -1,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Extract state variables for easier access
|
// Extract state variables for easier access
|
||||||
|
@ -99,10 +70,10 @@ export const PlaybackControls = ({
|
||||||
streamingText,
|
streamingText,
|
||||||
isStreamingText,
|
isStreamingText,
|
||||||
currentToolCall,
|
currentToolCall,
|
||||||
toolPlaybackIndex,
|
|
||||||
} = playbackState;
|
} = playbackState;
|
||||||
|
|
||||||
const playbackTimeout = useRef<NodeJS.Timeout | null>(null);
|
const playbackTimeout = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
const [isToolInitialized, setIsToolInitialized] = useState(false);
|
||||||
|
|
||||||
// Helper function to update playback state
|
// Helper function to update playback state
|
||||||
const updatePlaybackState = useCallback((updates: Partial<PlaybackState>) => {
|
const updatePlaybackState = useCallback((updates: Partial<PlaybackState>) => {
|
||||||
|
@ -129,9 +100,9 @@ export const PlaybackControls = ({
|
||||||
streamingText: '',
|
streamingText: '',
|
||||||
isStreamingText: false,
|
isStreamingText: false,
|
||||||
currentToolCall: null,
|
currentToolCall: null,
|
||||||
toolPlaybackIndex: -1,
|
|
||||||
});
|
});
|
||||||
setCurrentToolIndex(-1);
|
setCurrentToolIndex(0);
|
||||||
|
setIsToolInitialized(false);
|
||||||
|
|
||||||
if (playbackTimeout.current) {
|
if (playbackTimeout.current) {
|
||||||
clearTimeout(playbackTimeout.current);
|
clearTimeout(playbackTimeout.current);
|
||||||
|
@ -192,7 +163,6 @@ export const PlaybackControls = ({
|
||||||
streamingText: '',
|
streamingText: '',
|
||||||
isStreamingText: false,
|
isStreamingText: false,
|
||||||
currentToolCall: null,
|
currentToolCall: null,
|
||||||
toolPlaybackIndex: toolCalls.length - 1,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (toolCalls.length > 0) {
|
if (toolCalls.length > 0) {
|
||||||
|
@ -242,11 +212,9 @@ export const PlaybackControls = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the tool call
|
// Add the tool call
|
||||||
const toolName = match[1] || match[2];
|
|
||||||
chunks.push({
|
chunks.push({
|
||||||
text: match[0],
|
text: match[0],
|
||||||
isTool: true,
|
isTool: true,
|
||||||
toolName,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
lastIndex = toolCallRegex.lastIndex;
|
lastIndex = toolCallRegex.lastIndex;
|
||||||
|
@ -306,28 +274,12 @@ export const PlaybackControls = ({
|
||||||
// If this is a tool call chunk and we're at the start of it
|
// If this is a tool call chunk and we're at the start of it
|
||||||
if (currentChunk.isTool && currentIndex === 0) {
|
if (currentChunk.isTool && currentIndex === 0) {
|
||||||
// For tool calls, check if they should be hidden during streaming
|
// For tool calls, check if they should be hidden during streaming
|
||||||
if (
|
if (isToolInitialized) {
|
||||||
currentChunk.toolName &&
|
setCurrentToolIndex((prev) => prev + 1);
|
||||||
HIDE_STREAMING_XML_TAGS.has(currentChunk.toolName)
|
} else {
|
||||||
) {
|
setIsToolInitialized(true);
|
||||||
// Instead of showing the XML, create a tool call object
|
|
||||||
const toolCall = {
|
|
||||||
name: currentChunk.toolName,
|
|
||||||
arguments: currentChunk.text,
|
|
||||||
xml_tag_name: currentChunk.toolName,
|
|
||||||
};
|
|
||||||
|
|
||||||
updatePlaybackState({
|
|
||||||
currentToolCall: toolCall,
|
|
||||||
toolPlaybackIndex: toolPlaybackIndex + 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!isSidePanelOpen) {
|
|
||||||
onToggleSidePanel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentToolIndex(toolPlaybackIndex + 1);
|
|
||||||
|
|
||||||
// Pause streaming briefly while showing the tool
|
// Pause streaming briefly while showing the tool
|
||||||
isPaused = true;
|
isPaused = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -340,7 +292,6 @@ export const PlaybackControls = ({
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Handle normal text streaming for non-tool chunks or visible tool chunks
|
// Handle normal text streaming for non-tool chunks or visible tool chunks
|
||||||
if (currentIndex < currentChunk.text.length) {
|
if (currentIndex < currentChunk.text.length) {
|
||||||
|
@ -387,7 +338,6 @@ export const PlaybackControls = ({
|
||||||
isPlaying,
|
isPlaying,
|
||||||
messages,
|
messages,
|
||||||
currentMessageIndex,
|
currentMessageIndex,
|
||||||
toolPlaybackIndex,
|
|
||||||
setCurrentToolIndex,
|
setCurrentToolIndex,
|
||||||
isSidePanelOpen,
|
isSidePanelOpen,
|
||||||
onToggleSidePanel,
|
onToggleSidePanel,
|
||||||
|
|
|
@ -16,40 +16,8 @@ import { AgentLoader } from './loader';
|
||||||
import { parseXmlToolCalls, isNewXmlFormat } from '@/components/thread/tool-views/xml-parser';
|
import { parseXmlToolCalls, isNewXmlFormat } from '@/components/thread/tool-views/xml-parser';
|
||||||
import { ShowToolStream } from './ShowToolStream';
|
import { ShowToolStream } from './ShowToolStream';
|
||||||
import { ComposioUrlDetector } from './composio-url-detector';
|
import { ComposioUrlDetector } from './composio-url-detector';
|
||||||
|
import { HIDE_STREAMING_XML_TAGS } from '@/components/thread/utils';
|
||||||
|
|
||||||
const HIDE_STREAMING_XML_TAGS = new Set([
|
|
||||||
'execute-command',
|
|
||||||
'create-file',
|
|
||||||
'delete-file',
|
|
||||||
'full-file-rewrite',
|
|
||||||
'edit-file',
|
|
||||||
'str-replace',
|
|
||||||
'browser-click-element',
|
|
||||||
'browser-close-tab',
|
|
||||||
'browser-drag-drop',
|
|
||||||
'browser-get-dropdown-options',
|
|
||||||
'browser-go-back',
|
|
||||||
'browser-input-text',
|
|
||||||
'browser-navigate-to',
|
|
||||||
'browser-scroll-down',
|
|
||||||
'browser-scroll-to-text',
|
|
||||||
'browser-scroll-up',
|
|
||||||
'browser-select-dropdown-option',
|
|
||||||
'browser-send-keys',
|
|
||||||
'browser-switch-tab',
|
|
||||||
'browser-wait',
|
|
||||||
'deploy',
|
|
||||||
'ask',
|
|
||||||
'complete',
|
|
||||||
'crawl-webpage',
|
|
||||||
'web-search',
|
|
||||||
'see-image',
|
|
||||||
'execute_data_provider_call',
|
|
||||||
'execute_data_provider_endpoint',
|
|
||||||
|
|
||||||
'execute-data-provider-call',
|
|
||||||
'execute-data-provider-endpoint',
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Helper function to render attachments (keeping original implementation for now)
|
// Helper function to render attachments (keeping original implementation for now)
|
||||||
export function renderAttachments(attachments: string[], fileViewerHandler?: (filePath?: string, filePathList?: string[]) => void, sandboxId?: string, project?: Project) {
|
export function renderAttachments(attachments: string[], fileViewerHandler?: (filePath?: string, filePathList?: string[]) => void, sandboxId?: string, project?: Project) {
|
||||||
|
|
|
@ -151,10 +151,9 @@ export function ToolCallSidePanel({
|
||||||
}, [toolCalls, navigationMode, toolCallSnapshots.length, isInitialized]);
|
}, [toolCalls, navigationMode, toolCallSnapshots.length, isInitialized]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (isOpen && !isInitialized && toolCallSnapshots.length > 0) {
|
// This is used to sync the internal index to the current index
|
||||||
setInternalIndex(Math.min(currentIndex, toolCallSnapshots.length - 1));
|
setInternalIndex(Math.min(currentIndex, toolCallSnapshots.length - 1));
|
||||||
}
|
}, [currentIndex, toolCallSnapshots.length]);
|
||||||
}, [isOpen, currentIndex, isInitialized, toolCallSnapshots.length]);
|
|
||||||
|
|
||||||
const safeInternalIndex = Math.min(internalIndex, Math.max(0, toolCallSnapshots.length - 1));
|
const safeInternalIndex = Math.min(internalIndex, Math.max(0, toolCallSnapshots.length - 1));
|
||||||
const currentSnapshot = toolCallSnapshots[safeInternalIndex];
|
const currentSnapshot = toolCallSnapshots[safeInternalIndex];
|
||||||
|
|
|
@ -494,3 +494,38 @@ export function getUserFriendlyToolName(toolName: string): string {
|
||||||
}
|
}
|
||||||
return TOOL_DISPLAY_NAMES.get(toolName) || toolName;
|
return TOOL_DISPLAY_NAMES.get(toolName) || toolName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const HIDE_STREAMING_XML_TAGS = new Set([
|
||||||
|
'create-tasks',
|
||||||
|
'execute-command',
|
||||||
|
'create-file',
|
||||||
|
'delete-file',
|
||||||
|
'full-file-rewrite',
|
||||||
|
'edit-file',
|
||||||
|
'str-replace',
|
||||||
|
'browser-click-element',
|
||||||
|
'browser-close-tab',
|
||||||
|
'browser-drag-drop',
|
||||||
|
'browser-get-dropdown-options',
|
||||||
|
'browser-go-back',
|
||||||
|
'browser-input-text',
|
||||||
|
'browser-navigate-to',
|
||||||
|
'browser-scroll-down',
|
||||||
|
'browser-scroll-to-text',
|
||||||
|
'browser-scroll-up',
|
||||||
|
'browser-select-dropdown-option',
|
||||||
|
'browser-send-keys',
|
||||||
|
'browser-switch-tab',
|
||||||
|
'browser-wait',
|
||||||
|
'deploy',
|
||||||
|
'ask',
|
||||||
|
'complete',
|
||||||
|
'crawl-webpage',
|
||||||
|
'web-search',
|
||||||
|
'see-image',
|
||||||
|
'execute_data_provider_call',
|
||||||
|
'execute_data_provider_endpoint',
|
||||||
|
|
||||||
|
'execute-data-provider-call',
|
||||||
|
'execute-data-provider-endpoint',
|
||||||
|
]);
|
|
@ -45,7 +45,7 @@ function Slider({
|
||||||
<SliderPrimitive.Range
|
<SliderPrimitive.Range
|
||||||
data-slot="slider-range"
|
data-slot="slider-range"
|
||||||
className={cn(
|
className={cn(
|
||||||
'bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full',
|
'cursor-pointer bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full',
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</SliderPrimitive.Track>
|
</SliderPrimitive.Track>
|
||||||
|
@ -53,7 +53,7 @@ function Slider({
|
||||||
<SliderPrimitive.Thumb
|
<SliderPrimitive.Thumb
|
||||||
data-slot="slider-thumb"
|
data-slot="slider-thumb"
|
||||||
key={index}
|
key={index}
|
||||||
className="border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
|
className="cursor-pointer border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</SliderPrimitive.Root>
|
</SliderPrimitive.Root>
|
||||||
|
|
Loading…
Reference in New Issue