export async function setupPdfLayers() { // Extract immediately since we're called after documentloaded await extractAndSendLayers(); // Listen for layer visibility toggle requests window.addEventListener("message", async (event) => { // Only accept messages from the same origin to prevent malicious iframes if (event.origin !== window.location.origin) return; if (event.data?.type === "trilium-toggle-layer") { const layerId = event.data.layerId; const visible = event.data.visible; await toggleLayer(layerId, visible); } }); } async function extractAndSendLayers() { const app = window.PDFViewerApplication; try { // Get the config from the viewer if available (has updated state), otherwise from document const pdfViewer = app.pdfViewer; const optionalContentConfig = pdfViewer?.optionalContentConfigPromise ? await pdfViewer.optionalContentConfigPromise : await app.pdfDocument.getOptionalContentConfig(); if (!optionalContentConfig) { window.parent.postMessage({ type: "pdfjs-viewer-layers", layers: [] } satisfies PdfViewerLayersMessage, window.location.origin); return; } // Get all layer group IDs from the order const order = optionalContentConfig.getOrder(); if (!order || order.length === 0) { window.parent.postMessage({ type: "pdfjs-viewer-layers", layers: [] } satisfies PdfViewerLayersMessage, window.location.origin); return; } // Flatten the order array (it can be nested) and extract group IDs const groupIds: string[] = []; const flattenOrder = (items: any[]) => { for (const item of items) { if (typeof item === 'string') { groupIds.push(item); } else if (Array.isArray(item)) { flattenOrder(item); } else if (item && typeof item === 'object' && item.id) { groupIds.push(item.id); } } }; flattenOrder(order); // Get group details for each ID and only include valid, toggleable layers const layers = groupIds.map(id => { const group = optionalContentConfig.getGroup(id); // Only include groups that have a name and usage property (actual layers) if (!group || !group.name || !group.usage) { return null; } // Use group.visible property like PDF.js viewer does return { id, name: group.name, visible: group.visible }; }).filter(layer => layer !== null); // Filter out invalid layers window.parent.postMessage({ type: "pdfjs-viewer-layers", layers } satisfies PdfViewerLayersMessage, window.location.origin); } catch (error) { console.error("Error extracting layers:", error); window.parent.postMessage({ type: "pdfjs-viewer-layers", layers: [] } satisfies PdfViewerLayersMessage, window.location.origin); } } async function toggleLayer(layerId: string, visible: boolean) { const app = window.PDFViewerApplication; try { const pdfViewer = app.pdfViewer; if (!pdfViewer) { return; } const optionalContentConfig = await pdfViewer.optionalContentConfigPromise; if (!optionalContentConfig) { return; } // Set visibility on the config (like PDF.js viewer does) optionalContentConfig.setVisibility(layerId, visible); // Dispatch optionalcontentconfig event with the existing config app.eventBus.dispatch("optionalcontentconfig", { source: app, promise: Promise.resolve(optionalContentConfig) }); // Send updated layer state back await extractAndSendLayers(); } catch (error) { console.error("Error toggling layer:", error); } }