@lofcz/streamdown 2.9.1 → 2.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-NE4QKYRI.js +29 -0
- package/dist/highlighted-body-5PIGDDHF.js +2 -0
- package/dist/hooks/use-deferred-render.d.ts +57 -0
- package/dist/index.d.ts +53 -498
- package/dist/index.js +1 -1
- package/dist/lib/animate.d.ts +66 -0
- package/dist/lib/block-incomplete-context.d.ts +14 -0
- package/dist/lib/code-block/body.d.ts +12 -0
- package/dist/lib/code-block/container.d.ts +8 -0
- package/dist/lib/code-block/context.d.ts +6 -0
- package/dist/lib/code-block/copy-button.d.ts +9 -0
- package/dist/lib/code-block/download-button.d.ts +9 -0
- package/dist/lib/code-block/header.d.ts +5 -0
- package/dist/lib/code-block/highlighted-body.d.ts +12 -0
- package/dist/lib/code-block/index.d.ts +13 -0
- package/dist/lib/code-block/skeleton.d.ts +1 -0
- package/dist/lib/components.d.ts +2 -0
- package/dist/lib/detect-direction.d.ts +13 -0
- package/dist/lib/icon-context.d.ts +23 -0
- package/dist/lib/icons.d.ts +14 -0
- package/dist/lib/image.d.ts +5 -0
- package/dist/lib/incomplete-code-utils.d.ts +24 -0
- package/dist/lib/link-modal.d.ts +8 -0
- package/dist/lib/markdown.d.ts +30 -0
- package/dist/lib/mermaid/download-button.d.ts +11 -0
- package/dist/lib/mermaid/fullscreen-button.d.ts +10 -0
- package/dist/lib/mermaid/index.d.ts +10 -0
- package/dist/lib/mermaid/pan-zoom.d.ts +19 -0
- package/dist/lib/mermaid/utils.d.ts +41 -0
- package/dist/lib/parse-blocks.d.ts +12 -0
- package/dist/lib/plugin-context.d.ts +29 -0
- package/dist/lib/plugin-types.d.ts +156 -0
- package/dist/lib/prefix-context.d.ts +12 -0
- package/dist/lib/preprocess-custom-tags.d.ts +19 -0
- package/dist/lib/preprocess-literal-tag-content.d.ts +24 -0
- package/dist/lib/rehype/literal-tag-content.d.ts +12 -0
- package/dist/lib/rehype/markdown-in-custom-tags.d.ts +16 -0
- package/dist/lib/remark/code-meta.d.ts +9 -0
- package/dist/lib/remark/container-alerts.d.ts +60 -0
- package/dist/lib/remark/escape-html.d.ts +3 -0
- package/dist/lib/remark/github-alerts.d.ts +16 -0
- package/dist/lib/scroll-lock.d.ts +2 -0
- package/dist/lib/scrollable.d.ts +7 -0
- package/dist/lib/streamdown-context.d.ts +107 -0
- package/dist/lib/table/copy-dropdown.d.ts +8 -0
- package/dist/lib/table/download-dropdown.d.ts +16 -0
- package/dist/lib/table/fullscreen-button.d.ts +8 -0
- package/dist/lib/table/index.d.ts +10 -0
- package/dist/lib/table/utils.d.ts +9 -0
- package/dist/lib/tailwind-classes.d.ts +2 -4
- package/dist/lib/tailwind-classes.js +1 -1
- package/dist/lib/translations-context.d.ts +39 -0
- package/dist/lib/utils.d.ts +23 -0
- package/dist/mermaid-L7RAWDBU.js +2 -0
- package/package.json +8 -5
- package/styles.css +28 -0
- package/dist/chunk-WRHXGPDT.js +0 -27
- package/dist/highlighted-body-33HEA3YT.js +0 -2
- package/dist/mermaid-RQAJEEXV.js +0 -2
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context that indicates whether the current block has an incomplete code fence.
|
|
3
|
+
* True when: streaming is active AND this is the last block AND it has an unclosed code fence.
|
|
4
|
+
*/
|
|
5
|
+
declare const BlockIncompleteContext: import("react").Context<boolean>;
|
|
6
|
+
/**
|
|
7
|
+
* Hook to check if the current block has an incomplete (unclosed) code fence.
|
|
8
|
+
*
|
|
9
|
+
* Returns `true` when the code fence in this block is still being streamed.
|
|
10
|
+
* Useful for deferring expensive renders (syntax highlighting, Mermaid diagrams)
|
|
11
|
+
* until the code block is complete.
|
|
12
|
+
*/
|
|
13
|
+
export declare const useIsCodeFenceIncomplete: () => boolean;
|
|
14
|
+
export { BlockIncompleteContext };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ComponentProps } from "react";
|
|
2
|
+
import type { HighlightResult } from "../plugin-types";
|
|
3
|
+
type CodeBlockBodyProps = ComponentProps<"div"> & {
|
|
4
|
+
maxHeight?: number | string;
|
|
5
|
+
result: HighlightResult;
|
|
6
|
+
language: string;
|
|
7
|
+
startLine?: number;
|
|
8
|
+
/** Show line numbers in code blocks. @default true */
|
|
9
|
+
lineNumbers?: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare const CodeBlockBody: import("react").MemoExoticComponent<({ children, result, language, className, maxHeight, startLine, lineNumbers, ...rest }: CodeBlockBodyProps) => import("react/jsx-runtime").JSX.Element>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ComponentProps } from "react";
|
|
2
|
+
type CodeBlockContainerProps = ComponentProps<"div"> & {
|
|
3
|
+
language: string;
|
|
4
|
+
/** Whether the code block is still being streamed (incomplete) */
|
|
5
|
+
isIncomplete?: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare const CodeBlockContainer: ({ className, language, style, isIncomplete, ...props }: CodeBlockContainerProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ComponentProps } from "react";
|
|
2
|
+
export type CodeBlockCopyButtonProps = ComponentProps<"button"> & {
|
|
3
|
+
onCopy?: () => void;
|
|
4
|
+
onError?: (error: Error) => void;
|
|
5
|
+
timeout?: number;
|
|
6
|
+
};
|
|
7
|
+
export declare const CodeBlockCopyButton: ({ onCopy, onError, timeout, children, className, code: propCode, ...props }: CodeBlockCopyButtonProps & {
|
|
8
|
+
code?: string;
|
|
9
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ComponentProps } from "react";
|
|
2
|
+
export type CodeBlockDownloadButtonProps = ComponentProps<"button"> & {
|
|
3
|
+
onDownload?: () => void;
|
|
4
|
+
onError?: (error: Error) => void;
|
|
5
|
+
};
|
|
6
|
+
export declare const CodeBlockDownloadButton: ({ onDownload, onError, language, children, className, code: propCode, ...props }: CodeBlockDownloadButtonProps & {
|
|
7
|
+
code?: string;
|
|
8
|
+
language?: string;
|
|
9
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type HTMLAttributes } from "react";
|
|
2
|
+
import type { HighlightResult } from "../plugin-types";
|
|
3
|
+
type HighlightedCodeBlockBodyProps = HTMLAttributes<HTMLDivElement> & {
|
|
4
|
+
code: string;
|
|
5
|
+
language: string;
|
|
6
|
+
maxHeight?: number | string;
|
|
7
|
+
raw: HighlightResult;
|
|
8
|
+
startLine?: number;
|
|
9
|
+
lineNumbers?: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare const HighlightedCodeBlockBody: ({ code, language, maxHeight, raw, className, startLine, lineNumbers, ...rest }: HighlightedCodeBlockBodyProps) => import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type HTMLAttributes } from "react";
|
|
2
|
+
type CodeBlockProps = HTMLAttributes<HTMLDivElement> & {
|
|
3
|
+
code: string;
|
|
4
|
+
language: string;
|
|
5
|
+
/** Whether the code block is still being streamed (incomplete) */
|
|
6
|
+
isIncomplete?: boolean;
|
|
7
|
+
/** Custom starting line number for line numbering (default: 1) */
|
|
8
|
+
startLine?: number;
|
|
9
|
+
/** Show line numbers in code blocks. @default true */
|
|
10
|
+
lineNumbers?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare const CodeBlock: ({ code, language, className, children, isIncomplete, startLine, lineNumbers, ...rest }: CodeBlockProps) => import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const CodeBlockSkeleton: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect text direction using the "first strong character" algorithm.
|
|
3
|
+
* Strips common markdown syntax then finds the first Unicode letter
|
|
4
|
+
* with strong directionality.
|
|
5
|
+
*
|
|
6
|
+
* Note: markdown stripping is best-effort — nested formatting,
|
|
7
|
+
* multi-line fenced code blocks, and raw HTML are not fully handled.
|
|
8
|
+
* This is acceptable since the algorithm only needs to reach the first
|
|
9
|
+
* strong character, which is almost always in plain prose.
|
|
10
|
+
*
|
|
11
|
+
* @returns "rtl" if first strong char is RTL, "ltr" otherwise
|
|
12
|
+
*/
|
|
13
|
+
export declare function detectTextDirection(text: string): "ltr" | "rtl";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type SVGProps } from "react";
|
|
2
|
+
export type IconComponent = React.ComponentType<SVGProps<SVGSVGElement> & {
|
|
3
|
+
size?: number;
|
|
4
|
+
}>;
|
|
5
|
+
export interface IconMap {
|
|
6
|
+
CheckIcon: IconComponent;
|
|
7
|
+
CopyIcon: IconComponent;
|
|
8
|
+
DownloadIcon: IconComponent;
|
|
9
|
+
ExternalLinkIcon: IconComponent;
|
|
10
|
+
Loader2Icon: IconComponent;
|
|
11
|
+
Maximize2Icon: IconComponent;
|
|
12
|
+
RotateCcwIcon: IconComponent;
|
|
13
|
+
XIcon: IconComponent;
|
|
14
|
+
ZoomInIcon: IconComponent;
|
|
15
|
+
ZoomOutIcon: IconComponent;
|
|
16
|
+
}
|
|
17
|
+
export declare const defaultIcons: IconMap;
|
|
18
|
+
export declare const IconContext: import("react").Context<IconMap>;
|
|
19
|
+
export declare const IconProvider: ({ icons, children, }: {
|
|
20
|
+
icons?: Partial<IconMap>;
|
|
21
|
+
children: React.ReactNode;
|
|
22
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
export declare const useIcons: () => IconMap;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** biome-ignore-all lint/a11y/noSvgWithoutTitle: "Streamdown icons" */
|
|
2
|
+
import type { SVGProps } from "react";
|
|
3
|
+
type IconProps = SVGProps<SVGSVGElement>;
|
|
4
|
+
export declare const CheckIcon: (props: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare const CopyIcon: (props: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export declare const DownloadIcon: (props: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare const Loader2Icon: (props: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export declare const Maximize2Icon: (props: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare const RotateCcwIcon: (props: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare const XIcon: (props: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare const ExternalLinkIcon: (props: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export declare const ZoomInIcon: (props: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare const ZoomOutIcon: (props: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DetailedHTMLProps, ImgHTMLAttributes } from "react";
|
|
2
|
+
import type { ExtraProps } from "./markdown";
|
|
3
|
+
type ImageComponentProps = DetailedHTMLProps<ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement> & ExtraProps;
|
|
4
|
+
export declare const ImageComponent: ({ node: _node, className, src, alt, onLoad: onLoadProp, onError: onErrorProp, onClick: onClickProp, ...props }: ImageComponentProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
5
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if a markdown string contains an incomplete (unclosed) code fence
|
|
3
|
+
* by walking line-by-line per the CommonMark spec.
|
|
4
|
+
*
|
|
5
|
+
* Only counts fences that start at the beginning of a line (with up to 3
|
|
6
|
+
* spaces of indentation). This avoids false positives from inline backticks
|
|
7
|
+
* and correctly handles fences of any length (3+).
|
|
8
|
+
*
|
|
9
|
+
* A closing fence must use the same character as the opening fence and be
|
|
10
|
+
* at least as long.
|
|
11
|
+
*
|
|
12
|
+
* @param markdown - The markdown string to check
|
|
13
|
+
* @returns true if there's an unclosed code fence
|
|
14
|
+
*/
|
|
15
|
+
export declare const hasIncompleteCodeFence: (markdown: string) => boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Checks if a markdown block contains a GFM table by looking for a
|
|
18
|
+
* delimiter row (e.g., `| --- | --- |`). A delimiter row confirms that
|
|
19
|
+
* the preceding header row forms a table.
|
|
20
|
+
*
|
|
21
|
+
* @param markdown - The markdown string to check
|
|
22
|
+
* @returns true if the block contains a table
|
|
23
|
+
*/
|
|
24
|
+
export declare const hasTable: (markdown: string) => boolean;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface LinkSafetyModalProps {
|
|
2
|
+
isOpen: boolean;
|
|
3
|
+
onClose: () => void;
|
|
4
|
+
onConfirm: () => void;
|
|
5
|
+
url: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const LinkSafetyModal: ({ url, isOpen, onClose, onConfirm, }: LinkSafetyModalProps) => import("react").ReactPortal | null;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Element, Parents } from "hast";
|
|
2
|
+
import type { ComponentType, JSX, ReactElement } from "react";
|
|
3
|
+
import type { Options as RemarkRehypeOptions } from "remark-rehype";
|
|
4
|
+
import type { PluggableList } from "unified";
|
|
5
|
+
export interface ExtraProps {
|
|
6
|
+
node?: Element | undefined;
|
|
7
|
+
}
|
|
8
|
+
export type AllowElement = (element: Readonly<Element>, index: number, parent: Readonly<Parents> | undefined) => boolean | null | undefined;
|
|
9
|
+
export type UrlTransform = (url: string, key: string, node: Readonly<Element>) => string | null | undefined;
|
|
10
|
+
export type Components = {
|
|
11
|
+
[Key in keyof JSX.IntrinsicElements]?: ComponentType<JSX.IntrinsicElements[Key] & ExtraProps> | keyof JSX.IntrinsicElements;
|
|
12
|
+
} & {
|
|
13
|
+
inlineCode?: ComponentType<JSX.IntrinsicElements["code"] & ExtraProps>;
|
|
14
|
+
[key: string]: ComponentType<Record<string, unknown> & ExtraProps> | keyof JSX.IntrinsicElements | undefined;
|
|
15
|
+
};
|
|
16
|
+
export interface Options {
|
|
17
|
+
allowElement?: AllowElement;
|
|
18
|
+
allowedElements?: readonly string[];
|
|
19
|
+
children?: string;
|
|
20
|
+
components?: Components;
|
|
21
|
+
disallowedElements?: readonly string[];
|
|
22
|
+
rehypePlugins?: PluggableList;
|
|
23
|
+
remarkPlugins?: PluggableList;
|
|
24
|
+
remarkRehypeOptions?: Readonly<RemarkRehypeOptions>;
|
|
25
|
+
skipHtml?: boolean;
|
|
26
|
+
unwrapDisallowed?: boolean;
|
|
27
|
+
urlTransform?: UrlTransform;
|
|
28
|
+
}
|
|
29
|
+
export declare const Markdown: (options: Readonly<Options>) => ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
30
|
+
export declare const defaultUrlTransform: UrlTransform;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MermaidConfig } from "../plugin-types";
|
|
2
|
+
interface MermaidDownloadDropdownProps {
|
|
3
|
+
chart: string;
|
|
4
|
+
children?: React.ReactNode;
|
|
5
|
+
className?: string;
|
|
6
|
+
config?: MermaidConfig;
|
|
7
|
+
onDownload?: (format: "mmd" | "png" | "svg") => void;
|
|
8
|
+
onError?: (error: Error) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare const MermaidDownloadDropdown: ({ chart, children, className, onDownload, config, onError, }: MermaidDownloadDropdownProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ComponentProps } from "react";
|
|
2
|
+
import type { MermaidConfig } from "../plugin-types";
|
|
3
|
+
type MermaidFullscreenButtonProps = ComponentProps<"button"> & {
|
|
4
|
+
chart: string;
|
|
5
|
+
config?: MermaidConfig;
|
|
6
|
+
onFullscreen?: () => void;
|
|
7
|
+
onExit?: () => void;
|
|
8
|
+
};
|
|
9
|
+
export declare const MermaidFullscreenButton: ({ chart, config, onFullscreen, onExit, className, ...props }: MermaidFullscreenButtonProps) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MermaidConfig } from "../plugin-types";
|
|
2
|
+
interface MermaidProps {
|
|
3
|
+
chart: string;
|
|
4
|
+
className?: string;
|
|
5
|
+
config?: MermaidConfig;
|
|
6
|
+
fullscreen?: boolean;
|
|
7
|
+
showControls?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const Mermaid: ({ chart, className, config, fullscreen, showControls, }: MermaidProps) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
interface PanZoomProps {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
className?: string;
|
|
5
|
+
contentSize?: {
|
|
6
|
+
height: number;
|
|
7
|
+
width: number;
|
|
8
|
+
} | null;
|
|
9
|
+
fitKey?: string;
|
|
10
|
+
fullscreen?: boolean;
|
|
11
|
+
initialZoom?: number;
|
|
12
|
+
isAutoFit?: boolean;
|
|
13
|
+
maxZoom?: number;
|
|
14
|
+
minZoom?: number;
|
|
15
|
+
showControls?: boolean;
|
|
16
|
+
zoomStep?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare const PanZoom: ({ children, className, contentSize, fitKey: _fitKey, minZoom, maxZoom, zoomStep, showControls, initialZoom, isAutoFit, fullscreen, }: PanZoomProps) => import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensure the SVG root element declares the xlink namespace when the document
|
|
3
|
+
* contains xlink-prefixed attributes (e.g. `xlink:href` used by Mermaid in
|
|
4
|
+
* C4 and sequence diagrams). Without the declaration the SVG is not valid
|
|
5
|
+
* XML: browsers refuse to load it as an image, causing PNG export to fail
|
|
6
|
+
* silently.
|
|
7
|
+
*
|
|
8
|
+
* The fix mirrors the approach used for other Mermaid SVG normalisation
|
|
9
|
+
* issues: detect the problem via a lightweight string check and patch the
|
|
10
|
+
* root `<svg>` opening tag in-place before any further processing.
|
|
11
|
+
*
|
|
12
|
+
* Falls back to the original string when the namespace is already present or
|
|
13
|
+
* when xlink-prefixed attributes are absent.
|
|
14
|
+
*/
|
|
15
|
+
export declare const addXlinkNamespaceIfMissing: (svgString: string) => string;
|
|
16
|
+
/**
|
|
17
|
+
* Mermaid render output may be HTML-serialized. Serialize the SVG node as XML
|
|
18
|
+
* before downloading so embedded HTML like <br> becomes valid SVG markup.
|
|
19
|
+
*/
|
|
20
|
+
export declare const serializeSvgForDownload: (svgString: string) => string;
|
|
21
|
+
/**
|
|
22
|
+
* Normalize Mermaid SVG dimensions for inline rendering.
|
|
23
|
+
* Mermaid emits width="100%" with max-width style, which can shrink very wide
|
|
24
|
+
* diagrams until text becomes unreadable.
|
|
25
|
+
*/
|
|
26
|
+
export declare const getMermaidSvgSize: (svgString: string) => {
|
|
27
|
+
height: number;
|
|
28
|
+
width: number;
|
|
29
|
+
} | null;
|
|
30
|
+
/**
|
|
31
|
+
* Normalize Mermaid SVG dimensions for inline rendering.
|
|
32
|
+
* Mermaid emits width="100%" with max-width style, which can shrink very wide
|
|
33
|
+
* diagrams until text becomes unreadable.
|
|
34
|
+
*/
|
|
35
|
+
export declare const normalizeMermaidInlineSvg: (svgString: string) => string;
|
|
36
|
+
/**
|
|
37
|
+
* Convert SVG string to PNG blob for export
|
|
38
|
+
*/
|
|
39
|
+
export declare const svgToPngBlob: (svgString: string, options?: {
|
|
40
|
+
scale?: number;
|
|
41
|
+
}) => Promise<Blob>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const parseMarkdownIntoBlocks: (markdown: string) => string[];
|
|
2
|
+
export interface IncrementalParseState {
|
|
3
|
+
blocks: string[];
|
|
4
|
+
/** Tag-preprocessed markdown that produced `blocks` (before last-block remend). */
|
|
5
|
+
source: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Append-only parse for streaming: keep settled prefix blocks by identity and
|
|
9
|
+
* only re-lex the unfinished tail. Falls back to a full parse when the source
|
|
10
|
+
* is not a pure append (edits, rewinds, or structural shifts).
|
|
11
|
+
*/
|
|
12
|
+
export declare const parseMarkdownIntoBlocksIncremental: (markdown: string, prev: IncrementalParseState | null | undefined, parseFn?: (value: string) => string[]) => IncrementalParseState;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PluginConfig } from "./plugin-types";
|
|
2
|
+
/**
|
|
3
|
+
* Context for Streamdown plugins
|
|
4
|
+
*/
|
|
5
|
+
export declare const PluginContext: import("react").Context<PluginConfig | null>;
|
|
6
|
+
/**
|
|
7
|
+
* Hook to access all plugins
|
|
8
|
+
*/
|
|
9
|
+
export declare const usePlugins: () => PluginConfig | null;
|
|
10
|
+
/**
|
|
11
|
+
* Hook to access the code plugin
|
|
12
|
+
*/
|
|
13
|
+
export declare const useCodePlugin: () => import("./plugin-types").CodeHighlighterPlugin | null;
|
|
14
|
+
/**
|
|
15
|
+
* Hook to access the mermaid plugin
|
|
16
|
+
*/
|
|
17
|
+
export declare const useMermaidPlugin: () => import("./plugin-types").DiagramPlugin | null;
|
|
18
|
+
/**
|
|
19
|
+
* Hook to access the math plugin
|
|
20
|
+
*/
|
|
21
|
+
export declare const useMathPlugin: () => import("./plugin-types").MathPlugin | null;
|
|
22
|
+
/**
|
|
23
|
+
* Hook to access the cjk plugin
|
|
24
|
+
*/
|
|
25
|
+
export declare const useCjkPlugin: () => import("./plugin-types").CjkPlugin | null;
|
|
26
|
+
/**
|
|
27
|
+
* Hook to find a custom renderer for a given language
|
|
28
|
+
*/
|
|
29
|
+
export declare const useCustomRenderer: (language: string) => import("./plugin-types").CustomRenderer | null;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural type for Mermaid configuration.
|
|
3
|
+
* Avoids a hard dependency on the "mermaid" package in the core bundle.
|
|
4
|
+
* Users who need full type safety can import MermaidConfig from "mermaid"
|
|
5
|
+
* directly — it is structurally compatible with this type.
|
|
6
|
+
*/
|
|
7
|
+
export type MermaidConfig = Record<string, any>;
|
|
8
|
+
import type React from "react";
|
|
9
|
+
import type { BundledLanguage, BundledTheme, ThemeRegistrationAny } from "shiki";
|
|
10
|
+
import type { Pluggable } from "unified";
|
|
11
|
+
export type ThemeInput = BundledTheme | ThemeRegistrationAny;
|
|
12
|
+
/**
|
|
13
|
+
* A single token in a highlighted line
|
|
14
|
+
*/
|
|
15
|
+
export interface HighlightToken {
|
|
16
|
+
bgColor?: string;
|
|
17
|
+
color?: string;
|
|
18
|
+
content: string;
|
|
19
|
+
htmlAttrs?: Record<string, string>;
|
|
20
|
+
htmlStyle?: Record<string, string>;
|
|
21
|
+
offset?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Result from code highlighting (compatible with shiki's TokensResult)
|
|
25
|
+
*/
|
|
26
|
+
export interface HighlightResult {
|
|
27
|
+
bg?: string;
|
|
28
|
+
fg?: string;
|
|
29
|
+
rootStyle?: string | false;
|
|
30
|
+
tokens: HighlightToken[][];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Options for highlighting code
|
|
34
|
+
*/
|
|
35
|
+
export interface HighlightOptions {
|
|
36
|
+
code: string;
|
|
37
|
+
language: BundledLanguage;
|
|
38
|
+
themes: [ThemeInput, ThemeInput];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Plugin for code syntax highlighting (Shiki)
|
|
42
|
+
*/
|
|
43
|
+
export interface CodeHighlighterPlugin {
|
|
44
|
+
/**
|
|
45
|
+
* Get list of supported languages
|
|
46
|
+
*/
|
|
47
|
+
getSupportedLanguages: () => BundledLanguage[];
|
|
48
|
+
/**
|
|
49
|
+
* Get the configured themes
|
|
50
|
+
*/
|
|
51
|
+
getThemes: () => [ThemeInput, ThemeInput];
|
|
52
|
+
/**
|
|
53
|
+
* Highlight code and return tokens
|
|
54
|
+
* Returns null if highlighting not ready yet (async loading)
|
|
55
|
+
* Use callback for async result
|
|
56
|
+
*/
|
|
57
|
+
highlight: (options: HighlightOptions, callback?: (result: HighlightResult) => void) => HighlightResult | null;
|
|
58
|
+
name: "shiki";
|
|
59
|
+
/**
|
|
60
|
+
* Check if language is supported
|
|
61
|
+
*/
|
|
62
|
+
supportsLanguage: (language: BundledLanguage) => boolean;
|
|
63
|
+
type: "code-highlighter";
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Mermaid instance interface
|
|
67
|
+
*/
|
|
68
|
+
export interface MermaidInstance {
|
|
69
|
+
initialize: (config: MermaidConfig) => void;
|
|
70
|
+
render: (id: string, source: string) => Promise<{
|
|
71
|
+
svg: string;
|
|
72
|
+
}>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Plugin for diagram rendering (Mermaid)
|
|
76
|
+
*/
|
|
77
|
+
export interface DiagramPlugin {
|
|
78
|
+
/**
|
|
79
|
+
* Get the mermaid instance (initialized with optional config)
|
|
80
|
+
*/
|
|
81
|
+
getMermaid: (config?: MermaidConfig) => MermaidInstance;
|
|
82
|
+
/**
|
|
83
|
+
* Language identifier for code blocks
|
|
84
|
+
*/
|
|
85
|
+
language: string;
|
|
86
|
+
name: "mermaid";
|
|
87
|
+
type: "diagram";
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Plugin for math rendering (KaTeX)
|
|
91
|
+
*/
|
|
92
|
+
export interface MathPlugin {
|
|
93
|
+
/**
|
|
94
|
+
* Get CSS styles for math rendering (injected into head)
|
|
95
|
+
*/
|
|
96
|
+
getStyles?: () => string;
|
|
97
|
+
name: "katex";
|
|
98
|
+
/**
|
|
99
|
+
* Get rehype plugin for rendering math
|
|
100
|
+
*/
|
|
101
|
+
rehypePlugin: Pluggable;
|
|
102
|
+
/**
|
|
103
|
+
* Get remark plugin for parsing math syntax
|
|
104
|
+
*/
|
|
105
|
+
remarkPlugin: Pluggable;
|
|
106
|
+
type: "math";
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Plugin for CJK text handling
|
|
110
|
+
*/
|
|
111
|
+
export interface CjkPlugin {
|
|
112
|
+
name: "cjk";
|
|
113
|
+
/**
|
|
114
|
+
* @deprecated Use remarkPluginsBefore and remarkPluginsAfter instead
|
|
115
|
+
* All remark plugins (for backwards compatibility)
|
|
116
|
+
*/
|
|
117
|
+
remarkPlugins: Pluggable[];
|
|
118
|
+
/**
|
|
119
|
+
* Remark plugins that must run AFTER remarkGfm
|
|
120
|
+
* (e.g., autolink boundary splitting, strikethrough enhancements)
|
|
121
|
+
*/
|
|
122
|
+
remarkPluginsAfter: Pluggable[];
|
|
123
|
+
/**
|
|
124
|
+
* Remark plugins that must run BEFORE remarkGfm
|
|
125
|
+
* (e.g., remark-cjk-friendly which modifies emphasis handling)
|
|
126
|
+
*/
|
|
127
|
+
remarkPluginsBefore: Pluggable[];
|
|
128
|
+
type: "cjk";
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Union type for all plugins
|
|
132
|
+
*/
|
|
133
|
+
export type StreamdownPlugin = CodeHighlighterPlugin | DiagramPlugin | MathPlugin | CjkPlugin;
|
|
134
|
+
export interface CustomRendererProps {
|
|
135
|
+
code: string;
|
|
136
|
+
isIncomplete: boolean;
|
|
137
|
+
language: string;
|
|
138
|
+
/** Raw metastring from the code fence (everything after the language identifier).
|
|
139
|
+
* e.g. ```rust {1} title="foo" → meta = '{1} title="foo"'
|
|
140
|
+
* Undefined when no metastring is present. */
|
|
141
|
+
meta?: string;
|
|
142
|
+
}
|
|
143
|
+
export interface CustomRenderer {
|
|
144
|
+
component: React.ComponentType<CustomRendererProps>;
|
|
145
|
+
language: string | string[];
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Plugin configuration passed to Streamdown
|
|
149
|
+
*/
|
|
150
|
+
export interface PluginConfig {
|
|
151
|
+
cjk?: CjkPlugin;
|
|
152
|
+
code?: CodeHighlighterPlugin;
|
|
153
|
+
math?: MathPlugin;
|
|
154
|
+
mermaid?: DiagramPlugin;
|
|
155
|
+
renderers?: CustomRenderer[];
|
|
156
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type CnFunction } from "./utils";
|
|
2
|
+
/**
|
|
3
|
+
* Context for providing a prefix-aware `cn` function to all components.
|
|
4
|
+
* Defaults to the standard `cn` (no prefix) for zero-overhead when unused.
|
|
5
|
+
*/
|
|
6
|
+
export declare const PrefixContext: import("react").Context<CnFunction>;
|
|
7
|
+
/**
|
|
8
|
+
* Hook to access the prefix-aware `cn` function.
|
|
9
|
+
* When a prefix is set via `<Streamdown prefix="...">`, this returns
|
|
10
|
+
* a `cn` that prepends the prefix to all class names.
|
|
11
|
+
*/
|
|
12
|
+
export declare const useCn: () => CnFunction;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preprocesses markdown to prevent custom tag blocks from being broken apart
|
|
3
|
+
* by the CommonMark parser. Two issues are addressed:
|
|
4
|
+
*
|
|
5
|
+
* 1. **Inline content after opening tag**: In CommonMark, HTML block types 6/7
|
|
6
|
+
* require the opening tag to start a line on its own (or be followed only by
|
|
7
|
+
* whitespace). When content appears on the same line as the opening tag
|
|
8
|
+
* (e.g. `<custom>content`), the parser treats it as inline HTML inside a
|
|
9
|
+
* paragraph instead of an HTML block, causing the tag to lose its structure.
|
|
10
|
+
* This function ensures the opening tag, content, and closing tag each
|
|
11
|
+
* occupy their own lines.
|
|
12
|
+
*
|
|
13
|
+
* 2. **Blank lines inside tag content**: HTML blocks end at blank lines, which
|
|
14
|
+
* causes custom tags with blank lines in their content to be split across
|
|
15
|
+
* multiple tokens. This function replaces blank lines within matched custom
|
|
16
|
+
* tag pairs with HTML comments (`<!---->`), so the markdown parser treats
|
|
17
|
+
* the entire tag block as a single unit.
|
|
18
|
+
*/
|
|
19
|
+
export declare const preprocessCustomTags: (markdown: string, tagNames: string[]) => string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escapes markdown metacharacters inside the content of specified custom tags,
|
|
3
|
+
* so that markdown inside those tags is rendered as plain text rather than
|
|
4
|
+
* being interpreted as formatting.
|
|
5
|
+
*
|
|
6
|
+
* This must run BEFORE the markdown parser sees the string, because by the time
|
|
7
|
+
* rehype plugins execute the markdown has already been parsed and structural
|
|
8
|
+
* information (e.g. underscores around emphasis) is lost.
|
|
9
|
+
*
|
|
10
|
+
* Note: content that already contains backslash escapes (e.g. `\_`) will be
|
|
11
|
+
* double-escaped and render with a visible backslash. This is acceptable since
|
|
12
|
+
* the intended use case is raw data labels (usernames, handles) that should
|
|
13
|
+
* not contain markdown escape sequences.
|
|
14
|
+
*
|
|
15
|
+
* Example:
|
|
16
|
+
* Input: `<mention user_id="123">_some_username_</mention>`
|
|
17
|
+
* Output: `<mention user_id="123">\_some\_username\_</mention>`
|
|
18
|
+
* Rendered: literal `_some_username_`
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* For each tag in `tagNames`, escapes markdown metacharacters inside the tag's
|
|
22
|
+
* content so that the parser treats the children as plain text.
|
|
23
|
+
*/
|
|
24
|
+
export declare const preprocessLiteralTagContent: (markdown: string, tagNames: string[]) => string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Root } from "hast";
|
|
2
|
+
import type { Plugin } from "unified";
|
|
3
|
+
/**
|
|
4
|
+
* rehype plugin — replaces children of elements whose tag names are in
|
|
5
|
+
* `tagNames` with a single plain-text node. Run this after rehype-raw and
|
|
6
|
+
* rehype-sanitize so the custom elements already exist as proper HAST nodes.
|
|
7
|
+
*
|
|
8
|
+
* Works in tandem with `preprocessLiteralTagContent`, which escapes markdown
|
|
9
|
+
* syntax before remark parses it, ensuring the text value here reflects the
|
|
10
|
+
* original literal content (not stripped markdown markers).
|
|
11
|
+
*/
|
|
12
|
+
export declare const rehypeLiteralTagContent: Plugin<[string[]], Root>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rehype plugin — re-parses text content of custom tag elements as Markdown.
|
|
3
|
+
*
|
|
4
|
+
* When a custom tag contains multiline content (e.g. `<ai-thinking>\n**bold**</ai-thinking>`),
|
|
5
|
+
* the CommonMark parser treats the entire block as an HTML block, passing the
|
|
6
|
+
* inner content through as raw text rather than parsing it as Markdown. This
|
|
7
|
+
* plugin corrects that by finding affected elements and replacing their
|
|
8
|
+
* text-only children with a proper Markdown-parsed HAST subtree.
|
|
9
|
+
*
|
|
10
|
+
* Runs after rehype-raw and rehype-sanitize so that custom elements already
|
|
11
|
+
* exist as proper HAST nodes. Must NOT be applied to tags listed in
|
|
12
|
+
* `literalTagContent` (those intentionally suppress Markdown parsing).
|
|
13
|
+
*/
|
|
14
|
+
import type { Root } from "hast";
|
|
15
|
+
import type { Plugin } from "unified";
|
|
16
|
+
export declare const rehypeMarkdownInCustomTags: Plugin<[string[], string[]?], Root>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Root } from "mdast";
|
|
2
|
+
import type { Plugin } from "unified";
|
|
3
|
+
/**
|
|
4
|
+
* Remark plugin that forwards code fence meta strings to hast properties.
|
|
5
|
+
* This makes the meta string available as a `metastring` prop to the custom
|
|
6
|
+
* code component, enabling features like custom starting line numbers
|
|
7
|
+
* (e.g. `startLine=10`).
|
|
8
|
+
*/
|
|
9
|
+
export declare const remarkCodeMeta: Plugin<[], Root>;
|