@docsector/docsector-reader 0.12.3 → 1.0.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 CHANGED
@@ -26,6 +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
30
  - 🔌 **MCP Server** — Auto-generated [MCP](https://modelcontextprotocol.io) server at `/mcp` for AI assistant integration (Claude Desktop, VS Code, etc.)
30
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)
31
32
 
@@ -45,6 +46,8 @@ Transform Markdown content into beautiful, navigable documentation sites — wit
45
46
  - ✏️ **Edit on GitHub** — Direct links to edit pages on your repository
46
47
  - 📅 **Last Updated Date** — Automatic per-page "last updated" date from git commit history, locale-formatted
47
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
48
51
  - ⚙️ **Single Config File** — Customize branding, links, and languages via `docsector.config.js`
49
52
 
50
53
  ---
@@ -153,6 +156,56 @@ export default {
153
156
 
154
157
  ---
155
158
 
159
+ ## 🔗 Link Headers (Agent Discovery)
160
+
161
+ Docsector Reader adds homepage `Link` response headers at build time for agent discovery, following [RFC 8288](https://www.rfc-editor.org/rfc/rfc8288) and [RFC 9727](https://www.rfc-editor.org/rfc/rfc9727#section-3).
162
+
163
+ Default relations emitted on homepage (`/` and `/index.html`):
164
+
165
+ - `rel="service-doc"` → `</>`
166
+ - `rel="service-desc"` → `</mcp>` (only when `mcp` is enabled)
167
+ - `rel="describedby"` → `</llms.txt>` (only when `siteUrl` is configured, i.e. `llms.txt` is generated)
168
+
169
+ Generated in:
170
+
171
+ - `dist/spa/_headers`
172
+
173
+ ### Optional configuration
174
+
175
+ ```javascript
176
+ export default {
177
+ // ...other config
178
+
179
+ linkHeaders: {
180
+ enabled: true,
181
+ serviceDoc: '/',
182
+ serviceDesc: '/mcp',
183
+ describedBy: '/llms.txt'
184
+ }
185
+ }
186
+ ```
187
+
188
+ Set any target to `null` or `false` to disable that relation.
189
+
190
+ ### Validate
191
+
192
+ ```bash
193
+ npx docsector build
194
+ cat dist/spa/_headers
195
+ ```
196
+
197
+ Or scan discoverability:
198
+
199
+ ```bash
200
+ curl -X POST https://isitagentready.com/api/scan \
201
+ -H 'Content-Type: application/json' \
202
+ -d '{"url":"https://YOUR-SITE.com"}'
203
+ ```
204
+
205
+ Check `checks.discoverability.linkHeaders.status` equals `"pass"`.
206
+
207
+ ---
208
+
156
209
  ## �🚀 Quick Start
157
210
 
158
211
  ### 📦 Install
@@ -288,6 +341,13 @@ export default {
288
341
  editBaseUrl: 'https://github.com/org/repo/edit/main/src/pages'
289
342
  },
290
343
 
344
+ linkHeaders: {
345
+ enabled: true,
346
+ serviceDoc: '/',
347
+ serviceDesc: '/mcp',
348
+ describedBy: '/llms.txt'
349
+ },
350
+
291
351
  languages: [
292
352
  { image: '/images/flags/united-states-of-america.png', label: 'English (US)', value: 'en-US' },
293
353
  { 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 = '0.12.3'
26
+ const VERSION = '1.0.0'
27
27
 
28
28
  const HELP = `
29
29
  Docsector Reader v${VERSION}
@@ -133,6 +133,14 @@ export default {
133
133
  // toolSuffix: 'my_docs'
134
134
  // },
135
135
 
136
+ // @ Homepage Link headers for agent discovery (optional)
137
+ // linkHeaders: {
138
+ // enabled: true,
139
+ // serviceDoc: '/',
140
+ // serviceDesc: '/mcp',
141
+ // describedBy: '/llms.txt'
142
+ // },
143
+
136
144
  // @ Languages
137
145
  languages: [
138
146
  {
@@ -333,6 +341,22 @@ export default {
333
341
  }
334
342
  `
335
343
 
344
+ const TEMPLATE_HOMEPAGE_MD = `\
345
+ # Welcome to Docsector Reader
346
+
347
+ Docsector Reader is a markdown-first documentation engine.
348
+
349
+ ## Quick Links
350
+
351
+ - [Getting Started](/guide/getting-started/overview/)
352
+ - [Configuration](/guide/configuration/overview/)
353
+ - [Pages and Routing](/guide/pages-and-routing/overview/)
354
+
355
+ ## About
356
+
357
+ - Repository: [docsector/docsector-reader](https://github.com/docsector/docsector-reader)
358
+ `
359
+
336
360
  const TEMPLATE_BOOT_PAGE = `\
337
361
  <template>
338
362
  <q-page-container>
@@ -695,6 +719,18 @@ npm-debug.log*
695
719
  .thumbs.db
696
720
  `
697
721
 
722
+ const TEMPLATE_MARKDOWNLINT = `\
723
+ {
724
+ "MD013": false,
725
+ "MD033": {
726
+ "allowed_elements": [
727
+ "d-quick-links",
728
+ "d-quick-link"
729
+ ]
730
+ }
731
+ }
732
+ `
733
+
698
734
  const TEMPLATE_ROBOTS_TXT = `\
699
735
  User-agent: *
700
736
  Allow: /
@@ -814,9 +850,11 @@ Here's an overview of the project files:
814
850
  | --- | --- |
815
851
  | \`docsector.config.js\` | Branding, links, languages, and GitHub config |
816
852
  | \`quasar.config.js\` | Quasar/Vite build configuration (via factory) |
853
+ | \`.markdownlint.json\` | Markdown lint rules (allows Docsector custom tags) |
817
854
  | \`src/pages/index.js\` | Page registry — defines all documentation pages |
818
855
  | \`src/pages/boot.js\` | Boot metadata for the home page |
819
- | \`src/pages/@/\` | Special pages (Home, 404) |
856
+ | \`src/pages/Homepage.en-US.md\` | Home page content in Markdown |
857
+ | \`src/pages/404Page.vue\` | Not found page |
820
858
  | \`src/pages/guide/\` | Guide pages (Markdown files) |
821
859
  | \`src/i18n/languages/\` | Translation files (HJSON format) |
822
860
  | \`src/css/app.sass\` | Custom styles |
@@ -919,7 +957,6 @@ function initProject (name) {
919
957
  'src/i18n',
920
958
  'src/i18n/languages',
921
959
  'src/pages',
922
- 'src/pages/@',
923
960
  'src/pages/guide',
924
961
  'public',
925
962
  'public/images',
@@ -936,6 +973,7 @@ function initProject (name) {
936
973
  ['package.json', getTemplatePackageJson(name)],
937
974
  ['quasar.config.js', TEMPLATE_QUASAR_CONFIG],
938
975
  ['docsector.config.js', TEMPLATE_DOCSECTOR_CONFIG],
976
+ ['.markdownlint.json', TEMPLATE_MARKDOWNLINT],
939
977
  ['index.html', TEMPLATE_INDEX_HTML],
940
978
  ['postcss.config.cjs', TEMPLATE_POSTCSS],
941
979
  ['.gitignore', TEMPLATE_GITIGNORE],
@@ -945,8 +983,8 @@ function initProject (name) {
945
983
  ['src/i18n/languages/en-US.hjson', TEMPLATE_I18N_HJSON],
946
984
  ['src/pages/index.js', TEMPLATE_PAGES_INDEX],
947
985
  ['src/pages/boot.js', TEMPLATE_PAGES_BOOT],
948
- ['src/pages/@/BootPage.vue', TEMPLATE_BOOT_PAGE],
949
- ['src/pages/@/404Page.vue', TEMPLATE_404_PAGE],
986
+ ['src/pages/Homepage.en-US.md', TEMPLATE_HOMEPAGE_MD],
987
+ ['src/pages/404Page.vue', TEMPLATE_404_PAGE],
950
988
  ['src/pages/guide/getting-started.overview.en-US.md', TEMPLATE_GETTING_STARTED_MD]
951
989
  ]
952
990
 
@@ -964,6 +1002,7 @@ function initProject (name) {
964
1002
  console.log(` ${name}/`)
965
1003
  console.log(' ├── docsector.config.js')
966
1004
  console.log(' ├── quasar.config.js')
1005
+ console.log(' ├── .markdownlint.json')
967
1006
  console.log(' ├── package.json')
968
1007
  console.log(' ├── index.html')
969
1008
  console.log(' ├── postcss.config.cjs')
@@ -982,9 +1021,8 @@ function initProject (name) {
982
1021
  console.log(' └── pages/')
983
1022
  console.log(' ├── index.js')
984
1023
  console.log(' ├── boot.js')
985
- console.log(' ├── @/')
986
- console.log(' ├── BootPage.vue')
987
- console.log(' │ └── 404Page.vue')
1024
+ console.log(' ├── Homepage.en-US.md')
1025
+ console.log(' ├── 404Page.vue')
988
1026
  console.log(' └── guide/')
989
1027
  console.log(' └── getting-started.overview.en-US.md')
990
1028
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docsector/docsector-reader",
3
- "version": "0.12.3",
3
+ "version": "1.0.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(/&#125;/g, '}')
63
129
  .replace(/&amp;/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(normalizedSource, markdownEnv)
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>
@@ -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
@@ -44,6 +44,11 @@
44
44
  * @param {Object} [config.mcp] - MCP (Model Context Protocol) server settings
45
45
  * @param {string} config.mcp.serverName - Server name for MCP identification (e.g. 'my-docs')
46
46
  * @param {string} config.mcp.toolSuffix - Suffix for tool names (e.g. 'my_docs' → search_my_docs)
47
+ * @param {Object} [config.linkHeaders] - Homepage Link headers for agent discovery
48
+ * @param {boolean} [config.linkHeaders.enabled=true] - Enables homepage Link headers generation
49
+ * @param {string|null|false} [config.linkHeaders.serviceDoc='/'] - Target URI for rel="service-doc"
50
+ * @param {string|null|false} [config.linkHeaders.serviceDesc='/mcp'] - Target URI for rel="service-desc" (only emitted when MCP is enabled)
51
+ * @param {string|null|false} [config.linkHeaders.describedBy='/llms.txt'] - Target URI for rel="describedby" (only emitted when llms.txt is generated)
47
52
  * @returns {Object} Resolved Docsector configuration
48
53
  */
49
54
  export function createDocsector (config = {}) {
@@ -83,7 +88,15 @@ export function createDocsector (config = {}) {
83
88
 
84
89
  defaultLanguage: config.defaultLanguage || 'en-US',
85
90
 
86
- mcp: config.mcp || null
91
+ mcp: config.mcp || null,
92
+
93
+ linkHeaders: {
94
+ enabled: true,
95
+ serviceDoc: '/',
96
+ serviceDesc: '/mcp',
97
+ describedBy: '/llms.txt',
98
+ ...config.linkHeaders
99
+ }
87
100
  }
88
101
  }
89
102
 
@@ -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
@@ -665,6 +665,52 @@ function createMarkdownBuildPlugin (projectRoot) {
665
665
  }
666
666
  console.log(`\x1b[36m[docsector]\x1b[0m Added _headers rule for .md files`)
667
667
 
668
+ // Add homepage Link headers for agent discovery (RFC 8288 / RFC 9727)
669
+ const linkHeadersConfig = config.linkHeaders || {}
670
+ const linkHeadersEnabled = linkHeadersConfig.enabled !== false
671
+
672
+ if (linkHeadersEnabled) {
673
+ const homepageLinks = []
674
+
675
+ const serviceDocHref = linkHeadersConfig.serviceDoc === undefined
676
+ ? '/'
677
+ : linkHeadersConfig.serviceDoc
678
+ if (serviceDocHref) {
679
+ homepageLinks.push({ rel: 'service-doc', href: serviceDocHref })
680
+ }
681
+
682
+ const serviceDescHref = linkHeadersConfig.serviceDesc === undefined
683
+ ? '/mcp'
684
+ : linkHeadersConfig.serviceDesc
685
+ if (config.mcp && serviceDescHref) {
686
+ homepageLinks.push({ rel: 'service-desc', href: serviceDescHref })
687
+ }
688
+
689
+ const describedByHref = linkHeadersConfig.describedBy === undefined
690
+ ? '/llms.txt'
691
+ : linkHeadersConfig.describedBy
692
+ if (siteUrl && describedByHref) {
693
+ homepageLinks.push({ rel: 'describedby', href: describedByHref })
694
+ }
695
+
696
+ 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
+ const currentHeaders = readFileSync(headersPath, 'utf-8')
703
+ const hasAgentLinks = currentHeaders.includes('rel="service-doc"')
704
+ || currentHeaders.includes('rel="service-desc"')
705
+ || currentHeaders.includes('rel="describedby"')
706
+
707
+ if (!hasAgentLinks) {
708
+ writeFileSync(headersPath, currentHeaders.trimEnd() + '\n\n' + homepageRule)
709
+ console.log(`\x1b[36m[docsector]\x1b[0m Added homepage Link headers for agent discovery`)
710
+ }
711
+ }
712
+ }
713
+
668
714
  // Generate MCP server if configured
669
715
  if (config.mcp) {
670
716
  const mcpConfig = config.mcp
@@ -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('pages/@/BootPage.vue'),
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/@/404Page.vue')
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].path
16
- const secondRoutePath = routeMatched[1].path
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 !== '/') {
@@ -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 &amp; 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