@docsector/docsector-reader 3.0.0 → 3.2.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 +2 -0
- package/bin/docsector.js +1 -1
- package/package.json +3 -1
- package/src/components/DPage.vue +99 -5
- package/src/components/DSubpage.vue +1 -1
- package/src/components/page-section-tokens.js +40 -5
- package/src/composables/useReadingProgress.js +46 -0
- package/src/css/app.sass +12 -0
- package/src/pages/guide/i18n-and-markdown.overview.en-US.md +19 -3
- package/src/pages/guide/i18n-and-markdown.overview.pt-BR.md +16 -0
- package/src/pages/manual/components/d-page.overview.en-US.md +3 -0
- package/src/pages/manual/components/d-page.overview.pt-BR.md +3 -0
- package/src/pages/manual/components/d-subpage.overview.en-US.md +5 -1
- package/src/pages/manual/components/d-subpage.overview.pt-BR.md +5 -1
package/README.md
CHANGED
|
@@ -43,6 +43,7 @@ Transform Markdown content into beautiful, navigable documentation sites — wit
|
|
|
43
43
|
- 📝 **Markdown Rendering** — Write docs in Markdown, rendered with syntax highlighting (Prism.js)
|
|
44
44
|
- 🧱 **Raw HTML in Markdown** — Renders inline and block HTML tags inside markdown sections (including homepage remote README content)
|
|
45
45
|
- 🧩 **Mermaid Diagrams** — Native support for fenced ` ```mermaid ` blocks, with automatic dark/light theme switching
|
|
46
|
+
- ➗ **Math & KaTeX** — Native support for inline `$...$` and display `$$...$$` formulas rendered with KaTeX
|
|
46
47
|
- 🚨 **GitHub-Style Alerts** — Native support for `[!NOTE]`, `[!TIP]`, `[!IMPORTANT]`, `[!WARNING]`, and `[!CAUTION]`
|
|
47
48
|
- 🌍 **Internationalization (i18n)** — Multi-language support with HJSON locale files and per-page translations
|
|
48
49
|
- 🌗 **Dark/Light Mode** — Automatic theme switching with Quasar Dark Plugin
|
|
@@ -54,6 +55,7 @@ Transform Markdown content into beautiful, navigable documentation sites — wit
|
|
|
54
55
|
- 📚 **Book Tabs with Per-State Colors** — Define `*.book.js` tabs with icons, order, and `color.active` / `color.inactive`
|
|
55
56
|
- 🔀 **Internal Shortcut Pages** — Route entries can redirect with `config.link.to`, keeping localized titles while inheriting icon/status from the destination page
|
|
56
57
|
- 📐 **Responsive Subpage Toolbar** — Subpage actions align with the content column on desktop and dock to the bottom on mobile
|
|
58
|
+
- ⬆️ **Reading Progress Back to Top** — Documentation subpages can show a floating back-to-top control with circular reading progress that stays above the mobile subpage toolbar
|
|
57
59
|
- 🏷️ **Status Badges** — Mark pages as `done`, `draft`, `empty`, or `new` with visual indicators
|
|
58
60
|
- ✏️ **Edit on GitHub** — Direct links to edit pages on your repository
|
|
59
61
|
- 🧭 **Robust Edit Link Mapping** — Normalizes route paths (including trailing slashes) into `page.subpage.locale.md` source files for reliable GitHub edit URLs
|
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.2.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",
|
|
@@ -70,8 +70,10 @@
|
|
|
70
70
|
"axios": "^1.7.7",
|
|
71
71
|
"core-js": "^3.6.5",
|
|
72
72
|
"hjson": "^3.2.2",
|
|
73
|
+
"katex": "^0.17.0",
|
|
73
74
|
"markdown-it": "^13.0.1",
|
|
74
75
|
"markdown-it-attrs": "^4.1.6",
|
|
76
|
+
"markdown-it-texmath": "^1.0.0",
|
|
75
77
|
"mermaid": "^11.0.0",
|
|
76
78
|
"prismjs": "^1.27.0",
|
|
77
79
|
"q-colorize-mixin": "^2.0.0-alpha.5"
|
package/src/components/DPage.vue
CHANGED
|
@@ -5,6 +5,7 @@ import { useRoute, useRouter } from 'vue-router'
|
|
|
5
5
|
import { useQuasar } from 'quasar'
|
|
6
6
|
|
|
7
7
|
import useNavigator from '../composables/useNavigator'
|
|
8
|
+
import { getReadingProgressState } from '../composables/useReadingProgress'
|
|
8
9
|
|
|
9
10
|
import DPageAnchor from './DPageAnchor.vue'
|
|
10
11
|
import DPageMeta from './DPageMeta.vue'
|
|
@@ -20,6 +21,10 @@ const props = defineProps({
|
|
|
20
21
|
disableNav: {
|
|
21
22
|
type: Boolean,
|
|
22
23
|
default: false
|
|
24
|
+
},
|
|
25
|
+
showBackToTopControl: {
|
|
26
|
+
type: Boolean,
|
|
27
|
+
default: false
|
|
23
28
|
}
|
|
24
29
|
})
|
|
25
30
|
|
|
@@ -29,6 +34,26 @@ const submenu = ref(null)
|
|
|
29
34
|
const pageMinHeight = ref('calc(100vh - 86px)')
|
|
30
35
|
const submenuHeight = ref('36px')
|
|
31
36
|
const pageBottomInset = ref('0px')
|
|
37
|
+
const readingProgress = ref(getReadingProgressState())
|
|
38
|
+
|
|
39
|
+
const getPageScrollContainer = () => {
|
|
40
|
+
return pageScrollArea.value?.$el?.querySelector('.q-scrollarea__container') || null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const syncReadingProgress = (scrollTop = null) => {
|
|
44
|
+
const container = getPageScrollContainer()
|
|
45
|
+
|
|
46
|
+
if (!container) {
|
|
47
|
+
readingProgress.value = getReadingProgressState()
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
readingProgress.value = getReadingProgressState({
|
|
52
|
+
scrollTop: scrollTop ?? container.scrollTop,
|
|
53
|
+
scrollHeight: container.scrollHeight,
|
|
54
|
+
clientHeight: container.clientHeight
|
|
55
|
+
})
|
|
56
|
+
}
|
|
32
57
|
|
|
33
58
|
const updatePageMinHeight = () => {
|
|
34
59
|
const pageContainerEl = pageContainer.value?.$el || pageContainer.value
|
|
@@ -47,6 +72,7 @@ const updatePageMinHeight = () => {
|
|
|
47
72
|
pageMinHeight.value = `calc(100vh - ${totalOffset}px)`
|
|
48
73
|
submenuHeight.value = `${Math.max(36, Math.round(measuredSubmenuHeight))}px`
|
|
49
74
|
pageBottomInset.value = isMobile ? submenuHeight.value : '0px'
|
|
75
|
+
syncReadingProgress()
|
|
50
76
|
}
|
|
51
77
|
|
|
52
78
|
const schedulePageMinHeightUpdate = () => {
|
|
@@ -80,6 +106,12 @@ const main = computed(() => {
|
|
|
80
106
|
return 'overview'
|
|
81
107
|
}
|
|
82
108
|
})
|
|
109
|
+
const shouldShowBackToTopControl = computed(() => {
|
|
110
|
+
return props.showBackToTopControl && readingProgress.value.hasOverflow && readingProgress.value.isVisible
|
|
111
|
+
})
|
|
112
|
+
const backToTopRightOffset = computed(() => {
|
|
113
|
+
return layoutMeta.value && !$q.screen.lt.md ? '332px' : '24px'
|
|
114
|
+
})
|
|
83
115
|
|
|
84
116
|
const toggleSectionsTree = () => {
|
|
85
117
|
layoutMeta.value = !layoutMeta.value
|
|
@@ -133,10 +165,7 @@ const resetPageScroll = () => {
|
|
|
133
165
|
if (pageScrollArea.value !== null) {
|
|
134
166
|
pageScrollArea.value.setScrollPosition('vertical', 0, 0)
|
|
135
167
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const getPageScrollContainer = () => {
|
|
139
|
-
return pageScrollArea.value?.$el?.querySelector('.q-scrollarea__container') || null
|
|
168
|
+
syncReadingProgress(0)
|
|
140
169
|
}
|
|
141
170
|
|
|
142
171
|
const isEditableTarget = (target) => {
|
|
@@ -208,6 +237,15 @@ const handleMainScrollKeys = (event) => {
|
|
|
208
237
|
container.scrollTop = nextTop
|
|
209
238
|
}
|
|
210
239
|
|
|
240
|
+
const handlePageScroll = (scrollState) => {
|
|
241
|
+
scrolling(scrollState)
|
|
242
|
+
syncReadingProgress(scrollState?.position?.top)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const scrollToTop = () => {
|
|
246
|
+
navigate(0)
|
|
247
|
+
}
|
|
248
|
+
|
|
211
249
|
onMounted(() => {
|
|
212
250
|
window.addEventListener('keydown', handleMainScrollKeys)
|
|
213
251
|
window.addEventListener('resize', schedulePageMinHeightUpdate)
|
|
@@ -236,6 +274,7 @@ onBeforeUnmount(() => {
|
|
|
236
274
|
watch(() => route.fullPath, () => {
|
|
237
275
|
nextTick(() => {
|
|
238
276
|
schedulePageMinHeightUpdate()
|
|
277
|
+
syncReadingProgress(0)
|
|
239
278
|
})
|
|
240
279
|
})
|
|
241
280
|
</script>
|
|
@@ -292,10 +331,38 @@ watch(() => route.fullPath, () => {
|
|
|
292
331
|
<slot />
|
|
293
332
|
</div>
|
|
294
333
|
<d-page-meta v-if="!disableNav" />
|
|
295
|
-
<q-scroll-observer @scroll="
|
|
334
|
+
<q-scroll-observer @scroll="handlePageScroll" :debounce="300" />
|
|
296
335
|
</q-scroll-area>
|
|
297
336
|
</q-page>
|
|
298
337
|
|
|
338
|
+
<div
|
|
339
|
+
v-if="shouldShowBackToTopControl"
|
|
340
|
+
class="d-back-to-top"
|
|
341
|
+
:style="{ '--d-back-to-top-right': backToTopRightOffset }"
|
|
342
|
+
>
|
|
343
|
+
<q-circular-progress
|
|
344
|
+
class="d-back-to-top__progress"
|
|
345
|
+
:value="readingProgress.progressPercent"
|
|
346
|
+
size="58px"
|
|
347
|
+
:thickness="0.16"
|
|
348
|
+
color="primary"
|
|
349
|
+
track-color="grey-5"
|
|
350
|
+
/>
|
|
351
|
+
<q-btn
|
|
352
|
+
class="d-back-to-top__button"
|
|
353
|
+
round
|
|
354
|
+
dense
|
|
355
|
+
unelevated
|
|
356
|
+
color="dark"
|
|
357
|
+
text-color="white"
|
|
358
|
+
icon="north"
|
|
359
|
+
:aria-label="$t('system.backToTop')"
|
|
360
|
+
@click="scrollToTop"
|
|
361
|
+
>
|
|
362
|
+
<q-tooltip anchor="top middle" self="bottom middle" :offset="[10, 10]">{{ $t('system.backToTop') }}</q-tooltip>
|
|
363
|
+
</q-btn>
|
|
364
|
+
</div>
|
|
365
|
+
|
|
299
366
|
<q-drawer elevated show-if-above side="right" v-model="layoutMeta">
|
|
300
367
|
<d-page-anchor id="anchor" />
|
|
301
368
|
</q-drawer>
|
|
@@ -321,6 +388,25 @@ watch(() => route.fullPath, () => {
|
|
|
321
388
|
#page
|
|
322
389
|
min-height: var(--d-page-min-height, calc(100vh - 86px)) !important
|
|
323
390
|
|
|
391
|
+
.d-back-to-top
|
|
392
|
+
position: fixed
|
|
393
|
+
right: var(--d-back-to-top-right, 24px)
|
|
394
|
+
bottom: calc(24px + var(--d-page-bottom-inset, 0px) + env(safe-area-inset-bottom, 0px))
|
|
395
|
+
width: 58px
|
|
396
|
+
height: 58px
|
|
397
|
+
z-index: 1200
|
|
398
|
+
filter: drop-shadow(0 8px 18px rgba(0,0,0,0.2))
|
|
399
|
+
|
|
400
|
+
.d-back-to-top__progress,
|
|
401
|
+
.d-back-to-top__button
|
|
402
|
+
position: absolute
|
|
403
|
+
inset: 0
|
|
404
|
+
|
|
405
|
+
.d-back-to-top__button
|
|
406
|
+
margin: auto
|
|
407
|
+
width: 40px
|
|
408
|
+
height: 40px
|
|
409
|
+
|
|
324
410
|
#scroll-container
|
|
325
411
|
width: 100%
|
|
326
412
|
max-width: 1200px
|
|
@@ -425,4 +511,12 @@ body.mobile.body--dark
|
|
|
425
511
|
body.mobile
|
|
426
512
|
.q-drawer--right
|
|
427
513
|
background: rgba(255, 255, 255, 0.7)
|
|
514
|
+
|
|
515
|
+
body.body--light
|
|
516
|
+
.d-back-to-top__progress
|
|
517
|
+
color: var(--q-primary)
|
|
518
|
+
|
|
519
|
+
body.body--dark
|
|
520
|
+
.d-back-to-top__progress
|
|
521
|
+
color: #58d1a8
|
|
428
522
|
</style>
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import MarkdownIt from 'markdown-it'
|
|
2
2
|
import attrs from 'markdown-it-attrs'
|
|
3
|
+
import katex from 'katex'
|
|
4
|
+
import texmath from 'markdown-it-texmath'
|
|
3
5
|
|
|
4
6
|
const ALERT_MESSAGE_TYPES = new Set([
|
|
5
7
|
'note',
|
|
@@ -12,6 +14,10 @@ const ALERT_MESSAGE_TYPES = new Set([
|
|
|
12
14
|
const QUICK_LINKS_MARKER_PREFIX = '@@DOCSECTOR_QUICK_LINKS_'
|
|
13
15
|
const EXPANDABLE_MARKER_PREFIX = '@@DOCSECTOR_EXPANDABLE_'
|
|
14
16
|
const CODE_SEGMENT_MARKER_PREFIX = '@@DOCSECTOR_CODE_SEGMENT_'
|
|
17
|
+
const MATH_KATEX_OPTIONS = {
|
|
18
|
+
throwOnError: false,
|
|
19
|
+
strict: 'ignore'
|
|
20
|
+
}
|
|
15
21
|
|
|
16
22
|
const parseAlertMarker = (rawContent = '') => {
|
|
17
23
|
const match = String(rawContent).trim().match(/^\[!\s*([A-Za-z]+)\s*\]\s*(.*)$/s)
|
|
@@ -100,7 +106,7 @@ const restoreShieldedCodeSegments = (source = '', codeSegmentsMap = new Map()) =
|
|
|
100
106
|
let restored = String(source)
|
|
101
107
|
|
|
102
108
|
codeSegmentsMap.forEach((content, marker) => {
|
|
103
|
-
restored = restored.replaceAll(marker, content)
|
|
109
|
+
restored = restored.replaceAll(marker, () => content)
|
|
104
110
|
})
|
|
105
111
|
|
|
106
112
|
return restored
|
|
@@ -299,10 +305,24 @@ const pushSourceCodeToken = (tokens, element, parserState) => {
|
|
|
299
305
|
})
|
|
300
306
|
}
|
|
301
307
|
|
|
308
|
+
const installMathSupport = (markdown) => {
|
|
309
|
+
markdown.use(texmath, {
|
|
310
|
+
engine: katex,
|
|
311
|
+
delimiters: 'dollars',
|
|
312
|
+
katexOptions: MATH_KATEX_OPTIONS
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
return markdown
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const renderBlockToken = (markdown, element, env) => {
|
|
319
|
+
return markdown.renderer.render([element], markdown.options, env).trim()
|
|
320
|
+
}
|
|
321
|
+
|
|
302
322
|
const createMarkdownBlockParser = () => {
|
|
303
|
-
const markdown = new MarkdownIt({
|
|
323
|
+
const markdown = installMathSupport(new MarkdownIt({
|
|
304
324
|
html: true
|
|
305
|
-
})
|
|
325
|
+
}))
|
|
306
326
|
|
|
307
327
|
markdown.use(attrs, {
|
|
308
328
|
leftDelimiter: ':',
|
|
@@ -314,9 +334,9 @@ const createMarkdownBlockParser = () => {
|
|
|
314
334
|
}
|
|
315
335
|
|
|
316
336
|
const createMarkdownInlineParser = () => {
|
|
317
|
-
return new MarkdownIt({
|
|
337
|
+
return installMathSupport(new MarkdownIt({
|
|
318
338
|
html: true
|
|
319
|
-
})
|
|
339
|
+
}))
|
|
320
340
|
}
|
|
321
341
|
|
|
322
342
|
const normalizePageSectionSource = (source = '') => {
|
|
@@ -444,6 +464,11 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
|
|
|
444
464
|
return
|
|
445
465
|
}
|
|
446
466
|
|
|
467
|
+
if (element.type === 'math_block') {
|
|
468
|
+
blockquote.content += renderBlockToken(markdown, element, markdownEnv)
|
|
469
|
+
return
|
|
470
|
+
}
|
|
471
|
+
|
|
447
472
|
if (element.type.endsWith('_open')) {
|
|
448
473
|
appendBlockquoteTag(element, true)
|
|
449
474
|
return
|
|
@@ -519,6 +544,13 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
|
|
|
519
544
|
pushSourceCodeToken(tokens, element, parserState)
|
|
520
545
|
break
|
|
521
546
|
|
|
547
|
+
case 'math_block':
|
|
548
|
+
tokens.push({
|
|
549
|
+
tag: 'html',
|
|
550
|
+
content: renderBlockToken(markdown, element, markdownEnv)
|
|
551
|
+
})
|
|
552
|
+
break
|
|
553
|
+
|
|
522
554
|
case 'html_block':
|
|
523
555
|
tokens.push({
|
|
524
556
|
tag: 'html',
|
|
@@ -574,6 +606,9 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
|
|
|
574
606
|
case 'inline':
|
|
575
607
|
parent.content += element.content
|
|
576
608
|
break
|
|
609
|
+
case 'math_block':
|
|
610
|
+
parent.content += renderBlockToken(markdown, element, markdownEnv)
|
|
611
|
+
break
|
|
577
612
|
case 'html_inline':
|
|
578
613
|
case 'html_block':
|
|
579
614
|
parent.content += element.content
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export const DEFAULT_READING_PROGRESS_THRESHOLD = 0.12
|
|
2
|
+
|
|
3
|
+
const toFiniteNumber = (value) => {
|
|
4
|
+
const normalized = Number(value)
|
|
5
|
+
return Number.isFinite(normalized) ? normalized : 0
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const clamp = (value, min, max) => {
|
|
9
|
+
return Math.min(max, Math.max(min, value))
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getReadingProgressState({
|
|
13
|
+
scrollTop = 0,
|
|
14
|
+
scrollHeight = 0,
|
|
15
|
+
clientHeight = 0,
|
|
16
|
+
visibilityThreshold = DEFAULT_READING_PROGRESS_THRESHOLD
|
|
17
|
+
} = {}) {
|
|
18
|
+
const normalizedClientHeight = Math.max(0, toFiniteNumber(clientHeight))
|
|
19
|
+
const normalizedScrollHeight = Math.max(0, toFiniteNumber(scrollHeight))
|
|
20
|
+
const maxScrollTop = Math.max(0, normalizedScrollHeight - normalizedClientHeight)
|
|
21
|
+
|
|
22
|
+
if (maxScrollTop === 0) {
|
|
23
|
+
return {
|
|
24
|
+
hasOverflow: false,
|
|
25
|
+
maxScrollTop: 0,
|
|
26
|
+
scrollTop: 0,
|
|
27
|
+
progressPercent: 0,
|
|
28
|
+
visibleOffset: 0,
|
|
29
|
+
isVisible: false
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const normalizedScrollTop = clamp(toFiniteNumber(scrollTop), 0, maxScrollTop)
|
|
34
|
+
const normalizedThreshold = clamp(toFiniteNumber(visibilityThreshold), 0, 1)
|
|
35
|
+
const visibleOffset = Math.round(maxScrollTop * normalizedThreshold)
|
|
36
|
+
const progressPercent = Math.round((normalizedScrollTop / maxScrollTop) * 100)
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
hasOverflow: true,
|
|
40
|
+
maxScrollTop,
|
|
41
|
+
scrollTop: normalizedScrollTop,
|
|
42
|
+
progressPercent,
|
|
43
|
+
visibleOffset,
|
|
44
|
+
isVisible: normalizedScrollTop >= visibleOffset && normalizedScrollTop > 0
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/css/app.sass
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
@import 'katex/dist/katex.min.css'
|
|
2
|
+
|
|
1
3
|
/* --- Docsector Reader --- */
|
|
2
4
|
@font-face
|
|
3
5
|
font-family: "Fira Code Nerd Font"
|
|
@@ -187,6 +189,16 @@ body.body--dark
|
|
|
187
189
|
display: inline
|
|
188
190
|
line-height: 0.85em
|
|
189
191
|
|
|
192
|
+
.katex
|
|
193
|
+
color: inherit
|
|
194
|
+
max-width: 100%
|
|
195
|
+
|
|
196
|
+
.katex-display
|
|
197
|
+
max-width: 100%
|
|
198
|
+
overflow-x: auto
|
|
199
|
+
overflow-y: hidden
|
|
200
|
+
padding: 0.35rem 0.1rem
|
|
201
|
+
|
|
190
202
|
a
|
|
191
203
|
text-decoration: none
|
|
192
204
|
outline: 0
|
|
@@ -61,6 +61,25 @@ You can render Mermaid diagrams using fenced blocks with the `mermaid` language
|
|
|
61
61
|
```
|
|
62
62
|
```mermaid
|
|
63
63
|
flowchart TD
|
|
64
|
+
A[Start] --> B[End]
|
|
65
|
+
```
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Math and TeX
|
|
69
|
+
|
|
70
|
+
Docsector supports KaTeX-style math in standard Markdown flows, including paragraphs, alerts, and expandable content.
|
|
71
|
+
|
|
72
|
+
Use single dollar delimiters for inline math such as $E = mc^2$.
|
|
73
|
+
|
|
74
|
+
Use double dollar delimiters for display math:
|
|
75
|
+
|
|
76
|
+
```markdown
|
|
77
|
+
$$
|
|
78
|
+
\int_0^1 x^2 dx
|
|
79
|
+
$$
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Math delimiters remain literal inside inline code and fenced code blocks, so syntax examples can be documented without rendering the equation.
|
|
64
83
|
|
|
65
84
|
### GitHub Alerts
|
|
66
85
|
|
|
@@ -75,9 +94,6 @@ Docsector also supports GitHub-style alert blockquotes:
|
|
|
75
94
|
|
|
76
95
|
Supported types are `NOTE`, `TIP`, `IMPORTANT`, `WARNING`, and `CAUTION`.
|
|
77
96
|
Regular blockquotes (without `[!TYPE]`) continue to work as normal.
|
|
78
|
-
A[Start] --> B[End]
|
|
79
|
-
```
|
|
80
|
-
```
|
|
81
97
|
|
|
82
98
|
### Custom Attributes
|
|
83
99
|
|
|
@@ -65,6 +65,22 @@ flowchart TD
|
|
|
65
65
|
```
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
+
### Math e TeX
|
|
69
|
+
|
|
70
|
+
O Docsector suporta fórmulas com KaTeX nos fluxos normais de Markdown, incluindo parágrafos, alertas e conteúdo expansível.
|
|
71
|
+
|
|
72
|
+
Use delimitadores com um dólar para fórmulas inline, como $E = mc^2$.
|
|
73
|
+
|
|
74
|
+
Use delimitadores com dois dólares para fórmulas em bloco:
|
|
75
|
+
|
|
76
|
+
```markdown
|
|
77
|
+
$$
|
|
78
|
+
\int_0^1 x^2 dx
|
|
79
|
+
$$
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Os delimitadores matemáticos permanecem literais dentro de código inline e fenced code, então exemplos de sintaxe podem ser documentados sem renderizar a equação.
|
|
83
|
+
|
|
68
84
|
### Alertas do GitHub
|
|
69
85
|
|
|
70
86
|
O Docsector tambem suporta blockquotes de alerta no estilo do GitHub:
|
|
@@ -9,6 +9,7 @@ Every documentation page is rendered inside a `DPage` instance.
|
|
|
9
9
|
| Prop | Type | Default | Description |
|
|
10
10
|
|------|------|---------|-------------|
|
|
11
11
|
| `disableNav` | `Boolean` | `false` | Hides the DPageMeta navigation footer |
|
|
12
|
+
| `showBackToTopControl` | `Boolean` | `false` | Enables the floating back-to-top control with circular reading progress |
|
|
12
13
|
|
|
13
14
|
## Template Structure
|
|
14
15
|
|
|
@@ -46,6 +47,8 @@ DPage reads the route's `meta.subpages` configuration to determine which tabs to
|
|
|
46
47
|
|
|
47
48
|
DPage resets the scroll position on route changes via `router.beforeEach`. The scroll observer monitors vertical scroll position and updates the anchor selection via the `useNavigator` composable.
|
|
48
49
|
|
|
50
|
+
When `showBackToTopControl` is enabled, DPage also derives reading progress from the same scroll container. The floating control stays hidden at the top, appears after a small amount of scroll, shows circular progress, and returns to anchor `0` when clicked.
|
|
51
|
+
|
|
49
52
|
## Store Integration
|
|
50
53
|
|
|
51
54
|
DPage reads from and writes to several Vuex store modules:
|
|
@@ -9,6 +9,7 @@ Toda página de documentação é renderizada dentro de uma instância de `DPage
|
|
|
9
9
|
| Prop | Tipo | Padrão | Descrição |
|
|
10
10
|
|------|------|--------|-----------|
|
|
11
11
|
| `disableNav` | `Boolean` | `false` | Oculta o rodapé de navegação DPageMeta |
|
|
12
|
+
| `showBackToTopControl` | `Boolean` | `false` | Habilita o controle flutuante de voltar ao topo com progresso circular de leitura |
|
|
12
13
|
|
|
13
14
|
## Estrutura do Template
|
|
14
15
|
|
|
@@ -46,6 +47,8 @@ DPage lê a configuração `meta.subpages` da rota para determinar quais abas ex
|
|
|
46
47
|
|
|
47
48
|
DPage reseta a posição de scroll nas mudanças de rota via `router.beforeEach`. O observador de scroll monitora a posição vertical e atualiza a seleção de âncora via composable `useNavigator`.
|
|
48
49
|
|
|
50
|
+
Quando `showBackToTopControl` está habilitada, DPage também deriva o progresso de leitura a partir da mesma área de scroll. O controle flutuante fica oculto no topo, aparece após uma pequena rolagem, exibe progresso circular e retorna para a âncora `0` ao ser clicado.
|
|
51
|
+
|
|
49
52
|
## Integração com Store
|
|
50
53
|
|
|
51
54
|
DPage lê e escreve em vários módulos da Vuex store:
|
|
@@ -9,7 +9,7 @@ DSubpage generates a deterministic numeric ID from the current route path using
|
|
|
9
9
|
## Template
|
|
10
10
|
|
|
11
11
|
```html
|
|
12
|
-
<d-page>
|
|
12
|
+
<d-page show-back-to-top-control>
|
|
13
13
|
<header>
|
|
14
14
|
<d-h1 :id="0" />
|
|
15
15
|
</header>
|
|
@@ -54,3 +54,7 @@ DSubpage is automatically used by the router for all documentation pages. You do
|
|
|
54
54
|
- `DSubpage` **uses** `DPage` as its container
|
|
55
55
|
- `DPage` handles layout (scroll, toolbar, drawer)
|
|
56
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.
|
|
@@ -9,7 +9,7 @@ DSubpage gera um ID numérico determinístico a partir do caminho da rota atual
|
|
|
9
9
|
## Template
|
|
10
10
|
|
|
11
11
|
```html
|
|
12
|
-
<d-page>
|
|
12
|
+
<d-page show-back-to-top-control>
|
|
13
13
|
<header>
|
|
14
14
|
<d-h1 :id="0" />
|
|
15
15
|
</header>
|
|
@@ -54,3 +54,7 @@ DSubpage é usado automaticamente pelo roteador para todas as páginas de docume
|
|
|
54
54
|
- `DSubpage` **usa** `DPage` como container
|
|
55
55
|
- `DPage` cuida do layout (scroll, toolbar, drawer)
|
|
56
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.
|