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,
};
}