perf(plugin-md-power): improve code embed (#497)
This commit is contained in:
parent
5ee3bc4d2f
commit
99ba7be92d
@ -1,9 +1,9 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`codepenPlugin > should not work 1`] = `
|
||||
"<iframe class="code-pen-iframe-wrapper" src="https://codepen.io//embed/undefined?default-tab=result" title="Codepen" style="width:100%;height:400px;margin:16px auto;border-radius:5px;" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">See the Pen <a href="https://codepen.io//pen/undefined">Codepen</a> by (<a href="https://codepen.io/">@</a>) on <a href="https://codepen.io/">CodePen</a>.</iframe><p>@[codepen]xxx</p>
|
||||
"<CodePenViewer user="" slash="undefined" title="Code Pen" tab="result" width="100%" height="400px" /><p>@[codepen]xxx</p>
|
||||
<p>@[codepen preview](</p>
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`codepenPlugin > should work 1`] = `"<iframe class="code-pen-iframe-wrapper" src="https://codepen.io/user/embed/slash?default-tab=result" title="Codepen" style="width:100%;height:400px;margin:16px auto;border-radius:5px;" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">See the Pen <a href="https://codepen.io/user/pen/slash">Codepen</a> by user (<a href="https://codepen.io/user">@user</a>) on <a href="https://codepen.io/">CodePen</a>.</iframe><iframe class="code-pen-iframe-wrapper" src="https://codepen.io/user/embed/preview/slash?default-tab=result" title="Codepen" style="width:100%;height:400px;margin:16px auto;border-radius:5px;" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">See the Pen <a href="https://codepen.io/user/pen/slash">Codepen</a> by user (<a href="https://codepen.io/user">@user</a>) on <a href="https://codepen.io/">CodePen</a>.</iframe><iframe class="code-pen-iframe-wrapper" src="https://codepen.io/user/embed/preview/slash?editable=true&default-tab=css%2Cresult&theme-id=dark" title="codepen" style="width:100%;height:400px;margin:16px auto;border-radius:5px;" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">See the Pen <a href="https://codepen.io/user/pen/slash">codepen</a> by user (<a href="https://codepen.io/user">@user</a>) on <a href="https://codepen.io/">CodePen</a>.</iframe>"`;
|
||||
exports[`codepenPlugin > should work 1`] = `"<CodePenViewer user="user" slash="slash" title="Code Pen" tab="result" width="100%" height="400px" /><CodePenViewer user="user" slash="slash" title="Code Pen" preview tab="result" width="100%" height="400px" /><CodePenViewer user="user" slash="slash" title="codepen" preview editable tab="css,result" theme="dark" width="100%" height="400px" />"`;
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`codeSandboxPlugin > should not work 1`] = `
|
||||
"<iframe class="js-fiddle-iframe-wrapper" style="width:100%;height:400px;margin:16px auto;border:none;border-radius:5px;" title="JS Fiddle" src="https://jsfiddle.net//undefined/embedded/js,css,html,result/dark/" allowfullscreen="true" allowpaymentrequest="true"></iframe><p>@[jsfiddle]xxx</p>
|
||||
"<JSFiddleViewer source="" title="JS Fiddle" tab="js,css,html,result" width="100%" height="400px" /><p>@[jsfiddle]xxx</p>
|
||||
<p>@[jsfiddle](</p>
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`codeSandboxPlugin > should work 1`] = `"<iframe class="js-fiddle-iframe-wrapper" style="width:100%;height:400px;margin:16px auto;border:none;border-radius:5px;" title="JS Fiddle" src="https://jsfiddle.net/user/id/embedded/js,css,html,result/dark/" allowfullscreen="true" allowpaymentrequest="true"></iframe><iframe class="js-fiddle-iframe-wrapper" style="width:100%;height:400px;margin:16px auto;border:none;border-radius:5px;" title="JS Fiddle" src="https://jsfiddle.net/user/id/embedded/js,css,html,result/dark/" allowfullscreen="true" allowpaymentrequest="true"></iframe><iframe class="js-fiddle-iframe-wrapper" style="width:100%;height:400px;margin:16px auto;border:none;border-radius:5px;" title="JS Fiddle" src="https://jsfiddle.net/user/id/embedded/js,css,html,result" allowfullscreen="true" allowpaymentrequest="true"></iframe><iframe class="js-fiddle-iframe-wrapper" style="width:100%;height:500px;margin:16px auto;border:none;border-radius:5px;" title="xxx" src="https://jsfiddle.net/user/id/embedded/js,css,html,result/dark/" allowfullscreen="true" allowpaymentrequest="true"></iframe>"`;
|
||||
exports[`codeSandboxPlugin > should work 1`] = `"<JSFiddleViewer source="user/id" title="JS Fiddle" tab="js,css,html,result" width="100%" height="400px" /><JSFiddleViewer source="user/id" title="JS Fiddle" tab="js,css,html,result" width="100%" height="400px" /><JSFiddleViewer source="user/id" title="JS Fiddle" tab="js,css,html,result" width="100%" height="400px" theme="light" /><JSFiddleViewer source="user/id" title="xxx" tab="js,css,html,result" width="100%" height="500px" theme="dark" />"`;
|
||||
|
||||
@ -82,6 +82,12 @@ useEventListener('scroll', updatePosition, { passive: true })
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
@media print {
|
||||
.vpi-annotation {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.vp-annotation.active {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useDarkMode } from '@vuepress/helper/client'
|
||||
import { useEventListener } from '@vueuse/core'
|
||||
import { computed, getCurrentInstance, ref } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
interface MessageData {
|
||||
type: string
|
||||
@ -23,11 +24,10 @@ const props = withDefaults(defineProps<{
|
||||
})
|
||||
|
||||
const url = 'https://caniuse.pengzhanbo.cn/'
|
||||
const current = getCurrentInstance()
|
||||
|
||||
const height = ref('330px')
|
||||
|
||||
const isDark = computed(() => current?.appContext.config.globalProperties.$isDark.value)
|
||||
const isDark = useDarkMode()
|
||||
const source = computed(() => {
|
||||
const source = `${url}${props.feature}#past=${props.past}&future=${props.future}&meta=${props.meta}&theme=${isDark.value ? 'dark' : 'light'}`
|
||||
|
||||
|
||||
54
plugins/plugin-md-power/src/client/components/CodePen.vue
Normal file
54
plugins/plugin-md-power/src/client/components/CodePen.vue
Normal file
@ -0,0 +1,54 @@
|
||||
<script setup lang="ts">
|
||||
import { useDarkMode } from '@vuepress/helper/client'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
user: string
|
||||
slash: string
|
||||
title?: string
|
||||
preview?: boolean
|
||||
editable?: boolean
|
||||
tab?: string
|
||||
theme?: 'light' | 'dark'
|
||||
width?: string
|
||||
height?: string
|
||||
}>()
|
||||
|
||||
const CODEPEN_LINK = 'https://codepen.io/'
|
||||
|
||||
const isDark = useDarkMode()
|
||||
|
||||
const link = computed(() => {
|
||||
const middle = props.preview ? '/embed/preview/' : '/embed/'
|
||||
const params = new URLSearchParams()
|
||||
|
||||
props.editable && params.set('editable', 'true')
|
||||
props.tab && params.set('default-tab', props.tab)
|
||||
|
||||
const theme = props.theme ?? (isDark.value ? 'dark' : 'light')
|
||||
theme && params.set('theme-id', theme)
|
||||
|
||||
return `${CODEPEN_LINK}${props.user}${middle}${props.slash}?${params.toString()}`
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<iframe
|
||||
:src="link"
|
||||
class="code-pen-iframe"
|
||||
:title="title"
|
||||
:style="{ width, height }"
|
||||
frameborder="0"
|
||||
loading="lazy"
|
||||
allowtransparency="true"
|
||||
allowfullscreen="true"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.code-pen-iframe {
|
||||
margin: 16px auto;
|
||||
border: none;
|
||||
box-shadow: var(--vp-shadow-2);
|
||||
}
|
||||
</style>
|
||||
40
plugins/plugin-md-power/src/client/components/JsFiddle.vue
Normal file
40
plugins/plugin-md-power/src/client/components/JsFiddle.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<script setup lang="ts">
|
||||
import { useDarkMode } from '@vuepress/helper/client'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
source: string
|
||||
title?: string
|
||||
tab: string
|
||||
theme?: 'light' | 'dark'
|
||||
width?: string
|
||||
height?: string
|
||||
}>()
|
||||
|
||||
const isDark = useDarkMode()
|
||||
|
||||
const link = computed(() => {
|
||||
const theme = props.theme === 'dark' ? '/dark/' : isDark.value ? '/dark/' : ''
|
||||
return `https://jsfiddle.net/${props.source}/embedded/${props.tab}${theme}`
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<iframe
|
||||
class="js-fiddle-iframe"
|
||||
:src="link"
|
||||
:title="title"
|
||||
:style="{ width, height }"
|
||||
frameborder="0"
|
||||
allowfullscreen="true"
|
||||
allowpaymentrequest="true"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.js-fiddle-iframe {
|
||||
margin: 16px auto;
|
||||
border: none;
|
||||
box-shadow: var(--vp-shadow-2);
|
||||
}
|
||||
</style>
|
||||
@ -1,18 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import type { ReplitTokenMeta } from '../../shared/index.js'
|
||||
import { computed, getCurrentInstance, ref } from 'vue'
|
||||
import { useDarkMode } from '@vuepress/helper/client'
|
||||
import { computed, ref } from 'vue'
|
||||
import Loading from './icons/Loading.vue'
|
||||
|
||||
const props = defineProps<ReplitTokenMeta>()
|
||||
|
||||
const current = getCurrentInstance()
|
||||
// magic height
|
||||
const height = ref('47px')
|
||||
const loaded = ref(false)
|
||||
|
||||
const REPLIT_LINK = 'https://replit.com/'
|
||||
|
||||
const isDark = computed(() => current?.appContext.config.globalProperties.$isDark.value)
|
||||
const isDark = useDarkMode()
|
||||
|
||||
const link = computed(() => {
|
||||
const url = new URL(`/${props.source}`, REPLIT_LINK)
|
||||
@ -50,7 +50,7 @@ function onload() {
|
||||
width: 100%;
|
||||
margin: 16px auto;
|
||||
border: none;
|
||||
border-top: 1px solid var(--vp-c-divider, #e2e2e3);
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
border-bottom-right-radius: 8px;
|
||||
border-bottom-left-radius: 8px;
|
||||
transition: border 0.25s;
|
||||
|
||||
@ -12,10 +12,10 @@
|
||||
*/
|
||||
|
||||
import type { PDFEmbedType, PDFTokenMeta } from '../../shared/index.js'
|
||||
import { checkIsiPad, checkIsMobile, checkIsSafari } from '@vuepress/helper/client'
|
||||
import { withBase } from 'vuepress/client'
|
||||
import { ensureEndingSlash, isLinkHttp } from 'vuepress/shared'
|
||||
import { pluginOptions } from '../options.js'
|
||||
import { checkIsiPad, checkIsMobile, checkIsSafari } from '../utils/is.js'
|
||||
|
||||
function queryStringify(options: PDFTokenMeta): string {
|
||||
const { page, noToolbar, zoom } = options
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
export function checkIsMobile(ua: string): boolean {
|
||||
return /\b(?:Android|iPhone)/i.test(ua)
|
||||
}
|
||||
|
||||
export function checkIsSafari(ua: string): boolean {
|
||||
return /version\/[\w.]+ .*(?:mobile ?safari|safari)/i.test(ua)
|
||||
}
|
||||
|
||||
export function checkIsiPad(ua: string): boolean {
|
||||
return [
|
||||
/\((ipad);[-\w),; ]+apple/i,
|
||||
/applecoremedia\/[\w.]+ \((ipad)/i,
|
||||
/\b(ipad)\d\d?,\d\d?[;\]].+ios/i,
|
||||
].some(item => item.test(ua))
|
||||
}
|
||||
@ -30,9 +30,9 @@ export const codeTabs: PluginWithOptions<CodeTabsOptions> = (md, options: CodeTa
|
||||
tab(md, {
|
||||
name: 'code-tabs',
|
||||
|
||||
tabsOpenRenderer: ({ active, data }, tokens, index) => {
|
||||
tabsOpenRenderer: ({ active, data }, tokens, index, _, env) => {
|
||||
const { meta } = tokens[index]
|
||||
const titles = data.map(({ title }) => md.renderInline(title))
|
||||
const titles = data.map(({ title }) => md.renderInline(title, env))
|
||||
const tabsData = data.map((item, dataIndex) => {
|
||||
const { id = titles[dataIndex] } = item
|
||||
|
||||
|
||||
@ -6,9 +6,9 @@ export const tabs: PluginSimple = (md) => {
|
||||
tab(md, {
|
||||
name: 'tabs',
|
||||
|
||||
tabsOpenRenderer: ({ active, data }, tokens, index) => {
|
||||
tabsOpenRenderer: ({ active, data }, tokens, index, _, env) => {
|
||||
const { meta } = tokens[index]
|
||||
const titles = data.map(({ title }) => md.renderInline(title))
|
||||
const titles = data.map(({ title }) => md.renderInline(title, env))
|
||||
const tabsData = data.map((item, dataIndex) => {
|
||||
const { id = titles[dataIndex] } = item
|
||||
|
||||
|
||||
@ -9,48 +9,25 @@ import { parseRect } from '../../utils/parseRect.js'
|
||||
import { resolveAttrs } from '../../utils/resolveAttrs.js'
|
||||
import { createEmbedRuleBlock } from '../createEmbedRuleBlock.js'
|
||||
|
||||
const CODEPEN_LINK = 'https://codepen.io/'
|
||||
|
||||
export const codepenPlugin: PluginWithOptions<never> = (md) => {
|
||||
createEmbedRuleBlock<CodepenTokenMeta>(md, {
|
||||
type: 'codepen',
|
||||
syntaxPattern: /^@\[codepen([^\]]*)\]\(([^)]*)\)/,
|
||||
meta: ([, info, source]) => {
|
||||
const { attrs } = resolveAttrs(info)
|
||||
const { width, height, title, tab, ...rest } = resolveAttrs<CodepenTokenMeta>(info).attrs
|
||||
const [user, slash] = source.split('/')
|
||||
|
||||
return {
|
||||
width: attrs.width ? parseRect(attrs.width) : '100%',
|
||||
height: attrs.height ? parseRect(attrs.height) : '400px',
|
||||
width: width ? parseRect(width) : '100%',
|
||||
height: height ? parseRect(height) : '400px',
|
||||
user,
|
||||
slash,
|
||||
title: attrs.title,
|
||||
preview: attrs.preview,
|
||||
editable: attrs.editable,
|
||||
tab: attrs.tab || 'result',
|
||||
theme: attrs.theme,
|
||||
title: title || 'Code Pen',
|
||||
tab: tab || 'result',
|
||||
...rest,
|
||||
}
|
||||
},
|
||||
content: (meta) => {
|
||||
const { title = 'Codepen', height, width } = meta
|
||||
const params = new URLSearchParams()
|
||||
|
||||
if (meta.editable) {
|
||||
params.set('editable', 'true')
|
||||
}
|
||||
|
||||
params.set('default-tab', meta.tab!)
|
||||
|
||||
if (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 `<iframe class="code-pen-iframe-wrapper" src="${link}" title="${title}" style="${style}" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">See the Pen <a href="${CODEPEN_LINK}${meta.user}/pen/${meta.slash}">${title}</a> by ${meta.user} (<a href="${CODEPEN_LINK}${meta.user}">@${meta.user}</a>) on <a href="${CODEPEN_LINK}">CodePen</a>.</iframe>`
|
||||
},
|
||||
content: ({ title, height, width, user, slash, preview, editable, tab, theme }) =>
|
||||
`<CodePenViewer user="${user}" slash="${slash}" title="${title}"${preview ? ' preview' : ''}${editable ? ' editable' : ''} tab="${tab}"${theme ? ` theme="${theme}"` : ''} width="${width}" height="${height}" />`,
|
||||
})
|
||||
}
|
||||
|
||||
@ -13,25 +13,18 @@ export const jsfiddlePlugin: PluginWithOptions<never> = (md) => {
|
||||
type: 'jsfiddle',
|
||||
syntaxPattern: /^@\[jsfiddle([^\]]*)\]\(([^)]*)\)/,
|
||||
meta([, info, source]) {
|
||||
const { attrs } = resolveAttrs(info)
|
||||
const [user, id] = source.split('/')
|
||||
const { width, height, title, tab, theme } = resolveAttrs<JSFiddleTokenMeta>(info).attrs
|
||||
|
||||
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',
|
||||
width: width ? parseRect(width) : '100%',
|
||||
height: height ? parseRect(height) : '400px',
|
||||
source,
|
||||
title: title || 'JS Fiddle',
|
||||
tab: tab?.replace(/\s+/g, '') || 'js,css,html,result',
|
||||
theme,
|
||||
}
|
||||
},
|
||||
content: ({ title, height, width, user, id, tab, theme }) => {
|
||||
theme = theme === 'dark' ? '/dark/' : ''
|
||||
|
||||
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 `<iframe class="js-fiddle-iframe-wrapper" style="${style}" title="${title}" src="${link}" allowfullscreen="true" allowpaymentrequest="true"></iframe>`
|
||||
},
|
||||
content: ({ title, height, width, source, tab, theme }) =>
|
||||
`<JSFiddleViewer source="${source}" title="${title}" tab="${tab}" width="${width}" height="${height}"${theme ? ` theme="${theme}"` : ''} />`,
|
||||
})
|
||||
}
|
||||
|
||||
@ -35,6 +35,16 @@ export async function prepareConfigFile(app: App, options: MarkdownPowerPluginOp
|
||||
enhances.add(`app.component('VideoYoutube', Youtube)`)
|
||||
}
|
||||
|
||||
if (options.codepen) {
|
||||
imports.add(`import CodePen from '${CLIENT_FOLDER}components/CodePen.vue'`)
|
||||
enhances.add(`app.component('CodePenViewer', CodePen)`)
|
||||
}
|
||||
|
||||
if (options.jsfiddle) {
|
||||
imports.add(`import JSFiddle from '${CLIENT_FOLDER}components/JSFiddle.vue'`)
|
||||
enhances.add(`app.component('JSFiddleViewer', JSFiddle)`)
|
||||
}
|
||||
|
||||
if (options.replit) {
|
||||
imports.add(`import Replit from '${CLIENT_FOLDER}components/Replit.vue'`)
|
||||
enhances.add(`app.component('ReplitViewer', Replit)`)
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import type { SizeOptions } from './size'
|
||||
|
||||
export interface JSFiddleTokenMeta extends SizeOptions {
|
||||
user?: string
|
||||
id?: string
|
||||
source: string
|
||||
title?: string
|
||||
theme?: string
|
||||
tab?: string
|
||||
|
||||
@ -4,7 +4,7 @@ import { argv } from '../../scripts/tsup-args.js'
|
||||
|
||||
const config = [
|
||||
{ dir: 'composables', files: ['codeRepl.ts', 'pdf.ts', 'rustRepl.ts', 'size.ts', 'audio.ts', 'demo.ts'] },
|
||||
{ dir: 'utils', files: ['http.ts', 'is.ts', 'link.ts', 'sleep.ts'] },
|
||||
{ dir: 'utils', files: ['http.ts', 'link.ts', 'sleep.ts'] },
|
||||
{ dir: '', files: ['index.ts', 'options.ts'] },
|
||||
]
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user