@docsector/docsector-reader 4.3.2 → 4.4.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 (37) hide show
  1. package/.env.example +18 -0
  2. package/README.md +136 -5
  3. package/bin/docsector.js +36 -1
  4. package/docsector.config.js +44 -0
  5. package/package.json +3 -2
  6. package/public/robots.txt +4 -0
  7. package/src/ai-assistant/config.js +91 -0
  8. package/src/ai-assistant/indexing.js +50 -0
  9. package/src/ai-assistant/layout.js +56 -0
  10. package/src/ai-assistant/messages.js +41 -0
  11. package/src/ai-assistant/panel.js +22 -0
  12. package/src/ai-assistant/server.js +348 -0
  13. package/src/ai-assistant/session.js +91 -0
  14. package/src/ai-assistant/stream.js +125 -0
  15. package/src/components/DAssistantPanel.vue +701 -0
  16. package/src/components/DPage.vue +114 -4
  17. package/src/components/DPageAnchor.vue +11 -7
  18. package/src/components/DPageRichContent.vue +105 -0
  19. package/src/components/DPageTokens.vue +27 -16
  20. package/src/components/api-block-model.js +77 -1
  21. package/src/components/inline-code-copy.js +58 -0
  22. package/src/components/page-section-tokens.js +6 -4
  23. package/src/components/quasar-api-extends.json +235 -0
  24. package/src/composables/useAssistant.js +201 -0
  25. package/src/i18n/helpers.js +2 -0
  26. package/src/i18n/languages/en-US.hjson +22 -0
  27. package/src/i18n/languages/pt-BR.hjson +22 -0
  28. package/src/layouts/DefaultLayout.vue +22 -0
  29. package/src/markdown-agent.js +32 -0
  30. package/src/pages/manual/basic/ai-assistant.overview.en-US.md +69 -0
  31. package/src/pages/manual/basic/ai-assistant.overview.pt-BR.md +69 -0
  32. package/src/pages/manual/basic/d-page-anchor.overview.en-US.md +1 -1
  33. package/src/pages/manual/basic/d-page-anchor.overview.pt-BR.md +1 -1
  34. package/src/pages/manual.index.js +29 -0
  35. package/src/quasar.factory.js +166 -33
  36. package/src/sitemap.js +103 -0
  37. package/src/store/Layout.js +9 -1
