Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#434 vscode插件支持配置图床 #534

Merged
merged 2 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading