fix(ui): per-field sub-schema for escape-hatch Monaco editors + dispose guard (#261)

This commit is contained in:
Joseph Doherty
2026-06-19 03:28:58 -04:00
parent 282bc8b53c
commit e814bf5efb
3 changed files with 171 additions and 24 deletions
@@ -53,12 +53,6 @@ else
private InboundApiSchema? _rootSchema;
private string? _parsedFor;
// The resolved root schema serialized back to a JSON Schema string (with every
// {"$ref":"lib:Name"} inlined). Fed to the raw-JSON escape-hatch Monaco editor
// so Monaco's built-in JSON language offers schema-driven hover + completion on
// the entered JSON. Computed once per parse alongside the resolved schema.
private string? _resolvedSchemaJson;
// Raw text and per-field parse/validation errors keyed by canonical PATH
// (e.g. "order.id", "tags[0]") so nested inputs each carry their own state.
private readonly Dictionary<string, string> _rawText = new();
@@ -76,9 +70,22 @@ else
_shapes = ScriptParameterNames.ParseShapes(ParameterDefinitions);
_rootSchema = await ParseRootAsync();
_topLevelFields = _rootSchema?.Type == "object" ? _rootSchema.Fields : Array.Empty<InboundApiSchemaField>();
_resolvedSchemaJson = _rootSchema is null ? null : ToJsonSchema(_rootSchema);
}
/// <summary>
/// Serializes the escape-hatch FIELD's own (resolved) sub-schema to a JSON
/// Schema string for that field's Monaco editor — NOT the whole root object
/// schema. So a field's raw-JSON editor validates/completes against just that
/// field's portion of the type, not the entire object. Any
/// <c>{"$ref":"lib:Name"}</c> the parse already inlined is emitted as its
/// resolved shape; a node whose ref could NOT be resolved (Type
/// <c>"ref"</c>) — or any unknown type — has no derivable sub-schema and
/// collapses to a permissive <c>{}</c>, so the editor still validates
/// well-formed JSON without false errors. Cheap and on-demand: only the rare
/// escape-hatch nodes pay for it, and only at render time.
/// </summary>
private static string FieldSchemaJson(InboundApiSchema fieldSchema) => ToJsonSchema(fieldSchema);
/// <summary>
/// Serializes a (resolved) <see cref="InboundApiSchema"/> back to a JSON Schema
/// string for the escape-hatch Monaco editor. Any <c>{"$ref":"lib:Name"}</c> the
@@ -250,7 +257,7 @@ else
default: // unresolved $ref / unknown — raw-JSON escape hatch (Monaco).
<div class="param-json-editor" data-path="@path">
<MonacoEditor Language="json" Height="120px" ShowToolbar="false"
JsonSchema="@_resolvedSchemaJson"
JsonSchema="@FieldSchemaJson(schema)"
Value="@AsRaw(path)"
ValueChanged="@(v => SetJson(path, v))" />
</div>