import { useCallback, useEffect, useRef, useState } from 'react'; import type { FileSystemProvider } from '../filesystem/types'; import type { OpenDocRequest } from '../workspaces/workspace'; import { startDocumentOpen } from './documentSlotOpen'; import type { DocumentHandle } from './types'; export type DocumentSlotState = | { kind: 'empty' } | { kind: 'loading'; request: OpenDocRequest } | { kind: 'open'; request: OpenDocRequest; handle: DocumentHandle } | { kind: 'error'; request: OpenDocRequest; message: string }; export const emptyDocumentSlot = (): DocumentSlotState => ({ kind: 'empty' }); export type UseDocumentSlotInput = { openRequestId: number; openRequest: OpenDocRequest | null; resolveProvider: (providerId: string) => FileSystemProvider | undefined; /** When false, open intents are ignored (e.g. inactive tiling tiles). */ accepts?: boolean; }; /** * Owns one document slot: open on shell intent, track loading/error/open, * and release handles on close or unmount. */ export function useDocumentSlot({ openRequestId, openRequest, resolveProvider, accepts = true, }: UseDocumentSlotInput): { state: DocumentSlotState; close: () => void; } { const [state, setState] = useState(emptyDocumentSlot); // Seed with the current id so new slots do not replay stale opens. const lastSeenIdRef = useRef(openRequestId); const handleRef = useRef(null); useEffect(() => { if (openRequestId === lastSeenIdRef.current) return; lastSeenIdRef.current = openRequestId; if (!accepts || !openRequest) return; const request = openRequest; return startDocumentOpen(request, resolveProvider, { onLoading: () => setState({ kind: 'loading', request }), onOpen: (handle) => { handleRef.current = handle; setState({ kind: 'open', request, handle }); }, onError: (message) => { handleRef.current = null; setState({ kind: 'error', request, message }); }, takePreviousHandle: () => { const prev = handleRef.current; handleRef.current = null; return prev; }, isStaleHandle: (handle) => handleRef.current !== handle, }); }, [openRequestId, openRequest, resolveProvider, accepts]); useEffect(() => { return () => { handleRef.current?.release(); handleRef.current = null; }; }, []); const close = useCallback(() => { handleRef.current?.release(); handleRef.current = null; setState(emptyDocumentSlot()); }, []); return { state, close }; }