---
name: page-builder
description: Guide for AI assistants editing pages in the Vvveb visual builder through the page and ui eval tools — covers tool semantics, querying the page, safe edit patterns, page CSS, discovery with inspect(), and pitfalls. Use when changing page content, structure, or styling via tool calls instead of editing theme files directly.
---

# Vvveb Page Builder (AI Tool Use)

Edit the **live page in the builder iframe** with the `page` tool, and drive the **builder application itself** with the `ui` tool. Both take a single `code` string of JavaScript and return the result as JSON.

Reference implementation: `public/js/vvvebjs/builder.js` (`exec`, `eval_page`, `eval_ui`, `inspect`, `toolCall`), tool schemas in `public/js/tools.js`, pipeline in `plugins/ai-assistant/public/js/ai-core.js`.

## Tool semantics

| Tool | Runs in | Has access to | Use for |
|---|---|---|---|
| `page` | Page iframe (`window.FrameWindow`) | Page `document`/`window` (theme DOM only) | All content and structure edits |
| `ui` | Builder window | `Vvveb.*` (Gui, Builder, FileManager, StyleManager…) | Save, select, load pages, builder control |

Example call shape:

```json
{"name": "page", "arguments": {"code": "document.querySelectorAll('section').length"}}
```

Rules from `exec` you must rely on:

- The **completion value** of the code is returned. A single top-level `return expr` is unwrapped, so `return document.querySelector('h1')` works.
- DOM nodes come back as `[{html: node.outerHTML, selector: "..."}]` — re-query each call, never cache elements across calls.
- `console.log(x)` output is captured and returned instead of the raw value — use it to debug (`console.log(document.title)`).
- Results are bounded and JSON-safe (depth/keys/string caps, `[Circular]` markers). Errors come back as `"Name: message"` strings — retry with different code when you see one.
- Async code stays async: a returned Promise is awaited and tracked until it settles.
- After each `page` call, the **last changed element is auto-selected and scrolled into view** (MutationObserver) — check it visually instead of assuming success.

Discovery: call `inspect(Vvveb)`, `inspect(Vvveb.Builder)`, `inspect(Vvveb.StyleManager)` etc. in the `ui` tool to list methods and properties. Functions are summarized as signatures (`save(event, saveUrl, saveBtn)`), objects as key lists.

## Workflow

1. **Query first.** Count or locate targets before editing (`querySelectorAll(...).length`, or return the nodes to see their HTML).
2. **One change per call.** Small calls stream back fast, stay under token limits, and make failures obvious.
3. **Verify every result.** Confirm counts, read back changed HTML, then continue. Never chain blind edits.
4. **Use optional chaining** (`?.`) everywhere — a null dereference aborts the whole call.

## Querying the page

```js
// count + locate
document.querySelectorAll('main section').length
return document.querySelector('.hero h1')

// text search via XPath (note the argument order)
document.evaluate('//*[contains(text(),"some text")]/../..', document, null, 7, null).snapshotItem(0)

// theme data attributes (see theme-building skill)
document.querySelectorAll('[data-v-component-products]')
```

## Editing patterns (page tool)

Prefer `textContent` for text, `insertAdjacentHTML` for markup, direct properties for attributes:

```js
// change heading text
document.querySelector('.hero h1').textContent = 'Fresh headline'

// append a card to a grid (Bootstrap 5 markup, matching the theme)
document.querySelector('.row.features')?.insertAdjacentHTML('beforeend',
  `<div class="col-md-4"><div class="card"><div class="card-body">
     <h5 class="card-title">New feature</h5>
     <p class="card-text">Description here.</p>
   </div></div></div>`)

// swap an image
const img = document.querySelector('.hero img');
if (img) { img.src = '/media/new-hero.jpg'; img.alt = 'New hero image'; }

// retarget a CTA button
document.querySelector('a.btn-primary')?.setAttribute('href', '/contact')

// add a full section before the footer
document.querySelector('footer')?.insertAdjacentHTML('beforebegin',
  `<section class="py-5 bg-light"><div class="container">
     <h2 class="text-center">Call to action</h2>
     <p class="text-center"><a class="btn btn-primary btn-lg" href="/contact">Get started</a></p>
   </div></section>`)
```

Removing is allowed but cautious — there is no undo for raw DOM edits (see Pitfalls):

```js
document.querySelector('.promo-banner')?.remove()
```

## Page CSS

All page CSS lives in one element: `head > style#vvvebjs-styles`. Never use inline `style=` attributes for final styling — append rules there and move any inline styles over when done:

```js
// add a rule
document.querySelector('style#vvvebjs-styles').textContent +=
  '\n.hero h1 { letter-spacing: -0.02em; }\n'

// read current page css first when extending it
document.querySelector('style#vvvebjs-styles')?.textContent.slice(-2000)
```

## Builder tool patterns

Use `ui` only for application control, never for content edits:

```js
inspect(Vvveb.Builder)          // discover builder API
inspect(Vvveb.Gui)              // save, preview, panels...
Vvveb.Gui.save()                // save the page when all edits are done
Vvveb.Builder.selectNode(document.querySelector('.hero'))  // no — document here is the BUILDER document, not the page!
```

Mind the context split: inside `ui` code, `document` is the builder UI document, not the page. To touch page nodes from the builder side, go through `window.FrameDocument` / `window.FrameWindow` explicitly.

```js
// reload the current page, discarding unsaved AI edits
Vvveb.FileManager.loadPage(Vvveb.FileManager.currentPage)
```

## Pitfalls

- **No undo for raw DOM edits.** `exec` mutations bypass `Vvveb.Undo`. Keep edits small and verified; `FileManager.loadPage(currentPage)` discards everything since the last save. Save (`Vvveb.Gui.save()`) only when the result looks right.
- **Page vs builder `document`.** `page` code sees the theme page; `ui` code sees the admin UI. Mixing them up is the most common failure — `document.querySelector('.hero')` in the builder tool finds nothing.
- **Stale references.** Elements are re-queried per call by design; always re-query after structural changes instead of reusing selectors from earlier results.
- **Iframe state.** If the page reloads (navigation, `loadPage`), previous selectors and editor state are gone — re-query.
- **Keep queries narrow.** Never return `document.body.outerHTML` or whole-page HTML — it floods context. Count first, then read back only the edited fragment.
- **Async has no progress UI.** Long `fetch`-based code just runs; prefer synchronous DOM work and return promptly.
- **Missing iframe fails loudly.** If the page isn't loaded, the `page` tool returns `TypeError: eval context has no eval()` — ask the user to open a page instead of retrying blindly.
- **Prefer `ui`-registered helpers over reimplementing.** `Vvveb.StyleManager.getSelectorForElement(el)` gives a stable selector; `Vvveb.Builder.selectNode(el)` needs a builder-window element, not a page one.
