From 76e6786af5577a125f87c6c4bce4907ec44cdeda Mon Sep 17 00:00:00 2001 From: SRHTNIN Date: Tue, 13 Oct 2026 18:30:26 +0200 Subject: [PATCH] Live editor: link clicks land right, tidy variable results Co-Authored-By: Claude Opus 5.5 --- Frontend/src/LivePreview.js | 18 +++++++++++++----- Frontend/src/Styles/Theme.css | 14 ++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Frontend/src/LivePreview.js b/Frontend/src/LivePreview.js index d6f8161..9540fcd 100644 --- a/Frontend/src/LivePreview.js +++ b/Frontend/src/LivePreview.js @@ -311,6 +311,10 @@ function BuildDecorations(View) { return; } + if (Name === "URL" && ParentName !== "Link" && ParentName !== "Image" && Node.to > Node.from) { + Ranges.push(Decoration.mark({ class: "cm-LiveLink" }).range(Node.from, Node.to)); + } + if (InlineClasses[Name] && Node.to > Node.from) { Ranges.push(Decoration.mark({ class: InlineClasses[Name] }).range(Node.from, Node.to)); } @@ -434,7 +438,7 @@ function BuildProperties(State) { if (CursorIn(State, 0, To)) return Decoration.none; const Parsed = ParseProperties(State.doc.sliceString(0, To)); if (!Parsed || Parsed.Properties.length === 0) return Decoration.none; - const Html = PropertiesHtml(Parsed, EscapeHtml, EscapeHtml, true); + const Html = PropertiesHtml(Parsed, (Value) => VariableRenderer.renderInline(Value), EscapeHtml, true); return Decoration.set([Decoration.replace({ widget: new PropertiesWidget(Html), block: true }).range(0, To)]); } @@ -561,7 +565,7 @@ function ToEditorOffsets(Text, Found) { } // Asks the server for the spans when the note opens, and again once typing -// pauses; an answer for text that's changed since is dropped. +// pauses (0.3 s); an answer for text that's changed since is dropped. function FetchVariables(RootName, FilePath) { return ViewPlugin.fromClass( class { @@ -572,7 +576,7 @@ function FetchVariables(RootName, FilePath) { } update(Update) { - if (Update.docChanged) this.Request(500); + if (Update.docChanged) this.Request(300); } Request(Delay) { @@ -616,11 +620,15 @@ function LinkAt(State, Position) { return null; } +// Where in the text the clicked link is comes from its element (posAtDOM), +// not the mouse position: the editor maps coordinates through estimated line +// heights, which a tall widget above (the properties panel) throws off. const ClickLinks = EditorView.domEventHandlers({ mousedown(Event, View) { if (Event.button !== 0) return false; - const Position = View.posAtCoords({ x: Event.clientX, y: Event.clientY }); - if (Position === null) return false; + const Mark = Event.target.closest(".cm-LiveLink"); + if (!Mark || !View.contentDOM.contains(Mark)) return false; + const Position = View.posAtDOM(Mark, 0); const Link = LinkAt(View.state, Position); if (!Link) return false; const Editing = diff --git a/Frontend/src/Styles/Theme.css b/Frontend/src/Styles/Theme.css index f10e328..38765e3 100644 --- a/Frontend/src/Styles/Theme.css +++ b/Frontend/src/Styles/Theme.css @@ -592,6 +592,14 @@ img, /* A variable's result in the live editor (LivePreview.js): marked so it reads as calculated, and clickable to edit what's written. */ +/* Results are HTML: the editor's pre-wrap would show markdown-it's + newlines (after each
, after a paragraph) as extra lines. */ +.LiveEditor .cm-LiveVariable, +.LiveEditor .cm-LiveVariableBlock, +.LiveEditor .cm-LiveProperties { + white-space: normal; +} + .LiveEditor .cm-LiveVariable { border-bottom: 1px dotted var(--Purple); cursor: pointer; @@ -605,6 +613,12 @@ img, cursor: pointer; } +.LiveEditor .cm-LiveVariable a, +.LiveEditor .cm-LiveVariableBlock a, +.LiveEditor .cm-LiveProperties a { + color: var(--Blue); +} + /* Its Markdown sits in editor lines, so no paragraph or list gaps. */ .LiveEditor .cm-LiveVariableBlock p, .LiveEditor .cm-LiveVariableBlock ul,