@docsector/docsector-reader 3.1.0 → 3.2.1

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.
Files changed (134) hide show
  1. package/README.md +13 -2
  2. package/bin/docsector.js +1 -1
  3. package/package.json +10 -7
  4. package/src/components/DH2.vue +1 -1
  5. package/src/components/DH3.vue +1 -1
  6. package/src/components/DH4.vue +1 -1
  7. package/src/components/DH5.vue +1 -1
  8. package/src/components/DH6.vue +1 -1
  9. package/src/components/DMenu.vue +1 -1
  10. package/src/components/DPage.vue +26 -6
  11. package/src/components/DPageAnchor.vue +3 -9
  12. package/src/components/DPageMeta.vue +1 -0
  13. package/src/components/DPageTokens.vue +5 -5
  14. package/src/components/page-section-tokens.js +59 -6
  15. package/src/composables/useNavigator.js +45 -15
  16. package/src/css/app.sass +12 -0
  17. package/src/pages/404Page.vue +2 -2
  18. package/src/pages/Homepage.en-US.md +3 -3
  19. package/src/pages/Homepage.pt-BR.md +3 -3
  20. package/src/pages/guide/i18n-and-markdown.overview.en-US.md +26 -4
  21. package/src/pages/guide/i18n-and-markdown.overview.pt-BR.md +23 -1
  22. package/src/pages/guide/pages-and-routing.overview.en-US.md +11 -9
  23. package/src/pages/guide/pages-and-routing.overview.pt-BR.md +11 -9
  24. package/src/pages/guide.index.js +0 -28
  25. package/src/pages/manual/basic/branding.overview.en-US.md +27 -0
  26. package/src/pages/manual/basic/branding.overview.pt-BR.md +27 -0
  27. package/src/pages/manual/{components → basic}/d-menu.overview.en-US.md +11 -3
  28. package/src/pages/manual/{components → basic}/d-menu.overview.pt-BR.md +11 -3
  29. package/src/pages/manual/{components → basic}/d-page-anchor.overview.en-US.md +5 -3
  30. package/src/pages/manual/{components → basic}/d-page-anchor.overview.pt-BR.md +5 -3
  31. package/src/pages/manual/{components → basic}/d-page-meta.overview.en-US.md +10 -2
  32. package/src/pages/manual/{components → basic}/d-page-meta.overview.pt-BR.md +10 -2
  33. package/src/pages/manual/basic/edit-on-github.overview.en-US.md +26 -0
  34. package/src/pages/manual/basic/edit-on-github.overview.pt-BR.md +26 -0
  35. package/src/pages/manual/basic/previous-and-next.overview.en-US.md +17 -0
  36. package/src/pages/manual/basic/previous-and-next.overview.pt-BR.md +17 -0
  37. package/src/pages/manual/basic/search.overview.en-US.md +24 -0
  38. package/src/pages/manual/basic/search.overview.pt-BR.md +24 -0
  39. package/src/pages/manual/basic/translation-progress.overview.en-US.md +19 -0
  40. package/src/pages/manual/basic/translation-progress.overview.pt-BR.md +19 -0
  41. package/src/pages/manual/basic/version-switcher.overview.en-US.md +28 -0
  42. package/src/pages/manual/basic/version-switcher.overview.pt-BR.md +28 -0
  43. package/src/pages/manual/components/d-subpage.overview.en-US.md +2 -2
  44. package/src/pages/manual/components/d-subpage.overview.pt-BR.md +2 -2
  45. package/src/pages/manual/content/blocks/code-blocks.overview.en-US.md +55 -0
  46. package/src/pages/manual/content/blocks/code-blocks.overview.pt-BR.md +55 -0
  47. package/src/pages/manual/{components/d-page-source-code.showcase.en-US.md → content/blocks/code-blocks.showcase.en-US.md} +14 -12
  48. package/src/pages/manual/{components/d-page-source-code.showcase.pt-BR.md → content/blocks/code-blocks.showcase.pt-BR.md} +28 -26
  49. package/src/pages/manual/{components/d-page-expandable.overview.en-US.md → content/blocks/expandable.overview.en-US.md} +3 -12
  50. package/src/pages/manual/{components/d-page-expandable.overview.pt-BR.md → content/blocks/expandable.overview.pt-BR.md} +3 -12
  51. package/src/pages/manual/content/blocks/headings.overview.en-US.md +45 -0
  52. package/src/pages/manual/content/blocks/headings.overview.pt-BR.md +45 -0
  53. package/src/pages/manual/{components/d-headings.showcase.en-US.md → content/blocks/headings.showcase.en-US.md} +1 -1
  54. package/src/pages/manual/{components/d-headings.showcase.pt-BR.md → content/blocks/headings.showcase.pt-BR.md} +1 -1
  55. package/src/pages/manual/content/blocks/hints.overview.en-US.md +30 -0
  56. package/src/pages/manual/content/blocks/hints.overview.pt-BR.md +30 -0
  57. package/src/pages/manual/content/blocks/hints.showcase.en-US.md +30 -0
  58. package/src/pages/manual/content/blocks/hints.showcase.pt-BR.md +30 -0
  59. package/src/pages/manual/content/blocks/images.overview.en-US.md +16 -0
  60. package/src/pages/manual/content/blocks/images.overview.pt-BR.md +16 -0
  61. package/src/pages/manual/content/blocks/images.showcase.en-US.md +11 -0
  62. package/src/pages/manual/content/blocks/images.showcase.pt-BR.md +11 -0
  63. package/src/pages/manual/content/blocks/math-and-tex.overview.en-US.md +27 -0
  64. package/src/pages/manual/content/blocks/math-and-tex.overview.pt-BR.md +27 -0
  65. package/src/pages/manual/content/blocks/math-and-tex.showcase.en-US.md +14 -0
  66. package/src/pages/manual/content/blocks/math-and-tex.showcase.pt-BR.md +14 -0
  67. package/src/pages/manual/content/blocks/mermaid-diagrams.overview.en-US.md +22 -0
  68. package/src/pages/manual/content/blocks/mermaid-diagrams.overview.pt-BR.md +22 -0
  69. package/src/pages/manual/content/blocks/ordered-lists.overview.en-US.md +19 -0
  70. package/src/pages/manual/content/blocks/ordered-lists.overview.pt-BR.md +19 -0
  71. package/src/pages/manual/content/blocks/ordered-lists.showcase.en-US.md +21 -0
  72. package/src/pages/manual/content/blocks/ordered-lists.showcase.pt-BR.md +21 -0
  73. package/src/pages/manual/content/blocks/paragraphs.overview.en-US.md +19 -0
  74. package/src/pages/manual/content/blocks/paragraphs.overview.pt-BR.md +19 -0
  75. package/src/pages/manual/content/blocks/paragraphs.showcase.en-US.md +9 -0
  76. package/src/pages/manual/content/blocks/paragraphs.showcase.pt-BR.md +9 -0
  77. package/src/pages/manual/content/blocks/quick-links.overview.en-US.md +28 -0
  78. package/src/pages/manual/content/blocks/quick-links.overview.pt-BR.md +28 -0
  79. package/src/pages/manual/content/blocks/quick-links.showcase.en-US.md +34 -0
  80. package/src/pages/manual/content/blocks/quick-links.showcase.pt-BR.md +34 -0
  81. package/src/pages/manual/content/blocks/quotes.overview.en-US.md +24 -0
  82. package/src/pages/manual/content/blocks/quotes.overview.pt-BR.md +24 -0
  83. package/src/pages/manual/content/blocks/quotes.showcase.en-US.md +17 -0
  84. package/src/pages/manual/content/blocks/quotes.showcase.pt-BR.md +17 -0
  85. package/src/pages/manual/content/blocks/raw-html.overview.en-US.md +19 -0
  86. package/src/pages/manual/content/blocks/raw-html.overview.pt-BR.md +19 -0
  87. package/src/pages/manual/content/blocks/raw-html.showcase.en-US.md +12 -0
  88. package/src/pages/manual/content/blocks/raw-html.showcase.pt-BR.md +12 -0
  89. package/src/pages/manual/content/blocks/tables.overview.en-US.md +19 -0
  90. package/src/pages/manual/content/blocks/tables.overview.pt-BR.md +19 -0
  91. package/src/pages/manual/content/blocks/tables.showcase.en-US.md +17 -0
  92. package/src/pages/manual/content/blocks/tables.showcase.pt-BR.md +17 -0
  93. package/src/pages/manual/content/blocks/unordered-lists.overview.en-US.md +21 -0
  94. package/src/pages/manual/content/blocks/unordered-lists.overview.pt-BR.md +21 -0
  95. package/src/pages/manual/content/blocks/unordered-lists.showcase.en-US.md +24 -0
  96. package/src/pages/manual/content/blocks/unordered-lists.showcase.pt-BR.md +24 -0
  97. package/src/pages/manual/content/structures/books.overview.en-US.md +36 -0
  98. package/src/pages/manual/content/structures/books.overview.pt-BR.md +36 -0
  99. package/src/pages/manual/content/structures/page.overview.en-US.md +61 -0
  100. package/src/pages/manual/content/structures/page.overview.pt-BR.md +61 -0
  101. package/src/pages/manual/content/structures/subpage.overview.en-US.md +62 -0
  102. package/src/pages/manual/content/structures/subpage.overview.pt-BR.md +62 -0
  103. package/src/pages/manual.index.js +487 -153
  104. package/src/router/routes.js +1 -1
  105. package/src/pages/guide/alerts-and-blockquotes.overview.en-US.md +0 -65
  106. package/src/pages/guide/alerts-and-blockquotes.overview.pt-BR.md +0 -65
  107. package/src/pages/manual/components/d-headings.overview.en-US.md +0 -54
  108. package/src/pages/manual/components/d-headings.overview.pt-BR.md +0 -54
  109. package/src/pages/manual/components/d-mermaid-diagram.overview.en-US.md +0 -31
  110. package/src/pages/manual/components/d-mermaid-diagram.overview.pt-BR.md +0 -29
  111. package/src/pages/manual/components/d-page-blockquote.overview.en-US.md +0 -66
  112. package/src/pages/manual/components/d-page-blockquote.overview.pt-BR.md +0 -66
  113. package/src/pages/manual/components/d-page-blockquote.showcase.en-US.md +0 -34
  114. package/src/pages/manual/components/d-page-blockquote.showcase.pt-BR.md +0 -34
  115. package/src/pages/manual/components/d-page-section.overview.en-US.md +0 -57
  116. package/src/pages/manual/components/d-page-section.overview.pt-BR.md +0 -57
  117. package/src/pages/manual/components/d-page-section.showcase.en-US.md +0 -43
  118. package/src/pages/manual/components/d-page-section.showcase.pt-BR.md +0 -43
  119. package/src/pages/manual/components/d-page-source-code.overview.en-US.md +0 -68
  120. package/src/pages/manual/components/d-page-source-code.overview.pt-BR.md +0 -68
  121. package/src/pages/manual/components/d-page.overview.en-US.md +0 -59
  122. package/src/pages/manual/components/d-page.overview.pt-BR.md +0 -59
  123. package/src/pages/manual/components/d-page.showcase.en-US.md +0 -35
  124. package/src/pages/manual/components/d-page.showcase.pt-BR.md +0 -35
  125. package/src/pages/manual/components/q-zoom.overview.en-US.md +0 -71
  126. package/src/pages/manual/components/q-zoom.overview.pt-BR.md +0 -71
  127. package/src/pages/manual/composables/use-navigator.overview.en-US.md +0 -89
  128. package/src/pages/manual/composables/use-navigator.overview.pt-BR.md +0 -89
  129. package/src/pages/manual/store/modules.overview.en-US.md +0 -96
  130. package/src/pages/manual/store/modules.overview.pt-BR.md +0 -96
  131. /package/src/pages/manual/{components/d-page-expandable.showcase.en-US.md → content/blocks/expandable.showcase.en-US.md} +0 -0
  132. /package/src/pages/manual/{components/d-page-expandable.showcase.pt-BR.md → content/blocks/expandable.showcase.pt-BR.md} +0 -0
  133. /package/src/pages/manual/{components/d-mermaid-diagram.showcase.en-US.md → content/blocks/mermaid-diagrams.showcase.en-US.md} +0 -0
  134. /package/src/pages/manual/{components/d-mermaid-diagram.showcase.pt-BR.md → content/blocks/mermaid-diagrams.showcase.pt-BR.md} +0 -0
