+
+ Drop a .md file anywhere to open it
+
diff --git a/script.js b/script.js
index 94057ea..39b2f76 100644
--- a/script.js
+++ b/script.js
@@ -20,8 +20,7 @@ document.addEventListener("DOMContentLoaded", function () {
const exportHtml = document.getElementById("export-html");
const exportPdf = document.getElementById("export-pdf");
const copyMarkdownButton = document.getElementById("copy-markdown-button");
- const dropzone = document.getElementById("dropzone");
- const closeDropzoneBtn = document.getElementById("close-dropzone");
+ const dragOverlay = document.getElementById("drag-overlay");
const toggleSyncButton = document.getElementById("toggle-sync");
const editorPane = document.getElementById("markdown-editor");
const previewPane = document.querySelector(".preview-pane");
@@ -976,7 +975,6 @@ This is a fully client-side application. Your content never leaves your browser
const reader = new FileReader();
reader.onload = function(e) {
newTab(e.target.result, file.name.replace(/\.md$/i, ''));
- dropzone.style.display = "none";
};
reader.readAsText(file);
}
@@ -2776,44 +2774,40 @@ This is a fully client-side application. Your content never leaves your browser
loadFromShareHash();
- const dropEvents = ["dragenter", "dragover", "dragleave", "drop"];
+ // Full-window drag-and-drop: track nesting level for reliable enter/leave detection
+ let dragDepth = 0;
- dropEvents.forEach((eventName) => {
- dropzone.addEventListener(eventName, preventDefaults, false);
- document.body.addEventListener(eventName, preventDefaults, false);
- });
-
- function preventDefaults(e) {
- e.preventDefault();
- e.stopPropagation();
- }
-
- ["dragenter", "dragover"].forEach((eventName) => {
- dropzone.addEventListener(eventName, highlight, false);
- });
-
- ["dragleave", "drop"].forEach((eventName) => {
- dropzone.addEventListener(eventName, unhighlight, false);
- });
-
- function highlight() {
- dropzone.classList.add("active");
- }
+ document.addEventListener("dragenter", function(e) {
+ if (e.dataTransfer && e.dataTransfer.types && e.dataTransfer.types.includes("Files")) {
+ e.preventDefault();
+ dragDepth++;
+ dragOverlay.classList.add("active");
+ dragOverlay.setAttribute("aria-hidden", "false");
+ }
+ }, false);
- function unhighlight() {
- dropzone.classList.remove("active");
- }
+ document.addEventListener("dragover", function(e) {
+ if (e.dataTransfer && e.dataTransfer.types && e.dataTransfer.types.includes("Files")) {
+ e.preventDefault();
+ }
+ }, false);
- dropzone.addEventListener("drop", handleDrop, false);
- dropzone.addEventListener("click", function (e) {
- if (e.target !== closeDropzoneBtn && !closeDropzoneBtn.contains(e.target)) {
- fileInput.click();
+ document.addEventListener("dragleave", function(e) {
+ dragDepth--;
+ if (dragDepth <= 0) {
+ dragDepth = 0;
+ dragOverlay.classList.remove("active");
+ dragOverlay.setAttribute("aria-hidden", "true");
}
- });
- closeDropzoneBtn.addEventListener("click", function(e) {
- e.stopPropagation();
- dropzone.style.display = "none";
- });
+ }, false);
+
+ document.addEventListener("drop", function(e) {
+ e.preventDefault();
+ dragDepth = 0;
+ dragOverlay.classList.remove("active");
+ dragOverlay.setAttribute("aria-hidden", "true");
+ handleDrop(e);
+ }, false);
function handleDrop(e) {
const dt = e.dataTransfer;
diff --git a/styles.css b/styles.css
index a71467f..99c4713 100644
--- a/styles.css
+++ b/styles.css
@@ -9,7 +9,6 @@
--button-bg: #f6f8fa;
--button-hover: #e1e4e8;
--button-active: #d1d5da;
- --dropzone-bg: rgba(255, 255, 255, 0.8);
--scrollbar-thumb: #c1c1c1;
--scrollbar-track: #f1f1f1;
--accent-color: #0366d6;
@@ -28,7 +27,6 @@
--button-bg: #21262d;
--button-hover: #30363d;
--button-active: #3b434b;
- --dropzone-bg: rgba(13, 17, 23, 0.8);
--scrollbar-thumb: #484f58;
--scrollbar-track: #21262d;
--accent-color: #58a6ff;
@@ -315,28 +313,73 @@ body {
display: none;
}
-.dropzone {
- border: 2px dashed var(--border-color);
- border-radius: 6px;
- padding: 20px;
+/* Drag overlay: full-screen drop target shown when user drags a file over the window */
+.drag-overlay {
+ display: none;
+ position: fixed;
+ inset: 0;
+ z-index: 9999;
+ background-color: rgba(0, 0, 0, 0.45);
+ pointer-events: none;
+ align-items: center;
+ justify-content: center;
+}
+
+.drag-overlay.active {
+ display: flex;
+ pointer-events: auto;
+}
+
+.drag-overlay-inner {
+ border: 3px dashed var(--accent-color);
+ border-radius: 12px;
+ padding: 48px 64px;
text-align: center;
- margin-bottom: 20px;
- cursor: pointer;
- transition: all 0.3s ease;
- background-color: var(--dropzone-bg);
+ color: #ffffff;
+ background-color: rgba(3, 102, 214, 0.15);
+ animation: overlayPulse 1.4s ease-in-out infinite;
}
-.dropzone.active {
- border-color: var(--accent-color);
- background-color: rgba(var(--accent-color), 0.05);
+.drag-overlay-icon {
+ display: block;
+ font-size: 3rem;
+ margin-bottom: 12px;
+ color: var(--accent-color);
+}
+
+.drag-overlay-text {
+ font-size: 1.4rem;
+ font-weight: 600;
+ margin-bottom: 4px;
}
-.dropzone p {
- transition: transform 0.2s ease;
+.drag-overlay-sub {
+ font-size: 0.85rem;
+ opacity: 0.75;
+ margin-bottom: 0;
}
-.dropzone:hover p {
- transform: scale(1.02);
+@keyframes overlayPulse {
+ 0%, 100% { transform: scale(1); }
+ 50% { transform: scale(1.015); }
+}
+
+/* Editor drop hint: subtle text at bottom of editor pane, shown only when empty */
+.drop-hint {
+ position: absolute;
+ bottom: 14px;
+ left: 0;
+ right: 0;
+ text-align: center;
+ font-size: 0.75rem;
+ color: var(--text-color);
+ opacity: 0.35;
+ pointer-events: none;
+ user-select: none;
+}
+
+.editor-pane:has(#markdown-editor:not(:placeholder-shown)) .drop-hint {
+ display: none;
}
/* Dropdown improvements */
@@ -590,62 +633,6 @@ a:focus {
opacity: 0.8;
}
-.dropzone {
- border: 2px dashed var(--border-color);
- border-radius: 6px;
- padding: 20px;
- text-align: center;
- margin-bottom: 10px;
- cursor: pointer;
- transition: all 0.3s ease;
- background-color: var(--dropzone-bg);
- position: relative;
-}
-
-.dropzone.active {
- border-color: var(--accent-color);
- background-color: rgba(var(--accent-color), 0.05);
-}
-
-.dropzone p {
- transition: transform 0.2s ease;
-}
-
-.dropzone:hover {
- border: var(--accent-color) 2px dashed;
-}
-
-.dropzone:hover p {
- transform: scale(1.02);
-}
-
-.close-btn {
- position: absolute;
- top: 5px;
- right: 5px;
- background: none;
- border: none;
- color: var(--text-color);
- font-size: 1rem;
- cursor: pointer;
- padding: 5px;
- display: flex;
- align-items: center;
- justify-content: center;
- border-radius: 50%;
- width: 28px;
- height: 28px;
- opacity: 0.6;
- transition: all 0.2s ease;
- background-color: var(--button-bg);
- border: 1px solid var(--border-color);
-}
-
-.close-btn:hover {
- opacity: 1;
- background-color: var(--color-danger-fg);
-}
-
.editor-pane {
overflow: hidden;
}
@@ -772,11 +759,6 @@ a:focus {
font-size: 1rem;
}
-/* ensure dropzone doesn’t cover menu */
-.mobile-menu-panel .dropzone {
- margin-bottom: 0;
-}
-
/* Mobile document tabs section */
.mobile-tabs-section {
border-bottom: 1px solid var(--border-color);
From c7cfdd61b3ceda8333f21f28de383b2b42ef4f90 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 5 May 2026 05:28:17 +0000
Subject: [PATCH 2/2] fix: guard dragleave handler against non-file drag events
to prevent depth counter desync
Agent-Logs-Url: https://github.com/ThisIs-Developer/Markdown-Viewer/sessions/32f3ea84-25cb-422a-8e8f-941055bdd220
Co-authored-by: ThisIs-Developer <109382325+ThisIs-Developer@users.noreply.github.com>
---
desktop-app/resources/js/script.js | 12 +++++++-----
script.js | 12 +++++++-----
2 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/desktop-app/resources/js/script.js b/desktop-app/resources/js/script.js
index 39b2f76..c90154a 100644
--- a/desktop-app/resources/js/script.js
+++ b/desktop-app/resources/js/script.js
@@ -2793,11 +2793,13 @@ This is a fully client-side application. Your content never leaves your browser
}, false);
document.addEventListener("dragleave", function(e) {
- dragDepth--;
- if (dragDepth <= 0) {
- dragDepth = 0;
- dragOverlay.classList.remove("active");
- dragOverlay.setAttribute("aria-hidden", "true");
+ if (e.dataTransfer && e.dataTransfer.types && e.dataTransfer.types.includes("Files")) {
+ dragDepth--;
+ if (dragDepth <= 0) {
+ dragDepth = 0;
+ dragOverlay.classList.remove("active");
+ dragOverlay.setAttribute("aria-hidden", "true");
+ }
}
}, false);
diff --git a/script.js b/script.js
index 39b2f76..c90154a 100644
--- a/script.js
+++ b/script.js
@@ -2793,11 +2793,13 @@ This is a fully client-side application. Your content never leaves your browser
}, false);
document.addEventListener("dragleave", function(e) {
- dragDepth--;
- if (dragDepth <= 0) {
- dragDepth = 0;
- dragOverlay.classList.remove("active");
- dragOverlay.setAttribute("aria-hidden", "true");
+ if (e.dataTransfer && e.dataTransfer.types && e.dataTransfer.types.includes("Files")) {
+ dragDepth--;
+ if (dragDepth <= 0) {
+ dragDepth = 0;
+ dragOverlay.classList.remove("active");
+ dragOverlay.setAttribute("aria-hidden", "true");
+ }
}
}, false);