import type { Vec3 } from './types'; export type GuidedTourStatus = 'idle' | 'playing' | 'paused' | 'complete'; export interface GuidedTourCamera { position: Vec3; target: Vec3; fov?: number; duration?: number; easing?: string; } export interface GuidedTourStep { id: string; title: string; body: string; durationSeconds: number; partIds: string[]; camera?: GuidedTourCamera; /** * Optional normalized machine cycle range the animation player should show * while the caption is active. For example [0, 0.25] for an intake stroke. */ phaseRange?: [number, number]; rpm?: number; highlightIntensity?: number; } export interface GuidedTour { id: string; machineId: string; title: string; description: string; steps: GuidedTourStep[]; } export interface GuidedTourSnapshot { tourId: string | null; machineId: string | null; status: GuidedTourStatus; currentStepIndex: number; currentStep: GuidedTourStep | null; elapsedInStepSeconds: number; totalElapsedSeconds: number; progress: number; stepProgress: number; reducedMotion: boolean; } export interface GuidedTourSinks { highlightParts?: (partIds: string[], intensity: number) => void; requestCamera?: (camera: GuidedTourCamera, durationSeconds: number) => void; setAnimationPhase?: (phaseRange: [number, number] | undefined, rpm: number | undefined) => void; onStepChange?: (snapshot: GuidedTourSnapshot, step: GuidedTourStep | null) => void; onComplete?: () => void; } export type GuidedTourSubscriber = (snapshot: GuidedTourSnapshot) => void; export function getGuidedTourDurationSeconds(tour: GuidedTour | null | undefined): number { return tour?.steps.reduce((total, step) => total + Math.max(0.1, step.durationSeconds), 0) ?? 0; } export function getStepStartSeconds(tour: GuidedTour, stepIndex: number): number { return tour.steps .slice(0, Math.max(0, stepIndex)) .reduce((total, step) => total + Math.max(0.1, step.durationSeconds), 0); } export function normalizeTourStepIndex(tour: GuidedTour, stepIndex: number): number { if (tour.steps.length === 0) return 0; return Math.min(tour.steps.length - 1, Math.max(0, Math.round(stepIndex))); }