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
hands the edited definition back 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 hands back
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.
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.
const el = /** @type {import('@quario/editor').QuarioEditor} */ (
document.createElement('quario-editor')
)
/** @type {import('@quario/editor').QuarioEditor['page']} */
const page = { 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
const el = /** @type {import('@quario/editor').QuarioEditor} */ (
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 handed 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 has landed, 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.
Fonts and chrome
fonts takes the same record the viewer and the PDF target take. Pass the
identical object to all of them, and hold 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.
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 says what that boundary does and doesn’t claim.