<!-- Plugins · clayjs · https://clayjs.com/plugins · generated from the page; every page has one at its path plus .md -->

Plugins

# The heavyweights

Bigger capabilities for malleable files. Some are configurator checkboxes, some are their own libraries.

cms checkbox plugin

“Other people need to edit this page's content without touching its code.”

A content sidebar that builds itself from your page. You name the parts of the file that count as content, and the CMS turns them into friendly fields: text, rich text, images, lists. Whoever you share edit access with sees the panel, not the markup.

**Setup is two tags.** Check _“A content sidebar”_ in the [configurator](https://clayjs.com/#configure) (or add `?plugins=cms` yourself), then tell it what's content with a rules tag:

```html
<script src="https://clayjs.com/v1/clay.js?plugins=cms"></script>
<script data-rules-name="cms" data-rules-version="1" type="application/json">
{
  "title":    ".page-title",
  "intro":    "p.intro",
  "avatar":   "img.avatar@src",
  "tags":     "ul.tags li[]",
  "products": [".product", { "name": ".name", "price": ".price" }]
}
</script>
```

Each entry is a field: a name, then the selector it edits. `@src` becomes an image-upload control, `li[]` a reorderable list, and the `products` shape a list of cards. Open the sidebar with `?cms=true` on the URL or `clay.cms.open()` from a button. The same rules tag can carry more tokens (`data-rules-name="api cms"`) to also power the **data** plugin's JSON API, so you configure content once.

quickcrop plugin · loads with cms, or ask by name

“People upload a 4000px photo and it lands in the page sideways and uncropped.”

A crop modal for image uploads: pick a file, drag a box, confirm. It resolves a cropped, re-encoded blob and a data URL. The CMS uses it for any image field marked `data-hcms-crop`, so `?plugins=cms` loads it for you; ask for it by name to call it from your own code.

```html
<img class="avatar" data-hcms-crop="1:1" src="avatar.jpg">
```

```javascript
const shot = await clay.quickcrop(file, { aspect: 16/9 });
// { blob, dataURL, width, height }, or null if they cancelled
```

Aspects are `1:1`, `16:9` or `free` on the attribute, a plain number in code. The modal and its stylesheet are marked `save-remove`, the legacy alias for `no-save`, so cropping chrome never reaches the saved file.

richclay checkbox plugin · on by default

“Editable text should mean bold, lists, and links, not just characters.”

The `editable` attribute from the tutorial. A floating toolbar appears while editing; output stays clean HTML. It's part of the default clay.js because editing text is the heart of a malleable file; uncheck it in the configurator if you're building something else.

```html
<article editable>Multi-line rich text…</article>
<h1 editable="single-line">Page title</h1>
<p editable="single-line no-toolbar">Caption</p>
```

Tokens combine like classes: `single-line`, `no-toolbar`, `toolbar-on-select`. In the saved file the attribute is an inert marker; no editor chrome is ever written to disk.

undo checkbox plugin

“⌘Z should undo what I did to the page, not just the last keystroke.”

Document-wide undo/redo built on DOM mutations: moves, deletions, and attribute changes all reverse cleanly. Respects `clay="no-undo"` regions.

```html
<script src="https://clayjs.com/v1/clay.js?plugins=undo"></script>
```

upload plugin · ask by name

“Someone needs to put a file into this page without knowing what a data URL is.”

`clay.upload(file)` sends a file you supply (from a file input, a drop or a paste) to the host and resolves with its URL, which you write into the page. It opens no picker itself. The CMS uses it for image fields.

```html
<script src="https://clayjs.com/v1/clay.js?plugins=upload"></script>
```

people plugin · ask by name

“Comments and cards need to say who wrote them, and the page has no idea who anyone is.”

`clay.me` is the person editing, `clay.people` the names this page records, and `clay.author(el)` stamps an element with your id. On a host that knows you, names come from your account; on a file with no host, it asks your name once.

```html
<script src="https://clayjs.com/v1/clay.js?plugins=people"></script>
```

wire plugin · ask by name

“I want a program on my own machine to work on this file while I have it open.”

A per-file control channel between the page and a process running in your terminal. The page sends a request, the process answers with progress, and the process edits _the file_. HTML never travels over the wire: the change reaches your open page as an ordinary external file change, through live sync. Nothing in the wire depends on what a payload contains.

```html
<script src="https://clayjs.com/v1/clay.js?plugins=wire,sync"></script>
```

```javascript
const run = clay.wire.send({ ask: "tighten the intro" });
clay.wire.on((state, frame) => console.log(state.state, frame && frame.text));
const outcome = await run.done;
```

`send` returns its handle immediately, and `handle.done` resolves with the final snapshot and never rejects. `on` takes a function, not an event name. Its second argument is the inbound frame when one caused the update, which today means a status line and nothing else: `state.text` is sticky across later states, so the frame is the only way to tell a new line from the same line arriving again. Also `cancel`, `get`, `list` and `isBusy`. It runs in view mode too, so a read-only review page can drive one; nothing in it assumes the save lane exists. The stream is lazy and idle between requests, because a browser allows only six connections per origin and live sync already holds one.

ai-edit checkbox plugin

“I want to describe a change to this section in words and have it made.”

Comment-to-edit AI editing, built on the wire. Hover a heading or paragraph for the chip, press ⌘J with the caret in one (a selection rides along as a quote), click bare section padding for the nearest unit, or use the bottom-right bubble for the whole page. The comment goes to the host’s `ai-edit` helper as a named wire request and comes back as that element’s HTML, morphed in place: **Keep** saves it, **Revert** puts the snapshot back. `@file.ext` in the comment adds context, and `@page` saves the page first so the helper reads it from disk.

```html
<script src="https://clayjs.com/v1/clay.js?plugins=ai-edit"></script>
```

Check _“AI editing”_ in the [configurator](https://clayjs.com/#configure), or add `?plugins=ai-edit` yourself. Asking for `ai-edit` loads `wire` with it. It is dormant unless the host lists a ready `ai-edit` helper, so a page on HTML Clay loads it and does nothing with it. Edit mode only. The host side is Hyperclay Local with AI editing turned on.

source plugin · on by default

“Saving reformats my whole file, and now I cannot read my own diffs.”

Saves the file rather than a fresh printout of the page. Without it, every save rebuilds the document from the browser's DOM, which reorders attributes, renormalises quoting and reprints every tag: a save that changes nothing rewrites about 88% of a hand-written file's lines. With it, clay keeps the bytes the file was loaded from, pairs each element to its place in them, and reprints only what actually changed. A save that changes nothing changes nothing.

```html
<!-- on by default; this is how you turn it off -->
<script src="https://clayjs.com/v1/clay.js?exclude=source"></script>
```

```javascript
clay.source.stats();   // { paired, saves, reprints, lastReprint, ... }
document.addEventListener("clay:save-reprinted", (e) => console.warn(e.detail.reason));

clay.source.text();      // the bytes clay believes are on disk right now
clay.source.locate(el);  // { from, to, line, column } for a live element, or null
```

`text()` and `locate()` are what an editing tool needs: read the file as it will be written, point at an element in the live page, and express the change as a range in those bytes instead of as a DOM mutation. The offsets and `column` are **UTF-16 code units** into `text()`, not bytes, so slice the string `text()` hands you and the answer is exact. `locate()` returns `null` for an element that is not in the file, which includes one the page created after load.

Every save is reparsed and compared to the page before it is sent, and anything that does not match exactly is sent as the ordinary full serialization instead, so the worst case is the behaviour you already have. `clay:save-reprinted` fires when that happens and `clay.source.stats()` counts it. Edit mode only, and it costs an HTML parser (about 48 KB compressed) plus one request for the file's own bytes at load, which is what `exclude=source` is for.

sap separate library

“I want reactive templates, with the DOM itself as the state.”

Reactivity without a virtual DOM or a store: attributes and elements are the source of truth, and templates re-render when the DOM they depend on changes. Made for malleable files, where the page already is the database.

```html
<script src="https://clayjs.com/v1/sap.js"></script>
```

data separate library

“I want the page's content in and out as JSON.”

`clay.extractData()` reads your live page into structured JSON; `clay.applyData(values, {rules})` applies content-only JSON changes atomically. Core adds `clay.readData()` and `clay.writeData()` for hosts that announce the matching saved-file capabilities.

```html
<script src="https://clayjs.com/v1/clay-data.js"></script>
```

Both attach to `window.clay`. From an inline script, wait for the tag first: `await clay.loaded.data`, then `const data = clay.extractData()`.
