You've already forked filebrowser
mirror of
https://github.com/filebrowser/filebrowser.git
synced 2025-11-26 14:25:26 +08:00
feat: implement markdown file preview in Ace editor (#3431)
--------- Co-authored-by: Oleg Lobanov <oleg@lobanov.me>
This commit is contained in:
@@ -11,11 +11,26 @@
|
||||
:label="t('buttons.save')"
|
||||
@action="save()"
|
||||
/>
|
||||
|
||||
<action
|
||||
icon="preview"
|
||||
:label="t('buttons.preview')"
|
||||
@action="preview()"
|
||||
v-show="isMarkdownFile"
|
||||
/>
|
||||
</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 +48,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 +67,37 @@ 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 +124,7 @@ onMounted(() => {
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener("keydown", keyEvent);
|
||||
window.removeEventListener("wheel", handleScroll);
|
||||
editor.value?.destroy();
|
||||
});
|
||||
|
||||
@@ -102,6 +145,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 +176,8 @@ const close = () => {
|
||||
let uri = url.removeLastDir(route.path) + "/";
|
||||
router.push({ path: uri });
|
||||
};
|
||||
|
||||
const preview = () => {
|
||||
isPreview.value = !isPreview.value;
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user