@docsector/docsector-reader 2.0.7 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -7
- package/bin/docsector.js +28 -7
- package/docsector.config.js +13 -1
- package/package.json +1 -1
- package/src/components/DMenu.vue +241 -12
- package/src/components/DMenuItem.vue +25 -2
- package/src/components/DPageBar.vue +35 -5
- package/src/components/DPageMeta.vue +40 -26
- package/src/i18n/helpers.js +84 -18
- package/src/i18n/index.js +5 -3
- package/src/i18n/languages/en-US.hjson +14 -0
- package/src/i18n/languages/pt-BR.hjson +14 -0
- package/src/i18n/path.js +15 -2
- package/src/index.js +2 -2
- package/src/layouts/DefaultLayout.vue +16 -2
- package/src/pages/.old/v0.x/guide/getting-started.overview.en-US.md +7 -0
- package/src/pages/.old/v0.x/guide/getting-started.overview.pt-BR.md +7 -0
- package/src/pages/.old/v0.x/guide.book.js +12 -0
- package/src/pages/.old/v0.x/guide.index.js +28 -0
- package/src/pages/guide/configuration.overview.en-US.md +13 -2
- package/src/pages/guide/configuration.overview.pt-BR.md +13 -2
- package/src/pages/guide/pages-and-routing.overview.en-US.md +6 -2
- package/src/pages/guide/pages-and-routing.overview.pt-BR.md +6 -2
- package/src/pages/guide.index.js +3 -1
- package/src/pages/manual/components/d-menu.overview.en-US.md +6 -2
- package/src/pages/manual/components/d-menu.overview.pt-BR.md +6 -2
- package/src/pages/manual/components/d-page-meta.overview.en-US.md +1 -0
- package/src/pages/manual/components/d-page-meta.overview.pt-BR.md +1 -0
- package/src/pages/manual.index.js +2 -1
- package/src/quasar.factory.js +648 -91
- package/src/router/routes.js +129 -95
- package/src/store/App.js +15 -5
|
@@ -20,7 +20,17 @@ function normalizeEditBaseUrl (url = '') {
|
|
|
20
20
|
|
|
21
21
|
const base = normalizeEditBaseUrl(docsectorConfig.github?.editBaseUrl || '')
|
|
22
22
|
|
|
23
|
+
function getCurrentSubpageName () {
|
|
24
|
+
const relative = store.state.page.relative || '/overview'
|
|
25
|
+
return String(relative).replace(/^\/+|\/+$/g, '') || 'overview'
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
function routePathToSourcePath (path = '') {
|
|
29
|
+
const sourcePathBase = route.matched?.[0]?.meta?.sourcePathBase
|
|
30
|
+
if (typeof sourcePathBase === 'string' && sourcePathBase.length > 0) {
|
|
31
|
+
return `/${sourcePathBase}.${getCurrentSubpageName()}`
|
|
32
|
+
}
|
|
33
|
+
|
|
24
34
|
const cleanPath = String(path)
|
|
25
35
|
.replace(/\/index\.html$/, '')
|
|
26
36
|
.replace(/\/+$/, '')
|
|
@@ -44,7 +54,7 @@ const URL = computed(() => {
|
|
|
44
54
|
return `${base}${path}.${locale.value}.md`
|
|
45
55
|
})
|
|
46
56
|
const color = computed(() => {
|
|
47
|
-
if (status.value === 'done') {
|
|
57
|
+
if (status.value === 'done' || status.value === 'new') {
|
|
48
58
|
return 'white'
|
|
49
59
|
} else if (status.value === 'draft') {
|
|
50
60
|
return 'warning'
|
|
@@ -53,7 +63,7 @@ const color = computed(() => {
|
|
|
53
63
|
}
|
|
54
64
|
})
|
|
55
65
|
const icon = computed(() => {
|
|
56
|
-
if (status.value === 'done') {
|
|
66
|
+
if (status.value === 'done' || status.value === 'new') {
|
|
57
67
|
return 'edit'
|
|
58
68
|
} else if (status.value === 'draft') {
|
|
59
69
|
return 'border_color'
|
|
@@ -131,34 +141,38 @@ const languages = computed(() => {
|
|
|
131
141
|
return `${i18nLocalesAvailable}/${i18nLocales.length}`
|
|
132
142
|
})
|
|
133
143
|
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
for (let i = 0; i < routes.length; i++) {
|
|
139
|
-
if ('/' + base === routes[i].path) {
|
|
140
|
-
if (i > 0) {
|
|
141
|
-
return routes[i - 1].path
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
+
const normalizeRoutePath = (path) => {
|
|
145
|
+
const normalized = String(path || '').trim()
|
|
146
|
+
if (normalized === '' || normalized === '/') {
|
|
147
|
+
return '/'
|
|
144
148
|
}
|
|
145
149
|
|
|
146
|
-
|
|
150
|
+
const sanitized = normalized.replace(/\/+$/, '')
|
|
151
|
+
return sanitized === '' ? '/' : sanitized
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const getVersionSiblingPath = (offset) => {
|
|
155
|
+
const versionId = route.matched?.[0]?.meta?.version ?? null
|
|
156
|
+
const currentPath = normalizeRoutePath(route.matched?.[0]?.path || `/${store.state.page.base}`)
|
|
157
|
+
const routes = router.options.routes
|
|
158
|
+
.slice(0, -2)
|
|
159
|
+
.filter(item => !versionId || item?.meta?.version === versionId)
|
|
160
|
+
|
|
161
|
+
const index = routes.findIndex(item => normalizeRoutePath(item.path) === currentPath)
|
|
162
|
+
if (index < 0) return ''
|
|
163
|
+
|
|
164
|
+
const sibling = routes[index + offset]
|
|
165
|
+
if (!sibling) return ''
|
|
166
|
+
|
|
167
|
+
return sibling.path
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const prev = computed(() => {
|
|
171
|
+
return getVersionSiblingPath(-1)
|
|
147
172
|
})
|
|
148
173
|
|
|
149
174
|
const next = computed(() => {
|
|
150
|
-
|
|
151
|
-
const routes = router.options.routes.slice(0, -2)
|
|
152
|
-
|
|
153
|
-
for (let i = 0; i < routes.length; i++) {
|
|
154
|
-
if ('/' + base === routes[i].path) {
|
|
155
|
-
if (typeof routes[i + 1] !== 'undefined') {
|
|
156
|
-
return routes[i + 1].path
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return ''
|
|
175
|
+
return getVersionSiblingPath(1)
|
|
162
176
|
})
|
|
163
177
|
|
|
164
178
|
const hideRemoteHomeFooterMeta = computed(() => {
|
|
@@ -180,7 +194,7 @@ const getRouteTitle = (path) => {
|
|
|
180
194
|
<div id="d-page-edit" class="col">
|
|
181
195
|
<q-btn dense no-caps text-color="black" :color="color" @click="openURL(URL)" aria-label="Edit page on Github">
|
|
182
196
|
<q-icon class="q-mr-xs" name="fab fa-github" size="20px" />
|
|
183
|
-
<span class="hm" v-if="status === 'done'">{{ $t('page.edit.github.edit') }}</span>
|
|
197
|
+
<span class="hm" v-if="status === 'done' || status === 'new'">{{ $t('page.edit.github.edit') }}</span>
|
|
184
198
|
<span class="hm" v-else-if="status === 'draft'">{{ $t('page.edit.github.complete') }}</span>
|
|
185
199
|
<span class="hm" v-else-if="status === 'empty'">{{ $t('page.edit.github.start') }}</span>
|
|
186
200
|
</q-btn>
|
package/src/i18n/helpers.js
CHANGED
|
@@ -25,6 +25,7 @@ const engineDefaults = {
|
|
|
25
25
|
'en-US': {
|
|
26
26
|
page: {
|
|
27
27
|
lastUpdated: 'Last updated',
|
|
28
|
+
newVersion: 'New in',
|
|
28
29
|
copyPage: 'Copy page',
|
|
29
30
|
copyPageCaption: 'Copy page as Markdown for LLMs',
|
|
30
31
|
copied: 'Copied!',
|
|
@@ -44,11 +45,36 @@ const engineDefaults = {
|
|
|
44
45
|
connectClaudeCodeCaption: 'Use this MCP in Claude Code',
|
|
45
46
|
connectCodex: 'Connect to Codex',
|
|
46
47
|
connectCodexCaption: 'Use this MCP in Codex'
|
|
48
|
+
},
|
|
49
|
+
menu: {
|
|
50
|
+
status: {
|
|
51
|
+
empty: {
|
|
52
|
+
_: 'empty',
|
|
53
|
+
tooltip: 'This page is empty!'
|
|
54
|
+
},
|
|
55
|
+
draft: {
|
|
56
|
+
_: 'draft',
|
|
57
|
+
tooltip: 'This page is under construction.'
|
|
58
|
+
},
|
|
59
|
+
new: {
|
|
60
|
+
_: 'new',
|
|
61
|
+
tooltip: 'This page is new.',
|
|
62
|
+
tooltipVersion: 'New in {version}'
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
version: {
|
|
66
|
+
status: {
|
|
67
|
+
released: 'released',
|
|
68
|
+
draft: 'draft',
|
|
69
|
+
deprecated: 'deprecated'
|
|
70
|
+
}
|
|
71
|
+
}
|
|
47
72
|
}
|
|
48
73
|
},
|
|
49
74
|
'pt-BR': {
|
|
50
75
|
page: {
|
|
51
76
|
lastUpdated: 'Última atualização',
|
|
77
|
+
newVersion: 'Novo em',
|
|
52
78
|
copyPage: 'Copiar página',
|
|
53
79
|
copyPageCaption: 'Copiar página como Markdown para LLMs',
|
|
54
80
|
copied: 'Copiado!',
|
|
@@ -68,6 +94,30 @@ const engineDefaults = {
|
|
|
68
94
|
connectClaudeCodeCaption: 'Use este MCP no Claude Code',
|
|
69
95
|
connectCodex: 'Conectar ao Codex',
|
|
70
96
|
connectCodexCaption: 'Use este MCP no Codex'
|
|
97
|
+
},
|
|
98
|
+
menu: {
|
|
99
|
+
status: {
|
|
100
|
+
empty: {
|
|
101
|
+
_: 'vazia',
|
|
102
|
+
tooltip: 'Esta página está vazia!'
|
|
103
|
+
},
|
|
104
|
+
draft: {
|
|
105
|
+
_: 'rascunho',
|
|
106
|
+
tooltip: 'Esta página está em construção.'
|
|
107
|
+
},
|
|
108
|
+
new: {
|
|
109
|
+
_: 'novo',
|
|
110
|
+
tooltip: 'Esta página é nova.',
|
|
111
|
+
tooltipVersion: 'Novo na {version}'
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
version: {
|
|
115
|
+
status: {
|
|
116
|
+
released: 'publicada',
|
|
117
|
+
draft: 'rascunho',
|
|
118
|
+
deprecated: 'obsoleta'
|
|
119
|
+
}
|
|
120
|
+
}
|
|
71
121
|
}
|
|
72
122
|
}
|
|
73
123
|
}
|
|
@@ -116,12 +166,13 @@ export function filter (source) {
|
|
|
116
166
|
* @param {Object} options.mdModules - Result of recursively globbing markdown files under ../pages with eager raw imports
|
|
117
167
|
* @param {Object} [options.pages] - Legacy merged page registry from virtual:docsector-books (allPages)
|
|
118
168
|
* @param {Object} [options.books] - Book registry from virtual:docsector-books (preferred, avoids path collisions)
|
|
169
|
+
* @param {Array} [options.pageEntries] - Version-aware page entries from virtual:docsector-books
|
|
119
170
|
* @param {Object} options.boot - Boot meta from pages/boot.js
|
|
120
171
|
* @param {string[]} [options.langs] - Language codes to process (auto-detected from langModules if omitted)
|
|
121
172
|
* @param {Object<string,string>} [options.homePageOverride] - Optional per-language Home markdown override
|
|
122
173
|
* @returns {Object} Complete i18n messages object keyed by locale
|
|
123
174
|
*/
|
|
124
|
-
export function buildMessages ({ langModules, mdModules, pages, books, boot, langs, homePageOverride = {} }) {
|
|
175
|
+
export function buildMessages ({ langModules, mdModules, pages, books, pageEntries, boot, langs, homePageOverride = {} }) {
|
|
125
176
|
// Auto-detect languages from HJSON files if not provided
|
|
126
177
|
if (!langs) {
|
|
127
178
|
langs = Object.keys(langModules).map(key => {
|
|
@@ -133,8 +184,9 @@ export function buildMessages ({ langModules, mdModules, pages, books, boot, lan
|
|
|
133
184
|
|
|
134
185
|
const i18n = {}
|
|
135
186
|
|
|
136
|
-
function load (topPage, path, subpage, lang) {
|
|
137
|
-
const
|
|
187
|
+
function load (topPage, path, subpage, lang, sourceRoot = '') {
|
|
188
|
+
const normalizedSourceRoot = String(sourceRoot || '').replace(/^\/+|\/+$/g, '')
|
|
189
|
+
const key = `../pages/${normalizedSourceRoot ? normalizedSourceRoot + '/' : ''}${topPage}/${path}.${subpage}.${lang}.md`
|
|
138
190
|
const content = mdModules[key]
|
|
139
191
|
|
|
140
192
|
if (!content) {
|
|
@@ -200,20 +252,30 @@ export function buildMessages ({ langModules, mdModules, pages, books, boot, lan
|
|
|
200
252
|
return match[1].trim()
|
|
201
253
|
}
|
|
202
254
|
|
|
203
|
-
const
|
|
255
|
+
const resolvedPageEntries = []
|
|
204
256
|
|
|
205
|
-
if (
|
|
257
|
+
if (Array.isArray(pageEntries) && pageEntries.length > 0) {
|
|
258
|
+
for (const entry of pageEntries) {
|
|
259
|
+
resolvedPageEntries.push({
|
|
260
|
+
key: entry.pagePath,
|
|
261
|
+
page: entry.page,
|
|
262
|
+
fallbackBook: entry.book,
|
|
263
|
+
sourceRoot: entry.sourceRoot || '',
|
|
264
|
+
i18nSegments: entry.i18nSegments
|
|
265
|
+
})
|
|
266
|
+
}
|
|
267
|
+
} else if (books && typeof books === 'object' && Object.keys(books).length > 0) {
|
|
206
268
|
for (const [bookId, book] of Object.entries(books)) {
|
|
207
269
|
const routes = book?.routes || {}
|
|
208
270
|
const fallbackBook = book?.config?.id || bookId || 'manual'
|
|
209
271
|
|
|
210
272
|
for (const [key, page] of Object.entries(routes)) {
|
|
211
|
-
|
|
273
|
+
resolvedPageEntries.push({ key, page, fallbackBook })
|
|
212
274
|
}
|
|
213
275
|
}
|
|
214
276
|
} else {
|
|
215
277
|
for (const [key, page] of Object.entries(pages || {})) {
|
|
216
|
-
|
|
278
|
+
resolvedPageEntries.push({ key, page, fallbackBook: null })
|
|
217
279
|
}
|
|
218
280
|
}
|
|
219
281
|
|
|
@@ -249,8 +311,8 @@ export function buildMessages ({ langModules, mdModules, pages, books, boot, lan
|
|
|
249
311
|
i18n[lang]._.home.overview.source = loadHomepage(lang)
|
|
250
312
|
|
|
251
313
|
// @ Iterate pages
|
|
252
|
-
for (const entry of
|
|
253
|
-
const { key, page, fallbackBook } = entry
|
|
314
|
+
for (const entry of resolvedPageEntries) {
|
|
315
|
+
const { key, page, fallbackBook, sourceRoot = '', i18nSegments: entryI18nSegments } = entry
|
|
254
316
|
const path = key.startsWith('/') ? key.slice(1) : key
|
|
255
317
|
|
|
256
318
|
const config = page.config
|
|
@@ -258,13 +320,13 @@ export function buildMessages ({ langModules, mdModules, pages, books, boot, lan
|
|
|
258
320
|
const meta = page.meta || boot.meta
|
|
259
321
|
|
|
260
322
|
const topPage = config?.book ?? config?.type ?? fallbackBook ?? 'manual'
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
323
|
+
const i18nSegments = Array.isArray(entryI18nSegments) && entryI18nSegments.length > 0
|
|
324
|
+
? entryI18nSegments
|
|
325
|
+
: [topPage, ...path.split('/').filter(Boolean)]
|
|
264
326
|
|
|
265
327
|
// ---
|
|
266
328
|
|
|
267
|
-
const _ =
|
|
329
|
+
const _ = i18nSegments.reduce((accumulator, current, index) => {
|
|
268
330
|
let node = accumulator[current]
|
|
269
331
|
|
|
270
332
|
// Set object if not exists
|
|
@@ -275,7 +337,7 @@ export function buildMessages ({ langModules, mdModules, pages, books, boot, lan
|
|
|
275
337
|
|
|
276
338
|
// @ Set metadata
|
|
277
339
|
// title
|
|
278
|
-
if (node._ === undefined) {
|
|
340
|
+
if (index === i18nSegments.length - 1 && node._ === undefined) {
|
|
279
341
|
node._ = data?.[lang]?.title || data?.['*']?.title || data?.['en-US']?.title || ''
|
|
280
342
|
}
|
|
281
343
|
|
|
@@ -283,6 +345,10 @@ export function buildMessages ({ langModules, mdModules, pages, books, boot, lan
|
|
|
283
345
|
return node
|
|
284
346
|
}
|
|
285
347
|
|
|
348
|
+
if (index < i18nSegments.length - 1) {
|
|
349
|
+
return node
|
|
350
|
+
}
|
|
351
|
+
|
|
286
352
|
// Set subpages sources if not exists
|
|
287
353
|
if (node.overview === undefined) {
|
|
288
354
|
node.overview = {
|
|
@@ -307,7 +373,7 @@ export function buildMessages ({ langModules, mdModules, pages, books, boot, lan
|
|
|
307
373
|
}
|
|
308
374
|
|
|
309
375
|
return node
|
|
310
|
-
}, i18n[lang]._
|
|
376
|
+
}, i18n[lang]._)
|
|
311
377
|
|
|
312
378
|
// ---
|
|
313
379
|
|
|
@@ -322,14 +388,14 @@ export function buildMessages ({ langModules, mdModules, pages, books, boot, lan
|
|
|
322
388
|
|
|
323
389
|
// @ Subpages
|
|
324
390
|
// Overview
|
|
325
|
-
_.overview.source = load(topPage, path, 'overview', lang)
|
|
391
|
+
_.overview.source = load(topPage, path, 'overview', lang, sourceRoot)
|
|
326
392
|
// showcase
|
|
327
393
|
if (config.subpages?.showcase === true) {
|
|
328
|
-
_.showcase.source = load(topPage, path, 'showcase', lang)
|
|
394
|
+
_.showcase.source = load(topPage, path, 'showcase', lang, sourceRoot)
|
|
329
395
|
}
|
|
330
396
|
// Vs
|
|
331
397
|
if (config.subpages?.vs === true) {
|
|
332
|
-
_.vs.source = load(topPage, path, 'vs', lang)
|
|
398
|
+
_.vs.source = load(topPage, path, 'vs', lang, sourceRoot)
|
|
333
399
|
}
|
|
334
400
|
}
|
|
335
401
|
}
|
package/src/i18n/index.js
CHANGED
|
@@ -5,10 +5,12 @@ import homePageOverride from 'virtual:docsector-homepage-override'
|
|
|
5
5
|
// @ Import language HJSON files (Vite-compatible eager import)
|
|
6
6
|
const langModules = import.meta.glob('./languages/*.hjson', { eager: true })
|
|
7
7
|
// @ Import markdown files (Vite-compatible eager import as raw strings)
|
|
8
|
-
const
|
|
8
|
+
const currentMdModules = import.meta.glob('../pages/**/*.md', { eager: true, query: '?raw', import: 'default' })
|
|
9
|
+
const oldMdModules = import.meta.glob('../pages/.old/**/*.md', { eager: true, query: '?raw', import: 'default' })
|
|
10
|
+
const mdModules = { ...currentMdModules, ...oldMdModules }
|
|
9
11
|
|
|
10
12
|
// @ Import pages
|
|
11
13
|
import boot from 'pages/boot'
|
|
12
|
-
import { books } from 'virtual:docsector-books'
|
|
14
|
+
import { books, pageEntries } from 'virtual:docsector-books'
|
|
13
15
|
|
|
14
|
-
export default buildMessages({ langModules, mdModules, books, boot, homePageOverride })
|
|
16
|
+
export default buildMessages({ langModules, mdModules, books, pageEntries, boot, homePageOverride })
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
next: 'Next page'
|
|
33
33
|
},
|
|
34
34
|
lastUpdated: 'Last updated',
|
|
35
|
+
newVersion: 'New in',
|
|
35
36
|
copyPage: 'Copy page',
|
|
36
37
|
copyPageCaption: 'Copy page as Markdown for LLMs',
|
|
37
38
|
copied: 'Copied!',
|
|
@@ -60,6 +61,19 @@
|
|
|
60
61
|
draft: {
|
|
61
62
|
_: 'draft',
|
|
62
63
|
tooltip: 'This page is under constrution.'
|
|
64
|
+
},
|
|
65
|
+
new: {
|
|
66
|
+
_: 'new',
|
|
67
|
+
tooltip: 'This page is new.',
|
|
68
|
+
tooltipVersion: 'New in {version}'
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
version: {
|
|
73
|
+
status: {
|
|
74
|
+
released: 'released',
|
|
75
|
+
draft: 'draft',
|
|
76
|
+
deprecated: 'deprecated'
|
|
63
77
|
}
|
|
64
78
|
},
|
|
65
79
|
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
next: 'Próxima página'
|
|
32
32
|
},
|
|
33
33
|
lastUpdated: 'Última atualização',
|
|
34
|
+
newVersion: 'Novo em',
|
|
34
35
|
copyPage: 'Copiar página',
|
|
35
36
|
copyPageCaption: 'Copiar página como Markdown para LLMs',
|
|
36
37
|
copied: 'Copiado!',
|
|
@@ -59,6 +60,19 @@
|
|
|
59
60
|
draft: {
|
|
60
61
|
_: 'rascunho',
|
|
61
62
|
tooltip: 'Esta página está em construção.'
|
|
63
|
+
},
|
|
64
|
+
new: {
|
|
65
|
+
_: 'novo',
|
|
66
|
+
tooltip: 'Esta página é nova.',
|
|
67
|
+
tooltipVersion: 'Novo na {version}'
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
version: {
|
|
72
|
+
status: {
|
|
73
|
+
released: 'publicada',
|
|
74
|
+
draft: 'rascunho',
|
|
75
|
+
deprecated: 'obsoleta'
|
|
62
76
|
}
|
|
63
77
|
},
|
|
64
78
|
|
package/src/i18n/path.js
CHANGED
|
@@ -73,6 +73,19 @@ export function splitDotPathSegments (dotPath) {
|
|
|
73
73
|
return normalized.split('.').filter(Boolean)
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
function splitI18nPathSegments (path) {
|
|
77
|
+
if (Array.isArray(path)) {
|
|
78
|
+
return normalizeSegments(path)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const normalized = String(path || '').trim()
|
|
82
|
+
if (normalized.includes('/')) {
|
|
83
|
+
return splitRoutePathSegments(normalized)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return splitDotPathSegments(normalized)
|
|
87
|
+
}
|
|
88
|
+
|
|
76
89
|
export function routeTitleI18nPath (routePath) {
|
|
77
90
|
return buildI18nPath('_', ...splitRoutePathSegments(routePath), '_')
|
|
78
91
|
}
|
|
@@ -82,11 +95,11 @@ export function routeSubpageSourceI18nPath (routePath, subpage = 'overview') {
|
|
|
82
95
|
}
|
|
83
96
|
|
|
84
97
|
export function pageTitleI18nPath (basePath) {
|
|
85
|
-
return buildI18nPath('_', ...
|
|
98
|
+
return buildI18nPath('_', ...splitI18nPathSegments(basePath), '_')
|
|
86
99
|
}
|
|
87
100
|
|
|
88
101
|
export function pageValueI18nPath (absolutePath, key = 'source') {
|
|
89
|
-
return buildI18nPath('_', ...
|
|
102
|
+
return buildI18nPath('_', ...splitI18nPathSegments(absolutePath), key)
|
|
90
103
|
}
|
|
91
104
|
|
|
92
105
|
export function namespacedLabelI18nPath (book, nodePath) {
|
package/src/index.js
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* @param {string} config.branding.name - Project name displayed in sidebar
|
|
28
28
|
* @param {string} config.branding.version - Version label
|
|
29
29
|
* @param {string} [config.branding.description] - Project description (used in llms.txt)
|
|
30
|
-
* @param {string
|
|
30
|
+
* @param {Array<string|Object>} [config.branding.versions] - Available versions for dropdown. Current docs keep unprefixed routes; archived versions can live in src/pages/.old/<version>/ and use /<version>/ route prefixes. Version objects may set released:false/status:'draft', status:'deprecated'/deprecated:true, or badge:{label,color,textColor} for selector badges.
|
|
31
31
|
* @param {Object} config.links - External links
|
|
32
32
|
* @param {string} [config.links.github] - GitHub repository URL
|
|
33
33
|
* @param {string} [config.links.discussions] - GitHub discussions URL
|
|
@@ -111,7 +111,7 @@ export function createDocsector (config = {}) {
|
|
|
111
111
|
logo: '/images/logo.png',
|
|
112
112
|
name: 'My Project',
|
|
113
113
|
version: 'v0.0.1',
|
|
114
|
-
versions: ['v0.0.1'],
|
|
114
|
+
versions: [{ id: 'v0.0.1', current: true, released: false }],
|
|
115
115
|
...config.branding
|
|
116
116
|
},
|
|
117
117
|
|
|
@@ -59,7 +59,7 @@ import { useMeta, colors } from 'quasar'
|
|
|
59
59
|
|
|
60
60
|
import DMenu from '../components/DMenu.vue'
|
|
61
61
|
import docsectorConfig from 'docsector.config.js'
|
|
62
|
-
import { allBooks } from 'virtual:docsector-books'
|
|
62
|
+
import { allBooks, booksByVersion } from 'virtual:docsector-books'
|
|
63
63
|
import { pageTitleI18nPath } from '../i18n/path'
|
|
64
64
|
|
|
65
65
|
defineOptions({ name: 'LayoutDefault' })
|
|
@@ -178,8 +178,20 @@ const getBookTabStyle = (book) => {
|
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
const activeVersionId = computed(() => {
|
|
182
|
+
return route.matched?.[0]?.meta?.version ?? route.meta?.version ?? null
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
const activeVersionBooks = computed(() => {
|
|
186
|
+
if (activeVersionId.value && booksByVersion?.[activeVersionId.value]?.allBooks) {
|
|
187
|
+
return booksByVersion[activeVersionId.value].allBooks
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return allBooks || []
|
|
191
|
+
})
|
|
192
|
+
|
|
181
193
|
const sortedBooks = computed(() => {
|
|
182
|
-
return [...
|
|
194
|
+
return [...activeVersionBooks.value]
|
|
183
195
|
.filter(book => book && typeof book.id === 'string' && book.id.length > 0)
|
|
184
196
|
.sort((a, b) => {
|
|
185
197
|
const orderA = Number.isFinite(a.order) ? a.order : Number.MAX_SAFE_INTEGER
|
|
@@ -261,10 +273,12 @@ function openSettingsDialog () {
|
|
|
261
273
|
|
|
262
274
|
function getFirstRoutePathByBook (bookId) {
|
|
263
275
|
const routes = router.options?.routes || []
|
|
276
|
+
const versionId = activeVersionId.value
|
|
264
277
|
let fallbackPath = null
|
|
265
278
|
|
|
266
279
|
for (const topRoute of routes) {
|
|
267
280
|
if (!topRoute || typeof topRoute.path !== 'string') continue
|
|
281
|
+
if (versionId && topRoute.meta?.version !== versionId) continue
|
|
268
282
|
if ((topRoute.meta?.book ?? topRoute.meta?.type) !== bookId) continue
|
|
269
283
|
|
|
270
284
|
const children = Array.isArray(topRoute.children) ? topRoute.children : []
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
## Archived Version
|
|
2
|
+
|
|
3
|
+
This page is a minimal archived `v0.x` fixture used to validate Docsector Reader version switching.
|
|
4
|
+
|
|
5
|
+
## Current Behavior
|
|
6
|
+
|
|
7
|
+
Selecting `v0.x` in the sidebar keeps the active book and page when an archived equivalent exists, then serves this Markdown from `src/pages/.old/v0.x`.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
## Versao Arquivada
|
|
2
|
+
|
|
3
|
+
Esta pagina e um fixture minimo arquivado `v0.x` usado para validar a troca de versoes do Docsector Reader.
|
|
4
|
+
|
|
5
|
+
## Comportamento Atual
|
|
6
|
+
|
|
7
|
+
Ao selecionar `v0.x` no menu lateral, o leitor preserva o book e a pagina ativos quando existe um equivalente arquivado, entao serve este Markdown a partir de `src/pages/.old/v0.x`.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
'/getting-started': {
|
|
3
|
+
config: {
|
|
4
|
+
icon: 'history',
|
|
5
|
+
status: 'done',
|
|
6
|
+
meta: {
|
|
7
|
+
description: {
|
|
8
|
+
'en-US': 'Getting Started - Archived v0.x documentation of Docsector Reader',
|
|
9
|
+
'pt-BR': 'Comecando - Documentacao arquivada v0.x do Docsector Reader'
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
book: 'guide',
|
|
13
|
+
menu: {
|
|
14
|
+
header: {
|
|
15
|
+
icon: 'history_edu',
|
|
16
|
+
label: 'Archived v0.x'
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
subpages: {
|
|
20
|
+
showcase: false
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
data: {
|
|
24
|
+
'en-US': { title: 'Getting Started v0.x' },
|
|
25
|
+
'pt-BR': { title: 'Comecando v0.x' }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -9,10 +9,17 @@ branding: {
|
|
|
9
9
|
logo: '/images/logo.png', // Path to logo in public/
|
|
10
10
|
name: 'My Project', // Project name shown in sidebar
|
|
11
11
|
version: 'v1.0.0', // Current version badge
|
|
12
|
-
versions: [
|
|
12
|
+
versions: [
|
|
13
|
+
{ id: 'v1.0.0', current: true, released: false },
|
|
14
|
+
{ id: 'v0.x', released: true, status: 'deprecated' }
|
|
15
|
+
] // Version dropdown options
|
|
13
16
|
}
|
|
14
17
|
```
|
|
15
18
|
|
|
19
|
+
The current version keeps unprefixed routes such as `/guide/getting-started/overview/`. Archived major versions can live under `src/pages/.old/{version}/` using the same book registry and Markdown layout, and are exposed at `/<version>/...`, for example `/v0.x/guide/getting-started/overview/`.
|
|
20
|
+
|
|
21
|
+
Every version shows a release badge next to the version in the selector. Released versions default to `released`; versions with `released: false` or `status: 'draft'` default to `draft`; versions with `status: 'deprecated'` or `deprecated: true` default to `deprecated` in red. Use `badge: { label, color, textColor }` to customize the badge.
|
|
22
|
+
|
|
16
23
|
The `logo` path is relative to the `public/` folder. Recommended size: **85×85px**.
|
|
17
24
|
|
|
18
25
|
## Links
|
|
@@ -84,7 +91,11 @@ export default {
|
|
|
84
91
|
logo: '/images/logo.png',
|
|
85
92
|
name: 'Acme Docs',
|
|
86
93
|
version: 'v2.1.0',
|
|
87
|
-
versions: [
|
|
94
|
+
versions: [
|
|
95
|
+
{ id: 'v2.1.0', current: true, released: false },
|
|
96
|
+
{ id: 'v2.0.0', released: true },
|
|
97
|
+
{ id: 'v1.0.0', released: true, status: 'deprecated' }
|
|
98
|
+
]
|
|
88
99
|
},
|
|
89
100
|
links: {
|
|
90
101
|
github: 'https://github.com/acme/acme',
|
|
@@ -9,10 +9,17 @@ branding: {
|
|
|
9
9
|
logo: '/images/logo.png', // Caminho do logo em public/
|
|
10
10
|
name: 'Meu Projeto', // Nome exibido na sidebar
|
|
11
11
|
version: 'v1.0.0', // Badge de versão atual
|
|
12
|
-
versions: [
|
|
12
|
+
versions: [
|
|
13
|
+
{ id: 'v1.0.0', current: true, released: false },
|
|
14
|
+
{ id: 'v0.x', released: true, status: 'deprecated' }
|
|
15
|
+
] // Opções do dropdown de versão
|
|
13
16
|
}
|
|
14
17
|
```
|
|
15
18
|
|
|
19
|
+
A versão atual mantém rotas sem prefixo, como `/guide/getting-started/overview/`. Versões major arquivadas podem ficar em `src/pages/.old/{version}/` usando o mesmo layout de book registry e Markdown, e são expostas em `/<version>/...`, por exemplo `/v0.x/guide/getting-started/overview/`.
|
|
20
|
+
|
|
21
|
+
Toda versão mostra um badge de release ao lado da versão no seletor. Versões lançadas usam `released` por padrão; versões com `released: false` ou `status: 'draft'` usam `draft`; versões com `status: 'deprecated'` ou `deprecated: true` usam `deprecated` em vermelho. Use `badge: { label, color, textColor }` para customizar o badge.
|
|
22
|
+
|
|
16
23
|
O caminho do `logo` é relativo à pasta `public/`. Tamanho recomendado: **85×85px**.
|
|
17
24
|
|
|
18
25
|
## Links
|
|
@@ -84,7 +91,11 @@ export default {
|
|
|
84
91
|
logo: '/images/logo.png',
|
|
85
92
|
name: 'Acme Docs',
|
|
86
93
|
version: 'v2.1.0',
|
|
87
|
-
versions: [
|
|
94
|
+
versions: [
|
|
95
|
+
{ id: 'v2.1.0', current: true, released: false },
|
|
96
|
+
{ id: 'v2.0.0', released: true },
|
|
97
|
+
{ id: 'v1.0.0', released: true, status: 'deprecated' }
|
|
98
|
+
]
|
|
88
99
|
},
|
|
89
100
|
links: {
|
|
90
101
|
github: 'https://github.com/acme/acme',
|
|
@@ -8,7 +8,8 @@ Documentation pages are defined in split registries such as `src/pages/guide.ind
|
|
|
8
8
|
'/my-page': {
|
|
9
9
|
config: {
|
|
10
10
|
icon: 'description',
|
|
11
|
-
status: '
|
|
11
|
+
status: 'new',
|
|
12
|
+
version: 'v2.1.0',
|
|
12
13
|
book: 'guide',
|
|
13
14
|
menu: {},
|
|
14
15
|
subpages: { showcase: false }
|
|
@@ -23,7 +24,8 @@ Documentation pages are defined in split registries such as `src/pages/guide.ind
|
|
|
23
24
|
## Config Properties
|
|
24
25
|
|
|
25
26
|
- **book** — Route prefix: `'guide'`, `'manual'`, or `'API'` (legacy `type` is still supported)
|
|
26
|
-
- **status** — Page status: `'done'`, `'draft'`, or `'
|
|
27
|
+
- **status** — Page status: `'done'`, `'draft'`, `'empty'`, or `'new'`; `new` is shown in green
|
|
28
|
+
- **version** — Optional version where the page was introduced, shown under the last updated date as `New in: ...` (for example, `'v2.1.0'`)
|
|
27
29
|
- **icon** — Material Design icon name shown in the sidebar
|
|
28
30
|
- **menu** — Object controlling menu display (header, subheader, separator)
|
|
29
31
|
- **subpages** — Enable additional tabs: `showcase`, `vs`
|
|
@@ -83,3 +85,5 @@ Routes are automatically generated from the page registry. A page with path `/my
|
|
|
83
85
|
- `/guide/my-page/overview` — Main content tab
|
|
84
86
|
- `/guide/my-page/showcase` — Showcase tab (if enabled)
|
|
85
87
|
- `/guide/my-page/vs` — Comparison tab (if enabled)
|
|
88
|
+
|
|
89
|
+
Archived major versions use the same structure under `src/pages/.old/{version}/`. A page registered in `src/pages/.old/v0.x/guide.index.js` produces `/v0.x/guide/my-page/overview` while the current version remains `/guide/my-page/overview`.
|
|
@@ -8,7 +8,8 @@ As páginas de documentação são definidas em registros separados, como `src/p
|
|
|
8
8
|
'/minha-pagina': {
|
|
9
9
|
config: {
|
|
10
10
|
icon: 'description',
|
|
11
|
-
status: '
|
|
11
|
+
status: 'new',
|
|
12
|
+
version: 'v2.1.0',
|
|
12
13
|
book: 'guide',
|
|
13
14
|
menu: {},
|
|
14
15
|
subpages: { showcase: false }
|
|
@@ -23,7 +24,8 @@ As páginas de documentação são definidas em registros separados, como `src/p
|
|
|
23
24
|
## Propriedades do Config
|
|
24
25
|
|
|
25
26
|
- **book** — Prefixo da rota: `'guide'`, `'manual'` ou `'API'` (compatível com `type` legado)
|
|
26
|
-
- **status** — Status da página: `'done'`, `'draft'` ou `'
|
|
27
|
+
- **status** — Status da página: `'done'`, `'draft'`, `'empty'` ou `'new'`; `new` é exibido em verde
|
|
28
|
+
- **version** — Versão opcional em que a página foi introduzida, exibida abaixo da data de última atualização como `Novo em: ...` (por exemplo, `'v2.1.0'`)
|
|
27
29
|
- **icon** — Nome do ícone Material Design exibido no menu lateral
|
|
28
30
|
- **menu** — Objeto controlando exibição do menu (header, subheader, separator)
|
|
29
31
|
- **subpages** — Ativar abas adicionais: `showcase`, `vs`
|
|
@@ -83,3 +85,5 @@ Rotas são geradas automaticamente a partir do registro de páginas. Uma página
|
|
|
83
85
|
- `/guide/my-page/overview` — Aba de conteúdo principal
|
|
84
86
|
- `/guide/my-page/showcase` — Aba de demonstração (se habilitada)
|
|
85
87
|
- `/guide/my-page/vs` — Aba de comparação (se habilitada)
|
|
88
|
+
|
|
89
|
+
Versões major arquivadas usam a mesma estrutura em `src/pages/.old/{version}/`. Uma página registrada em `src/pages/.old/v0.x/guide.index.js` produz `/v0.x/guide/my-page/overview`, enquanto a versão atual continua em `/guide/my-page/overview`.
|