@sanity/assist 4.4.6 → 4.4.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/assist",
3
- "version": "4.4.6",
3
+ "version": "4.4.8",
4
4
  "description": "You create the instructions; Sanity AI Assist does the rest.",
5
5
  "keywords": [
6
6
  "sanity",
@@ -83,7 +83,7 @@
83
83
  "react": "^18.2.0",
84
84
  "react-dom": "^18.2.0",
85
85
  "rimraf": "^5.0.5",
86
- "sanity": "^3.93.0",
86
+ "sanity": "^4.0.0",
87
87
  "semantic-release": "^23.0.8",
88
88
  "styled-components": "^6.1.16",
89
89
  "typescript": "^5.7.2",
@@ -1,16 +1,17 @@
1
1
  import {useCallback, useEffect, useMemo, useState} from 'react'
2
2
  import {getDraftId, getVersionId, type ObjectSchemaType, useSchema} from 'sanity'
3
3
  import {useDocumentPane} from 'sanity/structure'
4
-
5
- import {asFieldRefsByTypePath, getFieldRefs, useAiPaneRouter} from '../../assistInspector/helpers'
6
4
  import {fieldPathParam, InstructionTask} from '../../types'
7
5
  import {AssistDocumentContextValue} from '../AssistDocumentContext'
8
6
  import {isDocAssistable} from '../RequestRunInstructionProvider'
9
7
  import {useStudioAssistDocument} from './useStudioAssistDocument'
8
+ import {useAiAssistanceConfig} from '../../assistLayout/AiAssistanceConfigContext'
9
+ import {useAiPaneRouter} from '../../assistInspector/helpers'
10
10
 
11
11
  export function useAssistDocumentContextValue(documentId: string, documentType: string) {
12
12
  const schema = useSchema()
13
13
 
14
+ const {getFieldRefs, getFieldRefsByTypePath} = useAiAssistanceConfig()
14
15
  const documentSchemaType = useMemo(() => {
15
16
  const schemaType = schema.get(documentType) as ObjectSchemaType | undefined
16
17
  if (!schemaType) {
@@ -20,13 +21,11 @@ export function useAssistDocumentContextValue(documentId: string, documentType:
20
21
  }, [documentType, schema])
21
22
 
22
23
  const {fieldRefs, fieldRefsByTypePath} = useMemo(() => {
23
- const fieldRefs = getFieldRefs(documentSchemaType)
24
- const fieldRefsByTypePath = asFieldRefsByTypePath(fieldRefs)
25
24
  return {
26
- fieldRefs,
27
- fieldRefsByTypePath,
25
+ fieldRefs: getFieldRefs(documentType),
26
+ fieldRefsByTypePath: getFieldRefsByTypePath(documentType),
28
27
  }
29
- }, [documentSchemaType])
28
+ }, [getFieldRefs, getFieldRefsByTypePath, documentType])
30
29
 
31
30
  const {
32
31
  openInspector,
@@ -4,7 +4,8 @@ import {createElement, useCallback, useMemo} from 'react'
4
4
  import {ObjectSchemaType} from 'sanity'
5
5
 
6
6
  import {isType} from '../helpers/typeUtils'
7
- import {FieldRef, getFieldRefs, getFieldRefsWithDocument} from './helpers'
7
+ import {FieldRef, getDocumentFieldRef} from './helpers'
8
+ import {useAiAssistanceConfig} from '../assistLayout/AiAssistanceConfigContext'
8
9
 
9
10
  interface FieldSelectorProps {
10
11
  id: string
@@ -18,12 +19,16 @@ interface FieldSelectorProps {
18
19
  export function FieldAutocomplete(props: FieldSelectorProps) {
19
20
  const {id, schemaType, fieldPath, onSelect, includeDocument, filter} = props
20
21
 
22
+ const {getFieldRefs} = useAiAssistanceConfig()
23
+
21
24
  const fieldRefs = useMemo(() => {
25
+ const refs = getFieldRefs(schemaType.name)
22
26
  if (includeDocument) {
23
- return getFieldRefsWithDocument(schemaType)
27
+ return [getDocumentFieldRef(schemaType), ...refs]
24
28
  }
25
- return getFieldRefs(schemaType)
26
- }, [schemaType, includeDocument])
29
+ return refs
30
+ }, [schemaType, includeDocument, getFieldRefs])
31
+
27
32
  const currentField = useMemo(
28
33
  () => fieldRefs.find((f) => f.key === fieldPath),
29
34
  [fieldPath, fieldRefs],
@@ -8,7 +8,7 @@ import {
8
8
  StringIcon,
9
9
  } from '@sanity/icons'
10
10
  import {extractWithPath} from '@sanity/mutator'
11
- import {type ComponentType, useContext, useMemo} from 'react'
11
+ import {type ComponentType, useMemo} from 'react'
12
12
  import {
13
13
  type ArraySchemaType,
14
14
  isKeySegment,
@@ -21,11 +21,10 @@ import {
21
21
  stringToPath,
22
22
  } from 'sanity'
23
23
  import {type PaneRouterContextValue, usePaneRouter} from 'sanity/structure'
24
-
25
- import {SelectedFieldContext} from '../assistDocument/components/SelectedFieldContext'
26
24
  import {isAssistSupported} from '../helpers/assistSupported'
27
25
  import {isPortableTextArray, isType} from '../helpers/typeUtils'
28
- import {type AssistInspectorRouteParams, documentRootKey, fieldPathParam} from '../types'
26
+ import {type AssistInspectorRouteParams, documentRootKey} from '../types'
27
+ import {useAiAssistanceConfig} from '../assistLayout/AiAssistanceConfigContext'
29
28
 
30
29
  export interface FieldRef {
31
30
  key: string
@@ -65,19 +64,14 @@ export function asFieldRefsByTypePath(fieldRefs: FieldRef[]): Record<string, Fie
65
64
  return lookup
66
65
  }
67
66
 
68
- export function getFieldRefsWithDocument(schemaType: ObjectSchemaType): FieldRef[] {
69
- const fields = getFieldRefs(schemaType)
70
- return [
71
- {
72
- key: documentRootKey,
73
- icon: schemaType.icon ?? DocumentIcon,
74
- title: `The entire document`,
75
- path: [],
76
- schemaType: schemaType,
77
- },
78
-
79
- ...fields,
80
- ]
67
+ export function getDocumentFieldRef(schemaType: ObjectSchemaType) {
68
+ return {
69
+ key: documentRootKey,
70
+ icon: schemaType.icon ?? DocumentIcon,
71
+ title: `The entire document`,
72
+ path: [],
73
+ schemaType: schemaType,
74
+ }
81
75
  }
82
76
 
83
77
  export function getFieldRefs(
@@ -182,12 +176,14 @@ export function useSelectedField(
182
176
  documentSchemaType?: SchemaType,
183
177
  path?: string,
184
178
  ): FieldRef | undefined {
179
+ const {getFieldRefs} = useAiAssistanceConfig()
180
+
185
181
  const selectableFields = useMemo(
186
182
  () =>
187
183
  documentSchemaType && isObjectSchemaType(documentSchemaType)
188
- ? getFieldRefsWithDocument(documentSchemaType)
184
+ ? [getDocumentFieldRef(documentSchemaType), ...getFieldRefs(documentSchemaType.name)]
189
185
  : [],
190
- [documentSchemaType],
186
+ [documentSchemaType, getFieldRefs],
191
187
  )
192
188
 
193
189
  return useMemo(() => {
@@ -195,13 +191,6 @@ export function useSelectedField(
195
191
  }, [selectableFields, path])
196
192
  }
197
193
 
198
- export function useSelectedFieldTitle() {
199
- const {params} = useAiPaneRouter()
200
- const {documentSchema} = useContext(SelectedFieldContext) ?? {}
201
- const selectedField = useSelectedField(documentSchema, params[fieldPathParam])
202
- return getFieldTitle(selectedField)
203
- }
204
-
205
194
  export function getFieldTitle(field?: FieldRef) {
206
195
  const schemaType = field?.schemaType
207
196
  return field?.title ?? schemaType?.title ?? schemaType?.name ?? 'Untitled'
@@ -1,18 +1,9 @@
1
- import {
2
- createContext,
3
- ReactNode,
4
- useCallback,
5
- useContext,
6
- useEffect,
7
- useMemo,
8
- useState,
9
- } from 'react'
1
+ import {createContext, useContext} from 'react'
10
2
 
11
3
  import {AssistPluginConfig} from '../plugin'
12
- import {InstructStatus, useApiClient, useGetInstructStatus, useInitInstruct} from '../useApiClient'
4
+ import {InstructStatus} from '../useApiClient'
13
5
  import {SerializedSchemaType} from '../types'
14
- import {serializeSchema} from '../schemas/serialize/serializeSchema'
15
- import {useSchema} from 'sanity'
6
+ import {FieldRef} from '../assistInspector/helpers'
16
7
 
17
8
  export interface AiAssistanceConfigContextValue {
18
9
  config: AssistPluginConfig
@@ -22,6 +13,8 @@ export interface AiAssistanceConfigContextValue {
22
13
  init: () => void
23
14
  error?: Error
24
15
  serializedTypes: SerializedSchemaType[]
16
+ getFieldRefs: (documentType: string) => FieldRef[]
17
+ getFieldRefsByTypePath: (documentType: string) => Record<string, FieldRef | undefined>
25
18
  }
26
19
 
27
20
  export const AiAssistanceConfigContext = createContext<AiAssistanceConfigContextValue>({} as any)
@@ -37,58 +30,3 @@ export function useAiAssistanceConfig() {
37
30
  export function useSerializedTypes() {
38
31
  return useAiAssistanceConfig().serializedTypes
39
32
  }
40
-
41
- export function AiAssistanceConfigProvider(props: {
42
- children?: ReactNode
43
- config: AssistPluginConfig
44
- }) {
45
- const [status, setStatus] = useState<InstructStatus | undefined>()
46
- const [error, setError] = useState<Error | undefined>()
47
-
48
- const apiClient = useApiClient(props.config?.__customApiClient)
49
- const {getInstructStatus, loading: statusLoading} = useGetInstructStatus(apiClient)
50
- const {initInstruct, loading: initLoading} = useInitInstruct(apiClient)
51
-
52
- const schema = useSchema()
53
- const serializedTypes = useMemo(() => serializeSchema(schema, {leanFormat: true}), [schema])
54
-
55
- useEffect(() => {
56
- getInstructStatus()
57
- .then((s) => setStatus(s))
58
- .catch((e) => {
59
- console.error(e)
60
- setError(e as Error)
61
- })
62
- }, [getInstructStatus])
63
-
64
- const init = useCallback(async () => {
65
- setError(undefined)
66
- try {
67
- await initInstruct()
68
- const status = await getInstructStatus()
69
- setStatus(status)
70
- } catch (e) {
71
- console.error('Failed to init ai assistance', e)
72
- setError(e as Error)
73
- }
74
- }, [initInstruct, getInstructStatus, setStatus])
75
-
76
- const {config, children} = props
77
- const context = useMemo<AiAssistanceConfigContextValue>(() => {
78
- return {
79
- config,
80
- status,
81
- statusLoading,
82
- init,
83
- initLoading,
84
- error,
85
- serializedTypes,
86
- }
87
- }, [config, status, init, statusLoading, initLoading, error, serializedTypes])
88
-
89
- return (
90
- <AiAssistanceConfigContext.Provider value={context}>
91
- {children}
92
- </AiAssistanceConfigContext.Provider>
93
- )
94
- }
@@ -0,0 +1,98 @@
1
+ import {ReactNode, useCallback, useEffect, useMemo, useState} from 'react'
2
+ import {AssistPluginConfig} from '../plugin'
3
+ import {InstructStatus, useApiClient, useGetInstructStatus, useInitInstruct} from '../useApiClient'
4
+ import {type ObjectSchemaType, Schema, useSchema} from 'sanity'
5
+ import {serializeSchema} from '../schemas/serialize/serializeSchema'
6
+ import {
7
+ AiAssistanceConfigContext,
8
+ AiAssistanceConfigContextValue,
9
+ } from './AiAssistanceConfigContext'
10
+ import {createFieldRefCache} from './fieldRefCache'
11
+
12
+ export function AiAssistanceConfigProvider(props: {
13
+ children?: ReactNode
14
+ config: AssistPluginConfig
15
+ }) {
16
+ const [status, setStatus] = useState<InstructStatus | undefined>()
17
+ const [error, setError] = useState<Error | undefined>()
18
+
19
+ const apiClient = useApiClient(props.config?.__customApiClient)
20
+ const {getInstructStatus, loading: statusLoading} = useGetInstructStatus(apiClient)
21
+ const {initInstruct, loading: initLoading} = useInitInstruct(apiClient)
22
+
23
+ const schema = useSchema()
24
+ const serializedTypes = useMemo(() => serializeSchema(schema, {leanFormat: true}), [schema])
25
+ const {getFieldRefs, getFieldRefsByTypePath} = useFieldRefGetters(schema)
26
+
27
+ useEffect(() => {
28
+ getInstructStatus()
29
+ .then((s) => setStatus(s))
30
+ .catch((e) => {
31
+ console.error(e)
32
+ setError(e as Error)
33
+ })
34
+ }, [getInstructStatus])
35
+
36
+ const init = useCallback(async () => {
37
+ setError(undefined)
38
+ try {
39
+ await initInstruct()
40
+ const status = await getInstructStatus()
41
+ setStatus(status)
42
+ } catch (e) {
43
+ console.error('Failed to init ai assistance', e)
44
+ setError(e as Error)
45
+ }
46
+ }, [initInstruct, getInstructStatus, setStatus])
47
+
48
+ const {config, children} = props
49
+ const context = useMemo<AiAssistanceConfigContextValue>(() => {
50
+ return {
51
+ config,
52
+ status,
53
+ statusLoading,
54
+ init,
55
+ initLoading,
56
+ error,
57
+ serializedTypes,
58
+ getFieldRefs,
59
+ getFieldRefsByTypePath,
60
+ }
61
+ }, [
62
+ config,
63
+ status,
64
+ init,
65
+ statusLoading,
66
+ initLoading,
67
+ error,
68
+ serializedTypes,
69
+ getFieldRefs,
70
+ getFieldRefsByTypePath,
71
+ ])
72
+
73
+ return (
74
+ <AiAssistanceConfigContext.Provider value={context}>
75
+ {children}
76
+ </AiAssistanceConfigContext.Provider>
77
+ )
78
+ }
79
+
80
+ function useFieldRefGetters(schema: Schema) {
81
+ return useMemo(() => {
82
+ const getForSchemaType = createFieldRefCache()
83
+
84
+ function getRefsForType(documentType: string) {
85
+ const schemaType = schema.get(documentType) as ObjectSchemaType | undefined
86
+ if (!schemaType) {
87
+ throw new Error(`Schema type "${documentType}" not found`)
88
+ }
89
+ return getForSchemaType(schemaType)
90
+ }
91
+
92
+ return {
93
+ getFieldRefs: (documentType: string) => getRefsForType(documentType).fieldRefs,
94
+ getFieldRefsByTypePath: (documentType: string) =>
95
+ getRefsForType(documentType).fieldRefsByTypePath,
96
+ }
97
+ }, [schema])
98
+ }
@@ -8,8 +8,8 @@ import {AssistPluginConfig} from '../plugin'
8
8
  import {FieldTranslationProvider} from '../translate/FieldTranslationProvider'
9
9
  import {StudioInstruction} from '../types'
10
10
  import {RunInstructionRequest} from '../useApiClient'
11
- import {AiAssistanceConfigProvider} from './AiAssistanceConfigContext'
12
11
  import {RunInstructionProvider} from './RunInstructionProvider'
12
+ import {AiAssistanceConfigProvider} from './AiAssistanceConfigProvider'
13
13
 
14
14
  export interface AIStudioLayoutProps extends LayoutProps {
15
15
  config: AssistPluginConfig
@@ -0,0 +1,34 @@
1
+ import {
2
+ asFieldRefsByTypePath,
3
+ FieldRef,
4
+ getFieldRefs as createFieldRefs,
5
+ } from '../assistInspector/helpers'
6
+ import type {ObjectSchemaType} from 'sanity'
7
+
8
+ export function createFieldRefCache() {
9
+ const byType: Record<
10
+ string,
11
+ | {
12
+ fieldRefs: FieldRef[]
13
+ fieldRefsByTypePath: Record<string, FieldRef | undefined>
14
+ }
15
+ | undefined
16
+ > = {}
17
+
18
+ function getRefsForType(schemaType: ObjectSchemaType) {
19
+ const documentType = schemaType.name
20
+ const cached = byType[documentType]
21
+ if (cached) return cached
22
+
23
+ const fieldRefs = createFieldRefs(schemaType)
24
+ const fieldRefsByTypePath = asFieldRefsByTypePath(fieldRefs)
25
+ const refs = {
26
+ fieldRefs,
27
+ fieldRefsByTypePath,
28
+ }
29
+ byType[documentType] = refs
30
+ return refs
31
+ }
32
+
33
+ return getRefsForType
34
+ }
@@ -4,10 +4,11 @@ import {
4
4
  DocumentFieldActionItem,
5
5
  DocumentFieldActionNode,
6
6
  ObjectSchemaType,
7
+ Path,
7
8
  SanityDocumentLike,
9
+ SchemaType,
8
10
  useWorkspaceSchemaId,
9
11
  } from 'sanity'
10
- import {Path, SchemaType} from '@sanity/types'
11
12
  import {useMemo} from 'react'
12
13
  import {useAiAssistanceConfig} from '../assistLayout/AiAssistanceConfigContext'
13
14
  import {ToastParams, useToast} from '@sanity/ui'
@@ -227,6 +227,49 @@ describe('conditionalMembers', () => {
227
227
  ])
228
228
  })
229
229
 
230
+ test('should include field with conditional state inside fieldset', () => {
231
+ const docSchema: ObjectSchemaType = Schema.compile({
232
+ name: 'test',
233
+ types: [
234
+ defineType({
235
+ type: 'document',
236
+ name: 'article',
237
+ fieldsets: [{name: 'set'}],
238
+ fields: [{type: 'string', fieldset: 'set', name: 'title', hidden: () => false}],
239
+ }),
240
+ ],
241
+ }).get('article')
242
+
243
+ const docState = {
244
+ path: [],
245
+ schemaType: docSchema,
246
+ members: [
247
+ {
248
+ kind: 'fieldSet',
249
+ fieldSet: {
250
+ name: 'set',
251
+ path: ['set'],
252
+ members: [
253
+ {
254
+ kind: 'field',
255
+ field: {path: [docSchema.fields[0].name], schemaType: docSchema.fields[0].type},
256
+ },
257
+ ],
258
+ },
259
+ },
260
+ ],
261
+ } as any
262
+ const conditionalMembers = getConditionalMembers(docState)
263
+
264
+ expect(conditionalMembers).toEqual([
265
+ {
266
+ path: 'title',
267
+ hidden: false,
268
+ readOnly: false,
269
+ },
270
+ ])
271
+ })
272
+
230
273
  test('should respect max-depth', () => {
231
274
  const docSchema: ObjectSchemaType = Schema.compile({
232
275
  name: 'test',
@@ -67,8 +67,8 @@ function conditionalState(memberState: {
67
67
  }): ConditionalMemberInnerState {
68
68
  return {
69
69
  path: pathToString(memberState.path),
70
- readOnly: !!memberState.readOnly,
71
- hidden: false, // if its in members, its not hidden
70
+ readOnly: !!memberState.readOnly, // Use actual form state readOnly value
71
+ hidden: false, // If it's in form state members, it's not hidden
72
72
  conditional: isConditional(memberState.schemaType),
73
73
  }
74
74
  }
@@ -124,7 +124,7 @@ function extractConditionalPaths(
124
124
  const innerFields = extractConditionalPaths(member.fieldSet, maxDepth).map((f) => ({
125
125
  ...f,
126
126
  // if fieldset is conditional, visible fields must also be considered conditional
127
- conditional: conditionalFieldset ?? f.conditional,
127
+ conditional: conditionalFieldset || f.conditional,
128
128
  }))
129
129
  return [...acc, ...innerFields]
130
130
  }
@@ -24,7 +24,6 @@ import {InstructionOutputInput} from '../assistDocument/components/instruction/I
24
24
  import {PromptInput} from '../assistDocument/components/instruction/PromptInput'
25
25
  import {InstructionsArrayField} from '../assistDocument/components/InstructionsArrayField'
26
26
  import {InstructionsArrayInput} from '../assistDocument/components/InstructionsArrayInput'
27
- import {getFieldRefsWithDocument} from '../assistInspector/helpers'
28
27
  import {instructionGuideUrl} from '../constants'
29
28
  import {getInstructionTitle} from '../helpers/misc'
30
29
  import {
@@ -43,6 +42,8 @@ import {
43
42
  } from '../types'
44
43
  import {contextDocumentSchema} from './contextDocumentSchema'
45
44
 
45
+ import {createFieldRefCache} from '../assistLayout/fieldRefCache'
46
+
46
47
  export const fieldReference = defineType({
47
48
  type: 'object',
48
49
  name: fieldReferenceTypeName,
@@ -57,8 +58,9 @@ export const fieldReference = defineType({
57
58
  components: {
58
59
  input: FieldRefPathInput,
59
60
  },
60
- validation: (rule) =>
61
- rule.custom((value, context) => {
61
+ validation: (rule) => {
62
+ const getForSchemaType = createFieldRefCache()
63
+ return rule.custom((value, context) => {
62
64
  if (!value) {
63
65
  return 'Please select a field'
64
66
  }
@@ -72,8 +74,8 @@ export const fieldReference = defineType({
72
74
  if (!schema) {
73
75
  return `Field reference cannot be used outside document inspector context. Could not resolve schema: ${targetDocType}`
74
76
  }
75
- const refs = getFieldRefsWithDocument(schema as ObjectSchemaType)
76
- const fieldRef = refs.find((r) => r.key === value)
77
+ const {fieldRefs} = getForSchemaType(schema as ObjectSchemaType)
78
+ const fieldRef = fieldRefs.find((r) => r.key === value)
77
79
  if (!fieldRef) {
78
80
  return `Field with path "${value}" does not exist in the schema.`
79
81
  }
@@ -82,7 +84,8 @@ export const fieldReference = defineType({
82
84
  console.error('Failed to resolve field reference', e)
83
85
  return 'Invalid field reference.'
84
86
  }
85
- }),
87
+ })
88
+ },
86
89
  }),
87
90
  ],
88
91
  preview: {
@@ -1,3 +1,4 @@
1
+ import 'sanity'
1
2
  /* eslint-disable no-unused-vars */
2
3
  export interface AssistOptions {
3
4
  aiAssist?: {