@sanity/assist 5.0.4 → 6.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 (131) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +28 -254
  3. package/dist/index.d.ts +322 -410
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +3205 -2670
  6. package/dist/index.js.map +1 -1
  7. package/package.json +41 -81
  8. package/dist/index.cjs +0 -4239
  9. package/dist/index.cjs.map +0 -1
  10. package/dist/index.d.cts +0 -791
  11. package/sanity.json +0 -8
  12. package/src/_lib/connector/ConnectFromRegion.tsx +0 -25
  13. package/src/_lib/connector/ConnectToRegion.tsx +0 -23
  14. package/src/_lib/connector/ConnectorRegion.tsx +0 -24
  15. package/src/_lib/connector/ConnectorsProvider.tsx +0 -20
  16. package/src/_lib/connector/ConnectorsStore.ts +0 -122
  17. package/src/_lib/connector/ConnectorsStoreContext.ts +0 -5
  18. package/src/_lib/connector/helpers.ts +0 -5
  19. package/src/_lib/connector/index.ts +0 -9
  20. package/src/_lib/connector/mapConnectorToLine.ts +0 -83
  21. package/src/_lib/connector/types.ts +0 -56
  22. package/src/_lib/connector/useConnectorsStore.ts +0 -14
  23. package/src/_lib/connector/useRegionRects.ts +0 -142
  24. package/src/_lib/fixedListenQuery.ts +0 -101
  25. package/src/_lib/form/DocumentForm.tsx +0 -201
  26. package/src/_lib/form/constants.ts +0 -1
  27. package/src/_lib/form/helpers.ts +0 -32
  28. package/src/_lib/form/index.ts +0 -1
  29. package/src/_lib/randomKey.ts +0 -29
  30. package/src/_lib/useListeningQuery.ts +0 -62
  31. package/src/_lib/usePrevious.ts +0 -9
  32. package/src/assistConnectors/AssistConnectorsOverlay.tsx +0 -133
  33. package/src/assistConnectors/ConnectorPath.tsx +0 -63
  34. package/src/assistConnectors/draw/arrowPath.ts +0 -9
  35. package/src/assistConnectors/draw/connectorPath.ts +0 -142
  36. package/src/assistConnectors/index.ts +0 -1
  37. package/src/assistDocument/AssistDocumentContext.tsx +0 -51
  38. package/src/assistDocument/AssistDocumentContextProvider.tsx +0 -17
  39. package/src/assistDocument/AssistDocumentInput.tsx +0 -61
  40. package/src/assistDocument/AssistDocumentLayout.tsx +0 -12
  41. package/src/assistDocument/RequestRunInstructionProvider.tsx +0 -61
  42. package/src/assistDocument/components/AssistDocumentForm.tsx +0 -287
  43. package/src/assistDocument/components/AssistTypeContext.tsx +0 -7
  44. package/src/assistDocument/components/FieldRefPreview.tsx +0 -26
  45. package/src/assistDocument/components/InstructionsArrayField.tsx +0 -8
  46. package/src/assistDocument/components/InstructionsArrayInput.tsx +0 -27
  47. package/src/assistDocument/components/SelectedFieldContext.tsx +0 -10
  48. package/src/assistDocument/components/generic/HiddenFieldTitle.tsx +0 -5
  49. package/src/assistDocument/components/helpers.ts +0 -21
  50. package/src/assistDocument/components/instruction/BackToInstructionsLink.tsx +0 -32
  51. package/src/assistDocument/components/instruction/FieldRefInput.tsx +0 -54
  52. package/src/assistDocument/components/instruction/InstructionInput.tsx +0 -89
  53. package/src/assistDocument/components/instruction/InstructionOutputField.tsx +0 -46
  54. package/src/assistDocument/components/instruction/InstructionOutputInput.tsx +0 -206
  55. package/src/assistDocument/components/instruction/PromptInput.tsx +0 -59
  56. package/src/assistDocument/components/instruction/appearance/IconInput.tsx +0 -46
  57. package/src/assistDocument/components/instruction/appearance/InstructionVisibility.tsx +0 -37
  58. package/src/assistDocument/hooks/useAssistDocumentContextValue.tsx +0 -127
  59. package/src/assistDocument/hooks/useDocumentState.ts +0 -6
  60. package/src/assistDocument/hooks/useInstructionToaster.tsx +0 -75
  61. package/src/assistDocument/hooks/useStudioAssistDocument.ts +0 -99
  62. package/src/assistDocument/index.ts +0 -1
  63. package/src/assistFormComponents/AssistField.tsx +0 -63
  64. package/src/assistFormComponents/AssistFormBlock.tsx +0 -31
  65. package/src/assistFormComponents/AssistInlineFormBlock.tsx +0 -13
  66. package/src/assistFormComponents/AssistItem.tsx +0 -21
  67. package/src/assistFormComponents/validation/listItem.tsx +0 -63
  68. package/src/assistFormComponents/validation/validationList.tsx +0 -90
  69. package/src/assistInspector/AssistInspector.tsx +0 -419
  70. package/src/assistInspector/FieldAutocomplete.tsx +0 -146
  71. package/src/assistInspector/InstructionTaskHistoryButton.tsx +0 -262
  72. package/src/assistInspector/constants.ts +0 -1
  73. package/src/assistInspector/helpers.ts +0 -211
  74. package/src/assistInspector/index.ts +0 -27
  75. package/src/assistLayout/AiAssistanceConfigContext.tsx +0 -32
  76. package/src/assistLayout/AiAssistanceConfigProvider.tsx +0 -98
  77. package/src/assistLayout/AssistLayout.tsx +0 -39
  78. package/src/assistLayout/RunInstructionProvider.tsx +0 -278
  79. package/src/assistLayout/fieldRefCache.tsx +0 -34
  80. package/src/assistTypes.ts +0 -83
  81. package/src/components/AssistFeatureBadge.tsx +0 -9
  82. package/src/components/FadeInContent.tsx +0 -40
  83. package/src/components/HideReferenceChangedBannerInput.tsx +0 -25
  84. package/src/components/ImageContext.tsx +0 -85
  85. package/src/components/SafeValueInput.tsx +0 -74
  86. package/src/components/TimeAgo.tsx +0 -18
  87. package/src/constants.ts +0 -20
  88. package/src/fieldActions/PrivateIcon.tsx +0 -20
  89. package/src/fieldActions/assistFieldActions.tsx +0 -320
  90. package/src/fieldActions/customFieldActions.tsx +0 -333
  91. package/src/fieldActions/generateCaptionActions.tsx +0 -77
  92. package/src/fieldActions/generateImageActions.tsx +0 -58
  93. package/src/fieldActions/useUserInput.ts +0 -107
  94. package/src/globals.d.ts +0 -4
  95. package/src/helpers/assistSupported.ts +0 -49
  96. package/src/helpers/conditionalMembers.test.ts +0 -319
  97. package/src/helpers/conditionalMembers.ts +0 -134
  98. package/src/helpers/ids.test.ts +0 -28
  99. package/src/helpers/ids.ts +0 -23
  100. package/src/helpers/misc.ts +0 -25
  101. package/src/helpers/styleguide.ts +0 -24
  102. package/src/helpers/typeUtils.ts +0 -60
  103. package/src/helpers/useAssistSupported.ts +0 -8
  104. package/src/index.ts +0 -26
  105. package/src/onboarding/FirstAssistedPathProvider.tsx +0 -30
  106. package/src/onboarding/InspectorOnboarding.tsx +0 -47
  107. package/src/onboarding/onboardingStore.ts +0 -32
  108. package/src/plugin.tsx +0 -162
  109. package/src/presence/AiFieldPresence.tsx +0 -28
  110. package/src/presence/AssistAvatar.tsx +0 -96
  111. package/src/presence/AssistDocumentPresence.tsx +0 -50
  112. package/src/presence/useAssistPresence.ts +0 -64
  113. package/src/schemas/assistDocumentSchema.tsx +0 -497
  114. package/src/schemas/contextDocumentSchema.tsx +0 -57
  115. package/src/schemas/index.ts +0 -69
  116. package/src/schemas/serialize/SchemTypeTool.tsx +0 -103
  117. package/src/schemas/serialize/schemaUtils.ts +0 -38
  118. package/src/schemas/serialize/serializeSchema.test.ts +0 -819
  119. package/src/schemas/serialize/serializeSchema.ts +0 -224
  120. package/src/schemas/serializedSchemaTypeSchema.ts +0 -60
  121. package/src/schemas/typeDefExtensions.ts +0 -127
  122. package/src/translate/FieldTranslationProvider.tsx +0 -382
  123. package/src/translate/getLanguageParams.ts +0 -26
  124. package/src/translate/languageStore.ts +0 -18
  125. package/src/translate/paths.test.ts +0 -181
  126. package/src/translate/paths.ts +0 -183
  127. package/src/translate/translateActions.tsx +0 -205
  128. package/src/translate/types.ts +0 -197
  129. package/src/types.ts +0 -220
  130. package/src/useApiClient.ts +0 -338
  131. package/v2-incompatible.js +0 -11
