@tai42/studio-sdk 1.0.0 → 1.1.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.
Files changed (45) hide show
  1. package/dist/hooks/useApi.d.ts +142 -0
  2. package/dist/hooks/useApi.d.ts.map +1 -1
  3. package/dist/index.d.ts +2 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/schema-form/SecretRefField.d.ts +42 -0
  8. package/dist/schema-form/SecretRefField.d.ts.map +1 -0
  9. package/dist/schema-form/SecretRefField.js +94 -0
  10. package/dist/schema-form/SecretRefField.js.map +1 -0
  11. package/dist/schema-form/classify.d.ts +3 -0
  12. package/dist/schema-form/classify.d.ts.map +1 -1
  13. package/dist/schema-form/classify.js +17 -0
  14. package/dist/schema-form/classify.js.map +1 -1
  15. package/dist/schema-form/context.d.ts +24 -0
  16. package/dist/schema-form/context.d.ts.map +1 -1
  17. package/dist/schema-form/context.js +3 -0
  18. package/dist/schema-form/context.js.map +1 -1
  19. package/dist/schema-form/default-value.d.ts.map +1 -1
  20. package/dist/schema-form/default-value.js +2 -0
  21. package/dist/schema-form/default-value.js.map +1 -1
  22. package/dist/schema-form/field-node.d.ts.map +1 -1
  23. package/dist/schema-form/field-node.js +3 -0
  24. package/dist/schema-form/field-node.js.map +1 -1
  25. package/dist/schema-form/index.d.ts +4 -0
  26. package/dist/schema-form/index.d.ts.map +1 -1
  27. package/dist/schema-form/index.js +2 -0
  28. package/dist/schema-form/index.js.map +1 -1
  29. package/dist/schema-form/record-field.d.ts +15 -0
  30. package/dist/schema-form/record-field.d.ts.map +1 -0
  31. package/dist/schema-form/record-field.js +144 -0
  32. package/dist/schema-form/record-field.js.map +1 -0
  33. package/dist/schema-form/validate.d.ts.map +1 -1
  34. package/dist/schema-form/validate.js +10 -0
  35. package/dist/schema-form/validate.js.map +1 -1
  36. package/package.json +2 -2
  37. package/src/index.ts +6 -0
  38. package/src/schema-form/SecretRefField.tsx +247 -0
  39. package/src/schema-form/classify.ts +25 -0
  40. package/src/schema-form/context.ts +29 -0
  41. package/src/schema-form/default-value.ts +2 -0
  42. package/src/schema-form/field-node.tsx +16 -0
  43. package/src/schema-form/index.ts +4 -0
  44. package/src/schema-form/record-field.tsx +243 -0
  45. package/src/schema-form/validate.ts +10 -0
@@ -6,6 +6,10 @@
6
6
  */
7
7
  export { SchemaForm } from './SchemaForm';
8
8
  export type { SchemaFormProps, CompletionProvider } from './SchemaForm';
9
+ export { RecordEntryRendererContext } from './context';
10
+ export type { RecordEntryRenderer, RecordEntryContext } from './context';
11
+ export { SecretRefField } from './SecretRefField';
12
+ export type { SecretRefFieldProps, SecretRef } from './SecretRefField';
9
13
  export { defaultValueForSchema } from './default-value';
10
14
  export { validateAgainstSchema } from './validate';
11
15
  export { resolveRef } from './resolve';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema-form/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema-form/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AACvD,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC"}
@@ -5,6 +5,8 @@
5
5
  * and submit.
6
6
  */
7
7
  export { SchemaForm } from './SchemaForm';
8
+ export { RecordEntryRendererContext } from './context';
9
+ export { SecretRefField } from './SecretRefField';
8
10
  export { defaultValueForSchema } from './default-value';
9
11
  export { validateAgainstSchema } from './validate';
10
12
  export { resolveRef } from './resolve';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schema-form/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schema-form/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { JsonSchema, SchemaFormErrors } from './types';
