From 8fcb77fe6fcc91bd82f290b6db604c4c8bdb01c9 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Sun, 5 Jul 2026 12:04:59 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=B5=20fix:=20Preserve=20Fenced=20Markd?= =?UTF-8?q?own=20Artifacts=20(#14121)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: Preserve fenced markdown artifacts * fix: Satisfy artifact CI checks * fix: Handle longer artifact fences in updates --- api/app/clients/prompts/artifacts.js | 50 +++---- api/server/services/Artifacts/update.spec.js | 67 +++++++++ .../MarkdownBlocks.artifacts.test.tsx | 34 ++++- .../Artifacts/__tests__/useArtifacts.test.ts | 69 ++++++++- client/src/hooks/Artifacts/useArtifacts.ts | 138 +++++++++++++++++- packages/api/src/artifacts/update.ts | 12 +- packages/api/src/prompts/artifacts/index.ts | 40 ++--- 7 files changed, 350 insertions(+), 60 deletions(-) diff --git a/api/app/clients/prompts/artifacts.js b/api/app/clients/prompts/artifacts.js index 915ccae629..f5e0335f88 100644 --- a/api/app/clients/prompts/artifacts.js +++ b/api/app/clients/prompts/artifacts.js @@ -39,9 +39,9 @@ Artifacts are for substantial, self-contained content that users might modify or 1. Create the artifact using the following format: :::artifact{identifier="unique-identifier" type="mime-type" title="Artifact Title"} - \`\`\` + \`\`\`\` Your artifact content here - \`\`\` + \`\`\`\` ::: 2. Assign an identifier to the \`identifier\` attribute. For updates, reuse the prior identifier. For new artifacts, the identifier should be descriptive and relevant to the content, using kebab-case (e.g., "example-code-snippet"). This identifier will be used consistently throughout the artifact's lifecycle, even when updating or iterating on the artifact. @@ -67,7 +67,7 @@ Artifacts are for substantial, self-contained content that users might modify or - If you are unable to follow the above requirements for any reason, don't use artifacts and use regular code blocks instead, which will not attempt to render the component. 5. Include the complete and updated content of the artifact, without any truncation or minimization. Don't use "// rest of the code remains the same...". 6. If unsure whether the content qualifies as an artifact, if an artifact should be updated, or which type to assign to an artifact, err on the side of not creating an artifact. - 7. Always use triple backticks (\`\`\`) to enclose the content within the artifact, regardless of the content type. + 7. Use a backtick fence longer than any backtick fence in the artifact content. Use a 4-backtick fence by default; if the artifact content contains a 4-backtick fence, use 5 backticks, and so on. Here are some examples of correct usage of artifacts: @@ -84,7 +84,7 @@ Here are some examples of correct usage of artifacts: Sure! Here's a simple flow chart depicting the process of making tea using Mermaid syntax: :::artifact{identifier="tea-making-flowchart" type="application/vnd.mermaid" title="Flow chart: Making Tea"} - \`\`\`mermaid + \`\`\`\`mermaid graph TD A[Start] --> B{Water boiled?} B -->|Yes| C[Add tea leaves to cup] @@ -96,7 +96,7 @@ Here are some examples of correct usage of artifacts: G --> H[Add milk or sugar, if desired] H --> I[Enjoy your tea!] I --> J[End] - \`\`\` + \`\`\`\` ::: This flow chart uses Mermaid syntax to visualize the steps involved in making a cup of tea. Here's a brief explanation of the process: @@ -152,9 +152,9 @@ Artifacts are for substantial, self-contained content that users might modify or 1. Create the artifact using the following format: :::artifact{identifier="unique-identifier" type="mime-type" title="Artifact Title"} - \`\`\` + \`\`\`\` Your artifact content here - \`\`\` + \`\`\`\` ::: 2. Assign an identifier to the \`identifier\` attribute. For updates, reuse the prior identifier. For new artifacts, the identifier should be descriptive and relevant to the content, using kebab-case (e.g., "example-code-snippet"). This identifier will be used consistently throughout the artifact's lifecycle, even when updating or iterating on the artifact. @@ -191,7 +191,7 @@ Artifacts are for substantial, self-contained content that users might modify or - If you are unable to follow the above requirements for any reason, don't use artifacts and use regular code blocks instead, which will not attempt to render the component. 5. Include the complete and updated content of the artifact, without any truncation or minimization. Don't use "// rest of the code remains the same...". 6. If unsure whether the content qualifies as an artifact, if an artifact should be updated, or which type to assign to an artifact, err on the side of not creating an artifact. - 7. Always use triple backticks (\`\`\`) to enclose the content within the artifact, regardless of the content type. + 7. Use a backtick fence longer than any backtick fence in the artifact content. Use a 4-backtick fence by default; if the artifact content contains a 4-backtick fence, use 5 backticks, and so on. Here are some examples of correct usage of artifacts: @@ -208,7 +208,7 @@ Here are some examples of correct usage of artifacts: Sure! Here's a simple flow chart depicting the process of making tea using Mermaid syntax: :::artifact{identifier="tea-making-flowchart" type="application/vnd.mermaid" title="Flow chart: Making Tea"} - \`\`\`mermaid + \`\`\`\`mermaid graph TD A[Start] --> B{Water boiled?} B -->|Yes| C[Add tea leaves to cup] @@ -220,7 +220,7 @@ Here are some examples of correct usage of artifacts: G --> H[Add milk or sugar, if desired] H --> I[Enjoy your tea!] I --> J[End] - \`\`\` + \`\`\`\` ::: This flow chart uses Mermaid syntax to visualize the steps involved in making a cup of tea. Here's a brief explanation of the process: @@ -246,7 +246,7 @@ Here are some examples of correct usage of artifacts: Here's a simple React counter component: :::artifact{identifier="react-counter" type="application/vnd.react" title="React Counter"} - \`\`\` + \`\`\`\` import { useState } from 'react'; export default function Counter() { @@ -260,7 +260,7 @@ Here are some examples of correct usage of artifacts: ); } - \`\`\` + \`\`\`\` ::: This component creates a simple counter with an increment button. @@ -273,7 +273,7 @@ Here are some examples of correct usage of artifacts: Here's a basic HTML structure for a blog post: :::artifact{identifier="blog-post-html" type="text/html" title="Blog Post HTML"} - \`\`\` + \`\`\`\` @@ -300,7 +300,7 @@ Here are some examples of correct usage of artifacts: - \`\`\` + \`\`\`\` ::: This HTML structure provides a simple layout for a blog post. @@ -343,18 +343,18 @@ Artifacts are for substantial, self-contained content that users might modify or 1. Create the artifact using the following remark-directive markdown format: :::artifact{identifier="unique-identifier" type="mime-type" title="Artifact Title"} - \`\`\` + \`\`\`\` Your artifact content here - \`\`\` + \`\`\`\` ::: a. Example of correct format: :::artifact{identifier="example-artifact" type="text/plain" title="Example Artifact"} - \`\`\` + \`\`\`\` This is the content of the artifact. It can span multiple lines. - \`\`\` + \`\`\`\` ::: b. Common mistakes to avoid: @@ -396,7 +396,7 @@ Artifacts are for substantial, self-contained content that users might modify or - If you are unable to follow the above requirements for any reason, don't use artifacts and use regular code blocks instead, which will not attempt to render the component. 5. Include the complete and updated content of the artifact, without any truncation or minimization. Don't use "// rest of the code remains the same...". 6. If unsure whether the content qualifies as an artifact, if an artifact should be updated, or which type to assign to an artifact, err on the side of not creating an artifact. - 7. NEVER use triple backticks to enclose the artifact, ONLY the content within the artifact. + 7. Use a backtick fence longer than any backtick fence in the artifact content. Use a 4-backtick fence by default; if the artifact content contains a 4-backtick fence, use 5 backticks, and so on. Here are some examples of correct usage of artifacts: @@ -411,7 +411,7 @@ Here are some examples of correct usage of artifacts: Assistant: Sure! Here's a simple flow chart depicting the process of making tea using Mermaid syntax: :::artifact{identifier="tea-making-flowchart" type="application/vnd.mermaid" title="Flow chart: Making Tea"} - \`\`\`mermaid + \`\`\`\`mermaid graph TD A[Start] --> B{Water boiled?} B -->|Yes| C[Add tea leaves to cup] @@ -423,7 +423,7 @@ Here are some examples of correct usage of artifacts: G --> H[Add milk or sugar, if desired] H --> I[Enjoy your tea!] I --> J[End] - \`\`\` + \`\`\`\` ::: This flow chart uses Mermaid syntax to visualize the steps involved in making a cup of tea. Here's a brief explanation of the process: @@ -450,7 +450,7 @@ Here are some examples of correct usage of artifacts: Assistant: Here's a simple React counter component: :::artifact{identifier="react-counter" type="application/vnd.react" title="React Counter"} - \`\`\` + \`\`\`\` import { useState } from 'react'; export default function Counter() { @@ -464,7 +464,7 @@ Here are some examples of correct usage of artifacts: ); } - \`\`\` + \`\`\`\` ::: This component creates a simple counter with an increment button. @@ -476,7 +476,7 @@ Here are some examples of correct usage of artifacts: Assistant: Here's a basic HTML structure for a blog post: :::artifact{identifier="blog-post-html" type="text/html" title="Blog Post HTML"} - \`\`\` + \`\`\`\` @@ -503,7 +503,7 @@ Here are some examples of correct usage of artifacts: - \`\`\` + \`\`\`\` ::: This HTML structure provides a simple layout for a blog post. diff --git a/api/server/services/Artifacts/update.spec.js b/api/server/services/Artifacts/update.spec.js index 59cd1325a9..407ad2db42 100644 --- a/api/server/services/Artifacts/update.spec.js +++ b/api/server/services/Artifacts/update.spec.js @@ -136,6 +136,30 @@ ${ARTIFACT_END}`; expect(result[0].end).toBe(artifactText.length); }); + test('should preserve markdown artifacts wrapped in longer fences with internal code blocks', () => { + const content = `# Title + +\`\`\`bash +echo one +\`\`\` +Text between sections. +## Section B +\`\`\`bash +echo two +\`\`\``; + const artifactText = `${ARTIFACT_START}{identifier="git-cheatsheet" type="text/markdown" title="Git Cheatsheet"} +\`\`\`\`markdown +${content} +\`\`\`\` +${ARTIFACT_END}`; + const message = { text: `${artifactText}\ntrailer` }; + + const result = findAllArtifacts(message); + + expect(result).toHaveLength(1); + expect(result[0].end).toBe(artifactText.length); + }); + test('should preserve the first fallback close in an unclosed fence', () => { const firstArtifact = `${ARTIFACT_START}{identifier="first" type="text/html" title="First"} \`\`\`html @@ -245,6 +269,49 @@ ${ARTIFACT_END}`; expect(result).toContain("console.log('inside');"); }); + test('should replace markdown artifacts wrapped in longer fences with internal code blocks', () => { + const original = `# Notes + +\`\`\`bash +echo one +\`\`\` + +## More +\`\`\`bash +echo two +\`\`\``; + const updated = original.replace('## More', '## Updated'); + const artifactText = `${ARTIFACT_START}{identifier="notes" type="text/markdown" title="Notes"} +\`\`\`\`markdown +${original} +\`\`\`\` +${ARTIFACT_END}`; + const message = { text: artifactText }; + const artifacts = findAllArtifacts(message); + + const result = replaceArtifactContent(artifactText, artifacts[0], original, updated); + + expect(result).not.toBeNull(); + expect(result).toContain('## Updated'); + expect(result).toContain('```bash'); + expect(result).toContain('````markdown'); + }); + + test('should normalize editor trailing newlines before longer closing fences', () => { + const original = '# Notes'; + const artifactText = `${ARTIFACT_START}{identifier="notes" type="text/markdown" title="Notes"} +\`\`\`\`markdown +${original} +\`\`\`\` +${ARTIFACT_END}`; + const message = { text: artifactText }; + const artifacts = findAllArtifacts(message); + + const result = replaceArtifactContent(artifactText, artifacts[0], original, `${original}\n`); + + expect(result).toBe(artifactText); + }); + test('should replace unclosed artifacts with internal markers in fenced content', () => { const original = `before \`\`\`markdown diff --git a/client/src/components/Chat/Messages/Content/__tests__/MarkdownBlocks.artifacts.test.tsx b/client/src/components/Chat/Messages/Content/__tests__/MarkdownBlocks.artifacts.test.tsx index 2e2ade6e3e..f055d6e46a 100644 --- a/client/src/components/Chat/Messages/Content/__tests__/MarkdownBlocks.artifacts.test.tsx +++ b/client/src/components/Chat/Messages/Content/__tests__/MarkdownBlocks.artifacts.test.tsx @@ -17,11 +17,16 @@ import Markdown from '../Markdown'; */ jest.mock('~/components/Artifacts/ArtifactButton', () => ({ __esModule: true, - default: ({ artifact }: { artifact?: { index?: number; identifier?: string } }) => ( + default: ({ + artifact, + }: { + artifact?: { index?: number; identifier?: string; content?: string }; + }) => (
), })); @@ -114,4 +119,31 @@ describe('MarkdownBlocks artifact-index parity (e2e)', () => { expect(await readArtifacts()).toEqual([{ idx: '0', id: 'a' }]); }); + + it('preserves markdown artifact content with inner fenced code blocks', async () => { + const markdown = [ + '# Title', + '', + '```bash', + 'echo one', + '```', + 'Text between sections.', + '## Section B', + '```bash', + 'echo two', + '```', + ].join('\n'); + const content = [ + ':::artifact{identifier="git-cheatsheet" type="text/markdown" title="Git Cheatsheet"}', + '````markdown', + markdown, + '````', + ':::', + ].join('\n'); + + render(wrap()); + + const [artifactNode] = await screen.findAllByTestId('art'); + expect(artifactNode.getAttribute('data-content')).toBe(`${markdown}\n`); + }); }); diff --git a/client/src/hooks/Artifacts/__tests__/useArtifacts.test.ts b/client/src/hooks/Artifacts/__tests__/useArtifacts.test.ts index ff9dfb0d4c..02228a8695 100644 --- a/client/src/hooks/Artifacts/__tests__/useArtifacts.test.ts +++ b/client/src/hooks/Artifacts/__tests__/useArtifacts.test.ts @@ -1,5 +1,5 @@ -import { renderHook, act } from '@testing-library/react'; import { Constants } from 'librechat-data-provider'; +import { renderHook, act } from '@testing-library/react'; import type { Artifact } from '~/common'; /** Mock dependencies */ @@ -176,6 +176,73 @@ describe('useArtifacts', () => { expect(result.current.activeTab).toBe('preview'); }); + it('should switch to preview when enclosed artifact uses a longer code fence', () => { + (useRecoilValue as jest.Mock).mockReturnValue({}); + (useRecoilState as jest.Mock).mockReturnValue([null, mockSetCurrentArtifactId]); + + const { result, rerender } = renderHook(() => useArtifacts()); + + act(() => { + result.current.setActiveTab('code'); + }); + + expect(result.current.activeTab).toBe('code'); + + (useArtifactsContext as jest.Mock).mockReturnValue({ + ...defaultContext, + isSubmitting: true, + latestMessageText: [ + ':::artifact{title="Git" type="text/markdown"}', + '````markdown', + '# Title', + '```bash', + 'echo one', + '```', + '````', + ':::', + ].join('\n'), + }); + + rerender(); + + expect(result.current.activeTab).toBe('preview'); + }); + + it('should not switch to preview when an inner marker appears before the longer fence closes', () => { + (useRecoilValue as jest.Mock).mockReturnValue({}); + (useRecoilState as jest.Mock).mockReturnValue([null, mockSetCurrentArtifactId]); + + const { result, rerender } = renderHook(() => useArtifacts()); + + act(() => { + result.current.setActiveTab('code'); + }); + + expect(result.current.activeTab).toBe('code'); + + (useArtifactsContext as jest.Mock).mockReturnValue({ + ...defaultContext, + isSubmitting: true, + latestMessageText: [ + ':::artifact{title="Git" type="text/markdown"}', + '````markdown', + '# Title', + '```bash', + 'echo one', + '```', + ':::', + ].join('\n'), + }); + + rerender(); + + expect(result.current.activeTab).toBe('code'); + expect(logger.log).not.toHaveBeenCalledWith( + 'artifacts', + expect.stringContaining('Enclosed artifact'), + ); + }); + it('should not switch to preview if artifact is not enclosed', () => { const artifact = createArtifact({ content: 'const App = () =>
Test
', diff --git a/client/src/hooks/Artifacts/useArtifacts.ts b/client/src/hooks/Artifacts/useArtifacts.ts index a498aad5d9..279883ecfc 100644 --- a/client/src/hooks/Artifacts/useArtifacts.ts +++ b/client/src/hooks/Artifacts/useArtifacts.ts @@ -1,11 +1,140 @@ import { useMemo, useState, useEffect, useRef } from 'react'; import { Constants } from 'librechat-data-provider'; import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'; -import { useArtifactsContext } from '~/Providers'; import { isCodeOnlyArtifact } from '~/utils/artifacts'; +import { useArtifactsContext } from '~/Providers'; import { logger } from '~/utils'; import store from '~/store'; +type ArtifactFence = { + marker: string; + length: number; + contentStart: number; +}; + +const getLineEnd = (text: string, start: number): number => { + const index = text.indexOf('\n', start); + return index === -1 ? text.length : index; +}; + +const getNextLineStart = (text: string, lineEnd: number): number => + lineEnd >= text.length ? text.length : lineEnd + 1; + +const getFirstContentLineStart = (text: string, start: number): number => { + let currentIndex = start; + + while (currentIndex < text.length) { + const lineEnd = getLineEnd(text, currentIndex); + const line = text.slice(currentIndex, lineEnd); + if (line.trim().length > 0) { + return currentIndex; + } + currentIndex = getNextLineStart(text, lineEnd); + } + + return text.length; +}; + +const getArtifactFence = (text: string, lineStart: number): ArtifactFence | null => { + const lineEnd = getLineEnd(text, lineStart); + const line = text.slice(lineStart, lineEnd); + const match = line.trimStart().match(/^(`{3,}|~{3,})/); + + if (!match) { + return null; + } + + return { + marker: match[1][0], + length: match[1].length, + contentStart: getNextLineStart(text, lineEnd), + }; +}; + +const isClosingArtifactFence = (line: string, openingFence: ArtifactFence): boolean => { + const closePattern = new RegExp(`^\\${openingFence.marker}{${openingFence.length},}\\s*$`); + return closePattern.test(line.trim()); +}; + +const isArtifactCloseLine = (line: string): boolean => { + const trimmed = line.trimStart(); + return trimmed.startsWith(':::') && !trimmed.startsWith(':::artifact'); +}; + +const hasArtifactCloseAfter = (text: string, start: number): boolean => { + const closeStart = getFirstContentLineStart(text, start); + if (closeStart >= text.length) { + return false; + } + + const closeLineEnd = getLineEnd(text, closeStart); + return isArtifactCloseLine(text.slice(closeStart, closeLineEnd)); +}; + +const hasUnfencedArtifactClose = (text: string, start: number): boolean => { + let currentIndex = start; + let codeFence: ArtifactFence | null = null; + + while (currentIndex < text.length) { + const lineEnd = getLineEnd(text, currentIndex); + const line = text.slice(currentIndex, lineEnd); + const fence = getArtifactFence(text, currentIndex); + + if (isArtifactCloseLine(line) && !codeFence) { + return true; + } + + if (fence && !codeFence) { + codeFence = fence; + } else if (codeFence && isClosingArtifactFence(line, codeFence)) { + codeFence = null; + } + + currentIndex = getNextLineStart(text, lineEnd); + } + + return false; +}; + +const hasEnclosedArtifact = (messageText: string): boolean => { + const text = messageText.trim(); + const artifactPattern = /:::artifact(?:\{[^}]*\})?/g; + let artifactMatch = artifactPattern.exec(text); + + while (artifactMatch) { + const openingLineEnd = getLineEnd(text, artifactMatch.index); + const contentStart = getFirstContentLineStart(text, getNextLineStart(text, openingLineEnd)); + const openingFence = getArtifactFence(text, contentStart); + + if (!openingFence) { + if (hasUnfencedArtifactClose(text, contentStart)) { + return true; + } + artifactMatch = artifactPattern.exec(text); + continue; + } + + let currentIndex = openingFence.contentStart; + while (currentIndex < text.length) { + const lineEnd = getLineEnd(text, currentIndex); + const line = text.slice(currentIndex, lineEnd); + + if (isClosingArtifactFence(line, openingFence)) { + if (hasArtifactCloseAfter(text, getNextLineStart(text, lineEnd))) { + return true; + } + break; + } + + currentIndex = getNextLineStart(text, lineEnd); + } + + artifactMatch = artifactPattern.exec(text); + } + + return false; +}; + export default function useArtifacts() { const [activeTab, setActiveTab] = useState('preview'); const { isSubmitting, latestMessageId, latestMessageText, conversationId } = @@ -136,12 +265,7 @@ export default function useArtifacts() { return; } - const hasEnclosedArtifact = - /:::artifact(?:\{[^}]*\})?(?:\s|\n)*(?:```[\s\S]*?```(?:\s|\n)*)?:::/m.test( - latestMessageText.trim(), - ); - - if (hasEnclosedArtifact) { + if (hasEnclosedArtifact(latestMessageText)) { logger.log('artifacts', 'Enclosed artifact detected during generation, switching to preview'); setActiveTab('preview'); hasEnclosedArtifactRef.current = true; diff --git a/packages/api/src/artifacts/update.ts b/packages/api/src/artifacts/update.ts index b91e6b0fee..b38bf9670d 100644 --- a/packages/api/src/artifacts/update.ts +++ b/packages/api/src/artifacts/update.ts @@ -213,6 +213,9 @@ const replaceRange = ( return originalText.substring(0, start) + updated + separator + endText; }; +const normalizeBeforeClosingArtifactFence = (text: string): string => + text.replace(/\n+(?=(?:`{3,}|~{3,})\s*\n\s*:::)/g, '\n'); + export const findAllArtifacts = (message: ArtifactMessage): ArtifactBoundary[] => { const artifacts: ArtifactBoundary[] = []; @@ -287,10 +290,7 @@ export const replaceArtifactContent = ( } const absoluteIndex = artifact.start + searchStart + relativeIndex; - return replaceRange( - originalText, - absoluteIndex, - absoluteIndex + originalTrimmed.length, - updated, - ).replace(/\n+(?=```\n:::)/g, '\n'); + return normalizeBeforeClosingArtifactFence( + replaceRange(originalText, absoluteIndex, absoluteIndex + originalTrimmed.length, updated), + ); }; diff --git a/packages/api/src/prompts/artifacts/index.ts b/packages/api/src/prompts/artifacts/index.ts index 422318d6d7..5e845f932b 100644 --- a/packages/api/src/prompts/artifacts/index.ts +++ b/packages/api/src/prompts/artifacts/index.ts @@ -38,9 +38,9 @@ Artifacts are for substantial, self-contained content that users might modify or 1. Create the artifact using the following format: :::artifact{identifier="unique-identifier" type="mime-type" title="Artifact Title"} - \`\`\` + \`\`\`\` Your artifact content here - \`\`\` + \`\`\`\` ::: 2. Assign an identifier to the \`identifier\` attribute. For updates, reuse the prior identifier. For new artifacts, the identifier should be descriptive and relevant to the content, using kebab-case (e.g., "example-code-snippet"). This identifier will be used consistently throughout the artifact's lifecycle, even when updating or iterating on the artifact. @@ -77,7 +77,7 @@ Artifacts are for substantial, self-contained content that users might modify or - If you are unable to follow the above requirements for any reason, don't use artifacts and use regular code blocks instead, which will not attempt to render the component. 5. Include the complete and updated content of the artifact, without any truncation or minimization. Don't use "// rest of the code remains the same...". 6. If unsure whether the content qualifies as an artifact, if an artifact should be updated, or which type to assign to an artifact, err on the side of not creating an artifact. - 7. Always use triple backticks (\`\`\`) to enclose the content within the artifact, regardless of the content type. + 7. Use a backtick fence longer than any backtick fence in the artifact content. Use a 4-backtick fence by default; if the artifact content contains a 4-backtick fence, use 5 backticks, and so on. Here are some examples of correct usage of artifacts: @@ -94,7 +94,7 @@ Here are some examples of correct usage of artifacts: Sure! Here's a simple flow chart depicting the process of making tea using Mermaid syntax: :::artifact{identifier="tea-making-flowchart" type="application/vnd.mermaid" title="Flow chart: Making Tea"} - \`\`\`mermaid + \`\`\`\`mermaid graph TD A[Start] --> B{Water boiled?} B -->|Yes| C[Add tea leaves to cup] @@ -106,7 +106,7 @@ Here are some examples of correct usage of artifacts: G --> H[Add milk or sugar, if desired] H --> I[Enjoy your tea!] I --> J[End] - \`\`\` + \`\`\`\` ::: This flow chart uses Mermaid syntax to visualize the steps involved in making a cup of tea. Here's a brief explanation of the process: @@ -132,7 +132,7 @@ Here are some examples of correct usage of artifacts: Here's a simple React counter component: :::artifact{identifier="react-counter" type="application/vnd.react" title="React Counter"} - \`\`\` + \`\`\`\` import { useState } from 'react'; export default function Counter() { @@ -146,7 +146,7 @@ Here are some examples of correct usage of artifacts:
); } - \`\`\` + \`\`\`\` ::: This component creates a simple counter with an increment button. @@ -159,7 +159,7 @@ Here are some examples of correct usage of artifacts: Here's a basic HTML structure for a blog post: :::artifact{identifier="blog-post-html" type="text/html" title="Blog Post HTML"} - \`\`\` + \`\`\`\` @@ -186,7 +186,7 @@ Here are some examples of correct usage of artifacts: - \`\`\` + \`\`\`\` ::: This HTML structure provides a simple layout for a blog post. @@ -229,18 +229,18 @@ Artifacts are for substantial, self-contained content that users might modify or 1. Create the artifact using the following remark-directive markdown format: :::artifact{identifier="unique-identifier" type="mime-type" title="Artifact Title"} - \`\`\` + \`\`\`\` Your artifact content here - \`\`\` + \`\`\`\` ::: a. Example of correct format: :::artifact{identifier="example-artifact" type="text/plain" title="Example Artifact"} - \`\`\` + \`\`\`\` This is the content of the artifact. It can span multiple lines. - \`\`\` + \`\`\`\` ::: b. Common mistakes to avoid: @@ -282,7 +282,7 @@ Artifacts are for substantial, self-contained content that users might modify or - If you are unable to follow the above requirements for any reason, don't use artifacts and use regular code blocks instead, which will not attempt to render the component. 5. Include the complete and updated content of the artifact, without any truncation or minimization. Don't use "// rest of the code remains the same...". 6. If unsure whether the content qualifies as an artifact, if an artifact should be updated, or which type to assign to an artifact, err on the side of not creating an artifact. - 7. NEVER use triple backticks to enclose the artifact, ONLY the content within the artifact. + 7. Use a backtick fence longer than any backtick fence in the artifact content. Use a 4-backtick fence by default; if the artifact content contains a 4-backtick fence, use 5 backticks, and so on. Here are some examples of correct usage of artifacts: @@ -297,7 +297,7 @@ Here are some examples of correct usage of artifacts: Assistant: Sure! Here's a simple flow chart depicting the process of making tea using Mermaid syntax: :::artifact{identifier="tea-making-flowchart" type="application/vnd.mermaid" title="Flow chart: Making Tea"} - \`\`\`mermaid + \`\`\`\`mermaid graph TD A[Start] --> B{Water boiled?} B -->|Yes| C[Add tea leaves to cup] @@ -309,7 +309,7 @@ Here are some examples of correct usage of artifacts: G --> H[Add milk or sugar, if desired] H --> I[Enjoy your tea!] I --> J[End] - \`\`\` + \`\`\`\` ::: This flow chart uses Mermaid syntax to visualize the steps involved in making a cup of tea. Here's a brief explanation of the process: @@ -336,7 +336,7 @@ Here are some examples of correct usage of artifacts: Assistant: Here's a simple React counter component: :::artifact{identifier="react-counter" type="application/vnd.react" title="React Counter"} - \`\`\` + \`\`\`\` import { useState } from 'react'; export default function Counter() { @@ -350,7 +350,7 @@ Here are some examples of correct usage of artifacts: ); } - \`\`\` + \`\`\`\` ::: This component creates a simple counter with an increment button. @@ -362,7 +362,7 @@ Here are some examples of correct usage of artifacts: Assistant: Here's a basic HTML structure for a blog post: :::artifact{identifier="blog-post-html" type="text/html" title="Blog Post HTML"} - \`\`\` + \`\`\`\` @@ -389,7 +389,7 @@ Here are some examples of correct usage of artifacts: - \`\`\` + \`\`\`\` ::: This HTML structure provides a simple layout for a blog post.