@docsector/docsector-reader 3.3.1 → 3.4.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 +1 -0
- package/bin/docsector.js +1 -1
- package/package.json +2 -1
- package/src/components/DPageTokens.vue +2 -0
- package/src/components/page-section-tokens.js +52 -6
- package/src/css/app.sass +43 -0
- package/src/pages/manual/content/blocks/task-lists.overview.en-US.md +20 -0
- package/src/pages/manual/content/blocks/task-lists.overview.pt-BR.md +20 -0
- package/src/pages/manual/content/blocks/task-lists.showcase.en-US.md +23 -0
- package/src/pages/manual/content/blocks/task-lists.showcase.pt-BR.md +23 -0
- package/src/pages/manual.index.js +28 -0
- package/src/pages/manual/components/d-subpage.overview.en-US.md +0 -60
- package/src/pages/manual/components/d-subpage.overview.pt-BR.md +0 -60
package/README.md
CHANGED
|
@@ -42,6 +42,7 @@ Transform Markdown content into beautiful, navigable documentation sites — wit
|
|
|
42
42
|
|
|
43
43
|
- 📝 **Markdown Rendering** — Write docs in Markdown, rendered with syntax highlighting (Prism.js)
|
|
44
44
|
- 🔽 **Nested Markdown Lists** — Ordered and unordered lists preserve sublist hierarchy across multiple indentation levels
|
|
45
|
+
- ☑️ **Markdown Task Lists** — GitBook-style `- [ ]` and `- [x]` items render as read-only checkboxes with nested subtasks
|
|
45
46
|
- 🖼️ **Block Image Captions & Zoom** — Standalone Markdown images render as zoomable figures, and raw `figure` / `picture` markup supports separate alt text and captions
|
|
46
47
|
- 🧱 **Raw HTML in Markdown** — Renders inline and block HTML tags inside markdown sections (including homepage remote README content)
|
|
47
48
|
- 🧩 **Mermaid Diagrams** — Native support for fenced ` ```mermaid ` blocks, with automatic dark/light theme switching
|
package/bin/docsector.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docsector/docsector-reader",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "A documentation rendering engine built with Vue 3, Quasar v2 and Vite. Transform Markdown into beautiful, navigable documentation sites.",
|
|
5
5
|
"productName": "Docsector Reader",
|
|
6
6
|
"author": "Rodrigo de Araujo Vieira",
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"katex": "^0.17.0",
|
|
75
75
|
"markdown-it": "^13.0.1",
|
|
76
76
|
"markdown-it-attrs": "^4.1.6",
|
|
77
|
+
"markdown-it-task-lists": "^2.1.1",
|
|
77
78
|
"markdown-it-texmath": "^1.0.0",
|
|
78
79
|
"mermaid": "^11.0.0",
|
|
79
80
|
"prismjs": "^1.27.0",
|
|
@@ -58,10 +58,12 @@ import DPageExpandable from './DPageExpandable.vue'
|
|
|
58
58
|
|
|
59
59
|
<ul
|
|
60
60
|
v-else-if="token.tag === 'ul'"
|
|
61
|
+
v-bind="token.attrs || {}"
|
|
61
62
|
v-html="token.content"
|
|
62
63
|
></ul>
|
|
63
64
|
<ol
|
|
64
65
|
v-else-if="token.tag === 'ol'"
|
|
66
|
+
v-bind="token.attrs || {}"
|
|
65
67
|
v-html="token.content"
|
|
66
68
|
></ol>
|
|
67
69
|
|
|
@@ -2,6 +2,7 @@ import MarkdownIt from 'markdown-it'
|
|
|
2
2
|
import attrs from 'markdown-it-attrs'
|
|
3
3
|
import GithubSlugger from 'github-slugger'
|
|
4
4
|
import katex from 'katex'
|
|
5
|
+
import taskLists from 'markdown-it-task-lists'
|
|
5
6
|
import texmath from 'markdown-it-texmath'
|
|
6
7
|
|
|
7
8
|
const ALERT_MESSAGE_TYPES = new Set([
|
|
@@ -114,6 +115,32 @@ const restoreShieldedCodeSegments = (source = '', codeSegmentsMap = new Map()) =
|
|
|
114
115
|
return restored
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
const escapeHtmlAttributeValue = (value = '') => {
|
|
119
|
+
return String(value)
|
|
120
|
+
.replace(/&/g, '&')
|
|
121
|
+
.replace(/"/g, '"')
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const getTokenAttributes = (element) => {
|
|
125
|
+
if (!Array.isArray(element?.attrs) || element.attrs.length === 0) {
|
|
126
|
+
return null
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return Object.fromEntries(element.attrs.map(([name, value]) => [name, value ?? '']))
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const renderTokenAttributes = (element) => {
|
|
133
|
+
const attributes = getTokenAttributes(element)
|
|
134
|
+
|
|
135
|
+
if (attributes === null) {
|
|
136
|
+
return ''
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return Object.entries(attributes)
|
|
140
|
+
.map(([name, value]) => ` ${name}="${escapeHtmlAttributeValue(value)}"`)
|
|
141
|
+
.join('')
|
|
142
|
+
}
|
|
143
|
+
|
|
117
144
|
const extractQuickLinksBlocks = (source = '') => {
|
|
118
145
|
const map = new Map()
|
|
119
146
|
let index = 0
|
|
@@ -468,6 +495,14 @@ const renderBlockToken = (markdown, element, env) => {
|
|
|
468
495
|
return markdown.renderer.render([element], markdown.options, env).trim()
|
|
469
496
|
}
|
|
470
497
|
|
|
498
|
+
const renderInlineToken = (markdown, markdownInline, element, env) => {
|
|
499
|
+
if (Array.isArray(element.children) && element.children.length > 0) {
|
|
500
|
+
return markdown.renderer.renderInline(element.children, markdown.options, env)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return markdownInline.renderInline(element.content, env)
|
|
504
|
+
}
|
|
505
|
+
|
|
471
506
|
const createMarkdownBlockParser = () => {
|
|
472
507
|
const markdown = installMathSupport(new MarkdownIt({
|
|
473
508
|
html: true
|
|
@@ -478,6 +513,11 @@ const createMarkdownBlockParser = () => {
|
|
|
478
513
|
rightDelimiter: ';',
|
|
479
514
|
allowedAttributes: ['filename', 'group', 'tab', 'breadcrumb']
|
|
480
515
|
})
|
|
516
|
+
markdown.use(taskLists, {
|
|
517
|
+
enabled: false,
|
|
518
|
+
label: false,
|
|
519
|
+
labelAfter: false
|
|
520
|
+
})
|
|
481
521
|
|
|
482
522
|
return markdown
|
|
483
523
|
}
|
|
@@ -662,7 +702,7 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
|
|
|
662
702
|
}
|
|
663
703
|
|
|
664
704
|
if (element.type === 'inline') {
|
|
665
|
-
element.content = markdownInline
|
|
705
|
+
element.content = renderInlineToken(markdown, markdownInline, element, markdownEnv)
|
|
666
706
|
}
|
|
667
707
|
|
|
668
708
|
if (level === 0) {
|
|
@@ -779,23 +819,29 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
|
|
|
779
819
|
switch (element.type) {
|
|
780
820
|
case 'bullet_list_open':
|
|
781
821
|
if (level === 1) {
|
|
822
|
+
const attributes = getTokenAttributes(element)
|
|
823
|
+
|
|
782
824
|
tokens.push({
|
|
783
825
|
tag: 'ul',
|
|
784
|
-
content: ''
|
|
826
|
+
content: '',
|
|
827
|
+
...(attributes !== null ? { attrs: attributes } : {})
|
|
785
828
|
})
|
|
786
829
|
} else {
|
|
787
|
-
parent.content +=
|
|
830
|
+
parent.content += `<ul${renderTokenAttributes(element)}>`
|
|
788
831
|
}
|
|
789
832
|
break
|
|
790
833
|
|
|
791
834
|
case 'ordered_list_open':
|
|
792
835
|
if (level === 1) {
|
|
836
|
+
const attributes = getTokenAttributes(element)
|
|
837
|
+
|
|
793
838
|
tokens.push({
|
|
794
839
|
tag: 'ol',
|
|
795
|
-
content: ''
|
|
840
|
+
content: '',
|
|
841
|
+
...(attributes !== null ? { attrs: attributes } : {})
|
|
796
842
|
})
|
|
797
843
|
} else {
|
|
798
|
-
parent.content +=
|
|
844
|
+
parent.content += `<ol${renderTokenAttributes(element)}>`
|
|
799
845
|
}
|
|
800
846
|
break
|
|
801
847
|
|
|
@@ -811,7 +857,7 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
|
|
|
811
857
|
break
|
|
812
858
|
|
|
813
859
|
case 'list_item_open':
|
|
814
|
-
parent.content +=
|
|
860
|
+
parent.content += `<li${renderTokenAttributes(element)}>`
|
|
815
861
|
break
|
|
816
862
|
|
|
817
863
|
case 'thead_open':
|
package/src/css/app.sass
CHANGED
|
@@ -15,6 +15,11 @@ body
|
|
|
15
15
|
body.body--light
|
|
16
16
|
--q-primary: #655529 !important
|
|
17
17
|
--q-secondary: #000080 !important
|
|
18
|
+
--task-list-checkbox-bg: #ffffff
|
|
19
|
+
--task-list-checkbox-border: #7c6a3a
|
|
20
|
+
--task-list-checkbox-checked-bg: #5e4c1f
|
|
21
|
+
--task-list-checkbox-checked-border: #4f3f18
|
|
22
|
+
--task-list-checkbox-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2.3' d='M3.25 8.5 6.4 11.4 12.75 4.9'/%3E%3C/svg%3E")
|
|
18
23
|
|
|
19
24
|
--q-primary-background: #FFF !important
|
|
20
25
|
|
|
@@ -48,6 +53,11 @@ body.body--dark
|
|
|
48
53
|
--q-primary-in-dark-bg: #C1A667 !important
|
|
49
54
|
--q-secondary: #FFA07A !important
|
|
50
55
|
--q-negative: #fc001b !important
|
|
56
|
+
--task-list-checkbox-bg: #181818
|
|
57
|
+
--task-list-checkbox-border: #8f8a78
|
|
58
|
+
--task-list-checkbox-checked-bg: #d3b874
|
|
59
|
+
--task-list-checkbox-checked-border: #ebd08a
|
|
60
|
+
--task-list-checkbox-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23151515' stroke-linecap='round' stroke-linejoin='round' stroke-width='2.3' d='M3.25 8.5 6.4 11.4 12.75 4.9'/%3E%3C/svg%3E")
|
|
51
61
|
|
|
52
62
|
--q-light-in-dark-1: #ababab
|
|
53
63
|
--q-light-in-dark-2: #c9c9c9
|
|
@@ -199,6 +209,39 @@ body.body--dark
|
|
|
199
209
|
overflow-y: hidden
|
|
200
210
|
padding: 0.35rem 0.1rem
|
|
201
211
|
|
|
212
|
+
ul.contains-task-list,
|
|
213
|
+
ol.contains-task-list
|
|
214
|
+
list-style: none
|
|
215
|
+
padding-left: 1.75rem
|
|
216
|
+
|
|
217
|
+
li.task-list-item
|
|
218
|
+
list-style: none
|
|
219
|
+
|
|
220
|
+
input.task-list-item-checkbox
|
|
221
|
+
appearance: none
|
|
222
|
+
-webkit-appearance: none
|
|
223
|
+
width: 1rem
|
|
224
|
+
height: 1rem
|
|
225
|
+
margin: 0 0.55rem 0 -1.45rem
|
|
226
|
+
vertical-align: middle
|
|
227
|
+
pointer-events: none
|
|
228
|
+
opacity: 1
|
|
229
|
+
border: 2px solid var(--task-list-checkbox-border)
|
|
230
|
+
border-radius: 0.2rem
|
|
231
|
+
background-color: var(--task-list-checkbox-bg)
|
|
232
|
+
background-position: center
|
|
233
|
+
background-repeat: no-repeat
|
|
234
|
+
background-size: 0.72rem 0.72rem
|
|
235
|
+
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08)
|
|
236
|
+
|
|
237
|
+
&:checked
|
|
238
|
+
background-color: var(--task-list-checkbox-checked-bg)
|
|
239
|
+
border-color: var(--task-list-checkbox-checked-border)
|
|
240
|
+
background-image: var(--task-list-checkbox-check)
|
|
241
|
+
|
|
242
|
+
&:disabled
|
|
243
|
+
opacity: 1
|
|
244
|
+
|
|
202
245
|
a
|
|
203
246
|
text-decoration: none
|
|
204
247
|
outline: 0
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## Overview
|
|
2
|
+
|
|
3
|
+
Use task lists when each item should carry an explicit done or not-done state.
|
|
4
|
+
|
|
5
|
+
They work well for release preparation, editorial queues, migration follow-ups, and any checklist where progress matters as much as the content itself.
|
|
6
|
+
|
|
7
|
+
## Markdown Syntax
|
|
8
|
+
|
|
9
|
+
```markdown
|
|
10
|
+
- [ ] Write the overview page
|
|
11
|
+
- [x] Add localized examples
|
|
12
|
+
- [ ] Review screenshots
|
|
13
|
+
- [x] Update internal links
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Notes
|
|
17
|
+
|
|
18
|
+
- Task lists follow the same indentation rules as regular Markdown lists.
|
|
19
|
+
- Use `[ ]` for pending items and `[x]` for completed items.
|
|
20
|
+
- Published readers see static checkboxes and cannot toggle them from the page.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## Visão Geral
|
|
2
|
+
|
|
3
|
+
Use listas de tarefas quando cada item precisar deixar explícito se foi concluído ou não.
|
|
4
|
+
|
|
5
|
+
Elas funcionam bem para preparação de releases, filas editoriais, acompanhamentos de migração e qualquer checklist em que o progresso importa tanto quanto o conteúdo.
|
|
6
|
+
|
|
7
|
+
## Sintaxe em Markdown
|
|
8
|
+
|
|
9
|
+
```markdown
|
|
10
|
+
- [ ] Escreva a página de visão geral
|
|
11
|
+
- [x] Adicione exemplos localizados
|
|
12
|
+
- [ ] Revise as capturas de tela
|
|
13
|
+
- [x] Atualize os links internos
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Observações
|
|
17
|
+
|
|
18
|
+
- Listas de tarefas seguem as mesmas regras de indentação das listas Markdown comuns.
|
|
19
|
+
- Use `[ ]` para itens pendentes e `[x]` para itens concluídos.
|
|
20
|
+
- Leitores da documentação publicada veem checkboxes estáticos e não podem alterná-los pela página.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## Showcase
|
|
2
|
+
|
|
3
|
+
### Simple Tasks
|
|
4
|
+
|
|
5
|
+
- [ ] Write the overview page
|
|
6
|
+
- [x] Add the showcase page
|
|
7
|
+
- [ ] Review internal links
|
|
8
|
+
|
|
9
|
+
### Nested Tasks
|
|
10
|
+
|
|
11
|
+
- [ ] Prepare the release
|
|
12
|
+
- [x] Update the changelog
|
|
13
|
+
- [ ] Run smoke tests
|
|
14
|
+
- [ ] Publish the package
|
|
15
|
+
|
|
16
|
+
### Mixed Guidance
|
|
17
|
+
|
|
18
|
+
> [!NOTE]
|
|
19
|
+
> Readers can see the authored state of each task, but published checkboxes stay read-only.
|
|
20
|
+
|
|
21
|
+
- [x] Keep completed items checked in the source.
|
|
22
|
+
- [ ] Use task lists when the state matters more than the order.
|
|
23
|
+
- Regular bullet lists are still better for collections that do not need a done/not-done signal.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## Showcase
|
|
2
|
+
|
|
3
|
+
### Tarefas Simples
|
|
4
|
+
|
|
5
|
+
- [ ] Escreva a página de visão geral
|
|
6
|
+
- [x] Adicione a página de showcase
|
|
7
|
+
- [ ] Revise os links internos
|
|
8
|
+
|
|
9
|
+
### Tarefas Aninhadas
|
|
10
|
+
|
|
11
|
+
- [ ] Prepare a release
|
|
12
|
+
- [x] Atualize o changelog
|
|
13
|
+
- [ ] Execute smoke tests
|
|
14
|
+
- [ ] Publique o pacote
|
|
15
|
+
|
|
16
|
+
### Orientação de Uso
|
|
17
|
+
|
|
18
|
+
> [!NOTE]
|
|
19
|
+
> Leitores conseguem ver o estado definido no Markdown, mas os checkboxes publicados continuam somente leitura.
|
|
20
|
+
|
|
21
|
+
- [x] Mantenha marcados os itens concluídos no conteúdo-fonte.
|
|
22
|
+
- [ ] Use listas de tarefas quando o estado importar mais do que a ordem.
|
|
23
|
+
- Listas com marcadores continuam melhores para coleções que não precisam de sinal de concluído ou pendente.
|
|
@@ -402,6 +402,34 @@ export default {
|
|
|
402
402
|
}
|
|
403
403
|
},
|
|
404
404
|
|
|
405
|
+
'/content/blocks/task-lists': {
|
|
406
|
+
config: {
|
|
407
|
+
icon: 'check_box',
|
|
408
|
+
status: 'done',
|
|
409
|
+
meta: {
|
|
410
|
+
description: {
|
|
411
|
+
'en-US': 'Task lists — Documentation of Docsector Reader',
|
|
412
|
+
'pt-BR': 'Listas de tarefas — Documentacao do Docsector Reader'
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
book: 'manual',
|
|
416
|
+
menu: {},
|
|
417
|
+
subpages: {
|
|
418
|
+
showcase: true
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
data: {
|
|
422
|
+
'en-US': { title: 'Task lists' },
|
|
423
|
+
'pt-BR': { title: 'Listas de tarefas' }
|
|
424
|
+
},
|
|
425
|
+
metadata: {
|
|
426
|
+
tags: {
|
|
427
|
+
'en-US': 'task list checklist checkbox todo done pending nested markdown gitbook',
|
|
428
|
+
'pt-BR': 'lista de tarefas checklist checkbox todo concluído pendente aninhada markdown gitbook'
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
|
|
405
433
|
'/content/blocks/hints': {
|
|
406
434
|
config: {
|
|
407
435
|
icon: 'lightbulb',
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
## Overview
|
|
2
|
-
|
|
3
|
-
`DSubpage` is a **convenience wrapper** that composes `DPage`, `DH1`, and `DPageSection` into a standard documentation page layout. It is the component loaded by the router for every subpage route.
|
|
4
|
-
|
|
5
|
-
## How It Works
|
|
6
|
-
|
|
7
|
-
DSubpage generates a deterministic numeric ID from the current route path using a hash function. This ID is passed to `DPageSection` to keep per-page renderer indexes stable across page navigations.
|
|
8
|
-
|
|
9
|
-
## Template
|
|
10
|
-
|
|
11
|
-
```html
|
|
12
|
-
<d-page show-back-to-top-control>
|
|
13
|
-
<header>
|
|
14
|
-
<d-h1 :id="0" />
|
|
15
|
-
</header>
|
|
16
|
-
<main>
|
|
17
|
-
<d-page-section :id="id" />
|
|
18
|
-
</main>
|
|
19
|
-
</d-page>
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## ID Generation
|
|
23
|
-
|
|
24
|
-
The `id` computed property creates a consistent hash from the route path:
|
|
25
|
-
|
|
26
|
-
```javascript
|
|
27
|
-
const id = computed(() => {
|
|
28
|
-
const path = route.path
|
|
29
|
-
let hash = 5381
|
|
30
|
-
for (let i = 0; i < path.length; i++) {
|
|
31
|
-
hash = (hash * 33) ^ path.charCodeAt(i)
|
|
32
|
-
}
|
|
33
|
-
return hash >>> 0
|
|
34
|
-
})
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
This keeps per-page renderer state isolated when switching between pages. Markdown section headings themselves use GitHub-compatible slugs derived from the heading text, so README-style Table of Contents links keep working.
|
|
38
|
-
|
|
39
|
-
## When to Use
|
|
40
|
-
|
|
41
|
-
DSubpage is automatically used by the router for all documentation pages. You don't need to use it directly unless creating custom page layouts. For standard documentation, the router handles everything:
|
|
42
|
-
|
|
43
|
-
```javascript
|
|
44
|
-
// In routes.js - this happens automatically
|
|
45
|
-
{
|
|
46
|
-
path: 'overview',
|
|
47
|
-
component: () => import('components/DSubpage.vue'),
|
|
48
|
-
meta: { status: config.status }
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## Relationship with DPage
|
|
53
|
-
|
|
54
|
-
- `DSubpage` **uses** `DPage` as its container
|
|
55
|
-
- `DPage` handles layout (scroll, toolbar, drawer)
|
|
56
|
-
- `DSubpage` handles content composition (H1 + sections)
|
|
57
|
-
|
|
58
|
-
## Built-in Back to Top Control
|
|
59
|
-
|
|
60
|
-
Routed documentation subpages enable DPage's floating back-to-top control automatically. The control is only shown when the content actually overflows, becomes visible after the reader scrolls a little, and visualizes the current reading progress with a circular indicator.
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
## Visão Geral
|
|
2
|
-
|
|
3
|
-
`DSubpage` é um **wrapper de conveniência** que compõe `DPage`, `DH1` e `DPageSection` em um layout de página de documentação padrão. É o componente carregado pelo roteador para cada rota de sub-página.
|
|
4
|
-
|
|
5
|
-
## Como Funciona
|
|
6
|
-
|
|
7
|
-
DSubpage gera um ID numérico determinístico a partir do caminho da rota atual usando uma função hash. Esse ID é passado ao `DPageSection` para manter estáveis os índices internos do renderer em cada página.
|
|
8
|
-
|
|
9
|
-
## Template
|
|
10
|
-
|
|
11
|
-
```html
|
|
12
|
-
<d-page show-back-to-top-control>
|
|
13
|
-
<header>
|
|
14
|
-
<d-h1 :id="0" />
|
|
15
|
-
</header>
|
|
16
|
-
<main>
|
|
17
|
-
<d-page-section :id="id" />
|
|
18
|
-
</main>
|
|
19
|
-
</d-page>
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Geração de ID
|
|
23
|
-
|
|
24
|
-
A propriedade computada `id` cria um hash consistente a partir do caminho da rota:
|
|
25
|
-
|
|
26
|
-
```javascript
|
|
27
|
-
const id = computed(() => {
|
|
28
|
-
const path = route.path
|
|
29
|
-
let hash = 5381
|
|
30
|
-
for (let i = 0; i < path.length; i++) {
|
|
31
|
-
hash = (hash * 33) ^ path.charCodeAt(i)
|
|
32
|
-
}
|
|
33
|
-
return hash >>> 0
|
|
34
|
-
})
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Isso mantém o estado interno do renderer isolado ao navegar entre páginas. Os headings Markdown em si usam slugs compatíveis com GitHub derivados do texto do título, então links de Table of Contents no estilo README continuam funcionando.
|
|
38
|
-
|
|
39
|
-
## Quando Usar
|
|
40
|
-
|
|
41
|
-
DSubpage é usado automaticamente pelo roteador para todas as páginas de documentação. Você não precisa usá-lo diretamente, a menos que crie layouts de página customizados. Para documentação padrão, o roteador cuida de tudo:
|
|
42
|
-
|
|
43
|
-
```javascript
|
|
44
|
-
// Em routes.js - isso acontece automaticamente
|
|
45
|
-
{
|
|
46
|
-
path: 'overview',
|
|
47
|
-
component: () => import('components/DSubpage.vue'),
|
|
48
|
-
meta: { status: config.status }
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## Relação com DPage
|
|
53
|
-
|
|
54
|
-
- `DSubpage` **usa** `DPage` como container
|
|
55
|
-
- `DPage` cuida do layout (scroll, toolbar, drawer)
|
|
56
|
-
- `DSubpage` cuida da composição de conteúdo (H1 + seções)
|
|
57
|
-
|
|
58
|
-
## Controle Integrado de Voltar ao Topo
|
|
59
|
-
|
|
60
|
-
As sub-páginas de documentação roteadas habilitam automaticamente o controle flutuante de voltar ao topo do DPage. O controle só é exibido quando o conteúdo realmente tem overflow, fica visível após uma pequena rolagem do leitor e mostra o progresso atual de leitura com um indicador circular.
|