@docsector/docsector-reader 0.9.1 β†’ 0.10.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
@@ -27,6 +27,7 @@ Transform Markdown content into beautiful, navigable documentation sites β€” wit
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
29
  - πŸ”Œ **MCP Server** β€” Auto-generated [MCP](https://modelcontextprotocol.io) server at `/mcp` for AI assistant integration (Claude Desktop, VS Code, etc.)
30
+ - πŸ“„ **llms.txt / llms-full.txt** β€” Auto-generated [llms.txt](https://llmstxt.org) index and full-content file for LLM discovery (requires `siteUrl` in config)
30
31
 
31
32
  ---
32
33
 
@@ -128,7 +129,31 @@ curl -X POST http://localhost:8788/mcp \
128
129
 
129
130
  ---
130
131
 
131
- ## πŸš€ Quick Start
132
+ ## οΏ½ llms.txt (LLM Discovery)
133
+
134
+ Docsector Reader automatically generates [llms.txt](https://llmstxt.org) files at build time when `siteUrl` is configured (same requirement as sitemap.xml).
135
+
136
+ | File | Purpose |
137
+ |---|---|
138
+ | `/llms.txt` | Markdown index of all pages with links to `.md` versions, grouped by type |
139
+ | `/llms-full.txt` | Full documentation content concatenated in a single file for LLM context |
140
+
141
+ Optionally add a `description` to your branding for a richer `llms.txt` blockquote:
142
+
143
+ ```javascript
144
+ export default {
145
+ branding: {
146
+ name: 'My Project',
147
+ version: 'v1.0.0',
148
+ description: 'A framework for building awesome things'
149
+ },
150
+ siteUrl: 'https://my-docs.example.com'
151
+ }
152
+ ```
153
+
154
+ ---
155
+
156
+ ## οΏ½πŸš€ Quick Start
132
157
 
133
158
  ### πŸ“¦ Install
134
159
 
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.9.1'
26
+ const VERSION = '0.10.0'
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": "0.9.1",
3
+ "version": "0.10.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",
@@ -93,16 +93,15 @@ const MCP_PATHS = [
93
93
  'M14.485 4.703a.823.823 0 000-1.18.863.863 0 00-1.204 0l-7.119 6.982a4.115 4.115 0 000 5.9 4.314 4.314 0 006.016 0l7.12-6.982a.823.823 0 000-1.18.863.863 0 00-1.204 0l-7.119 6.982a2.588 2.588 0 01-3.61 0 2.47 2.47 0 010-3.54l7.12-6.982z'
94
94
  ]
95
95
  const VSCODE_PATH = 'M70.9119 99.3171C72.4869 99.9307 74.2828 99.8914 75.8725 99.1264L96.4608 89.2197C98.6242 88.1787 100 85.9892 100 83.5872V16.4133C100 14.0113 98.6243 11.8218 96.4609 10.7808L75.8725 0.873756C73.7862 -0.130129 71.3446 0.11576 69.5135 1.44695C69.252 1.63711 69.0028 1.84943 68.769 2.08341L29.3551 38.0415L12.1872 25.0096C10.589 23.7965 8.35363 23.8959 6.86933 25.2461L1.36303 30.2549C-0.452552 31.9064 -0.454633 34.7627 1.35853 36.417L16.2471 50.0001L1.35853 63.5832C-0.454633 65.2374 -0.452552 68.0938 1.36303 69.7453L6.86933 74.7541C8.35363 76.1043 10.589 76.2037 12.1872 74.9905L29.3551 61.9587L68.769 97.9167C69.3925 98.5406 70.1246 99.0104 70.9119 99.3171ZM75.0152 27.2989L45.1091 50.0001L75.0152 72.7012V27.2989Z'
96
- const CODEX_PATH = 'M15.672 11.249a.75.75 0 00-.006-1.5 3.504 3.504 0 01-3.26-2.26.75.75 0 00-1.392 0 3.504 3.504 0 01-3.26 2.26.75.75 0 000 1.5 3.504 3.504 0 013.258 2.252.75.75 0 001.396-.004A3.504 3.504 0 0115.672 11.249zM21.665 7.317a.75.75 0 000-1.5 5.253 5.253 0 01-4.887-3.386.75.75 0 00-1.392 0A5.253 5.253 0 0110.5 5.817a.75.75 0 000 1.5 5.253 5.253 0 014.886 3.386.75.75 0 001.392 0 5.253 5.253 0 014.887-3.386z'
97
-
98
96
  const mcpIcon = computed(() => buildIconURI(MCP_PATHS, 'evenodd'))
99
97
 
100
- function buildVSCodeIconURI () {
101
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><path fill-rule="evenodd" clip-rule="evenodd" d="${VSCODE_PATH}" fill="%23007ACC"/></svg>`
98
+ function buildVSCodeIconURI (color) {
99
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><path fill-rule="evenodd" clip-rule="evenodd" d="${VSCODE_PATH}" fill="%23${color}"/></svg>`
102
100
  return `img:data:image/svg+xml,${svg}`
103
101
  }
104
- const vscodeIcon = computed(() => buildVSCodeIconURI())
105
- const codexIcon = computed(() => buildIconURI(CODEX_PATH))
102
+ const vscodeIcon = computed(() => buildVSCodeIconURI('007ACC'))
103
+ const vscodeInsidersIcon = computed(() => buildVSCodeIconURI('24bfa5'))
104
+ const codexIcon = computed(() => buildIconURI(OPENAI_PATH))
106
105
 
107
106
  const mcpURL = computed(() => {
108
107
  if (!docsectorConfig.mcp) return null
@@ -116,6 +115,13 @@ const vscodeMcpURL = computed(() => {
116
115
  return `vscode:mcp/install?${encodeURIComponent(JSON.stringify({ name, url }))}`
117
116
  })
118
117
 
118
+ const vscodeInsidersMcpURL = computed(() => {
119
+ if (!docsectorConfig.mcp) return null
120
+ const name = docsectorConfig.mcp.serverName
121
+ const url = `${window.location.origin}/mcp`
122
+ return `vscode-insiders:mcp/install?${encodeURIComponent(JSON.stringify({ name, url }))}`
123
+ })
124
+
119
125
  const claudeCodeCommand = computed(() => {
120
126
  if (!docsectorConfig.mcp) return null
121
127
  const name = docsectorConfig.mcp.serverName
@@ -246,6 +252,19 @@ const copyPage = () => {
246
252
  </q-item-section>
247
253
  </q-item>
248
254
 
255
+ <q-item clickable v-close-popup :href="vscodeInsidersMcpURL" class="q-py-sm">
256
+ <q-item-section avatar>
257
+ <q-icon :name="vscodeInsidersIcon" size="xs" />
258
+ </q-item-section>
259
+ <q-item-section>
260
+ <q-item-label>{{ t('page.connectVSCodeInsiders') }}</q-item-label>
261
+ <q-item-label caption>{{ t('page.connectVSCodeInsidersCaption') }}</q-item-label>
262
+ </q-item-section>
263
+ <q-item-section side>
264
+ <q-icon name="open_in_new" size="xs" />
265
+ </q-item-section>
266
+ </q-item>
267
+
249
268
  <q-item clickable v-close-popup @click="copyMcpCommand(claudeCodeCommand, 'claude')" class="q-py-sm">
250
269
  <q-item-section avatar>
251
270
  <q-icon :name="claudeIcon" size="xs" />
@@ -38,6 +38,8 @@ const engineDefaults = {
38
38
  mcpServerCaption: 'Connect AI assistants via MCP',
39
39
  connectVSCode: 'Connect to VSCode',
40
40
  connectVSCodeCaption: 'Use this MCP in VSCode',
41
+ connectVSCodeInsiders: 'Connect to VSCode Insiders',
42
+ connectVSCodeInsidersCaption: 'Use this MCP in VSCode Insiders',
41
43
  connectClaudeCode: 'Connect to Claude Code',
42
44
  connectClaudeCodeCaption: 'Use this MCP in Claude Code',
43
45
  connectCodex: 'Connect to Codex',
@@ -60,6 +62,8 @@ const engineDefaults = {
60
62
  mcpServerCaption: 'Conecte assistentes de IA via MCP',
61
63
  connectVSCode: 'Conectar ao VSCode',
62
64
  connectVSCodeCaption: 'Use este MCP no VSCode',
65
+ connectVSCodeInsiders: 'Conectar ao VSCode Insiders',
66
+ connectVSCodeInsidersCaption: 'Use este MCP no VSCode Insiders',
63
67
  connectClaudeCode: 'Conectar ao Claude Code',
64
68
  connectClaudeCodeCaption: 'Use este MCP no Claude Code',
65
69
  connectCodex: 'Conectar ao Codex',
package/src/index.js CHANGED
@@ -26,6 +26,7 @@
26
26
  * @param {string} config.branding.logo - Logo image path (relative to public/)
27
27
  * @param {string} config.branding.name - Project name displayed in sidebar
28
28
  * @param {string} config.branding.version - Version label
29
+ * @param {string} [config.branding.description] - Project description (used in llms.txt)
29
30
  * @param {string[]} [config.branding.versions] - Available versions for dropdown
30
31
  * @param {Object} config.links - External links
31
32
  * @param {string} [config.links.github] - GitHub repository URL
@@ -467,6 +467,63 @@ function createMarkdownBuildPlugin (projectRoot) {
467
467
  const sitemap = `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${urls}</urlset>\n`
468
468
  writeFileSync(resolve(distDir, 'sitemap.xml'), sitemap)
469
469
  console.log(`\x1b[36m[docsector]\x1b[0m Generated sitemap.xml`)
470
+
471
+ // Generate llms.txt and llms-full.txt (LLM-friendly page index and full content)
472
+ const brandingName = config.branding?.name || 'Documentation'
473
+ const brandingVersion = config.branding?.version || ''
474
+ const brandingDesc = config.branding?.description || `${brandingName} documentation`
475
+
476
+ let llmsIndex = `# ${brandingName}${brandingVersion ? ' ' + brandingVersion : ''}\n\n> ${brandingDesc}\n\n`
477
+ let llmsFull = `# ${brandingName}${brandingVersion ? ' ' + brandingVersion : ''}\n\n> ${brandingDesc}\n\n---\n\n`
478
+
479
+ const llmsSections = {}
480
+
481
+ for (const [pagePath, page] of Object.entries(pages)) {
482
+ if (page.config === null) continue
483
+ if (page.config.status === 'empty') continue
484
+
485
+ const type = page.config.type ?? 'manual'
486
+ const title = page.data?.['*']?.title
487
+ || page.data?.[defaultLang]?.title
488
+ || page.data?.['en-US']?.title
489
+ || pagePath.split('/').pop()
490
+ || pagePath
491
+
492
+ const subpages = ['overview']
493
+ if (page.config.subpages?.showcase) subpages.push('showcase')
494
+ if (page.config.subpages?.vs) subpages.push('vs')
495
+
496
+ for (const subpage of subpages) {
497
+ const srcFile = resolve(pagesDir, `${type}${pagePath}.${subpage}.${defaultLang}.md`)
498
+ if (!existsSync(srcFile)) continue
499
+
500
+ const routePath = `${type}${pagePath}/${subpage}`
501
+ const mdUrl = `${siteUrl}/${routePath}.md`
502
+ const pageUrl = `${siteUrl}/${routePath}`
503
+
504
+ const desc = page.config.meta?.description
505
+ const descText = typeof desc === 'object' ? (desc[defaultLang] || desc['en-US'] || '') : (desc || '')
506
+
507
+ if (!llmsSections[type]) llmsSections[type] = []
508
+ llmsSections[type].push(
509
+ descText
510
+ ? `- [${title}](${mdUrl}): ${descText}`
511
+ : `- [${title}](${mdUrl})`
512
+ )
513
+
514
+ const content = readFileSync(srcFile, 'utf-8')
515
+ llmsFull += `## ${title}\n\nSource: ${pageUrl}\n\n${content}\n\n---\n\n`
516
+ }
517
+ }
518
+
519
+ for (const [section, entries] of Object.entries(llmsSections)) {
520
+ const sectionName = section.charAt(0).toUpperCase() + section.slice(1)
521
+ llmsIndex += `## ${sectionName}\n\n${entries.join('\n')}\n\n`
522
+ }
523
+
524
+ writeFileSync(resolve(distDir, 'llms.txt'), llmsIndex.trimEnd() + '\n')
525
+ writeFileSync(resolve(distDir, 'llms-full.txt'), llmsFull.trimEnd() + '\n')
526
+ console.log(`\x1b[36m[docsector]\x1b[0m Generated llms.txt and llms-full.txt`)
470
527
  }
471
528
 
472
529
  // Generate _headers file for Cloudflare Pages (append if exists)