@sanity/assist 1.2.16 → 2.0.1

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 (52) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +551 -30
  3. package/dist/index.cjs.mjs +1 -0
  4. package/dist/index.d.ts +333 -9
  5. package/dist/index.esm.js +2463 -390
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/index.js +2457 -383
  8. package/dist/index.js.map +1 -1
  9. package/package.json +12 -11
  10. package/src/_lib/form/DocumentForm.tsx +2 -1
  11. package/src/_lib/form/constants.ts +1 -0
  12. package/src/assistDocument/AssistDocumentInput.tsx +24 -4
  13. package/src/assistDocument/RequestRunInstructionProvider.tsx +37 -21
  14. package/src/assistDocument/components/AssistDocumentForm.tsx +65 -21
  15. package/src/assistDocument/components/instruction/InstructionInput.tsx +5 -4
  16. package/src/assistDocument/components/instruction/InstructionOutputField.tsx +45 -0
  17. package/src/assistDocument/components/instruction/InstructionOutputInput.tsx +205 -0
  18. package/src/assistDocument/hooks/useStudioAssistDocument.ts +5 -32
  19. package/src/assistFormComponents/AssistField.tsx +11 -5
  20. package/src/assistFormComponents/AssistFormBlock.tsx +2 -3
  21. package/src/assistFormComponents/validation/listItem.tsx +2 -2
  22. package/src/assistInspector/AssistInspector.tsx +6 -0
  23. package/src/assistInspector/FieldAutocomplete.tsx +1 -0
  24. package/src/assistInspector/helpers.ts +9 -11
  25. package/src/assistLayout/AssistLayout.tsx +9 -9
  26. package/src/components/ImageContext.tsx +30 -13
  27. package/src/components/SafeValueInput.tsx +4 -1
  28. package/src/fieldActions/assistFieldActions.tsx +42 -13
  29. package/src/fieldActions/generateCaptionActions.tsx +17 -6
  30. package/src/fieldActions/generateImageActions.tsx +57 -0
  31. package/src/helpers/assistSupported.ts +10 -16
  32. package/src/helpers/conditionalMembers.test.ts +200 -0
  33. package/src/helpers/conditionalMembers.ts +127 -0
  34. package/src/helpers/misc.ts +8 -4
  35. package/src/helpers/typeUtils.ts +19 -5
  36. package/src/index.ts +3 -0
  37. package/src/plugin.tsx +18 -4
  38. package/src/schemas/assistDocumentSchema.tsx +40 -1
  39. package/src/schemas/serialize/serializeSchema.test.ts +239 -8
  40. package/src/schemas/serialize/serializeSchema.ts +77 -10
  41. package/src/schemas/typeDefExtensions.ts +89 -5
  42. package/src/translate/FieldTranslationProvider.tsx +360 -0
  43. package/src/translate/getLanguageParams.ts +26 -0
  44. package/src/translate/languageStore.ts +18 -0
  45. package/src/translate/paths.test.ts +133 -0
  46. package/src/translate/paths.ts +175 -0
  47. package/src/translate/translateActions.tsx +188 -0
  48. package/src/translate/types.ts +160 -0
  49. package/src/types.ts +67 -15
  50. package/src/useApiClient.ts +134 -2
  51. package/src/assistLayout/AlphaMigration.tsx +0 -310
  52. package/src/legacy-types.ts +0 -72
@@ -1,41 +1,34 @@
1
- import {SchemaType} from 'sanity'
1
+ import {ReferenceOptions, SchemaType} from 'sanity'
2
2
  import {AssistOptions} from '../schemas/typeDefExtensions'
3
3
  import {isType} from './typeUtils'
4
4
 
5
5
  export function isSchemaAssistEnabled(type: SchemaType) {
6
- return !(type.options as AssistOptions | undefined)?.aiWritingAssistance?.exclude
6
+ return !(type.options as AssistOptions | undefined)?.aiAssist?.exclude
7
7
  }
8
8
 
