@ai-react-markdown/core 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -5
- package/dist/index.cjs +13 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -2
- package/dist/index.d.ts +35 -2
- package/dist/index.js +13 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -81,7 +81,7 @@ function StreamingChat({ content, isStreaming }: { content: string; isStreaming:
|
|
|
81
81
|
| ---------------------- | -------------------------------- | ------------------------------- | ---------------------------------------------------------------------- |
|
|
82
82
|
| `content` | `string` | **(required)** | Raw markdown content to render. |
|
|
83
83
|
| `streaming` | `boolean` | `false` | Whether content is actively being streamed (e.g. from an LLM). |
|
|
84
|
-
| `fontSize` | `number \| string` | `'0.
|
|
84
|
+
| `fontSize` | `number \| string` | `'0.9375rem'` | Base font size. Numbers are treated as pixels. |
|
|
85
85
|
| `variant` | `AIMarkdownVariant` | `'default'` | Typography variant name. |
|
|
86
86
|
| `colorScheme` | `AIMarkdownColorScheme` | `'light'` | Color scheme name (`'light'`, `'dark'`, or custom). |
|
|
87
87
|
| `config` | `PartialDeep<TConfig>` | `undefined` | Partial render config, deep-merged with defaults. |
|
|
@@ -209,7 +209,7 @@ The `<AIMarkdown>` component wraps its content in a typography component that co
|
|
|
209
209
|
The built-in `DefaultTypography` renders a `<div>` with CSS class names for the active variant and color scheme:
|
|
210
210
|
|
|
211
211
|
```html
|
|
212
|
-
<div class="aim-typography-root default light" style="width: 100%; font-size: 0.
|
|
212
|
+
<div class="aim-typography-root default light" style="width: 100%; font-size: 0.9375rem">
|
|
213
213
|
<!-- markdown content -->
|
|
214
214
|
</div>
|
|
215
215
|
```
|
|
@@ -222,14 +222,14 @@ import '@ai-react-markdown/core/typography/default.css';
|
|
|
222
222
|
|
|
223
223
|
### Custom Typography Component
|
|
224
224
|
|
|
225
|
-
Replace the typography wrapper by passing a custom component:
|
|
225
|
+
Replace the typography wrapper by passing a custom component. The `style` prop carries CSS custom properties injected by the core renderer — **merge it onto your root element** so that descendant CSS can reference these variables:
|
|
226
226
|
|
|
227
227
|
```tsx
|
|
228
228
|
import type { AIMarkdownTypographyProps } from '@ai-react-markdown/core';
|
|
229
229
|
|
|
230
|
-
function MyTypography({ children, fontSize, variant, colorScheme }: AIMarkdownTypographyProps) {
|
|
230
|
+
function MyTypography({ children, fontSize, variant, colorScheme, style }: AIMarkdownTypographyProps) {
|
|
231
231
|
return (
|
|
232
|
-
<div className={`my-markdown ${colorScheme}`} style={{ fontSize }}>
|
|
232
|
+
<div className={`my-markdown ${colorScheme}`} style={{ fontSize, ...style }}>
|
|
233
233
|
{children}
|
|
234
234
|
</div>
|
|
235
235
|
);
|
|
@@ -238,6 +238,16 @@ function MyTypography({ children, fontSize, variant, colorScheme }: AIMarkdownTy
|
|
|
238
238
|
<AIMarkdown content={markdown} Typography={MyTypography} />;
|
|
239
239
|
```
|
|
240
240
|
|
|
241
|
+
#### Injected CSS Custom Properties
|
|
242
|
+
|
|
243
|
+
The core renderer injects the following CSS custom properties via the Typography `style` prop:
|
|
244
|
+
|
|
245
|
+
| Variable | Value | Purpose |
|
|
246
|
+
| ---------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
247
|
+
| `--aim-font-size-root` | `fontSize` prop | Absolute font-size anchor for the component instance. Inner CSS can use `var(--aim-font-size-root)` to bypass `em` compounding in deeply nested markdown structures (e.g. code inside blockquotes). |
|
|
248
|
+
|
|
249
|
+
**Why `--aim-font-size-root`?** Markdown content frequently nests elements that use relative `em` units — blockquotes, lists, code blocks. Each nesting level compounds the effective size: a `0.875em` code span inside a `1.125em` blockquote resolves to `0.984em` of the parent, not `0.875em` of the root. This variable provides a stable, absolute reference that inner CSS rules can use to opt out of compounding when a fixed size is needed.
|
|
250
|
+
|
|
241
251
|
### Extra Styles Wrapper
|
|
242
252
|
|
|
243
253
|
The `ExtraStyles` prop accepts a component rendered between the typography wrapper and the markdown content. Useful for injecting additional CSS scope or theme providers:
|
package/dist/index.cjs
CHANGED
|
@@ -406,11 +406,11 @@ function useStableValue(value) {
|
|
|
406
406
|
// src/components/typography/Default.tsx
|
|
407
407
|
var import_react4 = require("react");
|
|
408
408
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
409
|
-
var DefaultTypography = (0, import_react4.memo)(({ children, fontSize, variant, colorScheme }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
409
|
+
var DefaultTypography = (0, import_react4.memo)(({ children, fontSize, variant, colorScheme, style }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
410
410
|
"div",
|
|
411
411
|
{
|
|
412
412
|
className: `aim-typography-root ${variant ?? ""} ${colorScheme ?? ""}`.trim(),
|
|
413
|
-
style: { width: "100%", fontSize },
|
|
413
|
+
style: { width: "100%", fontSize, ...style },
|
|
414
414
|
children
|
|
415
415
|
}
|
|
416
416
|
));
|
|
@@ -433,7 +433,7 @@ var AIMarkdownComponent = ({
|
|
|
433
433
|
variant = "default",
|
|
434
434
|
colorScheme = "light"
|
|
435
435
|
}) => {
|
|
436
|
-
const usedFontSize = fontSize ? typeof fontSize === "number" ? `${fontSize}px` : fontSize : "0.
|
|
436
|
+
const usedFontSize = fontSize ? typeof fontSize === "number" ? `${fontSize}px` : fontSize : "0.9375rem";
|
|
437
437
|
const stableDefaultConfig = useStableValue(defaultConfig);
|
|
438
438
|
const stableConfig = useStableValue(config);
|
|
439
439
|
const stablePreprocessors = useStableValue(contentPreprocessors);
|
|
@@ -451,7 +451,16 @@ var AIMarkdownComponent = ({
|
|
|
451
451
|
colorScheme,
|
|
452
452
|
defaultConfig: stableDefaultConfig,
|
|
453
453
|
config: stableConfig,
|
|
454
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
454
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
455
|
+
Typography,
|
|
456
|
+
{
|
|
457
|
+
fontSize: usedFontSize,
|
|
458
|
+
variant,
|
|
459
|
+
colorScheme,
|
|
460
|
+
style: { "--aim-font-size-root": usedFontSize },
|
|
461
|
+
children: ExtraStyles ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ExtraStyles, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MarkdownContent_default, { content: usedContent, customComponents: stableCustomComponents }) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MarkdownContent_default, { content: usedContent, customComponents: stableCustomComponents })
|
|
462
|
+
}
|
|
463
|
+
)
|
|
455
464
|
}
|
|
456
465
|
) });
|
|
457
466
|
};
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.tsx","../src/context.tsx","../src/defs.ts","../src/preprocessors/latex.ts","../src/preprocessors/index.ts","../src/components/MarkdownContent.tsx","../src/hooks/useStableValue.ts","../src/components/typography/Default.tsx"],"sourcesContent":["/**\n * @ai-react-markdown/core\n *\n * A batteries-included React component for rendering AI-generated markdown\n * with first-class support for LaTeX math, GFM, CJK text, syntax highlighting,\n * and streaming content.\n *\n * ## Quick Start\n *\n * ```tsx\n * import AIMarkdown from '@ai-react-markdown/core';\n * import '@ai-react-markdown/core/typography/default.css';\n *\n * function App() {\n * return <AIMarkdown content=\"Hello **world**!\" />;\n * }\n * ```\n *\n * @module @ai-react-markdown/core\n */\n\n'use client';\n\nimport { useMemo, memo } from 'react';\nimport AIMarkdownRenderStateProvider, {\n AIMarkdownMetadataProvider,\n AIMarkdownRenderStateProviderProps,\n AIMarkdownMetadataProviderProps,\n} from './context';\nimport { AIMDContentPreprocessor } from './preprocessors/defs';\nimport preprocessAIMDContent from './preprocessors';\nimport AIMarkdownContent from './components/MarkdownContent';\nimport {\n AIMarkdownCustomComponents,\n AIMarkdownRenderConfig,\n AIMarkdownMetadata,\n AIMarkdownTypographyComponent,\n AIMarkdownExtraStylesComponent,\n AIMarkdownVariant,\n AIMarkdownColorScheme,\n} from './defs';\nimport useStableValue from './hooks/useStableValue';\nimport DefaultTypography from './components/typography/Default';\n\n/**\n * Props for the `<AIMarkdown>` component.\n *\n * @typeParam TConfig - Custom render configuration type (extends {@link AIMarkdownRenderConfig}).\n * @typeParam TRenderData - Custom metadata type (extends {@link AIMarkdownMetadata}).\n */\nexport interface AIMarkdownProps<\n TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig,\n TRenderData extends AIMarkdownMetadata = AIMarkdownMetadata,\n>\n extends\n Omit<AIMarkdownRenderStateProviderProps<TConfig>, 'streaming' | 'fontSize' | 'variant' | 'colorScheme'>,\n AIMarkdownMetadataProviderProps<TRenderData> {\n /**\n * Whether content is actively being streamed (e.g. token-by-token from an LLM).\n * When `true`, the flag is propagated via context so custom components can adapt\n * their behavior (show cursors, disable copy buttons, skip animations, etc.).\n * Defaults to `false`.\n */\n streaming?: boolean;\n /**\n * Base font size for the rendered output.\n * Accepts a CSS length string (e.g. `'14px'`, `'0.875rem'`) or a number\n * which is treated as pixels. Defaults to `'0.875rem'`.\n */\n fontSize?: number | string;\n /** Raw markdown content to render. */\n content: string;\n /**\n * Additional preprocessors to run on the raw markdown before rendering.\n * These run *after* the built-in LaTeX preprocessor.\n */\n contentPreprocessors?: AIMDContentPreprocessor[];\n /**\n * Custom `react-markdown` component overrides.\n * Use this to replace the default renderers for specific HTML elements\n * (e.g. code blocks, links, images).\n */\n customComponents?: AIMarkdownCustomComponents;\n /**\n * Typography wrapper component. Receives `fontSize`, `variant`, and `colorScheme`.\n * Defaults to the built-in {@link DefaultTypography}.\n */\n Typography?: AIMarkdownTypographyComponent;\n /**\n * Optional extra style wrapper component rendered between the typography\n * wrapper and the markdown content. Useful for injecting additional\n * CSS scope or theme providers.\n */\n ExtraStyles?: AIMarkdownExtraStylesComponent;\n /** Typography variant name. Defaults to `'default'`. */\n variant?: AIMarkdownVariant;\n /** Color scheme name. Defaults to `'light'`. */\n colorScheme?: AIMarkdownColorScheme;\n}\n\n/**\n * Root component that preprocesses markdown content and renders it through\n * a configurable remark/rehype pipeline wrapped in typography and style layers.\n */\nconst AIMarkdownComponent = <\n TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig,\n TRenderData extends AIMarkdownMetadata = AIMarkdownMetadata,\n>({\n streaming = false,\n content,\n fontSize,\n contentPreprocessors,\n customComponents,\n defaultConfig,\n config,\n metadata,\n Typography = DefaultTypography,\n ExtraStyles,\n variant = 'default',\n colorScheme = 'light',\n}: AIMarkdownProps<TConfig, TRenderData>) => {\n // Normalize fontSize: number -> px string, undefined -> default rem value.\n const usedFontSize = fontSize ? (typeof fontSize === 'number' ? `${fontSize}px` : fontSize) : '0.875rem';\n\n // Stabilize object/array props to prevent unnecessary re-renders\n // when the consumer creates new references on each render.\n const stableDefaultConfig = useStableValue(defaultConfig);\n const stableConfig = useStableValue(config);\n const stablePreprocessors = useStableValue(contentPreprocessors);\n const stableCustomComponents = useStableValue(customComponents);\n\n // Run the preprocessing pipeline (LaTeX normalization + user preprocessors).\n const usedContent = useMemo(\n () => (content ? preprocessAIMDContent(content, stablePreprocessors) : content),\n [content, stablePreprocessors]\n );\n\n return (\n <AIMarkdownMetadataProvider<TRenderData> metadata={metadata}>\n <AIMarkdownRenderStateProvider<TConfig>\n streaming={streaming}\n fontSize={usedFontSize}\n variant={variant}\n colorScheme={colorScheme}\n defaultConfig={stableDefaultConfig}\n config={stableConfig}\n >\n <Typography fontSize={usedFontSize} variant={variant} colorScheme={colorScheme}>\n {ExtraStyles ? (\n <ExtraStyles>\n <AIMarkdownContent content={usedContent} customComponents={stableCustomComponents} />\n </ExtraStyles>\n ) : (\n <AIMarkdownContent content={usedContent} customComponents={stableCustomComponents} />\n )}\n </Typography>\n </AIMarkdownRenderStateProvider>\n </AIMarkdownMetadataProvider>\n );\n};\n\n/**\n * A React component for rendering AI-generated markdown with rich formatting support.\n *\n * Features:\n * - GFM (tables, strikethrough, task lists, autolinks)\n * - LaTeX math rendering via KaTeX\n * - Emoji shortcodes\n * - CJK-friendly line breaking and spacing\n * - Configurable syntax extensions (highlight, definition lists, super/subscript)\n * - Configurable display optimizations (SmartyPants, pangu, comment removal)\n * - Streaming-aware rendering\n * - Customizable typography, color scheme, and component overrides\n *\n * @example\n * ```tsx\n * <AIMarkdown\n * content={markdownString}\n * streaming={isStreaming}\n * colorScheme=\"dark\"\n * config={{ extraSyntaxSupported: [AIMarkdownRenderExtraSyntax.HIGHLIGHT] }}\n * />\n * ```\n */\nconst AIMarkdown = memo(AIMarkdownComponent);\nAIMarkdown.displayName = 'AIMarkdown';\n\nexport default AIMarkdown as typeof AIMarkdownComponent;\n\n// ── Public API re-exports ───────────────────────────────────────────────────\n\n// Types\nexport type { AIMDContentPreprocessor };\nexport type {\n AIMarkdownCustomComponents,\n AIMarkdownRenderConfig,\n AIMarkdownRenderState,\n AIMarkdownMetadata,\n AIMarkdownTypographyProps,\n AIMarkdownTypographyComponent,\n AIMarkdownExtraStylesProps,\n AIMarkdownExtraStylesComponent,\n AIMarkdownVariant,\n AIMarkdownColorScheme,\n} from './defs';\n\n// Enums & Constants\nexport {\n AIMarkdownRenderExtraSyntax,\n AIMarkdownRenderDisplayOptimizeAbility,\n defaultAIMarkdownRenderConfig,\n} from './defs';\n\n// Hooks -- for custom components to access render state & metadata\nexport { useAIMarkdownRenderState, useAIMarkdownMetadata } from './context';\nexport { useStableValue };\n\n// Utils\nexport type { PartialDeep } from './typings/partial-deep';\n","/**\n * React context for the AIMarkdown render state.\n *\n * Provides an immutable {@link AIMarkdownRenderState} object to all descendant\n * components. The provider deep-merges user-supplied partial configuration with\n * the built-in defaults so that consumers always receive a complete config.\n *\n * @module context\n */\n\nimport { PropsWithChildren, createContext, useContext, useMemo } from 'react';\nimport cloneDeep from 'lodash-es/cloneDeep';\nimport mergeWith from 'lodash-es/mergeWith';\nimport {\n AIMarkdownRenderConfig,\n AIMarkdownMetadata,\n AIMarkdownRenderState,\n AIMarkdownVariant,\n AIMarkdownColorScheme,\n defaultAIMarkdownRenderConfig,\n} from './defs';\nimport type { PartialDeep } from './typings/partial-deep';\n\nconst AIMarkdownRenderStateContext = createContext<AIMarkdownRenderState<AIMarkdownRenderConfig> | null>(null);\n\nconst AIMarkdownMetadataContext = createContext<AIMarkdownMetadata | undefined>(undefined);\n\n/**\n * Access the current {@link AIMarkdownRenderState} from within the `<AIMarkdown>` tree.\n *\n * Must be called inside a component rendered as a descendant of `<AIMarkdown>`.\n * Throws if called outside the provider boundary.\n *\n * @typeParam TConfig - Expected configuration shape (defaults to {@link AIMarkdownRenderConfig}).\n * @returns The current render state (does not include metadata — use {@link useAIMarkdownMetadata} for that).\n *\n * @example\n * ```tsx\n * function CustomCodeBlock({ children }: PropsWithChildren) {\n * const { streaming, config } = useAIMarkdownRenderState();\n * // ...\n * }\n * ```\n */\nexport function useAIMarkdownRenderState<TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig>() {\n const context = useContext(AIMarkdownRenderStateContext) as AIMarkdownRenderState<TConfig>;\n\n if (!context) {\n throw new Error('useAIMarkdownRenderState must be used within an <AIMarkdown /> component.');\n }\n\n return context;\n}\n\n/**\n * Access the current metadata from within the `<AIMarkdown>` tree.\n *\n * Metadata lives in a separate React context so that changes to metadata\n * do not cause re-renders in components that only consume render state\n * (e.g. {@link MarkdownContent}).\n *\n * @typeParam TMetadata - Expected metadata shape (defaults to {@link AIMarkdownMetadata}).\n * @returns The current metadata, or `undefined` if none was provided.\n */\nexport function useAIMarkdownMetadata<TMetadata extends AIMarkdownMetadata = AIMarkdownMetadata>() {\n return useContext(AIMarkdownMetadataContext) as TMetadata | undefined;\n}\n\n/** Props for {@link AIMarkdownRenderStateProvider}. */\nexport interface AIMarkdownRenderStateProviderProps<\n TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig,\n> extends PropsWithChildren {\n streaming: boolean;\n fontSize: string;\n variant: AIMarkdownVariant;\n colorScheme: AIMarkdownColorScheme;\n /**\n * Base default config to merge against. When omitted, falls back to\n * {@link defaultAIMarkdownRenderConfig}. Sub-packages (e.g. mantine) can\n * pass their own extended defaults here.\n */\n defaultConfig?: TConfig;\n /** Partial config that will be deep-merged with the default config. */\n config?: PartialDeep<TConfig>;\n}\n\n/** Props for {@link AIMarkdownMetadataProvider}. */\nexport interface AIMarkdownMetadataProviderProps<\n TMetadata extends AIMarkdownMetadata = AIMarkdownMetadata,\n> extends PropsWithChildren {\n metadata?: TMetadata;\n}\n\n/**\n * Custom lodash `mergeWith` handler: arrays from the source (user config)\n * fully replace the target (default config) instead of being merged by index.\n */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nconst configMergeCustomizer = (\n _objValue: any,\n srcValue: any,\n _key: string,\n _object: any,\n _source: any,\n _stack: any\n) => {\n if (Array.isArray(srcValue)) {\n return srcValue;\n }\n};\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\n/**\n * Provider that exposes consumer-provided metadata via a dedicated React context.\n * Separated from render state so that metadata changes do not trigger\n * re-renders in components that only consume render state.\n */\nexport const AIMarkdownMetadataProvider = <RDT extends AIMarkdownMetadata = AIMarkdownMetadata>({\n metadata,\n children,\n}: AIMarkdownMetadataProviderProps<RDT>) => {\n return <AIMarkdownMetadataContext.Provider value={metadata}>{children}</AIMarkdownMetadataContext.Provider>;\n};\n\n/**\n * Internal provider that deep-merges user config with defaults and exposes\n * the resulting {@link AIMarkdownRenderState} to the component tree.\n */\nconst AIMarkdownRenderStateProvider = <RCT extends AIMarkdownRenderConfig = AIMarkdownRenderConfig>({\n streaming,\n fontSize,\n variant,\n colorScheme,\n defaultConfig,\n config,\n children,\n}: AIMarkdownRenderStateProviderProps<RCT>) => {\n // Deep-merge user config with defaults; clone first to avoid mutating the frozen default.\n const baseConfig = defaultConfig ?? defaultAIMarkdownRenderConfig;\n const mergedConfig = useMemo(\n () => (config ? mergeWith(cloneDeep(baseConfig), config, configMergeCustomizer) : baseConfig),\n [baseConfig, config]\n );\n\n // Freeze the state object to enforce immutability downstream.\n const state = useMemo(\n () =>\n Object.freeze({\n streaming,\n fontSize,\n variant,\n colorScheme,\n config: mergedConfig,\n }),\n [streaming, fontSize, variant, colorScheme, mergedConfig]\n );\n\n return <AIMarkdownRenderStateContext.Provider value={state}>{children}</AIMarkdownRenderStateContext.Provider>;\n};\n\nexport default AIMarkdownRenderStateProvider;\n","/**\n * Core type definitions, enums, and default configuration for ai-react-markdown.\n *\n * This module defines the public API surface for configuring the renderer,\n * including extra markdown syntax extensions, display optimization abilities,\n * typography theming, and the shared render state shape.\n *\n * @module defs\n */\n\nimport { ComponentType, PropsWithChildren } from 'react';\nimport type { Components } from 'react-markdown';\n\n/**\n * Custom component overrides for the markdown renderer.\n * Alias for `react-markdown`'s `Components` type, re-exported under the\n * library's `AIMarkdown` naming convention so consumers don't need a\n * direct `react-markdown` dependency for type imports.\n */\nexport type AIMarkdownCustomComponents = Components;\n\n/**\n * Extra markdown syntax extensions beyond standard GFM.\n * Enable or disable these via {@link AIMarkdownRenderConfig.extraSyntaxSupported}.\n */\nexport enum AIMarkdownRenderExtraSyntax {\n /** `==Highlight==` syntax support. */\n HIGHLIGHT = 'HIGHLIGHT',\n /** Definition list syntax. @see https://michelf.ca/projects/php-markdown/extra/#def-list */\n DEFINITION_LIST = 'DEFINITION_LIST',\n /** Superscript (`^text^`) and subscript (`~text~`) syntax. */\n SUBSCRIPT = 'SUBSCRIPT',\n}\n\n/**\n * Display optimization abilities applied during markdown processing.\n * Enable or disable these via {@link AIMarkdownRenderConfig.displayOptimizeAbilities}.\n */\nexport enum AIMarkdownRenderDisplayOptimizeAbility {\n /** Strip HTML comments from the content. */\n REMOVE_COMMENTS = 'REMOVE_COMMENTS',\n /** Typographic enhancements via SmartyPants (curly quotes, em-dashes, etc.). @see https://www.npmjs.com/package/smartypants */\n SMARTYPANTS = 'SMARTYPANTS',\n /** Automatically insert spaces between CJK and half-width characters. */\n PANGU = 'PANGU',\n}\n\n/**\n * Configuration object controlling which markdown extensions and\n * display optimizations are active during rendering.\n */\nexport interface AIMarkdownRenderConfig {\n /** Extra syntax extensions to enable. */\n extraSyntaxSupported: AIMarkdownRenderExtraSyntax[];\n /** Display optimization abilities to enable. */\n displayOptimizeAbilities: AIMarkdownRenderDisplayOptimizeAbility[];\n}\n\n/**\n * Sensible default configuration with all extensions and optimizations enabled.\n * Frozen to prevent accidental mutation.\n */\nexport const defaultAIMarkdownRenderConfig: AIMarkdownRenderConfig = Object.freeze({\n extraSyntaxSupported: Object.freeze([\n AIMarkdownRenderExtraSyntax.HIGHLIGHT,\n AIMarkdownRenderExtraSyntax.DEFINITION_LIST,\n AIMarkdownRenderExtraSyntax.SUBSCRIPT,\n ]),\n displayOptimizeAbilities: Object.freeze([\n AIMarkdownRenderDisplayOptimizeAbility.REMOVE_COMMENTS,\n AIMarkdownRenderDisplayOptimizeAbility.SMARTYPANTS,\n AIMarkdownRenderDisplayOptimizeAbility.PANGU,\n ]),\n}) as AIMarkdownRenderConfig;\n\n/**\n * Arbitrary metadata that consumers can pass through a dedicated React context.\n * Custom renderers can access this via the {@link useAIMarkdownMetadata} hook.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface AIMarkdownMetadata extends Record<string, any> {}\n\n/**\n * Typography variant identifier. Built-in variant is `'default'`;\n * consumers may define additional variants via custom typography components.\n */\nexport type AIMarkdownVariant = 'default' | (string & {});\n\n/**\n * Color scheme identifier. Built-in schemes are `'light'` and `'dark'`;\n * consumers may define additional schemes via custom typography CSS.\n */\nexport type AIMarkdownColorScheme = 'light' | 'dark' | (string & {});\n\n/** Props accepted by a typography wrapper component. */\nexport interface AIMarkdownTypographyProps extends PropsWithChildren {\n /** Resolved CSS font-size value (e.g. `'14px'`, `'0.875rem'`). */\n fontSize: string;\n /** Active typography variant. */\n variant?: AIMarkdownVariant;\n /** Active color scheme. */\n colorScheme?: AIMarkdownColorScheme;\n}\n\n/** React component type for the typography wrapper. */\nexport type AIMarkdownTypographyComponent = ComponentType<AIMarkdownTypographyProps>;\n\n/** Props accepted by an optional extra style wrapper component. */\nexport interface AIMarkdownExtraStylesProps extends PropsWithChildren {}\n\n/** React component type for an optional extra style wrapper. */\nexport type AIMarkdownExtraStylesComponent = ComponentType<AIMarkdownExtraStylesProps>;\n\n/**\n * Immutable render state exposed to all descendant components via React context.\n * Access this with the {@link useAIMarkdownRenderState} hook.\n *\n * Metadata is provided via a separate context — use {@link useAIMarkdownMetadata} instead.\n *\n * @typeParam TConfig - Render configuration type (defaults to {@link AIMarkdownRenderConfig}).\n */\nexport interface AIMarkdownRenderState<TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig> {\n /** Whether the content is currently being streamed (e.g. from an LLM). */\n streaming: boolean;\n /** Resolved CSS font-size value. */\n fontSize: string;\n /** Active typography variant. */\n variant: AIMarkdownVariant;\n /** Active color scheme. */\n colorScheme: AIMarkdownColorScheme;\n /** Active render configuration. */\n config: TConfig;\n}\n","/**\n * LaTeX preprocessing pipeline.\n *\n * Normalizes raw markdown so that LaTeX expressions survive the remark/rehype\n * rendering pipeline intact. The main entry point is {@link preprocessLaTeX},\n * which splits content into protected regions (code blocks, inline code, HTML\n * tags) and applies a sequence of transformations to the unprotected text:\n *\n * 1. Escape mhchem commands (`\\ce`, `\\pu`)\n * 2. Escape currency dollar signs (e.g. `$100`, `$1,000.50`)\n * 3. Convert bracket delimiters (`\\[...\\]`, `\\(...\\)`) to dollar delimiters\n * 4. Escape pipes inside LaTeX to prevent GFM table interference\n * 5. Escape underscores inside `\\text{...}` commands\n * 6. Convert single-dollar delimiters to double-dollar delimiters\n *\n * Thanks to the implementations from the following repositories:\n * - https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n * - https://github.com/danny-avila/LibreChat/blob/main/client/src/utils/latex.ts\n *\n * @module preprocessors/latex\n */\n\ninterface Segment {\n text: string;\n isCode: boolean;\n}\n\n/**\n * Split content into alternating text and protected segments.\n * Protected segments (isCode: true) are excluded from LaTeX processing:\n * - ``` multiline code blocks\n * - ` inline code\n * - HTML tags (e.g. <span>$</span> where $ should not be treated as LaTeX)\n */\nfunction splitByProtectedRegions(content: string): Segment[] {\n const segments: Segment[] = [];\n let lastIndex = 0;\n let inlineStart = -1;\n let multilineStart = -1;\n\n function pushProtected(start: number, end: number) {\n if (start > lastIndex) {\n segments.push({ text: content.substring(lastIndex, start), isCode: false });\n }\n segments.push({ text: content.substring(start, end), isCode: true });\n lastIndex = end;\n }\n\n for (let i = 0; i < content.length; i++) {\n const char = content[i];\n\n // Check for multiline code blocks\n if (char === '`' && i + 2 < content.length && content[i + 1] === '`' && content[i + 2] === '`') {\n if (multilineStart === -1) {\n // Cancel any pending inline code — ``` takes priority over `\n inlineStart = -1;\n multilineStart = i;\n i += 2;\n } else {\n pushProtected(multilineStart, i + 3);\n multilineStart = -1;\n i += 2;\n }\n }\n // Check for inline code (only if not in multiline)\n else if (char === '`' && multilineStart === -1) {\n if (inlineStart === -1) {\n inlineStart = i;\n } else {\n pushProtected(inlineStart, i + 1);\n inlineStart = -1;\n }\n }\n // Check for HTML tags (only if not in code block)\n else if (char === '<' && multilineStart === -1 && inlineStart === -1) {\n // Only match known HTML tags to avoid false positives with angle brackets\n // in markdown links (<Slides Demo>), math comparisons ($a < b$), etc.\n const rest = content.substring(i);\n const tagMatch = rest.match(\n /^<\\/?(span|div|p|br|hr|img|a|em|strong|b|i|u|s|sub|sup|code|pre|table|tr|td|th|thead|tbody|tfoot|ul|ol|li|dl|dt|dd|h[1-6]|blockquote|details|summary|figure|figcaption|section|article|aside|nav|header|footer|main|mark|del|ins|small|abbr|cite|dfn|kbd|samp|var|ruby|rt|rp|bdo|wbr|input|button|select|textarea|label|fieldset|legend|output|iframe|video|audio|source|canvas|svg|math|time)(?:\\s[^>]*)?\\/?>/i\n );\n if (tagMatch) {\n pushProtected(i, i + tagMatch[0].length);\n i += tagMatch[0].length - 1; // -1 because loop does i++\n }\n }\n }\n\n // Push remaining text\n if (lastIndex < content.length) {\n segments.push({ text: content.substring(lastIndex), isCode: false });\n }\n\n return segments;\n}\n\n/**\n * Escape mhchem commands in LaTeX expressions to ensure proper rendering.\n *\n * @param text Input string containing LaTeX expressions with mhchem commands\n * @returns String with escaped mhchem commands\n * @from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nfunction escapeMhchemCommands(text: string) {\n return text.replaceAll('$\\\\ce{', '$\\\\\\\\ce{').replaceAll('$\\\\pu{', '$\\\\\\\\pu{');\n}\n\nconst CURRENCY_REGEX = /(?<![\\\\$])\\$(?!\\$)(?=\\d+(?:,\\d{3})*(?:\\.\\d+)?(?:[KMBkmb])?(?:\\s|$|[^a-zA-Z\\d]))/g;\nconst NO_ESCAPED_DOLLAR_REGEX = /(?<![\\\\$])\\$(?!\\$)/g;\n// Match \\[...\\] and \\(...\\) as LaTeX delimiters, but exclude:\n// - !\\[...\\] (markdown image)\n// - \\[...\\]( (markdown link)\nconst DELIMITERS_REGEX = /(?<!!)\\\\\\[([\\S\\s]*?[^\\\\])\\\\](?!\\()|\\\\\\((.*?)\\\\\\)/g;\nconst ARRAY_COL_SPEC_OR_PIPE_REGEX = /(\\\\begin\\{(?:array|tabular[x*]?)\\}\\{[^}]*\\})|(?<!\\\\)\\|/g;\n// Display $$ allows multiline; inline $ forbids newlines (consistent with SINGLE_DOLLAR_REGEX)\nconst LATEX_BLOCK_REGEX = /\\$\\$([\\S\\s]*?)\\$\\$|(?<![\\\\$])\\$(?!\\$)((?:[^$\\n]|\\\\\\$)*?)(?<![\\\\`])\\$(?!\\$)/g;\nconst ESCAPE_TEXT_UNDERSCORES_REGEX = /\\\\text{([^}]*)}/g;\nconst SINGLE_DOLLAR_REGEX = /(?<![\\\\$])\\$(?!\\$)((?:[^$\\n]|\\\\[$])+?)(?<!\\\\)(?<!`)\\$(?!\\$)/g;\n\n/**\n * Escape currency dollar signs (e.g. $100, $1,000.50) so they are not\n * misinterpreted as LaTeX delimiters.\n *\n * The tricky part: a `$` followed by digits might still be inside a LaTeX\n * expression (e.g. `$8.29 \\text{ B} \\times 4$`). We detect this by checking\n * whether there is an odd number of unescaped `$` on the same line after the\n * current match — if so, the current `$` is a LaTeX opener, not currency.\n */\nfunction escapeCurrencyDollarSigns(text: string): string {\n const parts: string[] = [];\n let lastIndex = 0;\n const currencyMatches = Array.from(text.matchAll(CURRENCY_REGEX));\n\n for (let i = 0; i < currencyMatches.length; i++) {\n const match = currencyMatches[i];\n parts.push(text.substring(lastIndex, match.index));\n\n let needEscape = true;\n let restBeforeNextMatchOrEnd = '';\n if (i < currencyMatches.length - 1) {\n const nextMatch = currencyMatches[i + 1];\n if (nextMatch.index - match.index > 1) {\n restBeforeNextMatchOrEnd = text.substring(match.index + 1, nextMatch.index);\n }\n } else {\n restBeforeNextMatchOrEnd = text.substring(match.index + 1);\n }\n const firstLineBeforeNextMatch = restBeforeNextMatchOrEnd.split(/\\r\\n|\\r|\\n/g)[0];\n if (Array.from(firstLineBeforeNextMatch.matchAll(NO_ESCAPED_DOLLAR_REGEX)).length % 2 !== 0) {\n const previousNewContent = parts.join('');\n const previousLastLineContent = previousNewContent.split(/\\r\\n|\\r|\\n/g).pop();\n const wholeLineBeforeNextMatchWithoutCurrentDollar = previousLastLineContent + firstLineBeforeNextMatch;\n if (Array.from(wholeLineBeforeNextMatchWithoutCurrentDollar.matchAll(NO_ESCAPED_DOLLAR_REGEX)).length % 2 !== 0) {\n needEscape = false;\n }\n }\n\n parts.push(needEscape ? '\\\\$' : '$');\n lastIndex = match.index + 1;\n }\n parts.push(text.substring(lastIndex));\n return parts.join('');\n}\n\n/**\n * Convert LaTeX bracket delimiters to dollar sign delimiters.\n * Converts \\[...\\] to $$...$$ and \\(...\\) to $...$\n *\n * @param text Input string containing LaTeX expressions\n * @returns String with LaTeX bracket delimiters converted to dollar sign delimiters\n * @modified from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nfunction convertLatexDelimiters(text: string): string {\n return text.replaceAll(\n DELIMITERS_REGEX,\n (match: string, squareBracket: string | undefined, roundBracket: string | undefined): string => {\n if (squareBracket !== undefined) {\n return `$$${squareBracket}$$`;\n } else if (roundBracket !== undefined) {\n return `$${roundBracket}$`;\n }\n return match;\n }\n );\n}\n\n/**\n * Helper function: replace unescaped pipes with \\vert in LaTeX math fragments\n * @from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nconst replaceUnescapedPipes = (formula: string): string =>\n // Use \\vert{} so the control sequence terminates before the next token.\n // Preserve `|` inside \\begin{array}{...} / \\begin{tabular}{...} column specifiers.\n formula.replaceAll(ARRAY_COL_SPEC_OR_PIPE_REGEX, (match, colSpec: string | undefined) =>\n colSpec !== undefined ? match : '\\\\vert{}'\n );\n/**\n * Escape pipes in LaTeX expressions to prevent them from being interpreted as\n * column separators in markdown tables.\n *\n * @param text Input string containing LaTeX expressions\n * @returns String with pipes escaped in LaTeX expressions\n * @modified from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nfunction escapeLatexPipes(text: string): string {\n return text.replaceAll(LATEX_BLOCK_REGEX, (match, display, inline) => {\n if (display !== undefined) return `$$${replaceUnescapedPipes(display)}$$`;\n if (inline !== undefined) return `$${replaceUnescapedPipes(inline)}$`;\n return match;\n });\n}\n\n/**\n * Escape unescaped underscores within \\text{...} commands in LaTeX expressions.\n * For example, \\text{node_domain} becomes \\text{node\\_domain},\n * but \\text{node\\_domain} remains \\text{node\\_domain}.\n *\n * @param text Input string that may contain LaTeX expressions\n * @returns String with unescaped underscores escaped within \\text{...} commands\n * @modified from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nfunction escapeTextUnderscores(text: string): string {\n return text.replaceAll(ESCAPE_TEXT_UNDERSCORES_REGEX, (_match, textContent: string) => {\n const escapedTextContent = textContent.replaceAll(/(?<!\\\\)_/g, '\\\\_');\n return `\\\\text{${escapedTextContent}}`;\n });\n}\n\n/**\n * Convert single dollar delimiters to double dollar delimiters.\n * e.g. $x^2$ → $$x^2$$\n */\nfunction convertSingleToDoubleDollar(text: string): string {\n return text.replaceAll(SINGLE_DOLLAR_REGEX, (_match, content: string) => `$$${content}$$`);\n}\n\n/**\n * Main LaTeX preprocessor entry point.\n *\n * Splits the input into protected regions (code blocks, inline code, HTML tags)\n * and applies the full normalization pipeline to unprotected text segments.\n * Returns the input unchanged when no LaTeX-related characters (`$`, `\\[`, `\\(`)\n * are detected.\n *\n * @param str - Raw markdown string.\n * @returns The preprocessed string with normalized LaTeX delimiters.\n */\nexport function preprocessLaTeX(str: string): string {\n // Return early if no LaTeX patterns are found\n if (!str.includes('$') && !str.includes('\\\\[') && !str.includes('\\\\(')) return str;\n\n // Step 1: split by code blocks\n const segments = splitByProtectedRegions(str);\n\n // Step 2: process each non-code segment through the LaTeX pipeline\n const result = segments.map((segment) => {\n if (segment.isCode) return segment.text;\n\n let text = segment.text;\n text = escapeMhchemCommands(text);\n text = escapeCurrencyDollarSigns(text);\n text = convertLatexDelimiters(text);\n text = escapeLatexPipes(text);\n text = escapeTextUnderscores(text);\n text = convertSingleToDoubleDollar(text);\n return text;\n });\n\n return result.join('');\n}\n","/**\n * Content preprocessing pipeline.\n *\n * Runs all preprocessors (built-in + user-supplied) in sequence before\n * the markdown string is handed to react-markdown. The built-in LaTeX\n * preprocessor always runs first, followed by any extra preprocessors\n * provided by the consumer.\n *\n * @module preprocessors\n */\n\nimport { AIMDContentPreprocessor } from './defs';\nimport { preprocessLaTeX } from './latex';\n\n/** Sequentially apply an array of preprocessor functions via left-fold. */\nfunction applyPreprocessors(value: string, ...fns: Array<AIMDContentPreprocessor>): string {\n return fns.reduce((result, fn) => fn(result), value);\n}\n\n/** Stable empty array to avoid re-renders when no extra preprocessors are given. */\nconst defaultExtraPreprocessors: AIMDContentPreprocessor[] = [];\n\n/**\n * Run the full preprocessing pipeline on raw markdown content.\n *\n * @param content - Raw markdown string.\n * @param extraPreprocessors - Optional user-supplied preprocessors appended after the built-in ones.\n * @returns The preprocessed markdown string ready for rendering.\n */\nexport default function preprocessAIMDContent(\n content: string,\n extraPreprocessors: AIMDContentPreprocessor[] = defaultExtraPreprocessors\n) {\n return applyPreprocessors(content, preprocessLaTeX, ...extraPreprocessors);\n}\n","/**\n * Core markdown rendering component.\n *\n * Wraps `react-markdown` with a curated set of remark and rehype plugins\n * for GFM, math/LaTeX, emoji, CJK support, and configurable extra syntax\n * extensions and display optimizations. Plugin selection is driven by the\n * {@link AIMarkdownRenderConfig} from context.\n *\n * @module components/MarkdownContent\n */\n\nimport { memo, useMemo } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport rehypeKatex from 'rehype-katex';\nimport rehypeRaw from 'rehype-raw';\nimport rehypeUnwrapImages from 'rehype-unwrap-images';\nimport rehypeSanitize, { defaultSchema } from 'rehype-sanitize';\nimport remarkBreaks from 'remark-breaks';\nimport remarkCjkFriendly from 'remark-cjk-friendly';\nimport remarkCjkFriendlyGfmStrikethrough from 'remark-cjk-friendly-gfm-strikethrough';\nimport remarkEmoji from 'remark-emoji';\nimport remarkGfm from 'remark-gfm';\nimport remarkMath from 'remark-math';\nimport { remarkDefinitionList, defListHastHandlers } from 'remark-definition-list';\nimport remarkSupersub from 'remark-supersub';\nimport { remarkMark as remarkMarkHighlight } from 'remark-mark-highlight';\nimport remarkSqueezeParagraphs from 'remark-squeeze-paragraphs';\nimport remarkSmartypants from 'remark-smartypants';\nimport remarkPangu from 'remark-pangu';\nimport remarkRemoveComments from 'remark-remove-comments';\nimport { useAIMarkdownRenderState } from '../context';\nimport {\n AIMarkdownCustomComponents,\n AIMarkdownRenderDisplayOptimizeAbility,\n AIMarkdownRenderExtraSyntax,\n} from '../defs';\n\n/** Maps display optimization abilities to their corresponding remark plugins. */\nconst DisplayOptimizeRemarkPluginMap = {\n [AIMarkdownRenderDisplayOptimizeAbility.REMOVE_COMMENTS]: remarkRemoveComments,\n [AIMarkdownRenderDisplayOptimizeAbility.SMARTYPANTS]: remarkSmartypants,\n [AIMarkdownRenderDisplayOptimizeAbility.PANGU]: remarkPangu,\n};\n\n/** Maps extra syntax extensions to their corresponding remark plugins. */\nconst ExtraSyntaxRemarkPluginMap = {\n [AIMarkdownRenderExtraSyntax.HIGHLIGHT]: remarkMarkHighlight,\n [AIMarkdownRenderExtraSyntax.DEFINITION_LIST]: remarkDefinitionList,\n [AIMarkdownRenderExtraSyntax.SUBSCRIPT]: remarkSupersub,\n};\n\n/** Stable empty object to avoid unnecessary re-renders when no custom components are given. */\nconst DefaultCustomComponents: AIMarkdownCustomComponents = {};\n\ninterface AIMarkdownContentProps {\n /** Preprocessed markdown string to render. */\n content: string;\n /** Optional react-markdown component overrides (e.g. custom code block renderer). */\n customComponents?: AIMarkdownCustomComponents;\n}\n\n/**\n * Internal component that assembles the remark/rehype plugin chain based on\n * the current render config and delegates to `ReactMarkdown`.\n */\nconst AIMarkdownContent = memo(({ content, customComponents }: AIMarkdownContentProps) => {\n const { config } = useAIMarkdownRenderState();\n\n // Resolve extra-syntax remark plugins and check if definition list HAST handlers are needed.\n const { extraSyntaxRemarkPlugins, enableDefinitionList } = useMemo(\n () => ({\n extraSyntaxRemarkPlugins: config.extraSyntaxSupported.map((syntax) => ExtraSyntaxRemarkPluginMap[syntax]),\n enableDefinitionList: config.extraSyntaxSupported.includes(AIMarkdownRenderExtraSyntax.DEFINITION_LIST),\n }),\n [config.extraSyntaxSupported]\n );\n\n const displayOptimizeRemarkPlugins = useMemo(() => {\n return config.displayOptimizeAbilities.map((ability) => DisplayOptimizeRemarkPluginMap[ability]);\n }, [config.displayOptimizeAbilities]);\n\n const usedComponents = useMemo(() => {\n return customComponents ? { ...DefaultCustomComponents, ...customComponents } : DefaultCustomComponents;\n }, [customComponents]);\n\n return (\n <ReactMarkdown\n remarkPlugins={[\n // --- Core plugins (always active) ---\n remarkGfm,\n [\n remarkMath,\n {\n // Disable single-dollar inline math to avoid conflicts with currency\n // signs and other dollar usages; the preprocessor converts $...$ to $$...$$.\n singleDollarTextMath: false,\n },\n ],\n // --- Configurable extra syntax plugins ---\n ...extraSyntaxRemarkPlugins,\n // --- Formatting & normalization ---\n remarkBreaks,\n remarkEmoji,\n remarkSqueezeParagraphs,\n remarkCjkFriendly,\n remarkCjkFriendlyGfmStrikethrough,\n // --- Configurable display optimizations ---\n ...displayOptimizeRemarkPlugins,\n ]}\n rehypePlugins={[\n // Allow raw HTML through so rehype-sanitize can handle it.\n [\n rehypeRaw,\n {\n passThrough: [],\n },\n ],\n // Sanitize HTML while allowing <mark> (highlight) and KaTeX class names.\n [\n rehypeSanitize,\n {\n ...defaultSchema,\n tagNames: [...(defaultSchema.tagNames || []), 'mark'],\n attributes: {\n ...defaultSchema.attributes,\n // The `language-*` regex is allowed by default.\n code: [['className', /^language-./, 'math-inline', 'math-display']],\n },\n },\n ],\n rehypeKatex,\n rehypeUnwrapImages,\n ]}\n remarkRehypeOptions={{\n allowDangerousHtml: true,\n handlers: {\n // Inject definition-list HAST handlers when the extension is active.\n ...(enableDefinitionList ? defListHastHandlers : {}),\n },\n }}\n components={usedComponents}\n // NOTE: The default `urlTransform` in Windows environments treats local\n // paths (e.g. `C:/...`) as unsafe. Uncomment the line below if needed:\n // urlTransform={(url: string) => url}\n >\n {content}\n </ReactMarkdown>\n );\n});\n\nAIMarkdownContent.displayName = 'AIMarkdownContent';\n\nexport default AIMarkdownContent;\n","/**\n * Hook for referential stability of deep-equal values.\n *\n * @module hooks/useStableValue\n */\n\nimport { useRef, useEffect } from 'react';\nimport isEqual from 'lodash-es/isEqual';\n\n/**\n * Returns a referentially stable version of `value`.\n *\n * On each render the new value is deep-compared (via `lodash/isEqual`) against\n * the previous one. If they are structurally equal the *previous* reference is\n * returned, preventing unnecessary re-renders in downstream `useMemo` / `useEffect`\n * consumers that depend on reference equality.\n *\n * @typeParam T - The value type.\n * @param value - The potentially new value to stabilize.\n * @returns The previous reference when deep-equal, otherwise the new value.\n *\n * @example\n * ```tsx\n * const stableConfig = useStableValue(config);\n * // stableConfig keeps the same reference as long as config is deep-equal.\n * ```\n */\nexport default function useStableValue<T>(value: T): T {\n const ref = useRef(value);\n\n // eslint-disable-next-line react-hooks/refs\n const prev = ref.current;\n const stableValue = isEqual(prev, value) ? prev : value;\n\n useEffect(() => {\n ref.current = stableValue;\n }, [stableValue]);\n\n return stableValue;\n}\n","/**\n * Default typography wrapper component.\n *\n * Renders a `<div>` container that applies CSS class names for the active\n * variant and color scheme, and sets the root font-size as an inline style.\n * The corresponding CSS custom properties are defined in the SCSS variant\n * files under `typography/variants/`.\n *\n * Consumers can replace this with a custom {@link AIMarkdownTypographyComponent}\n * via the `Typography` prop on `<AIMarkdown>`.\n *\n * @module components/typography/Default\n */\n\nimport { memo } from 'react';\nimport type { AIMarkdownTypographyProps } from '../../defs';\n\nconst DefaultTypography = memo(({ children, fontSize, variant, colorScheme }: AIMarkdownTypographyProps) => (\n <div\n className={`aim-typography-root ${variant ?? ''} ${colorScheme ?? ''}`.trim()}\n style={{ width: '100%', fontSize }}\n >\n {children}\n </div>\n));\n\nDefaultTypography.displayName = 'DefaultTypography';\n\nexport default DefaultTypography;\n"],"mappings":"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","names":["import_react","AIMarkdownRenderExtraSyntax","AIMarkdownRenderDisplayOptimizeAbility","mergeWith","cloneDeep","import_react","import_jsx_runtime","remarkRemoveComments","remarkSmartypants","remarkPangu","remarkMarkHighlight","remarkSupersub","ReactMarkdown","remarkGfm","remarkMath","remarkBreaks","remarkEmoji","remarkSqueezeParagraphs","remarkCjkFriendly","remarkCjkFriendlyGfmStrikethrough","rehypeRaw","rehypeSanitize","rehypeKatex","rehypeUnwrapImages","import_react","isEqual","import_react","import_jsx_runtime","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx","../src/context.tsx","../src/defs.ts","../src/preprocessors/latex.ts","../src/preprocessors/index.ts","../src/components/MarkdownContent.tsx","../src/hooks/useStableValue.ts","../src/components/typography/Default.tsx"],"sourcesContent":["/**\n * @ai-react-markdown/core\n *\n * A batteries-included React component for rendering AI-generated markdown\n * with first-class support for LaTeX math, GFM, CJK text, syntax highlighting,\n * and streaming content.\n *\n * ## Quick Start\n *\n * ```tsx\n * import AIMarkdown from '@ai-react-markdown/core';\n * import '@ai-react-markdown/core/typography/default.css';\n *\n * function App() {\n * return <AIMarkdown content=\"Hello **world**!\" />;\n * }\n * ```\n *\n * @module @ai-react-markdown/core\n */\n\n'use client';\n\nimport { useMemo, memo, type CSSProperties } from 'react';\nimport AIMarkdownRenderStateProvider, {\n AIMarkdownMetadataProvider,\n AIMarkdownRenderStateProviderProps,\n AIMarkdownMetadataProviderProps,\n} from './context';\nimport { AIMDContentPreprocessor } from './preprocessors/defs';\nimport preprocessAIMDContent from './preprocessors';\nimport AIMarkdownContent from './components/MarkdownContent';\nimport {\n AIMarkdownCustomComponents,\n AIMarkdownRenderConfig,\n AIMarkdownMetadata,\n AIMarkdownTypographyComponent,\n AIMarkdownExtraStylesComponent,\n AIMarkdownVariant,\n AIMarkdownColorScheme,\n} from './defs';\nimport useStableValue from './hooks/useStableValue';\nimport DefaultTypography from './components/typography/Default';\n\n/**\n * Props for the `<AIMarkdown>` component.\n *\n * @typeParam TConfig - Custom render configuration type (extends {@link AIMarkdownRenderConfig}).\n * @typeParam TRenderData - Custom metadata type (extends {@link AIMarkdownMetadata}).\n */\nexport interface AIMarkdownProps<\n TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig,\n TRenderData extends AIMarkdownMetadata = AIMarkdownMetadata,\n>\n extends\n Omit<AIMarkdownRenderStateProviderProps<TConfig>, 'streaming' | 'fontSize' | 'variant' | 'colorScheme'>,\n AIMarkdownMetadataProviderProps<TRenderData> {\n /**\n * Whether content is actively being streamed (e.g. token-by-token from an LLM).\n * When `true`, the flag is propagated via context so custom components can adapt\n * their behavior (show cursors, disable copy buttons, skip animations, etc.).\n * Defaults to `false`.\n */\n streaming?: boolean;\n /**\n * Base font size for the rendered output.\n * Accepts a CSS length string (e.g. `'14px'`, `'0.875rem'`) or a number\n * which is treated as pixels. Defaults to `'0.9375rem'`.\n */\n fontSize?: number | string;\n /** Raw markdown content to render. */\n content: string;\n /**\n * Additional preprocessors to run on the raw markdown before rendering.\n * These run *after* the built-in LaTeX preprocessor.\n */\n contentPreprocessors?: AIMDContentPreprocessor[];\n /**\n * Custom `react-markdown` component overrides.\n * Use this to replace the default renderers for specific HTML elements\n * (e.g. code blocks, links, images).\n */\n customComponents?: AIMarkdownCustomComponents;\n /**\n * Typography wrapper component. Receives `fontSize`, `variant`, and `colorScheme`.\n * Defaults to the built-in {@link DefaultTypography}.\n */\n Typography?: AIMarkdownTypographyComponent;\n /**\n * Optional extra style wrapper component rendered between the typography\n * wrapper and the markdown content. Useful for injecting additional\n * CSS scope or theme providers.\n */\n ExtraStyles?: AIMarkdownExtraStylesComponent;\n /** Typography variant name. Defaults to `'default'`. */\n variant?: AIMarkdownVariant;\n /** Color scheme name. Defaults to `'light'`. */\n colorScheme?: AIMarkdownColorScheme;\n}\n\n/**\n * Root component that preprocesses markdown content and renders it through\n * a configurable remark/rehype pipeline wrapped in typography and style layers.\n */\nconst AIMarkdownComponent = <\n TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig,\n TRenderData extends AIMarkdownMetadata = AIMarkdownMetadata,\n>({\n streaming = false,\n content,\n fontSize,\n contentPreprocessors,\n customComponents,\n defaultConfig,\n config,\n metadata,\n Typography = DefaultTypography,\n ExtraStyles,\n variant = 'default',\n colorScheme = 'light',\n}: AIMarkdownProps<TConfig, TRenderData>) => {\n // Normalize fontSize: number -> px string, undefined -> default rem value.\n const usedFontSize = fontSize ? (typeof fontSize === 'number' ? `${fontSize}px` : fontSize) : '0.9375rem';\n\n // Stabilize object/array props to prevent unnecessary re-renders\n // when the consumer creates new references on each render.\n const stableDefaultConfig = useStableValue(defaultConfig);\n const stableConfig = useStableValue(config);\n const stablePreprocessors = useStableValue(contentPreprocessors);\n const stableCustomComponents = useStableValue(customComponents);\n\n // Run the preprocessing pipeline (LaTeX normalization + user preprocessors).\n const usedContent = useMemo(\n () => (content ? preprocessAIMDContent(content, stablePreprocessors) : content),\n [content, stablePreprocessors]\n );\n\n return (\n <AIMarkdownMetadataProvider<TRenderData> metadata={metadata}>\n <AIMarkdownRenderStateProvider<TConfig>\n streaming={streaming}\n fontSize={usedFontSize}\n variant={variant}\n colorScheme={colorScheme}\n defaultConfig={stableDefaultConfig}\n config={stableConfig}\n >\n <Typography\n fontSize={usedFontSize}\n variant={variant}\n colorScheme={colorScheme}\n // Inject CSS custom properties onto the Typography root element.\n // --aim-font-size-root: absolute font-size anchor so inner CSS can\n // bypass em-compounding in deeply nested markdown structures.\n // See AIMarkdownTypographyProps.style JSDoc for the full variable list.\n style={{ '--aim-font-size-root': usedFontSize } as CSSProperties}\n >\n {ExtraStyles ? (\n <ExtraStyles>\n <AIMarkdownContent content={usedContent} customComponents={stableCustomComponents} />\n </ExtraStyles>\n ) : (\n <AIMarkdownContent content={usedContent} customComponents={stableCustomComponents} />\n )}\n </Typography>\n </AIMarkdownRenderStateProvider>\n </AIMarkdownMetadataProvider>\n );\n};\n\n/**\n * A React component for rendering AI-generated markdown with rich formatting support.\n *\n * Features:\n * - GFM (tables, strikethrough, task lists, autolinks)\n * - LaTeX math rendering via KaTeX\n * - Emoji shortcodes\n * - CJK-friendly line breaking and spacing\n * - Configurable syntax extensions (highlight, definition lists, super/subscript)\n * - Configurable display optimizations (SmartyPants, pangu, comment removal)\n * - Streaming-aware rendering\n * - Customizable typography, color scheme, and component overrides\n *\n * @example\n * ```tsx\n * <AIMarkdown\n * content={markdownString}\n * streaming={isStreaming}\n * colorScheme=\"dark\"\n * config={{ extraSyntaxSupported: [AIMarkdownRenderExtraSyntax.HIGHLIGHT] }}\n * />\n * ```\n */\nconst AIMarkdown = memo(AIMarkdownComponent);\nAIMarkdown.displayName = 'AIMarkdown';\n\nexport default AIMarkdown as typeof AIMarkdownComponent;\n\n// ── Public API re-exports ───────────────────────────────────────────────────\n\n// Types\nexport type { AIMDContentPreprocessor };\nexport type {\n AIMarkdownCustomComponents,\n AIMarkdownRenderConfig,\n AIMarkdownRenderState,\n AIMarkdownMetadata,\n AIMarkdownTypographyProps,\n AIMarkdownTypographyComponent,\n AIMarkdownExtraStylesProps,\n AIMarkdownExtraStylesComponent,\n AIMarkdownVariant,\n AIMarkdownColorScheme,\n} from './defs';\n\n// Enums & Constants\nexport {\n AIMarkdownRenderExtraSyntax,\n AIMarkdownRenderDisplayOptimizeAbility,\n defaultAIMarkdownRenderConfig,\n} from './defs';\n\n// Hooks -- for custom components to access render state & metadata\nexport { useAIMarkdownRenderState, useAIMarkdownMetadata } from './context';\nexport { useStableValue };\n\n// Utils\nexport type { PartialDeep } from './typings/partial-deep';\n","/**\n * React context for the AIMarkdown render state.\n *\n * Provides an immutable {@link AIMarkdownRenderState} object to all descendant\n * components. The provider deep-merges user-supplied partial configuration with\n * the built-in defaults so that consumers always receive a complete config.\n *\n * @module context\n */\n\nimport { PropsWithChildren, createContext, useContext, useMemo } from 'react';\nimport cloneDeep from 'lodash-es/cloneDeep';\nimport mergeWith from 'lodash-es/mergeWith';\nimport {\n AIMarkdownRenderConfig,\n AIMarkdownMetadata,\n AIMarkdownRenderState,\n AIMarkdownVariant,\n AIMarkdownColorScheme,\n defaultAIMarkdownRenderConfig,\n} from './defs';\nimport type { PartialDeep } from './typings/partial-deep';\n\nconst AIMarkdownRenderStateContext = createContext<AIMarkdownRenderState<AIMarkdownRenderConfig> | null>(null);\n\nconst AIMarkdownMetadataContext = createContext<AIMarkdownMetadata | undefined>(undefined);\n\n/**\n * Access the current {@link AIMarkdownRenderState} from within the `<AIMarkdown>` tree.\n *\n * Must be called inside a component rendered as a descendant of `<AIMarkdown>`.\n * Throws if called outside the provider boundary.\n *\n * @typeParam TConfig - Expected configuration shape (defaults to {@link AIMarkdownRenderConfig}).\n * @returns The current render state (does not include metadata — use {@link useAIMarkdownMetadata} for that).\n *\n * @example\n * ```tsx\n * function CustomCodeBlock({ children }: PropsWithChildren) {\n * const { streaming, config } = useAIMarkdownRenderState();\n * // ...\n * }\n * ```\n */\nexport function useAIMarkdownRenderState<TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig>() {\n const context = useContext(AIMarkdownRenderStateContext) as AIMarkdownRenderState<TConfig>;\n\n if (!context) {\n throw new Error('useAIMarkdownRenderState must be used within an <AIMarkdown /> component.');\n }\n\n return context;\n}\n\n/**\n * Access the current metadata from within the `<AIMarkdown>` tree.\n *\n * Metadata lives in a separate React context so that changes to metadata\n * do not cause re-renders in components that only consume render state\n * (e.g. {@link MarkdownContent}).\n *\n * @typeParam TMetadata - Expected metadata shape (defaults to {@link AIMarkdownMetadata}).\n * @returns The current metadata, or `undefined` if none was provided.\n */\nexport function useAIMarkdownMetadata<TMetadata extends AIMarkdownMetadata = AIMarkdownMetadata>() {\n return useContext(AIMarkdownMetadataContext) as TMetadata | undefined;\n}\n\n/** Props for {@link AIMarkdownRenderStateProvider}. */\nexport interface AIMarkdownRenderStateProviderProps<\n TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig,\n> extends PropsWithChildren {\n streaming: boolean;\n fontSize: string;\n variant: AIMarkdownVariant;\n colorScheme: AIMarkdownColorScheme;\n /**\n * Base default config to merge against. When omitted, falls back to\n * {@link defaultAIMarkdownRenderConfig}. Sub-packages (e.g. mantine) can\n * pass their own extended defaults here.\n */\n defaultConfig?: TConfig;\n /** Partial config that will be deep-merged with the default config. */\n config?: PartialDeep<TConfig>;\n}\n\n/** Props for {@link AIMarkdownMetadataProvider}. */\nexport interface AIMarkdownMetadataProviderProps<\n TMetadata extends AIMarkdownMetadata = AIMarkdownMetadata,\n> extends PropsWithChildren {\n metadata?: TMetadata;\n}\n\n/**\n * Custom lodash `mergeWith` handler: arrays from the source (user config)\n * fully replace the target (default config) instead of being merged by index.\n */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nconst configMergeCustomizer = (\n _objValue: any,\n srcValue: any,\n _key: string,\n _object: any,\n _source: any,\n _stack: any\n) => {\n if (Array.isArray(srcValue)) {\n return srcValue;\n }\n};\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\n/**\n * Provider that exposes consumer-provided metadata via a dedicated React context.\n * Separated from render state so that metadata changes do not trigger\n * re-renders in components that only consume render state.\n */\nexport const AIMarkdownMetadataProvider = <RDT extends AIMarkdownMetadata = AIMarkdownMetadata>({\n metadata,\n children,\n}: AIMarkdownMetadataProviderProps<RDT>) => {\n return <AIMarkdownMetadataContext.Provider value={metadata}>{children}</AIMarkdownMetadataContext.Provider>;\n};\n\n/**\n * Internal provider that deep-merges user config with defaults and exposes\n * the resulting {@link AIMarkdownRenderState} to the component tree.\n */\nconst AIMarkdownRenderStateProvider = <RCT extends AIMarkdownRenderConfig = AIMarkdownRenderConfig>({\n streaming,\n fontSize,\n variant,\n colorScheme,\n defaultConfig,\n config,\n children,\n}: AIMarkdownRenderStateProviderProps<RCT>) => {\n // Deep-merge user config with defaults; clone first to avoid mutating the frozen default.\n const baseConfig = defaultConfig ?? defaultAIMarkdownRenderConfig;\n const mergedConfig = useMemo(\n () => (config ? mergeWith(cloneDeep(baseConfig), config, configMergeCustomizer) : baseConfig),\n [baseConfig, config]\n );\n\n // Freeze the state object to enforce immutability downstream.\n const state = useMemo(\n () =>\n Object.freeze({\n streaming,\n fontSize,\n variant,\n colorScheme,\n config: mergedConfig,\n }),\n [streaming, fontSize, variant, colorScheme, mergedConfig]\n );\n\n return <AIMarkdownRenderStateContext.Provider value={state}>{children}</AIMarkdownRenderStateContext.Provider>;\n};\n\nexport default AIMarkdownRenderStateProvider;\n","/**\n * Core type definitions, enums, and default configuration for ai-react-markdown.\n *\n * This module defines the public API surface for configuring the renderer,\n * including extra markdown syntax extensions, display optimization abilities,\n * typography theming, and the shared render state shape.\n *\n * @module defs\n */\n\nimport { ComponentType, CSSProperties, PropsWithChildren } from 'react';\nimport type { Components } from 'react-markdown';\n\n/**\n * Custom component overrides for the markdown renderer.\n * Alias for `react-markdown`'s `Components` type, re-exported under the\n * library's `AIMarkdown` naming convention so consumers don't need a\n * direct `react-markdown` dependency for type imports.\n */\nexport type AIMarkdownCustomComponents = Components;\n\n/**\n * Extra markdown syntax extensions beyond standard GFM.\n * Enable or disable these via {@link AIMarkdownRenderConfig.extraSyntaxSupported}.\n */\nexport enum AIMarkdownRenderExtraSyntax {\n /** `==Highlight==` syntax support. */\n HIGHLIGHT = 'HIGHLIGHT',\n /** Definition list syntax. @see https://michelf.ca/projects/php-markdown/extra/#def-list */\n DEFINITION_LIST = 'DEFINITION_LIST',\n /** Superscript (`^text^`) and subscript (`~text~`) syntax. */\n SUBSCRIPT = 'SUBSCRIPT',\n}\n\n/**\n * Display optimization abilities applied during markdown processing.\n * Enable or disable these via {@link AIMarkdownRenderConfig.displayOptimizeAbilities}.\n */\nexport enum AIMarkdownRenderDisplayOptimizeAbility {\n /** Strip HTML comments from the content. */\n REMOVE_COMMENTS = 'REMOVE_COMMENTS',\n /** Typographic enhancements via SmartyPants (curly quotes, em-dashes, etc.). @see https://www.npmjs.com/package/smartypants */\n SMARTYPANTS = 'SMARTYPANTS',\n /** Automatically insert spaces between CJK and half-width characters. */\n PANGU = 'PANGU',\n}\n\n/**\n * Configuration object controlling which markdown extensions and\n * display optimizations are active during rendering.\n */\nexport interface AIMarkdownRenderConfig {\n /** Extra syntax extensions to enable. */\n extraSyntaxSupported: AIMarkdownRenderExtraSyntax[];\n /** Display optimization abilities to enable. */\n displayOptimizeAbilities: AIMarkdownRenderDisplayOptimizeAbility[];\n}\n\n/**\n * Sensible default configuration with all extensions and optimizations enabled.\n * Frozen to prevent accidental mutation.\n */\nexport const defaultAIMarkdownRenderConfig: AIMarkdownRenderConfig = Object.freeze({\n extraSyntaxSupported: Object.freeze([\n AIMarkdownRenderExtraSyntax.HIGHLIGHT,\n AIMarkdownRenderExtraSyntax.DEFINITION_LIST,\n AIMarkdownRenderExtraSyntax.SUBSCRIPT,\n ]),\n displayOptimizeAbilities: Object.freeze([\n AIMarkdownRenderDisplayOptimizeAbility.REMOVE_COMMENTS,\n AIMarkdownRenderDisplayOptimizeAbility.SMARTYPANTS,\n AIMarkdownRenderDisplayOptimizeAbility.PANGU,\n ]),\n}) as AIMarkdownRenderConfig;\n\n/**\n * Arbitrary metadata that consumers can pass through a dedicated React context.\n * Custom renderers can access this via the {@link useAIMarkdownMetadata} hook.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface AIMarkdownMetadata extends Record<string, any> {}\n\n/**\n * Typography variant identifier. Built-in variant is `'default'`;\n * consumers may define additional variants via custom typography components.\n */\nexport type AIMarkdownVariant = 'default' | (string & {});\n\n/**\n * Color scheme identifier. Built-in schemes are `'light'` and `'dark'`;\n * consumers may define additional schemes via custom typography CSS.\n */\nexport type AIMarkdownColorScheme = 'light' | 'dark' | (string & {});\n\n/** Props accepted by a typography wrapper component. */\nexport interface AIMarkdownTypographyProps extends PropsWithChildren {\n /** Resolved CSS font-size value (e.g. `'14px'`, `'0.875rem'`). */\n fontSize: string;\n /** Active typography variant. */\n variant?: AIMarkdownVariant;\n /** Active color scheme. */\n colorScheme?: AIMarkdownColorScheme;\n /**\n * Inline styles injected by the core renderer. Custom Typography implementations\n * **must** merge this object into their root element's `style` to ensure CSS\n * custom properties set by the core are available to all descendant nodes.\n *\n * ### Currently injected variables\n *\n * | Variable | Value | Purpose |\n * |-------------------------|----------------|----------------------------------------------------------|\n * | `--aim-font-size-root` | `fontSize` prop | Absolute font-size anchor for the component instance. |\n *\n * #### Why `--aim-font-size-root`?\n *\n * Markdown content frequently nests elements that use relative `em` units\n * (blockquotes, lists, code blocks). Each nesting level compounds the\n * effective size — a `0.875em` code span inside a `1.125em` blockquote\n * becomes `0.984375em` of the parent, not `0.875em` of the root.\n *\n * `--aim-font-size-root` provides the component-level root font-size as an\n * absolute reference so that inner CSS rules can use\n * `font-size: var(--aim-font-size-root)` to opt out of `em` compounding\n * when a stable size is needed.\n *\n * @example\n * ```tsx\n * const MyTypography: AIMarkdownTypographyComponent = ({ children, fontSize, style }) => (\n * <div className=\"my-typo\" style={{ fontSize, ...style }}>\n * {children}\n * </div>\n * );\n * ```\n */\n style?: CSSProperties;\n}\n\n/** React component type for the typography wrapper. */\nexport type AIMarkdownTypographyComponent = ComponentType<AIMarkdownTypographyProps>;\n\n/** Props accepted by an optional extra style wrapper component. */\nexport interface AIMarkdownExtraStylesProps extends PropsWithChildren {}\n\n/** React component type for an optional extra style wrapper. */\nexport type AIMarkdownExtraStylesComponent = ComponentType<AIMarkdownExtraStylesProps>;\n\n/**\n * Immutable render state exposed to all descendant components via React context.\n * Access this with the {@link useAIMarkdownRenderState} hook.\n *\n * Metadata is provided via a separate context — use {@link useAIMarkdownMetadata} instead.\n *\n * @typeParam TConfig - Render configuration type (defaults to {@link AIMarkdownRenderConfig}).\n */\nexport interface AIMarkdownRenderState<TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig> {\n /** Whether the content is currently being streamed (e.g. from an LLM). */\n streaming: boolean;\n /** Resolved CSS font-size value. */\n fontSize: string;\n /** Active typography variant. */\n variant: AIMarkdownVariant;\n /** Active color scheme. */\n colorScheme: AIMarkdownColorScheme;\n /** Active render configuration. */\n config: TConfig;\n}\n","/**\n * LaTeX preprocessing pipeline.\n *\n * Normalizes raw markdown so that LaTeX expressions survive the remark/rehype\n * rendering pipeline intact. The main entry point is {@link preprocessLaTeX},\n * which splits content into protected regions (code blocks, inline code, HTML\n * tags) and applies a sequence of transformations to the unprotected text:\n *\n * 1. Escape mhchem commands (`\\ce`, `\\pu`)\n * 2. Escape currency dollar signs (e.g. `$100`, `$1,000.50`)\n * 3. Convert bracket delimiters (`\\[...\\]`, `\\(...\\)`) to dollar delimiters\n * 4. Escape pipes inside LaTeX to prevent GFM table interference\n * 5. Escape underscores inside `\\text{...}` commands\n * 6. Convert single-dollar delimiters to double-dollar delimiters\n *\n * Thanks to the implementations from the following repositories:\n * - https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n * - https://github.com/danny-avila/LibreChat/blob/main/client/src/utils/latex.ts\n *\n * @module preprocessors/latex\n */\n\ninterface Segment {\n text: string;\n isCode: boolean;\n}\n\n/**\n * Split content into alternating text and protected segments.\n * Protected segments (isCode: true) are excluded from LaTeX processing:\n * - ``` multiline code blocks\n * - ` inline code\n * - HTML tags (e.g. <span>$</span> where $ should not be treated as LaTeX)\n */\nfunction splitByProtectedRegions(content: string): Segment[] {\n const segments: Segment[] = [];\n let lastIndex = 0;\n let inlineStart = -1;\n let multilineStart = -1;\n\n function pushProtected(start: number, end: number) {\n if (start > lastIndex) {\n segments.push({ text: content.substring(lastIndex, start), isCode: false });\n }\n segments.push({ text: content.substring(start, end), isCode: true });\n lastIndex = end;\n }\n\n for (let i = 0; i < content.length; i++) {\n const char = content[i];\n\n // Check for multiline code blocks\n if (char === '`' && i + 2 < content.length && content[i + 1] === '`' && content[i + 2] === '`') {\n if (multilineStart === -1) {\n // Cancel any pending inline code — ``` takes priority over `\n inlineStart = -1;\n multilineStart = i;\n i += 2;\n } else {\n pushProtected(multilineStart, i + 3);\n multilineStart = -1;\n i += 2;\n }\n }\n // Check for inline code (only if not in multiline)\n else if (char === '`' && multilineStart === -1) {\n if (inlineStart === -1) {\n inlineStart = i;\n } else {\n pushProtected(inlineStart, i + 1);\n inlineStart = -1;\n }\n }\n // Check for HTML tags (only if not in code block)\n else if (char === '<' && multilineStart === -1 && inlineStart === -1) {\n // Only match known HTML tags to avoid false positives with angle brackets\n // in markdown links (<Slides Demo>), math comparisons ($a < b$), etc.\n const rest = content.substring(i);\n const tagMatch = rest.match(\n /^<\\/?(span|div|p|br|hr|img|a|em|strong|b|i|u|s|sub|sup|code|pre|table|tr|td|th|thead|tbody|tfoot|ul|ol|li|dl|dt|dd|h[1-6]|blockquote|details|summary|figure|figcaption|section|article|aside|nav|header|footer|main|mark|del|ins|small|abbr|cite|dfn|kbd|samp|var|ruby|rt|rp|bdo|wbr|input|button|select|textarea|label|fieldset|legend|output|iframe|video|audio|source|canvas|svg|math|time)(?:\\s[^>]*)?\\/?>/i\n );\n if (tagMatch) {\n pushProtected(i, i + tagMatch[0].length);\n i += tagMatch[0].length - 1; // -1 because loop does i++\n }\n }\n }\n\n // Push remaining text\n if (lastIndex < content.length) {\n segments.push({ text: content.substring(lastIndex), isCode: false });\n }\n\n return segments;\n}\n\n/**\n * Escape mhchem commands in LaTeX expressions to ensure proper rendering.\n *\n * @param text Input string containing LaTeX expressions with mhchem commands\n * @returns String with escaped mhchem commands\n * @from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nfunction escapeMhchemCommands(text: string) {\n return text.replaceAll('$\\\\ce{', '$\\\\\\\\ce{').replaceAll('$\\\\pu{', '$\\\\\\\\pu{');\n}\n\nconst CURRENCY_REGEX = /(?<![\\\\$])\\$(?!\\$)(?=\\d+(?:,\\d{3})*(?:\\.\\d+)?(?:[KMBkmb])?(?:\\s|$|[^a-zA-Z\\d]))/g;\nconst NO_ESCAPED_DOLLAR_REGEX = /(?<![\\\\$])\\$(?!\\$)/g;\n// Match \\[...\\] and \\(...\\) as LaTeX delimiters, but exclude:\n// - !\\[...\\] (markdown image)\n// - \\[...\\]( (markdown link)\nconst DELIMITERS_REGEX = /(?<!!)\\\\\\[([\\S\\s]*?[^\\\\])\\\\](?!\\()|\\\\\\((.*?)\\\\\\)/g;\nconst ARRAY_COL_SPEC_OR_PIPE_REGEX = /(\\\\begin\\{(?:array|tabular[x*]?)\\}\\{[^}]*\\})|(?<!\\\\)\\|/g;\n// Display $$ allows multiline; inline $ forbids newlines (consistent with SINGLE_DOLLAR_REGEX)\nconst LATEX_BLOCK_REGEX = /\\$\\$([\\S\\s]*?)\\$\\$|(?<![\\\\$])\\$(?!\\$)((?:[^$\\n]|\\\\\\$)*?)(?<![\\\\`])\\$(?!\\$)/g;\nconst ESCAPE_TEXT_UNDERSCORES_REGEX = /\\\\text{([^}]*)}/g;\nconst SINGLE_DOLLAR_REGEX = /(?<![\\\\$])\\$(?!\\$)((?:[^$\\n]|\\\\[$])+?)(?<!\\\\)(?<!`)\\$(?!\\$)/g;\n\n/**\n * Escape currency dollar signs (e.g. $100, $1,000.50) so they are not\n * misinterpreted as LaTeX delimiters.\n *\n * The tricky part: a `$` followed by digits might still be inside a LaTeX\n * expression (e.g. `$8.29 \\text{ B} \\times 4$`). We detect this by checking\n * whether there is an odd number of unescaped `$` on the same line after the\n * current match — if so, the current `$` is a LaTeX opener, not currency.\n */\nfunction escapeCurrencyDollarSigns(text: string): string {\n const parts: string[] = [];\n let lastIndex = 0;\n const currencyMatches = Array.from(text.matchAll(CURRENCY_REGEX));\n\n for (let i = 0; i < currencyMatches.length; i++) {\n const match = currencyMatches[i];\n parts.push(text.substring(lastIndex, match.index));\n\n let needEscape = true;\n let restBeforeNextMatchOrEnd = '';\n if (i < currencyMatches.length - 1) {\n const nextMatch = currencyMatches[i + 1];\n if (nextMatch.index - match.index > 1) {\n restBeforeNextMatchOrEnd = text.substring(match.index + 1, nextMatch.index);\n }\n } else {\n restBeforeNextMatchOrEnd = text.substring(match.index + 1);\n }\n const firstLineBeforeNextMatch = restBeforeNextMatchOrEnd.split(/\\r\\n|\\r|\\n/g)[0];\n if (Array.from(firstLineBeforeNextMatch.matchAll(NO_ESCAPED_DOLLAR_REGEX)).length % 2 !== 0) {\n const previousNewContent = parts.join('');\n const previousLastLineContent = previousNewContent.split(/\\r\\n|\\r|\\n/g).pop();\n const wholeLineBeforeNextMatchWithoutCurrentDollar = previousLastLineContent + firstLineBeforeNextMatch;\n if (Array.from(wholeLineBeforeNextMatchWithoutCurrentDollar.matchAll(NO_ESCAPED_DOLLAR_REGEX)).length % 2 !== 0) {\n needEscape = false;\n }\n }\n\n parts.push(needEscape ? '\\\\$' : '$');\n lastIndex = match.index + 1;\n }\n parts.push(text.substring(lastIndex));\n return parts.join('');\n}\n\n/**\n * Convert LaTeX bracket delimiters to dollar sign delimiters.\n * Converts \\[...\\] to $$...$$ and \\(...\\) to $...$\n *\n * @param text Input string containing LaTeX expressions\n * @returns String with LaTeX bracket delimiters converted to dollar sign delimiters\n * @modified from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nfunction convertLatexDelimiters(text: string): string {\n return text.replaceAll(\n DELIMITERS_REGEX,\n (match: string, squareBracket: string | undefined, roundBracket: string | undefined): string => {\n if (squareBracket !== undefined) {\n return `$$${squareBracket}$$`;\n } else if (roundBracket !== undefined) {\n return `$${roundBracket}$`;\n }\n return match;\n }\n );\n}\n\n/**\n * Helper function: replace unescaped pipes with \\vert in LaTeX math fragments\n * @from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nconst replaceUnescapedPipes = (formula: string): string =>\n // Use \\vert{} so the control sequence terminates before the next token.\n // Preserve `|` inside \\begin{array}{...} / \\begin{tabular}{...} column specifiers.\n formula.replaceAll(ARRAY_COL_SPEC_OR_PIPE_REGEX, (match, colSpec: string | undefined) =>\n colSpec !== undefined ? match : '\\\\vert{}'\n );\n/**\n * Escape pipes in LaTeX expressions to prevent them from being interpreted as\n * column separators in markdown tables.\n *\n * @param text Input string containing LaTeX expressions\n * @returns String with pipes escaped in LaTeX expressions\n * @modified from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nfunction escapeLatexPipes(text: string): string {\n return text.replaceAll(LATEX_BLOCK_REGEX, (match, display, inline) => {\n if (display !== undefined) return `$$${replaceUnescapedPipes(display)}$$`;\n if (inline !== undefined) return `$${replaceUnescapedPipes(inline)}$`;\n return match;\n });\n}\n\n/**\n * Escape unescaped underscores within \\text{...} commands in LaTeX expressions.\n * For example, \\text{node_domain} becomes \\text{node\\_domain},\n * but \\text{node\\_domain} remains \\text{node\\_domain}.\n *\n * @param text Input string that may contain LaTeX expressions\n * @returns String with unescaped underscores escaped within \\text{...} commands\n * @modified from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nfunction escapeTextUnderscores(text: string): string {\n return text.replaceAll(ESCAPE_TEXT_UNDERSCORES_REGEX, (_match, textContent: string) => {\n const escapedTextContent = textContent.replaceAll(/(?<!\\\\)_/g, '\\\\_');\n return `\\\\text{${escapedTextContent}}`;\n });\n}\n\n/**\n * Convert single dollar delimiters to double dollar delimiters.\n * e.g. $x^2$ → $$x^2$$\n */\nfunction convertSingleToDoubleDollar(text: string): string {\n return text.replaceAll(SINGLE_DOLLAR_REGEX, (_match, content: string) => `$$${content}$$`);\n}\n\n/**\n * Main LaTeX preprocessor entry point.\n *\n * Splits the input into protected regions (code blocks, inline code, HTML tags)\n * and applies the full normalization pipeline to unprotected text segments.\n * Returns the input unchanged when no LaTeX-related characters (`$`, `\\[`, `\\(`)\n * are detected.\n *\n * @param str - Raw markdown string.\n * @returns The preprocessed string with normalized LaTeX delimiters.\n */\nexport function preprocessLaTeX(str: string): string {\n // Return early if no LaTeX patterns are found\n if (!str.includes('$') && !str.includes('\\\\[') && !str.includes('\\\\(')) return str;\n\n // Step 1: split by code blocks\n const segments = splitByProtectedRegions(str);\n\n // Step 2: process each non-code segment through the LaTeX pipeline\n const result = segments.map((segment) => {\n if (segment.isCode) return segment.text;\n\n let text = segment.text;\n text = escapeMhchemCommands(text);\n text = escapeCurrencyDollarSigns(text);\n text = convertLatexDelimiters(text);\n text = escapeLatexPipes(text);\n text = escapeTextUnderscores(text);\n text = convertSingleToDoubleDollar(text);\n return text;\n });\n\n return result.join('');\n}\n","/**\n * Content preprocessing pipeline.\n *\n * Runs all preprocessors (built-in + user-supplied) in sequence before\n * the markdown string is handed to react-markdown. The built-in LaTeX\n * preprocessor always runs first, followed by any extra preprocessors\n * provided by the consumer.\n *\n * @module preprocessors\n */\n\nimport { AIMDContentPreprocessor } from './defs';\nimport { preprocessLaTeX } from './latex';\n\n/** Sequentially apply an array of preprocessor functions via left-fold. */\nfunction applyPreprocessors(value: string, ...fns: Array<AIMDContentPreprocessor>): string {\n return fns.reduce((result, fn) => fn(result), value);\n}\n\n/** Stable empty array to avoid re-renders when no extra preprocessors are given. */\nconst defaultExtraPreprocessors: AIMDContentPreprocessor[] = [];\n\n/**\n * Run the full preprocessing pipeline on raw markdown content.\n *\n * @param content - Raw markdown string.\n * @param extraPreprocessors - Optional user-supplied preprocessors appended after the built-in ones.\n * @returns The preprocessed markdown string ready for rendering.\n */\nexport default function preprocessAIMDContent(\n content: string,\n extraPreprocessors: AIMDContentPreprocessor[] = defaultExtraPreprocessors\n) {\n return applyPreprocessors(content, preprocessLaTeX, ...extraPreprocessors);\n}\n","/**\n * Core markdown rendering component.\n *\n * Wraps `react-markdown` with a curated set of remark and rehype plugins\n * for GFM, math/LaTeX, emoji, CJK support, and configurable extra syntax\n * extensions and display optimizations. Plugin selection is driven by the\n * {@link AIMarkdownRenderConfig} from context.\n *\n * @module components/MarkdownContent\n */\n\nimport { memo, useMemo } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport rehypeKatex from 'rehype-katex';\nimport rehypeRaw from 'rehype-raw';\nimport rehypeUnwrapImages from 'rehype-unwrap-images';\nimport rehypeSanitize, { defaultSchema } from 'rehype-sanitize';\nimport remarkBreaks from 'remark-breaks';\nimport remarkCjkFriendly from 'remark-cjk-friendly';\nimport remarkCjkFriendlyGfmStrikethrough from 'remark-cjk-friendly-gfm-strikethrough';\nimport remarkEmoji from 'remark-emoji';\nimport remarkGfm from 'remark-gfm';\nimport remarkMath from 'remark-math';\nimport { remarkDefinitionList, defListHastHandlers } from 'remark-definition-list';\nimport remarkSupersub from 'remark-supersub';\nimport { remarkMark as remarkMarkHighlight } from 'remark-mark-highlight';\nimport remarkSqueezeParagraphs from 'remark-squeeze-paragraphs';\nimport remarkSmartypants from 'remark-smartypants';\nimport remarkPangu from 'remark-pangu';\nimport remarkRemoveComments from 'remark-remove-comments';\nimport { useAIMarkdownRenderState } from '../context';\nimport {\n AIMarkdownCustomComponents,\n AIMarkdownRenderDisplayOptimizeAbility,\n AIMarkdownRenderExtraSyntax,\n} from '../defs';\n\n/** Maps display optimization abilities to their corresponding remark plugins. */\nconst DisplayOptimizeRemarkPluginMap = {\n [AIMarkdownRenderDisplayOptimizeAbility.REMOVE_COMMENTS]: remarkRemoveComments,\n [AIMarkdownRenderDisplayOptimizeAbility.SMARTYPANTS]: remarkSmartypants,\n [AIMarkdownRenderDisplayOptimizeAbility.PANGU]: remarkPangu,\n};\n\n/** Maps extra syntax extensions to their corresponding remark plugins. */\nconst ExtraSyntaxRemarkPluginMap = {\n [AIMarkdownRenderExtraSyntax.HIGHLIGHT]: remarkMarkHighlight,\n [AIMarkdownRenderExtraSyntax.DEFINITION_LIST]: remarkDefinitionList,\n [AIMarkdownRenderExtraSyntax.SUBSCRIPT]: remarkSupersub,\n};\n\n/** Stable empty object to avoid unnecessary re-renders when no custom components are given. */\nconst DefaultCustomComponents: AIMarkdownCustomComponents = {};\n\ninterface AIMarkdownContentProps {\n /** Preprocessed markdown string to render. */\n content: string;\n /** Optional react-markdown component overrides (e.g. custom code block renderer). */\n customComponents?: AIMarkdownCustomComponents;\n}\n\n/**\n * Internal component that assembles the remark/rehype plugin chain based on\n * the current render config and delegates to `ReactMarkdown`.\n */\nconst AIMarkdownContent = memo(({ content, customComponents }: AIMarkdownContentProps) => {\n const { config } = useAIMarkdownRenderState();\n\n // Resolve extra-syntax remark plugins and check if definition list HAST handlers are needed.\n const { extraSyntaxRemarkPlugins, enableDefinitionList } = useMemo(\n () => ({\n extraSyntaxRemarkPlugins: config.extraSyntaxSupported.map((syntax) => ExtraSyntaxRemarkPluginMap[syntax]),\n enableDefinitionList: config.extraSyntaxSupported.includes(AIMarkdownRenderExtraSyntax.DEFINITION_LIST),\n }),\n [config.extraSyntaxSupported]\n );\n\n const displayOptimizeRemarkPlugins = useMemo(() => {\n return config.displayOptimizeAbilities.map((ability) => DisplayOptimizeRemarkPluginMap[ability]);\n }, [config.displayOptimizeAbilities]);\n\n const usedComponents = useMemo(() => {\n return customComponents ? { ...DefaultCustomComponents, ...customComponents } : DefaultCustomComponents;\n }, [customComponents]);\n\n return (\n <ReactMarkdown\n remarkPlugins={[\n // --- Core plugins (always active) ---\n remarkGfm,\n [\n remarkMath,\n {\n // Disable single-dollar inline math to avoid conflicts with currency\n // signs and other dollar usages; the preprocessor converts $...$ to $$...$$.\n singleDollarTextMath: false,\n },\n ],\n // --- Configurable extra syntax plugins ---\n ...extraSyntaxRemarkPlugins,\n // --- Formatting & normalization ---\n remarkBreaks,\n remarkEmoji,\n remarkSqueezeParagraphs,\n remarkCjkFriendly,\n remarkCjkFriendlyGfmStrikethrough,\n // --- Configurable display optimizations ---\n ...displayOptimizeRemarkPlugins,\n ]}\n rehypePlugins={[\n // Allow raw HTML through so rehype-sanitize can handle it.\n [\n rehypeRaw,\n {\n passThrough: [],\n },\n ],\n // Sanitize HTML while allowing <mark> (highlight) and KaTeX class names.\n [\n rehypeSanitize,\n {\n ...defaultSchema,\n tagNames: [...(defaultSchema.tagNames || []), 'mark'],\n attributes: {\n ...defaultSchema.attributes,\n // The `language-*` regex is allowed by default.\n code: [['className', /^language-./, 'math-inline', 'math-display']],\n },\n },\n ],\n rehypeKatex,\n rehypeUnwrapImages,\n ]}\n remarkRehypeOptions={{\n allowDangerousHtml: true,\n handlers: {\n // Inject definition-list HAST handlers when the extension is active.\n ...(enableDefinitionList ? defListHastHandlers : {}),\n },\n }}\n components={usedComponents}\n // NOTE: The default `urlTransform` in Windows environments treats local\n // paths (e.g. `C:/...`) as unsafe. Uncomment the line below if needed:\n // urlTransform={(url: string) => url}\n >\n {content}\n </ReactMarkdown>\n );\n});\n\nAIMarkdownContent.displayName = 'AIMarkdownContent';\n\nexport default AIMarkdownContent;\n","/**\n * Hook for referential stability of deep-equal values.\n *\n * @module hooks/useStableValue\n */\n\nimport { useRef, useEffect } from 'react';\nimport isEqual from 'lodash-es/isEqual';\n\n/**\n * Returns a referentially stable version of `value`.\n *\n * On each render the new value is deep-compared (via `lodash/isEqual`) against\n * the previous one. If they are structurally equal the *previous* reference is\n * returned, preventing unnecessary re-renders in downstream `useMemo` / `useEffect`\n * consumers that depend on reference equality.\n *\n * @typeParam T - The value type.\n * @param value - The potentially new value to stabilize.\n * @returns The previous reference when deep-equal, otherwise the new value.\n *\n * @example\n * ```tsx\n * const stableConfig = useStableValue(config);\n * // stableConfig keeps the same reference as long as config is deep-equal.\n * ```\n */\nexport default function useStableValue<T>(value: T): T {\n const ref = useRef(value);\n\n // eslint-disable-next-line react-hooks/refs\n const prev = ref.current;\n const stableValue = isEqual(prev, value) ? prev : value;\n\n useEffect(() => {\n ref.current = stableValue;\n }, [stableValue]);\n\n return stableValue;\n}\n","/**\n * Default typography wrapper component.\n *\n * Renders a `<div>` container that applies CSS class names for the active\n * variant and color scheme, and sets the root font-size as an inline style.\n * The corresponding CSS custom properties are defined in the SCSS variant\n * files under `typography/variants/`.\n *\n * Consumers can replace this with a custom {@link AIMarkdownTypographyComponent}\n * via the `Typography` prop on `<AIMarkdown>`.\n *\n * @module components/typography/Default\n */\n\nimport { memo } from 'react';\nimport type { AIMarkdownTypographyProps } from '../../defs';\n\nconst DefaultTypography = memo(({ children, fontSize, variant, colorScheme, style }: AIMarkdownTypographyProps) => (\n <div\n className={`aim-typography-root ${variant ?? ''} ${colorScheme ?? ''}`.trim()}\n style={{ width: '100%', fontSize, ...style }}\n >\n {children}\n </div>\n));\n\nDefaultTypography.displayName = 'DefaultTypography';\n\nexport default DefaultTypography;\n"],"mappings":"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","names":["import_react","AIMarkdownRenderExtraSyntax","AIMarkdownRenderDisplayOptimizeAbility","mergeWith","cloneDeep","import_react","import_jsx_runtime","remarkRemoveComments","remarkSmartypants","remarkPangu","remarkMarkHighlight","remarkSupersub","ReactMarkdown","remarkGfm","remarkMath","remarkBreaks","remarkEmoji","remarkSqueezeParagraphs","remarkCjkFriendly","remarkCjkFriendlyGfmStrikethrough","rehypeRaw","rehypeSanitize","rehypeKatex","rehypeUnwrapImages","import_react","isEqual","import_react","import_jsx_runtime","import_jsx_runtime"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ComponentType, PropsWithChildren } from 'react';
|
|
2
|
+
import { ComponentType, PropsWithChildren, CSSProperties } from 'react';
|
|
3
3
|
import { Components } from 'react-markdown';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -82,6 +82,39 @@ interface AIMarkdownTypographyProps extends PropsWithChildren {
|
|
|
82
82
|
variant?: AIMarkdownVariant;
|
|
83
83
|
/** Active color scheme. */
|
|
84
84
|
colorScheme?: AIMarkdownColorScheme;
|
|
85
|
+
/**
|
|
86
|
+
* Inline styles injected by the core renderer. Custom Typography implementations
|
|
87
|
+
* **must** merge this object into their root element's `style` to ensure CSS
|
|
88
|
+
* custom properties set by the core are available to all descendant nodes.
|
|
89
|
+
*
|
|
90
|
+
* ### Currently injected variables
|
|
91
|
+
*
|
|
92
|
+
* | Variable | Value | Purpose |
|
|
93
|
+
* |-------------------------|----------------|----------------------------------------------------------|
|
|
94
|
+
* | `--aim-font-size-root` | `fontSize` prop | Absolute font-size anchor for the component instance. |
|
|
95
|
+
*
|
|
96
|
+
* #### Why `--aim-font-size-root`?
|
|
97
|
+
*
|
|
98
|
+
* Markdown content frequently nests elements that use relative `em` units
|
|
99
|
+
* (blockquotes, lists, code blocks). Each nesting level compounds the
|
|
100
|
+
* effective size — a `0.875em` code span inside a `1.125em` blockquote
|
|
101
|
+
* becomes `0.984375em` of the parent, not `0.875em` of the root.
|
|
102
|
+
*
|
|
103
|
+
* `--aim-font-size-root` provides the component-level root font-size as an
|
|
104
|
+
* absolute reference so that inner CSS rules can use
|
|
105
|
+
* `font-size: var(--aim-font-size-root)` to opt out of `em` compounding
|
|
106
|
+
* when a stable size is needed.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```tsx
|
|
110
|
+
* const MyTypography: AIMarkdownTypographyComponent = ({ children, fontSize, style }) => (
|
|
111
|
+
* <div className="my-typo" style={{ fontSize, ...style }}>
|
|
112
|
+
* {children}
|
|
113
|
+
* </div>
|
|
114
|
+
* );
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
style?: CSSProperties;
|
|
85
118
|
}
|
|
86
119
|
/** React component type for the typography wrapper. */
|
|
87
120
|
type AIMarkdownTypographyComponent = ComponentType<AIMarkdownTypographyProps>;
|
|
@@ -568,7 +601,7 @@ interface AIMarkdownProps<TConfig extends AIMarkdownRenderConfig = AIMarkdownRen
|
|
|
568
601
|
/**
|
|
569
602
|
* Base font size for the rendered output.
|
|
570
603
|
* Accepts a CSS length string (e.g. `'14px'`, `'0.875rem'`) or a number
|
|
571
|
-
* which is treated as pixels. Defaults to `'0.
|
|
604
|
+
* which is treated as pixels. Defaults to `'0.9375rem'`.
|
|
572
605
|
*/
|
|
573
606
|
fontSize?: number | string;
|
|
574
607
|
/** Raw markdown content to render. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ComponentType, PropsWithChildren } from 'react';
|
|
2
|
+
import { ComponentType, PropsWithChildren, CSSProperties } from 'react';
|
|
3
3
|
import { Components } from 'react-markdown';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -82,6 +82,39 @@ interface AIMarkdownTypographyProps extends PropsWithChildren {
|
|
|
82
82
|
variant?: AIMarkdownVariant;
|
|
83
83
|
/** Active color scheme. */
|
|
84
84
|
colorScheme?: AIMarkdownColorScheme;
|
|
85
|
+
/**
|
|
86
|
+
* Inline styles injected by the core renderer. Custom Typography implementations
|
|
87
|
+
* **must** merge this object into their root element's `style` to ensure CSS
|
|
88
|
+
* custom properties set by the core are available to all descendant nodes.
|
|
89
|
+
*
|
|
90
|
+
* ### Currently injected variables
|
|
91
|
+
*
|
|
92
|
+
* | Variable | Value | Purpose |
|
|
93
|
+
* |-------------------------|----------------|----------------------------------------------------------|
|
|
94
|
+
* | `--aim-font-size-root` | `fontSize` prop | Absolute font-size anchor for the component instance. |
|
|
95
|
+
*
|
|
96
|
+
* #### Why `--aim-font-size-root`?
|
|
97
|
+
*
|
|
98
|
+
* Markdown content frequently nests elements that use relative `em` units
|
|
99
|
+
* (blockquotes, lists, code blocks). Each nesting level compounds the
|
|
100
|
+
* effective size — a `0.875em` code span inside a `1.125em` blockquote
|
|
101
|
+
* becomes `0.984375em` of the parent, not `0.875em` of the root.
|
|
102
|
+
*
|
|
103
|
+
* `--aim-font-size-root` provides the component-level root font-size as an
|
|
104
|
+
* absolute reference so that inner CSS rules can use
|
|
105
|
+
* `font-size: var(--aim-font-size-root)` to opt out of `em` compounding
|
|
106
|
+
* when a stable size is needed.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```tsx
|
|
110
|
+
* const MyTypography: AIMarkdownTypographyComponent = ({ children, fontSize, style }) => (
|
|
111
|
+
* <div className="my-typo" style={{ fontSize, ...style }}>
|
|
112
|
+
* {children}
|
|
113
|
+
* </div>
|
|
114
|
+
* );
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
style?: CSSProperties;
|
|
85
118
|
}
|
|
86
119
|
/** React component type for the typography wrapper. */
|
|
87
120
|
type AIMarkdownTypographyComponent = ComponentType<AIMarkdownTypographyProps>;
|
|
@@ -568,7 +601,7 @@ interface AIMarkdownProps<TConfig extends AIMarkdownRenderConfig = AIMarkdownRen
|
|
|
568
601
|
/**
|
|
569
602
|
* Base font size for the rendered output.
|
|
570
603
|
* Accepts a CSS length string (e.g. `'14px'`, `'0.875rem'`) or a number
|
|
571
|
-
* which is treated as pixels. Defaults to `'0.
|
|
604
|
+
* which is treated as pixels. Defaults to `'0.9375rem'`.
|
|
572
605
|
*/
|
|
573
606
|
fontSize?: number | string;
|
|
574
607
|
/** Raw markdown content to render. */
|
package/dist/index.js
CHANGED
|
@@ -367,11 +367,11 @@ function useStableValue(value) {
|
|
|
367
367
|
// src/components/typography/Default.tsx
|
|
368
368
|
import { memo as memo2 } from "react";
|
|
369
369
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
370
|
-
var DefaultTypography = memo2(({ children, fontSize, variant, colorScheme }) => /* @__PURE__ */ jsx3(
|
|
370
|
+
var DefaultTypography = memo2(({ children, fontSize, variant, colorScheme, style }) => /* @__PURE__ */ jsx3(
|
|
371
371
|
"div",
|
|
372
372
|
{
|
|
373
373
|
className: `aim-typography-root ${variant ?? ""} ${colorScheme ?? ""}`.trim(),
|
|
374
|
-
style: { width: "100%", fontSize },
|
|
374
|
+
style: { width: "100%", fontSize, ...style },
|
|
375
375
|
children
|
|
376
376
|
}
|
|
377
377
|
));
|
|
@@ -394,7 +394,7 @@ var AIMarkdownComponent = ({
|
|
|
394
394
|
variant = "default",
|
|
395
395
|
colorScheme = "light"
|
|
396
396
|
}) => {
|
|
397
|
-
const usedFontSize = fontSize ? typeof fontSize === "number" ? `${fontSize}px` : fontSize : "0.
|
|
397
|
+
const usedFontSize = fontSize ? typeof fontSize === "number" ? `${fontSize}px` : fontSize : "0.9375rem";
|
|
398
398
|
const stableDefaultConfig = useStableValue(defaultConfig);
|
|
399
399
|
const stableConfig = useStableValue(config);
|
|
400
400
|
const stablePreprocessors = useStableValue(contentPreprocessors);
|
|
@@ -412,7 +412,16 @@ var AIMarkdownComponent = ({
|
|
|
412
412
|
colorScheme,
|
|
413
413
|
defaultConfig: stableDefaultConfig,
|
|
414
414
|
config: stableConfig,
|
|
415
|
-
children: /* @__PURE__ */ jsx4(
|
|
415
|
+
children: /* @__PURE__ */ jsx4(
|
|
416
|
+
Typography,
|
|
417
|
+
{
|
|
418
|
+
fontSize: usedFontSize,
|
|
419
|
+
variant,
|
|
420
|
+
colorScheme,
|
|
421
|
+
style: { "--aim-font-size-root": usedFontSize },
|
|
422
|
+
children: ExtraStyles ? /* @__PURE__ */ jsx4(ExtraStyles, { children: /* @__PURE__ */ jsx4(MarkdownContent_default, { content: usedContent, customComponents: stableCustomComponents }) }) : /* @__PURE__ */ jsx4(MarkdownContent_default, { content: usedContent, customComponents: stableCustomComponents })
|
|
423
|
+
}
|
|
424
|
+
)
|
|
416
425
|
}
|
|
417
426
|
) });
|
|
418
427
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.tsx","../src/context.tsx","../src/defs.ts","../src/preprocessors/latex.ts","../src/preprocessors/index.ts","../src/components/MarkdownContent.tsx","../src/hooks/useStableValue.ts","../src/components/typography/Default.tsx"],"sourcesContent":["/**\n * @ai-react-markdown/core\n *\n * A batteries-included React component for rendering AI-generated markdown\n * with first-class support for LaTeX math, GFM, CJK text, syntax highlighting,\n * and streaming content.\n *\n * ## Quick Start\n *\n * ```tsx\n * import AIMarkdown from '@ai-react-markdown/core';\n * import '@ai-react-markdown/core/typography/default.css';\n *\n * function App() {\n * return <AIMarkdown content=\"Hello **world**!\" />;\n * }\n * ```\n *\n * @module @ai-react-markdown/core\n */\n\n'use client';\n\nimport { useMemo, memo } from 'react';\nimport AIMarkdownRenderStateProvider, {\n AIMarkdownMetadataProvider,\n AIMarkdownRenderStateProviderProps,\n AIMarkdownMetadataProviderProps,\n} from './context';\nimport { AIMDContentPreprocessor } from './preprocessors/defs';\nimport preprocessAIMDContent from './preprocessors';\nimport AIMarkdownContent from './components/MarkdownContent';\nimport {\n AIMarkdownCustomComponents,\n AIMarkdownRenderConfig,\n AIMarkdownMetadata,\n AIMarkdownTypographyComponent,\n AIMarkdownExtraStylesComponent,\n AIMarkdownVariant,\n AIMarkdownColorScheme,\n} from './defs';\nimport useStableValue from './hooks/useStableValue';\nimport DefaultTypography from './components/typography/Default';\n\n/**\n * Props for the `<AIMarkdown>` component.\n *\n * @typeParam TConfig - Custom render configuration type (extends {@link AIMarkdownRenderConfig}).\n * @typeParam TRenderData - Custom metadata type (extends {@link AIMarkdownMetadata}).\n */\nexport interface AIMarkdownProps<\n TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig,\n TRenderData extends AIMarkdownMetadata = AIMarkdownMetadata,\n>\n extends\n Omit<AIMarkdownRenderStateProviderProps<TConfig>, 'streaming' | 'fontSize' | 'variant' | 'colorScheme'>,\n AIMarkdownMetadataProviderProps<TRenderData> {\n /**\n * Whether content is actively being streamed (e.g. token-by-token from an LLM).\n * When `true`, the flag is propagated via context so custom components can adapt\n * their behavior (show cursors, disable copy buttons, skip animations, etc.).\n * Defaults to `false`.\n */\n streaming?: boolean;\n /**\n * Base font size for the rendered output.\n * Accepts a CSS length string (e.g. `'14px'`, `'0.875rem'`) or a number\n * which is treated as pixels. Defaults to `'0.875rem'`.\n */\n fontSize?: number | string;\n /** Raw markdown content to render. */\n content: string;\n /**\n * Additional preprocessors to run on the raw markdown before rendering.\n * These run *after* the built-in LaTeX preprocessor.\n */\n contentPreprocessors?: AIMDContentPreprocessor[];\n /**\n * Custom `react-markdown` component overrides.\n * Use this to replace the default renderers for specific HTML elements\n * (e.g. code blocks, links, images).\n */\n customComponents?: AIMarkdownCustomComponents;\n /**\n * Typography wrapper component. Receives `fontSize`, `variant`, and `colorScheme`.\n * Defaults to the built-in {@link DefaultTypography}.\n */\n Typography?: AIMarkdownTypographyComponent;\n /**\n * Optional extra style wrapper component rendered between the typography\n * wrapper and the markdown content. Useful for injecting additional\n * CSS scope or theme providers.\n */\n ExtraStyles?: AIMarkdownExtraStylesComponent;\n /** Typography variant name. Defaults to `'default'`. */\n variant?: AIMarkdownVariant;\n /** Color scheme name. Defaults to `'light'`. */\n colorScheme?: AIMarkdownColorScheme;\n}\n\n/**\n * Root component that preprocesses markdown content and renders it through\n * a configurable remark/rehype pipeline wrapped in typography and style layers.\n */\nconst AIMarkdownComponent = <\n TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig,\n TRenderData extends AIMarkdownMetadata = AIMarkdownMetadata,\n>({\n streaming = false,\n content,\n fontSize,\n contentPreprocessors,\n customComponents,\n defaultConfig,\n config,\n metadata,\n Typography = DefaultTypography,\n ExtraStyles,\n variant = 'default',\n colorScheme = 'light',\n}: AIMarkdownProps<TConfig, TRenderData>) => {\n // Normalize fontSize: number -> px string, undefined -> default rem value.\n const usedFontSize = fontSize ? (typeof fontSize === 'number' ? `${fontSize}px` : fontSize) : '0.875rem';\n\n // Stabilize object/array props to prevent unnecessary re-renders\n // when the consumer creates new references on each render.\n const stableDefaultConfig = useStableValue(defaultConfig);\n const stableConfig = useStableValue(config);\n const stablePreprocessors = useStableValue(contentPreprocessors);\n const stableCustomComponents = useStableValue(customComponents);\n\n // Run the preprocessing pipeline (LaTeX normalization + user preprocessors).\n const usedContent = useMemo(\n () => (content ? preprocessAIMDContent(content, stablePreprocessors) : content),\n [content, stablePreprocessors]\n );\n\n return (\n <AIMarkdownMetadataProvider<TRenderData> metadata={metadata}>\n <AIMarkdownRenderStateProvider<TConfig>\n streaming={streaming}\n fontSize={usedFontSize}\n variant={variant}\n colorScheme={colorScheme}\n defaultConfig={stableDefaultConfig}\n config={stableConfig}\n >\n <Typography fontSize={usedFontSize} variant={variant} colorScheme={colorScheme}>\n {ExtraStyles ? (\n <ExtraStyles>\n <AIMarkdownContent content={usedContent} customComponents={stableCustomComponents} />\n </ExtraStyles>\n ) : (\n <AIMarkdownContent content={usedContent} customComponents={stableCustomComponents} />\n )}\n </Typography>\n </AIMarkdownRenderStateProvider>\n </AIMarkdownMetadataProvider>\n );\n};\n\n/**\n * A React component for rendering AI-generated markdown with rich formatting support.\n *\n * Features:\n * - GFM (tables, strikethrough, task lists, autolinks)\n * - LaTeX math rendering via KaTeX\n * - Emoji shortcodes\n * - CJK-friendly line breaking and spacing\n * - Configurable syntax extensions (highlight, definition lists, super/subscript)\n * - Configurable display optimizations (SmartyPants, pangu, comment removal)\n * - Streaming-aware rendering\n * - Customizable typography, color scheme, and component overrides\n *\n * @example\n * ```tsx\n * <AIMarkdown\n * content={markdownString}\n * streaming={isStreaming}\n * colorScheme=\"dark\"\n * config={{ extraSyntaxSupported: [AIMarkdownRenderExtraSyntax.HIGHLIGHT] }}\n * />\n * ```\n */\nconst AIMarkdown = memo(AIMarkdownComponent);\nAIMarkdown.displayName = 'AIMarkdown';\n\nexport default AIMarkdown as typeof AIMarkdownComponent;\n\n// ── Public API re-exports ───────────────────────────────────────────────────\n\n// Types\nexport type { AIMDContentPreprocessor };\nexport type {\n AIMarkdownCustomComponents,\n AIMarkdownRenderConfig,\n AIMarkdownRenderState,\n AIMarkdownMetadata,\n AIMarkdownTypographyProps,\n AIMarkdownTypographyComponent,\n AIMarkdownExtraStylesProps,\n AIMarkdownExtraStylesComponent,\n AIMarkdownVariant,\n AIMarkdownColorScheme,\n} from './defs';\n\n// Enums & Constants\nexport {\n AIMarkdownRenderExtraSyntax,\n AIMarkdownRenderDisplayOptimizeAbility,\n defaultAIMarkdownRenderConfig,\n} from './defs';\n\n// Hooks -- for custom components to access render state & metadata\nexport { useAIMarkdownRenderState, useAIMarkdownMetadata } from './context';\nexport { useStableValue };\n\n// Utils\nexport type { PartialDeep } from './typings/partial-deep';\n","/**\n * React context for the AIMarkdown render state.\n *\n * Provides an immutable {@link AIMarkdownRenderState} object to all descendant\n * components. The provider deep-merges user-supplied partial configuration with\n * the built-in defaults so that consumers always receive a complete config.\n *\n * @module context\n */\n\nimport { PropsWithChildren, createContext, useContext, useMemo } from 'react';\nimport cloneDeep from 'lodash-es/cloneDeep';\nimport mergeWith from 'lodash-es/mergeWith';\nimport {\n AIMarkdownRenderConfig,\n AIMarkdownMetadata,\n AIMarkdownRenderState,\n AIMarkdownVariant,\n AIMarkdownColorScheme,\n defaultAIMarkdownRenderConfig,\n} from './defs';\nimport type { PartialDeep } from './typings/partial-deep';\n\nconst AIMarkdownRenderStateContext = createContext<AIMarkdownRenderState<AIMarkdownRenderConfig> | null>(null);\n\nconst AIMarkdownMetadataContext = createContext<AIMarkdownMetadata | undefined>(undefined);\n\n/**\n * Access the current {@link AIMarkdownRenderState} from within the `<AIMarkdown>` tree.\n *\n * Must be called inside a component rendered as a descendant of `<AIMarkdown>`.\n * Throws if called outside the provider boundary.\n *\n * @typeParam TConfig - Expected configuration shape (defaults to {@link AIMarkdownRenderConfig}).\n * @returns The current render state (does not include metadata — use {@link useAIMarkdownMetadata} for that).\n *\n * @example\n * ```tsx\n * function CustomCodeBlock({ children }: PropsWithChildren) {\n * const { streaming, config } = useAIMarkdownRenderState();\n * // ...\n * }\n * ```\n */\nexport function useAIMarkdownRenderState<TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig>() {\n const context = useContext(AIMarkdownRenderStateContext) as AIMarkdownRenderState<TConfig>;\n\n if (!context) {\n throw new Error('useAIMarkdownRenderState must be used within an <AIMarkdown /> component.');\n }\n\n return context;\n}\n\n/**\n * Access the current metadata from within the `<AIMarkdown>` tree.\n *\n * Metadata lives in a separate React context so that changes to metadata\n * do not cause re-renders in components that only consume render state\n * (e.g. {@link MarkdownContent}).\n *\n * @typeParam TMetadata - Expected metadata shape (defaults to {@link AIMarkdownMetadata}).\n * @returns The current metadata, or `undefined` if none was provided.\n */\nexport function useAIMarkdownMetadata<TMetadata extends AIMarkdownMetadata = AIMarkdownMetadata>() {\n return useContext(AIMarkdownMetadataContext) as TMetadata | undefined;\n}\n\n/** Props for {@link AIMarkdownRenderStateProvider}. */\nexport interface AIMarkdownRenderStateProviderProps<\n TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig,\n> extends PropsWithChildren {\n streaming: boolean;\n fontSize: string;\n variant: AIMarkdownVariant;\n colorScheme: AIMarkdownColorScheme;\n /**\n * Base default config to merge against. When omitted, falls back to\n * {@link defaultAIMarkdownRenderConfig}. Sub-packages (e.g. mantine) can\n * pass their own extended defaults here.\n */\n defaultConfig?: TConfig;\n /** Partial config that will be deep-merged with the default config. */\n config?: PartialDeep<TConfig>;\n}\n\n/** Props for {@link AIMarkdownMetadataProvider}. */\nexport interface AIMarkdownMetadataProviderProps<\n TMetadata extends AIMarkdownMetadata = AIMarkdownMetadata,\n> extends PropsWithChildren {\n metadata?: TMetadata;\n}\n\n/**\n * Custom lodash `mergeWith` handler: arrays from the source (user config)\n * fully replace the target (default config) instead of being merged by index.\n */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nconst configMergeCustomizer = (\n _objValue: any,\n srcValue: any,\n _key: string,\n _object: any,\n _source: any,\n _stack: any\n) => {\n if (Array.isArray(srcValue)) {\n return srcValue;\n }\n};\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\n/**\n * Provider that exposes consumer-provided metadata via a dedicated React context.\n * Separated from render state so that metadata changes do not trigger\n * re-renders in components that only consume render state.\n */\nexport const AIMarkdownMetadataProvider = <RDT extends AIMarkdownMetadata = AIMarkdownMetadata>({\n metadata,\n children,\n}: AIMarkdownMetadataProviderProps<RDT>) => {\n return <AIMarkdownMetadataContext.Provider value={metadata}>{children}</AIMarkdownMetadataContext.Provider>;\n};\n\n/**\n * Internal provider that deep-merges user config with defaults and exposes\n * the resulting {@link AIMarkdownRenderState} to the component tree.\n */\nconst AIMarkdownRenderStateProvider = <RCT extends AIMarkdownRenderConfig = AIMarkdownRenderConfig>({\n streaming,\n fontSize,\n variant,\n colorScheme,\n defaultConfig,\n config,\n children,\n}: AIMarkdownRenderStateProviderProps<RCT>) => {\n // Deep-merge user config with defaults; clone first to avoid mutating the frozen default.\n const baseConfig = defaultConfig ?? defaultAIMarkdownRenderConfig;\n const mergedConfig = useMemo(\n () => (config ? mergeWith(cloneDeep(baseConfig), config, configMergeCustomizer) : baseConfig),\n [baseConfig, config]\n );\n\n // Freeze the state object to enforce immutability downstream.\n const state = useMemo(\n () =>\n Object.freeze({\n streaming,\n fontSize,\n variant,\n colorScheme,\n config: mergedConfig,\n }),\n [streaming, fontSize, variant, colorScheme, mergedConfig]\n );\n\n return <AIMarkdownRenderStateContext.Provider value={state}>{children}</AIMarkdownRenderStateContext.Provider>;\n};\n\nexport default AIMarkdownRenderStateProvider;\n","/**\n * Core type definitions, enums, and default configuration for ai-react-markdown.\n *\n * This module defines the public API surface for configuring the renderer,\n * including extra markdown syntax extensions, display optimization abilities,\n * typography theming, and the shared render state shape.\n *\n * @module defs\n */\n\nimport { ComponentType, PropsWithChildren } from 'react';\nimport type { Components } from 'react-markdown';\n\n/**\n * Custom component overrides for the markdown renderer.\n * Alias for `react-markdown`'s `Components` type, re-exported under the\n * library's `AIMarkdown` naming convention so consumers don't need a\n * direct `react-markdown` dependency for type imports.\n */\nexport type AIMarkdownCustomComponents = Components;\n\n/**\n * Extra markdown syntax extensions beyond standard GFM.\n * Enable or disable these via {@link AIMarkdownRenderConfig.extraSyntaxSupported}.\n */\nexport enum AIMarkdownRenderExtraSyntax {\n /** `==Highlight==` syntax support. */\n HIGHLIGHT = 'HIGHLIGHT',\n /** Definition list syntax. @see https://michelf.ca/projects/php-markdown/extra/#def-list */\n DEFINITION_LIST = 'DEFINITION_LIST',\n /** Superscript (`^text^`) and subscript (`~text~`) syntax. */\n SUBSCRIPT = 'SUBSCRIPT',\n}\n\n/**\n * Display optimization abilities applied during markdown processing.\n * Enable or disable these via {@link AIMarkdownRenderConfig.displayOptimizeAbilities}.\n */\nexport enum AIMarkdownRenderDisplayOptimizeAbility {\n /** Strip HTML comments from the content. */\n REMOVE_COMMENTS = 'REMOVE_COMMENTS',\n /** Typographic enhancements via SmartyPants (curly quotes, em-dashes, etc.). @see https://www.npmjs.com/package/smartypants */\n SMARTYPANTS = 'SMARTYPANTS',\n /** Automatically insert spaces between CJK and half-width characters. */\n PANGU = 'PANGU',\n}\n\n/**\n * Configuration object controlling which markdown extensions and\n * display optimizations are active during rendering.\n */\nexport interface AIMarkdownRenderConfig {\n /** Extra syntax extensions to enable. */\n extraSyntaxSupported: AIMarkdownRenderExtraSyntax[];\n /** Display optimization abilities to enable. */\n displayOptimizeAbilities: AIMarkdownRenderDisplayOptimizeAbility[];\n}\n\n/**\n * Sensible default configuration with all extensions and optimizations enabled.\n * Frozen to prevent accidental mutation.\n */\nexport const defaultAIMarkdownRenderConfig: AIMarkdownRenderConfig = Object.freeze({\n extraSyntaxSupported: Object.freeze([\n AIMarkdownRenderExtraSyntax.HIGHLIGHT,\n AIMarkdownRenderExtraSyntax.DEFINITION_LIST,\n AIMarkdownRenderExtraSyntax.SUBSCRIPT,\n ]),\n displayOptimizeAbilities: Object.freeze([\n AIMarkdownRenderDisplayOptimizeAbility.REMOVE_COMMENTS,\n AIMarkdownRenderDisplayOptimizeAbility.SMARTYPANTS,\n AIMarkdownRenderDisplayOptimizeAbility.PANGU,\n ]),\n}) as AIMarkdownRenderConfig;\n\n/**\n * Arbitrary metadata that consumers can pass through a dedicated React context.\n * Custom renderers can access this via the {@link useAIMarkdownMetadata} hook.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface AIMarkdownMetadata extends Record<string, any> {}\n\n/**\n * Typography variant identifier. Built-in variant is `'default'`;\n * consumers may define additional variants via custom typography components.\n */\nexport type AIMarkdownVariant = 'default' | (string & {});\n\n/**\n * Color scheme identifier. Built-in schemes are `'light'` and `'dark'`;\n * consumers may define additional schemes via custom typography CSS.\n */\nexport type AIMarkdownColorScheme = 'light' | 'dark' | (string & {});\n\n/** Props accepted by a typography wrapper component. */\nexport interface AIMarkdownTypographyProps extends PropsWithChildren {\n /** Resolved CSS font-size value (e.g. `'14px'`, `'0.875rem'`). */\n fontSize: string;\n /** Active typography variant. */\n variant?: AIMarkdownVariant;\n /** Active color scheme. */\n colorScheme?: AIMarkdownColorScheme;\n}\n\n/** React component type for the typography wrapper. */\nexport type AIMarkdownTypographyComponent = ComponentType<AIMarkdownTypographyProps>;\n\n/** Props accepted by an optional extra style wrapper component. */\nexport interface AIMarkdownExtraStylesProps extends PropsWithChildren {}\n\n/** React component type for an optional extra style wrapper. */\nexport type AIMarkdownExtraStylesComponent = ComponentType<AIMarkdownExtraStylesProps>;\n\n/**\n * Immutable render state exposed to all descendant components via React context.\n * Access this with the {@link useAIMarkdownRenderState} hook.\n *\n * Metadata is provided via a separate context — use {@link useAIMarkdownMetadata} instead.\n *\n * @typeParam TConfig - Render configuration type (defaults to {@link AIMarkdownRenderConfig}).\n */\nexport interface AIMarkdownRenderState<TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig> {\n /** Whether the content is currently being streamed (e.g. from an LLM). */\n streaming: boolean;\n /** Resolved CSS font-size value. */\n fontSize: string;\n /** Active typography variant. */\n variant: AIMarkdownVariant;\n /** Active color scheme. */\n colorScheme: AIMarkdownColorScheme;\n /** Active render configuration. */\n config: TConfig;\n}\n","/**\n * LaTeX preprocessing pipeline.\n *\n * Normalizes raw markdown so that LaTeX expressions survive the remark/rehype\n * rendering pipeline intact. The main entry point is {@link preprocessLaTeX},\n * which splits content into protected regions (code blocks, inline code, HTML\n * tags) and applies a sequence of transformations to the unprotected text:\n *\n * 1. Escape mhchem commands (`\\ce`, `\\pu`)\n * 2. Escape currency dollar signs (e.g. `$100`, `$1,000.50`)\n * 3. Convert bracket delimiters (`\\[...\\]`, `\\(...\\)`) to dollar delimiters\n * 4. Escape pipes inside LaTeX to prevent GFM table interference\n * 5. Escape underscores inside `\\text{...}` commands\n * 6. Convert single-dollar delimiters to double-dollar delimiters\n *\n * Thanks to the implementations from the following repositories:\n * - https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n * - https://github.com/danny-avila/LibreChat/blob/main/client/src/utils/latex.ts\n *\n * @module preprocessors/latex\n */\n\ninterface Segment {\n text: string;\n isCode: boolean;\n}\n\n/**\n * Split content into alternating text and protected segments.\n * Protected segments (isCode: true) are excluded from LaTeX processing:\n * - ``` multiline code blocks\n * - ` inline code\n * - HTML tags (e.g. <span>$</span> where $ should not be treated as LaTeX)\n */\nfunction splitByProtectedRegions(content: string): Segment[] {\n const segments: Segment[] = [];\n let lastIndex = 0;\n let inlineStart = -1;\n let multilineStart = -1;\n\n function pushProtected(start: number, end: number) {\n if (start > lastIndex) {\n segments.push({ text: content.substring(lastIndex, start), isCode: false });\n }\n segments.push({ text: content.substring(start, end), isCode: true });\n lastIndex = end;\n }\n\n for (let i = 0; i < content.length; i++) {\n const char = content[i];\n\n // Check for multiline code blocks\n if (char === '`' && i + 2 < content.length && content[i + 1] === '`' && content[i + 2] === '`') {\n if (multilineStart === -1) {\n // Cancel any pending inline code — ``` takes priority over `\n inlineStart = -1;\n multilineStart = i;\n i += 2;\n } else {\n pushProtected(multilineStart, i + 3);\n multilineStart = -1;\n i += 2;\n }\n }\n // Check for inline code (only if not in multiline)\n else if (char === '`' && multilineStart === -1) {\n if (inlineStart === -1) {\n inlineStart = i;\n } else {\n pushProtected(inlineStart, i + 1);\n inlineStart = -1;\n }\n }\n // Check for HTML tags (only if not in code block)\n else if (char === '<' && multilineStart === -1 && inlineStart === -1) {\n // Only match known HTML tags to avoid false positives with angle brackets\n // in markdown links (<Slides Demo>), math comparisons ($a < b$), etc.\n const rest = content.substring(i);\n const tagMatch = rest.match(\n /^<\\/?(span|div|p|br|hr|img|a|em|strong|b|i|u|s|sub|sup|code|pre|table|tr|td|th|thead|tbody|tfoot|ul|ol|li|dl|dt|dd|h[1-6]|blockquote|details|summary|figure|figcaption|section|article|aside|nav|header|footer|main|mark|del|ins|small|abbr|cite|dfn|kbd|samp|var|ruby|rt|rp|bdo|wbr|input|button|select|textarea|label|fieldset|legend|output|iframe|video|audio|source|canvas|svg|math|time)(?:\\s[^>]*)?\\/?>/i\n );\n if (tagMatch) {\n pushProtected(i, i + tagMatch[0].length);\n i += tagMatch[0].length - 1; // -1 because loop does i++\n }\n }\n }\n\n // Push remaining text\n if (lastIndex < content.length) {\n segments.push({ text: content.substring(lastIndex), isCode: false });\n }\n\n return segments;\n}\n\n/**\n * Escape mhchem commands in LaTeX expressions to ensure proper rendering.\n *\n * @param text Input string containing LaTeX expressions with mhchem commands\n * @returns String with escaped mhchem commands\n * @from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nfunction escapeMhchemCommands(text: string) {\n return text.replaceAll('$\\\\ce{', '$\\\\\\\\ce{').replaceAll('$\\\\pu{', '$\\\\\\\\pu{');\n}\n\nconst CURRENCY_REGEX = /(?<![\\\\$])\\$(?!\\$)(?=\\d+(?:,\\d{3})*(?:\\.\\d+)?(?:[KMBkmb])?(?:\\s|$|[^a-zA-Z\\d]))/g;\nconst NO_ESCAPED_DOLLAR_REGEX = /(?<![\\\\$])\\$(?!\\$)/g;\n// Match \\[...\\] and \\(...\\) as LaTeX delimiters, but exclude:\n// - !\\[...\\] (markdown image)\n// - \\[...\\]( (markdown link)\nconst DELIMITERS_REGEX = /(?<!!)\\\\\\[([\\S\\s]*?[^\\\\])\\\\](?!\\()|\\\\\\((.*?)\\\\\\)/g;\nconst ARRAY_COL_SPEC_OR_PIPE_REGEX = /(\\\\begin\\{(?:array|tabular[x*]?)\\}\\{[^}]*\\})|(?<!\\\\)\\|/g;\n// Display $$ allows multiline; inline $ forbids newlines (consistent with SINGLE_DOLLAR_REGEX)\nconst LATEX_BLOCK_REGEX = /\\$\\$([\\S\\s]*?)\\$\\$|(?<![\\\\$])\\$(?!\\$)((?:[^$\\n]|\\\\\\$)*?)(?<![\\\\`])\\$(?!\\$)/g;\nconst ESCAPE_TEXT_UNDERSCORES_REGEX = /\\\\text{([^}]*)}/g;\nconst SINGLE_DOLLAR_REGEX = /(?<![\\\\$])\\$(?!\\$)((?:[^$\\n]|\\\\[$])+?)(?<!\\\\)(?<!`)\\$(?!\\$)/g;\n\n/**\n * Escape currency dollar signs (e.g. $100, $1,000.50) so they are not\n * misinterpreted as LaTeX delimiters.\n *\n * The tricky part: a `$` followed by digits might still be inside a LaTeX\n * expression (e.g. `$8.29 \\text{ B} \\times 4$`). We detect this by checking\n * whether there is an odd number of unescaped `$` on the same line after the\n * current match — if so, the current `$` is a LaTeX opener, not currency.\n */\nfunction escapeCurrencyDollarSigns(text: string): string {\n const parts: string[] = [];\n let lastIndex = 0;\n const currencyMatches = Array.from(text.matchAll(CURRENCY_REGEX));\n\n for (let i = 0; i < currencyMatches.length; i++) {\n const match = currencyMatches[i];\n parts.push(text.substring(lastIndex, match.index));\n\n let needEscape = true;\n let restBeforeNextMatchOrEnd = '';\n if (i < currencyMatches.length - 1) {\n const nextMatch = currencyMatches[i + 1];\n if (nextMatch.index - match.index > 1) {\n restBeforeNextMatchOrEnd = text.substring(match.index + 1, nextMatch.index);\n }\n } else {\n restBeforeNextMatchOrEnd = text.substring(match.index + 1);\n }\n const firstLineBeforeNextMatch = restBeforeNextMatchOrEnd.split(/\\r\\n|\\r|\\n/g)[0];\n if (Array.from(firstLineBeforeNextMatch.matchAll(NO_ESCAPED_DOLLAR_REGEX)).length % 2 !== 0) {\n const previousNewContent = parts.join('');\n const previousLastLineContent = previousNewContent.split(/\\r\\n|\\r|\\n/g).pop();\n const wholeLineBeforeNextMatchWithoutCurrentDollar = previousLastLineContent + firstLineBeforeNextMatch;\n if (Array.from(wholeLineBeforeNextMatchWithoutCurrentDollar.matchAll(NO_ESCAPED_DOLLAR_REGEX)).length % 2 !== 0) {\n needEscape = false;\n }\n }\n\n parts.push(needEscape ? '\\\\$' : '$');\n lastIndex = match.index + 1;\n }\n parts.push(text.substring(lastIndex));\n return parts.join('');\n}\n\n/**\n * Convert LaTeX bracket delimiters to dollar sign delimiters.\n * Converts \\[...\\] to $$...$$ and \\(...\\) to $...$\n *\n * @param text Input string containing LaTeX expressions\n * @returns String with LaTeX bracket delimiters converted to dollar sign delimiters\n * @modified from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nfunction convertLatexDelimiters(text: string): string {\n return text.replaceAll(\n DELIMITERS_REGEX,\n (match: string, squareBracket: string | undefined, roundBracket: string | undefined): string => {\n if (squareBracket !== undefined) {\n return `$$${squareBracket}$$`;\n } else if (roundBracket !== undefined) {\n return `$${roundBracket}$`;\n }\n return match;\n }\n );\n}\n\n/**\n * Helper function: replace unescaped pipes with \\vert in LaTeX math fragments\n * @from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nconst replaceUnescapedPipes = (formula: string): string =>\n // Use \\vert{} so the control sequence terminates before the next token.\n // Preserve `|` inside \\begin{array}{...} / \\begin{tabular}{...} column specifiers.\n formula.replaceAll(ARRAY_COL_SPEC_OR_PIPE_REGEX, (match, colSpec: string | undefined) =>\n colSpec !== undefined ? match : '\\\\vert{}'\n );\n/**\n * Escape pipes in LaTeX expressions to prevent them from being interpreted as\n * column separators in markdown tables.\n *\n * @param text Input string containing LaTeX expressions\n * @returns String with pipes escaped in LaTeX expressions\n * @modified from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nfunction escapeLatexPipes(text: string): string {\n return text.replaceAll(LATEX_BLOCK_REGEX, (match, display, inline) => {\n if (display !== undefined) return `$$${replaceUnescapedPipes(display)}$$`;\n if (inline !== undefined) return `$${replaceUnescapedPipes(inline)}$`;\n return match;\n });\n}\n\n/**\n * Escape unescaped underscores within \\text{...} commands in LaTeX expressions.\n * For example, \\text{node_domain} becomes \\text{node\\_domain},\n * but \\text{node\\_domain} remains \\text{node\\_domain}.\n *\n * @param text Input string that may contain LaTeX expressions\n * @returns String with unescaped underscores escaped within \\text{...} commands\n * @modified from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nfunction escapeTextUnderscores(text: string): string {\n return text.replaceAll(ESCAPE_TEXT_UNDERSCORES_REGEX, (_match, textContent: string) => {\n const escapedTextContent = textContent.replaceAll(/(?<!\\\\)_/g, '\\\\_');\n return `\\\\text{${escapedTextContent}}`;\n });\n}\n\n/**\n * Convert single dollar delimiters to double dollar delimiters.\n * e.g. $x^2$ → $$x^2$$\n */\nfunction convertSingleToDoubleDollar(text: string): string {\n return text.replaceAll(SINGLE_DOLLAR_REGEX, (_match, content: string) => `$$${content}$$`);\n}\n\n/**\n * Main LaTeX preprocessor entry point.\n *\n * Splits the input into protected regions (code blocks, inline code, HTML tags)\n * and applies the full normalization pipeline to unprotected text segments.\n * Returns the input unchanged when no LaTeX-related characters (`$`, `\\[`, `\\(`)\n * are detected.\n *\n * @param str - Raw markdown string.\n * @returns The preprocessed string with normalized LaTeX delimiters.\n */\nexport function preprocessLaTeX(str: string): string {\n // Return early if no LaTeX patterns are found\n if (!str.includes('$') && !str.includes('\\\\[') && !str.includes('\\\\(')) return str;\n\n // Step 1: split by code blocks\n const segments = splitByProtectedRegions(str);\n\n // Step 2: process each non-code segment through the LaTeX pipeline\n const result = segments.map((segment) => {\n if (segment.isCode) return segment.text;\n\n let text = segment.text;\n text = escapeMhchemCommands(text);\n text = escapeCurrencyDollarSigns(text);\n text = convertLatexDelimiters(text);\n text = escapeLatexPipes(text);\n text = escapeTextUnderscores(text);\n text = convertSingleToDoubleDollar(text);\n return text;\n });\n\n return result.join('');\n}\n","/**\n * Content preprocessing pipeline.\n *\n * Runs all preprocessors (built-in + user-supplied) in sequence before\n * the markdown string is handed to react-markdown. The built-in LaTeX\n * preprocessor always runs first, followed by any extra preprocessors\n * provided by the consumer.\n *\n * @module preprocessors\n */\n\nimport { AIMDContentPreprocessor } from './defs';\nimport { preprocessLaTeX } from './latex';\n\n/** Sequentially apply an array of preprocessor functions via left-fold. */\nfunction applyPreprocessors(value: string, ...fns: Array<AIMDContentPreprocessor>): string {\n return fns.reduce((result, fn) => fn(result), value);\n}\n\n/** Stable empty array to avoid re-renders when no extra preprocessors are given. */\nconst defaultExtraPreprocessors: AIMDContentPreprocessor[] = [];\n\n/**\n * Run the full preprocessing pipeline on raw markdown content.\n *\n * @param content - Raw markdown string.\n * @param extraPreprocessors - Optional user-supplied preprocessors appended after the built-in ones.\n * @returns The preprocessed markdown string ready for rendering.\n */\nexport default function preprocessAIMDContent(\n content: string,\n extraPreprocessors: AIMDContentPreprocessor[] = defaultExtraPreprocessors\n) {\n return applyPreprocessors(content, preprocessLaTeX, ...extraPreprocessors);\n}\n","/**\n * Core markdown rendering component.\n *\n * Wraps `react-markdown` with a curated set of remark and rehype plugins\n * for GFM, math/LaTeX, emoji, CJK support, and configurable extra syntax\n * extensions and display optimizations. Plugin selection is driven by the\n * {@link AIMarkdownRenderConfig} from context.\n *\n * @module components/MarkdownContent\n */\n\nimport { memo, useMemo } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport rehypeKatex from 'rehype-katex';\nimport rehypeRaw from 'rehype-raw';\nimport rehypeUnwrapImages from 'rehype-unwrap-images';\nimport rehypeSanitize, { defaultSchema } from 'rehype-sanitize';\nimport remarkBreaks from 'remark-breaks';\nimport remarkCjkFriendly from 'remark-cjk-friendly';\nimport remarkCjkFriendlyGfmStrikethrough from 'remark-cjk-friendly-gfm-strikethrough';\nimport remarkEmoji from 'remark-emoji';\nimport remarkGfm from 'remark-gfm';\nimport remarkMath from 'remark-math';\nimport { remarkDefinitionList, defListHastHandlers } from 'remark-definition-list';\nimport remarkSupersub from 'remark-supersub';\nimport { remarkMark as remarkMarkHighlight } from 'remark-mark-highlight';\nimport remarkSqueezeParagraphs from 'remark-squeeze-paragraphs';\nimport remarkSmartypants from 'remark-smartypants';\nimport remarkPangu from 'remark-pangu';\nimport remarkRemoveComments from 'remark-remove-comments';\nimport { useAIMarkdownRenderState } from '../context';\nimport {\n AIMarkdownCustomComponents,\n AIMarkdownRenderDisplayOptimizeAbility,\n AIMarkdownRenderExtraSyntax,\n} from '../defs';\n\n/** Maps display optimization abilities to their corresponding remark plugins. */\nconst DisplayOptimizeRemarkPluginMap = {\n [AIMarkdownRenderDisplayOptimizeAbility.REMOVE_COMMENTS]: remarkRemoveComments,\n [AIMarkdownRenderDisplayOptimizeAbility.SMARTYPANTS]: remarkSmartypants,\n [AIMarkdownRenderDisplayOptimizeAbility.PANGU]: remarkPangu,\n};\n\n/** Maps extra syntax extensions to their corresponding remark plugins. */\nconst ExtraSyntaxRemarkPluginMap = {\n [AIMarkdownRenderExtraSyntax.HIGHLIGHT]: remarkMarkHighlight,\n [AIMarkdownRenderExtraSyntax.DEFINITION_LIST]: remarkDefinitionList,\n [AIMarkdownRenderExtraSyntax.SUBSCRIPT]: remarkSupersub,\n};\n\n/** Stable empty object to avoid unnecessary re-renders when no custom components are given. */\nconst DefaultCustomComponents: AIMarkdownCustomComponents = {};\n\ninterface AIMarkdownContentProps {\n /** Preprocessed markdown string to render. */\n content: string;\n /** Optional react-markdown component overrides (e.g. custom code block renderer). */\n customComponents?: AIMarkdownCustomComponents;\n}\n\n/**\n * Internal component that assembles the remark/rehype plugin chain based on\n * the current render config and delegates to `ReactMarkdown`.\n */\nconst AIMarkdownContent = memo(({ content, customComponents }: AIMarkdownContentProps) => {\n const { config } = useAIMarkdownRenderState();\n\n // Resolve extra-syntax remark plugins and check if definition list HAST handlers are needed.\n const { extraSyntaxRemarkPlugins, enableDefinitionList } = useMemo(\n () => ({\n extraSyntaxRemarkPlugins: config.extraSyntaxSupported.map((syntax) => ExtraSyntaxRemarkPluginMap[syntax]),\n enableDefinitionList: config.extraSyntaxSupported.includes(AIMarkdownRenderExtraSyntax.DEFINITION_LIST),\n }),\n [config.extraSyntaxSupported]\n );\n\n const displayOptimizeRemarkPlugins = useMemo(() => {\n return config.displayOptimizeAbilities.map((ability) => DisplayOptimizeRemarkPluginMap[ability]);\n }, [config.displayOptimizeAbilities]);\n\n const usedComponents = useMemo(() => {\n return customComponents ? { ...DefaultCustomComponents, ...customComponents } : DefaultCustomComponents;\n }, [customComponents]);\n\n return (\n <ReactMarkdown\n remarkPlugins={[\n // --- Core plugins (always active) ---\n remarkGfm,\n [\n remarkMath,\n {\n // Disable single-dollar inline math to avoid conflicts with currency\n // signs and other dollar usages; the preprocessor converts $...$ to $$...$$.\n singleDollarTextMath: false,\n },\n ],\n // --- Configurable extra syntax plugins ---\n ...extraSyntaxRemarkPlugins,\n // --- Formatting & normalization ---\n remarkBreaks,\n remarkEmoji,\n remarkSqueezeParagraphs,\n remarkCjkFriendly,\n remarkCjkFriendlyGfmStrikethrough,\n // --- Configurable display optimizations ---\n ...displayOptimizeRemarkPlugins,\n ]}\n rehypePlugins={[\n // Allow raw HTML through so rehype-sanitize can handle it.\n [\n rehypeRaw,\n {\n passThrough: [],\n },\n ],\n // Sanitize HTML while allowing <mark> (highlight) and KaTeX class names.\n [\n rehypeSanitize,\n {\n ...defaultSchema,\n tagNames: [...(defaultSchema.tagNames || []), 'mark'],\n attributes: {\n ...defaultSchema.attributes,\n // The `language-*` regex is allowed by default.\n code: [['className', /^language-./, 'math-inline', 'math-display']],\n },\n },\n ],\n rehypeKatex,\n rehypeUnwrapImages,\n ]}\n remarkRehypeOptions={{\n allowDangerousHtml: true,\n handlers: {\n // Inject definition-list HAST handlers when the extension is active.\n ...(enableDefinitionList ? defListHastHandlers : {}),\n },\n }}\n components={usedComponents}\n // NOTE: The default `urlTransform` in Windows environments treats local\n // paths (e.g. `C:/...`) as unsafe. Uncomment the line below if needed:\n // urlTransform={(url: string) => url}\n >\n {content}\n </ReactMarkdown>\n );\n});\n\nAIMarkdownContent.displayName = 'AIMarkdownContent';\n\nexport default AIMarkdownContent;\n","/**\n * Hook for referential stability of deep-equal values.\n *\n * @module hooks/useStableValue\n */\n\nimport { useRef, useEffect } from 'react';\nimport isEqual from 'lodash-es/isEqual';\n\n/**\n * Returns a referentially stable version of `value`.\n *\n * On each render the new value is deep-compared (via `lodash/isEqual`) against\n * the previous one. If they are structurally equal the *previous* reference is\n * returned, preventing unnecessary re-renders in downstream `useMemo` / `useEffect`\n * consumers that depend on reference equality.\n *\n * @typeParam T - The value type.\n * @param value - The potentially new value to stabilize.\n * @returns The previous reference when deep-equal, otherwise the new value.\n *\n * @example\n * ```tsx\n * const stableConfig = useStableValue(config);\n * // stableConfig keeps the same reference as long as config is deep-equal.\n * ```\n */\nexport default function useStableValue<T>(value: T): T {\n const ref = useRef(value);\n\n // eslint-disable-next-line react-hooks/refs\n const prev = ref.current;\n const stableValue = isEqual(prev, value) ? prev : value;\n\n useEffect(() => {\n ref.current = stableValue;\n }, [stableValue]);\n\n return stableValue;\n}\n","/**\n * Default typography wrapper component.\n *\n * Renders a `<div>` container that applies CSS class names for the active\n * variant and color scheme, and sets the root font-size as an inline style.\n * The corresponding CSS custom properties are defined in the SCSS variant\n * files under `typography/variants/`.\n *\n * Consumers can replace this with a custom {@link AIMarkdownTypographyComponent}\n * via the `Typography` prop on `<AIMarkdown>`.\n *\n * @module components/typography/Default\n */\n\nimport { memo } from 'react';\nimport type { AIMarkdownTypographyProps } from '../../defs';\n\nconst DefaultTypography = memo(({ children, fontSize, variant, colorScheme }: AIMarkdownTypographyProps) => (\n <div\n className={`aim-typography-root ${variant ?? ''} ${colorScheme ?? ''}`.trim()}\n style={{ width: '100%', fontSize }}\n >\n {children}\n </div>\n));\n\nDefaultTypography.displayName = 'DefaultTypography';\n\nexport default DefaultTypography;\n"],"mappings":"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","names":["useMemo","memo","AIMarkdownRenderExtraSyntax","AIMarkdownRenderDisplayOptimizeAbility","useMemo","jsx","useMemo","memo","jsx","jsx","useMemo","memo"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx","../src/context.tsx","../src/defs.ts","../src/preprocessors/latex.ts","../src/preprocessors/index.ts","../src/components/MarkdownContent.tsx","../src/hooks/useStableValue.ts","../src/components/typography/Default.tsx"],"sourcesContent":["/**\n * @ai-react-markdown/core\n *\n * A batteries-included React component for rendering AI-generated markdown\n * with first-class support for LaTeX math, GFM, CJK text, syntax highlighting,\n * and streaming content.\n *\n * ## Quick Start\n *\n * ```tsx\n * import AIMarkdown from '@ai-react-markdown/core';\n * import '@ai-react-markdown/core/typography/default.css';\n *\n * function App() {\n * return <AIMarkdown content=\"Hello **world**!\" />;\n * }\n * ```\n *\n * @module @ai-react-markdown/core\n */\n\n'use client';\n\nimport { useMemo, memo, type CSSProperties } from 'react';\nimport AIMarkdownRenderStateProvider, {\n AIMarkdownMetadataProvider,\n AIMarkdownRenderStateProviderProps,\n AIMarkdownMetadataProviderProps,\n} from './context';\nimport { AIMDContentPreprocessor } from './preprocessors/defs';\nimport preprocessAIMDContent from './preprocessors';\nimport AIMarkdownContent from './components/MarkdownContent';\nimport {\n AIMarkdownCustomComponents,\n AIMarkdownRenderConfig,\n AIMarkdownMetadata,\n AIMarkdownTypographyComponent,\n AIMarkdownExtraStylesComponent,\n AIMarkdownVariant,\n AIMarkdownColorScheme,\n} from './defs';\nimport useStableValue from './hooks/useStableValue';\nimport DefaultTypography from './components/typography/Default';\n\n/**\n * Props for the `<AIMarkdown>` component.\n *\n * @typeParam TConfig - Custom render configuration type (extends {@link AIMarkdownRenderConfig}).\n * @typeParam TRenderData - Custom metadata type (extends {@link AIMarkdownMetadata}).\n */\nexport interface AIMarkdownProps<\n TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig,\n TRenderData extends AIMarkdownMetadata = AIMarkdownMetadata,\n>\n extends\n Omit<AIMarkdownRenderStateProviderProps<TConfig>, 'streaming' | 'fontSize' | 'variant' | 'colorScheme'>,\n AIMarkdownMetadataProviderProps<TRenderData> {\n /**\n * Whether content is actively being streamed (e.g. token-by-token from an LLM).\n * When `true`, the flag is propagated via context so custom components can adapt\n * their behavior (show cursors, disable copy buttons, skip animations, etc.).\n * Defaults to `false`.\n */\n streaming?: boolean;\n /**\n * Base font size for the rendered output.\n * Accepts a CSS length string (e.g. `'14px'`, `'0.875rem'`) or a number\n * which is treated as pixels. Defaults to `'0.9375rem'`.\n */\n fontSize?: number | string;\n /** Raw markdown content to render. */\n content: string;\n /**\n * Additional preprocessors to run on the raw markdown before rendering.\n * These run *after* the built-in LaTeX preprocessor.\n */\n contentPreprocessors?: AIMDContentPreprocessor[];\n /**\n * Custom `react-markdown` component overrides.\n * Use this to replace the default renderers for specific HTML elements\n * (e.g. code blocks, links, images).\n */\n customComponents?: AIMarkdownCustomComponents;\n /**\n * Typography wrapper component. Receives `fontSize`, `variant`, and `colorScheme`.\n * Defaults to the built-in {@link DefaultTypography}.\n */\n Typography?: AIMarkdownTypographyComponent;\n /**\n * Optional extra style wrapper component rendered between the typography\n * wrapper and the markdown content. Useful for injecting additional\n * CSS scope or theme providers.\n */\n ExtraStyles?: AIMarkdownExtraStylesComponent;\n /** Typography variant name. Defaults to `'default'`. */\n variant?: AIMarkdownVariant;\n /** Color scheme name. Defaults to `'light'`. */\n colorScheme?: AIMarkdownColorScheme;\n}\n\n/**\n * Root component that preprocesses markdown content and renders it through\n * a configurable remark/rehype pipeline wrapped in typography and style layers.\n */\nconst AIMarkdownComponent = <\n TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig,\n TRenderData extends AIMarkdownMetadata = AIMarkdownMetadata,\n>({\n streaming = false,\n content,\n fontSize,\n contentPreprocessors,\n customComponents,\n defaultConfig,\n config,\n metadata,\n Typography = DefaultTypography,\n ExtraStyles,\n variant = 'default',\n colorScheme = 'light',\n}: AIMarkdownProps<TConfig, TRenderData>) => {\n // Normalize fontSize: number -> px string, undefined -> default rem value.\n const usedFontSize = fontSize ? (typeof fontSize === 'number' ? `${fontSize}px` : fontSize) : '0.9375rem';\n\n // Stabilize object/array props to prevent unnecessary re-renders\n // when the consumer creates new references on each render.\n const stableDefaultConfig = useStableValue(defaultConfig);\n const stableConfig = useStableValue(config);\n const stablePreprocessors = useStableValue(contentPreprocessors);\n const stableCustomComponents = useStableValue(customComponents);\n\n // Run the preprocessing pipeline (LaTeX normalization + user preprocessors).\n const usedContent = useMemo(\n () => (content ? preprocessAIMDContent(content, stablePreprocessors) : content),\n [content, stablePreprocessors]\n );\n\n return (\n <AIMarkdownMetadataProvider<TRenderData> metadata={metadata}>\n <AIMarkdownRenderStateProvider<TConfig>\n streaming={streaming}\n fontSize={usedFontSize}\n variant={variant}\n colorScheme={colorScheme}\n defaultConfig={stableDefaultConfig}\n config={stableConfig}\n >\n <Typography\n fontSize={usedFontSize}\n variant={variant}\n colorScheme={colorScheme}\n // Inject CSS custom properties onto the Typography root element.\n // --aim-font-size-root: absolute font-size anchor so inner CSS can\n // bypass em-compounding in deeply nested markdown structures.\n // See AIMarkdownTypographyProps.style JSDoc for the full variable list.\n style={{ '--aim-font-size-root': usedFontSize } as CSSProperties}\n >\n {ExtraStyles ? (\n <ExtraStyles>\n <AIMarkdownContent content={usedContent} customComponents={stableCustomComponents} />\n </ExtraStyles>\n ) : (\n <AIMarkdownContent content={usedContent} customComponents={stableCustomComponents} />\n )}\n </Typography>\n </AIMarkdownRenderStateProvider>\n </AIMarkdownMetadataProvider>\n );\n};\n\n/**\n * A React component for rendering AI-generated markdown with rich formatting support.\n *\n * Features:\n * - GFM (tables, strikethrough, task lists, autolinks)\n * - LaTeX math rendering via KaTeX\n * - Emoji shortcodes\n * - CJK-friendly line breaking and spacing\n * - Configurable syntax extensions (highlight, definition lists, super/subscript)\n * - Configurable display optimizations (SmartyPants, pangu, comment removal)\n * - Streaming-aware rendering\n * - Customizable typography, color scheme, and component overrides\n *\n * @example\n * ```tsx\n * <AIMarkdown\n * content={markdownString}\n * streaming={isStreaming}\n * colorScheme=\"dark\"\n * config={{ extraSyntaxSupported: [AIMarkdownRenderExtraSyntax.HIGHLIGHT] }}\n * />\n * ```\n */\nconst AIMarkdown = memo(AIMarkdownComponent);\nAIMarkdown.displayName = 'AIMarkdown';\n\nexport default AIMarkdown as typeof AIMarkdownComponent;\n\n// ── Public API re-exports ───────────────────────────────────────────────────\n\n// Types\nexport type { AIMDContentPreprocessor };\nexport type {\n AIMarkdownCustomComponents,\n AIMarkdownRenderConfig,\n AIMarkdownRenderState,\n AIMarkdownMetadata,\n AIMarkdownTypographyProps,\n AIMarkdownTypographyComponent,\n AIMarkdownExtraStylesProps,\n AIMarkdownExtraStylesComponent,\n AIMarkdownVariant,\n AIMarkdownColorScheme,\n} from './defs';\n\n// Enums & Constants\nexport {\n AIMarkdownRenderExtraSyntax,\n AIMarkdownRenderDisplayOptimizeAbility,\n defaultAIMarkdownRenderConfig,\n} from './defs';\n\n// Hooks -- for custom components to access render state & metadata\nexport { useAIMarkdownRenderState, useAIMarkdownMetadata } from './context';\nexport { useStableValue };\n\n// Utils\nexport type { PartialDeep } from './typings/partial-deep';\n","/**\n * React context for the AIMarkdown render state.\n *\n * Provides an immutable {@link AIMarkdownRenderState} object to all descendant\n * components. The provider deep-merges user-supplied partial configuration with\n * the built-in defaults so that consumers always receive a complete config.\n *\n * @module context\n */\n\nimport { PropsWithChildren, createContext, useContext, useMemo } from 'react';\nimport cloneDeep from 'lodash-es/cloneDeep';\nimport mergeWith from 'lodash-es/mergeWith';\nimport {\n AIMarkdownRenderConfig,\n AIMarkdownMetadata,\n AIMarkdownRenderState,\n AIMarkdownVariant,\n AIMarkdownColorScheme,\n defaultAIMarkdownRenderConfig,\n} from './defs';\nimport type { PartialDeep } from './typings/partial-deep';\n\nconst AIMarkdownRenderStateContext = createContext<AIMarkdownRenderState<AIMarkdownRenderConfig> | null>(null);\n\nconst AIMarkdownMetadataContext = createContext<AIMarkdownMetadata | undefined>(undefined);\n\n/**\n * Access the current {@link AIMarkdownRenderState} from within the `<AIMarkdown>` tree.\n *\n * Must be called inside a component rendered as a descendant of `<AIMarkdown>`.\n * Throws if called outside the provider boundary.\n *\n * @typeParam TConfig - Expected configuration shape (defaults to {@link AIMarkdownRenderConfig}).\n * @returns The current render state (does not include metadata — use {@link useAIMarkdownMetadata} for that).\n *\n * @example\n * ```tsx\n * function CustomCodeBlock({ children }: PropsWithChildren) {\n * const { streaming, config } = useAIMarkdownRenderState();\n * // ...\n * }\n * ```\n */\nexport function useAIMarkdownRenderState<TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig>() {\n const context = useContext(AIMarkdownRenderStateContext) as AIMarkdownRenderState<TConfig>;\n\n if (!context) {\n throw new Error('useAIMarkdownRenderState must be used within an <AIMarkdown /> component.');\n }\n\n return context;\n}\n\n/**\n * Access the current metadata from within the `<AIMarkdown>` tree.\n *\n * Metadata lives in a separate React context so that changes to metadata\n * do not cause re-renders in components that only consume render state\n * (e.g. {@link MarkdownContent}).\n *\n * @typeParam TMetadata - Expected metadata shape (defaults to {@link AIMarkdownMetadata}).\n * @returns The current metadata, or `undefined` if none was provided.\n */\nexport function useAIMarkdownMetadata<TMetadata extends AIMarkdownMetadata = AIMarkdownMetadata>() {\n return useContext(AIMarkdownMetadataContext) as TMetadata | undefined;\n}\n\n/** Props for {@link AIMarkdownRenderStateProvider}. */\nexport interface AIMarkdownRenderStateProviderProps<\n TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig,\n> extends PropsWithChildren {\n streaming: boolean;\n fontSize: string;\n variant: AIMarkdownVariant;\n colorScheme: AIMarkdownColorScheme;\n /**\n * Base default config to merge against. When omitted, falls back to\n * {@link defaultAIMarkdownRenderConfig}. Sub-packages (e.g. mantine) can\n * pass their own extended defaults here.\n */\n defaultConfig?: TConfig;\n /** Partial config that will be deep-merged with the default config. */\n config?: PartialDeep<TConfig>;\n}\n\n/** Props for {@link AIMarkdownMetadataProvider}. */\nexport interface AIMarkdownMetadataProviderProps<\n TMetadata extends AIMarkdownMetadata = AIMarkdownMetadata,\n> extends PropsWithChildren {\n metadata?: TMetadata;\n}\n\n/**\n * Custom lodash `mergeWith` handler: arrays from the source (user config)\n * fully replace the target (default config) instead of being merged by index.\n */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nconst configMergeCustomizer = (\n _objValue: any,\n srcValue: any,\n _key: string,\n _object: any,\n _source: any,\n _stack: any\n) => {\n if (Array.isArray(srcValue)) {\n return srcValue;\n }\n};\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\n/**\n * Provider that exposes consumer-provided metadata via a dedicated React context.\n * Separated from render state so that metadata changes do not trigger\n * re-renders in components that only consume render state.\n */\nexport const AIMarkdownMetadataProvider = <RDT extends AIMarkdownMetadata = AIMarkdownMetadata>({\n metadata,\n children,\n}: AIMarkdownMetadataProviderProps<RDT>) => {\n return <AIMarkdownMetadataContext.Provider value={metadata}>{children}</AIMarkdownMetadataContext.Provider>;\n};\n\n/**\n * Internal provider that deep-merges user config with defaults and exposes\n * the resulting {@link AIMarkdownRenderState} to the component tree.\n */\nconst AIMarkdownRenderStateProvider = <RCT extends AIMarkdownRenderConfig = AIMarkdownRenderConfig>({\n streaming,\n fontSize,\n variant,\n colorScheme,\n defaultConfig,\n config,\n children,\n}: AIMarkdownRenderStateProviderProps<RCT>) => {\n // Deep-merge user config with defaults; clone first to avoid mutating the frozen default.\n const baseConfig = defaultConfig ?? defaultAIMarkdownRenderConfig;\n const mergedConfig = useMemo(\n () => (config ? mergeWith(cloneDeep(baseConfig), config, configMergeCustomizer) : baseConfig),\n [baseConfig, config]\n );\n\n // Freeze the state object to enforce immutability downstream.\n const state = useMemo(\n () =>\n Object.freeze({\n streaming,\n fontSize,\n variant,\n colorScheme,\n config: mergedConfig,\n }),\n [streaming, fontSize, variant, colorScheme, mergedConfig]\n );\n\n return <AIMarkdownRenderStateContext.Provider value={state}>{children}</AIMarkdownRenderStateContext.Provider>;\n};\n\nexport default AIMarkdownRenderStateProvider;\n","/**\n * Core type definitions, enums, and default configuration for ai-react-markdown.\n *\n * This module defines the public API surface for configuring the renderer,\n * including extra markdown syntax extensions, display optimization abilities,\n * typography theming, and the shared render state shape.\n *\n * @module defs\n */\n\nimport { ComponentType, CSSProperties, PropsWithChildren } from 'react';\nimport type { Components } from 'react-markdown';\n\n/**\n * Custom component overrides for the markdown renderer.\n * Alias for `react-markdown`'s `Components` type, re-exported under the\n * library's `AIMarkdown` naming convention so consumers don't need a\n * direct `react-markdown` dependency for type imports.\n */\nexport type AIMarkdownCustomComponents = Components;\n\n/**\n * Extra markdown syntax extensions beyond standard GFM.\n * Enable or disable these via {@link AIMarkdownRenderConfig.extraSyntaxSupported}.\n */\nexport enum AIMarkdownRenderExtraSyntax {\n /** `==Highlight==` syntax support. */\n HIGHLIGHT = 'HIGHLIGHT',\n /** Definition list syntax. @see https://michelf.ca/projects/php-markdown/extra/#def-list */\n DEFINITION_LIST = 'DEFINITION_LIST',\n /** Superscript (`^text^`) and subscript (`~text~`) syntax. */\n SUBSCRIPT = 'SUBSCRIPT',\n}\n\n/**\n * Display optimization abilities applied during markdown processing.\n * Enable or disable these via {@link AIMarkdownRenderConfig.displayOptimizeAbilities}.\n */\nexport enum AIMarkdownRenderDisplayOptimizeAbility {\n /** Strip HTML comments from the content. */\n REMOVE_COMMENTS = 'REMOVE_COMMENTS',\n /** Typographic enhancements via SmartyPants (curly quotes, em-dashes, etc.). @see https://www.npmjs.com/package/smartypants */\n SMARTYPANTS = 'SMARTYPANTS',\n /** Automatically insert spaces between CJK and half-width characters. */\n PANGU = 'PANGU',\n}\n\n/**\n * Configuration object controlling which markdown extensions and\n * display optimizations are active during rendering.\n */\nexport interface AIMarkdownRenderConfig {\n /** Extra syntax extensions to enable. */\n extraSyntaxSupported: AIMarkdownRenderExtraSyntax[];\n /** Display optimization abilities to enable. */\n displayOptimizeAbilities: AIMarkdownRenderDisplayOptimizeAbility[];\n}\n\n/**\n * Sensible default configuration with all extensions and optimizations enabled.\n * Frozen to prevent accidental mutation.\n */\nexport const defaultAIMarkdownRenderConfig: AIMarkdownRenderConfig = Object.freeze({\n extraSyntaxSupported: Object.freeze([\n AIMarkdownRenderExtraSyntax.HIGHLIGHT,\n AIMarkdownRenderExtraSyntax.DEFINITION_LIST,\n AIMarkdownRenderExtraSyntax.SUBSCRIPT,\n ]),\n displayOptimizeAbilities: Object.freeze([\n AIMarkdownRenderDisplayOptimizeAbility.REMOVE_COMMENTS,\n AIMarkdownRenderDisplayOptimizeAbility.SMARTYPANTS,\n AIMarkdownRenderDisplayOptimizeAbility.PANGU,\n ]),\n}) as AIMarkdownRenderConfig;\n\n/**\n * Arbitrary metadata that consumers can pass through a dedicated React context.\n * Custom renderers can access this via the {@link useAIMarkdownMetadata} hook.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface AIMarkdownMetadata extends Record<string, any> {}\n\n/**\n * Typography variant identifier. Built-in variant is `'default'`;\n * consumers may define additional variants via custom typography components.\n */\nexport type AIMarkdownVariant = 'default' | (string & {});\n\n/**\n * Color scheme identifier. Built-in schemes are `'light'` and `'dark'`;\n * consumers may define additional schemes via custom typography CSS.\n */\nexport type AIMarkdownColorScheme = 'light' | 'dark' | (string & {});\n\n/** Props accepted by a typography wrapper component. */\nexport interface AIMarkdownTypographyProps extends PropsWithChildren {\n /** Resolved CSS font-size value (e.g. `'14px'`, `'0.875rem'`). */\n fontSize: string;\n /** Active typography variant. */\n variant?: AIMarkdownVariant;\n /** Active color scheme. */\n colorScheme?: AIMarkdownColorScheme;\n /**\n * Inline styles injected by the core renderer. Custom Typography implementations\n * **must** merge this object into their root element's `style` to ensure CSS\n * custom properties set by the core are available to all descendant nodes.\n *\n * ### Currently injected variables\n *\n * | Variable | Value | Purpose |\n * |-------------------------|----------------|----------------------------------------------------------|\n * | `--aim-font-size-root` | `fontSize` prop | Absolute font-size anchor for the component instance. |\n *\n * #### Why `--aim-font-size-root`?\n *\n * Markdown content frequently nests elements that use relative `em` units\n * (blockquotes, lists, code blocks). Each nesting level compounds the\n * effective size — a `0.875em` code span inside a `1.125em` blockquote\n * becomes `0.984375em` of the parent, not `0.875em` of the root.\n *\n * `--aim-font-size-root` provides the component-level root font-size as an\n * absolute reference so that inner CSS rules can use\n * `font-size: var(--aim-font-size-root)` to opt out of `em` compounding\n * when a stable size is needed.\n *\n * @example\n * ```tsx\n * const MyTypography: AIMarkdownTypographyComponent = ({ children, fontSize, style }) => (\n * <div className=\"my-typo\" style={{ fontSize, ...style }}>\n * {children}\n * </div>\n * );\n * ```\n */\n style?: CSSProperties;\n}\n\n/** React component type for the typography wrapper. */\nexport type AIMarkdownTypographyComponent = ComponentType<AIMarkdownTypographyProps>;\n\n/** Props accepted by an optional extra style wrapper component. */\nexport interface AIMarkdownExtraStylesProps extends PropsWithChildren {}\n\n/** React component type for an optional extra style wrapper. */\nexport type AIMarkdownExtraStylesComponent = ComponentType<AIMarkdownExtraStylesProps>;\n\n/**\n * Immutable render state exposed to all descendant components via React context.\n * Access this with the {@link useAIMarkdownRenderState} hook.\n *\n * Metadata is provided via a separate context — use {@link useAIMarkdownMetadata} instead.\n *\n * @typeParam TConfig - Render configuration type (defaults to {@link AIMarkdownRenderConfig}).\n */\nexport interface AIMarkdownRenderState<TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig> {\n /** Whether the content is currently being streamed (e.g. from an LLM). */\n streaming: boolean;\n /** Resolved CSS font-size value. */\n fontSize: string;\n /** Active typography variant. */\n variant: AIMarkdownVariant;\n /** Active color scheme. */\n colorScheme: AIMarkdownColorScheme;\n /** Active render configuration. */\n config: TConfig;\n}\n","/**\n * LaTeX preprocessing pipeline.\n *\n * Normalizes raw markdown so that LaTeX expressions survive the remark/rehype\n * rendering pipeline intact. The main entry point is {@link preprocessLaTeX},\n * which splits content into protected regions (code blocks, inline code, HTML\n * tags) and applies a sequence of transformations to the unprotected text:\n *\n * 1. Escape mhchem commands (`\\ce`, `\\pu`)\n * 2. Escape currency dollar signs (e.g. `$100`, `$1,000.50`)\n * 3. Convert bracket delimiters (`\\[...\\]`, `\\(...\\)`) to dollar delimiters\n * 4. Escape pipes inside LaTeX to prevent GFM table interference\n * 5. Escape underscores inside `\\text{...}` commands\n * 6. Convert single-dollar delimiters to double-dollar delimiters\n *\n * Thanks to the implementations from the following repositories:\n * - https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n * - https://github.com/danny-avila/LibreChat/blob/main/client/src/utils/latex.ts\n *\n * @module preprocessors/latex\n */\n\ninterface Segment {\n text: string;\n isCode: boolean;\n}\n\n/**\n * Split content into alternating text and protected segments.\n * Protected segments (isCode: true) are excluded from LaTeX processing:\n * - ``` multiline code blocks\n * - ` inline code\n * - HTML tags (e.g. <span>$</span> where $ should not be treated as LaTeX)\n */\nfunction splitByProtectedRegions(content: string): Segment[] {\n const segments: Segment[] = [];\n let lastIndex = 0;\n let inlineStart = -1;\n let multilineStart = -1;\n\n function pushProtected(start: number, end: number) {\n if (start > lastIndex) {\n segments.push({ text: content.substring(lastIndex, start), isCode: false });\n }\n segments.push({ text: content.substring(start, end), isCode: true });\n lastIndex = end;\n }\n\n for (let i = 0; i < content.length; i++) {\n const char = content[i];\n\n // Check for multiline code blocks\n if (char === '`' && i + 2 < content.length && content[i + 1] === '`' && content[i + 2] === '`') {\n if (multilineStart === -1) {\n // Cancel any pending inline code — ``` takes priority over `\n inlineStart = -1;\n multilineStart = i;\n i += 2;\n } else {\n pushProtected(multilineStart, i + 3);\n multilineStart = -1;\n i += 2;\n }\n }\n // Check for inline code (only if not in multiline)\n else if (char === '`' && multilineStart === -1) {\n if (inlineStart === -1) {\n inlineStart = i;\n } else {\n pushProtected(inlineStart, i + 1);\n inlineStart = -1;\n }\n }\n // Check for HTML tags (only if not in code block)\n else if (char === '<' && multilineStart === -1 && inlineStart === -1) {\n // Only match known HTML tags to avoid false positives with angle brackets\n // in markdown links (<Slides Demo>), math comparisons ($a < b$), etc.\n const rest = content.substring(i);\n const tagMatch = rest.match(\n /^<\\/?(span|div|p|br|hr|img|a|em|strong|b|i|u|s|sub|sup|code|pre|table|tr|td|th|thead|tbody|tfoot|ul|ol|li|dl|dt|dd|h[1-6]|blockquote|details|summary|figure|figcaption|section|article|aside|nav|header|footer|main|mark|del|ins|small|abbr|cite|dfn|kbd|samp|var|ruby|rt|rp|bdo|wbr|input|button|select|textarea|label|fieldset|legend|output|iframe|video|audio|source|canvas|svg|math|time)(?:\\s[^>]*)?\\/?>/i\n );\n if (tagMatch) {\n pushProtected(i, i + tagMatch[0].length);\n i += tagMatch[0].length - 1; // -1 because loop does i++\n }\n }\n }\n\n // Push remaining text\n if (lastIndex < content.length) {\n segments.push({ text: content.substring(lastIndex), isCode: false });\n }\n\n return segments;\n}\n\n/**\n * Escape mhchem commands in LaTeX expressions to ensure proper rendering.\n *\n * @param text Input string containing LaTeX expressions with mhchem commands\n * @returns String with escaped mhchem commands\n * @from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nfunction escapeMhchemCommands(text: string) {\n return text.replaceAll('$\\\\ce{', '$\\\\\\\\ce{').replaceAll('$\\\\pu{', '$\\\\\\\\pu{');\n}\n\nconst CURRENCY_REGEX = /(?<![\\\\$])\\$(?!\\$)(?=\\d+(?:,\\d{3})*(?:\\.\\d+)?(?:[KMBkmb])?(?:\\s|$|[^a-zA-Z\\d]))/g;\nconst NO_ESCAPED_DOLLAR_REGEX = /(?<![\\\\$])\\$(?!\\$)/g;\n// Match \\[...\\] and \\(...\\) as LaTeX delimiters, but exclude:\n// - !\\[...\\] (markdown image)\n// - \\[...\\]( (markdown link)\nconst DELIMITERS_REGEX = /(?<!!)\\\\\\[([\\S\\s]*?[^\\\\])\\\\](?!\\()|\\\\\\((.*?)\\\\\\)/g;\nconst ARRAY_COL_SPEC_OR_PIPE_REGEX = /(\\\\begin\\{(?:array|tabular[x*]?)\\}\\{[^}]*\\})|(?<!\\\\)\\|/g;\n// Display $$ allows multiline; inline $ forbids newlines (consistent with SINGLE_DOLLAR_REGEX)\nconst LATEX_BLOCK_REGEX = /\\$\\$([\\S\\s]*?)\\$\\$|(?<![\\\\$])\\$(?!\\$)((?:[^$\\n]|\\\\\\$)*?)(?<![\\\\`])\\$(?!\\$)/g;\nconst ESCAPE_TEXT_UNDERSCORES_REGEX = /\\\\text{([^}]*)}/g;\nconst SINGLE_DOLLAR_REGEX = /(?<![\\\\$])\\$(?!\\$)((?:[^$\\n]|\\\\[$])+?)(?<!\\\\)(?<!`)\\$(?!\\$)/g;\n\n/**\n * Escape currency dollar signs (e.g. $100, $1,000.50) so they are not\n * misinterpreted as LaTeX delimiters.\n *\n * The tricky part: a `$` followed by digits might still be inside a LaTeX\n * expression (e.g. `$8.29 \\text{ B} \\times 4$`). We detect this by checking\n * whether there is an odd number of unescaped `$` on the same line after the\n * current match — if so, the current `$` is a LaTeX opener, not currency.\n */\nfunction escapeCurrencyDollarSigns(text: string): string {\n const parts: string[] = [];\n let lastIndex = 0;\n const currencyMatches = Array.from(text.matchAll(CURRENCY_REGEX));\n\n for (let i = 0; i < currencyMatches.length; i++) {\n const match = currencyMatches[i];\n parts.push(text.substring(lastIndex, match.index));\n\n let needEscape = true;\n let restBeforeNextMatchOrEnd = '';\n if (i < currencyMatches.length - 1) {\n const nextMatch = currencyMatches[i + 1];\n if (nextMatch.index - match.index > 1) {\n restBeforeNextMatchOrEnd = text.substring(match.index + 1, nextMatch.index);\n }\n } else {\n restBeforeNextMatchOrEnd = text.substring(match.index + 1);\n }\n const firstLineBeforeNextMatch = restBeforeNextMatchOrEnd.split(/\\r\\n|\\r|\\n/g)[0];\n if (Array.from(firstLineBeforeNextMatch.matchAll(NO_ESCAPED_DOLLAR_REGEX)).length % 2 !== 0) {\n const previousNewContent = parts.join('');\n const previousLastLineContent = previousNewContent.split(/\\r\\n|\\r|\\n/g).pop();\n const wholeLineBeforeNextMatchWithoutCurrentDollar = previousLastLineContent + firstLineBeforeNextMatch;\n if (Array.from(wholeLineBeforeNextMatchWithoutCurrentDollar.matchAll(NO_ESCAPED_DOLLAR_REGEX)).length % 2 !== 0) {\n needEscape = false;\n }\n }\n\n parts.push(needEscape ? '\\\\$' : '$');\n lastIndex = match.index + 1;\n }\n parts.push(text.substring(lastIndex));\n return parts.join('');\n}\n\n/**\n * Convert LaTeX bracket delimiters to dollar sign delimiters.\n * Converts \\[...\\] to $$...$$ and \\(...\\) to $...$\n *\n * @param text Input string containing LaTeX expressions\n * @returns String with LaTeX bracket delimiters converted to dollar sign delimiters\n * @modified from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nfunction convertLatexDelimiters(text: string): string {\n return text.replaceAll(\n DELIMITERS_REGEX,\n (match: string, squareBracket: string | undefined, roundBracket: string | undefined): string => {\n if (squareBracket !== undefined) {\n return `$$${squareBracket}$$`;\n } else if (roundBracket !== undefined) {\n return `$${roundBracket}$`;\n }\n return match;\n }\n );\n}\n\n/**\n * Helper function: replace unescaped pipes with \\vert in LaTeX math fragments\n * @from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nconst replaceUnescapedPipes = (formula: string): string =>\n // Use \\vert{} so the control sequence terminates before the next token.\n // Preserve `|` inside \\begin{array}{...} / \\begin{tabular}{...} column specifiers.\n formula.replaceAll(ARRAY_COL_SPEC_OR_PIPE_REGEX, (match, colSpec: string | undefined) =>\n colSpec !== undefined ? match : '\\\\vert{}'\n );\n/**\n * Escape pipes in LaTeX expressions to prevent them from being interpreted as\n * column separators in markdown tables.\n *\n * @param text Input string containing LaTeX expressions\n * @returns String with pipes escaped in LaTeX expressions\n * @modified from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nfunction escapeLatexPipes(text: string): string {\n return text.replaceAll(LATEX_BLOCK_REGEX, (match, display, inline) => {\n if (display !== undefined) return `$$${replaceUnescapedPipes(display)}$$`;\n if (inline !== undefined) return `$${replaceUnescapedPipes(inline)}$`;\n return match;\n });\n}\n\n/**\n * Escape unescaped underscores within \\text{...} commands in LaTeX expressions.\n * For example, \\text{node_domain} becomes \\text{node\\_domain},\n * but \\text{node\\_domain} remains \\text{node\\_domain}.\n *\n * @param text Input string that may contain LaTeX expressions\n * @returns String with unescaped underscores escaped within \\text{...} commands\n * @modified from https://github.com/lobehub/lobe-ui/blob/master/src/hooks/useMarkdown/latex.ts\n */\nfunction escapeTextUnderscores(text: string): string {\n return text.replaceAll(ESCAPE_TEXT_UNDERSCORES_REGEX, (_match, textContent: string) => {\n const escapedTextContent = textContent.replaceAll(/(?<!\\\\)_/g, '\\\\_');\n return `\\\\text{${escapedTextContent}}`;\n });\n}\n\n/**\n * Convert single dollar delimiters to double dollar delimiters.\n * e.g. $x^2$ → $$x^2$$\n */\nfunction convertSingleToDoubleDollar(text: string): string {\n return text.replaceAll(SINGLE_DOLLAR_REGEX, (_match, content: string) => `$$${content}$$`);\n}\n\n/**\n * Main LaTeX preprocessor entry point.\n *\n * Splits the input into protected regions (code blocks, inline code, HTML tags)\n * and applies the full normalization pipeline to unprotected text segments.\n * Returns the input unchanged when no LaTeX-related characters (`$`, `\\[`, `\\(`)\n * are detected.\n *\n * @param str - Raw markdown string.\n * @returns The preprocessed string with normalized LaTeX delimiters.\n */\nexport function preprocessLaTeX(str: string): string {\n // Return early if no LaTeX patterns are found\n if (!str.includes('$') && !str.includes('\\\\[') && !str.includes('\\\\(')) return str;\n\n // Step 1: split by code blocks\n const segments = splitByProtectedRegions(str);\n\n // Step 2: process each non-code segment through the LaTeX pipeline\n const result = segments.map((segment) => {\n if (segment.isCode) return segment.text;\n\n let text = segment.text;\n text = escapeMhchemCommands(text);\n text = escapeCurrencyDollarSigns(text);\n text = convertLatexDelimiters(text);\n text = escapeLatexPipes(text);\n text = escapeTextUnderscores(text);\n text = convertSingleToDoubleDollar(text);\n return text;\n });\n\n return result.join('');\n}\n","/**\n * Content preprocessing pipeline.\n *\n * Runs all preprocessors (built-in + user-supplied) in sequence before\n * the markdown string is handed to react-markdown. The built-in LaTeX\n * preprocessor always runs first, followed by any extra preprocessors\n * provided by the consumer.\n *\n * @module preprocessors\n */\n\nimport { AIMDContentPreprocessor } from './defs';\nimport { preprocessLaTeX } from './latex';\n\n/** Sequentially apply an array of preprocessor functions via left-fold. */\nfunction applyPreprocessors(value: string, ...fns: Array<AIMDContentPreprocessor>): string {\n return fns.reduce((result, fn) => fn(result), value);\n}\n\n/** Stable empty array to avoid re-renders when no extra preprocessors are given. */\nconst defaultExtraPreprocessors: AIMDContentPreprocessor[] = [];\n\n/**\n * Run the full preprocessing pipeline on raw markdown content.\n *\n * @param content - Raw markdown string.\n * @param extraPreprocessors - Optional user-supplied preprocessors appended after the built-in ones.\n * @returns The preprocessed markdown string ready for rendering.\n */\nexport default function preprocessAIMDContent(\n content: string,\n extraPreprocessors: AIMDContentPreprocessor[] = defaultExtraPreprocessors\n) {\n return applyPreprocessors(content, preprocessLaTeX, ...extraPreprocessors);\n}\n","/**\n * Core markdown rendering component.\n *\n * Wraps `react-markdown` with a curated set of remark and rehype plugins\n * for GFM, math/LaTeX, emoji, CJK support, and configurable extra syntax\n * extensions and display optimizations. Plugin selection is driven by the\n * {@link AIMarkdownRenderConfig} from context.\n *\n * @module components/MarkdownContent\n */\n\nimport { memo, useMemo } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport rehypeKatex from 'rehype-katex';\nimport rehypeRaw from 'rehype-raw';\nimport rehypeUnwrapImages from 'rehype-unwrap-images';\nimport rehypeSanitize, { defaultSchema } from 'rehype-sanitize';\nimport remarkBreaks from 'remark-breaks';\nimport remarkCjkFriendly from 'remark-cjk-friendly';\nimport remarkCjkFriendlyGfmStrikethrough from 'remark-cjk-friendly-gfm-strikethrough';\nimport remarkEmoji from 'remark-emoji';\nimport remarkGfm from 'remark-gfm';\nimport remarkMath from 'remark-math';\nimport { remarkDefinitionList, defListHastHandlers } from 'remark-definition-list';\nimport remarkSupersub from 'remark-supersub';\nimport { remarkMark as remarkMarkHighlight } from 'remark-mark-highlight';\nimport remarkSqueezeParagraphs from 'remark-squeeze-paragraphs';\nimport remarkSmartypants from 'remark-smartypants';\nimport remarkPangu from 'remark-pangu';\nimport remarkRemoveComments from 'remark-remove-comments';\nimport { useAIMarkdownRenderState } from '../context';\nimport {\n AIMarkdownCustomComponents,\n AIMarkdownRenderDisplayOptimizeAbility,\n AIMarkdownRenderExtraSyntax,\n} from '../defs';\n\n/** Maps display optimization abilities to their corresponding remark plugins. */\nconst DisplayOptimizeRemarkPluginMap = {\n [AIMarkdownRenderDisplayOptimizeAbility.REMOVE_COMMENTS]: remarkRemoveComments,\n [AIMarkdownRenderDisplayOptimizeAbility.SMARTYPANTS]: remarkSmartypants,\n [AIMarkdownRenderDisplayOptimizeAbility.PANGU]: remarkPangu,\n};\n\n/** Maps extra syntax extensions to their corresponding remark plugins. */\nconst ExtraSyntaxRemarkPluginMap = {\n [AIMarkdownRenderExtraSyntax.HIGHLIGHT]: remarkMarkHighlight,\n [AIMarkdownRenderExtraSyntax.DEFINITION_LIST]: remarkDefinitionList,\n [AIMarkdownRenderExtraSyntax.SUBSCRIPT]: remarkSupersub,\n};\n\n/** Stable empty object to avoid unnecessary re-renders when no custom components are given. */\nconst DefaultCustomComponents: AIMarkdownCustomComponents = {};\n\ninterface AIMarkdownContentProps {\n /** Preprocessed markdown string to render. */\n content: string;\n /** Optional react-markdown component overrides (e.g. custom code block renderer). */\n customComponents?: AIMarkdownCustomComponents;\n}\n\n/**\n * Internal component that assembles the remark/rehype plugin chain based on\n * the current render config and delegates to `ReactMarkdown`.\n */\nconst AIMarkdownContent = memo(({ content, customComponents }: AIMarkdownContentProps) => {\n const { config } = useAIMarkdownRenderState();\n\n // Resolve extra-syntax remark plugins and check if definition list HAST handlers are needed.\n const { extraSyntaxRemarkPlugins, enableDefinitionList } = useMemo(\n () => ({\n extraSyntaxRemarkPlugins: config.extraSyntaxSupported.map((syntax) => ExtraSyntaxRemarkPluginMap[syntax]),\n enableDefinitionList: config.extraSyntaxSupported.includes(AIMarkdownRenderExtraSyntax.DEFINITION_LIST),\n }),\n [config.extraSyntaxSupported]\n );\n\n const displayOptimizeRemarkPlugins = useMemo(() => {\n return config.displayOptimizeAbilities.map((ability) => DisplayOptimizeRemarkPluginMap[ability]);\n }, [config.displayOptimizeAbilities]);\n\n const usedComponents = useMemo(() => {\n return customComponents ? { ...DefaultCustomComponents, ...customComponents } : DefaultCustomComponents;\n }, [customComponents]);\n\n return (\n <ReactMarkdown\n remarkPlugins={[\n // --- Core plugins (always active) ---\n remarkGfm,\n [\n remarkMath,\n {\n // Disable single-dollar inline math to avoid conflicts with currency\n // signs and other dollar usages; the preprocessor converts $...$ to $$...$$.\n singleDollarTextMath: false,\n },\n ],\n // --- Configurable extra syntax plugins ---\n ...extraSyntaxRemarkPlugins,\n // --- Formatting & normalization ---\n remarkBreaks,\n remarkEmoji,\n remarkSqueezeParagraphs,\n remarkCjkFriendly,\n remarkCjkFriendlyGfmStrikethrough,\n // --- Configurable display optimizations ---\n ...displayOptimizeRemarkPlugins,\n ]}\n rehypePlugins={[\n // Allow raw HTML through so rehype-sanitize can handle it.\n [\n rehypeRaw,\n {\n passThrough: [],\n },\n ],\n // Sanitize HTML while allowing <mark> (highlight) and KaTeX class names.\n [\n rehypeSanitize,\n {\n ...defaultSchema,\n tagNames: [...(defaultSchema.tagNames || []), 'mark'],\n attributes: {\n ...defaultSchema.attributes,\n // The `language-*` regex is allowed by default.\n code: [['className', /^language-./, 'math-inline', 'math-display']],\n },\n },\n ],\n rehypeKatex,\n rehypeUnwrapImages,\n ]}\n remarkRehypeOptions={{\n allowDangerousHtml: true,\n handlers: {\n // Inject definition-list HAST handlers when the extension is active.\n ...(enableDefinitionList ? defListHastHandlers : {}),\n },\n }}\n components={usedComponents}\n // NOTE: The default `urlTransform` in Windows environments treats local\n // paths (e.g. `C:/...`) as unsafe. Uncomment the line below if needed:\n // urlTransform={(url: string) => url}\n >\n {content}\n </ReactMarkdown>\n );\n});\n\nAIMarkdownContent.displayName = 'AIMarkdownContent';\n\nexport default AIMarkdownContent;\n","/**\n * Hook for referential stability of deep-equal values.\n *\n * @module hooks/useStableValue\n */\n\nimport { useRef, useEffect } from 'react';\nimport isEqual from 'lodash-es/isEqual';\n\n/**\n * Returns a referentially stable version of `value`.\n *\n * On each render the new value is deep-compared (via `lodash/isEqual`) against\n * the previous one. If they are structurally equal the *previous* reference is\n * returned, preventing unnecessary re-renders in downstream `useMemo` / `useEffect`\n * consumers that depend on reference equality.\n *\n * @typeParam T - The value type.\n * @param value - The potentially new value to stabilize.\n * @returns The previous reference when deep-equal, otherwise the new value.\n *\n * @example\n * ```tsx\n * const stableConfig = useStableValue(config);\n * // stableConfig keeps the same reference as long as config is deep-equal.\n * ```\n */\nexport default function useStableValue<T>(value: T): T {\n const ref = useRef(value);\n\n // eslint-disable-next-line react-hooks/refs\n const prev = ref.current;\n const stableValue = isEqual(prev, value) ? prev : value;\n\n useEffect(() => {\n ref.current = stableValue;\n }, [stableValue]);\n\n return stableValue;\n}\n","/**\n * Default typography wrapper component.\n *\n * Renders a `<div>` container that applies CSS class names for the active\n * variant and color scheme, and sets the root font-size as an inline style.\n * The corresponding CSS custom properties are defined in the SCSS variant\n * files under `typography/variants/`.\n *\n * Consumers can replace this with a custom {@link AIMarkdownTypographyComponent}\n * via the `Typography` prop on `<AIMarkdown>`.\n *\n * @module components/typography/Default\n */\n\nimport { memo } from 'react';\nimport type { AIMarkdownTypographyProps } from '../../defs';\n\nconst DefaultTypography = memo(({ children, fontSize, variant, colorScheme, style }: AIMarkdownTypographyProps) => (\n <div\n className={`aim-typography-root ${variant ?? ''} ${colorScheme ?? ''}`.trim()}\n style={{ width: '100%', fontSize, ...style }}\n >\n {children}\n </div>\n));\n\nDefaultTypography.displayName = 'DefaultTypography';\n\nexport default DefaultTypography;\n"],"mappings":"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","names":["useMemo","memo","AIMarkdownRenderExtraSyntax","AIMarkdownRenderDisplayOptimizeAbility","useMemo","jsx","useMemo","memo","jsx","jsx","useMemo","memo"]}
|
package/package.json
CHANGED