You've already forked obsidian-visualiser
New HyperMD implementation with custom behaviour.
This commit is contained in:
@@ -1,107 +1,157 @@
|
||||
<script lang="ts">
|
||||
const External = Annotation.define<boolean>();
|
||||
import type { Editor, EditorConfiguration, EditorChange } from 'codemirror';
|
||||
import { changeEnd } from 'codemirror';
|
||||
import { fromTextArea } from 'hypermd';
|
||||
|
||||
import '#shared/hypermd.extend';
|
||||
|
||||
function onChange(cm: Editor, change: EditorChange)
|
||||
{
|
||||
if (changeEnd(change).line == cm.lastLine())
|
||||
updateBottomMargin(cm);
|
||||
}
|
||||
|
||||
|
||||
function updateBottomMargin(cm: Editor)
|
||||
{
|
||||
let padding = "";
|
||||
if (cm.lineCount() > 1)
|
||||
{
|
||||
//@ts-ignore
|
||||
let totalH = cm.display.scroller.clientHeight - 30, lastLineH = cm.getLineHandle(cm.lastLine()).height;
|
||||
padding = (totalH / 2 - lastLineH) + "px";
|
||||
}
|
||||
|
||||
if (cm.state.scrollPastEndPadding != padding)
|
||||
{
|
||||
cm.state.scrollPastEndPadding = padding;
|
||||
cm.display.lineSpace.parentNode.style.paddingBottom = padding;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dropCursor, crosshairCursor, keymap, EditorView, ViewUpdate, placeholder as placeholderExtension } from '@codemirror/view';
|
||||
import { Annotation, EditorState } from '@codemirror/state';
|
||||
import { indentOnInput, syntaxHighlighting, defaultHighlightStyle, bracketMatching, foldKeymap } from '@codemirror/language';
|
||||
import { history, defaultKeymap, historyKeymap } from '@codemirror/commands';
|
||||
import { search, searchKeymap } from '@codemirror/search';
|
||||
import { closeBrackets, closeBracketsKeymap, completionKeymap } from '@codemirror/autocomplete';
|
||||
import { lintKeymap } from '@codemirror/lint';
|
||||
|
||||
const editor = useTemplateRef('editor');
|
||||
const view = ref<EditorView>();
|
||||
const state = ref<EditorState>();
|
||||
|
||||
const { placeholder, autofocus = false } = defineProps<{
|
||||
const { placeholder, autofocus = false, gutters = true, format = 'd-any' } = defineProps<{
|
||||
placeholder?: string
|
||||
autofocus?: boolean
|
||||
gutters?: boolean
|
||||
format?: 'hypermd' | 'd-any'
|
||||
}>();
|
||||
const model = defineModel<string>();
|
||||
const editor = ref<Editor>();
|
||||
|
||||
const input = useTemplateRef('input');
|
||||
|
||||
onMounted(() => {
|
||||
if(editor.value)
|
||||
if(input.value)
|
||||
{
|
||||
state.value = EditorState.create({
|
||||
doc: model.value,
|
||||
extensions: [
|
||||
history(),
|
||||
search(),
|
||||
dropCursor(),
|
||||
EditorState.allowMultipleSelections.of(true),
|
||||
indentOnInput(),
|
||||
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
||||
bracketMatching(),
|
||||
closeBrackets(),
|
||||
crosshairCursor(),
|
||||
placeholderExtension(placeholder ?? ''),
|
||||
EditorView.lineWrapping,
|
||||
keymap.of([
|
||||
...closeBracketsKeymap,
|
||||
...defaultKeymap,
|
||||
...searchKeymap,
|
||||
...historyKeymap,
|
||||
...foldKeymap,
|
||||
...completionKeymap,
|
||||
...lintKeymap
|
||||
]),
|
||||
EditorView.updateListener.of((viewUpdate: ViewUpdate) => {
|
||||
if (viewUpdate.docChanged && !viewUpdate.transactions.some(tr => tr.annotation(External)))
|
||||
{
|
||||
model.value = viewUpdate.state.doc.toString();
|
||||
}
|
||||
}),
|
||||
EditorView.contentAttributes.of({spellcheck: "true"}),
|
||||
]
|
||||
});
|
||||
view.value = new EditorView({
|
||||
state: state.value,
|
||||
parent: editor.value,
|
||||
});
|
||||
const e = editor.value = fromTextArea(input.value, {
|
||||
mode: {
|
||||
name: format,
|
||||
hashtag: true,
|
||||
toc: false,
|
||||
math: false,
|
||||
orgModeMarkup: false,
|
||||
tokenTypeOverrides: {
|
||||
hr: "line-HyperMD-hr line-background-HyperMD-hr-bg hr",
|
||||
list1: "list-1",
|
||||
list2: "list-2",
|
||||
list3: "list-3",
|
||||
code: "inline-code",
|
||||
hashtag: "hashtag meta"
|
||||
}
|
||||
},
|
||||
spellcheck: true,
|
||||
autofocus: autofocus,
|
||||
lineNumbers: false,
|
||||
showCursorWhenSelecting: true,
|
||||
indentUnit: 4,
|
||||
autoCloseBrackets: true,
|
||||
foldGutter: gutters,
|
||||
theme: 'custom'
|
||||
} as EditorConfiguration);
|
||||
|
||||
if(autofocus)
|
||||
{
|
||||
view.value.focus();
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (view.value) {
|
||||
view.value?.destroy()
|
||||
view.value = undefined
|
||||
e.setValue(model.value ?? '');
|
||||
updateBottomMargin(e);
|
||||
e.on('change', onChange);
|
||||
e.on('change', (cm: Editor, change: EditorChange) => model.value = cm.getValue());
|
||||
e.on('refresh', updateBottomMargin);
|
||||
}
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (model.value === void 0) {
|
||||
return;
|
||||
}
|
||||
const currentValue = view.value ? view.value.state.doc.toString() : "";
|
||||
if (view.value && model.value !== currentValue) {
|
||||
view.value.dispatch({
|
||||
changes: { from: 0, to: currentValue.length, insert: model.value || "" },
|
||||
annotations: [External.of(true)],
|
||||
});
|
||||
const value = editor.value?.getValue();
|
||||
if (editor.value && model.value !== value) {
|
||||
editor.value.setValue(model.value ?? '');
|
||||
editor.value.clearHistory();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="editor" class="flex flex-1 w-full justify-stretch items-stretch border border-light-35 dark:border-dark-35 caret-light-100 dark:caret-dark-100 py-2 px-1.5 font-sans text-base" />
|
||||
<div :class="{ 'cancel-gutters': !gutters }" class="flex flex-1 w-full justify-stretch items-stretch !font-sans !text-base">
|
||||
<textarea ref="input" class="hidden"></textarea>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.cm-editor
|
||||
.CodeMirror
|
||||
{
|
||||
@apply bg-transparent;
|
||||
@apply flex-1;
|
||||
@apply flex-1 h-full;
|
||||
@apply font-sans;
|
||||
|
||||
@apply text-light-100 dark:text-dark-100;
|
||||
}
|
||||
.cm-editor .cm-content
|
||||
.cancel-gutters .CodeMirror-gutters
|
||||
{
|
||||
@apply caret-light-100;
|
||||
@apply dark:caret-dark-100;
|
||||
@apply hidden;
|
||||
}
|
||||
.cancel-gutters .CodeMirror-sizer
|
||||
{
|
||||
@apply ms-2;
|
||||
}
|
||||
.CodeMirror-gutters
|
||||
{
|
||||
@apply bg-transparent;
|
||||
@apply border-transparent;
|
||||
}
|
||||
.CodeMirror-gutter-wrapper
|
||||
{
|
||||
@apply absolute top-0 bottom-0;
|
||||
@apply flex justify-center items-center;
|
||||
}
|
||||
.CodeMirror-foldmarker
|
||||
{
|
||||
@apply text-light-100;
|
||||
@apply dark:text-dark-100;
|
||||
@apply ps-3;
|
||||
text-shadow: none;
|
||||
}
|
||||
.hmd-inactive-line .cm-formatting-header, .hmd-inactive-line .cm-formatting-link, .hmd-inactive-line .cm-link-has-alias, .hmd-inactive-line .cm-link-alias-pipe
|
||||
{
|
||||
@apply hidden;
|
||||
}
|
||||
.CodeMirror-line
|
||||
{
|
||||
@apply text-base;
|
||||
}
|
||||
.CodeMirror-cursor
|
||||
{
|
||||
@apply border-light-100 dark:border-dark-100;
|
||||
}
|
||||
.CodeMirror-selected
|
||||
{
|
||||
@apply bg-light-35 dark:bg-dark-35;
|
||||
}
|
||||
.HyperMD-list-line-1 {
|
||||
@apply !ps-0;
|
||||
}
|
||||
.HyperMD-list-line-2 {
|
||||
@apply !ps-6;
|
||||
}
|
||||
.HyperMD-list-line-3 {
|
||||
@apply !ps-12;
|
||||
}
|
||||
</style>
|
||||
@@ -25,8 +25,8 @@
|
||||
<span @mousedown.left="(e) => resizeNode(e, 1, 0, -1, 1)" id="sw" class="cursor-sw-resize absolute -bottom-4 -left-4 w-8 h-8"></span> <!-- South West -->
|
||||
</div>
|
||||
</div>
|
||||
<div v-else style="outline-style: solid;" :class="[style.border, style.outline, { '!outline-4': focusing }]" class="outline-0 transition-[outline-width] border-2 bg-light-20 dark:bg-dark-20 overflow-hidden contain-strict w-full h-full flex" >
|
||||
<Editor v-model="node.text" autofocus />
|
||||
<div v-else style="outline-style: solid;" :class="[style.border, style.outline, { '!outline-4': focusing }]" class="outline-0 transition-[outline-width] border-2 bg-light-20 dark:bg-dark-20 overflow-hidden contain-strict w-full h-full flex py-2" >
|
||||
<Editor v-model="node.text" autofocus :gutters="false"/>
|
||||
</div>
|
||||
<div v-if="!editing && node.type === 'group' && node.label !== undefined" @click.left="(e) => selectNode(e)" @dblclick.left="(e) => editNode(e)" :class="style.border" style="max-width: 100%; font-size: calc(18px * var(--zoom-multiplier))" class="origin-bottom-left tracking-wider border-4 truncate inline-block text-light-100 dark:text-dark-100 absolute bottom-[100%] mb-2 px-2 py-1 font-thin">{{ node.label }}</div>
|
||||
<input v-else-if="editing && node.type === 'group'" v-model="node.label" @click="e => e.stopImmediatePropagation()" v-autofocus :class="[style.border, style.outline]" style="max-width: 100%; font-size: calc(18px * var(--zoom-multiplier))" class="origin-bottom-left tracking-wider border-4 truncate inline-block text-light-100 dark:text-dark-100 absolute bottom-[100%] appearance-none bg-transparent outline-4 mb-2 px-2 py-1 font-thin min-w-4" />
|
||||
|
||||
@@ -27,4 +27,10 @@ const { hash, pathname } = parseURL(href);
|
||||
|
||||
const { content } = useContent();
|
||||
const overview = computed(() => content.value.find(e => e.path === pathname));
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.cm-link {
|
||||
@apply text-accent-blue inline-flex items-center cursor-pointer hover:text-opacity-85;
|
||||
}
|
||||
</style>
|
||||
@@ -2,4 +2,27 @@
|
||||
<blockquote class="empty:before:hidden ps-4 my-4 relative before:absolute before:-top-1 before:-bottom-1 before:left-0 before:w-1 before:bg-light-30 dark:before:bg-dark-30" ref="el">
|
||||
<slot />
|
||||
</blockquote>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.HyperMD-quote
|
||||
{
|
||||
@apply before:hidden;
|
||||
}
|
||||
.HyperMD-quote.hmd-inactive-line
|
||||
{
|
||||
@apply before:block empty:before:!hidden !pb-2 !ps-4 !relative before:!absolute before:!-top-1 before:!-bottom-1 before:!left-0 before:!w-1 before:!bg-none before:!bg-light-30 dark:before:!bg-dark-30;
|
||||
}
|
||||
.HyperMD-quote.HyperMD-header
|
||||
{
|
||||
@apply before:!hidden;
|
||||
}
|
||||
.hmd-inactive-line .cm-formatting-quote
|
||||
{
|
||||
@apply !hidden;
|
||||
}
|
||||
.cm-quote
|
||||
{
|
||||
@apply text-light-100 dark:text-dark-100;
|
||||
}
|
||||
</style>
|
||||
@@ -1,3 +1,10 @@
|
||||
<template>
|
||||
<code><slot /></code>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.cm-inline-code
|
||||
{
|
||||
@apply !border-none !bg-transparent !text-light-100 dark:!text-dark-100 !p-0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<h1 :id="parseId(id)" class="text-5xl font-thin mt-3 mb-8 first:pt-0 pt-2 relative lg:right-8 sm:right-4 right-2">
|
||||
<h1 :id="parseId(id)" class="text-5xl font-thin mt-3 mb-8 first:pt-0 pt-2">
|
||||
<slot />
|
||||
</h1>
|
||||
</template>
|
||||
@@ -8,3 +8,14 @@
|
||||
import { parseId } from '#shared/general.util';
|
||||
const props = defineProps<{ id?: string }>()
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.HyperMD-header-1
|
||||
{
|
||||
@apply text-5xl pt-4 pb-2 after:hidden;
|
||||
}
|
||||
.HyperMD-header-1 .cm-header
|
||||
{
|
||||
@apply font-thin;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<h2 :id="parseId(id)" class="text-4xl font-semibold mt-3 mb-6 ms-1 first:pt-0 pt-2 relative sm:right-4 right-2">
|
||||
<h2 :id="parseId(id)" class="text-4xl font-semibold mt-3 mb-6 ms-1 first:pt-0 pt-2">
|
||||
<slot />
|
||||
</h2>
|
||||
</template>
|
||||
@@ -10,3 +10,14 @@ const props = defineProps<{ id?: string }>()
|
||||
|
||||
const generate = computed(() => props.id)
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.HyperMD-header-2
|
||||
{
|
||||
@apply !text-4xl !pt-4 !pb-2 !ps-1 leading-loose after:hidden;
|
||||
}
|
||||
.HyperMD-header-2 .cm-header
|
||||
{
|
||||
@apply font-semibold;
|
||||
}
|
||||
</style>
|
||||
@@ -10,3 +10,14 @@ const props = defineProps<{ id?: string }>()
|
||||
|
||||
const generate = computed(() => props.id)
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.HyperMD-header-3
|
||||
{
|
||||
@apply !text-2xl !font-bold !pt-1 after:!hidden;
|
||||
}
|
||||
.HyperMD-header-3 .cm-header
|
||||
{
|
||||
@apply font-bold;
|
||||
}
|
||||
</style>
|
||||
@@ -8,3 +8,15 @@
|
||||
import { parseId } from '#shared/general.util';
|
||||
const props = defineProps<{ id?: string }>()
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.HyperMD-header-4
|
||||
{
|
||||
@apply !text-xl font-semibold pt-1 after:hidden;
|
||||
font-variant: small-caps;
|
||||
}
|
||||
.HyperMD-header-4 .cm-header
|
||||
{
|
||||
@apply font-semibold;
|
||||
}
|
||||
</style>
|
||||
@@ -1,3 +1,10 @@
|
||||
<template>
|
||||
<Separator class="border-b border-light-35 dark:border-dark-35 m-4" />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.HyperMD-hr
|
||||
{
|
||||
@apply bg-light-35 dark:bg-dark-35 h-px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,3 +1,22 @@
|
||||
<template>
|
||||
<li class="before:absolute before:top-2 before:left-0 before:inline-block before:w-2 before:h-2 before:rounded before:bg-light-40 dark:before:bg-dark-40 relative ps-4"><slot /></li>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.HyperMD-list-line
|
||||
{
|
||||
@apply !py-1;
|
||||
}
|
||||
.HyperMD-list-line.hmd-inactive-line > span
|
||||
{
|
||||
@apply before:absolute before:top-2 before:left-0 before:inline-block before:w-2 before:h-2 before:rounded before:bg-light-40 dark:before:bg-dark-40 relative ps-4;
|
||||
}
|
||||
.hmd-inactive-line .cm-formatting-list
|
||||
{
|
||||
@apply hidden;
|
||||
}
|
||||
.cm-hmd-list-indent
|
||||
{
|
||||
@apply !hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -2,4 +2,15 @@
|
||||
<span class="before:content-['#'] cursor-default bg-accent-blue bg-opacity-10 hover:bg-opacity-20 text-accent-blue text-sm px-1 ms-1 pb-0.5 rounded-full rounded-se-none border border-accent-blue border-opacity-30">
|
||||
<slot></slot>
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.cm-hashtag.cm-hashtag-begin
|
||||
{
|
||||
@apply bg-accent-blue bg-opacity-10 text-accent-blue text-sm pb-0.5 ps-1 rounded-l-[12px] border border-r-0 border-accent-blue border-opacity-30;
|
||||
}
|
||||
.cm-hashtag.cm-hashtag-end
|
||||
{
|
||||
@apply bg-accent-blue bg-opacity-10 text-accent-blue text-sm pb-0.5 pe-1 rounded-r-[12px] !rounded-se-none border border-l-0 border-accent-blue border-opacity-30;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user