@@ -1,320 +0,0 @@
1
- import {ControlsIcon, SparklesIcon} from '@sanity/icons'
2
- import {useCallback, useMemo, useRef} from 'react'
3
- import {
4
- type DocumentFieldAction,
5
- type DocumentFieldActionGroup,
6
- type DocumentFieldActionItem,
7
- pathToString,
8
- stringToPath,
9
- typed,
10
- useCurrentUser,
11
- } from 'sanity'
12
- import {useDocumentPane} from 'sanity/structure'
13
-
14
- import {useAssistDocumentContext} from '../assistDocument/AssistDocumentContext'
15
- import {getIcon} from '../assistDocument/components/instruction/appearance/IconInput'
16
- import {useRequestRunInstruction} from '../assistDocument/RequestRunInstructionProvider'
17
- import {aiInspectorId} from '../assistInspector/constants'
18
- import {getTypePath, useSelectedField, useTypePath} from '../assistInspector/helpers'
19
- import {pluginTitleShort} from '../constants'
20
- import {isSchemaAssistEnabled} from '../helpers/assistSupported'
21
- import {getConditionalMembers} from '../helpers/conditionalMembers'
22
- import {getInstructionTitle, usePathKey} from '../helpers/misc'
23
- import {useAssistSupported} from '../helpers/useAssistSupported'
24
- import {translateActions, type TranslateProps} from '../translate/translateActions'
25
- import {documentRootKey, fieldPathParam, instructionParam, type StudioInstruction} from '../types'
26
- import {generateCaptionsActions} from './generateCaptionActions'
27
- import {generateImagActions} from './generateImageActions'
28
- import {PrivateIcon} from './PrivateIcon'
29
- import {AgentActionConditionalPath, useCustomFieldActions} from './customFieldActions'
30
- import {AgentActionPath} from '@sanity/client/stega'
31
-
32
- function node(node: DocumentFieldActionItem | DocumentFieldActionGroup) {
33
- return node
34
- }
35
-
36
- export const assistFieldActions: DocumentFieldAction = {
37
- name: 'sanity-assist-actions',
38
- useAction(props) {
39
- const {schemaType} = props
40
-
41
- const {
42
- assistDocument,
43
- documentIsNew,
44
- documentIsAssistable,
45
- openInspector,
46
- closeInspector,
47
- inspector,
48
- documentOnChange,
49
- documentSchemaType,
50
- selectedPath,
51
- assistableDocumentId,
52
- fieldRefsByTypePath,
53
- } = useAssistDocumentContext()
54
-
55
- const {value: docValue, formState} = useDocumentPane()
56
- const docValueRef = useRef(docValue)
57
- const formStateRef = useRef(formState)
58
- formStateRef.current = formState
59
-
60
- const currentUser = useCurrentUser()
61
- const isHidden = !assistDocument
62
- const pathKey = usePathKey(props.path)
63
- const typePath = useTypePath(docValue, pathKey)
64
- const assistDocumentId = assistDocument?._id
65
-
66
- const {requestRunInstruction} = useRequestRunInstruction({
67
- documentOnChange,
68
- isDocAssistable: documentIsAssistable ?? false,
69
- })
70
-
71
- const isSelectable = !!useSelectedField(documentSchemaType, typePath)
72
- const assistSupported =
73
- useAssistSupported(props.path, schemaType) &&
74
- isSelectable &&
75
- isSchemaAssistEnabled(documentSchemaType) &&
76
- schemaType.readOnly !== true
77
-
78
- const fieldAssist = useMemo(
79
- () =>
80
- (assistDocument?.fields ?? []).find(
81
- (f) => f.path === typePath || (pathKey === documentRootKey && f.path === pathKey),
82
- ),
83
- [assistDocument?.fields, pathKey, typePath],
84
- )
85
-
86
- const fieldAssistKey = fieldAssist?._key
87
- const isInspectorOpen = inspector?.name === aiInspectorId
88
- const isPathSelected = pathKey === selectedPath
89
- const isSelected = isInspectorOpen && isPathSelected
90
-
91
- const imageCaptionAction = generateCaptionsActions.useAction(props)
92
- const imageGenAction = generateImagActions.useAction(props)
93
- const translateAction = translateActions.useAction(
94
- typed<TranslateProps>({
95
- ...props,
96
- documentId: assistableDocumentId,
97
- documentIsAssistable,
98
- documentSchemaType,
99
- }),
100
- )
101
- const manageInstructions = useCallback(
102
- () =>
103
- isSelected
104
- ? closeInspector(aiInspectorId)
105
- : openInspector(aiInspectorId, {
106
- [fieldPathParam]: pathKey,
107
- [instructionParam]: undefined as any,
108
- }),
109
- [openInspector, closeInspector, isSelected, pathKey],
110
- )
111
-
112
- const onInstructionAction = useCallback(
113
- (instruction: StudioInstruction) => {
114
- if (!pathKey || !fieldAssistKey || !assistDocumentId || !assistableDocumentId) {
115
- return
116
- }
117
- requestRunInstruction({
118
- documentId: assistableDocumentId,
119
- assistDocumentId,
120
- path: pathKey,
121
- typePath,
122
- instruction,
123
- conditionalMembers: formStateRef.current
124
- ? getConditionalMembers(formStateRef.current)
125
- : [],
126
- })
127
- },
128
- [
129
- requestRunInstruction,
130
- assistableDocumentId,
131
- pathKey,
132
- typePath,
133
- assistDocumentId,
134
- fieldAssistKey,
135
- ],
136
- )
137
-
138
- const privateInstructions = useMemo(
139
- () =>
140
- fieldAssist?.instructions?.filter((i) => i.userId && i.userId === currentUser?.id) || [],
141
- [fieldAssist?.instructions, currentUser],
142
- )
143
-
144
- const sharedInstructions = useMemo(
145
- () => fieldAssist?.instructions?.filter((i) => !i.userId) || [],
146
- [fieldAssist?.instructions],
147
- )
148
-
149
- const instructions = useMemo(
150
- () => [...privateInstructions, ...sharedInstructions],
151
- [privateInstructions, sharedInstructions],
152
- )
153
-
154
- const runInstructionsGroup = useMemo(() => {
155
- return instructions?.length || imageCaptionAction || translateAction || imageGenAction
156
- ? node({
157
- type: 'group',
158
- icon: () => null,
159
- title: 'Run instructions',
160
- children: [
161
- ...(instructions?.map((instruction) =>
162
- instructionItem({
163
- instruction,
164
- isPrivate: Boolean(instruction.userId && instruction.userId === currentUser?.id),
165
- onInstructionAction,
166
- hidden: isHidden,
167
- documentIsNew: !!documentIsNew,
168
- assistSupported,
169
- }),
170
- ) || []),
171
- imageCaptionAction,
172
- imageGenAction,
173
- ].filter((a): a is DocumentFieldActionItem => !!a),
174
- expanded: true,
175
- })
176
- : undefined
177
- }, [
178
- instructions,
179
- currentUser?.id,
180
- onInstructionAction,
181
- isHidden,
182
- documentIsNew,
183
- assistSupported,
184
- imageCaptionAction,
185
- translateAction,
186
- imageGenAction,
187
- ])
188
-
189
- const getDocumentValue = useCallback(() => {
190
- return docValueRef.current
191
- }, [])
192
-
193
- const getConditionalPaths: () => AgentActionConditionalPath[] = useCallback(() => {
194
- return (formStateRef.current ? getConditionalMembers(formStateRef.current) : []).flatMap(
195
- (cm) => {
196
- const path = stringToPath(cm.path)
197
- if (path.some((s) => typeof s === 'number')) {
198
- //agent actions does not support indexed paths
199
- return []
200
- }
201
- return {
202
- ...cm,
203
- path: path as AgentActionPath,
204
- }
205
- },
206
- )
207
- }, [])
208
-
209
- const parentSchemaType = useMemo(() => {
210
- if (!props.path.length) {
211
- return undefined
212
- } else if (props.path.length === 1) {
213
- return documentSchemaType
214
- }
215
- const parentPath = props.path.slice(0, -1)
216
- const typePath = getTypePath(docValueRef.current, pathToString(parentPath))
217
- return typePath ? fieldRefsByTypePath[typePath]?.schemaType : undefined
218
- }, [fieldRefsByTypePath, props.path, documentSchemaType])
219
-
220
- const customActions = useCustomFieldActions({
221
- actionType: props.path.length ? 'field' : 'document',
222
- documentIdForAction: assistableDocumentId,
223
- schemaType,
224
- documentSchemaType,
225
- path: props.path,
226
- getDocumentValue,
227
- getConditionalPaths,
228
- parentSchemaType,
229
- })
230
-
231
- const manageInstructionsItem = useMemo(
232
- () =>
233
- node({
234
- type: 'action',
235
- icon: ControlsIcon,
236
- title: 'Manage instructions',
237
- onAction: manageInstructions,
238
- selected: isSelected,
239
- }),
240
- [manageInstructions, isSelected],
241
- )
242
-
243
- const group = useMemo(
244
- () =>
245
- node({
246
- type: 'group',
247
- icon: SparklesIcon,
248
- title: pluginTitleShort,
249
- children: [
250
- runInstructionsGroup,
251
- translateAction,
252
- ...customActions,
253
- assistSupported && manageInstructionsItem,
254
- ]
255
- .filter((c): c is DocumentFieldActionItem | DocumentFieldActionGroup => !!c)
256
- .filter((c) => (c.type === 'group' ? c.children.length : true)),
257
- expanded: false,
258
- renderAsButton: true,
259
- hidden: !assistSupported && !imageCaptionAction && !translateAction && !imageGenAction,
260
- }),
261
- [
262
- //documentIsNew,
263
- runInstructionsGroup,
264
- manageInstructionsItem,
265
- assistSupported,
266
- imageCaptionAction,
267
- translateAction,
268
- imageGenAction,
269
- customActions,
270
- ],
271
- )
272
-
273
- const emptyAction = useMemo(
274
- () =>
275
- node({
276
- type: 'action',
277
- hidden: !assistSupported,
278
- icon: SparklesIcon,
279
- onAction: manageInstructions,
280
- renderAsButton: true,
281
- title: pluginTitleShort,
282
- selected: isSelected,
283
- }),
284
- [assistSupported, manageInstructions, isSelected],
285
- )
286
-
287
- // If there are no instructions, we don't want to render the group
288
- if (
289
- !instructions?.length &&
290
- !imageCaptionAction &&
291
- !translateAction &&
292
- !imageGenAction &&
293
- !customActions.length
294
- ) {
295
- return emptyAction
296
- }
297
-
298
- return group
299
- },
300
- }
301
-
302
- function instructionItem(props: {
303
- instruction: StudioInstruction
304
- isPrivate: boolean
305
- onInstructionAction: (ins: StudioInstruction) => void
306
- assistSupported: boolean
307
- documentIsNew: boolean
308
- hidden: boolean
309
- }) {
310
- const {hidden, isPrivate, onInstructionAction, assistSupported, instruction} = props
311
- return node({
312
- type: 'action',
313
- icon: getIcon(instruction.icon),
314
- iconRight: isPrivate ? PrivateIcon : undefined,
315
- title: getInstructionTitle(instruction),
316
- onAction: () => onInstructionAction(instruction),
317
- disabled: !assistSupported,
318
- hidden,
319
- })
320
- }
@@ -1,333 +0,0 @@
1
- import {
2
- DocumentFieldActionDivider,
3
- DocumentFieldActionGroup,
4
- DocumentFieldActionItem,
5
- DocumentFieldActionNode,
6
- ObjectSchemaType,
7
- Path,
8
- SanityDocumentLike,
9
- SchemaType,
10
- useWorkspaceSchemaId,
11
- } from 'sanity'
12
- import {useMemo} from 'react'
13
- import {useAiAssistanceConfig} from '../assistLayout/AiAssistanceConfigContext'
14
- import {ToastParams, useToast} from '@sanity/ui'
15
- import {AgentActionPath} from '@sanity/client/stega'
16
- import {useAssistDocumentContext} from '../assistDocument/AssistDocumentContext'
17
- import {
18
- documentRootKey,
19
- fieldPresenceTypeName,
20
- InstructionTask,
21
- instructionTaskTypeName,
22
- } from '../types'
23
- import {randomKey} from '../_lib/randomKey'
24
- import {isDefined} from '../helpers/misc'
25
-
26
- export interface AgentActionConditionalPath {
27
- path: AgentActionPath
28
- readOnly: boolean
29
- hidden: boolean
30
- }
31
-
32
- export interface AssistFieldActionProps {
33
- /**
34
- * `actionType` will be `document` for action invoked from the top right document action menu, and
35
- * `field` when invoked from a field action menu.
36
- */
37
- actionType: 'document' | 'field'
38
- /**
39
- * This is the id of the current document pane; it contains `drafts.`or `versions. prefix` ect depending on context.
40
- * Use this for `documentId` when calling any `client.agent.action`.
41
- *
42
- * It is generally recommended to call actions from the studio like this:
43
- * ```ts
44
- * await client.agent.action.generate({
45
- * targetDocument: {
46
- * operation: 'createIfNotExists',
47
- * _id: props.documentIdForAction,
48
- * _type: props.documentSchemaType.name,
49
- * initialValues: props.getDocumentValue()
50
- * },
51
- * //...
52
- * })
53
- * ```
54
- */
55
- documentIdForAction: string
56
-
57
- /**
58
- * Schema type of the current document.
59
- * @see documentIdForAction
60
- */
61
- documentSchemaType: ObjectSchemaType
62
-
63
- /**
64
- * Returns the current document value.
65
- *
66
- * Prefer passing this function to your hooks instead of passing the document value directly to avoid unnecessary re-renders.
67
- * @see documentIdForAction
68
- */
69
- getDocumentValue: () => SanityDocumentLike
70
-
71
- /**
72
- * Returns the current readOnly and hidden state of all conditional members in the current document form.
73
- *
74
- * Intended to be passed to agent actions `conditionalPaths.paths`.
75
- */
76
- getConditionalPaths: () => AgentActionConditionalPath[]
77
-
78
- /**
79
- * `schemaId` for the current workspace.
80
- *
81
- * Note: the workspace schema has to be deployed using `sanity schema deploy` or `sanity deploy`.
82
- *
83
- * Use this for `schemaId` when calling any `client.agent.action`.
84
- *
85
- * It is generally recommended to call actions from the studio like this:
86
- * ```ts
87
- * await client.agent.action.generate({
88
- * targetDocument: {
89
- * operation: 'createIfNotExists',
90
- * _id: props.documentIdForAction,
91
- * _type: props.documentSchemaType.name,
92
- * initialValues: props.getDocumentValue()
93
- * },
94
- * //...
95
- * })
96
- *
97
- * ```
98
- */
99
- schemaId: string
100
-
101
- /**
102
- * This is the schema type of the field the actions will be attached to (ie, schemaType for `path`)
103
- *
104
- * It can be used with agent actions using `target.path`, to scope the action to a specific field.
105
- *
106
- * It is generally recommended to call actions from the studio like this:
107
- * ```ts
108
- * await client.agent.action.generate({
109
- * targetDocument: {
110
- * operation: 'createIfNotExists',
111
- * _id: props.documentIdForAction,
112
- * _type: props.documentSchemaType.name,
113
- * initialValues: props.getDocumentValue()
114
- * },
115
- * target: {
116
- * path: props.path
117
- * },
118
- * })
119
- * ```
120
- */
121
- path: AgentActionPath
122
-
123
- /**
124
- * This is the schema type of the field the actions will be attached to (ie, schemaType for `path`).
125
- *
126
- * Typically useful to dynamically return different actions based on the schema type of the field.
127
- *
128
- * ```ts
129
- * if(isObjectSchemaType(schemaType)) {
130
- * return [
131
- * defineAssistFieldAction({
132
- * title: 'Fill the object fields',
133
- * icon: RobotIcon,
134
- * onAction: () => {
135
- * //...
136
- * }
137
- * })
138
- * ]
139
- * }
140
- * return useMemo(() => {
141
- *
142
- *
143
- * }, [])
144
- *
145
- * ```
146
- */
147
- schemaType: SchemaType
148
-
149
- /**
150
- * Schema type of the parent field or array item holding this field.
151
- *
152
- * This can be undefined if the action was unable to resolve the parent type is excluded from AI Assist.
153
- *
154
- * @see schemaType
155
- * @see documentSchemaType
156
- */
157
- parentSchemaType?: SchemaType
158
- }
159
-
160
- export type AssistFieldActionNode =
161
- | AssistFieldActionItem
162
- | AssistFieldActionGroup
163
- | DocumentFieldActionDivider
164
-
165
- export type AssistFieldActionItem = Omit<
166
- DocumentFieldActionItem,
167
- 'renderAsButton' | 'selected' | 'onAction'
168
- > & {
169
- onAction: () => void | Promise<void>
170
- }
171
-
172
- export type AssistFieldActionGroup = Omit<
173
- DocumentFieldActionGroup,
174
- 'renderAsButton' | 'expanded' | 'children'
175
- > & {
176
- /**
177
- * `children` can include undefined entries in the action array. These will be filtered out.
178
- * If the group has no defined children, the group will also be filtered out.
179
- */
180
- children: (AssistFieldActionNode | undefined)[]
181
- }
182
-
183
- type PushToast = (params: ToastParams) => string
184
-
185
- export function defineAssistFieldAction(
186
- action: Omit<AssistFieldActionItem, 'type'>,
187
- ): AssistFieldActionItem {
188
- return {
189
- ...action,
190
- type: 'action',
191
- }
192
- }
193
-
194
- export function defineFieldActionDivider(): DocumentFieldActionDivider {
195
- return {
196
- type: 'divider',
197
- }
198
- }
199
-
200
- export function defineAssistFieldActionGroup(
201
- group: Omit<AssistFieldActionGroup, 'type'>,
202
- ): AssistFieldActionGroup {
203
- return {
204
- ...group,
205
- type: 'group',
206
- }
207
- }
208
-
209
- export function useCustomFieldActions(
210
- props: Omit<AssistFieldActionProps, 'schemaId' | 'path'> & {path: Path},
211
- ) {
212
- const {
213
- config: {fieldActions},
214
- } = useAiAssistanceConfig()
215
- const {addSyntheticTask, removeSyntheticTask} = useAssistDocumentContext()
216
-
217
- const schemaId = useWorkspaceSchemaId()
218
- const {push: pushToast} = useToast()
219
- const configActions = fieldActions?.useFieldActions?.({
220
- ...props,
221
- schemaId,
222
- path: props.path as AgentActionPath,
223
- })
224
-
225
- return useMemo(() => {
226
- const title = fieldActions?.title
227
- const customActions = configActions
228
- ?.filter(isDefined)
229
- .map((node) => {
230
- return createSafeNode({
231
- node,
232
- pushToast,
233
- addSyntheticTask,
234
- removeSyntheticTask,
235
- })
236
- })
237
- .filter(isDefined)
238
- const onlyGroups =
239
- customActions?.length && customActions?.every((node) => node.type === 'group')
240
- const groups = customActions?.length
241
- ? onlyGroups
242
- ? customActions
243
- : [
244
- {
245
- type: 'group',
246
- title: title || 'Custom actions',
247
- children: customActions,
248
- expanded: true,
249
- } satisfies DocumentFieldActionGroup,
250
- ]
251
- : []
252
- return groups ?? []
253
- }, [configActions, fieldActions, pushToast])
254
- }
255
-
256
- function createSafeNode(args: {
257
- node: AssistFieldActionNode
258
- pushToast: PushToast
259
- addSyntheticTask: (task: InstructionTask) => void
260
- removeSyntheticTask: (task: InstructionTask) => void
261
- }): DocumentFieldActionNode | undefined {
262
- const {node} = args
263
- switch (node.type) {
264
- case 'action':
265
- return createSafeAction({...args, action: node})
266
- case 'group':
267
- // eslint-disable-next-line no-case-declarations
268
- const children = node.children
269
- ?.filter(isDefined)
270
- .map((child) => createSafeNode({...args, node: child}))
271
- .filter(isDefined)
272
- if (!children?.length) {
273
- return undefined
274
- }
275
- return {
276
- ...node,
277
- renderAsButton: false,
278
- expanded: true,
279
- children,
280
- }
281
- case 'divider':
282
- default:
283
- return node
284
- }
285
- }
286
-
287
- function createSafeAction(args: {
288
- action: AssistFieldActionItem
289
- pushToast: PushToast
290
- addSyntheticTask: (task: InstructionTask) => void
291
- removeSyntheticTask: (task: InstructionTask) => void
292
- }) {
293
- const {action, pushToast, addSyntheticTask, removeSyntheticTask} = args
294
- return {
295
- ...action,
296
- onAction: () => {
297
- async function runAction() {
298
- const task: InstructionTask = {
299
- _type: instructionTaskTypeName,
300
- _key: randomKey(12),
301
- started: new Date().toISOString(),
302
- presence: [
303
- {
304
- _type: fieldPresenceTypeName,
305
- _key: randomKey(12),
306
- path: documentRootKey,
307
- started: new Date().toISOString(),
308
- },
309
- ],
310
- }
311
- try {
312
- addSyntheticTask(task)
313
- const actionResult = action.onAction?.()
314
- if (actionResult instanceof Promise) {
315
- await actionResult
316
- }
317
- } catch (err: any) {
318
- console.error('Failed to execute action', action, err)
319
- pushToast({
320
- title: 'Failed to execute action',
321
- description: err?.message,
322
- status: 'error',
323
- })
324
- } finally {
325
- removeSyntheticTask(task)
326
- }
327
- }
328
- runAction()
329
- },
330
- renderAsButton: false,
331
- selected: false,
332
- }
333
- }