9
- export function isAssistSupported(type: SchemaType, allowReadonlyHidden = false) {
9
+ export function isAssistSupported(type: SchemaType) {
10
10
  if (!isSchemaAssistEnabled(type)) {
11
11
  return false
12
12
  }
13
13
 
14
- if (isDisabled(type, allowReadonlyHidden)) {
14
+ if (isDisabled(type)) {
15
15
  return false
16
16
  }
17
17
 
18
18
  if (type.jsonType === 'array') {
19
- const unsupportedArray = type.of.every((t) => isDisabled(t, allowReadonlyHidden))
19
+ const unsupportedArray = type.of.every((t) => isDisabled(t))
20
20
  return !unsupportedArray
21
21
  }
22
22
 
23
23
  if (type.jsonType === 'object') {
24
- const unsupportedObject = type.fields.every((field) =>
25
- isDisabled(field.type, allowReadonlyHidden)
26
- )
24
+ const unsupportedObject = type.fields.every((field) => isDisabled(field.type))
27
25
  return !unsupportedObject
28
26
  }
29
27
  return true
30
28
  }
31
29
 
32
- function isDisabled(type: SchemaType, allowReadonlyHidden: boolean) {
33
- const readonlyHidden = !!type.readOnly || !!type.hidden
34
- return (
35
- !isSchemaAssistEnabled(type) ||
36
- isUnsupportedType(type) ||
37
- (!allowReadonlyHidden && readonlyHidden)
38
- )
30
+ function isDisabled(type: SchemaType) {
31
+ return !isSchemaAssistEnabled(type) || isUnsupportedType(type)
39
32
  }
40
33
 
41
34
  function isUnsupportedType(type: SchemaType) {
@@ -43,7 +36,8 @@ function isUnsupportedType(type: SchemaType) {
43
36
  type.jsonType === 'number' ||
44
37
  type.name === 'sanity.imageCrop' ||
45
38
  type.name === 'sanity.imageHotspot' ||
46
- isType(type, 'reference') ||
39
+ (isType(type, 'reference') &&
40
+ !(type?.options as ReferenceOptions)?.aiAssist?.embeddingsIndex) ||
47
41
  isType(type, 'crossDatasetReference') ||
48
42
  isType(type, 'slug') ||
49
43
  isType(type, 'url') ||
@@ -0,0 +1,200 @@
1
+ import {describe, expect, test} from 'vitest'
2
+ import {Schema} from '@sanity/schema'
3
+ import {ArraySchemaType, defineField, defineType, ObjectSchemaType} from 'sanity'
4
+ import {getConditionalMembers} from './conditionalMembers'
5
+
6
+ describe('conditionalMembers', () => {
7
+ test('should not include paths without conditional hidden/readonly', () => {
8
+ const docSchema: ObjectSchemaType = Schema.compile({
9
+ name: 'test',
10
+ types: [
11
+ defineType({
12
+ type: 'document',
13
+ name: 'article',
14
+ fields: [{type: 'string', name: 'title'}],
15
+ }),
16
+ ],
17
+ }).get('article')
18
+
19
+ const docState = {
20
+ path: [],
21
+ schemaType: docSchema,
22
+ members: [
23
+ {
24
+ kind: 'field',
25
+ field: {path: [docSchema.fields[0].name], schemaType: docSchema.fields[0].type},
26
+ },
27
+ ],
28
+ } as any
29
+ const conditionalMembers = getConditionalMembers(docState)
30
+
31
+ expect(conditionalMembers).toEqual([])
32
+ })
33
+
34
+ test('should include path with conditional readonly', () => {
35
+ const docSchema: ObjectSchemaType = Schema.compile({
36
+ name: 'test',
37
+ types: [
38
+ defineType({
39
+ type: 'document',
40
+ name: 'article',
41
+ fields: [{type: 'string', name: 'title', readOnly: () => false}],
42
+ }),
43
+ ],
44
+ }).get('article')
45
+
46
+ const docState = {
47
+ path: [],
48
+ schemaType: docSchema,
49
+ members: [
50
+ {
51
+ kind: 'field',
52
+ field: {path: [docSchema.fields[0].name], schemaType: docSchema.fields[0].type},
53
+ },
54
+ ],
55
+ } as any
56
+ const conditionalMembers = getConditionalMembers(docState)
57
+
58
+ expect(conditionalMembers).toEqual([{path: 'title', hidden: false, readOnly: false}])
59
+ })
60
+
61
+ test('should include array item path with conditional readonly', () => {
62
+ const docSchema: ObjectSchemaType = Schema.compile({
63
+ name: 'test',
64
+ types: [
65
+ defineType({
66
+ type: 'document',
67
+ name: 'article',
68
+ fields: [{type: 'array', name: 'array', of: [{type: 'string', readOnly: () => true}]}],
69
+ }),
70
+ ],
71
+ }).get('article')
72
+
73
+ const docState = {
74
+ path: [],
75
+ schemaType: docSchema,
76
+ members: [
77
+ {
78
+ kind: 'field',
79
+ field: {
80
+ path: [docSchema.fields[0].name],
81
+ schemaType: docSchema.fields[0].type,
82
+ members: [
83
+ {
84
+ kind: 'item',
85
+ item: {
86
+ path: [docSchema.fields[0].name, 0],
87
+ schemaType: (docSchema.fields[0].type as ArraySchemaType).of[0],
88
+ readOnly: true,
89
+ },
90
+ },
91
+ ],
92
+ },
93
+ },
94
+ ],
95
+ } as any
96
+ const conditionalMembers = getConditionalMembers(docState)
97
+
98
+ expect(conditionalMembers).toEqual([
99
+ {
100
+ path: 'array[0]',
101
+ hidden: false,
102
+ readOnly: true,
103
+ },
104
+ ])
105
+ })
106
+
107
+ test('should include object path with conditional hidden', () => {
108
+ const docSchema: ObjectSchemaType = Schema.compile({
109
+ name: 'test',
110
+ types: [
111
+ defineType({
112
+ type: 'document',
113
+ name: 'article',
114
+ fields: [
115
+ defineField({
116
+ type: 'object',
117
+ name: 'object',
118
+ fields: [{type: 'string', name: 'title', hidden: () => false}],
119
+ }),
120
+ ],
121
+ }),
122
+ ],
123
+ }).get('article')
124
+
125
+ const docState = {
126
+ path: [],
127
+ schemaType: docSchema,
128
+ members: [
129
+ {
130
+ kind: 'field',
131
+ field: {
132
+ path: [docSchema.fields[0].name],
133
+ schemaType: docSchema.fields[0].type,
134
+ members: [
135
+ {
136
+ kind: 'field',
137
+ field: {
138
+ path: [docSchema.fields[0].name, 'title'],
139
+ schemaType: (docSchema.fields[0].type as ObjectSchemaType).fields[0].type,
140
+ },
141
+ },
142
+ ],
143
+ },
144
+ },
145
+ ],
146
+ } as any
147
+ const conditionalMembers = getConditionalMembers(docState)
148
+
149
+ expect(conditionalMembers).toEqual([
150
+ {
151
+ path: 'object.title',
152
+ hidden: false,
153
+ readOnly: false,
154
+ },
155
+ ])
156
+ })
157
+
158
+ test('should include path with fieldset with conditional state', () => {
159
+ const docSchema: ObjectSchemaType = Schema.compile({
160
+ name: 'test',
161
+ types: [
162
+ defineType({
163
+ type: 'document',
164
+ name: 'article',
165
+ fieldsets: [{name: 'set', hidden: () => false}],
166
+ fields: [{type: 'string', fieldset: 'set', name: 'title'}],
167
+ }),
168
+ ],
169
+ }).get('article')
170
+
171
+ const docState = {
172
+ path: [],
173
+ schemaType: docSchema,
174
+ members: [
175
+ {
176
+ kind: 'fieldSet',
177
+ fieldSet: {
178
+ name: 'set',
179
+ path: ['set'],
180
+ members: [
181
+ {
182
+ kind: 'field',
183
+ field: {path: [docSchema.fields[0].name], schemaType: docSchema.fields[0].type},
184
+ },
185
+ ],
186
+ },
187
+ },
188
+ ],
189
+ } as any
190
+ const conditionalMembers = getConditionalMembers(docState)
191
+
192
+ expect(conditionalMembers).toEqual([
193
+ {
194
+ path: 'title',
195
+ hidden: false,
196
+ readOnly: false,
197
+ },
198
+ ])
199
+ })
200
+ })
@@ -0,0 +1,127 @@
1
+ /* eslint-disable max-depth */
2
+ import {
3
+ ArrayOfObjectsFormNode,
4
+ ArrayOfObjectsItemMember,
5
+ ArrayOfPrimitivesFormNode,
6
+ DocumentFormNode,
7
+ FieldsetState,
8
+ isObjectSchemaType,
9
+ ObjectFormNode,
10
+ Path,
11
+ pathToString,
12
+ SchemaType,
13
+ } from 'sanity'
14
+
15
+ const MAX_DEPTH = 8
16
+
17
+ export interface ConditionalMemberState {
18
+ path: string
19
+ hidden: boolean
20
+ readOnly: boolean
21
+ }
22
+
23
+ interface ConditionalMemberInnerState extends ConditionalMemberState {
24
+ conditional: boolean
25
+ }
26
+
27
+ /**
28
+ * This is used to statically determine the state of the functions on the server-side.
29
+ * Paths which has a schema with conditional config should be considered hidden: true and/or readOnly: true
30
+ * Only conditional paths are included, as static props can be determined from schema.
31
+ *
32
+ * Returns paths that has conditional hidden or readOnly schema config (function) and that.
33
+ * Form-state does not contain hidden members.
34
+ *
35
+ * Note:
36
+ * * If a parent path is hidden, no child paths are included
37
+ * * If a parent path is readOnly, no child paths are included
38
+ * * If a path is hidden, it is not included; only conditionally visible paths will be returned, with hidden: false
39
+ */
40
+ export function getConditionalMembers(docState: DocumentFormNode): ConditionalMemberState[] {
41
+ const doc: ConditionalMemberInnerState = {
42
+ path: '',
43
+ hidden: false,
44
+ readOnly: !!docState.readOnly,
45
+ conditional: typeof docState.schemaType.hidden === 'function',
46
+ }
47
+ return [doc, ...extractConditionalPaths(docState, MAX_DEPTH)]
48
+ .filter((v) => v.conditional)
49
+ .map(({conditional, ...state}) => ({...state}))
50
+ }
51
+
52
+ function isConditional(schemaType: SchemaType) {
53
+ return typeof schemaType.hidden === 'function' || typeof schemaType.readOnly === 'function'
54
+ }
55
+
56
+ function conditionalState(memberState: {
57
+ path: Path
58
+ schemaType: SchemaType
59
+ readOnly?: boolean
60
+ }): ConditionalMemberInnerState {
61
+ return {
62
+ path: pathToString(memberState.path),
63
+ readOnly: !!memberState.readOnly,
64
+ hidden: false, // if its in members, its not hidden
65
+ conditional: isConditional(memberState.schemaType),
66
+ }
67
+ }
68
+
69
+ function extractConditionalPaths(
70
+ node: ObjectFormNode | FieldsetState,
71
+ maxDepth: number
72
+ ): ConditionalMemberInnerState[] {
73
+ if (node.path.length >= maxDepth) {
74
+ return []
75
+ }
76
+
77
+ return node.members.reduce<ConditionalMemberInnerState[]>((acc, member) => {
78
+ if (member.kind === 'error') {
79
+ return acc
80
+ }
81
+ if (member.kind === 'field') {
82
+ const schemaType = member.field.schemaType
83
+ if (schemaType.jsonType === 'object') {
84
+ const innerFields = member.field.readOnly
85
+ ? []
86
+ : extractConditionalPaths(member.field as ObjectFormNode, maxDepth)
87
+ return [...acc, conditionalState(member.field), ...innerFields]
88
+ } else if (schemaType.jsonType === 'array') {
89
+ const array = member.field as ArrayOfObjectsFormNode | ArrayOfPrimitivesFormNode
90
+
91
+ let arrayPaths: ConditionalMemberInnerState[] = []
92
+ const isObjectsArray = array.members.some(
93
+ (m) => m.kind === 'item' && isObjectSchemaType(m.item.schemaType)
94
+ )
95
+ if (!array.readOnly) {
96
+ for (const arrayMember of array.members) {
97
+ if (arrayMember.kind === 'error') {
98
+ continue
99
+ }
100
+
101
+ const innerFields =
102
+ isObjectsArray && !arrayMember.item.readOnly
103
+ ? extractConditionalPaths((arrayMember as ArrayOfObjectsItemMember).item, maxDepth)
104
+ : []
105
+
106
+ arrayPaths = [...arrayPaths, conditionalState(arrayMember.item), ...innerFields]
107
+ }
108
+ }
109
+ return [...acc, conditionalState(array), ...arrayPaths]
110
+ }
111
+
112
+ return [...acc, conditionalState(member.field)]
113
+ } else if (member.kind === 'fieldSet') {
114
+ const conditionalFieldset = !!(node as ObjectFormNode).schemaType?.fieldsets?.some(
115
+ (f) => !f.single && f.name === member.fieldSet.name && typeof f.hidden === 'function'
116
+ )
117
+ const innerFields = extractConditionalPaths(member.fieldSet, maxDepth).map((f) => ({
118
+ ...f,
119
+ // if fieldset is conditional, visible fields must also be considered conditional
120
+ conditional: conditionalFieldset ?? f.conditional,
121
+ }))
122
+ return [...acc, ...innerFields]
123
+ }
124
+
125
+ return acc
126
+ }, [])
127
+ }
@@ -4,13 +4,17 @@ import {useMemo} from 'react'
4
4
 
5
5
  export function usePathKey(path: Path | string) {
6
6
  return useMemo(() => {
7
- if (path.length) {
8
- return Array.isArray(path) ? pathToString(path) : path
9
- }
10
- return documentRootKey
7
+ return getPathKey(path)
11
8
  }, [path])
12
9
  }
13
10
 
11
+ export function getPathKey(path: Path | string) {
12
+ if (path.length) {
13
+ return Array.isArray(path) ? pathToString(path) : path
14
+ }
15
+ return documentRootKey
16
+ }
17
+
14
18
  export function getInstructionTitle(instruction?: StudioInstruction) {
15
19
  return instruction?.title ?? 'Untitled'
16
20
  }
@@ -18,13 +18,27 @@ export function isImage(schemaType: SchemaType) {
18
18
  return isType(schemaType, 'image')
19
19
  }
20
20
 
21
- export function getCaptionFieldOption(schemaType: SchemaType | undefined): string | undefined {
21
+ export function getDescriptionFieldOption(schemaType: SchemaType | undefined): string | undefined {
22
22
  if (!schemaType) {
23
23
  return undefined
24
24
  }
25
- const captionField = (schemaType.options as ImageOptions)?.captionField
26
- if (captionField) {
27
- return captionField
25
+ const descriptionField = (schemaType.options as ImageOptions)?.aiAssist?.imageDescriptionField
26
+ if (descriptionField) {
27
+ return descriptionField
28
28
  }
29
- return getCaptionFieldOption(schemaType.type)
29
+ return getDescriptionFieldOption(schemaType.type)
30
+ }
31
+
32
+ export function getImageInstructionFieldOption(
33
+ schemaType: SchemaType | undefined
34
+ ): string | undefined {
35
+ if (!schemaType) {
36
+ return undefined
37
+ }
38
+ const imageInstructionField = (schemaType.options as ImageOptions)?.aiAssist
39
+ ?.imageInstructionField
40
+ if (imageInstructionField) {
41
+ return imageInstructionField
42
+ }
43
+ return getImageInstructionFieldOption(schemaType.type)
30
44
  }
package/src/index.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  export * from './schemas/typeDefExtensions'
2
2
  export * from './schemas/serialize/SchemTypeTool'
3
3
 
4
+ export {defaultLanguageOutputs} from './translate/paths'
5
+ export * from './translate/types'
6
+
4
7
  export {contextDocumentTypeName} from './types'
5
8
 
6
9
  export {assist} from './plugin'
package/src/plugin.tsx CHANGED
@@ -15,17 +15,21 @@ import {createAssistDocumentPresence} from './presence/AssistDocumentPresence'
15
15
  import {isSchemaAssistEnabled} from './helpers/assistSupported'
16
16
  import {isImage} from './helpers/typeUtils'
17
17
  import {ImageContextProvider} from './components/ImageContext'
18
+ import {TranslationConfig} from './translate/types'
19
+ import {assistDocumentTypeName, AssistPreset} from './types'
18
20
 
19
21
  export interface AssistPluginConfig {
22
+ translate?: TranslationConfig
23
+
20
24
  /**
21
- * Set this to false to disable model migration from the alpha version of this plugin
25
+ * @internal
22
26
  */
23
- alphaMigration?: boolean
27
+ __customApiClient?: (defaultClient: SanityClient) => SanityClient
24
28
 
25
29
  /**
26
30
  * @internal
27
31
  */
28
- __customApiClient?: (defaultClient: SanityClient) => SanityClient
32
+ __presets?: Record<string, AssistPreset>
29
33
  }
30
34
 
31
35
  export const assist = definePlugin<AssistPluginConfig | void>((config) => {
@@ -36,16 +40,23 @@ export const assist = definePlugin<AssistPluginConfig | void>((config) => {
36
40
  schema: {
37
41
  types: schemaTypes,
38
42
  },
43
+ i18n: {
44
+ bundles: [{}],
45
+ },
39
46
 
40
47
  document: {
41
48
  inspectors: (prev, context) => {
42
- const docSchema = context.schema.get(context.documentType)
49
+ const documentType = context.documentType
50
+ const docSchema = context.schema.get(documentType)
43
51
  if (docSchema && isSchemaAssistEnabled(docSchema)) {
44
52
  return [...prev, assistInspector]
45
53
  }
46
54
  return prev
47
55
  },
48
56
  unstable_fieldActions: (prev, {documentType, schema}) => {
57
+ if (documentType === assistDocumentTypeName) {
58
+ return []
59
+ }
49
60
  const docSchema = schema.get(documentType)
50
61
  if (docSchema && isSchemaAssistEnabled(docSchema)) {
51
62
  return [...prev, assistFieldActions]
@@ -53,6 +64,9 @@ export const assist = definePlugin<AssistPluginConfig | void>((config) => {
53
64
  return prev
54
65
  },
55
66
  unstable_languageFilter: (prev, {documentId, schema, schemaType}) => {
67
+ if (schemaType === assistDocumentTypeName) {
68
+ return []
69
+ }
56
70
  const docSchema = schema.get(schemaType)
57
71
  if (docSchema && isObjectSchemaType(docSchema) && isSchemaAssistEnabled(docSchema)) {
58
72
  return [...prev, createAssistDocumentPresence(documentId, docSchema)]
@@ -18,6 +18,8 @@ import {
18
18
  instructionContextTypeName,
19
19
  instructionTaskTypeName,
20
20
  instructionTypeName,
21
+ outputFieldTypeName,
22
+ outputTypeTypeName,
21
23
  promptTypeName,
22
24
  userInputTypeName,
23
25
  } from '../types'
@@ -37,11 +39,13 @@ import {PromptInput} from '../assistDocument/components/instruction/PromptInput'
37
39
  import {instructionGuideUrl} from '../constants'
38
40
  import {InstructionsArrayField} from '../assistDocument/components/InstructionsArrayField'
39
41
  import {getFieldRefsWithDocument} from '../assistInspector/helpers'
42
+ import {InstructionOutputField} from '../assistDocument/components/instruction/InstructionOutputField'
43
+ import {InstructionOutputInput} from '../assistDocument/components/instruction/InstructionOutputInput'
40
44
 
41
45
  export const fieldReference = defineType({
42
46
  type: 'object',
43
47
  name: fieldReferenceTypeName,
44
- title: 'Document field',
48
+ title: 'Field',
45
49
  icon: ThListIcon,
46
50
 
47
51
  fields: [
@@ -329,6 +333,41 @@ export const instruction = defineType({
329
333
  return context.currentUser?.id ?? ''
330
334
  },
331
335
  }),
336
+ defineField({
337
+ type: 'array',
338
+ name: 'output',
339
+ title: 'Output filter',
340
+ components: {
341
+ input: InstructionOutputInput,
342
+ field: InstructionOutputField,
343
+ },
344
+ of: [
345
+ defineArrayMember({
346
+ type: 'object' as const,
347
+ name: outputFieldTypeName,
348
+ title: 'Output field',
349
+ fields: [
350
+ {
351
+ type: 'string',
352
+ name: 'path',
353
+ title: 'Path',
354
+ },
355
+ ],
356
+ }),
357
+ defineArrayMember({
358
+ type: 'object' as const,
359
+ name: outputTypeTypeName,
360
+ title: 'Output type',
361
+ fields: [
362
+ {
363
+ type: 'string',
364
+ name: 'type',
365
+ title: 'Type',
366
+ },
367
+ ],
368
+ }),
369
+ ],
370
+ }),
332
371
  ],
333
372
  })
334
373