@docsector/docsector-reader 4.0.1 → 4.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.
@@ -0,0 +1,56 @@
1
+ ## Visão geral
2
+
3
+ Blocos de exemplos de código renderizam SFCs Vue do projeto como previews ao vivo dentro das páginas Markdown.
4
+
5
+ Eles são úteis quando a documentação precisa mostrar o comportamento real de um componente e ainda permitir que leitores inspecionem o código fonte exato por trás do preview.
6
+
7
+ O bloco é escrito com o elemento Markdown customizado `<d-block-code-example>`.
8
+
9
+ ## Arquivos de exemplo
10
+
11
+ Coloque os componentes de exemplo em `src/examples/**/*.vue` no projeto que usa o Docsector.
12
+
13
+ O Docsector descobre esses arquivos durante o build com Vite. O valor de `src` é normalizado para kebab-case, então este bloco:
14
+
15
+ ```html
16
+ <d-block-code-example src="manual/code-examples/basic-counter" title="Basic counter" />
17
+ ```
18
+
19
+ resolve este arquivo:
20
+
21
+ ```text
22
+ src/examples/manual/code-examples/BasicCounter.vue
23
+ ```
24
+
25
+ Também é possível usar `file` no lugar de `src` ao migrar exemplos inspirados nos padrões do Quasar Docs.
26
+
27
+ ## Atributos
28
+
29
+ | Atributo | Finalidade |
30
+ |----------|------------|
31
+ | `src` | Id do exemplo dentro de `src/examples/**/*.vue` |
32
+ | `file` | Alias de `src` |
33
+ | `title` | Título exibido acima do preview |
34
+ | `expanded` | Abre o painel de fonte por padrão quando definido como `true` |
35
+ | `codepen` | Mostra a ação do CodePen, exceto quando definido como `false` |
36
+ | `scrollable` | Dá ao preview uma altura fixa com rolagem |
37
+ | `overflow` | Permite overflow horizontal e vertical no preview |
38
+ | `height` | Define uma altura customizada para o preview, como `360` ou `420px` |
39
+
40
+ ## Painel de fonte
41
+
42
+ O botão de fonte abre o SFC Vue dividido em abas Template, Script, Style e All quando essas seções existem.
43
+
44
+ O painel reutiliza o renderizador padrão de blocos de código do Docsector, então leitores recebem syntax highlighting, suporte a cópia e o mesmo tratamento visual em temas claro e escuro.
45
+
46
+ ## Link de fonte no GitHub
47
+
48
+ O botão do GitHub abre o SFC do exemplo no repositório do projeto quando o Docsector consegue derivar a URL a partir de `github.editBaseUrl` ou `links.github` em `docsector.config.js`.
49
+
50
+ ## Exportação para CodePen
51
+
52
+ O botão do CodePen fica disponível quando o código pode ser transformado com segurança para uma demo somente no navegador.
53
+
54
+ A primeira implementação suporta SFCs Vue simples com template, style opcional e script Options API com `export default`. Imports nomeados de `vue` e `quasar` são convertidos para globais do navegador. Exemplos que usam `<script setup>`, scripts TypeScript ou imports locais continuam renderizando no Docsector, mas a ação do CodePen fica desativada.
55
+
56
+ Use `codepen="false"` quando um exemplo intencionalmente não deve ser exportado.
@@ -0,0 +1,38 @@
1
+ ## Showcase
2
+
3
+ ### Basic Counter
4
+
5
+ This example is rendered from a real Vue SFC under `src/examples/manual/code-examples/BasicCounter.vue`.
6
+
7
+ <d-block-code-example src="manual/code-examples/basic-counter" title="Basic counter">
8
+ Use the source button to inspect the SFC sections, or open the compatible demo in CodePen.
9
+ </d-block-code-example>
10
+
11
+ ### Expanded Source by Default
12
+
13
+ Use `expanded="true"` when the source code is part of the lesson and should be visible as soon as the reader reaches the example.
14
+
15
+ <d-block-code-example src="manual/code-examples/inline-notice" title="Expanded source example" expanded="true">
16
+ This example intentionally starts with the source panel open.
17
+ </d-block-code-example>
18
+
19
+ ## Authoring Syntax
20
+
21
+ ```html
22
+ <d-block-code-example src="manual/code-examples/basic-counter" title="Basic counter">
23
+ Optional caption rendered as inline Markdown.
24
+ </d-block-code-example>
25
+
26
+ <d-block-code-example src="manual/code-examples/inline-notice" title="Expanded source example" expanded="true">
27
+ The source panel starts open.
28
+ </d-block-code-example>
29
+ ```
30
+
31
+ ## Features Visible Above
32
+
33
+ - **Live preview** rendered from the bundled Vue component
34
+ - **Source toggle** with Template, Script, Style, and All tabs
35
+ - **CodePen action** for compatible examples
36
+ - **GitHub action** pointing to the example SFC
37
+ - **Expanded source state** with `expanded="true"`
38
+ - **Inline Markdown caption** below the preview
@@ -0,0 +1,38 @@
1
+ ## Showcase
2
+
3
+ ### Contador básico
4
+
5
+ Este exemplo é renderizado a partir de um SFC Vue real em `src/examples/manual/code-examples/BasicCounter.vue`.
6
+
7
+ <d-block-code-example src="manual/code-examples/basic-counter" title="Basic counter">
8
+ Use o botão de fonte para inspecionar as seções do SFC, ou abra a demo compatível no CodePen.
9
+ </d-block-code-example>
10
+
11
+ ### Fonte expandida por padrão
12
+
13
+ Use `expanded="true"` quando o código fonte faz parte da explicação e deve aparecer assim que o leitor chegar ao exemplo.
14
+
15
+ <d-block-code-example src="manual/code-examples/inline-notice" title="Expanded source example" expanded="true">
16
+ Este exemplo intencionalmente começa com o painel de fonte aberto.
17
+ </d-block-code-example>
18
+
19
+ ## Sintaxe de autoria
20
+
21
+ ```html
22
+ <d-block-code-example src="manual/code-examples/basic-counter" title="Basic counter">
23
+ Legenda opcional renderizada como Markdown inline.
24
+ </d-block-code-example>
25
+
26
+ <d-block-code-example src="manual/code-examples/inline-notice" title="Expanded source example" expanded="true">
27
+ O painel de fonte começa aberto.
28
+ </d-block-code-example>
29
+ ```
30
+
31
+ ## Recursos visíveis acima
32
+
33
+ - **Preview ao vivo** renderizado a partir do componente Vue empacotado
34
+ - **Alternância de fonte** com abas Template, Script, Style e All
35
+ - **Ação do CodePen** para exemplos compatíveis
36
+ - **Ação do GitHub** apontando para o SFC do exemplo
37
+ - **Estado de fonte expandida** com `expanded="true"`
38
+ - **Legenda em Markdown inline** abaixo do preview
@@ -514,6 +514,34 @@ export default {
514
514
  }
