@terrafirmagreg-team/kit 0.0.0-stage → 0.1.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/package.json +68 -3
- package/src/components/blocks/nav-title.tsx +65 -0
- package/src/components/docs-image.tsx +49 -0
- package/src/components/outline/dropdown.tsx +67 -0
- package/src/components/outline/index.ts +10 -0
- package/src/components/outline/list.tsx +120 -0
- package/src/components/outline/outline.tsx +49 -0
- package/src/components/outline/popover.tsx +76 -0
- package/src/components/outline/types.ts +29 -0
- package/src/components/page-actions.tsx +201 -0
- package/src/components/relative-link.tsx +18 -0
- package/src/components/search/dialog.tsx +145 -0
- package/src/components/toc/base.tsx +65 -0
- package/src/components/toc/block.tsx +111 -0
- package/src/components/toc/clerk.tsx +265 -0
- package/src/components/toc/default.tsx +333 -0
- package/src/components/version-menu.tsx +128 -0
- package/src/components/views/dataset.tsx +63 -0
- package/src/components/views/section.tsx +108 -0
- package/src/components/workspace/content.tsx +53 -0
- package/src/components/workspace/index.ts +12 -0
- package/src/components/workspace/pagination.tsx +73 -0
- package/src/components/workspace/workspace.tsx +148 -0
- package/src/hooks/toc.ts +9 -0
- package/src/hooks/use-catalog.ts +11 -0
- package/src/hooks/use-current-dataset.ts +14 -0
- package/src/hooks/use-dataset-accent.ts +13 -0
- package/src/hooks/use-nav-options.ts +84 -0
- package/src/hooks/use-version-pref.ts +30 -0
- package/src/lib/breadcrumb.ts +78 -0
- package/src/lib/config.ts +109 -0
- package/src/lib/dataset-links.ts +30 -0
- package/src/lib/datasets.ts +211 -0
- package/src/lib/landing.ts +46 -0
- package/src/lib/layout.ts +15 -0
- package/src/lib/legacy.ts +18 -0
- package/src/lib/location.ts +63 -0
- package/src/lib/markdown/dom.ts +90 -0
- package/src/lib/markdown/index.ts +13 -0
- package/src/lib/markdown/providers.ts +25 -0
- package/src/lib/mdx/accordions.ts +93 -0
- package/src/lib/mdx/banner.ts +52 -0
- package/src/lib/mdx/directive-utils.ts +125 -0
- package/src/lib/mdx/directives.ts +141 -0
- package/src/lib/mdx/game-export.ts +44 -0
- package/src/lib/mdx/html-style.ts +135 -0
- package/src/lib/mdx/preview.ts +252 -0
- package/src/lib/mdx/references.ts +56 -0
- package/src/lib/mdx/tabs.ts +80 -0
- package/src/lib/mdx-components.tsx +139 -0
- package/src/lib/order.ts +88 -0
- package/src/lib/pack-assets.ts +45 -0
- package/src/lib/page.ts +108 -0
- package/src/lib/pin-tree.ts +35 -0
- package/src/lib/preview/index.ts +8 -0
- package/src/lib/preview/lookup.ts +48 -0
- package/src/lib/preview/og.tsx +115 -0
- package/src/lib/preview/resolvers.ts +98 -0
- package/src/lib/search/config.ts +13 -0
- package/src/lib/search/filters.ts +22 -0
- package/src/lib/search/index.ts +19 -0
- package/src/lib/search/indexes.ts +43 -0
- package/src/lib/search/merge.ts +22 -0
- package/src/lib/search/server.ts +18 -0
- package/src/lib/search/sources.ts +19 -0
- package/src/lib/search/structured.ts +23 -0
- package/src/lib/section/schema.ts +36 -0
- package/src/lib/section/source.ts +40 -0
- package/src/lib/sources.ts +21 -0
- package/src/lib/tree.ts +60 -0
- package/src/lib/urls.ts +16 -0
- package/src/locales/en_us.json +10 -0
- package/src/locales/ru_ru.json +10 -0
- package/src/locales.ts +5 -0
- package/src/types/index.ts +83 -0
- package/src/types/mdx.ts +55 -0
- package/src/types/turndown-plugin-gfm.d.ts +9 -0
- package/README.md +0 -3
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import TurndownService from 'turndown';
|
|
3
|
+
import { gfm } from 'turndown-plugin-gfm';
|
|
4
|
+
|
|
5
|
+
const service = new TurndownService({
|
|
6
|
+
headingStyle: 'atx',
|
|
7
|
+
codeBlockStyle: 'fenced',
|
|
8
|
+
bulletListMarker: '-',
|
|
9
|
+
});
|
|
10
|
+
service.use(gfm);
|
|
11
|
+
service.remove([
|
|
12
|
+
'script',
|
|
13
|
+
'style',
|
|
14
|
+
'noscript',
|
|
15
|
+
'template',
|
|
16
|
+
'button',
|
|
17
|
+
'select',
|
|
18
|
+
'nav',
|
|
19
|
+
'aside',
|
|
20
|
+
'footer',
|
|
21
|
+
]);
|
|
22
|
+
service.remove(
|
|
23
|
+
(node) =>
|
|
24
|
+
node.nodeName === 'SVG' ||
|
|
25
|
+
(node instanceof HTMLImageElement &&
|
|
26
|
+
(node.src.startsWith('data:') ||
|
|
27
|
+
(node.width > 0 && node.width <= 48))) ||
|
|
28
|
+
(node instanceof HTMLElement && node.dataset.mdSkip !== undefined),
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
function decodedUrl(raw: string): string {
|
|
32
|
+
const url = raw.replace(/(?:%[0-9A-Fa-f]{2})+/g, (run) => {
|
|
33
|
+
let decoded: string;
|
|
34
|
+
try {
|
|
35
|
+
decoded = decodeURIComponent(run);
|
|
36
|
+
} catch {
|
|
37
|
+
return run;
|
|
38
|
+
}
|
|
39
|
+
return /[/?#&=%+]/.test(decoded) ? run : decoded;
|
|
40
|
+
});
|
|
41
|
+
return /[\s<>()]/.test(url) ? `<${url}>` : url;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
service.addRule('decodedLink', {
|
|
45
|
+
filter: 'a',
|
|
46
|
+
replacement(content, node) {
|
|
47
|
+
const anchor = node as HTMLAnchorElement;
|
|
48
|
+
const href = anchor.getAttribute('href');
|
|
49
|
+
if (!href) return content;
|
|
50
|
+
const title = anchor.getAttribute('title');
|
|
51
|
+
const titlePart = title ? ` "${title.replace(/"/g, '\"')}"` : '';
|
|
52
|
+
return `[${content}](${decodedUrl(href)}${titlePart})`;
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
service.addRule('decodedImage', {
|
|
56
|
+
filter: 'img',
|
|
57
|
+
replacement(_content, node) {
|
|
58
|
+
const image = node as HTMLImageElement;
|
|
59
|
+
const src = image.getAttribute('src');
|
|
60
|
+
if (!src) return '';
|
|
61
|
+
const alt = image.getAttribute('alt') ?? '';
|
|
62
|
+
const title = image.getAttribute('title');
|
|
63
|
+
const titlePart = title ? ` "${title.replace(/"/g, '\\"')}"` : '';
|
|
64
|
+
return `}${titlePart})`;
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export function domToMarkdown(root: Element | null | undefined): string {
|
|
69
|
+
if (!root) return '';
|
|
70
|
+
return service.turndown(root as HTMLElement);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function pageMarkdown(
|
|
74
|
+
root: Element | null | undefined,
|
|
75
|
+
title: string,
|
|
76
|
+
): Promise<string> {
|
|
77
|
+
return Promise.resolve(`# ${title}\n\n${domToMarkdown(root)}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function openPageMarkdown(
|
|
81
|
+
root: Element | null | undefined,
|
|
82
|
+
title: string,
|
|
83
|
+
): Promise<void> {
|
|
84
|
+
const markdown = await pageMarkdown(root, title);
|
|
85
|
+
const url = URL.createObjectURL(
|
|
86
|
+
new Blob([markdown], { type: 'text/markdown;charset=utf-8' }),
|
|
87
|
+
);
|
|
88
|
+
window.open(url, '_blank', 'noopener');
|
|
89
|
+
setTimeout(() => URL.revokeObjectURL(url), 60_000);
|
|
90
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from './dom';
|
|
2
|
+
export * from './providers';
|
|
3
|
+
|
|
4
|
+
export async function renderSectionMarkdown(page: {
|
|
5
|
+
data: {
|
|
6
|
+
title?: string;
|
|
7
|
+
getText: (type: 'processed' | 'raw') => Promise<string>;
|
|
8
|
+
};
|
|
9
|
+
}) {
|
|
10
|
+
return `# ${page.data.title}
|
|
11
|
+
|
|
12
|
+
${await page.data.getText('processed')}`;
|
|
13
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
|
|
2
|
+
|
|
3
|
+
export interface MarkdownProviderCtx {
|
|
4
|
+
|
|
5
|
+
slugs: string[];
|
|
6
|
+
lang: Lang;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type MarkdownProvider = (
|
|
10
|
+
req: Request,
|
|
11
|
+
ctx: MarkdownProviderCtx,
|
|
12
|
+
) => Promise<Response | undefined>;
|
|
13
|
+
|
|
14
|
+
const providers = new Map<string, MarkdownProvider>();
|
|
15
|
+
|
|
16
|
+
export function registerMarkdownProvider(
|
|
17
|
+
section: string,
|
|
18
|
+
provider: MarkdownProvider,
|
|
19
|
+
): void {
|
|
20
|
+
providers.set(section, provider);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function markdownProvider(section: string): MarkdownProvider | undefined {
|
|
24
|
+
return providers.get(section);
|
|
25
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {
|
|
2
|
+
contentChildren,
|
|
3
|
+
extractDirectiveLabel,
|
|
4
|
+
extractLeafLabel,
|
|
5
|
+
jsxAttr,
|
|
6
|
+
walkContainers,
|
|
7
|
+
type DirectiveNode,
|
|
8
|
+
type JsxAttribute,
|
|
9
|
+
type JsxElement,
|
|
10
|
+
type MdastNode,
|
|
11
|
+
} from './directive-utils.ts';
|
|
12
|
+
|
|
13
|
+
export function remarkAccordions() {
|
|
14
|
+
return (tree: MdastNode) => {
|
|
15
|
+
walkContainers(tree, (directive) => {
|
|
16
|
+
if (directive.name === 'accordion' || directive.name === 'details') {
|
|
17
|
+
wrapSingle(directive);
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
if (directive.name === 'accordions') {
|
|
21
|
+
toAccordions(directive);
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function toAccordion(node: DirectiveNode): JsxElement {
|
|
29
|
+
return {
|
|
30
|
+
type: 'mdxJsxFlowElement',
|
|
31
|
+
name: 'Accordion',
|
|
32
|
+
attributes: [jsxAttr('title', extractDirectiveLabel(node))],
|
|
33
|
+
children: contentChildren(node),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function wrapSingle(node: DirectiveNode): void {
|
|
38
|
+
Object.assign(node, {
|
|
39
|
+
type: 'mdxJsxFlowElement',
|
|
40
|
+
name: 'Accordions',
|
|
41
|
+
attributes: [],
|
|
42
|
+
children: [toAccordion(node)],
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function accordionsAttributes(node: DirectiveNode): JsxAttribute[] {
|
|
47
|
+
const attrs = node.attributes ?? {};
|
|
48
|
+
const out: JsxAttribute[] = [];
|
|
49
|
+
const isMultiple =
|
|
50
|
+
attrs.multiple === true ||
|
|
51
|
+
attrs.multiple === '' ||
|
|
52
|
+
attrs.type === 'multiple';
|
|
53
|
+
|
|
54
|
+
if (isMultiple) out.push(jsxAttr('multiple', true));
|
|
55
|
+
|
|
56
|
+
for (const [name, value] of Object.entries(attrs)) {
|
|
57
|
+
if (name === 'type' || name === 'multiple') continue;
|
|
58
|
+
out.push({ type: 'mdxJsxAttribute', name, value: value ?? true });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function toAccordions(node: DirectiveNode): void {
|
|
65
|
+
const items: JsxElement[] = [];
|
|
66
|
+
let current: JsxElement | undefined;
|
|
67
|
+
|
|
68
|
+
for (const item of node.children ?? []) {
|
|
69
|
+
if (
|
|
70
|
+
item.type === 'leafDirective' &&
|
|
71
|
+
(item as { name?: string }).name === 'accordion'
|
|
72
|
+
) {
|
|
73
|
+
current = {
|
|
74
|
+
type: 'mdxJsxFlowElement',
|
|
75
|
+
name: 'Accordion',
|
|
76
|
+
attributes: [jsxAttr('title', extractLeafLabel(item))],
|
|
77
|
+
children: [],
|
|
78
|
+
};
|
|
79
|
+
items.push(current);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
current?.children.push(item);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
Object.assign(node, {
|
|
87
|
+
type: 'mdxJsxFlowElement',
|
|
88
|
+
name: 'Accordions',
|
|
89
|
+
attributes: accordionsAttributes(node),
|
|
90
|
+
children: items,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
estreeBoolean,
|
|
3
|
+
estreeStringArray,
|
|
4
|
+
jsxExpression,
|
|
5
|
+
walkContainers,
|
|
6
|
+
type DirectiveNode,
|
|
7
|
+
type MdastNode,
|
|
8
|
+
} from './directive-utils.ts';
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
export function remarkBanner() {
|
|
12
|
+
return (tree: MdastNode) => {
|
|
13
|
+
walkContainers(tree, (directive) => {
|
|
14
|
+
if (directive.name !== 'banner') return;
|
|
15
|
+
Object.assign(directive, {
|
|
16
|
+
type: 'mdxJsxFlowElement',
|
|
17
|
+
name: 'Banner',
|
|
18
|
+
attributes: [
|
|
19
|
+
{
|
|
20
|
+
type: 'mdxJsxAttribute',
|
|
21
|
+
name: 'changeLayout',
|
|
22
|
+
value: jsxExpression(estreeBoolean(false)),
|
|
23
|
+
},
|
|
24
|
+
...toAttributes(directive),
|
|
25
|
+
],
|
|
26
|
+
children: directive.children,
|
|
27
|
+
});
|
|
28
|
+
return false;
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function toAttributes(directive: DirectiveNode) {
|
|
34
|
+
return Object.entries(directive.attributes ?? {})
|
|
35
|
+
.filter(([name]) => name !== 'id')
|
|
36
|
+
.map(([name, value]) => {
|
|
37
|
+
if (name === 'rainbowColors' && typeof value === 'string') {
|
|
38
|
+
const colors = value
|
|
39
|
+
.split(',')
|
|
40
|
+
.map((color) => color.trim())
|
|
41
|
+
.filter(Boolean);
|
|
42
|
+
return {
|
|
43
|
+
type: 'mdxJsxAttribute' as const,
|
|
44
|
+
name,
|
|
45
|
+
value: jsxExpression(estreeStringArray(colors)),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return { type: 'mdxJsxAttribute' as const, name, value: value ?? true };
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
|
|
2
|
+
import type {
|
|
3
|
+
DirectiveNode,
|
|
4
|
+
JsxAttribute,
|
|
5
|
+
JsxAttributeValue,
|
|
6
|
+
MdastNode,
|
|
7
|
+
} from '@terrafirmagreg-team/kit/types/mdx';
|
|
8
|
+
|
|
9
|
+
export type {
|
|
10
|
+
DirectiveNode,
|
|
11
|
+
JsxAttribute,
|
|
12
|
+
JsxAttributeValue,
|
|
13
|
+
JsxElement,
|
|
14
|
+
MdastNode,
|
|
15
|
+
} from '@terrafirmagreg-team/kit/types/mdx';
|
|
16
|
+
|
|
17
|
+
export function walkContainers(
|
|
18
|
+
tree: MdastNode,
|
|
19
|
+
fn: (node: DirectiveNode) => boolean | void,
|
|
20
|
+
): void {
|
|
21
|
+
for (const child of tree.children ?? []) {
|
|
22
|
+
if (child.type !== 'containerDirective') {
|
|
23
|
+
walkContainers(child, fn);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const directive = child as DirectiveNode;
|
|
27
|
+
if (fn(directive) !== false) walkContainers(directive, fn);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const DIRECTIVE_TYPES = new Set([
|
|
32
|
+
'containerDirective',
|
|
33
|
+
'leafDirective',
|
|
34
|
+
'textDirective',
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
export function walkDirectives(
|
|
38
|
+
tree: MdastNode,
|
|
39
|
+
fn: (node: DirectiveNode) => boolean | void,
|
|
40
|
+
): void {
|
|
41
|
+
for (const child of tree.children ?? []) {
|
|
42
|
+
if (DIRECTIVE_TYPES.has(child.type)) {
|
|
43
|
+
if (fn(child as DirectiveNode) !== false) walkDirectives(child, fn);
|
|
44
|
+
} else {
|
|
45
|
+
walkDirectives(child, fn);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function toJsxAttributes(node: DirectiveNode): JsxAttribute[] {
|
|
51
|
+
return Object.entries(node.attributes ?? {}).map(([name, value]) => ({
|
|
52
|
+
type: 'mdxJsxAttribute',
|
|
53
|
+
name,
|
|
54
|
+
value: value ?? true,
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function labelText(node: MdastNode): string {
|
|
59
|
+
const t = node.type;
|
|
60
|
+
const v = (node as { value?: unknown }).value;
|
|
61
|
+
if (typeof v === 'string') return v;
|
|
62
|
+
if (t === 'textDirective' || t === 'leafDirective') {
|
|
63
|
+
const d = node as DirectiveNode;
|
|
64
|
+
const inner = (d.children ?? []).map(labelText).join('');
|
|
65
|
+
const attrs = Object.entries(d.attributes ?? {})
|
|
66
|
+
.map(([k, v]) => (v == null ? k : v === true ? k : `${k}="${v}"`))
|
|
67
|
+
.join(' ');
|
|
68
|
+
const prefix = t === 'leafDirective' ? '::' : ':';
|
|
69
|
+
return `${prefix}${d.name ?? ''}${inner ? `[${inner}]` : ''}${attrs ? `{${attrs}}` : ''}`;
|
|
70
|
+
}
|
|
71
|
+
return (node.children ?? []).map(labelText).join('');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function extractDirectiveLabel(node: DirectiveNode): string {
|
|
75
|
+
for (const child of node.children ?? []) {
|
|
76
|
+
if (child.type === 'paragraph' && child.data?.directiveLabel) {
|
|
77
|
+
return (child.children ?? []).map(labelText).join('');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return '';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function extractLeafLabel(node: MdastNode): string {
|
|
84
|
+
return (node.children ?? []).map(labelText).join('');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function contentChildren(node: DirectiveNode): MdastNode[] {
|
|
88
|
+
return (node.children ?? []).filter(
|
|
89
|
+
(child) =>
|
|
90
|
+
!(child.type === 'paragraph' && child.data?.directiveLabel),
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function jsxAttr(name: string, value: string | boolean): JsxAttribute {
|
|
95
|
+
return { type: 'mdxJsxAttribute', name, value };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function jsxExpression(expression: unknown): JsxAttributeValue {
|
|
99
|
+
return {
|
|
100
|
+
type: 'mdxJsxAttributeValueExpression',
|
|
101
|
+
value: '',
|
|
102
|
+
data: {
|
|
103
|
+
estree: {
|
|
104
|
+
type: 'Program',
|
|
105
|
+
body: [{ type: 'ExpressionStatement', expression }],
|
|
106
|
+
sourceType: 'script',
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function estreeString(value: string): unknown {
|
|
113
|
+
return { type: 'Literal', value, raw: JSON.stringify(value) };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function estreeStringArray(labels: string[]): unknown {
|
|
117
|
+
return {
|
|
118
|
+
type: 'ArrayExpression',
|
|
119
|
+
elements: labels.map(estreeString),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function estreeBoolean(value: boolean): unknown {
|
|
124
|
+
return { type: 'Literal', value, raw: String(value) };
|
|
125
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractLeafLabel,
|
|
3
|
+
jsxExpression,
|
|
4
|
+
toJsxAttributes,
|
|
5
|
+
walkDirectives,
|
|
6
|
+
type DirectiveNode,
|
|
7
|
+
type MdastNode,
|
|
8
|
+
} from './directive-utils.ts';
|
|
9
|
+
import { gradientImage } from '@terrafirmagreg-team/ui/lib/mdx/gradient';
|
|
10
|
+
|
|
11
|
+
export function remarkDirectives() {
|
|
12
|
+
return (tree: MdastNode) => {
|
|
13
|
+
walkDirectives(tree, (node) => {
|
|
14
|
+
const { type, name } = node;
|
|
15
|
+
if (type === 'textDirective' && name === 'gradient') {
|
|
16
|
+
Object.assign(node, {
|
|
17
|
+
type: 'mdxJsxTextElement',
|
|
18
|
+
name: 'span',
|
|
19
|
+
attributes: [
|
|
20
|
+
styleAttr(gradientStyle(node)),
|
|
21
|
+
{ type: 'mdxJsxAttribute', name: 'data-md', value: 'gradient' },
|
|
22
|
+
],
|
|
23
|
+
});
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (
|
|
27
|
+
name === 'glow' &&
|
|
28
|
+
(type === 'textDirective' ||
|
|
29
|
+
type === 'leafDirective' ||
|
|
30
|
+
type === 'containerDirective')
|
|
31
|
+
) {
|
|
32
|
+
Object.assign(node, {
|
|
33
|
+
type:
|
|
34
|
+
type === 'textDirective'
|
|
35
|
+
? 'mdxJsxTextElement'
|
|
36
|
+
: 'mdxJsxFlowElement',
|
|
37
|
+
name: 'span',
|
|
38
|
+
attributes: [
|
|
39
|
+
styleAttr(glowStyle(node)),
|
|
40
|
+
{ type: 'mdxJsxAttribute', name: 'data-md', value: 'glow' },
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
return type === 'containerDirective' ? undefined : false;
|
|
44
|
+
}
|
|
45
|
+
if (type === 'leafDirective' && name === 'recipe') {
|
|
46
|
+
Object.assign(node, {
|
|
47
|
+
type: 'mdxJsxFlowElement',
|
|
48
|
+
name: 'Recipe',
|
|
49
|
+
attributes: [
|
|
50
|
+
{
|
|
51
|
+
type: 'mdxJsxAttribute',
|
|
52
|
+
name: 'id',
|
|
53
|
+
value: extractLeafLabel(node).trim(),
|
|
54
|
+
},
|
|
55
|
+
...toJsxAttributes(node),
|
|
56
|
+
],
|
|
57
|
+
children: [],
|
|
58
|
+
});
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function estreeObject(entries: [string, string][]): unknown {
|
|
67
|
+
return {
|
|
68
|
+
type: 'ObjectExpression',
|
|
69
|
+
properties: entries.map(([key, value]) => ({
|
|
70
|
+
type: 'Property',
|
|
71
|
+
key: { type: 'Identifier', name: key },
|
|
72
|
+
value: { type: 'Literal', value, raw: JSON.stringify(value) },
|
|
73
|
+
kind: 'init',
|
|
74
|
+
computed: false,
|
|
75
|
+
shorthand: false,
|
|
76
|
+
})),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function styleAttr(entries: [string, string][]) {
|
|
81
|
+
return {
|
|
82
|
+
type: 'mdxJsxAttribute' as const,
|
|
83
|
+
name: 'style',
|
|
84
|
+
value: jsxExpression(estreeObject(entries)),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function attr(node: DirectiveNode, name: string) {
|
|
89
|
+
const value = node.attributes?.[name];
|
|
90
|
+
return typeof value === 'string' && value ? value : undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function hasFlag(node: DirectiveNode, name: string) {
|
|
94
|
+
return node.attributes?.[name] !== undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function gradientStyle(node: DirectiveNode): [string, string][] {
|
|
98
|
+
return [
|
|
99
|
+
['backgroundImage', gradientImage(attrObject(node))],
|
|
100
|
+
['WebkitBackgroundClip', 'text'],
|
|
101
|
+
['backgroundClip', 'text'],
|
|
102
|
+
['WebkitTextFillColor', 'transparent'],
|
|
103
|
+
['color', 'transparent'],
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function glowStyle(node: DirectiveNode): [string, string][] {
|
|
108
|
+
const from = attr(node, 'from') ?? '#fa18cf';
|
|
109
|
+
const to = attr(node, 'to') ?? '#ff7967';
|
|
110
|
+
const gradient = gradientImage(attrObject(node));
|
|
111
|
+
const boxBg = `radial-gradient(color-mix(in srgb, ${from} 18%, transparent), color-mix(in srgb, ${to} 8%, transparent))`;
|
|
112
|
+
const style: [string, string][] = [
|
|
113
|
+
['display', 'block'],
|
|
114
|
+
['borderRadius', '20px'],
|
|
115
|
+
['border', '1px solid transparent'],
|
|
116
|
+
['padding', '0.625rem 1rem'],
|
|
117
|
+
['marginBottom', '1.5rem'],
|
|
118
|
+
['textAlign', 'center'],
|
|
119
|
+
['backgroundImage', hasFlag(node, 'gradient') ? `${gradient}, ${boxBg}` : boxBg],
|
|
120
|
+
['boxShadow', `0 10px 20px color-mix(in srgb, ${from} 25%, transparent)`],
|
|
121
|
+
];
|
|
122
|
+
if (hasFlag(node, 'gradient')) {
|
|
123
|
+
style.push(
|
|
124
|
+
['WebkitBackgroundClip', 'text, border-box'],
|
|
125
|
+
['backgroundClip', 'text, border-box'],
|
|
126
|
+
['WebkitTextFillColor', 'transparent'],
|
|
127
|
+
['color', 'transparent'],
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
if (hasFlag(node, 'fade')) style.push(['borderImage', `${gradient} 1`]);
|
|
131
|
+
return style;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function attrObject(node: DirectiveNode) {
|
|
135
|
+
return {
|
|
136
|
+
from: attr(node, 'from'),
|
|
137
|
+
to: attr(node, 'to'),
|
|
138
|
+
dir: attr(node, 'dir'),
|
|
139
|
+
image: attr(node, 'image'),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
estreeString,
|
|
3
|
+
jsxExpression,
|
|
4
|
+
walkContainers,
|
|
5
|
+
type MdastNode,
|
|
6
|
+
} from './directive-utils.ts';
|
|
7
|
+
|
|
8
|
+
export function remarkGameExport() {
|
|
9
|
+
return (tree: MdastNode) => {
|
|
10
|
+
walkContainers(tree, (directive) => {
|
|
11
|
+
if (
|
|
12
|
+
directive.name !== 'item' &&
|
|
13
|
+
directive.name !== 'multiblock' &&
|
|
14
|
+
directive.name !== 'image'
|
|
15
|
+
)
|
|
16
|
+
return;
|
|
17
|
+
|
|
18
|
+
const attrs = Object.entries(directive.attributes ?? {}).map(
|
|
19
|
+
([name, value]) => ({
|
|
20
|
+
type: 'mdxJsxAttribute' as const,
|
|
21
|
+
name,
|
|
22
|
+
value:
|
|
23
|
+
typeof value === 'string'
|
|
24
|
+
? value
|
|
25
|
+
: jsxExpression(estreeString(String(value ?? ''))),
|
|
26
|
+
}),
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const component =
|
|
30
|
+
directive.name === 'item'
|
|
31
|
+
? 'ItemIcon'
|
|
32
|
+
: directive.name === 'multiblock'
|
|
33
|
+
? 'Multiblock'
|
|
34
|
+
: 'DocsImage';
|
|
35
|
+
|
|
36
|
+
Object.assign(directive, {
|
|
37
|
+
type: 'mdxJsxFlowElement',
|
|
38
|
+
name: component,
|
|
39
|
+
attributes: attrs,
|
|
40
|
+
children: directive.children ?? [],
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
}
|