Skip to content

Commit 08dcaaa

Browse files
committed
refactor(BtwPanel): Add Markdown Rendering
1 parent 1718f0d commit 08dcaaa

2 files changed

Lines changed: 14 additions & 35 deletions

File tree

source/ui/components/common/MarkdownRenderer.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,17 @@ function trimLines(lines: string[]): string[] {
163163
return result;
164164
}
165165

166+
export function renderMarkdownToLines(content: string): string[] {
167+
try {
168+
const sanitized = sanitizeMarkdownContent(content);
169+
const rendered = marked.parse(sanitized) as string;
170+
if (!rendered || typeof rendered !== 'string') return content.split('\n');
171+
return trimLines(rendered.split('\n'));
172+
} catch {
173+
return content.split('\n');
174+
}
175+
}
176+
166177
export default function MarkdownRenderer({content}: Props) {
167178
try {
168179
const sanitizedContent = sanitizeMarkdownContent(content);

source/ui/components/panels/BtwPanel.tsx

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,13 @@ import {useI18n} from '../../../i18n/I18nContext.js';
55
import {useTerminalSize} from '../../../hooks/ui/useTerminalSize.js';
66
import {streamBtwResponse} from '../../../utils/commands/btwStream.js';
77
import {visualWidth} from '../../../utils/core/textUtils.js';
8+
import {renderMarkdownToLines} from '../common/MarkdownRenderer.js';
89

910
type Step = 'streaming' | 'done' | 'error';
1011

1112
const VISIBLE_ROWS = 8;
1213
const DEBOUNCE_MS = 80;
1314

14-
/**
15-
* Split text into visual lines that each fit within `maxWidth` columns.
16-
* Accounts for wide characters (CJK, emoji) via visualWidth.
17-
*/
18-
function toVisualLines(text: string, maxWidth: number): string[] {
19-
if (maxWidth <= 0) return text.split('\n');
20-
21-
const result: string[] = [];
22-
for (const logical of text.split('\n')) {
23-
if (!logical || visualWidth(logical) <= maxWidth) {
24-
result.push(logical);
25-
continue;
26-
}
27-
28-
const chars = [...logical];
29-
let cur = '';
30-
let curW = 0;
31-
for (const ch of chars) {
32-
const w = visualWidth(ch);
33-
if (curW + w > maxWidth) {
34-
result.push(cur);
35-
cur = ch;
36-
curW = w;
37-
} else {
38-
cur += ch;
39-
curW += w;
40-
}
41-
}
42-
if (cur) result.push(cur);
43-
}
44-
return result;
45-
}
46-
4715
interface Props {
4816
prompt: string;
4917
onClose: () => void;
@@ -68,8 +36,8 @@ export const BtwPanel: React.FC<Props> = ({prompt, onClose}) => {
6836
const contentWidth = Math.max(1, columns - 4);
6937

7038
const visualLines = useMemo(
71-
() => toVisualLines(response, contentWidth),
72-
[response, contentWidth],
39+
() => (response ? renderMarkdownToLines(response) : []),
40+
[response],
7341
);
7442

7543
const flushPending = useCallback(() => {

0 commit comments

Comments
 (0)