@docsector/docsector-reader 4.0.1 → 4.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.
Files changed (29) hide show
  1. package/README.md +19 -0
  2. package/bin/docsector.js +1 -1
  3. package/package.json +1 -1
  4. package/public/api/manual/http-client.json +91 -0
  5. package/public/quasar-api/QSeparator.json +39 -0
  6. package/src/components/DBlockApi.vue +634 -0
  7. package/src/components/DBlockApiEntry.js +623 -0
  8. package/src/components/DBlockCodeExample.vue +445 -0
  9. package/src/components/DBlockSourceCode.vue +3 -11
  10. package/src/components/DMenu.vue +70 -25
  11. package/src/components/DPageTokens.vue +22 -0
  12. package/src/components/api-block-model.js +326 -0
  13. package/src/components/code-block-highlighting.js +16 -0
  14. package/src/components/code-example-source.js +363 -0
  15. package/src/components/page-section-tokens.js +141 -1
  16. package/src/components/source-code-lines.js +17 -0
  17. package/src/examples/manual/code-examples/BasicCounter.vue +63 -0
  18. package/src/examples/manual/code-examples/InlineNotice.vue +60 -0
  19. package/src/pages/manual/content/blocks/api-reference.overview.en-US.md +40 -0
  20. package/src/pages/manual/content/blocks/api-reference.overview.pt-BR.md +40 -0
  21. package/src/pages/manual/content/blocks/api-reference.showcase.en-US.md +33 -0
  22. package/src/pages/manual/content/blocks/api-reference.showcase.pt-BR.md +33 -0
  23. package/src/pages/manual/content/blocks/code-examples.overview.en-US.md +56 -0
  24. package/src/pages/manual/content/blocks/code-examples.overview.pt-BR.md +56 -0
  25. package/src/pages/manual/content/blocks/code-examples.showcase.en-US.md +38 -0
  26. package/src/pages/manual/content/blocks/code-examples.showcase.pt-BR.md +38 -0
  27. package/src/pages/manual.index.js +56 -0
  28. package/src/quasar.factory.js +77 -0
  29. package/src/store/Page.js +26 -2
@@ -20,6 +20,8 @@ const STEPPER_MARKER_PREFIX = '@@DOCSECTOR_STEPPER_'
20
20
  const EXPANDABLE_MARKER_PREFIX = '@@DOCSECTOR_EXPANDABLE_'
21
21
  const FILE_MARKER_PREFIX = '@@DOCSECTOR_FILE_'
22
22
  const EMBEDDED_URL_MARKER_PREFIX = '@@DOCSECTOR_EMBEDDED_URL_'
23
+ const CODE_EXAMPLE_MARKER_PREFIX = '@@DOCSECTOR_CODE_EXAMPLE_'
24
+ const API_BLOCK_MARKER_PREFIX = '@@DOCSECTOR_API_BLOCK_'
23
25
  const CODE_SEGMENT_MARKER_PREFIX = '@@DOCSECTOR_CODE_SEGMENT_'
