Skip to content

Commit e148e4b

Browse files
authored
🐛 Fix: Resolve Markdown list display issues on Windows (QwenLM#693)
1 parent 48d8587 commit e148e4b

File tree

3 files changed

+38
-20
lines changed

3 files changed

+38
-20
lines changed

packages/cli/src/ui/utils/MarkdownDisplay.test.tsx

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
99
import { MarkdownDisplay } from './MarkdownDisplay.js';
1010
import { LoadedSettings } from '../../config/settings.js';
1111
import { SettingsContext } from '../contexts/SettingsContext.js';
12-
import { EOL } from 'node:os';
1312

1413
describe('<MarkdownDisplay />', () => {
1514
const baseProps = {
@@ -57,7 +56,7 @@ describe('<MarkdownDisplay />', () => {
5756
## Header 2
5857
### Header 3
5958
#### Header 4
60-
`.replace(/\n/g, EOL);
59+
`;
6160
const { lastFrame } = render(
6261
<SettingsContext.Provider value={mockSettings}>
6362
<MarkdownDisplay {...baseProps} text={text} />
@@ -67,10 +66,7 @@ describe('<MarkdownDisplay />', () => {
6766
});
6867

6968
it('renders a fenced code block with a language', () => {
70-
const text = '```javascript\nconst x = 1;\nconsole.log(x);\n```'.replace(
71-
/\n/g,
72-
EOL,
73-
);
69+
const text = '```javascript\nconst x = 1;\nconsole.log(x);\n```';
7470
const { lastFrame } = render(
7571
<SettingsContext.Provider value={mockSettings}>
7672
<MarkdownDisplay {...baseProps} text={text} />
@@ -80,7 +76,7 @@ describe('<MarkdownDisplay />', () => {
8076
});
8177

8278
it('renders a fenced code block without a language', () => {
83-
const text = '```\nplain text\n```'.replace(/\n/g, EOL);
79+
const text = '```\nplain text\n```';
8480
const { lastFrame } = render(
8581
<SettingsContext.Provider value={mockSettings}>
8682
<MarkdownDisplay {...baseProps} text={text} />
@@ -90,7 +86,7 @@ describe('<MarkdownDisplay />', () => {
9086
});
9187

9288
it('handles unclosed (pending) code blocks', () => {
93-
const text = '```typescript\nlet y = 2;'.replace(/\n/g, EOL);
89+
const text = '```typescript\nlet y = 2;';
9490
const { lastFrame } = render(
9591
<SettingsContext.Provider value={mockSettings}>
9692
<MarkdownDisplay {...baseProps} text={text} isPending={true} />
@@ -104,7 +100,7 @@ describe('<MarkdownDisplay />', () => {
104100
- item A
105101
* item B
106102
+ item C
107-
`.replace(/\n/g, EOL);
103+
`;
108104
const { lastFrame } = render(
109105
<SettingsContext.Provider value={mockSettings}>
110106
<MarkdownDisplay {...baseProps} text={text} />
@@ -118,7 +114,7 @@ describe('<MarkdownDisplay />', () => {
118114
* Level 1
119115
* Level 2
120116
* Level 3
121-
`.replace(/\n/g, EOL);
117+
`;
122118
const { lastFrame } = render(
123119
<SettingsContext.Provider value={mockSettings}>
124120
<MarkdownDisplay {...baseProps} text={text} />
@@ -131,7 +127,7 @@ describe('<MarkdownDisplay />', () => {
131127
const text = `
132128
1. First item
133129
2. Second item
134-
`.replace(/\n/g, EOL);
130+
`;
135131
const { lastFrame } = render(
136132
<SettingsContext.Provider value={mockSettings}>
137133
<MarkdownDisplay {...baseProps} text={text} />
@@ -147,7 +143,7 @@ Hello
147143
World
148144
***
149145
Test
150-
`.replace(/\n/g, EOL);
146+
`;
151147
const { lastFrame } = render(
152148
<SettingsContext.Provider value={mockSettings}>
153149
<MarkdownDisplay {...baseProps} text={text} />
@@ -162,7 +158,7 @@ Test
162158
|----------|:--------:|
163159
| Cell 1 | Cell 2 |
164160
| Cell 3 | Cell 4 |
165-
`.replace(/\n/g, EOL);
161+
`;
166162
const { lastFrame } = render(
167163
<SettingsContext.Provider value={mockSettings}>
168164
<MarkdownDisplay {...baseProps} text={text} />
@@ -176,7 +172,7 @@ Test
176172
Some text before.
177173
| A | B |
178174
|---|
179-
| 1 | 2 |`.replace(/\n/g, EOL);
175+
| 1 | 2 |`;
180176
const { lastFrame } = render(
181177
<SettingsContext.Provider value={mockSettings}>
182178
<MarkdownDisplay {...baseProps} text={text} />
@@ -188,7 +184,7 @@ Some text before.
188184
it('inserts a single space between paragraphs', () => {
189185
const text = `Paragraph 1.
190186
191-
Paragraph 2.`.replace(/\n/g, EOL);
187+
Paragraph 2.`;
192188
const { lastFrame } = render(
193189
<SettingsContext.Provider value={mockSettings}>
194190
<MarkdownDisplay {...baseProps} text={text} />
@@ -211,7 +207,7 @@ some code
211207
\`\`\`
212208
213209
Another paragraph.
214-
`.replace(/\n/g, EOL);
210+
`;
215211
const { lastFrame } = render(
216212
<SettingsContext.Provider value={mockSettings}>
217213
<MarkdownDisplay {...baseProps} text={text} />
@@ -221,7 +217,7 @@ Another paragraph.
221217
});
222218

223219
it('hides line numbers in code blocks when showLineNumbers is false', () => {
224-
const text = '```javascript\nconst x = 1;\n```'.replace(/\n/g, EOL);
220+
const text = '```javascript\nconst x = 1;\n```';
225221
const settings = new LoadedSettings(
226222
{ path: '', settings: {} },
227223
{ path: '', settings: {} },
@@ -242,7 +238,7 @@ Another paragraph.
242238
});
243239

244240
it('shows line numbers in code blocks by default', () => {
245-
const text = '```javascript\nconst x = 1;\n```'.replace(/\n/g, EOL);
241+
const text = '```javascript\nconst x = 1;\n```';
246242
const { lastFrame } = render(
247243
<SettingsContext.Provider value={mockSettings}>
248244
<MarkdownDisplay {...baseProps} text={text} />
@@ -251,4 +247,21 @@ Another paragraph.
251247
expect(lastFrame()).toMatchSnapshot();
252248
expect(lastFrame()).toContain(' 1 ');
253249
});
250+
251+
it('correctly splits lines using \\n regardless of platform EOL', () => {
252+
// Test that the component uses \n for splitting, not EOL
253+
const textWithUnixLineEndings = 'Line 1\nLine 2\nLine 3';
254+
255+
const { lastFrame } = render(
256+
<SettingsContext.Provider value={mockSettings}>
257+
<MarkdownDisplay {...baseProps} text={textWithUnixLineEndings} />
258+
</SettingsContext.Provider>,
259+
);
260+
261+
const output = lastFrame();
262+
expect(output).toContain('Line 1');
263+
expect(output).toContain('Line 2');
264+
expect(output).toContain('Line 3');
265+
expect(output).toMatchSnapshot();
266+
});
254267
});

packages/cli/src/ui/utils/MarkdownDisplay.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import React from 'react';
88
import { Text, Box } from 'ink';
9-
import { EOL } from 'node:os';
109
import { Colors } from '../colors.js';
1110
import { colorizeCode } from './CodeColorizer.js';
1211
import { TableRenderer } from './TableRenderer.js';
@@ -35,7 +34,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
3534
}) => {
3635
if (!text) return <></>;
3736

38-
const lines = text.split(EOL);
37+
const lines = text.split(`\n`);
3938
const headerRegex = /^ *(#{1,4}) +(.*)/;
4039
const codeFenceRegex = /^ *(`{3,}|~{3,}) *(\w*?) *$/;
4140
const ulItemRegex = /^([ \t]*)([-*+]) +(.*)/;

packages/cli/src/ui/utils/__snapshots__/MarkdownDisplay.test.tsx.snap

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ Another paragraph.
1414
"
1515
`;
1616

17+
exports[`<MarkdownDisplay /> > correctly splits lines using \\n regardless of platform EOL 1`] = `
18+
"Line 1
19+
Line 2
20+
Line 3"
21+
`;
22+
1723
exports[`<MarkdownDisplay /> > handles a table at the end of the input 1`] = `
1824
"Some text before.
1925
| A | B |

0 commit comments

Comments
 (0)