515
515
  },
516
516
 
517
+ '/content/blocks/code-examples': {
518
+ config: {
519
+ icon: 'integration_instructions',
520
+ status: 'new',
521
+ meta: {
522
+ description: {
523
+ 'en-US': 'Code examples — Documentation of Docsector Reader',
524
+ 'pt-BR': 'Exemplos de código — Documentacao do Docsector Reader'
525
+ }
526
+ },
527
+ book: 'manual',
528
+ menu: {},
529
+ subpages: {
530
+ showcase: true
531
+ }
532
+ },
533
+ data: {
534
+ 'en-US': { title: 'Code examples' },
535
+ 'pt-BR': { title: 'Exemplos de código' }
536
+ },
537
+ metadata: {
538
+ tags: {
539
+ 'en-US': 'code examples live preview vue sfc source codepen component demo',
540
+ 'pt-BR': 'exemplos código preview ao vivo vue sfc fonte codepen componente demo'
541
+ }
542
+ }
543
+ },
544
+
517
545
  '/content/blocks/mermaid-diagrams': {
518
546
  config: {
519
547
  icon: 'account_tree',
@@ -1144,6 +1144,82 @@ function createBooksPlugin (projectRoot) {
1144
1144
  }
1145
1145
  }
1146
1146
 
1147
+ function buildVirtualCodeExamplesModule () {
1148
+ return `const componentModules = import.meta.glob('/src/examples/**/*.vue')
1149
+ const sourceModules = import.meta.glob('/src/examples/**/*.vue', { query: '?raw', import: 'default' })
1150
+
1151
+ const trimSlashes = (value) => String(value || '').replace(/\\\\/g, '/').replace(/^\\/+|\\/+$/g, '')
1152
+ const toKebabSegment = (value) => String(value || '')
1153
+ .replace(/\\.vue$/i, '')
1154
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
1155
+ .replace(/[\\s_]+/g, '-')
1156
+ .replace(/-+/g, '-')
1157
+ .toLowerCase()
1158
+
1159
+ export const normalizeCodeExampleId = (value) => trimSlashes(value)
1160
+ .replace(/^src\\/examples\\//i, '')
1161
+ .replace(/^examples\\//i, '')
1162
+ .replace(/\\.vue$/i, '')
1163
+ .split('/')
1164
+ .filter(Boolean)
1165
+ .map(toKebabSegment)
1166
+ .join('/')
1167
+
1168
+ export const codeExamples = Object.keys(componentModules).reduce((examples, filePath) => {
1169
+ const id = normalizeCodeExampleId(filePath)
1170
+
1171
+ examples[id] = {
1172
+ id,
1173
+ filePath,
1174
+ loadComponent: componentModules[filePath],
1175
+ loadSource: sourceModules[filePath]
1176
+ }
1177
+
1178
+ return examples
1179
+ }, {})
1180
+
1181
+ export const codeExampleIds = Object.keys(codeExamples).sort()
1182
+
1183
+ export const resolveCodeExample = (value) => {
1184
+ const id = normalizeCodeExampleId(value)
1185
+ const entry = codeExamples[id]
1186
+
1187
+ if (!entry) {
1188
+ return {
1189
+ id,
1190
+ filePath: '',
1191
+ exists: false,
1192
+ loadComponent: null,
1193
+ loadSource: null
1194
+ }
1195
+ }
1196
+
1197
+ return {
1198
+ ...entry,
1199
+ exists: true
1200
+ }
1201
+ }
1202
+
1203
+ export default codeExamples
1204
+ `
1205
+ }
1206
+
1207
+ function createCodeExamplesPlugin () {
1208
+ const virtualId = 'virtual:docsector-code-examples'
1209
+ const resolvedId = '\0' + virtualId
1210
+
1211
+ return {
1212
+ name: 'docsector-code-examples',
1213
+ resolveId (id) {
1214
+ if (id === virtualId) return resolvedId
1215
+ },
1216
+ load (id) {
1217
+ if (id !== resolvedId) return null
1218
+ return buildVirtualCodeExamplesModule()
1219
+ }
1220
+ }
1221
+ }
1222
+
1147
1223
  /**
1148
1224
  * Create the HJSON Vite plugin for loading .hjson files as ES modules.
1149
1225
  */
@@ -2919,6 +2995,7 @@ export function createQuasarConfig (options = {}) {
2919
2995
 
2920
2996
  vitePlugins: [
2921
2997
  createBooksPlugin(projectRoot),
2998
+ createCodeExamplesPlugin(),
2922
2999
  createHjsonPlugin(),
2923
3000
  createHomePageOverridePlugin(projectRoot),
2924
3001
  createGitDatesPlugin(projectRoot),
package/src/store/Page.js CHANGED
@@ -1,3 +1,23 @@
1
+ const getNodePath = (nodes, targetId, ancestry = []) => {
2
+ for (const node of nodes) {
3
+ const nextAncestry = [...ancestry, node.id]
4
+
5
+ if (node.id === targetId) {
6
+ return nextAncestry
7
+ }
8
+
9
+ if (Array.isArray(node.children) && node.children.length > 0) {
10
+ const childPath = getNodePath(node.children, targetId, nextAncestry)
11
+
12
+ if (childPath !== null) {
13
+ return childPath
14
+ }
15
+ }
16
+ }
17
+
18
+ return null
19
+ }
20
+
1
21
  export default {
2
22
  namespaced: true,
3
23
 
@@ -82,8 +102,12 @@ export default {
82
102
  state.nodesExpanded = [0]
83
103
  },
84
104
  pushNodesExpanded (state, nodeId) {
85
- if (!state.nodesExpanded.includes(nodeId)) {
86
- state.nodesExpanded.push(nodeId)
105
+ const nodePath = getNodePath(state.nodes, nodeId) || [nodeId]
106
+
107
+ for (const pathNodeId of nodePath) {
108
+ if (!state.nodesExpanded.includes(pathNodeId)) {
109
+ state.nodesExpanded.push(pathNodeId)
110
+ }
87
111
  }
88
112
  },
89
113