export type OfficeFileKind = "docx" | "xlsx" | "pptx" | "eml" | "mail"; const mimeTypes: Record = { docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation", eml: "message/rfc822", mail: "application/octet-stream" }; export function isDesktopOfficeBridgeAvailable() { return Boolean(window.qoffice?.isDesktop && window.qoffice.openOfficeFile && window.qoffice.saveOfficeFile); } export async function openDesktopOfficeFile(kind: OfficeFileKind): Promise { const result = await window.qoffice?.openOfficeFile?.(kind); if (!result || result.canceled) { return null; } if (!result.base64Data) { throw new Error("Desktop-оболочка QOffice не вернула данные файла."); } return new File([base64ToBytes(result.base64Data)], result.fileName ?? `qoffice.${kind}`, { type: mimeTypes[kind] }); } export async function saveDesktopOfficeFile(kind: OfficeFileKind, defaultFileName: string, blob: Blob) { const bridge = window.qoffice; if (!bridge?.saveOfficeFile) { throw new Error("Desktop-оболочка QOffice недоступна."); } const base64Data = bytesToBase64(new Uint8Array(await blob.arrayBuffer())); return bridge.saveOfficeFile(kind, defaultFileName, base64Data); } export function bytesToBase64(bytes: Uint8Array) { let binary = ""; const chunkSize = 0x8000; for (let index = 0; index < bytes.length; index += chunkSize) { binary += String.fromCharCode(...bytes.slice(index, index + chunkSize)); } return btoa(binary); } export function base64ToBytes(base64Data: string) { const binary = atob(base64Data); const bytes = new Uint8Array(binary.length); for (let index = 0; index < binary.length; index += 1) { bytes[index] = binary.charCodeAt(index); } return bytes; }