@docsector/docsector-reader 3.2.1 → 3.2.2
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 +2 -0
- package/bin/docsector.js +1 -1
- package/jsconfig.json +1 -0
- package/package.json +1 -1
- package/src/components/DPageImage.vue +80 -0
- package/src/components/DPageTokens.vue +7 -0
- package/src/components/QZoom.js +68 -14
- package/src/components/QZoom.sass +38 -3
- package/src/components/page-section-tokens.js +160 -15
- package/src/pages/manual/content/blocks/images.overview.en-US.md +20 -2
- package/src/pages/manual/content/blocks/images.showcase.en-US.md +10 -2
package/README.md
CHANGED
|
@@ -41,6 +41,8 @@ Transform Markdown content into beautiful, navigable documentation sites — wit
|
|
|
41
41
|
## ✨ Features
|
|
42
42
|
|
|
43
43
|
- 📝 **Markdown Rendering** — Write docs in Markdown, rendered with syntax highlighting (Prism.js)
|
|
44
|
+
- 🔽 **Nested Markdown Lists** — Ordered and unordered lists preserve sublist hierarchy across multiple indentation levels
|
|
45
|
+
- 🖼️ **Block Image Captions & Zoom** — Standalone Markdown images render as zoomable figures, and raw `figure` / `picture` markup supports separate alt text and captions
|
|
44
46
|
- 🧱 **Raw HTML in Markdown** — Renders inline and block HTML tags inside markdown sections (including homepage remote README content)
|
|
45
47
|
- 🧩 **Mermaid Diagrams** — Native support for fenced ` ```mermaid ` blocks, with automatic dark/light theme switching
|
|
46
48
|
- ➗ **Math & KaTeX** — Native support for inline `$...$` and display `$$...$$` formulas rendered with KaTeX
|
package/bin/docsector.js
CHANGED
package/jsconfig.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docsector/docsector-reader",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.2",
|
|
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",
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
defineOptions({
|
|
5
|
+
name: 'DPageImage'
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
const props = defineProps({
|
|
9
|
+
content: {
|
|
10
|
+
type: String,
|
|
11
|
+
default: ''
|
|
12
|
+
},
|
|
13
|
+
captionHtml: {
|
|
14
|
+
type: String,
|
|
15
|
+
default: ''
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const hasCaption = computed(() => {
|
|
20
|
+
return String(props.captionHtml || '').trim() !== ''
|
|
21
|
+
})
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<figure class="d-page-image">
|
|
26
|
+
<q-zoom
|
|
27
|
+
class="d-page-image__zoom"
|
|
28
|
+
background-color="rgba(18, 18, 20, 0.96)"
|
|
29
|
+
show-close-button
|
|
30
|
+
>
|
|
31
|
+
<div class="d-page-image__media" v-html="content"></div>
|
|
32
|
+
</q-zoom>
|
|
33
|
+
|
|
34
|
+
<figcaption v-if="hasCaption" class="d-page-image__caption" v-html="captionHtml"></figcaption>
|
|
35
|
+
</figure>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<style lang="sass" scoped>
|
|
39
|
+
.d-page-image
|
|
40
|
+
display: flex
|
|
41
|
+
flex-direction: column
|
|
42
|
+
align-items: center
|
|
43
|
+
gap: 0.75rem
|
|
44
|
+
width: 100%
|
|
45
|
+
margin: 1.75rem auto
|
|
46
|
+
text-align: center
|
|
47
|
+
|
|
48
|
+
.d-page-image__zoom
|
|
49
|
+
display: inline-block
|
|
50
|
+
width: fit-content
|
|
51
|
+
max-width: 100%
|
|
52
|
+
|
|
53
|
+
.d-page-image__media
|
|
54
|
+
display: inline-flex
|
|
55
|
+
align-items: center
|
|
56
|
+
justify-content: center
|
|
57
|
+
line-height: 0
|
|
58
|
+
max-width: 100%
|
|
59
|
+
|
|
60
|
+
:deep(img),
|
|
61
|
+
:deep(picture)
|
|
62
|
+
display: block
|
|
63
|
+
max-width: 100%
|
|
64
|
+
|
|
65
|
+
.d-page-image__caption
|
|
66
|
+
max-width: min(100%, 42rem)
|
|
67
|
+
margin: 0
|
|
68
|
+
padding: 0 1rem
|
|
69
|
+
color: inherit
|
|
70
|
+
opacity: 0.72
|
|
71
|
+
font-size: 0.92rem
|
|
72
|
+
line-height: 1.45
|
|
73
|
+
text-align: center
|
|
74
|
+
|
|
75
|
+
:deep(p)
|
|
76
|
+
margin: 0
|
|
77
|
+
|
|
78
|
+
:deep(*)
|
|
79
|
+
color: inherit
|
|
80
|
+
</style>
|
|
@@ -22,6 +22,7 @@ import DH6 from './DH6.vue'
|
|
|
22
22
|
import DPageSourceCode from './DPageSourceCode.vue'
|
|
23
23
|
import DMermaidDiagram from './DMermaidDiagram.vue'
|
|
24
24
|
import DPageBlockquote from './DPageBlockquote.vue'
|
|
25
|
+
import DPageImage from './DPageImage.vue'
|
|
25
26
|
import DQuickLinks from './DQuickLinks.vue'
|
|
26
27
|
import DPageExpandable from './DPageExpandable.vue'
|
|
27
28
|
</script>
|
|
@@ -75,6 +76,12 @@ import DPageExpandable from './DPageExpandable.vue'
|
|
|
75
76
|
v-html="token.content"
|
|
76
77
|
></div>
|
|
77
78
|
|
|
79
|
+
<d-page-image
|
|
80
|
+
v-else-if="token.tag === 'image'"
|
|
81
|
+
:content="token.content"
|
|
82
|
+
:caption-html="token.captionHtml"
|
|
83
|
+
/>
|
|
84
|
+
|
|
78
85
|
<p
|
|
79
86
|
v-else-if="token.tag === 'p'"
|
|
80
87
|
v-html="token.content"
|
package/src/components/QZoom.js
CHANGED
|
@@ -6,15 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
h,
|
|
9
|
+
Teleport,
|
|
9
10
|
ref, computed,
|
|
10
11
|
onMounted, onBeforeUnmount
|
|
11
12
|
} from 'vue'
|
|
12
13
|
|
|
13
14
|
import { useColorize } from 'q-colorize-mixin'
|
|
14
15
|
|
|
15
|
-
import { dom } from 'quasar'
|
|
16
|
-
const { offset } = dom
|
|
17
|
-
|
|
18
16
|
import './QZoom.sass'
|
|
19
17
|
|
|
20
18
|
// @
|
|
@@ -34,7 +32,12 @@ export default {
|
|
|
34
32
|
initialScaleText: { type: Number, default: 100, validator: v => v >= 50 && v <= 500 },
|
|
35
33
|
noCenter: Boolean,
|
|
36
34
|
noWheelScale: Boolean,
|
|
37
|
-
noEscClose: Boolean
|
|
35
|
+
noEscClose: Boolean,
|
|
36
|
+
showCloseButton: Boolean,
|
|
37
|
+
closeButtonLabel: {
|
|
38
|
+
type: String,
|
|
39
|
+
default: 'Close zoom'
|
|
40
|
+
}
|
|
38
41
|
},
|
|
39
42
|
|
|
40
43
|
setup (props, { emit, slots }) {
|
|
@@ -183,13 +186,29 @@ export default {
|
|
|
183
186
|
}
|
|
184
187
|
}
|
|
185
188
|
|
|
189
|
+
const onOverlayClick = (e) => {
|
|
190
|
+
if (isZoomed.value) {
|
|
191
|
+
hide()
|
|
192
|
+
|
|
193
|
+
e.preventDefault()
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const stopEvent = (e) => {
|
|
198
|
+
e.stopPropagation()
|
|
199
|
+
}
|
|
200
|
+
|
|
186
201
|
const getPosition = () => {
|
|
187
|
-
const
|
|
202
|
+
const rect = vComponent.value.getBoundingClientRect()
|
|
203
|
+
const position = {
|
|
204
|
+
left: rect.left,
|
|
205
|
+
top: rect.top
|
|
206
|
+
}
|
|
188
207
|
|
|
189
208
|
position.left = position.left + 'px'
|
|
190
209
|
position.top = position.top + 'px'
|
|
191
|
-
position.width =
|
|
192
|
-
position.height =
|
|
210
|
+
position.width = rect.width + 'px'
|
|
211
|
+
position.height = rect.height + 'px'
|
|
193
212
|
|
|
194
213
|
return position
|
|
195
214
|
}
|
|
@@ -245,7 +264,34 @@ export default {
|
|
|
245
264
|
fontSize: props.scaleText && !props.scale && `${scaleTextValue.value}%`
|
|
246
265
|
}
|
|
247
266
|
}), [
|
|
248
|
-
|
|
267
|
+
h('div', {
|
|
268
|
+
class: 'q-zoom__content-inner',
|
|
269
|
+
onClick: stopEvent
|
|
270
|
+
}, [
|
|
271
|
+
slot && slot({ zoomed: isZoomed.value })
|
|
272
|
+
])
|
|
273
|
+
])
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const __renderCloseButton = () => {
|
|
277
|
+
if (!props.showCloseButton) {
|
|
278
|
+
return null
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return h('button', {
|
|
282
|
+
type: 'button',
|
|
283
|
+
class: 'q-zoom__close-button',
|
|
284
|
+
'aria-label': props.closeButtonLabel,
|
|
285
|
+
title: props.closeButtonLabel,
|
|
286
|
+
onClick: (e) => {
|
|
287
|
+
stopEvent(e)
|
|
288
|
+
hide()
|
|
289
|
+
}
|
|
290
|
+
}, [
|
|
291
|
+
h('span', {
|
|
292
|
+
class: 'q-zoom__close-icon',
|
|
293
|
+
'aria-hidden': 'true'
|
|
294
|
+
}, '×')
|
|
249
295
|
])
|
|
250
296
|
}
|
|
251
297
|
|
|
@@ -256,13 +302,18 @@ export default {
|
|
|
256
302
|
|
|
257
303
|
return h('div', Colorize.setBackgroundColor(bgColor.value, {
|
|
258
304
|
class: 'q-zoom__overlay' +
|
|
259
|
-
(props.manual ? '' : ' q-zoom__zoom-out')
|
|
305
|
+
(props.manual ? '' : ' q-zoom__zoom-out'),
|
|
306
|
+
onClick: onOverlayClick
|
|
260
307
|
}), [
|
|
308
|
+
__renderCloseButton(),
|
|
261
309
|
__renderOverlayContent()
|
|
262
310
|
])
|
|
263
311
|
}
|
|
264
312
|
|
|
265
|
-
return () =>
|
|
313
|
+
return () => {
|
|
314
|
+
const overlay = __renderOverlay()
|
|
315
|
+
|
|
316
|
+
return h('div', {
|
|
266
317
|
class: 'q-zoom' +
|
|
267
318
|
(props.manual ? '' : ' q-zoom__zoom-in'),
|
|
268
319
|
|
|
@@ -270,9 +321,12 @@ export default {
|
|
|
270
321
|
onWheel: wheelEvent,
|
|
271
322
|
|
|
272
323
|
ref: vComponent
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
324
|
+
}, [
|
|
325
|
+
slots.default && slots.default({ zoomed: isZoomed.value }),
|
|
326
|
+
overlay
|
|
327
|
+
? h(Teleport, { to: 'body' }, overlay)
|
|
328
|
+
: null
|
|
329
|
+
])
|
|
330
|
+
}
|
|
277
331
|
}
|
|
278
332
|
}
|
|
@@ -22,19 +22,54 @@ $box-shadow: 1px 1px 7px 1px rgba(0,0,0,.2) !default
|
|
|
22
22
|
padding: 0
|
|
23
23
|
margin: 0
|
|
24
24
|
z-index: 6000
|
|
25
|
+
overflow: hidden
|
|
25
26
|
|
|
26
27
|
&__content
|
|
27
|
-
position:
|
|
28
|
-
display:
|
|
28
|
+
position: absolute
|
|
29
|
+
display: flex
|
|
30
|
+
align-items: center
|
|
31
|
+
justify-content: center
|
|
29
32
|
transition: all .5s cubic-bezier(.2,0,.2,1)
|
|
30
33
|
text-align: center
|
|
31
|
-
vertical-align: middle
|
|
32
34
|
width: 100%
|
|
33
35
|
height: 0
|
|
34
36
|
max-width: 100%
|
|
35
37
|
max-height: 100%
|
|
36
38
|
overflow: hidden
|
|
37
39
|
|
|
40
|
+
& > *
|
|
41
|
+
max-width: 100%
|
|
42
|
+
max-height: 100%
|
|
43
|
+
|
|
44
|
+
&__content-inner
|
|
45
|
+
display: inline-flex
|
|
46
|
+
align-items: center
|
|
47
|
+
justify-content: center
|
|
48
|
+
max-width: 100%
|
|
49
|
+
max-height: 100%
|
|
50
|
+
|
|
51
|
+
&__close-button
|
|
52
|
+
position: absolute
|
|
53
|
+
top: 24px
|
|
54
|
+
right: 24px
|
|
55
|
+
width: 56px
|
|
56
|
+
height: 56px
|
|
57
|
+
display: inline-flex
|
|
58
|
+
align-items: center
|
|
59
|
+
justify-content: center
|
|
60
|
+
border: 0
|
|
61
|
+
border-radius: 999px
|
|
62
|
+
background: rgba(255,255,255,.12)
|
|
63
|
+
color: #fff
|
|
64
|
+
cursor: pointer
|
|
65
|
+
z-index: 6001
|
|
66
|
+
box-shadow: $box-shadow
|
|
67
|
+
backdrop-filter: blur(8px)
|
|
68
|
+
|
|
69
|
+
&__close-icon
|
|
70
|
+
font-size: 34px
|
|
71
|
+
line-height: 1
|
|
72
|
+
|
|
38
73
|
&__no-center
|
|
39
74
|
text-align: unset
|
|
40
75
|
vertical-align: unset
|
|
@@ -215,6 +215,97 @@ const parseTokenAttributes = (element) => {
|
|
|
215
215
|
return parsed
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
const decodeHtmlEntities = (value = '') => {
|
|
219
|
+
return String(value)
|
|
220
|
+
.replace(/"/g, '"')
|
|
221
|
+
.replace(/'|'/g, "'")
|
|
222
|
+
.replace(/</g, '<')
|
|
223
|
+
.replace(/>/g, '>')
|
|
224
|
+
.replace(/&/g, '&')
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const escapeHtml = (value = '') => {
|
|
228
|
+
return String(value)
|
|
229
|
+
.replace(/&/g, '&')
|
|
230
|
+
.replace(/</g, '<')
|
|
231
|
+
.replace(/>/g, '>')
|
|
232
|
+
.replace(/"/g, '"')
|
|
233
|
+
.replace(/'/g, ''')
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const extractTagAttributes = (html = '', tagName = '') => {
|
|
237
|
+
const match = String(html).match(new RegExp(`<${tagName}\\b([^>]*)\\/?>(?![\\s\\S]*<${tagName}\\b)`, 'i'))
|
|
238
|
+
|
|
239
|
+
if (!match) {
|
|
240
|
+
return {}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return parseCustomTagAttributes(match[1])
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const createImageTokenData = (mediaHtml = '', options = {}) => {
|
|
247
|
+
const {
|
|
248
|
+
captionHtml = '',
|
|
249
|
+
fallbackCaptionFromAlt = false
|
|
250
|
+
} = options
|
|
251
|
+
const attrs = extractTagAttributes(mediaHtml, 'img')
|
|
252
|
+
const alt = decodeHtmlEntities(attrs.alt || '')
|
|
253
|
+
const title = decodeHtmlEntities(attrs.title || '')
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
tag: 'image',
|
|
257
|
+
content: String(mediaHtml).trim(),
|
|
258
|
+
alt,
|
|
259
|
+
title,
|
|
260
|
+
captionHtml: captionHtml !== ''
|
|
261
|
+
? captionHtml
|
|
262
|
+
: (fallbackCaptionFromAlt ? escapeHtml(alt) : '')
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const parseStandaloneImageToken = (content = '') => {
|
|
267
|
+
const trimmed = String(content).trim()
|
|
268
|
+
|
|
269
|
+
if (!trimmed.match(/^<img\b[^>]*\/?>(?:\s*)$/i)) {
|
|
270
|
+
return null
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return createImageTokenData(trimmed, {
|
|
274
|
+
fallbackCaptionFromAlt: true
|
|
275
|
+
})
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const parseFigureImageToken = (content = '') => {
|
|
279
|
+
const trimmed = String(content).trim()
|
|
280
|
+
|
|
281
|
+
if (!trimmed.match(/^<figure\b[\s\S]*<\/figure>$/i)) {
|
|
282
|
+
return null
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const figureBody = trimmed
|
|
286
|
+
.replace(/^<figure\b[^>]*>/i, '')
|
|
287
|
+
.replace(/<\/figure>$/i, '')
|
|
288
|
+
.trim()
|
|
289
|
+
const figcaptionMatch = figureBody.match(/<figcaption\b[^>]*>([\s\S]*?)<\/figcaption>/i)
|
|
290
|
+
const mediaBody = figcaptionMatch
|
|
291
|
+
? figureBody.replace(figcaptionMatch[0], '').trim()
|
|
292
|
+
: figureBody
|
|
293
|
+
const pictureMatch = mediaBody.match(/^<picture\b[\s\S]*?<\/picture>$/i)
|
|
294
|
+
const imgMatch = pictureMatch
|
|
295
|
+
? pictureMatch[0].match(/<img\b[^>]*\/?>/i)
|
|
296
|
+
: mediaBody.match(/^<img\b[^>]*\/?>$/i)
|
|
297
|
+
|
|
298
|
+
if (!imgMatch) {
|
|
299
|
+
return null
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const mediaHtml = pictureMatch ? pictureMatch[0] : imgMatch[0]
|
|
303
|
+
|
|
304
|
+
return createImageTokenData(mediaHtml, {
|
|
305
|
+
captionHtml: figcaptionMatch ? figcaptionMatch[1].trim() : ''
|
|
306
|
+
})
|
|
307
|
+
}
|
|
308
|
+
|
|
218
309
|
const parseFenceLanguage = (raw = '') => {
|
|
219
310
|
const cleaned = String(raw)
|
|
220
311
|
.replace(/:[^;]*;/g, ' ')
|
|
@@ -520,7 +611,7 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
|
|
|
520
611
|
}
|
|
521
612
|
|
|
522
613
|
switch (element.type) {
|
|
523
|
-
case 'inline':
|
|
614
|
+
case 'inline': {
|
|
524
615
|
const anchorId = getHeadingAnchorId(markdown, tag, element, markdownEnv, parserState)
|
|
525
616
|
|
|
526
617
|
if (expandableMap.has(element.content.trim())) {
|
|
@@ -549,6 +640,18 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
|
|
|
549
640
|
break
|
|
550
641
|
}
|
|
551
642
|
|
|
643
|
+
if (tag === 'p') {
|
|
644
|
+
const imageToken = parseStandaloneImageToken(element.content)
|
|
645
|
+
|
|
646
|
+
if (imageToken !== null) {
|
|
647
|
+
tokens.push({
|
|
648
|
+
...imageToken,
|
|
649
|
+
map: element.map
|
|
650
|
+
})
|
|
651
|
+
break
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
552
655
|
tokens.push({
|
|
553
656
|
tag,
|
|
554
657
|
map: element.map,
|
|
@@ -557,6 +660,7 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
|
|
|
557
660
|
info: element.info
|
|
558
661
|
})
|
|
559
662
|
break
|
|
663
|
+
}
|
|
560
664
|
|
|
561
665
|
case 'fence':
|
|
562
666
|
pushSourceCodeToken(tokens, element, parserState)
|
|
@@ -569,36 +673,59 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
|
|
|
569
673
|
})
|
|
570
674
|
break
|
|
571
675
|
|
|
572
|
-
case 'html_block':
|
|
676
|
+
case 'html_block': {
|
|
677
|
+
const figureImageToken = parseFigureImageToken(element.content)
|
|
678
|
+
|
|
679
|
+
if (figureImageToken !== null) {
|
|
680
|
+
tokens.push({
|
|
681
|
+
...figureImageToken,
|
|
682
|
+
map: element.map
|
|
683
|
+
})
|
|
684
|
+
break
|
|
685
|
+
}
|
|
686
|
+
|
|
573
687
|
tokens.push({
|
|
574
688
|
tag: 'html',
|
|
575
689
|
content: element.content
|
|
576
690
|
})
|
|
577
691
|
break
|
|
692
|
+
}
|
|
578
693
|
}
|
|
579
|
-
} else if (level
|
|
694
|
+
} else if (level > 0) {
|
|
580
695
|
const parent = tokens[tokens.length - 1]
|
|
581
696
|
|
|
582
697
|
switch (element.type) {
|
|
583
698
|
case 'bullet_list_open':
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
699
|
+
if (level === 1) {
|
|
700
|
+
tokens.push({
|
|
701
|
+
tag: 'ul',
|
|
702
|
+
content: ''
|
|
703
|
+
})
|
|
704
|
+
} else {
|
|
705
|
+
parent.content += '<ul>'
|
|
706
|
+
}
|
|
588
707
|
break
|
|
589
708
|
|
|
590
709
|
case 'ordered_list_open':
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
710
|
+
if (level === 1) {
|
|
711
|
+
tokens.push({
|
|
712
|
+
tag: 'ol',
|
|
713
|
+
content: ''
|
|
714
|
+
})
|
|
715
|
+
} else {
|
|
716
|
+
parent.content += '<ol>'
|
|
717
|
+
}
|
|
595
718
|
break
|
|
596
719
|
|
|
597
720
|
case 'table_open':
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
721
|
+
if (level === 1) {
|
|
722
|
+
tokens.push({
|
|
723
|
+
tag: 'table',
|
|
724
|
+
content: ''
|
|
725
|
+
})
|
|
726
|
+
} else {
|
|
727
|
+
parent.content += '<table>'
|
|
728
|
+
}
|
|
602
729
|
break
|
|
603
730
|
|
|
604
731
|
case 'list_item_open':
|
|
@@ -636,6 +763,24 @@ export const tokenizePageSectionSource = (source = '', options = {}) => {
|
|
|
636
763
|
parent.content += '</li>'
|
|
637
764
|
break
|
|
638
765
|
|
|
766
|
+
case 'bullet_list_close':
|
|
767
|
+
if (level > 1) {
|
|
768
|
+
parent.content += '</ul>'
|
|
769
|
+
}
|
|
770
|
+
break
|
|
771
|
+
|
|
772
|
+
case 'ordered_list_close':
|
|
773
|
+
if (level > 1) {
|
|
774
|
+
parent.content += '</ol>'
|
|
775
|
+
}
|
|
776
|
+
break
|
|
777
|
+
|
|
778
|
+
case 'table_close':
|
|
779
|
+
if (level > 1) {
|
|
780
|
+
parent.content += '</table>'
|
|
781
|
+
}
|
|
782
|
+
break
|
|
783
|
+
|
|
639
784
|
case 'thead_close':
|
|
640
785
|
parent.content += '</thead>'
|
|
641
786
|
break
|
|
@@ -2,15 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
Use standard Markdown images to place screenshots, illustrations, diagrams, and product UI directly inside the reading flow.
|
|
4
4
|
|
|
5
|
+
When an image appears on its own line, Docsector renders it as a block figure with click-to-zoom behavior.
|
|
6
|
+
For plain Markdown images, the label is used as both the visible caption and the alt text, matching GitBook's block image model.
|
|
7
|
+
|
|
5
8
|
## Markdown Syntax
|
|
6
9
|
|
|
7
10
|
```markdown
|
|
8
11
|

