refactor(markdown): extract post-render effects into composable
Four components duplicated the watch -> nextTick -> listenToClick -> runTikz shape. Collapse to useMarkdownPostRender, which also accepts optional flags for Mermaid, Shikiji and image hydration so StackedNote no longer needs its own inline conditional chain.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, nextTick, onMounted, onUnmounted, toRefs, watch } from "vue"
|
import { computed, onMounted, onUnmounted, toRefs, watch } from "vue"
|
||||||
|
|
||||||
import HeaderNote from "@/components/HeaderNote.vue"
|
import HeaderNote from "@/components/HeaderNote.vue"
|
||||||
import SignInGithub from "@/components/SignInGithub.vue"
|
import SignInGithub from "@/components/SignInGithub.vue"
|
||||||
@@ -7,7 +7,8 @@ import SkeletonLoader from "@/components/SkeletonLoader.vue"
|
|||||||
import StackedNote from "@/components/StackedNote.vue"
|
import StackedNote from "@/components/StackedNote.vue"
|
||||||
import { useGitHubLogin } from "@/hooks/useGitHubLogin.hook"
|
import { useGitHubLogin } from "@/hooks/useGitHubLogin.hook"
|
||||||
import { useLinks } from "@/hooks/useLinks.hook"
|
import { useLinks } from "@/hooks/useLinks.hook"
|
||||||
import { markdownBuilder, runTikz } from "@/hooks/useMarkdown.hook"
|
import { markdownBuilder } from "@/hooks/useMarkdown.hook"
|
||||||
|
import { useMarkdownPostRender } from "@/hooks/useMarkdownPostRender.hook"
|
||||||
import { useNoteView } from "@/hooks/useNoteView.hook"
|
import { useNoteView } from "@/hooks/useNoteView.hook"
|
||||||
import { useResizeContainer } from "@/hooks/useResizeContainer.hook"
|
import { useResizeContainer } from "@/hooks/useResizeContainer.hook"
|
||||||
import { useRouteQueryStackedNotes } from "@/hooks/useRouteQueryStackedNotes.hook"
|
import { useRouteQueryStackedNotes } from "@/hooks/useRouteQueryStackedNotes.hook"
|
||||||
@@ -59,15 +60,10 @@ const renderedContent = computed(() =>
|
|||||||
const isLoading = computed(() => renderedContent.value === undefined)
|
const isLoading = computed(() => renderedContent.value === undefined)
|
||||||
const hasContent = computed(() => !!renderedContent.value)
|
const hasContent = computed(() => !!renderedContent.value)
|
||||||
|
|
||||||
watch(
|
useMarkdownPostRender(renderedContent, () => ".note-display", {
|
||||||
renderedContent,
|
onReady: () => listenToClick(),
|
||||||
async () => {
|
tikz: true
|
||||||
await nextTick()
|
})
|
||||||
listenToClick()
|
|
||||||
void runTikz(".note-display .tikz")
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
[refProps.user, refProps.repo],
|
[refProps.user, refProps.repo],
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
defineAsyncComponent,
|
defineAsyncComponent,
|
||||||
nextTick,
|
|
||||||
onMounted,
|
onMounted,
|
||||||
ref,
|
ref,
|
||||||
watch
|
watch
|
||||||
@@ -11,14 +10,9 @@ import {
|
|||||||
import { useEditionMode } from "@/hooks/useEditionMode"
|
import { useEditionMode } from "@/hooks/useEditionMode"
|
||||||
import { useFile } from "@/hooks/useFile.hook"
|
import { useFile } from "@/hooks/useFile.hook"
|
||||||
import { useGitHubContent } from "@/hooks/useGitHubContent.hook"
|
import { useGitHubContent } from "@/hooks/useGitHubContent.hook"
|
||||||
import { useImages } from "@/hooks/useImages.hook"
|
|
||||||
import { useLinks } from "@/hooks/useLinks.hook"
|
import { useLinks } from "@/hooks/useLinks.hook"
|
||||||
import {
|
import { renderCodeFile } from "@/hooks/useMarkdown.hook"
|
||||||
renderCodeFile,
|
import { useMarkdownPostRender } from "@/hooks/useMarkdownPostRender.hook"
|
||||||
runMermaid,
|
|
||||||
runTikz,
|
|
||||||
useShikiji
|
|
||||||
} from "@/hooks/useMarkdown.hook"
|
|
||||||
import { useNoteFreshness } from "@/hooks/useNoteFreshness.hook"
|
import { useNoteFreshness } from "@/hooks/useNoteFreshness.hook"
|
||||||
import { useNoteOverlay } from "@/hooks/useNoteOverlay.hook"
|
import { useNoteOverlay } from "@/hooks/useNoteOverlay.hook"
|
||||||
import { useRouteQueryStackedNotes } from "@/hooks/useRouteQueryStackedNotes.hook"
|
import { useRouteQueryStackedNotes } from "@/hooks/useRouteQueryStackedNotes.hook"
|
||||||
@@ -145,30 +139,14 @@ watch(
|
|||||||
|
|
||||||
const { mode, toggleMode } = useEditionMode()
|
const { mode, toggleMode } = useEditionMode()
|
||||||
|
|
||||||
watch([content, mode], () => {
|
useMarkdownPostRender(content, () => `.note-${sha.value}`, {
|
||||||
if (!content.value) {
|
onReady: () => listenToClick(),
|
||||||
return
|
tikz: true,
|
||||||
}
|
mermaid: () => rawContent.value.includes("```mermaid"),
|
||||||
|
shikiji: () => isMarkdown.value && rawContent.value.includes("```"),
|
||||||
nextTick(() => {
|
images: () =>
|
||||||
listenToClick()
|
/\!\[.*?\]\(.*?\)/.test(rawContent.value) ? props.sha : null,
|
||||||
|
triggers: [mode]
|
||||||
if (/\!\[.*?\]\(.*?\)/.test(rawContent.value)) {
|
|
||||||
useImages(props.sha)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawContent.value.includes("```mermaid")) {
|
|
||||||
runMermaid(`.note-${sha.value} .mermaid`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawContent.value.includes("```tikz")) {
|
|
||||||
void runTikz(`.note-${sha.value} .tikz`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMarkdown.value && rawContent.value.includes("```")) {
|
|
||||||
useShikiji()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const performSave = async (overrideSha?: string) => {
|
const performSave = async (overrideSha?: string) => {
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computedAsync } from "@vueuse/core"
|
import { computedAsync } from "@vueuse/core"
|
||||||
import { computed, nextTick, ref, watch } from "vue"
|
import { computed, ref, watch } from "vue"
|
||||||
import { useRoute } from "vue-router"
|
import { useRoute } from "vue-router"
|
||||||
|
|
||||||
import SkeletonLoader from "@/components/SkeletonLoader.vue"
|
import SkeletonLoader from "@/components/SkeletonLoader.vue"
|
||||||
import { useATProtoLinks } from "@/hooks/useATProtoLinks.hook"
|
import { useATProtoLinks } from "@/hooks/useATProtoLinks.hook"
|
||||||
import { markdownBuilder, runTikz } from "@/hooks/useMarkdown.hook"
|
import { markdownBuilder } from "@/hooks/useMarkdown.hook"
|
||||||
|
import { useMarkdownPostRender } from "@/hooks/useMarkdownPostRender.hook"
|
||||||
import { useNoteOverlay } from "@/hooks/useNoteOverlay.hook"
|
import { useNoteOverlay } from "@/hooks/useNoteOverlay.hook"
|
||||||
import { useRouteQueryStackedNotes } from "@/hooks/useRouteQueryStackedNotes.hook"
|
import { useRouteQueryStackedNotes } from "@/hooks/useRouteQueryStackedNotes.hook"
|
||||||
import { getAuthor } from "@/modules/atproto/getAuthor"
|
import { getAuthor } from "@/modules/atproto/getAuthor"
|
||||||
@@ -76,15 +77,10 @@ const content = computed(() =>
|
|||||||
: ""
|
: ""
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(
|
useMarkdownPostRender(content, () => `.note-${classNameId.value}`, {
|
||||||
content,
|
onReady: () => listenToClick(),
|
||||||
async () => {
|
tikz: true
|
||||||
await nextTick()
|
})
|
||||||
listenToClick()
|
|
||||||
void runTikz(`.note-${classNameId.value} .tikz`)
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
54
src/hooks/useMarkdownPostRender.hook.ts
Normal file
54
src/hooks/useMarkdownPostRender.hook.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { nextTick, type Ref, toValue, watch } from "vue"
|
||||||
|
|
||||||
|
import { useImages } from "@/hooks/useImages.hook"
|
||||||
|
import {
|
||||||
|
runMermaid,
|
||||||
|
runTikz,
|
||||||
|
useShikiji
|
||||||
|
} from "@/hooks/useMarkdown.hook"
|
||||||
|
|
||||||
|
interface MarkdownPostRenderOptions {
|
||||||
|
onReady?: () => void
|
||||||
|
tikz?: boolean
|
||||||
|
mermaid?: () => boolean
|
||||||
|
shikiji?: () => boolean
|
||||||
|
images?: () => string | null | undefined
|
||||||
|
triggers?: Ref<unknown>[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useMarkdownPostRender = (
|
||||||
|
contentRef: Ref<unknown>,
|
||||||
|
scopeSelector: () => string,
|
||||||
|
options: MarkdownPostRenderOptions = {}
|
||||||
|
) => {
|
||||||
|
const sources = [contentRef, ...(options.triggers ?? [])]
|
||||||
|
|
||||||
|
watch(
|
||||||
|
sources,
|
||||||
|
async () => {
|
||||||
|
if (!toValue(contentRef)) return
|
||||||
|
await nextTick()
|
||||||
|
options.onReady?.()
|
||||||
|
|
||||||
|
const scope = scopeSelector()
|
||||||
|
|
||||||
|
if (options.tikz) {
|
||||||
|
void runTikz(`${scope} .tikz`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.mermaid?.()) {
|
||||||
|
runMermaid(`${scope} .mermaid`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.shikiji?.()) {
|
||||||
|
void useShikiji()
|
||||||
|
}
|
||||||
|
|
||||||
|
const imagesSha = options.images?.()
|
||||||
|
if (imagesSha) {
|
||||||
|
useImages(imagesSha)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computedAsync } from "@vueuse/core"
|
import { computedAsync } from "@vueuse/core"
|
||||||
import { useTitle } from "@vueuse/core"
|
import { useTitle } from "@vueuse/core"
|
||||||
import { computed, nextTick, ref, watch } from "vue"
|
import { computed, ref, watch } from "vue"
|
||||||
import { useRouter } from "vue-router"
|
import { useRouter } from "vue-router"
|
||||||
|
|
||||||
import HomeButton from "@/components/HomeButton.vue"
|
import HomeButton from "@/components/HomeButton.vue"
|
||||||
@@ -9,7 +9,8 @@ import SkeletonLoader from "@/components/SkeletonLoader.vue"
|
|||||||
import StackedPublicNote from "@/components/StackedPublicNote.vue"
|
import StackedPublicNote from "@/components/StackedPublicNote.vue"
|
||||||
import ThemeSwap from "@/components/ThemeSwap.vue"
|
import ThemeSwap from "@/components/ThemeSwap.vue"
|
||||||
import { useATProtoLinks } from "@/hooks/useATProtoLinks.hook"
|
import { useATProtoLinks } from "@/hooks/useATProtoLinks.hook"
|
||||||
import { markdownBuilder, runTikz } from "@/hooks/useMarkdown.hook"
|
import { markdownBuilder } from "@/hooks/useMarkdown.hook"
|
||||||
|
import { useMarkdownPostRender } from "@/hooks/useMarkdownPostRender.hook"
|
||||||
import { useResizeContainer } from "@/hooks/useResizeContainer.hook"
|
import { useResizeContainer } from "@/hooks/useResizeContainer.hook"
|
||||||
import { useRouteQueryStackedNotes } from "@/hooks/useRouteQueryStackedNotes.hook"
|
import { useRouteQueryStackedNotes } from "@/hooks/useRouteQueryStackedNotes.hook"
|
||||||
import { getAuthor } from "@/modules/atproto/getAuthor"
|
import { getAuthor } from "@/modules/atproto/getAuthor"
|
||||||
@@ -115,15 +116,10 @@ const { stackedNotes, scrollToFocusedNote } = useRouteQueryStackedNotes()
|
|||||||
const { listenToClick } = useATProtoLinks("note-display", { mainNoteId })
|
const { listenToClick } = useATProtoLinks("note-display", { mainNoteId })
|
||||||
useResizeContainer("note-container", stackedNotes)
|
useResizeContainer("note-container", stackedNotes)
|
||||||
|
|
||||||
watch(
|
useMarkdownPostRender(content, () => ".public-note-view .note-display", {
|
||||||
content,
|
onReady: () => listenToClick(),
|
||||||
async () => {
|
tikz: true
|
||||||
await nextTick()
|
})
|
||||||
listenToClick()
|
|
||||||
void runTikz(".public-note-view .note-display .tikz")
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
Reference in New Issue
Block a user