package/README.md CHANGED
@@ -43,13 +43,13 @@ 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
49
50
  - 🔗 **Anchor Navigation** — Right-side Table of Contents tree with scroll tracking and auto-scroll to active section
50
51
  - 🖱️ **Active Menu Item UX** — Active menu entries keep pointer cursor, clear URL hash without redundant navigation, and prevent accidental label text selection
51
52
  - 🔎 **Search** — Menu search across all documentation content and tags
52
- - 🌐 **WebMCP Browser Tools** — Registers in-page tools for browser agents with `registerTool` and optional `provideContext` fallback
53
53
  - 📱 **Responsive** — Mobile-friendly with collapsible sidebar and drawers
54
54
  - 📚 **Book Tabs with Per-State Colors** — Define `*.book.js` tabs with icons, order, and `color.active` / `color.inactive`
55
55
  - 🔀 **Internal Shortcut Pages** — Route entries can redirect with `config.link.to`, keeping localized titles while inheriting icon/status from the destination page
@@ -66,6 +66,7 @@ Transform Markdown content into beautiful, navigable documentation sites — wit
66
66
  - 🧭 **Content Signals** — Injects `Content-Signal` policy in `robots.txt` with deterministic, idempotent build output
67
67
  - 🏠 **Markdown Home at Root** — Homepage is rendered from `src/pages/Homepage.{lang}.md` directly at `/`
