Bonsai-Chat-WebGPU / src /app /components /ArtifactPane.tsx
Valeriy Selitskiy
Release v1.2.1 adaptive chat workspace
6c62075
Raw
History Blame Contribute Delete
2.5 kB
import { useEffect, useState } from 'react';
import type { ArtifactDocument } from '../../lib/agent-tools';
interface ArtifactPaneProps {
artifact: ArtifactDocument | null;
onClose: () => void;
}
export function ArtifactPane({ artifact, onClose }: ArtifactPaneProps) {
const [tab, setTab] = useState<'preview' | 'code'>('code');
const [running, setRunning] = useState(false);
const [runId, setRunId] = useState(0);
useEffect(() => {
setTab('code');
setRunning(false);
}, [artifact?.id]);
const run = () => {
setTab('preview');
setRunId((current) => current + 1);
setRunning(true);
};
return (
<section className="inspector-section artifact-section" aria-labelledby="artifact-title">
<div className="section-heading compact">
<h2 id="artifact-title">Artifact</h2>
{artifact && <button className="artifact-close" type="button" onClick={onClose} aria-label="Close artifact">×</button>}
</div>
{!artifact ? (
<p className="artifact-empty">An inert HTML/CSS tool result will open here. Scripts and network access are blocked.</p>
) : (
<div className="artifact-pane">
<div className="artifact-tabs" role="tablist" aria-label="Artifact view">
<button type="button" role="tab" aria-selected={tab === 'code'} onClick={() => { setTab('code'); setRunning(false); }}>Code</button>
<button type="button" role="tab" aria-selected={tab === 'preview'} onClick={() => setTab('preview')}>Preview</button>
<span>{artifact.title}</span>
</div>
{tab === 'code' ? (
<pre className="artifact-code"><code>{artifact.source}</code></pre>
) : running ? (
<div className="artifact-running">
<button type="button" onClick={() => setRunning(false)}>Stop preview</button>
<iframe
key={`${artifact.id}-${runId}`}
ref={(node) => node?.setAttribute('credentialless', '')}
title={artifact.title}
sandbox=""
srcDoc={artifact.sandboxedSource}
/>
</div>
) : (
<div className="artifact-launch">
<p>Scripts are inert until you explicitly run this artifact. The preview has an opaque origin and no network access.</p>
<button type="button" onClick={run}>Run isolated preview</button>
</div>
)}
</div>
)}
</section>
);
}