Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

重写文档内容 #255

Draft
wants to merge 25 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
235e61b
chore: move image files for further rewrite
boxsnake Jul 10, 2024
e6675a3
chore: 优化【批量选择】文本颜色显示
boxsnake Jul 10, 2024
5d0e631
fix: 优化【批量选择】图片格式
boxsnake Jul 10, 2024
d2080f1
fix: 修复右侧侧边导航显示2-4层级标题
boxsnake Jul 10, 2024
e781b2e
refactor: 优化【批量选择】文档
boxsnake Jul 10, 2024
30de672
refactor: 优化【彩蛋】文档
boxsnake Jul 10, 2024
54b88ee
chore: resolve conflicts from main branch
boxsnake Jul 10, 2024
adc2b08
chore: remove moved images
boxsnake Jul 10, 2024
db5d976
fix: fix TS types
boxsnake Jul 14, 2024
886d660
chore: update TS type import
boxsnake Jul 14, 2024
7ebc08d
fix: fix props stringify
boxsnake Jul 14, 2024
8beedd7
chore: remove unused vue macro imports
boxsnake Jul 14, 2024
2405992
fix: update TS type import
boxsnake Jul 14, 2024
1093169
chore: update md guide
boxsnake Jul 14, 2024
3ed2ed2
fix: ignore prettier dealing with `.md` files
boxsnake Jul 14, 2024
2a5eaaa
chore: update md guide
boxsnake Jul 14, 2024
7fa82ab
feat: add QA markdown support
boxsnake Jul 15, 2024
7c20bf5
chore: 优化Markdown增强指引
boxsnake Jul 15, 2024
c3ade54
chore: 优化Markdown增强指引
boxsnake Jul 16, 2024
9f26812
chore: 添加Markdown增强指引:颜色
boxsnake Jul 16, 2024
b085565
refactor: 优化card组件Markdown解析与渲染
boxsnake Jul 16, 2024
857974f
chore: 优化Markdown增强指引
boxsnake Jul 16, 2024
f75e7cc
chore: remove unused imports
boxsnake Jul 16, 2024
c978b8a
feat: 优化QA组件样式
boxsnake Jul 16, 2024
9e0f61f
refactor: 优化Q&A展开/折叠逻辑
boxsnake Jul 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ cache/
.vscode/
auto-imports.d.ts
pnpm-lock.yaml