3
+ export declare function RecordField({ heading, description, error, values, root, value, onChange, path, errors, idPrefix, }: {
4
+ heading: string;
5
+ description: string | undefined;
6
+ error: string | undefined;
7
+ values: JsonSchema;
8
+ root: JsonSchema;
9
+ value: unknown;
10
+ onChange: (value: unknown) => void;
11
+ path: string;
12
+ errors: SchemaFormErrors | undefined;
13
+ idPrefix: string;
14
+ }): ReactNode;
15
+ //# sourceMappingURL=record-field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-field.d.ts","sourceRoot":"","sources":["../../src/schema-form/record-field.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAWvC,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAuC5D,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,WAAW,EACX,KAAK,EACL,MAAM,EACN,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,SAAS,CA2HZ"}
@@ -0,0 +1,144 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * The record (string-keyed map) field renderer. Each entry is a key input plus a
4
+ * value editor built from the map's value schema; add appends a blank-key entry,
5
+ * remove drops one by row. Rows carry a synthetic stable id — not their key — so
6
+ * an entry keeps its identity (and focus) while its key is being typed, and so a
7
+ * transiently blank or duplicate key never remounts a row or clobbers another.
8
+ *
9
+ * The emitted value is a plain object of the VALID entries. A blank key and a
10
+ * duplicate key are held in local row state and surfaced (never emitted, never
11
+ * silently merged): the map cannot represent two entries under one key, so the
12
+ * later duplicate is withheld and flagged rather than overwriting the first.
13
+ */
14
+ import { useContext, useRef, useState } from 'react';
15
+ import { CloseIcon } from '../components/icons';
16
+ import { Field } from '../components/field';
17
+ import { TextInput } from '../components/inputs';
18
+ import { Button } from '../components/primitives';
19
+ import { classifySchema } from './classify';
20
+ import { RecordEntryRendererContext } from './context';
21
+ import { defaultValueForSchema } from './default-value';
22
+ import { FieldGroup } from './field-group';
23
+ import { FieldNode } from './field-node';
24
+ function isPlainObject(value) {
25
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
26
+ }
27
+ function analyzeRows(rows) {
28
+ const value = {};
29
+ const seen = new Set();
30
+ const blankRowIds = new Set();
31
+ const duplicateRowIds = new Set();
32
+ for (const row of rows) {
33
+ if (row.key.trim() === '') {
34
+ blankRowIds.add(row.id);
35
+ continue;
36
+ }
37
+ if (seen.has(row.key)) {
38
+ duplicateRowIds.add(row.id);
39
+ continue;
40
+ }
41
+ seen.add(row.key);
42
+ value[row.key] = row.value;
43
+ }
44
+ return { value, blankRowIds, duplicateRowIds };
45
+ }
46
+ export function RecordField({ heading, description, error, values, root, value, onChange, path, errors, idPrefix, }) {
47
+ const nextId = useRef(0);
48
+ const rowsFromValue = (source) => isPlainObject(source)
49
+ ? Object.entries(source).map(([key, entryValue]) => ({
50
+ id: nextId.current++,
51
+ key,
52
+ value: entryValue,
53
+ }))
54
+ : [];
55
+ const [rows, setRows] = useState(() => rowsFromValue(value));
56
+ // The last object THIS field emitted. A parent echoing our own value back
57
+ // passes it identity-equal, so the rows (which may hold a blank/duplicate key
58
+ // the emitted object cannot) survive; a parent that swaps in a different value
59
+ // (a reset, another record loaded) fails the check and resyncs the rows.
60
+ const lastEmitted = useRef(value);
61
+ if (value !== lastEmitted.current) {
62
+ lastEmitted.current = value;
63
+ setRows(rowsFromValue(value));
64
+ }
65
+ const renderEntry = useContext(RecordEntryRendererContext);
66
+ const commit = (nextRows) => {
67
+ setRows(nextRows);
68
+ const emitted = analyzeRows(nextRows).value;
69
+ lastEmitted.current = emitted;
70
+ onChange(emitted);
71
+ };
72
+ const setKey = (id, key) => {
73
+ commit(rows.map((row) => (row.id === id ? { ...row, key } : row)));
74
+ };
75
+ const setValue = (id, next) => {
76
+ commit(rows.map((row) => (row.id === id ? { ...row, value: next } : row)));
77
+ };
78
+ const addRow = () => {
79
+ commit([...rows, { id: nextId.current++, key: '', value: newEntryValue(values, root) }]);
80
+ };
81
+ const removeRow = (id) => {
82
+ commit(rows.filter((row) => row.id !== id));
83
+ };
84
+ const { blankRowIds, duplicateRowIds } = analyzeRows(rows);
85
+ const summary = keyFaultSummary(blankRowIds.size, duplicateRowIds.size);
86
+ return (_jsxs(FieldGroup, { heading: heading, description: description, error: error, atRoot: false, children: [rows.length === 0 ? _jsx("span", { className: "tai-field-hint", children: "No entries" }) : null, summary !== undefined ? (_jsx("span", { role: "alert", className: "tai-field-error", children: summary })) : null, rows.map((row, index) => {
87
+ const position = index + 1;
88
+ const keyFault = rowKeyFault(row.id, blankRowIds, duplicateRowIds);
89
+ const entryPath = path === '' ? row.key : `${path}.${row.key}`;
90
+ const defaultField = (_jsx(FieldNode, { schema: values, root: root, value: row.value, onChange: (next) => {
91
+ setValue(row.id, next);
92
+ }, path: entryPath, label: undefined, required: true, errors: errors, idPrefix: idPrefix }));
93
+ return (_jsxs("div", { className: "tai-row", children: [_jsx(Field, { label: `Key ${String(position)}`, error: keyFault, children: _jsx(TextInput, { value: row.key, onChange: (event) => {
94
+ setKey(row.id, event.target.value);
95
+ } }) }), _jsx("div", { style: { flex: 1 }, children: renderEntry === undefined
96
+ ? defaultField
97
+ : renderEntry({
98
+ keyName: row.key,
99
+ path: entryPath,
100
+ valueSchema: values,
101
+ value: row.value,
102
+ onChange: (next) => {
103
+ setValue(row.id, next);
104
+ },
105
+ defaultField,
106
+ }) }), _jsxs(Button, { type: "button", variant: "secondary", "aria-label": `Remove entry ${String(position)}`, onClick: () => {
107
+ removeRow(row.id);
108
+ }, children: [_jsx(CloseIcon, {}), "Remove"] })] }, row.id));
109
+ }), _jsx("div", { children: _jsx(Button, { type: "button", variant: "secondary", onClick: addRow, children: "Add entry" }) })] }));
110
+ }
111
+ /** The inline key error for one row, or `undefined` when its key is fine. */
112
+ function rowKeyFault(id, blankRowIds, duplicateRowIds) {
113
+ if (blankRowIds.has(id))
114
+ return 'Key is required';
115
+ if (duplicateRowIds.has(id))
116
+ return 'Duplicate key';
117
+ return undefined;
118
+ }
119
+ /** A one-line summary of the map's key faults, or `undefined` when there are none. */
120
+ function keyFaultSummary(blankCount, duplicateCount) {
121
+ const parts = [];
122
+ if (blankCount > 0)
123
+ parts.push(`${String(blankCount)} entry with a blank key`);
124
+ if (duplicateCount > 0)
125
+ parts.push(`${String(duplicateCount)} entry with a duplicate key`);
126
+ if (parts.length === 0)
127
+ return undefined;
128
+ return `${parts.join(' and ')} (not saved until fixed)`;
129
+ }
130
+ function newEntryValue(schema, root) {
131
+ const seeded = defaultValueForSchema(schema, root);
132
+ if (seeded !== undefined)
133
+ return seeded;
134
+ const classified = classifySchema(schema, root);
135
+ switch (classified.model.kind) {
136
+ case 'string':
137
+ return '';
138
+ case 'boolean':
139
+ return false;
140
+ default:
141
+ return undefined;
142
+ }
143
+ }
144
+ //# sourceMappingURL=record-field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-field.js","sourceRoot":"","sources":["../../src/schema-form/record-field.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/schema-form/validate.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE5D,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAmJD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,eAAe,GACxB,gBAAgB,CAIlB"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/schema-form/validate.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE5D,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AA6JD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,eAAe,GACxB,gBAAgB,CAIlB"}
@@ -136,6 +136,16 @@ function walk(schema, value, path, root, errors, maxUploadBytes) {
136
136
  }
