methanol 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/state.js CHANGED
@@ -103,10 +103,9 @@ const withCommonOptions = (y) =>
103
103
  })
104
104
  .option('code-highlighting', {
105
105
  describe: 'Enable or disable code highlighting',
106
- type: 'string',
106
+ type: 'boolean',
107
107
  coerce: (value) => {
108
108
  if (value == null) return null
109
- if (value === true || value === '') return true
110
109
  if (typeof value === 'boolean') return value
111
110
  const normalized = String(value).trim().toLowerCase()
112
111
  if (normalized === 'true') return true
@@ -114,6 +113,18 @@ const withCommonOptions = (y) =>
114
113
  return null
115
114
  }
116
115
  })
116
+ .option('verbose', {
117
+ alias: 'v',
118
+ describe: 'Enable verbose output',
119
+ type: 'boolean',
120
+ default: false
121
+ })
122
+ .option('base', {
123
+ describe: 'Base URL override',
124
+ type: 'string',
125
+ requiresArg: true,
126
+ nargs: 1
127
+ })
117
128
 
118
129
  const parser = yargs(hideBin(process.argv))
119
130
  .scriptName('methanol')
@@ -141,13 +152,17 @@ export const cli = {
141
152
  CLI_OUTPUT_DIR: argv.output || null,
142
153
  CLI_CONFIG_PATH: argv.config || null,
143
154
  CLI_SITE_NAME: argv['site-name'] || null,
144
- CLI_CODE_HIGHLIGHTING: typeof argv['code-highlighting'] === 'boolean' ? argv['code-highlighting'] : null
155
+ CLI_CODE_HIGHLIGHTING: typeof argv['code-highlighting'] === 'boolean' ? argv['code-highlighting'] : null,
156
+ CLI_VERBOSE: Boolean(argv.verbose),
157
+ CLI_BASE: argv.base || null
145
158
  }
146
159
 
