@intlayer/design-system 9.3.0 → 9.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/README.md +22 -22
  2. package/dist/esm/api/hooks/utils.mjs +2 -1
  3. package/dist/esm/api/hooks/utils.mjs.map +1 -1
  4. package/dist/esm/api/index.mjs +2 -2
  5. package/dist/esm/api/useAuth/useOAuth2.mjs +14 -4
  6. package/dist/esm/api/useAuth/useOAuth2.mjs.map +1 -1
  7. package/dist/esm/api/useAuth/useSession.mjs +7 -5
  8. package/dist/esm/api/useAuth/useSession.mjs.map +1 -1
  9. package/dist/esm/components/Accordion/Accordion.mjs +1 -1
  10. package/dist/esm/components/Avatar/index.mjs +1 -1
  11. package/dist/esm/components/Badge/index.mjs +1 -1
  12. package/dist/esm/components/Breadcrumb/index.mjs +1 -1
  13. package/dist/esm/components/Browser/Browser.mjs +1 -1
  14. package/dist/esm/components/Button/Button.mjs +1 -1
  15. package/dist/esm/components/Carousel/index.mjs +1 -1
  16. package/dist/esm/components/CollapsibleTable/CollapsibleTable.mjs +1 -1
  17. package/dist/esm/components/Command/index.mjs +1 -1
  18. package/dist/esm/components/Container/index.mjs +1 -1
  19. package/dist/esm/components/ContentEditor/ContentEditor.mjs +1 -1
  20. package/dist/esm/components/ContentEditor/ContentEditorInput.mjs +1 -1
  21. package/dist/esm/components/ContentEditor/ContentEditorTextArea.mjs +2 -2
  22. package/dist/esm/components/CopyButton/index.mjs +1 -1
  23. package/dist/esm/components/CopyToClipboard/index.mjs +1 -1
  24. package/dist/esm/components/DictionaryEditor/NodeWrapper/BooleanWrapper.mjs +2 -1
  25. package/dist/esm/components/DictionaryEditor/NodeWrapper/BooleanWrapper.mjs.map +1 -1
  26. package/dist/esm/components/DictionaryEditor/NodeWrapper/NumberWrapper.mjs +2 -1
  27. package/dist/esm/components/DictionaryEditor/NodeWrapper/NumberWrapper.mjs.map +1 -1
  28. package/dist/esm/components/DictionaryFieldEditor/ContentEditor.mjs +2 -2
  29. package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs +3 -3
  30. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/DictionaryCreationForm.mjs +1 -1
  31. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.mjs +1 -1
  32. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.mjs.map +1 -1
  33. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs +7 -7
  34. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.mjs +1 -1
  35. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.mjs.map +1 -1
  36. package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs +2 -2
  37. package/dist/esm/components/DictionaryFieldEditor/KeyPathBreadcrumb.mjs +8 -7
  38. package/dist/esm/components/DictionaryFieldEditor/KeyPathBreadcrumb.mjs.map +1 -1
  39. package/dist/esm/components/DictionaryFieldEditor/NavigationView/NavigationViewNode.mjs +8 -5
  40. package/dist/esm/components/DictionaryFieldEditor/NavigationView/NavigationViewNode.mjs.map +1 -1
  41. package/dist/esm/components/DictionaryFieldEditor/NodeTypeSelector.mjs +2 -1
  42. package/dist/esm/components/DictionaryFieldEditor/NodeTypeSelector.mjs.map +1 -1
  43. package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs +3 -3
  44. package/dist/esm/components/DictionaryFieldEditor/StructureEditor.mjs +2 -1
  45. package/dist/esm/components/DictionaryFieldEditor/StructureEditor.mjs.map +1 -1
  46. package/dist/esm/components/DictionaryFieldEditor/StructureView/StructureView.mjs +2 -2
  47. package/dist/esm/components/DictionaryFieldEditor/VersionSwitcherDropDown/VersionSwitcher.mjs +1 -1
  48. package/dist/esm/components/EditableField/EditableFieldInput.mjs +6 -5
  49. package/dist/esm/components/EditableField/EditableFieldInput.mjs.map +1 -1
  50. package/dist/esm/components/EditableField/EditableFieldLayout.mjs +1 -1
  51. package/dist/esm/components/EditableField/EditableFieldTextArea.mjs +2 -1
  52. package/dist/esm/components/EditableField/EditableFieldTextArea.mjs.map +1 -1
  53. package/dist/esm/components/Form/FormBase.mjs.map +1 -1
  54. package/dist/esm/components/Form/elements/FormElementWrapper.mjs +1 -1
  55. package/dist/esm/components/Form/elements/FormElementWrapper.mjs.map +1 -1
  56. package/dist/esm/components/Form/elements/OTPElement.mjs +1 -1
  57. package/dist/esm/components/HTMLRender/HTMLRender.mjs +5 -3
  58. package/dist/esm/components/HTMLRender/HTMLRender.mjs.map +1 -1
  59. package/dist/esm/components/HideShow/index.mjs +1 -1
  60. package/dist/esm/components/IDE/Code.mjs.map +1 -1
  61. package/dist/esm/components/IDE/CodeBlockClient.mjs.map +1 -1
  62. package/dist/esm/components/IDE/CodeBlockHighlight.mjs +3 -14
  63. package/dist/esm/components/IDE/CodeBlockHighlight.mjs.map +1 -1
  64. package/dist/esm/components/IDE/CodeBlockServer.mjs +14 -12
  65. package/dist/esm/components/IDE/CodeBlockServer.mjs.map +1 -1
  66. package/dist/esm/components/IDE/CodeBlockShiki.mjs +32 -86
  67. package/dist/esm/components/IDE/CodeBlockShiki.mjs.map +1 -1
  68. package/dist/esm/components/IDE/FileTree.mjs +3 -2
  69. package/dist/esm/components/IDE/FileTree.mjs.map +1 -1
  70. package/dist/esm/components/IDE/MarkDownRender.mjs.map +1 -1
  71. package/dist/esm/components/IDE/shikiLanguages.mjs +122 -0
  72. package/dist/esm/components/IDE/shikiLanguages.mjs.map +1 -0
  73. package/dist/esm/components/Input/Checkbox.mjs +1 -1
  74. package/dist/esm/components/Input/Input.mjs +1 -1
  75. package/dist/esm/components/Input/InputPassword.mjs +1 -1
  76. package/dist/esm/components/Input/OTPInput.mjs +5 -4
  77. package/dist/esm/components/Input/OTPInput.mjs.map +1 -1
  78. package/dist/esm/components/Input/Radio.mjs +1 -1
  79. package/dist/esm/components/Input/SearchInput.mjs +1 -1
  80. package/dist/esm/components/LanguageBackground/LanguageBackground.mjs +2 -1
  81. package/dist/esm/components/LanguageBackground/LanguageBackground.mjs.map +1 -1
  82. package/dist/esm/components/Link/Link.mjs +1 -1
  83. package/dist/esm/components/LocaleSwitcherContentDropDown/LocaleSwitcherContent.mjs +7 -6
  84. package/dist/esm/components/LocaleSwitcherContentDropDown/LocaleSwitcherContent.mjs.map +1 -1
  85. package/dist/esm/components/LocaleSwitcherDropDown/LocaleSwitcher.mjs +1 -1
  86. package/dist/esm/components/MarkDownRender/MarkDownIframe.mjs +1 -1
  87. package/dist/esm/components/MarkDownRender/MarkDownRender.mjs +12 -7
  88. package/dist/esm/components/MarkDownRender/MarkDownRender.mjs.map +1 -1
  89. package/dist/esm/components/MarkdownEditor/generative/AICompletionCommands.mjs +1 -1
  90. package/dist/esm/components/MarkdownEditor/generative/AISelector.mjs +1 -1
  91. package/dist/esm/components/MarkdownEditor/generative/AISelectorCommands.mjs +1 -1
  92. package/dist/esm/components/MarkdownEditor/generative/GenerativeMenuSwitch.mjs +1 -1
  93. package/dist/esm/components/MarkdownEditor/selectors/ColorSelector.mjs +1 -1
  94. package/dist/esm/components/MarkdownEditor/selectors/LinkSelector.mjs +1 -1
  95. package/dist/esm/components/MarkdownEditor/selectors/NodeSelector.mjs +1 -1
  96. package/dist/esm/components/MarkdownEditor/selectors/TextButtons.mjs +1 -1
  97. package/dist/esm/components/MarkdownEditor/slashCommand.mjs +1 -1
  98. package/dist/esm/components/Modal/Modal.mjs +3 -3
  99. package/dist/esm/components/Navbar/MobileNavbar.mjs +1 -1
  100. package/dist/esm/components/Pagination/Pagination.mjs +2 -2
  101. package/dist/esm/components/PressableSpan/PressableSpan.mjs +0 -2
  102. package/dist/esm/components/PressableSpan/PressableSpan.mjs.map +1 -1
  103. package/dist/esm/components/RightDrawer/RightDrawer.mjs +4 -4
  104. package/dist/esm/components/Select/Multiselect.mjs +3 -5
  105. package/dist/esm/components/Select/Multiselect.mjs.map +1 -1
  106. package/dist/esm/components/Select/Select.mjs +1 -1
  107. package/dist/esm/components/SwitchSelector/SwitchSelector.mjs +1 -1
  108. package/dist/esm/components/SwitchSelector/VerticalSwitchSelector.mjs +1 -1
  109. package/dist/esm/components/Tab/Tab.mjs +2 -2
  110. package/dist/esm/components/TabSelector/TabSelector.mjs +1 -1
  111. package/dist/esm/components/Table/ExpandButton.mjs +1 -1
  112. package/dist/esm/components/Tag/index.mjs +1 -1
  113. package/dist/esm/components/Terminal/Terminal.mjs +7 -5
  114. package/dist/esm/components/Terminal/Terminal.mjs.map +1 -1
  115. package/dist/esm/components/TextArea/AutocompleteTextArea.mjs +6 -3
  116. package/dist/esm/components/TextArea/AutocompleteTextArea.mjs.map +1 -1
  117. package/dist/esm/components/ThemeSwitcherDropDown/DesktopThemeSwitcher.mjs +1 -1
  118. package/dist/esm/components/ThemeSwitcherDropDown/MobileThemeSwitcher.mjs +1 -1
  119. package/dist/esm/components/Toaster/Toast.mjs +1 -1
  120. package/dist/esm/components/Toaster/useToast.mjs +1 -1
  121. package/dist/esm/components/Toaster/useToast.mjs.map +1 -1
  122. package/dist/esm/hooks/index.mjs +8 -8
  123. package/dist/esm/hooks/useScrollDetection.mjs +4 -2
  124. package/dist/esm/hooks/useScrollDetection.mjs.map +1 -1
  125. package/dist/esm/providers/DeferredAuthProvider.mjs +75 -0
  126. package/dist/esm/providers/DeferredAuthProvider.mjs.map +1 -0
  127. package/dist/esm/providers/ReactQueryProvider.mjs +4 -4
  128. package/dist/esm/providers/ReactQueryProvider.mjs.map +1 -1
  129. package/dist/esm/providers/index.mjs +2 -1
  130. package/dist/esm/routes.mjs +12 -11
  131. package/dist/esm/routes.mjs.map +1 -1
  132. package/dist/esm/structured-data/buildAuthorJsonLd.mjs +6 -3
  133. package/dist/esm/structured-data/buildAuthorJsonLd.mjs.map +1 -1
  134. package/dist/esm/structured-data/buildBreadcrumbsJsonLd.mjs +4 -1
  135. package/dist/esm/structured-data/buildBreadcrumbsJsonLd.mjs.map +1 -1
  136. package/dist/esm/structured-data/buildCreativeWorkJsonLd.mjs +7 -5
  137. package/dist/esm/structured-data/buildCreativeWorkJsonLd.mjs.map +1 -1
  138. package/dist/esm/structured-data/buildOrganizationJsonLd.mjs +27 -21
  139. package/dist/esm/structured-data/buildOrganizationJsonLd.mjs.map +1 -1
  140. package/dist/esm/structured-data/buildProductJsonLd.mjs +5 -2
  141. package/dist/esm/structured-data/buildProductJsonLd.mjs.map +1 -1
  142. package/dist/esm/structured-data/buildSoftwareApplicationJsonLd.mjs +59 -51
  143. package/dist/esm/structured-data/buildSoftwareApplicationJsonLd.mjs.map +1 -1
  144. package/dist/esm/structured-data/buildWebsiteJsonLd.mjs +31 -25
  145. package/dist/esm/structured-data/buildWebsiteJsonLd.mjs.map +1 -1
  146. package/dist/esm/structured-data/index.mjs +2 -1
  147. package/dist/esm/structured-data/normalizeJsonLdUrl.mjs +34 -0
  148. package/dist/esm/structured-data/normalizeJsonLdUrl.mjs.map +1 -0
  149. package/dist/esm/tailwind.config.mjs +4 -2
  150. package/dist/esm/tailwind.config.mjs.map +1 -1
  151. package/dist/types/api/index.d.ts +2 -2
  152. package/dist/types/api/useAuth/index.d.ts +2 -2
  153. package/dist/types/api/useAuth/useOAuth2.d.ts +12 -3
  154. package/dist/types/api/useAuth/useOAuth2.d.ts.map +1 -1
  155. package/dist/types/api/useAuth/useSession.d.ts.map +1 -1
  156. package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.d.ts +1 -1
  157. package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/useDictionaryDetailsSchema.d.ts +1 -1
  158. package/dist/types/components/Form/FormBase.d.ts +1 -1
  159. package/dist/types/components/Form/elements/FormElementWrapper.d.ts +1 -1
  160. package/dist/types/components/IDE/Code.d.ts +2 -2
  161. package/dist/types/components/IDE/CodeBlockClient.d.ts +2 -2
  162. package/dist/types/components/IDE/CodeBlockHighlight.d.ts +2 -2
  163. package/dist/types/components/IDE/CodeBlockHighlight.d.ts.map +1 -1
  164. package/dist/types/components/IDE/CodeBlockShiki.d.ts +2 -2
  165. package/dist/types/components/IDE/CodeBlockShiki.d.ts.map +1 -1
  166. package/dist/types/components/IDE/index.d.ts +2 -1
  167. package/dist/types/components/IDE/shikiLanguages.d.ts +138 -0
  168. package/dist/types/components/IDE/shikiLanguages.d.ts.map +1 -0
  169. package/dist/types/components/index.d.ts +2 -1
  170. package/dist/types/providers/DeferredAuthProvider.d.ts +33 -0
  171. package/dist/types/providers/DeferredAuthProvider.d.ts.map +1 -0
  172. package/dist/types/providers/index.d.ts +2 -1
  173. package/dist/types/routes.d.ts +17 -16
  174. package/dist/types/routes.d.ts.map +1 -1
  175. package/dist/types/structured-data/buildAuthorJsonLd.d.ts.map +1 -1
  176. package/dist/types/structured-data/buildBreadcrumbsJsonLd.d.ts.map +1 -1
  177. package/dist/types/structured-data/buildCreativeWorkJsonLd.d.ts.map +1 -1
  178. package/dist/types/structured-data/buildOrganizationJsonLd.d.ts.map +1 -1
  179. package/dist/types/structured-data/buildProductJsonLd.d.ts.map +1 -1
  180. package/dist/types/structured-data/buildSoftwareApplicationJsonLd.d.ts.map +1 -1
  181. package/dist/types/structured-data/buildWebsiteJsonLd.d.ts.map +1 -1
  182. package/dist/types/structured-data/index.d.ts +2 -1
  183. package/dist/types/structured-data/normalizeJsonLdUrl.d.ts +28 -0
  184. package/dist/types/structured-data/normalizeJsonLdUrl.d.ts.map +1 -0
  185. package/package.json +43 -43