137
137
  return;
138
138
  }
139
+ case 'record': {
140
+ if (!isPlainObject(value)) {
141
+ errors[path] = 'must be an object';
142
+ return;
143
+ }
144
+ for (const [key, entryValue] of Object.entries(value)) {
145
+ walk(model.values, entryValue, joinPath(path, key), root, errors, maxUploadBytes);
146
+ }
147
+ return;
148
+ }
139
149
  case 'union': {
140
150
  const index = activeVariantIndex(value, model.variants, model.discriminator, root);
141
151
  const variant = index === -1 ? undefined : model.variants[index];
@@ -1 +1 @@
1
- {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/schema-form/validate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/schema-form/validate.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tai42/studio-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "The Studio plugin contract, shared UI components, and hooks that every feature builds on.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "tai42 <oss@tai42.ai>",
@@ -89,7 +89,7 @@
89
89
  "@radix-ui/react-select": "^2.3.2",
90
90
  "@radix-ui/react-tabs": "^1.1.16",
91
91
  "@radix-ui/react-tooltip": "^1.2.11",
92
- "@tai42/api-client": "1.0.0"
92
+ "@tai42/api-client": "1.1.0"
93
93
  },
94
94
  "scripts": {
95
95
  "build": "tsc -b tsconfig.build.json && node scripts/copy-assets.mjs",
package/src/index.ts CHANGED
@@ -321,6 +321,8 @@ export type { IconProps, IconComponent } from './components/icons';
321
321
  // -- Schema-driven forms -----------------------------------------------------
322
322
  export {
323
323
  SchemaForm,
324
+ RecordEntryRendererContext,
325
+ SecretRefField,
324
326
  defaultValueForSchema,
325
327
  validateAgainstSchema,
326
328
  resolveRef,
@@ -328,6 +330,10 @@ export {
328
330
  export type {
329
331
  SchemaFormProps,
330
332
  CompletionProvider,
333
+ RecordEntryRenderer,
334
+ RecordEntryContext,
335
+ SecretRefFieldProps,
336
+ SecretRef,
331
337
  JsonSchema,
332
338
  JsonSchemaType,
333
339
  Discriminator,
@@ -0,0 +1,247 @@
1
+ /**
2
+ * `SecretRefField` — a data-agnostic masked editor for ONE secret value that is
3
+ * EITHER a reference to an existing env key OR a freshly pasted secret. It knows
4
+ * nothing about the `!ENV ${KEY}` marker syntax or any wire format: it emits a
5
+ * discriminated {@link SecretRef} and the host maps that to whatever it stores
6
+ * (the consumer that mounts this — the McpTab editor — writes an `!ENV ${KEY}`
7
+ * marker for a `key` ref and runs the combined store-then-mark op for a `paste`).
8
+ *
9
+ * WRITE-ONLY for a pasted secret: the plaintext lives only in local editor state
10
+ * until the user commits it, at which point it leaves the DOM entirely (held only
11
+ * in the emitted value the host holds) — the committed chip renders a fixed mask,
12
+ * never the secret, and offers NO reveal. A picked key's NAME is not itself the
13
+ * secret, so its chip is revealable on click.
14
+ *
15
+ * FAIL CLOSED: env-key picking is offered only when `keyPickingAvailable` is
16
+ * explicitly true (the host passes it from whether the projection carries the env
17
+ * route). Absent, the field is paste-only.
18
+ */
19
+ import { useEffect, useState } from 'react';
20
+ import type { ReactNode } from 'react';
21
+
22
+ import { Field } from '../components/field';
23
+ import { EyeIcon, EyeOffIcon } from '../components/icons';
24
+ import { TextInput } from '../components/inputs';
25
+ import { Button } from '../components/primitives';
26
+ import { Select } from '../components/select';
27
+
28
+ /**
29
+ * The value {@link SecretRefField} emits and reads back. The `source` discriminant
30
+ * is what the host branches on:
31
+ * - `key` — reference an existing env key; the host writes `!ENV ${key}`.
32
+ * - `paste` — a new plaintext secret; the host stores it under a generated key
33
+ * (combined op) and writes the resulting marker. `secret` is
34
+ * write-only and is never rendered.
35
+ */
36
+ export type SecretRef =
37
+ | { readonly source: 'key'; readonly key: string }
38
+ | { readonly source: 'paste'; readonly secret: string };
39
+
40
+ export interface SecretRefFieldProps {
41
+ /** The current value, or `undefined` when nothing has been provided yet. */
42
+ readonly value: SecretRef | undefined;
43
+ /** Fired once when a value is provided (a key picked, or a paste committed). */
44
+ readonly onChange: (value: SecretRef) => void;
45
+ /** Env key NAMES the host supplies (e.g. envConfig.secret_keys). Never fetched here. */
46
+ readonly availableKeys: readonly string[];
47
+ /**
48
+ * Whether env-key picking is available at all — the host passes it from whether
49
+ * the projection carries the env route. Omitted or false: paste-only (fail
50
+ * closed).
51
+ */
52
+ readonly keyPickingAvailable?: boolean;
53
+ /**
54
+ * When set, PASTING a new secret is blocked and this reason is surfaced inline
55
+ * (referencing an existing key stays available). The host passes it when a paste
56
+ * right now would land in the wrong place — an unsaved structural edit has shifted
57
+ * the target pointer, or the entry has no key yet to hint the generated name.
58
+ */
59
+ readonly pasteDisabledReason?: string;
60
+ /** Visible field label; the accessible name of the inner control. */
61
+ readonly label?: string;
62
+ readonly idPrefix?: string;
63
+ }
64
+
65
+ /** A fixed-width mask that never encodes the masked value's length. */
66
+ const MASK = '••••••••';
67
+
68
+ export function SecretRefField({
69
+ value,
70
+ onChange,
71
+ availableKeys,
72
+ keyPickingAvailable,
73
+ pasteDisabledReason,
74
+ label = 'Secret',
75
+ idPrefix = 'secret-ref',
76
+ }: SecretRefFieldProps): ReactNode {
77
+ const canPick = keyPickingAvailable === true;
78
+ const pasteBlocked = pasteDisabledReason !== undefined;
79
+
80
+ // Editing is forced while there is no value to show as a chip; otherwise the
81
+ // chip is shown until the user asks to replace it.
82
+ const [editing, setEditing] = useState(value === undefined);
83
+ const [mode, setMode] = useState<'key' | 'paste'>(
84
+ value?.source === 'key' && canPick ? 'key' : 'paste',
85
+ );
86
+ const [draftSecret, setDraftSecret] = useState('');
87
+ const [pickedKey, setPickedKey] = useState('');
88
+ const [revealed, setRevealed] = useState(false);
89
+
90
+ // A different committed value must never keep the prior reveal: revealing key B
91
+ // after B replaced A would name the wrong key. Reset on a STABLE signature of the
92
+ // value rather than its object identity, so a host that re-creates an equal
93
+ // `value` object each render never collapses an open reveal.
94
+ const valueSignature =
95
+ value === undefined ? '' : value.source === 'key' ? `key:${value.key}` : 'paste';
96
+ useEffect(() => {
97
+ setRevealed(false);
98
+ }, [valueSignature]);
99
+
100
+ const effectiveMode = canPick ? mode : 'paste';
101
+
102
+ const commitKey = (key: string): void => {
103
+ setPickedKey(key);
104
+ if (key === '') return;
105
+ onChange({ source: 'key', key });
106
+ setEditing(false);
107
+ };
108
+ const commitPaste = (): void => {
109
+ if (draftSecret === '' || pasteBlocked) return;
110
+ onChange({ source: 'paste', secret: draftSecret });
111
+ // The plaintext leaves the DOM: it now lives only in the emitted value.
112
+ setDraftSecret('');
113
+ setEditing(false);
114
+ };
115
+ const startEditing = (): void => {
116
+ setDraftSecret('');
117
+ setPickedKey('');
118
+ setRevealed(false);
119
+ setMode(value?.source === 'key' && canPick ? 'key' : 'paste');
120
+ setEditing(true);
121
+ };
122
+
123
+ if (!editing && value !== undefined) {
124
+ return (
125
+ <div data-testid={idPrefix}>
126
+ <SecretChip
127
+ value={value}
128
+ revealed={revealed}
129
+ onToggleReveal={() => {
130
+ setRevealed((prev) => !prev);
131
+ }}
132
+ />
133
+ <Button type="button" variant="secondary" onClick={startEditing}>
134
+ {value.source === 'key' ? 'Change reference' : 'Replace secret'}
135
+ </Button>
136
+ </div>
137
+ );
138
+ }
139
+
140
+ return (
141
+ <div data-testid={idPrefix}>
142
+ {canPick ? (
143
+ <div className="tai-row" role="group" aria-label={`${label} source`}>
144
+ <Button
145
+ type="button"
146
+ variant={effectiveMode === 'key' ? 'primary' : 'secondary'}
147
+ aria-pressed={effectiveMode === 'key'}
148
+ onClick={() => {
149
+ setMode('key');
150
+ }}
151
+ >
152
+ Reference existing key
153
+ </Button>
154
+ <Button
155
+ type="button"
156
+ variant={effectiveMode === 'paste' ? 'primary' : 'secondary'}
157
+ aria-pressed={effectiveMode === 'paste'}
158
+ onClick={() => {
159
+ setMode('paste');
160
+ }}
161
+ >
162
+ Paste new secret
163
+ </Button>
164
+ </div>
165
+ ) : null}
166
+
167
+ {effectiveMode === 'key' ? (
168
+ availableKeys.length === 0 ? (
169
+ <span className="tai-field-hint">No keys available</span>
170
+ ) : (
171
+ <Field label={label}>
172
+ <Select
173
+ value={pickedKey === '' ? undefined : pickedKey}
174
+ onValueChange={commitKey}
175
+ placeholder="Select a key"
176
+ options={availableKeys.map((key) => ({ value: key, label: key }))}
177
+ />
178
+ </Field>
179
+ )
180
+ ) : (
181
+ <div className="tai-row">
182
+ <div style={{ flex: 1 }}>
183
+ <Field label={label} error={pasteDisabledReason}>
184
+ {/* type=password: the plaintext is masked and offers no reveal —
185
+ it is write-only. */}
186
+ <TextInput
187
+ type="password"
188
+ value={draftSecret}
189
+ onChange={(event) => {
190
+ setDraftSecret(event.target.value);
191
+ }}
192
+ autoComplete="off"
193
+ spellCheck={false}
194
+ placeholder="Paste a secret value"
195
+ />
196
+ </Field>
197
+ </div>
198
+ <Button
199
+ type="button"
200
+ variant="primary"
201
+ disabled={draftSecret === '' || pasteBlocked}
202
+ onClick={commitPaste}
203
+ >
204
+ Use secret
205
+ </Button>
206
+ </div>
207
+ )}
208
+ </div>
209
+ );
210
+ }
211
+
212
+ /**
213
+ * The committed masked chip. A `key` ref names the key, revealable on click; a
214
+ * `paste` secret shows only that a new secret is staged — the plaintext is never
215
+ * a child of this tree.
216
+ */
217
+ function SecretChip({
218
+ value,
219
+ revealed,
220
+ onToggleReveal,
221
+ }: {
222
+ value: SecretRef;
223
+ revealed: boolean;
224
+ onToggleReveal: () => void;
225
+ }): ReactNode {
226
+ if (value.source === 'paste') {
227
+ return (
228
+ <span className="tai-chip tai-chip-static">
229
+ New secret <span aria-hidden="true">{MASK}</span>
230
+ </span>
231
+ );
232
+ }
233
+ const toggleLabel = revealed ? 'Hide value' : 'Show value';
234
+ return (
235
+ <span className="tai-chip tai-chip-static">
236
+ <span>{revealed ? value.key : MASK}</span>
237
+ <button
238
+ type="button"
239
+ className="tai-icon-btn"
240
+ aria-label={toggleLabel}
241
+ onClick={onToggleReveal}
242
+ >
243
+ {revealed ? <EyeOffIcon /> : <EyeIcon />}
244
+ </button>
245
+ </span>
246
+ );
247
+ }
@@ -66,6 +66,14 @@ export type FieldModel =
66
66
  readonly properties: readonly (readonly [string, JsonSchema])[];
67
67
  readonly required: ReadonlySet<string>;
68
68
  }
69
+ | {
70
+ // A string-keyed map typed by `additionalProperties` (no fixed
71
+ // `properties`), edited as key/value rows. `values` is the entry value
72
+ // schema AS WRITTEN — the entry editor resolves any `$ref` itself, exactly
73
+ // as `array.items` is.
74
+ readonly kind: 'record';
75
+ readonly values: JsonSchema;
76
+ }
69
77
  | {
70
78
  readonly kind: 'union';
71
79
  readonly variants: readonly UnionVariant[];
@@ -279,6 +287,23 @@ export function classifySchema(raw: JsonSchema, root: JsonSchema): ClassifiedFie
279
287
  }
280
288
  case 'object': {
281
289
  if (resolved.properties === undefined) {
290
+ // A free-form object typed by a VALUE SCHEMA (`additionalProperties` is a
291
+ // schema, not `false`/`true`/absent) is a string→X map — renderable as
292
+ // key/value rows. A boolean `additionalProperties` carries no value type
293
+ // to build an entry editor from, so it stays unsupported.
294
+ // Read as `unknown`: the value is parsed JSON, where `additionalProperties:
295
+ // null` is possible and `typeof null === 'object'` would otherwise classify
296
+ // it as a record with a `null` value schema.
297
+ const additional: unknown = resolved.additionalProperties;
298
+ if (typeof additional === 'object' && additional !== null) {
299
+ return {
300
+ model: { kind: 'record', values: additional as JsonSchema },
301
+ nullable: nullableByType,
302
+ schema: resolved,
303
+ title,
304
+ description,
305
+ };
306
+ }
282
307
  return unsupported(
283
308
  resolved,
284
309
  'free-form object (additionalProperties map) is not supported',
@@ -4,9 +4,11 @@
4
4
  * tree reads these here rather than every intermediate node drilling the prop.
5
5
  */
6
6
  import { createContext } from 'react';
7
+ import type { ReactNode } from 'react';
7
8
 
8
9
  import { DEFAULT_MAX_UPLOAD_BYTES } from './media';
9
10
  import type { CompletionProvider } from './SchemaForm';
11
+ import type { JsonSchema } from './types';
10
12
 
11
13
  // Ambient completion provider for the whole form. A string field deep in the
12
14
  // tree reads it here rather than every intermediate node drilling the prop.
@@ -15,3 +17,30 @@ export const CompletionProviderContext = createContext<CompletionProvider | unde
15
17
  // Ambient default upload cap for the whole form; a media field reads it here so
16
18
  // intermediate object/array nodes never drill the prop.
17
19
  export const MaxUploadBytesContext = createContext<number>(DEFAULT_MAX_UPLOAD_BYTES);
20
+
21
+ /** One record entry, handed to a host-supplied value renderer. */
22
+ export interface RecordEntryContext {
23
+ /** The entry's current key (may be blank/duplicate while the user edits). */
24
+ readonly keyName: string;
25
+ /** The entry value's form path (dotted from the form root). */
26
+ readonly path: string;
27
+ /** The entry value schema, as written (the renderer resolves any `$ref`). */
28
+ readonly valueSchema: JsonSchema;
29
+ readonly value: unknown;
30
+ readonly onChange: (value: unknown) => void;
31
+ /** The default value editor the record field would render for this entry. */
32
+ readonly defaultField: ReactNode;
33
+ }
34
+
35
+ /**
36
+ * A host-supplied per-entry value renderer for `record` fields — the injection
37
+ * point a masked/secret editor (or any other value affordance) mounts through.
38
+ * It returns the node to render in the entry's value slot; returning
39
+ * `entry.defaultField` falls back to the built-in editor. Absent (the default),
40
+ * every entry renders its `defaultField`.
41
+ */
42
+ export type RecordEntryRenderer = (entry: RecordEntryContext) => ReactNode;
43
+
44
+ // Ambient record-entry value renderer for the whole form; the record field reads
45
+ // it here so intermediate object/array/record nodes never drill the prop.
46
+ export const RecordEntryRendererContext = createContext<RecordEntryRenderer | undefined>(undefined);
@@ -54,6 +54,8 @@ export function defaultValueForSchema(schema: JsonSchema, root: JsonSchema = sch
54
54
  }
55
55
  case 'array':
56
56
  return [];
57
+ case 'record':
58
+ return {};
57
59
  case 'enum':
58
60
  case 'string':
59
61
  case 'number':