import type { ReactNode, Ref } from 'react' import { useImperativeHandle } from 'react' import { Button } from '@/client/components/ui/button' import { Spinner } from '@/client/components/ui/spinner' import { DrawingLayer } from '@/client/features/drawings/DrawingLayer' import { DrawingToolbar } from '@/client/features/drawings/DrawingToolbar' import { type DrawingControls, useDrawingHistoryState } from '@/client/features/drawings/useDrawingLayer' import { type ViewBuilderSketchController, useViewBuilderSketch } from '@/client/features/drawings/useViewBuilderSketch' import { cn } from '@/client/lib/cn' import type { ViewBuilder as ViewBuilderData } from '@/lib/types' import { viewBuilderTabId } from '@/lib/workspace-tabs' import { IconScribble } from '@tabler/icons-react' export type ViewBuilderHandle = { prepareSketchForSend: () => Promise resetSketch: () => Promise } type ViewBuilderProps = { ref?: Ref active: boolean builder: ViewBuilderData chatDocked: boolean workspaceId: string onEditingStart: () => void onContinueInChat: () => void onOpenChat: () => void onDiscard: () => void } export function ViewBuilder({ ref, active, builder, chatDocked, workspaceId, onEditingStart, onContinueInChat, onOpenChat, onDiscard }: ViewBuilderProps) { const sketch = useViewBuilderSketch({ active: active && builder.status === 'draft', builderId: builder.id, sessionId: builder.sessionId, sourceTab: viewBuilderTabId(builder.id), workspaceId, onEditingStart, onContinueInChat }) useImperativeHandle( ref, () => ({ prepareSketchForSend: sketch.prepareForSend, resetSketch: sketch.resetDocument }), [sketch.prepareForSend, sketch.resetDocument] ) switch (builder.status) { case 'ready': return null case 'draft': case 'waiting': case 'building': return ( ) } } type ViewBuilderSketchSurfaceProps = { active: boolean chatDocked: boolean error?: string sketch: ViewBuilderSketchController status: Exclude onDiscard: () => void onOpenChat: () => void } function ViewBuilderSketchSurface({ active, chatDocked, error, sketch, status, onDiscard, onOpenChat }: ViewBuilderSketchSurfaceProps) { const draft = status === 'draft' return (
{draft && }
{draft && ( void sketch.continueInChat()} /> )}
{status === 'building' && } {status === 'waiting' && ( )}
) } type ViewBuilderDraftStateProps = { chatDocked: boolean sketch: ViewBuilderSketchController } function ViewBuilderDraftState({ chatDocked, sketch }: ViewBuilderDraftStateProps) { const { hasStrokes } = useDrawingHistoryState(sketch.controls) return (
{!sketch.controls.active && !hasStrokes && (
Sketch how the view should look
)}
) } type ViewBuilderWaitingStateProps = { error?: string onDiscard: () => void onOpenChat: () => void } function ViewBuilderWaitingState({ error, onDiscard, onOpenChat }: ViewBuilderWaitingStateProps) { return (

The view needs your attention

{error ?? 'Open the chat to answer or ask the agent to continue'}

) } type ViewBuilderBuildingStateProps = { controls: DrawingControls } function ViewBuilderBuildingState({ controls }: ViewBuilderBuildingStateProps) { const { hasStrokes } = useDrawingHistoryState(controls) return ( {!hasStrokes && } ) } type ViewBuilderStatusLayoutProps = { children?: ReactNode className?: string } function ViewBuilderStatusLayout({ children, className }: ViewBuilderStatusLayoutProps) { return (
{children}
) }