diff --git a/CHANGELOG.md b/CHANGELOG.md
index a367b75..85c9613 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,15 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
---
+## [1.26.12] — 2026-09-10
+
+### Fixed
+- **Page-Wide PDF Drag-and-Drop Interception & Dropzone Navigation Hook.**
+ - Installed an immediate, eager `window` drag-and-drop safety net (`installGlobalMagDropGuard()`) that prevents dropping a PDF anywhere on the page from opening the PDF in the browser tab.
+ - Added `window.magOnShow()` navigation hook in `nav.js` and guaranteed `setupMagDropzone()` wires the `#mag-dropzone` target whenever the Magazine section is visited or mounted, stopping `magInit()` from exiting silently if the DOM element was not yet present at script load time.
+
+---
+
## [1.26.11] — 2026-09-10
### Added
diff --git a/VERSION b/VERSION
index fabf663..b74a856 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.26.11
+1.26.12
diff --git a/static/index.html b/static/index.html
index c9acda3..70ab96b 100644
--- a/static/index.html
+++ b/static/index.html
@@ -10,7 +10,7 @@
-
+
@@ -27,12 +27,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/static/js/magazine.js b/static/js/magazine.js
index e7bf8e7..0668fee 100644
--- a/static/js/magazine.js
+++ b/static/js/magazine.js
@@ -210,25 +210,25 @@ function magShowDroppedFile(file) {
}
}
-let _magDropGuardInstalled = false;
+function installGlobalMagDropGuard() {
+ if (window._magDropGuardInstalled) return;
+ window._magDropGuardInstalled = true;
+ window.addEventListener('dragover', (e) => {
+ e.preventDefault();
+ if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy';
+ });
+ window.addEventListener('drop', (e) => {
+ e.preventDefault();
+ });
+}
+installGlobalMagDropGuard();
function setupMagDropzone() {
+ installGlobalMagDropGuard();
const zone = $('mag-dropzone');
const input = $('mag-new-file');
if (!zone || !input) return;
- // A page-wide safety net: if a drop ever lands outside the zone (or the
- // zone's own handler below fails to intercept it for any reason), the
- // browser's native behaviour is to navigate to/open the dropped file --
- // confirmed live as exactly what happened before this existed. Installed
- // once regardless of which section is active, since a stray drop can land
- // anywhere on the page.
- if (!_magDropGuardInstalled) {
- _magDropGuardInstalled = true;
- window.addEventListener('dragover', (e) => { e.preventDefault(); });
- window.addEventListener('drop', (e) => { e.preventDefault(); });
- }
-
if (zone.dataset.magDropzoneWired) return;
zone.dataset.magDropzoneWired = '1';
@@ -242,12 +242,22 @@ function setupMagDropzone() {
// dragenter AND dragover both need preventDefault() -- some browsers only
// arm the element as a valid drop target once dragenter itself has been
// cancelled, not just the dragover events that follow it.
- const armDropTarget = (e) => { e.preventDefault(); zone.classList.add('dragover'); };
+ const armDropTarget = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy';
+ zone.classList.add('dragover');
+ };
zone.addEventListener('dragenter', armDropTarget);
zone.addEventListener('dragover', armDropTarget);
- zone.addEventListener('dragleave', () => zone.classList.remove('dragover'));
+ zone.addEventListener('dragleave', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ zone.classList.remove('dragover');
+ });
zone.addEventListener('drop', (e) => {
e.preventDefault();
+ e.stopPropagation();
zone.classList.remove('dragover');
const file = e.dataTransfer?.files?.[0];
if (!file) return;
@@ -1750,12 +1760,21 @@ function syncDrivingPlayButton() {
// ── Boot ─────────────────────────────────────────────────────────────────────
+async function magOnShow() {
+ setupMagDropzone();
+ setupMagIngestPreviewResize();
+ if ($('mag-library-list')) {
+ magLoadLibrary();
+ magLoadVoices();
+ }
+}
+
async function magInit() {
+ setupMagDropzone();
+ setupMagIngestPreviewResize();
if (!$('mag-library-list')) return; // section not mounted
magLoadLibrary();
magLoadVoices();
- setupMagDropzone();
- setupMagIngestPreviewResize();
let lastId = null;
try { lastId = localStorage.getItem(MAG_LAST_DOC_KEY); } catch (_) {}
if (lastId) {
@@ -1775,6 +1794,7 @@ async function magInit() {
magShow('library');
}
+root.magOnShow = magOnShow;
root.magOpen = magOpen;
root.magDelete = magDelete;
root.magCreate = magCreate;
diff --git a/static/nav.js b/static/nav.js
index 883673a..1f8272f 100644
--- a/static/nav.js
+++ b/static/nav.js
@@ -179,6 +179,7 @@
}
if (sectionId === 's-library' && typeof window.libraryRender === 'function') window.libraryRender(window._libraryView || 'books');
if (sectionId === 's-caststudio' && typeof window.studioOnShow === 'function') window.studioOnShow();
+ if (sectionId === 's-magazine' && typeof window.magOnShow === 'function') window.magOnShow();
if (typeof window.initCollapsibleCards === 'function') window.initCollapsibleCards();
setStoredSection(sectionId);
setSectionHash(sectionId);