Skip to content

Commit 0ebf67d

Browse files
1.3 changes (#145)
* Bump deps * Run Ultracite * Update code-block.tsx * Update parse-blocks.tsx * Create great-pants-trade.md
1 parent 64b5afa commit 0ebf67d

File tree

12 files changed

+132
-104
lines changed

12 files changed

+132
-104
lines changed

.changeset/great-pants-trade.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"streamdown": patch
3+
---
4+
5+
misc 1.3 fixes and cleanup

apps/website/app/components/mermaid.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,17 @@ sequenceDiagram
4545
<Section
4646
description={
4747
<>
48-
Streamdown supports Mermaid diagrams with customizable themes. Current theme is "base".
48+
Streamdown supports Mermaid diagrams with customizable themes. Current
49+
theme is "base".
4950
</>
5051
}
5152
markdown={mermaidExample}
53+
speed={60}
5254
streamdownProps={{
5355
mermaidConfig: {
54-
theme: "base"
55-
}
56+
theme: "base",
57+
},
5658
}}
57-
speed={60}
5859
title="Interactive Mermaid Diagrams"
5960
/>
6061
);

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
"changeset:publish": "pnpm build && changeset publish"
1616
},
1717
"devDependencies": {
18-
"@biomejs/biome": "2.2.2",
19-
"@changesets/cli": "^2.29.6",
18+
"@biomejs/biome": "2.2.4",
19+
"@changesets/cli": "^2.29.7",
2020
"turbo": "^2.5.6",
2121
"typescript": "5.9.2",
22-
"ultracite": "5.3.3"
22+
"ultracite": "5.3.4"
2323
},
24-
"packageManager": "pnpm@10.15.1",
24+
"packageManager": "pnpm@10.16.1",
2525
"engines": {
2626
"node": ">=18"
2727
}

packages/streamdown/__tests__/mermaid.test.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { render } from "@testing-library/react";
2-
import { beforeEach, describe, expect, it, vi } from "vitest";
32
import type { MermaidConfig } from "mermaid";
3+
import { beforeEach, describe, expect, it, vi } from "vitest";
44
import { Mermaid } from "../lib/mermaid";
55

