# Artifacts canvas beside chat

A split coding-agent app — chat on the left, a file tree and live artifact canvas on the right, wired with kai-resizable, kai-file-tree, and kai-artifact.

A coding-agent layout in the shape of Claude Artifacts or v0: the conversation drives the work on the left, and the right side pairs a `<kai-file-tree>` with a `<kai-artifact>` canvas. Pick a file in the tree and it loads into the canvas — HTML pages frame live in the sandboxed preview, every file opens its source on the Code tab.

## How it works

Three elements compose the shell. `<kai-resizable>` splits chat from the canvas column; a nested vertical `<kai-resizable>` stacks the tree above the artifact. Selecting a file in `<kai-file-tree>` fires `kai-select`; the handler sets the artifact's `activeFile` (and, for files with a hosted `url`, its `src`) so the canvas follows the tree.

```html
<kai-resizable orientation="horizontal" style="display:block;height:620px">
  <!-- left: the conversation -->
  <kai-resizable-item size="42%" min="280px">
    <kai-chat id="chat" chat-title="Build agent"></kai-chat>
  </kai-resizable-item>

  <!-- right: file tree above the artifact canvas -->
  <kai-resizable-item min="320px">
    <kai-resizable orientation="vertical" style="display:block;height:100%">
      <kai-resizable-item size="34%" min="120px" max="60%">
        <kai-file-tree id="tree"></kai-file-tree>
      </kai-resizable-item>
      <kai-resizable-item min="200px">
        <kai-artifact
          id="canvas"
          iframe-title="Project preview"
          open-in-tab
          sandbox="allow-scripts allow-forms allow-same-origin"
        ></kai-artifact>
      </kai-resizable-item>
    </kai-resizable>
  </kai-resizable-item>
</kai-resizable>

<script type="module">

  await customElements.whenDefined('kai-file-tree');

  const tree = document.getElementById('tree');
  const canvas = document.getElementById('canvas');

  // Each file: { path, url?, code?, language?, type? }. `url` points at a page
  // your backend hosts; `code` feeds the Code tab. Folders derive from `/`.
  const files = [
    { path: 'index.html', type: 'html', language: 'html', code: '<!DOCTYPE …', url: 'https://your-backend.example/artifacts/abc/index.html' },
    { path: 'about.html', type: 'html', language: 'html', code: '<!DOCTYPE …', url: 'https://your-backend.example/artifacts/abc/about.html' },
    { path: 'styles.css', type: 'other', language: 'css', code: ':root { … }', url: 'https://your-backend.example/artifacts/abc/styles.css' },
    { path: 'src/theme.ts', type: 'other', language: 'ts', code: 'export const theme = …' },
  ];

  tree.files = files;        // both take the same array, set in JS
  tree.activeFile = 'index.html';

  canvas.files = files;
  canvas.activeFile = 'index.html';
  canvas.src = files[0].url; // the preview frames this URL

  // Tree → canvas: load the picked file into the artifact.
  tree.addEventListener('kai-select', (e) => {
    const file = files.find((f) => f.path === e.detail.path);
    tree.activeFile = e.detail.path;
    canvas.activeFile = e.detail.path;
    if (file.url) { canvas.src = file.url; canvas.tab = 'preview'; }
    else { canvas.tab = 'code'; } // code-only file → show the source
  });
</script>
```

`<kai-artifact>` self-navigates its sandboxed iframe. The back/forward/reload/home toolbar and the address field always track navigations the canvas starts — picking a file, editing the path, home, reload. Tracking in-frame relative-link clicks (clicking "About →" inside the preview) also needs `allow-same-origin`, because the default `allow-scripts allow-forms` sandbox gives the framed document an opaque origin the component cannot read. This canvas opts in with `sandbox="allow-scripts allow-forms allow-same-origin"`, so it observes those clicks and keeps the address field and back/forward truthful. Add `allow-same-origin` only for first-party artifacts you trust — it lets the framed document reach its own origin.

> **note:** 
`<kai-artifact>` ships its own `<kai-file-tree>` inside the Code tab. The standalone tree here is the *project* explorer that drives which file the canvas frames; both stay in sync because the same handler listens for `kai-select` (tree) and `kai-file-select` (the canvas's own tree).

## Next steps

- **[`kai-artifact` reference](/components/artifact/)** — `src`, `files`, `activeFile`, `tab`, the `no-*` toolbar flags, and `kai-navigate` / `kai-tab-change` / `kai-file-select`.
- **[`kai-file-tree` reference](/components/file-tree/)** — the `files` shape, `active-file`, `default-expanded`, and the `kai-select` event.
- **[`kai-resizable` reference](/components/resizable/)** — `orientation`, item `size` / `min` / `max` / `locked` / `hidden`, nesting, and the maximize protocol.
- **[Resizable split layout](/patterns/resizable-split/)** — the two-pane chat shell this canvas layout builds on.
