@docsector/docsector-reader 4.0.0 → 4.1.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/README.md +22 -4
- package/bin/docsector.js +1 -1
- package/package.json +1 -1
- package/src/components/DBlockCodeExample.vue +423 -0
- package/src/components/DBlockExpandable.vue +1 -0
- package/src/components/DBlockSourceCode.vue +1 -7
- package/src/components/DPageSection.vue +5 -0
- package/src/components/DPageTokens.vue +24 -1
- package/src/components/DSubpage.vue +14 -2
- package/src/components/code-block-highlighting.js +16 -0
- package/src/components/code-example-source.js +363 -0
- package/src/components/page-section-tokens.js +89 -1
- package/src/composables/useActiveAnchor.js +42 -0
- package/src/composables/useNavigator.js +24 -17
- package/src/examples/manual/code-examples/BasicCounter.vue +63 -0
- package/src/examples/manual/code-examples/InlineNotice.vue +60 -0
- package/src/home-page-mode.js +5 -0
- package/src/pages/manual/basic/d-page-anchor.overview.en-US.md +1 -1
- package/src/pages/manual/basic/d-page-anchor.overview.pt-BR.md +1 -1
- package/src/pages/manual/content/blocks/code-examples.overview.en-US.md +56 -0
- package/src/pages/manual/content/blocks/code-examples.overview.pt-BR.md +56 -0
- package/src/pages/manual/content/blocks/code-examples.showcase.en-US.md +38 -0
- package/src/pages/manual/content/blocks/code-examples.showcase.pt-BR.md +38 -0
- package/src/pages/manual.index.js +28 -0
- package/src/quasar.factory.js +90 -11
- package/src/store/Page.js +28 -2
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="inline-notice-example q-pa-md">
|
|
3
|
+
<q-banner rounded class="inline-notice-example__banner">
|
|
4
|
+
<template #avatar>
|
|
5
|
+
<q-icon name="tips_and_updates" color="primary"></q-icon>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<div class="text-weight-medium">{{ title }}</div>
|
|
9
|
+
<div class="text-body2">{{ message }}</div>
|
|
10
|
+
|
|
11
|
+
<template #action>
|
|
12
|
+
<q-btn flat color="primary" label="Dismiss" @click="dismiss"></q-btn>
|
|
13
|
+
</template>
|
|
14
|
+
</q-banner>
|
|
15
|
+
|
|
16
|
+
<div v-if="dismissed" class="inline-notice-example__status text-caption">
|
|
17
|
+
The notice was dismissed.
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script>
|
|
23
|
+
import { ref } from 'vue'
|
|
24
|
+
|
|
25
|
+
export default {
|
|
26
|
+
setup () {
|
|
27
|
+
const title = 'Documentation tip'
|
|
28
|
+
const message = 'Use expanded examples when the source code is part of the lesson.'
|
|
29
|
+
const dismissed = ref(false)
|
|
30
|
+
|
|
31
|
+
function dismiss () {
|
|
32
|
+
dismissed.value = true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
dismissed,
|
|
37
|
+
dismiss,
|
|
38
|
+
message,
|
|
39
|
+
title
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<style scoped>
|
|
46
|
+
.inline-notice-example {
|
|
47
|
+
display: grid;
|
|
48
|
+
gap: 10px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.inline-notice-example__banner {
|
|
52
|
+
background: rgba(25, 118, 210, 0.08);
|
|
53
|
+
border: 1px solid rgba(25, 118, 210, 0.18);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.inline-notice-example__status {
|
|
57
|
+
color: #607d68;
|
|
58
|
+
padding-left: 8px;
|
|
59
|
+
}
|
|
60
|
+
</style>
|
|
@@ -39,7 +39,7 @@ The root node (from DH1) shows the page title from i18n when no label is set:
|
|
|
39
39
|
|
|
40
40
|
## Scroll Synchronization
|
|
41
41
|
|
|
42
|
-
When the user scrolls the page content, the `DPage` scroll observer calls `useNavigator().scrolling()`, which
|
|
42
|
+
When the user scrolls the page content, the `DPage` scroll observer calls `useNavigator().scrolling()`, which selects the last registered heading that crossed the content threshold. Missing or stale anchors are ignored so the table of contents stays in sync with the visible section instead of jumping ahead.
|
|
43
43
|
|
|
44
44
|
## Lifecycle
|
|
45
45
|
|
|
@@ -39,7 +39,7 @@ O nó raiz (do DH1) mostra o título da página do i18n quando nenhum label é d
|
|
|
39
39
|
|
|
40
40
|
## Sincronização de Scroll
|
|
41
41
|
|
|
42
|
-
Quando o usuário faz scroll no conteúdo da página, o observador de scroll do `DPage` chama `useNavigator().scrolling()`, que
|
|
42
|
+
Quando o usuário faz scroll no conteúdo da página, o observador de scroll do `DPage` chama `useNavigator().scrolling()`, que seleciona o último heading registrado que cruzou o limite superior da área de conteúdo. Âncoras ausentes ou obsoletas são ignoradas para manter o índice sincronizado com a seção realmente visível, sem saltar adiante.
|
|
43
43
|
|
|
44
44
|
## Ciclo de Vida
|
|
45
45
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
## Overview
|
|
2
|
+
|
|
3
|
+
Code example blocks render project Vue SFCs as live previews inside Markdown pages.
|
|
4
|
+
|
|
5
|
+
They are useful when documentation needs to show the real behavior of a component and still let readers inspect the exact source behind the preview.
|
|
6
|
+
|
|
7
|
+
The block is authored with the custom Markdown element `<d-block-code-example>`.
|
|
8
|
+
|
|
9
|
+
## Example Files
|
|
10
|
+
|
|
11
|
+
Place example components under `src/examples/**/*.vue` in the project using Docsector.
|
|
12
|
+
|
|
13
|
+
Docsector discovers those files at build time through Vite. The `src` value is normalized to kebab-case, so this block:
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
<d-block-code-example src="manual/code-examples/basic-counter" title="Basic counter" />
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
resolves this file:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
src/examples/manual/code-examples/BasicCounter.vue
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
You can also use `file` instead of `src` when migrating examples from Quasar Docs patterns.
|
|
26
|
+
|
|
27
|
+
## Attributes
|
|
28
|
+
|
|
29
|
+
| Attribute | Purpose |
|
|
30
|
+
|-----------|---------|
|
|
31
|
+
| `src` | Example id under `src/examples/**/*.vue` |
|
|
32
|
+
| `file` | Alias for `src` |
|
|
33
|
+
| `title` | Header title shown above the preview |
|
|
34
|
+
| `expanded` | Opens the source panel by default when set to `true` |
|
|
35
|
+
| `codepen` | Shows the CodePen action unless set to `false` |
|
|
36
|
+
| `scrollable` | Gives the preview a fixed scrollable height |
|
|
37
|
+
| `overflow` | Allows both horizontal and vertical overflow in the preview |
|
|
38
|
+
| `height` | Sets a custom preview height, such as `360` or `420px` |
|
|
39
|
+
|
|
40
|
+
## Source Panel
|
|
41
|
+
|
|
42
|
+
The source button opens the Vue SFC split into Template, Script, Style, and All tabs when those sections are present.
|
|
43
|
+
|
|
44
|
+
The source panel reuses the standard Docsector code block renderer, so readers get syntax highlighting, copy support, and the same dark/light treatment as regular code blocks.
|
|
45
|
+
|
|
46
|
+
## GitHub Source Link
|
|
47
|
+
|
|
48
|
+
The GitHub button opens the example SFC in the project repository when Docsector can derive a repository URL from `github.editBaseUrl` or `links.github` in `docsector.config.js`.
|
|
49
|
+
|
|
50
|
+
## CodePen Export
|
|
51
|
+
|
|
52
|
+
The CodePen button is available when the source can be transformed safely for a browser-only demo.
|
|
53
|
+
|
|
54
|
+
The first implementation supports plain Vue SFCs with a template, optional style, and an Options API `export default` script. Named imports from `vue` and `quasar` are converted to browser globals. Examples that use `<script setup>`, TypeScript scripts, or local imports still render in Docsector, but the CodePen action is disabled.
|
|
55
|
+
|
|
56
|
+
Set `codepen="false"` when an example is intentionally not meant to be exported.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
## Visão geral
|
|
2
|
+
|
|
3
|
+
Blocos de exemplos de código renderizam SFCs Vue do projeto como previews ao vivo dentro das páginas Markdown.
|
|
4
|
+
|
|
5
|
+
Eles são úteis quando a documentação precisa mostrar o comportamento real de um componente e ainda permitir que leitores inspecionem o código fonte exato por trás do preview.
|
|
6
|
+
|
|
7
|
+
O bloco é escrito com o elemento Markdown customizado `<d-block-code-example>`.
|
|
8
|
+
|
|
9
|
+
## Arquivos de exemplo
|
|
10
|
+
|
|
11
|
+
Coloque os componentes de exemplo em `src/examples/**/*.vue` no projeto que usa o Docsector.
|
|
12
|
+
|
|
13
|
+
O Docsector descobre esses arquivos durante o build com Vite. O valor de `src` é normalizado para kebab-case, então este bloco:
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
<d-block-code-example src="manual/code-examples/basic-counter" title="Basic counter" />
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
resolve este arquivo:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
src/examples/manual/code-examples/BasicCounter.vue
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Também é possível usar `file` no lugar de `src` ao migrar exemplos inspirados nos padrões do Quasar Docs.
|
|
26
|
+
|
|
27
|
+
## Atributos
|
|
28
|
+
|
|
29
|
+
| Atributo | Finalidade |
|
|
30
|
+
|----------|------------|
|
|
31
|
+
| `src` | Id do exemplo dentro de `src/examples/**/*.vue` |
|
|
32
|
+
| `file` | Alias de `src` |
|
|
33
|
+
| `title` | Título exibido acima do preview |
|
|
34
|
+
| `expanded` | Abre o painel de fonte por padrão quando definido como `true` |
|
|
35
|
+
| `codepen` | Mostra a ação do CodePen, exceto quando definido como `false` |
|
|
36
|
+
| `scrollable` | Dá ao preview uma altura fixa com rolagem |
|
|
37
|
+
| `overflow` | Permite overflow horizontal e vertical no preview |
|
|
38
|
+
| `height` | Define uma altura customizada para o preview, como `360` ou `420px` |
|
|
39
|
+
|
|
40
|
+
## Painel de fonte
|
|
41
|
+
|
|
42
|
+
O botão de fonte abre o SFC Vue dividido em abas Template, Script, Style e All quando essas seções existem.
|
|
43
|
+
|
|
44
|
+
O painel reutiliza o renderizador padrão de blocos de código do Docsector, então leitores recebem syntax highlighting, suporte a cópia e o mesmo tratamento visual em temas claro e escuro.
|
|
45
|
+
|
|
46
|
+
## Link de fonte no GitHub
|
|
47
|
+
|
|
48
|
+
O botão do GitHub abre o SFC do exemplo no repositório do projeto quando o Docsector consegue derivar a URL a partir de `github.editBaseUrl` ou `links.github` em `docsector.config.js`.
|
|
49
|
+
|
|
50
|
+
## Exportação para CodePen
|
|
51
|
+
|
|
52
|
+
O botão do CodePen fica disponível quando o código pode ser transformado com segurança para uma demo somente no navegador.
|
|
53
|
+
|
|
54
|
+
A primeira implementação suporta SFCs Vue simples com template, style opcional e script Options API com `export default`. Imports nomeados de `vue` e `quasar` são convertidos para globais do navegador. Exemplos que usam `<script setup>`, scripts TypeScript ou imports locais continuam renderizando no Docsector, mas a ação do CodePen fica desativada.
|
|
55
|
+
|
|
56
|
+
Use `codepen="false"` quando um exemplo intencionalmente não deve ser exportado.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
## Showcase
|
|
2
|
+
|
|
3
|
+
### Basic Counter
|
|
4
|
+
|
|
5
|
+
This example is rendered from a real Vue SFC under `src/examples/manual/code-examples/BasicCounter.vue`.
|
|
6
|
+
|
|
7
|
+
<d-block-code-example src="manual/code-examples/basic-counter" title="Basic counter">
|
|
8
|
+
Use the source button to inspect the SFC sections, or open the compatible demo in CodePen.
|
|
9
|
+
</d-block-code-example>
|
|
10
|
+
|
|
11
|
+
### Expanded Source by Default
|
|
12
|
+
|
|
13
|
+
Use `expanded="true"` when the source code is part of the lesson and should be visible as soon as the reader reaches the example.
|
|
14
|
+
|
|
15
|
+
<d-block-code-example src="manual/code-examples/inline-notice" title="Expanded source example" expanded="true">
|
|
16
|
+
This example intentionally starts with the source panel open.
|
|
17
|
+
</d-block-code-example>
|
|
18
|
+
|
|
19
|
+
## Authoring Syntax
|
|
20
|
+
|
|
21
|
+
```html
|
|
22
|
+
<d-block-code-example src="manual/code-examples/basic-counter" title="Basic counter">
|
|
23
|
+
Optional caption rendered as inline Markdown.
|
|
24
|
+
</d-block-code-example>
|
|
25
|
+
|
|
26
|
+
<d-block-code-example src="manual/code-examples/inline-notice" title="Expanded source example" expanded="true">
|
|
27
|
+
The source panel starts open.
|
|
28
|
+
</d-block-code-example>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Features Visible Above
|
|
32
|
+
|
|
33
|
+
- **Live preview** rendered from the bundled Vue component
|
|
34
|
+
- **Source toggle** with Template, Script, Style, and All tabs
|
|
35
|
+
- **CodePen action** for compatible examples
|
|
36
|
+
- **GitHub action** pointing to the example SFC
|
|
37
|
+
- **Expanded source state** with `expanded="true"`
|
|
38
|
+
- **Inline Markdown caption** below the preview
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
## Showcase
|
|
2
|
+
|
|
3
|
+
### Contador básico
|
|
4
|
+
|
|
5
|
+
Este exemplo é renderizado a partir de um SFC Vue real em `src/examples/manual/code-examples/BasicCounter.vue`.
|
|
6
|
+
|
|
7
|
+
<d-block-code-example src="manual/code-examples/basic-counter" title="Basic counter">
|
|
8
|
+
Use o botão de fonte para inspecionar as seções do SFC, ou abra a demo compatível no CodePen.
|
|
9
|
+
</d-block-code-example>
|
|
10
|
+
|
|
11
|
+
### Fonte expandida por padrão
|
|
12
|
+
|
|
13
|
+
Use `expanded="true"` quando o código fonte faz parte da explicação e deve aparecer assim que o leitor chegar ao exemplo.
|
|
14
|
+
|
|
15
|
+
<d-block-code-example src="manual/code-examples/inline-notice" title="Expanded source example" expanded="true">
|
|
16
|
+
Este exemplo intencionalmente começa com o painel de fonte aberto.
|
|
17
|
+
</d-block-code-example>
|
|
18
|
+
|
|
19
|
+
## Sintaxe de autoria
|
|
20
|
+
|
|
21
|
+
```html
|
|
22
|
+
<d-block-code-example src="manual/code-examples/basic-counter" title="Basic counter">
|
|
23
|
+
Legenda opcional renderizada como Markdown inline.
|
|
24
|
+
</d-block-code-example>
|
|
25
|
+
|
|
26
|
+
<d-block-code-example src="manual/code-examples/inline-notice" title="Expanded source example" expanded="true">
|
|
27
|
+
O painel de fonte começa aberto.
|
|
28
|
+
</d-block-code-example>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Recursos visíveis acima
|
|
32
|
+
|
|
33
|
+
- **Preview ao vivo** renderizado a partir do componente Vue empacotado
|
|
34
|
+
- **Alternância de fonte** com abas Template, Script, Style e All
|
|
35
|
+
- **Ação do CodePen** para exemplos compatíveis
|
|
36
|
+
- **Ação do GitHub** apontando para o SFC do exemplo
|
|
37
|
+
- **Estado de fonte expandida** com `expanded="true"`
|
|
38
|
+
- **Legenda em Markdown inline** abaixo do preview
|
|
@@ -514,6 +514,34 @@ export default {
|
|
|
514
514
|
}
|
|
515
515
|
},
|
|
516
516
|
|
|
517
|
+
'/content/blocks/code-examples': {
|
|
518
|
+
config: {
|
|
519
|
+
icon: 'integration_instructions',
|
|
520
|
+
status: 'new',
|
|
521
|
+
meta: {
|
|
522
|
+
description: {
|
|
523
|
+
'en-US': 'Code examples — Documentation of Docsector Reader',
|
|
524
|
+
'pt-BR': 'Exemplos de código — Documentacao do Docsector Reader'
|
|
525
|
+
}
|
|
526
|
+
},
|
|
527
|
+
book: 'manual',
|
|
528
|
+
menu: {},
|
|
529
|
+
subpages: {
|
|
530
|
+
showcase: true
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
data: {
|
|
534
|
+
'en-US': { title: 'Code examples' },
|
|
535
|
+
'pt-BR': { title: 'Exemplos de código' }
|
|
536
|
+
},
|
|
537
|
+
metadata: {
|
|
538
|
+
tags: {
|
|
539
|
+
'en-US': 'code examples live preview vue sfc source codepen component demo',
|
|
540
|
+
'pt-BR': 'exemplos código preview ao vivo vue sfc fonte codepen componente demo'
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
|
|
517
545
|
'/content/blocks/mermaid-diagrams': {
|
|
518
546
|
config: {
|
|
519
547
|
icon: 'account_tree',
|
package/src/quasar.factory.js
CHANGED
|
@@ -1144,6 +1144,82 @@ function createBooksPlugin (projectRoot) {
|
|
|
1144
1144
|
}
|
|
1145
1145
|
}
|
|
1146
1146
|
|
|
1147
|
+
function buildVirtualCodeExamplesModule () {
|
|
1148
|
+
return `const componentModules = import.meta.glob('/src/examples/**/*.vue')
|
|
1149
|
+
const sourceModules = import.meta.glob('/src/examples/**/*.vue', { query: '?raw', import: 'default' })
|
|
1150
|
+
|
|
1151
|
+
const trimSlashes = (value) => String(value || '').replace(/\\\\/g, '/').replace(/^\\/+|\\/+$/g, '')
|
|
1152
|
+
const toKebabSegment = (value) => String(value || '')
|
|
1153
|
+
.replace(/\\.vue$/i, '')
|
|
1154
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
1155
|
+
.replace(/[\\s_]+/g, '-')
|
|
1156
|
+
.replace(/-+/g, '-')
|
|
1157
|
+
.toLowerCase()
|
|
1158
|
+
|
|
1159
|
+
export const normalizeCodeExampleId = (value) => trimSlashes(value)
|
|
1160
|
+
.replace(/^src\\/examples\\//i, '')
|
|
1161
|
+
.replace(/^examples\\//i, '')
|
|
1162
|
+
.replace(/\\.vue$/i, '')
|
|
1163
|
+
.split('/')
|
|
1164
|
+
.filter(Boolean)
|
|
1165
|
+
.map(toKebabSegment)
|
|
1166
|
+
.join('/')
|
|
1167
|
+
|
|
1168
|
+
export const codeExamples = Object.keys(componentModules).reduce((examples, filePath) => {
|
|
1169
|
+
const id = normalizeCodeExampleId(filePath)
|
|
1170
|
+
|
|
1171
|
+
examples[id] = {
|
|
1172
|
+
id,
|
|
1173
|
+
filePath,
|
|
1174
|
+
loadComponent: componentModules[filePath],
|
|
1175
|
+
loadSource: sourceModules[filePath]
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
return examples
|
|
1179
|
+
}, {})
|
|
1180
|
+
|
|
1181
|
+
export const codeExampleIds = Object.keys(codeExamples).sort()
|
|
1182
|
+
|
|
1183
|
+
export const resolveCodeExample = (value) => {
|
|
1184
|
+
const id = normalizeCodeExampleId(value)
|
|
1185
|
+
const entry = codeExamples[id]
|
|
1186
|
+
|
|
1187
|
+
if (!entry) {
|
|
1188
|
+
return {
|
|
1189
|
+
id,
|
|
1190
|
+
filePath: '',
|
|
1191
|
+
exists: false,
|
|
1192
|
+
loadComponent: null,
|
|
1193
|
+
loadSource: null
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
return {
|
|
1198
|
+
...entry,
|
|
1199
|
+
exists: true
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
export default codeExamples
|
|
1204
|
+
`
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
function createCodeExamplesPlugin () {
|
|
1208
|
+
const virtualId = 'virtual:docsector-code-examples'
|
|
1209
|
+
const resolvedId = '\0' + virtualId
|
|
1210
|
+
|
|
1211
|
+
return {
|
|
1212
|
+
name: 'docsector-code-examples',
|
|
1213
|
+
resolveId (id) {
|
|
1214
|
+
if (id === virtualId) return resolvedId
|
|
1215
|
+
},
|
|
1216
|
+
load (id) {
|
|
1217
|
+
if (id !== resolvedId) return null
|
|
1218
|
+
return buildVirtualCodeExamplesModule()
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1147
1223
|
/**
|
|
1148
1224
|
* Create the HJSON Vite plugin for loading .hjson files as ES modules.
|
|
1149
1225
|
*/
|
|
@@ -1504,7 +1580,7 @@ async function fetchRemoteMarkdown (url, timeoutMs = 8000) {
|
|
|
1504
1580
|
}
|
|
1505
1581
|
}
|
|
1506
1582
|
|
|
1507
|
-
async function resolveHomePageSources (projectRoot, config = {}, options = {}) {
|
|
1583
|
+
export async function resolveHomePageSources (projectRoot, config = {}, options = {}) {
|
|
1508
1584
|
const { logPrefix = '[docsector]' } = options
|
|
1509
1585
|
const pagesDir = resolve(projectRoot, 'src', 'pages')
|
|
1510
1586
|
const { defaultLang, langs } = getConfiguredLanguages(config)
|
|
@@ -1551,18 +1627,17 @@ async function resolveHomePageSources (projectRoot, config = {}, options = {}) {
|
|
|
1551
1627
|
function createHomePageOverridePlugin (projectRoot) {
|
|
1552
1628
|
const virtualId = 'virtual:docsector-homepage-override'
|
|
1553
1629
|
const resolvedId = '\0' + virtualId
|
|
1554
|
-
let
|
|
1630
|
+
let homePageSources = null
|
|
1555
1631
|
let loadPromise = null
|
|
1556
1632
|
|
|
1557
1633
|
const ensureSources = async () => {
|
|
1558
|
-
if (
|
|
1634
|
+
if (homePageSources) return homePageSources
|
|
1559
1635
|
if (!loadPromise) {
|
|
1560
1636
|
loadPromise = (async () => {
|
|
1561
1637
|
const configUrl = pathToFileURL(resolve(projectRoot, 'docsector.config.js')).href
|
|
1562
1638
|
const { default: config } = await import(configUrl)
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
return byLang
|
|
1639
|
+
homePageSources = await resolveHomePageSources(projectRoot, config, { logPrefix: '[docsector]' })
|
|
1640
|
+
return homePageSources
|
|
1566
1641
|
})().finally(() => {
|
|
1567
1642
|
loadPromise = null
|
|
1568
1643
|
})
|
|
@@ -1586,18 +1661,21 @@ function createHomePageOverridePlugin (projectRoot) {
|
|
|
1586
1661
|
},
|
|
1587
1662
|
async load (id) {
|
|
1588
1663
|
if (id === resolvedId) {
|
|
1589
|
-
await ensureSources()
|
|
1590
|
-
return
|
|
1664
|
+
const sources = await ensureSources()
|
|
1665
|
+
return [
|
|
1666
|
+
`export const homePageSourceMode = ${JSON.stringify(sources?.mode || 'local')}`,
|
|
1667
|
+
`export default ${JSON.stringify(sources?.byLang || {})}`
|
|
1668
|
+
].join('\n')
|
|
1591
1669
|
}
|
|
1592
1670
|
|
|
1593
|
-
await ensureSources()
|
|
1594
|
-
if (!byLang) return null
|
|
1671
|
+
const sources = await ensureSources()
|
|
1672
|
+
if (!sources?.byLang) return null
|
|
1595
1673
|
|
|
1596
1674
|
const match = id.match(/Homepage\.([A-Za-z0-9-]+)\.md\?raw(?:$|&)/)
|
|
1597
1675
|
if (!match) return null
|
|
1598
1676
|
|
|
1599
1677
|
const lang = match[1]
|
|
1600
|
-
const content = byLang[lang]
|
|
1678
|
+
const content = sources.byLang[lang]
|
|
1601
1679
|
if (typeof content !== 'string') return null
|
|
1602
1680
|
|
|
1603
1681
|
return `export default ${JSON.stringify(content)}`
|
|
@@ -2917,6 +2995,7 @@ export function createQuasarConfig (options = {}) {
|
|
|
2917
2995
|
|
|
2918
2996
|
vitePlugins: [
|
|
2919
2997
|
createBooksPlugin(projectRoot),
|
|
2998
|
+
createCodeExamplesPlugin(),
|
|
2920
2999
|
createHjsonPlugin(),
|
|
2921
3000
|
createHomePageOverridePlugin(projectRoot),
|
|
2922
3001
|
createGitDatesPlugin(projectRoot),
|
package/src/store/Page.js
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
const getNodePath = (nodes, targetId, ancestry = []) => {
|
|
2
|
+
for (const node of nodes) {
|
|
3
|
+
const nextAncestry = [...ancestry, node.id]
|
|
4
|
+
|
|
5
|
+
if (node.id === targetId) {
|
|
6
|
+
return nextAncestry
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
if (Array.isArray(node.children) && node.children.length > 0) {
|
|
10
|
+
const childPath = getNodePath(node.children, targetId, nextAncestry)
|
|
11
|
+
|
|
12
|
+
if (childPath !== null) {
|
|
13
|
+
return childPath
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return null
|
|
19
|
+
}
|
|
20
|
+
|
|
1
21
|
export default {
|
|
2
22
|
namespaced: true,
|
|
3
23
|
|
|
@@ -46,7 +66,7 @@ export default {
|
|
|
46
66
|
pushAnchors (state, value) {
|
|
47
67
|
if (value === false) {
|
|
48
68
|
state.anchors = []
|
|
49
|
-
} else {
|
|
69
|
+
} else if (!state.anchors.includes(value)) {
|
|
50
70
|
// index: id
|
|
51
71
|
state.anchors.push(value)
|
|
52
72
|
}
|
|
@@ -82,7 +102,13 @@ export default {
|
|
|
82
102
|
state.nodesExpanded = [0]
|
|
83
103
|
},
|
|
84
104
|
pushNodesExpanded (state, nodeId) {
|
|
85
|
-
state.
|
|
105
|
+
const nodePath = getNodePath(state.nodes, nodeId) || [nodeId]
|
|
106
|
+
|
|
107
|
+
for (const pathNodeId of nodePath) {
|
|
108
|
+
if (!state.nodesExpanded.includes(pathNodeId)) {
|
|
109
|
+
state.nodesExpanded.push(pathNodeId)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
86
112
|
},
|
|
87
113
|
|
|
88
114
|
setScrolling (state, val) {
|