Skip to main content

Visual Context

Visual Context is UI Bridge's answer to "what is on the screen right now, in a form an LLM can read". It comes in two layers, and they are answered by different processes:

LayerWhat it producesWho answers
SemanticStructured, text-only snapshots, diffs and summaries of the registryThe SDK, in-page
PixelScreenshots, OCR, VLM captions, visual baselinesThe runner, over /vision/*

Everything in the semantic layer works with no runner attached. The pixel layer needs one — see Pixel-level vision below.

Semantic Snapshots​

SemanticSnapshotManager turns a control snapshot into an AI-readable SemanticSnapshot: every element with a generated description and aliases, plus form state, modal state, focus, and a prose summary.

import { createSnapshotManager, getGlobalRegistry } from '@qontinui/ui-bridge';

const registry = getGlobalRegistry();
const snapshots = createSnapshotManager();

const snapshot = snapshots.createSnapshot(registry.createSnapshot());

console.log(snapshot.summary); // LLM-readable prose
console.log(snapshot.elementCounts); // { button: 4, input: 2, link: 7, ... }
console.log(snapshot.focusedElement); // 'email-input'

Over HTTP: GET /ai/snapshot (add ?includeForms=true for DOM-level form detail).

The Snapshot Shape​

interface SemanticSnapshot {
timestamp: number;
snapshotId: string;
page: PageContext;
elements: AIDiscoveredElement[];
forms: FormState[];
activeModals: ModalState[];
focusedElement?: string;
summary: string;
elementCounts: Record<string, number>;
formsDetail?: FormsResponse; // only when includeForms is set
networkActivity?: { inFlightCount: number /* … */ };
}

Snapshot History​

The manager keeps a bounded history so you can diff against an earlier state:

snapshots.getLastSnapshot(); // SemanticSnapshot | null
snapshots.getSnapshot(snapshotId); // by id
snapshots.getHistory(); // SemanticSnapshot[]
snapshots.clearHistory();

For a named point you want to come back to, the server exposes snapshot bookmarks: POST /ai/bookmarks, GET /ai/bookmarks, GET /ai/bookmarks/:name, GET /ai/bookmarks/:name/diff, DELETE /ai/bookmarks/:name.

Page Summary​

For prompts where a full snapshot is too much, generatePageSummary produces a compact prose description. It returns a plain string — there is no structured summary object:

import { generatePageSummary } from '@qontinui/ui-bridge';

const summary = generatePageSummary(elements, { title: 'Login', pageType: 'form' });
console.log(summary);
// Page: "Login"
// Type: Form
// 2 inputs, 1 button, 1 link
// …

Over HTTP: GET /ai/summary, whose data field is that string. The handler falls back to a DOM scan when the registry is empty, so an unregistered page still yields a summary.

generateSnapshotSummary does the same for an existing SemanticSnapshot, and serializeSnapshot renders one to a compact transport form.

Semantic Diffs​

SemanticDiffManager answers "what changed" between two snapshots:

import { createSnapshotManager, createDiffManager, getGlobalRegistry } from '@qontinui/ui-bridge';

const registry = getGlobalRegistry();
const snapshots = createSnapshotManager();
const diffs = createDiffManager();

diffs.update(snapshots.createSnapshot(registry.createSnapshot())); // seeds; returns null

await clickSomething();

const diff = diffs.update(snapshots.createSnapshot(registry.createSnapshot()));
console.log(diff?.summary); // prose description of the change
console.log(diff?.changes.appeared, diff?.changes.disappeared, diff?.changes.modified);

diffFrom(snapshot) diffs an arbitrary earlier snapshot against the latest one, and reset() clears the baseline. Two helpers make the result easy to gate on:

import { hasSignificantChanges, describeDiff } from '@qontinui/ui-bridge';

if (hasSignificantChanges(diff)) {
console.log(describeDiff(diff));
}

Over HTTP: GET /ai/diff (diff since the previous call), POST /ai/scoped-diff, POST /ai/summarize-diff, and POST /ai/execute-with-diff to perform an action and get its diff in one round trip.

The response's success is the action's verdict. A failed action answers { success: false, error, code, data }, with the inner action's error and code on the envelope and the full result — actionSuccess: false, actionResult, and the diff around the failure — kept under data. Both the in-process server and the relay path answer this way.

Configuration​

SemanticSnapshotConfig — merged over DEFAULT_SNAPSHOT_CONFIG:

import { createSnapshotManager } from '@qontinui/ui-bridge';

const snapshots = createSnapshotManager({
analyzeForms: true, // detect and summarise forms
detectModals: true, // detect blocking modals
inferPageType: true, // classify the page (form, list, detail, …)
generateDescriptions: true, // generate element descriptions
useAnnotations: true, // merge in the annotation store
includeForms: false, // attach DOM-level form discovery detail
maxElements: 500, // hard element cap
maxTokens: 0, // token budget; 0 = unlimited
});

SemanticDiffConfig — merged over DEFAULT_DIFF_CONFIG:

import { createDiffManager } from '@qontinui/ui-bridge';

const diffs = createDiffManager({
ignoreInsignificant: true,
trackedProperties: ['visible', 'enabled', 'focused', 'checked', 'value', 'textContent'],
generateSuggestions: true,
maxModifications: 20,
});

There is no includePositions / includeBoundingBoxes / textFormat option: element geometry is already carried on each element, and the output format is fixed.

Pixel-Level Vision​

Screenshots and anything derived from them are runner-direct. The route table is the single source of truth, so the SDK ships stubs for all thirteen /vision/* routes that return RUNNER_REQUIRED when no runner is mounted:

RoutePurpose
POST /vision/captureCapture a screenshot
POST /vision/annotateCapture with element overlays drawn
POST /vision/diffPixel diff against a previous capture
POST /vision/rawRaw capture, no post-processing
POST /vision/extractOCR — visible text with bounding boxes
POST /vision/describeVLM caption of the capture
POST /vision/analyzeDeclarative analyzers (layout, typography, colour, …)
POST /vision/assertThe visual assertion DSL
POST /vision/baselineRecord a visual baseline
GET /vision/baselinesList recorded baselines
POST /vision/mutation-occurredFrontend → runner mutation signal
GET /vision/cache/:sha256Stream a cached capture
GET /vision/healthVision pipeline health

These replaced the older /control/*screenshot* routes. The /ai/media/* routes were retired without a successor: image facts arrive on find's media rows. POST /vision/describe can fail its structured-parse step and return prose with structured: null; that case is reported as UB-VLM-STRUCTURED-PARSE-FAIL.