Lesson 5.3: UI extension — semantic kinds
Difficulty: Intermediate | Duration: 15 min | Prerequisites: Lesson 5.1; .NET 10 SDK
Runnable scaffold. This lesson ships
start/(the[BowireExtension]descriptor is complete + discovered, but the JSviewer.mountonly renders a placeholder) andcompleted/(the JS reads the paired lat/lon fromctx.interpretationsand renders a coordinate card with an OpenStreetMap link). The C# is identical between the two — the work is entirely in the embedded JS bundle. It's an offline-safe, dependency-free stand-in for the shippedKuestenlogik.Bowire.MapMapLibre widget: samecoordinate.wgs84kind, same mechanism, no 800 KB renderer. Both build against theKuestenlogik.Bowireversion pinned in their.csproj— the release cascade keeps that pin current.
Overview
Protocol plugins extend what Bowire can talk to. UI extensions extend how Bowire renders a response. When a response field carries a semantic kind — a tag like coordinate.wgs84 — Bowire can auto-mount a purpose-built widget (a map, a chart, a hex viewer) over it instead of showing raw JSON.
How it works
- A field is annotated with a semantic kind (e.g.
coordinate.wgs84for a lat/long pair). - A UI extension declares which kind it handles via the
[BowireExtension]attribute and implementsIBowireUiExtension. - Auto-discovery (the same
[BowireExtension]assembly scan that finds field-detectors) mounts the widget wherever a matching kind appears — no per-response wiring.
The canonical example: a coordinate.wgs84 field auto-renders a map widget with a pin, instead of { "lat": 53.55, "lon": 9.99 }.
Steps
1. Add the extension
In a plugin assembly (scaffolded as in Lesson 5.1), implement IBowireUiExtension and tag it:
[BowireExtension]
public sealed class MapWidgetExtension : IBowireUiExtension
{
public string SemanticKind => "coordinate.wgs84";
// render contract: emit the widget markup/props for a matching field
}
[BowireExtension] opts the type into the auto-discovery scan (the same mechanism protocol/field-detector extensions use), so no manual registration is needed.
2. Tag the data
A field surfaces the widget when its semantic kind resolves to coordinate.wgs84 — either declared by the protocol's schema, or inferred by a field-detector (Bowire ships a WGS84 coordinate detector). When the workbench renders a response containing that field, your widget mounts automatically.
3. Verify
Invoke a method whose response carries a coordinate. Instead of raw JSON, the response pane shows the map with a pin. Remove the extension (or its assembly) and the field degrades gracefully back to JSON — extensions are additive.
Ship UI extensions in the same NuGet as a protocol plugin, or standalone. The
Kuestenlogik.Bowire.Mappackage is the shipped map-widget extension — a real reference.
Key Takeaways
- Semantic kinds drive rendering — a tag like
coordinate.wgs84picks a widget. [BowireExtension]+IBowireUiExtension= auto-discovered UI extension; no per-response wiring.- Additive + graceful — no matching extension → the field falls back to JSON.
What's Next
Continue: → Lesson 5.4: Plugin lifecycle