@sanity/assist 1.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.
Files changed (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +205 -0
  3. package/dist/index.d.ts +52 -0
  4. package/dist/index.esm.js +2341 -0
  5. package/dist/index.esm.js.map +1 -0
  6. package/dist/index.js +2341 -0
  7. package/dist/index.js.map +1 -0
  8. package/package.json +98 -0
  9. package/sanity.json +8 -0
  10. package/src/_lib/connector/ConnectFromRegion.tsx +24 -0
  11. package/src/_lib/connector/ConnectToRegion.tsx +22 -0
  12. package/src/_lib/connector/ConnectorRegion.tsx +23 -0
  13. package/src/_lib/connector/ConnectorsProvider.tsx +19 -0
  14. package/src/_lib/connector/ConnectorsStore.ts +122 -0
  15. package/src/_lib/connector/ConnectorsStoreContext.ts +4 -0
  16. package/src/_lib/connector/helpers.ts +5 -0
  17. package/src/_lib/connector/index.ts +9 -0
  18. package/src/_lib/connector/mapConnectorToLine.ts +83 -0
  19. package/src/_lib/connector/types.ts +56 -0
  20. package/src/_lib/connector/useConnectorsStore.ts +13 -0
  21. package/src/_lib/connector/useRegionRects.ts +141 -0
  22. package/src/_lib/fixedListenQuery.ts +101 -0
  23. package/src/_lib/form/DocumentForm.tsx +197 -0
  24. package/src/_lib/form/helpers.ts +31 -0
  25. package/src/_lib/form/index.ts +1 -0
  26. package/src/_lib/randomKey.ts +29 -0
  27. package/src/_lib/useListeningQuery.ts +61 -0
  28. package/src/_lib/usePrevious.ts +9 -0
  29. package/src/assistConnectors/AssistConnectorsOverlay.tsx +132 -0
  30. package/src/assistConnectors/ConnectorPath.tsx +62 -0
  31. package/src/assistConnectors/draw/arrowPath.ts +9 -0
  32. package/src/assistConnectors/draw/connectorPath.ts +142 -0
  33. package/src/assistConnectors/index.ts +1 -0
  34. package/src/assistDocument/AssistDocumentContext.tsx +31 -0
  35. package/src/assistDocument/AssistDocumentContextProvider.tsx +17 -0
  36. package/src/assistDocument/AssistDocumentInput.tsx +46 -0
  37. package/src/assistDocument/RequestRunInstructionProvider.tsx +50 -0
  38. package/src/assistDocument/components/AssistDocumentForm.tsx +188 -0
  39. package/src/assistDocument/components/FieldRefPreview.tsx +27 -0
  40. package/src/assistDocument/components/InstructionsArrayField.tsx +8 -0
  41. package/src/assistDocument/components/InstructionsArrayInput.tsx +26 -0
  42. package/src/assistDocument/components/SelectedFieldContext.tsx +10 -0
  43. package/src/assistDocument/components/generic/HiddenFieldTitle.tsx +5 -0
  44. package/src/assistDocument/components/helpers.ts +21 -0
  45. package/src/assistDocument/components/instruction/BackToInstructionsLink.tsx +31 -0
  46. package/src/assistDocument/components/instruction/FieldRefInput.tsx +33 -0
  47. package/src/assistDocument/components/instruction/InstructionInput.tsx +87 -0
  48. package/src/assistDocument/components/instruction/PromptInput.tsx +52 -0
  49. package/src/assistDocument/components/instruction/appearance/IconInput.tsx +46 -0
  50. package/src/assistDocument/components/instruction/appearance/InstructionVisibility.tsx +37 -0
  51. package/src/assistDocument/hooks/useAssistDocumentContextValue.tsx +68 -0
  52. package/src/assistDocument/hooks/useDocumentState.ts +6 -0
  53. package/src/assistDocument/hooks/useInstructionToaster.tsx +74 -0
  54. package/src/assistDocument/hooks/useStudioAssistDocument.ts +119 -0
  55. package/src/assistDocument/index.ts +1 -0
  56. package/src/assistFormComponents/AssistField.tsx +51 -0
  57. package/src/assistFormComponents/AssistFormBlock.tsx +31 -0
  58. package/src/assistFormComponents/AssistInlineFormBlock.tsx +14 -0
  59. package/src/assistFormComponents/AssistItem.tsx +20 -0
  60. package/src/assistFormComponents/validation/listItem.tsx +63 -0
  61. package/src/assistFormComponents/validation/validationList.tsx +89 -0
  62. package/src/assistInspector/AssistInspector.tsx +379 -0
  63. package/src/assistInspector/FieldAutocomplete.tsx +119 -0
  64. package/src/assistInspector/InstructionTaskHistoryButton.tsx +261 -0
  65. package/src/assistInspector/constants.ts +1 -0
  66. package/src/assistInspector/helpers.ts +125 -0
  67. package/src/assistInspector/index.ts +26 -0
  68. package/src/assistLayout/AiAssistanceConfigContext.tsx +81 -0
  69. package/src/assistLayout/AlphaMigration.tsx +311 -0
  70. package/src/assistLayout/AssistLayout.tsx +38 -0
  71. package/src/assistLayout/RunInstructionProvider.tsx +222 -0
  72. package/src/components/AssistFeatureBadge.tsx +9 -0
  73. package/src/components/Delay.tsx +25 -0
  74. package/src/components/HideReferenceChangedBannerInput.tsx +25 -0
  75. package/src/components/SafeValueInput.tsx +73 -0
  76. package/src/components/TimeAgo.tsx +18 -0
  77. package/src/constants.ts +20 -0
  78. package/src/fieldActions/PrivateIcon.tsx +20 -0
  79. package/src/fieldActions/assistFieldActions.tsx +230 -0
  80. package/src/globals.d.ts +4 -0
  81. package/src/helpers/assistSupported.ts +44 -0
  82. package/src/helpers/ids.ts +19 -0
  83. package/src/helpers/misc.ts +16 -0
  84. package/src/helpers/typeUtils.ts +15 -0
  85. package/src/helpers/useAssistSupported.ts +10 -0
  86. package/src/index.ts +6 -0
  87. package/src/legacy-types.ts +72 -0
  88. package/src/onboarding/FieldActionsOnboarding.tsx +90 -0
  89. package/src/onboarding/FirstAssistedPathProvider.tsx +29 -0
  90. package/src/onboarding/InspectorOnboarding.tsx +46 -0
  91. package/src/onboarding/onboardingStore.ts +33 -0
  92. package/src/plugin.tsx +80 -0
  93. package/src/presence/AiFieldPresence.tsx +28 -0
  94. package/src/presence/AssistAvatar.tsx +96 -0
  95. package/src/presence/AssistDocumentPresence.tsx +58 -0
  96. package/src/presence/useAssistPresence.ts +61 -0
  97. package/src/schemas/assistDocumentSchema.tsx +450 -0
  98. package/src/schemas/contextDocumentSchema.tsx +56 -0
  99. package/src/schemas/index.ts +25 -0
  100. package/src/schemas/serialize/SchemTypeTool.tsx +102 -0
  101. package/src/schemas/serialize/schemaUtils.ts +37 -0
  102. package/src/schemas/serialize/serializeSchema.test.ts +382 -0
  103. package/src/schemas/serialize/serializeSchema.ts +162 -0
  104. package/src/schemas/serializedSchemaTypeSchema.ts +59 -0
  105. package/src/schemas/typeDefExtensions.ts +30 -0
  106. package/src/types.ts +167 -0
  107. package/src/useApiClient.ts +140 -0
  108. package/src/vite.config.ts +9 -0
  109. package/v2-incompatible.js +11 -0
@@ -0,0 +1,450 @@
1
+ import {defineArrayMember, defineField, defineType, ObjectSchemaType} from 'sanity'
2
+ import {
3
+ ArrowRightIcon,
4
+ CodeIcon,
5
+ ComposeIcon,
6
+ icons,
7
+ IconSymbol,
8
+ LockIcon,
9
+ SparklesIcon,
10
+ ThListIcon,
11
+ } from '@sanity/icons'
12
+ import {
13
+ assistDocumentIdPrefix,
14
+ assistDocumentTypeName,
15
+ assistFieldTypeName,
16
+ assistTasksStatusTypeName,
17
+ fieldReferenceTypeName,
18
+ instructionContextTypeName,
19
+ instructionTaskTypeName,
20
+ instructionTypeName,
21
+ promptTypeName,
22
+ userInputTypeName,
23
+ } from '../types'
24
+ import {Box, Flex, Stack, Text, Tooltip} from '@sanity/ui'
25
+ import {getInstructionTitle} from '../helpers/misc'
26
+ import {AssistDocumentForm} from '../assistDocument/components/AssistDocumentForm'
27
+ import {InstructionInput} from '../assistDocument/components/instruction/InstructionInput'
28
+ import {HiddenFieldTitle} from '../assistDocument/components/generic/HiddenFieldTitle'
29
+ import {InstructionVisibility} from '../assistDocument/components/instruction/appearance/InstructionVisibility'
30
+ import {IconInput} from '../assistDocument/components/instruction/appearance/IconInput'
31
+ import {FieldRefPathInput} from '../assistDocument/components/instruction/FieldRefInput'
32
+ import {InstructionsArrayInput} from '../assistDocument/components/InstructionsArrayInput'
33
+ import {FieldRefPreview} from '../assistDocument/components/FieldRefPreview'
34
+ import {createElement} from 'react'
35
+ import {contextDocumentSchema} from './contextDocumentSchema'
36
+ import {PromptInput} from '../assistDocument/components/instruction/PromptInput'
37
+ import {instructionGuideUrl} from '../constants'
38
+ import {InstructionsArrayField} from '../assistDocument/components/InstructionsArrayField'
39
+ import {getFieldRefsWithDocument} from '../assistInspector/helpers'
40
+
41
+ export const fieldReference = defineType({
42
+ type: 'object',
43
+ name: fieldReferenceTypeName,
44
+ title: 'Document field',
45
+ icon: ThListIcon,
46
+
47
+ fields: [
48
+ defineField({
49
+ type: 'string',
50
+ name: 'path',
51
+ title: 'Field',
52
+ components: {
53
+ input: FieldRefPathInput,
54
+ },
55
+ validation: (rule) =>
56
+ rule.custom((value, context) => {
57
+ if (!value) {
58
+ return 'Please select a field'
59
+ }
60
+ try {
61
+ const docId = context.document?._id
62
+ if (!docId) {
63
+ return `Field reference cannot be used outside document inspector context. Could not resolve document id.`
64
+ }
65
+ const targetDocType = docId.replace(new RegExp(`^${assistDocumentIdPrefix}`), '')
66
+ const schema = context.schema.get(targetDocType)
67
+ if (!schema) {
68
+ return `Field reference cannot be used outside document inspector context. Could not resolve schema: ${targetDocType}`
69
+ }
70
+ const refs = getFieldRefsWithDocument(schema as ObjectSchemaType)
71
+ const fieldRef = refs.find((r) => r.key === value)
72
+ if (!fieldRef) {
73
+ return `Field with path "${value}" does not exist in the schema.`
74
+ }
75
+ return true
76
+ } catch (e) {
77
+ console.error('Failed to resolve field reference', e)
78
+ return 'Invalid field reference.'
79
+ }
80
+ }),
81
+ }),
82
+ ],
83
+ preview: {
84
+ select: {
85
+ path: 'path',
86
+ },
87
+ prepare({path}) {
88
+ return {
89
+ title: path,
90
+ path,
91
+ icon: CodeIcon,
92
+ }
93
+ },
94
+ },
95
+ components: {
96
+ preview: FieldRefPreview,
97
+ },
98
+ options: {
99
+ modal: {
100
+ type: 'popover',
101
+ },
102
+ },
103
+ })
104
+
105
+ export const userInput = defineType({
106
+ type: 'object',
107
+ name: userInputTypeName,
108
+ title: 'User input',
109
+ icon: ComposeIcon,
110
+ fields: [
111
+ defineField({
112
+ type: 'string',
113
+ name: 'message',
114
+ title: 'User input title',
115
+ placeholder: 'Provide instruction text',
116
+ description: 'The header above the user input text field',
117
+ validation: (rule) => rule.required(),
118
+ }),
119
+ defineField({
120
+ type: 'text',
121
+ rows: 3,
122
+ name: 'description',
123
+ title: 'User input description',
124
+ description: 'The description above the user input text field',
125
+ }),
126
+ ],
127
+ preview: {
128
+ select: {
129
+ title: 'message',
130
+ },
131
+ },
132
+ options: {
133
+ modal: {
134
+ type: 'popover',
135
+ width: 1,
136
+ },
137
+ },
138
+ })
139
+
140
+ export const promptContext = defineType({
141
+ type: 'object',
142
+ name: instructionContextTypeName,
143
+ title: contextDocumentSchema.title,
144
+ icon: contextDocumentSchema.icon,
145
+ fields: [
146
+ defineField({
147
+ type: 'reference',
148
+ name: 'reference',
149
+ to: [{type: contextDocumentSchema.name}],
150
+ title: 'Context',
151
+ description: 'The referenced context will be inserted into the instruction',
152
+ validation: (rule) => rule.required(),
153
+ components: {
154
+ input: function Fix(props) {
155
+ return <Box style={{maxWidth: 300}}>{props.renderDefault(props)}</Box>
156
+ },
157
+ },
158
+ }),
159
+ ],
160
+ preview: {
161
+ select: {
162
+ ref: 'reference._ref',
163
+ title: 'reference.title',
164
+ context: 'reference.context',
165
+ },
166
+ prepare(select) {
167
+ return select.ref
168
+ ? contextDocumentSchema?.preview?.prepare?.(select) ?? select
169
+ : {title: 'No reference selected', media: contextDocumentSchema.icon}
170
+ },
171
+ },
172
+ options: {
173
+ modal: {
174
+ type: 'popover',
175
+ width: 'auto',
176
+ },
177
+ },
178
+ })
179
+
180
+ export const prompt = defineType({
181
+ type: 'array',
182
+ name: promptTypeName,
183
+ title: 'Prompt',
184
+ of: [
185
+ defineArrayMember({
186
+ type: 'block',
187
+ styles: [{title: 'Normal', value: 'normal'}],
188
+ lists: [],
189
+ marks: {
190
+ decorators: [],
191
+ annotations: [],
192
+ },
193
+ of: [
194
+ defineArrayMember({
195
+ type: fieldReference.name,
196
+ }),
197
+ defineArrayMember({
198
+ type: promptContext.name,
199
+ }),
200
+ defineArrayMember({
201
+ type: userInput.name,
202
+ }),
203
+ ],
204
+ }),
205
+ /* defineArrayMember({
206
+ type: fieldReference.name,
207
+ }),
208
+ defineArrayMember({
209
+ type: promptContext.name,
210
+ }),
211
+ defineArrayMember({
212
+ type: userInput.name,
213
+ }),*/
214
+ ],
215
+ })
216
+
217
+ export const instruction = defineType({
218
+ type: 'object',
219
+ name: instructionTypeName,
220
+ title: 'Instruction',
221
+ fieldsets: [
222
+ {name: 'appearance', title: 'Appearance', options: {collapsible: true, collapsed: true}},
223
+ ],
224
+ preview: {
225
+ select: {
226
+ icon: 'icon',
227
+ title: 'title',
228
+ userId: 'userId',
229
+ },
230
+ prepare: ({icon, title, userId}) => {
231
+ return {
232
+ title,
233
+ icon: icon ? icons[icon as IconSymbol] : SparklesIcon,
234
+ userId,
235
+ }
236
+ },
237
+ },
238
+ components: {
239
+ input: InstructionInput,
240
+ preview: (props: any) => {
241
+ return (
242
+ <Flex gap={3} align="center" padding={2}>
243
+ {props.icon && (
244
+ <Box flex="none">
245
+ <Text size={1}>{createElement(props.icon)}</Text>
246
+ </Box>
247
+ )}
248
+
249
+ <Stack flex={1} space={2}>
250
+ <Text size={1} textOverflow="ellipsis" weight="medium">
251
+ {getInstructionTitle(props)}
252
+ </Text>
253
+ </Stack>
254
+
255
+ {props.userId && (
256
+ <Text size={1}>
257
+ <Tooltip
258
+ content={<Text size={1}>Only visible to you</Text>}
259
+ padding={2}
260
+ placement="top"
261
+ portal
262
+ >
263
+ <LockIcon />
264
+ </Tooltip>
265
+ </Text>
266
+ )}
267
+ </Flex>
268
+ )
269
+ },
270
+ },
271
+ fields: [
272
+ defineField({
273
+ type: prompt.name,
274
+ name: 'prompt',
275
+ title: 'Instruction',
276
+ description: (
277
+ <>
278
+ Learn from{' '}
279
+ <a href={instructionGuideUrl} target="_blank" rel="noreferrer">
280
+ our instruction guide <ArrowRightIcon />
281
+ </a>
282
+ </>
283
+ ),
284
+ components: {
285
+ input: PromptInput,
286
+ },
287
+ }),
288
+ defineField({
289
+ type: 'string',
290
+ name: 'icon',
291
+ title: 'Icon',
292
+ fieldset: 'appearance',
293
+ components: {
294
+ field: HiddenFieldTitle,
295
+ input: IconInput,
296
+ },
297
+ }),
298
+ defineField({
299
+ type: 'string',
300
+ name: 'title',
301
+ title: 'Title',
302
+ fieldset: 'appearance',
303
+ components: {
304
+ field: HiddenFieldTitle,
305
+ },
306
+ }),
307
+ defineField({
308
+ type: 'string',
309
+ name: 'userId',
310
+ title: 'Visibility',
311
+ fieldset: 'appearance',
312
+ components: {
313
+ field: HiddenFieldTitle,
314
+ input: InstructionVisibility,
315
+ },
316
+ initialValue: (params, context) => context.currentUser?.id ?? '',
317
+ readOnly: (context) =>
318
+ Boolean(
319
+ context.parent?.createdById && context.parent?.createdById !== context.currentUser?.id
320
+ ),
321
+ }),
322
+ defineField({
323
+ type: 'string',
324
+ name: 'createdById',
325
+ title: 'Created by',
326
+ hidden: true,
327
+ fieldset: 'appearance',
328
+ initialValue: (params, context) => {
329
+ return context.currentUser?.id ?? ''
330
+ },
331
+ }),
332
+ ],
333
+ })
334
+
335
+ export const fieldInstructions = defineType({
336
+ type: 'object',
337
+ name: assistFieldTypeName,
338
+ title: 'Field prompt',
339
+ /* components: {
340
+ input: FieldPromptInput,
341
+ },*/
342
+ fields: [
343
+ defineField({
344
+ type: 'string',
345
+ name: 'path',
346
+ title: 'Path',
347
+ readOnly: true,
348
+ hidden: true,
349
+ }),
350
+ defineField({
351
+ type: 'array',
352
+ name: 'instructions',
353
+ title: 'Instructions',
354
+ of: [{type: instruction.name}],
355
+ components: {
356
+ field: InstructionsArrayField,
357
+ input: InstructionsArrayInput,
358
+ },
359
+ }),
360
+ ],
361
+ preview: {
362
+ select: {
363
+ title: 'path',
364
+ },
365
+ },
366
+ })
367
+
368
+ export const assistDocumentSchema = defineType({
369
+ //NOTE: this is a document type. Using object here ensures it does not appear in structure menus
370
+ type: 'object',
371
+ //workaround for using object and not document
372
+ ...({liveEdit: true} as any),
373
+ name: assistDocumentTypeName,
374
+ title: 'AI Document',
375
+
376
+ components: {
377
+ input: AssistDocumentForm,
378
+ field: (props) => {
379
+ return props.renderDefault({...props, title: ''})
380
+ },
381
+ },
382
+ fields: [
383
+ defineField({
384
+ type: 'string',
385
+ name: 'title',
386
+ title: 'Title',
387
+ }),
388
+ defineField({
389
+ type: 'array',
390
+ name: 'fields',
391
+ title: 'Fields',
392
+ of: [{type: fieldInstructions.name}],
393
+ }),
394
+ ],
395
+ preview: {
396
+ select: {
397
+ title: 'title',
398
+ },
399
+ },
400
+ })
401
+
402
+ export const instructionTask = defineType({
403
+ type: 'object',
404
+ name: instructionTaskTypeName,
405
+ title: 'Instruction task',
406
+ fields: [
407
+ defineField({
408
+ type: 'string',
409
+ name: 'path',
410
+ title: 'Path',
411
+ }),
412
+ defineField({
413
+ type: 'string',
414
+ name: 'instructionKey',
415
+ title: 'Instruction key',
416
+ }),
417
+ defineField({
418
+ type: 'datetime',
419
+ name: 'started',
420
+ title: 'Started',
421
+ }),
422
+ defineField({
423
+ type: 'datetime',
424
+ name: 'updated',
425
+ title: 'Updated',
426
+ }),
427
+ defineField({
428
+ type: 'string',
429
+ name: 'info',
430
+ title: 'Info',
431
+ }),
432
+ ],
433
+ })
434
+
435
+ export const documentInstructionStatus = defineType({
436
+ //NOTE: this is a document type. Using object here ensures it does not appear in structure menus
437
+ type: 'object',
438
+ //workaround for using object and not document
439
+ ...({liveEdit: true} as any),
440
+ name: assistTasksStatusTypeName,
441
+ title: 'Document instruction status',
442
+ fields: [
443
+ defineField({
444
+ type: 'array',
445
+ name: 'tasks',
446
+ title: 'Tasks',
447
+ of: [{type: instructionTask.name}],
448
+ }),
449
+ ],
450
+ })
@@ -0,0 +1,56 @@
1
+ import {defineArrayMember, defineField, defineType} from 'sanity'
2
+ import {contextDocumentTypeName} from '../types'
3
+ import {DocumentTextIcon, TokenIcon} from '@sanity/icons'
4
+ import {HideReferenceChangedBannerInput} from '../components/HideReferenceChangedBannerInput'
5
+
6
+ export const contextDocumentSchema = defineType({
7
+ type: 'document',
8
+ name: contextDocumentTypeName,
9
+ title: 'AI context',
10
+ liveEdit: true,
11
+ icon: TokenIcon,
12
+ components: {
13
+ input: HideReferenceChangedBannerInput,
14
+ },
15
+ fields: [
16
+ defineField({
17
+ type: 'string',
18
+ name: 'title',
19
+ title: 'Title',
20
+ }),
21
+ defineField({
22
+ name: 'context',
23
+ type: 'array',
24
+ title: 'Context',
25
+ of: [
26
+ defineArrayMember({
27
+ type: 'block',
28
+ styles: [{title: 'Normal', value: 'normal'}],
29
+ lists: [],
30
+ marks: {
31
+ decorators: [],
32
+ annotations: [],
33
+ },
34
+ }),
35
+ ],
36
+ }),
37
+ ],
38
+ preview: {
39
+ select: {
40
+ title: 'title',
41
+ context: 'context',
42
+ },
43
+ prepare({title, context}) {
44
+ const text = context
45
+ ?.flatMap((block: any) => block?.children)
46
+ .flatMap((child: any) => child?.text?.split(' '))
47
+ .filter(Boolean)
48
+ const words = text?.length ?? 0
49
+ return {
50
+ title,
51
+ subtitle: `Words: ${words}`,
52
+ media: DocumentTextIcon,
53
+ }
54
+ },
55
+ },
56
+ })
@@ -0,0 +1,25 @@
1
+ import {
2
+ assistDocumentSchema,
3
+ documentInstructionStatus,
4
+ fieldInstructions,
5
+ fieldReference,
6
+ instruction,
7
+ instructionTask,
8
+ prompt,
9
+ promptContext,
10
+ userInput,
11
+ } from './assistDocumentSchema'
12
+ import {contextDocumentSchema} from './contextDocumentSchema'
13
+
14
+ export const schemaTypes = [
15
+ fieldInstructions,
16
+ assistDocumentSchema,
17
+ prompt,
18
+ fieldReference,
19
+ instruction,
20
+ documentInstructionStatus,
21
+ instructionTask,
22
+ contextDocumentSchema,
23
+ userInput,
24
+ promptContext,
25
+ ]
@@ -0,0 +1,102 @@
1
+ import {useClient, useSchema} from 'sanity'
2
+ import {Box, Button, Card, Flex, Label, Spinner, Stack} from '@sanity/ui'
3
+ import {useCallback, useMemo, useState} from 'react'
4
+ import {assistSerializedTypeName, SerializedSchemaType} from '../../types'
5
+ import {SyncIcon} from '@sanity/icons'
6
+ import {useListeningQuery} from '../../_lib/useListeningQuery'
7
+ import {serializeSchema} from './serializeSchema'
8
+
9
+ const NO_DATA: SerializedSchemaType[] = []
10
+
11
+ const defaultTitle = 'Sync schema'
12
+
13
+ export function SchemaTypeTool() {
14
+ const schema = useSchema()
15
+ const client = useClient({apiVersion: '2023-01-01'})
16
+ const [saving, setSaving] = useState(false)
17
+ const [syncTitle, setSyncTitle] = useState(defaultTitle)
18
+
19
+ const {data} = useListeningQuery<SerializedSchemaType[]>('*[_type==$type] | order(_type)', {
20
+ type: assistSerializedTypeName,
21
+ })
22
+
23
+ const types = useMemo(() => {
24
+ return serializeSchema(schema)
25
+ }, [schema])
26
+
27
+ const storeTypes = useCallback(() => {
28
+ setSaving(true)
29
+ let canSave = true
30
+ async function store() {
31
+ setSyncTitle(`Syncing 0/${types.length}`)
32
+ const transaction = client.transaction()
33
+ for (let i = 0; i < types.length; i++) {
34
+ if (!canSave) {
35
+ break
36
+ }
37
+ const type = types[i]
38
+ await transaction.createOrReplace(type as Required<typeof type>)
39
+ if (i > 0 && i % 50 === 0) {
40
+ await transaction.commit()
41
+ transaction.reset()
42
+ setSyncTitle(`Syncing ${i}/${types.length}`)
43
+ }
44
+ }
45
+ await transaction.commit()
46
+ }
47
+ store()
48
+ .catch(console.error)
49
+ .finally(() => {
50
+ setSaving(false)
51
+ setSyncTitle(defaultTitle)
52
+ })
53
+ return () => {
54
+ canSave = false
55
+ setSaving(false)
56
+ setSyncTitle(defaultTitle)
57
+ }
58
+ }, [types, client, setSaving, setSyncTitle])
59
+
60
+ return (
61
+ <Card padding={4} overflow="auto" style={{height: 'calc(100vh - 81px)'}}>
62
+ <Stack space={4}>
63
+ <Box>
64
+ <Button
65
+ icon={saving ? <Spinner style={{marginTop: 5}} /> : SyncIcon}
66
+ text={syncTitle}
67
+ disabled={saving}
68
+ onClick={storeTypes}
69
+ />
70
+ </Box>
71
+ <Flex gap={2}>
72
+ <Stack space={2}>
73
+ <Label>Studio schema</Label>
74
+ <ul>
75
+ {types.map((type) => (
76
+ <li key={type.name}>
77
+ <SchemaEntry schemaStub={type} />
78
+ </li>
79
+ ))}
80
+ </ul>
81
+ </Stack>
82
+
83
+ <Stack space={2}>
84
+ <Label>Stored schema</Label>
85
+ <ul>
86
+ {(data ?? NO_DATA).map((type) => (
87
+ <li key={type.name}>
88
+ <SchemaEntry schemaStub={type} />
89
+ </li>
90
+ ))}
91
+ </ul>
92
+ </Stack>
93
+ </Flex>
94
+ </Stack>
95
+ </Card>
96
+ )
97
+ }
98
+
99
+ function SchemaEntry({schemaStub}: {schemaStub: SerializedSchemaType}) {
100
+ const out = useMemo(() => JSON.stringify(schemaStub, null, 2), [schemaStub])
101
+ return <pre>{out}</pre>
102
+ }
@@ -0,0 +1,37 @@
1
+ import {assistSerializedFieldTypeName, assistSerializedTypeName} from '../../types'
2
+
3
+ export const hiddenTypes = [
4
+ 'any',
5
+ 'array',
6
+ 'block',
7
+ 'boolean',
8
+ 'crossDatasetReference',
9
+ 'date',
10
+ 'datetime',
11
+ 'document',
12
+ 'email',
13
+ 'file',
14
+ 'image',
15
+ 'number',
16
+ 'object',
17
+ 'reference',
18
+ 'span',
19
+ 'string',
20
+ 'text',
21
+ 'url',
22
+ 'slug',
23
+ 'geopoint',
24
+ 'sanity.assetSourceData',
25
+ 'sanity.imageAsset',
26
+ 'sanity.fileAsset',
27
+ 'sanity.imageCrop',
28
+ 'sanity.imageHotspot',
29
+ 'sanity.imageMetadata',
30
+ 'sanity.imageDimensions',
31
+ 'sanity.imagePalette',
32
+ 'sanity.imagePaletteSwatch',
33
+
34
+ assistSerializedTypeName,
35
+ assistSerializedFieldTypeName,
36
+ 'sanity-agent.job.document',
37
+ ]