@tai42/studio-sdk 9.2.1 → 9.3.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/README.md +5 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/jq.d.ts +8 -0
- package/dist/jq.d.ts.map +1 -1
- package/dist/jq.js +8 -0
- package/dist/jq.js.map +1 -1
- package/dist/schema-form/SchemaForm.d.ts +20 -1
- package/dist/schema-form/SchemaForm.d.ts.map +1 -1
- package/dist/schema-form/SchemaForm.js +8 -3
- package/dist/schema-form/SchemaForm.js.map +1 -1
- package/dist/schema-form/context.d.ts +73 -1
- package/dist/schema-form/context.d.ts.map +1 -1
- package/dist/schema-form/context.js +16 -2
- package/dist/schema-form/context.js.map +1 -1
- package/dist/schema-form/index.d.ts +27 -0
- package/dist/schema-form/index.d.ts.map +1 -1
- package/dist/schema-form/index.js +26 -0
- package/dist/schema-form/index.js.map +1 -1
- package/dist/schema-form/string-field.d.ts +14 -16
- package/dist/schema-form/string-field.d.ts.map +1 -1
- package/dist/schema-form/string-field.js +43 -50
- package/dist/schema-form/string-field.js.map +1 -1
- package/package.json +8 -4
- package/src/index.ts +10 -0
- package/src/jq.ts +8 -0
- package/src/schema-form/SchemaForm.tsx +45 -14
- package/src/schema-form/context.ts +86 -3
- package/src/schema-form/index.ts +32 -0
- package/src/schema-form/string-field.tsx +63 -70
|
@@ -1,51 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The string field renderer. A plain string renders as a text input (its
|
|
3
3
|
* `type` derived from any JSON-Schema `format`); an expression-annotated schema
|
|
4
|
-
* renders the
|
|
5
|
-
*
|
|
6
|
-
* supplies a completion provider, the input is
|
|
4
|
+
* renders the INJECTED expression door (a resting textarea with the visual-editor
|
|
5
|
+
* button) when the host supplies one; a media-annotated schema renders the upload
|
|
6
|
+
* control instead; and when the form supplies a completion provider, the input is
|
|
7
|
+
* backed by argument autocomplete.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* SDK (a host that externalises the SDK is unaffected either way); the dynamic
|
|
19
|
-
* import keeps it out of `schema-form`'s static graph and pulls it only when an
|
|
20
|
-
* annotated field actually mounts.
|
|
9
|
+
* NO JQ EDGE. This module — and every module under `schema-form` — names no jq
|
|
10
|
+
* type and imports the SDK's `../jq` re-export neither statically NOR dynamically.
|
|
11
|
+
* The jq authoring door drags a heavy subgraph (the xyflow visual editor, a Web
|
|
12
|
+
* Worker entry, and a multi-megabyte wasm engine), and a bundler EMITS that whole
|
|
13
|
+
* subgraph for a dynamic import just as it does for a static one — `lazy` defers
|
|
14
|
+
* the FETCH, never the emission. A consumer that bundles the SDK to render forms
|
|
15
|
+
* would still ship the chunks, the worker file, and the wasm it can never run.
|
|
16
|
+
* So the door arrives from the host instead, through
|
|
17
|
+
* {@link ExpressionFieldContext} (or the form's `expressionField` prop), and an
|
|
18
|
+
* annotated field with no injected door falls back to the plain string input.
|
|
21
19
|
*/
|
|
22
20
|
import type { ReactNode } from 'react';
|
|
23
|
-
import { Suspense,
|
|
21
|
+
import { Suspense, useCallback, useContext, useMemo, useState } from 'react';
|
|
24
22
|
|
|
25
23
|
import { CompletionInput } from '../components/completion-input';
|
|
26
24
|
import { Field } from '../components/field';
|
|
27
25
|
import { AlertTriangleIcon } from '../components/icons';
|
|
28
26
|
import { TextInput, Textarea } from '../components/inputs';
|
|
29
27
|
import { errorMessage } from '../errors';
|
|
30
|
-
import type { JqInputShapeDescriptor } from '../jq';
|
|
31
28
|
import type { ExpressionAnnotation, MediaUpload } from './classify';
|
|
32
|
-
import {
|
|
29
|
+
import type { ExpressionFieldComponent, ExpressionInputShape } from './context';
|
|
30
|
+
import { CompletionProviderContext, ExpressionFieldContext } from './context';
|
|
33
31
|
import { MediaField } from './media-field';
|
|
34
32
|
import type { CompletionProvider } from './SchemaForm';
|
|
35
33
|
|
|
36
|
-
// The lazily-loaded jq door. Defining it at module scope (never inside a
|
|
37
|
-
// component) is what keeps the lazy component's identity stable across renders,
|
|
38
|
-
// so React resolves the chunk once and never re-suspends a mounted field. The
|
|
39
|
-
// `import('../jq')` here is a DYNAMIC import — the sole runtime edge from
|
|
40
|
-
// `schema-form` to the jq door — so the visual editor, worker, and wasm live in
|
|
41
|
-
// a split chunk that a non-annotated form never requests. The `type`-only
|
|
42
|
-
// import of `JqInputShapeDescriptor` above is erased at compile time and adds no
|
|
43
|
-
// such edge.
|
|
44
|
-
const JqField = lazy(async () => {
|
|
45
|
-
const { JqField } = await import('../jq');
|
|
46
|
-
return { default: JqField };
|
|
47
|
-
});
|
|
48
|
-
|
|
49
34
|
export function StringField({
|
|
50
35
|
heading,
|
|
51
36
|
description,
|
|
@@ -70,6 +55,7 @@ export function StringField({
|
|
|
70
55
|
onChange: (value: unknown) => void;
|
|
71
56
|
}): ReactNode {
|
|
72
57
|
const completionProvider = useContext(CompletionProviderContext);
|
|
58
|
+
const expressionField = useContext(ExpressionFieldContext);
|
|
73
59
|
const current = typeof value === 'string' ? value : '';
|
|
74
60
|
// An empty optional field emits `undefined` (drops the key); a required one
|
|
75
61
|
// keeps the empty string so validation can flag it.
|
|
@@ -77,15 +63,20 @@ export function StringField({
|
|
|
77
63
|
onChange(next === '' && !required ? undefined : next);
|
|
78
64
|
};
|
|
79
65
|
|
|
80
|
-
// An expression-annotated field renders the
|
|
81
|
-
//
|
|
66
|
+
// An expression-annotated field renders the injected door — the resting control
|
|
67
|
+
// plus its visual-editor button. Checked before `media` because the expression
|
|
82
68
|
// annotation is the more specific, deliberate opt-in (`x-tai42-expression`);
|
|
83
|
-
// a schema carrying both is contradictory, and
|
|
69
|
+
// a schema carrying both is contradictory, and an expression is text, not an
|
|
84
70
|
// upload. Not reachable together with the completion provider path: an
|
|
85
71
|
// expression is authored, not completed from server suggestions.
|
|
86
|
-
|
|
72
|
+
//
|
|
73
|
+
// With no door injected the annotation is INERT: the field falls through to the
|
|
74
|
+
// ordinary paths below, exactly as a malformed annotation does, so a form
|
|
75
|
+
// renders the same bytes either way.
|
|
76
|
+
if (expression !== undefined && expressionField !== undefined) {
|
|
87
77
|
return (
|
|
88
|
-
<
|
|
78
|
+
<ExpressionField
|
|
79
|
+
component={expressionField}
|
|
89
80
|
heading={heading}
|
|
90
81
|
description={description}
|
|
91
82
|
error={error}
|
|
@@ -198,29 +189,31 @@ function CompletionField({
|
|
|
198
189
|
}
|
|
199
190
|
|
|
200
191
|
/**
|
|
201
|
-
* An expression-annotated string field, rendered
|
|
202
|
-
* resting control with the
|
|
203
|
-
* its own label/description/error chrome (a11y-linked slots), so it is NOT
|
|
204
|
-
* wrapped in the form's `Field` component — the annotation's descriptor fields
|
|
205
|
-
*
|
|
206
|
-
*
|
|
192
|
+
* An expression-annotated string field, rendered through the injected door: a
|
|
193
|
+
* multiline resting control with the door's own visual-editor button. The door
|
|
194
|
+
* brings its own label/description/error chrome (a11y-linked slots), so it is NOT
|
|
195
|
+
* wrapped in the form's `Field` component — the annotation's descriptor fields map
|
|
196
|
+
* onto the input-shape descriptor, and the form's per-field error feeds `error`.
|
|
197
|
+
*
|
|
198
|
+
* The door is rendered as an ELEMENT, never called as a function, so its hooks and
|
|
199
|
+
* state (editor open, worker handle) belong to it and survive re-renders of this
|
|
200
|
+
* field.
|
|
207
201
|
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
* -
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* the compact variant (visually-hidden label, icon-only door) is for dense
|
|
215
|
-
* host rows, which the schema form does not have.
|
|
202
|
+
* It mounts inside a `Suspense` boundary because a host is free to inject a `lazy`
|
|
203
|
+
* door to keep the jq subgraph in a split chunk of ITS OWN bundle. The fallback
|
|
204
|
+
* paints the resting shell — label/description/error chrome around the current
|
|
205
|
+
* value in a read-only multiline control — so the field holds its layout and keeps
|
|
206
|
+
* the value visible while that chunk resolves, and the swap does not flash. A door
|
|
207
|
+
* injected eagerly never suspends and renders straight through.
|
|
216
208
|
*
|
|
217
|
-
* The
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
209
|
+
* The form passes only the props the field owns; a jq door's `serverValidate`,
|
|
210
|
+
* `onEditorOpenChange`, and `compact` stay unwired, because no author-time
|
|
211
|
+
* validation endpoint applies to a schema-declared field, the hosts that render
|
|
212
|
+
* `SchemaForm` register no global shortcuts to mute, and every sibling field shows
|
|
213
|
+
* a visible label (the compact variant is for dense host rows).
|
|
222
214
|
*/
|
|
223
|
-
function
|
|
215
|
+
function ExpressionField({
|
|
216
|
+
component: Door,
|
|
224
217
|
heading,
|
|
225
218
|
description,
|
|
226
219
|
error,
|
|
@@ -229,6 +222,7 @@ function JqExpressionField({
|
|
|
229
222
|
value,
|
|
230
223
|
onChange,
|
|
231
224
|
}: {
|
|
225
|
+
component: ExpressionFieldComponent;
|
|
232
226
|
heading: string;
|
|
233
227
|
description: string | undefined;
|
|
234
228
|
error: string | undefined;
|
|
@@ -237,13 +231,13 @@ function JqExpressionField({
|
|
|
237
231
|
value: string;
|
|
238
232
|
onChange: (value: string) => void;
|
|
239
233
|
}): ReactNode {
|
|
240
|
-
// Memoised: the descriptor's identity feeds
|
|
234
|
+
// Memoised: the descriptor's identity feeds the door's own memoisation, so a
|
|
241
235
|
// fresh object each keystroke would defeat it.
|
|
242
236
|
const shape = useMemo(() => expressionShape(expression, argName), [expression, argName]);
|
|
243
237
|
return (
|
|
244
238
|
<Suspense
|
|
245
239
|
fallback={
|
|
246
|
-
<
|
|
240
|
+
<ExpressionFieldSkeleton
|
|
247
241
|
heading={heading}
|
|
248
242
|
description={description}
|
|
249
243
|
error={error}
|
|
@@ -251,7 +245,7 @@ function JqExpressionField({
|
|
|
251
245
|
/>
|
|
252
246
|
}
|
|
253
247
|
>
|
|
254
|
-
<
|
|
248
|
+
<Door
|
|
255
249
|
label={heading}
|
|
256
250
|
description={description}
|
|
257
251
|
error={error}
|
|
@@ -265,13 +259,13 @@ function JqExpressionField({
|
|
|
265
259
|
}
|
|
266
260
|
|
|
267
261
|
/**
|
|
268
|
-
* The resting shell shown while
|
|
269
|
-
* door's footprint — the same label/description/error chrome wrapping a
|
|
262
|
+
* The resting shell shown while a lazily-injected door resolves. It mirrors the
|
|
263
|
+
* live door's footprint — the same label/description/error chrome wrapping a
|
|
270
264
|
* multiline control seeded with the current value — but the control is inert
|
|
271
265
|
* (read-only, `aria-busy`) because there is nothing to edit yet. Matching the
|
|
272
266
|
* footprint is what keeps the Suspense swap from shifting layout or flashing.
|
|
273
267
|
*/
|
|
274
|
-
function
|
|
268
|
+
function ExpressionFieldSkeleton({
|
|
275
269
|
heading,
|
|
276
270
|
description,
|
|
277
271
|
error,
|
|
@@ -290,17 +284,16 @@ function JqExpressionFieldSkeleton({
|
|
|
290
284
|
}
|
|
291
285
|
|
|
292
286
|
/**
|
|
293
|
-
* Map a classified expression annotation onto
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
* than invented copy.
|
|
287
|
+
* Map a classified expression annotation onto the door's input-shape descriptor.
|
|
288
|
+
* A bare annotation (`language` only) maps to NO descriptor — an undeclared shape
|
|
289
|
+
* is the honest rendering when the server said nothing about `.`. When any
|
|
290
|
+
* descriptor field is present, the required descriptor members the annotation
|
|
291
|
+
* omits fall back to neutral values rather than invented copy.
|
|
299
292
|
*/
|
|
300
293
|
function expressionShape(
|
|
301
294
|
expression: ExpressionAnnotation,
|
|
302
295
|
argName: string,
|
|
303
|
-
):
|
|
296
|
+
): ExpressionInputShape | undefined {
|
|
304
297
|
const declared =
|
|
305
298
|
expression.label !== undefined ||
|
|
306
299
|
expression.blurb !== undefined ||
|