import figures from 'figures' import * as React from 'react' import { useState } from 'react' import type { Root } from '@anthropic/ink' import { Box, Text, useAnimationFrame } from '@anthropic/ink' import { AppStateProvider } from '../state/AppState.js' import { checkOutTeleportedSessionBranch, processMessagesForTeleportResume, type TeleportProgressStep, type TeleportResult, teleportResumeCodeSession, } from '../utils/teleport.js' type Props = { currentStep: TeleportProgressStep sessionId?: string } const SPINNER_FRAMES = ['◐', '◓', '◑', '◒'] const STEPS: { key: TeleportProgressStep; label: string }[] = [ { key: 'validating', label: 'Validating session' }, { key: 'fetching_logs', label: 'Fetching session logs' }, { key: 'fetching_branch', label: 'Getting branch info' }, { key: 'checking_out', label: 'Checking out branch' }, ] export function TeleportProgress({ currentStep, sessionId, }: Props): React.ReactNode { const [ref, time] = useAnimationFrame(100) const frame = Math.floor(time / 100) % SPINNER_FRAMES.length const currentStepIndex = STEPS.findIndex(s => s.key === currentStep) return ( {SPINNER_FRAMES[frame]} Teleporting session… {sessionId && ( {sessionId} )} {STEPS.map((step, index) => { const isComplete = index < currentStepIndex const isCurrent = index === currentStepIndex const isPending = index > currentStepIndex let icon: string let color: string | undefined if (isComplete) { icon = figures.tick color = 'green' } else if (isCurrent) { icon = SPINNER_FRAMES[frame]! color = 'claude' } else { icon = figures.circle color = undefined } return ( {icon} {step.label} ) })} ) } /** * Teleports to a remote session with progress UI rendered into the existing root. * Fetches the session, checks out the branch, and returns the result. */ export async function teleportWithProgress( root: Root, sessionId: string, ): Promise { // Capture the setState function from the rendered component let setStep: (step: TeleportProgressStep) => void = () => {} function TeleportProgressWrapper(): React.ReactNode { const [step, _setStep] = useState('validating') setStep = _setStep return } root.render( , ) const result = await teleportResumeCodeSession(sessionId, setStep) setStep('checking_out') const { branchName, branchError } = await checkOutTeleportedSessionBranch( result.branch, ) return { messages: processMessagesForTeleportResume(result.log, branchError), branchName, } }