/** * MessageBridge — postMessage protocol between the PDF viewer iframe * and the parent Leptos application. * * Inbound (parent → iframe): * "pdf.page.next" | "pdf.page.prev" * { type: "brittle:annotations-set", annotations: [...] } * * Outbound (iframe → parent): * { type: "brittle:keydown", key, ctrlKey, … } * { type: "brittle:viewer-state", refId, zoom, scrollTop } * { type: "brittle:annotations-request", refId } * { type: "brittle:annotation-create", refId, page, quads, selectedText } * { type: "brittle:annotation-click", refId, annotationId } */ export class MessageBridge { /** * @param {Function} onPageNext - () => void * @param {Function} onPagePrev - () => void * @param {object} [options] * @param {Function} [options.onAnnotationsSet] - (annotations: object[]) => void */ constructor(onPageNext, onPagePrev, options = {}) { this._onAnnotationsSet = options.onAnnotationsSet ?? null; this._handler = ev => { if (ev.data === "pdf.page.next") { onPageNext(); return; } if (ev.data === "pdf.page.prev") { onPagePrev(); return; } if (ev.data?.type === "brittle:annotations-set") { this._onAnnotationsSet?.(ev.data.annotations ?? []); } }; window.addEventListener("message", this._handler); } // ── Outbound ─────────────────────────────────────────────────────────────── /** Send the current viewer state (zoom + scroll) to the parent window. */ postViewerState(refId, zoom, scrollTop) { if (window.parent === window) return; window.parent.postMessage({ type: "brittle:viewer-state", refId, zoom, scrollTop, }, "*"); } /** Ask the parent to send the annotation set for this reference. */ requestAnnotations(refId) { if (window.parent === window) return; window.parent.postMessage({ type: "brittle:annotations-request", refId }, "*"); } /** * Notify the parent that the user selected text and wants to create a * highlight annotation. * * @param {string} refId * @param {number} page - 0-indexed physical page number * @param {object[]} quads - [{points:[{x,y}×4]}] in PDF coordinate space * @param {string} selectedText */ postAnnotationCreate(refId, page, quads, selectedText) { if (window.parent === window) return; window.parent.postMessage({ type: "brittle:annotation-create", refId, page, quads, selectedText, }, "*"); } /** * Notify the parent that the user clicked an existing annotation. * * @param {string} refId * @param {string} annotationId */ postAnnotationClick(refId, annotationId) { if (window.parent === window) return; window.parent.postMessage({ type: "brittle:annotation-click", refId, annotationId, }, "*"); } /** Forward a keydown event to the parent window for global keybindings. */ forwardKeydown(ev) { if (window.parent === window) return; window.parent.postMessage({ type: "brittle:keydown", key: ev.key, ctrlKey: ev.ctrlKey, shiftKey: ev.shiftKey, altKey: ev.altKey, metaKey: ev.metaKey, }, "*"); } disconnect() { window.removeEventListener("message", this._handler); } }