---
title: "The editor — quario"
description: "Embedding the quario banded document designer, and handling the plan it hands back on every change."
url: "https://getquario.com/docs/diving-deeper/editor"
---

# The editor

## Intent

`<quario-editor>` is a banded document designer whose design surface is the
same layout the viewer paints. It takes your instance and your definition, and
returns the edited definition on every change. Reach for it when your users
should author their own documents rather than receive yours.

## Design decisions

**The editor takes your instance rather than making one.** Locale, currency,
timezone, query budgets, and the license all stay yours. A document authored
in the editor compiles the same way it will in your service.

**Every change carries a plan, not only a schema.** A change event returns the
edited definition together with the problems the same traversal found. One
descent serves both persisting and reporting, which is what `plan` exists for.

**The design surface is the layout, not a second renderer.** What an author
drags is the same display list the PDF target writes, so what they arrange is
what will print.

**Undo is a snapshot stack.** The editor keeps whole definitions rather than
inverse operations. A definition is small, plain JSON, and a snapshot can’t
drift from what an inverse operation meant to undo.

**Span coverage holds by construction.** A total row must cover its columns
exactly once. The editor maintains that as an invariant rather than letting an
author reach a state the engine would reject.

**A failed render resolves the mount.** The element reports through an `error`
event and stays mounted, the same contract the viewer honours.

**An editor you insert again renders once more, unless its last render landed
on the sheet.** Two cases leave the render owed. One is a property written
while the element was out of the document. The other is a document that compiled
to nothing because it still has problems. A document you’re typing into is the
ordinary case of one that doesn’t yet compile. The debt can therefore stand for
a stretch of an editing session, where a run is the plan alone and nothing
renders or paints.

## API walkthrough

### Mounting

Import `@quario/editor/register` to define the element, or import
`QuarioEditor` and define it yourself. Set properties as element
properties, since an instance and a registry aren’t strings.

```ts title="editor.ts"
import '@quario/editor/register'
import type { EditorPage } from '@quario/editor'

const el = document.createElement('quario-editor')

const page: EditorPage = { size: 'A4', margin: 54 }

el.schema = definition
el.instance = q
el.functions = {}
el.data = data
el.page = page

document.body.append(el)
```

**`instance`** is a `quario()` you made. **`functions`** is the registry the
definition may call. It should be the registry your service will compile
with, or an author will build a document that calls something you don’t have.

**`data`** is sample data for the preview. It never leaves the browser and it
isn’t part of the definition.

### Handling a change

```ts title="editor.ts"
const el = document.createElement('quario-editor')

el.addEventListener('change', (event) => {
  const { schema, problems, warnings } = event.detail
  if (problems.length === 0) save(schema)
  else showProblems(problems)
  for (const w of warnings) console.warn(w.path, w.message)
})
```

`problems` is the same structured list `plan` returns, each with a `path`, a
`message` and the author `source` behind it. Persist on an empty list, and
surface the rest against the nodes they name.

The event also carries `warnings`, the advisory list the rail shows as
**Issues** beside the problems. A document carrying only warnings compiles, so
a save button driven by `problems.length` should stay driven by it.

Don’t re-validate what the editor gave you. The plan came from the
traversal that produced the change.

### Reacting to failure

**`error`** carries `{ error, kind }`, where `kind` is `mount-render` before
any render lands, `update-render` after, and `host-option` for a property the
editor rejected.

`renderComplete` resolves once the pages within reach have painted, not every
page. A host that reads pixels off a page far down the document finds it blank
until it’s scrolled to.

### Anchoring your own UI to a node

**`boxes(path)`** answers every rectangle the render drew for a schema node, in
document order, as client rectangles. Use it to attach your own UI to a node:
a badge, a comment marker, a step in a product tour. It answers for every page of
the document, whether or not that page has an element yet.

It answers an empty array for a node that draws nothing, for a group, and for a
path the schema doesn’t have. A render that hasn’t landed answers empty too.
Await `renderComplete` first.

### Reaching the document and the bands off the page

The rail opens with a **Document** panel. It sits above Properties, and it
shows whether or not you have selected anything. The report default belongs to
the document rather than to any band, so no selection on the sheet could reach
it. The panel offers exactly `family` and `size`, with the same controls, `fx`
toggle, and inline errors every other field has. It writes the document root,
so the preview repaints at the new base as you type.

That panel also lists `page.header`, `page.footer` and `empty`. None of the
three renders on the preview. Only a paginating target draws a page band, and
`empty` renders only over a dataset with no rows. So until now the editor
preserved them and offered no way in. Selecting one opens a panel holding its
items, with the verbs that add, remove, and reorder them. The breadcrumb leads
back up from an item. They’re drop targets like any other band, under the same
anchor rule: a page band binds `page`, and `empty` binds nothing row-scoped.

A run or a cell that may carry a link gets an **`href`** field. Its placeholder
is where an author meets the `#label` form for an internal target.
A table row withholds it, as it withholds every declaration that presents a
value.

Properties groups the styles a node may carry into Text, Alignment, Fill,
Format, Link, Spacing, Padding, and Border. A group shows only what the selected
node declares, and its `+` shows the rest. Padding draws as a grid of four
sides, and a border as a table of sides by width, line, and colour. A group
stays open as you select another node, so you can compare two nodes side by
side.

Every style control that un-declares by emptying carries a × beside it: a
number, a colour, a currency code, a text field, an expression. The × appears
only while the document declares that name. Its presence means one thing
everywhere: the document declares this name, and clearing it restores the
layer beneath. A select keeps its blank option and gains no ×.

### Fonts, chrome, and language

`fonts` takes the same record the viewer and the PDF target take. Pass the
identical object to all of them, and keep it across renders.

`colorScheme` takes `'light'`, `'dark'` or `'auto'` and reaches the chrome. The
sheet stays the document’s, as it does in the viewer.

`strings` works as the viewer’s does, key by key, with `lang` set to match. It
changes the chrome’s words only. The Issues list keeps the engine’s messages,
and a field keeps its schema name, because an author writes those names into
the definition.

### Where the boundary sits

An author using the editor is inside the trust boundary. They’re writing a
definition, and a definition is configuration you trust. If the people
editing aren’t the people you would let write a config file, the editor isn’t
the control that changes that. [Trust and CSP](https://getquario.com/docs/diving-deeper/security.md)
says what that boundary does and doesn’t claim.

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://getquario.com/"},{"@type":"ListItem","position":2,"name":"Docs","item":"https://getquario.com/docs"},{"@type":"ListItem","position":3,"name":"The editor","item":"https://getquario.com/docs/diving-deeper/editor"}]}
```
