Skip to content

Commit

Permalink
feat(plugin-md-power): add @[codesandbox](user/id) syntax supported
Browse files Browse the repository at this point in the history
  • Loading branch information
pengzhanbo committed Apr 2, 2024
1 parent 9124f78 commit 3a6ebcf
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/.vuepress/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export const theme: Theme = themePlume({
icons: true,
codepen: true,
replit: true,
codeSandbox: true,
},
comment: {
provider: 'Giscus',
Expand Down
64 changes: 64 additions & 0 deletions plugins/plugin-md-power/src/client/components/CodeSandbox.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<script setup lang="ts">
import { computed } from 'vue'
import type { CodeSandboxTokenMeta } from '../../shared/codeSandbox.js'
const props = defineProps<CodeSandboxTokenMeta>()
const EMBED_LINK = 'https://codesandbox.io/embed/'
const SHARE_LINK = 'https://codesandbox.io/p/sandbox/'
const ALLOW = 'accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking'
const SANDBOX = 'allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts'
const source = computed(() => {
const params = new URLSearchParams()
props.filepath && params.set(props.type === 'embed' ? 'module' : 'file', encodeURIComponent(props.filepath))
if (props.type === 'embed') {
params.set('view', props.layout ? props.layout.replace(/,/g, '+') : 'Editor+Preview')
props.console && params.set('expanddevtools', '1')
props.navbar === false && params.set('hidenavigation', '1')
}
else {
params.set('from-embed', '')
}
const link = props.type === 'embed' ? EMBED_LINK : SHARE_LINK
const workspace = props.type !== 'embed' && props.user ? `${props.user}-${props.id}` : props.id
return `${link}${workspace}?${params.toString()}`
})
</script>

<template>
<ClientOnly v-if="type === 'embed'">
<iframe
:src="source" class="code-sandbox-iframe" :title="title || 'CodeSandbox'" :allow="ALLOW" :sandbox="SANDBOX"
:style="{ width, height }"
/>
</ClientOnly>
<p v-else>
<a :href="source" target="_blank" rel="noopener noreferrer" :aria-label="title || 'CodeSandbox'">
<svg xmlns="http://www.w3.org/2000/svg" width="165" height="32" viewBox="0 0 165 32" fill="none">
<rect width="165" height="32" rx="4" fill="#E3FF73" />
<rect x="0.5" y="0.5" width="164" height="31" rx="3.5" stroke="black" stroke-opacity="0.1" />
<line x1="31.5" y1="2.18557e-08" x2="31.5" y2="32" stroke="black" stroke-opacity="0.1" />
<path
fill-rule="evenodd" clip-rule="evenodd"
d="M10 10L23 10V23H10V10ZM21.6705 11.3295V21.6705H11.3295V11.3295H21.6705Z" fill="#191919"
/>
<path
d="M38.9988 21H44.7516V19.8192H40.3704V17.036H44.4009V15.8597H40.3704V13.0898H44.6983V11.9091H38.9988V21ZM48.6013 21.1332C49.8442 21.1332 50.3325 20.3741 50.5722 19.9391H50.6832V21H51.9794V11.9091H50.6521V15.2871H50.5722C50.3325 14.8654 49.8797 14.093 48.6102 14.093C46.9634 14.093 45.7516 15.3936 45.7516 17.6042C45.7516 19.8104 46.9456 21.1332 48.6013 21.1332ZM48.8943 20.0012C47.7091 20.0012 47.0921 18.9581 47.0921 17.5909C47.0921 16.237 47.6958 15.2205 48.8943 15.2205C50.0529 15.2205 50.6743 16.166 50.6743 17.5909C50.6743 19.0247 50.0396 20.0012 48.8943 20.0012ZM53.4987 21H54.8259V14.1818H53.4987V21ZM54.1689 13.1298C54.6262 13.1298 55.0079 12.7747 55.0079 12.3397C55.0079 11.9047 54.6262 11.5451 54.1689 11.5451C53.7073 11.5451 53.33 11.9047 53.33 12.3397C53.33 12.7747 53.7073 13.1298 54.1689 13.1298ZM59.4192 14.1818H58.0209V12.5483H56.6937V14.1818H55.6949V15.2472H56.6937V19.2733C56.6892 20.5117 57.6347 21.111 58.6823 21.0888C59.104 21.0843 59.3881 21.0044 59.5435 20.9467L59.3038 19.8503C59.215 19.8681 59.0507 19.908 58.8377 19.908C58.4071 19.908 58.0209 19.766 58.0209 18.998V15.2472H59.4192V14.1818ZM63.4454 21H64.7727V14.1818H63.4454V21ZM64.1157 13.1298C64.5729 13.1298 64.9547 12.7747 64.9547 12.3397C64.9547 11.9047 64.5729 11.5451 64.1157 11.5451C63.6541 11.5451 63.2768 11.9047 63.2768 12.3397C63.2768 12.7747 63.6541 13.1298 64.1157 13.1298ZM67.5105 16.9517C67.5105 15.8642 68.1763 15.2427 69.0996 15.2427C70.0007 15.2427 70.5467 15.8331 70.5467 16.823V21H71.8739V16.6632C71.8739 14.9764 70.9462 14.093 69.5524 14.093C68.527 14.093 67.8567 14.568 67.5415 15.2915H67.4572V14.1818H66.1832V21H67.5105V16.9517ZM83.8692 14.8654C83.5585 12.9212 82.0359 11.7848 80.1139 11.7848C77.7612 11.7848 76.0256 13.5471 76.0256 16.4545C76.0256 19.362 77.7524 21.1243 80.1139 21.1243C82.1114 21.1243 83.5718 19.8725 83.8692 18.0748L82.4842 18.0703C82.249 19.2333 81.2724 19.8725 80.1227 19.8725C78.5647 19.8725 77.3884 18.6784 77.3884 16.4545C77.3884 14.2484 78.5602 13.0366 80.1272 13.0366C81.2857 13.0366 82.2579 13.6891 82.4842 14.8654H83.8692ZM87.9009 21.1376C89.823 21.1376 91.0792 19.7305 91.0792 17.622C91.0792 15.5002 89.823 14.093 87.9009 14.093C85.9789 14.093 84.7227 15.5002 84.7227 17.622C84.7227 19.7305 85.9789 21.1376 87.9009 21.1376ZM87.9054 20.0234C86.6491 20.0234 86.0632 18.927 86.0632 17.6175C86.0632 16.3125 86.6491 15.2028 87.9054 15.2028C89.1527 15.2028 89.7386 16.3125 89.7386 17.6175C89.7386 18.927 89.1527 20.0234 87.9054 20.0234ZM94.7381 21.1332C95.981 21.1332 96.4692 20.3741 96.7089 19.9391H96.8199V21H98.1161V11.9091H96.7888V15.2871H96.7089C96.4692 14.8654 96.0165 14.093 94.7469 14.093C93.1001 14.093 91.8883 15.3936 91.8883 17.6042C91.8883 19.8104 93.0823 21.1332 94.7381 21.1332ZM95.031 20.0012C93.8458 20.0012 93.2288 18.9581 93.2288 17.5909C93.2288 16.237 93.8325 15.2205 95.031 15.2205C96.1896 15.2205 96.811 16.166 96.811 17.5909C96.811 19.0247 96.1763 20.0012 95.031 20.0012ZM102.574 21.1376C104.061 21.1376 105.113 20.4052 105.415 19.2955L104.159 19.0691C103.919 19.7127 103.342 20.0412 102.587 20.0412C101.451 20.0412 100.687 19.3043 100.652 17.9904H105.499V17.5199C105.499 15.0563 104.025 14.093 102.481 14.093C100.581 14.093 99.3291 15.5401 99.3291 17.6353C99.3291 19.7527 100.563 21.1376 102.574 21.1376ZM100.656 16.9961C100.71 16.0284 101.411 15.1895 102.49 15.1895C103.519 15.1895 104.194 15.9529 104.199 16.9961H100.656ZM111.662 14.2972H112.984C112.944 12.8413 111.657 11.7848 109.811 11.7848C107.986 11.7848 106.588 12.8279 106.588 14.3949C106.588 15.66 107.493 16.4013 108.954 16.7963L110.028 17.0893C111 17.3468 111.75 17.6664 111.75 18.4743C111.75 19.362 110.903 19.948 109.735 19.948C108.679 19.948 107.8 19.4775 107.72 18.4876H106.344C106.433 20.1344 107.707 21.1509 109.744 21.1509C111.879 21.1509 113.109 20.0279 113.109 18.4876C113.109 16.8496 111.648 16.2148 110.494 15.9308L109.606 15.6999C108.896 15.5179 107.951 15.185 107.955 14.315C107.955 13.5426 108.661 12.97 109.78 12.97C110.823 12.97 111.564 13.4583 111.662 14.2972ZM116.25 21.1509C117.378 21.1509 118.013 20.5783 118.266 20.0678H118.319V21H119.615V16.4723C119.615 14.4881 118.053 14.093 116.97 14.093C115.736 14.093 114.599 14.5902 114.155 15.8331L115.403 16.1172C115.598 15.6333 116.095 15.1673 116.987 15.1673C117.844 15.1673 118.283 15.6156 118.283 16.388V16.419C118.283 16.9029 117.786 16.894 116.561 17.036C115.269 17.187 113.947 17.5243 113.947 19.0735C113.947 20.4141 114.954 21.1509 116.25 21.1509ZM116.539 20.0856C115.789 20.0856 115.247 19.7482 115.247 19.0913C115.247 18.381 115.878 18.128 116.646 18.0259C117.076 17.9682 118.097 17.8528 118.288 17.6619V18.5408C118.288 19.3487 117.644 20.0856 116.539 20.0856ZM122.335 16.9517C122.335 15.8642 123.001 15.2427 123.924 15.2427C124.825 15.2427 125.371 15.8331 125.371 16.823V21H126.699V16.6632C126.699 14.9764 125.771 14.093 124.377 14.093C123.352 14.093 122.681 14.568 122.366 15.2915H122.282V14.1818H121.008V21H122.335V16.9517ZM130.656 21.1332C131.899 21.1332 132.387 20.3741 132.627 19.9391H132.738V21H134.034V11.9091H132.707V15.2871H132.627C132.387 14.8654 131.934 14.093 130.665 14.093C129.018 14.093 127.806 15.3936 127.806 17.6042C127.806 19.8104 129 21.1332 130.656 21.1332ZM130.949 20.0012C129.764 20.0012 129.147 18.9581 129.147 17.5909C129.147 16.237 129.75 15.2205 130.949 15.2205C132.108 15.2205 132.729 16.166 132.729 17.5909C132.729 19.0247 132.094 20.0012 130.949 20.0012ZM135.66 21H136.956V19.9391H137.067C137.307 20.3741 137.795 21.1332 139.038 21.1332C140.689 21.1332 141.888 19.8104 141.888 17.6042C141.888 15.3936 140.671 14.093 139.025 14.093C137.759 14.093 137.302 14.8654 137.067 15.2871H136.987V11.9091H135.66V21ZM136.96 17.5909C136.96 16.166 137.582 15.2205 138.741 15.2205C139.943 15.2205 140.547 16.237 140.547 17.5909C140.547 18.9581 139.926 20.0012 138.741 20.0012C137.6 20.0012 136.96 19.0247 136.96 17.5909ZM145.875 21.1376C147.797 21.1376 149.053 19.7305 149.053 17.622C149.053 15.5002 147.797 14.093 145.875 14.093C143.953 14.093 142.697 15.5002 142.697 17.622C142.697 19.7305 143.953 21.1376 145.875 21.1376ZM145.879 20.0234C144.623 20.0234 144.037 18.927 144.037 17.6175C144.037 16.3125 144.623 15.2028 145.879 15.2028C147.127 15.2028 147.713 16.3125 147.713 17.6175C147.713 18.927 147.127 20.0234 145.879 20.0234ZM150.927 14.1818H149.471L151.566 17.5909L149.444 21H150.9L152.431 18.4521L153.967 21H155.419L153.275 17.5909L155.401 14.1818H153.95L152.431 16.8363L150.927 14.1818Z"
fill="#191919"
/>
</svg>
</a>
</p>
</template>

<style>
.code-sandbox-iframe {
width: 100%;
height: 500px;
overflow: hidden;
border: 0;
border-radius: 4px;
}
</style>
4 changes: 4 additions & 0 deletions plugins/plugin-md-power/src/client/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import PDFViewer from './components/PDFViewer.vue'
import Bilibili from './components/Bilibili.vue'
import Youtube from './components/Youtube.vue'
import Replit from './components/Replit.vue'
import CodeSandbox from './components/CodeSandbox.vue'

import '@internal/md-power/icons.css'

Expand All @@ -25,6 +26,9 @@ export default defineClientConfig({
if (pluginOptions.replit)
app.component('ReplitViewer', Replit)

if (pluginOptions.codeSandbox)
app.component('CodeSandboxViewer', CodeSandbox)

if (__VUEPRESS_SSR__)
return

Expand Down
97 changes: 97 additions & 0 deletions plugins/plugin-md-power/src/node/features/codeSandbox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* @[codesandbox](id)
* @[codesandbox share](user/id)
* @[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.js'
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|share))?(?:\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 `<CodeSandboxViewer title="${title}" height="${height}" width="${width}" user="${user}" id="${id}" type="${type}" filepath="${filepath}" :console=${console} :navbar=${navbar} layout="${layout}" />`
}

export const codeSandboxPlugin: PluginWithOptions<never> = (md) => {
md.block.ruler.before(
'import_code',
'code_sandbox',
createCodeSandboxRuleBlock(),
)

md.renderer.rules.code_sandbox = (tokens, index) => {
const token = tokens[index]

const content = resolveCodeSandbox(token.meta)
token.content = content

return content
}
}
6 changes: 6 additions & 0 deletions plugins/plugin-md-power/src/node/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { bilibiliPlugin } from './features/video/bilibili.js'
import { youtubePlugin } from './features/video/youtube.js'
import { codepenPlugin } from './features/codepen.js'
import { replitPlugin } from './features/replit.js'
import { codeSandboxPlugin } from './features/codeSandbox.js'

const __dirname = getDirname(import.meta.url)

Expand Down Expand Up @@ -64,6 +65,11 @@ export function markdownPowerPlugin(options: MarkdownPowerPluginOptions = {}): P
// @[replit](user/repl-name)
md.use(replitPlugin)
}

if (options.codeSandbox) {
// @[codesandbox](id)
md.use(codeSandboxPlugin)
}
},
}
}
Expand Down
12 changes: 12 additions & 0 deletions plugins/plugin-md-power/src/shared/codeSandbox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { SizeOptions } from './size.js'

export interface CodeSandboxTokenMeta extends SizeOptions {
user?: string
id?: string
layout?: string
type?: 'share' | 'embed'
title?: string
filepath?: string
navbar?: boolean
console?: boolean
}
2 changes: 2 additions & 0 deletions plugins/plugin-md-power/src/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ export * from './pdf.js'
export * from './icons.js'
export * from './video.js'
export * from './codepen.js'
export * from './codeSandbox.js'
export * from './replit.js'
export * from './plugin.js'
1 change: 1 addition & 0 deletions plugins/plugin-md-power/src/shared/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface MarkdownPowerPluginOptions {
// code embed
codepen?: boolean
replit?: boolean
codeSandbox?: boolean

caniuse?: boolean | CanIUseOptions
}

0 comments on commit 3a6ebcf

Please sign in to comment.