mirror of
https://github.com/stackblitz/bolt.new
synced 2025-02-06 04:48:04 +00:00
[fix]: artifact actionlist rendering in chat
This commit is contained in:
parent
f0a668b811
commit
43839b1318
48
app/components/chat/Markdown.spec.ts
Normal file
48
app/components/chat/Markdown.spec.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { stripCodeFenceFromArtifact } from './Markdown';
|
||||||
|
|
||||||
|
describe('stripCodeFenceFromArtifact', () => {
|
||||||
|
it('should remove code fences around artifact element', () => {
|
||||||
|
const input = "```xml\n<div class='__boltArtifact__'></div>\n```";
|
||||||
|
const expected = "\n<div class='__boltArtifact__'></div>\n";
|
||||||
|
expect(stripCodeFenceFromArtifact(input)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle code fence with language specification', () => {
|
||||||
|
const input = "```typescript\n<div class='__boltArtifact__'></div>\n```";
|
||||||
|
const expected = "\n<div class='__boltArtifact__'></div>\n";
|
||||||
|
expect(stripCodeFenceFromArtifact(input)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not modify content without artifacts', () => {
|
||||||
|
const input = '```\nregular code block\n```';
|
||||||
|
expect(stripCodeFenceFromArtifact(input)).toBe(input);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle empty input', () => {
|
||||||
|
expect(stripCodeFenceFromArtifact('')).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle artifact without code fences', () => {
|
||||||
|
const input = "<div class='__boltArtifact__'></div>";
|
||||||
|
expect(stripCodeFenceFromArtifact(input)).toBe(input);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle multiple artifacts but only remove fences around them', () => {
|
||||||
|
const input = [
|
||||||
|
'Some text',
|
||||||
|
'```typescript',
|
||||||
|
"<div class='__boltArtifact__'></div>",
|
||||||
|
'```',
|
||||||
|
'```',
|
||||||
|
'regular code',
|
||||||
|
'```',
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
const expected = ['Some text', '', "<div class='__boltArtifact__'></div>", '', '```', 'regular code', '```'].join(
|
||||||
|
'\n',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(stripCodeFenceFromArtifact(input)).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
@ -68,7 +68,51 @@ export const Markdown = memo(({ children, html = false, limitedMarkdown = false
|
|||||||
remarkPlugins={remarkPlugins(limitedMarkdown)}
|
remarkPlugins={remarkPlugins(limitedMarkdown)}
|
||||||
rehypePlugins={rehypePlugins(html)}
|
rehypePlugins={rehypePlugins(html)}
|
||||||
>
|
>
|
||||||
{children}
|
{stripCodeFenceFromArtifact(children)}
|
||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes code fence markers (```) surrounding an artifact element while preserving the artifact content.
|
||||||
|
* This is necessary because artifacts should not be wrapped in code blocks when rendered for rendering action list.
|
||||||
|
*
|
||||||
|
* @param content - The markdown content to process
|
||||||
|
* @returns The processed content with code fence markers removed around artifacts
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // Removes code fences around artifact
|
||||||
|
* const input = "```xml\n<div class='__boltArtifact__'></div>\n```";
|
||||||
|
* stripCodeFenceFromArtifact(input);
|
||||||
|
* // Returns: "\n<div class='__boltArtifact__'></div>\n"
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* - Only removes code fences that directly wrap an artifact (marked with __boltArtifact__ class)
|
||||||
|
* - Handles code fences with optional language specifications (e.g. ```xml, ```typescript)
|
||||||
|
* - Preserves original content if no artifact is found
|
||||||
|
* - Safely handles edge cases like empty input or artifacts at start/end of content
|
||||||
|
*/
|
||||||
|
export const stripCodeFenceFromArtifact = (content: string) => {
|
||||||
|
if (!content || !content.includes('__boltArtifact__')) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines = content.split('\n');
|
||||||
|
const artifactLineIndex = lines.findIndex((line) => line.includes('__boltArtifact__'));
|
||||||
|
|
||||||
|
// Return original content if artifact line not found
|
||||||
|
if (artifactLineIndex === -1) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check previous line for code fence
|
||||||
|
if (artifactLineIndex > 0 && lines[artifactLineIndex - 1]?.trim().match(/^```\w*$/)) {
|
||||||
|
lines[artifactLineIndex - 1] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (artifactLineIndex < lines.length - 1 && lines[artifactLineIndex + 1]?.trim().match(/^```$/)) {
|
||||||
|
lines[artifactLineIndex + 1] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines.join('\n');
|
||||||
|
};
|
||||||
|
@ -11,7 +11,7 @@ interface Logger {
|
|||||||
setLevel: (level: DebugLevel) => void;
|
setLevel: (level: DebugLevel) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentLevel: DebugLevel = (import.meta.env.VITE_LOG_LEVEL ?? import.meta.env.DEV) ? 'debug' : 'info';
|
let currentLevel: DebugLevel = import.meta.env.VITE_LOG_LEVEL ?? import.meta.env.DEV ? 'debug' : 'info';
|
||||||
|
|
||||||
const isWorker = 'HTMLRewriter' in globalThis;
|
const isWorker = 'HTMLRewriter' in globalThis;
|
||||||
const supportsColor = !isWorker;
|
const supportsColor = !isWorker;
|
||||||
|
Loading…
Reference in New Issue
Block a user