methanol 0.0.0 → 0.0.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.
Files changed (48) hide show
  1. package/LICENSE +203 -0
  2. package/README.md +58 -0
  3. package/banner.txt +6 -0
  4. package/bin/methanol.js +24 -0
  5. package/index.js +22 -0
  6. package/package.json +51 -9
  7. package/src/assets.js +30 -0
  8. package/src/build-system.js +200 -0
  9. package/src/components.js +145 -0
  10. package/src/config.js +396 -0
  11. package/src/dev-server.js +632 -0
  12. package/src/main.js +133 -0
  13. package/src/mdx.js +406 -0
  14. package/src/node-loader.js +88 -0
  15. package/src/pagefind.js +107 -0
  16. package/src/pages.js +771 -0
  17. package/src/preview-server.js +58 -0
  18. package/src/public-assets.js +73 -0
  19. package/src/register-loader.js +29 -0
  20. package/src/rehype-plugins/link-resolve.js +116 -0
  21. package/src/rehype-plugins/methanol-ctx.js +89 -0
  22. package/src/renderer.js +25 -0
  23. package/src/rewind.js +117 -0
  24. package/src/stage-logger.js +59 -0
  25. package/src/state.js +179 -0
  26. package/src/virtual-module/inject.js +30 -0
  27. package/src/virtual-module/loader.js +116 -0
  28. package/src/virtual-module/pagefind.js +108 -0
  29. package/src/vite-plugins.js +173 -0
  30. package/themes/default/components/ThemeAccentSwitch.client.jsx +95 -0
  31. package/themes/default/components/ThemeAccentSwitch.static.jsx +23 -0
  32. package/themes/default/components/ThemeColorSwitch.client.jsx +95 -0
  33. package/themes/default/components/ThemeColorSwitch.static.jsx +23 -0
  34. package/themes/default/components/ThemeSearchBox.client.jsx +324 -0
  35. package/themes/default/components/ThemeSearchBox.static.jsx +40 -0
  36. package/themes/default/components/ThemeToCContainer.client.jsx +154 -0
  37. package/themes/default/components/ThemeToCContainer.static.jsx +61 -0
  38. package/themes/default/components/pre.client.jsx +84 -0
  39. package/themes/default/components/pre.static.jsx +27 -0
  40. package/themes/default/heading.jsx +35 -0
  41. package/themes/default/index.js +41 -0
  42. package/themes/default/page.jsx +303 -0
  43. package/themes/default/pages/404.mdx +8 -0
  44. package/themes/default/pages/index.mdx +31 -0
  45. package/themes/default/public/favicon.png +0 -0
  46. package/themes/default/public/logo.png +0 -0
  47. package/themes/default/sources/prefetch.js +49 -0
  48. package/themes/default/sources/style.css +1660 -0