@@ -1 +1 @@
1
- {"version":3,"file":"Code.mjs","names":[],"sources":["../../../../src/components/IDE/Code.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport type { FC, HTMLAttributes, ReactNode } from 'react';\nimport { useEffect, useMemo, useState } from 'react';\nimport type { BundledLanguage } from 'shiki/bundle/web';\nimport { Container } from '../Container';\nimport { ExpandCollapse } from '../ExpandCollapse';\nimport { CodeBlock } from './CodeBlockClient';\nimport { CodeBlockHighlight } from './CodeBlockHighlight';\nimport { CodeConditionalRender } from './CodeConditionalRenderer';\nimport type {\n CodeFormat,\n ContentDeclarationFormat,\n PackageManager,\n} from './CodeContext';\nimport { useCodeContext } from './CodeContext';\nimport { CodeFormatSelector } from './CodeFormatSelector';\nimport { ContentDeclarationFormatSelector } from './ContentDeclarationFormatSelector';\nimport { CopyCode } from './CopyCode';\nimport { PackageManagerSelector } from './PackageManagerSelector';\n\nexport type CodeCompAttributes = {\n fileName?: string;\n packageManager?: PackageManager;\n /** Single format, a JSON-array string, or an array of formats. */\n codeFormat?: CodeFormat | string | string[];\n contentDeclarationFormat?: ContentDeclarationFormat | string | string[];\n};\n\ntype CodeCompProps = {\n children: ReactNode;\n fileName?: string;\n language: BundledLanguage;\n isDarkMode?: boolean;\n showHeader?: boolean;\n showLineNumbers?: boolean;\n isRollable?: boolean;\n} & CodeCompAttributes &\n HTMLAttributes<HTMLDivElement>;\n\nconst MIN_HEIGHT = 700;\n\n/** Languages that use JSX syntax — CommonJS doesn't make sense for these. */\nconst JSX_LANGUAGES = new Set(['tsx', 'jsx']);\n\n/** Parse a codeFormat prop that may be a single value, a JSON-array string, or already an array. */\nconst parseFormats = (\n raw: string | string[] | undefined\n): CodeFormat[] | undefined => {\n if (!raw) return undefined;\n if (Array.isArray(raw)) return raw as CodeFormat[];\n if (typeof raw === 'string' && raw.startsWith('[')) {\n try {\n const parsed = JSON.parse(raw);\n if (Array.isArray(parsed)) return parsed as CodeFormat[];\n } catch {\n /* ignore */\n }\n }\n return [raw as CodeFormat];\n};\n\nexport const Code: FC<CodeCompProps> = ({\n children,\n language,\n isDarkMode,\n showHeader = true,\n showLineNumbers = true,\n className,\n fileName,\n packageManager,\n codeFormat: rawCodeFormat,\n contentDeclarationFormat: rawContentDeclarationFormat,\n isRollable = true,\n lang, // Destructure html lang prop to prevent passing invalid BCP-47 language tag to Code component\n ...props\n}) => {\n const {\n codeFormat: selectedCodeFormat,\n contentDeclarationFormat: selectedContentDeclarationFormat,\n } = useCodeContext();\n\n // Parse whichever attribute is present as an array of formats.\n const codeFormats = useMemo(\n () => parseFormats(rawCodeFormat as string | undefined),\n [rawCodeFormat]\n );\n const contentFormats = useMemo(\n () => parseFormats(rawContentDeclarationFormat as string | undefined),\n [rawContentDeclarationFormat]\n );\n\n // A block is \"multi-format\" when it has multiple formats including TypeScript\n // (the canonical source). Such blocks transform at runtime.\n const isMultiCodeFormat =\n codeFormats !== undefined &&\n codeFormats.length > 1 &&\n codeFormats.includes('typescript');\n\n const isMultiContentFormat =\n contentFormats !== undefined &&\n contentFormats.length > 1 &&\n contentFormats.includes('typescript');\n\n const isMultiFormat = isMultiCodeFormat || isMultiContentFormat;\n\n // Determine which context format drives this block's selection.\n // content declaration blocks use selectedContentDeclarationFormat;\n // regular code blocks use selectedCodeFormat.\n const selectedFormat: CodeFormat = isMultiContentFormat\n ? (selectedContentDeclarationFormat as CodeFormat)\n : selectedCodeFormat;\n\n // The formats actually relevant for transformation (no 'json', no 'commonjs' for JSX).\n const effectiveFormats = useMemo<CodeFormat[] | undefined>(() => {\n const base = isMultiContentFormat ? contentFormats : codeFormats;\n if (!base) return base;\n let filtered = base.filter((f) => f !== 'json') as CodeFormat[];\n if (JSX_LANGUAGES.has(language as string)) {\n filtered = filtered.filter((f) => f !== 'commonjs');\n }\n return filtered;\n }, [isMultiContentFormat, contentFormats, codeFormats, language]);\n\n // When the globally-selected format isn't valid for this block\n // (e.g. CJS selected but this is a JSX file), fall back to the last valid one.\n const resolvedFormat: Exclude<CodeFormat, 'json'> = useMemo(() => {\n if (!effectiveFormats || effectiveFormats.includes(selectedFormat)) {\n return selectedFormat as Exclude<CodeFormat, 'json'>;\n }\n return (effectiveFormats[effectiveFormats.length - 1] ??\n 'typescript') as Exclude<CodeFormat, 'json'>;\n }, [effectiveFormats, selectedFormat]);\n\n // ── Async filename derivation (dynamic import of transformer) ──────────────\n // We derive the displayed fileName so the header updates when format changes.\n // deriveFileName is tiny but it lives inside codeTransformer, which is only\n // imported when actually needed (non-TypeScript selection).\n const [displayedFileName, setDisplayedFileName] = useState<\n string | undefined\n >(fileName);\n\n useEffect(() => {\n if (!isMultiFormat || resolvedFormat === 'typescript') {\n setDisplayedFileName(fileName);\n return;\n }\n if (!fileName) return;\n\n let cancelled = false;\n (async () => {\n const { deriveFileName } = await import('./codeTransformer');\n if (!cancelled)\n setDisplayedFileName(deriveFileName(fileName, resolvedFormat));\n })();\n return () => {\n cancelled = true;\n };\n }, [fileName, isMultiFormat, resolvedFormat]);\n\n // ── Async copy text (transformed code for CopyCode) ───────────────────────\n const rawCode = useMemo(() => {\n const code = Array.isArray(children) ? children.join('') : String(children);\n return code.endsWith('\\n') ? code.slice(0, -1) : code;\n }, [children]);\n\n const [copyCode, setCopyCode] = useState<string>(rawCode);\n\n useEffect(() => {\n if (!isMultiFormat || resolvedFormat === 'typescript') {\n setCopyCode(rawCode);\n return;\n }\n let cancelled = false;\n (async () => {\n const { transformCode } = await import('./codeTransformer');\n if (!cancelled) setCopyCode(transformCode(rawCode, resolvedFormat));\n })();\n return () => {\n cancelled = true;\n };\n }, [rawCode, isMultiFormat, resolvedFormat]);\n\n const hadSelectInHeader =\n packageManager || rawCodeFormat || rawContentDeclarationFormat;\n\n return (\n <CodeConditionalRender\n packageManager={packageManager}\n codeFormat={rawCodeFormat as string | undefined}\n contentDeclarationFormat={\n rawContentDeclarationFormat as string | undefined\n }\n >\n <Container\n className={cn(\n 'relative min-w-0 max-w-full text-sm leading-6',\n showLineNumbers && 'with-line-number ml-0',\n className\n )}\n transparency=\"lg\"\n {...props}\n >\n {showHeader && (\n <>\n <div className=\"grid w-full grid-cols-[1fr_auto] items-center justify-between rounded-t-xl bg-card/50 py-1.5 pr-12 pl-4 text-neutral text-xs\">\n <span className=\"truncate\">{displayedFileName ?? language}</span>\n <div className=\"flex items-center gap-2\">\n {packageManager && <PackageManagerSelector />}\n {rawCodeFormat && (\n <CodeFormatSelector availableFormats={effectiveFormats} />\n )}\n {rawContentDeclarationFormat && (\n <ContentDeclarationFormatSelector />\n )}\n </div>\n </div>\n <div className=\"sticky top-46 z-20\">\n <div\n className={cn(\n 'absolute right-2 bottom-0 flex h-7 items-center',\n hadSelectInHeader && 'h-11'\n )}\n >\n <CopyCode code={copyCode} />\n </div>\n </div>\n </>\n )}\n <ExpandCollapse\n minHeight={MIN_HEIGHT}\n isRollable={isRollable}\n className=\"min-w-0 max-w-full overflow-x-auto p-2\"\n >\n {isMultiFormat ? (\n /*\n * Multi-format: CodeBlockHighlight manages both the transformation\n * (dynamic import of codeTransformer) and the Shiki highlighting in\n * a single useEffect. The previous highlighted output stays visible\n * while the new one loads — no white-text flash.\n */\n <CodeBlockHighlight\n originalLang={language}\n targetFormat={resolvedFormat}\n isDarkMode={isDarkMode}\n >\n {rawCode}\n </CodeBlockHighlight>\n ) : (\n /*\n * Single-format: use the original Suspense-based async Shiki renderer\n * (good for SSR / static content).\n */\n <CodeBlock lang={language} isDarkMode={isDarkMode}>\n {rawCode}\n </CodeBlock>\n )}\n </ExpandCollapse>\n </Container>\n </CodeConditionalRender>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"Code.mjs","names":[],"sources":["../../../../src/components/IDE/Code.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport type { FC, HTMLAttributes, ReactNode } from 'react';\nimport { useEffect, useMemo, useState } from 'react';\nimport { Container } from '../Container';\nimport { ExpandCollapse } from '../ExpandCollapse';\nimport { CodeBlock } from './CodeBlockClient';\nimport { CodeBlockHighlight } from './CodeBlockHighlight';\nimport { CodeConditionalRender } from './CodeConditionalRenderer';\nimport type {\n CodeFormat,\n ContentDeclarationFormat,\n PackageManager,\n} from './CodeContext';\nimport { useCodeContext } from './CodeContext';\nimport { CodeFormatSelector } from './CodeFormatSelector';\nimport { ContentDeclarationFormatSelector } from './ContentDeclarationFormatSelector';\nimport { CopyCode } from './CopyCode';\nimport { PackageManagerSelector } from './PackageManagerSelector';\nimport type { CodeLanguage } from './shikiLanguages';\n\nexport type CodeCompAttributes = {\n fileName?: string;\n packageManager?: PackageManager;\n /** Single format, a JSON-array string, or an array of formats. */\n codeFormat?: CodeFormat | string | string[];\n contentDeclarationFormat?: ContentDeclarationFormat | string | string[];\n};\n\ntype CodeCompProps = {\n children: ReactNode;\n fileName?: string;\n language: CodeLanguage;\n isDarkMode?: boolean;\n showHeader?: boolean;\n showLineNumbers?: boolean;\n isRollable?: boolean;\n} & CodeCompAttributes &\n HTMLAttributes<HTMLDivElement>;\n\nconst MIN_HEIGHT = 700;\n\n/** Languages that use JSX syntax — CommonJS doesn't make sense for these. */\nconst JSX_LANGUAGES = new Set(['tsx', 'jsx']);\n\n/** Parse a codeFormat prop that may be a single value, a JSON-array string, or already an array. */\nconst parseFormats = (\n raw: string | string[] | undefined\n): CodeFormat[] | undefined => {\n if (!raw) return undefined;\n if (Array.isArray(raw)) return raw as CodeFormat[];\n if (typeof raw === 'string' && raw.startsWith('[')) {\n try {\n const parsed = JSON.parse(raw);\n if (Array.isArray(parsed)) return parsed as CodeFormat[];\n } catch {\n /* ignore */\n }\n }\n return [raw as CodeFormat];\n};\n\nexport const Code: FC<CodeCompProps> = ({\n children,\n language,\n isDarkMode,\n showHeader = true,\n showLineNumbers = true,\n className,\n fileName,\n packageManager,\n codeFormat: rawCodeFormat,\n contentDeclarationFormat: rawContentDeclarationFormat,\n isRollable = true,\n lang, // Destructure html lang prop to prevent passing invalid BCP-47 language tag to Code component\n ...props\n}) => {\n const {\n codeFormat: selectedCodeFormat,\n contentDeclarationFormat: selectedContentDeclarationFormat,\n } = useCodeContext();\n\n // Parse whichever attribute is present as an array of formats.\n const codeFormats = useMemo(\n () => parseFormats(rawCodeFormat as string | undefined),\n [rawCodeFormat]\n );\n const contentFormats = useMemo(\n () => parseFormats(rawContentDeclarationFormat as string | undefined),\n [rawContentDeclarationFormat]\n );\n\n // A block is \"multi-format\" when it has multiple formats including TypeScript\n // (the canonical source). Such blocks transform at runtime.\n const isMultiCodeFormat =\n codeFormats !== undefined &&\n codeFormats.length > 1 &&\n codeFormats.includes('typescript');\n\n const isMultiContentFormat =\n contentFormats !== undefined &&\n contentFormats.length > 1 &&\n contentFormats.includes('typescript');\n\n const isMultiFormat = isMultiCodeFormat || isMultiContentFormat;\n\n // Determine which context format drives this block's selection.\n // content declaration blocks use selectedContentDeclarationFormat;\n // regular code blocks use selectedCodeFormat.\n const selectedFormat: CodeFormat = isMultiContentFormat\n ? (selectedContentDeclarationFormat as CodeFormat)\n : selectedCodeFormat;\n\n // The formats actually relevant for transformation (no 'json', no 'commonjs' for JSX).\n const effectiveFormats = useMemo<CodeFormat[] | undefined>(() => {\n const base = isMultiContentFormat ? contentFormats : codeFormats;\n if (!base) return base;\n let filtered = base.filter((f) => f !== 'json') as CodeFormat[];\n if (JSX_LANGUAGES.has(language as string)) {\n filtered = filtered.filter((f) => f !== 'commonjs');\n }\n return filtered;\n }, [isMultiContentFormat, contentFormats, codeFormats, language]);\n\n // When the globally-selected format isn't valid for this block\n // (e.g. CJS selected but this is a JSX file), fall back to the last valid one.\n const resolvedFormat: Exclude<CodeFormat, 'json'> = useMemo(() => {\n if (!effectiveFormats || effectiveFormats.includes(selectedFormat)) {\n return selectedFormat as Exclude<CodeFormat, 'json'>;\n }\n return (effectiveFormats[effectiveFormats.length - 1] ??\n 'typescript') as Exclude<CodeFormat, 'json'>;\n }, [effectiveFormats, selectedFormat]);\n\n // ── Async filename derivation (dynamic import of transformer) ──────────────\n // We derive the displayed fileName so the header updates when format changes.\n // deriveFileName is tiny but it lives inside codeTransformer, which is only\n // imported when actually needed (non-TypeScript selection).\n const [displayedFileName, setDisplayedFileName] = useState<\n string | undefined\n >(fileName);\n\n useEffect(() => {\n if (!isMultiFormat || resolvedFormat === 'typescript') {\n setDisplayedFileName(fileName);\n return;\n }\n if (!fileName) return;\n\n let cancelled = false;\n (async () => {\n const { deriveFileName } = await import('./codeTransformer');\n if (!cancelled)\n setDisplayedFileName(deriveFileName(fileName, resolvedFormat));\n })();\n return () => {\n cancelled = true;\n };\n }, [fileName, isMultiFormat, resolvedFormat]);\n\n // ── Async copy text (transformed code for CopyCode) ───────────────────────\n const rawCode = useMemo(() => {\n const code = Array.isArray(children) ? children.join('') : String(children);\n return code.endsWith('\\n') ? code.slice(0, -1) : code;\n }, [children]);\n\n const [copyCode, setCopyCode] = useState<string>(rawCode);\n\n useEffect(() => {\n if (!isMultiFormat || resolvedFormat === 'typescript') {\n setCopyCode(rawCode);\n return;\n }\n let cancelled = false;\n (async () => {\n const { transformCode } = await import('./codeTransformer');\n if (!cancelled) setCopyCode(transformCode(rawCode, resolvedFormat));\n })();\n return () => {\n cancelled = true;\n };\n }, [rawCode, isMultiFormat, resolvedFormat]);\n\n const hadSelectInHeader =\n packageManager || rawCodeFormat || rawContentDeclarationFormat;\n\n return (\n <CodeConditionalRender\n packageManager={packageManager}\n codeFormat={rawCodeFormat as string | undefined}\n contentDeclarationFormat={\n rawContentDeclarationFormat as string | undefined\n }\n >\n <Container\n className={cn(\n 'relative min-w-0 max-w-full text-sm leading-6',\n showLineNumbers && 'with-line-number ml-0',\n className\n )}\n transparency=\"lg\"\n {...props}\n >\n {showHeader && (\n <>\n <div className=\"grid w-full grid-cols-[1fr_auto] items-center justify-between rounded-t-xl bg-card/50 py-1.5 pr-12 pl-4 text-neutral text-xs\">\n <span className=\"truncate\">{displayedFileName ?? language}</span>\n <div className=\"flex items-center gap-2\">\n {packageManager && <PackageManagerSelector />}\n {rawCodeFormat && (\n <CodeFormatSelector availableFormats={effectiveFormats} />\n )}\n {rawContentDeclarationFormat && (\n <ContentDeclarationFormatSelector />\n )}\n </div>\n </div>\n <div className=\"sticky top-46 z-20\">\n <div\n className={cn(\n 'absolute right-2 bottom-0 flex h-7 items-center',\n hadSelectInHeader && 'h-11'\n )}\n >\n <CopyCode code={copyCode} />\n </div>\n </div>\n </>\n )}\n <ExpandCollapse\n minHeight={MIN_HEIGHT}\n isRollable={isRollable}\n className=\"min-w-0 max-w-full overflow-x-auto p-2\"\n >\n {isMultiFormat ? (\n /*\n * Multi-format: CodeBlockHighlight manages both the transformation\n * (dynamic import of codeTransformer) and the Shiki highlighting in\n * a single useEffect. The previous highlighted output stays visible\n * while the new one loads — no white-text flash.\n */\n <CodeBlockHighlight\n originalLang={language}\n targetFormat={resolvedFormat}\n isDarkMode={isDarkMode}\n >\n {rawCode}\n </CodeBlockHighlight>\n ) : (\n /*\n * Single-format: use the original Suspense-based async Shiki renderer\n * (good for SSR / static content).\n */\n <CodeBlock lang={language} isDarkMode={isDarkMode}>\n {rawCode}\n </CodeBlock>\n )}\n </ExpandCollapse>\n </Container>\n </CodeConditionalRender>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlockClient.mjs","names":[],"sources":["../../../../src/components/IDE/CodeBlockClient.tsx"],"sourcesContent":["import { cn } from '@utils/cn';\nimport {\n type FC,\n type HTMLAttributes,\n lazy,\n type ReactNode,\n Suspense,\n} from 'react';\nimport type { BundledLanguage } from 'shiki/bundle/web';\n\nexport type { BundledLanguage as CodeLanguage } from 'shiki/bundle/web';\n\nexport const CodeDefault: FC<{ children: ReactNode }> = ({ children }) => (\n <div className=\"min-w-0 max-w-full overflow-x-auto\">\n <pre className=\"min-w-0 max-w-full overflow-x-auto [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\">\n <code>\n {typeof children === 'string'\n ? children.split('\\n').map((line, index) => (\n <span\n className=\"line block w-full\"\n key={`line-${index}-${line.slice(0, 10)}`}\n >\n {line}\n </span>\n ))\n : children}\n </code>\n </pre>\n </div>\n);\n\n// Lazy load the Shiki component\nconst CodeBlockShiki = lazy(() =>\n import('./CodeBlockShiki').then((mod) => ({\n default: mod.CodeBlockShiki,\n }))\n);\n\nexport type CodeBlockProps = {\n children: React.ReactNode;\n lang: BundledLanguage;\n isDarkMode?: boolean;\n isEditable?: boolean;\n onChange?: (content: string) => void;\n} & Omit<HTMLAttributes<HTMLDivElement>, 'onChange'>;\n\nexport const CodeBlock: FC<CodeBlockProps> = ({\n className,\n onChange,\n isEditable,\n children,\n lang,\n isDarkMode,\n ...props\n}) => (\n <div\n className={cn('flex w-full min-w-0 max-w-full overflow-x-auto', className)}\n {...props}\n >\n <Suspense fallback={<CodeDefault>{children}</CodeDefault>}>\n <CodeBlockShiki lang={lang} isDarkMode={isDarkMode}>\n {children}\n </CodeBlockShiki>\n </Suspense>\n </div>\n);\n"],"mappings":";;;;;AAYA,MAAa,eAA4C,EAAE,eACzD,oBAAC,OAAD;CAAK,WAAU;WACb,oBAAC,OAAD;EAAK,WAAU;YACb,oBAAC,QAAD,YACG,OAAO,aAAa,WACjB,SAAS,MAAM,IAAI,CAAC,CAAC,KAAK,MAAM,UAC9B,oBAAC,QAAD;GACE,WAAU;aAGT;EACG,GAHC,QAAQ,MAAM,GAAG,KAAK,MAAM,GAAG,EAAE,GAGlC,CACP,IACD,SACA;CACH;AACF;AAIP,MAAM,iBAAiB,WACrB,OAAO,uBAAmB,CAAC,MAAM,SAAS,EACxC,SAAS,IAAI,eACf,EAAE,CACJ;AAUA,MAAa,aAAiC,EAC5C,WACA,UACA,YACA,UACA,MACA,YACA,GAAG,YAEH,oBAAC,OAAD;CACE,WAAW,GAAG,kDAAkD,SAAS;CACzE,GAAI;WAEJ,oBAAC,UAAD;EAAU,UAAU,oBAAC,aAAD,EAAc,SAAsB;YACtD,oBAAC,gBAAD;GAAsB;GAAkB;GACrC;EACa;CACR;AACP"}
1
+ {"version":3,"file":"CodeBlockClient.mjs","names":[],"sources":["../../../../src/components/IDE/CodeBlockClient.tsx"],"sourcesContent":["import { cn } from '@utils/cn';\nimport {\n type FC,\n type HTMLAttributes,\n lazy,\n type ReactNode,\n Suspense,\n} from 'react';\nimport type { CodeLanguage } from './shikiLanguages';\n\nexport type { CodeLanguage } from './shikiLanguages';\n\nexport const CodeDefault: FC<{ children: ReactNode }> = ({ children }) => (\n <div className=\"min-w-0 max-w-full overflow-x-auto\">\n <pre className=\"min-w-0 max-w-full overflow-x-auto [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\">\n <code>\n {typeof children === 'string'\n ? children.split('\\n').map((line, index) => (\n <span\n className=\"line block w-full\"\n key={`line-${index}-${line.slice(0, 10)}`}\n >\n {line}\n </span>\n ))\n : children}\n </code>\n </pre>\n </div>\n);\n\n// Lazy load the Shiki component\nconst CodeBlockShiki = lazy(() =>\n import('./CodeBlockShiki').then((mod) => ({\n default: mod.CodeBlockShiki,\n }))\n);\n\nexport type CodeBlockProps = {\n children: React.ReactNode;\n lang: CodeLanguage;\n isDarkMode?: boolean;\n isEditable?: boolean;\n onChange?: (content: string) => void;\n} & Omit<HTMLAttributes<HTMLDivElement>, 'onChange'>;\n\nexport const CodeBlock: FC<CodeBlockProps> = ({\n className,\n onChange,\n isEditable,\n children,\n lang,\n isDarkMode,\n ...props\n}) => (\n <div\n className={cn('flex w-full min-w-0 max-w-full overflow-x-auto', className)}\n {...props}\n >\n <Suspense fallback={<CodeDefault>{children}</CodeDefault>}>\n <CodeBlockShiki lang={lang} isDarkMode={isDarkMode}>\n {children}\n </CodeBlockShiki>\n </Suspense>\n </div>\n);\n"],"mappings":";;;;;AAYA,MAAa,eAA4C,EAAE,eACzD,oBAAC,OAAD;CAAK,WAAU;WACb,oBAAC,OAAD;EAAK,WAAU;YACb,oBAAC,QAAD,YACG,OAAO,aAAa,WACjB,SAAS,MAAM,IAAI,CAAC,CAAC,KAAK,MAAM,UAC9B,oBAAC,QAAD;GACE,WAAU;aAGT;EACG,GAHC,QAAQ,MAAM,GAAG,KAAK,MAAM,GAAG,EAAE,GAGlC,CACP,IACD,SACA;CACH;AACF;AAIP,MAAM,iBAAiB,WACrB,OAAO,uBAAmB,CAAC,MAAM,SAAS,EACxC,SAAS,IAAI,eACf,EAAE,CACJ;AAUA,MAAa,aAAiC,EAC5C,WACA,UACA,YACA,UACA,MACA,YACA,GAAG,YAEH,oBAAC,OAAD;CACE,WAAW,GAAG,kDAAkD,SAAS;CACzE,GAAI;WAEJ,oBAAC,UAAD;EAAU,UAAU,oBAAC,aAAD,EAAc,SAAsB;YACtD,oBAAC,gBAAD;GAAsB;GAAkB;GACrC;EACa;CACR;AACP"}
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import { resolveCodeLanguage } from "./shikiLanguages.mjs";
3
4
  import { useEffect, useRef, useState } from "react";
4
5
  import { jsx } from "react/jsx-runtime";
5
6
 
@@ -11,18 +12,6 @@ import { jsx } from "react/jsx-runtime";
11
12
  * - The transformer is only dynamically imported when a non-TypeScript format is selected.
12
13
  * - The previous highlighted HTML stays visible while the new one loads (no white-text flash).
13
14
  */
14
- /**
15
- * Map display language names to Shiki grammar identifiers.
16
- * Shiki's web bundle does not ship a separate 'jsx' grammar — tsx handles both.
17
- */
18
- const toShikiLang = (lang) => {
19
- switch (lang) {
20
- case "jsx": return "tsx";
21
- case "mjs":
22
- case "cjs": return "javascript";
23
- default: return lang;
24
- }
25
- };
26
15
  const CodeBlockHighlight = ({ children, originalLang, targetFormat, isDarkMode }) => {
27
16
  const [html, setHtml] = useState(null);
28
17
  const prevHtml = useRef(null);
@@ -31,12 +20,12 @@ const CodeBlockHighlight = ({ children, originalLang, targetFormat, isDarkMode }
31
20
  (async () => {
32
21
  try {
33
22
  let code = children;
34
- let shikiLang = toShikiLang(originalLang);
23
+ let shikiLang = resolveCodeLanguage(originalLang).id;
35
24
  if (targetFormat !== "typescript") {
36
25
  const { transformCode, deriveLanguage } = await import("./codeTransformer.mjs");
37
26
  if (cancelled) return;
38
27
  code = transformCode(String(children), targetFormat);
39
- shikiLang = toShikiLang(deriveLanguage(originalLang, targetFormat));
28
+ shikiLang = resolveCodeLanguage(deriveLanguage(originalLang, targetFormat)).id;
40
29
  }
41
30
  const { codeToHtml } = await import("shiki/bundle/web");
42
31
  if (cancelled) return;
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlockHighlight.mjs","names":[],"sources":["../../../../src/components/IDE/CodeBlockHighlight.tsx"],"sourcesContent":["'use client';\n\n/**\n * Client-side Shiki highlighter that also handles TypeScript→ESM/CJS transformation.\n *\n * Everything runs inside a single useEffect so that:\n * - The transformer is only dynamically imported when a non-TypeScript format is selected.\n * - The previous highlighted HTML stays visible while the new one loads (no white-text flash).\n */\n\nimport { type ReactNode, useEffect, useRef, useState } from 'react';\nimport type { BundledLanguage } from 'shiki/bundle/web';\nimport type { CodeFormat } from './CodeContext';\n\ntype Props = {\n /** Raw TypeScript source code (the canonical \"source of truth\"). */\n children: ReactNode;\n /** Language of the source (e.g. 'tsx', 'typescript'). */\n originalLang: BundledLanguage;\n /** Currently selected format: 'typescript' | 'esm' | 'commonjs'. */\n targetFormat: Exclude<CodeFormat, 'json'>;\n isDarkMode?: boolean;\n};\n\n/**\n * Map display language names to Shiki grammar identifiers.\n * Shiki's web bundle does not ship a separate 'jsx' grammar — tsx handles both.\n */\nconst toShikiLang = (lang: string): string => {\n switch (lang) {\n case 'jsx':\n return 'tsx';\n case 'mjs':\n case 'cjs':\n return 'javascript';\n default:\n return lang;\n }\n};\n\nexport const CodeBlockHighlight = ({\n children,\n originalLang,\n targetFormat,\n isDarkMode,\n}: Props) => {\n const [html, setHtml] = useState<string | null>(null);\n const prevHtml = useRef<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n\n (async () => {\n try {\n let code = children;\n let shikiLang = toShikiLang(originalLang);\n\n // Only import the transformer when we actually need it.\n if (targetFormat !== 'typescript') {\n const { transformCode, deriveLanguage } = await import(\n './codeTransformer'\n );\n if (cancelled) return;\n code = transformCode(String(children), targetFormat);\n shikiLang = toShikiLang(deriveLanguage(originalLang, targetFormat));\n }\n\n const { codeToHtml } = await import('shiki/bundle/web');\n if (cancelled) return;\n\n const out = await codeToHtml(String(code), {\n lang: shikiLang,\n theme: isDarkMode ? 'github-dark' : 'github-light',\n });\n\n if (!cancelled) {\n prevHtml.current = out;\n setHtml(out);\n }\n } catch {\n // Shiki failed (unknown language, etc.) — fall through to plain-text.\n if (!cancelled) setHtml('');\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [children, originalLang, targetFormat, isDarkMode]);\n\n // Keep the previous highlighted output visible while the new one is loading.\n // This prevents the white-text flash on format switches.\n const display = html ?? prevHtml.current;\n\n if (!display) {\n return (\n <pre className=\"min-w-0 max-w-full overflow-x-auto\">\n <code>{children}</code>\n </pre>\n );\n }\n\n return (\n <div\n dangerouslySetInnerHTML={{ __html: display }}\n style={{ backgroundColor: 'transparent' }}\n />\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AA4BA,MAAM,eAAe,SAAyB;CAC5C,QAAQ,MAAR;EACE,KAAK,OACH,OAAO;EACT,KAAK;EACL,KAAK,OACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAEA,MAAa,sBAAsB,EACjC,UACA,cACA,cACA,iBACW;CACX,MAAM,CAAC,MAAM,WAAW,SAAwB,IAAI;CACpD,MAAM,WAAW,OAAsB,IAAI;CAE3C,gBAAgB;EACd,IAAI,YAAY;EAEhB,CAAC,YAAY;GACX,IAAI;IACF,IAAI,OAAO;IACX,IAAI,YAAY,YAAY,YAAY;IAGxC,IAAI,iBAAiB,cAAc;KACjC,MAAM,EAAE,eAAe,mBAAmB,MAAM,OAC9C;KAEF,IAAI,WAAW;KACf,OAAO,cAAc,OAAO,QAAQ,GAAG,YAAY;KACnD,YAAY,YAAY,eAAe,cAAc,YAAY,CAAC;IACpE;IAEA,MAAM,EAAE,eAAe,MAAM,OAAO;IACpC,IAAI,WAAW;IAEf,MAAM,MAAM,MAAM,WAAW,OAAO,IAAI,GAAG;KACzC,MAAM;KACN,OAAO,aAAa,gBAAgB;IACtC,CAAC;IAED,IAAI,CAAC,WAAW;KACd,SAAS,UAAU;KACnB,QAAQ,GAAG;IACb;GACF,QAAQ;IAEN,IAAI,CAAC,WAAW,QAAQ,EAAE;GAC5B;EACF,EAAC,CAAE;EAEH,aAAa;GACX,YAAY;EACd;CACF,GAAG;EAAC;EAAU;EAAc;EAAc;CAAU,CAAC;CAIrD,MAAM,UAAU,QAAQ,SAAS;CAEjC,IAAI,CAAC,SACH,OACE,oBAAC,OAAD;EAAK,WAAU;YACb,oBAAC,QAAD,EAAO,SAAe;CACnB;CAIT,OACE,oBAAC,OAAD;EACE,yBAAyB,EAAE,QAAQ,QAAQ;EAC3C,OAAO,EAAE,iBAAiB,cAAc;CACzC;AAEL"}
1
+ {"version":3,"file":"CodeBlockHighlight.mjs","names":[],"sources":["../../../../src/components/IDE/CodeBlockHighlight.tsx"],"sourcesContent":["'use client';\n\n/**\n * Client-side Shiki highlighter that also handles TypeScript→ESM/CJS transformation.\n *\n * Everything runs inside a single useEffect so that:\n * - The transformer is only dynamically imported when a non-TypeScript format is selected.\n * - The previous highlighted HTML stays visible while the new one loads (no white-text flash).\n */\n\nimport { type ReactNode, useEffect, useRef, useState } from 'react';\nimport type { CodeFormat } from './CodeContext';\nimport { type CodeLanguage, resolveCodeLanguage } from './shikiLanguages';\n\ntype Props = {\n /** Raw TypeScript source code (the canonical \"source of truth\"). */\n children: ReactNode;\n /** Language of the source (e.g. 'tsx', 'typescript'). */\n originalLang: CodeLanguage;\n /** Currently selected format: 'typescript' | 'esm' | 'commonjs'. */\n targetFormat: Exclude<CodeFormat, 'json'>;\n isDarkMode?: boolean;\n};\n\nexport const CodeBlockHighlight = ({\n children,\n originalLang,\n targetFormat,\n isDarkMode,\n}: Props) => {\n const [html, setHtml] = useState<string | null>(null);\n const prevHtml = useRef<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n\n (async () => {\n try {\n let code = children;\n let shikiLang = resolveCodeLanguage(originalLang).id;\n\n // Only import the transformer when we actually need it.\n if (targetFormat !== 'typescript') {\n const { transformCode, deriveLanguage } = await import(\n './codeTransformer'\n );\n if (cancelled) return;\n code = transformCode(String(children), targetFormat);\n shikiLang = resolveCodeLanguage(\n deriveLanguage(originalLang, targetFormat)\n ).id;\n }\n\n const { codeToHtml } = await import('shiki/bundle/web');\n if (cancelled) return;\n\n const out = await codeToHtml(String(code), {\n lang: shikiLang,\n theme: isDarkMode ? 'github-dark' : 'github-light',\n });\n\n if (!cancelled) {\n prevHtml.current = out;\n setHtml(out);\n }\n } catch {\n // Shiki failed (unknown language, etc.) — fall through to plain-text.\n if (!cancelled) setHtml('');\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [children, originalLang, targetFormat, isDarkMode]);\n\n // Keep the previous highlighted output visible while the new one is loading.\n // This prevents the white-text flash on format switches.\n const display = html ?? prevHtml.current;\n\n if (!display) {\n return (\n <pre className=\"min-w-0 max-w-full overflow-x-auto\">\n <code>{children}</code>\n </pre>\n );\n }\n\n return (\n <div\n dangerouslySetInnerHTML={{ __html: display }}\n style={{ backgroundColor: 'transparent' }}\n />\n );\n};\n"],"mappings":";;;;;;;;;;;;;;AAwBA,MAAa,sBAAsB,EACjC,UACA,cACA,cACA,iBACW;CACX,MAAM,CAAC,MAAM,WAAW,SAAwB,IAAI;CACpD,MAAM,WAAW,OAAsB,IAAI;CAE3C,gBAAgB;EACd,IAAI,YAAY;EAEhB,CAAC,YAAY;GACX,IAAI;IACF,IAAI,OAAO;IACX,IAAI,YAAY,oBAAoB,YAAY,CAAC,CAAC;IAGlD,IAAI,iBAAiB,cAAc;KACjC,MAAM,EAAE,eAAe,mBAAmB,MAAM,OAC9C;KAEF,IAAI,WAAW;KACf,OAAO,cAAc,OAAO,QAAQ,GAAG,YAAY;KACnD,YAAY,oBACV,eAAe,cAAc,YAAY,CAC3C,CAAC,CAAC;IACJ;IAEA,MAAM,EAAE,eAAe,MAAM,OAAO;IACpC,IAAI,WAAW;IAEf,MAAM,MAAM,MAAM,WAAW,OAAO,IAAI,GAAG;KACzC,MAAM;KACN,OAAO,aAAa,gBAAgB;IACtC,CAAC;IAED,IAAI,CAAC,WAAW;KACd,SAAS,UAAU;KACnB,QAAQ,GAAG;IACb;GACF,QAAQ;IAEN,IAAI,CAAC,WAAW,QAAQ,EAAE;GAC5B;EACF,EAAC,CAAE;EAEH,aAAa;GACX,YAAY;EACd;CACF,GAAG;EAAC;EAAU;EAAc;EAAc;CAAU,CAAC;CAIrD,MAAM,UAAU,QAAQ,SAAS;CAEjC,IAAI,CAAC,SACH,OACE,oBAAC,OAAD;EAAK,WAAU;YACb,oBAAC,QAAD,EAAO,SAAe;CACnB;CAIT,OACE,oBAAC,OAAD;EACE,yBAAyB,EAAE,QAAQ,QAAQ;EAC3C,OAAO,EAAE,iBAAiB,cAAc;CACzC;AAEL"}
@@ -6,19 +6,21 @@ import { codeToHtml } from "shiki/bundle/web";
6
6
 
7
7
  //#region src/components/IDE/CodeBlockServer.tsx
8
8
  const CodeBlockShiki = (async ({ children, lang, isDarkMode, onChange, ...props }) => {
9
+ const shikiOptions = {
10
+ lang,
11
+ theme: isDarkMode ? "github-dark" : "github-light",
12
+ transformers: [
13
+ transformerNotationDiff(),
14
+ transformerNotationHighlight(),
15
+ transformerNotationWordHighlight(),
16
+ transformerNotationErrorLevel(),
17
+ transformerMetaHighlight(),
18
+ transformerMetaWordHighlight()
19
+ ]
20
+ };
21
+ const out = await codeToHtml(children, shikiOptions);
9
22
  return /* @__PURE__ */ jsx("div", {
10
- dangerouslySetInnerHTML: { __html: await codeToHtml(children, {
11
- lang,
12
- theme: isDarkMode ? "github-dark" : "github-light",
13
- transformers: [
14
- transformerNotationDiff(),
15
- transformerNotationHighlight(),
16
- transformerNotationWordHighlight(),
17
- transformerNotationErrorLevel(),
18
- transformerMetaHighlight(),
19
- transformerMetaWordHighlight()
20
- ]
21
- }) },
23
+ dangerouslySetInnerHTML: { __html: out },
22
24
  ...props,
23
25
  style: { backgroundColor: "transparent" }
24
26
  });
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlockServer.mjs","names":[],"sources":["../../../../src/components/IDE/CodeBlockServer.tsx"],"sourcesContent":["import {\n transformerMetaHighlight,\n transformerMetaWordHighlight,\n transformerNotationDiff,\n transformerNotationErrorLevel,\n transformerNotationHighlight,\n transformerNotationWordHighlight,\n} from '@shikijs/transformers';\nimport { cn } from '@utils/cn';\nimport { type FC, type HTMLAttributes, Suspense } from 'react';\nimport {\n type BundledLanguage,\n type BundledTheme,\n type CodeToHastOptions,\n codeToHtml,\n} from 'shiki/bundle/web';\n\nexport const CodeBlockShiki = (async ({\n children,\n lang,\n isDarkMode,\n onChange,\n ...props\n}: CodeBlockProps) => {\n const shikiOptions: CodeToHastOptions<BundledLanguage, BundledTheme> = {\n lang,\n theme: isDarkMode ? 'github-dark' : 'github-light',\n transformers: [\n transformerNotationDiff(),\n transformerNotationHighlight(),\n transformerNotationWordHighlight(),\n transformerNotationErrorLevel(),\n transformerMetaHighlight(),\n transformerMetaWordHighlight(),\n ],\n };\n\n const out = await codeToHtml(children, shikiOptions);\n\n return (\n <div\n dangerouslySetInnerHTML={{ __html: out }}\n {...props}\n style={{ backgroundColor: 'transparent' }}\n />\n );\n}) as unknown as FC<CodeBlockProps>;\n\nconst CodeDefault: FC<CodeBlockProps> = ({\n children,\n isEditable,\n isDarkMode,\n onChange,\n ...props\n}) => (\n <div contentEditable={isEditable} {...props}>\n <pre>\n <code>\n {typeof children === 'string'\n ? children.split('\\n').map((line, index) => (\n <span className=\"line block w-full\" key={index}>\n {line}\n </span>\n ))\n : children}\n </code>\n </pre>\n </div>\n);\n\nexport type CodeBlockProps = {\n children: string;\n lang: BundledLanguage;\n isDarkMode?: boolean;\n isEditable?: boolean;\n onChange?: (content: string) => void;\n} & Omit<HTMLAttributes<HTMLDivElement>, 'onChange'>;\n\nexport const CodeBlock: FC<CodeBlockProps> = ({\n className,\n onChange,\n isEditable,\n ...props\n}) => (\n <Suspense fallback={<CodeDefault {...props} />}>\n <CodeBlockShiki\n className={cn('flex w-full', className)}\n contentEditable={isEditable}\n onInput={(e) => onChange?.(e.currentTarget.textContent ?? '')}\n {...props}\n />\n </Suspense>\n);\n"],"mappings":";;;;;;;AAiBA,MAAa,kBAAkB,OAAO,EACpC,UACA,MACA,YACA,UACA,GAAG,YACiB;CAgBpB,OACE,oBAAC,OAAD;EACE,yBAAyB,EAAE,QAAQ,MAJrB,WAAW,UAAU;GAZrC;GACA,OAAO,aAAa,gBAAgB;GACpC,cAAc;IACZ,wBAAwB;IACxB,6BAA6B;IAC7B,iCAAiC;IACjC,8BAA8B;IAC9B,yBAAyB;IACzB,6BAA6B;GAC/B;EAGgD,CAAC,EAIR;EACvC,GAAI;EACJ,OAAO,EAAE,iBAAiB,cAAc;CACzC;AAEL;AAEA,MAAM,eAAmC,EACvC,UACA,YACA,YACA,UACA,GAAG,YAEH,oBAAC,OAAD;CAAK,iBAAiB;CAAY,GAAI;WACpC,oBAAC,OAAD,YACE,oBAAC,QAAD,YACG,OAAO,aAAa,WACjB,SAAS,MAAM,IAAI,CAAC,CAAC,KAAK,MAAM,UAC9B,oBAAC,QAAD;EAAM,WAAU;YACb;CACG,GAFmC,KAEnC,CACP,IACD,SACA,GACH;AACF;AAWP,MAAa,aAAiC,EAC5C,WACA,UACA,YACA,GAAG,YAEH,oBAAC,UAAD;CAAU,UAAU,oBAAC,aAAD,EAAa,GAAI,MAAQ;WAC3C,oBAAC,gBAAD;EACE,WAAW,GAAG,eAAe,SAAS;EACtC,iBAAiB;EACjB,UAAU,MAAM,WAAW,EAAE,cAAc,eAAe,EAAE;EAC5D,GAAI;CACL;AACO"}
1
+ {"version":3,"file":"CodeBlockServer.mjs","names":[],"sources":["../../../../src/components/IDE/CodeBlockServer.tsx"],"sourcesContent":["import {\n transformerMetaHighlight,\n transformerMetaWordHighlight,\n transformerNotationDiff,\n transformerNotationErrorLevel,\n transformerNotationHighlight,\n transformerNotationWordHighlight,\n} from '@shikijs/transformers';\nimport { cn } from '@utils/cn';\nimport { type FC, type HTMLAttributes, Suspense } from 'react';\nimport {\n type BundledLanguage,\n type BundledTheme,\n type CodeToHastOptions,\n codeToHtml,\n} from 'shiki/bundle/web';\n\nexport const CodeBlockShiki = (async ({\n children,\n lang,\n isDarkMode,\n onChange,\n ...props\n}: CodeBlockProps) => {\n const shikiOptions: CodeToHastOptions<BundledLanguage, BundledTheme> = {\n lang,\n theme: isDarkMode ? 'github-dark' : 'github-light',\n transformers: [\n transformerNotationDiff(),\n transformerNotationHighlight(),\n transformerNotationWordHighlight(),\n transformerNotationErrorLevel(),\n transformerMetaHighlight(),\n transformerMetaWordHighlight(),\n ],\n };\n\n const out = await codeToHtml(children, shikiOptions);\n\n return (\n <div\n dangerouslySetInnerHTML={{ __html: out }}\n {...props}\n style={{ backgroundColor: 'transparent' }}\n />\n );\n}) as unknown as FC<CodeBlockProps>;\n\nconst CodeDefault: FC<CodeBlockProps> = ({\n children,\n isEditable,\n isDarkMode,\n onChange,\n ...props\n}) => (\n <div contentEditable={isEditable} {...props}>\n <pre>\n <code>\n {typeof children === 'string'\n ? children.split('\\n').map((line, index) => (\n <span className=\"line block w-full\" key={index}>\n {line}\n </span>\n ))\n : children}\n </code>\n </pre>\n </div>\n);\n\nexport type CodeBlockProps = {\n children: string;\n lang: BundledLanguage;\n isDarkMode?: boolean;\n isEditable?: boolean;\n onChange?: (content: string) => void;\n} & Omit<HTMLAttributes<HTMLDivElement>, 'onChange'>;\n\nexport const CodeBlock: FC<CodeBlockProps> = ({\n className,\n onChange,\n isEditable,\n ...props\n}) => (\n <Suspense fallback={<CodeDefault {...props} />}>\n <CodeBlockShiki\n className={cn('flex w-full', className)}\n contentEditable={isEditable}\n onInput={(e) => onChange?.(e.currentTarget.textContent ?? '')}\n {...props}\n />\n </Suspense>\n);\n"],"mappings":";;;;;;;AAiBA,MAAa,kBAAkB,OAAO,EACpC,UACA,MACA,YACA,UACA,GAAG,YACiB;CACpB,MAAM,eAAiE;EACrE;EACA,OAAO,aAAa,gBAAgB;EACpC,cAAc;GACZ,wBAAwB;GACxB,6BAA6B;GAC7B,iCAAiC;GACjC,8BAA8B;GAC9B,yBAAyB;GACzB,6BAA6B;EAC/B;CACF;CAEA,MAAM,MAAM,MAAM,WAAW,UAAU,YAAY;CAEnD,OACE,oBAAC,OAAD;EACE,yBAAyB,EAAE,QAAQ,IAAI;EACvC,GAAI;EACJ,OAAO,EAAE,iBAAiB,cAAc;CACzC;AAEL;AAEA,MAAM,eAAmC,EACvC,UACA,YACA,YACA,UACA,GAAG,YAEH,oBAAC,OAAD;CAAK,iBAAiB;CAAY,GAAI;WACpC,oBAAC,OAAD,YACE,oBAAC,QAAD,YACG,OAAO,aAAa,WACjB,SAAS,MAAM,IAAI,CAAC,CAAC,KAAK,MAAM,UAC9B,oBAAC,QAAD;EAAM,WAAU;YACb;CACG,GAFmC,KAEnC,CACP,IACD,SACA,GACH;AACF;AAWP,MAAa,aAAiC,EAC5C,WACA,UACA,YACA,GAAG,YAEH,oBAAC,UAAD;CAAU,UAAU,oBAAC,aAAD,EAAa,GAAI,MAAQ;WAC3C,oBAAC,gBAAD;EACE,WAAW,GAAG,eAAe,SAAS;EACtC,iBAAiB;EACjB,UAAU,MAAM,WAAW,EAAE,cAAc,eAAe,EAAE;EAC5D,GAAI;CACL;AACO"}
@@ -1,97 +1,36 @@
1
1
  'use client';
2
2
 
3
3
  import { CodeDefault } from "./CodeBlockClient.mjs";
4
+ import { resolveCodeLanguage } from "./shikiLanguages.mjs";
4
5
  import { useEffect, useState } from "react";
5
6
  import { jsx } from "react/jsx-runtime";
6
7
 
7
8
  //#region src/components/IDE/CodeBlockShiki.tsx
8
9
  const languageCache = /* @__PURE__ */ new Map();
9
10
  const themeCache = /* @__PURE__ */ new Map();
10
- const loadLanguage = async (lang) => {
11
- if (languageCache.has(lang)) return languageCache.get(lang);
12
- let languageModule;
13
- switch (lang) {
14
- case "angular-html":
15
- languageModule = await import("shiki/langs/angular-html.mjs");
16
- break;
17
- case "angular-ts":
18
- languageModule = await import("shiki/langs/angular-ts.mjs");
19
- break;
20
- case "astro":
21
- languageModule = await import("shiki/langs/astro.mjs");
22
- break;
23
- case "typescript":
24
- case "ts":
25
- languageModule = await import("shiki/langs/typescript.mjs");
26
- break;
27
- case "javascript":
28
- case "js":
29
- case "cjs":
30
- case "mjs":
31
- languageModule = await import("shiki/langs/javascript.mjs");
32
- break;
33
- case "bash":
34
- case "sh":
35
- case "shell":
36
- languageModule = await import("shiki/langs/bash.mjs");
37
- break;
38
- case "json":
39
- languageModule = await import("shiki/langs/json.mjs");
40
- break;
41
- case "jsonc":
42
- case "json5":
43
- languageModule = await import("shiki/langs/json5.mjs");
44
- break;
45
- case "tsx":
46
- case "jsx":
47
- languageModule = await import("shiki/langs/tsx.mjs");
48
- break;
49
- case "vue":
50
- languageModule = await import("shiki/langs/vue.mjs");
51
- break;
52
- case "svelte":
53
- languageModule = await import("shiki/langs/svelte.mjs");
54
- break;
55
- case "markdown":
56
- case "md":
57
- case "mdx":
58
- languageModule = await import("shiki/langs/markdown.mjs");
59
- break;
60
- case "css":
61
- languageModule = await import("shiki/langs/css.mjs");
62
- break;
63
- case "html":
64
- languageModule = await import("shiki/langs/html.mjs");
65
- break;
66
- case "xml":
67
- languageModule = await import("shiki/langs/xml.mjs");
68
- break;
69
- case "yml":
70
- case "yaml":
71
- languageModule = await import("shiki/langs/yaml.mjs");
72
- break;
73
- default:
74
- languageModule = await import("shiki/langs/typescript.mjs");
75
- break;
76
- }
77
- const language = languageModule.default;
78
- languageCache.set(lang, language);
79
- return language;
11
+ /**
12
+ * Lazy load the grammar of an already resolved language.
13
+ *
14
+ * @returns The grammar registrations, or `null` for languages Shiki renders
15
+ * without a grammar (plain text).
16
+ */
17
+ const loadLanguage = async ({ id, loadGrammar }) => {
18
+ if (!loadGrammar) return null;
19
+ const cachedLanguage = languageCache.get(id);
20
+ if (cachedLanguage) return cachedLanguage;
21
+ const languagePromise = loadGrammar().then((module) => module.default);
22
+ languageCache.set(id, languagePromise);
23
+ return languagePromise;
80
24
  };
25
+ /**
26
+ * Lazy load a theme module.
27
+ */
81
28
  const loadTheme = async (themeName) => {
82
- if (themeCache.has(themeName)) return themeCache.get(themeName);
83
- let themeModule;
84
- switch (themeName) {
85
- case "github-dark":
86
- themeModule = await import("shiki/themes/github-dark.mjs");
87
- break;
88
- default:
89
- themeModule = await import("shiki/themes/github-light.mjs");
90
- break;
91
- }
92
- const theme = themeModule.default;
93
- themeCache.set(themeName, theme);
94
- return theme;
29
+ const cachedTheme = themeCache.get(themeName);
30
+ if (cachedTheme) return cachedTheme;
31
+ const themePromise = (themeName === "github-dark" ? import("shiki/themes/github-dark.mjs") : import("shiki/themes/github-light.mjs")).then((module) => module.default);
32
+ themeCache.set(themeName, themePromise);
33
+ return themePromise;
95
34
  };
96
35
  let highlighterPromise = null;
97
36
  const getHighlighterInstance = async () => {
@@ -101,17 +40,24 @@ const getHighlighterInstance = async () => {
101
40
  }));
102
41
  return highlighterPromise;
103
42
  };
43
+ /**
44
+ * Highlight a snippet, lazily loading only the grammar and theme it needs.
45
+ *
46
+ * The language is resolved to a canonical Shiki id first, so that the id passed
47
+ * to `codeToHtml` always matches the grammar that was loaded.
48
+ */
104
49
  const highlightCode = async (code, lang, isDarkMode) => {
105
50
  const themeName = isDarkMode ? "github-dark" : "github-light";
51
+ const resolvedLanguage = resolveCodeLanguage(lang);
106
52
  const [highlighter, languageModule, themeModule] = await Promise.all([
107
53
  getHighlighterInstance(),
108
- loadLanguage(lang),
54
+ loadLanguage(resolvedLanguage),
109
55
  loadTheme(themeName)
110
56
  ]);
111
- if (!highlighter.getLoadedLanguages().includes(lang)) await highlighter.loadLanguage(languageModule);
57
+ if (languageModule && !highlighter.getLoadedLanguages().includes(resolvedLanguage.id)) await highlighter.loadLanguage(languageModule);
112
58
  if (!highlighter.getLoadedThemes().includes(themeName)) await highlighter.loadTheme(themeModule);
113
59
  return highlighter.codeToHtml(String(code), {
114
- lang,
60
+ lang: resolvedLanguage.id,
115
61
  theme: themeName
116
62
  });
117
63
  };
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlockShiki.mjs","names":[],"sources":["../../../../src/components/IDE/CodeBlockShiki.tsx"],"sourcesContent":["'use client';\n\nimport { type FC, type ReactNode, useEffect, useState } from 'react';\nimport type {\n BundledLanguage,\n BundledTheme,\n HighlighterGeneric,\n} from 'shiki/bundle/web';\nimport { CodeDefault } from './CodeBlockClient';\n\n// Map of loaded modules to avoid re-importing\nconst languageCache = new Map<BundledLanguage, any>();\nconst themeCache = new Map<BundledTheme, any>();\n\n// Lazy load language modules\nconst loadLanguage = async (lang: BundledLanguage): Promise<any> => {\n if (languageCache.has(lang)) return languageCache.get(lang);\n\n let languageModule: any;\n switch (lang) {\n case 'angular-html':\n languageModule = await import('shiki/langs/angular-html.mjs');\n break;\n case 'angular-ts':\n languageModule = await import('shiki/langs/angular-ts.mjs');\n break;\n case 'astro':\n languageModule = await import('shiki/langs/astro.mjs');\n break;\n case 'typescript':\n case 'ts':\n languageModule = await import('shiki/langs/typescript.mjs');\n break;\n case 'javascript':\n case 'js':\n case 'cjs':\n case 'mjs':\n languageModule = await import('shiki/langs/javascript.mjs');\n break;\n case 'bash':\n case 'sh':\n case 'shell':\n languageModule = await import('shiki/langs/bash.mjs');\n break;\n case 'json':\n languageModule = await import('shiki/langs/json.mjs');\n break;\n case 'jsonc':\n case 'json5':\n languageModule = await import('shiki/langs/json5.mjs');\n break;\n case 'tsx':\n case 'jsx':\n languageModule = await import('shiki/langs/tsx.mjs');\n break;\n case 'vue':\n languageModule = await import('shiki/langs/vue.mjs');\n break;\n case 'svelte':\n languageModule = await import('shiki/langs/svelte.mjs');\n break;\n case 'markdown':\n case 'md':\n case 'mdx':\n languageModule = await import('shiki/langs/markdown.mjs');\n break;\n case 'css':\n languageModule = await import('shiki/langs/css.mjs');\n break;\n case 'html':\n languageModule = await import('shiki/langs/html.mjs');\n break;\n case 'xml':\n languageModule = await import('shiki/langs/xml.mjs');\n break;\n case 'yml':\n case 'yaml':\n languageModule = await import('shiki/langs/yaml.mjs');\n break;\n default:\n languageModule = await import('shiki/langs/typescript.mjs');\n break;\n }\n\n const language = languageModule.default;\n languageCache.set(lang, language);\n return language;\n};\n\n// Lazy load theme modules\nconst loadTheme = async (themeName: BundledTheme): Promise<any> => {\n if (themeCache.has(themeName)) return themeCache.get(themeName);\n\n let themeModule: any;\n switch (themeName) {\n case 'github-dark':\n themeModule = await import('shiki/themes/github-dark.mjs');\n break;\n default:\n themeModule = await import('shiki/themes/github-light.mjs');\n break;\n }\n\n const theme = themeModule.default;\n themeCache.set(themeName, theme);\n return theme;\n};\n\n// Singleton Highlighter Instance\nlet highlighterPromise: Promise<HighlighterGeneric<any, any>> | null = null;\n\nconst getHighlighterInstance = async () => {\n if (!highlighterPromise) {\n highlighterPromise = import('shiki/bundle/web').then(\n ({ createHighlighter }) =>\n createHighlighter({\n langs: [],\n themes: [],\n })\n );\n }\n return highlighterPromise;\n};\n\n// Create a promise for highlighting\nconst highlightCode = async (\n code: any,\n lang: BundledLanguage,\n isDarkMode?: boolean\n): Promise<string> => {\n const themeName: BundledTheme = isDarkMode ? 'github-dark' : 'github-light';\n\n // Load highlighter, language, and theme in parallel\n const [highlighter, languageModule, themeModule] = await Promise.all([\n getHighlighterInstance(),\n loadLanguage(lang),\n loadTheme(themeName),\n ]);\n\n // Load into the singleton instance if not already loaded\n if (!highlighter.getLoadedLanguages().includes(lang)) {\n await highlighter.loadLanguage(languageModule);\n }\n if (!highlighter.getLoadedThemes().includes(themeName)) {\n await highlighter.loadTheme(themeModule);\n }\n\n return highlighter.codeToHtml(String(code), {\n lang,\n theme: themeName,\n });\n};\n\nexport type CodeBlockShikiProps = {\n children: ReactNode;\n lang: BundledLanguage;\n isDarkMode?: boolean;\n};\n\nexport const CodeBlockShiki: FC<CodeBlockShikiProps> = ({\n children,\n lang,\n isDarkMode,\n}) => {\n const [html, setHtml] = useState<string | null>(null);\n\n useEffect(() => {\n let isCancelled = false;\n\n highlightCode(children, lang, isDarkMode)\n .then((result) => {\n if (!isCancelled) setHtml(result);\n })\n .catch((error) => {\n console.error('Failed to highlight code:', error);\n if (!isCancelled && html === null) setHtml('');\n });\n\n return () => {\n isCancelled = true;\n };\n }, [children, lang, isDarkMode]);\n\n return (\n <div className=\"[&_pre.shiki]:!bg-transparent min-w-0 max-w-full overflow-auto bg-transparent [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden [&_pre.shiki]:max-w-full [&_pre.shiki]:overflow-x-auto [&_pre::-webkit-scrollbar]:hidden [&_pre]:[-ms-overflow-style:none] [&_pre]:[scrollbar-width:none]\">\n {html ? (\n // biome-ignore lint/security/noDangerouslySetInnerHtml: Shiki generates safe HTML for code highlighting\n <div dangerouslySetInnerHTML={{ __html: html }} />\n ) : (\n <CodeDefault>{children}</CodeDefault>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"CodeBlockShiki.mjs","names":[],"sources":["../../../../src/components/IDE/CodeBlockShiki.tsx"],"sourcesContent":["'use client';\n\nimport { type FC, type ReactNode, useEffect, useState } from 'react';\nimport type { BundledTheme } from 'shiki/bundle/web';\nimport { CodeDefault } from './CodeBlockClient';\nimport {\n type CodeLanguage,\n type LanguageGrammar,\n type ResolvedCodeLanguage,\n resolveCodeLanguage,\n} from './shikiLanguages';\n\n/** Theme registration, as default-exported by the `shiki/themes/*` modules. */\ntype ThemeRegistration =\n typeof import('shiki/themes/github-dark.mjs')['default'];\n\n/** Minimal Shiki highlighter surface used here, with unrestricted language ids. */\ntype CodeHighlighter = {\n getLoadedLanguages(): string[];\n getLoadedThemes(): string[];\n loadLanguage(grammar: LanguageGrammar): Promise<void>;\n loadTheme(theme: ThemeRegistration): Promise<void>;\n codeToHtml(code: string, options: { lang: string; theme: string }): string;\n};\n\n// Map of in-flight/loaded modules to avoid re-importing\nconst languageCache = new Map<string, Promise<LanguageGrammar>>();\nconst themeCache = new Map<BundledTheme, Promise<ThemeRegistration>>();\n\n/**\n * Lazy load the grammar of an already resolved language.\n *\n * @returns The grammar registrations, or `null` for languages Shiki renders\n * without a grammar (plain text).\n */\nconst loadLanguage = async ({\n id,\n loadGrammar,\n}: ResolvedCodeLanguage): Promise<LanguageGrammar | null> => {\n if (!loadGrammar) return null;\n\n const cachedLanguage = languageCache.get(id);\n if (cachedLanguage) return cachedLanguage;\n\n const languagePromise = loadGrammar().then((module) => module.default);\n languageCache.set(id, languagePromise);\n\n return languagePromise;\n};\n\n/**\n * Lazy load a theme module.\n */\nconst loadTheme = async (\n themeName: BundledTheme\n): Promise<ThemeRegistration> => {\n const cachedTheme = themeCache.get(themeName);\n if (cachedTheme) return cachedTheme;\n\n const themePromise = (\n themeName === 'github-dark'\n ? import('shiki/themes/github-dark.mjs')\n : import('shiki/themes/github-light.mjs')\n ).then((module) => module.default);\n\n themeCache.set(themeName, themePromise);\n\n return themePromise;\n};\n\n// Singleton Highlighter Instance\nlet highlighterPromise: Promise<CodeHighlighter> | null = null;\n\nconst getHighlighterInstance = async () => {\n if (!highlighterPromise) {\n highlighterPromise = import('shiki/bundle/web').then(\n ({ createHighlighter }) =>\n createHighlighter({\n langs: [],\n themes: [],\n })\n );\n }\n return highlighterPromise;\n};\n\n/**\n * Highlight a snippet, lazily loading only the grammar and theme it needs.\n *\n * The language is resolved to a canonical Shiki id first, so that the id passed\n * to `codeToHtml` always matches the grammar that was loaded.\n */\nconst highlightCode = async (\n code: ReactNode,\n lang: CodeLanguage,\n isDarkMode?: boolean\n): Promise<string> => {\n const themeName: BundledTheme = isDarkMode ? 'github-dark' : 'github-light';\n const resolvedLanguage = resolveCodeLanguage(lang);\n\n // Load highlighter, language, and theme in parallel\n const [highlighter, languageModule, themeModule] = await Promise.all([\n getHighlighterInstance(),\n loadLanguage(resolvedLanguage),\n loadTheme(themeName),\n ]);\n\n // Load into the singleton instance if not already loaded\n if (\n languageModule &&\n !highlighter.getLoadedLanguages().includes(resolvedLanguage.id)\n ) {\n await highlighter.loadLanguage(languageModule);\n }\n if (!highlighter.getLoadedThemes().includes(themeName)) {\n await highlighter.loadTheme(themeModule);\n }\n\n return highlighter.codeToHtml(String(code), {\n lang: resolvedLanguage.id,\n theme: themeName,\n });\n};\n\nexport type CodeBlockShikiProps = {\n children: ReactNode;\n lang: CodeLanguage;\n isDarkMode?: boolean;\n};\n\nexport const CodeBlockShiki: FC<CodeBlockShikiProps> = ({\n children,\n lang,\n isDarkMode,\n}) => {\n const [html, setHtml] = useState<string | null>(null);\n\n useEffect(() => {\n let isCancelled = false;\n\n highlightCode(children, lang, isDarkMode)\n .then((result) => {\n if (!isCancelled) setHtml(result);\n })\n .catch((error) => {\n console.error('Failed to highlight code:', error);\n if (!isCancelled && html === null) setHtml('');\n });\n\n return () => {\n isCancelled = true;\n };\n }, [children, lang, isDarkMode]);\n\n return (\n <div className=\"[&_pre.shiki]:!bg-transparent min-w-0 max-w-full overflow-auto bg-transparent [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden [&_pre.shiki]:max-w-full [&_pre.shiki]:overflow-x-auto [&_pre::-webkit-scrollbar]:hidden [&_pre]:[-ms-overflow-style:none] [&_pre]:[scrollbar-width:none]\">\n {html ? (\n // biome-ignore lint/security/noDangerouslySetInnerHtml: Shiki generates safe HTML for code highlighting\n <div dangerouslySetInnerHTML={{ __html: html }} />\n ) : (\n <CodeDefault>{children}</CodeDefault>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;;AA0BA,MAAM,gCAAgB,IAAI,IAAsC;AAChE,MAAM,6BAAa,IAAI,IAA8C;;;;;;;AAQrE,MAAM,eAAe,OAAO,EAC1B,IACA,kBAC2D;CAC3D,IAAI,CAAC,aAAa,OAAO;CAEzB,MAAM,iBAAiB,cAAc,IAAI,EAAE;CAC3C,IAAI,gBAAgB,OAAO;CAE3B,MAAM,kBAAkB,YAAY,CAAC,CAAC,MAAM,WAAW,OAAO,OAAO;CACrE,cAAc,IAAI,IAAI,eAAe;CAErC,OAAO;AACT;;;;AAKA,MAAM,YAAY,OAChB,cAC+B;CAC/B,MAAM,cAAc,WAAW,IAAI,SAAS;CAC5C,IAAI,aAAa,OAAO;CAExB,MAAM,gBACJ,cAAc,gBACV,OAAO,kCACP,OAAO,iCAAgC,CAC3C,MAAM,WAAW,OAAO,OAAO;CAEjC,WAAW,IAAI,WAAW,YAAY;CAEtC,OAAO;AACT;AAGA,IAAI,qBAAsD;AAE1D,MAAM,yBAAyB,YAAY;CACzC,IAAI,CAAC,oBACH,qBAAqB,OAAO,mBAAmB,CAAC,MAC7C,EAAE,wBACD,kBAAkB;EAChB,OAAO,CAAC;EACR,QAAQ,CAAC;CACX,CAAC,CACL;CAEF,OAAO;AACT;;;;;;;AAQA,MAAM,gBAAgB,OACpB,MACA,MACA,eACoB;CACpB,MAAM,YAA0B,aAAa,gBAAgB;CAC7D,MAAM,mBAAmB,oBAAoB,IAAI;CAGjD,MAAM,CAAC,aAAa,gBAAgB,eAAe,MAAM,QAAQ,IAAI;EACnE,uBAAuB;EACvB,aAAa,gBAAgB;EAC7B,UAAU,SAAS;CACrB,CAAC;CAGD,IACE,kBACA,CAAC,YAAY,mBAAmB,CAAC,CAAC,SAAS,iBAAiB,EAAE,GAE9D,MAAM,YAAY,aAAa,cAAc;CAE/C,IAAI,CAAC,YAAY,gBAAgB,CAAC,CAAC,SAAS,SAAS,GACnD,MAAM,YAAY,UAAU,WAAW;CAGzC,OAAO,YAAY,WAAW,OAAO,IAAI,GAAG;EAC1C,MAAM,iBAAiB;EACvB,OAAO;CACT,CAAC;AACH;AAQA,MAAa,kBAA2C,EACtD,UACA,MACA,iBACI;CACJ,MAAM,CAAC,MAAM,WAAW,SAAwB,IAAI;CAEpD,gBAAgB;EACd,IAAI,cAAc;EAElB,cAAc,UAAU,MAAM,UAAU,CAAC,CACtC,MAAM,WAAW;GAChB,IAAI,CAAC,aAAa,QAAQ,MAAM;EAClC,CAAC,CAAC,CACD,OAAO,UAAU;GAChB,QAAQ,MAAM,6BAA6B,KAAK;GAChD,IAAI,CAAC,eAAe,SAAS,MAAM,QAAQ,EAAE;EAC/C,CAAC;EAEH,aAAa;GACX,cAAc;EAChB;CACF,GAAG;EAAC;EAAU;EAAM;CAAU,CAAC;CAE/B,OACE,oBAAC,OAAD;EAAK,WAAU;YACZ,OAEC,oBAAC,OAAD,EAAK,yBAAyB,EAAE,QAAQ,KAAK,EAAI,KAEjD,oBAAC,aAAD,EAAc,SAAsB;CAEnC;AAET"}
@@ -4,8 +4,8 @@ import { cn } from "../../utils/cn.mjs";
4
4
  import { MaxHeightSmoother } from "../MaxHeightSmoother/index.mjs";
5
5
  import { createFileTree } from "./createFileTree.mjs";
6
6
  import { useState } from "react";
7
- import { ChevronRight } from "lucide-react";
8
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
+ import { ChevronRight } from "lucide-react";
9
9
 
10
10
  //#region src/components/IDE/FileTree.tsx
11
11
  const concatFilePath = (paths) => paths.join("/");
@@ -46,9 +46,10 @@ const FileItem = ({ filesPaths, path, subPath, onClickFile, activeFile, prePaths
46
46
  })] });
47
47
  };
48
48
  const FileTree = ({ filesPaths, activeFile, onClickFile, prePaths = [] }) => {
49
+ const fileTree = createFileTree(filesPaths);
49
50
  return /* @__PURE__ */ jsx("div", {
50
51
  className: "flex size-full flex-col items-start justify-start py-1 text-neutral",
51
- children: createFileTree(filesPaths).map(({ path, subPath, isFile }) => /* @__PURE__ */ jsx(FileItem, {
52
+ children: fileTree.map(({ path, subPath, isFile }) => /* @__PURE__ */ jsx(FileItem, {
52
53
  isFile,
53
54
  subPath,
54
55
  path,
@@ -1 +1 @@
1
- {"version":3,"file":"FileTree.mjs","names":[],"sources":["../../../../src/components/IDE/FileTree.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport { ChevronRight } from 'lucide-react';\nimport { type FC, useState } from 'react';\nimport { MaxHeightSmoother } from '../MaxHeightSmoother';\nimport { createFileTree, type FilePath } from './createFileTree';\n\ntype FileTreeProps = {\n filesPaths: string[];\n activeFile?: string;\n onClickFile?: (filePath: string) => void;\n prePaths?: string[];\n};\n\nconst concatFilePath = (paths: string[]) => paths.join('/');\n\ntype FileItemProps = {\n filesPaths: string[];\n subPath?: FilePath[];\n path: string;\n onClickFile?: (title: string) => void;\n activeFile?: string;\n prePaths: string[];\n isFile: boolean;\n};\n\nconst FileItem: FC<FileItemProps> = ({\n filesPaths,\n path,\n subPath,\n onClickFile,\n activeFile,\n prePaths,\n isFile,\n}) => {\n const [subPathOpen, setSubPathOpen] = useState(true);\n\n const level = prePaths.length + 1;\n const currentPath = concatFilePath([\n ...prePaths.slice(level - 1, level),\n path,\n ]);\n const totalPath = concatFilePath([...prePaths, path]);\n\n const filteredFilePaths = filesPaths\n .map(\n (path) => path.replace(/^\\/?/, '') // This regex matches the first slash, if it exists, at the start of the string)\n )\n .filter((filePath) => filePath.startsWith(currentPath));\n\n const newPath = filteredFilePaths.map((path) =>\n path.replace(currentPath, '').replace(/^\\/?/, '')\n );\n\n const isActive = totalPath === activeFile;\n\n const indentation = new Array(level).fill(' ').join('');\n\n return (\n <>\n <button\n className={cn(\n 'flex w-full items-start justify-start whitespace-pre text-nowrap px-2 py-1 text-xs transition',\n isActive\n ? 'bg-neutral-200 dark:bg-neutral-800'\n : 'cursor-pointer hover:bg-neutral-300 dark:hover:bg-neutral-900'\n )}\n key={path}\n onClick={() => {\n if (isFile) {\n onClickFile?.(totalPath);\n } else {\n setSubPathOpen(!subPathOpen);\n }\n }}\n >\n <span className={cn('whitespace-pre', isFile && 'ml-2')}>\n {indentation}\n </span>\n\n {!isFile && (\n <ChevronRight\n className={cn(`transition`, subPathOpen && `rotate-90 transform`)}\n size={16}\n />\n )}\n {path}\n </button>\n {subPath && (\n <MaxHeightSmoother\n isHidden={!subPathOpen}\n className=\"overflow-x-hidden\"\n >\n <FileTree\n filesPaths={newPath}\n activeFile={activeFile}\n onClickFile={onClickFile}\n prePaths={[...prePaths, path]}\n />\n </MaxHeightSmoother>\n )}\n </>\n );\n};\n\nexport const FileTree: FC<FileTreeProps> = ({\n filesPaths,\n activeFile,\n onClickFile,\n prePaths = [],\n}) => {\n const fileTree = createFileTree(filesPaths);\n\n return (\n <div className=\"flex size-full flex-col items-start justify-start py-1 text-neutral\">\n {fileTree.map(({ path, subPath, isFile }) => (\n <FileItem\n key={path}\n isFile={isFile}\n subPath={subPath}\n path={path}\n onClickFile={onClickFile}\n activeFile={activeFile}\n prePaths={prePaths}\n filesPaths={filesPaths}\n />\n ))}\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;AAeA,MAAM,kBAAkB,UAAoB,MAAM,KAAK,GAAG;AAY1D,MAAM,YAA+B,EACnC,YACA,MACA,SACA,aACA,YACA,UACA,aACI;CACJ,MAAM,CAAC,aAAa,kBAAkB,SAAS,IAAI;CAEnD,MAAM,QAAQ,SAAS,SAAS;CAChC,MAAM,cAAc,eAAe,CACjC,GAAG,SAAS,MAAM,QAAQ,GAAG,KAAK,GAClC,IACF,CAAC;CACD,MAAM,YAAY,eAAe,CAAC,GAAG,UAAU,IAAI,CAAC;CAQpD,MAAM,UANoB,WACvB,KACE,SAAS,KAAK,QAAQ,QAAQ,EAAE,CACnC,CAAC,CACA,QAAQ,aAAa,SAAS,WAAW,WAAW,CAEvB,CAAC,CAAC,KAAK,SACrC,KAAK,QAAQ,aAAa,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE,CAClD;CAEA,MAAM,WAAW,cAAc;CAE/B,MAAM,cAAc,IAAI,MAAM,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,EAAE;CAEvD,OACE,8CACE,qBAAC,UAAD;EACE,WAAW,GACT,iGACA,WACI,uCACA,+DACN;EAEA,eAAe;GACb,IAAI,QACF,cAAc,SAAS;QAEvB,eAAe,CAAC,WAAW;EAE/B;YAdF;GAgBE,oBAAC,QAAD;IAAM,WAAW,GAAG,kBAAkB,UAAU,MAAM;cACnD;GACG;GAEL,CAAC,UACA,oBAAC,cAAD;IACE,WAAW,GAAG,cAAc,eAAe,qBAAqB;IAChE,MAAM;GACP;GAEF;EACK;IApBD,IAoBC,GACP,WACC,oBAAC,mBAAD;EACE,UAAU,CAAC;EACX,WAAU;YAEV,oBAAC,UAAD;GACE,YAAY;GACA;GACC;GACb,UAAU,CAAC,GAAG,UAAU,IAAI;EAC7B;CACgB,EAErB;AAEN;AAEA,MAAa,YAA+B,EAC1C,YACA,YACA,aACA,WAAW,CAAC,QACR;CAGJ,OACE,oBAAC,OAAD;EAAK,WAAU;YAHA,eAAe,UAIpB,CAAC,CAAC,KAAK,EAAE,MAAM,SAAS,aAC9B,oBAAC,UAAD;GAEU;GACC;GACH;GACO;GACD;GACF;GACE;EACb,GARM,IAQN,CACF;CACE;AAET"}
1
+ {"version":3,"file":"FileTree.mjs","names":[],"sources":["../../../../src/components/IDE/FileTree.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport { ChevronRight } from 'lucide-react';\nimport { type FC, useState } from 'react';\nimport { MaxHeightSmoother } from '../MaxHeightSmoother';\nimport { createFileTree, type FilePath } from './createFileTree';\n\ntype FileTreeProps = {\n filesPaths: string[];\n activeFile?: string;\n onClickFile?: (filePath: string) => void;\n prePaths?: string[];\n};\n\nconst concatFilePath = (paths: string[]) => paths.join('/');\n\ntype FileItemProps = {\n filesPaths: string[];\n subPath?: FilePath[];\n path: string;\n onClickFile?: (title: string) => void;\n activeFile?: string;\n prePaths: string[];\n isFile: boolean;\n};\n\nconst FileItem: FC<FileItemProps> = ({\n filesPaths,\n path,\n subPath,\n onClickFile,\n activeFile,\n prePaths,\n isFile,\n}) => {\n const [subPathOpen, setSubPathOpen] = useState(true);\n\n const level = prePaths.length + 1;\n const currentPath = concatFilePath([\n ...prePaths.slice(level - 1, level),\n path,\n ]);\n const totalPath = concatFilePath([...prePaths, path]);\n\n const filteredFilePaths = filesPaths\n .map(\n (path) => path.replace(/^\\/?/, '') // This regex matches the first slash, if it exists, at the start of the string)\n )\n .filter((filePath) => filePath.startsWith(currentPath));\n\n const newPath = filteredFilePaths.map((path) =>\n path.replace(currentPath, '').replace(/^\\/?/, '')\n );\n\n const isActive = totalPath === activeFile;\n\n const indentation = new Array(level).fill(' ').join('');\n\n return (\n <>\n <button\n className={cn(\n 'flex w-full items-start justify-start whitespace-pre text-nowrap px-2 py-1 text-xs transition',\n isActive\n ? 'bg-neutral-200 dark:bg-neutral-800'\n : 'cursor-pointer hover:bg-neutral-300 dark:hover:bg-neutral-900'\n )}\n key={path}\n onClick={() => {\n if (isFile) {\n onClickFile?.(totalPath);\n } else {\n setSubPathOpen(!subPathOpen);\n }\n }}\n >\n <span className={cn('whitespace-pre', isFile && 'ml-2')}>\n {indentation}\n </span>\n\n {!isFile && (\n <ChevronRight\n className={cn(`transition`, subPathOpen && `rotate-90 transform`)}\n size={16}\n />\n )}\n {path}\n </button>\n {subPath && (\n <MaxHeightSmoother\n isHidden={!subPathOpen}\n className=\"overflow-x-hidden\"\n >\n <FileTree\n filesPaths={newPath}\n activeFile={activeFile}\n onClickFile={onClickFile}\n prePaths={[...prePaths, path]}\n />\n </MaxHeightSmoother>\n )}\n </>\n );\n};\n\nexport const FileTree: FC<FileTreeProps> = ({\n filesPaths,\n activeFile,\n onClickFile,\n prePaths = [],\n}) => {\n const fileTree = createFileTree(filesPaths);\n\n return (\n <div className=\"flex size-full flex-col items-start justify-start py-1 text-neutral\">\n {fileTree.map(({ path, subPath, isFile }) => (\n <FileItem\n key={path}\n isFile={isFile}\n subPath={subPath}\n path={path}\n onClickFile={onClickFile}\n activeFile={activeFile}\n prePaths={prePaths}\n filesPaths={filesPaths}\n />\n ))}\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;AAeA,MAAM,kBAAkB,UAAoB,MAAM,KAAK,GAAG;AAY1D,MAAM,YAA+B,EACnC,YACA,MACA,SACA,aACA,YACA,UACA,aACI;CACJ,MAAM,CAAC,aAAa,kBAAkB,SAAS,IAAI;CAEnD,MAAM,QAAQ,SAAS,SAAS;CAChC,MAAM,cAAc,eAAe,CACjC,GAAG,SAAS,MAAM,QAAQ,GAAG,KAAK,GAClC,IACF,CAAC;CACD,MAAM,YAAY,eAAe,CAAC,GAAG,UAAU,IAAI,CAAC;CAQpD,MAAM,UANoB,WACvB,KACE,SAAS,KAAK,QAAQ,QAAQ,EAAE,CACnC,CAAC,CACA,QAAQ,aAAa,SAAS,WAAW,WAAW,CAEvB,CAAC,CAAC,KAAK,SACrC,KAAK,QAAQ,aAAa,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE,CAClD;CAEA,MAAM,WAAW,cAAc;CAE/B,MAAM,cAAc,IAAI,MAAM,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,EAAE;CAEvD,OACE,8CACE,qBAAC,UAAD;EACE,WAAW,GACT,iGACA,WACI,uCACA,+DACN;EAEA,eAAe;GACb,IAAI,QACF,cAAc,SAAS;QAEvB,eAAe,CAAC,WAAW;EAE/B;YAdF;GAgBE,oBAAC,QAAD;IAAM,WAAW,GAAG,kBAAkB,UAAU,MAAM;cACnD;GACG;GAEL,CAAC,UACA,oBAAC,cAAD;IACE,WAAW,GAAG,cAAc,eAAe,qBAAqB;IAChE,MAAM;GACP;GAEF;EACK;IApBD,IAoBC,GACP,WACC,oBAAC,mBAAD;EACE,UAAU,CAAC;EACX,WAAU;YAEV,oBAAC,UAAD;GACE,YAAY;GACA;GACC;GACb,UAAU,CAAC,GAAG,UAAU,IAAI;EAC7B;CACgB,EAErB;AAEN;AAEA,MAAa,YAA+B,EAC1C,YACA,YACA,aACA,WAAW,CAAC,QACR;CACJ,MAAM,WAAW,eAAe,UAAU;CAE1C,OACE,oBAAC,OAAD;EAAK,WAAU;YACZ,SAAS,KAAK,EAAE,MAAM,SAAS,aAC9B,oBAAC,UAAD;GAEU;GACC;GACH;GACO;GACD;GACF;GACE;EACb,GARM,IAQN,CACF;CACE;AAET"}
@@ -1 +1 @@
1
- {"version":3,"file":"MarkDownRender.mjs","names":["MarkdownRendererIntlayer"],"sources":["../../../../src/components/IDE/MarkDownRender.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { MarkdownRenderer as MarkdownRendererIntlayer } from 'react-intlayer';\nimport type { BundledLanguage } from 'shiki/bundle/web';\nimport { Code } from './Code';\n\ntype MarkdownRendererProps = {\n isDarkMode?: boolean;\n children: string;\n};\n\nexport const MarkdownRenderer: FC<MarkdownRendererProps> = ({\n children,\n isDarkMode,\n}) => (\n <MarkdownRendererIntlayer\n components={{\n code: (props) => (\n <Code\n {...props}\n isDarkMode={isDarkMode}\n language={props.className?.replace('lang-', '') as BundledLanguage}\n showHeader={false}\n className=\"text-xs leading-5\"\n >\n {props.children as string}\n </Code>\n ),\n pre: (props) => props.children,\n }}\n >\n {children ?? ''}\n </MarkdownRendererIntlayer>\n);\n"],"mappings":";;;;;AAUA,MAAa,oBAA+C,EAC1D,UACA,iBAEA,oBAACA,oBAAD;CACE,YAAY;EACV,OAAO,UACL,oBAAC,MAAD;GACE,GAAI;GACQ;GACZ,UAAU,MAAM,WAAW,QAAQ,SAAS,EAAE;GAC9C,YAAY;GACZ,WAAU;aAET,MAAM;EACH;EAER,MAAM,UAAU,MAAM;CACxB;WAEC,YAAY;AACW"}
1
+ {"version":3,"file":"MarkDownRender.mjs","names":["MarkdownRendererIntlayer"],"sources":["../../../../src/components/IDE/MarkDownRender.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { MarkdownRenderer as MarkdownRendererIntlayer } from 'react-intlayer';\nimport { Code } from './Code';\nimport type { CodeLanguage } from './shikiLanguages';\n\ntype MarkdownRendererProps = {\n isDarkMode?: boolean;\n children: string;\n};\n\nexport const MarkdownRenderer: FC<MarkdownRendererProps> = ({\n children,\n isDarkMode,\n}) => (\n <MarkdownRendererIntlayer\n components={{\n code: (props) => (\n <Code\n {...props}\n isDarkMode={isDarkMode}\n language={props.className?.replace('lang-', '') as CodeLanguage}\n showHeader={false}\n className=\"text-xs leading-5\"\n >\n {props.children as string}\n </Code>\n ),\n pre: (props) => props.children,\n }}\n >\n {children ?? ''}\n </MarkdownRendererIntlayer>\n);\n"],"mappings":";;;;;AAUA,MAAa,oBAA+C,EAC1D,UACA,iBAEA,oBAACA,oBAAD;CACE,YAAY;EACV,OAAO,UACL,oBAAC,MAAD;GACE,GAAI;GACQ;GACZ,UAAU,MAAM,WAAW,QAAQ,SAAS,EAAE;GAC9C,YAAY;GACZ,WAAU;aAET,MAAM;EACH;EAER,MAAM,UAAU,MAAM;CACxB;WAEC,YAAY;AACW"}
@@ -0,0 +1,122 @@
1
+ //#region src/components/IDE/shikiLanguages.ts
2
+ /**
3
+ * Shiki grammar loaders, keyed by the canonical Shiki language id.
4
+ *
5
+ * Import specifiers are written as literals (never template strings) so that
6
+ * bundlers can statically analyse them and emit one lazily fetched chunk per
7
+ * grammar instead of bundling every language up front.
8
+ */
9
+ const grammarLoaders = {
10
+ "angular-html": () => import("shiki/langs/angular-html.mjs"),
11
+ "angular-ts": () => import("shiki/langs/angular-ts.mjs"),
12
+ astro: () => import("shiki/langs/astro.mjs"),
13
+ bash: () => import("shiki/langs/bash.mjs"),
14
+ c: () => import("shiki/langs/c.mjs"),
15
+ cpp: () => import("shiki/langs/cpp.mjs"),
16
+ csharp: () => import("shiki/langs/csharp.mjs"),
17
+ css: () => import("shiki/langs/css.mjs"),
18
+ csv: () => import("shiki/langs/csv.mjs"),
19
+ dart: () => import("shiki/langs/dart.mjs"),
20
+ diff: () => import("shiki/langs/diff.mjs"),
21
+ docker: () => import("shiki/langs/docker.mjs"),
22
+ go: () => import("shiki/langs/go.mjs"),
23
+ graphql: () => import("shiki/langs/graphql.mjs"),
24
+ html: () => import("shiki/langs/html.mjs"),
25
+ http: () => import("shiki/langs/http.mjs"),
26
+ ini: () => import("shiki/langs/ini.mjs"),
27
+ java: () => import("shiki/langs/java.mjs"),
28
+ javascript: () => import("shiki/langs/javascript.mjs"),
29
+ json: () => import("shiki/langs/json.mjs"),
30
+ json5: () => import("shiki/langs/json5.mjs"),
31
+ kotlin: () => import("shiki/langs/kotlin.mjs"),
32
+ less: () => import("shiki/langs/less.mjs"),
33
+ lua: () => import("shiki/langs/lua.mjs"),
34
+ markdown: () => import("shiki/langs/markdown.mjs"),
35
+ nginx: () => import("shiki/langs/nginx.mjs"),
36
+ php: () => import("shiki/langs/php.mjs"),
37
+ powershell: () => import("shiki/langs/powershell.mjs"),
38
+ prisma: () => import("shiki/langs/prisma.mjs"),
39
+ python: () => import("shiki/langs/python.mjs"),
40
+ ruby: () => import("shiki/langs/ruby.mjs"),
41
+ rust: () => import("shiki/langs/rust.mjs"),
42
+ scss: () => import("shiki/langs/scss.mjs"),
43
+ sql: () => import("shiki/langs/sql.mjs"),
44
+ svelte: () => import("shiki/langs/svelte.mjs"),
45
+ swift: () => import("shiki/langs/swift.mjs"),
46
+ toml: () => import("shiki/langs/toml.mjs"),
47
+ tsx: () => import("shiki/langs/tsx.mjs"),
48
+ typescript: () => import("shiki/langs/typescript.mjs"),
49
+ vue: () => import("shiki/langs/vue.mjs"),
50
+ xml: () => import("shiki/langs/xml.mjs"),
51
+ yaml: () => import("shiki/langs/yaml.mjs")
52
+ };
53
+ /** Every canonical language id that ships with a grammar loader. */
54
+ const shikiLanguageIds = Object.keys(grammarLoaders);
55
+ /**
56
+ * Code fence names that are rendered by another grammar, mapped to the
57
+ * canonical id that actually owns the grammar.
58
+ */
59
+ const grammarAliases = {
60
+ "c#": "csharp",
61
+ "c++": "cpp",
62
+ cjs: "javascript",
63
+ console: "bash",
64
+ cs: "csharp",
65
+ cts: "typescript",
66
+ dockerfile: "docker",
67
+ golang: "go",
68
+ gql: "graphql",
69
+ htm: "html",
70
+ js: "javascript",
71
+ jsonc: "json5",
72
+ jsx: "tsx",
73
+ kt: "kotlin",
74
+ md: "markdown",
75
+ mdx: "markdown",
76
+ mjs: "javascript",
77
+ mts: "typescript",
78
+ postcss: "css",
79
+ ps1: "powershell",
80
+ pwsh: "powershell",
81
+ py: "python",
82
+ rb: "ruby",
83
+ rs: "rust",
84
+ sh: "bash",
85
+ shell: "bash",
86
+ shellscript: "bash",
87
+ ts: "typescript",
88
+ yml: "yaml",
89
+ zsh: "bash"
90
+ };
91
+ const PLAIN_TEXT_LANGUAGE = "plaintext";
92
+ /**
93
+ * Resolve a code fence language name to a canonical Shiki id and its grammar
94
+ * loader.
95
+ *
96
+ * Unknown names resolve to plain text rather than to an arbitrary grammar, so
97
+ * that `codeToHtml` never receives a language it has not loaded.
98
+ *
99
+ * @param language - Language name coming from a code fence or a `lang` prop.
100
+ *
101
+ * @example
102
+ * resolveCodeLanguage('ts'); // { id: 'typescript', loadGrammar: fn }
103
+ * resolveCodeLanguage('toml'); // { id: 'toml', loadGrammar: fn }
104
+ * resolveCodeLanguage('brainfuck'); // { id: 'plaintext', loadGrammar: null }
105
+ */
106
+ const resolveCodeLanguage = (language) => {
107
+ const normalizedLanguage = (language ?? "").trim().toLowerCase();
108
+ const canonicalId = grammarAliases[normalizedLanguage] ?? normalizedLanguage;
109
+ const loadGrammar = grammarLoaders[canonicalId];
110
+ if (!loadGrammar) return {
111
+ id: PLAIN_TEXT_LANGUAGE,
112
+ loadGrammar: null
113
+ };
114
+ return {
115
+ id: canonicalId,
116
+ loadGrammar
117
+ };
118
+ };
119
+
120
+ //#endregion
121
+ export { resolveCodeLanguage, shikiLanguageIds };
122
+ //# sourceMappingURL=shikiLanguages.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shikiLanguages.mjs","names":[],"sources":["../../../../src/components/IDE/shikiLanguages.ts"],"sourcesContent":["/**\n * Grammar registrations, as default-exported by the `shiki/langs/*` modules.\n */\nexport type LanguageGrammar =\n typeof import('shiki/langs/typescript.mjs')['default'];\n\n/**\n * Loads a single Shiki grammar (and its embedded dependencies).\n */\nexport type GrammarLoader = () => Promise<{ default: LanguageGrammar }>;\n\n/**\n * Shiki grammar loaders, keyed by the canonical Shiki language id.\n *\n * Import specifiers are written as literals (never template strings) so that\n * bundlers can statically analyse them and emit one lazily fetched chunk per\n * grammar instead of bundling every language up front.\n */\nconst grammarLoaders = {\n 'angular-html': () => import('shiki/langs/angular-html.mjs'),\n 'angular-ts': () => import('shiki/langs/angular-ts.mjs'),\n astro: () => import('shiki/langs/astro.mjs'),\n bash: () => import('shiki/langs/bash.mjs'),\n c: () => import('shiki/langs/c.mjs'),\n cpp: () => import('shiki/langs/cpp.mjs'),\n csharp: () => import('shiki/langs/csharp.mjs'),\n css: () => import('shiki/langs/css.mjs'),\n csv: () => import('shiki/langs/csv.mjs'),\n dart: () => import('shiki/langs/dart.mjs'),\n diff: () => import('shiki/langs/diff.mjs'),\n docker: () => import('shiki/langs/docker.mjs'),\n go: () => import('shiki/langs/go.mjs'),\n graphql: () => import('shiki/langs/graphql.mjs'),\n html: () => import('shiki/langs/html.mjs'),\n http: () => import('shiki/langs/http.mjs'),\n ini: () => import('shiki/langs/ini.mjs'),\n java: () => import('shiki/langs/java.mjs'),\n javascript: () => import('shiki/langs/javascript.mjs'),\n json: () => import('shiki/langs/json.mjs'),\n json5: () => import('shiki/langs/json5.mjs'),\n kotlin: () => import('shiki/langs/kotlin.mjs'),\n less: () => import('shiki/langs/less.mjs'),\n lua: () => import('shiki/langs/lua.mjs'),\n markdown: () => import('shiki/langs/markdown.mjs'),\n nginx: () => import('shiki/langs/nginx.mjs'),\n php: () => import('shiki/langs/php.mjs'),\n powershell: () => import('shiki/langs/powershell.mjs'),\n prisma: () => import('shiki/langs/prisma.mjs'),\n python: () => import('shiki/langs/python.mjs'),\n ruby: () => import('shiki/langs/ruby.mjs'),\n rust: () => import('shiki/langs/rust.mjs'),\n scss: () => import('shiki/langs/scss.mjs'),\n sql: () => import('shiki/langs/sql.mjs'),\n svelte: () => import('shiki/langs/svelte.mjs'),\n swift: () => import('shiki/langs/swift.mjs'),\n toml: () => import('shiki/langs/toml.mjs'),\n tsx: () => import('shiki/langs/tsx.mjs'),\n typescript: () => import('shiki/langs/typescript.mjs'),\n vue: () => import('shiki/langs/vue.mjs'),\n xml: () => import('shiki/langs/xml.mjs'),\n yaml: () => import('shiki/langs/yaml.mjs'),\n} satisfies Record<string, GrammarLoader>;\n\n/** Canonical Shiki language ids this package can highlight. */\nexport type ShikiLanguageId = keyof typeof grammarLoaders;\n\n/** Every canonical language id that ships with a grammar loader. */\nexport const shikiLanguageIds = Object.keys(\n grammarLoaders\n) as ShikiLanguageId[];\n\n/**\n * Code fence names that are rendered by another grammar, mapped to the\n * canonical id that actually owns the grammar.\n */\nconst grammarAliases = {\n 'c#': 'csharp',\n 'c++': 'cpp',\n cjs: 'javascript',\n console: 'bash',\n cs: 'csharp',\n cts: 'typescript',\n dockerfile: 'docker',\n golang: 'go',\n gql: 'graphql',\n htm: 'html',\n js: 'javascript',\n jsonc: 'json5',\n jsx: 'tsx',\n kt: 'kotlin',\n md: 'markdown',\n mdx: 'markdown',\n mjs: 'javascript',\n mts: 'typescript',\n postcss: 'css',\n ps1: 'powershell',\n pwsh: 'powershell',\n py: 'python',\n rb: 'ruby',\n rs: 'rust',\n sh: 'bash',\n shell: 'bash',\n shellscript: 'bash',\n ts: 'typescript',\n yml: 'yaml',\n zsh: 'bash',\n} satisfies Record<string, ShikiLanguageId>;\n\n/** Code fence names accepted as aliases of a canonical language id. */\nexport type ShikiLanguageAlias = keyof typeof grammarAliases;\n\n/**\n * Languages Shiki renders without any grammar. They are also used as the\n * fallback for languages this package does not ship a grammar for.\n */\nexport type PlainTextLanguage = 'plaintext' | 'text' | 'txt';\n\nconst PLAIN_TEXT_LANGUAGE = 'plaintext' satisfies PlainTextLanguage;\n\n/** Every language name accepted by the code block components. */\nexport type CodeLanguage =\n | ShikiLanguageId\n | ShikiLanguageAlias\n | PlainTextLanguage;\n\n/**\n * A language name resolved to what Shiki actually needs to highlight it.\n */\nexport type ResolvedCodeLanguage = {\n /** Canonical language id to pass to `codeToHtml` / `codeToHast`. */\n id: ShikiLanguageId | PlainTextLanguage;\n /** Grammar loader, or `null` when the language needs no grammar. */\n loadGrammar: GrammarLoader | null;\n};\n\n/**\n * Resolve a code fence language name to a canonical Shiki id and its grammar\n * loader.\n *\n * Unknown names resolve to plain text rather than to an arbitrary grammar, so\n * that `codeToHtml` never receives a language it has not loaded.\n *\n * @param language - Language name coming from a code fence or a `lang` prop.\n *\n * @example\n * resolveCodeLanguage('ts'); // { id: 'typescript', loadGrammar: fn }\n * resolveCodeLanguage('toml'); // { id: 'toml', loadGrammar: fn }\n * resolveCodeLanguage('brainfuck'); // { id: 'plaintext', loadGrammar: null }\n */\nexport const resolveCodeLanguage = (\n language: CodeLanguage | string | undefined | null\n): ResolvedCodeLanguage => {\n const normalizedLanguage = (language ?? '').trim().toLowerCase();\n\n const canonicalId =\n grammarAliases[normalizedLanguage as ShikiLanguageAlias] ??\n (normalizedLanguage as ShikiLanguageId);\n\n const loadGrammar = grammarLoaders[canonicalId] as GrammarLoader | undefined;\n\n if (!loadGrammar) {\n return { id: PLAIN_TEXT_LANGUAGE, loadGrammar: null };\n }\n\n return { id: canonicalId, loadGrammar };\n};\n"],"mappings":";;;;;;;;AAkBA,MAAM,iBAAiB;CACrB,sBAAsB,OAAO;CAC7B,oBAAoB,OAAO;CAC3B,aAAa,OAAO;CACpB,YAAY,OAAO;CACnB,SAAS,OAAO;CAChB,WAAW,OAAO;CAClB,cAAc,OAAO;CACrB,WAAW,OAAO;CAClB,WAAW,OAAO;CAClB,YAAY,OAAO;CACnB,YAAY,OAAO;CACnB,cAAc,OAAO;CACrB,UAAU,OAAO;CACjB,eAAe,OAAO;CACtB,YAAY,OAAO;CACnB,YAAY,OAAO;CACnB,WAAW,OAAO;CAClB,YAAY,OAAO;CACnB,kBAAkB,OAAO;CACzB,YAAY,OAAO;CACnB,aAAa,OAAO;CACpB,cAAc,OAAO;CACrB,YAAY,OAAO;CACnB,WAAW,OAAO;CAClB,gBAAgB,OAAO;CACvB,aAAa,OAAO;CACpB,WAAW,OAAO;CAClB,kBAAkB,OAAO;CACzB,cAAc,OAAO;CACrB,cAAc,OAAO;CACrB,YAAY,OAAO;CACnB,YAAY,OAAO;CACnB,YAAY,OAAO;CACnB,WAAW,OAAO;CAClB,cAAc,OAAO;CACrB,aAAa,OAAO;CACpB,YAAY,OAAO;CACnB,WAAW,OAAO;CAClB,kBAAkB,OAAO;CACzB,WAAW,OAAO;CAClB,WAAW,OAAO;CAClB,YAAY,OAAO;AACrB;;AAMA,MAAa,mBAAmB,OAAO,KACrC,cACF;;;;;AAMA,MAAM,iBAAiB;CACrB,MAAM;CACN,OAAO;CACP,KAAK;CACL,SAAS;CACT,IAAI;CACJ,KAAK;CACL,YAAY;CACZ,QAAQ;CACR,KAAK;CACL,KAAK;CACL,IAAI;CACJ,OAAO;CACP,KAAK;CACL,IAAI;CACJ,IAAI;CACJ,KAAK;CACL,KAAK;CACL,KAAK;CACL,SAAS;CACT,KAAK;CACL,MAAM;CACN,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,OAAO;CACP,aAAa;CACb,IAAI;CACJ,KAAK;CACL,KAAK;AACP;AAWA,MAAM,sBAAsB;;;;;;;;;;;;;;;AAgC5B,MAAa,uBACX,aACyB;CACzB,MAAM,sBAAsB,YAAY,GAAE,CAAE,KAAK,CAAC,CAAC,YAAY;CAE/D,MAAM,cACJ,eAAe,uBACd;CAEH,MAAM,cAAc,eAAe;CAEnC,IAAI,CAAC,aACH,OAAO;EAAE,IAAI;EAAqB,aAAa;CAAK;CAGtD,OAAO;EAAE,IAAI;EAAa;CAAY;AACxC"}
@@ -1,6 +1,6 @@
1
1
  import { cn } from "../../utils/cn.mjs";
2
- import { cva } from "class-variance-authority";
3
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { cva } from "class-variance-authority";
4
4
 
5
5
  //#region src/components/Input/Checkbox.tsx
6
6
  const checkboxVariants = cva([
@@ -1,5 +1,5 @@
1
- import { cva } from "class-variance-authority";
2
1
  import { jsx } from "react/jsx-runtime";
2
+ import { cva } from "class-variance-authority";
3
3
 
4
4
  //#region src/components/Input/Input.tsx
5
5
  const cn = (...classes) => classes.filter(Boolean).join(" ");