24
26
  const MATH_KATEX_OPTIONS = {
25
27
  throwOnError: false,
@@ -246,6 +248,24 @@ const parseExpandableOpenState = (raw = '') => {
246
248
  return ['1', 'true', 'yes', 'on'].includes(String(raw).trim().toLowerCase())
247
249
  }
248
250
 
251
+ const parseBooleanAttribute = (raw, fallback = false) => {
252
+ if (raw === undefined || raw === null || raw === '') {
253
+ return fallback
254
+ }
255
+
256
+ const normalized = String(raw).trim().toLowerCase()
257
+
258
+ if (['1', 'true', 'yes', 'on'].includes(normalized)) {
259
+ return true
260
+ }
261
+
262
+ if (['0', 'false', 'no', 'off'].includes(normalized)) {
263
+ return false
264
+ }
265
+
266
+ return fallback
267
+ }
268
+
249
269
  const parseTimelineTags = (raw = '') => {
250
270
  return decodeHtmlEntities(raw)
251
271
  .split(',')
@@ -517,6 +537,83 @@ const extractEmbeddedUrlBlocks = (source = '') => {
517
537
  }
518
538
  }
519
539
 
540
+ const extractCodeExampleBlocks = (source = '') => {
541
+ const map = new Map()
542
+ let index = 0
543
+
544
+ const replaceBlock = (match, rawAttrs, rawCaption = '') => {
545
+ const attrs = parseCustomTagAttributes(rawAttrs)
546
+ const src = decodeHtmlEntities(attrs.src || attrs.file || '').trim()
547
+
548
+ if (!src) {
549
+ return match
550
+ }
551
+
552
+ const marker = `${CODE_EXAMPLE_MARKER_PREFIX}${index}@@`
553
+ index++
554
+
555
+ map.set(marker, {
556
+ src,
557
+ title: decodeHtmlEntities(attrs.title || '').trim(),
558
+ expanded: parseBooleanAttribute(attrs.expanded, false),
559
+ codepen: parseBooleanAttribute(attrs.codepen, true),
560
+ scrollable: parseBooleanAttribute(attrs.scrollable, false),
561
+ overflow: parseBooleanAttribute(attrs.overflow, false),
562
+ height: decodeHtmlEntities(attrs.height || '').trim(),
563
+ caption: String(rawCaption).trim()
564
+ })
565
+
566
+ return `\n${marker}\n`
567
+ }
568
+
569
+ const replacedSelfClosing = String(source).replace(/<d-block-code-example\b([^>]*)\/\s*>/gi, (match, rawAttrs) => {
570
+ return replaceBlock(match, rawAttrs)
571
+ })
572
+ const replaced = replacedSelfClosing.replace(/<d-block-code-example\b([^>]*)>([\s\S]*?)<\/d-block-code-example>/gi, replaceBlock)
573
+
574
+ return {
575
+ source: replaced,
576
+ codeExampleMap: map
577
+ }
578
+ }
579
+
580
+ const extractApiBlocks = (source = '') => {
581
+ const map = new Map()
582
+ let index = 0
583
+
584
+ const replaceBlock = (match, rawAttrs) => {
585
+ const attrs = parseCustomTagAttributes(rawAttrs)
586
+ const src = decodeHtmlEntities(attrs.src || '').trim()
587
+
588
+ if (!src) {
589
+ return match
590
+ }
591
+
592
+ const marker = `${API_BLOCK_MARKER_PREFIX}${index}@@`
593
+ index++
594
+
595
+ map.set(marker, {
596
+ src,
597
+ title: decodeHtmlEntities(attrs.title || '').trim(),
598
+ pageLink: parseBooleanAttribute(attrs['page-link'], false)
599
+ })
600
+
601
+ return `\n${marker}\n`
602
+ }
603
+
604
+ const replacedSelfClosing = String(source).replace(/<d-block-api\b([^>]*)\/\s*>/gi, (match, rawAttrs) => {
605
+ return replaceBlock(match, rawAttrs)
606
+ })
607
+ const replaced = replacedSelfClosing.replace(/<d-block-api\b([^>]*)>([\s\S]*?)<\/d-block-api>/gi, (match, rawAttrs) => {
608
+ return replaceBlock(match, rawAttrs)
609
+ })
610
+
611
+ return {
612
+ source: replaced,
613
+ apiBlockMap: map
614
+ }
615
+ }
616
+
520
617
  const parseFenceAttributes = (raw = '') => {
521
618
  const parsed = {}
522
619
  const pattern = /([\w-]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s;]+))/g
@@ -871,6 +968,8 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
871
968
  const { source: sourceWithQuickLinks, quickLinksMap } = extractQuickLinksBlocks(sourceWithCards)
872
969
  const { source: sourceWithFiles, fileMap } = extractFileBlocks(sourceWithQuickLinks)
873
970
  const { source: sourceWithEmbeddedUrls, embeddedUrlMap } = extractEmbeddedUrlBlocks(sourceWithFiles)
971
+ const { source: sourceWithCodeExamples, codeExampleMap } = extractCodeExampleBlocks(sourceWithEmbeddedUrls)
972
+ const { source: sourceWithApiBlocks, apiBlockMap } = extractApiBlocks(sourceWithCodeExamples)
874
973
 