**/*.md
7 changes: 7 additions & 0 deletions .vitepress/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Unocss from 'unocss/vite'
import MarkdownItFootnote from 'markdown-it-footnote'
import MarkdownItKbd from 'markdown-it-kbd-better'
import MarkdownItColorInline from 'markdown-it-color-inline'
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vitepress'
import type {
Expand All @@ -22,6 +23,7 @@ import { mark } from '@mdit/plugin-mark'
import { sub } from '@mdit/plugin-sub'
import { sup } from '@mdit/plugin-sup'
import { timeline } from './theme/markdown/timeline'
import { qa } from './theme/markdown/qa'

import { enConfig } from './locales/en'
import { zhConfig } from './locales/zh'
Expand Down Expand Up @@ -241,6 +243,9 @@ export default defineConfig({
},
},
},
outline: {
level: [2, 4],
},
},
rewrites: {
'zh/:splat(.*)': ':splat',
Expand Down Expand Up @@ -421,6 +426,8 @@ export default defineConfig({
md.use(obsidianImageSize)
md.use(figure)
md.use(timeline)
md.use(qa)
md.use(MarkdownItColorInline)
md.use(MarkdownItKbd, {
presets: [
{
Expand Down
2 changes: 1 addition & 1 deletion .vitepress/theme/components/Card.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
<script setup lang="ts">
import { withBase } from 'vitepress'
import { isLinkExternal, isRelativeLink } from '../utils'
import { computed, defineProps, withDefaults } from 'vue'
import { computed } from 'vue'

import '../styles/card.scss'

Expand Down
4 changes: 2 additions & 2 deletions .vitepress/theme/components/DocFeedbackForm.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { ref, onMounted, watch, inject } from 'vue'
import { useData, useRoute } from 'vitepress'
import { ref, inject } from 'vue'
import { useData } from 'vitepress'

import { newDocFeedback } from '../apis/newDocFeedback'

Expand Down
1 change: 1 addition & 0 deletions .vitepress/theme/components/Link.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { computed } from 'vue'
import { withBase } from 'vitepress'

const props = defineProps<{
href?: string
noIcon?: boolean
Expand Down
43 changes: 43 additions & 0 deletions .vitepress/theme/components/QA.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<template>
<div
class="qa"
:class="{
'qa-collapsible': collapsible,
'qa-opened': isOpened,
}"
>
<div class="qa-wrapper qa-summary" @click="handleToggle">
<div class="qa-mark qa-mark-question">Q</div>
<div class="qa-content">
<slot name="summary"></slot>
</div>
</div>
<div class="qa-wrapper qa-detail">
<div class="qa-mark qa-mark-answer">A</div>
<div class="qa-content">
<slot name="detail"></slot>
</div>
</div>
</div>
</template>

<script setup lang="ts">
import { ref } from 'vue'

import '../styles/qa.scss'

interface QAProps {
collapsible?: boolean
}

const props = withDefaults(defineProps<QAProps>(), {
collapsible: true,
})

const isOpened = ref<boolean>(!props.collapsible)

const handleToggle = () => {
if (!props.collapsible) return
isOpened.value = !isOpened.value
}
</script>
2 changes: 2 additions & 0 deletions .vitepress/theme/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Link from './components/Link.vue'
import Coins from './components/Coins.vue'
import googleAnalytics from '../plugins/google-analytics'
import Card from '../theme/components/Card.vue'
import QA from '../theme/components/QA.vue'
import LinkGrid from '../theme/components/LinkGrid.vue'
import { createPinia } from 'pinia'
import DocAside from './components/DocAside.vue'
Expand Down Expand Up @@ -50,6 +51,7 @@ export default {
app.component('Card', Card)
app.component('LinkGrid', LinkGrid)
app.component('Badge', VPBadge)
app.component('QA', QA)
},
setup() {
const route = useRoute()
Expand Down
65 changes: 36 additions & 29 deletions .vitepress/theme/markdown/card.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { container } from '@mdit/plugin-container'
import { type MarkdownEnv } from 'vitepress'
import { load } from 'js-yaml'
import { type Options, type PluginSimple } from 'markdown-it'
import type Token from 'markdown-it/lib/token.js'

import MarkdownIt from 'markdown-it'
import type { Options, PluginSimple } from 'markdown-it'
import type Token from 'markdown-it/lib/token.mjs'
import type { RenderRule } from 'markdown-it/lib/renderer.mjs'
import MarkdownItFence from 'markdown-it-fence'
import {
stringifyProp,
entries,
Expand Down Expand Up @@ -93,30 +94,36 @@ ${content}
return ''
}

export const cardPlugin: PluginSimple = (md) => {
// add card container
md.use(container, {
name: 'card',
openRender: () =>
`\
<div class="card-container">
`,
})

// Handle ```card blocks
const fence = md.renderer.rules.fence

md.renderer.rules.fence = (...args): string => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const [tokens, index, options, env] = args
const { info } = tokens[index]
const realInfo = info.split(':', 2)[0]

if (realInfo === 'card')
return cardRender(tokens, index, options, <MarkdownEnv>env)

return fence!(...args)
}
export const cardPlugin: PluginSimple = (md: MarkdownIt) => {
md.use(...createFence('card', md))
}

md.renderer.rules['card'] = cardRender
type FenceArgs = [
typeof MarkdownItFence,
string,
{
marker?: string
validate?: (params: string) => boolean
render: RenderRule
},
]
const createFence = (klass: string, _md: MarkdownIt): FenceArgs => {
return [
MarkdownItFence,
klass,
{
marker: '`',
validate(params) {
return params.split(':', 2)[0] === 'card'
},
render(
tokens: Token[],
idx: number,
options: MarkdownIt.Options,
env: any,
) {
return cardRender(tokens, idx, options, <MarkdownEnv>env)
},
},
]
}
166 changes: 166 additions & 0 deletions .vitepress/theme/markdown/qa.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import type { MarkdownEnv } from 'vitepress'
import { load } from 'js-yaml'
import MarkdownIt from 'markdown-it'
import type Token from 'markdown-it/lib/token.mjs'
import type { RenderRule } from 'markdown-it/lib/renderer.mjs'
import MarkdownItFence from 'markdown-it-fence'
import {
isPlainObject,
isString,
isBoolean,
fromEntries,
entries,
stringifyProp,
} from '../utils'

enum QAPartEnum {
CONFIG,
SUMMARY,
DETAIL,
}

interface QAParts {
summary?: string
detail?: string
config: QAOptions | null
}

export interface QAOptions {
collapsible?: boolean
}

const SEPARATOR_TOKEN_TEST = '----'
const CONFIG_LABEL_TEST = '@config'
const OPEN_TAG_TEST = /^:{3,}\s*qa$/giu
const CLOSE_TAG_TEST = /^:{3,}$/giu
const QA_PROPS = ['collapsible']

const parseChunks = (chunks: string[]): QAParts => {
const parts: QAParts = {
config: null,
}

// Split lines
let curIter: number = QAPartEnum.CONFIG
let tagLevels: number[] = []
let lines: [string[], string[], string[]] = [[], [], []]
for (let chunk of chunks) {
if (SEPARATOR_TOKEN_TEST === chunk.trim()) {
if (tagLevels.length <= 0) {
curIter++
continue
}
}
if (OPEN_TAG_TEST.test(chunk)) {
const levelCount: number = chunk.length - chunk.replace(/^:+/g, '').length
tagLevels.push(levelCount)
} else if (CLOSE_TAG_TEST.test(chunk)) {
const levelCount: number = chunk.length
const lastLevelFound = tagLevels.lastIndexOf(levelCount)
if (lastLevelFound !== -1) {
tagLevels = tagLevels.filter((_v, i) => i < lastLevelFound)
}
}
if (lines[curIter]) lines[curIter].push(chunk)
}

// Re-arrange config, summary and detail
const hasConfig: boolean =
lines[QAPartEnum.CONFIG].findIndex(
(text) => CONFIG_LABEL_TEST === text.trim(),
) !== -1
if (hasConfig) {
const [configLines, summaryLines, detailLines] = lines
const cleanConfigLines = configLines.filter(
(line) => CONFIG_LABEL_TEST !== line.trim(),
)
lines = [cleanConfigLines, summaryLines, detailLines]
} else {
const [summaryLines, detailLines] = lines
lines = [[], summaryLines, detailLines]
}

// Parse config
try {
if (lines[QAPartEnum.CONFIG].length > 0) {
const config: unknown = load(lines[QAPartEnum.CONFIG].join('\n').trim())
const parsedConfig = checkQAProps(config)
parts.config = parsedConfig
}
} catch (err) {
console.error(`Parsing QA config failed:`, err)
}

// Set summary & detail
parts.summary = lines[QAPartEnum.SUMMARY].join('\n')
parts.detail = lines[QAPartEnum.DETAIL].join('\n')

console.log(parts)
return parts
}

const checkQAProps = (config: unknown): QAOptions | null => {
if (!isPlainObject(config)) return null

return fromEntries(
entries(config).filter(
(item): item is [string, string | boolean] =>
QA_PROPS.includes(item[0]) && (isString(item[1]) || isBoolean(item[1])),
),
) as QAOptions
}

const qaRender = (
tokens: Token[],
index: number,
_opts: MarkdownIt.Options,
_env: MarkdownEnv,
md?: MarkdownIt,
): string => {
const token = tokens[index]
const { content } = token

const contentChunks: string[] = content.split(/\n/g)
const contentParts: QAParts = parseChunks(contentChunks)

// Render title & detail
const renderedTitle: string | undefined = md?.render(
contentParts.summary ?? '',
)
const renderedDetail: string | undefined = md?.render(
contentParts.detail ?? '',
)

return `\
<QA v-bind='${stringifyProp(contentParts.config)}'>
<template #summary>${renderedTitle}</template>
<template #detail>${renderedDetail}</template>
</QA>`
}

export const qa = (md: MarkdownIt, _options: any) => {
md.use(...createFence('qa', md))
}

type FenceArgs = [
typeof MarkdownItFence,
string,
{ marker?: string; render: RenderRule },
]
const createFence = (klass: string, md: MarkdownIt): FenceArgs => {
return [
MarkdownItFence,
klass,
{
marker: ':',
render(
tokens: Token[],
idx: number,
options: MarkdownIt.Options,
env: any,
) {
return qaRender(tokens, idx, options, <MarkdownEnv>env, md)
},
},
]
}
4 changes: 2 additions & 2 deletions .vitepress/theme/markdown/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import container from 'markdown-it-container'
import dayjs from 'dayjs'

import type MarkdownIt from 'markdown-it'
import type Token from 'markdown-it/lib/token'
import type { RenderRule } from 'markdown-it/lib/renderer'
import type Token from 'markdown-it/lib/token.mjs'
import type { RenderRule } from 'markdown-it/lib/renderer.mjs'

export const getdefaultTime = () => {
return dayjs().format('YYYY-MM-DD')
Expand Down
Loading
Loading