function __classPrivateFieldGet(receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
}
function __classPrivateFieldSet(receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value), value;
}
var _Channel_instances;
var _Channel_onmessage;
var _Channel_nextMessageIndex;
var _Channel_pendingMessages;
var _Channel_messageEndIndex;
var _Channel_deliver;
var _Resource_rid;
var SERIALIZE_TO_IPC_FN = "__TAURI_TO_IPC_KEY__";
function transformCallback(callback, once = false) {
return window.__TAURI_INTERNALS__.transformCallback(callback, once);
}
var Channel = class {
constructor(onmessage) {
_Channel_instances.add(this);
_Channel_onmessage.set(this, void 0);
_Channel_nextMessageIndex.set(this, 0);
_Channel_pendingMessages.set(this, []);
_Channel_messageEndIndex.set(this, void 0);
__classPrivateFieldSet(this, _Channel_onmessage, onmessage || (() => {
}), "f");
this.id = transformCallback((rawMessage) => {
const index = rawMessage.index;
if ("end" in rawMessage) {
if (index == __classPrivateFieldGet(this, _Channel_nextMessageIndex, "f")) {
this.cleanupCallback();
} else {
__classPrivateFieldSet(this, _Channel_messageEndIndex, index, "f");
}
return;
}
const message = rawMessage.message;
if (index == __classPrivateFieldGet(this, _Channel_nextMessageIndex, "f")) {
__classPrivateFieldGet(this, _Channel_instances, "m", _Channel_deliver).call(this, message);
__classPrivateFieldSet(this, _Channel_nextMessageIndex, __classPrivateFieldGet(this, _Channel_nextMessageIndex, "f") + 1, "f");
while (__classPrivateFieldGet(this, _Channel_nextMessageIndex, "f") in __classPrivateFieldGet(this, _Channel_pendingMessages, "f")) {
const message2 = __classPrivateFieldGet(this, _Channel_pendingMessages, "f")[__classPrivateFieldGet(this, _Channel_nextMessageIndex, "f")];
__classPrivateFieldGet(this, _Channel_instances, "m", _Channel_deliver).call(this, message2);
delete __classPrivateFieldGet(this, _Channel_pendingMessages, "f")[__classPrivateFieldGet(this, _Channel_nextMessageIndex, "f")];
__classPrivateFieldSet(this, _Channel_nextMessageIndex, __classPrivateFieldGet(this, _Channel_nextMessageIndex, "f") + 1, "f");
}
if (__classPrivateFieldGet(this, _Channel_nextMessageIndex, "f") === __classPrivateFieldGet(this, _Channel_messageEndIndex, "f")) {
this.cleanupCallback();
}
} else {
__classPrivateFieldGet(this, _Channel_pendingMessages, "f")[index] = message;
}
});
}
cleanupCallback() {
window.__TAURI_INTERNALS__.unregisterCallback(this.id);
}
set onmessage(handler) {
__classPrivateFieldSet(this, _Channel_onmessage, handler, "f");
}
get onmessage() {
return __classPrivateFieldGet(this, _Channel_onmessage, "f");
}
[(_Channel_onmessage = new WeakMap(), _Channel_nextMessageIndex = new WeakMap(), _Channel_pendingMessages = new WeakMap(), _Channel_messageEndIndex = new WeakMap(), _Channel_instances = new WeakSet(), _Channel_deliver = function _Channel_deliver2(message) {
try {
__classPrivateFieldGet(this, _Channel_onmessage, "f").call(this, message);
} catch (error) {
queueMicrotask(() => {
throw error;
});
}
}, SERIALIZE_TO_IPC_FN)]() {
return `__CHANNEL__:${this.id}`;
}
toJSON() {
return this[SERIALIZE_TO_IPC_FN]();
}
};
async function invoke(cmd, args = {}, options) {
return window.__TAURI_INTERNALS__.invoke(cmd, args, options);
}
var Resource = class {
get rid() {
return __classPrivateFieldGet(this, _Resource_rid, "f");
}
constructor(rid) {
_Resource_rid.set(this, void 0);
__classPrivateFieldSet(this, _Resource_rid, rid, "f");
}
async close() {
return invoke("plugin:resources|close", {
rid: this.rid
});
}
async [(_Resource_rid = new WeakMap(), Symbol.asyncDispose)]() {
await this.close();
}
};
var IMAGE_KINDS = new Set(["image", "ink"]);
var IMAGE_SCALE = 150 / 72;
function toEntry(a) {
const { id, type, pageLabel, sortIndex, image, onlyTextOrComment, readOnly, position, ...rest } = a;
const entry = {
id,
kind: type,
page: pageLabel || String(position.pageIndex + 1),
color: rest.color,
text: rest.text,
comment: rest.comment,
image: IMAGE_KINDS.has(type) ? `img/${id}.png` : null,
source: "reader",
sort_index: sortIndex,
...rest,
position
};
for (const [key, value] of Object.entries(entry)) {
if (value === void 0 || value === "" || Array.isArray(value) && !value.length) {
entry[key] = null;
}
}
return entry;
}
function fromEntry(e) {
const { kind, page, sort_index, image, ...rest } = e;
return {
text: "",
comment: "",
tags: [],
...rest,
type: kind,
pageLabel: page,
sortIndex: sort_index,
readOnly: rest.source === "pdf"
};
}
var imageKey = (a) => JSON.stringify([a.position, a.color]);
var AnnotationStore = class {
rendered = new Map();
async load() {
const annotations = (await invoke("load_annotations")).map(fromEntry);
await Promise.all(annotations.filter((a) => IMAGE_KINDS.has(a.type)).map(async (a) => {
try {
a.image = await dataUrl(await invoke("read_image", { id: a.id }));
this.rendered.set(a.id, imageKey(a));
} catch {
}
}));
return annotations;
}
async save(annotations, showImage) {
await invoke("save_annotations", { annotations: annotations.map(toEntry) });
for (const a of annotations.filter((a2) => IMAGE_KINDS.has(a2.type))) {
if (this.rendered.get(a.id) !== imageKey(a)) {
showImage({ ...a, image: await this.renderImage(a) });
}
}
}
async renderMissing(annotations, showImage) {
for (const a of annotations.filter((a2) => IMAGE_KINDS.has(a2.type) && !this.rendered.has(a2.id))) {
showImage({ ...a, image: await this.renderImage(a) });
}
}
async delete(ids) {
await invoke("delete_annotations", { ids });
ids.forEach((id) => this.rendered.delete(id));
}
async renderImage(a) {
const canvas = await renderRegion(a);
const png = await new Promise((resolve, reject) => {
canvas.toBlob((b) => b ? resolve(b) : reject(new Error("PNG encoding failed")), "image/png");
});
await invoke("save_image", new Uint8Array(await png.arrayBuffer()), { headers: { id: a.id } });
this.rendered.set(a.id, imageKey(a));
return canvas.toDataURL("image/png");
}
};
async function renderRegion(a) {
const doc = window.PDFViewerApplication?.pdfDocument;
if (!doc) throw new Error("PDF not loaded");
const page = await doc.getPage(a.position.pageIndex + 1);
const viewport = page.getViewport({ scale: IMAGE_SCALE });
const iframe = document.querySelector("#primary-view iframe");
const full = (iframe?.contentDocument ?? document).createElement("canvas");
full.width = Math.ceil(viewport.width);
full.height = Math.ceil(viewport.height);
const ctx = full.getContext("2d");
ctx.skipBlender = true;
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, full.width, full.height);
await page.render({ canvasContext: ctx, viewport }).promise;
const { paths, width = 1 } = a.position;
if (paths) {
ctx.lineCap = ctx.lineJoin = "round";
ctx.lineWidth = width * IMAGE_SCALE;
ctx.strokeStyle = a.color ?? "#000000";
for (const path of paths) {
ctx.beginPath();
for (let i = 0; i < path.length; i += 2) {
ctx.lineTo(...viewport.convertToViewportPoint(path[i], path[i + 1]));
}
ctx.stroke();
}
}
const [x1, y1, x2, y2] = viewport.convertToViewportRectangle(pdfRect(a.position));
const x = Math.max(0, Math.floor(Math.min(x1, x2)));
const y = Math.max(0, Math.floor(Math.min(y1, y2)));
const crop = document.createElement("canvas");
crop.width = Math.max(1, Math.min(full.width, Math.ceil(Math.max(x1, x2))) - x);
crop.height = Math.max(1, Math.min(full.height, Math.ceil(Math.max(y1, y2))) - y);
crop.getContext("2d").drawImage(full, x, y, crop.width, crop.height, 0, 0, crop.width, crop.height);
return crop;
}
function pdfRect({ rects, paths, width = 1 }) {
if (rects?.length) return rects[0];
const xs = paths.flatMap((p) => p.filter((_, i) => i % 2 === 0));
const ys = paths.flatMap((p) => p.filter((_, i) => i % 2 === 1));
return [Math.min(...xs) - width, Math.min(...ys) - width, Math.max(...xs) + width, Math.max(...ys) + width];
}
function dataUrl(buf) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsDataURL(new Blob([buf], { type: "image/png" }));
});
}
var EINK_KEY = "yeda.eink";
var PALM_AFTER_PEN_MS = 600;
var PALM_AREA = 5e3;
var SWIPE = 60;
var DEFAULT_ERASER = 16;
var PEN_ERASER = 40;
var ERASE_STEP = 6;
var PAGE_SCROLL_MODE = 3;
var einkMode = () => localStorage.getItem(EINK_KEY) === "1";
var erasing = (e) => (e.buttons & 34) !== 0 || e.button === 5;
var NO_MOTION = "*, *::before, *::after { transition: none !important; animation: none !important; scroll-behavior: auto !important; }";
var BIG_TARGETS = ".toolbar button, .toolbar .toolbar-button { min-width: 48px; min-height: 48px; }";
function addStyle(doc, css) {
doc.head.append(Object.assign(doc.createElement("style"), { textContent: css }));
}
function installPen(reader) {
const view = reader._primaryView;
if (!view) return;
const eink = einkMode();
if (eink) {
addStyle(document, NO_MOTION + BIG_TARGETS);
if (reader._tools.ink) reader._tools.ink.color = "#000000";
}
if (eink) {
const setTool = view.setTool.bind(view);
view.setTool = (tool) => {
setTool(tool);
const container = view._iframeWindow?.document.getElementById("viewerContainer");
if (container) container.style.touchAction = "none";
};
}
const down = view._handlePointerDown.bind(view);
const move = view._handlePointerMove;
const up = view._handlePointerUp.bind(view);
let penSeen = false;
let penDown = false;
let penUpAt = 0;
let lastPen = null;
let stroke = null;
const startStroke = (e) => {
const v = viewer();
const win = view._iframeWindow;
const pageIndex = view.action?.annotation?.position.pageIndex;
const page = pageIndex === void 0 ? void 0 : v?._pages[pageIndex]?.div;
const ink = reader._tools.ink;
if (!v || !win || !page || !ink) return;
const canvas = win.document.createElement("canvas");
const dpr = win.devicePixelRatio;
Object.assign(canvas.style, { position: "absolute", left: "0", top: "0", width: "100%", height: "100%", pointerEvents: "none", zIndex: "100" });
canvas.width = page.clientWidth * dpr;
canvas.height = page.clientHeight * dpr;
page.append(canvas);
const ctx = canvas.getContext("2d");
ctx.scale(dpr, dpr);
ctx.strokeStyle = ink.color ?? "#000000";
ctx.lineWidth = (ink.size ?? 2) * v.currentScale * 96 / 72;
ctx.lineCap = ctx.lineJoin = "round";
const r = page.getBoundingClientRect();
ctx.beginPath();
ctx.moveTo(e.clientX - r.left, e.clientY - r.top);
stroke = { canvas, ctx, page, render: view._render };
view._render = () => {
};
};
const endStroke = () => {
if (!stroke) return;
const { canvas, render } = stroke;
view._render = render;
stroke = null;
requestAnimationFrame(() => requestAnimationFrame(() => canvas.remove()));
};
let restoreTool = null;
const touches = new Map();
let pinch = 0;
let moved = false;
const viewer = () => view._iframeWindow?.PDFViewerApplication.pdfViewer;
const drawing = () => !["pointer", "hand"].includes(view._tool.type);
const ours = (e) => e.pointerType === "touch" && (eink || penSeen && drawing());
const zoomed = (v) => {
const page = v._pages[v.currentPageNumber - 1]?.div;
return !!page && (page.offsetWidth > v.container.clientWidth + 2 || page.offsetHeight > v.container.clientHeight + 2);
};
const palm = (e) => penDown || performance.now() - penUpAt < PALM_AFTER_PEN_MS || e.width * e.height > PALM_AREA;
const spread = () => {
const [a, b] = [...touches.values()];
return a && b ? Math.hypot(a.x - b.x, a.y - b.y) : 0;
};
view._handlePointerDown = (e) => {
if (e.pointerType === "pen") {
endStroke();
if (!penSeen && reader._tools.eraser?.size === DEFAULT_ERASER) reader._tools.eraser.size = PEN_ERASER;
penSeen = penDown = true;
lastPen = e;
if (erasing(e) && view._tool.type !== "eraser") {
restoreTool = reader._state.tool.type;
reader.setTool({ type: "eraser" });
view.setTool(reader._tools.eraser);
}
} else if (ours(e)) {
if (!palm(e)) {
if (touches.size === 0) moved = false;
touches.set(e.pointerId, { x: e.clientX, y: e.clientY, start: e });
pinch = touches.size === 2 ? spread() : 0;
}
return;
}
down(e);
if (e.pointerType === "pen" && view.action?.annotation && view._tool.type === "ink") startStroke(e);
};
view._handlePointerMove = (e) => {
const t = touches.get(e.pointerId);
if (t) {
const [dx, dy] = [e.clientX - t.x, e.clientY - t.y];
t.x = e.clientX;
t.y = e.clientY;
const v = viewer();
if (!v) return;
if (touches.size === 2) {
const now = spread();
if (pinch && Math.abs(now / pinch - 1) > 0.08) {
v.currentScale *= now / pinch;
pinch = now;
moved = true;
}
} else if (!eink || zoomed(v)) {
v.container.scrollBy(-dx, -dy);
moved ||= Math.hypot(e.clientX - t.start.clientX, e.clientY - t.start.clientY) > 8;
}
return;
}
if (ours(e)) return;
if (e.pointerType === "pen" && e.buttons && ["ink", "eraser"].includes(view._tool.type)) {
const points = e.getCoalescedEvents?.() ?? [];
const render = view._render;
view._render = () => {
};
const page = stroke?.page.getBoundingClientRect();
try {
for (const p of [...points.slice(0, -1), e]) {
if (view._tool.type === "eraser" && lastPen) {
const steps = Math.floor(Math.hypot(p.clientX - lastPen.clientX, p.clientY - lastPen.clientY) / ERASE_STEP);
for (let i = 1; i < steps; i++) {
const f = i / steps;
move(new PointerEvent("pointermove", {
pointerId: p.pointerId,
pointerType: "pen",
buttons: p.buttons,
clientX: lastPen.clientX + (p.clientX - lastPen.clientX) * f,
clientY: lastPen.clientY + (p.clientY - lastPen.clientY) * f
}));
}
}
if (p === e && !stroke) view._render = render;
move(p);
lastPen = p;
if (stroke && page) stroke.ctx.lineTo(p.clientX - page.left, p.clientY - page.top);
}
if (stroke && page) {
const { ctx } = stroke;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(e.clientX - page.left, e.clientY - page.top);
}
} finally {
if (!stroke) view._render = render;
}
return;
}
move(e);
};
view._handlePointerUp = (e) => {
const t = touches.get(e.pointerId);
if (t) {
const single = touches.size === 1;
touches.delete(e.pointerId);
const [dx, dy] = [e.clientX - t.start.clientX, e.clientY - t.start.clientY];
const dominant = Math.abs(dx) > Math.abs(dy) ? dx : dy;
if (single && !moved) {
if (eink && Math.abs(dominant) > SWIPE) {
if (dominant < 0) reader.navigateToNextPage();
else reader.navigateToPreviousPage();
} else if (Math.abs(dominant) <= SWIPE && !drawing()) {
down(t.start);
up(e);
}
}
if (touches.size < 2) pinch = 0;
return;
}
if (ours(e)) return;
if (e.pointerType === "pen" && stroke) view._render = stroke.render;
up(e);
if (e.pointerType === "pen") {
endStroke();
penDown = false;
penUpAt = performance.now();
if (restoreTool) {
reader.setTool({ type: restoreTool });
view.setTool(reader._tools[restoreTool]);
restoreTool = null;
}
}
};
}
async function settleEink(reader) {
const view = reader._primaryView;
if (!einkMode() || !view) return;
await view.initializedPromise;
const win = view._iframeWindow;
if (!win) return;
addStyle(win.document, NO_MOTION);
const container = win.document.getElementById("viewerContainer");
if (container) container.style.touchAction = "none";
const v = win.PDFViewerApplication.pdfViewer;
v.scrollMode = PAGE_SCROLL_MODE;
v.currentScaleValue = "page-fit";
}
var TOOLS = ["highlight", "underline", "note", "text", "image", "ink", "eraser"];
var LINE = 60;
function isTextInput(target) {
const node = target;
return !!node && (node.isContentEditable || ["INPUT", "TEXTAREA", "SELECT"].includes(node.nodeName));
}
function scrollContainer() {
return window.PDFViewerApplication?.pdfViewer?.container;
}
function installVim(getReader) {
let pending = "";
let count = "";
const toggleSidebarView = (reader, view) => {
const { sidebarOpen, sidebarView } = reader._state;
if (sidebarOpen && sidebarView === view) {
reader.toggleSidebar(false);
} else {
reader.setSidebarView(view);
reader.toggleSidebar(true);
}
};
const editComment = (reader) => {
const [id] = reader._state.selectedAnnotationIDs;
if (!id) return;
reader.setSidebarView("annotations");
reader.toggleSidebar(true);
requestAnimationFrame(() => {
const editor = document.querySelector(
`[data-sidebar-annotation-id="${id}"] .comment [contenteditable="true"]`
);
if (!editor) return;
editor.focus();
const range = document.createRange();
range.selectNodeContents(editor);
range.collapse(false);
window.getSelection()?.removeAllRanges();
window.getSelection()?.addRange(range);
});
};
const handle = (event) => {
const reader = getReader();
if (!reader || event.altKey || event.metaKey) return false;
if (isTextInput(event.target)) {
if (event.key === "Enter" && !event.shiftKey && event.target.closest(".find-popup")) {
setTimeout(() => reader.focusView());
}
return false;
}
if (["Shift", "Control", "Alt", "Meta"].includes(event.key)) return false;
const key = event.ctrlKey ? `C-${event.key}` : event.key;
if (/^[0-9]$/.test(key) && !pending && (count || key !== "0")) {
count += key;
return true;
}
const n = Number(count);
count = "";
if (key === "G" && n) {
reader.navigate({ pageIndex: n - 1 });
return true;
}
if (key === "t") {
if (TOOLS[n - 1]) reader.setTool({ type: TOOLS[n - 1] });
return true;
}
const seq = pending + key;
pending = "";
switch (seq) {
case "j":
scrollContainer()?.scrollBy({ top: LINE });
return true;
case "k":
scrollContainer()?.scrollBy({ top: -LINE });
return true;
case "C-d":
case "C-u": {
const container = scrollContainer();
container?.scrollBy({ top: (key === "C-d" ? 0.5 : -0.5) * container.clientHeight });
return true;
}
case "J":
reader.navigateToNextPage();
return true;
case "K":
reader.navigateToPreviousPage();
return true;
case " ":
if (event.shiftKey) reader.navigateToPreviousPage();
else reader.navigateToNextPage();
return true;
case "gg":
reader.navigateToFirstPage();
return true;
case "G":
reader.navigateToLastPage();
return true;
case "/":
reader.toggleFindPopup({ open: true });
return true;
case "n":
reader.findNext();
return true;
case "N":
reader.findPrevious();
return true;
case "+":
reader.zoomIn();
return true;
case "-":
reader.zoomOut();
return true;
case "=":
reader.zoomReset();
return true;
case "o":
toggleSidebarView(reader, "outline");
return true;
case "a":
toggleSidebarView(reader, "annotations");
return true;
case "dd":
reader.deleteAnnotations(reader._state.selectedAnnotationIDs);
return true;
case "c":
editComment(reader);
return true;
case "y":
reader.copy();
return true;
case "Escape":
reader.setTool({ type: "pointer" });
return false;
case "g":
case "d":
pending = seq;
return true;
}
return false;
};
window.addEventListener("keydown", (event) => {
if (handle(event)) {
event.preventDefault();
event.stopImmediatePropagation();
}
}, true);
return (reader) => {
const keyboard = reader._keyboardManager;
const original = keyboard.handleViewKeyDown.bind(keyboard);
keyboard.handleViewKeyDown = (event) => {
if (handle(event)) {
event.preventDefault();
event.stopPropagation();
return;
}
original(event);
};
};
}
var noop = () => {
};
var requiredCallbacks = Object.fromEntries([
"onAddToNote",
"onChangeSidebarView",
"onChangeSidebarWidth",
"onChangeViewState",
"onDeletePages",
"onOpenLink",
"onOpenReadAloudFirstRunPopup",
"onReadAloudEngineStateChanged",
"onRotatePages",
"onSaveCustomThemes",
"onSetDarkTheme",
"onSetDataTransferAnnotations",
"onSetLightTheme",
"onSetReadAloudVoice",
"onSetZoom",
"onTextSelectionAnnotationModeChange",
"onToggleSidebar"
].map((name) => [name, noop]));
if (!("speechSynthesis" in window)) {
Object.defineProperty(window, "speechSynthesis", {
value: Object.assign(new EventTarget(), { getVoices: () => [], speak: noop, cancel: noop })
});
}
function reportError(action) {
return (e) => {
console.error(e);
window.alert(`${action} failed: ${e}`);
};
}
async function openReader() {
let reader;
const hookVimIntoView = installVim(() => reader);
const store = new AnnotationStore();
const launch = await invoke("launch");
if (!launch) return false;
const buf = new Uint8Array(await invoke("read_pdf"));
const annotations = await store.load().catch((e) => {
reportError("Loading annotations")(e);
return [];
});
const showImage = (a) => reader.setAnnotations([a]);
document.title = launch.title;
document.head.append(Object.assign(document.createElement("style"), {
textContent: "@media (max-width: 799px) { .toolbar { overflow-x: auto; scrollbar-width: none; } .toolbar > * { flex-shrink: 0; } .toolbar #numPages div { position: static; } }"
}));
reader = window.createReader({
...requiredCallbacks,
type: "pdf",
data: { buf },
title: launch.title,
annotations,
primaryViewState: launch.page ? { pageIndex: launch.page - 1 } : void 0,
readOnly: false,
showAnnotations: true,
sidebarOpen: window.innerWidth >= 800,
sidebarWidth: 240,
sidebarView: "annotations",
authorName: "",
onConfirm: (title, text) => window.confirm(`${title}
${text}`),
onSaveAnnotations: (changed) => store.save(changed, showImage).catch(reportError("Saving annotations")),
onDeleteAnnotations: (ids) => store.delete(ids).catch(reportError("Deleting annotations"))
});
hookVimIntoView(reader);
installPen(reader);
settleEink(reader).catch(reportError("Setting up e-ink mode"));
reader.initializedPromise.then(() => store.renderMissing(annotations, showImage)).catch(reportError("Rendering annotation images"));
return true;
}
await openReader();