68
68
  - 🌍 **Remote README as Home** — Optional build-time remote README source for homepage with automatic local fallback
69
+ - 🔗 **GitHub-Compatible Heading Anchors** — Markdown headings use GitHub-style slugs so standard README Table of Contents links work inside Docsector
69
70
  - 🧬 **Scaffolded Homepage Override Wiring** — New consumer projects automatically wire `virtual:docsector-homepage-override` into i18n message building
70
71
  - 📖 **Expandable Markdown Sections** — Use `<d-expandable title="...">...</d-expandable>` to collapse secondary content while keeping rich Markdown support inside the body
71
72
  - 🧭 **Quick Links Custom Element** — Use `<d-quick-links>` and `<d-quick-link>` in Markdown to render rich home navigation cards
@@ -355,6 +356,7 @@ You can configure Docsector Reader to use a remote README as homepage content.
355
356
  - Fetch happens at build-time.
356
357
  - The same README content is used for all configured languages.
357
358
  - If fetch fails, it falls back to local `src/pages/Homepage.{lang}.md` by default.
359
+ - Standard GitHub-style heading links and README Table of Contents fragments keep working in the rendered homepage.
358
360
 
359
361
  ### Configure
360
362
 
@@ -855,7 +857,12 @@ my-docs/
855
857
  │ │ ├── guide.index.js # Guide page registry (routes + metadata)