875
974
  fileMap.forEach((data, marker) => {
876
975
  fileMap.set(marker, {
@@ -886,10 +985,17 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
886
985
  })
887
986
  })
888
987
 
988
+ codeExampleMap.forEach((data, marker) => {
989
+ codeExampleMap.set(marker, {
990
+ ...data,
991
+ caption: restoreShieldedCodeSegments(data.caption, codeSegmentsMap)
992
+ })
993
+ })
994
+
889
995
  const markdown = createMarkdownBlockParser()
890
996
  const markdownInline = createMarkdownInlineParser()
891
997
  const markdownEnv = {}
892
- const parsed = markdown.parse(restoreShieldedCodeSegments(sourceWithEmbeddedUrls, codeSegmentsMap), markdownEnv)
998
+ const parsed = markdown.parse(restoreShieldedCodeSegments(sourceWithApiBlocks, codeSegmentsMap), markdownEnv)
893
999
  const tokens = []
894
1000
 
895
1001
  let level = 0
@@ -1146,6 +1252,40 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
1146
1252
  break
1147
1253
  }
1148
1254
 
1255
+ if (codeExampleMap.has(element.content.trim())) {
1256
+ const data = codeExampleMap.get(element.content.trim())
1257
+
1258
+ tokens.push({
1259
+ tag: 'code-example',
1260
+ map: element.map,
1261
+ codeIndex: parserState.codeIndex++,
1262
+ src: data.src,
1263
+ title: data.title,
1264
+ expanded: data.expanded,
1265
+ codepen: data.codepen,
1266
+ scrollable: data.scrollable,
1267
+ overflow: data.overflow,
1268
+ height: data.height,
1269
+ caption: data.caption !== ''
1270
+ ? markdownInline.renderInline(data.caption, markdownEnv)
1271
+ : ''
1272
+ })
1273
+ break
1274
+ }
1275
+
1276
+ if (apiBlockMap.has(element.content.trim())) {
1277
+ const data = apiBlockMap.get(element.content.trim())
1278
+
1279
+ tokens.push({
1280
+ tag: 'api',
1281
+ map: element.map,
1282
+ src: data.src,
1283
+ title: data.title,
1284
+ pageLink: data.pageLink
1285
+ })
1286
+ break
1287
+ }
1288
+
1149
1289
  if (tag === 'p') {
1150
1290
  const imageToken = parseStandaloneImageToken(element.content)
1151
1291
 
@@ -0,0 +1,17 @@
1
+ const normalizeLineBreaks = (text = '') => String(text || '').replace(/\r\n/g, '\n')
2
+
3
+ export const countRenderedCodeLines = (text = '') => {
4
+ const normalized = normalizeLineBreaks(text)
5
+
6
+ if (normalized === '') {
7
+ return 0
8
+ }
9
+
10
+ const withoutTerminalBreak = normalized.replace(/\n$/, '')
11
+
12
+ if (withoutTerminalBreak === '') {
13
+ return 1
14
+ }
15
+
16
+ return withoutTerminalBreak.split('\n').length
17
+ }
@@ -0,0 +1,63 @@
1
+ <template>
2
+ <div class="basic-counter-example q-pa-md">
3
+ <q-card class="basic-counter-example__card" flat bordered>
4
+ <q-card-section>
5
+ <div class="text-subtitle2 text-grey-7">Interactive preview</div>
6
+ <div class="basic-counter-example__value">{{ count }}</div>
7
+ <div class="text-body2 text-grey-7">{{ countLabel }}</div>
8
+ </q-card-section>
9
+
10
+ <q-separator></q-separator>
11
+
12
+ <q-card-actions align="right">
13
+ <q-btn flat color="primary" label="Reset" @click="reset"></q-btn>
14
+ <q-btn unelevated color="primary" label="Increment" @click="increment"></q-btn>
15
+ </q-card-actions>
16
+ </q-card>
17
+ </div>
18
+ </template>
19
+
20
+ <script>
21
+ import { computed, ref } from 'vue'
22
+
23
+ export default {
24
+ setup () {
25
+ const count = ref(0)
26
+ const countLabel = computed(() => count.value === 1 ? 'click recorded' : 'clicks recorded')
27
+
28
+ function increment () {
29
+ count.value++
30
+ }
31
+
32
+ function reset () {
33
+ count.value = 0
34
+ }
35
+
36
+ return {
37
+ count,
38
+ countLabel,
39
+ increment,
40
+ reset
41
+ }
42
+ }
43
+ }
44
+ </script>
45
+
46
+ <style scoped>
47
+ .basic-counter-example {
48
+ display: flex;
49
+ justify-content: center;
50
+ }
51
+
52
+ .basic-counter-example__card {
53
+ max-width: 360px;
54
+ width: 100%;
55
+ }
56
+
57
+ .basic-counter-example__value {
58
+ font-size: 48px;
59
+ font-weight: 700;
60
+ line-height: 1;
61
+ margin: 12px 0 6px;
62
+ }
63
+ </style>
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <div class="inline-notice-example q-pa-md">
3
+ <q-banner rounded class="inline-notice-example__banner">
4
+ <template #avatar>
5
+ <q-icon name="tips_and_updates" color="primary"></q-icon>
6
+ </template>
7
+
8
+ <div class="text-weight-medium">{{ title }}</div>
9
+ <div class="text-body2">{{ message }}</div>
10
+
11
+ <template #action>
12
+ <q-btn flat color="primary" label="Dismiss" @click="dismiss"></q-btn>
13
+ </template>
14
+ </q-banner>
15
+
16
+ <div v-if="dismissed" class="inline-notice-example__status text-caption">
17
+ The notice was dismissed.
18
+ </div>
19
+ </div>
20
+ </template>
21
+
22
+ <script>
23
+ import { ref } from 'vue'
24
+
25
+ export default {
26
+ setup () {
27
+ const title = 'Documentation tip'
28
+ const message = 'Use expanded examples when the source code is part of the lesson.'
29
+ const dismissed = ref(false)
30
+
31
+ function dismiss () {
32
+ dismissed.value = true
33
+ }
34
+
35
+ return {
36
+ dismissed,
37
+ dismiss,
38
+ message,
39
+ title
40
+ }
41
+ }
42
+ }
43
+ </script>
44
+
45
+ <style scoped>
46
+ .inline-notice-example {
47
+ display: grid;
48
+ gap: 10px;
49
+ }
50
+
51
+ .inline-notice-example__banner {
52
+ background: rgba(25, 118, 210, 0.08);
53
+ border: 1px solid rgba(25, 118, 210, 0.18);
54
+ }
55
+
56
+ .inline-notice-example__status {
57
+ color: #607d68;
58
+ padding-left: 8px;
59
+ }
60
+ </style>
@@ -0,0 +1,40 @@
1
+ ## Overview
2
+
3
+ API Reference blocks render a JSON document that follows the existing Quasar API schema directly inside Markdown.
4
+
5
+ This keeps the viewer compatible with Quasar-style API files while still allowing non-Vue APIs to reuse the same section model for props, methods, events, values, arguments, and config shapes.
6
+
7
+ The block is authored with the custom Markdown element `<d-block-api>`.
8
+
9
+ ## Markdown Syntax
10
+
11
+ ```html
12
+ <d-block-api src="/quasar-api/QSeparator.json" />
13
+
14
+ <d-block-api
15
+ src="/api/manual/http-client.json"
16
+ title="HTTP Client API"
17
+ page-link="true"
18
+ />
19
+ ```
20
+
21
+ ## Attributes
22
+
23
+ | Attribute | Purpose |
24
+ |-----------|---------|
25
+ | `src` | Same-origin JSON path to fetch in the browser |
26
+ | `title` | Optional header override shown above the API card |
27
+ | `page-link` | Shows the Docs button when the JSON has `meta.docsUrl` |
28
+
29
+ ## JSON Source Model
30
+
31
+ - The first implementation follows the same delivery model as Quasar Docs: the JSON file is served as a public asset and fetched on demand.
32
+ - No Docsector-specific schema is required. If your file already follows the Quasar API structure, it can be rendered as-is.
33
+ - Non-Vue APIs can still use the same shape by filling the sections they need, such as `props`, `methods`, `events`, `value`, `arg`, or `quasarConfOptions`.
34
+
35
+ ## Notes
36
+
37
+ - `props` are grouped into subtabs when more than one `category` is present.
38
+ - Entries marked with `internal: true` are hidden from the rendered block.
39
+ - The current version expects same-origin JSON assets so the browser can fetch them without CORS workarounds.
40
+ - If the JSON exposes `meta.docsUrl`, `page-link="true"` can surface a Docs button without changing the schema.
@@ -0,0 +1,40 @@
1
+ ## Visão geral
2
+
3
+ Os blocos de Referência de API renderizam um documento JSON que segue o schema de API já existente do Quasar diretamente dentro do Markdown.
4
+
5
+ Isso mantém o viewer compatível com arquivos de API no estilo do Quasar e ainda permite que APIs não-Vue reutilizem o mesmo modelo de seções para props, methods, events, values, arguments e estruturas de configuração.
6
+
7
+ O bloco é escrito com o elemento Markdown customizado `<d-block-api>`.
8
+
9
+ ## Sintaxe Markdown
10
+
11
+ ```html
12
+ <d-block-api src="/quasar-api/QSeparator.json" />
13
+
14
+ <d-block-api
15
+ src="/api/manual/http-client.json"
16
+ title="HTTP Client API"
17
+ page-link="true"
18
+ />
19
+ ```
20
+
21
+ ## Atributos
22
+
23
+ | Atributo | Finalidade |
24
+ |----------|------------|
25
+ | `src` | Caminho same-origin do JSON a ser buscado no navegador |
26
+ | `title` | Sobrescreve opcionalmente o título exibido acima do card |
27
+ | `page-link` | Exibe o botão Docs quando o JSON possui `meta.docsUrl` |
28
+
29
+ ## Modelo da Fonte JSON
30
+
31
+ - A primeira implementação segue o mesmo modelo de entrega do Quasar Docs: o arquivo JSON é servido como asset público e carregado sob demanda.
32
+ - Nenhum schema específico do Docsector é exigido. Se o arquivo já seguir a estrutura de API do Quasar, ele pode ser renderizado sem alterações.
33
+ - APIs não-Vue ainda podem usar a mesma forma preenchendo apenas as seções necessárias, como `props`, `methods`, `events`, `value`, `arg` ou `quasarConfOptions`.
34
+
35
+ ## Notas
36
+
37
+ - `props` são agrupadas em subtabs quando mais de uma `category` está presente.
38
+ - Entradas marcadas com `internal: true` são ocultadas do bloco renderizado.
39
+ - A versão atual espera assets JSON same-origin para que o navegador faça o fetch sem workarounds de CORS.
40
+ - Se o JSON expuser `meta.docsUrl`, `page-link="true"` pode exibir um botão Docs sem alterar o schema.
@@ -0,0 +1,33 @@
1
+ ## Showcase
2
+
3
+ ### Quasar JSON Without Refactoring
4
+
5
+ This example renders a real Quasar API JSON file copied into `public/quasar-api/`.
6
+
7
+ <d-block-api src="/quasar-api/QSeparator.json" />
8
+
9
+ ### Generic SDK JSON With the Same Schema
10
+
11
+ This example uses the same section model for a non-Vue HTTP client and also enables the optional Docs button.
12
+
13
+ <d-block-api src="/api/manual/http-client.json" title="HTTP Client API" page-link="true" />
14
+
15
+ ## Authoring Syntax
16
+
17
+ ```html
18
+ <d-block-api src="/quasar-api/QSeparator.json" />
19
+
20
+ <d-block-api
21
+ src="/api/manual/http-client.json"
22
+ title="HTTP Client API"
23
+ page-link="true"
24
+ />
25
+ ```
26
+
27
+ ## Features Visible Above
28
+
29
+ - **Quasar JSON compatibility** with a real file served from `public/quasar-api/`
30
+ - **Generic API support** without introducing a new schema
31
+ - **Local filter** across names and descriptions inside the loaded API sections
32
+ - **Grouped props subtabs** when multiple categories exist in the JSON
33
+ - **Optional Docs link** when `meta.docsUrl` is present and `page-link="true"` is used
@@ -0,0 +1,33 @@
1
+ ## Showcase
2
+
3
+ ### JSON do Quasar Sem Refatoração
4
+
5
+ Este exemplo renderiza um arquivo JSON de API real do Quasar copiado para `public/quasar-api/`.
6
+
7
+ <d-block-api src="/quasar-api/QSeparator.json" />
8
+
9
+ ### JSON de SDK Genérico Com o Mesmo Schema
10
+
11
+ Este exemplo usa o mesmo modelo de seções para um cliente HTTP não-Vue e também ativa o botão opcional de Docs.
12
+
13
+ <d-block-api src="/api/manual/http-client.json" title="HTTP Client API" page-link="true" />
14
+
15
+ ## Sintaxe de Autoria
16
+
17
+ ```html
18
+ <d-block-api src="/quasar-api/QSeparator.json" />
19
+
20
+ <d-block-api
21
+ src="/api/manual/http-client.json"
22
+ title="HTTP Client API"
23
+ page-link="true"
24
+ />
25
+ ```
26
+
27
+ ## Recursos Visíveis Acima
28
+
29
+ - **Compatibilidade com JSON do Quasar** usando um arquivo real servido de `public/quasar-api/`
30
+ - **Suporte a APIs genéricas** sem introduzir um novo schema
31
+ - **Filtro local** por nomes e descrições dentro das seções carregadas
32
+ - **Subtabs agrupadas em props** quando múltiplas categorias existem no JSON
33
+ - **Link opcional para Docs** quando `meta.docsUrl` está presente e `page-link="true"` é usado
@@ -0,0 +1,56 @@
1
+ ## Overview
2
+
3
+ Code example blocks render project Vue SFCs as live previews inside Markdown pages.
4
+
5
+ They are useful when documentation needs to show the real behavior of a component and still let readers inspect the exact source behind the preview.
6
+
7
+ The block is authored with the custom Markdown element `<d-block-code-example>`.
8
+
9
+ ## Example Files
10
+
11
+ Place example components under `src/examples/**/*.vue` in the project using Docsector.
12
+
13
+ Docsector discovers those files at build time through Vite. The `src` value is normalized to kebab-case, so this block:
14
+
15
+ ```html
16
+ <d-block-code-example src="manual/code-examples/basic-counter" title="Basic counter" />
17
+ ```
18
+
19
+ resolves this file:
20
+
21
+ ```text
22
+ src/examples/manual/code-examples/BasicCounter.vue
23
+ ```
24
+
25
+ You can also use `file` instead of `src` when migrating examples from Quasar Docs patterns.
26
+
27
+ ## Attributes
28
+
29
+ | Attribute | Purpose |
30
+ |-----------|---------|
31
+ | `src` | Example id under `src/examples/**/*.vue` |
32
+ | `file` | Alias for `src` |
33
+ | `title` | Header title shown above the preview |
34
+ | `expanded` | Opens the source panel by default when set to `true` |
35
+ | `codepen` | Shows the CodePen action unless set to `false` |
36
+ | `scrollable` | Gives the preview a fixed scrollable height |
37
+ | `overflow` | Allows both horizontal and vertical overflow in the preview |
38
+ | `height` | Sets a custom preview height, such as `360` or `420px` |
39
+
40
+ ## Source Panel
41
+
42
+ The source button opens the Vue SFC split into Template, Script, Style, and All tabs when those sections are present.
43
+
44
+ The source panel reuses the standard Docsector code block renderer, so readers get syntax highlighting, copy support, and the same dark/light treatment as regular code blocks.
45
+
46
+ ## GitHub Source Link
47
+
48
+ The GitHub button opens the example SFC in the project repository when Docsector can derive a repository URL from `github.editBaseUrl` or `links.github` in `docsector.config.js`.
49
+
50
+ ## CodePen Export
51
+
52
+ The CodePen button is available when the source can be transformed safely for a browser-only demo.
53
+
54
+ The first implementation supports plain Vue SFCs with a template, optional style, and an Options API `export default` script. Named imports from `vue` and `quasar` are converted to browser globals. Examples that use `<script setup>`, TypeScript scripts, or local imports still render in Docsector, but the CodePen action is disabled.
55
+
56
+ Set `codepen="false"` when an example is intentionally not meant to be exported.
@@ -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