feat(core): code artifact tool (#13015)

<img width="1272" alt="image"
src="https://github.com/user-attachments/assets/429ec60a-48a9-490b-b45f-3ce7150ef32a"
/>


#### PR Dependency Tree


* **PR #13015** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Introduced a new AI tool for generating self-contained HTML artifacts,
including a dedicated interface for previewing, copying, and downloading
generated HTML.
* Added syntax highlighting and preview capabilities for HTML artifacts
in chat and tool panels.
* Integrated the new HTML artifact tool into the AI chat prompt and
Copilot toolset.

* **Enhancements**
* Improved artifact preview panel layout and sizing for a better user
experience.
* Enhanced HTML preview components to support both model-based and raw
HTML rendering.

* **Dependency Updates**
  * Added the "shiki" library for advanced syntax highlighting.

* **Bug Fixes**
  * None.

* **Chores**
* Updated internal imports and code structure to support new features
and maintain consistency.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Peng Xiao
2025-07-04 07:39:51 +08:00
committed by GitHub
parent 53968f6f8c
commit 8ed7dea823
16 changed files with 671 additions and 56 deletions
@@ -0,0 +1,84 @@
import { Logger } from '@nestjs/common';
import { tool } from 'ai';
import { z } from 'zod';
import { toolError } from './error';
const logger = new Logger('CodeArtifactTool');
/**
* A copilot tool that produces a completely self-contained HTML artifact.
* The returned HTML must include <style> and <script> tags directly so that
* it can be saved as a single .html file and opened in any browser with no
* external dependencies.
*/
export const createCodeArtifactTool = () => {
return tool({
description:
'Generate a single-file HTML snippet (with inline <style> and <script>) that accomplishes the requested functionality. The final HTML should be runnable when saved as an .html file and opened in a browser. Do NOT reference external resources (CSS, JS, images) except through data URIs.',
parameters: z.object({
/**
* The <title> text that will appear in the browser tab.
*/
title: z.string().describe('The title of the HTML page'),
/**
* The raw HTML that should be placed inside <body>. *Do not* include
* <body> tags here the tool will wrap it for you.
*/
body: z
.string()
.describe('HTML markup that goes inside the <body> element'),
/**
* Optional CSS rules to be wrapped in a single <style> tag inside <head>.
*/
css: z
.string()
.optional()
.describe('CSS to inline in a <style> tag (omit if none).'),
/**
* Optional JavaScript code to be wrapped in a single <script> tag before
* </body>.
*/
js: z
.string()
.optional()
.describe('JavaScript to inline in a <script> tag (omit if none).'),
}),
execute: async ({ title, body, css = '', js = '' }) => {
try {
const parts: string[] = [];
parts.push('<!DOCTYPE html>');
parts.push('<html lang="en">');
parts.push('<head>');
parts.push('<meta charset="UTF-8" />');
parts.push(`<title>${title}</title>`);
if (css.trim().length) {
parts.push('<style>');
parts.push(css);
parts.push('</style>');
}
parts.push('</head>');
parts.push('<body>');
parts.push(body);
if (js.trim().length) {
parts.push('<script>');
parts.push(js);
parts.push('</script>');
}
parts.push('</body>');
parts.push('</html>');
const html = parts.join('\n');
return {
title,
html,
size: html.length,
};
} catch (err: any) {
logger.error(`Failed to compose code artifact (${title})`, err);
return toolError('Code Artifact Failed', err.message ?? String(err));
}
},
});
};
@@ -1,3 +1,4 @@
export * from './code-artifact';
export * from './doc-compose';
export * from './doc-edit';
export * from './doc-keyword-search';