import hljs from 'highlight.js/lib/core'
import json from 'highlight.js/lib/languages/json'
import { marked } from 'marked'
import { route } from 'preact-router'
import { useCallback, useEffect, useMemo, useState } from 'preact/hooks'
import { Ad, Btn, ChangelogTag, Footer, Giscus, Octicon, VersionSwitcher } from '../components/index.js'
import config from '../Config.js'
import { useLocale, useTitle, useVersion } from '../contexts/index.js'
import { useActiveTimeout, useAsync, useHash } from '../hooks/index.js'
import type { VersionId } from '../services/index.js'
import { parseFrontMatter, versionContent } from '../Utils.js'
const HASH = ''
hljs.registerLanguage('json', json)
marked.use({
highlight: (code, lang) => {
if (lang === '') return undefined
return hljs.highlight(code, { language: lang }).value
},
})
interface Props {
path?: string
id?: string
}
export function Guide({ id }: Props) {
const { locale } = useLocale()
const { version, changeVersion } = useVersion()
const { changeTitle } = useTitle()
const { value: content, refresh } = useAsync(async () => {
const res = await fetch(`../../guides/${id}.md`)
return await res.text()
}, [id])
if ((import.meta as any).hot) {
(import.meta as any).hot.on('guide-update', (updateId: string) => {
if (id === updateId) refresh()
})
}
const frontMatter = useMemo(() => {
if (!content) return undefined
const data = parseFrontMatter(content)
changeTitle(data?.title, data?.versions)
return data
}, [content])
const allowedVersions = useMemo(() => {
const orderedVersions = config.versions.map(v => v.id)
return (frontMatter?.versions as VersionId[])
?.sort((a, b) => orderedVersions.indexOf(b) - orderedVersions.indexOf(a))
}, [frontMatter?.versions])
const guideVersion = useMemo(() => {
if (!allowedVersions) return version
if (allowedVersions.includes(version)) return version
return allowedVersions[0]
}, [version, frontMatter?.versions])
const html = useMemo(() => {
if (!content) return undefined
const headings: marked.Tokens.Heading[] = []
let insertedToc = false
marked.use({
extensions: [
{
name: 'styledCode',
level: 'inline',
start(src) {
return src.match(/\b[fsnj]`/)?.index ?? -1
},
tokenizer(src) {
const match = src.match(/^([fsnj])`([^`]+)`/)
if (match) {
return {
type: 'styledCode',
raw: match[0],
prefix: match[1],
text: match[2],
}
}
return undefined
},
renderer(token) {
let content = token.text
let c = {
f: 'hljs-attr',
s: 'hljs-string',
n: 'hljs-number',
}[token.prefix as string]
if (token.prefix === 'j') {
content = hljs.highlight('json', token.text).value
c = 'language-json'
}
return `${content}`
},
},
],
walkTokens(token) {
if (token.type === 'heading') {
headings.push(token)
}
},
renderer: {
link(href, title, text) {
if (href === null) return text
const title2 = title ? ` title="${title}"` : ''
const target = href?.match(/^https?:\/\//) ? ' target="_blank"' : ''
return `${text}`
},
heading(text, level, raw, slugger) {
let toc = ''
if (!insertedToc) {
toc = `