diff --git a/apps/landing/package.json b/apps/landing/package.json index e0c1ffe9..71f959ff 100644 --- a/apps/landing/package.json +++ b/apps/landing/package.json @@ -17,6 +17,7 @@ "braillify": "workspace:*", "clsx": "^2.1.1", "katex": "^0.17.0", + "mathlive": "^0.110.0", "next": "16.2.10", "react": "^19.2.7", "react-dom": "^19.2.7", diff --git a/apps/landing/public/mathlive/fonts/KaTeX_AMS-Regular.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_AMS-Regular.woff2 new file mode 100644 index 00000000..0acaaff0 Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_AMS-Regular.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_Caligraphic-Bold.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_Caligraphic-Bold.woff2 new file mode 100644 index 00000000..f390922e Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_Caligraphic-Bold.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_Caligraphic-Regular.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_Caligraphic-Regular.woff2 new file mode 100644 index 00000000..75344a1f Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_Caligraphic-Regular.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_Fraktur-Bold.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_Fraktur-Bold.woff2 new file mode 100644 index 00000000..395f28be Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_Fraktur-Bold.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_Fraktur-Regular.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_Fraktur-Regular.woff2 new file mode 100644 index 00000000..735f6948 Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_Fraktur-Regular.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_Main-Bold.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_Main-Bold.woff2 new file mode 100644 index 00000000..ab2ad21d Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_Main-Bold.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_Main-BoldItalic.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_Main-BoldItalic.woff2 new file mode 100644 index 00000000..5931794d Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_Main-BoldItalic.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_Main-Italic.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_Main-Italic.woff2 new file mode 100644 index 00000000..b50920e1 Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_Main-Italic.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_Main-Regular.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_Main-Regular.woff2 new file mode 100644 index 00000000..eb24a7ba Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_Main-Regular.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_Math-BoldItalic.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_Math-BoldItalic.woff2 new file mode 100644 index 00000000..29657023 Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_Math-BoldItalic.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_Math-Italic.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_Math-Italic.woff2 new file mode 100644 index 00000000..215c143f Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_Math-Italic.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_SansSerif-Bold.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_SansSerif-Bold.woff2 new file mode 100644 index 00000000..cfaa3bda Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_SansSerif-Bold.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_SansSerif-Italic.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_SansSerif-Italic.woff2 new file mode 100644 index 00000000..349c06dc Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_SansSerif-Italic.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_SansSerif-Regular.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_SansSerif-Regular.woff2 new file mode 100644 index 00000000..a90eea85 Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_SansSerif-Regular.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_Script-Regular.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_Script-Regular.woff2 new file mode 100644 index 00000000..b3048fc1 Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_Script-Regular.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_Size1-Regular.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_Size1-Regular.woff2 new file mode 100644 index 00000000..c5a8462f Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_Size1-Regular.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_Size2-Regular.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_Size2-Regular.woff2 new file mode 100644 index 00000000..e1bccfe2 Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_Size2-Regular.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_Size3-Regular.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_Size3-Regular.woff2 new file mode 100644 index 00000000..249a2866 Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_Size3-Regular.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_Size4-Regular.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_Size4-Regular.woff2 new file mode 100644 index 00000000..680c1308 Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_Size4-Regular.woff2 differ diff --git a/apps/landing/public/mathlive/fonts/KaTeX_Typewriter-Regular.woff2 b/apps/landing/public/mathlive/fonts/KaTeX_Typewriter-Regular.woff2 new file mode 100644 index 00000000..771f1af7 Binary files /dev/null and b/apps/landing/public/mathlive/fonts/KaTeX_Typewriter-Regular.woff2 differ diff --git a/apps/landing/src/app/DemoChrome.tsx b/apps/landing/src/app/DemoChrome.tsx new file mode 100644 index 00000000..551f147b --- /dev/null +++ b/apps/landing/src/app/DemoChrome.tsx @@ -0,0 +1,50 @@ +import { Box, Flex, Image, Text } from '@devup-ui/react' + +/** 데모 섹션 상단의 손가락 아이콘 + 안내 문구. 한글·수학 데모가 공유한다. */ +export function DemoHeading({ children }: { children: string }) { + return ( + + + ) +} + +/** 입력 박스와 출력 박스 사이의 방향 화살표. 한글·수학 데모가 공유한다. */ +export function DemoArrow() { + return ( + + ) +} diff --git a/apps/landing/src/app/DemoTabs.tsx b/apps/landing/src/app/DemoTabs.tsx new file mode 100644 index 00000000..2d53a04f --- /dev/null +++ b/apps/landing/src/app/DemoTabs.tsx @@ -0,0 +1,80 @@ +'use client' +import { Box, Flex, Text, VStack } from '@devup-ui/react' +import { useRef, useState } from 'react' + +import { MathTrans } from './MathTrans' +import { Trans } from './Trans' + +const TABS = [ + { key: 'korean', label: '한글' }, + { key: 'math', label: '수학' }, +] as const + +type DemoMode = (typeof TABS)[number]['key'] + +const PANEL_ID = 'demo-panel' +const tabId = (key: DemoMode) => `demo-tab-${key}` + +export function DemoTabs() { + const [mode, setMode] = useState('korean') + const tabRefs = useRef<(HTMLElement | null)[]>([]) + + const handleKeyDown = (e: React.KeyboardEvent, index: number) => { + if (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft') return + e.preventDefault() + const dir = e.key === 'ArrowRight' ? 1 : -1 + const next = (index + dir + TABS.length) % TABS.length + setMode(TABS[next].key) + tabRefs.current[next]?.focus() + } + + return ( + + + {TABS.map(({ key, label }, index) => ( + { + tabRefs.current[index] = el + }} + aria-controls={PANEL_ID} + aria-selected={mode === key} + as="button" + bg={mode === key ? '$text' : 'transparent'} + border="1px solid $text" + borderRadius="9999px" + color={mode === key ? '$background' : '$text'} + cursor="pointer" + id={tabId(key)} + onClick={() => setMode(key)} + onKeyDown={(e) => handleKeyDown(e, index)} + px={['20px', null, null, '28px']} + py={['8px', null, null, '10px']} + role="tab" + tabIndex={mode === key ? 0 : -1} + type="button" + typography="button" + > + {label} + + ))} + + + {mode === 'korean' ? : } + + + ) +} diff --git a/apps/landing/src/app/MathTrans.tsx b/apps/landing/src/app/MathTrans.tsx new file mode 100644 index 00000000..75d8704b --- /dev/null +++ b/apps/landing/src/app/MathTrans.tsx @@ -0,0 +1,55 @@ +'use client' +import { VStack } from '@devup-ui/react' +import { useEffect, useState } from 'react' + +import { DemoArrow, DemoHeading } from './DemoChrome' +import { MathTransInput } from './MathTransInput' +import { TransInput } from './TransInput' + +export function MathTrans() { + const [latex, setLatex] = useState('') + const [translateToUnicode, setTranslateToUnicode] = useState< + (input: string) => string + >(() => () => '') + useEffect(() => { + import('braillify').then((mod) => { + setTranslateToUnicode(() => (input: string) => { + try { + return mod.translateToUnicode(input) + } catch (e) { + console.error(e) + return '점역할 수 없는 수식이 포함되어 있습니다.' + } + }) + }) + }, []) + + const braille = latex.trim() ? translateToUnicode(`$${latex}$`) : '' + + return ( + + + 수식도 점자가 됩니다. 수식 키보드로 입력해 수학 점역을 체험해보세요! + + + + + + + + ) +} diff --git a/apps/landing/src/app/MathTransInput.tsx b/apps/landing/src/app/MathTransInput.tsx new file mode 100644 index 00000000..d5f5afa1 --- /dev/null +++ b/apps/landing/src/app/MathTransInput.tsx @@ -0,0 +1,168 @@ +'use client' + +import { css, Flex, Text, VStack } from '@devup-ui/react' +import type { MathfieldElement } from 'mathlive' +import { useEffect, useRef, useState } from 'react' + +declare global { + namespace React.JSX { + interface IntrinsicElements { + 'math-field': React.DetailedHTMLProps< + React.HTMLAttributes, + MathfieldElement + > & { 'math-virtual-keyboard-policy'?: 'auto' | 'manual' } + } + } +} + +/** + * MathLive는 한 글자 인자를 중괄호 없이 직렬화한다 (예: \frac34, \frac{3}4). + * braillify의 LaTeX 파서는 \frac{분자}{분모} 형태만 분수로 인식하므로 + * \frac의 두 인자를 항상 중괄호로 감싼 정규형으로 변환한다. + */ +export function normalizeFracBraces(latex: string): string { + let out = '' + let i = 0 + while (i < latex.length) { + if (latex.startsWith('\\frac', i) && !/[a-zA-Z]/.test(latex[i + 5] ?? '')) { + const [num, j] = readArg(latex, i + 5) + const [den, k] = readArg(latex, j) + out += `\\frac${num}${den}` + i = k + continue + } + out += latex[i] + i += 1 + } + return out +} + +/** i 위치부터 LaTeX 인자 하나를 읽어 중괄호로 감싼 형태와 다음 인덱스를 돌려준다. */ +function readArg(latex: string, i: number): [string, number] { + while (latex[i] === ' ') i += 1 + if (latex[i] === '{') { + let depth = 0 + let j = i + do { + if (latex[j] === '{') depth += 1 + else if (latex[j] === '}') depth -= 1 + j += 1 + } while (j < latex.length && depth > 0) + // depth === 0 이면 짝이 맞는 '}' 를 j-1 에서 소비한 것이고, + // depth > 0 이면 닫는 중괄호 없이 끝난 것이라 내용을 j 까지 살린다. + const end = depth === 0 ? j - 1 : j + return [`{${normalizeFracBraces(latex.slice(i + 1, end))}}`, j] + } + if (latex[i] === '\\') { + let j = i + 1 + while (j < latex.length && /[a-zA-Z]/.test(latex[j] ?? '')) j += 1 + // 제어기호(\, \! 등)는 백슬래시 뒤에 글자가 없으므로 기호 한 글자를 포함시킨다. + if (j === i + 1 && j < latex.length) j += 1 + return [`{${latex.slice(i, j)}}`, j] + } + return [`{${latex[i] ?? ''}}`, i + 1] +} + +export function MathTransInput({ + latex, + onLatexChange, + placeholder, +}: { + latex: string + onLatexChange: (latex: string) => void + placeholder: string +}) { + const [ready, setReady] = useState(false) + const fieldRef = useRef(null) + + useEffect(() => { + let cancelled = false + import('mathlive').then(({ MathfieldElement }) => { + if (cancelled) return + MathfieldElement.fontsDirectory = '/mathlive/fonts' + MathfieldElement.soundsDirectory = null + setReady(true) + }) + return () => { + cancelled = true + } + }, []) + + useEffect(() => { + const field = fieldRef.current + if (!ready || !field) return + const show = () => window.mathVirtualKeyboard.show() + const hide = () => window.mathVirtualKeyboard.hide() + field.addEventListener('focusin', show) + field.addEventListener('focusout', hide) + return () => { + field.removeEventListener('focusin', show) + field.removeEventListener('focusout', hide) + window.mathVirtualKeyboard.hide() + } + }, [ready]) + + return ( + // 바깥 Flex 는 padding 없는 flex 아이템으로, 출력측 TransInput 의 외곽 + // Flex(flex=1 h=100% w=100%)와 flex-basis 를 동일하게 맞춰 좌우 박스 너비를 + // 같게 한다. 실제 배경/여백은 안쪽 박스가 담당한다. + + fieldRef.current?.focus()} + p={['16px', null, null, '40px']} + > + + {ready && ( + + onLatexChange( + normalizeFracBraces( + (e.target as MathfieldElement).getValue( + 'latex-without-placeholders', + ), + ), + ) + } + className={css({ + background: 'transparent', + border: 'none', + display: 'block', + fontSize: '28px', + width: '100%', + })} + /> + )} + {!latex && ( + + {placeholder} + + )} + + + {latex ? `LaTeX: $${latex}$` : 'LaTeX가 자동으로 생성됩니다'} + + + + ) +} diff --git a/apps/landing/src/app/Trans.tsx b/apps/landing/src/app/Trans.tsx index 3d6c57d8..6d208c7a 100644 --- a/apps/landing/src/app/Trans.tsx +++ b/apps/landing/src/app/Trans.tsx @@ -1,7 +1,8 @@ 'use client' -import { Box, Flex, Image, Text, VStack } from '@devup-ui/react' +import { VStack } from '@devup-ui/react' import { useEffect, useState } from 'react' +import { DemoArrow, DemoHeading } from './DemoChrome' import { TransInput } from './TransInput' export function Trans() { @@ -27,29 +28,10 @@ export function Trans() { return ( - - - 직접 입력해 실시간 점자 번역을 체험해보세요! + @@ -70,23 +52,7 @@ export function Trans() { }} value={input} /> - + - + ) } diff --git a/apps/landing/src/app/page.tsx b/apps/landing/src/app/page.tsx index 6842cea7..16a6bc9c 100644 --- a/apps/landing/src/app/page.tsx +++ b/apps/landing/src/app/page.tsx @@ -5,7 +5,7 @@ import PillButton from '@/components/home/PillButton' import IconDiscord from '@/components/icons/IconDiscord' import IconKakao from '@/components/icons/IconKakao' -import { Trans } from './Trans' +import { DemoTabs } from './DemoTabs' const SITE_URL = 'https://braillify.kr' @@ -168,7 +168,7 @@ export default function HomePage() { - + diff --git a/bun.lock b/bun.lock index 93fe691e..fb78c793 100644 --- a/bun.lock +++ b/bun.lock @@ -25,6 +25,7 @@ "braillify": "workspace:*", "clsx": "^2.1.1", "katex": "^0.17.0", + "mathlive": "^0.110.0", "next": "16.2.10", "react": "^19.2.7", "react-dom": "^19.2.7", @@ -42,13 +43,15 @@ }, "packages/node": { "name": "braillify", - "version": "2.0.1", + "version": "2.1.0", "devDependencies": { "binaryen": "^130.0.0", }, }, }, "packages": { + "@arnog/colors": ["@arnog/colors@0.5.0", "", {}, "sha512-NB7yqrO7qvInsqJdqz6C6mDU+2oiKmbBbk3czMS0E7KkGxLRy538tEHCsB5TKqmaxxXpi6/U120TnGl8NsDePg=="], + "@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], "@babel/compat-data": ["@babel/compat-data@7.29.7", "", {}, "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg=="], @@ -83,6 +86,8 @@ "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "@cortex-js/compute-engine": ["@cortex-js/compute-engine@0.58.0", "", { "dependencies": { "@arnog/colors": "^0.5.0", "complex-esm": "^2.1.1-esm1", "decimal.js": "^10.6.0" } }, "sha512-N0+vXjVZfKwJS7ZIGvq41mojI55m5TrdDXveAXzNjzjZ9iNiNrdEzXL2EmrcP+9GCWCwTonrEeZWAIG78f3INg=="], + "@devup-ui/components": ["@devup-ui/components@0.1.47", "", { "dependencies": { "@devup-ui/react": "^1.0.37", "clsx": "^2.1", "react": "^19.2.7" } }, "sha512-B/V2fTbSUIFObF/Zz4gyGhDmuY3vmbej9678494VrmrAM/5JeaN/X+0quWGIpjwPy/rhvAW6nNLEf0XJPZQ7ew=="], "@devup-ui/eslint-plugin": ["@devup-ui/eslint-plugin@1.0.16", "", { "dependencies": { "@typescript-eslint/utils": "^8.60", "typescript-eslint": "^8.60" } }, "sha512-gXhEVO9c4qGfR6HcCXsnRZHZlepDBZ1BnA0M2pB1/9asXSqWoJmt75xE0beXtt7wgrBHO/Z5gh+iX8Xu3e2ewQ=="], @@ -417,6 +422,8 @@ "commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], + "complex-esm": ["complex-esm@2.1.1-esm1", "", {}, "sha512-IShBEWHILB9s7MnfyevqNGxV0A1cfcSnewL/4uPFiSxkcQL4Mm3FxJ0pXMtCXuWLjYz3lRRyk6OfkeDZcjD6nw=="], + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], "concat-stream": ["concat-stream@2.0.0", "", { "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A=="], @@ -437,6 +444,8 @@ "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], + "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], @@ -753,6 +762,8 @@ "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + "mathlive": ["mathlive@0.110.0", "", { "dependencies": { "@cortex-js/compute-engine": "0.58.0" } }, "sha512-UOpJsQ6h1eeN0xZULGTl1MwUB/lZLCDuzKeHvKEh7Zra8U/rtgDNrssj5PZdJtBTIV48AfEhtv6rv47AeMOxJA=="], + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="], "mdast-util-mdx": ["mdast-util-mdx@3.0.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w=="],