diff --git a/.vscode/settings.json b/.vscode/settings.json
index 67740e9..826250c 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -24,7 +24,7 @@
"editor.formatOnSave": false
},
"editor.codeActionsOnSave": {
- "source.fixAll.eslint": true
+ "source.fixAll.eslint": "explicit"
},
"eslint.packageManager": "yarn",
"typescript.tsdk": "node_modules\\typescript\\lib",
diff --git a/.vscodeignore b/.vscodeignore
index b9247f8..74dfdbc 100644
--- a/.vscodeignore
+++ b/.vscodeignore
@@ -6,3 +6,4 @@
!LICENSE
!package.json
!package.nls.json
+!package.nls.zh-cn.json
diff --git a/README.md b/README.md
index e52f742..ebc9dca 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,16 @@
## Features
+
+Uploading all local images in the current file and get those links replaced
+
+
+
+
+Generating uploaded version of the document while keeping the original hierarchy(when the option is enabled)
+
+
+
Uploading an image from clipboard
diff --git a/assets/TestFolder/A/AA/testAA.md b/assets/TestFolder/A/AA/testAA.md
new file mode 100644
index 0000000..9a435ed
--- /dev/null
+++ b/assets/TestFolder/A/AA/testAA.md
@@ -0,0 +1,15 @@
+This
+
+![TEST](../../../test.png "picgo logo")
+
+is
+
+
+
+a
+
+
+![[../../../test.png]]
+
+
+test file
\ No newline at end of file
diff --git a/assets/TestFolder/A/AB/testAB.md b/assets/TestFolder/A/AB/testAB.md
new file mode 100644
index 0000000..9a435ed
--- /dev/null
+++ b/assets/TestFolder/A/AB/testAB.md
@@ -0,0 +1,15 @@
+This
+
+![TEST](../../../test.png "picgo logo")
+
+is
+
+
+
+a
+
+
+![[../../../test.png]]
+
+
+test file
\ No newline at end of file
diff --git a/assets/TestFolder/A/testA.md b/assets/TestFolder/A/testA.md
new file mode 100644
index 0000000..751b7b8
--- /dev/null
+++ b/assets/TestFolder/A/testA.md
@@ -0,0 +1,15 @@
+This
+
+![TEST](../../test.png "picgo logo")
+
+is
+
+
+
+a
+
+
+![[../../test.png]]
+
+
+test file
\ No newline at end of file
diff --git a/assets/TestFolder/B/testB.md b/assets/TestFolder/B/testB.md
new file mode 100644
index 0000000..751b7b8
--- /dev/null
+++ b/assets/TestFolder/B/testB.md
@@ -0,0 +1,15 @@
+This
+
+![TEST](../../test.png "picgo logo")
+
+is
+
+
+
+a
+
+
+![[../../test.png]]
+
+
+test file
\ No newline at end of file
diff --git a/assets/test.md b/assets/test.md
deleted file mode 100644
index e69de29..0000000
diff --git a/assets/testRoot.md b/assets/testRoot.md
new file mode 100644
index 0000000..3d585ca
--- /dev/null
+++ b/assets/testRoot.md
@@ -0,0 +1,16 @@
+This
+
+![TEST](test.png "picgo logo")
+![TEST]( "picgo logo")
+
+is
+
+
+
+a
+
+
+![[test.png]]
+
+
+test file
\ No newline at end of file
diff --git a/assets/uploadedVersion/TestFolder/A/AA/testAA.md b/assets/uploadedVersion/TestFolder/A/AA/testAA.md
new file mode 100644
index 0000000..9a435ed
--- /dev/null
+++ b/assets/uploadedVersion/TestFolder/A/AA/testAA.md
@@ -0,0 +1,15 @@
+This
+
+![TEST](../../../test.png "picgo logo")
+
+is
+
+
+
+a
+
+
+![[../../../test.png]]
+
+
+test file
\ No newline at end of file
diff --git a/assets/uploadedVersion/TestFolder/A/AB/testAB.md b/assets/uploadedVersion/TestFolder/A/AB/testAB.md
new file mode 100644
index 0000000..9a435ed
--- /dev/null
+++ b/assets/uploadedVersion/TestFolder/A/AB/testAB.md
@@ -0,0 +1,15 @@
+This
+
+![TEST](../../../test.png "picgo logo")
+
+is
+
+
+
+a
+
+
+![[../../../test.png]]
+
+
+test file
\ No newline at end of file
diff --git a/assets/uploadedVersion/TestFolder/A/testA.md b/assets/uploadedVersion/TestFolder/A/testA.md
new file mode 100644
index 0000000..751b7b8
--- /dev/null
+++ b/assets/uploadedVersion/TestFolder/A/testA.md
@@ -0,0 +1,15 @@
+This
+
+![TEST](../../test.png "picgo logo")
+
+is
+
+
+
+a
+
+
+![[../../test.png]]
+
+
+test file
\ No newline at end of file
diff --git a/assets/uploadedVersion/TestFolder/B/testB.md b/assets/uploadedVersion/TestFolder/B/testB.md
new file mode 100644
index 0000000..751b7b8
--- /dev/null
+++ b/assets/uploadedVersion/TestFolder/B/testB.md
@@ -0,0 +1,15 @@
+This
+
+![TEST](../../test.png "picgo logo")
+
+is
+
+
+
+a
+
+
+![[../../test.png]]
+
+
+test file
\ No newline at end of file
diff --git a/assets/uploadedVersion/testRoot.md b/assets/uploadedVersion/testRoot.md
new file mode 100644
index 0000000..c8fd79f
--- /dev/null
+++ b/assets/uploadedVersion/testRoot.md
@@ -0,0 +1,15 @@
+This
+
+![TEST](test.png "picgo logo")
+
+is
+
+
+
+a
+
+
+![[test.png]]
+
+
+test file
\ No newline at end of file
diff --git a/esbuild.mjs b/esbuild.mjs
index a74d832..9f2b0c9 100644
--- a/esbuild.mjs
+++ b/esbuild.mjs
@@ -53,7 +53,9 @@ const resultHandler = async (result) => {
const outdir = './dist'
// clean old built files
-fse.rmdirSync(outdir, { recursive: true })
+if (fse.existsSync(outdir)) {
+ fse.rmdirSync(outdir, { recursive: true })
+}
/** @type {import('esbuild').BuildOptions} */
const commonOptions = {
diff --git a/package.json b/package.json
index 21a9120..c0a5364 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,7 @@
"image upload",
"picture upload"
],
- "version": "2.1.6",
+ "version": "2.2.1",
"publisher": "Spades",
"engines": {
"vscode": "^1.60.0"
@@ -29,6 +29,8 @@
"onCommand:picgo.uploadImageFromInputBox",
"onCommand:picgo.webviewDemo",
"onCommand:picgo.webviewPicGoControlPanel",
+ "onCommand:picgo.generateUploadedImageVersionMarkdown",
+ "onCommand:picgo.uploadAndReplaceAllImageLinksInTheCurrentFile",
"workspaceContains:vs-picgo-auto-launch.txt"
],
"main": "./dist/extension",
@@ -58,6 +60,16 @@
"command": "picgo.webviewPicGoControlPanel",
"title": "%command.webview.picGoControlPanel.title%",
"category": "PicGo"
+ },
+ {
+ "command": "picgo.generateUploadedImageVersionMarkdown",
+ "title": "%command.generate.uploadedImageVersionMarkdown.title%",
+ "category": "PicGo"
+ },
+ {
+ "command": "picgo.uploadAndReplaceAllImageLinksInTheCurrentFile",
+ "title": "%command.uploadedImageVersionMarkdownInTheSameFile.title%",
+ "category": "PicGo"
}
],
"keybindings": [
@@ -75,12 +87,27 @@
"command": "picgo.uploadImageFromInputBox",
"key": "ctrl+alt+O",
"mac": "cmd+alt+O"
+ },
+ {
+ "command": "picgo.generateUploadedImageVersionMarkdown",
+ "key": "ctrl+alt+G",
+ "mac": "ctrl+alt+G"
+ },
+ {
+ "command": "picgo.uploadAndReplaceAllImageLinksInTheCurrentFile",
+ "key": "ctrl+alt+R",
+ "mac": "ctrl+alt+R"
}
],
"configuration": {
"type": "object",
"title": "%config.title%",
"properties": {
+ "picgo.useUploadVersionFolder": {
+ "type": "boolean",
+ "default": false,
+ "description": "Whether to save uploaded version files in a separate 'uploadVersion' folder"
+ },
"picgo.configPath": {
"type": "string",
"markdownDescription": "%config.configPath.description%",
@@ -310,9 +337,22 @@
"default": ""
}
}
- }
+ },
+ "localizations": [{
+ "languageId": "zh-cn",
+ "languageName": "Chinese (Simplified)",
+ "localizedLanguageName": "中文(简体)",
+ "translations": [
+ {
+ "id": "vs-picgo",
+ "path": "./package.nls.zh-cn.json"
+ }
+ ]
+ }]
},
- "extensionKind": ["ui"],
+ "extensionKind": [
+ "ui"
+ ],
"scripts": {
"vscode:prepublish": "yarn && yarn build:prod",
"build": "node esbuild.mjs",
@@ -426,5 +466,6 @@
"react-use": "17.3.1",
"redux": "4.1.1"
},
- "license": "MIT"
+ "license": "MIT",
+ "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
diff --git a/package.nls.json b/package.nls.json
index f0eb142..7767538 100644
--- a/package.nls.json
+++ b/package.nls.json
@@ -22,5 +22,8 @@
"config.picBed.github.description": "GitHub configuration.",
"config.picBed.github.repo.description": "`Username/Repo`. For example, PicGo/Images",
"config.picBed.aliyun.description": "Aliyun OSS configuration.",
- "config.picBed.imgur.description": "Imgur picBed configuration."
+ "config.picBed.imgur.description": "Imgur picBed configuration.",
+ "config.useUploadVersionFolder.description": "Whether to save uploaded version files in a separate 'uploadVersion' folder",
+ "command.generate.uploadedImageVersionMarkdown.title": "Generate uploaded image version markdown",
+ "command.uploadedImageVersionMarkdownInTheSameFile.title": "Upload and replace all image links in the current file"
}
diff --git a/package.nls.zh-cn.json b/package.nls.zh-cn.json
new file mode 100644
index 0000000..21b9eb7
--- /dev/null
+++ b/package.nls.zh-cn.json
@@ -0,0 +1,29 @@
+{
+ "ext.displayName": "PicGo",
+ "ext.description": "基于 PicGo 的 VSCode 图片上传插件。支持多种图片格式(png, jpg, gif等)和多个图床服务(sm.ms、七牛云、imgur等)!",
+ "command.upload.clipboard.title": "从剪贴板上传",
+ "command.upload.explorer.title": "从资源管理器上传",
+ "command.upload.inputBox.title": "从输入框上传",
+ "command.generate.uploadedImageVersionMarkdown.title": "生成已上传图片的版本markdown",
+ "command.uploadedImageVersionMarkdownInTheSameFile.title": "上传并替换同一文件中的所有图片链接",
+ "command.webview.demo.title": "打开演示页面",
+ "command.webview.picGoControlPanel.title": "打开 PicGo 控制面板",
+ "config.title": "PicGo",
+ "config.configPath.description": "PicGo-Core 配置文件的路径。如果未指定,PicGo 将使用 `#picgo.picBed#`。更多配置文件相关信息请参考 [PicGo-Core 文档](https://picgo.github.io/PicGo-Core-Doc/zh/guide/config.html#%E9%BB%98%E8%AE%A4%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6) 和 [PicGo 文档](https://picgo.github.io/PicGo-Doc/zh/guide/config.html#%E5%9B%BE%E5%BA%8A%E5%8C%BA)",
+ "config.dataPath.description": "数据文件的路径,包含所有已上传图片的信息。如果未指定,PicGo 将使用 `your_home_dir/vs-picgo-data.json`",
+ "config.customUploadName.description": "自定义上传图片的名称,图片将在上传前重命名。\n- `${fileName}`: 原始图片的名称,不包含扩展名。\n **注意:如果在上传前选择了文本,选中的文本将作为上传图片的 `fileName`。**\n- `${extName}`: 原始图片的扩展名。\n- `${mdFileName}`: 当前编辑的 markdown 文件名。\n- `${date}`: YY-MM-DD 格式的日期。\n- `${dateTime}`: YY-MM-DD-hh-mm-ss 格式的日期时间。\n\n示例:\n- `${fileName}-${date}${extName}` -> `picName-2016-07-25.jpg`\n- `${mdFileName}-${dateTime}${extName}` -> `markdownName-2017-04-12-22-28-10.jpg`",
+ "config.customOutputFormat.description": "自定义上传图片的输出格式。\n- `${url}`: 上传后的图片URL。\n- `${uploadedName}`: 上传后的图片名称(不含扩展名),参见 `#picgo.customUploadName#`,注意即使在 `#picgo.customUploadName#` 中使用了 `${extName}`,输出中仍然不会包含扩展名。\n\n示例:\n- `![${uploadedName}](${url})` -> `![picName-2016-07-25](https://example.com/xxx.jpg)`\n- `` -> ``",
+ "config.picBed.uploader": "请参考 [`picBed.uploader`](https://picgo.github.io/PicGo-Core-Doc/zh/guide/config.html#picbed-uploader)",
+ "config.picBed.current": "请参考 [`picBed.current`](https://picgo.github.io/PicGo-Core-Doc/zh/guide/config.html#picbed-current)",
+ "config.picBed.proxy": "请求的代理设置,更多详情请参考 [`picBed.proxy`](https://picgo.github.io/PicGo-Core-Doc/zh/guide/config.html#picbed-proxy)",
+ "config.picBed.smms.description": "SM.MS 图床配置",
+ "config.picBed.weibo.description": "微博图床配置",
+ "config.picBed.qiniu.description": "七牛云图床配置",
+ "config.picBed.upyun.description": "又拍云图床配置",
+ "config.picBed.tcyun.description": "腾讯云 COS 图床配置",
+ "config.picBed.github.description": "GitHub 配置",
+ "config.picBed.github.repo.description": "`用户名/仓库名`。例如: PicGo/Images",
+ "config.picBed.aliyun.description": "阿里云 OSS 配置",
+ "config.picBed.imgur.description": "Imgur 图床配置",
+ "config.useUploadVersionFolder.description": "是否将上传版本文件保存在单独的 'uploadVersion' 文件夹中"
+}
diff --git a/src/extension.ts b/src/extension.ts
index b6688a3..ca10f6c 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -19,6 +19,20 @@ export async function activate(context: vscode.ExtensionContext) {
'picgo.uploadImageFromInputBox',
async () => await CommandManager.commandManager.uploadImageFromInputBox()
),
+ vscode.commands.registerCommand(
+ 'picgo.generateUploadedImageVersionMarkdown',
+ async () =>
+ await CommandManager.commandManager.generateUploadedImageVersionMarkdown(
+ false
+ )
+ ),
+ vscode.commands.registerCommand(
+ 'picgo.uploadAndReplaceAllImageLinksInTheCurrentFile',
+ async () =>
+ await CommandManager.commandManager.generateUploadedImageVersionMarkdown(
+ true
+ )
+ ),
vscode.commands.registerCommand('picgo.webviewDemo', () =>
panelManager.createOrShowWebviewPanel('Demo')
diff --git a/src/vscode/CommandManager.ts b/src/vscode/CommandManager.ts
index cbe2165..21c43be 100644
--- a/src/vscode/CommandManager.ts
+++ b/src/vscode/CommandManager.ts
@@ -4,7 +4,7 @@ import * as vscode from 'vscode'
import { Editor } from './Editor'
import { PicgoAPI } from './PicgoAPI'
import { PicgoAddon } from './PicgoAddon'
-import { showError } from './utils'
+import { showError, showInfo, showWarning } from './utils'
export class CommandManager {
static commandManager: CommandManager = new CommandManager()
@@ -28,6 +28,228 @@ export class CommandManager {
return outputString
}
+ async silentUploadCommand(input?: string[]) {
+ const pluginName = 'vspicgo'
+ PicgoAPI.picgoAPI.setCurrentPluginName(pluginName)
+ const [id, plugin] = PicgoAddon.picgoAddon.beforeUploadPlugin()
+ PicgoAPI.picgoAPI.helper.beforeUploadPlugins.register(id, plugin)
+
+ const output = await PicgoAPI.picgoAPI.upload(input)
+ PicgoAPI.picgoAPI.helper.beforeUploadPlugins.unregister(pluginName)
+
+ // error has been handled in picgoAPI.upload
+ if (!output) return
+
+ return PicgoAddon.picgoAddon.outputToURLs(output)
+ }
+
+ async generateUploadedImageVersionMarkdown(sameFile: boolean = false) {
+ // Get current editor
+ const editor = Editor.editor
+ if (!editor) {
+ showError('No active editor')
+ return
+ }
+
+ // Get document text and path
+ const document = editor.document
+
+ let newEditor: vscode.TextEditor
+ let newDocument: vscode.TextDocument
+
+ if (sameFile) {
+ // Use existing document and editor
+ newEditor = editor
+ newDocument = document
+ } else {
+ // Original file handling logic
+ const originalFilePath = document.uri.fsPath
+ const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri)
+ if (!workspaceFolder) {
+ showError('No workspace folder found')
+ return
+ }
+
+ const originalDir = path.dirname(originalFilePath)
+ const originalFileName = path.basename(
+ originalFilePath,
+ path.extname(originalFilePath)
+ )
+ const fileExt = path.extname(originalFilePath)
+
+ // Check setting for upload version folder
+ const useUploadFolder = vscode.workspace
+ .getConfiguration('picgo')
+ .get('useUploadVersionFolder', false)
+
+ // Determine target directory and create if needed
+ let targetDir
+ if (useUploadFolder) {
+ // Get relative path from workspace root to the original file's directory
+ const relativeDir = path.relative(
+ workspaceFolder.uri.fsPath,
+ originalDir
+ )
+ // Create target path in uploadedVersion folder
+ targetDir = path.join(
+ workspaceFolder.uri.fsPath,
+ 'uploadedVersion',
+ relativeDir
+ )
+ // Create all necessary directories
+ fs.mkdirSync(targetDir, { recursive: true })
+ } else {
+ targetDir = originalDir
+ }
+
+ // Create new file path
+ const newFileName = `${originalFileName}${
+ useUploadFolder ? '' : '_uploadedVersion'
+ }${fileExt}`
+ const newFilePath = path.join(targetDir, newFileName)
+
+ // Copy original file content
+ fs.copyFileSync(originalFilePath, newFilePath)
+
+ // Open the new file
+ newDocument = await vscode.workspace.openTextDocument(newFilePath)
+ newEditor = await vscode.window.showTextDocument(newDocument)
+ }
+
+ // Get text from new document
+ const text = newDocument.getText()
+
+ // Match various markdown image syntax:
+ // 1. Standard markdown: ![alt](url) or ![alt](url "title")
+ // 2. HTML:
+ // 3. Obsidian: ![[filename]]
+ const mdImageRegex = /!\[([^\]]*)\]\((?:<([^>]+)>|([^)\s]+))\s*(.*)?\)|]+src=["']([^"']+)["'][^>]*>|!\[\[([^\]]+)\]\]/g
+ let match
+ let hasLocalImage = false
+ const replacements: Array<{ original: string; replacement: string }> = []
+
+ // Add a cache to store path -> url mappings
+ const pathToUrlCache: Map = new Map()
+
+ // Iterate through all matches
+ while ((match = mdImageRegex.exec(text)) !== null) {
+ // Get image URL - Extract according to different formats
+ let imgUrl = ''
+
+ if (match[2]) {
+ // Standard markdown
+ imgUrl = match[2]
+ } else if (match[3]) {
+ // Standard markdown
+ imgUrl = match[3]
+ } else if (match[4]) {
+ // HTML format
+ imgUrl = match[4]
+ } else if (match[5]) {
+ // Obsidian format
+ imgUrl = match[5]
+ }
+
+ if (!imgUrl) continue
+
+ // Check whether it's a local path or a URL
+ if (!imgUrl.startsWith('http') && !imgUrl.startsWith('data:')) {
+ hasLocalImage = true
+
+ // Handle path
+ let absolutePath = imgUrl
+ if (!path.isAbsolute(imgUrl)) {
+ if (match[5]) {
+ // Obsidian syntax
+ const attachmentFolders = [
+ path.join(path.dirname(document.uri.fsPath), 'attachments'),
+ path.join(path.dirname(document.uri.fsPath), 'assets'),
+ path.dirname(document.uri.fsPath)
+ ]
+
+ // search for the image in the attachment folders (possible when in Obsidian)
+ for (const folder of attachmentFolders) {
+ const testPath = path.join(folder, imgUrl)
+ if (fs.existsSync(testPath)) {
+ absolutePath = testPath
+ break
+ }
+ }
+ } else {
+ absolutePath = path.resolve(
+ path.dirname(document.uri.fsPath),
+ imgUrl
+ )
+ }
+ }
+
+ if (fs.existsSync(absolutePath)) {
+ let newUrl: string
+
+ // Check if we've already uploaded this image
+ if (pathToUrlCache.has(absolutePath)) {
+ newUrl = pathToUrlCache.get(absolutePath) ?? ''
+ } else {
+ // Upload image and cache the result
+ const newUrls = await this.silentUploadCommand([absolutePath])
+ if (newUrls && newUrls.length > 0) {
+ newUrl = newUrls[0]
+ pathToUrlCache.set(absolutePath, newUrl)
+ } else {
+ continue // Skip if upload failed
+ }
+ }
+
+ let replacement = newUrl
+ if (match[2]) {
+ // Markdown format
+ const originalStr = match[0]
+ replacement = originalStr.replace(match[2], newUrl)
+ } else if (match[3]) {
+ // Markdown format
+ const originalStr = match[0]
+ replacement = originalStr.replace(match[3], newUrl)
+ } else if (match[4]) {
+ // HTML format
+ const originalStr = match[0]
+ replacement = originalStr.replace(match[4], newUrl)
+ } else if (match[5]) {
+ // Obsidian format
+ const originalStr = match[0]
+ replacement = originalStr.replace(match[5], newUrl)
+ }
+ replacements.push({
+ original: match[0],
+ replacement: replacement
+ })
+ } else {
+ showError(`Local image not found: ${absolutePath}`)
+ }
+ }
+ }
+
+ if (!hasLocalImage) {
+ showWarning('No local images found in current document')
+ return
+ }
+
+ // Replace all local image links in the new file
+ newEditor.edit((editBuilder) => {
+ for (let i = 0; i < replacements.length; i++) {
+ const { original, replacement } = replacements[i]
+ const fileText = newDocument.getText()
+ const startPos = newDocument.positionAt(fileText.indexOf(original))
+ const endPos = newDocument.positionAt(
+ fileText.indexOf(original) + original.length
+ )
+ editBuilder.replace(new vscode.Range(startPos, endPos), replacement)
+ showInfo(
+ `Replaced original image link ${original} with uploaded image link ${replacement}.`
+ )
+ }
+ })
+ }
+
async uploadImageFromClipboard() {
this.uploadCommand()
}
diff --git a/src/vscode/PicgoAddon.ts b/src/vscode/PicgoAddon.ts
index 8871a85..81f0f84 100644
--- a/src/vscode/PicgoAddon.ts
+++ b/src/vscode/PicgoAddon.ts
@@ -133,4 +133,8 @@ export class PicgoAddon {
)
.join('\n')
}
+
+ outputToURLs(output: IImgInfo[]) {
+ return output.map((imgInfo) => imgInfo.imgUrl ?? '')
+ }
}