diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f10dd00..d29f83a 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -12,16 +12,6 @@ "group": "build-tasks" } }, - { - "label": "watch", - "dependsOn": [ - "build:extension", - "watch:webview" - ], - "dependsOrder": "parallel", - "problemMatcher": [], - "group": "build" - }, { "label": "build:extension", "type": "shell", diff --git a/webview/src/editor.js b/webview/src/editor.js index 9388ab4..d79644f 100644 --- a/webview/src/editor.js +++ b/webview/src/editor.js @@ -380,6 +380,8 @@ export function createEditor({ parent, text, onApplyChanges }) { return insertHr(view, selection); case 'table': return insertTable(view, selection, level?.cols, level?.rows); + case 'link': + return insertLink(view, selection); } const newMarkerLen = insert.length; @@ -546,6 +548,26 @@ function insertHr(view, selection) { }); } +function insertLink(view, selection) { + const { state } = view; + + if (!selection.empty) { + const selectedText = state.doc.sliceString(selection.from, selection.to); + const insert = `[${selectedText}]()`; + view.dispatch({ + changes: { from: selection.from, to: selection.to, insert }, + selection: { anchor: selection.from + insert.length - 1 } + }); + return; + } + + const insert = '[]()'; + view.dispatch({ + changes: { from: selection.from, insert }, + selection: { anchor: selection.from + 1 } + }); +} + function sourceMode() { return [ markdown({ diff --git a/webview/src/index.js b/webview/src/index.js index 99d4495..ed2f067 100644 --- a/webview/src/index.js +++ b/webview/src/index.js @@ -1,5 +1,5 @@ import { createEditor } from './editor'; -import { createElement, Heading, Heading1, Heading2, Heading3, Heading4, Heading5, Heading6, List, ListOrdered, ListTodo, Save, ListTree, Code, Terminal, Quote, Minus, Table2 } from 'lucide'; +import { createElement, Heading, Heading1, Heading2, Heading3, Heading4, Heading5, Heading6, List, ListOrdered, ListTodo, Save, ListTree, Code, Terminal, Quote, Minus, Table2, Link } from 'lucide'; import * as colors from './theme'; for (const [name, value] of Object.entries(colors)) { @@ -192,6 +192,13 @@ hrBtn.dataset.action = 'hr'; hrBtn.title = 'Horizontal Rule'; hrBtn.appendChild(createElement(Minus, { width: 18, height: 18 })); +const linkBtn = document.createElement('button'); +linkBtn.type = 'button'; +linkBtn.className = 'format-button'; +linkBtn.dataset.action = 'link'; +linkBtn.title = 'Link'; +linkBtn.appendChild(createElement(Link, { width: 18, height: 18 })); + const tableBtn = document.createElement('button'); tableBtn.type = 'button'; tableBtn.className = 'format-button'; @@ -267,7 +274,7 @@ tableGrid.addEventListener('click', (event) => { editor.focus(); }); -formatGroup.append(headingWrapper, bulletListBtn, numberedListBtn, taskBtn, separator, tableWrapper, codeBlockBtn, inlineCodeBtn, quoteBtn, hrBtn); +formatGroup.append(headingWrapper, bulletListBtn, numberedListBtn, taskBtn, separator, tableWrapper, codeBlockBtn, inlineCodeBtn, linkBtn, quoteBtn, hrBtn); const rightGroup = document.createElement('div'); rightGroup.className = 'right-group'; @@ -659,6 +666,7 @@ codeBlockBtn.addEventListener('click', () => handleFormatAction('codeBlock')); inlineCodeBtn.addEventListener('click', () => handleFormatAction('inlineCode')); quoteBtn.addEventListener('click', () => handleFormatAction('quote')); hrBtn.addEventListener('click', () => handleFormatAction('hr')); +linkBtn.addEventListener('click', () => handleFormatAction('link')); autoSaveBtn.addEventListener('click', toggleAutoSave); outlineBtn.addEventListener('click', toggleOutline); diff --git a/webview/src/styles.css b/webview/src/styles.css index b2862ad..b2229bd 100644 --- a/webview/src/styles.css +++ b/webview/src/styles.css @@ -394,6 +394,8 @@ body { .cm-editor .meo-md-link { text-decoration: underline; + text-decoration-color: var(--meo-color-base05); + text-underline-offset: 4px; } .cm-editor .meo-md-quote {