注意:在保存之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。
?_=1
来访问最新页面。https://zh.moegirl.org.cn/User:%E6%9C%BA%E6%99%BA%E7%9A%84%E5%B0%8F%E9%B1%BC%E5%90%9B/gadget/Shiki.js?_=1
/**
* MediaWiki Gadget Shiki.js Code Highlighter
* @author Dragon-Fish <dragon-fish@qq.com>
* @license MIT
*/
'use strict'
;(async () => {
const targets = document.querySelectorAll([
'pre.highlight',
'pre.hljs',
'pre.prettyprint',
'pre.mw-code',
'pre[lang]',
'code[lang]',
'pre[data-lang]',
'code[data-lang]',
])
if (!targets.length) {
return console.info('[SHIKI]', 'No targets found')
}
console.info('[SHIKI]', 'Found targets', targets.length, targets)
main(await import('https://esm.sh/shiki@1.24.0'), targets)
async function main(shiki, targets) {
await injectStyles()
const hlBlocks = await Promise.all(
Array.from(targets).map((el) => renderBlock(shiki, el))
)
}
async function injectStyles() {
const style =
document.querySelector('style#shiki-styles') ||
(() => {
const style = document.createElement('style')
style.id = 'shiki-styles'
document.head.append(style)
return style
})()
style.textContent = `
/* base styles */
pre.shiki {
position: relative;
font-family: 'JetBrainsMono Nerd Font', 'JetBrains Mono NF', 'JetBrains Mono', 'Fira Code', 'Consolas', 'Monaco', 'Andale Mono', 'Ubuntu Mono', monospace;
font-size: 1rem;
padding: 1em;
border-radius: 0.5em;
white-space: pre;
overflow-x: auto;
}
/* lang badge */
pre.shiki > .shiki-lang-badge {
position: absolute;
right: 0.5em;
top: 0.5em;
font-size: 10px;
border-radius: 99vw;
background: #000;
padding: 0.2em 0.5em;
}
/* line number */
pre.shiki.line-number .shiki-code {
counter-reset: step;
counter-increment: step calc(var(--start, 1) - 1);
}
pre.shiki.line-number .shiki-code .line::before {
content: counter(step);
counter-increment: step;
width: 2em;
margin-right: 1em;
display: inline-block;
text-align: right;
color: rgba(115,138,148,0.4);
}
`
return true
}
function getLangFromContentModel() {
const nsNumber = mw.config.get('wgNamespaceNumber')
const pageName = mw.config.get('wgPageName')
const contentModel = mw.config.get('wgPageContentModel', '').toLowerCase()
if (pageName.endsWith('.js') || contentModel === 'javascript') {
return 'javascript'
} else if (pageName.endsWith('.css') || contentModel === 'css') {
return 'css'
} else if (
// Lua
(nsNumber === 828 || ['scribunto', 'lua'].includes(contentModel)) &&
!pageName.endsWith('/doc')
) {
return 'lua'
}
}
/**
*
* @param {HTMLElement} el
*/
function getLangFromElement(el) {
const lang =
el.getAttribute('lang') ||
el.dataset.lang ||
Array.from(el.classList).find(
(c) => c.startsWith('language-') || c.startsWith('lang-')
)
if (lang) {
return lang.includes('-') ? lang.split('-')[1] : lang
}
return ''
}
/**
* @param {any} shiki
* @param {HTMLElement} el
* @returns {Promise<HTMLElement | null>}
*/
function renderBlock(shiki, el) {
if (el.classList.contains('shiki') || !!el.dataset.shikiRendered) {
return Promise.resolve(null)
}
const lang = getLangFromElement(el) || getLangFromContentModel()
if (!lang) {
return Promise.resolve(null)
}
const langInfo = shiki.bundledLanguagesInfo.find(
(i) => i.aliases?.includes(lang) || i.id === lang || i.name === lang
)
const langLabel = (() => {
if (!langInfo) return lang
return [langInfo.aliases?.[0], langInfo.name, langInfo.id, lang]
.filter(Boolean)
.sort((a, b) => a.length - b.length)[0]
})()
console.info('[SHIKI]', 'Rendering', el, lang, langInfo)
const lineFrom = el.dataset.lineFrom || el.dataset.from || '1'
return shiki
.codeToHtml(el.innerText.trimEnd(), {
lang,
theme: 'one-dark-pro',
transformers: [
{
pre(node) {
node.properties.class += ` lang-${langLabel}`
node.properties.style += ';'
node.properties.style += `padding-right: ${(
10 * langLabel.length +
12
).toFixed()}px;`
if (lineFrom) {
node.properties.class += ' line-number'
}
},
code(node) {
node.properties.class += ` shiki-code`
node.tagName = 'div'
node.properties.style += `;--start: ${+lineFrom};`
},
line(node, line) {
node.properties['data-line-number-raw'] = line
},
postprocess(html) {
if (langLabel) {
return html.replace(
/<\/pre>/,
`<span class="shiki-lang-badge">${langLabel}</span></pre>`
)
}
},
},
],
})
.then((html) => {
el.style.display = 'none'
el.dataset.shikiRendered = '1'
const wrapper = document.createElement('div')
wrapper.innerHTML = html
const pre = wrapper.querySelector('pre')
el.insertAdjacentElement('afterend', pre)
return pre
})
.catch((e) => {
console.error('[SHIKI] Render failed', el, e)
return null
})
}
})()