Skip to content

Commit

Permalink
feat: 434 vscode插件支持配置图床 (#534)
Browse files Browse the repository at this point in the history
  • Loading branch information
ufec committed Aug 17, 2023
1 parent 7e4a125 commit 4fdbb93
Show file tree
Hide file tree
Showing 6 changed files with 369 additions and 58 deletions.
86 changes: 81 additions & 5 deletions vscodePlugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,84 @@
],
"description": "cherry-markdown's theme, available values: [ default | dark | light | green | red ]"
},
"cherryMarkdown.fileUploadUrl": {
"type": "url",
"description": "file upload url"
"cherryMarkdown.UploadType": {
"type": "string",
"default": "None",
"enum": [
"None",
"CustomUploader",
"PicGoServer"
],
"enumDescriptions": [
"dont use any uploader, use base64 to show image",
"use custom uploader",
"use picgo server"
],
"description": "cherry-markdown's upload mode, available values: [ None | CustomUploader | PicGoServer ]"
},
"cherryMarkdown.CustomUploader": {
"type": "object",
"default": {
"enable": true,
"url": "https://your-server.com/upload",
"headers": {
"Access-Control-Allow-Origin": "*"
}
},
"properties": {
"enable": {
"type": "boolean",
"default": false,
"description": "enable custom uploader"
},
"url": {
"type": "string",
"default": "",
"description": "custom uploader url"
},
"headers": {
"type": "object",
"default": {},
"description": "custom uploader headers",
"properties": {
"key": {
"type": "string",
"default": "",
"description": "custom uploader header key"
},
"value": {
"type": "string",
"default": "",
"description": "custom uploader header value"
}
}
}
},
"description": "cherry-markdown's custom uploader, you need config it if you want to upload images / video / audio to your own server"
},
"cherryMarkdown.PicGoServer": {
"type": "string",
"description": "cherry-markdown's picgo server, you need config it if you want to upload images / video / audio to picgo server,",
"default": "http://127.0.0.1:36677/upload"
},
"cherryMarkdown.BackfillImageProps": {
"type": "array",
"items": {
"type": "string",
"enum": [
"isBorder",
"isShadow",
"isRadius"
],
"description": "Select multiple items",
"enumDescriptions": [
"Whether to add a border to the image",
"Whether to add a shadow to the image",
"Whether to add a rounded corner to the image"
]
},
"default": [],
"description": "cherry-markdown's backfill image props, you need config it if you want to backfill image props"
}
}
}
Expand All @@ -79,7 +154,6 @@
"devDependencies": {
"@babel/core": "^7.20.12",
"@babel/preset-env": "^7.20.2",
"@tencent/eslint-config-tencent": "^0.15.0",
"@types/glob": "^7.1.3",
"@types/mocha": "^8.2.2",
"@types/node": "14.x",
Expand All @@ -89,6 +163,7 @@
"babel-loader": "^9.1.2",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.5.0",
"eslint-config-tencent": "^1.0.4",
"eslint-plugin-prettier": "^4.0.0",
"glob": "^7.1.7",
"mocha": "^8.4.0",
Expand All @@ -99,8 +174,9 @@
"webpack-cli": "^4.7.0"
},
"dependencies": {
"@tencent/eslint-config-tencent": "^0.15.2",
"@types/mathjax": "0.0.37",
"axios": "^1.4.0",
"eslint-config-tencent": "^1.0.4",
"katex": "^0.16.4",
"mathjax": "^3.2.2",
"md5": "^2.3.0",
Expand Down
69 changes: 52 additions & 17 deletions vscodePlugin/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import * as vscode from 'vscode';
import * as path from 'path';
import { getWebviewContent } from './webview';
import { uploadFileHandler } from './handler/uploadFile';

let cherryPanel: vscode.WebviewPanel; // 保存预览窗口的webview实例
let isCherryPanelInit: boolean = false;
let extensionPath: string = '';
let targetDocument: vscode.TextEditor;
let disableScrollTrigger: boolean = false; // true:滚动时不往webview发送滚动事件,反之发送
let disableEditTrigger: boolean = false; // true:变更内容时不往webview发送内容变更事件,反之发送
let cherryTheme: string | undefined = vscode.workspace.getConfiguration('cherryMarkdown').get('theme'); // 缓存主题
let cherryTheme: string | undefined = vscode.workspace
.getConfiguration('cherryMarkdown')
.get('theme'); // 缓存主题
export function activate(context: vscode.ExtensionContext) {
extensionPath = context.extensionPath;
// 注册命令
const disposable = vscode.commands.registerCommand(
'cherrymarkdown.preview',
() => {
triggerEditorContentChange();
}
},
);

context.subscriptions.push(disposable);
Expand All @@ -27,7 +30,7 @@ export function activate(context: vscode.ExtensionContext) {
});

