From cb71585079adeb269d2cb6c55794dae01a8e2ea5 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Thu, 13 Jun 2024 01:33:44 +0800 Subject: [PATCH] chore: tweak --- .../src/client/components/CodeSandbox.vue | 6 +- .../src/node/features/caniuse.ts | 163 ++++++------------ .../src/node/features/codeSandbox.ts | 109 +++--------- .../src/node/features/codepen.ts | 122 ++++--------- .../src/node/features/jsfiddle.ts | 112 +++--------- .../plugin-md-power/src/node/features/pdf.ts | 103 +++-------- .../src/node/features/replit.ts | 97 ++--------- .../src/node/features/video/bilibili.ts | 129 ++++---------- .../src/node/features/video/youtube.ts | 117 ++++--------- .../src/node/utils/createRuleBlock.ts | 79 +++++++++ theme/src/client/components/FriendsGroup.vue | 6 +- .../components/TransitionFadeSlideY.vue | 1 - theme/src/client/layouts/NotFound.vue | 14 +- 13 files changed, 328 insertions(+), 730 deletions(-) create mode 100644 plugins/plugin-md-power/src/node/utils/createRuleBlock.ts diff --git a/plugins/plugin-md-power/src/client/components/CodeSandbox.vue b/plugins/plugin-md-power/src/client/components/CodeSandbox.vue index 4302381a..e542f965 100644 --- a/plugins/plugin-md-power/src/client/components/CodeSandbox.vue +++ b/plugins/plugin-md-power/src/client/components/CodeSandbox.vue @@ -35,7 +35,7 @@ const source = computed(() => { />

- + @@ -54,6 +54,10 @@ const source = computed(() => {

- - - ${alt} -

` - } - - feature = feature.replace(UNDERLINE_RE, '_') - const { past, future } = resolveVersions(versions) - const meta = nanoid() - - return `` -} - -function resolveVersions(versions: string): { past: number, future: number } { - if (!versions) - return { past: 2, future: 1 } - - const list = versions - .split(',') - .map(v => Number(v.trim())) - .filter(v => !Number.isNaN(v) && v >= -5 && v <= 3) - - list.push(0) - - const uniq = [...new Set(list)].sort((a, b) => b - a) - return { - future: uniq[0], - past: Math.abs(uniq[uniq.length - 1]), - } -} - /** * @example * ```md @@ -113,25 +20,18 @@ function resolveVersions(versions: string): { past: number, future: number } { */ export const caniusePlugin: PluginWithOptions = ( md, - { mode = 'embed' }: CanIUseOptions = {}, + { mode: defaultMode = 'embed' }: CanIUseOptions = {}, ): void => { - md.block.ruler.before( - 'import_code', - 'caniuse', - createCanIUseRuleBlock(mode), - { - alt: ['paragraph', 'reference', 'blockquote', 'list'], - }, - ) - - md.renderer.rules.caniuse = (tokens, index) => { - const token = tokens[index] - - const content = resolveCanIUse(token.meta) - token.content = content - - return content - } + createRuleBlock(md, { + type: 'caniuse', + syntaxPattern: /^@\[caniuse(?:\s*?(embed|image)?(?:{([0-9,\-]*?)})?)\]\(([^)]*)\)/, + meta: ([, mode, versions = '', feature]) => ({ + feature, + mode: (mode as CanIUseMode) || defaultMode, + versions, + }), + content: meta => resolveCanIUse(meta), + }) } /** @@ -174,3 +74,42 @@ export function legacyCaniuse( md.use(container, type, { validate, render }) } + +function resolveCanIUse({ feature, mode, versions }: CanIUseTokenMeta): string { + if (!feature) + return '' + + if (mode === 'image') { + const link = 'https://caniuse.bitsofco.de/image/' + const alt = `Data on support for the ${feature} feature across the major browsers from caniuse.com` + return `

+ + + ${alt} +

` + } + + feature = feature.replace(UNDERLINE_RE, '_') + const { past, future } = resolveVersions(versions) + const meta = nanoid() + + return `` +} + +function resolveVersions(versions: string): { past: number, future: number } { + if (!versions) + return { past: 2, future: 1 } + + const list = versions + .split(',') + .map(v => Number(v.trim())) + .filter(v => !Number.isNaN(v) && v >= -5 && v <= 3) + + list.push(0) + + const uniq = [...new Set(list)].sort((a, b) => b - a) + return { + future: uniq[0], + past: Math.abs(uniq[uniq.length - 1]), + } +} diff --git a/plugins/plugin-md-power/src/node/features/codeSandbox.ts b/plugins/plugin-md-power/src/node/features/codeSandbox.ts index 5e55fbf0..38c78888 100644 --- a/plugins/plugin-md-power/src/node/features/codeSandbox.ts +++ b/plugins/plugin-md-power/src/node/features/codeSandbox.ts @@ -4,94 +4,35 @@ * @[codesanbox title="xxx" layout="Editor+Preview" height="500px" navbar="false" console="false"](id#filepath) */ import type { PluginWithOptions } from 'markdown-it' -import type { RuleBlock } from 'markdown-it/lib/parser_block.mjs' import { resolveAttrs } from '../utils/resolveAttrs.js' import { parseRect } from '../utils/parseRect.js' import type { CodeSandboxTokenMeta } from '../../shared/codeSandbox.js' - -// @[codesandbox]() -const MIN_LENGTH = 16 - -// char codes of `@[codesandbox` -const START_CODES = [64, 91, 99, 111, 100, 101, 115, 97, 110, 100, 98, 111, 120] - -// regexp to match the import syntax -const SYNTAX_RE = /^@\[codesandbox(?:\s+(embed|button))?(?:\s+([^]*?))?\]\(([^)]*?)\)/ - -function createCodeSandboxRuleBlock(): RuleBlock { - return (state, startLine, endLine, silent) => { - const pos = state.bMarks[startLine] + state.tShift[startLine] - const max = state.eMarks[startLine] - - // return false if the length is shorter than min length - if (pos + MIN_LENGTH > max) - return false - - // check if it's matched the start - for (let i = 0; i < START_CODES.length; i += 1) { - if (state.src.charCodeAt(pos + i) !== START_CODES[i]) - return false - } - - // check if it's matched the syntax - const match = state.src.slice(pos, max).match(SYNTAX_RE) - if (!match) - return false - - // return true as we have matched the syntax - if (silent) - return true - - const [, type, info = '', source] = match - - const { attrs } = resolveAttrs(info) - const [profile, filepath = ''] = source.split('#') - const [user, id] = profile.includes('/') ? profile.split('/') : ['', profile] - - const meta: CodeSandboxTokenMeta = { - width: attrs.width ? parseRect(attrs.width) : '100%', - height: attrs.height ? parseRect(attrs.height) : '500px', - user, - id, - title: attrs.title ?? '', - console: attrs.console ?? false, - navbar: attrs.navbar ?? true, - layout: attrs.layout ?? '', - type: (type || 'embed') as CodeSandboxTokenMeta['type'], - filepath, - } - - const token = state.push('code_sandbox', '', 0) - - token.meta = meta - token.map = [startLine, startLine + 1] - token.info = match[0] - - state.line = startLine + 1 - - return true - } -} - -function resolveCodeSandbox(meta: CodeSandboxTokenMeta) { - const { title, height, width, user, id, type, filepath, console, navbar, layout } = meta - - return `` -} +import { createRuleBlock } from '../utils/createRuleBlock.js' export const codeSandboxPlugin: PluginWithOptions = (md) => { - md.block.ruler.before( - 'import_code', - 'code_sandbox', - createCodeSandboxRuleBlock(), - ) + createRuleBlock(md, { + type: 'codesandbox', + syntaxPattern: /^@\[codesandbox(?:\s+(embed|button))?(?:\s+([^]*?))?\]\(([^)]*?)\)/, + meta([, type, info = '', source = '']) { + const { attrs } = resolveAttrs(info) + const [profile, filepath = ''] = source.split('#') + const [user, id] = profile.includes('/') ? profile.split('/') : ['', profile] - md.renderer.rules.code_sandbox = (tokens, index) => { - const token = tokens[index] - - const content = resolveCodeSandbox(token.meta) - token.content = content - - return content - } + return { + width: attrs.width ? parseRect(attrs.width) : '100%', + height: attrs.height ? parseRect(attrs.height) : '500px', + user, + id, + title: attrs.title ?? '', + console: attrs.console ?? false, + navbar: attrs.navbar ?? true, + layout: attrs.layout ?? '', + type: (type || 'embed') as CodeSandboxTokenMeta['type'], + filepath, + } + }, + content({ title, height, width, user, id, type, filepath, console, navbar, layout }) { + return `` + }, + }) } diff --git a/plugins/plugin-md-power/src/node/features/codepen.ts b/plugins/plugin-md-power/src/node/features/codepen.ts index 8320bbcb..403801de 100644 --- a/plugins/plugin-md-power/src/node/features/codepen.ts +++ b/plugins/plugin-md-power/src/node/features/codepen.ts @@ -4,106 +4,46 @@ * @[codepen preview editable title="" height="400px" tab="css,result" theme="dark"](user/slash) */ import type { PluginWithOptions } from 'markdown-it' -import type { RuleBlock } from 'markdown-it/lib/parser_block.mjs' import { resolveAttrs } from '../utils/resolveAttrs.js' import { parseRect } from '../utils/parseRect.js' import type { CodepenTokenMeta } from '../../shared/codepen.js' +import { createRuleBlock } from '../utils/createRuleBlock.js' const CODEPEN_LINK = 'https://codepen.io/' -// @[codepen]() -const MIN_LENGTH = 12 - -// char codes of `@[codepen` -const START_CODES = [64, 91, 99, 111, 100, 101, 112, 101, 110] - -// regexp to match the import syntax -const SYNTAX_RE = /^@\[codepen(?:\s+([^]*?))?\]\(([^)]*?)\)/ - -function createCodepenRuleBlock(): RuleBlock { - return (state, startLine, endLine, silent) => { - const pos = state.bMarks[startLine] + state.tShift[startLine] - const max = state.eMarks[startLine] - - // return false if the length is shorter than min length - if (pos + MIN_LENGTH > max) - return false - - // check if it's matched the start - for (let i = 0; i < START_CODES.length; i += 1) { - if (state.src.charCodeAt(pos + i) !== START_CODES[i]) - return false - } - - // check if it's matched the syntax - const match = state.src.slice(pos, max).match(SYNTAX_RE) - if (!match) - return false - - // return true as we have matched the syntax - if (silent) - return true - - const [, info = '', source] = match - - const { attrs } = resolveAttrs(info) - const [user, slash] = source.split('/') - - const meta: CodepenTokenMeta = { - width: attrs.width ? parseRect(attrs.width) : '100%', - height: attrs.height ? parseRect(attrs.height) : '400px', - user, - slash, - title: attrs.title, - preview: attrs.preview, - editable: attrs.editable, - tab: attrs.tab ?? 'result', - theme: attrs.theme, - } - - const token = state.push('codepen', '', 0) - - token.meta = meta - token.map = [startLine, startLine + 1] - token.info = info - - state.line = startLine + 1 - - return true - } -} - -function resolveCodepen(meta: CodepenTokenMeta): string { - const { title = 'Codepen', height, width } = meta - const params = new URLSearchParams() - meta.editable && params.set('editable', 'true') - meta.tab && params.set('default-tab', meta.tab) - meta.theme && params.set('theme-id', meta.theme) - - const middle = meta.preview ? '/embed/preview/' : '/embed/' - - const link = `${CODEPEN_LINK}${meta.user}${middle}${meta.slash}?${params.toString()}` - const style = `width:${width};height:${height};margin:16px auto;border-radius:5px;` - - return `` -} - export const codepenPlugin: PluginWithOptions = (md) => { - md.block.ruler.before( - 'import_code', - 'codepen', - createCodepenRuleBlock(), - { - alt: ['paragraph', 'reference', 'blockquote', 'list'], + createRuleBlock(md, { + type: 'codepen', + syntaxPattern: /^@\[codepen(?:\s+([^]*?))?\]\(([^)]*?)\)/, + meta: ([, info = '', source = '']) => { + const { attrs } = resolveAttrs(info) + const [user, slash] = source.split('/') + + return { + width: attrs.width ? parseRect(attrs.width) : '100%', + height: attrs.height ? parseRect(attrs.height) : '400px', + user, + slash, + title: attrs.title, + preview: attrs.preview, + editable: attrs.editable, + tab: attrs.tab ?? 'result', + theme: attrs.theme, + } }, - ) + content: (meta) => { + const { title = 'Codepen', height, width } = meta + const params = new URLSearchParams() + meta.editable && params.set('editable', 'true') + meta.tab && params.set('default-tab', meta.tab) + meta.theme && params.set('theme-id', meta.theme) - md.renderer.rules.codepen = (tokens, index) => { - const token = tokens[index] + const middle = meta.preview ? '/embed/preview/' : '/embed/' - const content = resolveCodepen(token.meta) - token.content = content + const link = `${CODEPEN_LINK}${meta.user}${middle}${meta.slash}?${params.toString()}` + const style = `width:${width};height:${height};margin:16px auto;border-radius:5px;` - return content - } + return `` + }, + }) } diff --git a/plugins/plugin-md-power/src/node/features/jsfiddle.ts b/plugins/plugin-md-power/src/node/features/jsfiddle.ts index 4e0b7426..913d75dc 100644 --- a/plugins/plugin-md-power/src/node/features/jsfiddle.ts +++ b/plugins/plugin-md-power/src/node/features/jsfiddle.ts @@ -3,99 +3,35 @@ * @[jsfiddle theme="dark" tab="js,css,html,result"](user/id) */ import type { PluginWithOptions } from 'markdown-it' -import type { RuleBlock } from 'markdown-it/lib/parser_block.mjs' import { resolveAttrs } from '../utils/resolveAttrs.js' import { parseRect } from '../utils/parseRect.js' import type { JSFiddleTokenMeta } from '../../shared/jsfiddle.js' - -const JS_FIDDLE_LINK = 'https://jsfiddle.net/' - -// @[jsfiddle]() -const MIN_LENGTH = 13 - -// char codes of `@[jsfiddle` -const START_CODES = [64, 91, 106, 115, 102, 105, 100, 100, 108, 101] - -// regexp to match the import syntax -const SYNTAX_RE = /^@\[jsfiddle(?:\s+([^]*?))?\]\(([^)]*?)\)/ - -function createJsFiddleRuleBlock(): RuleBlock { - return (state, startLine, endLine, silent) => { - const pos = state.bMarks[startLine] + state.tShift[startLine] - const max = state.eMarks[startLine] - - // return false if the length is shorter than min length - if (pos + MIN_LENGTH > max) - return false - - // check if it's matched the start - for (let i = 0; i < START_CODES.length; i += 1) { - if (state.src.charCodeAt(pos + i) !== START_CODES[i]) - return false - } - - // check if it's matched the syntax - const match = state.src.slice(pos, max).match(SYNTAX_RE) - if (!match) - return false - - // return true as we have matched the syntax - if (silent) - return true - - const [, info = '', source] = match - - const { attrs } = resolveAttrs(info) - const [user, id] = source.split('/') - - const meta: JSFiddleTokenMeta = { - width: attrs.width ? parseRect(attrs.width) : '100%', - height: attrs.height ? parseRect(attrs.height) : '400px', - user, - id, - title: attrs.title || 'JS Fiddle', - tab: attrs.tab?.replace(/\s+/g, '') || 'js,css,html,result', - theme: attrs.theme || 'dark', - } - - const token = state.push('js_fiddle', '', 0) - - token.meta = meta - token.map = [startLine, startLine + 1] - token.info = info - - state.line = startLine + 1 - - return true - } -} - -function resolveJsFiddle(meta: JSFiddleTokenMeta): string { - const { title = 'JS Fiddle', height, width, user, id, tab } = meta - const theme = meta.theme === 'dark' ? '/dark/' : '' - - const link = `${JS_FIDDLE_LINK}${user}/${id}/embedded/${tab}${theme}` - const style = `width:${width};height:${height};margin:16px auto;border:none;border-radius:5px;` - - return `` -} +import { createRuleBlock } from '../utils/createRuleBlock.js' export const jsfiddlePlugin: PluginWithOptions = (md) => { - md.block.ruler.before( - 'import_code', - 'js_fiddle', - createJsFiddleRuleBlock(), - { - alt: ['paragraph', 'reference', 'blockquote', 'list'], + createRuleBlock(md, { + type: 'jsfiddle', + syntaxPattern: /^@\[jsfiddle(?:\s+([^]*?))?\]\(([^)]*?)\)/, + meta([, info = '', source]) { + const { attrs } = resolveAttrs(info) + const [user, id] = source.split('/') + + return { + width: attrs.width ? parseRect(attrs.width) : '100%', + height: attrs.height ? parseRect(attrs.height) : '400px', + user, + id, + title: attrs.title || 'JS Fiddle', + tab: attrs.tab?.replace(/\s+/g, '') || 'js,css,html,result', + theme: attrs.theme || 'dark', + } }, - ) + content: ({ title = 'JS Fiddle', height, width, user, id, tab, theme }) => { + theme = theme === 'dark' ? '/dark/' : '' - md.renderer.rules.js_fiddle = (tokens, index) => { - const token = tokens[index] - - const content = resolveJsFiddle(token.meta) - token.content = content - - return content - } + const link = `https://jsfiddle.net/${user}/${id}/embedded/${tab}${theme}` + const style = `width:${width};height:${height};margin:16px auto;border:none;border-radius:5px;` + return `` + }, + }) } diff --git a/plugins/plugin-md-power/src/node/features/pdf.ts b/plugins/plugin-md-power/src/node/features/pdf.ts index 2b09294f..e351f8e1 100644 --- a/plugins/plugin-md-power/src/node/features/pdf.ts +++ b/plugins/plugin-md-power/src/node/features/pdf.ts @@ -5,93 +5,30 @@ */ import { path } from 'vuepress/utils' import type { PluginWithOptions } from 'markdown-it' -import type { RuleBlock } from 'markdown-it/lib/parser_block.mjs' import type { PDFTokenMeta } from '../../shared/pdf.js' import { resolveAttrs } from '../utils/resolveAttrs.js' import { parseRect } from '../utils/parseRect.js' - -// @[pdf]() -const MIN_LENGTH = 8 - -// char codes of `@[pdf` -const START_CODES = [64, 91, 112, 100, 102] - -// regexp to match the import syntax -const SYNTAX_RE = /^@\[pdf(?:\s+(\d+))?(?:\s+([^]*?))?\]\(([^)]*?)\)/ - -function createPDFRuleBlock(): RuleBlock { - return (state, startLine, endLine, silent) => { - const pos = state.bMarks[startLine] + state.tShift[startLine] - const max = state.eMarks[startLine] - - // return false if the length is shorter than min length - if (pos + MIN_LENGTH > max) - return false - - // check if it's matched the start - for (let i = 0; i < START_CODES.length; i += 1) { - if (state.src.charCodeAt(pos + i) !== START_CODES[i]) - return false - } - - // check if it's matched the syntax - const match = state.src.slice(pos, max).match(SYNTAX_RE) - if (!match) - return false - - // return true as we have matched the syntax - if (silent) - return true - - const [, page, info = '', src] = match - - const { attrs } = resolveAttrs(info) - - const meta: PDFTokenMeta = { - src, - page: +page || 1, - noToolbar: Boolean(attrs.noToolbar ?? false), - zoom: +attrs.zoom || 50, - width: attrs.width ? parseRect(attrs.width) : '100%', - height: attrs.height ? parseRect(attrs.height) : '', - ratio: attrs.ratio ? parseRect(attrs.ratio) : '', - title: path.basename(src || ''), - } - - const token = state.push('pdf', '', 0) - - token.meta = meta - token.map = [startLine, startLine + 1] - token.info = info - - state.line = startLine + 1 - - return true - } -} - -function resolvePDF(meta: PDFTokenMeta): string { - const { title, src, page, noToolbar, width, height, ratio, zoom } = meta - - return `` -} +import { createRuleBlock } from '../utils/createRuleBlock.js' export const pdfPlugin: PluginWithOptions = (md) => { - md.block.ruler.before( - 'import_code', - 'pdf', - createPDFRuleBlock(), - { - alt: ['paragraph', 'reference', 'blockquote', 'list'], + createRuleBlock(md, { + type: 'pdf', + syntaxPattern: /^@\[pdf(?:\s+(\d+))?(?:\s+([^]*?))?\]\(([^)]*?)\)/, + meta([, page, info = '', src = '']) { + const { attrs } = resolveAttrs(info) + return { + src, + page: +page || 1, + noToolbar: Boolean(attrs.noToolbar ?? false), + zoom: +attrs.zoom || 50, + width: attrs.width ? parseRect(attrs.width) : '100%', + height: attrs.height ? parseRect(attrs.height) : '', + ratio: attrs.ratio ? parseRect(attrs.ratio) : '', + title: path.basename(src || ''), + } }, - ) - - md.renderer.rules.pdf = (tokens, index) => { - const token = tokens[index] - - const content = resolvePDF(token.meta) - token.content = content - - return content - } + content({ title, src, page, noToolbar, width, height, ratio, zoom }) { + return `` + }, + }) } diff --git a/plugins/plugin-md-power/src/node/features/replit.ts b/plugins/plugin-md-power/src/node/features/replit.ts index 047cca7a..2666c42a 100644 --- a/plugins/plugin-md-power/src/node/features/replit.ts +++ b/plugins/plugin-md-power/src/node/features/replit.ts @@ -4,90 +4,27 @@ * @[replit title="" height="400px" width="100%" theme="dark"](user/repl-name) */ import type { PluginWithOptions } from 'markdown-it' -import type { RuleBlock } from 'markdown-it/lib/parser_block.mjs' import { resolveAttrs } from '../utils/resolveAttrs.js' import { parseRect } from '../utils/parseRect.js' import type { ReplitTokenMeta } from '../../shared/replit.js' - -// @[replit]() -const MIN_LENGTH = 11 - -// char codes of `@[replit` -const START_CODES = [64, 91, 114, 101, 112, 108, 105, 116] - -// regexp to match the import syntax -const SYNTAX_RE = /^@\[replit(?:\s+([^]*?))?\]\(([^)]*?)\)/ - -function createReplitRuleBlock(): RuleBlock { - return (state, startLine, endLine, silent) => { - const pos = state.bMarks[startLine] + state.tShift[startLine] - const max = state.eMarks[startLine] - - // return false if the length is shorter than min length - if (pos + MIN_LENGTH > max) - return false - - // check if it's matched the start - for (let i = 0; i < START_CODES.length; i += 1) { - if (state.src.charCodeAt(pos + i) !== START_CODES[i]) - return false - } - - // check if it's matched the syntax - const match = state.src.slice(pos, max).match(SYNTAX_RE) - if (!match) - return false - - // return true as we have matched the syntax - if (silent) - return true - - const [, info = '', source] = match - - const { attrs } = resolveAttrs(info) - - const meta: ReplitTokenMeta = { - width: attrs.width ? parseRect(attrs.width) : '100%', - height: attrs.height ? parseRect(attrs.height) : '450px', - source: source.startsWith('@') ? source : `@${source}`, - title: attrs.title, - theme: attrs.theme || '', - } - - const token = state.push('replit', '', 0) - - token.meta = meta - token.map = [startLine, startLine + 1] - token.info = info - - state.line = startLine + 1 - - return true - } -} - -function resolveReplit(meta: ReplitTokenMeta): string { - const { title, height, width, source, theme } = meta - - return `` -} +import { createRuleBlock } from '../utils/createRuleBlock.js' export const replitPlugin: PluginWithOptions = (md) => { - md.block.ruler.before( - 'import_code', - 'replit', - createReplitRuleBlock(), - { - alt: ['paragraph', 'reference', 'blockquote', 'list'], + createRuleBlock(md, { + type: 'replit', + syntaxPattern: /^@\[replit(?:\s+([^]*?))?\]\(([^)]*?)\)/, + meta: ([, info = '', source = '']) => { + const { attrs } = resolveAttrs(info) + return { + width: attrs.width ? parseRect(attrs.width) : '100%', + height: attrs.height ? parseRect(attrs.height) : '450px', + source: source.startsWith('@') ? source : `@${source}`, + title: attrs.title, + theme: attrs.theme || '', + } }, - ) - - md.renderer.rules.replit = (tokens, index) => { - const token = tokens[index] - - const content = resolveReplit(token.meta) - token.content = content - - return content - } + content({ title, height, width, source, theme }) { + return `` + }, + }) } diff --git a/plugins/plugin-md-power/src/node/features/video/bilibili.ts b/plugins/plugin-md-power/src/node/features/video/bilibili.ts index c1a7657d..f561d9e8 100644 --- a/plugins/plugin-md-power/src/node/features/video/bilibili.ts +++ b/plugins/plugin-md-power/src/node/features/video/bilibili.ts @@ -6,110 +6,51 @@ */ import { URLSearchParams } from 'node:url' import type { PluginWithOptions } from 'markdown-it' -import type { RuleBlock } from 'markdown-it/lib/parser_block.mjs' import type { BilibiliTokenMeta } from '../../../shared/video.js' import { resolveAttrs } from '../../utils/resolveAttrs.js' import { parseRect } from '../../utils/parseRect.js' import { timeToSeconds } from '../../utils/timeToSeconds.js' +import { createRuleBlock } from '../../utils/createRuleBlock.js' const BILIBILI_LINK = 'https://player.bilibili.com/player.html' -// @[bilibili]() -const MIN_LENGTH = 13 - -// char codes of '@[bilibili' -const START_CODES = [64, 91, 98, 105, 108, 105, 98, 105, 108, 105] - -// regexp to match the import syntax -const SYNTAX_RE = /^@\[bilibili(?:\s+p(\d+))?(?:\s+([^]*?))?\]\(([^)]*)\)/ - -function createBilibiliRuleBlock(): RuleBlock { - return (state, startLine, endLine, silent) => { - const pos = state.bMarks[startLine] + state.tShift[startLine] - const max = state.eMarks[startLine] - - // return false if the length is shorter than min length - if (pos + MIN_LENGTH > max) - return false - - // check if it's matched the start - for (let i = 0; i < START_CODES.length; i += 1) { - if (state.src.charCodeAt(pos + i) !== START_CODES[i]) - return false - } - - // check if it's matched the syntax - const match = state.src.slice(pos, max).match(SYNTAX_RE) - if (!match) - return false - - // return true as we have matched the syntax - if (silent) - return true - - const [, page, info = '', source = ''] = match - - const { attrs } = resolveAttrs(info) - const ids = source.trim().split(/\s+/) - const bvid = ids.find(id => id.startsWith('BV')) - const [aid, cid] = ids.filter(id => !id.startsWith('BV')) - - const meta: BilibiliTokenMeta = { - page: +page || 1, - bvid, - aid, - cid, - autoplay: attrs.autoplay ?? false, - time: timeToSeconds(attrs.time), - title: attrs.title, - width: attrs.width ? parseRect(attrs.width) : '100%', - height: attrs.height ? parseRect(attrs.height) : '', - ratio: attrs.ratio ? parseRect(attrs.ratio) : '', - } - - const token = state.push('video_bilibili', '', 0) - - token.meta = meta - token.map = [startLine, startLine + 1] - token.info = info - - state.line = startLine + 1 - - return true - } -} - -function resolveBilibili(meta: BilibiliTokenMeta): string { - const params = new URLSearchParams() - - meta.bvid && params.set('bvid', meta.bvid) - meta.aid && params.set('aid', meta.aid) - meta.cid && params.set('cid', meta.cid) - meta.page && params.set('p', meta.page.toString()) - meta.time && params.set('t', meta.time.toString()) - params.set('autoplay', meta.autoplay ? '1' : '0') - - const source = `${BILIBILI_LINK}?${params.toString()}` - - return `` -} - export const bilibiliPlugin: PluginWithOptions = (md) => { - md.block.ruler.before( - 'import_code', - 'video_bilibili', - createBilibiliRuleBlock(), - { - alt: ['paragraph', 'reference', 'blockquote', 'list'], + createRuleBlock(md, { + type: 'bilibili', + name: 'video_bilibili', + syntaxPattern: /^@\[bilibili(?:\s+p(\d+))?(?:\s+([^]*?))?\]\(([^)]*)\)/, + meta([, page, info = '', source = '']) { + const { attrs } = resolveAttrs(info) + const ids = source.trim().split(/\s+/) + const bvid = ids.find(id => id.startsWith('BV')) + const [aid, cid] = ids.filter(id => !id.startsWith('BV')) + + return { + page: +page || 1, + bvid, + aid, + cid, + autoplay: attrs.autoplay ?? false, + time: timeToSeconds(attrs.time), + title: attrs.title, + width: attrs.width ? parseRect(attrs.width) : '100%', + height: attrs.height ? parseRect(attrs.height) : '', + ratio: attrs.ratio ? parseRect(attrs.ratio) : '', + } }, - ) + content(meta) { + const params = new URLSearchParams() - md.renderer.rules.video_bilibili = (tokens, index) => { - const token = tokens[index] + meta.bvid && params.set('bvid', meta.bvid) + meta.aid && params.set('aid', meta.aid) + meta.cid && params.set('cid', meta.cid) + meta.page && params.set('p', meta.page.toString()) + meta.time && params.set('t', meta.time.toString()) + params.set('autoplay', meta.autoplay ? '1' : '0') - const content = resolveBilibili(token.meta) - token.content = content + const source = `${BILIBILI_LINK}?${params.toString()}` - return content - } + return `` + }, + }) } diff --git a/plugins/plugin-md-power/src/node/features/video/youtube.ts b/plugins/plugin-md-power/src/node/features/video/youtube.ts index ec9b11f2..b5bdf78e 100644 --- a/plugins/plugin-md-power/src/node/features/video/youtube.ts +++ b/plugins/plugin-md-power/src/node/features/video/youtube.ts @@ -3,104 +3,45 @@ */ import { URLSearchParams } from 'node:url' import type { PluginWithOptions } from 'markdown-it' -import type { RuleBlock } from 'markdown-it/lib/parser_block.mjs' import type { YoutubeTokenMeta } from '../../../shared/video.js' import { resolveAttrs } from '../../utils/resolveAttrs.js' import { parseRect } from '../../utils/parseRect.js' import { timeToSeconds } from '../../utils/timeToSeconds.js' +import { createRuleBlock } from '../../utils/createRuleBlock.js' const YOUTUBE_LINK = 'https://www.youtube.com/embed/' -// @[youtube]() -const MIN_LENGTH = 13 - -// char codes of '@[youtube' -const START_CODES = [64, 91, 121, 111, 117, 116, 117, 98, 101] - -// regexp to match the import syntax -const SYNTAX_RE = /^@\[youtube(?:\s+([^]*?))?\]\(([^)]*)\)/ - -function createYoutubeRuleBlock(): RuleBlock { - return (state, startLine, endLine, silent) => { - const pos = state.bMarks[startLine] + state.tShift[startLine] - const max = state.eMarks[startLine] - - // return false if the length is shorter than min length - if (pos + MIN_LENGTH > max) - return false - - // check if it's matched the start - for (let i = 0; i < START_CODES.length; i += 1) { - if (state.src.charCodeAt(pos + i) !== START_CODES[i]) - return false - } - - // check if it's matched the syntax - const match = state.src.slice(pos, max).match(SYNTAX_RE) - if (!match) - return false - - // return true as we have matched the syntax - if (silent) - return true - - const [, info = '', id = ''] = match - - const { attrs } = resolveAttrs(info) - - const meta: YoutubeTokenMeta = { - id, - autoplay: attrs.autoplay ?? false, - loop: attrs.loop ?? false, - start: timeToSeconds(attrs.start), - end: timeToSeconds(attrs.end), - title: attrs.title, - width: attrs.width ? parseRect(attrs.width) : '100%', - height: attrs.height ? parseRect(attrs.height) : '', - ratio: attrs.ratio ? parseRect(attrs.ratio) : '', - } - - const token = state.push('video_youtube', '', 0) - - token.meta = meta - token.map = [startLine, startLine + 1] - token.info = info - - state.line = startLine + 1 - - return true - } -} - -function resolveYoutube(meta: YoutubeTokenMeta): string { - const params = new URLSearchParams() - - meta.autoplay && params.set('autoplay', '1') - meta.loop && params.set('loop', '1') - meta.start && params.set('start', meta.start.toString()) - meta.end && params.set('end', meta.end.toString()) - - const source = `${YOUTUBE_LINK}/${meta.id}?${params.toString()}` - - return `` -} - export const youtubePlugin: PluginWithOptions = (md) => { - md.block.ruler.before( - 'import_code', - 'video_youtube', - createYoutubeRuleBlock(), - { - alt: ['paragraph', 'reference', 'blockquote', 'list'], + createRuleBlock(md, { + type: 'youtube', + name: 'video_youtube', + syntaxPattern: /^@\[youtube(?:\s+([^]*?))?\]\(([^)]*)\)/, + meta([, info = '', id = '']) { + const { attrs } = resolveAttrs(info) + + return { + id, + autoplay: attrs.autoplay ?? false, + loop: attrs.loop ?? false, + start: timeToSeconds(attrs.start), + end: timeToSeconds(attrs.end), + title: attrs.title, + width: attrs.width ? parseRect(attrs.width) : '100%', + height: attrs.height ? parseRect(attrs.height) : '', + ratio: attrs.ratio ? parseRect(attrs.ratio) : '', + } }, - ) + content(meta) { + const params = new URLSearchParams() - md.renderer.rules.video_youtube = (tokens, index) => { - const token = tokens[index] + meta.autoplay && params.set('autoplay', '1') + meta.loop && params.set('loop', '1') + meta.start && params.set('start', meta.start.toString()) + meta.end && params.set('end', meta.end.toString()) - const content = resolveYoutube(token.meta) - token.content = content + const source = `${YOUTUBE_LINK}/${meta.id}?${params.toString()}` - return content - } + return `` + }, + }) } diff --git a/plugins/plugin-md-power/src/node/utils/createRuleBlock.ts b/plugins/plugin-md-power/src/node/utils/createRuleBlock.ts new file mode 100644 index 00000000..7aa977e3 --- /dev/null +++ b/plugins/plugin-md-power/src/node/utils/createRuleBlock.ts @@ -0,0 +1,79 @@ +import type { RuleOptions } from 'markdown-it/lib/ruler.mjs' +import type { Markdown } from 'vuepress/markdown' + +export interface RuleBlockOptions> { + /** + * @[type]() + */ + type: string + /** + * token name + */ + name?: string + beforeName?: string + syntaxPattern: RegExp + ruleOptions?: RuleOptions + meta: (match: RegExpMatchArray) => Meta + content: (meta: Meta) => string +} + +// @[name]() +export function createRuleBlock = Record>( + md: Markdown, + { + type, + name = type, + syntaxPattern, + beforeName = 'import_code', + ruleOptions = { alt: ['paragraph', 'reference', 'blockquote', 'list'] }, + meta, + content, + }: RuleBlockOptions, +): void { + const MIN_LENGTH = type.length + 5 + const START_CODES = [64, 91, ...type.split('').map(c => c.charCodeAt(0))] + + md.block.ruler.before( + beforeName, + name, + (state, startLine, endLine, silent) => { + const pos = state.bMarks[startLine] + state.tShift[startLine] + const max = state.eMarks[startLine] + + // return false if the length is shorter than min length + if (pos + MIN_LENGTH > max) + return false + + // check if it's matched the start + for (let i = 0; i < START_CODES.length; i += 1) { + if (state.src.charCodeAt(pos + i) !== START_CODES[i]) + return false + } + + // check if it's matched the syntax + const match = state.src.slice(pos, max).match(syntaxPattern) + if (!match) + return false + + // return true as we have matched the syntax + if (silent) + return true + + const token = state.push(name, '', 0) + + token.meta = meta(match) + token.map = [startLine, startLine + 1] + + state.line = startLine + 1 + + return true + }, + ruleOptions, + ) + + md.renderer.rules[name] = (tokens, index) => { + const token = tokens[index] + token.content = content(token.meta) + return token.content + } +} diff --git a/theme/src/client/components/FriendsGroup.vue b/theme/src/client/components/FriendsGroup.vue index d76d080f..da1a68a0 100644 --- a/theme/src/client/components/FriendsGroup.vue +++ b/theme/src/client/components/FriendsGroup.vue @@ -16,7 +16,11 @@ defineProps<{ {{ group.desc }}

- +
diff --git a/theme/src/client/components/TransitionFadeSlideY.vue b/theme/src/client/components/TransitionFadeSlideY.vue index 80d942c3..4c5558c3 100644 --- a/theme/src/client/components/TransitionFadeSlideY.vue +++ b/theme/src/client/components/TransitionFadeSlideY.vue @@ -14,4 +14,3 @@ const { resolve: onBeforeEnter, pending: onBeforeLeave } = useScrollPromise() -../composables/scroll-promise.js diff --git a/theme/src/client/layouts/NotFound.vue b/theme/src/client/layouts/NotFound.vue index ca4bba18..931bef65 100644 --- a/theme/src/client/layouts/NotFound.vue +++ b/theme/src/client/layouts/NotFound.vue @@ -2,10 +2,10 @@ import { useRouteLocale, withBase } from 'vuepress/client' import LayoutContent from '../components/LayoutContent.vue' import Nav from '../components/Nav/index.vue' -import { useThemeLocaleData } from '../composables' +import { useData } from '../composables/data.js' const root = useRouteLocale() -const themeData = useThemeLocaleData() +const { theme } = useData()