@@ -2,18 +2,24 @@
2
2
  import { ref, computed, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'
3
3
  import { useStore } from 'vuex'
4
4
  import { useRoute, useRouter } from 'vue-router'
5
+ import { useI18n } from 'vue-i18n'
5
6
  import { useQuasar } from 'quasar'
7
+ import docsectorConfig from 'docsector.config.js'
6
8
 
7
9
  import useNavigator from '../composables/useNavigator'
8
10
  import { getReadingProgressState } from '../composables/useReadingProgress'
11
+ import { normalizeAiAssistantConfig } from '../ai-assistant/config'
12
+ import { getAssistantRightRailState } from '../ai-assistant/layout'
9
13
 
10
14
  import DPageAnchor from './DPageAnchor.vue'
15
+ import DAssistantPanel from './DAssistantPanel.vue'
11
16
  import DPageMeta from './DPageMeta.vue'
12
17
 
13
18
  const store = useStore()
14
19
  const router = useRouter()
15
20
  const route = useRoute()
16
21
  const $q = useQuasar()
22
+ const { locale } = useI18n()
17
23
 
18
24
  const { scrolling, navigate } = useNavigator()
19
25
 
@@ -35,6 +41,8 @@ const pageMinHeight = ref('calc(100vh - 86px)')
35
41
  const submenuHeight = ref('36px')
36
42
  const pageBottomInset = ref('0px')
37
43
  const readingProgress = ref(getReadingProgressState())
44
+ const assistantConfig = normalizeAiAssistantConfig(docsectorConfig)
45
+ const assistantEnabled = assistantConfig.enabled === true
38
46
 
39
47
  const getPageScrollContainer = () => {
40
48
  return pageScrollArea.value?.$el?.querySelector('.q-scrollarea__container') || null
@@ -96,6 +104,11 @@ const layoutMeta = computed({
96
104
  get: () => store.state.layout.meta,
97
105
  set: (value) => store.commit('layout/setMeta', value)
98
106
  })
107
+ const layoutAssistant = computed({
108
+ get: () => store.state.layout.assistant,
109
+ set: (value) => store.commit('layout/setAssistant', value)
110
+ })
111
+ const assistantWidth = computed(() => store.state.layout.assistantWidth || assistantConfig.ui.drawerWidth)
99
112
  const main = computed(() => {
100
113
  switch (store.state.page.relative) {
101
114
  case '/showcase':
@@ -109,14 +122,57 @@ const main = computed(() => {
109
122
  const shouldShowBackToTopControl = computed(() => {
110
123
  return props.showBackToTopControl && readingProgress.value.hasOverflow && readingProgress.value.isVisible
111
124
  })
125
+ const rightRailState = computed(() => getAssistantRightRailState({
126
+ tocOpen: layoutMeta.value,
127
+ assistantOpen: assistantEnabled && layoutAssistant.value,
128
+ screenWidth: $q.screen.width,
129
+ assistantWidth: assistantWidth.value,
130
+ mobileBreakpoint: 768
131
+ }))
132
+ const rightRailOpen = computed(() => {
133
+ return !rightRailState.value.isMobile && (rightRailState.value.showToc || rightRailState.value.showAssistant)
134
+ })
135
+ const mobileAssistantOpen = computed({
136
+ get: () => assistantEnabled && rightRailState.value.isMobile && layoutAssistant.value,
137
+ set: (value) => { layoutAssistant.value = value }
138
+ })
112
139
  const backToTopRightOffset = computed(() => {
113
- return layoutMeta.value && !$q.screen.lt.md ? '332px' : '24px'
140
+ return rightRailState.value.backToTopRightOffset
141
+ })
142
+ const currentMarkdownUrl = computed(() => {
143
+ if (store.state.page.base === 'home') {
144
+ return `${window.location.origin}/Homepage.${locale.value}.md`
145
+ }
146
+
147
+ const path = route.path.replace(/\/+$/, '')
148
+ return `${window.location.origin}${path || '/homepage'}.md`
149
+ })
150
+ const currentPageTitle = computed(() => {
151
+ const data = route.matched?.[0]?.meta?.data || route.meta?.data || {}
152
+ return data?.[locale.value]?.title || data?.['*']?.title || data?.['en-US']?.title || ''
114
153
  })
115
154
 
116
155
  const toggleSectionsTree = () => {
117
156
  layoutMeta.value = !layoutMeta.value
118
157
  }
119
158
 
159
+ const closeAssistant = () => {
160
+ layoutAssistant.value = false
161
+ }
162
+
163
+ const onAssistantResize = (value) => {
164
+ const maxWidth = Math.max(320, Math.min(620, Math.round($q.screen.width - 480)))
165
+ const clamped = Math.min(maxWidth, Math.max(320, Math.round(Number(value) || 0)))
166
+ store.commit('layout/setAssistantWidth', clamped)
167
+ }
168
+
169
+ const setRightRailOpen = (value) => {
170
+ if (!value) {
171
+ layoutMeta.value = false
172
+ layoutAssistant.value = false
173
+ }
174
+ }
175
+
120
176
  const pActive = (relative) => {
121
177
  if (relative === '/' && (store.state.page.relative === relative || store.state.page.relative === '')) {
122
178
  return 'active'
@@ -341,7 +397,9 @@ watch(() => route.fullPath, () => {
341
397
  />
342
398
  </q-btn-group>
343
399
  </q-toolbar-title>
344
- <q-btn class="d-submenu__toggle" @click="toggleSectionsTree" icon="account_tree" />
400
+ <q-btn class="d-submenu__toggle" :class="layoutMeta ? 'active' : null" @click="toggleSectionsTree" icon="account_tree">
401
+ <q-tooltip>{{ $t('page.edit.anchor') }}</q-tooltip>
402
+ </q-btn>
345
403
  </div>
346
404
  </q-toolbar>
347
405
 
@@ -383,9 +441,42 @@ watch(() => route.fullPath, () => {
383
441
  </q-btn>
384
442
  </div>
385
443
 
386
- <q-drawer elevated show-if-above side="right" v-model="layoutMeta">
387
- <d-page-anchor id="anchor" />
444
+ <q-drawer
445
+ v-if="!rightRailState.isMobile"
446
+ elevated
447
+ show-if-above
448
+ side="right"
449
+ :model-value="rightRailOpen"
450
+ :width="rightRailState.totalWidth || 308"
451
+ class="d-right-rail-drawer"
452
+ @update:model-value="setRightRailOpen"
453
+ >
454
+ <div class="d-right-rail">
455
+ <div v-if="rightRailState.showToc" class="d-right-rail__toc" :style="{ width: `${rightRailState.tocWidth}px` }">
456
+ <d-page-anchor id="anchor" />
457
+ </div>
458
+ <q-separator v-if="rightRailState.showToc && rightRailState.showAssistant" vertical />
459
+ <d-assistant-panel
460
+ v-if="rightRailState.showAssistant"
461
+ class="d-right-rail__assistant"
462
+ :style="{ width: `${rightRailState.assistantWidth}px` }"
463
+ :context-title="currentPageTitle"
464
+ :markdown-url="currentMarkdownUrl"
465
+ :width="rightRailState.assistantWidth"
466
+ resizable
467
+ @resize="onAssistantResize"
468
+ @close="closeAssistant"
469
+ />
470
+ </div>
388
471
  </q-drawer>
472
+
473
+ <q-dialog v-if="assistantEnabled" v-model="mobileAssistantOpen" maximized>
474
+ <d-assistant-panel
475
+ :context-title="currentPageTitle"
476
+ :markdown-url="currentMarkdownUrl"
477
+ @close="closeAssistant"
478
+ />
479
+ </q-dialog>
389
480
  </q-page-container>
390
481
  </template>
391
482
 
@@ -454,10 +545,29 @@ watch(() => route.fullPath, () => {
454
545
  border-radius: 0
455
546
  margin-right: 0
456
547
 
548
+ &.active
549
+ background: rgba(255, 255, 255, 0.16)
550
+
457
551
  .on-left
458
552
  margin-right: 5px
459
553
  .toolbar-container
460
554
  overflow: visible
555
+
556
+ .d-right-rail
557
+ height: 100%
558
+ display: flex
559
+ min-width: 0
560
+ overflow: hidden
561
+
562
+ &__toc
563
+ height: 100%
564
+ min-width: 0
565
+ overflow: auto
566
+
567
+ &__assistant
568
+ height: 100%
569
+ min-width: 320px
570
+ flex: 0 0 auto
461
571
  padding: 0
462
572
  .q-btn-group
463
573
  box-shadow: none
@@ -15,6 +15,8 @@ const { t } = useI18n()
15
15
  const { navigate, anchor, selected: navigatorSelected } = useNavigator()
16
16
 
17
17
  const scrolling = ref(null)
18
+ const enableScrollingTimeout = ref(null)
19
+ const initialAnchorTimeout = ref(null)
18
20
 
19
21
  const nodes = computed(() => store.getters['page/nodes'])
20
22
  const expanded = computed({
@@ -77,13 +79,13 @@ watch(selected, () => {
77
79
  onMounted(() => {
78
80
  store.commit('layout/setMetaToggle', true)
79
81
 
80
- setTimeout(() => {
82
+ enableScrollingTimeout.value = setTimeout(() => {
81
83
  store.commit('page/setScrolling', true)
82
84
  }, 1000)
83
85
 
84
86
  const id = route.hash.replace(/^#+/g, '')
85
87
  if (id) {
86
- setTimeout(() => {
88
+ initialAnchorTimeout.value = setTimeout(() => {
87
89
  anchor(route.hash)
88
90
  }, 500)
89
91
  }
@@ -94,13 +96,15 @@ onBeforeUnmount(() => {
94
96
  clearTimeout(scrolling.value)
95
97
  }
96
98
 
97
- store.commit('layout/setMetaToggle', false)
99
+ if (enableScrollingTimeout.value) {
100
+ clearTimeout(enableScrollingTimeout.value)
101
+ }
98
102
 
99
- store.commit('page/resetAnchor')
100
- store.commit('page/resetAnchors')
101
- store.commit('page/resetNodes')
103
+ if (initialAnchorTimeout.value) {
104
+ clearTimeout(initialAnchorTimeout.value)
105
+ }
102
106
 
103
- store.commit('page/setScrolling', false)
107
+ store.commit('layout/setMetaToggle', false)
104
108
  })
105
109
  </script>
106
110
 
@@ -0,0 +1,105 @@
1
+ <script>
2
+ import { defineComponent, h, onMounted, onUpdated, ref } from 'vue'
3
+ import { copyToClipboard, useQuasar } from 'quasar'
4
+ import { useI18n } from 'vue-i18n'
5
+
6
+ import {
7
+ decorateInlineCodeCopyTargets,
8
+ getInlineCodeCopyTarget
9
+ } from './inline-code-copy'
10
+
11
+ export default defineComponent({
12
+ name: 'DPageRichContent',
13
+
14
+ props: {
15
+ tag: {
16
+ type: String,
17
+ default: 'div'
18
+ },
19
+ html: {
20
+ type: String,
21
+ default: ''
22
+ },
23
+ attrs: {
24
+ type: Object,
25
+ default: null
26
+ }
27
+ },
28
+
29
+ setup(props) {
30
+ const rootRef = ref(null)
31
+ const $q = useQuasar()
32
+ const { t } = useI18n()
33
+
34
+ const syncInlineCodeTargets = () => {
35
+ decorateInlineCodeCopyTargets(rootRef.value, t('page.copyInlineCode'))
36
+ }
37
+
38
+ const notifyCopied = () => {
39
+ $q.notify({
40
+ message: t('page.copied'),
41
+ color: 'positive',
42
+ textColor: 'white',
43
+ icon: 'check',
44
+ position: 'top',
45
+ timeout: 1200
46
+ })
47
+ }
48
+
49
+ const copyInlineCode = (element) => {
50
+ const text = String(element?.textContent || '').trim()
51
+ if (!text) return
52
+
53
+ copyToClipboard(text)
54
+ .then(notifyCopied)
55
+ .catch(() => {})
56
+ }
57
+
58
+ const handleInlineCodeClick = (event) => {
59
+ const code = getInlineCodeCopyTarget(event.target, rootRef.value)
60
+ if (!code) return
61
+
62
+ event.preventDefault()
63
+ copyInlineCode(code)
64
+ }
65
+
66
+ const handleInlineCodeKeydown = (event) => {
67
+ if (event.key !== 'Enter' && event.key !== ' ') {
68
+ return
69
+ }
70
+
71
+ const code = getInlineCodeCopyTarget(event.target, rootRef.value)
72
+ if (!code) return
73
+
74
+ event.preventDefault()
75
+ copyInlineCode(code)
76
+ }
77
+
78
+ onMounted(syncInlineCodeTargets)
79
+ onUpdated(syncInlineCodeTargets)
80
+
81
+ return () => h(props.tag, {
82
+ ...(props.attrs || {}),
83
+ ref: rootRef,
84
+ class: ['d-page-rich-content', props.attrs?.class],
85
+ innerHTML: props.html,
86
+ onClick: handleInlineCodeClick,
87
+ onKeydown: handleInlineCodeKeydown
88
+ })
89
+ }
90
+ })
91
+ </script>
92
+
93
+ <style scoped lang="sass">
94
+ .d-page-rich-content
95
+ :deep(.d-copyable-inline-code)
96
+ cursor: copy
97
+ transition: background-color 0.15s ease, outline-color 0.15s ease
98
+
99
+ &:hover
100
+ background-color: rgba(25, 118, 210, 0.14)
101
+
102
+ &:focus-visible
103
+ outline: 2px solid currentColor
104
+ outline-offset: 2px
105
+ </style>
@@ -23,6 +23,7 @@ import DH3 from './DH3.vue'
23
23
  import DH4 from './DH4.vue'
24
24
  import DH5 from './DH5.vue'
25
25
  import DH6 from './DH6.vue'
26
+ import DPageRichContent from './DPageRichContent.vue'
26
27
  import DBlockSourceCode from './DBlockSourceCode.vue'
27
28
  import DBlockMermaidDiagram from './DBlockMermaidDiagram.vue'
28
29
  import DBlockBlockquote from './DBlockBlockquote.vue'
@@ -71,28 +72,34 @@ import DBlockApi from './DBlockApi.vue'
71
72
  :value="token.content"
72
73
  />
73
74
 
74
- <ul
75
+ <d-page-rich-content
75
76
  v-else-if="token.tag === 'ul'"
76
- v-bind="token.attrs || {}"
77
- v-html="token.content"
78
- ></ul>
79
- <ol
77
+ tag="ul"
78
+ :attrs="token.attrs"
79
+ :html="token.content"
80
+ />
81
+ <d-page-rich-content
80
82
  v-else-if="token.tag === 'ol'"
81
- v-bind="token.attrs || {}"
82
- v-html="token.content"
83
- ></ol>
83
+ tag="ol"
84
+ :attrs="token.attrs"
85
+ :html="token.content"
86
+ />
84
87
 
85
88
  <div
86
89
  v-else-if="token.tag === 'table'"
87
90
  class="d-table-wrapper"
88
91
  >
89
- <table v-html="token.content"></table>
92
+ <d-page-rich-content
93
+ tag="table"
94
+ :html="token.content"
95
+ />
90
96
  </div>
91
97
 
92
- <div
98
+ <d-page-rich-content
93
99
  v-else-if="token.tag === 'html'"
94
- v-html="token.content"
95
- ></div>
100
+ tag="div"
101
+ :html="token.content"
102
+ />
96
103
 
97
104
  <d-block-image
98
105
  v-else-if="token.tag === 'image'"
@@ -100,16 +107,20 @@ import DBlockApi from './DBlockApi.vue'
100
107
  :caption-html="token.captionHtml"
101
108
  />
102
109
 
103
- <p
110
+ <d-page-rich-content
104
111
  v-else-if="token.tag === 'p'"
105
- v-html="token.content"
106
- ></p>
112
+ tag="p"
113
+ :html="token.content"
114
+ />
107
115
 
108
116
  <d-block-blockquote
109
117
  v-else-if="token.tag === 'blockquote'"
110
118
  :message="token.alertType"
111
119
  >
112
- <div v-html="token.content"></div>
120
+ <d-page-rich-content
121
+ tag="div"
122
+ :html="token.content"
123
+ />
113
124
  </d-block-blockquote>
114
125
 
115
126
  <d-block-file
@@ -1,3 +1,5 @@
1
+ import quasarApiExtends from './quasar-api-extends.json'
2
+
1
3
  const defaultInnerTabName = '__default'
2
4
  const fallbackCategoryName = 'general'
3
5
 
@@ -9,6 +11,78 @@ const isSupportedTopLevelSection = (value) => {
9
11
  return typeof value === 'string' || isPlainObject(value)
10
12
  }
11
13
 
14
+ const mergeEntries = (baseValue, overrideValue) => {
15
+ if (!isPlainObject(baseValue) || !isPlainObject(overrideValue)) {
16
+ return overrideValue === undefined ? baseValue : overrideValue
17
+ }
18
+
19
+ const acc = {
20
+ ...baseValue
21
+ }
22
+
23
+ Object.entries(overrideValue).forEach(([key, value]) => {
24
+ acc[key] = isPlainObject(value) && isPlainObject(baseValue[key])
25
+ ? mergeEntries(baseValue[key], value)
26
+ : value
27
+ })
28
+
29
+ return acc
30
+ }
31
+
32
+ const resolveExtendsSource = (extendName = '', preferredSection = '') => {
33
+ const normalizedName = String(extendName || '').trim()
34
+
35
+ if (normalizedName === '') {
36
+ return undefined
37
+ }
38
+
39
+ if (isPlainObject(quasarApiExtends?.[preferredSection]?.[normalizedName])) {
40
+ return quasarApiExtends[preferredSection][normalizedName]
41
+ }
42
+
43
+ return Object.values(quasarApiExtends || {}).find((sectionEntries) => {
44
+ return isPlainObject(sectionEntries?.[normalizedName])
45
+ })?.[normalizedName]
46
+ }
47
+
48
+ const resolveExtendedEntries = (value, preferredSection = '', seen = new Set()) => {
49
+ if (Array.isArray(value)) {
50
+ return value.map((entry) => resolveExtendedEntries(entry, preferredSection, seen))
51
+ }
52
+
53
+ if (!isPlainObject(value)) {
54
+ return value
55
+ }
56
+
57
+ const extendName = typeof value.extends === 'string' ? value.extends.trim() : ''
58
+ const seenKey = `${preferredSection}:${extendName}`
59
+ const baseValue = extendName !== '' && !seen.has(seenKey)
60
+ ? resolveExtendsSource(extendName, preferredSection)
61
+ : undefined
62
+ const nextSeen = new Set(seen)
63
+
64
+ if (extendName !== '') {
65
+ nextSeen.add(seenKey)
66
+ }
67
+
68
+ const currentValue = {}
69
+
70
+ Object.entries(value).forEach(([key, entryValue]) => {
71
+ if (key === 'extends') {
72
+ return
73
+ }
74
+
75
+ currentValue[key] = resolveExtendedEntries(entryValue, preferredSection, nextSeen)
76
+ })
77
+
78
+ return mergeEntries(
79
+ baseValue === undefined
80
+ ? {}
81
+ : resolveExtendedEntries(baseValue, preferredSection, nextSeen),
82
+ currentValue
83
+ )
84
+ }
85
+
12
86
  const getEntryCategories = (entry = {}) => {
13
87
  const raw = String(entry?.category || '').trim()
14
88
 
@@ -296,7 +370,9 @@ export const createApiBlockModel = (sourceName = '', apiDocument = {}) => {
296
370
  const apiSections = {}
297
371
 
298
372
  Object.entries(apiSectionsRaw).forEach(([sectionName, sectionValue]) => {
299
- const sanitizedValue = pruneInternalEntries(sectionValue)
373
+ const sanitizedValue = pruneInternalEntries(
374
+ resolveExtendedEntries(sectionValue, sectionName)
375
+ )
300
376
 
301
377
  if (sanitizedValue !== undefined && isSupportedTopLevelSection(sanitizedValue)) {
302
378
  apiSections[sectionName] = sanitizedValue
@@ -0,0 +1,58 @@
1
+ export const INLINE_CODE_COPY_ATTRIBUTE = 'data-docsector-inline-code-copy'
2
+ export const INLINE_CODE_COPY_CLASS = 'd-copyable-inline-code'
3
+ export const INLINE_CODE_COPY_SELECTOR = `[${INLINE_CODE_COPY_ATTRIBUTE}]`
4
+
5
+ export const installInlineCodeCopyRenderer = (markdown) => {
6
+ const originalRender = markdown.renderer.rules.code_inline
7
+
8
+ markdown.renderer.rules.code_inline = (tokens, idx, options, env, self) => {
9
+ const token = tokens[idx]
10
+
11
+ token.attrJoin('class', INLINE_CODE_COPY_CLASS)
12
+ token.attrSet(INLINE_CODE_COPY_ATTRIBUTE, '')
13
+ token.attrSet('role', 'button')
14
+ token.attrSet('tabindex', '0')
15
+
16
+ if (typeof originalRender === 'function') {
17
+ return originalRender(tokens, idx, options, env, self)
18
+ }
19
+
20
+ return `<code${self.renderAttrs(token)}>${markdown.utils.escapeHtml(token.content)}</code>`
21
+ }
22
+
23
+ return markdown
24
+ }
25
+
26
+ export const decorateInlineCodeCopyTargets = (container, label = '') => {
27
+ if (!container || typeof container.querySelectorAll !== 'function') {
28
+ return
29
+ }
30
+
31
+ const normalizedLabel = String(label || '').trim()
32
+
33
+ container.querySelectorAll(INLINE_CODE_COPY_SELECTOR).forEach((element) => {
34
+ if (normalizedLabel) {
35
+ element.setAttribute('title', normalizedLabel)
36
+ element.setAttribute('aria-label', normalizedLabel)
37
+ }
38
+ })
39
+ }
40
+
41
+ export const getInlineCodeCopyTarget = (target, container) => {
42
+ const element = target?.nodeType === 3 ? target.parentElement : target
43
+
44
+ if (!element || typeof element.closest !== 'function') {
45
+ return null
46
+ }
47
+
48
+ const code = element.closest(INLINE_CODE_COPY_SELECTOR)
49
+ if (!code) {
50
+ return null
51
+ }
52
+
53
+ if (container && typeof container.contains === 'function' && !container.contains(code)) {
54
+ return null
55
+ }
56
+
57
+ return code
58
+ }
@@ -5,6 +5,8 @@ import katex from 'katex'
5
5
  import taskLists from 'markdown-it-task-lists'
6
6
  import texmath from 'markdown-it-texmath'
7
7
 
8
+ import { installInlineCodeCopyRenderer } from './inline-code-copy'
9
+
8
10
  const ALERT_MESSAGE_TYPES = new Set([
9
11
  'note',
10
12
  'tip',
@@ -850,9 +852,9 @@ const renderInlineToken = (markdown, markdownInline, element, env) => {
850
852
  }
851
853
 
852
854
  const createMarkdownBlockParser = () => {
853
- const markdown = installMathSupport(new MarkdownIt({
855
+ const markdown = installInlineCodeCopyRenderer(installMathSupport(new MarkdownIt({
854
856
  html: true
855
- }))
857
+ })))
856
858
 
857
859
  markdown.use(attrs, {
858
860
  leftDelimiter: ':',
@@ -869,9 +871,9 @@ const createMarkdownBlockParser = () => {
869
871
  }
870
872
 
871
873
  const createMarkdownInlineParser = () => {
872
- return installMathSupport(new MarkdownIt({
874
+ return installInlineCodeCopyRenderer(installMathSupport(new MarkdownIt({
873
875
  html: true
874
- }))
876
+ })) )
875
877
  }
876
878
 
877
879
  const normalizePageSectionSource = (source = '') => {