diff --git a/paperforge/plugin/main.js b/paperforge/plugin/main.js
index df669e04..8e7ba428 100644
--- a/paperforge/plugin/main.js
+++ b/paperforge/plugin/main.js
@@ -603,6 +603,9 @@ function deleteLocalAnnotation(vaultPath, annotationId) {
let _overlayInstalled = false;
let _currentVaultPath = null;
let _currentPdfPath = null;
+var _pdfNavStack = [];
+var _pdfNavPos = -1;
+var _navProgrammatic = false;
function detectConflictingPdfPlugins(app) {
try {
@@ -696,6 +699,9 @@ function injectPdfEventHooks(containerEl, view, vaultPath, pdfPath, plugin) {
_currentContainerEl = containerEl;
_pdfInternalHandle = null;
_pdfEventSubscriptions = [];
+ _pdfNavStack = [];
+ _pdfNavPos = -1;
+ _navProgrammatic = false;
_invalidatePdfPageCaches();
var anns = fetchAnnotationsForPaper(vaultPath, pdfPath);
// Try resolving PDF.js internal viewer handle
@@ -765,16 +771,106 @@ function _subscribePdfEvents(handle, pdfPath, vaultPath, plugin) {
_rebuildVisibleLayers(handle, pdfPath, vaultPath);
}, 80);
}
+ var _lastNavPage = null;
+ function onPageChanging(evt) {
+ if (_navProgrammatic) { _navProgrammatic = false; return; }
+ if (_lastNavPage !== null && Math.abs(evt.pageNumber - _lastNavPage) > 1) {
+ _pushPdfNav(_lastNavPage);
+ }
+ _lastNavPage = evt.pageNumber;
+ }
bus.on('pagerendered', onPageRendered);
bus.on('scalechanged', onScaleChanged);
+ bus.on('pagechanging', onPageChanging);
if (plugin && typeof plugin.register === 'function') {
plugin.register(function () {
try { bus.off('pagerendered', onPageRendered); } catch {}
try { bus.off('scalechanged', onScaleChanged); } catch {}
+ try { bus.off('pagechanging', onPageChanging); } catch {}
});
}
}
+/* ── PDF back/forward navigation ── */
+function _pushPdfNav(pageNumber) {
+ if (_pdfNavPos < _pdfNavStack.length - 1) {
+ _pdfNavStack = _pdfNavStack.slice(0, _pdfNavPos + 1);
+ }
+ _pdfNavStack.push(pageNumber);
+ if (_pdfNavStack.length > 100) _pdfNavStack.shift();
+ _pdfNavPos = _pdfNavStack.length - 1;
+ _renderPdfNavButtons();
+}
+
+function _renderPdfNavButtons() {
+ var c = document.querySelector('.pdf-viewer-container');
+ if (!c) return;
+ var old = c.querySelectorAll('.pf-nav-btn');
+ for (var oi = 0; oi < old.length; oi++) old[oi].remove();
+ if (_pdfNavPos < 0) return;
+ if (_pdfNavPos > 0) {
+ var bk = document.createElement('button');
+ bk.className = 'pf-nav-btn pf-nav-back';
+ bk.textContent = '\u25C0';
+ bk.title = 'Go back to page ' + _pdfNavStack[_pdfNavPos - 1];
+ bk.addEventListener('click', function () {
+ if (_pdfNavPos <= 0 || !_pdfInternalHandle) return;
+ _navProgrammatic = true;
+ _pdfNavPos--;
+ _pdfInternalHandle.scrollPageIntoView({ pageNumber: _pdfNavStack[_pdfNavPos] });
+ _renderPdfNavButtons();
+ });
+ c.appendChild(bk);
+ }
+ if (_pdfNavPos < _pdfNavStack.length - 1) {
+ var fw = document.createElement('button');
+ fw.className = 'pf-nav-btn pf-nav-forward';
+ fw.textContent = '\u25B6';
+ fw.title = 'Go forward to page ' + _pdfNavStack[_pdfNavPos + 1];
+ fw.addEventListener('click', function () {
+ if (_pdfNavPos >= _pdfNavStack.length - 1 || !_pdfInternalHandle) return;
+ _navProgrammatic = true;
+ _pdfNavPos++;
+ _pdfInternalHandle.scrollPageIntoView({ pageNumber: _pdfNavStack[_pdfNavPos] });
+ _renderPdfNavButtons();
+ });
+ c.appendChild(fw);
+ }
+}
+/* ── Undo pill on delete ── */
+var _undoPillEl = null;
+var _undoTimer = null;
+
+function _showUndoPill(undoEntry) {
+ if (_undoPillEl) _undoPillEl.remove();
+ if (_undoTimer) clearTimeout(_undoTimer);
+ _undoPillEl = document.createElement('div');
+ _undoPillEl.className = 'pf-undo-pill';
+ _undoPillEl.innerHTML = 'Annotation deleted ';
+ _undoPillEl.querySelector('button').addEventListener('click', function () {
+ _appendAnnotationToMemory(undoEntry.annotation);
+ _renderAnnotationToPage(undoEntry.annotation);
+ createLocalAnnotation(undoEntry.vaultPath, undoEntry.pdfPath, {
+ type: undoEntry.annotation.type,
+ page_index: undoEntry.annotation.page_index,
+ page_label: undoEntry.annotation.page_label || '',
+ selected_text: undoEntry.annotation.selected_text || '',
+ comment: undoEntry.annotation.comment || '',
+ color: undoEntry.annotation.color || '#ffd400',
+ position_json: JSON.stringify(undoEntry.annotation.position),
+ }).then(function () {
+ _scheduleAnnotationCacheFlush(undoEntry.vaultPath);
+ });
+ _undoPillEl.remove();
+ _undoPillEl = null;
+ });
+ var c = document.querySelector('.pdf-viewer-container');
+ if (c) c.appendChild(_undoPillEl);
+ _undoTimer = setTimeout(function () {
+ if (_undoPillEl) { _undoPillEl.remove(); _undoPillEl = null; }
+ }, 8000);
+}
+
function _getOrCreateAlignedLayer(pageView) {
var pageDiv = pageView.div;
if (!pageDiv) return null;
@@ -1296,7 +1392,11 @@ function showAnnotationPopover(event, ann, rectEl, vaultPath, pdfPath, container
delBtn.className = 'pf-annotation-popover-btn pf-annotation-popover-btn--danger';
delBtn.textContent = 'Delete';
delBtn.addEventListener('click', function () {
- if (!confirm('Delete this annotation?')) return;
+ _showUndoPill({
+ annotation: JSON.parse(JSON.stringify(ann)),
+ vaultPath: _currentVaultPath,
+ pdfPath: _currentPdfPath,
+ });
_removeAnnotationRectsFromDom(ann.id);
_removeAnnotationFromMemory(ann.id);
deleteLocalAnnotation(_currentVaultPath, ann.id).then(function (result) {
diff --git a/paperforge/plugin/styles.css b/paperforge/plugin/styles.css
index 30e52359..bfe588f7 100644
--- a/paperforge/plugin/styles.css
+++ b/paperforge/plugin/styles.css
@@ -2743,3 +2743,32 @@
background: var(--text-error);
color: var(--text-on-accent);
}
+
+
+/* $- PDF back/forward nav buttons -/
+.pf-nav-btn {
+ position: absolute; top: 8px; z-index: 100;
+ background: var(--background-primary);
+ border: 1px solid var(--background-modifier-border);
+ border-radius: 4px; color: var(--text-muted);
+ cursor: pointer; padding: 2px 8px; font-size: 16px;
+ line-height: 1.4; opacity: 0.5; transition: opacity 0.15s;
+}
+.pf-nav-btn:hover { opacity: 1; }
+.pf-nav-back { left: 8px; }
+.pf-nav-forward { left: 44px; }
+
+
+/* $- Undo pill -/
+.pf-undo-pill {
+ position: absolute; bottom: 12px; left: 50%; transform: translateX(-50%);
+ z-index: 200; background: var(--background-primary);
+ border: 1px solid var(--background-modifier-border);
+ border-radius: 6px; padding: 6px 14px; font-size: 13px;
+ display: flex; gap: 12px; align-items: center;
+ box-shadow: 0 2px 8px rgba(0,0,0,0.15);
+}
+.pf-undo-pill button {
+ background: none; border: none; color: var(--text-accent);
+ cursor: pointer; font-weight: 600; padding: 0;
+}