import { invoke } from "@tauri-apps/api/core"; import { listen, type UnlistenFn } from "@tauri-apps/api/event"; import type { Hotspot } from "../types/annotations"; export interface StepTranslation { title?: string; body?: string; description?: string; voiceover?: string; } export interface DisplayDevice { id: string; name: string; resolution: { width: number; height: number; }; refresh_rate: number; is_primary: boolean; } export interface WindowBounds { x: number; y: number; width: number; height: number; } export interface WindowInfo { id: number; owner_name: string; title: string | null; bounds: WindowBounds; is_on_screen: boolean; layer: number; } export interface SessionMetadata { session_id: string; created_at: string; display: DisplayInfo; duration_ms: number | null; event_count: number; recording_path: string; events_path: string; audio_path?: string | null; title: string | null; thumbnail_path: string | null; has_terminal: boolean | null; git_branch: string | null; git_commit: string | null; hotspots?: Hotspot[]; capture_limited?: boolean; capture_limit_reason?: string | null; /** Processing status: "converting" during post-recording conversion, null when ready */ processing_status?: "converting" | null; /** Actual encoded video resolution (if probed successfully post-conversion) */ actual_resolution?: string | null; crop_region?: { display_index: number; x: number; y: number; width: number; height: number; } | null; } export interface DisplayInfo { id: string; resolution: string; refresh_rate: number; } export interface SessionSummary { session_id: string; title: string | null; created_at: string; duration_ms: number | null; step_count: number; thumbnail_path: string | null; has_terminal: boolean; git_branch: string | null; git_repo: string | null; /** Processing status: "converting" during post-recording conversion, null when ready */ processing_status?: "converting" | null; } export interface SessionManifestFileEntry { path: string; size_bytes: number; sha256: string; } export interface SessionManifest { schema_version: number; session_id: string; generated_at_ms: number; files: SessionManifestFileEntry[]; } export interface SessionManifestVerification { valid: boolean; missing_files: string[]; mismatched_files: string[]; } export interface ImportedSessionPackage { session_id: string; source_session_id: string; imported_files: number; } export interface CaptureReadiness { sidecar_healthy: boolean; sidecar_events_healthy: boolean; accessibility_event_tap_available: boolean; checked_at_ms: number; } export async function enumerateDisplays(): Promise { return await invoke("enumerate_displays"); } export async function enumerateWindows(): Promise { return await invoke("enumerate_windows"); } export async function getCaptureReadiness(): Promise { return await invoke("get_capture_readiness"); } export async function startRecording( displayId: string, width: number, height: number ): Promise { return await invoke("start_recording", { displayId, width, height }); } export async function startWindowRecording( windowId: number, width: number, height: number ): Promise { // Pass window ID in the format expected by the recorder: "window-{id}" const displayId = `window-${windowId}`; return await invoke("start_recording", { displayId, width, height }); } export async function stopRecording(): Promise { return await invoke("stop_recording"); } export async function greet(name: string): Promise { return await invoke("greet", { name }); } // Terminal recording API export interface TerminalStatus { session_id: string; is_running: boolean; width: number; height: number; } export interface StopTerminalResponse { success: boolean; session_id: string | null; output_path: string | null; duration_ms: number | null; error: string | null; } export async function startTerminalRecording( sessionId: string, width: number, height: number, recordingStartMs: number ): Promise { return await invoke("start_terminal_recording", { sessionId, width, height, recordingStartMs }); } export async function stopTerminalRecording(): Promise { return await invoke("stop_terminal_recording"); } export async function getTerminalWsUrl(sessionId: string): Promise { return await invoke("get_terminal_ws_url", { sessionId }); } export async function getTerminalStatus(sessionId: string): Promise { return await invoke("get_terminal_status", { sessionId }); } // Global hotkey events export async function onToggleRecordingHotkey(callback: () => void): Promise { return await listen("hotkey-toggle-recording", () => { callback(); }); } // Processing events export async function onProcessingStarted( callback: (sessionId: string) => void ): Promise { return await listen("processing-started", (event) => { callback(event.payload); }); } export async function onProcessingComplete( callback: (sessionId: string) => void ): Promise { return await listen("processing-complete", (event) => { callback(event.payload); }); } export async function onProcessingError(callback: (error: string) => void): Promise { return await listen("processing-error", (event) => { callback(event.payload); }); } // Library API (Phase 4) export async function listSessions(): Promise { return await invoke("list_sessions"); } export async function deleteSession(sessionId: string): Promise { return await invoke("delete_session", { sessionId }); } export async function renameSession(sessionId: string, title: string): Promise { return await invoke("rename_session", { sessionId, title }); } export async function getSessionMetadata(sessionId: string): Promise { return await invoke("get_session_metadata", { sessionId }); } export async function getSessionManifest(sessionId: string): Promise { return await invoke("get_session_manifest", { sessionId }); } export async function verifySessionManifest( sessionId: string ): Promise { return await invoke("verify_session_manifest", { sessionId }); } export async function exportSessionPackage( sessionId: string, destPath?: string ): Promise { if (destPath) { return await invoke("export_session_package", { sessionId, destPath }); } return await invoke("export_session_package", { sessionId }); } export async function importSessionPackage( packagePath: string ): Promise { return await invoke("import_session_package", { packagePath }); } export async function generateShareLink(sessionId: string): Promise { return await invoke("generate_share_link", { sessionId }); } export interface ShareStepViewMetric { step_id: string; views: number; } export interface ShareAnalyticsSummary { token: string; session_id: string; generated_at_ms: number; total_events: number; unique_visitors: number; unique_accounts: number; viewer_sessions: number; completions: number; hotspot_clicks: number; voiceover_plays: number; step_views: ShareStepViewMetric[]; last_event_ts_ms: number | null; } export async function getShareAnalytics(shareUrl: string): Promise { const normalized = shareUrl.replace(/\/+$/, ""); const response = await fetch(`${normalized}/analytics`, { headers: { Accept: "application/json", }, }); if (!response.ok) { throw new Error(`Failed to load share analytics (${response.status})`); } return (await response.json()) as ShareAnalyticsSummary; } // Terminal event types for timeline sync export interface TerminalEvent { type: "input" | "output" | "command"; timestamp_ms: number; // ms since recording start data: string; } export interface TimelineMarker { id: string; timestamp_ms: number; label: string; type: | "command" | "click" | "file_change" | "marker" | "keypress" | "shortcut" | "mouse_scroll" | "app_focus" | "dom_action" | "dom_navigation"; // Optional structured metadata for richer step inference. meta?: Record; } export interface SessionStep { id: string; title: string; description: string; timestamp_start_ms: number; timestamp_end_ms: number; completion_mode?: StepCompletionMode; required_hotspot_ids?: string[]; hotspot_ids?: string[]; autoplay?: boolean; start_event_id?: string | null; end_event_id?: string | null; } export interface StepScreenshot { path: string; timestamp_ms: number; } export type StepCompletionMode = "none" | "any" | "all"; export interface SessionStepRecord { id: string; start_ms: number; end_ms: number; title: string; body: string; tags: string[]; hotspot_ids: string[]; completion_mode?: StepCompletionMode; required_hotspot_ids?: string[]; autoplay?: boolean; start_event_id?: string | null; end_event_id?: string | null; screenshot: StepScreenshot | null; translations?: Record; voiceover_script?: string | null; } export interface StepsDocument { version: number; steps: SessionStepRecord[]; } export interface LoadedSession { metadata: SessionMetadata; steps: StepsDocument; } export async function saveHotspots(sessionId: string, hotspots: Hotspot[]): Promise { return await invoke("save_hotspots", { sessionId, hotspots }); } export async function loadSession(sessionId: string): Promise { return await invoke("load_session", { sessionId }); } export async function saveSteps(sessionId: string, stepsJson: StepsDocument): Promise { return await invoke("save_steps", { sessionId, stepsJson }); } export async function loadSteps(sessionId: string): Promise { return await invoke("load_steps", { sessionId }); } export async function regenerateStepScreenshots( sessionId: string, stepsJson: StepsDocument ): Promise { return await invoke("regenerate_step_screenshots", { sessionId, stepsJson }); } export async function recordStepMarker(label?: string): Promise { return await invoke("record_step_marker", { label }); } export async function exportMarkdown(sessionId: string, destPath?: string): Promise { if (destPath) { return await invoke("export_markdown", { sessionId, destPath }); } return await invoke("export_markdown", { sessionId }); } export async function getSessionSteps(sessionId: string): Promise { return await invoke("get_session_steps", { sessionId }); } export async function analyzeSession(sessionId: string): Promise { return await invoke("analyze_session", { sessionId }); } export interface LocalizeCopyResponse { text: string; target_language: string; } export async function localizeCopy( text: string, targetLanguage: string, sourceLanguage?: string ): Promise { return await invoke("localize_copy", { text, targetLanguage, sourceLanguage }); } export interface VoiceoverScriptResponse { script: string; language: string | null; tone: string | null; } export async function generateVoiceoverScript( text: string, options?: { language?: string; tone?: string; maxSeconds?: number; } ): Promise { const language = options?.language; const tone = options?.tone; const maxSeconds = options?.maxSeconds; return await invoke("generate_voiceover_script", { text, language, tone, maxSeconds }); } export interface LocalizedVariantSummary { session_id: string; target_language: string; translated_step_fields: number; translated_hotspots: number; generated_voiceovers: number; } export async function localizeSessionVariant( sessionId: string, targetLanguage: string, options?: { includeVoiceover?: boolean; overwriteExisting?: boolean; } ): Promise { const includeVoiceover = options?.includeVoiceover ?? true; const overwriteExisting = options?.overwriteExisting ?? false; return await invoke("localize_session_variant", { sessionId, targetLanguage, includeVoiceover, overwriteExisting, }); } export interface ExportMp4Options { include_chapter_cards?: boolean; } export async function exportMp4( sessionId: string, destPath: string, options?: ExportMp4Options ): Promise { return await invoke("export_mp4", { sessionId, destPath, options }); } export async function revealInFileManager(path: string): Promise { return await invoke("reveal_in_file_manager", { path }); } export async function openAccessibilitySettings(): Promise { return await invoke("open_accessibility_settings"); } // Overlay window API export async function showRecordingOverlay(): Promise { return await invoke("show_recording_overlay"); } export async function hideRecordingOverlay(): Promise { return await invoke("hide_recording_overlay"); } export async function resizeRecordingOverlay( recording: boolean, expanded?: boolean ): Promise { return await invoke("resize_recording_overlay", { recording, expanded }); } export async function isOverlayVisible(): Promise { return await invoke("is_overlay_visible"); } // Click capture API export interface ClickEvent { timestamp_ms: number; x: number; // percentage (0-100) y: number; // percentage (0-100) button: "left" | "right"; } export async function getSessionClicks(sessionId: string): Promise { return await invoke("get_session_clicks", { sessionId }); } export interface ClickOcrHint { timestamp_ms: number; text: string; } export async function extractClickOcr( sessionId: string, timestampsMs: number[] ): Promise { return await invoke("extract_click_ocr", { sessionId, timestampsMs }); } // Region selection API export interface RegionBounds { x: number; y: number; width: number; height: number; // Optional debug/UX fields: emitted by the region selector so the overlay can show // the logical (CSS px) size even though capture uses physical pixels. logicalWidth?: number; logicalHeight?: number; scaleFactor?: number; } export async function showRegionSelector(): Promise { return await invoke("show_region_selector"); } export async function hideRegionSelector(): Promise { return await invoke("hide_region_selector"); } export async function setRegionSelectorPassthrough(ignore: boolean): Promise { return await invoke("set_region_selector_passthrough", { ignore }); } export async function startRegionRecording( x: number, y: number, width: number, height: number ): Promise { return await invoke("start_region_recording", { x, y, width, height }); } export async function onRegionSelected( callback: (region: RegionBounds) => void ): Promise { return await listen("region-selected", (event) => { callback(event.payload); }); } export async function onRegionCancelled(callback: () => void): Promise { return await listen("region-cancelled", () => { callback(); }); } // Marker API export async function logMarker( sessionId: string, label: string, timestampMs: number ): Promise { return await invoke("log_marker", { sessionId, label, timestampMs }); } export async function onMarkerHotkey(callback: () => void): Promise { return await listen("hotkey-mark-step", () => { callback(); }); } // Transcription types export interface TranscriptSegment { start_ms: number; end_ms: number; text: string; confidence?: number; } export interface Transcript { segments: TranscriptSegment[]; full_text: string; duration_ms: number; language?: string; } export async function transcribeSession(sessionId: string): Promise { return await invoke("transcribe_session", { sessionId }); } export async function getTranscript(sessionId: string): Promise { return await invoke("get_transcript", { sessionId }); }