Skip to content

Commit

Permalink
Preseve PDF on view change
Browse files Browse the repository at this point in the history
  • Loading branch information
pulsejet committed Feb 21, 2025
1 parent a73fe06 commit ea540f9
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 65 deletions.
4 changes: 1 addition & 3 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
<NavBar></NavBar>
<div class="router-view-inner">
<RouterView v-slot="{ Component }">
<Transition name="fade-2" mode="out-in">
<component :is="Component" />
</Transition>
<component :is="Component" />
</RouterView>
</div>
</main>
Expand Down
3 changes: 1 addition & 2 deletions src/assets/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ main.full-h {
}

.absolute-center,
.fixed-center,
.center-spinner > .spinner {
.fixed-center {
position: absolute;
top: 50%;
left: 50%;
Expand Down
26 changes: 18 additions & 8 deletions src/components/files/CodeEditor.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<template>
<div ref="container" class="container"></div>
<div ref="outer" class="outer"></div>
</template>

<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref, type PropType } from 'vue';
import { onBeforeUnmount, onMounted, ref, watch, type PropType } from 'vue';
import * as monaco from 'monaco-editor';
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
Expand Down Expand Up @@ -38,12 +38,22 @@ const props = defineProps({
},
});
const container = ref<InstanceType<typeof HTMLDivElement> | null>(null);
const outer = ref<InstanceType<typeof HTMLDivElement> | null>(null);
let editor: monaco.editor.IStandaloneCodeEditor | null = null;
let ybinding: MonacoBinding | null = null;
onMounted(() => {
watch(
() => props.ytext,
() => {
destroy();
create();
},
);
onMounted(create);
onBeforeUnmount(destroy);
function create() {
monacoRegister();
const ext = props.basename.split('.').pop()?.toLocaleLowerCase();
Expand All @@ -66,7 +76,7 @@ onMounted(() => {
break;
}
editor = monaco.editor.create(container.value!, {
editor = monaco.editor.create(outer.value!, {
value: String(),
language: language,
theme: utils.themeIsDark() ? 'vs-dark' : 'vs',
Expand All @@ -84,13 +94,13 @@ onMounted(() => {
new Set([editor]),
props.awareness,
);
});
}
onBeforeUnmount(() => {
function destroy() {
ybinding?.destroy();
editor?.getModel()?.dispose();
editor?.dispose();
});
}
</script>

<style scoped lang="scss">
Expand Down
31 changes: 21 additions & 10 deletions src/components/files/MilkdownEditor.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<template>
<div ref="container" class="container"></div>
<div ref="outer" class="outer"></div>
</template>

<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref, type PropType } from 'vue';
import { onBeforeUnmount, onMounted, ref, watch, type PropType } from 'vue';
import * as Y from 'yjs';
import type { Awareness } from 'y-protocols/awareness.js';
Expand All @@ -25,20 +25,30 @@ const props = defineProps({
},
});
const container = ref<InstanceType<typeof HTMLDivElement> | null>(null);
const outer = ref<InstanceType<typeof HTMLDivElement> | null>(null);
let crepe: Crepe | null = null;
let collabService: CollabService | null = null;
onMounted(async () => {
watch(
() => props.yxml,
async () => {
await destroy();
await create();
},
);
onMounted(create);
onBeforeUnmount(destroy);
async function create() {
if (utils.themeIsDark()) {
await import('@milkdown/crepe/theme/frame-dark.css');
} else {
await import('@milkdown/crepe/theme/frame.css');
}
crepe = new Crepe({
root: container.value!,
root: outer.value!,
});
crepe.editor.use(collab);
await crepe.create();
Expand All @@ -47,18 +57,19 @@ onMounted(async () => {
collabService = ctx.get(collabServiceCtx);
collabService.bindXmlFragment(props.yxml).setAwareness(props.awareness).connect();
});
});
}
onBeforeUnmount(() => {
async function destroy() {
collabService?.disconnect();
crepe?.destroy();
});
await crepe?.destroy();
}
</script>

<style scoped lang="scss">
.container :deep(.milkdown) {
.outer :deep(.milkdown) {
height: 100vh;
overflow-y: scroll;
overflow-x: hidden;
}
</style>

Expand Down
33 changes: 16 additions & 17 deletions src/components/files/PdfViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
>
Compile
</button>
<button class="button is-small soft-if-dark ml-1" :disabled="!rawPdf" @click="download">
<button class="button is-small soft-if-dark ml-1" :disabled="!pdf" @click="download">
Download
</button>
</div>
Expand All @@ -43,10 +43,10 @@
<!-- PDF Viewer -->
<div class="pdf-content">
<VuePdfEmbed
v-if="pdf && width"
v-if="pdfCopy && width"
annotation-layer
text-layer
:source="pdf"
:source="pdfCopy"
:width="width"
@loaded="loaded = true"
/>
Expand Down Expand Up @@ -103,28 +103,27 @@ const props = defineProps({
const pdfviewer = ref<InstanceType<typeof HTMLDivElement> | null>(null);
const width = ref(0);
const loaded = ref(false);
const rawPdf = shallowRef<Uint8Array | null>(null);
const pdfCopy = shallowRef<Uint8Array | null>(null);
watch(
() => props.pdf,
() => {
loaded.value = false;
// Copy the pdf to prevent viewer from destroying the original
// This is needed for the download button to work
rawPdf.value = props.pdf ? new Uint8Array(props.pdf) : null;
},
);
watch(() => props.pdf, create);
onMounted(() => {
width.value = (pdfviewer.value?.clientWidth ?? 800) - 20;
width.value = Math.max((pdfviewer.value?.clientWidth ?? 800) - 20, 400);
create();
});
function create() {
loaded.value = false;
// Copy the pdf to prevent viewer from destroying the original
// This is needed for the download button to work
pdfCopy.value = props.pdf ? new Uint8Array(props.pdf) : null;
}
async function download() {
if (!rawPdf.value) return;
if (!props.pdf) return;
const fileStream = streamSaver.createWriteStream(props.filename);
const writer = fileStream.getWriter();
await writer.write(rawPdf.value);
await writer.write(props.pdf);
await writer.close();
}
</script>
Expand Down
70 changes: 45 additions & 25 deletions src/views/ProjectFileView.vue
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
<template>
<div class="container" v-if="loading">
<div class="absolute-center">
<div class="outer">
<div class="absolute-center" v-if="loading">
<LoadingSpinner />
Loading your file ...
</div>
</div>

<div v-else-if="contentCode" class="container center-spinner">
<Suspense>
<Suspense v-if="contentCode">
<div class="code">
<CodeEditor
class="editor"
:ytext="contentCode"
:key="filepath"
:basename="basename"
:awareness="awareness!"
/>
Expand All @@ -35,10 +32,8 @@
</div>
</template>
</Suspense>
</div>

<div v-else-if="contentMilk" class="center-spinner">
<MilkdownEditor :yxml="contentMilk" :awareness="awareness!" />
<MilkdownEditor v-else-if="contentMilk" :yxml="contentMilk" :awareness="awareness!" />
</div>
</template>

Expand Down Expand Up @@ -95,7 +90,7 @@ const awareness = shallowRef<awareProto.Awareness | null>(null);
const contentCode = shallowRef<Y.Text | null>(null);
const contentMilk = shallowRef<Y.XmlFragment | null>(null);
const isLatex = computed(() => utils.isExtensionType(basename.value, 'latex'));
const isLatex = ref<boolean>(false); // ref to prevent ui glitch
const resultPdfName = computed(() => `${proj.value?.name}.pdf`);
const resultPdf = shallowRef<Uint8Array | null>(null);
const isPdfCompiling = ref(false);
Expand All @@ -109,29 +104,49 @@ onBeforeUnmount(destroy);
async function create() {
try {
loading.value = true;
await destroy();
// Load workspace
const wksp = await Workspace.setupOrRedir(router);
if (!wksp) return;
if (proj.value?.name !== projName.value) {
await destroy();
// Load project
proj.value = await wksp.proj.get(projName.value);
if (!proj.value) return;
await proj.value.activate();
// Load workspace
const wksp = await Workspace.setupOrRedir(router);
if (!wksp) return;
// Load project
proj.value = await wksp.proj.get(projName.value);
if (!proj.value) return;
await proj.value.activate();
}
// Load file content
contentDoc.value = await proj.value.getFile(filepath.value);
let newDoc: Y.Doc | null = null;
let newAwareness: awareProto.Awareness | null = null;
let newContentCode: Y.Text | null = null;
let newContentMilk: Y.XmlFragment | null = null;
if (utils.isExtensionType(basename.value, 'code')) {
awareness.value = await proj.value.getAwareness(filepath.value);
contentCode.value = contentDoc.value.getText('text');
// Text file (show as code)
newDoc = await proj.value.getFile(filepath.value);
newAwareness = await proj.value.getAwareness(filepath.value);
newContentCode = newDoc.getText('text');
} else if (utils.isExtensionType(basename.value, 'milkdown')) {
awareness.value = await proj.value.getAwareness(filepath.value);
contentMilk.value = contentDoc.value.getXmlFragment('milkdown');
// Milkdown XML fragment
newDoc = await proj.value.getFile(filepath.value);
newAwareness = await proj.value.getAwareness(filepath.value);
newContentMilk = newDoc.getXmlFragment('milkdown');
} else {
// Unsupported file type
throw new Error(`Unsupported content extension: ${basename.value}`);
}
// Do the reset synchronously so that the UI does not refresh
// This means that there will be no flicker and the PDF will stay.
resetDoc();
contentDoc.value = newDoc;
awareness.value = newAwareness;
contentCode.value = newContentCode;
contentMilk.value = newContentMilk;
isLatex.value = utils.isExtensionType(basename.value, 'latex');
} catch (err) {
console.error(err);
toast.error(`Failed to load file: ${err}`);
Expand All @@ -140,14 +155,17 @@ async function create() {
}
}
async function destroy() {
function resetDoc() {
contentCode.value = null;
contentMilk.value = null;
awareness.value = null;
contentDoc.value?.destroy();
contentDoc.value = null;
}
async function destroy() {
resetDoc();
isPdfCompiling.value = false;
resultPdf.value = null;
resultError.value = String();
Expand All @@ -168,8 +186,10 @@ async function compileLatex() {
</script>

<style scoped lang="scss">
.container {
.outer {
position: relative;
height: 100%;
width: 100%;
}
.code {
Expand Down

0 comments on commit ea540f9

Please sign in to comment.