147
160
  export const state = {
148
161
  PROJECT_ROOT,
149
162
  ROOT_DIR: PROJECT_ROOT,
150
163
  SITE_NAME: 'Methanol Site',
164
+ SITE_BASE: null,
165
+ VITE_BASE: null,
151
166
  PAGES_DIR: resolve(PROJECT_ROOT, 'pages'),
152
167
  COMPONENTS_DIR: resolve(PROJECT_ROOT, 'components'),
153
168
  STATIC_DIR: resolve(PROJECT_ROOT, 'public'),
@@ -178,6 +193,7 @@ export const state = {
178
193
  THEME_POST_BUNDLE_HOOKS: [],
179
194
  STARRY_NIGHT_ENABLED: false,
180
195
  STARRY_NIGHT_OPTIONS: null,
196
+ GFM_ENABLED: true,
181
197
  CURRENT_MODE: 'production',
182
198
  RESOLVED_MDX_CONFIG: undefined,
183
199
  RESOLVED_VITE_CONFIG: undefined
package/src/utils.js ADDED
@@ -0,0 +1,9 @@
1
+ export const cached = (fn) => {
2
+ let cache = null
3
+ return () => (cache ?? (cache = fn()))
4
+ }
5
+
6
+ export const cachedStr = (fn) => {
7
+ const cache = Object.create(null)
8
+ return (key) => (cache[key] ?? (cache[key] = fn(key)))
9
+ }
@@ -21,10 +21,9 @@
21
21
  import { createDOMRenderer } from 'refui/dom'
22
22
  import { defaults } from 'refui/browser'
23
23
  import { lazy } from 'refui'
24
- import { init } from '/.methanol_virtual_module/loader.js'
25
-
26
- const reg = import('/.methanol_virtual_module/registry.js')
24
+ import { init } from 'methanol:loader'
25
+ import { registry } from 'methanol:registry'
27
26
 
28
27
  const R = createDOMRenderer(defaults)
29
28
 
30
- reg.then((m) => init(m.registry, R))
29
+ init(registry, R)
@@ -22,6 +22,27 @@ let pagefindInit = null
22
22
  let pagefindUiInit = null
23
23
  let pagefindUiReady = false
24
24
 
25
+ const resolveBasePrefix = () => {
26
+ let base = import.meta.env?.BASE_URL || '/'
27
+ if (!base || base === '/' || base === './') return ''
28
+ if (base.startsWith('http://') || base.startsWith('https://')) {
29
+ try {
30
+ base = new URL(base).pathname
31
+ } catch {
32
+ return ''
33
+ }
34
+ }
35
+ if (!base.startsWith('/')) return ''
36
+ if (base.endsWith('/')) base = base.slice(0, -1)
37
+ return base
38
+ }
39
+
40
+ const withBase = (path) => {
41
+ const prefix = resolveBasePrefix()
42
+ if (!prefix || path.startsWith(`${prefix}/`)) return path
43
+ return `${prefix}${path}`
44
+ }
45
+
25
46
  const dynamicImport = (path) => {
26
47
  try {
27
48
  const importer = new Function('p', 'return import(p)')
@@ -38,7 +59,7 @@ export const loadPagefind = async () => {
38
59
  resolve(null)
39
60
  return
40
61
  }
41
- dynamicImport('/pagefind/pagefind.js')
62
+ dynamicImport(withBase('/pagefind/pagefind.js'))
42
63
  .then((mod) => {
43
64
  if (!mod) return resolve(null)
44
65
  if (mod.search) return resolve(mod)
@@ -89,7 +110,7 @@ export const loadPagefindUI = async (options = {}) => {
89
110
  return
90
111
  }
91
112
  const script = document.createElement('script')
92
- script.src = '/pagefind/pagefind-ui.js'
113
+ script.src = withBase('/pagefind/pagefind-ui.js')
93
114
  script.async = true
94
115
  script.onload = () => done(initPagefindUI(options))
95
116
  script.onerror = () => done(false)
@@ -24,8 +24,9 @@ import { existsSync } from 'node:fs'
24
24
  import { resolve } from 'node:path'
25
25
  import { normalizePath } from 'vite'
26
26
  import { state } from './state.js'
27
+ import { resolveBasePrefix } from './config.js'
27
28
  import { genRegistryScript } from './components.js'
28
- import { INJECT_SCRIPT, LOADER_SCRIPT, PAGEFIND_SCRIPT } from './assets.js'
29
+ import { INJECT_SCRIPT, LOADER_SCRIPT, PAGEFIND_LOADER_SCRIPT } from './assets.js'
29
30
  import { projectRequire } from './node-loader.js'
30
31
 
31
32
  const require = createRequire(import.meta.url)
@@ -60,6 +61,9 @@ export const methanolPreviewRoutingPlugin = (distDir, notFoundPath) => ({
60
61
  cachedHtml = await readFile(notFoundPath, 'utf-8')
61
62
  return cachedHtml
62
63
  }
64
+
65
+ const basePrefix = resolveBasePrefix()
66
+
63
67
  const handler = async (req, res, next) => {
64
68
  if (!req.url || req.method !== 'GET') {
65
69
  return next()
@@ -69,6 +73,13 @@ export const methanolPreviewRoutingPlugin = (distDir, notFoundPath) => ({
69
73
  try {
70
74
  pathname = new URL(req.url, 'http://methanol').pathname
71
75
  pathname = decodeURIComponent(pathname)
76
+ if (basePrefix) {
77
+ if (pathname.startsWith(basePrefix)) {
78
+ pathname = pathname.slice(basePrefix.length)
79
+ } else {
80
+ return next()
81
+ }
82
+ }
72
83
  } catch {}
73
84
  const hasTrailingSlash = pathname.endsWith('/') && pathname !== '/'
74
85
  if (pathname.includes('.') && !pathname.endsWith('.html')) {
@@ -109,20 +120,43 @@ export const methanolPreviewRoutingPlugin = (distDir, notFoundPath) => ({
109
120
 
110
121
  const virtualModulePrefix = '/.methanol_virtual_module/'
111
122
  const resolvedVirtualModulePrefix = '\0' + virtualModulePrefix
123
+ const virtualModuleScheme = 'methanol:'
112
124
 
113
125
  const virtualModuleMap = {
126
+ get registry() {
127
+ return `export const registry = ${genRegistryScript()}`
128
+ },
114
129
  get 'registry.js'() {
115
130
  return `export const registry = ${genRegistryScript()}`
116
131
  },
117
- 'loader.js': LOADER_SCRIPT,
132
+ loader: LOADER_SCRIPT,
118
133
  'inject.js': INJECT_SCRIPT,
119
- 'pagefind.js': PAGEFIND_SCRIPT
134
+ 'pagefind-loader': PAGEFIND_LOADER_SCRIPT
120
135
  }
121
136
 
122
137
  const getModuleIdSegment = (id, start) => {
123
138
  return new URL(id.slice(start), 'http://methanol').pathname.slice(1)
124
139
  }
125
140
 
141
+ const getSchemeModuleKey = (id) => {
142
+ if (!id.startsWith(virtualModuleScheme)) return null
143
+ return id.slice(virtualModuleScheme.length)
144
+ }
145
+
146
+ const resolveVirtualModuleId = (id) => {
147
+ if (id.startsWith(virtualModulePrefix)) {
148
+ return id
149
+ }
150
+ const basePrefix = resolveBasePrefix()
151
+ if (basePrefix) {
152
+ const prefixed = `${basePrefix}${virtualModulePrefix}`
153
+ if (id.startsWith(prefixed)) {
154
+ return id.slice(basePrefix.length)
155
+ }
156
+ }
157
+ return null
158
+ }
159
+
126
160
  export const methanolResolverPlugin = () => {
127
161
  return {
128
162
  name: 'methanol-resolver',
@@ -139,10 +173,16 @@ export const methanolResolverPlugin = () => {
139
173
  return require.resolve(id)
140
174
  }
141
175
 
142
- if (id.startsWith(virtualModulePrefix)) {
143
- const _moduleId = getModuleIdSegment(id, virtualModulePrefix.length)
176
+ const schemeKey = getSchemeModuleKey(id)
177
+ if (schemeKey && Object.prototype.hasOwnProperty.call(virtualModuleMap, schemeKey)) {
178
+ return '\0' + id
179
+ }
180
+
181
+ const virtualId = resolveVirtualModuleId(id)
182
+ if (virtualId) {
183
+ const _moduleId = getModuleIdSegment(virtualId, virtualModulePrefix.length)
144
184
  if (Object.prototype.hasOwnProperty.call(virtualModuleMap, _moduleId)) {
145
- return '\0' + id
185
+ return '\0' + virtualId
146
186
  }
147
187
  }
148
188
 
@@ -164,6 +204,10 @@ export const methanolResolverPlugin = () => {
164
204
  }
165
205
  },
166
206
  load(id) {
207
+ if (id.startsWith('\0' + virtualModuleScheme)) {
208
+ const key = id.slice(1 + virtualModuleScheme.length)
209
+ return virtualModuleMap[key]
210
+ }
167
211
  if (id.startsWith(resolvedVirtualModulePrefix)) {
168
212
  const _moduleId = getModuleIdSegment(id, resolvedVirtualModulePrefix.length)
169
213
  return virtualModuleMap[_moduleId]
@@ -20,7 +20,13 @@
20
20
 
21
21
  import { signal, $, t, If, For, onCondition } from 'refui'
22
22
  import { createPortal } from 'refui/extras'
23
- import { loadPagefind } from '/.methanol_virtual_module/pagefind.js'
23
+
24
+ let pagefindModule = null
25
+ const loadPagefindModule = async () => {
26
+ if (pagefindModule) return pagefindModule
27
+ pagefindModule = import('methanol:pagefind-loader')
28
+ return pagefindModule
29
+ }
24
30
 
25
31
  let keybindReady = false
26
32
  let cachedPagefind = null
@@ -35,7 +41,8 @@ const resolveShortcutLabel = () => {
35
41
 
36
42
  const ensurePagefind = async (options) => {
37
43
  if (cachedPagefind) return cachedPagefind
38
- const pagefind = await loadPagefind()
44
+ const module = await loadPagefindModule()
45
+ const pagefind = await module?.loadPagefind?.()
39
46
  if (!pagefind) return null
40
47
  if (pagefind.options) {
41
48
  const nextOptions = { excerptLength: 30, ...(options || {}) }
@@ -27,11 +27,11 @@ const renderPageTree = (nodes = [], currentRoute, depth = 0) => {
27
27
  const items = []
28
28
  let hasActive = false
29
29
  for (const node of nodes) {
30
- const nodeRoute = node.routeHref || node.routePath || ''
30
+ const nodeRoute = node.routeHref || ''
31
31
  if (node.type === 'directory') {
32
32
  const label = node.title || node.name
33
33
  const isActive = nodeRoute === currentRoute
34
- const href = node.routePath ? encodeURI(node.routeHref || node.routePath) : null
34
+ const href = node.routeHref
35
35
  const childResult = renderPageTree(node.children || [], currentRoute, depth + 1)
36
36
  const isOpen = depth < 1 || isActive || childResult.hasActive
37
37
  if (isOpen) hasActive = true
@@ -55,7 +55,7 @@ const renderPageTree = (nodes = [], currentRoute, depth = 0) => {
55
55
  const label = node.title || (node.isIndex ? 'Home' : node.name)
56
56
  const isActive = nodeRoute === currentRoute
57
57
  if (isActive) hasActive = true
58
- const href = encodeURI(node.routeHref || node.routePath)
58
+ const href = node.routeHref
59
59
  items.push(
60
60
  <li>
61
61
  <a class={isActive ? 'active' : null} href={href}>
@@ -67,27 +67,27 @@ const renderPageTree = (nodes = [], currentRoute, depth = 0) => {
67
67
  return { items, hasActive }
68
68
  }
69
69
 
70
- const PAGE_TEMPLATE = ({ Page, ExtraHead, components, ctx }) => {
70
+ const PAGE_TEMPLATE = ({ PageContent, ExtraHead, components, ctx }) => {
71
71
  const page = ctx.page
72
72
  const pagesByRoute = ctx.pagesByRoute
73
73
  const pages = ctx.pages || []
74
74
  const pagesTree = ctx.pagesTree || []
75
- const siteName = ctx.site?.name || 'Methanol Site'
76
- const title = page?.title || siteName
77
- const currentRoute = page?.routeHref || page?.routePath || ''
78
- const baseHref = page?.routePath === '/404' ? ctx.site?.base || '/' : null
79
- const toc = page?.toc?.length ? renderToc(page.toc) : null
75
+ const siteName = ctx.site.name || 'Methanol Site'
76
+ const title = page.title || siteName
77
+ const currentRoute = page.routeHref || ''
78
+ const baseHref = page.routeHref === '/404' ? ctx.site.base || '/' : null
79
+ const toc = page.toc?.length ? renderToc(page.toc) : null
80
80
  const hasToc = Boolean(toc)
81
81
  const layoutClass = hasToc ? 'layout-container' : 'layout-container no-toc'
82
82
  const tree = renderPageTree(pagesTree, currentRoute, 0)
83
83
  const { ThemeSearchBox, ThemeColorSwitch, ThemeAccentSwitch, ThemeToCContainer } = components
84
- const rootPage = pagesByRoute?.get?.('/') || pages.find((entry) => entry.routePath === '/')
85
- const pageFrontmatter = page?.frontmatter || {}
86
- const rootFrontmatter = rootPage?.frontmatter || {}
84
+ const rootPage = pagesByRoute.get('/') || pages.find((entry) => entry.routeHref === '/')
85
+ const pageFrontmatter = page.frontmatter || {}
86
+ const rootFrontmatter = rootPage.frontmatter || {}
87
87
  const themeLogo = '/logo.png'
88
88
  const themeFavIcon = '/favicon.png'
89
- const logo = pageFrontmatter.logo ?? rootFrontmatter.logo ?? ctx.site?.logo ?? themeLogo
90
- const favicon = pageFrontmatter.favicon ?? rootFrontmatter.favicon ?? ctx.site?.favicon ?? themeFavIcon
89
+ const logo = pageFrontmatter.logo ?? rootFrontmatter.logo ?? ctx.site.logo ?? themeLogo
90
+ const favicon = pageFrontmatter.favicon ?? rootFrontmatter.favicon ?? ctx.site.favicon ?? themeFavIcon
91
91
  const excerpt = pageFrontmatter.excerpt ?? `${title} | ${siteName} - Powered by Methanol`
92
92
  const _ogTitle = pageFrontmatter.ogTitle ?? title ?? null
93
93
  const ogTitle = _ogTitle ? `${_ogTitle} | ${siteName}` : null
@@ -98,15 +98,18 @@ const PAGE_TEMPLATE = ({ Page, ExtraHead, components, ctx }) => {
98
98
  const twitterDescription = pageFrontmatter.twitterDescription ?? ogDescription ?? excerpt
99
99
  const twitterImage = pageFrontmatter.twitterImage ?? ogImage
100
100
  const twitterCard = pageFrontmatter.twitterCard ?? (twitterImage ? 'summary_large_image' : null)
101
- const siblings = typeof page?.getSiblings === 'function' ? page.getSiblings() : null
101
+ const siblings = page.getSiblings()
102
102
  const prevPage = siblings?.prev || null
103
103
  const nextPage = siblings?.next || null
104
104
  const languages = Array.isArray(ctx.languages) ? ctx.languages : []
105
- const currentLanguageHref = ctx.language?.href || ctx.language?.routePath || null
105
+ const currentLanguageHref = ctx.language?.href || ctx.language?.routeHref || null
106
106
  const languageCode = pageFrontmatter.langCode ?? rootFrontmatter.langCode ?? ctx.language?.code ?? 'en'
107
107
  const htmlLang = typeof languageCode === 'string' && languageCode.trim() ? languageCode : 'en'
108
- const pagefindEnabled = ctx.site?.pagefind?.enabled !== false
109
- const pagefindOptions = ctx.site?.pagefind?.options || null
108
+ const pagefindEnabled = ctx.site.pagefind?.enabled !== false
109
+ const pagefindOptions = ctx.site.pagefind?.options || null
110
+ const repoBase = ctx.site.repoBase
111
+ const sourceUrl = pageFrontmatter.sourceURL
112
+ const editUrl = sourceUrl || (repoBase && page.relativePath ? new URL(page.relativePath, repoBase).href : null)
110
113
  const languageSelector = languages.length ? (
111
114
  <div class="lang-switch-wrapper">
112
115
  <select
@@ -116,7 +119,7 @@ const PAGE_TEMPLATE = ({ Page, ExtraHead, components, ctx }) => {
116
119
  value={currentLanguageHref || undefined}
117
120
  >
118
121
  {languages.map((lang) => {
119
- const optionValue = lang.href || lang.routePath
122
+ const optionValue = lang.href || lang.routeHref
120
123
  const isSelected = optionValue && optionValue === currentLanguageHref
121
124
  return (
122
125
  <option value={optionValue} selected={isSelected ? true : null}>
@@ -165,7 +168,11 @@ const PAGE_TEMPLATE = ({ Page, ExtraHead, components, ctx }) => {
165
168
  {twitterDescription ? <meta name="twitter:description" content={twitterDescription} /> : null}
166
169
  {twitterImage ? <meta name="twitter:image" content={twitterImage} /> : null}
167
170
  <ExtraHead />
168
- <link rel="preload stylesheet" as="style" href="/.methanol_theme_default/style.css" />
171
+ <link
172
+ rel="preload stylesheet"
173
+ as="style"
174
+ href="/.methanol_theme_default/style.css"
175
+ />
169
176
  <script src="/theme-prepare.js"></script>
170
177
  </head>
171
178
  <body>
@@ -246,28 +253,49 @@ const PAGE_TEMPLATE = ({ Page, ExtraHead, components, ctx }) => {
246
253
  </div>
247
254
  </aside>
248
255
  <main class="main-content" data-pagefind-body={pagefindEnabled ? '' : null}>
249
- <Page />
256
+ <PageContent />
250
257
  {prevPage || nextPage ? (
251
258
  <nav class="page-nav">
252
259
  {prevPage ? (
253
- <a class="page-nav-card prev" href={prevPage.routeHref || prevPage.routePath}>
260
+ <a
261
+ class="page-nav-card prev"
262
+ href={prevPage.routeHref}
263
+ >
254
264
  <span class="page-nav-label">Previous</span>
255
- <span class="page-nav-title">{prevPage.title || prevPage.routePath}</span>
265
+ <span class="page-nav-title">{prevPage.title || prevPage.routeHref}</span>
256
266
  </a>
257
267
  ) : (
258
268
  <div class="page-nav-spacer"></div>
259
269
  )}
260
270
  {nextPage ? (
261
- <a class="page-nav-card next" href={nextPage.routeHref || nextPage.routePath}>
271
+ <a
272
+ class="page-nav-card next"
273
+ href={nextPage.routeHref}
274
+ >
262
275
  <span class="page-nav-label">Next</span>
263
- <span class="page-nav-title">{nextPage.title || nextPage.routePath}</span>
276
+ <span class="page-nav-title">{nextPage.title || nextPage.routeHref}</span>
264
277
  </a>
265
278
  ) : null}
266
279
  </nav>
267
280
  ) : null}
268
281
  {page ? (
269
282
  <footer class="page-meta">
270
- <div class="page-meta-item">Updated: {page.updatedAt || '-'}</div>
283
+ <div class="page-meta-item">
284
+ {editUrl ? (
285
+ <>
286
+ <a
287
+ href={editUrl}
288
+ target="_blank"
289
+ rel="noopener noreferrer"
290
+ class="page-meta-link"
291
+ >
292
+ Edit this page
293
+ </a>
294
+ <span style="margin: 0 0.5rem; opacity: 0.5;">•</span>
295
+ </>
296
+ ) : null}
297
+ Updated: {page.updatedAt || '-'}
298
+ </div>
271
299
  <div class="page-meta-item">
272
300
  Powered by{' '}
273
301
  <a
@@ -1374,11 +1374,11 @@ a {
1374
1374
  align-items: center;
1375
1375
  }
1376
1376
 
1377
+ .page-meta-link,
1377
1378
  .methanol-link {
1378
1379
  font-weight: 600;
1379
1380
  color: var(--text);
1380
1381
  text-decoration: none !important;
1381
- margin-left: 0.25rem;
1382
1382
  transition: all 0.2s ease;
1383
1383
  border-bottom: 1px solid transparent;
1384
1384
 
@@ -1388,6 +1388,10 @@ a {
1388
1388
  }
1389
1389
  }
1390
1390
 
1391
+ .methanol-link {
1392
+ margin-left: 0.25rem;
1393
+ }
1394
+
1391
1395
  @media (max-width: 640px) {
1392
1396
  .page-meta {
1393
1397
  flex-direction: column;
@@ -1731,6 +1735,47 @@ a {
1731
1735
  justify-content: flex-end;
1732
1736
  }
1733
1737
 
1738
+ /* --- Tables (GFM) --- */
1739
+
1740
+ .main-content table {
1741
+ width: 100%;
1742
+ border-collapse: collapse;
1743
+ margin: 1.5rem 0;
1744
+ font-size: 0.95rem;
1745
+ overflow-x: auto;
1746
+ display: block; /* Enables horizontal scroll for large tables */
1747
+ }
1748
+
1749
+ .main-content table thead tr {
1750
+ border-bottom: 2px solid var(--border);
1751
+ }
1752
+
1753
+ .main-content table th {
1754
+ text-align: left;
1755
+ padding: 0.75rem 1rem;
1756
+ font-weight: 600;
1757
+ color: var(--text);
1758
+ white-space: nowrap;
1759
+ }
1760
+
1761
+ .main-content table tbody tr {
1762
+ border-bottom: 1px solid var(--border);
1763
+ transition: background-color 0.1s ease;
1764
+ }
1765
+
1766
+ .main-content table tbody tr:last-child {
1767
+ border-bottom: none;
1768
+ }
1769
+
1770
+ .main-content table td {
1771
+ padding: 0.75rem 1rem;
1772
+ color: var(--muted);
1773
+ }
1774
+
1775
+ .main-content table tbody tr:hover {
1776
+ background-color: var(--hover-bg);
1777
+ }
1778
+
1734
1779
  /* --- View Transitions --- */
1735
1780
 
1736
1781
  @view-transition {
@@ -1743,3 +1788,151 @@ a {
1743
1788
  view-transition-name: active-sidebar-bg;
1744
1789
  contain: layout paint;
1745
1790
  }
1791
+
1792
+ /* --- Print Styles --- */
1793
+
1794
+ @media print {
1795
+ :root {
1796
+ --bg: #ffffff;
1797
+ --surface: #ffffff;
1798
+ --surface-muted: #f4f4f5;
1799
+ --surface-elevated: #ffffff;
1800
+ --text: #09090b;
1801
+ --muted: #52525b;
1802
+ --border: #e4e4e7;
1803
+ --accent: #000000;
1804
+ --accent-soft: #f4f4f5;
1805
+ --hover-bg: transparent;
1806
+ }
1807
+
1808
+ body {
1809
+ background-color: white !important;
1810
+ background-image: none !important;
1811
+ color: black !important;
1812
+ width: 100% !important;
1813
+ margin: 0 !important;
1814
+ padding: 0 !important;
1815
+ overflow: visible !important;
1816
+ }
1817
+
1818
+ /* Hide UI Elements */
1819
+ .sidebar,
1820
+ .toc-panel,
1821
+ .nav-toggle-label,
1822
+ .toc-toggle-label,
1823
+ .search-toggle-label,
1824
+ .search-modal,
1825
+ .page-nav,
1826
+ .copy-btn,
1827
+ .heading-anchor,
1828
+ .theme-switch-wrapper,
1829
+ .lang-switch-wrapper,
1830
+ .page-meta {
1831
+ display: none !important;
1832
+ }
1833
+
1834
+ /* Layout Overrides */
1835
+ .layout-container,
1836
+ .layout-container.no-toc {
1837
+ display: block !important;
1838
+ max-width: none !important;
1839
+ width: 100% !important;
1840
+ margin: 0 !important;
1841
+ padding: 0 !important;
1842
+ }
1843
+
1844
+ .main-content {
1845
+ padding: 0 !important;
1846
+ width: 100% !important;
1847
+ max-width: none !important;
1848
+ }
1849
+
1850
+ /* Typography & Content */
1851
+ body {
1852
+ font-size: 11pt !important;
1853
+ line-height: 1.5 !important;
1854
+ }
1855
+
1856
+ .main-content {
1857
+ h1 {
1858
+ font-size: 24pt !important;
1859
+ margin-bottom: 1rem !important;
1860
+ }
1861
+
1862
+ h2 {
1863
+ font-size: 18pt !important;
1864
+ margin-top: 1.5rem !important;
1865
+ margin-bottom: 0.75rem !important;
1866
+ }
1867
+
1868
+ h3 {
1869
+ font-size: 14pt !important;
1870
+ margin-top: 1.25rem !important;
1871
+ margin-bottom: 0.5rem !important;
1872
+ }
1873
+
1874
+ p, li {
1875
+ font-size: 11pt !important;
1876
+ margin-bottom: 0.75rem !important;
1877
+ }
1878
+
1879
+ pre, code {
1880
+ font-size: 10pt !important;
1881
+ }
1882
+ }
1883
+
1884
+ h1, h2, h3, h4, h5, h6 {
1885
+ color: black !important;
1886
+ page-break-after: avoid;
1887
+ break-after: avoid;
1888
+ }
1889
+
1890
+ h2 {
1891
+ border-bottom: 1px solid #000 !important;
1892
+ }
1893
+
1894
+ p, li {
1895
+ orphans: 3;
1896
+ widows: 3;
1897
+ }
1898
+
1899
+ a {
1900
+ text-decoration: underline !important;
1901
+ color: black !important;
1902
+ }
1903
+
1904
+ /* Expand external links */
1905
+ a[href^="http"]::after {
1906
+ content: " (" attr(href) ")";
1907
+ font-size: 0.85em;
1908
+ color: #555;
1909
+ }
1910
+
1911
+ /* Clean up code blocks */
1912
+ pre {
1913
+ border: 1px solid #ccc !important;
1914
+ white-space: pre-wrap !important;
1915
+ break-inside: avoid;
1916
+ background: #fafafa !important;
1917
+ }
1918
+
1919
+ /* Blockquotes */
1920
+ blockquote {
1921
+ border-left: 3px solid #ccc !important;
1922
+ background: transparent !important;
1923
+ color: #333 !important;
1924
+ break-inside: avoid;
1925
+ }
1926
+
1927
+ /* Tables */
1928
+ table {
1929
+ break-inside: avoid;
1930
+ }
1931
+
1932
+ /* Page Setup */
1933
+ @page {
1934
+ margin: 2cm;
1935
+ size: auto;
1936
+ }
1937
+ }
1938
+