Skip to content

Commit

Permalink
Support to drag and drop images and text into MDX (#345)
Browse files Browse the repository at this point in the history
When dropping an image, the image is downloaded next to the file, and a
markdown link is generated. When text is dropped, the text is inserted
as-is. This only works in VS Code.

This uses a naive text-based approach. This is very similar to the
implementation for markdown files that’s builtin to VS Code.

Closes #322
  • Loading branch information
remcohaszing authored Oct 30, 2023
1 parent d092cdd commit e02ea4f
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/lucky-books-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'vscode-mdx': minor
---

Support drag and dropping text and images into the editor.
1 change: 1 addition & 0 deletions packages/vscode-mdx/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@volar/vscode": "~1.10.0",
"@vscode/vsce": "^2.0.0",
"esbuild": "^0.19.0",
"mdast-util-to-markdown": "^2.0.0",
"ovsx": "^0.8.0",
"undici": "^5.0.0",
"vscode-languageclient": "^9.0.0"
Expand Down
53 changes: 53 additions & 0 deletions packages/vscode-mdx/src/document-drop-edit-provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* @typedef {import('vscode').DocumentDropEditProvider} DocumentDropEditProvider
* @typedef {import('vscode').DataTransferItem} DataTransferItem
*/

import {Uri, WorkspaceEdit} from 'vscode'
import {toMarkdown} from 'mdast-util-to-markdown'

/**
* @type {DocumentDropEditProvider}
*/
export const documentDropEditProvider = {
async provideDocumentDropEdits(document, position, dataTransfer) {
/** @type {DataTransferItem | undefined} */
let textItem

for (const [mime, item] of dataTransfer) {
if (mime === 'text/plain') {
textItem = item
continue
}

if (!mime.startsWith('image/')) {
continue
}

const file = item.asFile()
if (!file) {
continue
}

const additionalEdit = new WorkspaceEdit()
additionalEdit.createFile(Uri.joinPath(document.uri, '..', file.name), {
contents: file,
ignoreIfExists: true
})

return {
insertText: toMarkdown({type: 'image', url: file.name}).trim(),
additionalEdit
}
}

if (textItem) {
const string = await textItem.asString()
return {insertText: string}
}

return {
insertText: ''
}
}
}
10 changes: 9 additions & 1 deletion packages/vscode-mdx/src/extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import * as path from 'node:path'
import {DiagnosticModel} from '@volar/language-server'
import * as languageServerProtocol from '@volar/language-server/protocol.js'
import {activateAutoInsertion, supportLabsVersion} from '@volar/vscode'
import {env, workspace} from 'vscode'
import {env, languages, workspace} from 'vscode'
import {LanguageClient} from 'vscode-languageclient/node.js'
import {documentDropEditProvider} from './document-drop-edit-provider.js'

/**
* @type {LanguageClient}
Expand Down Expand Up @@ -43,6 +44,13 @@ export async function activate(context) {

activateAutoInsertion([client], (document) => document.languageId === 'mdx')

context.subscriptions.push(
languages.registerDocumentDropEditProvider(
{language: 'mdx'},
documentDropEditProvider
)
)

return {
volarLabs: {
version: supportLabsVersion,
Expand Down
2 changes: 2 additions & 0 deletions packages/vscode-mdx/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
"include": ["**/*.js"],
"exclude": ["coverage/", "node_modules/", "out/"],
"compilerOptions": {
"module": "esnext",
"moduleResolution": "bundler",
"noEmit": true
}
}

0 comments on commit e02ea4f

Please sign in to comment.