Skip to content

Commit

Permalink
rewrite highlight
Browse files Browse the repository at this point in the history
  • Loading branch information
arily committed Jul 7, 2023
1 parent e6de7ee commit 473c82e
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 150 deletions.
7 changes: 4 additions & 3 deletions src/components/content/editor/index.client.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ import { generateJSON } from '@tiptap/html'
import useEditor from '~/composables/useEditor'
import '@/components/content/styles/typography.scss'
import '@/components/content/styles/editor.scss'
const props = withDefaults(
defineProps<{
modelValue?: JSONContent
Expand Down Expand Up @@ -66,3 +63,7 @@ defineExpose({
<EditorContent class="editor__content custom-typography" :editor="editor" />
</div>
</template>

<style src="@/components/content/styles/typography.scss" lang="scss"></style>

<style src="@/components/content/styles/editor.scss" lang="scss"></style>
48 changes: 48 additions & 0 deletions src/components/content/render.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<script setup lang="ts">
import { type JSONContent } from '@tiptap/vue-3'
const props = defineProps<{
json?: JSONContent
html: string
}>()
const html = shallowRef(props.html)
const el = shallowRef<HTMLElement>()
onBeforeMount(hl)
watch(() => props.html, hl)
async function hl() {
if (process.server) {
return props.html
}
const _hljs = await import('highlight.js/lib/core').then(m => m.default)
const hljs = _hljs.newInstance()
const libs = await parseAndImportHighlightLibFromHtml(props.html) as any[]
libs.forEach(([lKey, lib]) => {
if (!hljs.listLanguages().includes(lKey)) {
hljs.registerLanguage(lKey, lib)
}
})
el.value = document.createElement('div')
el.value.innerHTML = props.html
el.value.querySelectorAll('pre code').forEach((cb) => {
const language = [...cb.classList].find(i => i.startsWith('language-'))?.slice('language-'.length)
if (!language) {
return
}
cb.innerHTML = hljs.highlight(language, (cb as HTMLElement).innerText).value
})
html.value = el.value.innerHTML
}
</script>

<template>
<div
class="custom-typography"
v-html="html"
/>
</template>

<style src="@/components/content/styles/typography.scss" lang="scss"></style>
34 changes: 0 additions & 34 deletions src/components/content/renderer.client.vue

This file was deleted.

16 changes: 0 additions & 16 deletions src/components/content/renderer.server.vue

This file was deleted.

2 changes: 1 addition & 1 deletion src/components/userpage/profile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ const page = userpageStore()

<template>
<div class="container mx-auto mt-4 custom-container">
<content-renderer v-if="page.user?.profile" :json="page.user.profile.raw" :html="page.user.profile.html" />
<content-render v-if="page.user?.profile" :json="page.user.profile.raw" :html="page.user.profile.html" />
</div>
</template>
67 changes: 4 additions & 63 deletions src/composables/useEditor.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,9 @@
import type { Editor as EditorCore, JSONContent } from '@tiptap/core'
import { Editor as EditorVue } from '@tiptap/vue-3'

import { lowlight } from 'lowlight/lib/core.js'
import useEditorExtensions from './useEditorExtensionsClient'
import allLanguages from '~~/configs/hljs'

type JSONCallback = (content: JSONContent) => void
type LanguageKey = keyof typeof allLanguages
type Language = typeof allLanguages[LanguageKey]

export async function importLowlightLanguage(language: Language) {
return await import(`../../node_modules/highlight.js/es/languages/${language}.js`)
}

export async function useLowlightLanguage(language: Language) {
try {
const f = await importLowlightLanguage(language)
lowlight.registerLanguage(language, f.default)
}
catch (e) {
console.error('error on loading hljs lib:', e)
}
}
export async function parseAndImportHighlightLibFromHtml(html: string) {
const languages = getLanguagesFromHTML(html)
return Promise.all(languages.map(lKey => useLowlightLanguage(allLanguages[lKey])))
}

export function getLanguagesFromHTML(html: string) {
const matchedLanguages = html.matchAll(/language-(\w+)/gm)
const languages = [] as LanguageKey[]
for (const _language of matchedLanguages) {
const language = _language[1] as LanguageKey
if (!allLanguages[language]) {
continue
}
languages.push(_language[1] as LanguageKey)
}
return languages
}
export function loadAllLanguagesInJSONContent(json: JSONContent) {
return json.content
? Promise.allSettled(json.content?.map((node) => {
if (node.type !== 'codeBlock') {
return undefined
}

const language = node.attrs?.language as LanguageKey | undefined
if (!language) {
return undefined
}

if (lowlight.registered(language)) {
return undefined
}

if (!allLanguages[language]) {
return undefined
}

return useLowlightLanguage(allLanguages[language])
})).then(noop)
: Promise.resolve()
}

export default (
reactiveConfig: {
Expand All @@ -74,13 +15,13 @@ export default (
const extensions = useEditorExtensions(reactiveConfig)
const editor = shallowRef<EditorVue>()
let created = false
const lazyLoadCodeBlock = ({ editor }: { editor: EditorCore }) => {
const json = editor.getJSON()
return loadAllLanguagesInJSONContent(json)
}

const subscribedBeforeMounted = new Set<JSONCallback>()
onBeforeMount(() => {
const lazyLoadCodeBlock = ({ editor }: { editor: EditorCore }) => {
const json = editor.getJSON()
return loadAllLanguagesInJSONContent(json)
}
editor.value = new EditorVue({
extensions,
})
Expand Down
31 changes: 1 addition & 30 deletions src/composables/useEditorExtensionsBase.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight'
import { Highlight } from '@tiptap/extension-highlight'
import { TaskItem } from '@tiptap/extension-task-item'
import { TaskList } from '@tiptap/extension-task-list'
import { Link } from '@tiptap/extension-link'
import { TextAlign } from '@tiptap/extension-text-align'
import { Typography } from '@tiptap/extension-typography'
import { StarterKit } from '@tiptap/starter-kit'
import { Image } from '@tiptap/extension-image'
import { lowlight } from 'lowlight/lib/core.js'

export default <TEdit extends boolean>(config?: { indent?: string; edit?: TEdit }) => [
StarterKit.configure({
codeBlock: false,
}),
export default <TEdit extends boolean>(_?: { indent?: string; edit?: TEdit }) => [
TextAlign.configure({
types: ['heading', 'paragraph'],
}),
Expand All @@ -25,29 +19,6 @@ export default <TEdit extends boolean>(config?: { indent?: string; edit?: TEdit
}),
TaskItem,
Typography,
// CharacterCount.configure({
// limit: 10000
// }),
CodeBlockLowlight.extend({
addKeyboardShortcuts() {
return {
...this.parent?.(),
Tab: ({ editor }) => {
if (!this.editor.isActive('codeBlock')) {
return false
}
editor.commands.insertContent(config?.indent || ' ')
return true
},
}
},
}).configure({
lowlight,
exitOnArrowDown: true,
HTMLAttributes: {
class: 'border border-gbase-500/20',
},
}),
Image.configure({
allowBase64: true,
}),
Expand Down
26 changes: 26 additions & 0 deletions src/composables/useEditorExtensionsClient.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,35 @@
import { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight'
import { lowlight } from 'lowlight/lib/core.js'
import { StarterKit } from '@tiptap/starter-kit'
import useEditorExtensionsBase from './useEditorExtensionsBase'
import _Variable from '~/components/content/editor/extensions/variable.client'

export default <TEdit extends boolean>(config?: { indent?: string; edit?: TEdit }) => {
const Variable = _Variable()
return [
StarterKit.configure({
codeBlock: false,
}),
CodeBlockLowlight.extend({
addKeyboardShortcuts() {
return {
...this.parent?.(),
Tab: ({ editor }) => {
if (!this.editor.isActive('codeBlock')) {
return false
}
editor.commands.insertContent(config?.indent || ' ')
return true
},
}
},
}).configure({
lowlight,
exitOnArrowDown: true,
HTMLAttributes: {
class: 'border border-gbase-500/20',
},
}),
...useEditorExtensionsBase(config),
Variable,
]
Expand Down
2 changes: 2 additions & 0 deletions src/composables/useEditorExtensionsServer.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { StarterKit } from '@tiptap/starter-kit'
import useEditorExtensionsBase from './useEditorExtensionsBase'
import _Variable from '~/components/content/editor/extensions/variable.server'

export default <TEdit extends boolean>(config?: { indent?: string; edit?: TEdit }) => {
const Variable = _Variable()
return [
StarterKit,
...useEditorExtensionsBase(config),
Variable,
]
Expand Down
61 changes: 61 additions & 0 deletions src/composables/useLazyLoadLowlight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { JSONContent } from '@tiptap/core'
import { lowlight } from 'lowlight/lib/core.js'
import allLanguages from '~~/configs/hljs'

export type LanguageKey = keyof typeof allLanguages
export type Language = typeof allLanguages[LanguageKey]

export async function importHighlightLanguage(language: Language) {
return await import(`../../node_modules/highlight.js/es/languages/${language}.js`)
}

export async function useLowlightLanguage(language: Language) {
try {
const f = await importHighlightLanguage(language)
lowlight.registerLanguage(language, f.default)
}
catch (e) {
console.error('error on loading hljs lib:', e)
}
}
export async function parseAndImportHighlightLibFromHtml(html: string) {
const languages = getLanguagesFromHTML(html)
return Promise.all(languages.map(async lKey => [lKey, await importHighlightLanguage(allLanguages[lKey]).then(v => v.default)]))
}

export function getLanguagesFromHTML(html: string) {
const matchedLanguages = html.matchAll(/language-(\w+)/gm)
const languages = [] as LanguageKey[]
for (const _language of matchedLanguages) {
const language = _language[1] as LanguageKey
if (!allLanguages[language]) {
continue
}
languages.push(_language[1] as LanguageKey)
}
return languages
}
export function loadAllLanguagesInJSONContent(json: JSONContent) {
return json.content
? Promise.allSettled(json.content?.map((node) => {
if (node.type !== 'codeBlock') {
return undefined
}

const language = node.attrs?.language as LanguageKey | undefined
if (!language) {
return undefined
}

if (lowlight.registered(language)) {
return undefined
}

if (!allLanguages[language]) {
return undefined
}

return useLowlightLanguage(allLanguages[language])
})).then(noop)
: Promise.resolve()
}
6 changes: 3 additions & 3 deletions src/pages/article/[...id].vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<script setup lang="ts">
import '@/components/content/styles/typography.scss'
const route = useRoute()
const id = route.params.id
Expand All @@ -14,7 +12,7 @@ const content = await app$.$client.article.getRendered.query(id)

<template>
<section class="container mx-auto with-editor relative">
<content-renderer v-bind="content" />
<content-render v-bind="content" />
<button v-if="content.access.write" class="btn btn-neutral d-flex gap-1 absolute top-0 right-0">
Edit <icon name="ic:round-edit-note" class="w-5 h-5" />
</button>
Expand All @@ -36,3 +34,5 @@ const content = await app$.$client.article.getRendered.query(id)
}
}
</style>

<style src="@/components/content/styles/typography.scss" lang="scss"></style>

0 comments on commit 473c82e

Please sign in to comment.