From da748caf42538e8b6d7add44e5de374e9d7719e5 Mon Sep 17 00:00:00 2001 From: Gamosoft Date: Mon, 29 Jun 2026 13:07:00 +0200 Subject: [PATCH] double-click issue --- backend/export.py | 10 ++------- frontend/app.js | 50 ++++++++++++++++++++++++++++++--------------- frontend/index.html | 5 ++--- 3 files changed, 37 insertions(+), 28 deletions(-) diff --git a/backend/export.py b/backend/export.py index a5d45de..cea3fd2 100644 --- a/backend/export.py +++ b/backend/export.py @@ -528,9 +528,7 @@ def generate_export_html( color: var(--text-secondary, #6a737d); }} - /* Callouts / admonitions (GitHub-style: > [!NOTE], > [!TIP], etc.) - Mirrors the in-app preview styles so exported/shared notes match. - Hard-coded GitHub palette with rgba backgrounds blends with any theme. */ + /* Callouts — mirror the in-app preview. */ .markdown-preview .callout {{ margin: 1rem 0; padding: 0.75rem 1rem; @@ -797,11 +795,7 @@ def generate_export_html( // Raw markdown content const markdown = `{escaped_content}`; - // Convert GitHub/Obsidian-style callouts (admonitions) before marked parses. - // Mirrors the in-app preview preprocessor so exported/shared notes match. - // Code blocks are protected with placeholders so callout syntax inside - // fenced/inline code is preserved verbatim. Only the 5 GitHub-canonical - // types are recognised; unknown types fall through to plain blockquotes. + // GitHub-style callouts — mirrors the in-app preview preprocessor. let processed; {{ const CALLOUT_RE = /^>\\s*\\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\\]\\s*(.*)$/i; diff --git a/frontend/app.js b/frontend/app.js index 3d29186..5ff7d7d 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -5190,6 +5190,21 @@ function noteApp() { }, // Markdown formatting helpers + // Trim trailing whitespace from double-click word selections. + // No-op on browsers that already exclude it (Firefox, Safari). + trimSelectionTrailingSpace(event) { + const editor = event && event.target; + if (!editor) return; + const start = editor.selectionStart; + let end = editor.selectionEnd; + if (start >= end) return; + const text = editor.value; + while (end > start && /\s/.test(text.charAt(end - 1))) end--; + if (end !== editor.selectionEnd) { + editor.setSelectionRange(start, end); + } + }, + wrapSelection(before, after, placeholder) { const editor = document.getElementById('note-editor'); if (!editor) return; @@ -5197,23 +5212,29 @@ function noteApp() { const start = editor.selectionStart; const end = editor.selectionEnd; const selectedText = this.noteContent.substring(start, end); - const textToWrap = selectedText || placeholder; + + // Push edge whitespace OUTSIDE inline wrappers — "**house **" is + // invalid per CommonMark. Block wrappers (with \n) keep it intact. + const isInline = !before.includes('\n') && !after.includes('\n'); + let leading = '', trailing = '', core = selectedText; + if (isInline && selectedText) { + const m = selectedText.match(/^(\s*)([\s\S]*?)(\s*)$/); + leading = m[1]; + core = m[2]; + trailing = m[3]; + } + const textToWrap = core || placeholder; // Build the new text - const newText = before + textToWrap + after; + const newText = leading + before + textToWrap + after + trailing; // Update content this.noteContent = this.noteContent.substring(0, start) + newText + this.noteContent.substring(end); - // Set cursor position (select the wrapped text or placeholder) this.$nextTick(() => { - if (selectedText) { - // If text was selected, keep it selected (inside the wrapper) - editor.setSelectionRange(start + before.length, start + before.length + selectedText.length); - } else { - // If no text selected, select the placeholder - editor.setSelectionRange(start + before.length, start + before.length + placeholder.length); - } + const coreStart = start + leading.length + before.length; + const coreEnd = coreStart + textToWrap.length; + editor.setSelectionRange(coreStart, coreEnd); editor.focus(); }); @@ -5811,13 +5832,8 @@ function noteApp() { } ); - // Step 2c: Convert GitHub/Obsidian-style callouts (admonitions). - // Runs while code blocks are still placeholders so callout syntax - // inside fenced/inline code is preserved verbatim. The body is - // emitted as a raw HTML block surrounded by blank lines so marked - // still parses inner markdown (bold, links, lists, etc.) per CommonMark. - // Only the 5 GitHub-canonical types are recognised; unknown types - // (e.g. `> [!CUSTOM]`) fall through to plain blockquotes. + // Step 2c: GitHub-style callouts. Runs inside the code-protection + // window. Unknown types fall through to plain blockquotes. { const CALLOUT_RE = /^>\s*\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\s*(.*)$/i; const CALLOUT_ICONS = { note: 'ℹ️', tip: '💡', important: '❗', warning: '⚠️', caution: '🛑' }; diff --git a/frontend/index.html b/frontend/index.html index fcff80e..a167237 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -545,9 +545,7 @@ color: var(--text-secondary); } - /* Callouts / admonitions (GitHub-style: > [!NOTE], > [!TIP], etc.) - Colors are hard-coded GitHub palette with rgba backgrounds so the - tint blends with any theme bg without needing per-theme overrides. */ + /* Callouts — GitHub-style admonitions (> [!NOTE], etc.) */ .markdown-preview .callout { margin: 1rem 0; padding: 0.75rem 1rem; @@ -3061,6 +3059,7 @@ @scroll="syncOverlayScroll()" @keydown.enter="handleEditorEnterKey($event)" @keydown.tab="handleTabKey($event)" + @dblclick="trimSelectionTrailingSpace($event)" @drop="onEditorDrop($event)" @dragover.prevent="onEditorDragOver($event)" @dragenter="onEditorDragEnter($event)"