Files
hold-baby bb3123302c Release v2 (#328)
新增针对现有的 Http 接口提供了一个在线调试面板,可在线对当前 API
的参数进行调试,便于用户更直观的查看返回结果并提供复制和下载按钮。调试默认使用模拟账户的凭证,真实账户需要手动填写

---------

Co-authored-by: 石石 <lei.yang@longbridge-inc.com>
Co-authored-by: tzyoo <tzyitooo@gmail.com>
Co-authored-by: 袁昌瑞 <changrui.yuan@longbridge-inc.com>
2025-07-18 16:36:28 +08:00

164 lines
4.8 KiB
TypeScript

import type MarkdownItAsync from 'markdown-it'
import container from 'markdown-it-container'
interface ContainerOptions {
marker?: string
validate?: (params: string) => boolean
render?: (tokens: any[], idx: number, options: any, env: any, renderer: any) => string
}
export function tipContainerPlugin(md: MarkdownItAsync) {
const containers = ['tip', 'warning', 'danger', 'info', 'caution', 'success']
containers.forEach((name) => {
md.use(container, name, {
render: (tokens: any[], idx: number) => {
const token = tokens[idx]
const info = token.info.trim().slice(name.length).trim()
if (token.nesting === 1) {
const title = info || name.charAt(0).toUpperCase() + name.slice(1)
const escapedTitle = title
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
return `<TipContainer type="${name}" title="${escapedTitle}">\n`
} else {
return '</TipContainer>\n'
}
},
})
})
}
export function createContainer(md: MarkdownItAsync, name: string, options: ContainerOptions) {
const min_markers = 3
const marker_str = options.marker || ':'
const marker_char = marker_str.charCodeAt(0)
const marker_len = marker_str.length
const validate = options.validate || defaultValidate
const render = options.render || defaultRender
function defaultValidate(params: string) {
return params.trim().split(' ', 2)[0] === name
}
function defaultRender(tokens: any[], idx: number, _options: any, env: any, renderer: any) {
// Add your default render logic here if needed
if (tokens[idx].nesting === 1) {
tokens[idx].attrJoin('class', name)
}
return renderer.renderToken(tokens, idx, _options)
}
function ruleBlock(state: any, start: number, end: number, silent: boolean) {
let pos, nextLine, marker_count, markup, params, token
let old_parent, old_line_max
let auto_closed = false
let startLine = start
// Check out the first character quickly, which should filter out most of non-containers
if (marker_char !== state.src.charCodeAt(start)) {
return false
}
// Check out the rest of the marker string
for (pos = start + 1; pos <= end; pos++) {
if (marker_str[(pos - start) % marker_len] !== state.src[pos]) {
break
}
}
marker_count = Math.floor((pos - start) / marker_len)
if (marker_count < min_markers) {
return false
}
pos -= (pos - start) % marker_len
markup = state.src.slice(start, pos)
params = state.src.slice(pos, state.eMarks[startLine])
if (!validate(params)) {
return false
}
// Since start is found, we can report success here in validation mode
if (silent) {
return true
}
// Search for end of block
nextLine = startLine
for (;;) {
nextLine++
if (nextLine >= end) {
// Reached end of input without finding a closing marker, treat as an unclosed block
break
}
pos = state.bMarks[nextLine] + state.tShift[nextLine]
const max = state.eMarks[nextLine]
if (pos < max && state.sCount[nextLine] < state.blkIndent) {
// Non-empty line with negative indent should stop the block
break
}
if (marker_char !== state.src.charCodeAt(pos)) {
continue
}
if (state.sCount[nextLine] - state.blkIndent >= 4) {
// Closing fence should be indented less than 4 spaces
continue
}
pos = state.skipSpaces(pos)
// Check out the rest of the marker string
for (pos = state.bMarks[nextLine] + state.tShift[nextLine]; pos <= max; pos++) {
if (marker_str[(pos - (state.bMarks[nextLine] + state.tShift[nextLine])) % marker_len] !== state.src[pos]) {
break
}
}
// Found!
auto_closed = true
break
}
old_parent = state.parentType
old_line_max = state.lineMax
state.parentType = 'container'
state.lineMax = nextLine
token = state.push('container_' + name + '_open', 'div', 1)
token.markup = markup
token.block = true
token.info = params
token.map = [startLine, nextLine]
state.md.block.tokenize(state, startLine + 1, nextLine)
token = state.push('container_' + name + '_close', 'div', -1)
token.markup = state.src.slice(start, pos)
token.block = true
state.parentType = old_parent
state.lineMax = old_line_max
state.line = nextLine + (auto_closed ? 1 : 0)
return true
}
md?.block?.ruler.before('fence', 'container_' + name, ruleBlock, {
alt: ['paragraph', 'reference', 'blockquote', 'list'],
})
if (md?.renderer) {
md.renderer.rules['container_' + name + '_open'] = render
md.renderer.rules['container_' + name + '_close'] = render
}
}