@docsector/docsector-reader 1.7.1 → 2.0.1

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.
@@ -17,6 +17,27 @@
17
17
  </q-toolbar-title>
18
18
  <q-btn class="filled" square icon="settings" aria-label="Configuration" @click="openSettingsDialog" />
19
19
  </q-toolbar>
20
+
21
+ <q-tabs
22
+ v-if="sortedBooks.length > 0"
23
+ :model-value="activeBookTab"
24
+ dense
25
+ align="left"
26
+ narrow-indicator
27
+ class="bg-primary d-book-tabs"
28
+ @update:model-value="onBookTabChange"
29
+ >
30
+ <q-tab
31
+ v-for="book in sortedBooks"
32
+ :key="book.id"
33
+ :name="book.id"
34
+ :label="book.label"
35
+ :icon="book.icon"
36
+ class="d-book-tab"
37
+ :style="getBookTabStyle(book)"
38
+ no-caps
39
+ />
40
+ </q-tabs>
20
41
  </q-header>
21
42
 
22
43
  <q-drawer elevated show-if-above side="left" v-model="layout.menu">
@@ -32,10 +53,12 @@ import { ref, computed } from 'vue'
32
53
  import { useRoute, useRouter } from 'vue-router'
33
54
  import { useStore } from 'vuex'
34
55
  import { useI18n } from 'vue-i18n'
35
- import { useMeta } from 'quasar'
56
+ import { useMeta, colors } from 'quasar'
36
57
 
37
58
  import DMenu from '../components/DMenu.vue'
38
59
  import docsectorConfig from 'docsector.config.js'
60
+ import { allBooks } from 'virtual:docsector-books'
61
+ import { pageTitleI18nPath } from '../i18n/path'
39
62
 
40
63
  defineOptions({ name: 'LayoutDefault' })
41
64
 