856
858
  │ │ ├── boot.js # Boot page data
857
859
  │ │ ├── guide/ # Guide pages (.md files)
858
- │ │ └── manual/ # Manual pages (.md files)
860
+ │ │ └── manual/
861
+ │ │ ├── basic/ # Core UI docs exposed in the main manual nav
862
+ │ │ ├── content/
863
+ │ │ │ ├── blocks/ # User-facing Markdown block docs
864
+ │ │ │ └── structures/ # User-facing page structure docs
865
+ │ │ └── components/ # Legacy/internal engine-facing manual docs
859
866
  │ ├── i18n/
860
867
  │ │ ├── index.js # Uses buildMessages() from engine
861
868
  │ │ └── languages/ # HJSON locale files
@@ -869,6 +876,10 @@ my-docs/
869
876
  └── icons/ # PWA icons
870
877
  ```
871
878
 
879
+ A common manual pattern is to keep core UI references under `src/pages/manual/basic/` with user-friendly page titles and focused entry pages such as Search, Branding, Version Switcher, Edit on GitHub, Translation Progress, and Previous & Next, end-user content references under `src/pages/manual/content/blocks/`, structural docs under `src/pages/manual/content/structures/`, and legacy/internal engine-specific references under `src/pages/manual/components/`.
880
+
881
+ Blocks in `src/pages/manual/content/blocks/` should normally provide both `overview` and `showcase` markdown pages, while structural topics can stay overview-only when a visual demo adds little value.
882
+
872
883
  ---
873
884
 
874
885
  ## 📚 Defining Books (Tabs)
package/bin/docsector.js CHANGED
@@ -23,7 +23,7 @@ const packageRoot = resolve(__dirname, '..')
23
23
  const args = process.argv.slice(2)
24
24
  const command = args[0]
25
25
 
26
- const VERSION = '3.1.0'
26
+ const VERSION = '3.2.1'
27
27
 
28
28
  const HELP = `
29
29
  Docsector Reader v${VERSION}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docsector/docsector-reader",
3
- "version": "3.1.0",
3
+ "version": "3.2.1",
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",
@@ -69,9 +69,12 @@
69
69
  "autoprefixer": "^10.4.2",
70
70
  "axios": "^1.7.7",
71
71
  "core-js": "^3.6.5",
72
+ "github-slugger": "^2.0.0",
72
73
  "hjson": "^3.2.2",
74
+ "katex": "^0.17.0",
73
75
  "markdown-it": "^13.0.1",
74
76
  "markdown-it-attrs": "^4.1.6",
77
+ "markdown-it-texmath": "^1.0.0",
75
78
  "mermaid": "^11.0.0",
76
79
  "prismjs": "^1.27.0",
77
80
  "q-colorize-mixin": "^2.0.0-alpha.5"
@@ -86,11 +89,6 @@
86
89
  },
87
90
  "devDependencies": {
88
91
  "@quasar/extras": "^1.16.12",
89
- "quasar": "^2.16.6",
90
- "vue": "^3.5.13",
91
- "vue-i18n": "^9.0.0",
92
- "vue-router": "^4.0.0",
93
- "vuex": "^4.0.1",
94
92
  "eslint": "^8.57.0",
95
93
  "eslint-config-prettier": "^8.1.0",
96
94
  "eslint-plugin-import": "^2.19.1",
@@ -98,7 +96,12 @@
98
96
  "eslint-plugin-promise": "^6.0.0",
99
97
  "eslint-plugin-vue": "^9.0.0",
100
98
  "prettier": "^2.5.1",
101
- "vitest": "^2.1.8"
99
+ "quasar": "^2.16.6",
100
+ "vitest": "^2.1.8",
101
+ "vue": "^3.5.13",
102
+ "vue-i18n": "^9.0.0",
103
+ "vue-router": "^4.0.0",
104
+ "vuex": "^4.0.1"
102
105
  },
