@sanity/assist 1.2.14 → 1.2.15-lang.2
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 +392 -6
- package/dist/index.d.ts +170 -3
- package/dist/index.esm.js +2019 -125
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +2013 -119
- package/dist/index.js.map +1 -1
- package/package.json +12 -11
- package/src/_lib/form/DocumentForm.tsx +1 -1
- package/src/assistDocument/RequestRunInstructionProvider.tsx +37 -21
- package/src/assistDocument/components/instruction/InstructionInput.tsx +5 -4
- package/src/assistDocument/components/instruction/InstructionOutputField.tsx +45 -0
- package/src/assistDocument/components/instruction/InstructionOutputInput.tsx +205 -0
- package/src/assistDocument/hooks/useStudioAssistDocument.ts +5 -32
- package/src/assistFormComponents/AssistField.tsx +5 -4
- package/src/assistFormComponents/AssistFormBlock.tsx +2 -3
- package/src/assistFormComponents/validation/listItem.tsx +2 -2
- package/src/assistInspector/FieldAutocomplete.tsx +1 -0
- package/src/assistInspector/InstructionTaskHistoryButton.tsx +2 -3
- package/src/assistInspector/helpers.ts +7 -9
- package/src/assistLayout/AssistLayout.tsx +9 -6
- package/src/fieldActions/assistFieldActions.tsx +21 -8
- package/src/fieldActions/translateActions.tsx +141 -0
- package/src/helpers/assistSupported.ts +1 -1
- package/src/node_modules/.vitest/results.json +1 -0
- package/src/plugin.tsx +6 -0
- package/src/presence/AssistAvatar.tsx +1 -1
- package/src/schemas/assistDocumentSchema.tsx +39 -0
- package/src/schemas/serialize/serializeSchema.ts +6 -6
- package/src/schemas/typeDefExtensions.ts +12 -1
- package/src/translate/FieldTranslationProvider.tsx +267 -0
- package/src/translate/getLanguageParams.ts +26 -0
- package/src/translate/paths.test.ts +87 -0
- package/src/translate/paths.ts +151 -0
- package/src/translate/types.ts +159 -0
- package/src/types.ts +21 -2
- package/src/useApiClient.ts +63 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/assist",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.15-lang.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -54,18 +54,19 @@
|
|
|
54
54
|
"release": "semantic-release"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@sanity/icons": "^2.
|
|
57
|
+
"@sanity/icons": "^2.8.0",
|
|
58
58
|
"@sanity/incompatible-plugin": "^1.0.4",
|
|
59
|
-
"@sanity/ui": "^
|
|
59
|
+
"@sanity/ui": "^2.0.0-beta.17",
|
|
60
60
|
"date-fns": "^2.30.0",
|
|
61
|
+
"lodash.get": "^4.4.2",
|
|
61
62
|
"react-fast-compare": "^3.2.1",
|
|
62
63
|
"react-is": "^18.2.0",
|
|
63
64
|
"rxjs": "^7.8.0",
|
|
64
65
|
"rxjs-exhaustmap-with-trailing": "^2.1.1"
|
|
65
66
|
},
|
|
66
67
|
"devDependencies": {
|
|
67
|
-
"@commitlint/cli": "^
|
|
68
|
-
"@commitlint/config-conventional": "^
|
|
68
|
+
"@commitlint/cli": "^18.4.3",
|
|
69
|
+
"@commitlint/config-conventional": "^18.4.3",
|
|
69
70
|
"@rollup/plugin-image": "^3.0.3",
|
|
70
71
|
"@sanity/pkg-utils": "^2.4.10",
|
|
71
72
|
"@sanity/plugin-kit": "^3.1.10",
|
|
@@ -75,7 +76,7 @@
|
|
|
75
76
|
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
|
76
77
|
"@typescript-eslint/parser": "^5.62.0",
|
|
77
78
|
"date-fns": "^2.30.0",
|
|
78
|
-
"eslint": "^8.
|
|
79
|
+
"eslint": "^8.56.0",
|
|
79
80
|
"eslint-config-prettier": "^8.10.0",
|
|
80
81
|
"eslint-config-sanity": "^6.0.0",
|
|
81
82
|
"eslint-plugin-prettier": "^4.2.1",
|
|
@@ -84,17 +85,17 @@
|
|
|
84
85
|
"npm-run-all": "^4.1.5",
|
|
85
86
|
"react": "^18.2.0",
|
|
86
87
|
"react-dom": "^18.2.0",
|
|
87
|
-
"rimraf": "^
|
|
88
|
-
"sanity": "^3.
|
|
88
|
+
"rimraf": "^5.0.5",
|
|
89
|
+
"sanity": "^3.24.1",
|
|
89
90
|
"semantic-release": "^21.1.2",
|
|
90
|
-
"styled-components": "^
|
|
91
|
-
"typescript": "^5.
|
|
91
|
+
"styled-components": "^6.1.1",
|
|
92
|
+
"typescript": "^5.3.3",
|
|
92
93
|
"vitest": "^0.34.6"
|
|
93
94
|
},
|
|
94
95
|
"peerDependencies": {
|
|
95
96
|
"react": "^18",
|
|
96
97
|
"sanity": "^3.16",
|
|
97
|
-
"styled-components": "^5.2"
|
|
98
|
+
"styled-components": "^5.2 || ^6.0.0"
|
|
98
99
|
},
|
|
99
100
|
"engines": {
|
|
100
101
|
"node": ">=14"
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {useRunInstruction} from '../assistLayout/RunInstructionProvider'
|
|
2
2
|
import {useCallback, useEffect, useState} from 'react'
|
|
3
3
|
import {ObjectSchemaType, PatchEvent, SanityDocument, unset} from 'sanity'
|
|
4
|
-
import {RunInstructionArgs} from '../assistLayout/AssistLayout'
|
|
5
4
|
import {publicId} from '../helpers/ids'
|
|
6
5
|
|
|
7
|
-
export interface
|
|
6
|
+
export interface DraftDelayedTaskArgs<T> {
|
|
8
7
|
documentOnChange: (event: PatchEvent) => void
|
|
9
8
|
// indicates if the document is a draft or liveEditable currently
|
|
10
9
|
isDocAssistable: boolean
|
|
10
|
+
task: (args: T) => void
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function isDocAssistable(
|
|
@@ -23,28 +23,44 @@ export function getAssistableDocId(documentSchemaType: ObjectSchemaType, documen
|
|
|
23
23
|
return documentSchemaType.liveEdit ? baseId : `drafts.${baseId}`
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export function useRequestRunInstruction(args:
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
export function useRequestRunInstruction(args: {
|
|
27
|
+
documentOnChange: (event: PatchEvent) => void
|
|
28
|
+
// indicates if the document is a draft or liveEditable currently
|
|
29
|
+
isDocAssistable: boolean
|
|
30
|
+
}) {
|
|
29
31
|
const {runInstruction, instructionLoading} = useRunInstruction()
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
runInstruction(queuedTask)
|
|
35
|
-
setQueuedTask(undefined)
|
|
36
|
-
}
|
|
37
|
-
}, [queuedTask, isDocAssistable, runInstruction])
|
|
32
|
+
const requestRunInstruction = useDraftDelayedTask({
|
|
33
|
+
...args,
|
|
34
|
+
task: runInstruction,
|
|
35
|
+
})
|
|
38
36
|
|
|
39
37
|
return {
|
|
40
38
|
instructionLoading,
|
|
41
|
-
requestRunInstruction
|
|
42
|
-
(task: RunInstructionArgs) => {
|
|
43
|
-
// make a dummy edit: this will trigger the document/draft to be created
|
|
44
|
-
documentOnChange(PatchEvent.from([unset(['_force_document_creation'])]))
|
|
45
|
-
setQueuedTask(task)
|
|
46
|
-
},
|
|
47
|
-
[setQueuedTask, documentOnChange]
|
|
48
|
-
),
|
|
39
|
+
requestRunInstruction,
|
|
49
40
|
}
|
|
50
41
|
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Ensures that the current document is a draft before running task
|
|
45
|
+
*/
|
|
46
|
+
export function useDraftDelayedTask<T>(args: DraftDelayedTaskArgs<T>) {
|
|
47
|
+
const {documentOnChange, isDocAssistable, task} = args
|
|
48
|
+
|
|
49
|
+
const [queuedArgs, setQueuedArgs] = useState<T | undefined>(undefined)
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (queuedArgs && isDocAssistable) {
|
|
53
|
+
task(queuedArgs)
|
|
54
|
+
setQueuedArgs(undefined)
|
|
55
|
+
}
|
|
56
|
+
}, [queuedArgs, isDocAssistable, task])
|
|
57
|
+
|
|
58
|
+
return useCallback(
|
|
59
|
+
(taskArgs: T) => {
|
|
60
|
+
// make a dummy edit: this will trigger the document/draft to be created
|
|
61
|
+
documentOnChange(PatchEvent.from([unset(['_force_document_creation'])]))
|
|
62
|
+
setQueuedArgs(taskArgs)
|
|
63
|
+
},
|
|
64
|
+
[setQueuedArgs, documentOnChange]
|
|
65
|
+
)
|
|
66
|
+
}
|
|
@@ -8,14 +8,15 @@ export function InstructionInput(props: ObjectInputProps) {
|
|
|
8
8
|
<Stack space={[4, 4, 4, 5]}>
|
|
9
9
|
<NameField {...props} />
|
|
10
10
|
<ShareField {...props} />
|
|
11
|
-
<
|
|
11
|
+
<ObjectMember fieldName={'prompt'} {...props} />
|
|
12
|
+
<ObjectMember fieldName={'output'} {...props} />
|
|
12
13
|
</Stack>
|
|
13
14
|
)
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
function
|
|
17
|
-
const
|
|
18
|
-
return
|
|
17
|
+
function ObjectMember({fieldName, ...props}: ObjectInputProps & {fieldName: string}) {
|
|
18
|
+
const member = findFieldMember(props.members, fieldName)
|
|
19
|
+
return member ? <ObjectInputMember {...props} member={member} /> : null
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
const NONE: (FieldMember | FieldError)[] = []
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArrayFieldProps,
|
|
3
|
+
ArraySchemaType,
|
|
4
|
+
isArrayOfObjectsSchemaType,
|
|
5
|
+
isObjectSchemaType,
|
|
6
|
+
ObjectSchemaType,
|
|
7
|
+
} from 'sanity'
|
|
8
|
+
import {useCallback, useContext, useState} from 'react'
|
|
9
|
+
import {SelectedFieldContext} from '../SelectedFieldContext'
|
|
10
|
+
|
|
11
|
+
export function InstructionOutputField(props: ArrayFieldProps) {
|
|
12
|
+
const {fieldSchema} = useContext(SelectedFieldContext) ?? {}
|
|
13
|
+
|
|
14
|
+
if (
|
|
15
|
+
!fieldSchema ||
|
|
16
|
+
!(isObjectSchemaType(fieldSchema) || isArrayOfObjectsSchemaType(fieldSchema))
|
|
17
|
+
) {
|
|
18
|
+
return null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<EnabledOutputField {...props} fieldSchema={fieldSchema}>
|
|
23
|
+
{props.children}
|
|
24
|
+
</EnabledOutputField>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function EnabledOutputField({
|
|
29
|
+
fieldSchema,
|
|
30
|
+
...props
|
|
31
|
+
}: ArrayFieldProps & {fieldSchema: ObjectSchemaType | ArraySchemaType<ObjectSchemaType>}) {
|
|
32
|
+
const [open, setOpen] = useState(!!props.value?.length)
|
|
33
|
+
const onExpand = useCallback(() => setOpen(true), [])
|
|
34
|
+
const onCollapse = useCallback(() => setOpen(false), [])
|
|
35
|
+
|
|
36
|
+
return props.renderDefault({
|
|
37
|
+
...props,
|
|
38
|
+
collapsible: true,
|
|
39
|
+
onExpand,
|
|
40
|
+
onCollapse,
|
|
41
|
+
collapsed: !open,
|
|
42
|
+
level: 1,
|
|
43
|
+
title: isObjectSchemaType(fieldSchema) ? 'Allowed fields' : 'Allowed types',
|
|
44
|
+
})
|
|
45
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArrayOfObjectsInputProps,
|
|
3
|
+
ArraySchemaType,
|
|
4
|
+
FormPatch,
|
|
5
|
+
insert,
|
|
6
|
+
isArrayOfObjectsSchemaType,
|
|
7
|
+
isObjectSchemaType,
|
|
8
|
+
ObjectSchemaType,
|
|
9
|
+
PatchEvent,
|
|
10
|
+
setIfMissing,
|
|
11
|
+
typed,
|
|
12
|
+
unset,
|
|
13
|
+
} from 'sanity'
|
|
14
|
+
import {useCallback, useContext, useEffect, useMemo} from 'react'
|
|
15
|
+
import {SelectedFieldContext} from '../SelectedFieldContext'
|
|
16
|
+
import {Card, Checkbox, Flex, Stack, Text} from '@sanity/ui'
|
|
17
|
+
import {isType} from '../../../helpers/typeUtils'
|
|
18
|
+
import {isAssistSupported} from '../../../helpers/assistSupported'
|
|
19
|
+
import {OutputFieldItem, outputFieldTypeName, OutputTypeItem} from '../../../types'
|
|
20
|
+
|
|
21
|
+
export function InstructionOutputInput(props: ArrayOfObjectsInputProps) {
|
|
22
|
+
const {fieldSchema} = useContext(SelectedFieldContext) ?? {}
|
|
23
|
+
|
|
24
|
+
if (!fieldSchema) {
|
|
25
|
+
return null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (isObjectSchemaType(fieldSchema)) {
|
|
29
|
+
return <ObjectOutputInput {...props} fieldSchema={fieldSchema} />
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (isArrayOfObjectsSchemaType(fieldSchema)) {
|
|
33
|
+
return <ArrayOutputInput {...props} fieldSchema={fieldSchema} />
|
|
34
|
+
}
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function useEmptySelectAllValue(
|
|
39
|
+
value: (OutputTypeItem | OutputFieldItem)[],
|
|
40
|
+
allowedValues: {name: string}[],
|
|
41
|
+
onChange: (patch: FormPatch | FormPatch[] | PatchEvent) => void
|
|
42
|
+
) {
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
const validValues = value?.filter((v) =>
|
|
45
|
+
allowedValues.find(
|
|
46
|
+
(f) => f.name === (v._type === outputFieldTypeName ? v.relativePath : v.type)
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
const valueLength = value?.length ?? 0
|
|
50
|
+
const validLength = validValues?.length ?? 0
|
|
51
|
+
if ((!validLength && valueLength) || validLength >= allowedValues.length) {
|
|
52
|
+
// if we end up here, we consider this a "no selected fields/types" selections. This should render and behave as all values selected.
|
|
53
|
+
// we need this behaviour to accommodate new fields/types being added to the model, so they get visited by instructions without having to update the filter
|
|
54
|
+
// when things have been explicitly selected, we let the selection remain as is
|
|
55
|
+
onChange(PatchEvent.from([unset()]))
|
|
56
|
+
}
|
|
57
|
+
}, [allowedValues, value, onChange])
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function ObjectOutputInput({
|
|
61
|
+
fieldSchema,
|
|
62
|
+
...props
|
|
63
|
+
}: ArrayOfObjectsInputProps & {fieldSchema: ObjectSchemaType}) {
|
|
64
|
+
const {value, onChange} = props
|
|
65
|
+
|
|
66
|
+
const fields = useMemo(
|
|
67
|
+
() => fieldSchema.fields.filter((field) => isAssistSupported(field.type)),
|
|
68
|
+
[fieldSchema.fields]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
useEmptySelectAllValue(value as OutputTypeItem[], fields, onChange)
|
|
72
|
+
|
|
73
|
+
const onSelectChange = useCallback(
|
|
74
|
+
(checked: boolean, selectedValue: string) => {
|
|
75
|
+
if (checked) {
|
|
76
|
+
if (value?.length) {
|
|
77
|
+
onChange(PatchEvent.from(unset([{_key: selectedValue}])))
|
|
78
|
+
} else {
|
|
79
|
+
// we went from empty array to everything selected but one
|
|
80
|
+
const items = fields
|
|
81
|
+
.filter((f) => f.name !== selectedValue)
|
|
82
|
+
.map((field) =>
|
|
83
|
+
typed<OutputFieldItem>({
|
|
84
|
+
_key: field.name,
|
|
85
|
+
_type: 'sanity.assist.output.field',
|
|
86
|
+
relativePath: field.name,
|
|
87
|
+
})
|
|
88
|
+
)
|
|
89
|
+
onChange(PatchEvent.from([setIfMissing([]), insert(items, 'after', [-1])]))
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
const patchValue: OutputFieldItem = {
|
|
93
|
+
_key: selectedValue,
|
|
94
|
+
_type: 'sanity.assist.output.field',
|
|
95
|
+
relativePath: selectedValue,
|
|
96
|
+
}
|
|
97
|
+
onChange(PatchEvent.from([setIfMissing([]), insert([patchValue], 'after', [-1])]))
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
[onChange, value, fields]
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<Stack space={2}>
|
|
105
|
+
{fields.map((field) => {
|
|
106
|
+
return (
|
|
107
|
+
<Flex key={field.name} align="center" gap={2}>
|
|
108
|
+
<Selectable
|
|
109
|
+
value={field.name}
|
|
110
|
+
title={field.type.title ?? field.name}
|
|
111
|
+
arrayValue={value as OutputFieldItem[]}
|
|
112
|
+
onChange={onSelectChange}
|
|
113
|
+
/>
|
|
114
|
+
</Flex>
|
|
115
|
+
)
|
|
116
|
+
})}
|
|
117
|
+
</Stack>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function ArrayOutputInput({
|
|
122
|
+
fieldSchema,
|
|
123
|
+
...props
|
|
124
|
+
}: ArrayOfObjectsInputProps & {fieldSchema: ArraySchemaType}) {
|
|
125
|
+
const {value, onChange} = props
|
|
126
|
+
|
|
127
|
+
const ofItems = useMemo(
|
|
128
|
+
() => fieldSchema.of.filter((itemType) => isAssistSupported(itemType)),
|
|
129
|
+
[fieldSchema.of]
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
useEmptySelectAllValue(value as OutputTypeItem[], ofItems, onChange)
|
|
133
|
+
|
|
134
|
+
const onSelectChange = useCallback(
|
|
135
|
+
(checked: boolean, selectedValue: string) => {
|
|
136
|
+
if (checked) {
|
|
137
|
+
if (value?.length) {
|
|
138
|
+
onChange(PatchEvent.from(unset([{_key: selectedValue}])))
|
|
139
|
+
} else {
|
|
140
|
+
// we went from empty array to everything selected but one
|
|
141
|
+
const items = ofItems
|
|
142
|
+
.filter((f) => f.name !== selectedValue)
|
|
143
|
+
.map((field) =>
|
|
144
|
+
typed<OutputTypeItem>({
|
|
145
|
+
_key: field.name,
|
|
146
|
+
_type: 'sanity.assist.output.type',
|
|
147
|
+
type: field.name,
|
|
148
|
+
})
|
|
149
|
+
)
|
|
150
|
+
onChange(PatchEvent.from([setIfMissing([]), insert(items, 'after', [-1])]))
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
const patchValue: OutputTypeItem = {
|
|
154
|
+
_key: selectedValue,
|
|
155
|
+
_type: 'sanity.assist.output.type',
|
|
156
|
+
type: selectedValue,
|
|
157
|
+
}
|
|
158
|
+
onChange(PatchEvent.from([setIfMissing([]), insert([patchValue], 'after', [-1])]))
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
[onChange, value, ofItems]
|
|
162
|
+
)
|
|
163
|
+
return (
|
|
164
|
+
<Stack space={2}>
|
|
165
|
+
{ofItems.map((itemType) => {
|
|
166
|
+
return (
|
|
167
|
+
<Flex key={itemType.name}>
|
|
168
|
+
<Selectable
|
|
169
|
+
value={itemType.name}
|
|
170
|
+
title={isType(itemType, 'block') ? 'Text' : itemType.title ?? itemType.name}
|
|
171
|
+
arrayValue={value as OutputTypeItem[] | undefined}
|
|
172
|
+
onChange={onSelectChange}
|
|
173
|
+
/>
|
|
174
|
+
</Flex>
|
|
175
|
+
)
|
|
176
|
+
})}
|
|
177
|
+
</Stack>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function Selectable({
|
|
182
|
+
title,
|
|
183
|
+
arrayValue,
|
|
184
|
+
value,
|
|
185
|
+
onChange,
|
|
186
|
+
}: {
|
|
187
|
+
title: string
|
|
188
|
+
value: string
|
|
189
|
+
arrayValue?: {_key: string}[]
|
|
190
|
+
onChange: (checked: boolean, value: string) => void
|
|
191
|
+
}) {
|
|
192
|
+
const checked = !arrayValue?.length || !!arrayValue?.find((v) => v._key === value)
|
|
193
|
+
const handleChange = useCallback(() => onChange(checked, value), [onChange, checked, value])
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<Flex gap={2} align="flex-start">
|
|
197
|
+
<Checkbox checked={checked} onChange={handleChange} />
|
|
198
|
+
<Card marginTop={1} onClick={() => handleChange}>
|
|
199
|
+
<Text style={{cursor: 'default'}} size={1}>
|
|
200
|
+
{title}
|
|
201
|
+
</Text>
|
|
202
|
+
</Card>
|
|
203
|
+
</Flex>
|
|
204
|
+
)
|
|
205
|
+
}
|
|
@@ -3,24 +3,14 @@ import {
|
|
|
3
3
|
assistDocumentTypeName,
|
|
4
4
|
AssistTasksStatus,
|
|
5
5
|
assistTasksStatusTypeName,
|
|
6
|
-
FieldRef,
|
|
7
|
-
fieldReferenceTypeName,
|
|
8
6
|
InstructionTask,
|
|
9
7
|
StudioAssistDocument,
|
|
10
8
|
StudioAssistField,
|
|
11
9
|
StudioInstruction,
|
|
12
10
|
} from '../../types'
|
|
13
|
-
import {
|
|
14
|
-
ObjectSchemaType,
|
|
15
|
-
pathToString,
|
|
16
|
-
typed,
|
|
17
|
-
useClient,
|
|
18
|
-
useCurrentUser,
|
|
19
|
-
useValidationStatus,
|
|
20
|
-
ValidationMarker,
|
|
21
|
-
} from 'sanity'
|
|
11
|
+
import {ObjectSchemaType, typed, useClient, useCurrentUser} from 'sanity'
|
|
22
12
|
import {useDocumentState} from './useDocumentState'
|
|
23
|
-
import {assistDocumentId, assistTasksStatusId
|
|
13
|
+
import {assistDocumentId, assistTasksStatusId} from '../../helpers/ids'
|
|
24
14
|
import {maxHistoryVisibilityMs} from '../../constants'
|
|
25
15
|
|
|
26
16
|
interface UseAssistDocumentProps {
|
|
@@ -37,7 +27,6 @@ export function useStudioAssistDocument({
|
|
|
37
27
|
const documentTypeName = schemaType.name
|
|
38
28
|
const currentUser = useCurrentUser()
|
|
39
29
|
|
|
40
|
-
const validation = useValidationStatus(publicId(documentId), schemaType.name).validation
|
|
41
30
|
const assistDocument = useDocumentState<StudioAssistDocument>(
|
|
42
31
|
assistDocumentId(documentTypeName),
|
|
43
32
|
assistDocumentTypeName
|
|
@@ -73,7 +62,7 @@ export function useStudioAssistDocument({
|
|
|
73
62
|
tasks: tasks.filter((task) => task.path === assistField.path),
|
|
74
63
|
instructions: assistField.instructions
|
|
75
64
|
?.filter((p) => !p.userId || p.userId === currentUser?.id)
|
|
76
|
-
.map((instruction) => asStudioInstruction(instruction, tasks
|
|
65
|
+
.map((instruction) => asStudioInstruction(instruction, tasks)),
|
|
77
66
|
}
|
|
78
67
|
})
|
|
79
68
|
return typed<StudioAssistDocument>({
|
|
@@ -89,23 +78,13 @@ export function useStudioAssistDocument({
|
|
|
89
78
|
}),
|
|
90
79
|
fields: fields,
|
|
91
80
|
})
|
|
92
|
-
}, [assistDocument, assistTasksStatus, currentUser
|
|
81
|
+
}, [assistDocument, assistTasksStatus, currentUser])
|
|
93
82
|
}
|
|
94
83
|
|
|
95
84
|
function asStudioInstruction(
|
|
96
85
|
instruction: StudioInstruction,
|
|
97
|
-
run: InstructionTask[]
|
|
98
|
-
validation: ValidationMarker[]
|
|
86
|
+
run: InstructionTask[]
|
|
99
87
|
): StudioInstruction {
|
|
100
|
-
const errors = validation.filter((marker) => marker.level === 'error')
|
|
101
|
-
|
|
102
|
-
const fieldRefs: FieldRef[] = (instruction?.prompt ?? []).flatMap((block) => {
|
|
103
|
-
if (block._type === 'block') {
|
|
104
|
-
return block.children.filter((c): c is FieldRef => c._type === fieldReferenceTypeName)
|
|
105
|
-
}
|
|
106
|
-
return []
|
|
107
|
-
})
|
|
108
|
-
|
|
109
88
|
return {
|
|
110
89
|
...instruction,
|
|
111
90
|
tasks: run
|
|
@@ -115,11 +94,5 @@ function asStudioInstruction(
|
|
|
115
94
|
task.started &&
|
|
116
95
|
new Date().getTime() - new Date(task.started).getTime() < maxHistoryVisibilityMs
|
|
117
96
|
),
|
|
118
|
-
validation: errors.filter((marker) =>
|
|
119
|
-
fieldRefs
|
|
120
|
-
.map((r) => r.path)
|
|
121
|
-
.filter((p): p is string => !!p)
|
|
122
|
-
.find((path) => pathToString(marker.path) === path)
|
|
123
|
-
),
|
|
124
97
|
}
|
|
125
98
|
}
|
|
@@ -46,14 +46,15 @@ export function AssistField(props: FieldProps) {
|
|
|
46
46
|
)
|
|
47
47
|
|
|
48
48
|
const {showOnboarding, dismissOnboarding} = useOnboardingFeature(fieldOnboardingKey)
|
|
49
|
+
const singlePresence = presence[0]
|
|
49
50
|
|
|
50
51
|
const actions = (
|
|
51
52
|
<Flex gap={2} align="center" justify="space-between">
|
|
52
|
-
{
|
|
53
|
-
<Box
|
|
54
|
-
<AiFieldPresence
|
|
53
|
+
{singlePresence && (
|
|
54
|
+
<Box>
|
|
55
|
+
<AiFieldPresence presence={singlePresence} />
|
|
55
56
|
</Box>
|
|
56
|
-
)
|
|
57
|
+
)}
|
|
57
58
|
|
|
58
59
|
{isFirstAssisted && showOnboarding && <AssistOnboardingPopover dismiss={dismissOnboarding} />}
|
|
59
60
|
</Flex>
|
|
@@ -18,13 +18,12 @@ export function AssistFormBlock(props: BlockProps) {
|
|
|
18
18
|
},
|
|
19
19
|
[onChange, key]
|
|
20
20
|
)
|
|
21
|
+
const singlePresence = presence[0]
|
|
21
22
|
return (
|
|
22
23
|
<ErrorWrapper onChange={localOnChange}>
|
|
23
24
|
<Flex align="center" justify="space-between">
|
|
24
25
|
<Box flex={1}>{props.renderDefault(props)}</Box>
|
|
25
|
-
{presence
|
|
26
|
-
<AiFieldPresence key={pre.lastActiveAt} presence={pre} />
|
|
27
|
-
))}
|
|
26
|
+
{singlePresence && <AiFieldPresence presence={singlePresence} />}
|
|
28
27
|
</Flex>
|
|
29
28
|
</ErrorWrapper>
|
|
30
29
|
)
|
|
@@ -47,9 +47,9 @@ export function ListItem(props: ValidationListItemProps) {
|
|
|
47
47
|
{path}
|
|
48
48
|
</StyledText>
|
|
49
49
|
)}
|
|
50
|
-
{marker.item
|
|
50
|
+
{marker.item?.message && (
|
|
51
51
|
<StyledText muted size={1} textOverflow={truncate ? 'ellipsis' : undefined}>
|
|
52
|
-
{marker.item
|
|
52
|
+
{marker.item?.message}
|
|
53
53
|
</StyledText>
|
|
54
54
|
)}
|
|
55
55
|
</Stack>
|
|
@@ -32,6 +32,7 @@ export function FieldAutocomplete(props: FieldSelectorProps) {
|
|
|
32
32
|
() =>
|
|
33
33
|
fieldRefs
|
|
34
34
|
.filter((field) => (filter ? filter(field) : true))
|
|
35
|
+
.filter((f) => !isType(f.schemaType, 'reference'))
|
|
35
36
|
.map((field) => ({value: field.key, field})),
|
|
36
37
|
[fieldRefs, filter]
|
|
37
38
|
)
|
|
@@ -167,7 +167,7 @@ export function InstructionTaskHistoryButton(props: InstructionTaskHistoryButton
|
|
|
167
167
|
)
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
const TASK_STATUS_BUTTON_TOOLTIP_PROPS: StatusButtonProps['
|
|
170
|
+
const TASK_STATUS_BUTTON_TOOLTIP_PROPS: StatusButtonProps['tooltipProps'] = {
|
|
171
171
|
placement: 'top',
|
|
172
172
|
}
|
|
173
173
|
|
|
@@ -190,11 +190,10 @@ const TaskStatusButton = forwardRef(function TaskStatusButton(
|
|
|
190
190
|
mode="bleed"
|
|
191
191
|
onClick={onClick}
|
|
192
192
|
tone={hasErrors ? 'critical' : undefined}
|
|
193
|
-
fontSize={1}
|
|
194
193
|
disabled={disabled}
|
|
195
194
|
ref={ref}
|
|
196
195
|
selected={selected}
|
|
197
|
-
|
|
196
|
+
tooltipProps={TASK_STATUS_BUTTON_TOOLTIP_PROPS}
|
|
198
197
|
/>
|
|
199
198
|
)
|
|
200
199
|
})
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from 'sanity'
|
|
21
21
|
import {ComponentType, useContext, useMemo} from 'react'
|
|
22
22
|
import {AssistInspectorRouteParams, documentRootKey, fieldPathParam} from '../types'
|
|
23
|
-
import {usePaneRouter} from 'sanity/desk'
|
|
23
|
+
import {usePaneRouter, type PaneRouterContextValue} from 'sanity/desk'
|
|
24
24
|
import {isAssistSupported} from '../helpers/assistSupported'
|
|
25
25
|
import {isPortableTextArray, isType} from '../helpers/typeUtils'
|
|
26
26
|
import {SelectedFieldContext} from '../assistDocument/components/SelectedFieldContext'
|
|
@@ -198,18 +198,16 @@ export function getFieldTitle(field?: FieldRef) {
|
|
|
198
198
|
return field?.title ?? schemaType?.title ?? schemaType?.name ?? 'Untitled'
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
export type AiPaneRouter = Omit<PaneRouterContextValue, 'setParams' | 'params'> & {
|
|
202
|
+
params: AssistInspectorRouteParams
|
|
203
|
+
setParams: (p: Record<keyof AssistInspectorRouteParams, string | undefined>) => void
|
|
204
|
+
}
|
|
205
|
+
|
|
201
206
|
export function useAiPaneRouter() {
|
|
202
207
|
const paneRouter = usePaneRouter()
|
|
203
208
|
|
|
204
209
|
return useMemo(
|
|
205
|
-
() =>
|
|
206
|
-
({...paneRouter, params: paneRouter.params ?? {}} as Omit<
|
|
207
|
-
typeof paneRouter,
|
|
208
|
-
'setParams' | 'params'
|
|
209
|
-
> & {
|
|
210
|
-
params: AssistInspectorRouteParams
|
|
211
|
-
setParams: (p: Record<keyof AssistInspectorRouteParams, string | undefined>) => void
|
|
212
|
-
}),
|
|
210
|
+
() => ({...paneRouter, params: paneRouter.params ?? {}} as AiPaneRouter),
|
|
213
211
|
[paneRouter]
|
|
214
212
|
)
|
|
215
213
|
}
|
|
@@ -9,6 +9,7 @@ import {StudioInstruction} from '../types'
|
|
|
9
9
|
import {RunInstructionProvider} from './RunInstructionProvider'
|
|
10
10
|
import {ThemeProvider} from '@sanity/ui'
|
|
11
11
|
import {AlphaMigration} from './AlphaMigration'
|
|
12
|
+
import {FieldTranslationProvider} from '../translate/FieldTranslationProvider'
|
|
12
13
|
|
|
13
14
|
export interface AIStudioLayoutProps extends LayoutProps {
|
|
14
15
|
config: AssistPluginConfig
|
|
@@ -26,12 +27,14 @@ export function AssistLayout(props: AIStudioLayoutProps) {
|
|
|
26
27
|
<AiAssistanceConfigProvider config={props.config}>
|
|
27
28
|
{migrate ? <AlphaMigration /> : null}
|
|
28
29
|
<RunInstructionProvider>
|
|
29
|
-
<
|
|
30
|
-
{
|
|
31
|
-
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
<FieldTranslationProvider>
|
|
31
|
+
<ConnectorsProvider onConnectorsChange={setConnectors}>
|
|
32
|
+
{props.renderDefault(props)}
|
|
33
|
+
<ThemeProvider tone="default">
|
|
34
|
+
<AssistConnectorsOverlay connectors={connectors} />
|
|
35
|
+
</ThemeProvider>
|
|
36
|
+
</ConnectorsProvider>
|
|
37
|
+
</FieldTranslationProvider>
|
|
35
38
|
</RunInstructionProvider>
|
|
36
39
|
</AiAssistanceConfigProvider>
|
|
37
40
|
)
|