PuneetP16
commited on
Commit
·
43839b1
1
Parent(s):
f0a668b
[fix]: artifact actionlist rendering in chat
Browse files- app/components/chat/Markdown.spec.ts +48 -0
- app/components/chat/Markdown.tsx +45 -1
- app/utils/logger.ts +1 -1
app/components/chat/Markdown.spec.ts
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { describe, expect, it } from 'vitest';
|
2 |
+
import { stripCodeFenceFromArtifact } from './Markdown';
|
3 |
+
|
4 |
+
describe('stripCodeFenceFromArtifact', () => {
|
5 |
+
it('should remove code fences around artifact element', () => {
|
6 |
+
const input = "```xml\n<div class='__boltArtifact__'></div>\n```";
|
7 |
+
const expected = "\n<div class='__boltArtifact__'></div>\n";
|
8 |
+
expect(stripCodeFenceFromArtifact(input)).toBe(expected);
|
9 |
+
});
|
10 |
+
|
11 |
+
it('should handle code fence with language specification', () => {
|
12 |
+
const input = "```typescript\n<div class='__boltArtifact__'></div>\n```";
|
13 |
+
const expected = "\n<div class='__boltArtifact__'></div>\n";
|
14 |
+
expect(stripCodeFenceFromArtifact(input)).toBe(expected);
|
15 |
+
});
|
16 |
+
|
17 |
+
it('should not modify content without artifacts', () => {
|
18 |
+
const input = '```\nregular code block\n```';
|
19 |
+
expect(stripCodeFenceFromArtifact(input)).toBe(input);
|
20 |
+
});
|
21 |
+
|
22 |
+
it('should handle empty input', () => {
|
23 |
+
expect(stripCodeFenceFromArtifact('')).toBe('');
|
24 |
+
});
|
25 |
+
|
26 |
+
it('should handle artifact without code fences', () => {
|
27 |
+
const input = "<div class='__boltArtifact__'></div>";
|
28 |
+
expect(stripCodeFenceFromArtifact(input)).toBe(input);
|
29 |
+
});
|
30 |
+
|
31 |
+
it('should handle multiple artifacts but only remove fences around them', () => {
|
32 |
+
const input = [
|
33 |
+
'Some text',
|
34 |
+
'```typescript',
|
35 |
+
"<div class='__boltArtifact__'></div>",
|
36 |
+
'```',
|
37 |
+
'```',
|
38 |
+
'regular code',
|
39 |
+
'```',
|
40 |
+
].join('\n');
|
41 |
+
|
42 |
+
const expected = ['Some text', '', "<div class='__boltArtifact__'></div>", '', '```', 'regular code', '```'].join(
|
43 |
+
'\n',
|
44 |
+
);
|
45 |
+
|
46 |
+
expect(stripCodeFenceFromArtifact(input)).toBe(expected);
|
47 |
+
});
|
48 |
+
});
|
app/components/chat/Markdown.tsx
CHANGED
@@ -68,7 +68,51 @@ export const Markdown = memo(({ children, html = false, limitedMarkdown = false
|
|
68 |
remarkPlugins={remarkPlugins(limitedMarkdown)}
|
69 |
rehypePlugins={rehypePlugins(html)}
|
70 |
>
|
71 |
-
{children}
|
72 |
</ReactMarkdown>
|
73 |
);
|
74 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
remarkPlugins={remarkPlugins(limitedMarkdown)}
|
69 |
rehypePlugins={rehypePlugins(html)}
|
70 |
>
|
71 |
+
{stripCodeFenceFromArtifact(children)}
|
72 |
</ReactMarkdown>
|
73 |
);
|
74 |
});
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Removes code fence markers (```) surrounding an artifact element while preserving the artifact content.
|
78 |
+
* This is necessary because artifacts should not be wrapped in code blocks when rendered for rendering action list.
|
79 |
+
*
|
80 |
+
* @param content - The markdown content to process
|
81 |
+
* @returns The processed content with code fence markers removed around artifacts
|
82 |
+
*
|
83 |
+
* @example
|
84 |
+
* // Removes code fences around artifact
|
85 |
+
* const input = "```xml\n<div class='__boltArtifact__'></div>\n```";
|
86 |
+
* stripCodeFenceFromArtifact(input);
|
87 |
+
* // Returns: "\n<div class='__boltArtifact__'></div>\n"
|
88 |
+
*
|
89 |
+
* @remarks
|
90 |
+
* - Only removes code fences that directly wrap an artifact (marked with __boltArtifact__ class)
|
91 |
+
* - Handles code fences with optional language specifications (e.g. ```xml, ```typescript)
|
92 |
+
* - Preserves original content if no artifact is found
|
93 |
+
* - Safely handles edge cases like empty input or artifacts at start/end of content
|
94 |
+
*/
|
95 |
+
export const stripCodeFenceFromArtifact = (content: string) => {
|
96 |
+
if (!content || !content.includes('__boltArtifact__')) {
|
97 |
+
return content;
|
98 |
+
}
|
99 |
+
|
100 |
+
const lines = content.split('\n');
|
101 |
+
const artifactLineIndex = lines.findIndex((line) => line.includes('__boltArtifact__'));
|
102 |
+
|
103 |
+
// Return original content if artifact line not found
|
104 |
+
if (artifactLineIndex === -1) {
|
105 |
+
return content;
|
106 |
+
}
|
107 |
+
|
108 |
+
// Check previous line for code fence
|
109 |
+
if (artifactLineIndex > 0 && lines[artifactLineIndex - 1]?.trim().match(/^```\w*$/)) {
|
110 |
+
lines[artifactLineIndex - 1] = '';
|
111 |
+
}
|
112 |
+
|
113 |
+
if (artifactLineIndex < lines.length - 1 && lines[artifactLineIndex + 1]?.trim().match(/^```$/)) {
|
114 |
+
lines[artifactLineIndex + 1] = '';
|
115 |
+
}
|
116 |
+
|
117 |
+
return lines.join('\n');
|
118 |
+
};
|
app/utils/logger.ts
CHANGED
@@ -11,7 +11,7 @@ interface Logger {
|
|
11 |
setLevel: (level: DebugLevel) => void;
|
12 |
}
|
13 |
|
14 |
-
let currentLevel: DebugLevel =
|
15 |
|
16 |
const isWorker = 'HTMLRewriter' in globalThis;
|
17 |
const supportsColor = !isWorker;
|
|
|
11 |
setLevel: (level: DebugLevel) => void;
|
12 |
}
|
13 |
|
14 |
+
let currentLevel: DebugLevel = import.meta.env.VITE_LOG_LEVEL ?? import.meta.env.DEV ? 'debug' : 'info';
|
15 |
|
16 |
const isWorker = 'HTMLRewriter' in globalThis;
|
17 |
const supportsColor = !isWorker;
|