@docsector/docsector-reader 2.1.0 → 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 CHANGED
@@ -54,7 +54,7 @@ Transform Markdown content into beautiful, navigable documentation sites — wit
54
54
  - 📚 **Book Tabs with Per-State Colors** — Define `*.book.js` tabs with icons, order, and `color.active` / `color.inactive`
55
55
  - 🔀 **Internal Shortcut Pages** — Route entries can redirect with `config.link.to`, keeping localized titles while inheriting icon/status from the destination page
56
56
  - 📐 **Responsive Subpage Toolbar** — Subpage actions align with the content column on desktop and dock to the bottom on mobile
57
- - 🏷️ **Status Badges** — Mark pages as `done`, `draft`, or `empty` with visual indicators
57
+ - 🏷️ **Status Badges** — Mark pages as `done`, `draft`, `empty`, or `new` with visual indicators
58
58
  - ✏️ **Edit on GitHub** — Direct links to edit pages on your repository
59
59
  - 🧭 **Robust Edit Link Mapping** — Normalizes route paths (including trailing slashes) into `page.subpage.locale.md` source files for reliable GitHub edit URLs
60
60
  - 📅 **Last Updated Date** — Automatic per-page "last updated" date from git commit history, locale-formatted
@@ -903,7 +903,8 @@ export default {
903
903
  '/my-section/my-page': definePage({
904
904
  config: {
905
905
  icon: 'description',
906
- status: 'done', // 'done' | 'draft' | 'empty'
906
+ status: 'new', // 'done' | 'draft' | 'empty' | 'new'
907
+ version: 'v2.1.0', // Optional: shown as "New in" / "Novo em"
907
908
  menu: {
908
909
  header: { label: '.my-section', icon: 'category' }
909
910
  },
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.1.0'
26
+ const VERSION = '2.2.0'
27
27
 
28
28
  const HELP = `
29
29
  Docsector Reader v${VERSION}
@@ -315,6 +315,7 @@ const TEMPLATE_I18N_HJSON = `\
315
315
  prev: 'Previous page'
316
316
  next: 'Next page'
317
317
  }
318
+ newVersion: 'New in'
318
319
  }
319
320
 
320
321
  // @ Menu
@@ -336,6 +337,19 @@ const TEMPLATE_I18N_HJSON = `\
336
337
  _: 'draft'
337
338
  tooltip: 'This page is under construction.'
338
339
  }
340
+ new: {
341
+ _: 'new'
342
+ tooltip: 'This page is new.'
343
+ tooltipVersion: 'New in {version}'
344
+ }
345
+ }
346
+
347
+ version: {
348
+ status: {
349
+ released: 'released'
350
+ draft: 'draft'
351
+ deprecated: 'deprecated'
352
+ }
339
353
  }
340
354
 
341
355
  settings: 'Settings'
@@ -406,7 +420,8 @@ const TEMPLATE_PAGES_INDEX = `\
406
420
  * and each value configures the page's book, icon, status, and titles.
407
421
  *
408
422
  * config.book: top-level route prefix — 'guide', 'manual', etc.
409
- * config.status: 'done' | 'draft' | 'empty'
423
+ * config.status: 'done' | 'draft' | 'empty' | 'new'
424
+ * config.version: optional version where this page was introduced (e.g. 'v2.1.0')
410
425
  * config.meta.description: string or localized object for SEO/social description
411
426
  * config.icon: Material Design icon name
412
427
  * config.menu: menu display options (header, subheader, separator)
@@ -419,7 +434,8 @@ export default {
419
434
  '/getting-started': {
420
435
  config: {
421
436
  icon: 'flag',
422
- status: 'done',
437
+ status: 'new',
438
+ version: 'v0.1.0',
423
439
  meta: {
424
440
  description: {
425
441
  'en-US': 'Get started quickly with setup and project structure.'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docsector/docsector-reader",
3
- "version": "2.1.0",
3
+ "version": "2.2.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",
@@ -44,6 +44,23 @@ const activeBooks = computed(() => {
44
44
 
45
45
  const draftReleaseStatuses = new Set(['draft', 'unreleased', 'preview', 'next'])
46
46
 
47
+ const versionStatusLabel = (label, releaseStatus) => {
48
+ const normalizedStatus = String(releaseStatus || '').toLowerCase()
49
+ const normalizedLabel = String(label || normalizedStatus).toLowerCase()
50
+ const statusKey = normalizedStatus ? `menu.version.status.${normalizedStatus}` : null
51
+ const labelKey = normalizedLabel ? `menu.version.status.${normalizedLabel}` : null
52
+
53
+ if (statusKey && (!label || normalizedLabel === normalizedStatus) && te(statusKey)) {
54
+ return t(statusKey)
55
+ }
56
+
57
+ if (labelKey && te(labelKey)) {
58
+ return t(labelKey)
59
+ }
60
+
61
+ return label || releaseStatus
62
+ }
63
+
47
64
  const normalizeVersionBadge = (item) => {
48
65
  const configuredStatus = item.deprecated === true
49
66
  ? 'deprecated'
@@ -59,11 +76,11 @@ const normalizeVersionBadge = (item) => {
59
76
  const defaultTextColor = (deprecated || released) ? 'white' : 'dark'
60
77
 
61
78
  if (rawBadge === false || rawBadge === null) {
62
- return { label: releaseStatus, color: defaultColor, textColor: defaultTextColor }
79
+ return { label: versionStatusLabel(releaseStatus, releaseStatus), color: defaultColor, textColor: defaultTextColor }
63
80
  }
64
81
 
65
82
  if (typeof rawBadge === 'string') {
66
- return { label: rawBadge, color: defaultColor, textColor: defaultTextColor }
83
+ return { label: versionStatusLabel(rawBadge, releaseStatus), color: defaultColor, textColor: defaultTextColor }
67
84
  }
68
85
 
69
86
  if (typeof rawBadge === 'object' && rawBadge !== null) {
@@ -74,13 +91,13 @@ const normalizeVersionBadge = (item) => {
74
91
 
75
92
  return {
76
93
  ...rawBadge,
77
- label,
94
+ label: versionStatusLabel(label, releaseStatus),
78
95
  color: rawBadge.color || defaultColor,
79
96
  textColor: rawBadge.textColor || defaultTextColor
80
97
  }
81
98
  }
82
99
 
83
- return { label: releaseStatus, color: defaultColor, textColor: defaultTextColor }
100
+ return { label: versionStatusLabel(releaseStatus, releaseStatus), color: defaultColor, textColor: defaultTextColor }
84
101
  }
85
102
 
86
103
  const versionOptions = computed(() => {
@@ -53,6 +53,10 @@ const getMenuItemSubheader = (meta = {}) => {
53
53
  }
54
54
 
55
55
  const getPageStatusText = (status) => {
56
+ if (status === 'new') {
57
+ return t('menu.status.new._')
58
+ }
59
+
56
60
  if (status === 'draft') {
57
61
  return t('menu.status.draft._')
58
62
  } else {
@@ -61,6 +65,10 @@ const getPageStatusText = (status) => {
61
65
  }
62
66
 
63
67
  const getPageStatusTextColor = (status) => {
68
+ if (status === 'new') {
69
+ return null
70
+ }
71
+
64
72
  if (status === 'draft') {
65
73
  return 'dark'
66
74
  } else {
@@ -68,7 +76,15 @@ const getPageStatusTextColor = (status) => {
68
76
  }
69
77
  }
70
78
 
79
+ const getPageStatusStyle = (status) => {
80
+ return status === 'new' ? { color: '#151515' } : null
81
+ }
82
+
71
83
  const getPageStatusColor = (status) => {
84
+ if (status === 'new') {
85
+ return 'positive'
86
+ }
87
+
72
88
  if (status === 'draft') {
73
89
  return 'orange'
74
90
  } else {
@@ -76,7 +92,13 @@ const getPageStatusColor = (status) => {
76
92
  }
77
93
  }
78
94
 
79
- const getPageStatusTooltip = (status) => {
95
+ const getPageStatusTooltip = (status, pageVersion) => {
96
+ if (status === 'new') {
97
+ return pageVersion
98
+ ? t('menu.status.new.tooltipVersion', { version: pageVersion })
99
+ : t('menu.status.new.tooltip')
100
+ }
101
+
80
102
  if (status === 'draft') {
81
103
  return t('menu.status.draft.tooltip')
82
104
  } else {
@@ -148,8 +170,9 @@ const onMenuItemClick = (event, path, currentSubpage) => {
148
170
  :text-color="getPageStatusTextColor(subitem.meta.status)"
149
171
  :color="getPageStatusColor(subitem.meta.status)"
150
172
  :label="getPageStatusText(subitem.meta.status)"
173
+ :style="getPageStatusStyle(subitem.meta.status)"
151
174
  />
152
- <q-tooltip :hide-delay="3">{{ getPageStatusTooltip(subitem.meta.status) }}</q-tooltip>
175
+ <q-tooltip :hide-delay="3">{{ getPageStatusTooltip(subitem.meta.status, subitem.meta.pageVersion) }}</q-tooltip>
153
176
  </q-item-section>
154
177
  </q-item>
155
178
 
@@ -61,6 +61,11 @@ const formattedDate = computed(() => {
61
61
  }).format(date)
62
62
  })
63
63
 
64
+ const pageVersion = computed(() => {
65
+ const value = route.meta?.pageVersion
66
+ return typeof value === 'string' ? value.trim() : ''
67
+ })
68
+
64
69
  const rawMarkdown = computed(() => {
65
70
  const absolute = store.state.i18n.absolute
66
71
  if (!absolute) return ''
@@ -192,10 +197,14 @@ const copyPage = () => {
192
197
 
193
198
  <template>
194
199
  <div class="d-page-bar">
195
- <span v-if="formattedDate" class="d-page-bar__date">
196
- {{ t('page.lastUpdated') }}: <br class="d-page-bar__date-break"> {{ formattedDate }}
197
- </span>
198
- <span v-else class="d-page-bar__date"></span>
200
+ <div class="d-page-bar__meta">
201
+ <span v-if="formattedDate" class="d-page-bar__date">
202
+ {{ t('page.lastUpdated') }}: <br class="d-page-bar__date-break"> {{ formattedDate }}
203
+ </span>
204
+ <span v-if="pageVersion" class="d-page-bar__new-in">
205
+ {{ t('page.newVersion') }}: {{ pageVersion }}
206
+ </span>
207
+ </div>
199
208
 
200
209
  <q-btn-dropdown
201
210
  class="d-page-bar__actions"
@@ -343,6 +352,18 @@ const copyPage = () => {
343
352
  font-size: 0.8rem
344
353
  opacity: 0.6
345
354
 
355
+ &__meta
356
+ display: flex
357
+ flex-direction: column
358
+ min-height: 1.5rem
359
+
360
+ &__new-in
361
+ font-size: 0.8rem
362
+ opacity: 0.6
363
+ margin-top: 8px
364
+ padding-top: 8px
365
+ border-top: 1px solid rgba(0, 0, 0, 0.12)
366
+
346
367
  &__date-break
347
368
  display: none
348
369
 
@@ -350,9 +371,13 @@ const copyPage = () => {
350
371
  font-size: 0.75rem
351
372
 
352
373
  body.body--dark
353
- .d-page-bar__date
374
+ .d-page-bar__date,
375
+ .d-page-bar__new-in
354
376
  color: rgba(255, 255, 255, 0.7)
355
377
 
378
+ .d-page-bar__new-in
379
+ border-top-color: rgba(255, 255, 255, 0.18)
380
+
356
381
  @media (max-width: 376px)
357
382
  .d-page-bar__date-break
358
383
  display: block
@@ -54,7 +54,7 @@ const URL = computed(() => {
54
54
  return `${base}${path}.${locale.value}.md`
55
55
  })
56
56
  const color = computed(() => {
57
- if (status.value === 'done') {
57
+ if (status.value === 'done' || status.value === 'new') {
58
58
  return 'white'
59
59
  } else if (status.value === 'draft') {
60
60
  return 'warning'
@@ -63,7 +63,7 @@ const color = computed(() => {
63
63
  }
64
64
  })
65
65
  const icon = computed(() => {
66
- if (status.value === 'done') {
66
+ if (status.value === 'done' || status.value === 'new') {
67
67
  return 'edit'
68
68
  } else if (status.value === 'draft') {
69
69
  return 'border_color'
@@ -194,7 +194,7 @@ const getRouteTitle = (path) => {
194
194
  <div id="d-page-edit" class="col">
195
195
  <q-btn dense no-caps text-color="black" :color="color" @click="openURL(URL)" aria-label="Edit page on Github">
196
196
  <q-icon class="q-mr-xs" name="fab fa-github" size="20px" />
197
- <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>
198
198
  <span class="hm" v-else-if="status === 'draft'">{{ $t('page.edit.github.complete') }}</span>
199
199
  <span class="hm" v-else-if="status === 'empty'">{{ $t('page.edit.github.start') }}</span>
200
200
  </q-btn>
@@ -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
  }
@@ -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
 
@@ -8,7 +8,8 @@ Documentation pages are defined in split registries such as `src/pages/guide.ind
8
8
  '/my-page': &#123;
9
9
  config: &#123;
10
10
  icon: 'description',
11
- status: 'done',
11
+ status: 'new',
12
+ version: 'v2.1.0',
12
13
  book: 'guide',
13
14
  menu: &#123;&#125;,
14
15
  subpages: &#123; showcase: false &#125;
@@ -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 `'empty'`
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`
@@ -8,7 +8,8 @@ As páginas de documentação são definidas em registros separados, como `src/p
8
8
  '/minha-pagina': &#123;
9
9
  config: &#123;
10
10
  icon: 'description',
11
- status: 'done',
11
+ status: 'new',
12
+ version: 'v2.1.0',
12
13
  book: 'guide',
13
14
  menu: &#123;&#125;,
14
15
  subpages: &#123; showcase: false &#125;
@@ -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 `'empty'`
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`
@@ -30,6 +30,7 @@ export default {
30
30
  config: {
31
31
  icon: 'tune',
32
32
  status: 'done',
33
+ version: 'v2.1.0',
33
34
  meta: {
34
35
  description: {
35
36
  'en-US': 'Configuration — Documentation of Docsector Reader',
@@ -53,7 +54,8 @@ export default {
53
54
  '/pages-and-routing': {
54
55
  config: {
55
56
  icon: 'route',
56
- status: 'done',
57
+ status: 'new',
58
+ version: 'v2.1.0',
57
59
  meta: {
58
60
  description: {
59
61
  'en-US': 'Pages & Routing — Documentation of Docsector Reader',
@@ -17,6 +17,7 @@ The button label changes based on page status:
17
17
  | Status | Label | Color |
18
18
  |--------|-------|-------|
19
19
  | `done` | "Edit this page" | White |
20
+ | `new` | "Edit this page" | White |
20
21
  | `draft` | "Complete this page" | Warning (orange) |
21
22
  | `empty` | "Start this page" | Red |
22
23
 
@@ -17,6 +17,7 @@ O label do botão muda baseado no status da página:
17
17
  | Status | Label | Cor |
18
18
  |--------|-------|-----|
19
19
  | `done` | "Edite esta página" | Branco |
20
+ | `new` | "Edite esta página" | Branco |
20
21
  | `draft` | "Complete esta página" | Warning (laranja) |
21
22
  | `empty` | "Comece esta página" | Vermelho |
22
23
 
@@ -183,7 +183,8 @@ export default {
183
183
  '/components/d-menu': {
184
184
  config: {
185
185
  icon: 'menu_open',
186
- status: 'done',
186
+ status: 'new',
187
+ version: 'v2.1.0',
187
188
  meta: {
188
189
  description: {
189
190
  'en-US': 'DMenu — Documentation of Docsector Reader',
@@ -98,6 +98,7 @@ for (const entry of pageEntries || []) {
98
98
  const status = typeof config.status === 'string'
99
99
  ? config.status
100
100
  : (typeof linkedConfig?.status === 'string' ? linkedConfig.status : 'done')
101
+ const pageVersion = config.version ?? config.pageVersion ?? config.since ?? null
101
102
 
102
103
  // @ Construct children
103
104
  const children = hasInternalLink
@@ -152,6 +153,7 @@ for (const entry of pageEntries || []) {
152
153
  ...config,
153
154
  icon,
154
155
  status,
156
+ pageVersion,
155
157
  menu,
156
158
  subpages,
157
159
  data: page.data,