obsidian-visualiser/components/EditableMarkdown.vue

131 lines
3.3 KiB
Vue

<style>
.editor
{
white-space: pre-line;
overflow: auto;
outline: none;
box-shadow: none !important;
}
</style>
<template>
<div class="editor" contenteditable>
<template
v-if="model && model.length > 0">
<MarkdownRenderer
v-if="node"
:key="key"
:node="node"
:proses="{
'a': LiveA,
'h1': LiveH1,
'h2': LiveH2,
'h3': LiveH3,
'h4': LiveH4,
'h5': LiveH5,
'h6': LiveH6,
'blockquote': LiveBlockquote,
}"
></MarkdownRenderer>
</template>
</div>
</template>
<script setup lang="ts">
import LiveA from "~/components/prose/live/LiveA.vue";
import LiveH1 from "~/components/prose/live/LiveH1.vue";
import LiveH2 from "~/components/prose/live/LiveH2.vue";
import LiveH3 from "~/components/prose/live/LiveH3.vue";
import LiveH4 from "~/components/prose/live/LiveH4.vue";
import LiveH5 from "~/components/prose/live/LiveH5.vue";
import LiveH6 from "~/components/prose/live/LiveH6.vue";
import LiveBlockquote from "~/components/prose/ProseBlockquote.vue";
import { hash } from 'ohash'
import { watch, computed } from 'vue'
import type { Root, Node } from 'hast';
import { diffLines as diff } from 'diff';
const model = defineModel<string>();
const parser = useMarkdown();
const key = computed(() => hash(model.value));
const node = ref<Root>(), changes = ref();
watch(model, update);
update(model.value, "");
async function update(value: string | undefined, old: string | undefined) {
if(value && old)
{
if(node.value)
{
const differences = diff(old, value, {
newlineIsToken: true,
});
let removeStart = 0, removeEnd = 0; //Character count
let addStart = 0, addEnd = 0; //Character count
const needAdd = differences.find(e => e.added) !== undefined;
const needRemove = differences.find(e => e.removed) !== undefined;
for(const difference of differences)
{
if(!difference.added && !difference.removed)
{
removeStart += difference.value.length;
addStart += difference.value.length;
}
else if(difference.added)
{
addEnd = addStart + difference.value.length;
}
else if(difference.removed)
{
removeEnd = removeStart + difference.value.length;
}
if((!needAdd || addEnd !== 0) && (!needRemove || removeEnd !== 0))
break;
}
const oldNodes = getNodes(node.value.children, removeStart - 1, removeEnd + 1);
let newNodes;
if(oldNodes.length === 0)
{
node.value = parser(value);
}
else
{
const newStart = oldNodes[0].position?.start.offset;
const newEnd = oldNodes[oldNodes.length - 1].position?.end.offset;
const lengthDiff = value.length - old.length;
newNodes = parser(value.substring(newStart ?? 0, (newEnd ?? 0) + lengthDiff));
const root = node.value;
//root.position?.end.offset
node.value = parser(value);
}
console.log(node.value);
}
else
{
node.value = parser(value);
}
}
else if(value)
{
node.value = parser(value);
}
}
function getNodes(nodes: Node[], start: number, end: number)
{
return nodes.filter(e => (e.position?.start.offset ?? 0) <= end && (e.position?.end.offset ?? 0) >= start);
}
</script>