UtherealUI

Building with agents

Choose a component. Supply typed arguments. Update a working interface.

Describe your app in the Playground to generate a dropdown with Azure Luna. This guide covers integrating model output and composing layouts.

One component

Give your model the JSON Schema and allowed action names. Validate its completed output before applying it to the current component.

import { applyDropdownEdit, dropdownEditJsonSchema } from '@uthereal/ui/agent'

// Provide dropdownEditJsonSchema for partial updates to your model.
const result = applyDropdownEdit(currentConfig, modelOutput, ['save', 'rename'])

if (result.success) setConfig(result.config)
else sendErrorsToAgent(result.errors)

dropdownJsonSchema describes creation input: items is required and omitted settings receive defaults. Use dropdownEditJsonSchema for partial edits; omitted arguments keep their current values.

An edit merges nested option objects, including theme tokens and motion settings. An items array replaces the previous array. Callback functions and application credentials are never part of model output.

Compose components

The optional composition catalog supports Stack and DropdownMenu. Stacks arrange children into rows and columns and can be nested to create larger layouts. Component and item IDs retain user selections.

'use client'

import { useReducer, useState } from 'react'
import {
  createComposition,
  applyCompositionUpdate,
} from '@uthereal/ui/composition'
import {
  CompositionView,
  createStateStore,
} from '@uthereal/ui/composition/react'

const allowedActions = ['save']
const initialDocument = createComposition(
  {
    root: 'toolbar',
    elements: {
      toolbar: {
        type: 'Stack',
        props: { direction: 'row', gap: 'md' },
        children: ['files'],
      },
      files: {
        type: 'DropdownMenu',
        props: {
          label: 'File actions',
          items: [
            { kind: 'action', id: 'save', label: 'Save', action: 'save' },
          ],
        },
        children: [],
      },
    },
  },
  allowedActions,
)

export function AgentToolbar() {
  const [store] = useState(() => createStateStore({}))
  const [feedback, setFeedback] = useState('Ready')
  const [document, dispatch] = useReducer(
    (current: typeof initialDocument, output: unknown) => {
      const result = applyCompositionUpdate(current, output, allowedActions)
      return result.success ? result.document : current
    },
    initialDocument,
  )

  return (
    <>
      <button
        onClick={() =>
          dispatch({
            baseRevision: document.revision,
            patch: [
              {
                op: 'replace',
                path: '/elements/toolbar/props/direction',
                value: 'column',
              },
            ],
          })
        }
      >
        Arrange in a column
      </button>
      <CompositionView
        spec={document.spec}
        store={store}
        actions={{
          save: ({ componentId }) =>
            setFeedback(`Save requested by ${componentId}`),
        }}
      />
      <p role="status">{feedback}</p>
    </>
  )
}

This complete host example validates each update against the latest reducer state. The Playground also displays validation errors so output can be corrected. Keep a createStateStore({}) instance in host state and pass it to CompositionView with the validated spec and registered action handlers. Checkbox selections are stored by component and item ID, so moving a menu does not reset them.

Apply live changes

An update includes the revision it was based on, plus a complete spec or JSON Patch operations:

{
  "baseRevision": 0,
  "patch": [
    {
      "op": "replace",
      "path": "/elements/files/props/label",
      "value": "Project actions"
    },
    {
      "op": "replace",
      "path": "/elements/files/props/closeOnSelect",
      "value": false
    }
  ]
}

Use applyCompositionUpdate(currentDocument, output, allowedActions) against the latest document, for example inside a reducer. It validates the entire candidate before committing. Invalid arguments, missing children, cycles, unregistered actions, and stale revisions leave the working interface unchanged.

Connect your model

Export compositionPrompt(allowedActions) and compositionUpdateJsonSchema from @uthereal/ui/composition. Send these with the current document to your model. Pass its completed, parsed response to your reducer, and return validation errors for a retry when needed. Adapt the JSON Schema to your provider's supported structured-output subset if required.

The library does not impose a model provider, transport, or agent framework.

State and actions

  • The agent controls supported component arguments and layout structure.
  • Your application owns persistent selections and registered action handlers.
  • Open state and keyboard focus follow the mounted component's lifecycle. Moving a component to another parent can remount it.
  • Editing configuration does not execute an application action.

Documentation for agents

Use llms.txt to discover pages, llms-full.txt for the complete text, or Copy page on any documentation page. These are generated from the same MDX content as this website.

Theme and motion are ordinary arguments

The same theme and motionOptions objects work in React props, the composition catalog, and model-generated JSON. Agents can adjust presentation without providing CSS, callbacks, or executable code.

import { applyDropdownEdit } from '@uthereal/ui/agent'

const result = applyDropdownEdit(current, {
  theme: { mode: 'dark', accent: 'blue', tokens: { popupRadius: 16 } },
  motionOptions: {
    transition: { type: 'spring', stiffness: 340, damping: 38 },
    enter: { offset: 8 },
  }, allowedActions)

if (result.success) setConfig(result.config)

For a single component, applyDropdownEdit merges supplied object fields; item arrays replace. Changing the transition type replaces that transition variant. Omitted fields stay unchanged, and explicit zero values are preserved. Send { theme: null } or { motionOptions: null } to remove that entire override group and return to recipe or motion preset values. null is an edit operation, not a component creation argument.

Composition updates follow JSON Patch semantics: add or replace an object replaces that object. To preserve its siblings, update a specific existing token path. Create an absent theme object with add before addressing children; use remove to reset an individual token. Both update paths validate the full candidate atomically.

See Theming and Motion for the complete vocabulary, ranges, and defaults. Reduced motion and immediate keyboard feedback remain enforced regardless of agent output.

On this page