107 lines
3.4 KiB
JavaScript
107 lines
3.4 KiB
JavaScript
/**
|
||
* 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);
|
||
}
|
||
}
|