33 * Features: Entity selection, layout management, debug coordination
44 */
55
6- import { useEffect , useCallback , useState } from "react" ;
6+ import { useEffect , useCallback , useRef , useState } from "react" ;
77import { AppHeader , DebugPanel , SettingsModal , DeploymentModal } from "@/components/layout" ;
88import { GalleryView } from "@/components/features/gallery" ;
99import { AgentView } from "@/components/features/agent" ;
@@ -15,17 +15,22 @@ import type {
1515 AgentInfo ,
1616 WorkflowInfo ,
1717 ExtendedResponseStreamEvent ,
18+ ResponseTextDeltaEvent ,
1819} from "@/types" ;
1920import { Button } from "./components/ui/button" ;
2021import { Input } from "./components/ui/input" ;
2122import { useDevUIStore } from "@/stores" ;
2223
24+ const DEBUG_TEXT_EVENT_FLUSH_INTERVAL_MS = 50 ;
25+
2326export default function App ( ) {
2427 // Local state for auth handling
2528 const [ authRequired , setAuthRequired ] = useState ( false ) ;
2629 const [ authToken , setAuthToken ] = useState ( "" ) ;
2730 const [ isTestingToken , setIsTestingToken ] = useState ( false ) ;
2831 const [ authError , setAuthError ] = useState ( "" ) ;
32+ const bufferedDebugTextRef = useRef < ResponseTextDeltaEvent | null > ( null ) ;
33+ const lastBufferedDebugFlushAtRef = useRef ( 0 ) ;
2934
3035 // Entity state from Zustand
3136 const agents = useDevUIStore ( ( state ) => state . agents ) ;
@@ -303,16 +308,63 @@ export default function App() {
303308 [ selectEntity , updateAgent , updateWorkflow , addToast ]
304309 ) ;
305310
311+ const flushBufferedDebugText = useCallback ( ( ) => {
312+ const bufferedEvent = bufferedDebugTextRef . current ;
313+ if ( ! bufferedEvent ) {
314+ return ;
315+ }
316+
317+ bufferedDebugTextRef . current = null ;
318+ lastBufferedDebugFlushAtRef . current = performance . now ( ) ;
319+ addDebugEvent ( bufferedEvent ) ;
320+ } , [ addDebugEvent ] ) ;
321+
306322 // Handle debug events from active view
307323 const handleDebugEvent = useCallback (
308324 ( event : ExtendedResponseStreamEvent | "clear" ) => {
309325 if ( event === "clear" ) {
326+ bufferedDebugTextRef . current = null ;
310327 clearDebugEvents ( ) ;
311- } else {
312- addDebugEvent ( event ) ;
328+ return ;
313329 }
330+
331+ if (
332+ event . type === "response.output_text.delta" &&
333+ "delta" in event &&
334+ typeof event . delta === "string" &&
335+ event . delta . length > 0
336+ ) {
337+ const bufferedEvent = bufferedDebugTextRef . current ;
338+ const isSameOutput =
339+ bufferedEvent !== null &&
340+ bufferedEvent . item_id === event . item_id &&
341+ bufferedEvent . output_index === event . output_index &&
342+ bufferedEvent . content_index === event . content_index ;
343+
344+ if ( isSameOutput && bufferedEvent ) {
345+ bufferedDebugTextRef . current = {
346+ ...bufferedEvent ,
347+ delta : bufferedEvent . delta + event . delta ,
348+ sequence_number : event . sequence_number ?? bufferedEvent . sequence_number ,
349+ } ;
350+ } else {
351+ flushBufferedDebugText ( ) ;
352+ bufferedDebugTextRef . current = { ...event } as ResponseTextDeltaEvent ;
353+ }
354+
355+ if (
356+ performance . now ( ) - lastBufferedDebugFlushAtRef . current >=
357+ DEBUG_TEXT_EVENT_FLUSH_INTERVAL_MS
358+ ) {
359+ flushBufferedDebugText ( ) ;
360+ }
361+ return ;
362+ }
363+
364+ flushBufferedDebugText ( ) ;
365+ addDebugEvent ( event ) ;
314366 } ,
315- [ addDebugEvent , clearDebugEvents ]
367+ [ addDebugEvent , clearDebugEvents , flushBufferedDebugText ]
316368 ) ;
317369
318370 // Show loading state while initializing
0 commit comments