66
// Mock mermaid
@@ -79,7 +79,9 @@ describe("Mermaid", () => {
7979
theme: "forest",
8080
} as MermaidConfig;
8181

82-
const { rerender } = render(<Mermaid chart="graph TD; A-->B" config={config1} />);
82+
const { rerender } = render(
83+
<Mermaid chart="graph TD; A-->B" config={config1} />
84+
);
8385

8486
// Should render without error
8587
expect(mockRender).toBeDefined();
@@ -106,7 +108,9 @@ describe("Mermaid", () => {
106108
fontFamily: "Arial",
107109
} as MermaidConfig;
108110

109-
const { container } = render(<Mermaid chart="graph TD; A-->B" config={config} />);
111+
const { container } = render(
112+
<Mermaid chart="graph TD; A-->B" config={config} />
113+
);
110114

111115
// Should render without error even with complex config
112116
expect(container.firstChild).toBeTruthy();
@@ -117,14 +121,16 @@ describe("Mermaid", () => {
117121
const config2: MermaidConfig = { theme: "dark" } as MermaidConfig;
118122

119123
// Render first component
120-
const { rerender } = render(<Mermaid chart="graph TD; A-->B" config={config1} />);
121-
124+
const { rerender } = render(
125+
<Mermaid chart="graph TD; A-->B" config={config1} />
126+
);
127+
122128
await vi.waitFor(() => expect(mockInitialize).toHaveBeenCalledTimes(1));
123129
expect(mockInitialize.mock.calls[0][0].theme).toBe("forest");
124130

125131
// Render second component with different config
126132
rerender(<Mermaid chart="graph TD; X-->Y" config={config2} />);
127-
133+
128134
await vi.waitFor(() => expect(mockInitialize).toHaveBeenCalledTimes(2));
129135
expect(mockInitialize.mock.calls[1][0].theme).toBe("dark");
130136
});

packages/streamdown/__tests__/parse-incomplete-markdown.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,9 @@ describe("parseIncompleteMarkdown", () => {
196196
"\\_escaped\\_ and _unescaped_"
197197
);
198198

199-
expect(parseIncompleteMarkdown("Start \\_escaped\\_ middle _incomplete")).toBe(
200-
"Start \\_escaped\\_ middle _incomplete_"
201-
);
199+
expect(
200+
parseIncompleteMarkdown("Start \\_escaped\\_ middle _incomplete")
201+
).toBe("Start \\_escaped\\_ middle _incomplete_");
202202

203203
expect(parseIncompleteMarkdown("\\_fully\\_escaped\\_")).toBe(
204204
"\\_fully\\_escaped\\_"

packages/streamdown/__tests__/streamdown.test.tsx

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { render } from "@testing-library/react";
2-
import { describe, expect, it, vi } from "vitest";
32
import type { MermaidConfig } from "mermaid";
3+
import { describe, expect, it, vi } from "vitest";
44
import { Streamdown } from "../index";
55

66
// Mock the dependencies
@@ -241,21 +241,15 @@ And an incomplete [link
241241
} as MermaidConfig;
242242

243243
const { container } = render(
244-
<Streamdown mermaidConfig={mermaidConfig}>
245-
Test content
246-
</Streamdown>
244+
<Streamdown mermaidConfig={mermaidConfig}>Test content</Streamdown>
247245
);
248246

249247
// Just verify it renders without error when mermaidConfig is provided
250248
expect(container.firstElementChild).toBeTruthy();
251249
});
252250

253251
it("should render without mermaidConfig", () => {
254-
const { container } = render(
255-
<Streamdown>
256-
Test content
257-
</Streamdown>
258-
);
252+
const { container } = render(<Streamdown>Test content</Streamdown>);
259253

260254
// Just verify it renders without error when mermaidConfig is not provided
261255
expect(container.firstElementChild).toBeTruthy();

packages/streamdown/lib/code-block.tsx

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import {
1212
} from "react";
1313
import {
1414
type BundledLanguage,
15-
bundledLanguages,
1615
type BundledTheme,
16+
bundledLanguages,
1717
createHighlighter,
18-
SpecialLanguage,
18+
type SpecialLanguage,
1919
} from "shiki";
2020
import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
2121
import { ShikiThemeContext } from "../index";
@@ -49,13 +49,12 @@ class HighlighterManager {
4949
private readonly loadedLanguages: Set<BundledLanguage> = new Set();
5050
private initializationPromise: Promise<void> | null = null;
5151

52-
5352
private isLanguageSupported(language: string): language is BundledLanguage {
5453
return Object.hasOwn(bundledLanguages, language);
5554
}
5655

5756
private getFallbackLanguage(): SpecialLanguage {
58-
return 'text';
57+
return "text";
5958
}
6059

6160
private async ensureHighlightersInitialized(
@@ -78,7 +77,8 @@ class HighlighterManager {
7877

7978
// Check if we need to load the language
8079
const isLanguageSupported = this.isLanguageSupported(language);
81-
const needsLanguageLoad = !this.loadedLanguages.has(language) && isLanguageSupported;
80+
const needsLanguageLoad =
81+
!this.loadedLanguages.has(language) && isLanguageSupported;
8282

8383
// Create or recreate light highlighter if needed
8484
if (needsLightRecreation) {
@@ -100,12 +100,19 @@ class HighlighterManager {
100100
if (needsDarkRecreation) {
101101
// If recreating dark highlighter, load all previously loaded languages plus the new one
102102
const langsToLoad = needsLanguageLoad
103-
? [...this.loadedLanguages].concat(isLanguageSupported ? [language] : [])
103+
? [...this.loadedLanguages].concat(
104+
isLanguageSupported ? [language] : []
105+
)
104106
: Array.from(this.loadedLanguages);
105107

106108
this.darkHighlighter = await createHighlighter({
107109
themes: [darkTheme],
108-
langs: langsToLoad.length > 0 ? langsToLoad : isLanguageSupported ? [language] : [],
110+
langs:
111+
langsToLoad.length > 0
112+
? langsToLoad
113+
: isLanguageSupported
114+
? [language]
115+
: [],
109116
engine: jsEngine,
110117
});
111118
this.darkTheme = darkTheme;
@@ -120,7 +127,6 @@ class HighlighterManager {
120127
}
121128
}
122129

123-
124130
async highlightCode(
125131
code: string,
126132
language: BundledLanguage,
@@ -138,12 +144,13 @@ class HighlighterManager {
138144
);
139145
await this.initializationPromise;
140146
this.initializationPromise = null;
141-
147+
142148
const [lightTheme, darkTheme] = themes;
143149

144-
const lang = this.isLanguageSupported(language) ? language : this.getFallbackLanguage();
150+
const lang = this.isLanguageSupported(language)
151+
? language
152+
: this.getFallbackLanguage();
145153

146-
147154
const light = this.lightHighlighter?.codeToHtml(code, {
148155
lang,
149156
theme: lightTheme,
@@ -153,14 +160,14 @@ class HighlighterManager {
153160
lang,
154161
theme: darkTheme,
155162
});
156-
157-
163+
158164
const addPreClass = (html: string) => {
159-
if (!preClassName) return html;
165+
if (!preClassName) {
166+
return html;
167+
}
160168
return html.replace(PRE_TAG_REGEX, `<pre class="${preClassName}"$1`);
161169
};
162170

163-
164171
return [
165172
removePreBackground(addPreClass(light)),
166173
removePreBackground(addPreClass(dark)),

packages/streamdown/lib/mermaid.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import type { MermaidConfig } from "mermaid";
22
import { useEffect, useState } from "react";
33
import { cn } from "./utils";
44

5-
65
const initializeMermaid = async (customConfig?: MermaidConfig) => {
76
const defaultConfig: MermaidConfig = {
87
startOnLoad: false,
@@ -13,7 +12,7 @@ const initializeMermaid = async (customConfig?: MermaidConfig) => {
1312
} as MermaidConfig;
1413

1514
const config = { ...defaultConfig, ...customConfig };
16-
15+
1716
const mermaidModule = await import("mermaid");
1817
const mermaid = mermaidModule.default;
1918

packages/streamdown/lib/parse-blocks.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ export const parseMarkdownIntoBlocks = (markdown: string): string[] => {
77
// Post-process to merge consecutive blocks that are part of the same math block
88
const mergedBlocks: string[] = [];
99

10-
for (let i = 0; i < blocks.length; i++) {
11-
const currentBlock = blocks[i];
12-
10+
for (const currentBlock of blocks) {
1311
// Check if this is a standalone $$ that might be a closing delimiter
1412
if (currentBlock.trim() === "$$" && mergedBlocks.length > 0) {
1513
const previousBlock = mergedBlocks.at(-1);
1614

15+
if (!previousBlock) {
16+
continue;
17+
}
18+
1719
// Check if the previous block starts with $$ but doesn't end with $$
1820
const prevStartsWith$$ = previousBlock.trimStart().startsWith("$$");
1921
const prevDollarCount = (previousBlock.match(/\$\$/g) || []).length;
@@ -28,6 +30,11 @@ export const parseMarkdownIntoBlocks = (markdown: string): string[] => {
2830
// Check if current block ends with $$ and previous block started with $$ but didn't close
2931
if (mergedBlocks.length > 0 && currentBlock.trimEnd().endsWith("$$")) {
3032
const previousBlock = mergedBlocks.at(-1);
33+
34+
if (!previousBlock) {
35+
continue;
36+
}
37+
3138
const prevStartsWith$$ = previousBlock.trimStart().startsWith("$$");
3239
const prevDollarCount = (previousBlock.match(/\$\$/g) || []).length;
3340
const currDollarCount = (currentBlock.match(/\$\$/g) || []).length;

packages/streamdown/lib/parse-incomplete-markdown.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,12 @@ const countSingleUnderscores = (text: string): number => {
255255
return acc;
256256
}
257257
// Skip if underscore is word-internal (between word characters)
258-
if (prevChar && nextChar && /[\p{L}\p{N}_]/u.test(prevChar) && /[\p{L}\p{N}_]/u.test(nextChar)) {
258+
if (
259+
prevChar &&
260+
nextChar &&
261+
/[\p{L}\p{N}_]/u.test(prevChar) &&
262+
/[\p{L}\p{N}_]/u.test(nextChar)
263+
) {
259264
return acc;
260265
}
261266
if (prevChar !== "_" && nextChar !== "_") {

0 commit comments

Comments
 (0)