@brillout/docpress 0.1.14 → 0.1.17

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 (143) hide show
  1. package/dist/{chunk-GBXQT242.js → chunk-ZYYJWJMY.js} +38 -13
  2. package/dist/chunk-ZYYJWJMY.js.map +1 -0
  3. package/dist/{devServer-CXZI7V2M.js → devServer-J2XJQJGT.js} +2 -5
  4. package/dist/{devServer-CXZI7V2M.js.map → devServer-J2XJQJGT.js.map} +1 -1
  5. package/dist/index.d.ts +0 -136
  6. package/dist/index.js +27 -484
  7. package/dist/index.js.map +1 -1
  8. package/package.json +10 -13
  9. package/src/MobileHeader.tsx +68 -0
  10. package/src/PageLayout.css +41 -0
  11. package/src/PageLayout.tsx +37 -0
  12. package/src/algolia/DocSearch.css +29 -0
  13. package/src/algolia/DocSearch.ts +37 -0
  14. package/src/autoScrollNav.ts +22 -0
  15. package/src/cli/devServer.ts +31 -0
  16. package/src/cli/index.ts +29 -0
  17. package/src/components/CodeBlock.tsx +22 -0
  18. package/src/components/DocLink.tsx +108 -0
  19. package/src/components/EditPageNote.tsx +18 -0
  20. package/src/components/HorizontalLine.tsx +20 -0
  21. package/src/components/ImportMeta.tsx +11 -0
  22. package/src/components/Info.tsx +12 -0
  23. package/src/components/Link.tsx +18 -0
  24. package/src/components/Note.tsx +31 -0
  25. package/src/components/P.css +8 -0
  26. package/src/components/P.tsx +8 -0
  27. package/src/components/ReadingRecommendation.tsx +53 -0
  28. package/src/components/RepoLink.tsx +24 -0
  29. package/src/components/Sponsors/companyLogos/ccoli-logo.svg +1 -0
  30. package/src/components/Sponsors/companyLogos/ccoli-text.svg +1 -0
  31. package/{dist/ccoli-CHW3TQKS.svg → src/components/Sponsors/companyLogos/ccoli.svg} +0 -0
  32. package/{dist/contra-WLZBOPBV.svg → src/components/Sponsors/companyLogos/contra.svg} +0 -0
  33. package/{dist/mfqs-2EAEE7N6.svg → src/components/Sponsors/companyLogos/mfqs.svg} +0 -0
  34. package/src/components/Sponsors/label.draft.svg +108 -0
  35. package/{dist/label-MP75CTIA.svg → src/components/Sponsors/label.svg} +0 -0
  36. package/{dist/medalBronze-CO4CTUR4.svg → src/components/Sponsors/medalBronze.svg} +0 -0
  37. package/{dist/medalGold-UP6A73FL.svg → src/components/Sponsors/medalGold.svg} +0 -0
  38. package/{dist/medalSilver-FAPGGOBN.svg → src/components/Sponsors/medalSilver.svg} +0 -0
  39. package/src/components/Sponsors.tsx +242 -0
  40. package/{dist → src}/components/features/FeatureList.css +7 -2
  41. package/src/components/features/FeatureList.tsx +114 -0
  42. package/{dist/chevron-R2IYJD62.svg → src/components/features/chevron.svg} +0 -0
  43. package/src/components/features/initFeatureList.ts +66 -0
  44. package/src/components/index.ts +13 -0
  45. package/src/config/Config.ts +30 -0
  46. package/src/config/getConfig.ts +18 -0
  47. package/src/config/resolveConfig/resolveHeading.ts +0 -0
  48. package/src/config/resolvePageContext.ts +157 -0
  49. package/{dist/Inter-Var-IOAEQULN.ttf → src/css/Inter-Var.ttf} +0 -0
  50. package/src/css/button.css +7 -0
  51. package/src/css/code/block.css +36 -0
  52. package/src/css/code/inline.css +27 -0
  53. package/src/css/code.css +20 -0
  54. package/src/css/colorize-on-hover.css +29 -0
  55. package/src/css/font.css +19 -0
  56. package/src/css/heading.css +25 -0
  57. package/src/css/index.css +11 -0
  58. package/src/css/link.css +17 -0
  59. package/src/css/note.css +26 -0
  60. package/src/css/reset.css +12 -0
  61. package/src/css/table.css +14 -0
  62. package/src/css/tooltip.css +11 -0
  63. package/src/headings.ts +200 -0
  64. package/{dist/changelog-IPI5F42D.svg → src/icons/changelog.svg} +0 -0
  65. package/{dist/discord-JD33TUSF.svg → src/icons/discord.svg} +0 -0
  66. package/{dist/github-P5ZSKN2N.svg → src/icons/github.svg} +0 -0
  67. package/{dist/heart-OINVKOXO.svg → src/icons/heart.svg} +0 -0
  68. package/{dist/twitter-I7DXDN3J.svg → src/icons/twitter.svg} +0 -0
  69. package/src/index.ts +3 -0
  70. package/src/installSectionUrlHashs.ts +50 -0
  71. package/src/markdownHeadingsVitePlugin.ts +128 -0
  72. package/src/navigation/Navigation-highlight.css +41 -0
  73. package/src/navigation/Navigation-items.css +122 -0
  74. package/src/navigation/Navigation-layout.css +118 -0
  75. package/src/navigation/Navigation.client.old.ts +303 -0
  76. package/src/navigation/Navigation.client.ts +19 -0
  77. package/src/navigation/Navigation.css +12 -0
  78. package/src/navigation/Navigation.tsx +228 -0
  79. package/src/navigation/NavigationHeader.tsx +97 -0
  80. package/src/navigation/navigation-fullscreen/NavigationFullscreenButton.css +32 -0
  81. package/src/navigation/navigation-fullscreen/NavigationFullscreenButton.tsx +44 -0
  82. package/{dist/chevron-K3WPYLOP.svg → src/navigation/navigation-fullscreen/chevron.svg} +0 -0
  83. package/{dist/close-IQXTDOHV.svg → src/navigation/navigation-fullscreen/close.svg} +0 -0
  84. package/src/navigation/navigation-fullscreen/initNavigationFullscreen.ts +115 -0
  85. package/src/parseEmojis.ts +33 -0
  86. package/src/renderer/_default.page.client.ts +4 -0
  87. package/src/renderer/_default.page.server.tsx +69 -0
  88. package/src/renderer/usePageContext.tsx +25 -0
  89. package/src/types.ts +2 -0
  90. package/src/utils/Emoji/Emoji.ts +216 -0
  91. package/src/utils/Emoji/assets.ts +9 -0
  92. package/{dist/compass-2RWQU3E4.svg → src/utils/Emoji/compass.svg} +0 -0
  93. package/{dist/engine-6Q6VSCVA.png → src/utils/Emoji/engine.png} +0 -0
  94. package/src/utils/Emoji/index.ts +1 -0
  95. package/{dist/mechanical-arm-TR7IQQMG.svg → src/utils/Emoji/mechanical-arm.svg} +0 -0
  96. package/src/utils/Emoji/mountain.svg +1 -0
  97. package/{dist/road-fork-3WZLW3HB.svg → src/utils/Emoji/road-fork.svg} +0 -0
  98. package/{dist/shield-CU45RG5C.svg → src/utils/Emoji/shield.svg} +0 -0
  99. package/{dist/typescript-ALIPKLRM.svg → src/utils/Emoji/typescript.svg} +0 -0
  100. package/src/utils/assert.ts +39 -0
  101. package/src/utils/client.ts +2 -0
  102. package/src/utils/crawlAllFiles.ts +17 -0
  103. package/src/utils/determineSectionUrlHash.ts +35 -0
  104. package/src/utils/filesystemPathHandling.ts +42 -0
  105. package/src/utils/filter.ts +12 -0
  106. package/src/utils/isBrowser.ts +5 -0
  107. package/src/utils/jsxToTextContent.ts +11 -0
  108. package/src/utils/objectAssign.ts +9 -0
  109. package/src/utils/server.ts +8 -0
  110. package/src/vite.config.ts +44 -0
  111. package/dist/chunk-7HKDCMSZ.js +0 -154
  112. package/dist/chunk-7HKDCMSZ.js.map +0 -1
  113. package/dist/chunk-G2A5MZJA.js +0 -48
  114. package/dist/chunk-G2A5MZJA.js.map +0 -1
  115. package/dist/chunk-GBXQT242.js.map +0 -1
  116. package/dist/chunk-JS5BGVDK.js +0 -178
  117. package/dist/chunk-JS5BGVDK.js.map +0 -1
  118. package/dist/chunk-OEVBWUR6.js +0 -92
  119. package/dist/chunk-OEVBWUR6.js.map +0 -1
  120. package/dist/chunk-TTLAZ2T2.js +0 -8
  121. package/dist/chunk-TTLAZ2T2.js.map +0 -1
  122. package/dist/cli/index.d.ts +0 -1
  123. package/dist/cli/index.js +0 -35
  124. package/dist/cli/index.js.map +0 -1
  125. package/dist/components/features/FeatureList.css.map +0 -1
  126. package/dist/components/features/FeatureList.d.ts +0 -13
  127. package/dist/components/features/FeatureList.js +0 -8
  128. package/dist/components/features/FeatureList.js.map +0 -1
  129. package/dist/components/features/initFeatureList.d.ts +0 -3
  130. package/dist/components/features/initFeatureList.js +0 -60
  131. package/dist/components/features/initFeatureList.js.map +0 -1
  132. package/dist/index.css +0 -121
  133. package/dist/index.css.map +0 -1
  134. package/dist/renderer/_default.page.client.css +0 -263
  135. package/dist/renderer/_default.page.client.css.map +0 -1
  136. package/dist/renderer/_default.page.client.d.ts +0 -1
  137. package/dist/renderer/_default.page.client.js +0 -180
  138. package/dist/renderer/_default.page.client.js.map +0 -1
  139. package/dist/renderer/_default.page.server.css +0 -312
  140. package/dist/renderer/_default.page.server.css.map +0 -1
  141. package/dist/renderer/_default.page.server.d.ts +0 -22
  142. package/dist/renderer/_default.page.server.js +0 -602
  143. package/dist/renderer/_default.page.server.js.map +0 -1
