@nubisco/docs-sdk 0.0.0-stage → 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +101 -2
- package/THIRD_PARTY_NOTICES.md +100 -0
- package/dist/cli.js +33 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.js +361 -0
- package/package.json +122 -4
- package/src/plugins/mermaid/Mermaid.vue +93 -0
- package/src/plugins/mermaid/index.ts +66 -0
- package/src/plugins/mermaid/mermaid-markdown.ts +34 -0
- package/src/plugins/mermaid/mermaid-plugin.ts +46 -0
- package/src/plugins/mermaid/mermaid.ts +21 -0
- package/src/plugins/tabs/components/ContentTab.vue +41 -0
- package/src/plugins/tabs/components/ContentTabs.vue +150 -0
- package/src/plugins/tabs/components/index.ts +10 -0
- package/src/plugins/tabs/components/useStabilizeScrollPosition.ts +36 -0
- package/src/plugins/tabs/components/useTabLabels.ts +18 -0
- package/src/plugins/tabs/components/useTabsSelectedState.ts +95 -0
- package/src/plugins/tabs/components/useTabsSingleState.ts +22 -0
- package/src/plugins/tabs/index.ts +1 -0
- package/src/plugins/tabs/markdown/index.ts +1 -0
- package/src/plugins/tabs/markdown/markdownPlugin.ts +47 -0
- package/src/plugins/tabs/markdown/ruleBlockTab.ts +97 -0
- package/src/theme/Layout.vue +32 -0
- package/src/theme/Nubisco.vue +480 -0
- package/src/theme/components/DocHeader.vue +599 -0
- package/src/theme/components/DocOutline.vue +235 -0
- package/src/theme/components/DocsControls.vue +173 -0
- package/src/theme/components/DocsSidebarNav.vue +313 -0
- package/src/theme/custom.css +591 -0
- package/src/theme/index.ts +33 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { ref, computed, reactive, inject, watch, onMounted } from 'vue'
|
|
2
|
+
import type { App, Ref, InjectionKey } from 'vue'
|
|
3
|
+
|
|
4
|
+
type TabsSharedState = {
|
|
5
|
+
content?: TabsSharedStateContent
|
|
6
|
+
}
|
|
7
|
+
type TabsSharedStateContent = Record<string, string>
|
|
8
|
+
|
|
9
|
+
const injectionKey: InjectionKey<TabsSharedState> = Symbol(
|
|
10
|
+
'vitepress:tabSharedState',
|
|
11
|
+
)
|
|
12
|
+
const ls = typeof localStorage !== 'undefined' ? localStorage : null
|
|
13
|
+
const localStorageKey = 'vitepress:tabsSharedState'
|
|
14
|
+
|
|
15
|
+
const getLocalStorageValue = (): TabsSharedStateContent => {
|
|
16
|
+
const rawValue = ls?.getItem(localStorageKey)
|
|
17
|
+
if (rawValue) {
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(rawValue)
|
|
20
|
+
} catch {}
|
|
21
|
+
}
|
|
22
|
+
return {}
|
|
23
|
+
}
|
|
24
|
+
const setLocalStorageValue = (v: TabsSharedStateContent) => {
|
|
25
|
+
if (!ls) return
|
|
26
|
+
ls.setItem(localStorageKey, JSON.stringify(v))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const provideTabsSharedState = (app: App) => {
|
|
30
|
+
const state = reactive<TabsSharedState>({})
|
|
31
|
+
watch(
|
|
32
|
+
() => state.content,
|
|
33
|
+
(newStateContent, oldStateContent) => {
|
|
34
|
+
// skip initialize
|
|
35
|
+
if (newStateContent && oldStateContent) {
|
|
36
|
+
setLocalStorageValue(newStateContent)
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{ deep: true },
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
app.provide(injectionKey, state)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const useTabsSelectedState = <T extends string>(
|
|
46
|
+
acceptValues: Ref<T[]>,
|
|
47
|
+
sharedStateKey: Ref<string | undefined>,
|
|
48
|
+
) => {
|
|
49
|
+
const sharedState = inject(injectionKey)
|
|
50
|
+
if (!sharedState) {
|
|
51
|
+
throw new Error('[vitepress-doc-tabs] TabsSharedState should be injected')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
onMounted(() => {
|
|
55
|
+
if (!sharedState.content) {
|
|
56
|
+
sharedState.content = getLocalStorageValue()
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const nonSharedState = ref<T | undefined>()
|
|
61
|
+
|
|
62
|
+
const selected = computed({
|
|
63
|
+
get() {
|
|
64
|
+
const key = sharedStateKey.value
|
|
65
|
+
const acceptVals = acceptValues.value
|
|
66
|
+
if (key) {
|
|
67
|
+
const value = sharedState.content?.[key]
|
|
68
|
+
if (value && (acceptVals as string[]).includes(value)) {
|
|
69
|
+
return value as T
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
const nonSharedStateVal = nonSharedState.value
|
|
73
|
+
if (nonSharedStateVal) {
|
|
74
|
+
return nonSharedStateVal
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return acceptVals[0]
|
|
78
|
+
},
|
|
79
|
+
set(v) {
|
|
80
|
+
const key = sharedStateKey.value
|
|
81
|
+
if (key) {
|
|
82
|
+
if (sharedState.content) {
|
|
83
|
+
sharedState.content[key] = v
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
nonSharedState.value = v
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
})
|
|
90
|
+
const select = (newValue: T) => {
|
|
91
|
+
selected.value = newValue
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return { selected, select }
|
|
95
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { provide, type InjectionKey, type Ref, inject } from 'vue'
|
|
2
|
+
|
|
3
|
+
type TabsSingleState = {
|
|
4
|
+
uid: string
|
|
5
|
+
selected: Ref<string>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const injectionKey: InjectionKey<TabsSingleState> = Symbol(
|
|
9
|
+
'vitepress:tabSingleState',
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
export const provideTabsSingleState = (state: TabsSingleState) => {
|
|
13
|
+
provide(injectionKey, state)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const useTabsSingleState = () => {
|
|
17
|
+
const singleState = inject(injectionKey)
|
|
18
|
+
if (!singleState) {
|
|
19
|
+
throw new Error('[vitepress-doc-tabs] TabsSingleState should be injected')
|
|
20
|
+
}
|
|
21
|
+
return singleState
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './components'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { tabsPlugin as tabsMarkdownPlugin } from './markdownPlugin'
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// @ts-nocheck: adapted third-party code (see THIRD_PARTY_NOTICES.md), typed loosely upstream.
|
|
2
|
+
import type MarkdownIt from 'markdown-it'
|
|
3
|
+
import type Token from 'markdown-it/lib/token'
|
|
4
|
+
import container from 'markdown-it-container'
|
|
5
|
+
import { ruleBlockTab } from './ruleBlockTab'
|
|
6
|
+
|
|
7
|
+
type Params = {
|
|
8
|
+
shareStateKey: string | undefined
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const parseTabsParams = (input: string): Params => {
|
|
12
|
+
const match = input.match(/key:(\S+)/)
|
|
13
|
+
return {
|
|
14
|
+
shareStateKey: match?.[1],
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const tabsPlugin = (md: MarkdownIt) => {
|
|
19
|
+
md.use(container, 'tabs', {
|
|
20
|
+
render(tokens: Token[], index: number) {
|
|
21
|
+
const token = tokens[index]
|
|
22
|
+
if (token.nesting === 1) {
|
|
23
|
+
const params = parseTabsParams(token.info)
|
|
24
|
+
const shareStateKeyProp = params.shareStateKey
|
|
25
|
+
? `sharedStateKey="${md.utils.escapeHtml(params.shareStateKey)}"`
|
|
26
|
+
: ''
|
|
27
|
+
return `<ContentTabs ${shareStateKeyProp}>\n`
|
|
28
|
+
} else {
|
|
29
|
+
return `</ContentTabs>\n`
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
md.block.ruler.after('container_tabs', 'tab', ruleBlockTab)
|
|
35
|
+
const renderTab = (tokens: Token[], index: number): string => {
|
|
36
|
+
const token = tokens[index]
|
|
37
|
+
if (token.nesting === 1) {
|
|
38
|
+
const label = token.info
|
|
39
|
+
const labelProp = `label="${md.utils.escapeHtml(label)}"`
|
|
40
|
+
return `<ContentTab ${labelProp}>\n`
|
|
41
|
+
} else {
|
|
42
|
+
return `</ContentTab>\n`
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
md.renderer.rules['tab_open'] = renderTab
|
|
46
|
+
md.renderer.rules['tab_close'] = renderTab
|
|
47
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// @ts-nocheck: adapted third-party code (see THIRD_PARTY_NOTICES.md), typed loosely upstream.
|
|
2
|
+
import type { RuleBlock } from 'markdown-it/lib/parser_block'
|
|
3
|
+
|
|
4
|
+
const tabMarker = '='
|
|
5
|
+
const tabMarkerCode = tabMarker.charCodeAt(0)
|
|
6
|
+
const minTabMarkerLen = 2
|
|
7
|
+
|
|
8
|
+
export const ruleBlockTab: RuleBlock = (state, startLine, endLine, silent) => {
|
|
9
|
+
let pos = state.bMarks[startLine] + state.tShift[startLine]
|
|
10
|
+
const max = state.eMarks[startLine]
|
|
11
|
+
|
|
12
|
+
// @ts-expect-error markdown-it-container uses 'container'
|
|
13
|
+
if (state.parentType !== 'container') {
|
|
14
|
+
return false
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (pos + minTabMarkerLen > max) {
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const marker = state.src.charCodeAt(pos)
|
|
22
|
+
if (marker !== tabMarkerCode) {
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// scan marker length
|
|
27
|
+
const mem = pos
|
|
28
|
+
pos = state.skipChars(pos + 1, marker)
|
|
29
|
+
const tabMarkerLen = pos - mem
|
|
30
|
+
|
|
31
|
+
if (tabMarkerLen < minTabMarkerLen - 1) {
|
|
32
|
+
return false
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// for validation mode
|
|
36
|
+
if (silent) {
|
|
37
|
+
return true
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// search for the end of the block
|
|
41
|
+
let nextLine = startLine
|
|
42
|
+
let endStart = mem
|
|
43
|
+
let endPos = pos
|
|
44
|
+
|
|
45
|
+
for (;;) {
|
|
46
|
+
nextLine++
|
|
47
|
+
if (nextLine >= endLine) {
|
|
48
|
+
break // unclosed block is autoclosed
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
endStart = state.bMarks[nextLine] + state.tShift[nextLine]
|
|
52
|
+
const max = state.eMarks[nextLine]
|
|
53
|
+
|
|
54
|
+
if (endStart < max && state.sCount[nextLine] < state.blkIndent) {
|
|
55
|
+
// non-empty line with negative indent should stop the list:
|
|
56
|
+
// - ```
|
|
57
|
+
// test
|
|
58
|
+
break
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const startCharCode = state.src.charCodeAt(endStart)
|
|
62
|
+
if (startCharCode !== tabMarkerCode) {
|
|
63
|
+
continue
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const p = state.skipChars(endStart + 1, marker)
|
|
67
|
+
if (p - endStart !== tabMarkerLen) {
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
endPos = p
|
|
71
|
+
break
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const oldParent = state.parentType
|
|
75
|
+
const oldLineMax = state.lineMax
|
|
76
|
+
// @ts-expect-error use 'tab' for this rule
|
|
77
|
+
state.parentType = 'tab'
|
|
78
|
+
// this will prevent lazy continuations from ever going past our end marker
|
|
79
|
+
state.lineMax = nextLine
|
|
80
|
+
|
|
81
|
+
const startToken = state.push('tab_open', 'div', 1)
|
|
82
|
+
startToken.markup = state.src.slice(mem, pos)
|
|
83
|
+
startToken.block = true
|
|
84
|
+
startToken.info = state.src.slice(pos, max).trimStart()
|
|
85
|
+
startToken.map = [startLine, nextLine - 1]
|
|
86
|
+
|
|
87
|
+
state.md.block.tokenize(state, startLine + 1, nextLine)
|
|
88
|
+
|
|
89
|
+
const endToken = state.push('tab_close', 'div', -1)
|
|
90
|
+
endToken.markup = state.src.slice(endStart, endPos)
|
|
91
|
+
endToken.block = true
|
|
92
|
+
|
|
93
|
+
state.parentType = oldParent
|
|
94
|
+
state.lineMax = oldLineMax
|
|
95
|
+
state.line = nextLine
|
|
96
|
+
return true
|
|
97
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<DefaultLayout>
|
|
3
|
+
<!--
|
|
4
|
+
The site-wide colour-mode and appearance switches.
|
|
5
|
+
|
|
6
|
+
`nav-bar-content-after` is used rather than a bespoke bar so the controls
|
|
7
|
+
sit in the header VitePress already renders, on every page, including
|
|
8
|
+
pages that declare their own `layout:` in frontmatter. A custom layout
|
|
9
|
+
replaces the page content, not the chrome around it.
|
|
10
|
+
-->
|
|
11
|
+
<template #nav-bar-content-after>
|
|
12
|
+
<DocsControls />
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<!--
|
|
16
|
+
The left navigation. VitePress's own group list is hidden in
|
|
17
|
+
custom.css and this one renders from the same sidebar config, with a
|
|
18
|
+
filter box and collapsible groups.
|
|
19
|
+
-->
|
|
20
|
+
<template #sidebar-nav-before>
|
|
21
|
+
<DocsSidebarNav />
|
|
22
|
+
</template>
|
|
23
|
+
</DefaultLayout>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script setup lang="ts">
|
|
27
|
+
import DefaultTheme from 'vitepress/theme'
|
|
28
|
+
import DocsControls from './components/DocsControls.vue'
|
|
29
|
+
import DocsSidebarNav from './components/DocsSidebarNav.vue'
|
|
30
|
+
|
|
31
|
+
const DefaultLayout = DefaultTheme.Layout
|
|
32
|
+
</script>
|