+
+ Drop a .md file anywhere to open it
+
diff --git a/desktop-app/resources/js/main.js b/desktop-app/resources/js/main.js
index 70773eb..0425c8d 100644
--- a/desktop-app/resources/js/main.js
+++ b/desktop-app/resources/js/main.js
@@ -109,11 +109,9 @@ if (NL_OS != "Darwin") {
function applyContent() {
const editor = document.getElementById('markdown-editor');
- const dropzone = document.getElementById('dropzone');
if (!editor) return;
editor.value = content;
editor.dispatchEvent(new Event('input'));
- if (dropzone) dropzone.style.display = 'none';
}
if (document.readyState === 'loading') {
diff --git a/desktop-app/resources/js/script.js b/desktop-app/resources/js/script.js
index 94057ea..c90154a 100644
--- a/desktop-app/resources/js/script.js
+++ b/desktop-app/resources/js/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,42 @@ 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) {
+ 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");
+ }
}
- });
- 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/desktop-app/resources/styles.css b/desktop-app/resources/styles.css
index a71467f..99c4713 100644
--- a/desktop-app/resources/styles.css
+++ b/desktop-app/resources/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);
diff --git a/index.html b/index.html
index fe75e19..1c302ce 100644
--- a/index.html
+++ b/index.html
@@ -296,16 +296,21 @@
Menu
-
-
Drop your Markdown file here or click to browse
+
+
+
+
+
Drop your Markdown file anywhere
+
.md or .markdown files supported
+
+
+ Drop a .md file anywhere to open it
+
diff --git a/script.js b/script.js
index 94057ea..c90154a 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,42 @@ 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) {
+ 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");
+ }
}
- });
- 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);