@sanity/assist 1.1.4 → 1.2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/assist",
3
- "version": "1.1.4",
3
+ "version": "1.2.1",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "sanity",
@@ -81,7 +81,7 @@
81
81
  "react": "^18.2.0",
82
82
  "react-dom": "^18.2.0",
83
83
  "rimraf": "^4.4.0",
84
- "sanity": "^3.16.1",
84
+ "sanity": "^3.16.7",
85
85
  "semantic-release": "^21.0.5",
86
86
  "styled-components": "^5.3.9",
87
87
  "typescript": "^5.1.3",
@@ -1,13 +1,13 @@
1
1
  import {
2
+ AssistDocument,
3
+ AssistField,
2
4
  assistFieldTypeName,
3
5
  AssistInspectorRouteParams,
4
- AssistDocument,
5
6
  documentRootKey,
6
7
  fieldPathParam,
7
- AssistField,
8
8
  instructionParam,
9
9
  } from '../../types'
10
- import {useEffect, useMemo, useRef} from 'react'
10
+ import {createContext, useContext, useEffect, useMemo, useRef} from 'react'
11
11
  import {
12
12
  FormCallbacksProvider,
13
13
  FormCallbacksValue,
@@ -33,6 +33,8 @@ import {documentTypeFromAiDocumentId} from '../../helpers/ids'
33
33
 
34
34
  const EMPTY_FIELDS: AssistField[] = []
35
35
 
36
+ export const TypePathContext = createContext<string | undefined>(undefined)
37
+
36
38
  export function AssistDocumentForm(props: ObjectInputProps) {
37
39
  const {onChange} = props
38
40
  const value = props.value as AssistDocument | undefined
@@ -48,12 +50,15 @@ export function AssistDocumentForm(props: ObjectInputProps) {
48
50
 
49
51
  const {params, setParams} = useAiPaneRouter()
50
52
  const pathKey = params[fieldPathParam]
53
+ const typePath = useContext(TypePathContext)
51
54
  const instruction = params[instructionParam]
52
55
 
53
- const activeKey = useMemo(
54
- () => (fields ?? EMPTY_FIELDS).find((f) => f.path === pathKey)?._key,
55
- [fields, pathKey]
56
- )
56
+ const activeKey = useMemo(() => {
57
+ if (!typePath) {
58
+ return undefined
59
+ }
60
+ return (fields ?? EMPTY_FIELDS).find((f) => f.path === typePath)?._key
61
+ }, [fields, typePath])
57
62
 
58
63
  const activePath: Path | undefined = useMemo(() => {
59
64
  if (!activeKey) {
@@ -72,6 +77,7 @@ export function AssistDocumentForm(props: ObjectInputProps) {
72
77
  }, [schema, targetDocumentType])
73
78
 
74
79
  const fieldSchema = useSelectedSchema(pathKey, documentSchema)
80
+
75
81
  const context: SelectedFieldContextValue = useMemo(
76
82
  () => ({
77
83
  documentSchema,
@@ -88,7 +94,7 @@ export function AssistDocumentForm(props: ObjectInputProps) {
88
94
  }
89
95
  }, [title, documentSchema, onChange, id])
90
96
 
91
- const fieldExists = !!fields?.some((f) => f._key === pathKey)
97
+ const fieldExists = !!fields?.some((f) => f._key === typePath)
92
98
 
93
99
  const {onPathOpen, ...formCallbacks} = useFormCallbacks()
94
100
 
@@ -122,8 +128,8 @@ export function AssistDocumentForm(props: ObjectInputProps) {
122
128
  <SelectedFieldContextProvider value={context}>
123
129
  <Stack space={5}>
124
130
  <FieldsInitializer
125
- key={pathKey}
126
- pathKey={pathKey}
131
+ key={typePath}
132
+ pathKey={typePath}
127
133
  activePath={activePath}
128
134
  fieldExists={fieldExists}
129
135
  onChange={onChange}
@@ -3,6 +3,6 @@ import {ArrayFieldProps} from 'sanity'
3
3
  export function InstructionsArrayField(props: ArrayFieldProps) {
4
4
  return props.renderDefault({
5
5
  ...props,
6
- title: undefined,
6
+ title: ' ',
7
7
  })
8
8
  }
@@ -3,9 +3,12 @@ import {useCallback, useContext, useEffect, useId, useRef} from 'react'
3
3
  import {Box} from '@sanity/ui'
4
4
  import {SelectedFieldContext} from '../SelectedFieldContext'
5
5
  import {FieldAutocomplete} from '../../../assistInspector/FieldAutocomplete'
6
+ import {FieldRef} from '../../../assistInspector/helpers'
7
+ import {TypePathContext} from '../AssistDocumentForm'
6
8
 
7
9
  export function FieldRefPathInput(props: StringInputProps) {
8
10
  const documentSchema = useContext(SelectedFieldContext)?.documentSchema
11
+ const typePath = useContext(TypePathContext)
9
12
  const ref = useRef<HTMLDivElement>(null)
10
13
  const id = useId()
11
14
  const {onChange} = props
@@ -16,17 +19,29 @@ export function FieldRefPathInput(props: StringInputProps) {
16
19
 
17
20
  const onSelect = useCallback((path: string) => onChange(set(path)), [onChange])
18
21
 
22
+ const filter = useCallback(
23
+ (field: FieldRef) => {
24
+ if (!field.key.includes('|') || !typePath) {
25
+ return true
26
+ }
27
+ const dotSplit = typePath.split('.')
28
+ const base = dotSplit.slice(0, dotSplit.length - 1).join('.')
29
+ return field.key.includes(base)
30
+ },
31
+ [typePath]
32
+ )
19
33
  if (!documentSchema) {
20
34
  return props.renderDefault(props)
21
35
  }
22
36
 
23
37
  return (
24
- <Box flex={1} style={{minWidth: 200}} ref={ref}>
38
+ <Box flex={1} style={{minWidth: 300}} ref={ref}>
25
39
  <FieldAutocomplete
26
40
  id={id}
27
41
  schemaType={documentSchema}
28
42
  onSelect={onSelect}
29
43
  fieldPath={props.value}
44
+ filter={filter}
30
45
  />
31
46
  </Box>
32
47
  )
@@ -15,7 +15,7 @@ import {
15
15
  } from 'sanity/desk'
16
16
  import {DocumentForm} from '../_lib/form'
17
17
  import {assistDocumentTypeName, fieldPathParam, instructionParam} from '../types'
18
- import {getFieldTitle, useAiPaneRouter, useSelectedField} from './helpers'
18
+ import {FieldRef, getFieldTitle, useAiPaneRouter, useSelectedField, useTypePath} from './helpers'
19
19
  import styled from 'styled-components'
20
20
  import {useStudioAssistDocument} from '../assistDocument/hooks/useStudioAssistDocument'
21
21
  import {InstructionTaskHistoryButton} from './InstructionTaskHistoryButton'
@@ -29,6 +29,8 @@ import {
29
29
  } from '../assistDocument/RequestRunInstructionProvider'
30
30
  import {InspectorOnboarding} from '../onboarding/InspectorOnboarding'
31
31
  import {inspectorOnboardingKey, useOnboardingFeature} from '../onboarding/onboardingStore'
32
+ import {TypePathContext} from '../assistDocument/components/AssistDocumentForm'
33
+ import {FieldTitle} from './FieldAutocomplete'
32
34
 
33
35
  const CardWithShadowBelow = styled(Card)`
34
36
  position: relative;
@@ -191,7 +193,13 @@ export function AssistInspector(props: DocumentInspectorProps) {
191
193
  const pathKey = params?.[fieldPathParam]
192
194
  const instructionKey = params?.[instructionParam]
193
195
  const documentPane = useDocumentPane()
194
- const {documentId, documentType, schemaType, onChange: documentOnChange} = documentPane
196
+ const {
197
+ documentId,
198
+ documentType,
199
+ value: docValue,
200
+ schemaType,
201
+ onChange: documentOnChange,
202
+ } = documentPane
195
203
  const {published, draft} = useEditState(documentId, documentType, 'low')
196
204
 
197
205
  const assistableDocId = getAssistableDocId(schemaType, documentId)
@@ -199,12 +207,14 @@ export function AssistInspector(props: DocumentInspectorProps) {
199
207
  documentOnChange,
200
208
  isDocAssistable: isDocAssistable(schemaType, published, draft),
201
209
  })
202
- const selectedField = useSelectedField(schemaType, params[fieldPathParam])
210
+
211
+ const typePath = useTypePath(docValue, pathKey ?? '')
212
+ const selectedField = useSelectedField(schemaType, typePath)
203
213
 
204
214
  const aiDocId = assistDocumentId(documentType)
205
215
 
206
216
  const assistDocument = useStudioAssistDocument({documentId, schemaType})
207
- const assistField = assistDocument?.fields?.find((f) => f.path === pathKey)
217
+ const assistField = assistDocument?.fields?.find((f) => f.path === typePath)
208
218
  const instruction = assistField?.instructions?.find((i) => i._key === instructionKey)
209
219
  const tasks = useMemo(
210
220
  () =>
@@ -244,13 +254,15 @@ export function AssistInspector(props: DocumentInspectorProps) {
244
254
  () =>
245
255
  instruction &&
246
256
  pathKey &&
257
+ typePath &&
247
258
  requestRunInstruction({
248
259
  documentId: assistableDocId,
249
260
  path: pathKey,
261
+ typePath,
250
262
  assistDocumentId: assistDocumentId(documentType),
251
263
  instruction,
252
264
  }),
253
- [instruction, pathKey, documentType, assistableDocId, requestRunInstruction]
265
+ [pathKey, instruction, typePath, documentType, assistableDocId, requestRunInstruction]
254
266
  )
255
267
 
256
268
  const Region = useCallback((_props: any) => {
@@ -282,7 +294,11 @@ export function AssistInspector(props: DocumentInspectorProps) {
282
294
  sizing="border"
283
295
  style={{lineHeight: 0}}
284
296
  >
285
- <AiInspectorHeader onClose={props.onClose} fieldTitle={getFieldTitle(selectedField)} />
297
+ <AiInspectorHeader
298
+ onClose={props.onClose}
299
+ field={selectedField}
300
+ fieldTitle={getFieldTitle(selectedField)}
301
+ />
286
302
 
287
303
  <Card as={Region} flex={1} overflow="auto">
288
304
  <Flex direction="column" style={{minHeight: '100%'}}>
@@ -290,19 +306,21 @@ export function AssistInspector(props: DocumentInspectorProps) {
290
306
  <PresenceOverlay>
291
307
  <Box padding={4}>
292
308
  {selectedField && (
293
- <VirtualizerScrollInstanceProvider
294
- scrollElement={boundary.current}
295
- containerElement={boundary}
296
- >
297
- <DocumentPaneProvider
298
- paneKey={documentPane.paneKey}
299
- index={documentPane.index}
300
- itemId="ai"
301
- pane={paneNode}
309
+ <TypePathContext.Provider value={typePath}>
310
+ <VirtualizerScrollInstanceProvider
311
+ scrollElement={boundary.current}
312
+ containerElement={boundary}
302
313
  >
303
- <DocumentForm />
304
- </DocumentPaneProvider>
305
- </VirtualizerScrollInstanceProvider>
314
+ <DocumentPaneProvider
315
+ paneKey={documentPane.paneKey}
316
+ index={documentPane.index}
317
+ itemId="ai"
318
+ pane={paneNode}
319
+ >
320
+ <DocumentForm />
321
+ </DocumentPaneProvider>
322
+ </VirtualizerScrollInstanceProvider>
323
+ </TypePathContext.Provider>
306
324
  )}
307
325
  </Box>
308
326
  </PresenceOverlay>
@@ -323,7 +341,6 @@ export function AssistInspector(props: DocumentInspectorProps) {
323
341
  </Box>
324
342
  </Flex>
325
343
  </Card>
326
-
327
344
  <CardWithShadowAbove flex="none" paddingX={4} paddingY={3} style={{justifySelf: 'flex-end'}}>
328
345
  <Flex gap={2} flex={1} justify="flex-end">
329
346
  {schemaType?.name && pathKey && instructionKey && (
@@ -352,22 +369,28 @@ export function AssistInspector(props: DocumentInspectorProps) {
352
369
  )
353
370
  }
354
371
 
355
- function AiInspectorHeader(props: {fieldTitle: string; onClose: () => void}) {
356
- const {onClose, fieldTitle} = props
372
+ function AiInspectorHeader(props: {fieldTitle: string; field?: FieldRef; onClose: () => void}) {
373
+ const {onClose, field, fieldTitle} = props
357
374
  const {showOnboarding, dismissOnboarding} = useOnboardingFeature(inspectorOnboardingKey)
358
375
 
359
376
  return (
360
377
  <CardWithShadowBelow flex="none" padding={2}>
361
378
  <Flex flex={1} align="center">
362
379
  <Flex flex={1} padding={3} gap={2} align="center">
363
- <Flex gap={1} align="center">
364
- <Text size={1} weight="semibold">
365
- AI instructions for
366
- </Text>
367
- <Card radius={2} border padding={1} style={{margin: '-4px 0'}}>
380
+ <Flex gap={1} align="center" wrap="wrap" style={{marginTop: '-4px'}}>
381
+ <Box marginTop={1}>
368
382
  <Text size={1} weight="semibold">
369
- {fieldTitle}
383
+ AI instructions for
370
384
  </Text>
385
+ </Box>
386
+ <Card radius={2} border padding={1} marginTop={1}>
387
+ {field ? (
388
+ <FieldTitle field={field} />
389
+ ) : (
390
+ <Text size={1} weight="semibold">
391
+ {fieldTitle}
392
+ </Text>
393
+ )}
371
394
  </Card>
372
395
  </Flex>
373
396
  </Flex>
@@ -11,25 +11,29 @@ interface FieldSelectorProps {
11
11
  fieldPath?: string
12
12
  onSelect: (path: string) => void
13
13
  includeDocument?: boolean
14
+ filter?: (field: FieldRef) => boolean
14
15
  }
15
16
 
16
17
  export function FieldAutocomplete(props: FieldSelectorProps) {
17
- const {id, schemaType, fieldPath, onSelect, includeDocument} = props
18
+ const {id, schemaType, fieldPath, onSelect, includeDocument, filter} = props
18
19
 
19
- const fieldNames = useMemo(() => {
20
+ const fieldRefs = useMemo(() => {
20
21
  if (includeDocument) {
21
22
  return getFieldRefsWithDocument(schemaType)
22
23
  }
23
24
  return getFieldRefs(schemaType)
24
25
  }, [schemaType, includeDocument])
25
26
  const currentField = useMemo(
26
- () => fieldNames.find((f) => f.key === fieldPath),
27
- [fieldPath, fieldNames]
27
+ () => fieldRefs.find((f) => f.key === fieldPath),
28
+ [fieldPath, fieldRefs]
28
29
  )
29
30
 
30
31
  const autocompleteOptions = useMemo(
31
- () => fieldNames.map((field) => ({value: field.key, field})),
32
- [fieldNames]
32
+ () =>
33
+ fieldRefs
34
+ .filter((field) => (filter ? filter(field) : true))
35
+ .map((field) => ({value: field.key, field})),
36
+ [fieldRefs, filter]
33
37
  )
34
38
 
35
39
  const renderOption = useCallback((option: {value: string; field: FieldRef}) => {
@@ -55,33 +59,12 @@ export function FieldAutocomplete(props: FieldSelectorProps) {
55
59
  )
56
60
  }
57
61
 
58
- const splitTitle = field.title.split('/')
59
62
  return (
60
63
  <Card as="button" padding={3} radius={1}>
61
64
  <Flex gap={3}>
62
65
  <Text size={1}>{createElement(field.icon)}</Text>
63
66
 
64
- <Box flex="none">
65
- <Breadcrumbs
66
- separator={
67
- <Text muted size={1}>
68
- /
69
- </Text>
70
- }
71
- space={1}
72
- >
73
- {splitTitle.slice(0, splitTitle.length - 1).map((pt, i) => (
74
- // eslint-disable-next-line react/no-array-index-key
75
- <Text key={i} muted size={1}>
76
- {pt.trim()}
77
- </Text>
78
- ))}
79
-
80
- <Text size={1} weight="medium">
81
- {splitTitle[splitTitle.length - 1]}
82
- </Text>
83
- </Breadcrumbs>
84
- </Box>
67
+ <FieldTitle field={field} />
85
68
  </Flex>
86
69
  </Card>
87
70
  )
@@ -99,7 +82,6 @@ export function FieldAutocomplete(props: FieldSelectorProps) {
99
82
  )
100
83
  }, [])
101
84
 
102
- // const id = useId()
103
85
  return (
104
86
  <Autocomplete
105
87
  fontSize={1}
@@ -117,3 +99,41 @@ export function FieldAutocomplete(props: FieldSelectorProps) {
117
99
  />
118
100
  )
119
101
  }
102
+
103
+ export function FieldTitle(props: {field: FieldRef}) {
104
+ const splitTitle = props.field.title.split('/')
105
+ return (
106
+ <Box flex="none">
107
+ <Breadcrumbs
108
+ style={{
109
+ display: 'flex',
110
+ flexWrap: 'wrap',
111
+ alignItems: 'center',
112
+ marginTop: '-4px',
113
+ }}
114
+ separator={
115
+ <Box marginTop={1}>
116
+ <Text muted size={1}>
117
+ /
118
+ </Text>
119
+ </Box>
120
+ }
121
+ space={1}
122
+ >
123
+ {splitTitle.slice(0, splitTitle.length - 1).map((pt, i) => (
124
+ // eslint-disable-next-line react/no-array-index-key
125
+ <Box key={i} marginTop={1}>
126
+ <Text muted size={1}>
127
+ {pt.trim()}
128
+ </Text>
129
+ </Box>
130
+ ))}
131
+ <Box marginTop={1}>
132
+ <Text size={1} weight="medium">
133
+ {splitTitle[splitTitle.length - 1]}
134
+ </Text>
135
+ </Box>
136
+ </Breadcrumbs>
137
+ </Box>
138
+ )
139
+ }
@@ -7,13 +7,24 @@ import {
7
7
  OlistIcon,
8
8
  StringIcon,
9
9
  } from '@sanity/icons'
10
- import {isObjectSchemaType, ObjectSchemaType, Path, pathToString, SchemaType} from 'sanity'
10
+ import {
11
+ ArraySchemaType,
12
+ isKeySegment,
13
+ isObjectSchemaType,
14
+ ObjectSchemaType,
15
+ Path,
16
+ pathToString,
17
+ SanityDocumentLike,
18
+ SchemaType,
19
+ stringToPath,
20
+ } from 'sanity'
11
21
  import {ComponentType, useContext, useMemo} from 'react'
12
22
  import {AssistInspectorRouteParams, documentRootKey, fieldPathParam} from '../types'
13
23
  import {usePaneRouter} from 'sanity/desk'
14
24
  import {isAssistSupported} from '../helpers/assistSupported'
15
25
  import {isPortableTextArray, isType} from '../helpers/typeUtils'
16
26
  import {SelectedFieldContext} from '../assistDocument/components/SelectedFieldContext'
27
+ import {extractWithPath} from '@sanity/mutator'
17
28
 
18
29
  export interface FieldRef {
19
30
  key: string
@@ -21,9 +32,10 @@ export interface FieldRef {
21
32
  title: string
22
33
  schemaType: SchemaType
23
34
  icon: ComponentType
35
+ synthetic?: boolean
24
36
  }
25
37
 
26
- const maxDepth = 4
38
+ const maxDepth = 6
27
39
 
28
40
  export function getTypeIcon(schemaType: SchemaType) {
29
41
  let t: SchemaType | undefined = schemaType
@@ -73,7 +85,7 @@ export function getFieldRefs(
73
85
  const path: Path = parent ? [...parent.path, field.name] : [field.name]
74
86
  const title = field.type.title ?? field.name
75
87
  const fieldRef: FieldRef = {
76
- key: pathToString(path),
88
+ key: patchableKey(pathToString(path)),
77
89
  path,
78
90
  title: parent ? [parent.title, title].join(' / ') : title,
79
91
  schemaType: field.type,
@@ -82,13 +94,81 @@ export function getFieldRefs(
82
94
  const fields =
83
95
  field.type.jsonType === 'object' ? getFieldRefs(field.type, fieldRef, depth + 1) : []
84
96
 
97
+ const syntheticFields =
98
+ field.type.jsonType === 'array' ? getSyntheticFields(field.type, fieldRef, depth + 1) : []
85
99
  if (!isAssistSupported(field.type, true)) {
100
+ return [...fields, ...syntheticFields]
101
+ }
102
+ return [fieldRef, ...fields, ...syntheticFields]
103
+ })
104
+ }
105
+
106
+ function getSyntheticFields(schemaType: ArraySchemaType, parent?: FieldRef, depth = 0) {
107
+ if (depth >= maxDepth) {
108
+ return []
109
+ }
110
+
111
+ return schemaType.of
112
+ .filter((itemType) => !isType(itemType, 'block'))
113
+ .flatMap((itemType) => {
114
+ const segment = {_key: itemType.name}
115
+ const title = itemType.title ?? itemType.name
116
+ const path: Path = parent ? [...parent.path, segment] : [segment]
117
+ const fieldRef: FieldRef = {
118
+ key: patchableKey(pathToString(path)),
119
+ path,
120
+ title: parent ? [parent.title, title].join(' / ') : title,
121
+ schemaType: itemType,
122
+ icon: getTypeIcon(itemType),
123
+ synthetic: true,
124
+ }
125
+ const fields =
126
+ itemType.jsonType === 'object' ? getFieldRefs(itemType, fieldRef, depth + 1) : []
127
+
128
+ if (!isAssistSupported(itemType, true)) {
86
129
  return fields
87
130
  }
88
131
  return [fieldRef, ...fields]
89
132
  })
90
133
  }
91
134
 
135
+ export function getTypePath(doc: SanityDocumentLike, pathString: string) {
136
+ if (!pathString) {
137
+ return undefined
138
+ }
139
+
140
+ const path = stringToPath(pathString)
141
+ const currentPath: Path = []
142
+ let valid = true
143
+ const syntheticPath = path.map((segment) => {
144
+ currentPath.push(segment)
145
+
146
+ if (isKeySegment(segment)) {
147
+ const match = extractWithPath(pathToString(currentPath), doc)[0]
148
+ const value = match?.value
149
+ if (match && value && typeof value === 'object' && '_type' in value) {
150
+ return {_key: value._type as string}
151
+ }
152
+ valid = false
153
+ }
154
+ return segment
155
+ })
156
+
157
+ return valid ? patchableKey(pathToString(syntheticPath)) : undefined
158
+ }
159
+
160
+ /**
161
+ * mutator crashes if path contains certain letters
162
+ * @param pathKey
163
+ */
164
+ function patchableKey(pathKey: string) {
165
+ return pathKey.replace(/[=]=/g, ':').replace(/[[\]]/g, '|').replace(/"/g, '')
166
+ }
167
+
168
+ export function useTypePath(doc: SanityDocumentLike, pathString: string) {
169
+ return useMemo(() => getTypePath(doc, pathString), [doc, pathString])
170
+ }
171
+
92
172
  export function useSelectedField(
93
173
  documentSchemaType?: SchemaType,
94
174
  path?: string
@@ -115,7 +195,7 @@ export function useSelectedFieldTitle() {
115
195
 
116
196
  export function getFieldTitle(field?: FieldRef) {
117
197
  const schemaType = field?.schemaType
118
- return schemaType?.title ?? schemaType?.name ?? 'Untitled'
198
+ return field?.title ?? schemaType?.title ?? schemaType?.name ?? 'Untitled'
119
199
  }
120
200
 
121
201
  export function useAiPaneRouter() {
@@ -11,7 +11,7 @@ import {pluginTitle, pluginTitleShort} from '../constants'
11
11
  import {useAssistSupported} from '../helpers/useAssistSupported'
12
12
  import {useAssistDocumentContext} from '../assistDocument/AssistDocumentContext'
13
13
  import {getInstructionTitle, usePathKey} from '../helpers/misc'
14
- import {fieldPathParam, instructionParam, StudioInstruction} from '../types'
14
+ import {documentRootKey, fieldPathParam, instructionParam, StudioInstruction} from '../types'
15
15
  import {aiInspectorId} from '../assistInspector/constants'
16
16
  import {getIcon} from '../assistDocument/components/instruction/appearance/IconInput'
17
17
  import {useAssistDocumentContextValue} from '../assistDocument/hooks/useAssistDocumentContextValue'
@@ -21,6 +21,8 @@ import {
21
21
  } from '../assistDocument/RequestRunInstructionProvider'
22
22
  import {PrivateIcon} from './PrivateIcon'
23
23
  import {generateCaptionsActions} from './generateCaptionActions'
24
+ import {useDocumentPane} from 'sanity/desk'
25
+ import {useSelectedField, useTypePath} from '../assistInspector/helpers'
24
26
 
25
27
  function node(node: DocumentFieldActionItem | DocumentFieldActionGroup) {
26
28
  return node
@@ -30,7 +32,6 @@ export const assistFieldActions: DocumentFieldAction = {
30
32
  name: 'sanity-assist-actions',
31
33
  useAction(props) {
32
34
  const {schemaType} = props
33
- const assistSupported = useAssistSupported(props.path, schemaType)
34
35
 
35
36
  const isDocumentLevel = props.path.length === 0
36
37
 
@@ -54,9 +55,11 @@ export const assistFieldActions: DocumentFieldAction = {
54
55
  : // eslint-disable-next-line react-hooks/rules-of-hooks
55
56
  useAssistDocumentContext()
56
57
 
58
+ const {value: docValue} = useDocumentPane()
57
59
  const currentUser = useCurrentUser()
58
60
  const isHidden = !assistDocument
59
61
  const pathKey = usePathKey(props.path)
62
+ const typePath = useTypePath(docValue, pathKey)
60
63
  const assistDocumentId = assistDocument?._id
61
64
 
62
65
  const assistableDocId = getAssistableDocId(documentSchemaType, documentId)
@@ -65,10 +68,17 @@ export const assistFieldActions: DocumentFieldAction = {
65
68
  isDocAssistable: documentIsAssistable ?? false,
66
69
  })
67
70
 
71
+ const isSelectable = !!useSelectedField(documentSchemaType, typePath)
72
+ const assistSupported = useAssistSupported(props.path, schemaType) && isSelectable
73
+
68
74
  const fieldAssist = useMemo(
69
- () => (assistDocument?.fields ?? []).find((f) => f.path == pathKey),
70
- [assistDocument?.fields, pathKey]
75
+ () =>
76
+ (assistDocument?.fields ?? []).find(
77
+ (f) => f.path === typePath || (pathKey === documentRootKey && f.path === pathKey)
78
+ ),
79
+ [assistDocument?.fields, pathKey, typePath]
71
80
  )
81
+
72
82
  const fieldAssistKey = fieldAssist?._key
73
83
  const isInspectorOpen = inspector?.name === aiInspectorId
74
84
  const isPathSelected = pathKey === selectedPath
@@ -96,10 +106,11 @@ export const assistFieldActions: DocumentFieldAction = {
96
106
  documentId: assistableDocId,
97
107
  assistDocumentId,
98
108
  path: pathKey,
109
+ typePath,
99
110
  instruction,
100
111
  })
101
112
  },
102
- [requestRunInstruction, assistableDocId, pathKey, assistDocumentId, fieldAssistKey]
113
+ [requestRunInstruction, assistableDocId, pathKey, typePath, assistDocumentId, fieldAssistKey]
103
114
  )
104
115
 
105
116
  const privateInstructions = useMemo(
@@ -3,8 +3,5 @@ import {useMemo} from 'react'
3
3
  import {isAssistSupported} from './assistSupported'
4
4
 
5
5
  export function useAssistSupported(path: Path, schemaType: SchemaType) {
6
- return useMemo(
7
- () => path.every((p) => typeof p === 'string') && isAssistSupported(schemaType),
8
- [path, schemaType]
9
- )
6
+ return useMemo(() => isAssistSupported(schemaType), [schemaType])
10
7
  }
@@ -367,7 +367,7 @@ export const fieldInstructions = defineType({
367
367
 
368
368
  export const assistDocumentSchema = defineType({
369
369
  //NOTE: this is a document type. Using object here ensures it does not appear in structure menus
370
- type: 'object',
370
+ type: 'document',
371
371
  //workaround for using object and not document
372
372
  ...({liveEdit: true} as any),
373
373
  name: assistDocumentTypeName,
@@ -13,6 +13,7 @@ export interface RunInstructionRequest {
13
13
  documentId: string
14
14
  assistDocumentId: string
15
15
  path: string
16
+ typePath?: string
16
17
  instructionKey: string
17
18
  userId?: string
18
19
  userTexts?: UserTextInstance[]