Skip to content

Edits and multi-turn

Let a model change a UI that is already on screen by writing only what changed, with edit mode, patches, appends and merge.

Updated View as Markdown

In a conversation, the second request is often a small change: “make it a bar chart”, “add Q4”. Writing the whole screen again costs tokens and time. In edit mode the model writes only what changes.

The four edits

Edit Written as Meaning
Replace k3 = Stat("MRR", "$415k") A statement with an existing id replaces it
Patch k3.value = "$415k" Set one prop, by its schema name
Append kpis += k5 Add a child, or a table row
Delete k2 = null Remove a statement

A patch is the cheapest: changing one value costs about 9 tokens instead of re-emitting the statement.

trend.type = bar
rev += |Oct|468000
note = Callout("October is a projection.", tone:info)
root += note

How to run an edit turn

Give the model the current program

toEditSource prints the program in a canonical form in which every part has an id, so everything can be addressed.

import { library, prompt } from "@gistui/catalog";
import { toEditSource } from "@gistui/core";

const current = toEditSource(shownProgram, library);
const system = prompt({ mode: "edit" }).text;
const messages = [
  { role: "user", content: `Current UI:\n${current}\n\nRequest: make the chart a bar chart` },
];

Render the edit as it streams

createEditStream is a stream seeded with the current program, so the change shows live.

import { createEditStream } from "@gistui/core";

const stream = createEditStream(shownProgram, library);
for await (const chunk of modelText) stream.push(chunk);
stream.end();

Keep the merged program

merge applies an edit and returns the new program, canonical and with unused statements removed. Store it as the new current program.

import { merge } from "@gistui/core";

const { source, errors } = merge(shownProgram, editText, library);

Rules worth knowing

  • The last definition of an id wins.
  • A patch addresses a prop by name, even one that was written positionally.
  • A patch or append whose target does not exist is ignored and reported as patch-target-missing.
  • Re-declaring a $state variable in an edit keeps its current value on screen.
  • Statements that nothing uses after an edit are dropped from the merged program.

The same mechanism powers repair

When repairStream fixes a finished answer on the server, it appends the repairs as edits to the end of the stream. The renderer applies them like any other edit, so only what changed re-renders. See Autofix and repair.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close