@docsector/docsector-reader 2.0.6 → 2.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 +16 -5
- package/bin/docsector.js +10 -5
- package/docsector.config.js +13 -1
- package/package.json +1 -1
- package/src/components/DMenu.vue +224 -12
- package/src/components/DPageBar.vue +5 -0
- package/src/components/DPageMeta.vue +37 -23
- package/src/css/app.sass +2 -1
- package/src/i18n/helpers.js +34 -18
- package/src/i18n/index.js +5 -3
- 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 +2 -0
- package/src/pages/guide/pages-and-routing.overview.pt-BR.md +2 -0
- 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/quasar.factory.js +648 -91
- package/src/router/routes.js +127 -95
- package/src/store/App.js +15 -5
package/README.md
CHANGED
|
@@ -68,6 +68,8 @@ Transform Markdown content into beautiful, navigable documentation sites — wit
|
|
|
68
68
|
- 🧬 **Scaffolded Homepage Override Wiring** — New consumer projects automatically wire `virtual:docsector-homepage-override` into i18n message building
|
|
69
69
|
- 🧭 **Quick Links Custom Element** — Use `<d-quick-links>` and `<d-quick-link>` in Markdown to render rich home navigation cards
|
|
70
70
|
- 🗂️ **API Catalog Well-Known** — Auto-generates `/.well-known/api-catalog` as Linkset JSON for machine-readable API discovery
|
|
71
|
+
- 🗃️ **Multi-Version History** — Archive older major versions under `src/pages/.old/<version>/` and expose them at prefixed routes (e.g. `/v0.x/guide/...`) while keeping the current docs at unprefixed routes
|
|
72
|
+
- 🏷️ **Version Selector Badges** — Every version in the sidebar selector displays a color-coded badge: green for released, orange for draft, red for deprecated; fully customizable via `badge: { label, color, textColor }`
|
|
71
73
|
- ⚙️ **Single Config File** — Customize branding, links, and languages via `docsector.config.js`
|
|
72
74
|
|
|
73
75
|
---
|
|
@@ -675,7 +677,10 @@ export default {
|
|
|
675
677
|
logo: '/images/logo/my-logo.png',
|
|
676
678
|
name: 'My Project',
|
|
677
679
|
version: 'v1.0.0',
|
|
678
|
-
versions: [
|
|
680
|
+
versions: [
|
|
681
|
+
{ id: 'v1.0.0', current: true, released: false },
|
|
682
|
+
{ id: 'v0.9.0', released: true, status: 'deprecated' }
|
|
683
|
+
]
|
|
679
684
|
},
|
|
680
685
|
|
|
681
686
|
links: {
|
|
@@ -754,6 +759,10 @@ export default {
|
|
|
754
759
|
}
|
|
755
760
|
```
|
|
756
761
|
|
|
762
|
+
The current version keeps the normal unprefixed routes such as `/guide/getting-started/overview/`. Archived major versions can be placed under `src/pages/.old/<version>/` with the same book/index/Markdown layout, and are exposed with a URL prefix such as `/v0.x/guide/getting-started/overview/`.
|
|
763
|
+
|
|
764
|
+
Every version shows a release badge 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 }` when you need custom badge copy or colors.
|
|
765
|
+
|
|
757
766
|
### MCP (optional)
|
|
758
767
|
|
|
759
768
|
```javascript
|
|
@@ -777,15 +786,17 @@ import { buildMessages } from '@docsector/docsector-reader/i18n'
|
|
|
777
786
|
import homePageOverride from 'virtual:docsector-homepage-override'
|
|
778
787
|
|
|
779
788
|
const langModules = import.meta.glob('./languages/*.hjson', { eager: true })
|
|
780
|
-
const
|
|
789
|
+
const currentMdModules = import.meta.glob('../pages/**/*.md', { eager: true, query: '?raw', import: 'default' })
|
|
790
|
+
const oldMdModules = import.meta.glob('../pages/.old/**/*.md', { eager: true, query: '?raw', import: 'default' })
|
|
791
|
+
const mdModules = { ...currentMdModules, ...oldMdModules }
|
|
781
792
|
|
|
782
793
|
import boot from 'pages/boot'
|
|
783
|
-
import { books } from 'virtual:docsector-books'
|
|
794
|
+
import { books, pageEntries } from 'virtual:docsector-books'
|
|
784
795
|
|
|
785
|
-
export default buildMessages({ langModules, mdModules, books, boot, homePageOverride })
|
|
796
|
+
export default buildMessages({ langModules, mdModules, books, pageEntries, boot, homePageOverride })
|
|
786
797
|
```
|
|
787
798
|
|
|
788
|
-
> `
|
|
799
|
+
> `pageEntries` is the preferred source because it preserves per-book and per-version registries and avoids path collisions when books or archived versions reuse the same route key.
|
|
789
800
|
|
|
790
801
|
### Language files
|
|
791
802
|
|
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 = '2.0
|
|
26
|
+
const VERSION = '2.1.0'
|
|
27
27
|
|
|
28
28
|
const HELP = `
|
|
29
29
|
Docsector Reader v${VERSION}
|
|
@@ -105,7 +105,10 @@ export default {
|
|
|
105
105
|
branding: {
|
|
106
106
|
logo: '/images/logo.png',
|
|
107
107
|
name: 'My Documentation',
|
|
108
|
-
version: 'v0.1.0'
|
|
108
|
+
version: 'v0.1.0',
|
|
109
|
+
versions: [
|
|
110
|
+
{ id: 'v0.1.0', current: true, released: false }
|
|
111
|
+
]
|
|
109
112
|
},
|
|
110
113
|
|
|
111
114
|
// @ Links
|
|
@@ -269,13 +272,15 @@ import homePageOverride from 'virtual:docsector-homepage-override'
|
|
|
269
272
|
// @ Import language HJSON files (Vite-compatible eager import)
|
|
270
273
|
const langModules = import.meta.glob('./languages/*.hjson', { eager: true })
|
|
271
274
|
// @ Import markdown files (Vite-compatible eager import as raw strings)
|
|
272
|
-
const
|
|
275
|
+
const currentMdModules = import.meta.glob('../pages/**/*.md', { eager: true, query: '?raw', import: 'default' })
|
|
276
|
+
const oldMdModules = import.meta.glob('../pages/.old/**/*.md', { eager: true, query: '?raw', import: 'default' })
|
|
277
|
+
const mdModules = { ...currentMdModules, ...oldMdModules }
|
|
273
278
|
|
|
274
279
|
// @ Import pages
|
|
275
280
|
import boot from 'pages/boot'
|
|
276
|
-
import {
|
|
281
|
+
import { books, pageEntries } from 'virtual:docsector-books'
|
|
277
282
|
|
|
278
|
-
export default buildMessages({ langModules, mdModules,
|
|
283
|
+
export default buildMessages({ langModules, mdModules, books, pageEntries, boot, homePageOverride })
|
|
279
284
|
`
|
|
280
285
|
|
|
281
286
|
const TEMPLATE_I18N_HJSON = `\
|
package/docsector.config.js
CHANGED
|
@@ -15,7 +15,19 @@ export default {
|
|
|
15
15
|
// Project name displayed in the sidebar
|
|
16
16
|
name: 'Docsector Reader',
|
|
17
17
|
// Version label displayed next to the name
|
|
18
|
-
version: 'v' + pkg.version
|
|
18
|
+
version: 'v' + pkg.version,
|
|
19
|
+
versions: [
|
|
20
|
+
{
|
|
21
|
+
id: 'v' + pkg.version,
|
|
22
|
+
current: true,
|
|
23
|
+
released: false
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'v0.x',
|
|
27
|
+
released: true,
|
|
28
|
+
status: 'deprecated'
|
|
29
|
+
}
|
|
30
|
+
]
|
|
19
31
|
},
|
|
20
32
|
|
|
21
33
|
// @ Links
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docsector/docsector-reader",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.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",
|
package/src/components/DMenu.vue
CHANGED
|
@@ -7,7 +7,7 @@ import { useI18n } from 'vue-i18n'
|
|
|
7
7
|
import tags from '@docsector/tags'
|
|
8
8
|
import DMenuItem from './DMenuItem.vue'
|
|
9
9
|
import docsectorConfig from 'docsector.config.js'
|
|
10
|
-
import { allBooks } from 'virtual:docsector-books'
|
|
10
|
+
import { allBooks, booksByVersion, versions } from 'virtual:docsector-books'
|
|
11
11
|
import { namespacedLabelI18nPath, routeSubpageSourceI18nPath } from '../i18n/path'
|
|
12
12
|
|
|
13
13
|
const $q = useQuasar()
|
|
@@ -20,19 +20,93 @@ const links = docsectorConfig.links || {}
|
|
|
20
20
|
|
|
21
21
|
const term = ref(null)
|
|
22
22
|
const founds = ref(false)
|
|
23
|
-
const version = ref(branding.version || 'v0.x')
|
|
24
|
-
const versions = ref(branding.versions || ['v0.x'])
|
|
25
23
|
const items = ref([])
|
|
26
24
|
const scrolling = ref(null)
|
|
27
25
|
|
|
28
26
|
const subpage = computed(() => {
|
|
29
27
|
const parent = $route.matched[0]?.path
|
|
30
28
|
const child = $route.matched[1]?.path
|
|
29
|
+
if (!parent || !child) return '/overview'
|
|
31
30
|
return child.substring(parent.length)
|
|
32
31
|
})
|
|
33
32
|
|
|
33
|
+
const activeVersionId = computed(() => {
|
|
34
|
+
return $route.matched?.[0]?.meta?.version ?? versions?.[0]?.id ?? ''
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const activeBooks = computed(() => {
|
|
38
|
+
if (activeVersionId.value && booksByVersion?.[activeVersionId.value]?.allBooks) {
|
|
39
|
+
return booksByVersion[activeVersionId.value].allBooks
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return allBooks || []
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const draftReleaseStatuses = new Set(['draft', 'unreleased', 'preview', 'next'])
|
|
46
|
+
|
|
47
|
+
const normalizeVersionBadge = (item) => {
|
|
48
|
+
const configuredStatus = item.deprecated === true
|
|
49
|
+
? 'deprecated'
|
|
50
|
+
: (item.releaseStatus || item.status)
|
|
51
|
+
const explicitlyReleased = item.released !== undefined ? item.released !== false : null
|
|
52
|
+
const released = configuredStatus === 'deprecated'
|
|
53
|
+
? true
|
|
54
|
+
: (explicitlyReleased ?? !draftReleaseStatuses.has(String(configuredStatus || '').toLowerCase()))
|
|
55
|
+
const releaseStatus = configuredStatus || (released ? 'released' : 'draft')
|
|
56
|
+
const rawBadge = item.badge ?? item.releaseBadge
|
|
57
|
+
const deprecated = releaseStatus === 'deprecated'
|
|
58
|
+
const defaultColor = deprecated ? 'negative' : (released ? 'positive' : 'warning')
|
|
59
|
+
const defaultTextColor = (deprecated || released) ? 'white' : 'dark'
|
|
60
|
+
|
|
61
|
+
if (rawBadge === false || rawBadge === null) {
|
|
62
|
+
return { label: releaseStatus, color: defaultColor, textColor: defaultTextColor }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (typeof rawBadge === 'string') {
|
|
66
|
+
return { label: rawBadge, color: defaultColor, textColor: defaultTextColor }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (typeof rawBadge === 'object' && rawBadge !== null) {
|
|
70
|
+
const label = rawBadge.label || rawBadge.text || releaseStatus
|
|
71
|
+
if (!label) {
|
|
72
|
+
return null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
...rawBadge,
|
|
77
|
+
label,
|
|
78
|
+
color: rawBadge.color || defaultColor,
|
|
79
|
+
textColor: rawBadge.textColor || defaultTextColor
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { label: releaseStatus, color: defaultColor, textColor: defaultTextColor }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const versionOptions = computed(() => {
|
|
87
|
+
return (versions || [])
|
|
88
|
+
.filter(item => item && item.id)
|
|
89
|
+
.map(item => ({
|
|
90
|
+
label: item.label || item.id,
|
|
91
|
+
value: item.id,
|
|
92
|
+
badge: normalizeVersionBadge(item),
|
|
93
|
+
released: item.released !== false,
|
|
94
|
+
deprecated: item.deprecated === true || item.releaseStatus === 'deprecated' || item.status === 'deprecated',
|
|
95
|
+
releaseStatus: item.releaseStatus || item.status || (item.released === false ? 'draft' : 'released')
|
|
96
|
+
}))
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
const activeVersionOption = computed(() => {
|
|
100
|
+
return versionOptions.value.find(item => item.value === activeVersionId.value) || null
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const version = computed({
|
|
104
|
+
get: () => activeVersionId.value,
|
|
105
|
+
set: (versionId) => onVersionChange(versionId)
|
|
106
|
+
})
|
|
107
|
+
|
|
34
108
|
const defaultBookId = computed(() => {
|
|
35
|
-
const sortedBooks = [...
|
|
109
|
+
const sortedBooks = [...activeBooks.value]
|
|
36
110
|
.filter(book => book && typeof book.id === 'string' && book.id.length > 0)
|
|
37
111
|
.sort((a, b) => {
|
|
38
112
|
const orderA = Number.isFinite(a.order) ? a.order : Number.MAX_SAFE_INTEGER
|
|
@@ -52,13 +126,97 @@ const currentBookId = computed(() => {
|
|
|
52
126
|
return defaultBookId.value
|
|
53
127
|
})
|
|
54
128
|
|
|
129
|
+
const normalizeRoutePath = (path) => {
|
|
130
|
+
const normalized = String(path || '').trim()
|
|
131
|
+
if (normalized === '' || normalized === '/') {
|
|
132
|
+
return '/'
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const sanitized = normalized.replace(/\/+$/, '')
|
|
136
|
+
return sanitized === '' ? '/' : sanitized
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const getTopRoutes = () => {
|
|
140
|
+
return ($router.options.routes || []).slice(0, -2)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const routeHasSubpage = (route, subpageName) => {
|
|
144
|
+
return (route.children || []).some(child => child.path === subpageName)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const routeToSubpagePath = (route, subpageName) => {
|
|
148
|
+
return `${route.path.replace(/\/$/, '')}/${subpageName}/`
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const getCurrentSubpageName = () => {
|
|
152
|
+
return String(subpage.value || '/overview').replace(/^\/+|\/+$/g, '') || 'overview'
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const getFirstRoutePathByVersion = (versionId, preferredBook = null) => {
|
|
156
|
+
const routes = getTopRoutes()
|
|
157
|
+
|
|
158
|
+
for (const preferBook of [preferredBook, null]) {
|
|
159
|
+
for (const route of routes) {
|
|
160
|
+
if (route?.meta?.version !== versionId) continue
|
|
161
|
+
if (preferBook && (route.meta?.book ?? route.meta?.type) !== preferBook) continue
|
|
162
|
+
if (!routeHasSubpage(route, 'overview')) continue
|
|
163
|
+
|
|
164
|
+
const hasInternalLink = typeof route.meta?.link?.to === 'string' && route.meta.link.to.trim().length > 0
|
|
165
|
+
if (hasInternalLink) continue
|
|
166
|
+
|
|
167
|
+
return routeToSubpagePath(route, 'overview')
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return '/'
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const getEquivalentRoutePath = (versionId) => {
|
|
175
|
+
const routeMeta = $route.matched?.[0]?.meta || {}
|
|
176
|
+
const book = routeMeta.book ?? routeMeta.type ?? currentBookId.value
|
|
177
|
+
const pagePath = routeMeta.pagePath
|
|
178
|
+
const subpageName = getCurrentSubpageName()
|
|
179
|
+
|
|
180
|
+
if (book && typeof pagePath === 'string') {
|
|
181
|
+
const equivalentRoute = getTopRoutes().find(route => {
|
|
182
|
+
return route?.meta?.version === versionId &&
|
|
183
|
+
(route.meta?.book ?? route.meta?.type) === book &&
|
|
184
|
+
route.meta?.pagePath === pagePath &&
|
|
185
|
+
routeHasSubpage(route, subpageName)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
if (equivalentRoute) {
|
|
189
|
+
return routeToSubpagePath(equivalentRoute, subpageName)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return getFirstRoutePathByVersion(versionId, book)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function onVersionChange (versionId) {
|
|
197
|
+
if (!versionId || versionId === activeVersionId.value) return
|
|
198
|
+
|
|
199
|
+
const targetVersion = (versions || []).find(item => item.id === versionId)
|
|
200
|
+
if (!targetVersion) return
|
|
201
|
+
|
|
202
|
+
if (typeof targetVersion.url === 'string' && targetVersion.url.trim().length > 0) {
|
|
203
|
+
openURL(targetVersion.url)
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const targetPath = getEquivalentRoutePath(versionId)
|
|
208
|
+
if (normalizeRoutePath($route.path) !== normalizeRoutePath(targetPath)) {
|
|
209
|
+
$router.push(targetPath)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
55
213
|
const searchTerm = (term) => {
|
|
56
214
|
if (term.length > 1) {
|
|
57
215
|
term = term.toLowerCase()
|
|
58
216
|
const locale = $q.localStorage.getItem('setting.language')
|
|
59
217
|
founds.value = []
|
|
60
218
|
|
|
61
|
-
for (const
|
|
219
|
+
for (const group of items.value) {
|
|
62
220
|
searchTermIterate(group, term, locale)
|
|
63
221
|
}
|
|
64
222
|
} else {
|
|
@@ -74,13 +232,14 @@ const searchTermIterate = (items, term, locale) => {
|
|
|
74
232
|
} else if (typeof items === 'object') {
|
|
75
233
|
const item = items
|
|
76
234
|
const path = item.path
|
|
235
|
+
const tagPath = item.meta?.unversionedPath || path
|
|
77
236
|
founds.value[path] = false
|
|
78
237
|
|
|
79
238
|
// @ search in i18n/tags.hjson
|
|
80
239
|
if (tags[locale] && Object.keys(tags[locale]).length > 0) {
|
|
81
|
-
founds.value[path] = tags[locale][
|
|
240
|
+
founds.value[path] = tags[locale][tagPath]?.indexOf(term) !== -1
|
|
82
241
|
if (founds.value[path] === false && locale !== 'en-US') {
|
|
83
|
-
founds.value[path] = tags['en-US'][
|
|
242
|
+
founds.value[path] = tags['en-US'][tagPath]?.indexOf(term) !== -1
|
|
84
243
|
}
|
|
85
244
|
}
|
|
86
245
|
|
|
@@ -181,11 +340,13 @@ onBeforeUnmount(() => {
|
|
|
181
340
|
})
|
|
182
341
|
|
|
183
342
|
const buildMenuItems = () => {
|
|
184
|
-
const routes = (
|
|
343
|
+
const routes = getTopRoutes()
|
|
185
344
|
const activeBook = currentBookId.value
|
|
345
|
+
const activeVersion = activeVersionId.value
|
|
186
346
|
|
|
187
347
|
const filteredRoutes = routes.filter(route => {
|
|
188
348
|
const routeBook = route?.meta?.book ?? route?.meta?.type
|
|
349
|
+
if (activeVersion && route?.meta?.version !== activeVersion) return false
|
|
189
350
|
if (!activeBook) return true
|
|
190
351
|
return routeBook === activeBook
|
|
191
352
|
})
|
|
@@ -201,7 +362,7 @@ const buildMenuItems = () => {
|
|
|
201
362
|
})
|
|
202
363
|
|
|
203
364
|
// # Route
|
|
204
|
-
|
|
365
|
+
const basepath = route.meta?.menuGroupPath || route.path.split('/')[2]
|
|
205
366
|
const header = route.meta?.menu?.header
|
|
206
367
|
|
|
207
368
|
if (header !== undefined && basepath !== nodeBasepath) {
|
|
@@ -227,7 +388,7 @@ const rebuildItems = () => {
|
|
|
227
388
|
}
|
|
228
389
|
|
|
229
390
|
rebuildItems()
|
|
230
|
-
watch(currentBookId, rebuildItems)
|
|
391
|
+
watch([currentBookId, activeVersionId], rebuildItems)
|
|
231
392
|
</script>
|
|
232
393
|
|
|
233
394
|
<template>
|
|
@@ -254,10 +415,44 @@ watch(currentBookId, rebuildItems)
|
|
|
254
415
|
<div class="text-weight-medium">{{ branding.name || 'Docsector' }}</div>
|
|
255
416
|
<div class="text-caption q-pt-xs">{{ t('system.documentation') }}</div>
|
|
256
417
|
<q-select class="q-mr-md"
|
|
257
|
-
v-model="version" :options="
|
|
418
|
+
v-model="version" :options="versionOptions"
|
|
419
|
+
emit-value map-options
|
|
258
420
|
dense options-dense
|
|
259
421
|
behavior="menu"
|
|
260
|
-
|
|
422
|
+
>
|
|
423
|
+
<template v-slot:selected>
|
|
424
|
+
<div v-if="activeVersionOption" class="version-select-option">
|
|
425
|
+
<span class="version-select-label">{{ activeVersionOption.label }}</span>
|
|
426
|
+
<q-badge
|
|
427
|
+
v-if="activeVersionOption.badge"
|
|
428
|
+
class="version-select-badge"
|
|
429
|
+
:color="activeVersionOption.badge.color || 'warning'"
|
|
430
|
+
:text-color="activeVersionOption.badge.textColor || 'dark'"
|
|
431
|
+
:outline="activeVersionOption.badge.outline === true"
|
|
432
|
+
>
|
|
433
|
+
{{ activeVersionOption.badge.label }}
|
|
434
|
+
</q-badge>
|
|
435
|
+
</div>
|
|
436
|
+
</template>
|
|
437
|
+
<template v-slot:option="scope">
|
|
438
|
+
<q-item v-bind="scope.itemProps">
|
|
439
|
+
<q-item-section>
|
|
440
|
+
<div class="version-select-option">
|
|
441
|
+
<span class="version-select-label">{{ scope.opt.label }}</span>
|
|
442
|
+
<q-badge
|
|
443
|
+
v-if="scope.opt.badge"
|
|
444
|
+
class="version-select-badge"
|
|
445
|
+
:color="scope.opt.badge.color || 'warning'"
|
|
446
|
+
:text-color="scope.opt.badge.textColor || 'dark'"
|
|
447
|
+
:outline="scope.opt.badge.outline === true"
|
|
448
|
+
>
|
|
449
|
+
{{ scope.opt.badge.label }}
|
|
450
|
+
</q-badge>
|
|
451
|
+
</div>
|
|
452
|
+
</q-item-section>
|
|
453
|
+
</q-item>
|
|
454
|
+
</template>
|
|
455
|
+
</q-select>
|
|
261
456
|
</div>
|
|
262
457
|
</div>
|
|
263
458
|
|
|
@@ -470,6 +665,23 @@ body.body--light
|
|
|
470
665
|
width: 30px
|
|
471
666
|
height: 3px
|
|
472
667
|
|
|
668
|
+
.version-select-option
|
|
669
|
+
display: flex
|
|
670
|
+
align-items: center
|
|
671
|
+
gap: 6px
|
|
672
|
+
min-width: 0
|
|
673
|
+
max-width: 100%
|
|
674
|
+
|
|
675
|
+
.version-select-label
|
|
676
|
+
overflow: hidden
|
|
677
|
+
text-overflow: ellipsis
|
|
678
|
+
white-space: nowrap
|
|
679
|
+
|
|
680
|
+
.version-select-badge
|
|
681
|
+
flex: 0 0 auto
|
|
682
|
+
font-size: 10px
|
|
683
|
+
line-height: 1
|
|
684
|
+
|
|
473
685
|
// Search
|
|
474
686
|
label[for="search"]
|
|
475
687
|
z-index: 2
|
|
@@ -37,6 +37,11 @@ const subpage = computed(() => {
|
|
|
37
37
|
})
|
|
38
38
|
|
|
39
39
|
const fileKey = computed(() => {
|
|
40
|
+
const sourcePathBase = route.matched?.[0]?.meta?.sourcePathBase
|
|
41
|
+
if (typeof sourcePathBase === 'string' && sourcePathBase.length > 0) {
|
|
42
|
+
return `${sourcePathBase}.${subpage.value}.${locale.value}.md`
|
|
43
|
+
}
|
|
44
|
+
|
|
40
45
|
const base = store.state.page.base
|
|
41
46
|
if (!base) return ''
|
|
42
47
|
return `${base}.${subpage.value}.${locale.value}.md`
|
|
@@ -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(/\/+$/, '')
|
|
@@ -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(() => {
|
package/src/css/app.sass
CHANGED
|
@@ -179,7 +179,7 @@ body.body--dark
|
|
|
179
179
|
margin: 0.3em 0 0.1em
|
|
180
180
|
|
|
181
181
|
code:not(pre > code)
|
|
182
|
-
padding: 3px
|
|
182
|
+
padding: 1px 3px
|
|
183
183
|
margin: 0 2px
|
|
184
184
|
border-radius: 2px
|
|
185
185
|
white-space: normal
|
|
@@ -288,6 +288,7 @@ body.body--dark
|
|
|
288
288
|
overflow-x: auto
|
|
289
289
|
-webkit-overflow-scrolling: touch
|
|
290
290
|
max-width: 100%
|
|
291
|
+
margin-bottom: 10px
|
|
291
292
|
table
|
|
292
293
|
width: max-content
|
|
293
294
|
border: 1px solid #eee
|