🗑️ Clean code
Build Plugin JAR File / build (push) Waiting to run Details
Build Plugin JAR File / github-release (push) Blocked by required conditions Details

This commit is contained in:
zhengyi 2024-02-06 16:21:54 +08:00
parent ed5f59cb5c
commit 527ce8ceab
11 changed files with 274 additions and 18 deletions

View File

@ -42,5 +42,6 @@
"vite": "^3.2.7",
"vitest": "^0.24.5",
"vue-tsc": "^1.6.5"
}
},
"packageManager": "pnpm@8.0.0"
}

View File

@ -1,7 +1,7 @@
import {definePlugin, type EditorProvider} from "@halo-dev/console-shared";
import Vditor from "./views/Vditor.vue";
import logo from "./assets/vditor.png"
import {markRaw} from "vue";
import { definePlugin, type EditorProvider } from "@halo-dev/console-shared";
import logo from "./assets/vditor.png";
import { markRaw } from "vue";
import VditorMde from "./views/VditorMde.vue";
export default definePlugin({
components: {},
@ -11,10 +11,9 @@ export default definePlugin({
{
name: "vditor-mde",
displayName: "Vditor Markdown",
// @ts-ignore
component: markRaw(Vditor),
component: markRaw(VditorMde),
rawType: "markdown",
logo: logo
logo: logo,
},
];
},

View File

@ -1,5 +1,5 @@
import type { Schema } from "@/type/editor";
import {t} from "@/utils/i18n-utils";
import { t } from "@/utils/i18n-utils";
const schema: Schema = {
type: "template",

View File

@ -1,4 +1,4 @@
import type {EditorConfig} from "@/utils/config-utils";
import type { EditorConfig } from "@/utils/config-utils";
export declare type Options = {
defaultRenderMode: "ir" | "wysiwyg" | "sv" | undefined;

View File

@ -2,7 +2,7 @@ import zhCN from "@/i18n/zh-CN";
import type { I18nLang } from "@/type/i18n";
import zhTW from "@/i18n/zh-TW";
import enUS from "@/i18n/en-US";
import {getLanguage} from "@/utils/vditor-utils";
import { getLanguage } from "@/utils/vditor-utils";
const langDict: { [key: string]: I18nLang } = {
zh_CN: zhCN,

View File

@ -5,7 +5,7 @@ import tips from "@/schema/tips";
import git from "@/schema/git";
import drive from "@/schema/drive";
import gallery from "@/schema/gallery";
import {addScript, addStyle, addStyleSheet} from "@/utils/dom-utils";
import { addScript, addStyleSheet } from "@/utils/dom-utils";
declare const HaloJs: {
renderHalo: (content: string, cdn: string) => string;
@ -197,6 +197,9 @@ function getCustomRenders(options: Options):
render: (element: HTMLElement, vditor: IVditor) => void;
}[]
| undefined {
if (options.config.developer.debugger) {
console.log("QUICK INSERT: ", options.quickInsertList);
}
const renders: {
language: string;
render: (element: HTMLElement, vditor: IVditor) => void;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,255 @@
<script setup lang="ts">
import Vditor from "vditor";
import { onMounted, onUnmounted, ref } from "vue";
import "vditor/dist/index.css";
import type { Schema } from "@/type/editor";
import { getOptions } from "@/utils/vditor-utils";
import type { AttachmentLike } from "@halo-dev/console-shared";
import type { Attachment } from "@halo-dev/api-client";
import { VLoading } from "@halo-dev/components";
import TemplateModal from "@/model/TemplateModal.vue";
import joeProgress from "@/schema/joe-progress";
import { fetchAllQuickInsert } from "@/utils/fetch-utils";
import { quickInsertInject } from "@/utils/quick-insert-utils";
import { addStyle } from "@/utils/dom-utils";
import { getCursor, setCursor } from "@/utils/cursor-utils";
import { defaultEditorConfig, type EditorConfig } from "@/utils/config-utils";
const props = withDefaults(
defineProps<{
raw?: string;
content: string;
uploadImage?: (file: File) => Promise<Attachment>;
}>(),
{
raw: "",
content: "",
uploadImage: undefined,
}
);
const vditor = ref();
const vditorRef = ref();
const vditorLoaded = ref(false);
const attachmentSelectorModalShow = ref(false);
// none/tips/git
//
const customInsertOpen = ref(false);
const customInsertSchema = ref<Schema>(joeProgress);
let lastSelectionRange: Range | undefined = undefined;
// Image Upload
let imageUploadCursor: Range | undefined;
const imageUploadLock = false;
let allowImageUpload: string[] = [];
const emit = defineEmits<{
(event: "update:raw", value: string): void;
(event: "update:content", value: string): void;
(event: "update", value: string): void;
}>();
const debounceOnUpdate = () => {
emit("update:raw", vditor.value.getValue());
emit("update:content", vditor.value.getHTML() || "");
emit("update", vditor.value.getValue());
};
//
const attachmentSelect = (attachments: AttachmentLike[]) => {
// Reference https://github.com/guqing/willow-mde/blob/4b8e697132f8a8f4b08dd0f92cf10d070cb26793/console/src/components/toolbar/Toolbar.vue#L104
attachments.forEach((attachment) => {
if (typeof attachment === "string") {
vditor.value.insertValue(`![](${attachment})`);
} else if ("url" in attachment) {
vditor.value.insertValue(`![${attachment.type}](${attachment.url})`);
} else if ("spec" in attachment) {
const { displayName } = attachment.spec;
const { permalink } = attachment.status || {};
vditor.value.insertValue(`![${displayName}](${permalink})`);
}
});
};
onUnmounted(async () => {
document
.querySelectorAll("script[id^='vditor']")
.forEach((el) => el.remove());
document.querySelectorAll("link[id^='vditor']").forEach((el) => el.remove());
vditorLoaded.value = false;
});
onMounted(async () => {
// :
const lang = localStorage.getItem("locale") || "zh-CN";
let editorConfig: EditorConfig;
try {
const response = await fetch(
"/apis/api.vditor.mczhengyi.top/editor-options"
);
editorConfig = await response.json();
} catch (e) {
// ignore this
editorConfig = defaultEditorConfig;
}
// Assign allowImage
allowImageUpload = allowImageUpload.concat(
editorConfig.extension.allowImageType.split(",").map((i) => i.trim())
);
console.log("ALLOW", allowImageUpload);
// HTML
if (editorConfig.basic.disableHTMLBlockPreview)
addStyle(
"[data-type=html-block] pre {display: block!important;}\n" +
".vditor-ir__node[data-type=html-block] .vditor-ir__marker {height: auto; width: auto; display: inline;}",
"vditor-mde-hide-html"
);
const qil = await fetchAllQuickInsert(editorConfig.basic.quickInsertUrl);
qil.forEach((q) => {
quickInsertInject(q.inject || [], q.provider);
});
vditor.value = new Vditor(
vditorRef.value,
getOptions({
defaultRenderMode: editorConfig.basic.defaultRenderMode,
typeWriterMode: editorConfig.basic.typeWriterMode,
after: () => {
vditor.value.setValue(props.raw || "# Title Here");
vditorLoaded.value = true;
},
input: debounceOnUpdate,
showAttachment: () => (attachmentSelectorModalShow.value = true),
language: lang,
codeBlockPreview: editorConfig.basic.codeBlockPreview,
uploadImage: (files: File[]) => {
console.log("UPLOAD IMAGE");
if (imageUploadLock) {
vditor.value.tip("当前已经存在正在上传的文件,请等待上传完成", 2000);
return;
}
// Save cursor
imageUploadCursor = getCursor();
// Check extension name
const extendName = files[0].name
.slice(files[0].name.lastIndexOf(".") + 1)
.toLowerCase();
if (allowImageUpload.indexOf(extendName) === -1) {
vditor.value.tip("不允许上传该类型图片!", 2000);
return null;
}
// Upload
if (props.uploadImage) {
vditor.value.tip("正在上传图片...", 2000);
props.uploadImage(files[0]).then((res: Attachment) => {
if (!res.status) return;
vditor.value.disabled();
// Move cursor
const tmpCursor = getCursor();
setCursor(imageUploadCursor);
imageUploadCursor = undefined;
// Insert
vditor.value.insertValue(
`\n\n![${res.spec.displayName}](${res.status.permalink})\n\n`
);
// Restore cursor
setCursor(tmpCursor);
vditor.value.enable();
});
}
return null;
},
openModal: (schema: Schema) => {
lastSelectionRange = getCursor();
customInsertSchema.value = schema;
customInsertOpen.value = true;
},
enableQuickInsert: editorConfig.basic.enableQuickInsert,
quickInsertList: qil,
config: editorConfig,
})
);
});
const update = (val: string | null) => {
setCursor(lastSelectionRange);
if (!val) {
vditor.value.tip("未知错误,插入失败", 3000);
} else {
vditor.value.focus();
vditor.value.insertValue(`\n\n${val}\n\n`);
}
customInsertOpen.value = false;
};
</script>
<template>
<div id="plugin-vditor-mde">
<VLoading v-if="!vditorLoaded" style="height: 100%" />
<div id="vditor" ref="vditorRef"></div>
<TemplateModal
:open="customInsertOpen"
:schema="customInsertSchema"
@close="customInsertOpen = false"
@done="update"
/>
<AttachmentSelectorModal
v-model:visible="attachmentSelectorModalShow"
:accepts="['image/*']"
:max="1"
@select="attachmentSelect"
/>
</div>
</template>
<style>
#plugin-vditor-mde ol {
list-style: decimal;
}
/** Fix Link color in wysiwyg mode */
#plugin-vditor-mde a {
color: #3478cd;
}
/** Fix content was covered by vditor panel in wysiwyg mode */
#plugin-vditor-mde button,
#plugin-vditor-mde input {
line-height: normal;
}
.insert-modals label {
width: 100%;
display: flex;
}
.insert-modals label span {
width: 60px;
text-align: right;
}
.insert-modals select {
border: 1px solid #cccccc;
border-radius: 3px;
padding-top: 8px;
padding-bottom: 8px;
margin-left: 10px;
flex: 1;
}
.insert-modals textarea {
border: 1px solid #cccccc;
border-radius: 3px;
margin-left: 10px;
flex: 1;
}
.insert-modals input[type="text"] {
border: 1px solid #cccccc;
border-radius: 3px;
margin-left: 10px;
flex: 1;
padding: 8px 10px;
}
</style>

View File

@ -15,7 +15,7 @@ export default ({ mode }: { mode: string }) => {
: "../build/resources/main/console";
return defineConfig({
plugins: [Vue(),VueJsx(), Icons({ compiler: "vue3" })],
plugins: [Vue(), VueJsx(), Icons({ compiler: "vue3" })],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),

View File

@ -1,8 +1,8 @@
package top.mczhengyi.vditor;
import org.pf4j.PluginWrapper;
import org.springframework.stereotype.Component;
import run.halo.app.plugin.BasePlugin;
import run.halo.app.plugin.PluginContext;
/**
* <p>Plugin main class to manage the lifecycle of the plugin.</p>
@ -15,8 +15,8 @@ import run.halo.app.plugin.BasePlugin;
@Component
public class VditorMdePlugin extends BasePlugin {
public VditorMdePlugin(PluginWrapper wrapper) {
super(wrapper);
public VditorMdePlugin(PluginContext pluginContext) {
super(pluginContext);
}
@Override

View File

@ -5,7 +5,6 @@ import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.pf4j.PluginWrapper;
import org.springframework.stereotype.Component;
import org.springframework.util.PropertyPlaceholderHelper;
import reactor.core.publisher.Mono;
import run.halo.app.plugin.ReactiveSettingFetcher;
import run.halo.app.theme.ReactivePostContentHandler;