@brillout/docpress 0.1.13 → 0.1.15

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-V7ZWIKEX.js → chunk-J7PKG3ZA.js} +38 -15
  2. package/dist/chunk-J7PKG3ZA.js.map +1 -0
  3. package/dist/{devServer-6O6KCRPG.js → devServer-RUTLBMIJ.js} +2 -5
  4. package/dist/{devServer-6O6KCRPG.js.map → devServer-RUTLBMIJ.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 +8 -11
  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 +52 -0
  111. package/dist/chunk-G2A5MZJA.js +0 -48
  112. package/dist/chunk-G2A5MZJA.js.map +0 -1
  113. package/dist/chunk-M7LTQJLV.js +0 -178
  114. package/dist/chunk-M7LTQJLV.js.map +0 -1
  115. package/dist/chunk-OEVBWUR6.js +0 -92
  116. package/dist/chunk-OEVBWUR6.js.map +0 -1
  117. package/dist/chunk-P6M6A4HU.js +0 -154
  118. package/dist/chunk-P6M6A4HU.js.map +0 -1
  119. package/dist/chunk-TTLAZ2T2.js +0 -8
  120. package/dist/chunk-TTLAZ2T2.js.map +0 -1
  121. package/dist/chunk-V7ZWIKEX.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,157 @@
1
+ import { assert, jsxToTextContent, objectAssign } from '../utils/server'
2
+ import { getHeadings, HeadingWithoutLink, parseTitle } from '../headings'
3
+ import type { Heading } from '../headings'
4
+ import type { PageContextBuiltIn } from 'vite-plugin-ssr'
5
+ import type { MarkdownHeading } from '../markdownHeadingsVitePlugin'
6
+ import type { Config } from './Config'
7
+ import { getConfig } from './getConfig'
8
+
9
+ export { resolvePageContext }
10
+ export type { PageContextOriginal }
11
+ export type { PageContextResolved }
12
+ export type { Heading }
13
+
14
+ type ReactComponent = () => JSX.Element
15
+ type Exports = {
16
+ headings?: MarkdownHeading[]
17
+ }
18
+ type PageContextOriginal = PageContextBuiltIn & {
19
+ Page: ReactComponent
20
+ exports: Exports
21
+ }
22
+ type PageContextResolved = ReturnType<typeof resolvePageContext>
23
+
24
+ function resolvePageContext(pageContext: PageContextOriginal) {
25
+ const config = getConfig()
26
+ const { headings, headingsWithoutLink } = getHeadings(config)
27
+ const activeHeading = findActiveHeading(headings, headingsWithoutLink, pageContext)
28
+ const headingsWithSubHeadings = getHeadingsWithSubHeadings(headings, pageContext, activeHeading)
29
+ const { title, isLandingPage, pageTitle, isDetachedPage } = getMetaData(
30
+ headingsWithoutLink,
31
+ activeHeading,
32
+ pageContext,
33
+ config
34
+ )
35
+ const { faviconUrl, algolia, tagline, twitterHandle, bannerUrl, websiteUrl } = config
36
+ const pageContextResolved = {}
37
+ objectAssign(pageContextResolved, {
38
+ ...pageContext,
39
+ meta: {
40
+ title,
41
+ faviconUrl,
42
+ twitterHandle,
43
+ bannerUrl,
44
+ websiteUrl,
45
+ tagline,
46
+ algolia
47
+ },
48
+ headings,
49
+ headingsWithSubHeadings,
50
+ isLandingPage,
51
+ isDetachedPage,
52
+ pageTitle,
53
+ config
54
+ })
55
+ return pageContextResolved
56
+ }
57
+
58
+ function getMetaData(
59
+ headingsWithoutLink: HeadingWithoutLink[],
60
+ activeHeading: Heading | null,
61
+ pageContext: { urlOriginal: string; exports: Exports },
62
+ config: Config
63
+ ) {
64
+ const url = pageContext.urlOriginal
65
+
66
+ let title: string
67
+ let pageTitle: string | JSX.Element | null
68
+ let isDetachedPage: boolean
69
+ if (activeHeading) {
70
+ title = activeHeading.titleDocument || jsxToTextContent(activeHeading.title)
71
+ pageTitle = activeHeading.title
72
+ isDetachedPage = false
73
+ } else {
74
+ pageTitle = headingsWithoutLink.find((h) => h.url === url)!.title
75
+ title = jsxToTextContent(pageTitle)
76
+ isDetachedPage = true
77
+ }
78
+
79
+ const isLandingPage = url === '/'
80
+ if (!isLandingPage) {
81
+ title += ' | ' + config.projectInfo.projectName
82
+ }
83
+
84
+ if (isLandingPage) {
85
+ pageTitle = null
86
+ }
87
+
88
+ return { title, isLandingPage, pageTitle, isDetachedPage }
89
+ }
90
+
91
+ function findActiveHeading(
92
+ headings: Heading[],
93
+ headingsWithoutLink: HeadingWithoutLink[],
94
+ pageContext: { urlOriginal: string; exports: Exports }
95
+ ): Heading | null {
96
+ let activeHeading: Heading | null = null
97
+ assert(pageContext.urlOriginal)
98
+ const pageUrl = pageContext.urlOriginal
99
+ headings.forEach((heading) => {
100
+ if (heading.url === pageUrl) {
101
+ activeHeading = heading
102
+ assert(heading.level === 2, { pageUrl, heading })
103
+ }
104
+ })
105
+ const debugInfo = {
106
+ msg: 'Heading not found for url: ' + pageUrl,
107
+ urls: headings.map((h) => h.url),
108
+ url: pageUrl
109
+ }
110
+ assert(activeHeading || headingsWithoutLink.find(({ url }) => pageUrl === url), debugInfo)
111
+ return activeHeading
112
+ }
113
+
114
+ function getHeadingsWithSubHeadings(
115
+ headings: Heading[],
116
+ pageContext: { exports: Exports; urlOriginal: string },
117
+ activeHeading: Heading | null
118
+ ): Heading[] {
119
+ const headingsWithSubHeadings = headings.slice()
120
+ if (activeHeading === null) return headingsWithSubHeadings
121
+ const activeHeadingIdx = headingsWithSubHeadings.indexOf(activeHeading)
122
+ assert(activeHeadingIdx >= 0)
123
+ const pageHeadings = pageContext.exports.headings || []
124
+ pageHeadings.forEach((pageHeading, i) => {
125
+ const title = parseTitle(pageHeading.title)
126
+ const url = '#' + pageHeading.id
127
+ assert(
128
+ pageHeading.headingLevel !== 3,
129
+ 'Wrong page heading level `' +
130
+ pageHeading.headingLevel +
131
+ '` (it should be `<h2>`) for sub-heading `' +
132
+ pageHeading.title +
133
+ '` of page `' +
134
+ pageContext.urlOriginal +
135
+ '`.'
136
+ )
137
+ if (pageHeading.headingLevel === 2) {
138
+ const heading: Heading = {
139
+ url,
140
+ title,
141
+ parentHeadings: [activeHeading, ...activeHeading.parentHeadings],
142
+ titleInNav: title,
143
+ level: 3
144
+ }
145
+ headingsWithSubHeadings.splice(activeHeadingIdx + 1 + i, 0, heading)
146
+ }
147
+ })
148
+
149
+ if (activeHeading?.sectionTitles) {
150
+ activeHeading.sectionTitles.forEach((sectionTitle) => {
151
+ const pageHeadingTitles = pageHeadings.map((h) => h.title)
152
+ assert(pageHeadingTitles.includes(sectionTitle), { pageHeadingTitles, sectionTitle })
153
+ })
154
+ }
155
+
156
+ return headingsWithSubHeadings
157
+ }
@@ -0,0 +1,7 @@
1
+ button,
2
+ a.button {
3
+ background-color: #fafafa;
4
+ border: 1px solid #aaa;
5
+ border-radius: 5px;
6
+ cursor: pointer;
7
+ }
@@ -0,0 +1,36 @@
1
+ pre > code {
2
+ padding: 16px !important;
3
+ }
4
+ @media screen and (max-width: 900px) {
5
+ pre > code {
6
+ font-size: 0.7em;
7
+ }
8
+ }
9
+ pre {
10
+ display: grid;
11
+ }
12
+ pre > code {
13
+ padding-right: 0px !important;
14
+ overflow-x: auto;
15
+ }
16
+
17
+ @media screen and (max-width: 1139px) {
18
+ pre > code {
19
+ font-size: 0.95em;
20
+ }
21
+ }
22
+ @media screen and (max-width: 1040px) {
23
+ pre > code {
24
+ font-size: 0.8em;
25
+ }
26
+ }
27
+ @media screen and (max-width: 940px) {
28
+ pre > code {
29
+ font-size: 0.7em;
30
+ }
31
+ }
32
+
33
+ pre > code {
34
+ display: reset;
35
+ word-break: reset;
36
+ }
@@ -0,0 +1,27 @@
1
+ code {
2
+ padding: 2px 5px;
3
+ }
4
+
5
+ /*
6
+ * Line breaks in `<code>`:
7
+ * - Avoid them on desktop
8
+ * - Allow them on mobile
9
+ * - Allo them for `<code long>`
10
+ */
11
+ code {
12
+ display: inline-block;
13
+ }
14
+ @media screen and (max-width: 500px) {
15
+ code {
16
+ word-break: break-word;
17
+ display: inline;
18
+ }
19
+ }
20
+ .inline-code-break code {
21
+ display: inline;
22
+ word-break: break-word;
23
+ }
24
+ code.long {
25
+ word-break: break-word;
26
+ display: inline;
27
+ }
@@ -0,0 +1,20 @@
1
+ @import './code/inline.css';
2
+ @import './code/block.css';
3
+
4
+ code {
5
+ /*
6
+ background-color: #f4f4f4;
7
+ 0.043137255 = 1 - (#f4 / #ff)
8
+ */
9
+ background-color: rgba(0, 0, 0, 0.043137255);
10
+ border-radius: 4px;
11
+ }
12
+
13
+ /* Inline */
14
+ code {
15
+ font-size: 1.1em;
16
+ }
17
+ /* Block */
18
+ pre > code {
19
+ font-size: 1em;
20
+ }
@@ -0,0 +1,29 @@
1
+ .colorize-on-hover,
2
+ .colorize-on-hover [class^='decolorize-'],
3
+ .colorize-on-hover [class*=' decolorize-'] {
4
+ transition: filter 0.3s ease-in-out;
5
+ }
6
+ .colorize-on-hover:hover,
7
+ .colorize-on-hover:hover [class^='decolorize-'],
8
+ .colorize-on-hover:hover [class*=' decolorize-'] {
9
+ filter: grayscale(0) opacity(1) !important;
10
+ }
11
+
12
+ .decolorize-7 {
13
+ filter: grayscale(1) opacity(0.7);
14
+ }
15
+ .decolorize-6 {
16
+ filter: grayscale(1) opacity(0.6);
17
+ }
18
+ .decolorize-5 {
19
+ filter: grayscale(1) opacity(0.5);
20
+ }
21
+ .decolorize-4 {
22
+ filter: grayscale(1) opacity(0.4);
23
+ }
24
+
25
+ /*
26
+ * Twitter opacity(0.7): #ababab > #868686
27
+ * == Following computation does NOT work ==
28
+ * Discord opacity(0.7): #6c6c6c > (0.7 * (#fff - #ababab) = #868686 = x * (#fff - #6c6c6c) <=> x = 0.7 * ((#fff - #ababab) / (#fff - #6c6c6c)) = 0.7 * ((255 - 171) / (255 - 108)) = 0.4)
29
+ */
@@ -0,0 +1,19 @@
1
+ body {
2
+ font-family: 'Inter';
3
+ }
4
+ button {
5
+ font-family: inherit;
6
+ }
7
+ body {
8
+ --color-text: #323d48;
9
+ color: var(--color-text);
10
+ line-height: 1.5;
11
+ }
12
+
13
+ @font-face {
14
+ font-family: 'Inter';
15
+ font-weight: 100 900;
16
+ font-stretch: 75% 125%;
17
+ font-style: oblique 0deg 12deg;
18
+ src: url('./Inter-Var.ttf') format('truetype-variations');
19
+ }
@@ -0,0 +1,25 @@
1
+ .doc-page h2 {
2
+ margin-top: 50px;
3
+ margin-bottom: 16px;
4
+ }
5
+ .doc-page h2[id] {
6
+ cursor: pointer;
7
+ position: relative;
8
+ padding-left: 20px;
9
+ margin-left: -20px;
10
+ }
11
+ .doc-page h2[id]:hover::before {
12
+ content: '#';
13
+ position: absolute;
14
+ left: calc(-1 * (0.75em - 20px));
15
+ color: #aaa;
16
+ }
17
+
18
+ .doc-page h1 + h2 {
19
+ margin-top: 0;
20
+ }
21
+
22
+ .doc-page h4 {
23
+ margin-top: 32px;
24
+ margin-bottom: 16px;
25
+ }
@@ -0,0 +1,11 @@
1
+ @import './reset.css';
2
+ @import './colorize-on-hover.css';
3
+ @import './heading.css';
4
+ @import './button.css';
5
+ @import './link.css';
6
+ @import './font.css';
7
+ @import './code.css';
8
+ @import './note.css';
9
+ @import './table.css';
10
+ @import './tooltip.css';
11
+ @import '../algolia/DocSearch.css';
@@ -0,0 +1,17 @@
1
+ /*
2
+ :root {
3
+ --link-color: #4747ff;
4
+ }
5
+ */
6
+
7
+ a {
8
+ /*
9
+ color: inherit;
10
+ color: var(--link-color);
11
+ */
12
+ color: #4747ff;
13
+ text-decoration: none;
14
+ }
15
+ a > button {
16
+ color: var(--color-text);
17
+ }
@@ -0,0 +1,26 @@
1
+ blockquote {
2
+ --color: 0, 0, 0;
3
+ --color-strengh-bg: 0.7;
4
+ --color-strengh-border: 1.5;
5
+ border-left: 8px solid rgba(var(--color), calc(0.06 * var(--color-strengh-border)));
6
+ background-color: rgba(var(--color), calc(0.03 * var(--color-strengh-bg)));
7
+ margin-left: 0;
8
+ padding: 4px 16px;
9
+ }
10
+ blockquote.error {
11
+ --color-strengh-bg: 1.7;
12
+ --color-strengh-border: 6;
13
+ --color: 190, 25, 49;
14
+ }
15
+ blockquote.warning {
16
+ --color-strengh-bg: 4;
17
+ --color-strengh-border: 8;
18
+ --color: 255, 204, 50;
19
+ }
20
+ blockquote > p:first-child::before,
21
+ /* blockquote > p:first-of-type::before, */
22
+ blockquote > div.paragraph:first-child::before {
23
+ font-family: emoji;
24
+ content: 'ℹ️';
25
+ margin-right: 4px;
26
+ }
@@ -0,0 +1,12 @@
1
+ * {
2
+ box-sizing: border-box;
3
+ }
4
+ body {
5
+ margin: 0;
6
+ }
7
+
8
+ /* Avoid tweet embeds to overflow the body width.
9
+ * Settings `overflow: hidden` to a container doesn't work (This is a CSS bug?) */
10
+ iframe {
11
+ max-width: 100%;
12
+ }
@@ -0,0 +1,14 @@
1
+ table th {
2
+ background-color: #f7f7f7;
3
+ }
4
+ table {
5
+ border-collapse: collapse;
6
+ }
7
+ table td,
8
+ table th {
9
+ padding: 8px;
10
+ border: 1px solid #ccc;
11
+ }
12
+ table th {
13
+ text-align: left;
14
+ }
@@ -0,0 +1,11 @@
1
+ @import 'balloon-css';
2
+
3
+ :root {
4
+ --balloon-color: #444;
5
+ --balloon-move: 2px;
6
+ --balloon-font-size: 14px;
7
+ --balloon-border-radius: 5px;
8
+ }
9
+ [aria-label][data-balloon-pos]:after {
10
+ font-family: 'Inter';
11
+ }
@@ -0,0 +1,200 @@
1
+ import React from 'react'
2
+ import { assert, Emoji, EmojiName } from './utils/server'
3
+
4
+ export { getHeadings }
5
+ export { parseTitle }
6
+
7
+ export type Heading = Omit<HeadingDefinition, 'title' | 'titleInNav'> & {
8
+ title: JSX.Element
9
+ titleInNav: JSX.Element
10
+ parentHeadings: Heading[]
11
+ // Not sure why this is needed
12
+ isListTitle?: true
13
+ sectionTitles?: string[]
14
+ }
15
+ export type HeadingWithoutLink = {
16
+ url: string
17
+ title: string | JSX.Element
18
+ }
19
+ export type HeadingDefinition = HeadingBase &
20
+ (
21
+ | ({ level: 1; titleEmoji: EmojiName } & HeadingAbstract)
22
+ | ({ level: 4 } & HeadingAbstract)
23
+ | {
24
+ level: 2
25
+ isListTitle?: true
26
+ sectionTitles?: string[]
27
+ url: string
28
+ }
29
+ | {
30
+ level: 3
31
+ url: string
32
+ }
33
+ )
34
+ type HeadingBase = {
35
+ title: string
36
+ level: number
37
+ url?: string
38
+ titleDocument?: string
39
+ titleInNav?: string
40
+ // titleSize?: string
41
+ }
42
+ type HeadingAbstract = {
43
+ url?: undefined
44
+ titleDocument?: undefined
45
+ titleInNav?: undefined
46
+ }
47
+
48
+ function getHeadings(config: { headings: HeadingDefinition[]; headingsWithoutLink: HeadingWithoutLink[] }): {
49
+ headings: Heading[]
50
+ headingsWithoutLink: HeadingWithoutLink[]
51
+ } {
52
+ const headingsWithoutParent: Omit<Heading, 'parentHeadings'>[] = config.headings.map((heading: HeadingDefinition) => {
53
+ const titleProcessed: JSX.Element = parseTitle(heading.title)
54
+
55
+ const titleInNav = heading.titleInNav || heading.title
56
+ let titleInNavProcessed: JSX.Element
57
+ if ('isListTitle' in heading) {
58
+ assert(heading.isListTitle === true)
59
+ let titleParsed: JSX.Element = parseTitle(titleInNav)
60
+ // if (heading.titleSize) {
61
+ // titleParsed = React.createElement('span', { style: { fontSize: heading.titleSize } }, titleParsed)
62
+ // }
63
+ titleInNavProcessed = React.createElement(React.Fragment, {}, getListPrefix(), titleParsed)
64
+ } else {
65
+ titleInNavProcessed = parseTitle(titleInNav)
66
+ }
67
+ if ('titleEmoji' in heading) {
68
+ assert(heading.titleEmoji)
69
+ titleInNavProcessed = withEmoji(heading.titleEmoji, titleInNavProcessed)
70
+ }
71
+
72
+ const headingProcessed: Omit<Heading, 'parentHeadings'> = {
73
+ ...heading,
74
+ title: titleProcessed,
75
+ titleInNav: titleInNavProcessed
76
+ }
77
+ return headingProcessed
78
+ })
79
+
80
+ const headings: Heading[] = []
81
+ headingsWithoutParent.forEach((heading) => {
82
+ const parentHeadings = findParentHeadings(heading, headings)
83
+ headings.push({ ...heading, parentHeadings })
84
+ })
85
+
86
+ const headingsWithoutLink = config.headingsWithoutLink.map((headingsWithoutLink) => {
87
+ const { url, title } = headingsWithoutLink
88
+ assert(
89
+ headings.find((heading) => heading.url === url) === undefined,
90
+ `remove ${headingsWithoutLink.url} from headingsWithoutLink`
91
+ )
92
+ const titleProcessed = typeof title === 'string' ? parseTitle(title) : title
93
+ return {
94
+ ...headingsWithoutLink,
95
+ title: titleProcessed
96
+ }
97
+ })
98
+
99
+ assertHeadingsUrl([...headings, ...headingsWithoutLink])
100
+ return { headings, headingsWithoutLink }
101
+ }
102
+
103
+ function findParentHeadings(heading: Omit<Heading, 'parentHeadings'>, headings: Heading[]) {
104
+ const parentHeadings: Heading[] = []
105
+ let levelCurrent = heading.level
106
+ let listTitleParentFound = false
107
+ headings
108
+ .slice()
109
+ .reverse()
110
+ .forEach((parentCandidate) => {
111
+ let isListTitleParent = false
112
+ if (
113
+ !listTitleParentFound &&
114
+ levelCurrent === heading.level &&
115
+ parentCandidate.level === heading.level &&
116
+ !parentCandidate.isListTitle &&
117
+ heading.isListTitle
118
+ ) {
119
+ isListTitleParent = true
120
+ listTitleParentFound = true
121
+ }
122
+
123
+ const isParent = parentCandidate.level < levelCurrent
124
+
125
+ if (isParent || isListTitleParent) {
126
+ levelCurrent = parentCandidate.level
127
+ parentHeadings.push(parentCandidate)
128
+ }
129
+ })
130
+ return parentHeadings
131
+ }
132
+
133
+ function assertHeadingsUrl(headings: { url?: string }[]) {
134
+ const urls: Record<string, true> = {}
135
+ headings.forEach((heading) => {
136
+ if (heading.url) {
137
+ const { url } = heading
138
+ assert(url.startsWith('/'))
139
+ assert(!urls[url], { url })
140
+ urls[url] = true
141
+ }
142
+ })
143
+ }
144
+
145
+ function getListPrefix() {
146
+ const nonBreakingSpace = String.fromCodePoint(0x00a0)
147
+ const bulletPoint = String.fromCodePoint(0x2022)
148
+ return nonBreakingSpace + bulletPoint + nonBreakingSpace
149
+ }
150
+
151
+ function parseTitle(title: string): JSX.Element {
152
+ type Part = { nodeType: 'text' | 'code'; content: string }
153
+ const parts: Part[] = []
154
+ let current: Part | undefined
155
+ title.split('').forEach((letter) => {
156
+ if (letter === '`') {
157
+ if (current?.nodeType === 'code') {
158
+ // </code>
159
+ parts.push(current)
160
+ current = undefined
161
+ } else {
162
+ // <code>
163
+ if (current) {
164
+ parts.push(current)
165
+ }
166
+ current = { nodeType: 'code', content: '' }
167
+ }
168
+ } else {
169
+ if (!current) {
170
+ current = { nodeType: 'text', content: '' }
171
+ }
172
+ current.content += letter
173
+ }
174
+ })
175
+ if (current) {
176
+ parts.push(current)
177
+ }
178
+
179
+ const titleJsx = React.createElement(
180
+ React.Fragment,
181
+ {},
182
+ ...parts.map((part, i) =>
183
+ React.createElement(part.nodeType === 'code' ? 'code' : React.Fragment, { key: i }, part.content)
184
+ )
185
+ )
186
+
187
+ return titleJsx
188
+ }
189
+
190
+ function withEmoji(name: EmojiName, title: string | JSX.Element): JSX.Element {
191
+ const style = { fontSize: '1.4em' }
192
+ //return React.createElement(React.Fragment, null, Emoji({ name, style }), ' ', title)
193
+ return React.createElement(
194
+ 'span',
195
+ { style },
196
+ Emoji({ name }),
197
+ ' ',
198
+ React.createElement('span', { style: { fontSize: '1rem' } }, title)
199
+ )
200
+ }
File without changes
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './components'
2
+ export * from './utils/server'
3
+ export * from './types'
@@ -0,0 +1,50 @@
1
+ import { assert } from './utils/client'
2
+
3
+ installSectionUrlHashs()
4
+ /* Let browser restore previous scroll
5
+ jumpToSection()
6
+ */
7
+
8
+ function installSectionUrlHashs() {
9
+ const pageContainer = document.querySelector('.doc-page #page-container')
10
+ if (!pageContainer) {
11
+ assert(window.location.pathname === '/')
12
+ return
13
+ }
14
+ const navigationEl = document.getElementById('navigation-content')
15
+ assert(navigationEl)
16
+ const docSections = Array.from(document.querySelectorAll('h2'))
17
+ docSections.forEach((docSection) => {
18
+ const docTitle = docSection.textContent
19
+ assert(docTitle)
20
+ assert(docSection.id, { docSection })
21
+ const urlHash = '#' + docSection.id
22
+ assertNavLink(navigationEl, urlHash)
23
+ docSection.onclick = () => {
24
+ window.location.hash = urlHash
25
+ // The browser doesn't jump if hash doesn't change
26
+ jumpToSection()
27
+ }
28
+ })
29
+ }
30
+
31
+ function assertNavLink(navigationEl: HTMLElement, urlHash: string) {
32
+ const parentNavLinkMatch = Array.from(navigationEl.querySelectorAll(`a[href="${window.location.pathname}"]`))
33
+ assert(parentNavLinkMatch.length <= 1)
34
+ if (parentNavLinkMatch.length === 0) return
35
+ const navLinks: HTMLElement[] = Array.from(navigationEl.querySelectorAll(`a[href="${urlHash}"]`))
36
+ assert(navLinks.length === 1, { urlHash })
37
+ }
38
+
39
+ function jumpToSection() {
40
+ const { hash } = window.location
41
+ if (hash === '' || hash === '#') {
42
+ return
43
+ }
44
+ assert(hash.startsWith('#'))
45
+ const target = document.getElementById(hash.slice(1))
46
+ if (!target) {
47
+ return
48
+ }
49
+ target.scrollIntoView()
50
+ }