/** * Subtext cache operations (renderer-side). * * Bridges the renderer's Yjs documents with the Subtext local CRDT store via * the IPC-exposed TCP client. * * All functions below accept a `vaultPath` directly; there is no URI→vaultPath * mapping layer at this time. */ import * as Y from 'yjs'; const IPC_TIMEOUT_MS = 5000; async function ipcCall(method: string, ...args: unknown[]): Promise { if (!window.textile?.subtext) { throw new Error('subtext IPC not available'); } const fn = window.textile.subtext[method as keyof typeof window.textile.subtext]; if (typeof fn !== 'function') { throw new Error(`unknown subtext IPC method: ${method}`); } return Promise.race([ (fn as (...args: unknown[]) => Promise)(...args), new Promise((_, reject) => setTimeout(() => reject(new Error('subtext IPC timeout')), IPC_TIMEOUT_MS), ), ]); } /** Try to seed an existing Y.Doc from Subtext's local cache. Returns true if seeded. */ export async function seedYdocFromSubtext(vaultPath: string, ydoc: Y.Doc): Promise { try { const bytes = (await ipcCall('getFullState', vaultPath)) as Uint8Array | null; if (bytes && bytes.length > 0) { Y.applyUpdateV2(ydoc, bytes); return true; } } catch (e) { console.warn('[subtext] seed failed for', vaultPath, e); } return false; } /** Push the full Yjs state of a document to Subtext (overwrite local cache). */ export async function pushYdocToSubtext(vaultPath: string, ydoc: Y.Doc): Promise { const bytes = Y.encodeStateAsUpdateV2(ydoc); try { await ipcCall('applyUpdate', vaultPath, bytes); } catch (e) { console.warn('[subtext] push failed for', vaultPath, e); throw e; } } /** Push a single update diff to Subtext (lightweight, used on every local edit). */ export async function pushUpdateToSubtext(vaultPath: string, update: Uint8Array): Promise { try { await ipcCall('applyUpdate', vaultPath, update); } catch (e) { console.warn('[subtext] push update failed for', vaultPath, e); } } /** Create a new vault file in Subtext with the given initial Yjs state. */ export async function createFileInSubtext( vaultPath: string, initialState: Uint8Array, ): Promise { await ipcCall('createFile', vaultPath, initialState); } /** Rename a vault file in Subtext. */ export async function renameFileInSubtext( oldVaultPath: string, newVaultPath: string, ): Promise { await ipcCall('renameFile', oldVaultPath, newVaultPath); } /** Check whether Subtext has a local file at the given vault path. */ export async function fileExists(vaultPath: string): Promise { try { return await ipcCall('fileExists', vaultPath); } catch { return false; } } /** List all local vault paths from Subtext. */ export async function listCachedVaults(): Promise { return await ipcCall('listFiles'); }