@sanity/assist 4.1.0 → 4.3.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.
- package/README.md +302 -0
- package/dist/index.d.mts +269 -1
- package/dist/index.d.ts +269 -1
- package/dist/index.esm.js +244 -103
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +239 -98
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +244 -103
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -8
- package/src/assistDocument/AssistDocumentContext.tsx +14 -1
- package/src/assistDocument/hooks/useAssistDocumentContextValue.tsx +30 -3
- package/src/assistLayout/RunInstructionProvider.tsx +75 -23
- package/src/components/ImageContext.tsx +4 -4
- package/src/fieldActions/assistFieldActions.tsx +42 -2
- package/src/fieldActions/customFieldActions.tsx +304 -0
- package/src/fieldActions/useUserInput.ts +107 -0
- package/src/helpers/typeUtils.ts +13 -3
- package/src/index.ts +17 -0
- package/src/plugin.tsx +6 -0
- package/src/presence/AssistDocumentPresence.tsx +3 -3
- package/src/schemas/typeDefExtensions.ts +12 -1
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DocumentFieldActionDivider,
|
|
3
|
+
DocumentFieldActionGroup,
|
|
4
|
+
DocumentFieldActionItem,
|
|
5
|
+
DocumentFieldActionNode,
|
|
6
|
+
ObjectSchemaType,
|
|
7
|
+
SanityDocumentLike,
|
|
8
|
+
useWorkspaceSchemaId,
|
|
9
|
+
} from 'sanity'
|
|
10
|
+
import {Path, SchemaType} from '@sanity/types'
|
|
11
|
+
import {useMemo} from 'react'
|
|
12
|
+
import {useAiAssistanceConfig} from '../assistLayout/AiAssistanceConfigContext'
|
|
13
|
+
import {ToastParams, useToast} from '@sanity/ui'
|
|
14
|
+
import {AgentActionPath} from '@sanity/client/stega'
|
|
15
|
+
import {useAssistDocumentContext} from '../assistDocument/AssistDocumentContext'
|
|
16
|
+
import {
|
|
17
|
+
documentRootKey,
|
|
18
|
+
fieldPresenceTypeName,
|
|
19
|
+
InstructionTask,
|
|
20
|
+
instructionTaskTypeName,
|
|
21
|
+
} from '../types'
|
|
22
|
+
import {randomKey} from '../_lib/randomKey'
|
|
23
|
+
|
|
24
|
+
export interface AgentActionConditionalPath {
|
|
25
|
+
path: AgentActionPath
|
|
26
|
+
readOnly: boolean
|
|
27
|
+
hidden: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface AssistFieldActionProps {
|
|
31
|
+
/**
|
|
32
|
+
* `actionType` will be `document` for action invoked from the top right document action menu, and
|
|
33
|
+
* `field` when invoked from a field action menu.
|
|
34
|
+
*/
|
|
35
|
+
actionType: 'document' | 'field'
|
|
36
|
+
/**
|
|
37
|
+
* This is the id of the current document pane; it contains `drafts.`or `versions. prefix` ect depending on context.
|
|
38
|
+
* Use this for `documentId` when calling any `client.agent.action`.
|
|
39
|
+
*
|
|
40
|
+
* It is generally recommended to call actions from the studio like this:
|
|
41
|
+
* ```ts
|
|
42
|
+
* await client.agent.action.generate({
|
|
43
|
+
* targetDocument: {
|
|
44
|
+
* operation: 'createIfNotExists',
|
|
45
|
+
* _id: props.documentIdForAction,
|
|
46
|
+
* _type: props.documentSchemaType.name,
|
|
47
|
+
* initialValues: props.getDocumentValue()
|
|
48
|
+
* },
|
|
49
|
+
* //...
|
|
50
|
+
* })
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
documentIdForAction: string
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Schema type of the current document.
|
|
57
|
+
* @see documentIdForAction
|
|
58
|
+
*/
|
|
59
|
+
documentSchemaType: ObjectSchemaType
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Returns the current document value.
|
|
63
|
+
*
|
|
64
|
+
* Prefer passing this function to your hooks instead of passing the document value directly to avoid unnecessary re-renders.
|
|
65
|
+
* @see documentIdForAction
|
|
66
|
+
*/
|
|
67
|
+
getDocumentValue: () => SanityDocumentLike
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Returns the current readOnly and hidden state of all conditional members in the current document form.
|
|
71
|
+
*
|
|
72
|
+
* Intended to be passed to agent actions `conditionalPaths.paths`.
|
|
73
|
+
*/
|
|
74
|
+
getConditionalPaths: () => AgentActionConditionalPath[]
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* `schemaId` for the current workspace.
|
|
78
|
+
*
|
|
79
|
+
* Note: the workspace schema has to be deployed using `sanity schema deploy` or `sanity deploy`.
|
|
80
|
+
*
|
|
81
|
+
* Use this for `schemaId` when calling any `client.agent.action`.
|
|
82
|
+
*
|
|
83
|
+
* It is generally recommended to call actions from the studio like this:
|
|
84
|
+
* ```ts
|
|
85
|
+
* await client.agent.action.generate({
|
|
86
|
+
* targetDocument: {
|
|
87
|
+
* operation: 'createIfNotExists',
|
|
88
|
+
* _id: props.documentIdForAction,
|
|
89
|
+
* _type: props.documentSchemaType.name,
|
|
90
|
+
* initialValues: props.getDocumentValue()
|
|
91
|
+
* },
|
|
92
|
+
* //...
|
|
93
|
+
* })
|
|
94
|
+
*/
|
|
95
|
+
schemaId: string
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* This is the schema type of the field the actions will be attached to (ie, schemaType for `path`)
|
|
99
|
+
*
|
|
100
|
+
* It can be used with agent actions using `target.path`, to scope the action to a specific field.
|
|
101
|
+
*
|
|
102
|
+
* It is generally recommended to call actions from the studio like this:
|
|
103
|
+
* ```ts
|
|
104
|
+
* await client.agent.action.generate({
|
|
105
|
+
* targetDocument: {
|
|
106
|
+
* operation: 'createIfNotExists',
|
|
107
|
+
* _id: props.documentIdForAction,
|
|
108
|
+
* _type: props.documentSchemaType.name,
|
|
109
|
+
* initialValues: props.getDocumentValue()
|
|
110
|
+
* },
|
|
111
|
+
* target: {
|
|
112
|
+
* path: props.path
|
|
113
|
+
* },
|
|
114
|
+
* })
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
path: AgentActionPath
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* This is the schema type of the field the actions will be attached to (ie, schemaType for `path`).
|
|
121
|
+
*
|
|
122
|
+
* Typically useful to dynamically return different actions based on the schema type of the field.
|
|
123
|
+
*
|
|
124
|
+
* ```ts
|
|
125
|
+
* if(isObjectSchemaType(schemaType)) {
|
|
126
|
+
* return [
|
|
127
|
+
* defineAssistFieldAction({
|
|
128
|
+
* title: 'Fill the object fields',
|
|
129
|
+
* icon: RobotIcon,
|
|
130
|
+
* onAction: () => {
|
|
131
|
+
* //...
|
|
132
|
+
* }
|
|
133
|
+
* })
|
|
134
|
+
* ]
|
|
135
|
+
* }
|
|
136
|
+
* return useMemo(() => {
|
|
137
|
+
*
|
|
138
|
+
*
|
|
139
|
+
* }, [])
|
|
140
|
+
*
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
schemaType: SchemaType
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export type AssistFieldActionNode =
|
|
147
|
+
| AssistFieldActionItem
|
|
148
|
+
| AssistFieldActionGroup
|
|
149
|
+
| DocumentFieldActionDivider
|
|
150
|
+
|
|
151
|
+
export type AssistFieldActionItem = Omit<
|
|
152
|
+
DocumentFieldActionItem,
|
|
153
|
+
'renderAsButton' | 'selected' | 'onAction'
|
|
154
|
+
> & {
|
|
155
|
+
onAction: () => void | Promise<void>
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export type AssistFieldActionGroup = Omit<
|
|
159
|
+
DocumentFieldActionGroup,
|
|
160
|
+
'renderAsButton' | 'expanded' | 'children'
|
|
161
|
+
> & {
|
|
162
|
+
children: AssistFieldActionNode[]
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
type PushToast = (params: ToastParams) => string
|
|
166
|
+
|
|
167
|
+
export function defineAssistFieldAction(
|
|
168
|
+
action: Omit<AssistFieldActionItem, 'type'>,
|
|
169
|
+
): AssistFieldActionItem {
|
|
170
|
+
return {
|
|
171
|
+
...action,
|
|
172
|
+
type: 'action',
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function defineFieldActionDivider(): DocumentFieldActionDivider {
|
|
177
|
+
return {
|
|
178
|
+
type: 'divider',
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function defineAssistFieldActionGroup(
|
|
183
|
+
group: Omit<AssistFieldActionGroup, 'type'>,
|
|
184
|
+
): AssistFieldActionGroup {
|
|
185
|
+
return {
|
|
186
|
+
...group,
|
|
187
|
+
type: 'group',
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function useCustomFieldActions(
|
|
192
|
+
props: Omit<AssistFieldActionProps, 'schemaId' | 'path'> & {path: Path},
|
|
193
|
+
) {
|
|
194
|
+
const {
|
|
195
|
+
config: {fieldActions},
|
|
196
|
+
} = useAiAssistanceConfig()
|
|
197
|
+
const {addSyntheticTask, removeSyntheticTask} = useAssistDocumentContext()
|
|
198
|
+
|
|
199
|
+
const schemaId = useWorkspaceSchemaId()
|
|
200
|
+
const {push: pushToast} = useToast()
|
|
201
|
+
const configActions = fieldActions?.useFieldActions?.({
|
|
202
|
+
...props,
|
|
203
|
+
schemaId,
|
|
204
|
+
path: props.path as AgentActionPath,
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
return useMemo(() => {
|
|
208
|
+
const title = fieldActions?.title
|
|
209
|
+
const customActions = configActions?.map((node) => {
|
|
210
|
+
return createSafeNode({
|
|
211
|
+
node,
|
|
212
|
+
pushToast,
|
|
213
|
+
addSyntheticTask,
|
|
214
|
+
removeSyntheticTask,
|
|
215
|
+
})
|
|
216
|
+
})
|
|
217
|
+
const onlyGroups =
|
|
218
|
+
customActions?.length && customActions?.every((node) => node.type === 'group')
|
|
219
|
+
const groups = customActions?.length
|
|
220
|
+
? onlyGroups
|
|
221
|
+
? customActions
|
|
222
|
+
: [
|
|
223
|
+
{
|
|
224
|
+
type: 'group',
|
|
225
|
+
title: title || 'Custom actions',
|
|
226
|
+
children: customActions,
|
|
227
|
+
expanded: true,
|
|
228
|
+
} satisfies DocumentFieldActionGroup,
|
|
229
|
+
]
|
|
230
|
+
: []
|
|
231
|
+
return groups ?? []
|
|
232
|
+
}, [configActions, fieldActions, pushToast])
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function createSafeNode(args: {
|
|
236
|
+
node: AssistFieldActionNode
|
|
237
|
+
pushToast: PushToast
|
|
238
|
+
addSyntheticTask: (task: InstructionTask) => void
|
|
239
|
+
removeSyntheticTask: (task: InstructionTask) => void
|
|
240
|
+
}): DocumentFieldActionNode {
|
|
241
|
+
const {node} = args
|
|
242
|
+
switch (node.type) {
|
|
243
|
+
case 'action':
|
|
244
|
+
return createSafeAction({...args, action: node})
|
|
245
|
+
case 'group':
|
|
246
|
+
return {
|
|
247
|
+
...node,
|
|
248
|
+
renderAsButton: false,
|
|
249
|
+
expanded: true,
|
|
250
|
+
children: node.children?.map((child) => createSafeNode({...args, node: child})),
|
|
251
|
+
}
|
|
252
|
+
case 'divider':
|
|
253
|
+
default:
|
|
254
|
+
return node
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function createSafeAction(args: {
|
|
259
|
+
action: AssistFieldActionItem
|
|
260
|
+
pushToast: PushToast
|
|
261
|
+
addSyntheticTask: (task: InstructionTask) => void
|
|
262
|
+
removeSyntheticTask: (task: InstructionTask) => void
|
|
263
|
+
}) {
|
|
264
|
+
const {action, pushToast, addSyntheticTask, removeSyntheticTask} = args
|
|
265
|
+
return {
|
|
266
|
+
...action,
|
|
267
|
+
onAction: () => {
|
|
268
|
+
async function runAction() {
|
|
269
|
+
const task: InstructionTask = {
|
|
270
|
+
_type: instructionTaskTypeName,
|
|
271
|
+
_key: randomKey(12),
|
|
272
|
+
started: new Date().toISOString(),
|
|
273
|
+
presence: [
|
|
274
|
+
{
|
|
275
|
+
_type: fieldPresenceTypeName,
|
|
276
|
+
_key: randomKey(12),
|
|
277
|
+
path: documentRootKey,
|
|
278
|
+
started: new Date().toISOString(),
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
addSyntheticTask(task)
|
|
284
|
+
const actionResult = action.onAction?.()
|
|
285
|
+
if (actionResult instanceof Promise) {
|
|
286
|
+
await actionResult
|
|
287
|
+
}
|
|
288
|
+
} catch (err: any) {
|
|
289
|
+
console.error('Failed to execute action', action, err)
|
|
290
|
+
pushToast({
|
|
291
|
+
title: 'Failed to execute action',
|
|
292
|
+
description: err?.message,
|
|
293
|
+
status: 'error',
|
|
294
|
+
})
|
|
295
|
+
} finally {
|
|
296
|
+
removeSyntheticTask(task)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
runAction()
|
|
300
|
+
},
|
|
301
|
+
renderAsButton: false,
|
|
302
|
+
selected: false,
|
|
303
|
+
}
|
|
304
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {useRunInstruction} from '../assistLayout/RunInstructionProvider'
|
|
2
|
+
|
|
3
|
+
export type GetUserInput = (args: {
|
|
4
|
+
/**
|
|
5
|
+
* Dialog title
|
|
6
|
+
*/
|
|
7
|
+
title: string
|
|
8
|
+
/**
|
|
9
|
+
* One titled input per array item
|
|
10
|
+
*/
|
|
11
|
+
inputs: CustomInput[]
|
|
12
|
+
}) => Promise<CustomInputResult[] | undefined>
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
export interface CustomInput {
|
|
18
|
+
/**
|
|
19
|
+
* Id for the input
|
|
20
|
+
*/
|
|
21
|
+
id: string
|
|
22
|
+
/**
|
|
23
|
+
* Title of the input field
|
|
24
|
+
*/
|
|
25
|
+
title: string
|
|
26
|
+
/**
|
|
27
|
+
* Additional info that will be displayed over the input
|
|
28
|
+
*/
|
|
29
|
+
description?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type CustomInputResult = {
|
|
33
|
+
/**
|
|
34
|
+
* Identifies which custom input the `result`belongs to
|
|
35
|
+
*/
|
|
36
|
+
input: CustomInput
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The text provided by the user in the input
|
|
40
|
+
*/
|
|
41
|
+
result: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* `useUserInput` returns a function that can be used to await user input.
|
|
46
|
+
*
|
|
47
|
+
* Useful for custom `fieldActions` to get user input for populating Agent Action requests,.
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* fieldActions: {
|
|
51
|
+
* useFieldActions: (props) => {
|
|
52
|
+
* const {
|
|
53
|
+
* documentSchemaType,
|
|
54
|
+
* schemaId,
|
|
55
|
+
* getDocumentValue,
|
|
56
|
+
* getConditionalPaths,
|
|
57
|
+
* documentIdForAction,
|
|
58
|
+
* } = props
|
|
59
|
+
* const client = useClient({apiVersion: 'vX'})
|
|
60
|
+
* const getUserInput = useUserInput()
|
|
61
|
+
* return useMemo(() => {
|
|
62
|
+
* return [
|
|
63
|
+
* defineAssistFieldAction({
|
|
64
|
+
* title: 'Log user input',
|
|
65
|
+
* icon: UserIcon,
|
|
66
|
+
* onAction: async () => {
|
|
67
|
+
* const input = await getUserInput({
|
|
68
|
+
* title: 'Topic',
|
|
69
|
+
* inputs: [{id: 'about', title: 'What should the article be about?'}],
|
|
70
|
+
* })
|
|
71
|
+
* if (!input) return // user canceled input
|
|
72
|
+
* await client.agent.action.generate({
|
|
73
|
+
* schemaId,
|
|
74
|
+
* targetDocument: {
|
|
75
|
+
* operation: 'createIfNotExists',
|
|
76
|
+
* _id: documentIdForAction,
|
|
77
|
+
* _type: documentSchemaType.name,
|
|
78
|
+
* initialValues: getDocumentValue(),
|
|
79
|
+
* },
|
|
80
|
+
* instruction: `
|
|
81
|
+
* Create a document about the following topic:
|
|
82
|
+
* $about
|
|
83
|
+
* ---
|
|
84
|
+
* `,
|
|
85
|
+
* instructionParams: {about: input[0].result},
|
|
86
|
+
* conditionalPaths: {paths: getConditionalPaths()},
|
|
87
|
+
* })
|
|
88
|
+
* },
|
|
89
|
+
* }),
|
|
90
|
+
* ]
|
|
91
|
+
* }, [
|
|
92
|
+
* client,
|
|
93
|
+
* documentSchemaType,
|
|
94
|
+
* schemaId,
|
|
95
|
+
* getDocumentValue,
|
|
96
|
+
* getConditionalPaths,
|
|
97
|
+
* documentIdForAction,
|
|
98
|
+
* getUserInput,
|
|
99
|
+
* ])
|
|
100
|
+
* },
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export function useUserInput(): GetUserInput {
|
|
105
|
+
const {getUserInput} = useRunInstruction()
|
|
106
|
+
return getUserInput
|
|
107
|
+
}
|
package/src/helpers/typeUtils.ts
CHANGED
|
@@ -18,13 +18,23 @@ export function isImage(schemaType: SchemaType) {
|
|
|
18
18
|
return isType(schemaType, 'image')
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export function getDescriptionFieldOption(
|
|
21
|
+
export function getDescriptionFieldOption(
|
|
22
|
+
schemaType: SchemaType | undefined,
|
|
23
|
+
): {path: string; updateOnImageChange: boolean} | undefined {
|
|
22
24
|
if (!schemaType) {
|
|
23
25
|
return undefined
|
|
24
26
|
}
|
|
25
27
|
const descriptionField = (schemaType.options as ImageOptions)?.aiAssist?.imageDescriptionField
|
|
26
|
-
if (descriptionField) {
|
|
27
|
-
return
|
|
28
|
+
if (typeof descriptionField === 'string') {
|
|
29
|
+
return {
|
|
30
|
+
path: descriptionField,
|
|
31
|
+
updateOnImageChange: true,
|
|
32
|
+
}
|
|
33
|
+
} else if (descriptionField) {
|
|
34
|
+
return {
|
|
35
|
+
path: descriptionField.path,
|
|
36
|
+
updateOnImageChange: descriptionField.updateOnImageChange ?? true,
|
|
37
|
+
}
|
|
28
38
|
}
|
|
29
39
|
return getDescriptionFieldOption(schemaType.type)
|
|
30
40
|
}
|
package/src/index.ts
CHANGED
|
@@ -5,3 +5,20 @@ export {defaultLanguageOutputs} from './translate/paths'
|
|
|
5
5
|
export * from './translate/types'
|
|
6
6
|
export {contextDocumentTypeName} from './types'
|
|
7
7
|
export * from './assistTypes'
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
type AssistFieldActionProps,
|
|
11
|
+
type AssistFieldActionGroup,
|
|
12
|
+
type AssistFieldActionItem,
|
|
13
|
+
type AssistFieldActionNode,
|
|
14
|
+
defineAssistFieldAction,
|
|
15
|
+
defineFieldActionDivider,
|
|
16
|
+
defineAssistFieldActionGroup,
|
|
17
|
+
} from './fieldActions/customFieldActions'
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
type GetUserInput,
|
|
21
|
+
type CustomInput,
|
|
22
|
+
type CustomInputResult,
|
|
23
|
+
useUserInput,
|
|
24
|
+
} from './fieldActions/useUserInput'
|
package/src/plugin.tsx
CHANGED
|
@@ -21,6 +21,7 @@ import {createAssistDocumentPresence} from './presence/AssistDocumentPresence'
|
|
|
21
21
|
import {schemaTypes} from './schemas'
|
|
22
22
|
import {TranslationConfig} from './translate/types'
|
|
23
23
|
import {assistDocumentTypeName, AssistPreset} from './types'
|
|
24
|
+
import {AssistFieldActionNode, AssistFieldActionProps} from './fieldActions/customFieldActions'
|
|
24
25
|
|
|
25
26
|
export interface AssistPluginConfig {
|
|
26
27
|
translate?: TranslationConfig
|
|
@@ -30,6 +31,11 @@ export interface AssistPluginConfig {
|
|
|
30
31
|
*/
|
|
31
32
|
assist?: AssistConfig
|
|
32
33
|
|
|
34
|
+
fieldActions?: {
|
|
35
|
+
title?: string
|
|
36
|
+
useFieldActions?: (props: AssistFieldActionProps) => AssistFieldActionNode[]
|
|
37
|
+
}
|
|
38
|
+
|
|
33
39
|
/**
|
|
34
40
|
* @internal
|
|
35
41
|
*/
|
|
@@ -13,9 +13,9 @@ export function createAssistDocumentPresence(documentId: string | undefined) {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
function AssistDocumentPresence() {
|
|
16
|
-
const {assistDocument} = useAssistDocumentContext()
|
|
16
|
+
const {assistDocument, syntheticTasks} = useAssistDocumentContext()
|
|
17
17
|
const anyPresence = useMemo(() => {
|
|
18
|
-
const anyPresence = assistDocument?.tasks
|
|
18
|
+
const anyPresence = [...(assistDocument?.tasks ?? []), ...(syntheticTasks ?? [])]
|
|
19
19
|
?.filter((run) => !run.ended && !run.reason)
|
|
20
20
|
?.flatMap((run) => run.presence ?? [])
|
|
21
21
|
.find((f) => f.started && new Date().getTime() - new Date(f.started).getTime() < 30000)
|
|
@@ -36,7 +36,7 @@ function AssistDocumentPresence() {
|
|
|
36
36
|
[],
|
|
37
37
|
)
|
|
38
38
|
: undefined
|
|
39
|
-
}, [assistDocument?.tasks])
|
|
39
|
+
}, [assistDocument?.tasks, syntheticTasks])
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
42
|
<Card>
|
|
@@ -90,7 +90,18 @@ declare module 'sanity' {
|
|
|
90
90
|
* })
|
|
91
91
|
* ```
|
|
92
92
|
*/
|
|
93
|
-
imageDescriptionField?:
|
|
93
|
+
imageDescriptionField?:
|
|
94
|
+
| string
|
|
95
|
+
| {
|
|
96
|
+
path: string
|
|
97
|
+
/**
|
|
98
|
+
* When updateOnImageChange is true (or undefined), whenever the
|
|
99
|
+
* image asset changes, imageDescriptionField will be regenerated.
|
|
100
|
+
*
|
|
101
|
+
* default: true
|
|
102
|
+
* */
|
|
103
|
+
updateOnImageChange?: boolean
|
|
104
|
+
}
|
|
94
105
|
}
|
|
95
106
|
}
|
|
96
107
|
interface NumberOptions extends AssistOptions {}
|