From a58ca40a5ebf6faf1d4086d80ddbfeb7fcf9783e Mon Sep 17 00:00:00 2001 From: Vadim Melnicuk Date: Wed, 18 Feb 2026 20:27:49 +0000 Subject: [PATCH] feat: initialize project with essential files --- CHANGELOG.md | 4 +++ LICENSE | 21 ++++++++++++++ README.md | 41 ++++++++------------------- logo.png | Bin 0 -> 7294 bytes package.json | 51 +++++++++++++++++++++++++++++++--- src/extension.ts | 28 +++++++++++++++---- webview/src/helpers/tables.js | 19 +++++++++++++ webview/src/index.js | 2 +- 8 files changed, 126 insertions(+), 40 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 logo.png diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..3d3a110 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +# Markdown Editor Optimized (MEO) +--- +## 0.1.0 +- Initial build of the Markdown Editor Optimized (MEO) VSCode extension. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8aa2645 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [year] [fullname] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 34aa73a..0a96f3a 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,15 @@ -# MEO - Markdown Editor Optimized - -A native VS Code custom editor for Markdown files powered by CodeMirror 6 with live preview mode. +# Markdown Editor Optimized (MEO) +WYSIWYG editor for markdown files with live edit mode. ## Features -- Custom editor for `.md` files with live preview and modern toolbar -- Uses VS Code color tokens and layout guidance -- Live mode hides markdown syntax markers outside active blocks -- Source mode with full syntax highlighting - -## Development - -Install dependencies: - -```bash -bun install:all -``` - -Build the extension and webview: - -```bash -bun run build -``` - -Run extension in development mode: - -``` -FN+F5 (or F5) in VS Code to launch the extension in a new window with the webview. -``` +- Toggle between live and source modes seamlessly in a single tab. +- Syntax highlighting with Monokai inspired color scheme. +- Rendering of mermaid diagrams and other markdown features. +- Easy management of markdown tables with a dedicated table editor. +- Tasks list support with interactive checkboxes. +- Auto save of markdown files as you edit. +- Content outline view for easy navigation through document structure. ## Usage -- Open a `.md` file. -- Run the command `Markdown Editor Optimized: Open With Editor`. -- Set as default editor if desired. +- Open a `.md` or `.markdown` files with the Markdown Editor Optimized by right-clicking the file in the explorer and selecting `Open with Markdown Editor Optimized`. +- Set the editor as default by running command `Markdown Editor Optimized: Set as Default`. diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..676f3f32e022248a2bf5a059b09faa0d636bfd3d GIT binary patch literal 7294 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 literal 0 HcmV?d00001 diff --git a/package.json b/package.json index 087cf9e..1fb9c34 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,41 @@ { "name": "markdown-editor-optimized", "displayName": "Markdown Editor Optimized", - "description": "VS Code custom editor for Markdown files with live preview mode powered by CodeMirror 6.", + "description": "WYSIWYG editor for markdown files with live edit mode.", "version": "0.1.0", + "publisher": "vadimmelnicuk", + "private": true, + "icon": "logo.png", "engines": { "vscode": "^1.97.0" }, "categories": [ + "Programming Languages", "Other" ], + "keywords": [ + "markdown", + "editor", + "live-edit", + "wysiwyg", + "markdown-editor", + "markdown-preview" + ], "main": "./dist/extension.js", "activationEvents": [ "onLanguage:markdown" ], - "contributes": { + "contributes": { "commands": [ { "command": "markdownEditorOptimized.open", - "title": "Markdown Editor Optimized: Open With Editor", + "title": "Open With Markdown Editor Optimized", "icon": "$(book)" + }, + { + "command": "markdownEditorOptimized.setDefaultEditor", + "title": "Markdown Editor Optimized: Set as Default", + "icon": "$(check)" } ], "customEditors": [ @@ -35,7 +52,33 @@ ], "priority": "default" } - ] + ], + "menus": { + "explorer/context": [ + { + "command": "markdownEditorOptimized.open", + "when": "resourceFilename =~ /\\.(md|markdown)$/", + "group": "navigation" + } + ], + "editor/title/context": [ + { + "command": "markdownEditorOptimized.open", + "when": "resourceFilename =~ /\\.(md|markdown)$/", + "group": "navigation" + } + ] + }, + "configuration": { + "title": "Markdown Editor Optimized", + "properties": { + "markdownEditorOptimized.useAsDefault": { + "type": "boolean", + "default": true, + "description": "Use Markdown Editor Optimized as the default editor for Markdown files" + } + } + } }, "scripts": { "install:all": "bun install && (cd webview && bun install)", diff --git a/src/extension.ts b/src/extension.ts index faf9c25..a657f3c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -70,12 +70,30 @@ export function activate(context: vscode.ExtensionContext): void { ); context.subscriptions.push( - vscode.commands.registerCommand('markdownEditorOptimized.open', async () => { - const active = vscode.window.activeTextEditor; - if (!active || active.document.languageId !== 'markdown') { + vscode.commands.registerCommand('markdownEditorOptimized.open', async (uri?: vscode.Uri) => { + let targetUri = uri; + if (!targetUri) { + const active = vscode.window.activeTextEditor; + if (!active) { + return; + } + targetUri = active.document.uri; + } + if (!targetUri.fsPath.endsWith('.md') && !targetUri.fsPath.endsWith('.markdown')) { return; } - await vscode.commands.executeCommand('vscode.openWith', active.document.uri, VIEW_TYPE); + await vscode.commands.executeCommand('vscode.openWith', targetUri, VIEW_TYPE); + }) + ); + + context.subscriptions.push( + vscode.commands.registerCommand('markdownEditorOptimized.setDefaultEditor', async () => { + const config = vscode.workspace.getConfiguration('workbench'); + const associations = config.get>('editorAssociations') || {}; + associations['*.md'] = VIEW_TYPE; + associations['*.markdown'] = VIEW_TYPE; + await config.update('editorAssociations', associations, vscode.ConfigurationTarget.Global); + void vscode.window.showInformationMessage('Markdown Editor Optimized is now set as the default editor for Markdown files.'); }) ); } @@ -106,7 +124,7 @@ class MarkdownWebviewProvider implements vscode.CustomTextEditorProvider { panel.webview.html = this.getWebviewHtml(panel.webview); - let mode: EditorMode = 'source'; + let mode: EditorMode = 'live'; let applyQueue: Promise = Promise.resolve(); let initDelivered = false; let isApplyingOwnChange = false; diff --git a/webview/src/helpers/tables.js b/webview/src/helpers/tables.js index ad7b924..7ca9c85 100644 --- a/webview/src/helpers/tables.js +++ b/webview/src/helpers/tables.js @@ -404,6 +404,23 @@ class HtmlTableWidget extends WidgetType { event.clipboardData?.setData('text/plain', text); }; + const onKeyDown = (event) => { + if (this.selectedCellCount() <= 1) return; + if (event.key !== 'Backspace' && event.key !== 'Delete') return; + if (!this.selectionRange || !this.domRefs) return; + event.preventDefault(); + for (let row = this.selectionRange.fromRow; row <= this.selectionRange.toRow; row++) { + for (let col = this.selectionRange.fromCol; col <= this.selectionRange.toCol; col++) { + const input = this.domRefs.allRowInputs[row][col]; + if (input.value !== '') { + input.value = ''; + this.hasPendingCellEdits = true; + } + } + } + this.scheduleLayout({ resizeRows: true }); + }; + const onFocusOut = (event) => { const nextTarget = event.relatedTarget; if (nextTarget instanceof Node && table.contains(nextTarget)) return; @@ -457,6 +474,7 @@ class HtmlTableWidget extends WidgetType { table.addEventListener('pointerup', endPointerSelection); table.addEventListener('pointercancel', endPointerSelection); table.addEventListener('copy', onCopy); + table.addEventListener('keydown', onKeyDown); table.addEventListener('focusout', onFocusOut); document.addEventListener('pointerdown', onDocumentPointerDown, true); this.cleanupFns.push(() => { @@ -466,6 +484,7 @@ class HtmlTableWidget extends WidgetType { table.removeEventListener('pointerup', endPointerSelection); table.removeEventListener('pointercancel', endPointerSelection); table.removeEventListener('copy', onCopy); + table.removeEventListener('keydown', onKeyDown); table.removeEventListener('focusout', onFocusOut); document.removeEventListener('pointerdown', onDocumentPointerDown, true); }); diff --git a/webview/src/index.js b/webview/src/index.js index e9c2494..99d4495 100644 --- a/webview/src/index.js +++ b/webview/src/index.js @@ -312,7 +312,7 @@ let syncedText = ''; let inFlight = false; let inFlightText = null; let saveAfterSync = false; -let currentMode = 'source'; +let currentMode = 'live'; let hasLocalModePreference = false; const updateModeUI = () => {