103
106
  "browserslist": [
104
107
  "last 10 Chrome versions",
@@ -5,7 +5,7 @@ import useNavigator from '../composables/useNavigator'
5
5
 
6
6
  const props = defineProps({
7
7
  id: {
8
- type: Number,
8
+ type: [String, Number],
9
9
  required: true
10
10
  },
11
11
  value: {
@@ -5,7 +5,7 @@ import useNavigator from '../composables/useNavigator'
5
5
 
6
6
  const props = defineProps({
7
7
  id: {
8
- type: Number,
8
+ type: [String, Number],
9
9
  required: true
10
10
  },
11
11
  value: {
@@ -5,7 +5,7 @@ import useNavigator from '../composables/useNavigator'
5
5
 
6
6
  const props = defineProps({
7
7
  id: {
8
- type: Number,
8
+ type: [String, Number],
9
9
  required: true
10
10
  },
11
11
  value: {
@@ -5,7 +5,7 @@ import useNavigator from '../composables/useNavigator'
5
5
 
6
6
  const props = defineProps({
7
7
  id: {
8
- type: Number,
8
+ type: [String, Number],
9
9
  required: true
10
10
  },
11
11
  value: {
@@ -5,7 +5,7 @@ import useNavigator from '../composables/useNavigator'
5
5
 
6
6
  const props = defineProps({
7
7
  id: {
8
- type: Number,
8
+ type: [String, Number],
9
9
  required: true
10
10
  },
11
11
  value: {
@@ -162,7 +162,7 @@ const normalizeRoutePath = (path) => {
162
162
  }
163
163
 
164
164
  const getTopRoutes = () => {
165
- return ($router.options.routes || []).slice(0, -2)
165
+ return ($router.options.routes || []).slice(0, -2).filter(route => route?.meta?.menu?.hidden !== true)
166
166
  }
167
167
 
168
168
  const routeHasSubpage = (route, subpageName) => {
@@ -150,11 +150,7 @@ const subroute = (to) => {
150
150
  }
151
151
 
152
152
  if (relative === to) {
153
- if (to !== '/showcase') {
154
- return router.push({ hash: '#0' })
155
- } else {
156
- return router.push({ hash: '#1' })
157
- }
153
+ return router.push({ hash: '#0' })
158
154
  }
159
155
 
160
156
  router.push(path)
@@ -237,6 +233,30 @@ const handleMainScrollKeys = (event) => {
237
233
  container.scrollTop = nextTop
238
234
  }
239
235
 
236
+ const handleContentAnchorClick = (event) => {
237
+ if (event.defaultPrevented || event.button !== 0 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
238
+ return
239
+ }
240
+
241
+ const target = event.target
242
+ if (!(target instanceof Element)) {
243
+ return
244
+ }
245
+
246
+ const link = target.closest('a[href]')
247
+ if (!link) {
248
+ return
249
+ }
250
+
251
+ const href = link.getAttribute('href') || ''
252
+ if (!href.startsWith('#') || href === '#') {
253
+ return
254
+ }
255
+
256
+ event.preventDefault()
257
+ navigate(href)
258
+ }
259
+
240
260
  const handlePageScroll = (scrollState) => {
241
261
  scrolling(scrollState)
242
262
  syncReadingProgress(scrollState?.position?.top)
@@ -327,7 +347,7 @@ watch(() => route.fullPath, () => {
327
347
 
328
348
  <q-page id="page">
329
349
  <q-scroll-area class="content" :class="main" ref="pageScrollArea">
330
- <div id="scroll-container">
350
+ <div id="scroll-container" @click="handleContentAnchorClick">
331
351
  <slot />
332
352
  </div>
333
353
  <d-page-meta v-if="!disableNav" />
@@ -21,19 +21,13 @@ const expanded = computed({
21
21
  get() {
22
22
  return store.getters['page/nodesExpanded']
23
23
  },
24
- set(value) {
24
+ set() {
25
25
  // console.log(value)
26
26
  }
27
27
  })
28
28
  const selected = computed({
29
29
  get() {
30
- let anchor = store.state.page.anchor
31
-
32
- if (store.state.page.relative !== '' && anchor === 0) {
33
- anchor = anchor + 1
34
- }
35
-
36
- return anchor
30
+ return store.state.page.anchor
37
31
  },
38
32
  set(value) {
39
33
  navigate(value)
@@ -90,7 +84,7 @@ onMounted(() => {
90
84
  const id = route.hash.replace(/^#+/g, '')
91
85
  if (id) {
92
86
  setTimeout(() => {
93
- anchor(id)
87
+ anchor(route.hash)
94
88
  }, 500)
95
89
  }
96
90
  })
@@ -156,6 +156,7 @@ const getVersionSiblingPath = (offset) => {
156
156
  const currentPath = normalizeRoutePath(route.matched?.[0]?.path || `/${store.state.page.base}`)
157
157
  const routes = router.options.routes
158
158
  .slice(0, -2)
159
+ .filter(item => item?.meta?.menu?.hidden !== true)
159
160
  .filter(item => !versionId || item?.meta?.version === versionId)
160
161
 
161
162
  const index = routes.findIndex(item => normalizeRoutePath(item.path) === currentPath)
@@ -30,27 +30,27 @@ import DPageExpandable from './DPageExpandable.vue'
30
30
  <template v-for="(token, index) in tokens" :key="`${token.tag}-${index}`">
31
31
  <d-h2
32
32
  v-if="token.tag === 'h2'"
33
- :id="id + token.map[0]"
33
+ :id="token.anchorId"
34
34
  :value="token.content"
35
35
  />
36
36
  <d-h3
37
37
  v-else-if="token.tag === 'h3'"
38
- :id="id + token.map[0]"
38
+ :id="token.anchorId"
39
39
  :value="token.content"
40
40
  />
41
41
  <d-h4
42
42
  v-else-if="token.tag === 'h4'"
43
- :id="id + token.map[0]"
43
+ :id="token.anchorId"
44
44
  :value="token.content"
45
45
  />
46
46
  <d-h5
47
47
  v-else-if="token.tag === 'h5'"
48
- :id="id + token.map[0]"
48
+ :id="token.anchorId"
49
49
  :value="token.content"
50
50
  />
51
51
  <d-h6
52
52
  v-else-if="token.tag === 'h6'"
53
- :id="id + token.map[0]"
53
+ :id="token.anchorId"
54
54
  :value="token.content"
55
55
  />
56
56
 
@@ -1,5 +1,8 @@
1
1
  import MarkdownIt from 'markdown-it'
2
2
  import attrs from 'markdown-it-attrs'
3
+ import GithubSlugger from 'github-slugger'
4
+ import katex from 'katex'
5
+ import texmath from 'markdown-it-texmath'
3
6
 
4
7
  const ALERT_MESSAGE_TYPES = new Set([
5
8
  'note',
@@ -12,6 +15,10 @@ const ALERT_MESSAGE_TYPES = new Set([
12
15
  const QUICK_LINKS_MARKER_PREFIX = '@@DOCSECTOR_QUICK_LINKS_'
13
16
  const EXPANDABLE_MARKER_PREFIX = '@@DOCSECTOR_EXPANDABLE_'
14
17
  const CODE_SEGMENT_MARKER_PREFIX = '@@DOCSECTOR_CODE_SEGMENT_'
18
+ const MATH_KATEX_OPTIONS = {
19
+ throwOnError: false,
20
+ strict: 'ignore'
21
+ }
15
22
 
16
23
  const parseAlertMarker = (rawContent = '') => {
17
24
  const match = String(rawContent).trim().match(/^\[!\s*([A-Za-z]+)\s*\]\s*(.*)$/s)
@@ -100,7 +107,7 @@ const restoreShieldedCodeSegments = (source = '', codeSegmentsMap = new Map()) =
100
107
  let restored = String(source)
101
108
 
102
109
  codeSegmentsMap.forEach((content, marker) => {
103
- restored = restored.replaceAll(marker, content)
110
+ restored = restored.replaceAll(marker, () => content)
104
111
  })
105
112
 
106
113
  return restored
@@ -299,10 +306,24 @@ const pushSourceCodeToken = (tokens, element, parserState) => {
299
306
  })
300
307
  }
301
308
 
309
+ const installMathSupport = (markdown) => {
310
+ markdown.use(texmath, {
311
+ engine: katex,
312
+ delimiters: 'dollars',
313
+ katexOptions: MATH_KATEX_OPTIONS
314
+ })
315
+
316
+ return markdown
317
+ }
318
+
319
+ const renderBlockToken = (markdown, element, env) => {
320
+ return markdown.renderer.render([element], markdown.options, env).trim()
321
+ }
322
+
302
323
  const createMarkdownBlockParser = () => {
303
- const markdown = new MarkdownIt({
324
+ const markdown = installMathSupport(new MarkdownIt({
304
325
  html: true
305
- })
326
+ }))
306
327
 
307
328
  markdown.use(attrs, {
308
329
  leftDelimiter: ':',
@@ -314,9 +335,9 @@ const createMarkdownBlockParser = () => {
314
335
  }
315
336
 
316
337
  const createMarkdownInlineParser = () => {
317
- return new MarkdownIt({
338
+ return installMathSupport(new MarkdownIt({
318
339
  html: true
319
- })
340
+ }))
320
341
  }
321
342
 
322
343
  const normalizePageSectionSource = (source = '') => {
@@ -326,10 +347,24 @@ const normalizePageSectionSource = (source = '') => {
326
347
  .replace(/&amp;/g, '&')
327
348
  }
328
349
 
350
+ const createParserState = () => ({
351
+ codeIndex: 0,
352
+ headingSlugger: new GithubSlugger()
353
+ })
354
+
355
+ const getHeadingAnchorId = (markdown, currentTag, element, env, parserState) => {
356
+ if (!currentTag || !currentTag.match(/^h[2-6]$/)) {
357
+ return ''
358
+ }
359
+
360
+ const headingText = markdown.renderer.renderInlineAsText(element.children || [], markdown.options, env).trim()
361
+ return parserState.headingSlugger.slug(headingText)
362
+ }
363
+
329
364
  export const tokenizePageSectionSource = (source = '', options = {}) => {
330
365
  const {
331
366
  allowHeadingTokens = true,
332
- parserState = { codeIndex: 0 }
367
+ parserState = createParserState()
333
368
  } = options
334
369
  const normalizedSource = normalizePageSectionSource(source)
335
370
  const { source: sourceWithShieldedCode, codeSegmentsMap } = shieldMarkdownCodeSegments(normalizedSource)
@@ -444,6 +479,11 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
444
479
  return
445
480
  }
446
481
 
482
+ if (element.type === 'math_block') {
483
+ blockquote.content += renderBlockToken(markdown, element, markdownEnv)
484
+ return
485
+ }
486
+
447
487
  if (element.type.endsWith('_open')) {
448
488
  appendBlockquoteTag(element, true)
449
489
  return
@@ -481,6 +521,8 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
481
521
 
482
522
  switch (element.type) {
483
523
  case 'inline':
524
+ const anchorId = getHeadingAnchorId(markdown, tag, element, markdownEnv, parserState)
525
+
484
526
  if (expandableMap.has(element.content.trim())) {
485
527
  const data = expandableMap.get(element.content.trim())
486
528
 
@@ -510,6 +552,7 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
510
552
  tokens.push({
511
553
  tag,
512
554
  map: element.map,
555
+ anchorId,
513
556
  content: element.content,
514
557
  info: element.info
515
558
  })
@@ -519,6 +562,13 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
519
562
  pushSourceCodeToken(tokens, element, parserState)
520
563
  break
521
564
 
565
+ case 'math_block':
566
+ tokens.push({
567
+ tag: 'html',
568
+ content: renderBlockToken(markdown, element, markdownEnv)
569
+ })
570
+ break
571
+
522
572
  case 'html_block':
523
573
  tokens.push({
524
574
  tag: 'html',
@@ -574,6 +624,9 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
574
624
  case 'inline':
575
625
  parent.content += element.content
576
626
  break
627
+ case 'math_block':
628
+ parent.content += renderBlockToken(markdown, element, markdownEnv)
629
+ break
577
630
  case 'html_inline':
578
631
  case 'html_block':
579
632
  parent.content += element.content
@@ -9,13 +9,34 @@ export default function useNavigator() {
9
9
  const route = useRoute()
10
10
  const selected = ref(null)
11
11
 
12
+ const normalizeDomAnchorId = (id) => {
13
+ if (id === null || id === undefined || id === false) {
14
+ return ''
15
+ }
16
+
17
+ let normalized = String(id).replace(/^#+/g, '')
18
+
19
+ try {
20
+ normalized = decodeURIComponent(normalized)
21
+ } catch {
22
+ // Keep the raw fragment when it is not valid percent-encoding.
23
+ }
24
+
25
+ return normalized
26
+ }
27
+
28
+ const normalizeStoreAnchorId = (id) => {
29
+ const normalized = normalizeDomAnchorId(id)
30
+ return normalized === '0' ? 0 : normalized
31
+ }
32
+
12
33
  const register = (id) => {
13
- store.commit('page/pushAnchors', id)
34
+ store.commit('page/pushAnchors', normalizeStoreAnchorId(id))
14
35
  }
15
36
 
16
37
  const index = (id, child = false) => {
17
38
  store.commit('page/pushNodes', {
18
- id,
39
+ id: normalizeStoreAnchorId(id),
19
40
  label: selected.value,
20
41
  child,
21
42
  children: []
@@ -23,15 +44,17 @@ export default function useNavigator() {
23
44
  }
24
45
 
25
46
  const select = (id) => {
26
- store.commit('page/setAnchor', Number(id))
27
- store.commit('page/pushNodesExpanded', Number(id))
47
+ const normalized = normalizeStoreAnchorId(id)
48
+
49
+ store.commit('page/setAnchor', normalized)
50
+ store.commit('page/pushNodesExpanded', normalized)
28
51
  }
29
52
 
30
53
  const anchor = (id, toSelect = true) => {
31
54
  store.commit('page/setScrolling', false)
32
55
 
33
- id = '' + id
34
- const Anchor = document.getElementById(id)
56
+ const anchorId = normalizeDomAnchorId(id)
57
+ const Anchor = document.getElementById(anchorId)
35
58
 
36
59
  if (Anchor !== null && typeof Anchor === 'object') {
37
60
  const ScrollTarget = scroll.getScrollTarget(Anchor)
@@ -45,7 +68,7 @@ export default function useNavigator() {
45
68
  }
46
69
 
47
70
  if (toSelect) {
48
- select(id)
71
+ select(anchorId)
49
72
  }
50
73
  }
51
74
 
@@ -60,12 +83,13 @@ export default function useNavigator() {
60
83
 
61
84
  for (let i = 0; i < anchors.length; i++) {
62
85
  const anchorId = anchors[i]
86
+ const domAnchorId = normalizeDomAnchorId(anchorId)
63
87
 
64
- if (anchorId === 0) {
88
+ if (domAnchorId === '0') {
65
89
  continue
66
90
  }
67
91
 
68
- const Anchor = document.getElementById(anchorId)
92
+ const Anchor = document.getElementById(domAnchorId)
69
93
  let AnchorOffsetTop = 20
70
94
  if (Anchor !== null && typeof Anchor === 'object') {
71
95
  AnchorOffsetTop = Anchor.offsetTop
@@ -78,24 +102,30 @@ export default function useNavigator() {
78
102
  }
79
103
 
80
104
  const navigate = (value, toAnchor = true) => {
105
+ const domAnchorId = normalizeDomAnchorId(value)
106
+ const currentRouteAnchorId = normalizeDomAnchorId(route.hash)
107
+
81
108
  if (toAnchor) {
82
- if (('#' + value) === route.hash) {
83
- anchor(value)
109
+ if (domAnchorId !== '' && domAnchorId === currentRouteAnchorId) {
110
+ anchor(domAnchorId)
84
111
  return
85
112
  } else if (value === null) {
86
- anchor(selected.value, false)
113
+ anchor(store.state.page.anchor, false)
87
114
  return
88
115
  }
89
116
  }
90
117
 
91
- router.push(route.path + '#' + value)
118
+ router.push({
119
+ path: route.path,
120
+ hash: domAnchorId === '' ? '' : `#${domAnchorId}`
121
+ })
92
122
 
93
123
  if (toAnchor) {
94
124
  if (Platform.is.desktop) {
95
- anchor(value)
125
+ anchor(domAnchorId)
96
126
  } else {
97
127
  setTimeout(() => {
98
- anchor(value)
128
+ anchor(domAnchorId)
99
129
  }, 600)
100
130
  }
101
131
  }
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
@@ -3,12 +3,12 @@
3
3
  <q-page class="row" padding>
4
4
  <q-scroll-area class="content col text-center">
5
5
  <p>
6
- <img src="sad.svg" style="width:30vw;max-width:150px;" />
6
+ <img src="/sad.svg" alt="404 illustration" style="width:30vw;max-width:150px;" />
7
7
  </p>
8
8
  <p class="text-faded">Sorry, nothing here...
9
9
  <strong>(404)</strong>
10
10
  </p>
11
- <q-btn color="secondary" style="width:200px;" @click="$router.go(-1)">Go back</q-btn>
11
+ <q-btn color="secondary" style="width:200px;" @click="$router.push('/')">Go home</q-btn>
12
12
  </q-scroll-area>
13
13
  </q-page>
14
14
  </q-page-container>
@@ -7,9 +7,9 @@ Docsector Reader is a documentation rendering engine built with Vue 3, Quasar v2
7
7
  - [Getting Started](/guide/getting-started/overview/)
8
8
  - [Configuration](/guide/configuration/overview/)
9
9
  - [Pages and Routing](/guide/pages-and-routing/overview/)
10
- - [Components](/manual/components/d-page/overview/)
11
- - [Composables](/manual/composables/use-navigator/overview/)
12
- - [Store Modules](/manual/store/modules/overview/)
10
+ - [Basic](/manual/basic/d-menu/overview/)
11
+ - [Content](/manual/content/blocks/paragraphs/overview/)
12
+ - [Structures](/manual/content/structures/books/overview/)
13
13
 
14
14
  ## About
15
15
 
@@ -7,9 +7,9 @@ Docsector Reader e um motor de documentacao construído com Vue 3, Quasar v2 e V
7
7
  - [Comecando](/guide/getting-started/overview/)
8
8
  - [Configuracao](/guide/configuration/overview/)
9
9
  - [Paginas e Rotas](/guide/pages-and-routing/overview/)
10
- - [Componentes](/manual/components/d-page/overview/)
11
- - [Composables](/manual/composables/use-navigator/overview/)
12
- - [Modulos de Store](/manual/store/modules/overview/)
10
+ - [Básico](/manual/basic/d-menu/overview/)
11
+ - [Conteúdo](/manual/content/blocks/paragraphs/overview/)
12
+ - [Estruturas](/manual/content/structures/books/overview/)
13
13
 
14
14
  ## Sobre
15
15