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:
yeyan1996 2025-08-09 12:59:55 -07:00
parent a1ef96b245
commit 176182e2e9
6 changed files with 63 additions and 138 deletions

View File

@ -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 {
status: streamHookStatus,
toolCall: streamingToolCall,

View File

@ -11,42 +11,15 @@ import {
import { UnifiedMessage } from '@/components/thread/types';
import { safeJsonParse } from '@/components/thread/utils';
import Link from 'next/link';
// Define the set of tags whose raw XML should be hidden during streaming
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',
]);
import { parseXmlToolCalls } from '../tool-views/xml-parser';
import { HIDE_STREAMING_XML_TAGS } from '@/components/thread/utils';
export interface PlaybackControlsProps {
messages: UnifiedMessage[];
isSidePanelOpen: boolean;
onToggleSidePanel: () => void;
toolCalls: any[];
setCurrentToolIndex: (index: number) => void;
setCurrentToolIndex: React.Dispatch<React.SetStateAction<number>>;
onFileViewerOpen: () => void;
projectName?: string;
}
@ -58,7 +31,6 @@ export interface PlaybackState {
streamingText: string;
isStreamingText: boolean;
currentToolCall: any | null;
toolPlaybackIndex: number;
}
export interface PlaybackController {
@ -88,7 +60,6 @@ export const PlaybackControls = ({
streamingText: '',
isStreamingText: false,
currentToolCall: null,
toolPlaybackIndex: -1,
});
// Extract state variables for easier access
@ -99,10 +70,10 @@ export const PlaybackControls = ({
streamingText,
isStreamingText,
currentToolCall,
toolPlaybackIndex,
} = playbackState;
const playbackTimeout = useRef<NodeJS.Timeout | null>(null);
const [isToolInitialized, setIsToolInitialized] = useState(false);
// Helper function to update playback state
const updatePlaybackState = useCallback((updates: Partial<PlaybackState>) => {
@ -129,9 +100,9 @@ export const PlaybackControls = ({
streamingText: '',
isStreamingText: false,
currentToolCall: null,
toolPlaybackIndex: -1,
});
setCurrentToolIndex(-1);
setCurrentToolIndex(0);
setIsToolInitialized(false);
if (playbackTimeout.current) {
clearTimeout(playbackTimeout.current);
@ -192,7 +163,6 @@ export const PlaybackControls = ({
streamingText: '',
isStreamingText: false,
currentToolCall: null,
toolPlaybackIndex: toolCalls.length - 1,
});
if (toolCalls.length > 0) {
@ -242,11 +212,9 @@ export const PlaybackControls = ({
}
// Add the tool call
const toolName = match[1] || match[2];
chunks.push({
text: match[0],
isTool: true,
toolName,
});
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 (currentChunk.isTool && currentIndex === 0) {
// For tool calls, check if they should be hidden during streaming
if (
currentChunk.toolName &&
HIDE_STREAMING_XML_TAGS.has(currentChunk.toolName)
) {
// 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();
if (isToolInitialized) {
setCurrentToolIndex((prev) => prev + 1);
} else {
setIsToolInitialized(true);
}
setCurrentToolIndex(toolPlaybackIndex + 1);
// Pause streaming briefly while showing the tool
isPaused = true;
setTimeout(() => {
@ -340,7 +292,6 @@ export const PlaybackControls = ({
return;
}
}
// Handle normal text streaming for non-tool chunks or visible tool chunks
if (currentIndex < currentChunk.text.length) {
@ -387,7 +338,6 @@ export const PlaybackControls = ({
isPlaying,
messages,
currentMessageIndex,
toolPlaybackIndex,
setCurrentToolIndex,
isSidePanelOpen,
onToggleSidePanel,

View File

@ -16,40 +16,8 @@ import { AgentLoader } from './loader';
import { parseXmlToolCalls, isNewXmlFormat } from '@/components/thread/tool-views/xml-parser';
import { ShowToolStream } from './ShowToolStream';
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)
export function renderAttachments(attachments: string[], fileViewerHandler?: (filePath?: string, filePathList?: string[]) => void, sandboxId?: string, project?: Project) {

View File

@ -151,10 +151,9 @@ export function ToolCallSidePanel({
}, [toolCalls, navigationMode, toolCallSnapshots.length, isInitialized]);
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));
}
}, [isOpen, currentIndex, isInitialized, toolCallSnapshots.length]);
}, [currentIndex, toolCallSnapshots.length]);
const safeInternalIndex = Math.min(internalIndex, Math.max(0, toolCallSnapshots.length - 1));
const currentSnapshot = toolCallSnapshots[safeInternalIndex];

View File

@ -494,3 +494,38 @@ export function getUserFriendlyToolName(toolName: string): string {
}
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',
]);

View File

@ -45,7 +45,7 @@ function Slider({
<SliderPrimitive.Range
data-slot="slider-range"
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>
@ -53,7 +53,7 @@ function Slider({
<SliderPrimitive.Thumb
data-slot="slider-thumb"
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>