|
|
9
12
|
```
|
|
10
13
|
|
|
14
|
+
## Separate Alt Text And Caption
|
|
15
|
+
|
|
16
|
+
Use raw HTML when the accessibility text and the visible caption should be different, or when you need `<picture>` / `<source>` for light and dark mode variants.
|
|
17
|
+
|
|
18
|
+
```html
|
|
19
|
+
<figure>
|
|
20
|
+
<picture>
|
|
21
|
+
<source srcset="/images/logo-dark.png" media="(prefers-color-scheme: dark)">
|
|
22
|
+
<img src="/images/logo-light.png" alt="Docsector Reader logo">
|
|
23
|
+
</picture>
|
|
24
|
+
<figcaption><p>Docsector Reader brand mark</p></figcaption>
|
|
25
|
+
</figure>
|
|
26
|
+
```
|
|
27
|
+
|
|
11
28
|
## Good Practices
|
|
12
29
|
|
|
13
|
-
- Write
|
|
30
|
+
- Write labels that still make sense without the visual, because plain Markdown uses the same text for caption and alt.
|
|
31
|
+
- Use `<figure>` with `<figcaption>` when the visible caption should differ from the accessibility text.
|
|
14
32
|
- Prefer absolute site paths such as `/images/...` for assets stored in `public/images/`.
|
|
15
33
|
- Use screenshots to support the text, not replace the explanation.
|
|
16
|
-
-
|
|
34
|
+
- Click standalone block images to zoom; inline images that stay inside a paragraph remain on the inline path.
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
## Showcase
|
|
2
2
|
|
|
3
|
-
###
|
|
3
|
+
### Markdown Image With Caption
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
7
|
+
The label above is rendered as the visible caption and remains the image alt text.
|
|
8
|
+
|
|
7
9
|
### Image with Title
|
|
8
10
|
|
|
9
11
|

|
|
10
12
|
|
|
11
|
-
The
|
|
13
|
+
The title attribute is preserved on the rendered image, while the block still keeps the same visible caption.
|
|
14
|
+
|
|
15
|
+
### Raw HTML Figure With Separate Alt And Caption
|
|
16
|
+
|
|
17
|
+
<figure><img src="/images/logo.png" alt="Docsector Reader brand mark"><figcaption><p>Docsector Reader logo used as a standalone brand image.</p></figcaption></figure>
|
|
18
|
+
|
|
19
|
+
Click any standalone image block on this page to open the zoom view.
|