Merge pull request #1924 from embire2/fix/code-output-to-chat-issue
fix: auto-detect and convert code blocks to artifacts when missing tags
This commit is contained in:
@@ -72,6 +72,7 @@ function cleanEscapedTags(content: string) {
|
||||
}
|
||||
export class StreamingMessageParser {
|
||||
#messages = new Map<string, MessageState>();
|
||||
#artifactCounter = 0;
|
||||
|
||||
constructor(private _options: StreamingMessageParserOptions = {}) {}
|
||||
|
||||
@@ -300,6 +301,69 @@ export class StreamingMessageParser {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Check for code blocks outside of artifacts
|
||||
if (!state.insideArtifact && input[i] === '`' && input[i + 1] === '`' && input[i + 2] === '`') {
|
||||
// Find the end of the code block
|
||||
const languageEnd = input.indexOf('\n', i + 3);
|
||||
|
||||
if (languageEnd !== -1) {
|
||||
const codeBlockEnd = input.indexOf('\n```', languageEnd + 1);
|
||||
|
||||
if (codeBlockEnd !== -1) {
|
||||
// Extract language and code content
|
||||
const language = input.slice(i + 3, languageEnd).trim();
|
||||
const codeContent = input.slice(languageEnd + 1, codeBlockEnd);
|
||||
|
||||
// Determine file extension based on language
|
||||
const fileExtension = this.#getFileExtension(language);
|
||||
const fileName = `code_${++this.#artifactCounter}${fileExtension}`;
|
||||
|
||||
// Auto-generate artifact and action tags
|
||||
const artifactId = `artifact_${Date.now()}_${this.#artifactCounter}`;
|
||||
const autoArtifact = {
|
||||
id: artifactId,
|
||||
title: fileName,
|
||||
type: 'code',
|
||||
};
|
||||
|
||||
// Emit artifact open callback
|
||||
this._options.callbacks?.onArtifactOpen?.({ messageId, ...autoArtifact });
|
||||
|
||||
// Add artifact element to output
|
||||
const artifactFactory = this._options.artifactElement ?? createArtifactElement;
|
||||
output += artifactFactory({ messageId });
|
||||
|
||||
// Emit action for file creation
|
||||
const fileAction = {
|
||||
type: 'file' as const,
|
||||
filePath: fileName,
|
||||
content: codeContent + '\n',
|
||||
};
|
||||
|
||||
this._options.callbacks?.onActionOpen?.({
|
||||
artifactId,
|
||||
messageId,
|
||||
actionId: String(state.actionId++),
|
||||
action: fileAction,
|
||||
});
|
||||
|
||||
this._options.callbacks?.onActionClose?.({
|
||||
artifactId,
|
||||
messageId,
|
||||
actionId: String(state.actionId - 1),
|
||||
action: fileAction,
|
||||
});
|
||||
|
||||
// Emit artifact close callback
|
||||
this._options.callbacks?.onArtifactClose?.({ messageId, ...autoArtifact });
|
||||
|
||||
// Move position past the code block
|
||||
i = codeBlockEnd + 4; // +4 for \n```
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output += input[i];
|
||||
i++;
|
||||
}
|
||||
@@ -367,6 +431,78 @@ export class StreamingMessageParser {
|
||||
const match = tag.match(new RegExp(`${attributeName}="([^"]*)"`, 'i'));
|
||||
return match ? match[1] : undefined;
|
||||
}
|
||||
|
||||
#getFileExtension(language: string): string {
|
||||
const languageMap: Record<string, string> = {
|
||||
javascript: '.js',
|
||||
js: '.js',
|
||||
typescript: '.ts',
|
||||
ts: '.ts',
|
||||
jsx: '.jsx',
|
||||
tsx: '.tsx',
|
||||
python: '.py',
|
||||
py: '.py',
|
||||
java: '.java',
|
||||
c: '.c',
|
||||
cpp: '.cpp',
|
||||
'c++': '.cpp',
|
||||
csharp: '.cs',
|
||||
'c#': '.cs',
|
||||
php: '.php',
|
||||
ruby: '.rb',
|
||||
rb: '.rb',
|
||||
go: '.go',
|
||||
rust: '.rs',
|
||||
rs: '.rs',
|
||||
kotlin: '.kt',
|
||||
kt: '.kt',
|
||||
swift: '.swift',
|
||||
html: '.html',
|
||||
css: '.css',
|
||||
scss: '.scss',
|
||||
sass: '.sass',
|
||||
less: '.less',
|
||||
xml: '.xml',
|
||||
json: '.json',
|
||||
yaml: '.yaml',
|
||||
yml: '.yml',
|
||||
toml: '.toml',
|
||||
markdown: '.md',
|
||||
md: '.md',
|
||||
sql: '.sql',
|
||||
sh: '.sh',
|
||||
bash: '.sh',
|
||||
zsh: '.sh',
|
||||
fish: '.fish',
|
||||
powershell: '.ps1',
|
||||
ps1: '.ps1',
|
||||
dockerfile: '.dockerfile',
|
||||
docker: '.dockerfile',
|
||||
makefile: '.makefile',
|
||||
make: '.makefile',
|
||||
vim: '.vim',
|
||||
lua: '.lua',
|
||||
perl: '.pl',
|
||||
r: '.r',
|
||||
matlab: '.m',
|
||||
julia: '.jl',
|
||||
scala: '.scala',
|
||||
clojure: '.clj',
|
||||
haskell: '.hs',
|
||||
erlang: '.erl',
|
||||
elixir: '.ex',
|
||||
nim: '.nim',
|
||||
crystal: '.cr',
|
||||
dart: '.dart',
|
||||
vue: '.vue',
|
||||
svelte: '.svelte',
|
||||
astro: '.astro',
|
||||
};
|
||||
|
||||
const normalized = language.toLowerCase();
|
||||
|
||||
return languageMap[normalized] || '.txt';
|
||||
}
|
||||
}
|
||||
|
||||
const createArtifactElement: ElementFactory = (props) => {
|
||||
|
||||
Reference in New Issue
Block a user