Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "streamdown-monorepo",
"private": true,
"scripts": {
"build": "turbo run build",
"build": "pnpm dlx rimraf packages/streamdown/dist && turbo run build --force",
"dev": "turbo run dev",
"check": "ultracite check",
"fix": "ultracite fix",
Expand Down
22 changes: 17 additions & 5 deletions packages/streamdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
type CSSProperties,
createContext,
memo,
ReactNode,
ComponentType,
useEffect,
useId,
useMemo,
Expand All @@ -28,6 +30,7 @@ import { parseMarkdownIntoBlocks } from "./lib/parse-blocks";
import { remarkCjkAutolinkBoundary } from "./lib/remark/cjk-autolink";
import { cn } from "./lib/utils";
import packageJson from "./package.json";
import { CodeBlock } from "./lib/code-block";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import { CodeBlock } from "./lib/code-block";

Unused CodeBlock import on line 33 should be removed from packages/streamdown/index.tsx

Fix on Vercel


// Regex patterns defined at top level for performance
const MIDDLE_DOLLAR_PATTERN = /[^$]\$[^$]/;
Expand Down Expand Up @@ -84,6 +87,7 @@ export type StreamdownProps = Options & {
caret?: keyof typeof carets;
cdnUrl?: string | null;
remend?: RemendOptions;
extraCodeHeader?: ReactNode;
};

export const defaultRehypePlugins: Record<string, Pluggable> = {
Expand Down Expand Up @@ -293,6 +297,7 @@ export const Streamdown = memo(
caret,
cdnUrl = defaultCdnUrl,
remend: remendOptions,
extraCodeHeader,
...props
}: StreamdownProps) => {
// All hooks must be called before any conditional returns
Expand Down Expand Up @@ -353,13 +358,20 @@ export const Streamdown = memo(
);

// Memoize merged components to avoid recreating on every render
const mergedComponents = useMemo(
() => ({
const mergedComponents = useMemo(() => {
if (!extraCodeHeader) {
return { ...defaultComponents, ...components };
}

const Code = defaultComponents?.code as ComponentType<any>;
return {
...defaultComponents,
code: (props: any) => (
<Code {...props} extraCodeHeader={extraCodeHeader} />
),
...components,
}),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When extraCodeHeader is provided along with a custom code component via the components prop, the custom component overwrites the wrapped version that passes extraCodeHeader through, causing the extraCodeHeader prop to be lost

Fix on Vercel

[components]
);
};
}, [components, extraCodeHeader]);

// Only load KaTeX CSS when math syntax is detected in content
useEffect(() => {
Expand Down
9 changes: 7 additions & 2 deletions packages/streamdown/lib/code-block/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,23 @@ import type { ReactNode } from "react";
interface CodeBlockHeaderProps {
language: string;
children: ReactNode;
}
extraComponent: ReactNode
};

export const CodeBlockHeader = ({
language,
children,
extraComponent
}: CodeBlockHeaderProps) => (
<div
className="flex items-center justify-between bg-muted/80 p-3 text-muted-foreground text-xs"
data-language={language}
data-streamdown="code-block-header"
>
<span className="ml-1 font-mono lowercase">{language}</span>
<div className="flex items-center gap-2">{children}</div>
<div className="flex items-center gap-2">
{extraComponent}
{children}
</div>
</div>
);
5 changes: 4 additions & 1 deletion packages/streamdown/lib/code-block/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
useEffect,
useMemo,
useState,
ReactNode
} from "react";
import type { TokensResult } from "shiki";
import { StreamdownContext } from "../../index";
Expand All @@ -16,13 +17,15 @@ import { getHighlightedTokens } from "./highlight";
type CodeBlockProps = HTMLAttributes<HTMLPreElement> & {
code: string;
language: string;
extraCodeHeader?: ReactNode
};

export const CodeBlock = ({
code,
language,
className,
children,
extraCodeHeader,
...rest
}: CodeBlockProps) => {
const { shikiTheme, cdnUrl } = useContext(StreamdownContext);
Expand Down Expand Up @@ -82,7 +85,7 @@ export const CodeBlock = ({
return (
<CodeBlockContext.Provider value={{ code }}>
<CodeBlockContainer language={language}>
<CodeBlockHeader language={language}>{children}</CodeBlockHeader>
<CodeBlockHeader extraComponent={extraCodeHeader} language={language}>{children}</CodeBlockHeader>
<CodeBlockBody
className={className}
language={language}
Expand Down
1 change: 1 addition & 0 deletions packages/streamdown/lib/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,7 @@ const CodeComponent = ({
className={cn("overflow-x-auto border-border border-t", className)}
code={code}
language={language}
extraCodeHeader={props.extraCodeHeader ?? <></>}
>
{showCodeControls ? (
<>
Expand Down
2 changes: 1 addition & 1 deletion pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
packages:
- "apps/*"
- "packages/*"
- "packages/streamdown"

onlyBuiltDependencies:
- '@vercel/speed-insights'
Expand Down