diff --git a/CHANGELOG.md b/CHANGELOG.md index 67e27f9..14aac4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 0.8.6 - 2024-04-09 + +### 🐛 Fix a bug + +- Don't apply base link url to full links +- Fix full link detection +- Fix initial setting of base link URLs again + +## 0.8.6 - 2024-04-09 + +### 🐛 Fix a bug + +- Don't apply base link url to full links +- Fix full link detection +- Fix initial setting of base link URLs again + ## 0.8.5 - 2024-04-09 ### ✨ Introduce new features diff --git a/CodeMirror6/CodeMirror6.csproj b/CodeMirror6/CodeMirror6.csproj index a3d05eb..5ff7dae 100644 --- a/CodeMirror6/CodeMirror6.csproj +++ b/CodeMirror6/CodeMirror6.csproj @@ -9,7 +9,7 @@ GaelJ.BlazorCodeMirror6 true GaelJ.BlazorCodeMirror6 - 0.8.5 + 0.8.6 true snupkg true diff --git a/CodeMirror6/NodeLib/src/CmHyperlink.ts b/CodeMirror6/NodeLib/src/CmHyperlink.ts index 5b2b347..75dff9f 100644 --- a/CodeMirror6/NodeLib/src/CmHyperlink.ts +++ b/CodeMirror6/NodeLib/src/CmHyperlink.ts @@ -8,7 +8,6 @@ import { ViewUpdate, } from '@codemirror/view'; import { Extension, Range } from '@codemirror/state'; -import { CMInstances } from './CmInstance'; const linkSvgImage = ``; const anyLinkRegexp = /(!?\[(?:[^\]]+)\]\([^\)]+\))|\b((?:https?:\/\/|ftp:\/\/|data:)[^\s]*)\b/gi; @@ -34,16 +33,26 @@ class HyperLinkIcon extends WidgetType } toDOM() { const link = document.createElement('a'); - const matchAll = this.state.url.matchAll(linkRegexp); - const match = [...matchAll][0] - if (match && match.length > 1) { - const url = match[1]; - link.href = `${this.state.baseUrl}${url}`; + const matchUrlPart = this.state.url.matchAll(linkRegexp); + const matchUrl = [...matchUrlPart][0] + if (matchUrl && matchUrl.length > 1) { + const url = matchUrl[1]; + if (url.includes('://') || url.startsWith('data:')) + link.href = url; + else + link.href = `${this.state.baseUrl}${url}`; } - else - link.href = `${this.state.baseUrl}${this.state.url}`; - if (link.href.endsWith('.md') && this.state.viewer) + else { + if (this.state.url.includes('://') || this.state.url.startsWith('data:')) + link.href = this.state.url; + else + link.href = `${this.state.baseUrl}${this.state.url}`; + } + + // Open .md links in the optional markdown viewer + if (link.href.endsWith('.md') || link.href.includes('.md#') || link.href.includes('.md?')) link.href = `${this.state.viewer}${encodeURIComponent(link.href)}`; + link.target = '_blank'; link.innerHTML = linkSvgImage; link.className = 'cm-hyper-link-icon'; @@ -52,17 +61,15 @@ class HyperLinkIcon extends WidgetType } } -function hyperLinkDecorations(view: EditorView, id: string) +function hyperLinkDecorations(view: EditorView, basePathForLinks: string, markdownViewerPath: string) { const widgets: Array> = []; const doc = view.state.doc.toString(); let match; - const basePathForLinks = (CMInstances[id] !== undefined && CMInstances[id].config.basePathForLinks) - ? CMInstances[id].config.basePathForLinks.replace(/\/+$/, '') + "/" - : ''; - const markdownViewPath = (CMInstances[id] !== undefined && CMInstances[id].config.markdownViewPath) - ? CMInstances[id].config.markdownViewPath + basePathForLinks = basePathForLinks + ? basePathForLinks.replace(/\/+$/, '') + "/" : ''; + markdownViewerPath = markdownViewerPath ?? '' while ((match = anyLinkRegexp.exec(doc)) !== null) { const from = match.index; @@ -72,7 +79,7 @@ function hyperLinkDecorations(view: EditorView, id: string) at: to, url: match[0], baseUrl: basePathForLinks, - viewer: markdownViewPath, + viewer: markdownViewerPath, }), side: 1, }); @@ -82,18 +89,18 @@ function hyperLinkDecorations(view: EditorView, id: string) return Decoration.set(widgets); } -export function hyperLinkExtension(id: string) +export function hyperLinkExtension(basePathForLinks: string, markdownViewerPath: string) { return ViewPlugin.fromClass( class HyperLinkView { decorator?: MatchDecorator; decorations: DecorationSet; constructor(view: EditorView) { - this.decorations = hyperLinkDecorations(view, id); + this.decorations = hyperLinkDecorations(view, basePathForLinks, markdownViewerPath); } update(update: ViewUpdate) { if (update.docChanged || update.viewportChanged) { - this.decorations = hyperLinkDecorations(update.view, id); + this.decorations = hyperLinkDecorations(update.view, basePathForLinks, markdownViewerPath); } } }, @@ -117,4 +124,5 @@ export const hyperLinkStyle = EditorView.baseTheme({ }, }); -export const hyperLink = (id: string) => [hyperLinkExtension(id), hyperLinkStyle]; +export const hyperLink = (basePathForLinks: string, markdownViewerPath: string) => + [hyperLinkExtension(basePathForLinks, markdownViewerPath), hyperLinkStyle]; diff --git a/CodeMirror6/NodeLib/src/CmImages.ts b/CodeMirror6/NodeLib/src/CmImages.ts index 7bbb27e..b3e1616 100644 --- a/CodeMirror6/NodeLib/src/CmImages.ts +++ b/CodeMirror6/NodeLib/src/CmImages.ts @@ -4,25 +4,27 @@ import { RangeSet, StateField } from '@codemirror/state' import type { DecorationSet } from '@codemirror/view' import { Decoration, EditorView, WidgetType, ViewUpdate } from '@codemirror/view' import { buildWidget } from './lib/codemirror-kit' -import { CMInstances } from './CmInstance' -const imageWidget = (id: string, src: string, from: number) => buildWidget({ +const imageWidget = (basePathForLinks: string, src: string, from: number) => buildWidget({ src: src, eq(other) { return other.src === src }, toDOM(view: EditorView) { - const basePathForLinks = (CMInstances[id] !== undefined && CMInstances[id].config.basePathForLinks) - ? CMInstances[id].config.basePathForLinks.replace(/\/+$/, '') + "/" - : ''; + basePathForLinks = basePathForLinks + ? basePathForLinks.replace(/\/+$/, '') + "/" + : ''; const container = document.createElement('div') container.setAttribute('aria-hidden', 'true') const image = container.appendChild(document.createElement('img')) image.setAttribute('aria-hidden', 'true') - image.src = `${basePathForLinks}${src}` + if (src.includes('://') || src.startsWith('data:')) + image.src = src; + else + image.src = `${basePathForLinks}${src}` image.style.maxHeight = '320px' image.style.maxWidth = 'calc(100% - 2em)' image.style.objectFit = 'scale-down' @@ -54,7 +56,7 @@ const imageWidget = (id: string, src: string, from: number) => buildWidget({ }, }) -export const dynamicImagesExtension = (id: string, enabled: boolean = true): Extension => { +export const dynamicImagesExtension = (basePathForLinks: string, enabled: boolean = true): Extension => { if (!enabled) { return [] } @@ -62,7 +64,7 @@ export const dynamicImagesExtension = (id: string, enabled: boolean = true): Ext const imageRegex = /!\[.*?\]\((?.*?)\)/ const imageDecoration = (src: string, from: number) => Decoration.widget({ - widget: imageWidget(id, src, from), + widget: imageWidget(basePathForLinks, src, from), side: -1, block: true, }) diff --git a/CodeMirror6/NodeLib/src/index.ts b/CodeMirror6/NodeLib/src/index.ts index 1274850..7905761 100644 --- a/CodeMirror6/NodeLib/src/index.ts +++ b/CodeMirror6/NodeLib/src/index.ts @@ -109,7 +109,7 @@ export async function initCodeMirror( createEditorWithId(id), CMInstances[id].keymapCompartment.of(keymap.of(customLanguageKeyMap)), CMInstances[id].languageCompartment.of(await getLanguage(id, initialConfig.languageName, initialConfig.fileNameOrExtension) ?? []), - CMInstances[id].markdownStylingCompartment.of(initialConfig.languageName !== "Markdown" ? [] : autoFormatMarkdownExtensions(id, initialConfig.autoFormatMarkdown)), + CMInstances[id].markdownStylingCompartment.of(initialConfig.languageName !== "Markdown" ? [] : autoFormatMarkdownExtensions(id, initialConfig.previewImages, initialConfig.basePathForLinks, initialConfig.autoFormatMarkdown)), CMInstances[id].tabSizeCompartment.of(EditorState.tabSize.of(initialConfig.tabSize)), CMInstances[id].indentUnitCompartment.of(indentUnit.of(" ".repeat(initialConfig.indentationUnit))), CMInstances[id].placeholderCompartment.of(placeholder(initialConfig.placeholder)), @@ -138,7 +138,7 @@ export async function initCodeMirror( CMInstances[id].dropCursorCompartment.of(initialConfig.dropCursor ? dropCursor() : []), CMInstances[id].scrollPastEndCompartment.of(initialConfig.scrollPastEnd ? scrollPastEnd() : []), CMInstances[id].highlightActiveLineCompartment.of(initialConfig.highlightActiveLine ? highlightActiveLine() : []), - CMInstances[id].hyperLinksCompartment.of(hyperLink(id)), + CMInstances[id].hyperLinksCompartment.of(hyperLink(initialConfig.basePathForLinks, initialConfig.markdownViewPath)), EditorView.updateListener.of(async (update) => { await updateListenerExtension(id, update) }), linter(async view => maxDocLengthLintSource(id, view)), @@ -361,7 +361,7 @@ export async function setConfiguration(id: string, newConfig: CmConfiguration) { CMInstances[id].languageCompartment.reconfigure(language ?? []), CMInstances[id].keymapCompartment.reconfigure(keymap.of(customLanguageKeyMap)), languageChangeEffect.of(language?.language), - CMInstances[id].markdownStylingCompartment.reconfigure(autoFormatMarkdownExtensions(id, newConfig.languageName === 'Markdown')), + CMInstances[id].markdownStylingCompartment.reconfigure(autoFormatMarkdownExtensions(id, newConfig.previewImages, newConfig.basePathForLinks, newConfig.languageName === 'Markdown')), CMInstances[id].columnsStylingCompartment.reconfigure( newConfig.languageName === "CSV" || newConfig.languageName === "TSV" ? [ @@ -378,7 +378,7 @@ export async function setConfiguration(id: string, newConfig: CmConfiguration) { unfoldAll(CMInstances[id].view) } if (oldConfig.autoFormatMarkdown !== newConfig.autoFormatMarkdown || oldConfig.previewImages !== newConfig.previewImages || oldConfig.basePathForLinks !== newConfig.basePathForLinks) { - effects.push(CMInstances[id].markdownStylingCompartment.reconfigure(autoFormatMarkdownExtensions(id, newConfig.autoFormatMarkdown))) + effects.push(CMInstances[id].markdownStylingCompartment.reconfigure(autoFormatMarkdownExtensions(id, newConfig.previewImages, newConfig.basePathForLinks, newConfig.autoFormatMarkdown))) if (newConfig.languageName === "Markdown" && newConfig.autoFormatMarkdown) foldMarkdownDiagramCodeBlocks(CMInstances[id].view) else @@ -399,7 +399,8 @@ export async function setConfiguration(id: string, newConfig: CmConfiguration) { if (oldConfig.dropCursor !== newConfig.dropCursor) effects.push(CMInstances[id].dropCursorCompartment.reconfigure(newConfig.dropCursor ? dropCursor() : [])) if (oldConfig.scrollPastEnd !== newConfig.scrollPastEnd) effects.push(CMInstances[id].scrollPastEndCompartment.reconfigure(newConfig.scrollPastEnd ? scrollPastEnd() : [])) if (oldConfig.highlightActiveLine !== newConfig.highlightActiveLine) effects.push(CMInstances[id].highlightActiveLineCompartment.reconfigure(newConfig.highlightActiveLine ? highlightActiveLine() : [])) - if (oldConfig.basePathForLinks !== newConfig.basePathForLinks || oldConfig.markdownViewPath != newConfig.markdownViewPath) effects.push(CMInstances[id].hyperLinksCompartment.reconfigure(hyperLink(id))) + if (oldConfig.basePathForLinks !== newConfig.basePathForLinks || oldConfig.markdownViewPath != newConfig.markdownViewPath) + effects.push(CMInstances[id].hyperLinksCompartment.reconfigure(hyperLink(newConfig.basePathForLinks, newConfig.markdownViewPath))) CMInstances[id].config = newConfig if (effects.length > 0 || changes.length > 0) @@ -476,10 +477,10 @@ function saveToLocalStorage(id: string) { } } -const autoFormatMarkdownExtensions = (id: string, autoFormatMarkdown: boolean = true) => [ +const autoFormatMarkdownExtensions = (id: string, previewImages: boolean, basePathForLinks: string, autoFormatMarkdown: boolean = true) => [ getDynamicHeaderStyling(autoFormatMarkdown), dynamicHrExtension(autoFormatMarkdown), - dynamicImagesExtension(id, autoFormatMarkdown && CMInstances[id].config.previewImages === true), + dynamicImagesExtension(basePathForLinks, autoFormatMarkdown && previewImages === true), dynamicDiagramsExtension(autoFormatMarkdown, CMInstances[id].setup.krokiUrl.replace(/\/$/, '')), autocompletion({ override: [ diff --git a/Examples.BlazorServer/Examples.BlazorServer.csproj b/Examples.BlazorServer/Examples.BlazorServer.csproj index 1076965..974d656 100644 --- a/Examples.BlazorServer/Examples.BlazorServer.csproj +++ b/Examples.BlazorServer/Examples.BlazorServer.csproj @@ -4,7 +4,7 @@ enable false enable - 0.8.5 + 0.8.6 diff --git a/Examples.BlazorServerInteractive/Examples.BlazorServerInteractive.csproj b/Examples.BlazorServerInteractive/Examples.BlazorServerInteractive.csproj index 503b70e..56e5c8d 100644 --- a/Examples.BlazorServerInteractive/Examples.BlazorServerInteractive.csproj +++ b/Examples.BlazorServerInteractive/Examples.BlazorServerInteractive.csproj @@ -4,7 +4,7 @@ enable enable false - 0.8.5 + 0.8.6 diff --git a/Examples.BlazorWasm/Examples.BlazorWasm.csproj b/Examples.BlazorWasm/Examples.BlazorWasm.csproj index 2d69736..4f19c2b 100644 --- a/Examples.BlazorWasm/Examples.BlazorWasm.csproj +++ b/Examples.BlazorWasm/Examples.BlazorWasm.csproj @@ -4,7 +4,7 @@ enable enable false - 0.8.5 + 0.8.6 diff --git a/Examples.Common/Examples.Common.csproj b/Examples.Common/Examples.Common.csproj index 6fadd4f..f4123a4 100644 --- a/Examples.Common/Examples.Common.csproj +++ b/Examples.Common/Examples.Common.csproj @@ -5,7 +5,7 @@ enable enable false - 0.8.5 + 0.8.6 diff --git a/NEW_CHANGELOG.md b/NEW_CHANGELOG.md index 2a4eb9d..91130bf 100644 --- a/NEW_CHANGELOG.md +++ b/NEW_CHANGELOG.md @@ -1,8 +1,5 @@ -### ✨ Introduce new features - -- Allow opening markdown links by passing them as query parameter value to a viewer URL + query parameter name + '=' - ### 🐛 Fix a bug -- Update images when bath path for links is initially set -- Update links when bath path for links is initially set +- Don't apply base link url to full links +- Fix full link detection +- Fix initial setting of base link URLs again