@master/internal-site 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -0
- package/dist/astro.mjs +233 -47
- package/dist/config.d.mts +6 -0
- package/dist/localization-LfIbIysE.mjs +356 -0
- package/dist/localization.d.mts +69 -0
- package/dist/localization.mjs +1 -0
- package/package.json +12 -9
- package/src/astro/content-pages.ts +30 -11
- package/src/astro/index.ts +11 -5
- package/src/astro/localized-mdx.ts +45 -0
- package/src/astro/routes.template.ts +4 -0
- package/src/astro/site-context.ts +10 -2
- package/src/astro/style-assets.ts +107 -0
- package/src/benchmarks/BenchmarkBarRows.astro +3 -2
- package/src/benchmarks/BenchmarkBars.astro +1 -1
- package/src/benchmarks/BenchmarkChart.astro +5 -1
- package/src/benchmarks/BenchmarkChartGroup.astro +1 -1
- package/src/benchmarks/BenchmarkDataTable.astro +1 -1
- package/src/benchmarks/BenchmarkFigure.astro +2 -1
- package/src/benchmarks/BenchmarkMetricTable.astro +2 -2
- package/src/benchmarks/BenchmarkMetrics.astro +1 -1
- package/src/benchmarks/BenchmarkSampleSummary.astro +1 -1
- package/src/benchmarks/BenchmarkScrollRegion.svelte +2 -1
- package/src/benchmarks/BenchmarkSource.astro +1 -1
- package/src/benchmarks/BenchmarkStackedBars.astro +4 -3
- package/src/benchmarks/BenchmarkStatus.astro +3 -1
- package/src/benchmarks/localization.ts +22 -0
- package/src/benchmarks/types.ts +2 -0
- package/src/blog/article/BlogArticleContents.svelte +1 -1
- package/src/blog/article/BlogArticleEnd.astro +6 -9
- package/src/blog/article/BlogArticleMasthead.astro +17 -9
- package/src/config.ts +3 -0
- package/src/css/NamespaceUtilityTable.astro +1 -1
- package/src/css/ThemeNumberVariableTable.astro +1 -1
- package/src/css/foundation-data/specimen-content.ts +2 -2
- package/src/css/markdown.ts +64 -14
- package/src/demo/Demo.svelte +10 -4
- package/src/demo/DemoAsset.astro +6 -3
- package/src/demo/DemoAxes.svelte +4 -2
- package/src/demo/DemoCatalog.astro +9 -6
- package/src/demo/DemoComparison.svelte +3 -0
- package/src/demo/DemoContainer.svelte +8 -3
- package/src/demo/DemoControls.svelte +5 -1
- package/src/demo/DemoCopyButton.svelte +9 -4
- package/src/demo/DemoCopyGroup.svelte +6 -2
- package/src/demo/DemoExample.astro +3 -1
- package/src/demo/DemoFeatureSupport.svelte +5 -1
- package/src/demo/DemoGeneratedCSS.svelte +5 -1
- package/src/demo/DemoIndex.astro +6 -3
- package/src/demo/DemoLegend.svelte +3 -1
- package/src/demo/DemoMeasure.svelte +4 -2
- package/src/demo/DemoMedia.svelte +3 -1
- package/src/demo/DemoMotion.svelte +6 -3
- package/src/demo/DemoPaletteGroups.svelte +4 -2
- package/src/demo/DemoScrollArea.svelte +2 -0
- package/src/demo/DemoStyleComparison.astro +6 -3
- package/src/demo/DemoSurface.svelte +2 -0
- package/src/demo/DemoThemeComparison.astro +5 -2
- package/src/demo/DemoTokenTable.astro +5 -3
- package/src/demo/DemoViewTransition.astro +3 -1
- package/src/demo/DemoViewport.svelte +18 -11
- package/src/demo/DemoWaterfall.astro +4 -2
- package/src/demo/ProjectStyleExample.astro +4 -1
- package/src/demo/foundations/FoundationDialog.astro +6 -2
- package/src/demo/foundations/FoundationMedia.astro +1 -0
- package/src/demo/foundations/FoundationShrink.astro +1 -0
- package/src/demo/foundations/FoundationTokensView.astro +16 -5
- package/src/demo/foundations/Palette.svelte +11 -7
- package/src/demo/foundations/dialog-copy.d.ts +1 -0
- package/src/demo/foundations/dialog-copy.js +5 -0
- package/src/demo/illustration-materials.ts +1 -1
- package/src/demo/illustration.css +218 -0
- package/src/docs-shell/common/i18n.config.d.ts +1 -1
- package/src/docs-shell/common/i18n.config.js +4 -12
- package/src/docs-shell/common/locales.d.ts +8 -0
- package/src/docs-shell/common/locales.js +18 -0
- package/src/docs-shell/common/terminology.json +32 -0
- package/src/docs-shell/common/translations.d.ts +3 -0
- package/src/docs-shell/common/translations.js +294 -0
- package/src/docs-shell/components/ArticleHeader.astro +5 -3
- package/src/docs-shell/components/Callout.astro +1 -0
- package/src/docs-shell/components/CodeCopyButton.astro +5 -1
- package/src/docs-shell/components/CodeTabBar.astro +7 -3
- package/src/docs-shell/components/CodeTabs.astro +2 -1
- package/src/docs-shell/components/CodeView.astro +1 -1
- package/src/docs-shell/components/DocHeader.svelte +13 -17
- package/src/docs-shell/components/DocHeading.astro +3 -1
- package/src/docs-shell/components/DocMenuButton.svelte +126 -61
- package/src/docs-shell/components/DocSidebar.svelte +3 -2
- package/src/docs-shell/components/DocVersionSelect.svelte +2 -0
- package/src/docs-shell/components/DocumentAPIIndex.astro +1 -1
- package/src/docs-shell/components/DocumentChoices.astro +1 -1
- package/src/docs-shell/components/DocumentCodeExampleView.astro +3 -1
- package/src/docs-shell/components/DocumentCodeTable.astro +4 -2
- package/src/docs-shell/components/DocumentCopyButton.svelte +3 -0
- package/src/docs-shell/components/DocumentDisclosure.astro +2 -0
- package/src/docs-shell/components/DocumentFileTree.astro +3 -0
- package/src/docs-shell/components/DocumentFlow.astro +4 -1
- package/src/docs-shell/components/DocumentIllustration.astro +8 -12
- package/src/docs-shell/components/DocumentKeyList.astro +6 -4
- package/src/docs-shell/components/DocumentNamespaceTable.astro +5 -3
- package/src/docs-shell/components/DocumentOptionEntry.astro +0 -1
- package/src/docs-shell/components/DocumentOptionList.astro +1 -1
- package/src/docs-shell/components/DocumentOptions.astro +3 -2
- package/src/docs-shell/components/DocumentParameters.astro +1 -1
- package/src/docs-shell/components/DocumentPrompt.astro +2 -0
- package/src/docs-shell/components/DocumentValueList.astro +1 -1
- package/src/docs-shell/components/DocumentationIndex.svelte +8 -7
- package/src/docs-shell/components/ExpandContent.svelte +3 -0
- package/src/docs-shell/components/ExplorerView.astro +1 -0
- package/src/docs-shell/components/GeneratedCSS.astro +4 -1
- package/src/docs-shell/components/Header.svelte +1 -1
- package/src/docs-shell/components/InlineCode.astro +1 -0
- package/src/docs-shell/components/InteractingIndicator.astro +1 -0
- package/src/docs-shell/components/LanguageButton.svelte +1 -0
- package/src/docs-shell/components/LanguageSelect.svelte +2 -0
- package/src/docs-shell/components/Link.svelte +3 -2
- package/src/docs-shell/components/NotFound.astro +6 -4
- package/src/docs-shell/components/PMAction.astro +1 -0
- package/src/docs-shell/components/PageContent.svelte +1 -1
- package/src/docs-shell/components/PageContentDisclosure.astro +2 -1
- package/src/docs-shell/components/PageContentList.svelte +2 -0
- package/src/docs-shell/components/PageNavs.astro +3 -3
- package/src/docs-shell/components/SearchButton.svelte +3 -0
- package/src/docs-shell/components/SearchDialog.svelte +18 -16
- package/src/docs-shell/components/Segments.svelte +2 -0
- package/src/docs-shell/components/StepSection.astro +1 -2
- package/src/docs-shell/components/Tabs.astro +1 -0
- package/src/docs-shell/components/ThemeSelect.svelte +2 -0
- package/src/docs-shell/components/TimeAgo.svelte +6 -6
- package/src/docs-shell/components/markdown/Table.astro +1 -0
- package/src/docs-shell/components/markdown-components.ts +0 -1
- package/src/docs-shell/contexts/search.ts +1 -1
- package/src/docs-shell/factories/Page.astro +10 -5
- package/src/docs-shell/factories/create-page.ts +33 -6
- package/src/docs-shell/layouts/BlogArticle.astro +3 -11
- package/src/docs-shell/remark/markdown-components.js +28 -7
- package/src/docs-shell/remark/slug-and-toc.js +5 -7
- package/src/docs-shell/scripts/code.ts +5 -2
- package/src/docs-shell/scripts/document.ts +1 -1
- package/src/docs-shell/site.ts +9 -1
- package/src/docs-shell/types/Metadata.ts +11 -0
- package/src/docs-shell/utils/document-copy.d.ts +2 -0
- package/src/docs-shell/utils/document-copy.js +18 -0
- package/src/docs-shell/utils/generate-metadata.ts +5 -1
- package/src/docs-shell/utils/head.ts +5 -2
- package/src/docs-shell/utils/i18n-pathname.ts +13 -2
- package/src/docs-shell/utils/i18n.ts +4 -1
- package/src/docs-shell/utils/localized-metadata.ts +22 -0
- package/src/docs-shell/utils/markdown-links.ts +61 -0
- package/src/docs-shell/utils/markdown-tree.ts +19 -6
- package/src/docs-shell/utils/resolve-heading.d.ts +1 -0
- package/src/docs-shell/utils/resolve-heading.js +21 -0
- package/src/docs-shell/utils/search-pages.ts +75 -22
- package/src/examples/components/ArticleTransitionScene.svelte +4 -0
- package/src/examples/layout-system/page.astro +0 -1
- package/src/home/HomeBentoFrame.astro +1 -0
- package/src/home/HomeBentoGrid.astro +1 -0
- package/src/home/bento.css +167 -0
- package/src/home/design.css +1 -1
- package/src/home/hero.css +5 -0
- package/src/home/home.css +4 -576
- package/src/home/segmented-control.css +94 -0
- package/src/layouts/Document.astro +4 -2
- package/src/public/icons/json.svg +1 -0
- package/src/scripts/atomic-class-authoring.ts +98 -0
- package/src/scripts/localization.ts +239 -0
- package/src/scripts/mdx-sample.ts +12 -0
- package/src/scripts/static-social.ts +17 -3
- package/src/scripts/style-delivery.mjs +120 -0
- package/src/sections/blog.astro +1 -1
- package/src/sections/guide.astro +4 -3
- package/src/styles/blog-article.css +10 -7
- package/src/styles/blog-materials.css +13 -0
- package/src/styles/demo.css +0 -1
- package/src/styles/docs-shell/code.css +5 -61
- package/src/styles/docs-shell/documentation-search.css +1 -0
- package/src/styles/docs-shell/header.css +2 -73
- package/src/styles/docs-shell/menu.css +37 -17
- package/src/styles/docs-shell/page-content.css +2 -37
- package/src/styles/docs-shell/prose.css +18 -9
- package/src/styles/docs-shell/steps.css +2 -39
- package/src/styles/document-comparison.css +5 -2
- package/src/styles/documentation-illustration.css +1 -9
- package/src/styles/documentation-index.css +2 -1
- package/src/styles/documents.css +2 -4
- package/src/styles/interface.css +5 -3
- package/src/styles/system/DESIGN.md +34 -14
- package/src/styles/system/browser.css +21 -0
- package/src/styles/system/button-feedback.css +13 -0
- package/src/styles/system/callout.css +14 -0
- package/src/styles/system/chart.css +18 -0
- package/src/styles/system/checks.css +18 -0
- package/src/styles/system/code-panel.css +82 -0
- package/src/styles/system/code-tabs.css +40 -0
- package/src/styles/system/code.css +8 -85
- package/src/styles/system/compare.css +7 -0
- package/src/styles/system/controls.css +10 -109
- package/src/styles/system/copy.css +32 -0
- package/src/styles/system/data.css +10 -134
- package/src/styles/system/dialog.css +16 -0
- package/src/styles/system/disclosure.css +19 -0
- package/src/styles/system/expand.css +11 -0
- package/src/styles/system/fields.css +25 -0
- package/src/styles/system/header-core.css +101 -0
- package/src/styles/system/hint.css +5 -0
- package/src/styles/system/index.css +13 -0
- package/src/styles/system/inline-code.css +4 -0
- package/src/styles/system/kbd.css +4 -0
- package/src/styles/system/logos.css +10 -0
- package/src/styles/system/menu.css +14 -0
- package/src/styles/system/metrics.css +22 -0
- package/src/styles/system/pagehead.css +13 -0
- package/src/styles/system/pager.css +13 -0
- package/src/styles/system/parameters.css +17 -0
- package/src/styles/system/prompt.css +7 -0
- package/src/styles/system/prose.css +4 -75
- package/src/styles/system/range.css +12 -0
- package/src/styles/system/reading.css +44 -0
- package/src/styles/system/scroll.css +6 -0
- package/src/styles/system/search-trigger.css +10 -0
- package/src/styles/system/segmented.css +22 -0
- package/src/styles/system/sheet.css +8 -0
- package/src/styles/system/shell.css +8 -102
- package/src/styles/system/sidebar.css +14 -0
- package/src/styles/system/source.css +5 -0
- package/src/styles/system/specimens.css +3 -0
- package/src/styles/system/stage.css +18 -0
- package/src/styles/system/steps.css +51 -0
- package/src/styles/system/surfaces.css +8 -104
- package/src/styles/system/table.css +23 -0
- package/src/styles/system/toc.css +59 -0
- package/src/styles/system/tooltip.css +6 -0
- package/src/styles/system/tray-code.css +6 -0
- package/src/styles/system/tray.css +20 -0
- package/src/styles/system/tree.css +15 -0
- package/src/styles/system/waterfall.css +14 -0
- package/src/virtual.d.ts +5 -1
- package/src/blog/article/language.ts +0 -29
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import Slugger from 'github-slugger'
|
|
2
1
|
import { visit } from 'unist-util-visit'
|
|
3
|
-
import
|
|
2
|
+
import { createHeadingResolver } from '../utils/resolve-heading.js'
|
|
4
3
|
import { parse } from 'acorn'
|
|
5
4
|
|
|
6
5
|
export default function remarkSlugAndTOC() {
|
|
7
6
|
return (tree) => {
|
|
8
|
-
const
|
|
7
|
+
const resolveHeading = createHeadingResolver()
|
|
9
8
|
const toc = []
|
|
10
9
|
|
|
11
10
|
visit(tree, 'heading', (node) => {
|
|
12
11
|
const depth = node.depth
|
|
13
|
-
if (depth
|
|
12
|
+
if (depth < 2 || depth > 6) return
|
|
14
13
|
|
|
15
14
|
// Extract raw text from heading children
|
|
16
15
|
const title = node.children
|
|
@@ -19,8 +18,7 @@ export default function remarkSlugAndTOC() {
|
|
|
19
18
|
.join(' ')
|
|
20
19
|
.trim()
|
|
21
20
|
|
|
22
|
-
const
|
|
23
|
-
const resolved = resolveHeading(title, id)
|
|
21
|
+
const resolved = resolveHeading(title, depth)
|
|
24
22
|
// Explicit IDs survive title edits and translations; they are not visible prose.
|
|
25
23
|
const lastText = node.children.findLast((child) => child.type === 'text')
|
|
26
24
|
if (lastText) lastText.value = lastText.value.replace(/\s+\{#[\w-]+\}$/, '')
|
|
@@ -29,7 +27,7 @@ export default function remarkSlugAndTOC() {
|
|
|
29
27
|
node.data.id = resolved.id
|
|
30
28
|
node.data.hProperties.id = resolved.id
|
|
31
29
|
|
|
32
|
-
toc.push({
|
|
30
|
+
if (depth === 2 || depth === 3) toc.push({
|
|
33
31
|
id: resolved.id,
|
|
34
32
|
title: resolved.title,
|
|
35
33
|
level: depth
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
/** Code blocks render as static markup; these delegated handlers switch tabs and copy code. */
|
|
2
|
+
import { readSiteContext } from '../site'
|
|
3
|
+
|
|
4
|
+
const t = (text: string) => readSiteContext().translations[text] ?? text
|
|
2
5
|
|
|
3
6
|
const tabChange = 'site-code-tabs:change'
|
|
4
7
|
const resetTimers = new WeakMap<HTMLElement, ReturnType<typeof setTimeout>>()
|
|
@@ -24,7 +27,7 @@ function selectTab(root: HTMLElement, name: string) {
|
|
|
24
27
|
const copy = root.querySelector<HTMLButtonElement>('[data-code-copy]')
|
|
25
28
|
if (copy) {
|
|
26
29
|
copy.dataset.codeCopy = name
|
|
27
|
-
copy.ariaLabel = copy.title =
|
|
30
|
+
copy.ariaLabel = copy.title = t('Copy {name}').replace('{name}', t(name))
|
|
28
31
|
}
|
|
29
32
|
}
|
|
30
33
|
|
|
@@ -55,7 +58,7 @@ function showCopyStatus(button: HTMLButtonElement, status: 'idle' | 'pending' |
|
|
|
55
58
|
const element = button.closest('.ui-copy')?.querySelector<HTMLElement>('.ui-copy-status')
|
|
56
59
|
const result = status === 'copied' ? 'success' : status === 'error' ? 'error' : undefined
|
|
57
60
|
if (element) {
|
|
58
|
-
element.textContent = status === 'copied' ?
|
|
61
|
+
element.textContent = status === 'copied' ? t('{name} copied').replace('{name}', t(name || 'Code')) : status === 'error' ? t('Clipboard unavailable. Select the code to copy it.') : ''
|
|
59
62
|
if (result) element.dataset.result = result
|
|
60
63
|
else delete element.dataset.result
|
|
61
64
|
}
|
|
@@ -44,4 +44,4 @@ const tableSizes = new ResizeObserver(entries => {
|
|
|
44
44
|
else table.removeAttribute('tabindex')
|
|
45
45
|
}
|
|
46
46
|
})
|
|
47
|
-
document.querySelectorAll<HTMLElement>('.ui-table:not([tabindex])').forEach(table => { if (!table.closest('
|
|
47
|
+
document.querySelectorAll<HTMLElement>('.ui-table:not([tabindex])').forEach(table => { if (!table.closest('astro-island')) tableSizes.observe(table) })
|
package/src/docs-shell/site.ts
CHANGED
|
@@ -15,6 +15,7 @@ export interface SiteI18n {
|
|
|
15
15
|
defaultLocale: string
|
|
16
16
|
locales: readonly string[]
|
|
17
17
|
localizablePathnameRoots: string[]
|
|
18
|
+
localizableOrigins?: Record<string, readonly string[]>
|
|
18
19
|
hreflangOfLocale: Record<string, string>
|
|
19
20
|
nameOfLocale: Record<string, string>
|
|
20
21
|
}
|
|
@@ -47,6 +48,7 @@ export function createSite(value: SiteContextValue): Site {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
let clientSite: Site | undefined
|
|
51
|
+
let clientContext: string | undefined
|
|
50
52
|
|
|
51
53
|
/** Read the context Document.astro serializes for client islands. */
|
|
52
54
|
export function readSiteContext(): SiteContextValue {
|
|
@@ -63,5 +65,11 @@ export function getSite(): Site {
|
|
|
63
65
|
const value = getContext<SiteContextValue | undefined>(siteContextKey)
|
|
64
66
|
if (value) return createSite(value)
|
|
65
67
|
if (typeof document === 'undefined') throw new Error('getSite() must run inside a component rendered by the site')
|
|
66
|
-
|
|
68
|
+
const serialized = document.getElementById(siteContextElementId)?.textContent
|
|
69
|
+
if (!serialized) throw new Error(`Missing #${siteContextElementId}`)
|
|
70
|
+
if (serialized !== clientContext) {
|
|
71
|
+
clientContext = serialized
|
|
72
|
+
clientSite = createSite(JSON.parse(serialized))
|
|
73
|
+
}
|
|
74
|
+
return clientSite!
|
|
67
75
|
}
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
export declare type MetadataTitle = string | { absolute: string } | { default: string, template?: string }
|
|
2
2
|
|
|
3
|
+
/** An article's visible cover also supplies its social cards. */
|
|
4
|
+
export interface ArticleCover {
|
|
5
|
+
src: string
|
|
6
|
+
alt: string
|
|
7
|
+
width: number
|
|
8
|
+
height: number
|
|
9
|
+
/** Optional PNG/JPEG version of the same artwork for social clients. */
|
|
10
|
+
socialImage?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
3
13
|
/** Page metadata in the shape `docs-shell/utils/head` renders. */
|
|
4
14
|
export declare type Metadata = {
|
|
5
15
|
title?: MetadataTitle
|
|
6
16
|
description?: string
|
|
7
17
|
category?: string
|
|
18
|
+
cover?: ArticleCover
|
|
8
19
|
robots?: string | { index?: boolean, follow?: boolean }
|
|
9
20
|
alternates?: { canonical?: string }
|
|
10
21
|
openGraph?: { title?: string, description?: string, siteName?: string, images?: string | string[] }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const proseFields = new Set(['title', 'description', 'caption', 'summary', 'label', 'note', 'lead', 'sourceLabel', 'resultLabel', 'alt'])
|
|
2
|
+
|
|
3
|
+
/** Overlay article-owned prose without copying or changing executable examples. */
|
|
4
|
+
export function demoCopy(frontmatter, locale, key, original) {
|
|
5
|
+
if (locale === 'en') return original
|
|
6
|
+
const copy = frontmatter.demos?.[key]
|
|
7
|
+
const walk = (value, translated, field, location) => {
|
|
8
|
+
if (typeof value === 'string') {
|
|
9
|
+
if (!proseFields.has(field) || !value.trim()) return value
|
|
10
|
+
if (typeof translated !== 'string' || !translated.trim()) throw new Error(`Missing localized demo copy: ${key}.${location} (${locale})`)
|
|
11
|
+
return translated
|
|
12
|
+
}
|
|
13
|
+
if (Array.isArray(value)) return value.map((item, index) => walk(item, translated?.[index], field, `${location}[${index}]`))
|
|
14
|
+
if (value && typeof value === 'object') return Object.fromEntries(Object.entries(value).map(([name, item]) => [name, walk(item, translated?.[name], name, location ? `${location}.${name}` : name)]))
|
|
15
|
+
return value
|
|
16
|
+
}
|
|
17
|
+
return walk(original, copy, '', '')
|
|
18
|
+
}
|
|
@@ -2,6 +2,7 @@ import { defu } from 'defu'
|
|
|
2
2
|
import type { Metadata } from '../types/Metadata'
|
|
3
3
|
import type { Props } from '../types/Props'
|
|
4
4
|
import { createTranslation } from './i18n'
|
|
5
|
+
import { resolveMetadata } from '../factories/create-page'
|
|
5
6
|
|
|
6
7
|
export default async function generate(
|
|
7
8
|
metadata: Metadata | Record<string, any>,
|
|
@@ -9,6 +10,7 @@ export default async function generate(
|
|
|
9
10
|
dictionaries: Record<string, () => Promise<any>>
|
|
10
11
|
): Promise<Metadata> {
|
|
11
12
|
const { locale } = params
|
|
13
|
+
metadata = resolveMetadata(locale, metadata)
|
|
12
14
|
const $ = await createTranslation(locale, dictionaries)
|
|
13
15
|
const description = $(metadata.description)
|
|
14
16
|
const category = $(metadata.category)
|
|
@@ -38,7 +40,9 @@ export default async function generate(
|
|
|
38
40
|
card: 'summary_large_image'
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
|
-
if (metadata.
|
|
43
|
+
if (metadata.cover) {
|
|
44
|
+
baseMetadata.openGraph.images = baseMetadata.twitter.images = metadata.cover.socialImage ?? metadata.cover.src
|
|
45
|
+
} else if (metadata.openGraph?.images) {
|
|
42
46
|
baseMetadata.twitter.images = metadata.openGraph.images
|
|
43
47
|
} else {
|
|
44
48
|
// postbuild-static renders each placeholder into a static /og image.
|
|
@@ -57,16 +57,19 @@ export function resolveHead(metadata: Partial<Metadata>, { project, siteURL, pat
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
const { openGraph, twitter } = metadata
|
|
60
|
+
const coverImage = metadata.cover?.socialImage ?? metadata.cover?.src
|
|
60
61
|
meta('property', 'og:title', openGraph?.title)
|
|
61
62
|
meta('property', 'og:description', openGraph?.description)
|
|
62
63
|
meta('property', 'og:site_name', openGraph?.siteName)
|
|
63
64
|
// Open Graph and Twitter cards require absolute image URLs.
|
|
64
|
-
for (const image of list(openGraph?.images)) meta('property', 'og:image', absolute(image))
|
|
65
|
+
for (const image of list(coverImage ?? openGraph?.images)) meta('property', 'og:image', absolute(image))
|
|
66
|
+
if (metadata.cover) meta('property', 'og:image:alt', metadata.cover.alt)
|
|
65
67
|
meta('name', 'twitter:card', twitter?.card)
|
|
66
68
|
meta('name', 'twitter:site', twitter?.site)
|
|
67
69
|
meta('name', 'twitter:creator', twitter?.creator)
|
|
68
70
|
meta('name', 'twitter:title', twitter?.title)
|
|
69
71
|
meta('name', 'twitter:description', twitter?.description)
|
|
70
|
-
for (const image of list(twitter?.images)) meta('name', 'twitter:image', absolute(image))
|
|
72
|
+
for (const image of list(coverImage ?? twitter?.images)) meta('name', 'twitter:image', absolute(image))
|
|
73
|
+
if (metadata.cover) meta('name', 'twitter:image:alt', metadata.cover.alt)
|
|
71
74
|
return { title: resolveTitle(metadata.title, project), tags }
|
|
72
75
|
}
|
|
@@ -6,6 +6,7 @@ export interface LocalizePathnameOptions {
|
|
|
6
6
|
locales: readonly string[]
|
|
7
7
|
localePrefixMode?: LocalePrefixMode
|
|
8
8
|
localizablePathnameRoots?: readonly string[]
|
|
9
|
+
localizableOrigins?: Record<string, readonly string[]>
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
const nonPagePathnameRoots = new Set([
|
|
@@ -24,8 +25,18 @@ export function localizePathname(href: string, {
|
|
|
24
25
|
locale,
|
|
25
26
|
locales,
|
|
26
27
|
localePrefixMode = 'canonical',
|
|
27
|
-
localizablePathnameRoots
|
|
28
|
-
|
|
28
|
+
localizablePathnameRoots,
|
|
29
|
+
localizableOrigins
|
|
30
|
+
}: LocalizePathnameOptions): string {
|
|
31
|
+
if (/^https?:\/\//i.test(href)) {
|
|
32
|
+
let url: URL
|
|
33
|
+
try { url = new URL(href) } catch { return href }
|
|
34
|
+
const roots = localizableOrigins?.[url.origin]
|
|
35
|
+
if (!roots) return href
|
|
36
|
+
return url.origin + localizePathname(url.pathname + url.search + url.hash, {
|
|
37
|
+
defaultLocale, locale, locales, localePrefixMode, localizablePathnameRoots: roots
|
|
38
|
+
})
|
|
39
|
+
}
|
|
29
40
|
if (!href || !href.startsWith('/') || href.startsWith('//')) return href
|
|
30
41
|
|
|
31
42
|
const parsedHref = parseLocalHref(href)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import sharedTranslations from '../common/translations'
|
|
2
|
+
|
|
1
3
|
export async function createTranslation(locale: string, dictionaries: Record<string, () => Promise<any>>): Promise<(text: any) => string> {
|
|
2
4
|
const translations = await importTranslations(locale, dictionaries)
|
|
3
5
|
return (text: any) => (translations)[text || ''] || text
|
|
@@ -8,5 +10,6 @@ export const importTranslations = async (locale: string, dictionaries: Record<st
|
|
|
8
10
|
throw new Error('locale is required')
|
|
9
11
|
}
|
|
10
12
|
const load = dictionaries[locale as keyof typeof dictionaries]
|
|
11
|
-
|
|
13
|
+
if (!load && !sharedTranslations[locale]) throw new Error(`Unsupported locale: ${locale}`)
|
|
14
|
+
return { ...sharedTranslations[locale], ...(load ? await load() : {}) }
|
|
12
15
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Prose can vary by locale; publication identity and technical configuration stay with the source. */
|
|
2
|
+
export function localizedMetadata(metadata: Record<string, any>, frontmatter: Record<string, any>, locale: string) {
|
|
3
|
+
const localized: Record<string, any> = { ...metadata, locale }
|
|
4
|
+
for (const key of ['description', 'subtitle']) if (typeof frontmatter[key] === 'string') localized[key] = frontmatter[key]
|
|
5
|
+
if (typeof frontmatter.title === 'string') {
|
|
6
|
+
localized.title = metadata.title && typeof metadata.title === 'object'
|
|
7
|
+
? { ...metadata.title, ...('absolute' in metadata.title ? { absolute: frontmatter.title } : { default: frontmatter.title }) }
|
|
8
|
+
: frontmatter.title
|
|
9
|
+
}
|
|
10
|
+
if (metadata.cover && typeof frontmatter.cover?.alt === 'string') localized.cover = { ...metadata.cover, alt: frontmatter.cover.alt }
|
|
11
|
+
for (const channel of ['openGraph', 'twitter']) {
|
|
12
|
+
const authored = frontmatter[channel]
|
|
13
|
+
if (metadata[channel] || authored) {
|
|
14
|
+
const prose = Object.fromEntries(['title', 'description'].flatMap(key => {
|
|
15
|
+
const text = authored?.[key] ?? (locale !== 'en' ? frontmatter[key] : undefined)
|
|
16
|
+
return typeof text === 'string' ? [[key, text]] : []
|
|
17
|
+
}))
|
|
18
|
+
localized[channel] = { ...metadata[channel], ...prose }
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return localized
|
|
22
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { markdownTree } from './markdown-tree'
|
|
2
|
+
|
|
3
|
+
interface LinkNode {
|
|
4
|
+
type: string
|
|
5
|
+
url?: string
|
|
6
|
+
children?: LinkNode[]
|
|
7
|
+
position?: { start: { offset: number }; end: { offset: number } }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function destination(source: string, node: LinkNode): { start: number; end: number; angled: boolean } {
|
|
11
|
+
const { start, end } = node.position!
|
|
12
|
+
let at = start.offset
|
|
13
|
+
if (node.type === 'definition') {
|
|
14
|
+
for (; at < end.offset; at++) {
|
|
15
|
+
if (source[at] === '\\') { at++; continue }
|
|
16
|
+
if (source[at] === ']' && source[at + 1] === ':') { at += 2; break }
|
|
17
|
+
}
|
|
18
|
+
} else if (source[at] === '[') {
|
|
19
|
+
// Child positions include inline-code and nested formatting in the label.
|
|
20
|
+
at = node.children?.at(-1)?.position?.end.offset ?? at + 1
|
|
21
|
+
at = source.indexOf('](', at) + 2
|
|
22
|
+
if (at < 2) throw new Error(`Missing Markdown link destination: ${node.url}`)
|
|
23
|
+
} else {
|
|
24
|
+
// GFM bare links and CommonMark angle autolinks have no bracketed label.
|
|
25
|
+
const angled = source[at] === '<'
|
|
26
|
+
return { start: at + Number(angled), end: end.offset - Number(angled), angled }
|
|
27
|
+
}
|
|
28
|
+
while (/\s/.test(source[at] ?? '') && at < end.offset) at++
|
|
29
|
+
const angled = source[at] === '<'
|
|
30
|
+
const begin = at + Number(angled)
|
|
31
|
+
at = begin
|
|
32
|
+
let depth = 0
|
|
33
|
+
for (; at < end.offset; at++) {
|
|
34
|
+
const character = source[at]
|
|
35
|
+
if (character === '\\') { at++; continue }
|
|
36
|
+
if (angled ? character === '>' : /\s/.test(character) || (character === ')' && depth === 0)) break
|
|
37
|
+
if (!angled && character === '(') depth++
|
|
38
|
+
else if (!angled && character === ')') depth--
|
|
39
|
+
}
|
|
40
|
+
return { start: begin, end: at, angled }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Edit actual Markdown link/definition URLs without reserializing prose or code. */
|
|
44
|
+
export function transformMarkdownLinks(markdown: string, mapper: (url: string) => string): string {
|
|
45
|
+
const edits: { start: number; end: number; value: string }[] = []
|
|
46
|
+
function visit(node: LinkNode) {
|
|
47
|
+
if ((node.type === 'link' || node.type === 'definition') && node.position && typeof node.url === 'string') {
|
|
48
|
+
const mapped = mapper(node.url)
|
|
49
|
+
if (mapped !== node.url) {
|
|
50
|
+
const range = destination(markdown, node)
|
|
51
|
+
const escaped = mapped.replace(/([\\<>])/g, '\\$1')
|
|
52
|
+
const value = range.angled ? escaped : /\s/.test(mapped) ? `<${escaped}>` : escaped.replace(/([()])/g, '\\$1')
|
|
53
|
+
edits.push({ ...range, value })
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
node.children?.forEach(visit)
|
|
57
|
+
}
|
|
58
|
+
visit(markdownTree(markdown) as LinkNode)
|
|
59
|
+
for (const edit of edits.sort((a, b) => b.start - a.start)) markdown = markdown.slice(0, edit.start) + edit.value + markdown.slice(edit.end)
|
|
60
|
+
return markdown
|
|
61
|
+
}
|
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
import { remark } from 'remark'
|
|
2
2
|
import gfm from 'remark-gfm'
|
|
3
|
-
import
|
|
4
|
-
import resolveHeading from './resolve-heading'
|
|
3
|
+
import resolveHeading, { createHeadingResolver } from './resolve-heading'
|
|
5
4
|
|
|
6
5
|
const parser = remark().use(gfm)
|
|
7
|
-
export
|
|
6
|
+
export function markdownTree(source: string) {
|
|
7
|
+
const tree = parser.parse(source)
|
|
8
|
+
const resolveHeading = createHeadingResolver()
|
|
9
|
+
function visit(node: any) {
|
|
10
|
+
if (node.type === 'heading' && node.depth >= 2 && node.depth <= 6) {
|
|
11
|
+
const title = node.children.filter((child: any) => child.type === 'text' || child.type === 'inlineCode').map((child: any) => child.value).join(' ').trim()
|
|
12
|
+
const resolved = resolveHeading(title, node.depth)
|
|
13
|
+
node.data ??= {}
|
|
14
|
+
node.data.id = resolved.id
|
|
15
|
+
node.data.hProperties = { ...node.data.hProperties, id: resolved.id }
|
|
16
|
+
}
|
|
17
|
+
node.children?.forEach(visit)
|
|
18
|
+
}
|
|
19
|
+
visit(tree)
|
|
20
|
+
return tree
|
|
21
|
+
}
|
|
8
22
|
|
|
9
23
|
export function markdownHeadings(source: string) {
|
|
10
|
-
const slugger = new Slugger()
|
|
11
24
|
const headings: { id: string; title: string; depth: number }[] = []
|
|
12
25
|
function visit(node: any) {
|
|
13
26
|
if (node.type === 'heading' && (node.depth === 2 || node.depth === 3)) {
|
|
14
27
|
const title = node.children.filter((child: any) => child.type === 'text' || child.type === 'inlineCode').map((child: any) => child.value).join(' ').trim()
|
|
15
|
-
const resolved = resolveHeading(title,
|
|
16
|
-
headings.push({ id:
|
|
28
|
+
const resolved = resolveHeading(title, node.data.hProperties.id)
|
|
29
|
+
headings.push({ id: node.data.hProperties.id, title: resolved.title, depth: node.depth })
|
|
17
30
|
}
|
|
18
31
|
node.children?.forEach(visit)
|
|
19
32
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import Slugger from 'github-slugger'
|
|
2
|
+
|
|
1
3
|
export default function resolveHeading(title, id) {
|
|
2
4
|
const explicit = typeof title === 'string' && title.match(/\s+\{#([\w-]+)\}$/)
|
|
3
5
|
if (explicit) {
|
|
@@ -17,3 +19,22 @@ export default function resolveHeading(title, id) {
|
|
|
17
19
|
}
|
|
18
20
|
return { title, id }
|
|
19
21
|
}
|
|
22
|
+
|
|
23
|
+
/** Section counters stay stable when a detail heading is inserted between them. */
|
|
24
|
+
export function createHeadingResolver() {
|
|
25
|
+
const sections = new Slugger()
|
|
26
|
+
const details = new Slugger()
|
|
27
|
+
const assigned = new Set()
|
|
28
|
+
return (title, depth) => {
|
|
29
|
+
const explicit = /\s+\{#[\w-]+\}$/.test(title)
|
|
30
|
+
let heading
|
|
31
|
+
if (explicit) heading = resolveHeading(title)
|
|
32
|
+
else {
|
|
33
|
+
const slugs = depth === 2 || depth === 3 ? sections : details
|
|
34
|
+
do { heading = resolveHeading(title, slugs.slug(title)) }
|
|
35
|
+
while (heading.id && assigned.has(heading.id))
|
|
36
|
+
}
|
|
37
|
+
if (heading.id) assigned.add(heading.id)
|
|
38
|
+
return { ...heading, explicit }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import FastGlob from 'fast-glob'
|
|
2
|
-
import Slugger from 'github-slugger'
|
|
3
2
|
import { existsSync, mkdirSync, readFileSync } from 'node:fs'
|
|
4
3
|
import { dirname, join, resolve } from 'node:path'
|
|
5
4
|
import { pathToFileURL } from 'node:url'
|
|
6
|
-
import {
|
|
5
|
+
import { remark } from 'remark'
|
|
6
|
+
import gfm from 'remark-gfm'
|
|
7
7
|
import { mdxjsEsm } from 'micromark-extension-mdxjs-esm'
|
|
8
|
+
import { mdxJsx } from 'micromark-extension-mdx-jsx'
|
|
9
|
+
import { mdxJsxFromMarkdown } from 'mdast-util-mdx-jsx'
|
|
8
10
|
import { mdxjsEsmFromMarkdown } from 'mdast-util-mdxjs-esm'
|
|
9
11
|
import * as acorn from 'acorn'
|
|
10
|
-
import
|
|
12
|
+
import { createHeadingResolver } from './resolve-heading.js'
|
|
11
13
|
import { writeIfChangedSync } from '../../scripts/write-if-changed'
|
|
12
14
|
import { localizePathname, type LocalePrefixMode } from './i18n-pathname.js'
|
|
15
|
+
import { parseFrontmatter } from '@astrojs/markdown-remark'
|
|
16
|
+
import { localizedMetadata } from './localized-metadata'
|
|
17
|
+
|
|
18
|
+
// Parse complete MDX boundaries so imports in example props are not mistaken for article ESM.
|
|
19
|
+
const markdownParser = remark().use(gfm)
|
|
20
|
+
const searchParser = remark().use(gfm).use(function mdxSearchSyntax() {
|
|
21
|
+
const data = this.data()
|
|
22
|
+
;(data.micromarkExtensions ??= []).push({ disable: { null: ['htmlFlow', 'htmlText', 'codeIndented'] } }, mdxjsEsm({ acorn, addResult: true }), mdxJsx({ acorn, addResult: true }))
|
|
23
|
+
// Consumers may resolve another mdast type version through remark-parse.
|
|
24
|
+
const extensions = [mdxjsEsmFromMarkdown(), mdxJsxFromMarkdown()]
|
|
25
|
+
;(data.fromMarkdownExtensions ??= []).push(...extensions as unknown as NonNullable<typeof data.fromMarkdownExtensions>)
|
|
26
|
+
})
|
|
13
27
|
|
|
14
28
|
const DEFAULT_METADATA_GLOB = './content/**/*metadata.ts'
|
|
15
29
|
|
|
@@ -63,6 +77,7 @@ interface MdxNode {
|
|
|
63
77
|
depth?: number
|
|
64
78
|
type: string
|
|
65
79
|
value?: string
|
|
80
|
+
explicitId?: string
|
|
66
81
|
}
|
|
67
82
|
|
|
68
83
|
export async function generateSearchPages({
|
|
@@ -82,6 +97,12 @@ export async function generateSearchPages({
|
|
|
82
97
|
const translations = loadDictionaryTranslations(dictionariesDir, locale)
|
|
83
98
|
const translate = createDictionaryTranslate(translations)
|
|
84
99
|
const pages = entries
|
|
100
|
+
.filter(entry => {
|
|
101
|
+
if (entry.metadata.draft) return false
|
|
102
|
+
const content = locale === defaultLocale ? entry.content : entry.localizedContent?.[locale]
|
|
103
|
+
if (locale !== defaultLocale && entry.content && !content) return false
|
|
104
|
+
return parseFrontmatter(content ?? '').frontmatter.draft !== true
|
|
105
|
+
})
|
|
85
106
|
.map((entry) => createSearchPage({ defaultLocale, entry, locale, translate }))
|
|
86
107
|
.sort((a, b) => a.url.localeCompare(b.url))
|
|
87
108
|
result[locale] = pages
|
|
@@ -102,7 +123,7 @@ export async function loadSearchPageEntries({
|
|
|
102
123
|
.filter((metadataPath) => (existsSync(resolve(cwd, dirname(metadataPath), 'page.astro')) || existsSync(resolve(cwd, dirname(metadataPath), 'content.mdx'))))
|
|
103
124
|
.sort()
|
|
104
125
|
|
|
105
|
-
return await Promise.all(metadataPaths.map(async (metadataPath) => {
|
|
126
|
+
return (await Promise.all(metadataPaths.map(async (metadataPath) => {
|
|
106
127
|
const metadataModule = await import(pathToFileURL(resolve(cwd, metadataPath)).href)
|
|
107
128
|
const contentPath = resolve(cwd, dirname(metadataPath), 'content.mdx')
|
|
108
129
|
const contentDir = resolve(cwd, dirname(metadataPath))
|
|
@@ -112,7 +133,7 @@ export async function loadSearchPageEntries({
|
|
|
112
133
|
metadata: metadataModule.default,
|
|
113
134
|
metadataPath
|
|
114
135
|
}
|
|
115
|
-
}))
|
|
136
|
+
}))).filter(entry => !entry.metadata.draft && !parseFrontmatter(entry.content ?? '').frontmatter.draft)
|
|
116
137
|
}
|
|
117
138
|
|
|
118
139
|
export function createSearchPage({
|
|
@@ -121,11 +142,15 @@ export function createSearchPage({
|
|
|
121
142
|
locale,
|
|
122
143
|
translate = (text) => text
|
|
123
144
|
}: CreateSearchPageOptions): SearchPage {
|
|
124
|
-
const
|
|
145
|
+
const source = locale === defaultLocale ? entry.content : entry.localizedContent?.[locale]
|
|
146
|
+
if (locale !== defaultLocale && entry.content && !source) throw new Error(`Missing search translation: ${entry.metadata.pathname} (${locale})`)
|
|
147
|
+
const parsed = source === undefined ? undefined : parseFrontmatter(source)
|
|
148
|
+
if (parsed?.frontmatter.draft) throw new Error(`Draft translation cannot be indexed: ${entry.metadata.pathname} (${locale})`)
|
|
149
|
+
const metadata = localizedMetadata(entry.metadata, parsed?.frontmatter ?? {}, locale)
|
|
125
150
|
const title = translate(resolveMetadataString(metadata.title))
|
|
126
151
|
const description = translate(resolveMetadataString(metadata.description))
|
|
127
152
|
const category = translate(resolveMetadataString(metadata.category))
|
|
128
|
-
const content =
|
|
153
|
+
const content = parsed?.content
|
|
129
154
|
const page: SearchPage = {
|
|
130
155
|
category,
|
|
131
156
|
description,
|
|
@@ -170,26 +195,50 @@ export function createLocalizedPathname(pathname: string, locale: string, defaul
|
|
|
170
195
|
}
|
|
171
196
|
|
|
172
197
|
export function extractSearchNodesFromMdx(source: string): SearchNode[] {
|
|
173
|
-
const tree =
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
198
|
+
const tree = searchParser.parse(parseFrontmatter(source).content) as MdxNode
|
|
199
|
+
return searchNodesFromTree(tree)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Portable exports are Markdown and may include native HTML comments or type notation. */
|
|
203
|
+
export function extractSearchNodesFromMarkdown(source: string): SearchNode[] {
|
|
204
|
+
const tree = markdownParser.parse(parseFrontmatter(source).content) as MdxNode
|
|
205
|
+
function resolvePortableAnchors(parent: MdxNode) {
|
|
206
|
+
let pendingId: string | undefined
|
|
207
|
+
for (const node of parent.children ?? []) {
|
|
208
|
+
const markup = node.type === 'html' ? node.value : node.type === 'paragraph'
|
|
209
|
+
&& node.children?.every(child => child.type === 'html' || (child.type === 'text' && !child.value?.trim()))
|
|
210
|
+
? node.children.map(child => child.value ?? '').join('') : undefined
|
|
211
|
+
const anchors = [...(markup ?? '').matchAll(/<a\s+id="([^"]+)"\s*><\/a>/g)]
|
|
212
|
+
if (anchors.length && !markup!.replace(/<a\s+id="([^"]+)"\s*><\/a>/g, '').trim()) {
|
|
213
|
+
pendingId = anchors.at(-1)![1]
|
|
214
|
+
continue
|
|
215
|
+
}
|
|
216
|
+
if (node.type === 'heading' && pendingId) node.explicitId = pendingId
|
|
217
|
+
pendingId = undefined
|
|
218
|
+
if (node.children) resolvePortableAnchors(node)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
resolvePortableAnchors(tree)
|
|
222
|
+
return searchNodesFromTree(tree)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function searchNodesFromTree(tree: MdxNode): SearchNode[] {
|
|
177
226
|
const nodes: SearchNode[] = []
|
|
178
|
-
const
|
|
179
|
-
appendSearchNodes(tree.children ?? [], nodes,
|
|
227
|
+
const resolveHeading = createHeadingResolver()
|
|
228
|
+
appendSearchNodes(tree.children ?? [], nodes, resolveHeading)
|
|
180
229
|
return nodes
|
|
181
230
|
}
|
|
182
231
|
|
|
183
|
-
function appendSearchNodes(children: MdxNode[], nodes: SearchNode[],
|
|
232
|
+
function appendSearchNodes(children: MdxNode[], nodes: SearchNode[], resolveHeading: ReturnType<typeof createHeadingResolver>) {
|
|
184
233
|
for (const child of children) {
|
|
185
|
-
appendSearchNode(child, nodes,
|
|
234
|
+
appendSearchNode(child, nodes, resolveHeading)
|
|
186
235
|
}
|
|
187
236
|
}
|
|
188
237
|
|
|
189
|
-
function appendSearchNode(node: MdxNode, nodes: SearchNode[],
|
|
238
|
+
function appendSearchNode(node: MdxNode, nodes: SearchNode[], resolveHeading: ReturnType<typeof createHeadingResolver>) {
|
|
190
239
|
switch (node.type) {
|
|
191
240
|
case 'heading':
|
|
192
|
-
appendHeadingSearchNode(node, nodes,
|
|
241
|
+
appendHeadingSearchNode(node, nodes, resolveHeading)
|
|
193
242
|
return
|
|
194
243
|
case 'paragraph':
|
|
195
244
|
appendTextSearchNode(nodes, collectText(node), 'p')
|
|
@@ -202,8 +251,11 @@ function appendSearchNode(node: MdxNode, nodes: SearchNode[], slugger: Slugger)
|
|
|
202
251
|
appendTextSearchNode(nodes, collectText(child), 'li')
|
|
203
252
|
}
|
|
204
253
|
return
|
|
254
|
+
case 'table':
|
|
255
|
+
for (const row of node.children ?? []) appendTextSearchNode(nodes, (row.children ?? []).map(collectText).join(' | '), 'tr')
|
|
256
|
+
return
|
|
205
257
|
case 'blockquote':
|
|
206
|
-
appendSearchNodes(node.children ?? [], nodes,
|
|
258
|
+
appendSearchNodes(node.children ?? [], nodes, resolveHeading)
|
|
207
259
|
return
|
|
208
260
|
case 'mdxjsEsm':
|
|
209
261
|
case 'html':
|
|
@@ -212,18 +264,19 @@ function appendSearchNode(node: MdxNode, nodes: SearchNode[], slugger: Slugger)
|
|
|
212
264
|
return
|
|
213
265
|
default:
|
|
214
266
|
if (node.children?.length) {
|
|
215
|
-
appendSearchNodes(node.children, nodes,
|
|
267
|
+
appendSearchNodes(node.children, nodes, resolveHeading)
|
|
216
268
|
}
|
|
217
269
|
}
|
|
218
270
|
}
|
|
219
271
|
|
|
220
|
-
function appendHeadingSearchNode(node: MdxNode, nodes: SearchNode[],
|
|
272
|
+
function appendHeadingSearchNode(node: MdxNode, nodes: SearchNode[], resolveHeading: ReturnType<typeof createHeadingResolver>) {
|
|
221
273
|
const text = normalizeSearchText((node.children ?? []).filter(child => child.type === 'text' || child.type === 'inlineCode').map(child => child.value).join(' '))
|
|
222
274
|
if (!text) return
|
|
223
275
|
|
|
224
276
|
const tag = node.depth ? `h${node.depth}` : 'h'
|
|
225
|
-
if (node.depth
|
|
226
|
-
const
|
|
277
|
+
if (node.depth && node.depth >= 2 && node.depth <= 6) {
|
|
278
|
+
const title = node.explicitId && !/\{#[\w-]+\}/.test(text) ? `${text} {#${node.explicitId}}` : text
|
|
279
|
+
const resolved = resolveHeading(title, node.depth)
|
|
227
280
|
appendTextSearchNode(nodes, resolved.title, tag, resolved.id)
|
|
228
281
|
return
|
|
229
282
|
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import '@master/internal-site/styles/system/button-feedback.css'
|
|
3
|
+
import '@master/internal-site/styles/system/buttons.css'
|
|
4
|
+
import '@master/internal-site/styles/system/sheet.css'
|
|
5
|
+
|
|
2
6
|
import Image from '@master/internal-site/components/Image.svelte'
|
|
3
7
|
import IconArrowLeft from '@tabler/icons-svelte-runes/icons/arrow-left'
|
|
4
8
|
import IconArrowUpRight from '@tabler/icons-svelte-runes/icons/arrow-up-right'
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
---
|
|
2
2
|
// Bare examples import the system parts they render: sheets and specimens.
|
|
3
|
-
import '@master/internal-site/styles/system/surfaces.css'
|
|
4
3
|
import '@master/internal-site/styles/system/specimens.css'
|
|
5
4
|
import DemoItem from '@master/internal-site/demo/DemoItem.svelte'
|
|
6
5
|
import DemoLabel from '@master/internal-site/demo/DemoLabel.svelte'
|