@tai42/studio-sdk 10.0.0 → 11.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/components.css +18 -0
- package/dist/components/dirty-guard.d.ts +23 -10
- package/dist/components/dirty-guard.d.ts.map +1 -1
- package/dist/components/dirty-guard.js +22 -12
- package/dist/components/dirty-guard.js.map +1 -1
- package/dist/components/field.d.ts.map +1 -1
- package/dist/components/field.js +1 -1
- package/dist/components/field.js.map +1 -1
- package/dist/components/fleet-report.d.ts +12 -3
- package/dist/components/fleet-report.d.ts.map +1 -1
- package/dist/components/fleet-report.js +28 -9
- package/dist/components/fleet-report.js.map +1 -1
- package/dist/components/icons.d.ts +4 -0
- package/dist/components/icons.d.ts.map +1 -1
- package/dist/components/icons.js +5 -0
- package/dist/components/icons.js.map +1 -1
- package/dist/hooks/useApi.d.ts +348 -2
- package/dist/hooks/useApi.d.ts.map +1 -1
- package/dist/host.d.ts +1 -1
- package/dist/host.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/navigation/context.d.ts +6 -2
- package/dist/navigation/context.d.ts.map +1 -1
- package/dist/navigation/context.js +10 -2
- package/dist/navigation/context.js.map +1 -1
- package/dist/navigation/types.d.ts +1 -0
- package/dist/navigation/types.d.ts.map +1 -1
- package/dist/plugin/host-state.d.ts +16 -0
- package/dist/plugin/host-state.d.ts.map +1 -1
- package/dist/plugin/host-state.js +1 -0
- package/dist/plugin/host-state.js.map +1 -1
- package/dist/plugin/types.d.ts +17 -7
- package/dist/plugin/types.d.ts.map +1 -1
- package/dist/schema-form/SecretRefField.d.ts.map +1 -1
- package/dist/schema-form/SecretRefField.js +3 -2
- package/dist/schema-form/SecretRefField.js.map +1 -1
- package/dist/schema-form/default-value.d.ts +12 -0
- package/dist/schema-form/default-value.d.ts.map +1 -1
- package/dist/schema-form/default-value.js +34 -3
- package/dist/schema-form/default-value.js.map +1 -1
- package/dist/schema-form/field-node.d.ts +7 -0
- package/dist/schema-form/field-node.d.ts.map +1 -1
- package/dist/schema-form/field-node.js +3 -2
- package/dist/schema-form/field-node.js.map +1 -1
- package/dist/schema-form/object-fields.d.ts +3 -1
- package/dist/schema-form/object-fields.d.ts.map +1 -1
- package/dist/schema-form/object-fields.js +2 -2
- package/dist/schema-form/object-fields.js.map +1 -1
- package/dist/schema-form/union-field.d.ts +5 -2
- package/dist/schema-form/union-field.d.ts.map +1 -1
- package/dist/schema-form/union-field.js +68 -15
- package/dist/schema-form/union-field.js.map +1 -1
- package/package.json +2 -2
- package/src/components/components.css +18 -0
- package/src/components/dirty-guard.tsx +23 -13
- package/src/components/field.tsx +10 -5
- package/src/components/fleet-report.tsx +44 -13
- package/src/components/icons.tsx +19 -0
- package/src/host.ts +5 -1
- package/src/index.ts +3 -1
- package/src/navigation/context.tsx +16 -1
- package/src/navigation/types.ts +3 -0
- package/src/plugin/host-state.ts +18 -0
- package/src/plugin/types.ts +17 -7
- package/src/schema-form/SecretRefField.tsx +3 -2
- package/src/schema-form/default-value.ts +36 -3
- package/src/schema-form/field-node.tsx +21 -2
- package/src/schema-form/object-fields.tsx +4 -0
- package/src/schema-form/union-field.tsx +85 -19
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The union field renderer: a variant picker plus the active variant's fields.
|
|
3
3
|
* An object variant renders its properties inline (skipping the discriminator
|
|
4
|
-
* tag, which the picker owns)
|
|
5
|
-
*
|
|
4
|
+
* tag, which the picker owns), with the variant's required fields carrying the
|
|
5
|
+
* design system's required marker; a scalar/enum variant renders as a single
|
|
6
|
+
* nested {@link FieldNode}. Selecting a variant seeds a concrete value for it,
|
|
7
|
+
* preserving the fields the outgoing value shares with the new variant (see
|
|
8
|
+
* {@link switchVariantValue}).
|
|
6
9
|
*/
|
|
7
10
|
import type { ReactNode } from 'react';
|
|
8
11
|
|
|
9
12
|
import { Field } from '../components/field';
|
|
10
13
|
import { Select } from '../components/select';
|
|
11
|
-
import { classifySchema, type FieldModel } from './classify';
|
|
12
|
-
import {
|
|
14
|
+
import { classifySchema, type FieldModel, type UnionVariant } from './classify';
|
|
15
|
+
import { skeletonValueForSchema } from './default-value';
|
|
13
16
|
import { FieldGroup } from './field-group';
|
|
14
17
|
import { FieldNode } from './field-node';
|
|
15
18
|
import { ObjectFields } from './object-fields';
|
|
@@ -18,28 +21,87 @@ import { groupClass } from './styles';
|
|
|
18
21
|
import { activeVariantIndex } from './union';
|
|
19
22
|
import type { JsonSchema, SchemaFormErrors } from './types';
|
|
20
23
|
|
|
24
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
25
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
26
|
+
}
|
|
27
|
+
|
|
21
28
|
/**
|
|
22
|
-
* A concrete seed for a chosen union variant. Object variants get their
|
|
23
|
-
* required
|
|
29
|
+
* A concrete seed for a chosen union variant. Object variants get their full
|
|
30
|
+
* skeleton (every required field minted, so the seed validates against the
|
|
31
|
+
* variant's own schema); a scalar/enum variant that has no explicit default
|
|
24
32
|
* still needs a non-`undefined` value so the selection sticks (otherwise
|
|
25
33
|
* `activeVariantIndex` reads it back as "no variant" and the picker snaps empty).
|
|
26
34
|
*/
|
|
27
35
|
function seedVariantValue(schema: JsonSchema, root: JsonSchema): unknown {
|
|
28
|
-
const seeded =
|
|
36
|
+
const seeded = skeletonValueForSchema(schema, root);
|
|
29
37
|
if (seeded !== undefined) return seeded;
|
|
30
38
|
const resolved = resolveRef(schema, root);
|
|
31
39
|
if (resolved.const !== undefined) return resolved.const;
|
|
32
40
|
if (resolved.enum !== undefined && resolved.enum.length > 0) return resolved.enum[0];
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The value committed when the user switches to `target`. Typed values SURVIVE
|
|
46
|
+
* the switch: the target's skeleton is minted first, then every top-level field
|
|
47
|
+
* of the current value whose NAME exists in the target variant's properties is
|
|
48
|
+
* carried over wholesale — the merge happens at the top level only, each carried
|
|
49
|
+
* field keeping its nested contents as-is — so fields both variants share are
|
|
50
|
+
* never silently discarded. Only fields absent from the current value keep
|
|
51
|
+
* their freshly minted seed. The discriminator tag is never carried: it always
|
|
52
|
+
* comes from the target's seed (the old tag would re-select the old variant).
|
|
53
|
+
* A non-object side (scalar variant, or a non-object current value) has no
|
|
54
|
+
* fields to carry, so it reseeds wholesale.
|
|
55
|
+
*/
|
|
56
|
+
function switchVariantValue(
|
|
57
|
+
current: unknown,
|
|
58
|
+
target: UnionVariant,
|
|
59
|
+
discriminator: string | undefined,
|
|
60
|
+
root: JsonSchema,
|
|
61
|
+
): unknown {
|
|
62
|
+
const seeded = seedVariantValue(target.schema, root);
|
|
63
|
+
if (!isPlainObject(current) || !isPlainObject(seeded)) return seeded;
|
|
64
|
+
const { model } = classifySchema(target.schema, root);
|
|
65
|
+
if (model.kind !== 'object') return seeded;
|
|
66
|
+
const targetNames = new Set(model.properties.map(([name]) => name));
|
|
67
|
+
const merged: Record<string, unknown> = { ...seeded };
|
|
68
|
+
for (const [name, held] of Object.entries(current)) {
|
|
69
|
+
if (name === discriminator) continue;
|
|
70
|
+
if (targetNames.has(name)) merged[name] = held;
|
|
71
|
+
}
|
|
72
|
+
return merged;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Title-case a property name: `kind` → "Kind", `content_type` → "Content Type". */
|
|
76
|
+
function titleCaseName(name: string): string {
|
|
77
|
+
return name
|
|
78
|
+
.split(/[_\-\s]+/)
|
|
79
|
+
.filter((part) => part.length > 0)
|
|
80
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
81
|
+
.join(' ');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The variant picker's label. A discriminated union names the picker after the
|
|
86
|
+
* discriminator property it sets — the property's schema `title` when a variant
|
|
87
|
+
* declares one, else the property name title-cased — so the picker reads as the
|
|
88
|
+
* field it is ("Kind", "Format"). Only a union without a discriminator falls
|
|
89
|
+
* back to the generic "Variant".
|
|
90
|
+
*/
|
|
91
|
+
function pickerLabel(
|
|
92
|
+
discriminator: string | undefined,
|
|
93
|
+
variants: readonly UnionVariant[],
|
|
94
|
+
root: JsonSchema,
|
|
95
|
+
): string {
|
|
96
|
+
if (discriminator === undefined) return 'Variant';
|
|
97
|
+
for (const variant of variants) {
|
|
98
|
+
const property = resolveRef(variant.schema, root).properties?.[discriminator];
|
|
99
|
+
if (property === undefined) continue;
|
|
100
|
+
const title = property.title ?? resolveRef(property, root).title;
|
|
101
|
+
if (typeof title === 'string' && title.length > 0) return title;
|
|
42
102
|
}
|
|
103
|
+
const cased = titleCaseName(discriminator);
|
|
104
|
+
return cased.length > 0 ? cased : 'Variant';
|
|
43
105
|
}
|
|
44
106
|
|
|
45
107
|
export function UnionField({
|
|
@@ -74,15 +136,18 @@ export function UnionField({
|
|
|
74
136
|
const position = Number(key);
|
|
75
137
|
const variant = model.variants[position];
|
|
76
138
|
if (variant === undefined) return;
|
|
77
|
-
onChange(
|
|
139
|
+
onChange(switchVariantValue(value, variant, model.discriminator, root));
|
|
78
140
|
};
|
|
79
141
|
|
|
80
142
|
const active = index === -1 ? undefined : model.variants[index];
|
|
81
143
|
const activeClassified = active === undefined ? undefined : classifySchema(active.schema, root);
|
|
82
144
|
|
|
83
145
|
return (
|
|
84
|
-
|
|
85
|
-
|
|
146
|
+
// At the form root the union sits inside a field the HOST already labels
|
|
147
|
+
// and describes — a synthetic heading there is noise, so it is suppressed
|
|
148
|
+
// exactly as a root object's is; nested unions keep theirs.
|
|
149
|
+
<FieldGroup heading={heading} description={description} error={error} atRoot={path === ''}>
|
|
150
|
+
<Field label={pickerLabel(model.discriminator, model.variants, root)}>
|
|
86
151
|
<Select
|
|
87
152
|
options={options}
|
|
88
153
|
value={index === -1 ? '' : String(index)}
|
|
@@ -102,6 +167,7 @@ export function UnionField({
|
|
|
102
167
|
errors={errors}
|
|
103
168
|
idPrefix={idPrefix}
|
|
104
169
|
skip={model.discriminator}
|
|
170
|
+
markRequired
|
|
105
171
|
/>
|
|
106
172
|
</div>
|
|
107
173
|
) : active !== undefined ? (
|