feat: add implement markdown file preview in Ace editor

pull/3431/head
ultwcz 2024-08-28 20:53:01 +08:00
parent 8e67a12f26
commit 56ff13528f
7 changed files with 73 additions and 2 deletions

View File

@ -18,6 +18,7 @@
"js-base64": "^3.7.7",
"jwt-decode": "^4.0.0",
"lodash-es": "^4.17.21",
"marked": "^14.1.0",
"material-icons": "^1.13.12",
"normalize.css": "^8.0.1",
"pinia": "^2.1.7",
@ -5810,6 +5811,18 @@
"node": ">=12"
}
},
"node_modules/marked": {
"version": "14.1.0",
"resolved": "https://registry.npmmirror.com/marked/-/marked-14.1.0.tgz",
"integrity": "sha512-P93GikH/Pde0hM5TAXEd8I4JAYi8IB03n8qzW8Bh1BIEFpEyBoYxi/XWZA53LSpTeLBiMQOoSMj0u5E/tiVYTA==",
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/marks-pane": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/marks-pane/-/marks-pane-1.0.9.tgz",

View File

@ -29,6 +29,7 @@
"jwt-decode": "^4.0.0",
"lodash-es": "^4.17.21",
"material-icons": "^1.13.12",
"marked": "^14.1.0",
"normalize.css": "^8.0.1",
"pinia": "^2.1.7",
"pretty-bytes": "^6.1.1",

View File

@ -0,0 +1,13 @@
.md_preview {
overflow-y:auto;
max-height: 80vh;
padding: 1rem;
border: 1px solid #000;
font-size: 20px;
line-height: 1.2;
}
#preview-container {
overflow: auto;
max-height: 80vh; /* Match the max-height of md_preview for scrolling */
}

View File

@ -16,6 +16,7 @@
@import "./login.css";
@import "./mobile.css";
@import "./epubReader.css";
@import "./mdPreview.css";
/* For testing only
:focus {

View File

@ -24,6 +24,7 @@
"ok": "OK",
"permalink": "Get Permanent Link",
"previous": "Previous",
"preview": "Preview",
"publish": "Publish",
"rename": "Rename",
"replace": "Replace",

View File

@ -22,6 +22,7 @@
"ok": "确定",
"permalink": "获取永久链接",
"previous": "上一个",
"preview": "预览",
"publish": "发布",
"rename": "重命名",
"replace": "替换",

View File

@ -11,11 +11,16 @@
:label="t('buttons.save')"
@action="save()"
/>
<action icon="preview" :label="t('buttons.preview')" @action="preview()" />
</header-bar>
<Breadcrumbs base="/files" noLink />
<form id="editor"></form>
<!-- preview container -->
<div v-show="isPreview && isMarkdownFile" id="preview-container" class="md_preview" v-html="previewContent"></div>
<form v-show="!isPreview || !isMarkdownFile" id="editor"></form>
</div>
</template>
@ -33,10 +38,11 @@ import Breadcrumbs from "@/components/Breadcrumbs.vue";
import { useAuthStore } from "@/stores/auth";
import { useFileStore } from "@/stores/file";
import { useLayoutStore } from "@/stores/layout";
import { inject, onBeforeUnmount, onMounted, ref } from "vue";
import { inject, onBeforeUnmount, onMounted, ref, watchEffect } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
import { getTheme } from "@/utils/theme";
import { marked } from 'marked';
const $showError = inject<IToastError>("$showError")!;
@ -51,11 +57,33 @@ const router = useRouter();
const editor = ref<Ace.Editor | null>(null);
const isPreview = ref(false);
const previewContent = ref("");
const isMarkdownFile = fileStore.req?.name.endsWith('.md') || fileStore.req?.name.endsWith('.markdown');
onMounted(() => {
window.addEventListener("keydown", keyEvent);
window.addEventListener("wheel", handleScroll);
const fileContent = fileStore.req?.content || "";
watchEffect(async () => {
if (isMarkdownFile && isPreview.value) {
const new_value = editor.value?.getValue() || "";
try {
previewContent.value = await marked(new_value);
} catch (error) {
console.error("Failed to convert content to HTML:", error);
previewContent.value = "";
}
const previewContainer = document.getElementById('preview-container');
if (previewContainer) {
previewContainer.addEventListener("wheel", handleScroll, { capture: true });
}
}
});
ace.config.set(
"basePath",
`https://cdn.jsdelivr.net/npm/ace-builds@${ace_version}/src-min-noconflict/`
@ -82,6 +110,7 @@ onMounted(() => {
onBeforeUnmount(() => {
window.removeEventListener("keydown", keyEvent);
window.removeEventListener("wheel", handleScroll);
editor.value?.destroy();
});
@ -102,6 +131,13 @@ const keyEvent = (event: KeyboardEvent) => {
save();
};
const handleScroll = (event: WheelEvent) => {
const editorContainer = document.getElementById('preview-container');
if (editorContainer) {
editorContainer.scrollTop += event.deltaY;
}
};
const save = async () => {
const button = "save";
buttons.loading("save");
@ -126,4 +162,9 @@ const close = () => {
let uri = url.removeLastDir(route.path) + "/";
router.push({ path: uri });
};
const preview = () => {
isPreview.value = !isPreview.value;
};
</script>