@@ -56,12 +79,121 @@ const headerTitleIcon = computed(() => {
56
79
 
57
80
  const headerTitleText = computed(() => {
58
81
  if (store.state.i18n.base) {
59
- return t(`_.${store.state.i18n.base}._`)
82
+ return t(pageTitleI18nPath(store.state.i18n.base))
60
83
  } else {
61
84
  return t(`menu.${route.matched[1].meta.menu}`)
62
85
  }
63
86
  })
64
87
 
88
+ const defaultBookTabColors = Object.freeze({
89
+ active: 'white',
90
+ inactive: 'rgba(255, 255, 255, 0.72)'
91
+ })
92
+
93
+ const quasarBrandColorPattern = /^(primary|secondary|accent|dark|positive|negative|info|warning)$/
94
+ const quasarUtilityColorPattern = /^(white|black|transparent|separator|dark-separator)$/
95
+ const quasarPaletteColorPattern = /^(red|pink|purple|deep-purple|indigo|blue|light-blue|cyan|teal|green|light-green|lime|yellow|amber|orange|deep-orange|brown|grey|blue-grey)(-(?:[1-9]|1[0-4]))?$/
96
+
97
+ const isQuasarColorToken = (token) => {
98
+ return quasarBrandColorPattern.test(token)
99
+ || quasarUtilityColorPattern.test(token)
100
+ || quasarPaletteColorPattern.test(token)
101
+ }
102
+
103
+ const normalizeBookTabColors = (book) => {
104
+ const color = book?.color
105
+
106
+ if (typeof color === 'object' && color !== null && !Array.isArray(color)) {
107
+ const active = typeof color.active === 'string' && color.active.trim().length > 0
108
+ ? color.active.trim()
109
+ : defaultBookTabColors.active
110
+
111
+ const inactive = typeof color.inactive === 'string' && color.inactive.trim().length > 0
112
+ ? color.inactive.trim()
113
+ : active
114
+
115
+ return { active, inactive }
116
+ }
117
+
118
+ if (typeof color === 'string' && color.trim().length > 0) {
119
+ const normalized = color.trim()
120
+ return {
121
+ active: normalized,
122
+ inactive: normalized
123
+ }
124
+ }
125
+
126
+ return { ...defaultBookTabColors }
127
+ }
128
+
129
+ const resolveBookTabColor = (token) => {
130
+ const normalized = String(token || '').trim()
131
+ if (normalized.length === 0) {
132
+ return ''
133
+ }
134
+
135
+ if (normalized.startsWith('var(')) {
136
+ return normalized
137
+ }
138
+
139
+ if (normalized.startsWith('--')) {
140
+ return `var(${normalized})`
141
+ }
142
+
143
+ if (/^(#|rgb\(|rgba\(|hsl\(|hsla\()/i.test(normalized)) {
144
+ return normalized
145
+ }
146
+
147
+ if (['inherit', 'currentColor', 'transparent', 'initial', 'unset', 'revert', 'revert-layer'].includes(normalized)) {
148
+ return normalized
149
+ }
150
+
151
+ if (isQuasarColorToken(normalized)) {
152
+ if (typeof document === 'undefined') {
153
+ return quasarBrandColorPattern.test(normalized)
154
+ ? `var(--q-${normalized})`
155
+ : normalized
156
+ }
157
+
158
+ try {
159
+ return colors.getPaletteColor(normalized)
160
+ } catch {
161
+ return normalized
162
+ }
163
+ }
164
+
165
+ return normalized
166
+ }
167
+
168
+ const getBookTabStyle = (book) => {
169
+ const colors = normalizeBookTabColors(book)
170
+ const active = resolveBookTabColor(colors.active) || resolveBookTabColor(defaultBookTabColors.active)
171
+ const inactive = resolveBookTabColor(colors.inactive) || resolveBookTabColor(defaultBookTabColors.inactive)
172
+
173
+ return {
174
+ '--d-book-tab-active-color': active,
175
+ '--d-book-tab-inactive-color': inactive
176
+ }
177
+ }
178
+
179
+ const sortedBooks = computed(() => {
180
+ return [...(allBooks || [])]
181
+ .filter(book => book && typeof book.id === 'string' && book.id.length > 0)
182
+ .sort((a, b) => {
183
+ const orderA = Number.isFinite(a.order) ? a.order : Number.MAX_SAFE_INTEGER
184
+ const orderB = Number.isFinite(b.order) ? b.order : Number.MAX_SAFE_INTEGER
185
+ return orderA - orderB
186
+ })
187
+ })
188
+
189
+ const activeBookTab = computed(() => {
190
+ const routeBook = route.matched?.[0]?.meta?.book ?? route.meta?.book ?? null
191
+ if (!routeBook || routeBook === 'home') return null
192
+
193
+ const exists = sortedBooks.value.some(book => book.id === routeBook)
194
+ return exists ? routeBook : null
195
+ })
196
+
65
197
  const resolveLocalizedValue = (source) => {
66
198
  if (!source) return ''
67
199
  if (typeof source === 'string') return source
@@ -125,6 +257,43 @@ function openSettingsDialog () {
125
257
  store.commit('settings/dialog', true)
126
258
  }
127
259
 
260
+ function getFirstRoutePathByBook (bookId) {
261
+ const routes = router.options?.routes || []
262
+ let fallbackPath = null
263
+
264
+ for (const topRoute of routes) {
265
+ if (!topRoute || typeof topRoute.path !== 'string') continue
266
+ if ((topRoute.meta?.book ?? topRoute.meta?.type) !== bookId) continue
267
+
268
+ const children = Array.isArray(topRoute.children) ? topRoute.children : []
269
+ const hasOverview = children.some(child => child.path === 'overview')
270
+ if (!hasOverview) continue
271
+
272
+ const candidatePath = `${topRoute.path.replace(/\/$/, '')}/overview/`
273
+ if (fallbackPath === null) {
274
+ fallbackPath = candidatePath
275
+ }
276
+
277
+ const hasInternalLink = typeof topRoute.meta?.link?.to === 'string' && topRoute.meta.link.to.trim().length > 0
278
+ if (hasInternalLink) {
279
+ continue
280
+ }
281
+
282
+ return candidatePath
283
+ }
284
+
285
+ return fallbackPath || '/'
286
+ }
287
+
288
+ function onBookTabChange (bookId) {
289
+ if (!bookId) return
290
+
291
+ const targetPath = getFirstRoutePathByBook(bookId)
292
+ if (route.path !== targetPath) {
293
+ router.push(targetPath)
294
+ }
295
+ }
296
+
128
297
  // --- created logic (runs at setup time) ---
129
298
  store.dispatch('app/configureLanguage', route.matched)
130
299
 
@@ -143,6 +312,16 @@ store.commit('page/resetAnchors')
143
312
  &.left-btn
144
313
  .q-toolbar
145
314
  padding: 0
315
+ .q-tabs
316
+ margin-top: 2px
317
+ .d-book-tabs
318
+ .q-tab__indicator
319
+ background-color: currentColor
320
+ .d-book-tab
321
+ color: var(--d-book-tab-inactive-color, rgba(255, 255, 255, 0.72))
322
+ transition: color 0.2s ease
323
+ .d-book-tab.q-tab--active
324
+ color: var(--d-book-tab-active-color, #ffffff)
146
325
  .q-btn
147
326
  border-radius: 0
148
327
  .q-btn:before
@@ -59,7 +59,8 @@ After `init`, your project will have this structure:
59
59
  - `docsector.config.js` — Branding, links, languages, GitHub config
60
60
  - `quasar.config.js` — Thin wrapper using `createQuasarConfig()` from the package
61
61
  - `index.html` — HTML entry point with title and meta tags
62
- - `src/pages/index.js` — Page registry (routes and metadata)
62
+ - `src/pages/guide.book.js` — Guide book metadata (tab)
63
+ - `src/pages/guide.index.js` — Guide page registry (routes and metadata)
63
64
  - `src/pages/guide/` — Guide-type pages (Markdown files)
64
65
  - `src/pages/manual/` — Manual-type pages (Markdown files)
65
66
  - `src/i18n/index.js` — i18n loader using `buildMessages()` from the package
@@ -71,7 +72,7 @@ The rendering engine (components, layouts, router, store, composables) lives ins
71
72
  ## Next Steps
72
73
 
73
74
  - Configure your project branding in **docsector.config.js**
74
- - Define your pages in **src/pages/index.js**
75
+ - Define your pages in **src/pages/guide.index.js** (and other `*.index.js` registries)
75
76
  - Write your documentation in **Markdown**
76
77
  - Add search keywords in **src/i18n/tags.hjson**
77
78
  - Customize themes and appearance
@@ -59,7 +59,8 @@ Após o `init`, seu projeto terá esta estrutura:
59
59
  - `docsector.config.js` — Branding, links, idiomas, config do GitHub
60
60
  - `quasar.config.js` — Wrapper fino usando `createQuasarConfig()` do pacote
61
61
  - `index.html` — Ponto de entrada HTML com título e meta tags
62
- - `src/pages/index.js` — Registro de páginas (rotas e metadata)
62
+ - `src/pages/guide.book.js` — Metadata do book Guide (aba)
63
+ - `src/pages/guide.index.js` — Registro de páginas do Guide (rotas e metadata)
63
64
  - `src/pages/guide/` — Páginas tipo guia (arquivos Markdown)
64
65
  - `src/pages/manual/` — Páginas tipo manual (arquivos Markdown)
65
66
  - `src/i18n/index.js` — Loader i18n usando `buildMessages()` do pacote
@@ -71,7 +72,7 @@ O motor de renderização (componentes, layouts, router, store, composables) fic
71
72
  ## Próximos Passos
72
73
 
73
74
  - Configure o branding do seu projeto em **docsector.config.js**
74
- - Defina suas páginas em **src/pages/index.js**
75
+ - Defina suas páginas em **src/pages/guide.index.js** (e outros registros `*.index.js`)
75
76
  - Escreva sua documentação em **Markdown**
76
77
  - Adicione palavras-chave de busca em **src/i18n/tags.hjson**
77
78
  - Personalize temas e aparência
@@ -1,6 +1,6 @@
1
1
  ## Page Registry
2
2
 
3
- All documentation pages are defined in `src/pages/index.js`. Each entry maps a URL path to its configuration, translatable data, and optional metadata.
3
+ Documentation pages are defined in split registries such as `src/pages/guide.index.js` and `src/pages/manual.index.js`. Each entry maps a URL path to its configuration, translatable data, and optional metadata.
4
4
 
5
5
  ## Page Entry Structure
6
6
 
@@ -9,7 +9,7 @@ All documentation pages are defined in `src/pages/index.js`. Each entry maps a U
9
9
  config: &#123;
10
10
  icon: 'description',
11
11
  status: 'done',
12
- type: 'guide',
12
+ book: 'guide',
13
13
  menu: &#123;&#125;,
14
14
  subpages: &#123; showcase: false &#125;
15
15
  &#125;,
@@ -22,7 +22,7 @@ All documentation pages are defined in `src/pages/index.js`. Each entry maps a U
22
22
 
23
23
  ## Config Properties
24
24
 
25
- - **type** — Route prefix: `'guide'`, `'manual'`, or `'API'`
25
+ - **book** — Route prefix: `'guide'`, `'manual'`, or `'API'` (legacy `type` is still supported)
26
26
  - **status** — Page status: `'done'`, `'draft'`, or `'empty'`
27
27
  - **icon** — Material Design icon name shown in the sidebar
28
28
  - **menu** — Object controlling menu display (header, subheader, separator)
@@ -68,9 +68,9 @@ menu: &#123;
68
68
 
69
69
  Each page requires Markdown files following this naming pattern:
70
70
 
71
- `src/pages/&#123;type&#125;/&#123;path&#125;.&#123;subpage&#125;.&#123;lang&#125;.md`
71
+ `src/pages/&#123;book&#125;/&#123;path&#125;.&#123;subpage&#125;.&#123;lang&#125;.md`
72
72
 
73
- For example, a page at `/components/d-page` with type `manual`:
73
+ For example, a page at `/components/d-page` with book `manual`:
74
74
 
75
75
  - `src/pages/manual/components/d-page.overview.en-US.md`
76
76
  - `src/pages/manual/components/d-page.overview.pt-BR.md`
@@ -78,7 +78,7 @@ For example, a page at `/components/d-page` with type `manual`:
78
78
 
79
79
  ## Route Generation
80
80
 
81
- Routes are automatically generated from the page registry. A page with path `/my-page` and type `guide` produces:
81
+ Routes are automatically generated from the page registry. A page with path `/my-page` and book `guide` produces:
82
82
 
83
83
  - `/guide/my-page/overview` — Main content tab
84
84
  - `/guide/my-page/showcase` — Showcase tab (if enabled)
@@ -1,6 +1,6 @@
1
1
  ## Registro de Páginas
2
2
 
3
- Todas as páginas de documentação são definidas em `src/pages/index.js`. Cada entrada mapeia um caminho URL para sua configuração, dados traduzíveis e metadata opcional.
3
+ As páginas de documentação são definidas em registros separados, como `src/pages/guide.index.js` e `src/pages/manual.index.js`. Cada entrada mapeia um caminho URL para sua configuração, dados traduzíveis e metadata opcional.
4
4
 
5
5
  ## Estrutura de uma Entrada
6
6
 
@@ -9,7 +9,7 @@ Todas as páginas de documentação são definidas em `src/pages/index.js`. Cada
9
9
  config: &#123;
10
10
  icon: 'description',
11
11
  status: 'done',
12
- type: 'guide',
12
+ book: 'guide',
13
13
  menu: &#123;&#125;,
14
14
  subpages: &#123; showcase: false &#125;
15
15
  &#125;,
@@ -22,7 +22,7 @@ Todas as páginas de documentação são definidas em `src/pages/index.js`. Cada
22
22
 
23
23
  ## Propriedades do Config
24
24
 
25
- - **type** — Prefixo da rota: `'guide'`, `'manual'` ou `'API'`
25
+ - **book** — Prefixo da rota: `'guide'`, `'manual'` ou `'API'` (compatível com `type` legado)
26
26
  - **status** — Status da página: `'done'`, `'draft'` ou `'empty'`
27
27
  - **icon** — Nome do ícone Material Design exibido no menu lateral
28
28
  - **menu** — Objeto controlando exibição do menu (header, subheader, separator)
@@ -68,9 +68,9 @@ menu: &#123;
68
68
 
69
69
  Cada página requer arquivos Markdown seguindo este padrão de nomenclatura:
70
70
 
71
- `src/pages/&#123;type&#125;/&#123;path&#125;.&#123;subpage&#125;.&#123;lang&#125;.md`
71
+ `src/pages/&#123;book&#125;/&#123;path&#125;.&#123;subpage&#125;.&#123;lang&#125;.md`
72
72
 
73
- Por exemplo, uma página em `/components/d-page` com type `manual`:
73
+ Por exemplo, uma página em `/components/d-page` com book `manual`:
74
74
 
75
75
  - `src/pages/manual/components/d-page.overview.en-US.md`
76
76
  - `src/pages/manual/components/d-page.overview.pt-BR.md`
@@ -78,7 +78,7 @@ Por exemplo, uma página em `/components/d-page` com type `manual`:
78
78
 
79
79
  ## Geração de Rotas
80
80
 
81
- Rotas são geradas automaticamente a partir do registro de páginas. Uma página com path `/my-page` e type `guide` produz:
81
+ Rotas são geradas automaticamente a partir do registro de páginas. Uma página com path `/my-page` e book `guide` produz:
82
82
 
83
83
  - `/guide/my-page/overview` — Aba de conteúdo principal
84
84
  - `/guide/my-page/showcase` — Aba de demonstração (se habilitada)
@@ -0,0 +1,12 @@
1
+ import { defineBook } from '../index.js'
2
+
3
+ export default defineBook({
4
+ id: 'guide',
5
+ label: 'Guide',
6
+ icon: 'school',
7
+ order: 2,
8
+ color: {
9
+ active: 'white',
10
+ inactive: 'secondary'
11
+ }
12
+ })
@@ -0,0 +1,184 @@
1
+ export default {
2
+ '/getting-started': {
3
+ config: {
4
+ icon: 'flag',
5
+ status: 'done',
6
+ meta: {
7
+ description: {
8
+ 'en-US': 'Getting Started — Documentation of Docsector Reader',
9
+ 'pt-BR': 'Começando — Documentacao do Docsector Reader'
10
+ }
11
+ },
12
+ book: 'guide',
13
+ menu: {
14
+ header: {
15
+ icon: 'school',
16
+ label: 'Guides'
17
+ }
18
+ },
19
+ subpages: {
20
+ showcase: false
21
+ }
22
+ },
23
+ data: {
24
+ 'en-US': { title: 'Getting Started' },
25
+ 'pt-BR': { title: 'Começando' }
26
+ }
27
+ },
28
+
29
+ '/configuration': {
30
+ config: {
31
+ icon: 'tune',
32
+ status: 'done',
33
+ meta: {
34
+ description: {
35
+ 'en-US': 'Configuration — Documentation of Docsector Reader',
36
+ 'pt-BR': 'Configuração — Documentacao do Docsector Reader'
37
+ }
38
+ },
39
+ book: 'guide',
40
+ menu: {
41
+ separator: ' page'
42
+ },
43
+ subpages: {
44
+ showcase: false
45
+ }
46
+ },
47
+ data: {
48
+ 'en-US': { title: 'Configuration' },
49
+ 'pt-BR': { title: 'Configuração' }
50
+ }
51
+ },
52
+
53
+ '/pages-and-routing': {
54
+ config: {
55
+ icon: 'route',
56
+ status: 'done',
57
+ meta: {
58
+ description: {
59
+ 'en-US': 'Pages & Routing — Documentation of Docsector Reader',
60
+ 'pt-BR': 'Páginas & Rotas — Documentacao do Docsector Reader'
61
+ }
62
+ },
63
+ book: 'guide',
64
+ menu: {},
65
+ subpages: {
66
+ showcase: false
67
+ }
68
+ },
69
+ data: {
70
+ 'en-US': { title: 'Pages & Routing' },
71
+ 'pt-BR': { title: 'Páginas & Rotas' }
72
+ }
73
+ },
74
+
75
+ '/i18n-and-markdown': {
76
+ config: {
77
+ icon: 'translate',
78
+ status: 'done',
79
+ meta: {
80
+ description: {
81
+ 'en-US': 'i18n & Markdown — Documentation of Docsector Reader',
82
+ 'pt-BR': 'i18n & Markdown — Documentacao do Docsector Reader'
83
+ }
84
+ },
85
+ book: 'guide',
86
+ menu: {},
87
+ subpages: {
88
+ showcase: false
89
+ }
90
+ },
91
+ data: {
92
+ 'en-US': { title: 'i18n & Markdown' },
93
+ 'pt-BR': { title: 'i18n & Markdown' }
94
+ }
95
+ },
96
+
97
+ '/alerts-and-blockquotes': {
98
+ config: {
99
+ icon: 'notification_important',
100
+ status: 'done',
101
+ meta: {
102
+ description: {
103
+ 'en-US': 'Alerts & Blockquotes — Documentation of Docsector Reader',
104
+ 'pt-BR': 'Alertas & Blockquotes — Documentacao do Docsector Reader'
105
+ }
106
+ },
107
+ book: 'guide',
108
+ menu: {},
109
+ subpages: {
110
+ showcase: false
111
+ }
112
+ },
113
+ data: {
114
+ 'en-US': { title: 'Alerts & Blockquotes' },
115
+ 'pt-BR': { title: 'Alertas & Blockquotes' }
116
+ }
117
+ },
118
+
119
+ '/theming': {
120
+ config: {
121
+ icon: 'palette',
122
+ status: 'done',
123
+ meta: {
124
+ description: {
125
+ 'en-US': 'Theming — Documentation of Docsector Reader',
126
+ 'pt-BR': 'Temas — Documentacao do Docsector Reader'
127
+ }
128
+ },
129
+ book: 'guide',
130
+ menu: {},
131
+ subpages: {
132
+ showcase: false
133
+ }
134
+ },
135
+ data: {
136
+ 'en-US': { title: 'Theming' },
137
+ 'pt-BR': { title: 'Temas' }
138
+ }
139
+ },
140
+
141
+ '/deployment': {
142
+ config: {
143
+ icon: 'cloud_upload',
144
+ status: 'draft',
145
+ meta: {
146
+ description: {
147
+ 'en-US': 'Deployment — Documentation of Docsector Reader',
148
+ 'pt-BR': 'Deploy — Documentacao do Docsector Reader'
149
+ }
150
+ },
151
+ book: 'guide',
152
+ menu: {},
153
+ subpages: {
154
+ showcase: false
155
+ }
156
+ },
157
+ data: {
158
+ 'en-US': { title: 'Deployment' },
159
+ 'pt-BR': { title: 'Deploy' }
160
+ }
161
+ },
162
+
163
+ '/plugins': {
164
+ config: {
165
+ icon: 'extension',
166
+ status: 'empty',
167
+ meta: {
168
+ description: {
169
+ 'en-US': 'Plugins — Documentation of Docsector Reader',
170
+ 'pt-BR': 'Plugins — Documentacao do Docsector Reader'
171
+ }
172
+ },
173
+ book: 'guide',
174
+ menu: {},
175
+ subpages: {
176
+ showcase: false
177
+ }
178
+ },
179
+ data: {
180
+ 'en-US': { title: 'Plugins' },
181
+ 'pt-BR': { title: 'Plugins' }
182
+ }
183
+ }
184
+ }
@@ -0,0 +1,12 @@
1
+ import { defineBook } from '../index.js'
2
+
3
+ export default defineBook({
4
+ id: 'manual',
5
+ label: 'Manual',
6
+ icon: 'menu_book',
7
+ order: 1,
8
+ color: {
9
+ active: 'white',
10
+ inactive: 'primary'
11
+ }
12
+ })