8. UI, Queries, and Forms
Blueprint UI is an interpreter. It uses declared field, relationship, facet, entity, module, and app semantics; it does not reverse-engineer meaning from column names.
Generic enterprise UX belongs to vyuh_studio_ui. CDX packages own controls and view mechanics. vyuh_blueprint_ui maps Blueprint metadata and protocol records into those Studio contracts; it does not introduce a parallel widget system.
The UI hint cascade
BlueprintUI defaults
-> ModuleUI overrides
-> EntityUI overrides
-> FacetUI sections
-> FieldUI intrinsic controls and behavior
-> ListUI layouts and FormUI composition
-> client registry bindingsQuery limits already resolve property-by-property through entity → module → Blueprint → platform defaults:
final blueprint = Blueprint(
name: 'pharma',
version: '1.0.0',
ui: const BlueprintUI(
query: QueryUI(maxSortLevels: 3, maxGroupLevels: 3),
),
modules: [
Module(
// ...
ui: const ModuleUI(
routePrefix: '/ops',
query: QueryUI(maxGroupLevels: 2),
),
),
],
);Describe a field once
static const categoryId = UuidField(
'category_id',
title: 'Area Category',
indexed: true,
db: FieldDb(
queryPatterns: [QueryPattern.filter, QueryPattern.aggregate],
),
reference: UIReferenceHint(
targetEntity: 'ops.area_category',
projection: 'picker',
),
ui: FieldUI(
input: UIFieldInput.createAndUpdate,
widget: UIFieldControl.referencePicker,
formSection: 'hierarchy',
filterable: true,
sortable: true,
order: 40,
optionSearch: UIOptionSearch.auto,
),
);The same declaration drives:
- a searchable reference picker in the editor;
- a linked label in details;
- a projected table column;
- a typed filter operand;
- reference identity on the wire.
Raw UUID entry is not an acceptable reference experience.
Input capability is not storage writability
static const createdBy = ActorField(
'created_by',
ui: FieldUI(input: UIFieldInput.never),
);
static const code = CodeField.configured(
ui: FieldUI(input: UIFieldInput.create),
);UIFieldInput distinguishes never, create-only, update-only, and both. Runtime-managed fields remain excluded even when physically writable.
Derive form values declaratively
static const name = NameField();
static const code = CodeField.configured(
ui: FieldUI(
derivation: UIFieldDerivation(
sourceFields: [name],
transforms: [
UITrimTransform(),
UIUpperCaseTransform(),
UIReplacePatternTransform(
pattern: r'[^A-Z0-9]+',
replacement: '-',
),
UIReplacePatternTransform(pattern: r'^-+|-+$'),
],
override: UIFieldDerivationOverride.untilOverridden,
),
),
);The code follows the name until a person edits it. The editor then preserves the manual value and may expose a relink action.
UIFieldTransformRef('ops.area.code') is the typed escape hatch for a host-registered transform. Keep validation-critical invariants on the server; client derivation is an authoring aid.
Derived and explicit form structures
The app grammar can derive sections from FieldUI.formSection and the owning facet:
const editor = EditorUI(
name: 'area-editor',
title: 'Area',
presentation: EditorPresentation.adaptive,
operations: {EditorOperation.create, EditorOperation.update},
form: FormUI.derived(),
);Or declare a stable custom structure:
const editor = EditorUI(
name: 'area-editor',
title: 'Area',
kind: EditorUIKind.tabs,
presentation: EditorPresentation.dock,
parts: [
EditorPartUI(
name: 'main',
title: 'Area',
form: FormUI.sections(
sections: [
FormSectionUI(
name: 'identity',
title: 'Identity',
fieldRefs: [
ProjectedFieldRef('identity.code'),
ProjectedFieldRef('identity.name'),
],
columns: 2,
),
FormSectionUI(
name: 'hierarchy',
title: 'Hierarchy',
facetRefs: ['hierarchy'],
columns: 2,
),
],
),
),
],
);Explicit sections cannot make a runtime-managed field editable. Input policy still comes from the field.
Query operands follow field semantics
The field registry maps a projected field to its type, labels, relationship, and query capabilities. Therefore:
- enum → compact labeled option picker;
- boolean → semantic Yes/No or Active/Inactive control;
- date/timestamp → date or date-time picker;
- reference → entity picker;
- arrays → multi-value operand;
- numeric/range → typed numeric/range editor;
- text → CDX text control;
- operators with no operand → no value editor.
The client builds a Vyuh/CDX query AST. The server must validate the same field catalog and compile only declared queryable fields. A visible filter that the server cannot execute is a contract error.
Reference controls query the standard slim reference projection. That keeps picker reads to identity fields such as id, code, name, and description instead of loading detail-shaped records. Studio's EntityPicker owns the generic selection interaction; Blueprint UI supplies entity, projection, and query context.
Relationship tabs are explicit when EntityUI.relationshipMode: RelationshipUIMode.explicit is used. In that mode, only declared RelatedUI entries appear; storage edges do not become accidental product navigation.
Action placement references the action catalog
ui: EntityUI(
actionPlacements: [
ActionPlacementUI(
placement: ActionPlacementRef.recordHeader,
actions: [
ActionUIRef(
'governance.activate',
presentation: UIActionPresentation.icon,
tooltip: 'Activate area',
),
],
),
],
)The facet owns the action. Placements only decide where and how an effective action appears. Permission, policy, state, and context resolution may make the final action hidden, disabled with a reason, or enabled.
Checkpoint
Build an area editor with:
- derived identity and hierarchy sections;
- enum labels;
- a category reference picker;
- create-only code that follows name until overridden;
- at most three sort and group levels;
- header placement for
activate.
Next: Actors, work, and policy.
References: UI Hints · Blueprint UI.