@@ -0,0 +1,61 @@
1
+ /* Copyright Yukino Song, SudoMaker Ltd.
2
+ *
3
+ * Licensed to the Apache Software Foundation (ASF) under one
4
+ * or more contributor license agreements. See the NOTICE file
5
+ * distributed with this work for additional information
6
+ * regarding copyright ownership. The ASF licenses this file
7
+ * to you under the Apache License, Version 2.0 (the
8
+ * "License"); you may not use this file except in compliance
9
+ * with the License. You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing,
14
+ * software distributed under the License is distributed on an
15
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ * KIND, either express or implied. See the License for the
17
+ * specific language governing permissions and limitations
18
+ * under the License.
19
+ */
20
+
21
+ const buildTocItems = (items = []) => {
22
+ const nodes = []
23
+ for (const item of items) {
24
+ const childNodes = item?.children?.length ? buildTocItems(item.children) : []
25
+ const children = childNodes.length ? <ul>{childNodes}</ul> : null
26
+ if (item.depth < 2 || item.depth > 4) {
27
+ if (childNodes.length) {
28
+ nodes.push(...childNodes)
29
+ }
30
+ continue
31
+ }
32
+ nodes.push(
33
+ <li class={`toc-depth-${item.depth}`}>
34
+ <a href={`#${item.id}`}>{item.value}</a>
35
+ {children}
36
+ </li>
37
+ )
38
+ }
39
+ return nodes
40
+ }
41
+
42
+ export const renderToc = (toc = []) => {
43
+ const items = buildTocItems(toc)
44
+ if (!items.length) return null
45
+ return items
46
+ }
47
+
48
+ export default function (props, ...children) {
49
+ if (!children.length) {
50
+ return
51
+ }
52
+
53
+ return (
54
+ <aside class="toc-panel">
55
+ <div class="toc">
56
+ <h4>On this page</h4>
57
+ <ul>{...children}</ul>
58
+ </div>
59
+ </aside>
60
+ )
61
+ }
@@ -0,0 +1,84 @@
1
+ /* Copyright Yukino Song, SudoMaker Ltd.
2
+ *
3
+ * Licensed to the Apache Software Foundation (ASF) under one
4
+ * or more contributor license agreements. See the NOTICE file
5
+ * distributed with this work for additional information
6
+ * regarding copyright ownership. The ASF licenses this file
7
+ * to you under the Apache License, Version 2.0 (the
8
+ * "License"); you may not use this file except in compliance
9
+ * with the License. You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing,
14
+ * software distributed under the License is distributed on an
15
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ * KIND, either express or implied. See the License for the
17
+ * specific language governing permissions and limitations
18
+ * under the License.
19
+ */
20
+
21
+ import { signal } from 'refui'
22
+
23
+ export default function (props, ...children) {
24
+ const el = signal()
25
+ const copied = signal(false)
26
+
27
+ const copy = async function () {
28
+ if (el.value) {
29
+ try {
30
+ await navigator.clipboard.writeText(el.value.textContent)
31
+ copied.value = true
32
+ setTimeout(() => {
33
+ copied.value = false
34
+ }, 2000)
35
+ } catch (err) {
36
+ console.error('Failed to copy: ', err)
37
+ }
38
+ }
39
+ }
40
+
41
+ const Btn = copied.choose(
42
+ () => (
43
+ <svg
44
+ attr:width="14"
45
+ attr:height="14"
46
+ attr:viewBox="0 0 24 24"
47
+ attr:fill="none"
48
+ attr:stroke="currentColor"
49
+ attr:stroke-width="2.5"
50
+ attr:stroke-linecap="round"
51
+ attr:stroke-linejoin="round"
52
+ class="text-accent"
53
+ >
54
+ <polyline attr:points="20 6 9 17 4 12"></polyline>
55
+ </svg>
56
+ ),
57
+ () => (
58
+ <svg
59
+ attr:width="14"
60
+ attr:height="14"
61
+ attr:viewBox="0 0 24 24"
62
+ attr:fill="none"
63
+ attr:stroke="currentColor"
64
+ attr:stroke-width="2"
65
+ attr:stroke-linecap="round"
66
+ attr:stroke-linejoin="round"
67
+ >
68
+ <rect attr:x="9" attr:y="9" attr:width="13" attr:height="13" attr:rx="2" attr:ry="2"></rect>
69
+ <path attr:d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
70
+ </svg>
71
+ )
72
+ )
73
+
74
+ return (
75
+ <div class="code-block-container">
76
+ <button class="copy-btn" on:click={copy} attr:aria-label="Copy code">
77
+ <Btn />
78
+ </button>
79
+ <pre {...props} $ref={el}>
80
+ {...children}
81
+ </pre>
82
+ </div>
83
+ )
84
+ }
@@ -0,0 +1,27 @@
1
+ /* Copyright Yukino Song, SudoMaker Ltd.
2
+ *
3
+ * Licensed to the Apache Software Foundation (ASF) under one
4
+ * or more contributor license agreements. See the NOTICE file
5
+ * distributed with this work for additional information
6
+ * regarding copyright ownership. The ASF licenses this file
7
+ * to you under the Apache License, Version 2.0 (the
8
+ * "License"); you may not use this file except in compliance
9
+ * with the License. You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing,
14
+ * software distributed under the License is distributed on an
15
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ * KIND, either express or implied. See the License for the
17
+ * specific language governing permissions and limitations
18
+ * under the License.
19
+ */
20
+
21
+ export default function (props, ...children) {
22
+ return (
23
+ <div class="code-block-container">
24
+ <pre {...props}>{...children}</pre>
25
+ </div>
26
+ )
27
+ }
@@ -0,0 +1,35 @@
1
+ /* Copyright Yukino Song, SudoMaker Ltd.
2
+ *
3
+ * Licensed to the Apache Software Foundation (ASF) under one
4
+ * or more contributor license agreements. See the NOTICE file
5
+ * distributed with this work for additional information
6
+ * regarding copyright ownership. The ASF licenses this file
7
+ * to you under the Apache License, Version 2.0 (the
8
+ * "License"); you may not use this file except in compliance
9
+ * with the License. You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing,
14
+ * software distributed under the License is distributed on an
15
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ * KIND, either express or implied. See the License for the
17
+ * specific language governing permissions and limitations
18
+ * under the License.
19
+ */
20
+
21
+ export function heading(Tag) {
22
+ return (props, ...children) => (
23
+ <Tag {...props}>
24
+ {props.id && <a class="heading-anchor" href={`#${props.id}`} aria-hidden />}
25
+ {...children}
26
+ </Tag>
27
+ )
28
+ }
29
+
30
+ export function createHeadings() {
31
+ return Object.fromEntries([1,2,3,4,5,6].map((i) => {
32
+ const tag = `h${i}`
33
+ return [tag, heading(tag)]
34
+ }))
35
+ }
@@ -0,0 +1,41 @@
1
+ /* Copyright Yukino Song, SudoMaker Ltd.
2
+ *
3
+ * Licensed to the Apache Software Foundation (ASF) under one
4
+ * or more contributor license agreements. See the NOTICE file
5
+ * distributed with this work for additional information
6
+ * regarding copyright ownership. The ASF licenses this file
7
+ * to you under the Apache License, Version 2.0 (the
8
+ * "License"); you may not use this file except in compliance
9
+ * with the License. You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing,
14
+ * software distributed under the License is distributed on an
15
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ * KIND, either express or implied. See the License for the
17
+ * specific language governing permissions and limitations
18
+ * under the License.
19
+ */
20
+
21
+ import { readFileSync } from 'fs'
22
+ import { fileURLToPath } from 'url'
23
+ import { dirname, resolve } from 'path'
24
+ import PAGE_TEMPLATE from './page.jsx'
25
+ import { createHeadings } from './heading.jsx'
26
+
27
+ const __filename = fileURLToPath(import.meta.url)
28
+ const __dirname = dirname(__filename)
29
+
30
+ export default () => {
31
+ return {
32
+ root: __dirname,
33
+ sources: {
34
+ '/.methanol_theme_default': './sources'
35
+ },
36
+ components: {
37
+ ...createHeadings()
38
+ },
39
+ template: PAGE_TEMPLATE
40
+ }
41
+ }
@@ -0,0 +1,303 @@
1
+ /* Copyright Yukino Song, SudoMaker Ltd.
2
+ *
3
+ * Licensed to the Apache Software Foundation (ASF) under one
4
+ * or more contributor license agreements. See the NOTICE file
5
+ * distributed with this work for additional information
6
+ * regarding copyright ownership. The ASF licenses this file
7
+ * to you under the Apache License, Version 2.0 (the
8
+ * "License"); you may not use this file except in compliance
9
+ * with the License. You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing,
14
+ * software distributed under the License is distributed on an
15
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ * KIND, either express or implied. See the License for the
17
+ * specific language governing permissions and limitations
18
+ * under the License.
19
+ */
20
+
21
+ import { HTMLRenderer as R } from 'methanol'
22
+ import { renderToc } from './components/ThemeToCContainer.static.jsx'
23
+
24
+ const renderPageTree = (nodes = [], currentRoute, depth = 0) => {
25
+ const items = []
26
+ let hasActive = false
27
+ for (const node of nodes) {
28
+ const nodeRoute = node.routeHref || node.routePath || ''
29
+ if (node.type === 'directory') {
30
+ const label = node.title || node.name
31
+ const isActive = nodeRoute === currentRoute
32
+ const href = node.routePath ? encodeURI(node.routeHref || node.routePath) : null
33
+ const childResult = renderPageTree(node.children || [], currentRoute, depth + 1)
34
+ const isOpen = depth < 1 || isActive || childResult.hasActive
35
+ if (isOpen) hasActive = true
36
+ const header = href ? (
37
+ <a class={isActive ? 'nav-dir-link active' : 'nav-dir-link'} href={href}>
38
+ {label}
39
+ </a>
40
+ ) : (
41
+ <span class="nav-dir-label">{label}</span>
42
+ )
43
+ items.push(
44
+ <li class={isActive ? 'is-active' : null}>
45
+ <details class="sidebar-collapsible" open={isOpen ? true : null}>
46
+ <summary class="sb-dir-header">{header}</summary>
47
+ {childResult.items.length ? <ul data-depth={depth + 1}>{childResult.items}</ul> : null}
48
+ </details>
49
+ </li>
50
+ )
51
+ continue
52
+ }
53
+ const label = node.title || (node.isIndex ? 'Home' : node.name)
54
+ const isActive = nodeRoute === currentRoute
55
+ if (isActive) hasActive = true
56
+ const href = encodeURI(node.routeHref || node.routePath)
57
+ items.push(
58
+ <li>
59
+ <a class={isActive ? 'active' : null} href={href}>
60
+ {label}
61
+ </a>
62
+ </li>
63
+ )
64
+ }
65
+ return { items, hasActive }
66
+ }
67
+
68
+ const PAGE_TEMPLATE = ({ Page, ExtraHead, components, ctx }) => {
69
+ const page = ctx.page
70
+ const pagesByRoute = ctx.pagesByRoute
71
+ const pages = ctx.pages || []
72
+ const pagesTree = ctx.pagesTree || []
73
+ const siteName = ctx.site?.name || 'Methanol Site'
74
+ const title = page?.title || siteName
75
+ const currentRoute = page?.routeHref || page?.routePath || ''
76
+ const baseHref = page?.routePath === '/404' ? ctx.site?.base || '/' : null
77
+ const toc = page?.toc?.length ? renderToc(page.toc) : null
78
+ const hasToc = Boolean(toc)
79
+ const layoutClass = hasToc ? 'layout-container' : 'layout-container no-toc'
80
+ const tree = renderPageTree(pagesTree, currentRoute, 0)
81
+ const { ThemeSearchBox, ThemeColorSwitch, ThemeAccentSwitch, ThemeToCContainer } = components
82
+ const rootPage = pagesByRoute?.get?.('/') || pages.find((entry) => entry.routePath === '/')
83
+ const pageFrontmatter = page?.frontmatter || {}
84
+ const rootFrontmatter = rootPage?.frontmatter || {}
85
+ const themeLogo = '/logo.png'
86
+ const themeFavIcon = '/favicon.png'
87
+ const logo = pageFrontmatter.logo ?? rootFrontmatter.logo ?? ctx.site?.logo ?? themeLogo
88
+ const favicon = pageFrontmatter.favicon ?? rootFrontmatter.favicon ?? ctx.site?.favicon ?? themeFavIcon
89
+ const excerpt = pageFrontmatter.excerpt ?? null
90
+ const ogTitle = pageFrontmatter.ogTitle ?? null
91
+ const ogDescription = pageFrontmatter.ogDescription ?? null
92
+ const ogImage = pageFrontmatter.ogImage ?? null
93
+ const ogUrl = pageFrontmatter.ogUrl ?? null
94
+ const twitterTitle = pageFrontmatter.twitterTitle ?? ogTitle
95
+ const twitterDescription = pageFrontmatter.twitterDescription ?? ogDescription ?? excerpt
96
+ const twitterImage = pageFrontmatter.twitterImage ?? ogImage
97
+ const twitterCard = pageFrontmatter.twitterCard ?? (twitterImage ? 'summary_large_image' : null)
98
+ const siblings = typeof page?.getSiblings === 'function' ? page.getSiblings() : null
99
+ const prevPage = siblings?.prev || null
100
+ const nextPage = siblings?.next || null
101
+ const languages = Array.isArray(ctx.languages) ? ctx.languages : []
102
+ const currentLanguageHref = ctx.language?.href || ctx.language?.routePath || null
103
+ const languageCode =
104
+ pageFrontmatter.langCode ?? rootFrontmatter.langCode ?? ctx.language?.code ?? 'en'
105
+ const htmlLang = typeof languageCode === 'string' && languageCode.trim() ? languageCode : 'en'
106
+ const pagefindEnabled = ctx.site?.pagefind?.enabled !== false
107
+ const pagefindOptions = ctx.site?.pagefind?.options || null
108
+ const languageSelector = languages.length ? (
109
+ <div class="lang-switch-wrapper">
110
+ <select
111
+ class="lang-switch-select"
112
+ aria-label="Select language"
113
+ onchange="location.href=this.value"
114
+ value={currentLanguageHref || undefined}
115
+ >
116
+ {languages.map((lang) => {
117
+ const optionValue = lang.href || lang.routePath
118
+ const isSelected = optionValue && optionValue === currentLanguageHref
119
+ return (
120
+ <option value={optionValue} selected={isSelected ? true : null}>
121
+ {lang.label}
122
+ </option>
123
+ )
124
+ })}
125
+ </select>
126
+ <div class="lang-switch-icon">
127
+ <svg
128
+ width="18"
129
+ height="18"
130
+ viewBox="0 0 24 24"
131
+ fill="none"
132
+ stroke="currentColor"
133
+ stroke-width="2"
134
+ stroke-linecap="round"
135
+ stroke-linejoin="round"
136
+ >
137
+ <circle cx="12" cy="12" r="10"></circle>
138
+ <line x1="2" y1="12" x2="22" y2="12"></line>
139
+ <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path>
140
+ </svg>
141
+ </div>
142
+ </div>
143
+ ) : null
144
+ return (
145
+ <>
146
+ {R.rawHTML`<!DOCTYPE html>`}
147
+ <html lang={htmlLang}>
148
+ <head>
149
+ <meta charset="UTF-8" />
150
+ <meta name="viewport" content="width=device-width" />
151
+ <title>
152
+ {title} | {siteName}
153
+ </title>
154
+ {baseHref ? <base href={baseHref} /> : null}
155
+ <link rel="icon" href={favicon} />
156
+ {excerpt ? <meta name="description" content={excerpt} /> : null}
157
+ {ogTitle ? <meta property="og:title" content={ogTitle} /> : null}
158
+ {ogDescription ? <meta property="og:description" content={ogDescription} /> : null}
159
+ {ogImage ? <meta property="og:image" content={ogImage} /> : null}
160
+ {ogUrl ? <meta property="og:url" content={ogUrl} /> : null}
161
+ {twitterCard ? <meta name="twitter:card" content={twitterCard} /> : null}
162
+ {twitterTitle ? <meta name="twitter:title" content={twitterTitle} /> : null}
163
+ {twitterDescription ? <meta name="twitter:description" content={twitterDescription} /> : null}
164
+ {twitterImage ? <meta name="twitter:image" content={twitterImage} /> : null}
165
+ <ExtraHead />
166
+ <link rel="preload stylesheet" as="style" href="/.methanol_theme_default/style.css" />
167
+ {R.rawHTML`
168
+ <script>
169
+ (function() {
170
+ const savedTheme = localStorage.getItem('methanol-theme');
171
+ const systemTheme = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
172
+ const theme = savedTheme || systemTheme;
173
+ document.documentElement.classList.toggle('light', theme === 'light');
174
+ document.documentElement.classList.toggle('dark', theme === 'dark');
175
+
176
+ const savedAccent = localStorage.getItem('methanol-accent');
177
+ if (savedAccent && savedAccent !== 'default') {
178
+ document.documentElement.classList.add('accent-' + savedAccent);
179
+ }
180
+ })();
181
+ </script>
182
+ `}
183
+ <script type="module" src="/.methanol_theme_default/prefetch.js" defer></script>
184
+ </head>
185
+ <body>
186
+ <input type="checkbox" id="nav-toggle" class="nav-toggle" />
187
+ <label class="nav-toggle-label" for="nav-toggle" aria-label="Toggle navigation">
188
+ <svg
189
+ width="24"
190
+ height="24"
191
+ viewBox="0 0 24 24"
192
+ fill="none"
193
+ stroke="currentColor"
194
+ stroke-width="2"
195
+ stroke-linecap="round"
196
+ stroke-linejoin="round"
197
+ >
198
+ <line x1="3" y1="12" x2="21" y2="12"></line>
199
+ <line x1="3" y1="6" x2="21" y2="6"></line>
200
+ <line x1="3" y1="18" x2="21" y2="18"></line>
201
+ </svg>
202
+ </label>
203
+ {pagefindEnabled ? (
204
+ <button
205
+ class="search-toggle-label"
206
+ aria-label="Open search"
207
+ onclick="window.__methanolSearchOpen()"
208
+ >
209
+ <svg
210
+ width="24"
211
+ height="24"
212
+ viewBox="0 0 24 24"
213
+ fill="none"
214
+ stroke="currentColor"
215
+ stroke-width="2"
216
+ stroke-linecap="round"
217
+ stroke-linejoin="round"
218
+ >
219
+ <circle cx="11" cy="11" r="8"></circle>
220
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
221
+ </svg>
222
+ </button>
223
+ ) : null}
224
+ {hasToc ? (
225
+ <>
226
+ <input type="checkbox" id="toc-toggle" class="toc-toggle" />
227
+ <label class="toc-toggle-label" for="toc-toggle" aria-label="Toggle table of contents">
228
+ <svg
229
+ width="20"
230
+ height="20"
231
+ viewBox="0 0 24 24"
232
+ fill="none"
233
+ stroke="currentColor"
234
+ stroke-width="2"
235
+ stroke-linecap="round"
236
+ stroke-linejoin="round"
237
+ >
238
+ <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
239
+ <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
240
+ </svg>
241
+ </label>
242
+ </>
243
+ ) : null}
244
+ <label class="nav-scrim nav-scrim-nav" for="nav-toggle" aria-label="Close navigation"></label>
245
+ {hasToc ? (
246
+ <label class="nav-scrim nav-scrim-toc" for="toc-toggle" aria-label="Close table of contents"></label>
247
+ ) : null}
248
+ <div class={layoutClass}>
249
+ <aside class="sidebar">
250
+ <div class="sidebar-header">
251
+ <div class="logo">
252
+ <img src={logo} />
253
+ <span>{siteName}</span>
254
+ </div>
255
+ {pagefindEnabled ? <ThemeSearchBox options={pagefindOptions} /> : null}
256
+ </div>
257
+ <nav>
258
+ <ul data-depth="0">{tree.items}</ul>
259
+ </nav>
260
+ <div class="sidebar-footer">
261
+ {languageSelector}
262
+ <ThemeColorSwitch />
263
+ <ThemeAccentSwitch />
264
+ </div>
265
+ </aside>
266
+ <main class="main-content" data-pagefind-body={pagefindEnabled ? '' : null}>
267
+ <Page />
268
+ {prevPage || nextPage ? (
269
+ <nav class="page-nav">
270
+ {prevPage ? (
271
+ <a class="page-nav-card prev" href={prevPage.routeHref || prevPage.routePath}>
272
+ <span class="page-nav-label">Previous</span>
273
+ <span class="page-nav-title">{prevPage.title || prevPage.routePath}</span>
274
+ </a>
275
+ ) : <div class="page-nav-spacer"></div>}
276
+ {nextPage ? (
277
+ <a class="page-nav-card next" href={nextPage.routeHref || nextPage.routePath}>
278
+ <span class="page-nav-label">Next</span>
279
+ <span class="page-nav-title">{nextPage.title || nextPage.routePath}</span>
280
+ </a>
281
+ ) : null}
282
+ </nav>
283
+ ) : null}
284
+ {page ? (
285
+ <footer class="page-meta">
286
+ <div class="page-meta-item">
287
+ Updated: {page.updatedAt || '-'}
288
+ </div>
289
+ <div class="page-meta-item">
290
+ Powered by <a href="https://github.com/SudoMaker/Methanol" target="_blank" rel="noopener noreferrer" class="methanol-link">Methanol</a>
291
+ </div>
292
+ </footer>
293
+ ) : null}
294
+ </main>
295
+ {hasToc ? <ThemeToCContainer>{...toc}</ThemeToCContainer> : null}
296
+ </div>
297
+ </body>
298
+ </html>
299
+ </>
300
+ )
301
+ }
302
+
303
+ export default PAGE_TEMPLATE
@@ -0,0 +1,8 @@
1
+ # Page Not Found
2
+
3
+ The page you're looking for doesn't exist.
4
+
5
+ ## What you can do:
6
+
7
+ - Check the URL for typos
8
+ - Go back to the [home page](/)
@@ -0,0 +1,31 @@
1
+ ---
2
+ title: Welcome
3
+ ---
4
+
5
+ # Welcome to Methanol
6
+
7
+ Methanol turns your content folder into a static site with rEFui and MDX, file-based routing, and a fast dev server.
8
+
9
+ ## Quick Start
10
+
11
+ ```bash
12
+ npx methanol dev
13
+ ```
14
+
15
+ Open `http://localhost:5173`.
16
+
17
+ ## Your Content Folder
18
+
19
+ Pages are currently read from the configured `pagesDir`:
20
+
21
+ <p><code>{ctx.site?.pagesDir || 'pages/'}</code></p>
22
+
23
+ You can change this with `pagesDir` in your config or `--input` on the CLI. Create an `index.mdx` in that folder to replace this page.
24
+
25
+ ## Common Folders
26
+
27
+ ```
28
+ components/ # JSX/TSX components for MDX
29
+ public/ # static assets copied as-is
30
+ dist/ # production output
31
+ ```
Binary file
@@ -0,0 +1,49 @@
1
+ /* Copyright Yukino Song, SudoMaker Ltd.
2
+ *
3
+ * Licensed to the Apache Software Foundation (ASF) under one
4
+ * or more contributor license agreements. See the NOTICE file
5
+ * distributed with this work for additional information
6
+ * regarding copyright ownership. The ASF licenses this file
7
+ * to you under the Apache License, Version 2.0 (the
8
+ * "License"); you may not use this file except in compliance
9
+ * with the License. You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing,
14
+ * software distributed under the License is distributed on an
15
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ * KIND, either express or implied. See the License for the
17
+ * specific language governing permissions and limitations
18
+ * under the License.
19
+ */
20
+
21
+ (() => {
22
+ const prefetched = new Set()
23
+ const canPrefetch = (anchor) => {
24
+ if (!anchor || !anchor.href) return false
25
+ if (anchor.dataset && anchor.dataset.prefetch === 'false') return false
26
+ if (anchor.hasAttribute('download')) return false
27
+ if (anchor.target && anchor.target !== '_self') return false
28
+ const url = new URL(anchor.href, window.location.href)
29
+ if (url.origin !== window.location.origin) return false
30
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') return false
31
+ if (url.pathname === window.location.pathname && url.search === window.location.search) {
32
+ return false
33
+ }
34
+ return true
35
+ }
36
+ const onHover = (event) => {
37
+ const anchor = event.target && event.target.closest ? event.target.closest('a') : null
38
+ if (!canPrefetch(anchor)) return
39
+ const href = anchor.href
40
+ if (prefetched.has(href)) return
41
+ prefetched.add(href)
42
+ const link = document.createElement('link')
43
+ link.rel = 'prefetch'
44
+ link.as = 'document'
45
+ link.href = href
46
+ document.head.appendChild(link)
47
+ }
48
+ document.addEventListener('pointerover', onHover, { capture: true, passive: true })
49
+ })()