// 切换文件的时候更新预览区域内容
vscode.window.onDidChangeActiveTextEditor((e) => {
vscode.window.onDidChangeActiveTextEditor(e => {
if (e?.document) {
triggerEditorContentChange();
// 如果打开的不是md文件,则让cherry强制进入预览模式
Expand All @@ -40,18 +43,22 @@ export function activate(context: vscode.ExtensionContext) {
});

// 当修改文档内容的时候更新预览区域内容,如果已经关闭预览了,则不需要重新打开预览
vscode.workspace.onDidChangeTextDocument((e) => {
vscode.workspace.onDidChangeTextDocument(e => {
if (isCherryPanelInit && e?.document && !disableEditTrigger) {
triggerEditorContentChange();
}
});

// 滚动的时候让预览区域同步滚动
vscode.window.onDidChangeTextEditorVisibleRanges((e) => {
vscode.window.onDidChangeTextEditorVisibleRanges(e => {
if (!isCherryPanelInit) {
return true;
}
disableScrollTrigger || cherryPanel.webview.postMessage({ cmd: 'editor-scroll', data: e.visibleRanges[0].start.line });
disableScrollTrigger ||
cherryPanel.webview.postMessage({
cmd: 'editor-scroll',
data: e.visibleRanges[0].start.line,
});
});
}

Expand All @@ -68,7 +75,10 @@ const getMarkdownFileInfo = () => {
let currentDoc = currentEditor?.document;
let currentText = '';
let currentTitle = '';
if (currentDoc?.languageId !== 'markdown' && targetDocument.document.languageId === 'markdown') {
if (
currentDoc?.languageId !== 'markdown' &&
targetDocument.document.languageId === 'markdown'
) {
currentEditor = targetDocument;
currentDoc = targetDocument.document;
}
Expand All @@ -79,8 +89,12 @@ const getMarkdownFileInfo = () => {
currentText = currentDoc?.getText() || '';
currentTitle = path.basename(currentDoc?.fileName) || '';
}
currentTitle = currentTitle ? `预览 ${currentTitle} by cherry-markdown` : '不支持当前文件 by cherry-markdown';
const theme = cherryTheme ? cherryTheme : vscode.workspace.getConfiguration('cherryMarkdown').get('theme');
currentTitle = currentTitle
? `预览 ${currentTitle} by cherry-markdown`
: '不支持当前文件 by cherry-markdown';
const theme = cherryTheme
? cherryTheme
: vscode.workspace.getConfiguration('cherryMarkdown').get('theme');
const mdInfo = { text: currentText, theme };
return { mdInfo, currentTitle };
};
Expand All @@ -90,7 +104,8 @@ const getMarkdownFileInfo = () => {
*/
const initCherryPanel = () => {
const { mdInfo, currentTitle } = getMarkdownFileInfo();
const workspaceFolder = vscode.workspace.workspaceFolders?.[0].uri.fsPath ?? '';
const workspaceFolder =
vscode.workspace.workspaceFolders?.[0].uri.fsPath ?? '';
cherryPanel = vscode.window.createWebviewPanel(
'cherrymarkdown.preview',
currentTitle,
Expand All @@ -103,9 +118,13 @@ const initCherryPanel = () => {
vscode.Uri.file(path.join(extensionPath, 'dist')),
vscode.Uri.file(workspaceFolder),
],
}
},
);
cherryPanel.webview.html = getWebviewContent(
mdInfo,
cherryPanel,
extensionPath,
);
cherryPanel.webview.html = getWebviewContent(mdInfo, cherryPanel, extensionPath);
isCherryPanelInit = true;

initCherryPanelEvent();
Expand All @@ -116,7 +135,7 @@ let scrollTimeOut: NodeJS.Timeout;
// eslint-disable-next-line no-unused-vars, no-undef
let editTimeOut: NodeJS.Timeout;
const initCherryPanelEvent = () => {
cherryPanel?.webview?.onDidReceiveMessage((e) => {
cherryPanel?.webview?.onDidReceiveMessage(async e => {
const { type, data } = e;
switch (type) {
// 滚动的时候同步滚动
Expand All @@ -135,15 +154,20 @@ const initCherryPanelEvent = () => {
// 变更主题的时候同时更新配置
case 'change-theme':
cherryTheme = data;
vscode.workspace.getConfiguration('cherryMarkdown').update('theme', data, true);
vscode.workspace
.getConfiguration('cherryMarkdown')
.update('theme', data, true);
break;
// 内容变更的时候同时更新对应的文档内容
case 'cherry-change':
disableEditTrigger = true;
targetDocument.edit((editBuilder) => {
targetDocument.edit(editBuilder => {
const endNum = targetDocument.document.lineCount + 1;
const end = new vscode.Position(endNum, 0);
editBuilder.replace(new vscode.Range(new vscode.Position(0, 0), end), data.markdown);
editBuilder.replace(
new vscode.Range(new vscode.Position(0, 0), end),
data.markdown,
);
});
editTimeOut && clearTimeout(editTimeOut);
editTimeOut = setTimeout(() => {
Expand All @@ -157,9 +181,20 @@ const initCherryPanelEvent = () => {
// vscode.window.showInformationMessage('暂不支持展示图片,如需要,请前往 https://github.com/Tencent/cherry-markdown 反馈', 'OK');
// loadOneImg(data);
break;
case 'upload-file':
uploadFileHandler(data).then(res => {
if (res.url !== '') {
cherryPanel.webview.postMessage({
cmd: 'upload-file-callback',
data: res,
});
} else {
vscode.window.showInformationMessage('上传不成功');
}
});
break;
}
});

cherryPanel?.onDidDispose(() => {
isCherryPanelInit = false;
});
Expand Down
98 changes: 98 additions & 0 deletions vscodePlugin/src/handler/uploadFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import * as vscode from 'vscode';
import {
UploadType,
CustomUploader,
BackfillImageProps,
BackfillImage,
} from '../types';
import axios, { AxiosResponse } from 'axios';

export interface FileInfo {
name: string;
type: string;
path: string;
size: number;
}

export interface UploadFileHandlerRes extends BackfillImage {
name: string;
url: string;
poster?: string;
}

export const uploadFileHandler = async (fileInfo: FileInfo) => {
const { name = '', type = '', path = '' } = fileInfo;

const UploadType = vscode.workspace
.getConfiguration('cherryMarkdown')
.get<UploadType>('UploadType');

const res: UploadFileHandlerRes = { name, url: '' };

const BackfillImageProps = vscode.workspace
.getConfiguration('cherryMarkdown')
.get<BackfillImageProps>('BackfillImageProps', []);

BackfillImageProps.reduce((prev, curr) => ((prev[curr] = true), prev), res);

switch (UploadType) {
case 'CustomUploader':
// const CustomUploader = vscode.workspace
// .getConfiguration('cherryMarkdown')
// .get<CustomUploader>('CustomUploader');

// if (CustomUploader?.enable !== true) {
// vscode.window.showInformationMessage('请完善自定义上传配置');
// throw new Error('请完善自定义上传配置');
// }
// if (/^(http|https):\/\//.test(CustomUploader.url) == false) {
// vscode.window.showInformationMessage('自定义上传地址格式不正确');
// throw new Error('自定义上传地址格式不正确');
// }
// const file = await vscode.workspace.fs.readFile(vscode.Uri.file(path));
// // 将file上传到自定义的地址
// // 这里涉及到一些上传服务需要签名校验,并且响应体格式不一致,这里要再讨论
// const customUpload = await axios.post(CustomUploader.url, file);
vscode.window.showInformationMessage('自定义上传暂未开发');
throw new Error('自定义上传暂未开发');
break;
case 'PicGoServer':
const PicGoServer = vscode.workspace
.getConfiguration('cherryMarkdown')
.get<string>('PicGoServer', 'http://127.0.0.1:36677/upload');
// 请求PicGo服务
const upload = await axios.post<
any,
AxiosResponse<{ success: boolean; result: string[] }>,
{ list: string[] }
>(
PicGoServer,
{
list: [path],
},
{
headers: {
'Content-Type': 'application/json',
},
},
);
if (upload.data?.success !== true) {
throw new Error('上传失败');
} else {
res.url = upload.data?.result?.[0] ?? '';
}
break;
default:
if (type.startsWith('image')) {
// 读取图片转为base64
const file = await vscode.workspace.fs.readFile(vscode.Uri.file(path));
const base64 = Buffer.from(file).toString('base64');
res.url = `data:${type};base64,${base64}`;
} else {
vscode.window.showInformationMessage('未指定上传服务时暂时只支持图片');
throw new Error('未指定上传服务时暂时只支持图片');
}
break;
}
return res;
};
1 change: 1 addition & 0 deletions vscodePlugin/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './upload';
Loading

0 comments on commit 4fdbb93

Please sign in to comment.