@@ -0,0 +1,128 @@
1
+ import { assert, determineSectionUrlHash } from '../src/utils/server'
2
+
3
+ export { markdownHeadingsVitePlugin }
4
+ export type { MarkdownHeading }
5
+
6
+ type MarkdownHeading = {
7
+ title: string
8
+ id: string
9
+ headingLevel: number
10
+ titleAddendum?: string
11
+ }
12
+
13
+ function markdownHeadingsVitePlugin() {
14
+ return {
15
+ name: 'mdx-headings',
16
+ enforce: 'pre',
17
+ transform: async (code: string, id: string) => {
18
+ if (!id.includes('.page.') || !id.endsWith('.mdx')) {
19
+ return
20
+ }
21
+ const codeNew = transform(code)
22
+ return codeNew
23
+ }
24
+ }
25
+ }
26
+
27
+ function transform(code: string) {
28
+ const headings: MarkdownHeading[] = []
29
+ let isCodeBlock = false
30
+ let codeNew = code
31
+ .split('\n')
32
+ .map((line) => {
33
+ // Skip code blocks, e.g.
34
+ // ~~~md
35
+ // # Markdown Example
36
+ // Bla
37
+ // ~~~
38
+ if (line.startsWith('~~~') || line.startsWith('```')) {
39
+ isCodeBlock = !isCodeBlock
40
+ return line
41
+ }
42
+ if (isCodeBlock) {
43
+ return line
44
+ }
45
+
46
+ if (line.startsWith('#')) {
47
+ const { id, headingLevel, title, headingHtml } = parseMarkdownHeading(line)
48
+ headings.push({ id, headingLevel, title })
49
+ return headingHtml
50
+ }
51
+ if (line.startsWith('<h')) {
52
+ assert(false, { line })
53
+ }
54
+
55
+ return line
56
+ })
57
+ .join('\n')
58
+ const headingsExportCode = `export const headings = [${headings
59
+ .map((heading) => JSON.stringify(heading))
60
+ .join(', ')}];`
61
+ codeNew += `\n\n${headingsExportCode}\n`
62
+ return codeNew
63
+ }
64
+
65
+ function parseMarkdownHeading(line: string): MarkdownHeading & { headingHtml: string } {
66
+ const [lineBegin, ...lineWords] = line.split(' ')
67
+ assert(lineBegin.split('#').join('') === '', { line, lineWords })
68
+ const headingLevel = lineBegin.length
69
+
70
+ const titleMdx = lineWords.join(' ')
71
+ assert(!titleMdx.startsWith(' '), { line, lineWords })
72
+ assert(titleMdx, { line, lineWords })
73
+
74
+ const id = determineSectionUrlHash(titleMdx)
75
+ const title = titleMdx
76
+
77
+ const headingHtml = `<h${headingLevel} id="${id}">${parseTitle(title)}</h${headingLevel}>`
78
+
79
+ const heading = { headingLevel, title, id, headingHtml }
80
+ return heading
81
+ }
82
+
83
+ function parseTitle(titleMarkdown: string): string {
84
+ type Part = { nodeType: 'text' | 'code'; content: string }
85
+ const parts: Part[] = []
86
+ let current: Part | undefined
87
+ titleMarkdown.split('').forEach((letter) => {
88
+ if (letter === '`') {
89
+ if (current?.nodeType === 'code') {
90
+ // </code>
91
+ parts.push(current)
92
+ current = undefined
93
+ } else {
94
+ // <code>
95
+ if (current) {
96
+ parts.push(current)
97
+ }
98
+ current = { nodeType: 'code', content: '' }
99
+ }
100
+ } else {
101
+ if (!current) {
102
+ current = { nodeType: 'text', content: '' }
103
+ }
104
+ current.content += letter
105
+ }
106
+ })
107
+ if (current) {
108
+ parts.push(current)
109
+ }
110
+
111
+ const titleHtml = parts
112
+ .map((part) => {
113
+ if (part.nodeType === 'code') {
114
+ return `<code>${serializeText(part.content)}</code>`
115
+ } else {
116
+ assert(part.nodeType === 'text', { parts })
117
+ return serializeText(part.content)
118
+ }
119
+ })
120
+ .join('')
121
+
122
+ return titleHtml
123
+
124
+ function serializeText(text: string) {
125
+ const textEscaped = text.split("'").join("\\'")
126
+ return `{'${textEscaped}'}`
127
+ }
128
+ }
@@ -0,0 +1,41 @@
1
+ /*
2
+ .nav-item {
3
+ border-top: 1px solid transparent;
4
+ }
5
+ .nav-item.is-active {
6
+ border-color: #eaeaea;
7
+ }
8
+ .nav-item[is-active] {
9
+ background-color: attr(is-active)
10
+ }
11
+ */
12
+
13
+ /*
14
+ #navigation-container:not(:hover) ::-webkit-scrollbar {
15
+ display: none;
16
+ }
17
+ #navigation-container ::-webkit-scrollbar-thumb {
18
+ opacity: 0;
19
+ }
20
+ #navigation-container ::-webkit-scrollbar-track {
21
+ opacity: 0;
22
+ }
23
+ */
24
+
25
+ /*
26
+ .nav-tree:not(.expanded) > .nav-tree {
27
+ edisplay: none;
28
+ background:red;
29
+ }
30
+ */
31
+ /*
32
+ .nav-tree.expanded,
33
+ .nav-tree.expanded > .nav-tree {
34
+ display: inherit;
35
+ }
36
+ .nav-tree.expanded,
37
+ .nav-tree.expanded > .nav-tree {
38
+ display: inherit;
39
+ background:red;
40
+ }
41
+ */
@@ -0,0 +1,122 @@
1
+ .nav-item {
2
+ display: block;
3
+ white-space: nowrap;
4
+ overflow-x: hidden;
5
+ --padding-left-global: 9px;
6
+ --padding-left-additional: 0px;
7
+ }
8
+ .nav-item code {
9
+ font-size: 0.9em;
10
+ }
11
+ .nav-item-h1 + .nav-item-h4 {
12
+ margin-top: -2px;
13
+ }
14
+ .nav-item-h4 {
15
+ margin-top: 14px;
16
+ margin-bottom: -1px;
17
+ color: #999;
18
+ font-size: 12px;
19
+ font-weight: 400;
20
+ letter-spacing: 0.15ch;
21
+ padding-left: var(--padding-left-global);
22
+ padding-right: 4px;
23
+ text-decoration: none;
24
+ }
25
+ .nav-column:first-of-type > .nav-h1-group:first-of-type > .nav-item-h1:first-of-type {
26
+ margin-top: 20px;
27
+ }
28
+ .nav-item-h1 {
29
+ margin-top: 30px;
30
+ font-size: 15.4px;
31
+ text-transform: uppercase;
32
+ font-weight: 600;
33
+ letter-spacing: 0.15ch;
34
+ color: var(--color-text);
35
+ padding: 12px 0;
36
+ padding-left: calc(var(--padding-left-global) - 2px);
37
+ padding-right: 4px;
38
+ text-decoration: none;
39
+ }
40
+ .nav-item-h2 {
41
+ text-decoration: none;
42
+ font-size: 14.4px;
43
+ font-weight: 400;
44
+ letter-spacing: 0.15ch;
45
+ color: var(--color-text);
46
+ padding-left: var(--padding-left-global);
47
+ padding-right: 0;
48
+ --padding: 4px;
49
+ padding-top: var(--padding);
50
+ padding-bottom: var(--padding);
51
+ }
52
+ .nav-item-h3 {
53
+ font-size: 12px;
54
+ font-weight: 400;
55
+ letter-spacing: 0.15ch;
56
+ color: var(--color-text);
57
+ text-decoration: none;
58
+ --padding: 5px;
59
+
60
+ background-color: #f9f9f9;
61
+ padding: var(--padding) 0;
62
+ padding-left: calc(var(--padding-left-global) + var(--padding-left-additional));
63
+ }
64
+ html.navigation-fullscreen .nav-item-h3 {
65
+ border-right: 4px solid #eee;
66
+ }
67
+ .nav-item-h3.nav-item-first-of-its-kind {
68
+ padding-top: calc(var(--padding) * 1.6);
69
+ }
70
+ .nav-item-h3.nav-item-last-of-its-kind {
71
+ padding-bottom: calc(var(--padding) * 2);
72
+ }
73
+ .nav-item-h2,
74
+ .nav-item-h3 {
75
+ position: relative;
76
+ }
77
+ /*
78
+ .nav-item-h2.is-active .nav-item-text{
79
+ background-color: var(--background-color);
80
+ }
81
+ */
82
+ .nav-item-h2.is-active {
83
+ background-color: var(--background-color);
84
+ }
85
+ .nav-item-h3.is-active:before {
86
+ display: block;
87
+ content: '';
88
+ position: absolute;
89
+ width: 4px;
90
+ left: 0;
91
+ top: 0;
92
+ height: 100%;
93
+ background-color: var(--background-color);
94
+ z-index: 10;
95
+ }
96
+ .nav-item-h3.is-active-last:after {
97
+ display: block;
98
+ content: '';
99
+ position: absolute;
100
+ height: 4px;
101
+ left: 0;
102
+ bottom: 0;
103
+ width: 100%;
104
+ background-color: var(--background-color);
105
+ z-index: 10;
106
+ border-bottom-left-radius: 5px;
107
+ }
108
+ html.navigation-fullscreen .nav-item {
109
+ --expend-border-radius: 5px;
110
+ }
111
+ .nav-item.is-active-first {
112
+ border-top-left-radius: 5px;
113
+ border-top-right-radius: var(--expend-border-radius);
114
+ }
115
+ .nav-item.is-active-last,
116
+ .nav-item.is-active-last:before {
117
+ border-bottom-left-radius: 5px;
118
+ border-bottom-right-radius: var(--expend-border-radius);
119
+ }
120
+ .nav-item-h3.nav-item-parent-is-list-heading {
121
+ --padding-left-additional: 21px;
122
+ }
@@ -0,0 +1,118 @@
1
+ :root {
2
+ --mobile-header-height: 60px;
3
+ --navigation-min-height: 300px;
4
+ --navigation-max-height: 350px;
5
+ }
6
+ #navigation-wrapper {
7
+ min-width: var(--navigation-min-height);
8
+ }
9
+ .doc-page #navigation-wrapper {
10
+ max-width: var(--navigation-max-height);
11
+ }
12
+ .landing-page #navigation-wrapper {
13
+ /* prettier-ignore */
14
+ max-width: min(var(--navigation-max-height), max(var(--navigation-min-height), calc(var(--navigation-min-height) + 100vw - 1240px)));
15
+ }
16
+ #navigation-container {
17
+ /* `position: fixed` doesn't inherit the parent's width */
18
+ position: sticky;
19
+ overflow-y: auto;
20
+ overscroll-behavior: contain;
21
+ border-right: 1px solid #eee;
22
+ }
23
+ #navigation-mask {
24
+ position: fixed;
25
+ width: 100vw;
26
+ height: 100vh;
27
+ top: 0;
28
+ left: 0;
29
+ z-index: 2;
30
+ }
31
+ /* `1140px` is the breaking point that preserves the width of code blocks. */
32
+ /* BEFORE EDITING THIS: also change the `1139px` value below */
33
+ @media screen and (min-width: 1140px) {
34
+ #mobile-header {
35
+ display: none !important;
36
+ }
37
+ #navigation-container {
38
+ height: 100vh;
39
+ top: 0;
40
+ }
41
+ #navigation-mask {
42
+ display: none;
43
+ }
44
+ }
45
+ #navigation-container {
46
+ transition: transform 0.25s ease;
47
+ background: white;
48
+ z-index: 3;
49
+ }
50
+ #page-wrapper {
51
+ margin-left: calc(-1 * var(--navigation-fullscreen-button-width));
52
+ }
53
+ @media screen and (max-width: 1139px) {
54
+ #navigation-header-logo,
55
+ #docsearch {
56
+ display: none !important;
57
+ }
58
+ #navigation-wrapper {
59
+ min-width: 0px !important;
60
+ max-width: 0px !important;
61
+ }
62
+ #navigation-fullscreen-button {
63
+ display: none;
64
+ }
65
+ #page-wrapper {
66
+ margin-left: 0 !important;
67
+ }
68
+ #navigation-container {
69
+ --width: min(100vw, 350px);
70
+ width: var(--width);
71
+ left: 0;
72
+ height: calc(100vh - var(--mobile-header-height));
73
+ top: var(--mobile-header-height);
74
+ padding-top: 20px;
75
+ /* `position: sticky` doesn't seem to work on mobile */
76
+ position: fixed;
77
+ }
78
+ body:not(.show-menu) #navigation-container {
79
+ transform: translateX(calc(-1 * var(--width)));
80
+ }
81
+ body:not(.show-menu) #navigation-mask {
82
+ display: none;
83
+ }
84
+ #mobile-header {
85
+ display: inherit;
86
+ }
87
+ .doc-page h2 {
88
+ --padding-top: calc(var(--mobile-header-height) + 12px) !important;
89
+ }
90
+ }
91
+
92
+ html.navigation-fullscreen #navigation-container {
93
+ width: 100vw;
94
+ height: 100vh;
95
+ overflow-y: scroll;
96
+ position: fixed;
97
+ top: 0;
98
+ left: 0;
99
+ background-color: white;
100
+ }
101
+ html.navigation-fullscreen #navigation-header {
102
+ display: none !important;
103
+ }
104
+ html.navigation-fullscreen #navigation-content {
105
+ display: flex;
106
+ margin: auto; /* A `max-width` is set by src/navigation/navigation-fullscreen/initNavigationFullscreen.ts */
107
+ }
108
+ html.navigation-fullscreen #navigation-content > .nav-column {
109
+ flex-grow: 1;
110
+ max-width: 350px;
111
+ }
112
+ html.navigation-fullscreen .nav-column > .nav-h1-group:first-child > .nav-item-h1:first-child {
113
+ margin-top: 0px;
114
+ }
115
+ html.navigation-fullscreen {
116
+ /* disable scroll of main view */
117
+ overflow: hidden !important;
118
+ }
@@ -0,0 +1,303 @@
1
+ // @ts-nocheck
2
+
3
+ import { headings as headingsCrawled } from './Docs.mdx'
4
+ import { assert, isBrowser } from './utils/client'
5
+ import { Heading } from './types'
6
+ const headings: Heading[] = [
7
+ {
8
+ level: 2,
9
+ title: 'Overview',
10
+ isDocumentBegin: true
11
+ },
12
+ ...headingsCrawled
13
+ ]
14
+ assert_headings()
15
+
16
+ export { updateSidePanelScroll }
17
+
18
+ if (isBrowser()) {
19
+ setTimeout(updateSidePanelScroll, 0)
20
+ window.addEventListener('scroll', updateSidePanelScroll, { passive: true })
21
+ window.addEventListener('resize', updateSidePanelScroll, { passive: true })
22
+ }
23
+
24
+ function updateSidePanelScroll() {
25
+ const headingSectionsVisibility = getHeadingSectionsVisbility(headings)
26
+ setActiveHeadings(headingSectionsVisibility)
27
+ renderNavScrollBar(headingSectionsVisibility)
28
+ }
29
+
30
+ function setActiveHeadings(headingSectionsVisibility: HeadingSectionVisibility[]) {
31
+ /*
32
+ const viewportPercentageHighest = Math.max(
33
+ ...headingSectionsVisibility.map(({ viewportPercentage }) => viewportPercentage)
34
+ )
35
+ */
36
+ const visibleHeadings = getVisibleHeadings(headingSectionsVisibility)
37
+ // console.log('v', visibleHeadings);
38
+ headingSectionsVisibility.forEach((heading) => {
39
+ const navItem = findNavLink(heading)
40
+ const isVisibile = visibleHeadings.find((visibleHeading) => {
41
+ if ('isDocumentBegin' in heading || 'isDocumentBegin' in visibleHeading) {
42
+ return 'isDocumentBegin' in heading && 'isDocumentBegin' in visibleHeading
43
+ } else {
44
+ return heading.id === visibleHeading.id
45
+ }
46
+ })
47
+ navItem.style.display = isVisibile ? 'inherit' : 'none'
48
+ /*
49
+ if (viewportPercentageHighest === heading.viewportPercentage) {
50
+ const navItem = findNavLink(heading)
51
+ updateNavTreeExpendedState(navItem)
52
+ }
53
+ */
54
+ })
55
+ headingSectionsVisibility.forEach((heading) => {
56
+ const navItem = findNavLink(heading)
57
+ setNavItemBackgroundColor(navItem, heading.viewportPercentage)
58
+ })
59
+ }
60
+
61
+ function updateNavTreeExpendedState(navItem: HTMLElement) {
62
+ const navItemHref = navItem.getAttribute('href')
63
+ assert(typeof navItemHref === 'string')
64
+ const selector = `a[href="${navItemHref}"]`
65
+ //console.log(11, selector, 2)
66
+ assert(document.querySelector(selector))
67
+ document.querySelectorAll('.nav-tree').forEach((navTree) => {
68
+ const isExpended = !!navTree.querySelector(selector)
69
+ //console.log(navTree.href, isExpended)
70
+ navTree.classList[isExpended ? 'add' : 'remove']('expanded')
71
+ })
72
+ }
73
+
74
+ function setNavItemBackgroundColor(navItem: HTMLElement, viewportPercentage: number) {
75
+ if (viewportPercentage) {
76
+ assert(viewportPercentage >= 0 && viewportPercentage <= 1)
77
+ const backgroundColor = `rgba(0, 0, 0, ${viewportPercentage / 20})`
78
+ navItem.style.backgroundColor = backgroundColor
79
+ } else {
80
+ navItem.style.backgroundColor = 'transparent'
81
+ }
82
+ }
83
+
84
+ function findNavLink(heading: Heading): HTMLElement {
85
+ const href = getHref(heading)
86
+ const navigationEl = getNavigationEl()
87
+ const navLinks: HTMLElement[] = Array.from(navigationEl.querySelectorAll(`a[href="${href}"]`))
88
+ assert(navLinks.length === 1)
89
+ return navLinks[0]
90
+ }
91
+
92
+ let _navigationEl: HTMLElement
93
+ function getNavigationEl(): HTMLElement {
94
+ _navigationEl = _navigationEl || document.getElementById('navigation-content')
95
+ assert(_navigationEl)
96
+ return _navigationEl
97
+ }
98
+
99
+ function renderNavScrollBar(headingSectionsVisibility: HeadingSectionVisibility[]) {
100
+ const { headingVisibleFirst, headingVisibleLast } = getBoundaryHeading(headingSectionsVisibility)
101
+ assertBoundaryPosition(headingVisibleFirst.boundaryPosition)
102
+ assertBoundaryPosition(headingVisibleLast.boundaryPosition)
103
+
104
+ const navigationEl = getNavigationEl()
105
+ const getLinkNavPosition = (el: HTMLElement): number => {
106
+ let offsetY = el.offsetTop
107
+ const parentEl = el.offsetParent as HTMLElement
108
+ if (parentEl !== navigationEl) {
109
+ offsetY += getLinkNavPosition(parentEl)
110
+ }
111
+ return offsetY
112
+ }
113
+ const getOverlayPosition = ({
114
+ heading,
115
+ boundaryPosition
116
+ }: {
117
+ heading: Heading
118
+ boundaryPosition: number
119
+ }): number => {
120
+ const navLink = findNavLink(heading)
121
+ const navLinkPos = getLinkNavPosition(navLink)
122
+ const scrollOverlayBoundaryPos = navLinkPos + navLink.clientHeight * boundaryPosition
123
+ return scrollOverlayBoundaryPos
124
+ }
125
+ const overlayBegin = getOverlayPosition(headingVisibleFirst)
126
+ const overlayEnd = getOverlayPosition(headingVisibleLast)
127
+ const overlayHeight = Math.max(1, overlayEnd - overlayBegin)
128
+ const scrollOverlayEl = document.getElementById('scroll-overlay')!
129
+ scrollOverlayEl.style.top = overlayBegin + 'px'
130
+ scrollOverlayEl.style.height = overlayHeight + 'px'
131
+ }
132
+
133
+ function getHeadingPosition(heading: Heading): number {
134
+ if ('isDocumentBegin' in heading) return 0
135
+ const { id } = heading
136
+ assert(id)
137
+ const el = document.getElementById(id)
138
+ assert(el)
139
+
140
+ // `el.getBoundingClientRect()` returns position relative to viewport begin
141
+ // - https://stackoverflow.com/questions/5598743/finding-elements-position-relative-to-the-document
142
+ const headingPositionRelative = el.getBoundingClientRect().top
143
+
144
+ const scrollPosition = getScrollPosition()
145
+
146
+ // We add the viewport begin position
147
+ const headingPosition = headingPositionRelative + scrollPosition
148
+
149
+ return headingPosition
150
+ }
151
+ function getDocumentHeight(): number {
152
+ return document.body.clientHeight
153
+ }
154
+ function getViewportHeight(): number {
155
+ return window.innerHeight
156
+ }
157
+ function getScrollPosition(): number {
158
+ return window.scrollY
159
+ }
160
+
161
+ type HeadingVisible = { heading: Heading; boundaryPosition: number; viewportPercentage: number }
162
+ function getVisibleHeadings(headingSectionsVisibility: HeadingSectionVisibility[]): HeadingSectionVisibility[] {
163
+ const headings_withVisibileNavItem: Heading[] = filterHeadingsWithVisibileNavItem(headingSectionsVisibility)
164
+ const headingSectionsWithVisibleNavItemVisibility = getHeadingSectionsVisbility(headings_withVisibileNavItem)
165
+ return headingSectionsWithVisibleNavItemVisibility
166
+ }
167
+ function getBoundaryHeading(headingSectionsVisibility: HeadingSectionVisibility[]): {
168
+ headingVisibleFirst: HeadingVisible
169
+ headingVisibleLast: HeadingVisible
170
+ } {
171
+ const headingSectionsWithVisibleNavItemVisibility = getVisibleHeadings(headingSectionsVisibility)
172
+ const h: HeadingSectionVisibility[] = headingSectionsWithVisibleNavItemVisibility
173
+ const hFirst = h.find(({ screenBeginPosition }) => screenBeginPosition !== null)
174
+ const hLast = h.find(({ screenEndPosition }) => screenEndPosition !== null)
175
+ assert(hFirst)
176
+ assert(hLast)
177
+ assert(hFirst.screenBeginPosition !== null)
178
+ assert(hLast.screenEndPosition !== null)
179
+ const headingVisibleFirst = {
180
+ heading: hFirst,
181
+ boundaryPosition: hFirst.screenBeginPosition,
182
+ viewportPercentage: hFirst.viewportPercentage
183
+ }
184
+ const headingVisibleLast = {
185
+ heading: hLast,
186
+ boundaryPosition: hLast.screenEndPosition,
187
+ viewportPercentage: hLast.viewportPercentage
188
+ }
189
+ return { headingVisibleFirst, headingVisibleLast }
190
+ }
191
+
192
+ function filterHeadingsWithVisibileNavItem(headingSectionsVisibility: HeadingSectionVisibility[]): Heading[] {
193
+ const hs = headingSectionsVisibility
194
+ const viewportPercentageHighest = Math.max(...hs.map(({ viewportPercentage }) => viewportPercentage))
195
+ const mainHeading = hs.find((heading) => {
196
+ return heading.viewportPercentage === viewportPercentageHighest
197
+ })
198
+ assert(mainHeading)
199
+ const ancestors = getAncestors(mainHeading, hs)
200
+ const headings_withVisibileNavItem = hs.filter((heading) => {
201
+ const p = getParent(heading, hs)
202
+ return p === null || ancestors.includes(p)
203
+ })
204
+ return headings_withVisibileNavItem
205
+ }
206
+
207
+ function getParent(heading: Heading, headings: Heading[]): Heading | null {
208
+ return getAncestors(heading, headings)[0] || null
209
+ }
210
+
211
+ function getAncestors(heading: Heading, headings: Heading[]): Heading[] {
212
+ const headingIndex = headings.indexOf(heading)
213
+ assert(headingIndex >= 0)
214
+ const ancestors: Heading[] = []
215
+ let currentLevel = heading.level - 1
216
+ for (let i = headingIndex - 1; i >= 0; i--) {
217
+ const h = headings[i]
218
+ if (h.level === currentLevel) {
219
+ ancestors.push(h)
220
+ currentLevel--
221
+ }
222
+ }
223
+ assert(currentLevel === 1)
224
+ return ancestors
225
+ }
226
+
227
+ type HeadingSectionVisibility = Heading & {
228
+ viewportPercentage: number
229
+ screenBeginPosition: null | number
230
+ screenEndPosition: null | number
231
+ }
232
+ function getHeadingSectionsVisbility(headings: Heading[]): HeadingSectionVisibility[] {
233
+ const screenBeginPositionAbsolute = getScrollPosition()
234
+ const screenEndPositionAbsolute = screenBeginPositionAbsolute + getViewportHeight()
235
+
236
+ const headingSections: (HeadingSectionVisibility & { beginPosition: number; endPosition: number })[] = []
237
+ headings.forEach((heading, i) => {
238
+ const beginPosition = getHeadingPosition(heading)
239
+
240
+ const headingNext = headings[i + 1]
241
+ const endPosition = !headingNext ? getDocumentHeight() : getHeadingPosition(headingNext)
242
+
243
+ const sectionHeight = endPosition - beginPosition
244
+ assert(sectionHeight > 0)
245
+
246
+ let screenBeginPosition = null
247
+ if (beginPosition <= screenBeginPositionAbsolute && screenBeginPositionAbsolute <= endPosition) {
248
+ screenBeginPosition = (screenBeginPositionAbsolute - beginPosition) / sectionHeight
249
+ assert(0 <= screenBeginPosition && screenBeginPosition <= 1)
250
+ }
251
+
252
+ let screenEndPosition = null
253
+ if (beginPosition <= screenEndPositionAbsolute && screenEndPositionAbsolute <= endPosition) {
254
+ screenEndPosition = (screenEndPositionAbsolute - beginPosition) / sectionHeight
255
+ assert(0 <= screenEndPosition && screenEndPosition <= 1)
256
+ }
257
+
258
+ assert(beginPosition <= endPosition)
259
+ const getViewportPosition = (position: number) => {
260
+ const viewportPosition = Math.min(Math.max(position, screenBeginPositionAbsolute), screenEndPositionAbsolute)
261
+ assert(screenBeginPositionAbsolute <= viewportPosition && viewportPosition <= screenEndPositionAbsolute)
262
+ return viewportPosition
263
+ }
264
+ const viewportPositionBegin = getViewportPosition(beginPosition)
265
+ const viewportPositionEnd = getViewportPosition(endPosition)
266
+ const viewportPercentage = (viewportPositionEnd - viewportPositionBegin) / getViewportHeight()
267
+
268
+ headingSections.push({
269
+ ...heading,
270
+ viewportPercentage,
271
+ screenBeginPosition,
272
+ screenEndPosition,
273
+ beginPosition,
274
+ endPosition
275
+ })
276
+ })
277
+
278
+ const viewportPercentageTotal = sum(headingSections.map(({ viewportPercentage }) => viewportPercentage))
279
+ const debugInfo = JSON.stringify({ headingSections, screenBeginPositionAbsolute, screenEndPositionAbsolute }, null, 2)
280
+ assert(viewportPercentageTotal <= 1 + 0.00001, debugInfo)
281
+ // assert(1 - 0.00001 <= viewportPercentageTotal && viewportPercentageTotal <= 1 + 0.00001, debugInfo)
282
+ assert(headingSections.filter(({ screenBeginPosition }) => screenBeginPosition !== null).length === 1, debugInfo)
283
+ assert(headingSections.filter(({ screenEndPosition }) => screenEndPosition !== null).length === 1, debugInfo)
284
+
285
+ return headingSections
286
+ }
287
+
288
+ function assertBoundaryPosition(boundaryPosition: number) {
289
+ assert(0 <= boundaryPosition && boundaryPosition <= 1)
290
+ }
291
+
292
+ function sum(numbers: number[]): number {
293
+ return numbers.reduce((a, b) => a + b, 0)
294
+ }
295
+
296
+ function assert_headings() {
297
+ const hrefs: string[] = []
298
+ headings.forEach((heading) => {
299
+ const href = getHref(heading)
300
+ assert(!hrefs.includes(href), href)
301
+ hrefs.push(href)
302
+ })
303
+ }