@docsector/docsector-reader 0.13.0 → 1.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 +24 -1
- package/bin/docsector.js +46 -8
- package/package.json +1 -1
- package/src/components/DPageBar.vue +8 -0
- package/src/components/DPageSection.vue +86 -1
- package/src/components/DQuickLinks.vue +98 -0
- package/src/i18n/helpers.js +52 -0
- package/src/index.js +13 -0
- package/src/pages/Homepage.en-US.md +17 -0
- package/src/pages/Homepage.pt-BR.md +17 -0
- package/src/quasar.factory.js +104 -10
- package/src/router/routes.js +22 -3
- package/src/store/App.js +3 -3
- package/src/pages/@/BootPage.vue +0 -106
- /package/src/pages/{@/404Page.vue → 404Page.vue} +0 -0
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ Transform Markdown content into beautiful, navigable documentation sites — wit
|
|
|
26
26
|
- 🤖 **LLM Bot Detection** — Automatically serves raw Markdown to known AI crawlers (GPTBot, ClaudeBot, PerplexityBot, GrokBot, and others)
|
|
27
27
|
- 🗺️ **Sitemap Generation** — Automatic `sitemap.xml` generation at build time with all page URLs (requires `siteUrl` in config)
|
|
28
28
|
- 🤖 **AI-Friendly robots.txt** — Scaffold includes a `robots.txt` explicitly allowing 23 AI crawlers (GPTBot, ClaudeBot, PerplexityBot, GrokBot, etc.)
|
|
29
|
-
- 🔗 **Homepage Link Headers** — Auto-generated `Link` response headers for agent discovery (`service-doc`, `service-desc`, `describedby`) per RFC 8288 / RFC 9727
|
|
29
|
+
- 🔗 **Homepage Link Headers** — Auto-generated `Link` response headers for agent discovery (`api-catalog`, `service-doc`, `service-desc`, `describedby`) per RFC 8288 / RFC 9727
|
|
30
30
|
- 🔌 **MCP Server** — Auto-generated [MCP](https://modelcontextprotocol.io) server at `/mcp` for AI assistant integration (Claude Desktop, VS Code, etc.)
|
|
31
31
|
- 📄 **llms.txt / llms-full.txt** — Auto-generated [llms.txt](https://llmstxt.org) index and full-content file for LLM discovery (requires `siteUrl` in config)
|
|
32
32
|
|
|
@@ -46,6 +46,9 @@ Transform Markdown content into beautiful, navigable documentation sites — wit
|
|
|
46
46
|
- ✏️ **Edit on GitHub** — Direct links to edit pages on your repository
|
|
47
47
|
- 📅 **Last Updated Date** — Automatic per-page "last updated" date from git commit history, locale-formatted
|
|
48
48
|
- 📊 **Translation Progress** — Automatic translation percentage based on header coverage
|
|
49
|
+
- 🏠 **Markdown Home at Root** — Homepage is rendered from `src/pages/Homepage.{lang}.md` directly at `/`
|
|
50
|
+
- 🧭 **Quick Links Custom Element** — Use `<d-quick-links>` and `<d-quick-link>` in Markdown to render rich home navigation cards
|
|
51
|
+
- 🗂️ **API Catalog Well-Known** — Auto-generates `/.well-known/api-catalog` as Linkset JSON for machine-readable API discovery
|
|
49
52
|
- ⚙️ **Single Config File** — Customize branding, links, and languages via `docsector.config.js`
|
|
50
53
|
|
|
51
54
|
---
|
|
@@ -160,6 +163,7 @@ Docsector Reader adds homepage `Link` response headers at build time for agent d
|
|
|
160
163
|
|
|
161
164
|
Default relations emitted on homepage (`/` and `/index.html`):
|
|
162
165
|
|
|
166
|
+
- `rel="api-catalog"` → `</.well-known/api-catalog>`
|
|
163
167
|
- `rel="service-doc"` → `</>`
|
|
164
168
|
- `rel="service-desc"` → `</mcp>` (only when `mcp` is enabled)
|
|
165
169
|
- `rel="describedby"` → `</llms.txt>` (only when `siteUrl` is configured, i.e. `llms.txt` is generated)
|
|
@@ -167,6 +171,7 @@ Default relations emitted on homepage (`/` and `/index.html`):
|
|
|
167
171
|
Generated in:
|
|
168
172
|
|
|
169
173
|
- `dist/spa/_headers`
|
|
174
|
+
- `dist/spa/.well-known/api-catalog` (Linkset JSON)
|
|
170
175
|
|
|
171
176
|
### Optional configuration
|
|
172
177
|
|
|
@@ -176,9 +181,19 @@ export default {
|
|
|
176
181
|
|
|
177
182
|
linkHeaders: {
|
|
178
183
|
enabled: true,
|
|
184
|
+
apiCatalog: '/.well-known/api-catalog',
|
|
179
185
|
serviceDoc: '/',
|
|
180
186
|
serviceDesc: '/mcp',
|
|
181
187
|
describedBy: '/llms.txt'
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
apiCatalog: {
|
|
191
|
+
enabled: true,
|
|
192
|
+
path: '/.well-known/api-catalog',
|
|
193
|
+
items: [
|
|
194
|
+
'/mcp',
|
|
195
|
+
'https://api.example.com/openapi.json'
|
|
196
|
+
]
|
|
182
197
|
}
|
|
183
198
|
}
|
|
184
199
|
```
|
|
@@ -190,6 +205,7 @@ Set any target to `null` or `false` to disable that relation.
|
|
|
190
205
|
```bash
|
|
191
206
|
npx docsector build
|
|
192
207
|
cat dist/spa/_headers
|
|
208
|
+
cat dist/spa/.well-known/api-catalog
|
|
193
209
|
```
|
|
194
210
|
|
|
195
211
|
Or scan discoverability:
|
|
@@ -341,11 +357,18 @@ export default {
|
|
|
341
357
|
|
|
342
358
|
linkHeaders: {
|
|
343
359
|
enabled: true,
|
|
360
|
+
apiCatalog: '/.well-known/api-catalog',
|
|
344
361
|
serviceDoc: '/',
|
|
345
362
|
serviceDesc: '/mcp',
|
|
346
363
|
describedBy: '/llms.txt'
|
|
347
364
|
},
|
|
348
365
|
|
|
366
|
+
apiCatalog: {
|
|
367
|
+
enabled: true,
|
|
368
|
+
path: '/.well-known/api-catalog',
|
|
369
|
+
items: []
|
|
370
|
+
},
|
|
371
|
+
|
|
349
372
|
languages: [
|
|
350
373
|
{ image: '/images/flags/united-states-of-america.png', label: 'English (US)', value: 'en-US' },
|
|
351
374
|
{ image: '/images/flags/brazil.png', label: 'Português (BR)', value: 'pt-BR' }
|
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 = '
|
|
26
|
+
const VERSION = '1.1.0'
|
|
27
27
|
|
|
28
28
|
const HELP = `
|
|
29
29
|
Docsector Reader v${VERSION}
|
|
@@ -136,11 +136,19 @@ export default {
|
|
|
136
136
|
// @ Homepage Link headers for agent discovery (optional)
|
|
137
137
|
// linkHeaders: {
|
|
138
138
|
// enabled: true,
|
|
139
|
+
// apiCatalog: '/.well-known/api-catalog',
|
|
139
140
|
// serviceDoc: '/',
|
|
140
141
|
// serviceDesc: '/mcp',
|
|
141
142
|
// describedBy: '/llms.txt'
|
|
142
143
|
// },
|
|
143
144
|
|
|
145
|
+
// @ API catalog artifact (RFC 9727) (optional)
|
|
146
|
+
// apiCatalog: {
|
|
147
|
+
// enabled: true,
|
|
148
|
+
// path: '/.well-known/api-catalog',
|
|
149
|
+
// items: ['/mcp']
|
|
150
|
+
// },
|
|
151
|
+
|
|
144
152
|
// @ Languages
|
|
145
153
|
languages: [
|
|
146
154
|
{
|
|
@@ -341,6 +349,22 @@ export default {
|
|
|
341
349
|
}
|
|
342
350
|
`
|
|
343
351
|
|
|
352
|
+
const TEMPLATE_HOMEPAGE_MD = `\
|
|
353
|
+
# Welcome to Docsector Reader
|
|
354
|
+
|
|
355
|
+
Docsector Reader is a markdown-first documentation engine.
|
|
356
|
+
|
|
357
|
+
## Quick Links
|
|
358
|
+
|
|
359
|
+
- [Getting Started](/guide/getting-started/overview/)
|
|
360
|
+
- [Configuration](/guide/configuration/overview/)
|
|
361
|
+
- [Pages and Routing](/guide/pages-and-routing/overview/)
|
|
362
|
+
|
|
363
|
+
## About
|
|
364
|
+
|
|
365
|
+
- Repository: [docsector/docsector-reader](https://github.com/docsector/docsector-reader)
|
|
366
|
+
`
|
|
367
|
+
|
|
344
368
|
const TEMPLATE_BOOT_PAGE = `\
|
|
345
369
|
<template>
|
|
346
370
|
<q-page-container>
|
|
@@ -703,6 +727,18 @@ npm-debug.log*
|
|
|
703
727
|
.thumbs.db
|
|
704
728
|
`
|
|
705
729
|
|
|
730
|
+
const TEMPLATE_MARKDOWNLINT = `\
|
|
731
|
+
{
|
|
732
|
+
"MD013": false,
|
|
733
|
+
"MD033": {
|
|
734
|
+
"allowed_elements": [
|
|
735
|
+
"d-quick-links",
|
|
736
|
+
"d-quick-link"
|
|
737
|
+
]
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
`
|
|
741
|
+
|
|
706
742
|
const TEMPLATE_ROBOTS_TXT = `\
|
|
707
743
|
User-agent: *
|
|
708
744
|
Allow: /
|
|
@@ -822,9 +858,11 @@ Here's an overview of the project files:
|
|
|
822
858
|
| --- | --- |
|
|
823
859
|
| \`docsector.config.js\` | Branding, links, languages, and GitHub config |
|
|
824
860
|
| \`quasar.config.js\` | Quasar/Vite build configuration (via factory) |
|
|
861
|
+
| \`.markdownlint.json\` | Markdown lint rules (allows Docsector custom tags) |
|
|
825
862
|
| \`src/pages/index.js\` | Page registry — defines all documentation pages |
|
|
826
863
|
| \`src/pages/boot.js\` | Boot metadata for the home page |
|
|
827
|
-
| \`src/pages
|
|
864
|
+
| \`src/pages/Homepage.en-US.md\` | Home page content in Markdown |
|
|
865
|
+
| \`src/pages/404Page.vue\` | Not found page |
|
|
828
866
|
| \`src/pages/guide/\` | Guide pages (Markdown files) |
|
|
829
867
|
| \`src/i18n/languages/\` | Translation files (HJSON format) |
|
|
830
868
|
| \`src/css/app.sass\` | Custom styles |
|
|
@@ -927,7 +965,6 @@ function initProject (name) {
|
|
|
927
965
|
'src/i18n',
|
|
928
966
|
'src/i18n/languages',
|
|
929
967
|
'src/pages',
|
|
930
|
-
'src/pages/@',
|
|
931
968
|
'src/pages/guide',
|
|
932
969
|
'public',
|
|
933
970
|
'public/images',
|
|
@@ -944,6 +981,7 @@ function initProject (name) {
|
|
|
944
981
|
['package.json', getTemplatePackageJson(name)],
|
|
945
982
|
['quasar.config.js', TEMPLATE_QUASAR_CONFIG],
|
|
946
983
|
['docsector.config.js', TEMPLATE_DOCSECTOR_CONFIG],
|
|
984
|
+
['.markdownlint.json', TEMPLATE_MARKDOWNLINT],
|
|
947
985
|
['index.html', TEMPLATE_INDEX_HTML],
|
|
948
986
|
['postcss.config.cjs', TEMPLATE_POSTCSS],
|
|
949
987
|
['.gitignore', TEMPLATE_GITIGNORE],
|
|
@@ -953,8 +991,8 @@ function initProject (name) {
|
|
|
953
991
|
['src/i18n/languages/en-US.hjson', TEMPLATE_I18N_HJSON],
|
|
954
992
|
['src/pages/index.js', TEMPLATE_PAGES_INDEX],
|
|
955
993
|
['src/pages/boot.js', TEMPLATE_PAGES_BOOT],
|
|
956
|
-
['src/pages
|
|
957
|
-
['src/pages
|
|
994
|
+
['src/pages/Homepage.en-US.md', TEMPLATE_HOMEPAGE_MD],
|
|
995
|
+
['src/pages/404Page.vue', TEMPLATE_404_PAGE],
|
|
958
996
|
['src/pages/guide/getting-started.overview.en-US.md', TEMPLATE_GETTING_STARTED_MD]
|
|
959
997
|
]
|
|
960
998
|
|
|
@@ -972,6 +1010,7 @@ function initProject (name) {
|
|
|
972
1010
|
console.log(` ${name}/`)
|
|
973
1011
|
console.log(' ├── docsector.config.js')
|
|
974
1012
|
console.log(' ├── quasar.config.js')
|
|
1013
|
+
console.log(' ├── .markdownlint.json')
|
|
975
1014
|
console.log(' ├── package.json')
|
|
976
1015
|
console.log(' ├── index.html')
|
|
977
1016
|
console.log(' ├── postcss.config.cjs')
|
|
@@ -990,9 +1029,8 @@ function initProject (name) {
|
|
|
990
1029
|
console.log(' └── pages/')
|
|
991
1030
|
console.log(' ├── index.js')
|
|
992
1031
|
console.log(' ├── boot.js')
|
|
993
|
-
console.log(' ├──
|
|
994
|
-
console.log('
|
|
995
|
-
console.log(' │ └── 404Page.vue')
|
|
1032
|
+
console.log(' ├── Homepage.en-US.md')
|
|
1033
|
+
console.log(' ├── 404Page.vue')
|
|
996
1034
|
console.log(' └── guide/')
|
|
997
1035
|
console.log(' └── getting-started.overview.en-US.md')
|
|
998
1036
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docsector/docsector-reader",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.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,11 +70,19 @@ const rawMarkdown = computed(() => {
|
|
|
70
70
|
})
|
|
71
71
|
|
|
72
72
|
const markdownURL = computed(() => {
|
|
73
|
+
if (store.state.page.base === 'home') {
|
|
74
|
+
return `/Homepage.${locale.value}.md`
|
|
75
|
+
}
|
|
76
|
+
|
|
73
77
|
const path = route.path.replace(/\/+$/, '')
|
|
74
78
|
return `${path}.md`
|
|
75
79
|
})
|
|
76
80
|
|
|
77
81
|
const fullMarkdownURL = computed(() => {
|
|
82
|
+
if (store.state.page.base === 'home') {
|
|
83
|
+
return `${window.location.origin}/Homepage.${locale.value}.md`
|
|
84
|
+
}
|
|
85
|
+
|
|
78
86
|
const path = route.path.replace(/\/+$/, '')
|
|
79
87
|
return `${window.location.origin}${path}.md`
|
|
80
88
|
})
|
|
@@ -13,6 +13,7 @@ import DH6 from './DH6.vue'
|
|
|
13
13
|
import DPageSourceCode from './DPageSourceCode.vue'
|
|
14
14
|
import DMermaidDiagram from './DMermaidDiagram.vue'
|
|
15
15
|
import DPageBlockquote from './DPageBlockquote.vue'
|
|
16
|
+
import DQuickLinks from './DQuickLinks.vue'
|
|
16
17
|
|
|
17
18
|
const props = defineProps({
|
|
18
19
|
id: {
|
|
@@ -49,6 +50,71 @@ const parseAlertMarker = (rawContent = '') => {
|
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
const QUICK_LINKS_MARKER_PREFIX = '@@DOCSECTOR_QUICK_LINKS_'
|
|
54
|
+
|
|
55
|
+
const parseTagAttributes = (raw = '') => {
|
|
56
|
+
const attrs = {}
|
|
57
|
+
const pattern = /(\w+)\s*=\s*"([^"]*)"|(\w+)\s*=\s*'([^']*)'/g
|
|
58
|
+
|
|
59
|
+
let match = pattern.exec(raw)
|
|
60
|
+
while (match !== null) {
|
|
61
|
+
const key = match[1] || match[3]
|
|
62
|
+
const value = match[2] || match[4] || ''
|
|
63
|
+
attrs[key] = value
|
|
64
|
+
match = pattern.exec(raw)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return attrs
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const extractQuickLinksBlocks = (source = '') => {
|
|
71
|
+
const map = new Map()
|
|
72
|
+
let index = 0
|
|
73
|
+
|
|
74
|
+
const blockPattern = /<d-quick-links\b([^>]*)>([\s\S]*?)<\/d-quick-links>/gi
|
|
75
|
+
const replaced = String(source).replace(blockPattern, (_, blockAttrsRaw, inner) => {
|
|
76
|
+
const blockAttrs = parseTagAttributes(blockAttrsRaw)
|
|
77
|
+
const items = []
|
|
78
|
+
const itemPattern = /<d-quick-link\b([^>]*)\/?\s*>/gi
|
|
79
|
+
|
|
80
|
+
let itemMatch = itemPattern.exec(inner)
|
|
81
|
+
while (itemMatch !== null) {
|
|
82
|
+
const attrs = parseTagAttributes(itemMatch[1])
|
|
83
|
+
const title = attrs.title || ''
|
|
84
|
+
const description = attrs.description || ''
|
|
85
|
+
const to = attrs.to || ''
|
|
86
|
+
const href = attrs.href || ''
|
|
87
|
+
|
|
88
|
+
if (title && description && (to || href)) {
|
|
89
|
+
items.push({
|
|
90
|
+
icon: attrs.icon || 'link',
|
|
91
|
+
title,
|
|
92
|
+
description,
|
|
93
|
+
to,
|
|
94
|
+
href
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
itemMatch = itemPattern.exec(inner)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const marker = `${QUICK_LINKS_MARKER_PREFIX}${index}@@`
|
|
102
|
+
index++
|
|
103
|
+
|
|
104
|
+
map.set(marker, {
|
|
105
|
+
title: blockAttrs.title || '',
|
|
106
|
+
items
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
return `\n${marker}\n`
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
source: replaced,
|
|
114
|
+
quickLinksMap: map
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
52
118
|
const tokenized = computed(() => {
|
|
53
119
|
const absolute = store.state.i18n.absolute
|
|
54
120
|
|
|
@@ -62,6 +128,8 @@ const tokenized = computed(() => {
|
|
|
62
128
|
.replace(/}/g, '}')
|
|
63
129
|
.replace(/&/g, '&')
|
|
64
130
|
|
|
131
|
+
const { source: sourceWithQuickLinks, quickLinksMap } = extractQuickLinksBlocks(normalizedSource)
|
|
132
|
+
|
|
65
133
|
const Markdown = new MarkdownIt()
|
|
66
134
|
Markdown.use(attrs, {
|
|
67
135
|
leftDelimiter: ':',
|
|
@@ -75,7 +143,7 @@ const tokenized = computed(() => {
|
|
|
75
143
|
|
|
76
144
|
const markdownEnv = {}
|
|
77
145
|
|
|
78
|
-
const parsed = Markdown.parse(
|
|
146
|
+
const parsed = Markdown.parse(sourceWithQuickLinks, markdownEnv)
|
|
79
147
|
|
|
80
148
|
// @ map
|
|
81
149
|
const tokens = []
|
|
@@ -213,6 +281,17 @@ const tokenized = computed(() => {
|
|
|
213
281
|
// Push
|
|
214
282
|
switch (element.type) {
|
|
215
283
|
case 'inline':
|
|
284
|
+
if (quickLinksMap.has(element.content.trim())) {
|
|
285
|
+
const data = quickLinksMap.get(element.content.trim())
|
|
286
|
+
|
|
287
|
+
tokens.push({
|
|
288
|
+
tag: 'quick-links',
|
|
289
|
+
title: data.title,
|
|
290
|
+
items: data.items
|
|
291
|
+
})
|
|
292
|
+
break
|
|
293
|
+
}
|
|
294
|
+
|
|
216
295
|
tokens.push({
|
|
217
296
|
tag,
|
|
218
297
|
map: element.map,
|
|
@@ -395,6 +474,12 @@ const tokenized = computed(() => {
|
|
|
395
474
|
v-else-if="token.tag === 'mermaid'"
|
|
396
475
|
:content="token.content"
|
|
397
476
|
/>
|
|
477
|
+
|
|
478
|
+
<d-quick-links
|
|
479
|
+
v-else-if="token.tag === 'quick-links'"
|
|
480
|
+
:title="token.title"
|
|
481
|
+
:items="token.items"
|
|
482
|
+
/>
|
|
398
483
|
</template>
|
|
399
484
|
</section>
|
|
400
485
|
</template>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const props = defineProps({
|
|
3
|
+
items: {
|
|
4
|
+
type: Array,
|
|
5
|
+
default: () => []
|
|
6
|
+
},
|
|
7
|
+
title: {
|
|
8
|
+
type: String,
|
|
9
|
+
default: ''
|
|
10
|
+
}
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
const isExternal = (item) => {
|
|
14
|
+
const href = item?.href || ''
|
|
15
|
+
return /^https?:\/\//i.test(href)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const toTarget = (item) => {
|
|
19
|
+
if (item?.to) return item.to
|
|
20
|
+
return undefined
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const hrefTarget = (item) => {
|
|
24
|
+
if (item?.href) return item.href
|
|
25
|
+
return undefined
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const relValue = (item) => {
|
|
29
|
+
if (isExternal(item)) return 'noopener noreferrer'
|
|
30
|
+
return undefined
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const targetValue = (item) => {
|
|
34
|
+
if (isExternal(item)) return '_blank'
|
|
35
|
+
return undefined
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<div class="d-quick-links">
|
|
41
|
+
<h3 v-if="title" class="d-quick-links__title">{{ title }}</h3>
|
|
42
|
+
|
|
43
|
+
<q-list bordered separator class="d-quick-links__list rounded-borders">
|
|
44
|
+
<q-item
|
|
45
|
+
v-for="(item, index) in props.items"
|
|
46
|
+
:key="index"
|
|
47
|
+
clickable
|
|
48
|
+
:to="toTarget(item)"
|
|
49
|
+
:href="hrefTarget(item)"
|
|
50
|
+
:target="targetValue(item)"
|
|
51
|
+
:rel="relValue(item)"
|
|
52
|
+
>
|
|
53
|
+
<q-item-section avatar>
|
|
54
|
+
<q-icon :name="item.icon || 'link'" color="primary" />
|
|
55
|
+
</q-item-section>
|
|
56
|
+
|
|
57
|
+
<q-item-section>
|
|
58
|
+
<q-item-label class="d-quick-links__label">{{ item.title }}</q-item-label>
|
|
59
|
+
<q-item-label caption class="d-quick-links__caption">{{ item.description }}</q-item-label>
|
|
60
|
+
</q-item-section>
|
|
61
|
+
|
|
62
|
+
<q-item-section side>
|
|
63
|
+
<q-icon name="chevron_right" />
|
|
64
|
+
</q-item-section>
|
|
65
|
+
</q-item>
|
|
66
|
+
</q-list>
|
|
67
|
+
</div>
|
|
68
|
+
</template>
|
|
69
|
+
|
|
70
|
+
<style lang="sass" scoped>
|
|
71
|
+
.d-quick-links
|
|
72
|
+
margin: 0 auto
|
|
73
|
+
|
|
74
|
+
.d-quick-links__title
|
|
75
|
+
text-align: center
|
|
76
|
+
margin: 0 0 12px
|
|
77
|
+
|
|
78
|
+
.d-quick-links__list
|
|
79
|
+
border-color: rgba(255, 255, 255, 0.16)
|
|
80
|
+
|
|
81
|
+
.q-item
|
|
82
|
+
min-height: 58px
|
|
83
|
+
|
|
84
|
+
.d-quick-links__label
|
|
85
|
+
font-weight: 700
|
|
86
|
+
|
|
87
|
+
.d-quick-links__caption
|
|
88
|
+
font-size: 0.97rem
|
|
89
|
+
color: #4d5563
|
|
90
|
+
opacity: 1
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
:global(body.body--dark) .d-quick-links__list
|
|
94
|
+
border-color: rgba(255, 255, 255, 0.16)
|
|
95
|
+
|
|
96
|
+
:global(body.body--dark) .d-quick-links__caption
|
|
97
|
+
color: rgba(255, 255, 255, 0.9)
|
|
98
|
+
</style>
|
package/src/i18n/helpers.js
CHANGED
|
@@ -144,6 +144,38 @@ export function buildMessages ({ langModules, mdModules, pages, boot, langs }) {
|
|
|
144
144
|
return source
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
function loadHomepage (lang) {
|
|
148
|
+
const key = `../pages/Homepage.${lang}.md`
|
|
149
|
+
const fallbackKey = '../pages/Homepage.en-US.md'
|
|
150
|
+
|
|
151
|
+
const content = mdModules[key] ?? mdModules[fallbackKey]
|
|
152
|
+
if (!content) {
|
|
153
|
+
console.warn(`[i18n] Missing homepage markdown: ${key}`)
|
|
154
|
+
return ''
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const source = filter(typeof content === 'string' ? content : String(content))
|
|
158
|
+
return source
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function extractHeadingFromHomepage (lang) {
|
|
162
|
+
const key = `../pages/Homepage.${lang}.md`
|
|
163
|
+
const fallbackKey = '../pages/Homepage.en-US.md'
|
|
164
|
+
|
|
165
|
+
const content = mdModules[key] ?? mdModules[fallbackKey]
|
|
166
|
+
if (!content) {
|
|
167
|
+
return ''
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const raw = typeof content === 'string' ? content : String(content)
|
|
171
|
+
const match = raw.match(/^#\s+(.+)$/m)
|
|
172
|
+
if (!match) {
|
|
173
|
+
return ''
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return match[1].trim()
|
|
177
|
+
}
|
|
178
|
+
|
|
147
179
|
// @ Iterate langs
|
|
148
180
|
for (const lang of langs) {
|
|
149
181
|
// Load HJSON language file
|
|
@@ -155,6 +187,26 @@ export function buildMessages ({ langModules, mdModules, pages, boot, langs }) {
|
|
|
155
187
|
deepMerge(i18n[lang], engineDefaults[lang])
|
|
156
188
|
}
|
|
157
189
|
|
|
190
|
+
// @ Homepage markdown in root route
|
|
191
|
+
if (i18n[lang]._ === undefined) {
|
|
192
|
+
i18n[lang]._ = {}
|
|
193
|
+
}
|
|
194
|
+
if (i18n[lang]._.home === undefined) {
|
|
195
|
+
i18n[lang]._.home = {}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const homepageHeading = extractHeadingFromHomepage(lang)
|
|
199
|
+
i18n[lang]._.home._ = homepageHeading || i18n[lang]._.home._ || i18n[lang].menu?.home || 'Home'
|
|
200
|
+
|
|
201
|
+
if (i18n[lang]._.home.overview === undefined) {
|
|
202
|
+
i18n[lang]._.home.overview = {}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const homeMeta = boot?.meta?.[lang] || boot?.meta?.['en-US'] || {}
|
|
206
|
+
i18n[lang]._.home.overview._translations = homeMeta?.overview?._translations
|
|
207
|
+
i18n[lang]._.home.overview._sections = homeMeta?.overview?._sections
|
|
208
|
+
i18n[lang]._.home.overview.source = loadHomepage(lang)
|
|
209
|
+
|
|
158
210
|
// @ Iterate pages
|
|
159
211
|
for (const [key, page] of Object.entries(pages)) {
|
|
160
212
|
const path = key.slice(1)
|
package/src/index.js
CHANGED
|
@@ -47,8 +47,13 @@
|
|
|
47
47
|
* @param {Object} [config.linkHeaders] - Homepage Link headers for agent discovery
|
|
48
48
|
* @param {boolean} [config.linkHeaders.enabled=true] - Enables homepage Link headers generation
|
|
49
49
|
* @param {string|null|false} [config.linkHeaders.serviceDoc='/'] - Target URI for rel="service-doc"
|
|
50
|
+
* @param {string|null|false} [config.linkHeaders.apiCatalog='/.well-known/api-catalog'] - Target URI for rel="api-catalog"
|
|
50
51
|
* @param {string|null|false} [config.linkHeaders.serviceDesc='/mcp'] - Target URI for rel="service-desc" (only emitted when MCP is enabled)
|
|
51
52
|
* @param {string|null|false} [config.linkHeaders.describedBy='/llms.txt'] - Target URI for rel="describedby" (only emitted when llms.txt is generated)
|
|
53
|
+
* @param {Object} [config.apiCatalog] - API catalog generation settings
|
|
54
|
+
* @param {boolean} [config.apiCatalog.enabled=true] - Enables generation of API catalog artifact
|
|
55
|
+
* @param {string} [config.apiCatalog.path='/.well-known/api-catalog'] - Output URI path for API catalog artifact
|
|
56
|
+
* @param {Array<string|{href: string}>} [config.apiCatalog.items=[]] - Additional API endpoint links to include as item relations
|
|
52
57
|
* @returns {Object} Resolved Docsector configuration
|
|
53
58
|
*/
|
|
54
59
|
export function createDocsector (config = {}) {
|
|
@@ -93,9 +98,17 @@ export function createDocsector (config = {}) {
|
|
|
93
98
|
linkHeaders: {
|
|
94
99
|
enabled: true,
|
|
95
100
|
serviceDoc: '/',
|
|
101
|
+
apiCatalog: '/.well-known/api-catalog',
|
|
96
102
|
serviceDesc: '/mcp',
|
|
97
103
|
describedBy: '/llms.txt',
|
|
98
104
|
...config.linkHeaders
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
apiCatalog: {
|
|
108
|
+
enabled: true,
|
|
109
|
+
path: '/.well-known/api-catalog',
|
|
110
|
+
items: [],
|
|
111
|
+
...config.apiCatalog
|
|
99
112
|
}
|
|
100
113
|
}
|
|
101
114
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Docsector Reader
|
|
2
|
+
|
|
3
|
+
Docsector Reader is a documentation rendering engine built with Vue 3, Quasar v2 and Vite.
|
|
4
|
+
|
|
5
|
+
## Quick Links
|
|
6
|
+
|
|
7
|
+
- [Getting Started](/guide/getting-started/overview/)
|
|
8
|
+
- [Configuration](/guide/configuration/overview/)
|
|
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/)
|
|
13
|
+
|
|
14
|
+
## About
|
|
15
|
+
|
|
16
|
+
- Repository: [docsector/docsector-reader](https://github.com/docsector/docsector-reader)
|
|
17
|
+
- Focus: markdown-first docs with i18n, menu navigation and static deployment
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Docsector Reader
|
|
2
|
+
|
|
3
|
+
Docsector Reader e um motor de documentacao construído com Vue 3, Quasar v2 e Vite.
|
|
4
|
+
|
|
5
|
+
## Links rapidos
|
|
6
|
+
|
|
7
|
+
- [Comecando](/guide/getting-started/overview/)
|
|
8
|
+
- [Configuracao](/guide/configuration/overview/)
|
|
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/)
|
|
13
|
+
|
|
14
|
+
## Sobre
|
|
15
|
+
|
|
16
|
+
- Repositorio: [docsector/docsector-reader](https://github.com/docsector/docsector-reader)
|
|
17
|
+
- Foco: docs markdown-first com i18n, menu de navegacao e deploy estatico
|
package/src/quasar.factory.js
CHANGED
|
@@ -679,6 +679,13 @@ function createMarkdownBuildPlugin (projectRoot) {
|
|
|
679
679
|
homepageLinks.push({ rel: 'service-doc', href: serviceDocHref })
|
|
680
680
|
}
|
|
681
681
|
|
|
682
|
+
const apiCatalogHref = linkHeadersConfig.apiCatalog === undefined
|
|
683
|
+
? '/.well-known/api-catalog'
|
|
684
|
+
: linkHeadersConfig.apiCatalog
|
|
685
|
+
if (apiCatalogHref) {
|
|
686
|
+
homepageLinks.push({ rel: 'api-catalog', href: apiCatalogHref })
|
|
687
|
+
}
|
|
688
|
+
|
|
682
689
|
const serviceDescHref = linkHeadersConfig.serviceDesc === undefined
|
|
683
690
|
? '/mcp'
|
|
684
691
|
: linkHeadersConfig.serviceDesc
|
|
@@ -694,19 +701,106 @@ function createMarkdownBuildPlugin (projectRoot) {
|
|
|
694
701
|
}
|
|
695
702
|
|
|
696
703
|
if (homepageLinks.length > 0) {
|
|
697
|
-
const linkLines = homepageLinks.map(({ rel, href }) => ` Link: <${href}>; rel="${rel}"`).join('\n')
|
|
698
|
-
const homepageRule = ['/','/index.html']
|
|
699
|
-
.map(path => `${path}\n${linkLines}`)
|
|
700
|
-
.join('\n\n') + '\n'
|
|
701
|
-
|
|
702
704
|
const currentHeaders = readFileSync(headersPath, 'utf-8')
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
|
|
705
|
+
const missingHomepageLinks = homepageLinks.filter(({ rel }) => !currentHeaders.includes(`rel="${rel}"`))
|
|
706
|
+
|
|
707
|
+
if (missingHomepageLinks.length > 0) {
|
|
708
|
+
const linkLines = missingHomepageLinks
|
|
709
|
+
.map(({ rel, href }) => ` Link: <${href}>; rel="${rel}"`)
|
|
710
|
+
.join('\n')
|
|
711
|
+
const homepageRule = ['/', '/index.html']
|
|
712
|
+
.map(path => `${path}\n${linkLines}`)
|
|
713
|
+
.join('\n\n') + '\n'
|
|
706
714
|
|
|
707
|
-
if (!hasAgentLinks) {
|
|
708
715
|
writeFileSync(headersPath, currentHeaders.trimEnd() + '\n\n' + homepageRule)
|
|
709
|
-
console.log(`\x1b[36m[docsector]\x1b[0m Added homepage Link headers for agent discovery`)
|
|
716
|
+
console.log(`\x1b[36m[docsector]\x1b[0m Added homepage Link headers for agent discovery (${missingHomepageLinks.length} relation(s))`)
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// Generate /.well-known/api-catalog Linkset document (RFC 9727)
|
|
721
|
+
const apiCatalogConfig = config.apiCatalog || {}
|
|
722
|
+
const apiCatalogEnabled = apiCatalogConfig.enabled !== false
|
|
723
|
+
const apiCatalogPath = (apiCatalogConfig.path || apiCatalogHref || '/.well-known/api-catalog')
|
|
724
|
+
|
|
725
|
+
const toUrl = (href) => {
|
|
726
|
+
if (!href) return null
|
|
727
|
+
if (/^https?:\/\//i.test(href)) return href
|
|
728
|
+
const normalizedHref = href.startsWith('/') ? href : `/${href}`
|
|
729
|
+
return siteUrl ? `${siteUrl}${normalizedHref}` : normalizedHref
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
const normalizeLocalPath = (href) => {
|
|
733
|
+
if (!href || /^https?:\/\//i.test(href)) return null
|
|
734
|
+
const path = href.startsWith('/') ? href.slice(1) : href
|
|
735
|
+
return path || null
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if (apiCatalogEnabled && apiCatalogPath) {
|
|
739
|
+
const catalogDistPath = normalizeLocalPath(apiCatalogPath)
|
|
740
|
+
|
|
741
|
+
if (catalogDistPath) {
|
|
742
|
+
const catalogHref = apiCatalogPath.startsWith('/') ? apiCatalogPath : `/${apiCatalogPath}`
|
|
743
|
+
const catalogEntry = {
|
|
744
|
+
anchor: toUrl(catalogHref)
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const catalogServiceDoc = toUrl(serviceDocHref)
|
|
748
|
+
if (catalogServiceDoc) {
|
|
749
|
+
catalogEntry['service-doc'] = [{ href: catalogServiceDoc }]
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const catalogServiceDesc = config.mcp ? toUrl(serviceDescHref) : null
|
|
753
|
+
if (catalogServiceDesc) {
|
|
754
|
+
catalogEntry['service-desc'] = [{ href: catalogServiceDesc }]
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
const catalogDescribedBy = siteUrl ? toUrl(describedByHref) : null
|
|
758
|
+
if (catalogDescribedBy) {
|
|
759
|
+
catalogEntry.describedby = [{ href: catalogDescribedBy }]
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
const customItems = Array.isArray(apiCatalogConfig.items)
|
|
763
|
+
? apiCatalogConfig.items
|
|
764
|
+
: []
|
|
765
|
+
const itemHrefs = new Set()
|
|
766
|
+
|
|
767
|
+
if (catalogServiceDesc) {
|
|
768
|
+
itemHrefs.add(catalogServiceDesc)
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
for (const item of customItems) {
|
|
772
|
+
if (typeof item === 'string') {
|
|
773
|
+
const href = toUrl(item)
|
|
774
|
+
if (href) itemHrefs.add(href)
|
|
775
|
+
continue
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
if (item && typeof item === 'object' && typeof item.href === 'string') {
|
|
779
|
+
const href = toUrl(item.href)
|
|
780
|
+
if (href) itemHrefs.add(href)
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (itemHrefs.size > 0) {
|
|
785
|
+
catalogEntry.item = [...itemHrefs].map(href => ({ href }))
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
const catalogDir = resolve(distDir, catalogDistPath, '..')
|
|
789
|
+
mkdirSync(catalogDir, { recursive: true })
|
|
790
|
+
writeFileSync(
|
|
791
|
+
resolve(distDir, catalogDistPath),
|
|
792
|
+
JSON.stringify({ linkset: [catalogEntry] }, null, 2) + '\n'
|
|
793
|
+
)
|
|
794
|
+
console.log(`\x1b[36m[docsector]\x1b[0m Generated ${catalogHref}`)
|
|
795
|
+
|
|
796
|
+
const headersWithLinks = readFileSync(headersPath, 'utf-8')
|
|
797
|
+
if (!headersWithLinks.includes(catalogHref)) {
|
|
798
|
+
const apiCatalogHeaders = `${catalogHref}\n Content-Type: application/linkset+json; profile=\"https://www.rfc-editor.org/info/rfc9727\"\n`
|
|
799
|
+
writeFileSync(headersPath, headersWithLinks.trimEnd() + '\n\n' + apiCatalogHeaders)
|
|
800
|
+
console.log(`\x1b[36m[docsector]\x1b[0m Added _headers rule for ${catalogHref}`)
|
|
801
|
+
}
|
|
802
|
+
} else {
|
|
803
|
+
console.warn(`\x1b[33m[docsector]\x1b[0m Skipped API catalog generation: path must be a local URI path, got \"${apiCatalogPath}\"`)
|
|
710
804
|
}
|
|
711
805
|
}
|
|
712
806
|
}
|
package/src/router/routes.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import pages from 'pages'
|
|
2
|
+
import boot from 'pages/boot'
|
|
2
3
|
|
|
3
4
|
const pagesRoutes = []
|
|
4
5
|
for (const [path, page] of Object.entries(pages)) {
|
|
@@ -59,9 +60,27 @@ const routes = [
|
|
|
59
60
|
...pagesRoutes,
|
|
60
61
|
|
|
61
62
|
{
|
|
62
|
-
path: '/',
|
|
63
|
+
path: '/home',
|
|
64
|
+
alias: '/',
|
|
63
65
|
component: () => import('layouts/DefaultLayout.vue'),
|
|
64
66
|
meta: {
|
|
67
|
+
icon: 'home',
|
|
68
|
+
menu: {},
|
|
69
|
+
status: 'done',
|
|
70
|
+
type: 'home',
|
|
71
|
+
subpages: {
|
|
72
|
+
showcase: false,
|
|
73
|
+
vs: false
|
|
74
|
+
},
|
|
75
|
+
data: {
|
|
76
|
+
'en-US': {
|
|
77
|
+
title: 'Home'
|
|
78
|
+
},
|
|
79
|
+
'pt-BR': {
|
|
80
|
+
title: 'Pagina inicial'
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
meta: boot.meta,
|
|
65
84
|
layouts: {
|
|
66
85
|
footer: false,
|
|
67
86
|
submenu: false
|
|
@@ -71,7 +90,7 @@ const routes = [
|
|
|
71
90
|
children: [
|
|
72
91
|
{
|
|
73
92
|
path: '',
|
|
74
|
-
component: () => import('
|
|
93
|
+
component: () => import('components/DSubpage.vue'),
|
|
75
94
|
meta: {
|
|
76
95
|
icon: 'home',
|
|
77
96
|
menu: 'home'
|
|
@@ -89,7 +108,7 @@ const routes = [
|
|
|
89
108
|
children: [
|
|
90
109
|
{
|
|
91
110
|
path: '',
|
|
92
|
-
component: () => import('pages
|
|
111
|
+
component: () => import('pages/404Page.vue')
|
|
93
112
|
}
|
|
94
113
|
]
|
|
95
114
|
}
|
package/src/store/App.js
CHANGED
|
@@ -12,10 +12,10 @@ export default {
|
|
|
12
12
|
commit('page/resetNodes', null, { root: true })
|
|
13
13
|
|
|
14
14
|
// Route
|
|
15
|
-
const firstRoutePath = routeMatched[0]
|
|
16
|
-
const secondRoutePath = routeMatched[1]
|
|
15
|
+
const firstRoutePath = routeMatched[0]?.path || ''
|
|
16
|
+
const secondRoutePath = routeMatched[1]?.path || ''
|
|
17
17
|
|
|
18
|
-
const base = firstRoutePath.substr(1)
|
|
18
|
+
const base = firstRoutePath === '/' ? 'home' : firstRoutePath.substr(1)
|
|
19
19
|
let relative = secondRoutePath.substr(firstRoutePath.length)
|
|
20
20
|
|
|
21
21
|
if (relative !== '/') {
|
package/src/pages/@/BootPage.vue
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<q-page-container>
|
|
3
|
-
<q-page class="content">
|
|
4
|
-
<div class="text-center q-pa-xs q-pt-md">
|
|
5
|
-
<h1>Docsector Reader</h1>
|
|
6
|
-
|
|
7
|
-
<p class="caption">
|
|
8
|
-
{{ $t('_.home.texts[0]') }}
|
|
9
|
-
<a :href="projectUrl" target="_blank">{{ projectName }}</a>!
|
|
10
|
-
</p>
|
|
11
|
-
<hr />
|
|
12
|
-
</div>
|
|
13
|
-
|
|
14
|
-
<div class="q-pa-md" style="max-width: 700px; margin: 0 auto;">
|
|
15
|
-
<h3 class="text-center q-mb-md">Quick Links</h3>
|
|
16
|
-
|
|
17
|
-
<q-list bordered separator class="rounded-borders">
|
|
18
|
-
<q-item clickable to="/guide/getting-started/overview/">
|
|
19
|
-
<q-item-section avatar>
|
|
20
|
-
<q-icon name="flag" color="primary" />
|
|
21
|
-
</q-item-section>
|
|
22
|
-
<q-item-section>
|
|
23
|
-
<q-item-label>Getting Started</q-item-label>
|
|
24
|
-
<q-item-label caption>Installation, setup, and project structure</q-item-label>
|
|
25
|
-
</q-item-section>
|
|
26
|
-
<q-item-section side>
|
|
27
|
-
<q-icon name="chevron_right" />
|
|
28
|
-
</q-item-section>
|
|
29
|
-
</q-item>
|
|
30
|
-
|
|
31
|
-
<q-item clickable to="/guide/configuration/overview/">
|
|
32
|
-
<q-item-section avatar>
|
|
33
|
-
<q-icon name="tune" color="primary" />
|
|
34
|
-
</q-item-section>
|
|
35
|
-
<q-item-section>
|
|
36
|
-
<q-item-label>Configuration</q-item-label>
|
|
37
|
-
<q-item-label caption>docsector.config.js reference</q-item-label>
|
|
38
|
-
</q-item-section>
|
|
39
|
-
<q-item-section side>
|
|
40
|
-
<q-icon name="chevron_right" />
|
|
41
|
-
</q-item-section>
|
|
42
|
-
</q-item>
|
|
43
|
-
|
|
44
|
-
<q-item clickable to="/guide/pages-and-routing/overview/">
|
|
45
|
-
<q-item-section avatar>
|
|
46
|
-
<q-icon name="route" color="primary" />
|
|
47
|
-
</q-item-section>
|
|
48
|
-
<q-item-section>
|
|
49
|
-
<q-item-label>Pages & Routing</q-item-label>
|
|
50
|
-
<q-item-label caption>Page registry and route generation</q-item-label>
|
|
51
|
-
</q-item-section>
|
|
52
|
-
<q-item-section side>
|
|
53
|
-
<q-icon name="chevron_right" />
|
|
54
|
-
</q-item-section>
|
|
55
|
-
</q-item>
|
|
56
|
-
|
|
57
|
-
<q-item clickable to="/manual/components/d-page/overview/">
|
|
58
|
-
<q-item-section avatar>
|
|
59
|
-
<q-icon name="widgets" color="secondary" />
|
|
60
|
-
</q-item-section>
|
|
61
|
-
<q-item-section>
|
|
62
|
-
<q-item-label>Components</q-item-label>
|
|
63
|
-
<q-item-label caption>DPage, DPageSection, DH1–DH6, DMenu, and more</q-item-label>
|
|
64
|
-
</q-item-section>
|
|
65
|
-
<q-item-section side>
|
|
66
|
-
<q-icon name="chevron_right" />
|
|
67
|
-
</q-item-section>
|
|
68
|
-
</q-item>
|
|
69
|
-
|
|
70
|
-
<q-item clickable to="/manual/composables/use-navigator/overview/">
|
|
71
|
-
<q-item-section avatar>
|
|
72
|
-
<q-icon name="navigation" color="accent" />
|
|
73
|
-
</q-item-section>
|
|
74
|
-
<q-item-section>
|
|
75
|
-
<q-item-label>Composables</q-item-label>
|
|
76
|
-
<q-item-label caption>useNavigator — anchor navigation and ToC</q-item-label>
|
|
77
|
-
</q-item-section>
|
|
78
|
-
<q-item-section side>
|
|
79
|
-
<q-icon name="chevron_right" />
|
|
80
|
-
</q-item-section>
|
|
81
|
-
</q-item>
|
|
82
|
-
|
|
83
|
-
<q-item clickable to="/manual/store/modules/overview/">
|
|
84
|
-
<q-item-section avatar>
|
|
85
|
-
<q-icon name="storage" color="deep-orange" />
|
|
86
|
-
</q-item-section>
|
|
87
|
-
<q-item-section>
|
|
88
|
-
<q-item-label>Vuex Store</q-item-label>
|
|
89
|
-
<q-item-label caption>App, I18n, Page, Layout, Settings modules</q-item-label>
|
|
90
|
-
</q-item-section>
|
|
91
|
-
<q-item-section side>
|
|
92
|
-
<q-icon name="chevron_right" />
|
|
93
|
-
</q-item-section>
|
|
94
|
-
</q-item>
|
|
95
|
-
</q-list>
|
|
96
|
-
</div>
|
|
97
|
-
</q-page>
|
|
98
|
-
</q-page-container>
|
|
99
|
-
</template>
|
|
100
|
-
|
|
101
|
-
<script setup>
|
|
102
|
-
import docsectorConfig from 'docsector.config.js'
|
|
103
|
-
|
|
104
|
-
const projectName = docsectorConfig.branding?.name || 'My Project'
|
|
105
|
-
const projectUrl = docsectorConfig.links?.github || '#'
|
|
106
|
-
</script>
|
|
File without changes
|