@sanity/assist 1.2.13 → 1.2.15-lang.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/README.md +392 -6
- package/dist/index.d.ts +170 -3
- package/dist/index.esm.js +1986 -111
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1980 -105
- package/dist/index.js.map +1 -1
- package/package.json +15 -14
- package/src/_lib/form/DocumentForm.tsx +1 -1
- 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 +14 -8
- package/src/fieldActions/translateActions.tsx +118 -0
- package/src/helpers/assistSupported.ts +1 -1
- package/src/node_modules/.vitest/results.json +1 -0
- package/src/plugin.tsx +12 -2
- package/src/presence/AssistAvatar.tsx +1 -1
- package/src/schemas/assistDocumentSchema.tsx +39 -0
- package/src/schemas/serialize/serializeSchema.test.ts +15 -2
- package/src/schemas/serialize/serializeSchema.ts +8 -7
- package/src/schemas/typeDefExtensions.ts +12 -1
- package/src/translate/FieldTranslationProvider.tsx +254 -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.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -54,28 +54,29 @@
|
|
|
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",
|
|
72
|
-
"@sanity/semantic-release-preset": "^4.1.
|
|
73
|
-
"@types/react": "^18.2.
|
|
74
|
-
"@types/styled-components": "^5.1.
|
|
73
|
+
"@sanity/semantic-release-preset": "^4.1.6",
|
|
74
|
+
"@types/react": "^18.2.37",
|
|
75
|
+
"@types/styled-components": "^5.1.30",
|
|
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"
|
|
@@ -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
|
)
|
|
@@ -24,6 +24,7 @@ import {generateCaptionsActions} from './generateCaptionActions'
|
|
|
24
24
|
import {useDocumentPane} from 'sanity/desk'
|
|
25
25
|
import {useSelectedField, useTypePath} from '../assistInspector/helpers'
|
|
26
26
|
import {isSchemaAssistEnabled} from '../helpers/assistSupported'
|
|
27
|
+
import {translateActions} from './translateActions'
|
|
27
28
|
|
|
28
29
|
function node(node: DocumentFieldActionItem | DocumentFieldActionGroup) {
|
|
29
30
|
return node
|
|
@@ -47,6 +48,7 @@ export const assistFieldActions: DocumentFieldAction = {
|
|
|
47
48
|
documentSchemaType,
|
|
48
49
|
documentId,
|
|
49
50
|
selectedPath,
|
|
51
|
+
assistableDocumentId,
|
|
50
52
|
} =
|
|
51
53
|
// document field actions do not have access to the document context
|
|
52
54
|
// conditional hook _should_ be safe here since the logical path will be stable
|
|
@@ -89,7 +91,7 @@ export const assistFieldActions: DocumentFieldAction = {
|
|
|
89
91
|
const isSelected = isInspectorOpen && isPathSelected
|
|
90
92
|
|
|
91
93
|
const imageCaptionAction = generateCaptionsActions.useAction(props)
|
|
92
|
-
|
|
94
|
+
const translateAction = translateActions.useAction({...props, documentId: assistableDocumentId})
|
|
93
95
|
const manageInstructions = useCallback(
|
|
94
96
|
() =>
|
|
95
97
|
isSelected
|
|
@@ -134,7 +136,7 @@ export const assistFieldActions: DocumentFieldAction = {
|
|
|
134
136
|
)
|
|
135
137
|
|
|
136
138
|
const runInstructionsGroup = useMemo(() => {
|
|
137
|
-
return instructions?.length || imageCaptionAction
|
|
139
|
+
return instructions?.length || imageCaptionAction || translateAction
|
|
138
140
|
? node({
|
|
139
141
|
type: 'group',
|
|
140
142
|
icon: () => null,
|
|
@@ -151,7 +153,7 @@ export const assistFieldActions: DocumentFieldAction = {
|
|
|
151
153
|
})
|
|
152
154
|
),
|
|
153
155
|
imageCaptionAction,
|
|
154
|
-
].filter(
|
|
156
|
+
].filter((a): a is DocumentFieldActionItem => !!a),
|
|
155
157
|
expanded: true,
|
|
156
158
|
})
|
|
157
159
|
: undefined
|
|
@@ -163,6 +165,7 @@ export const assistFieldActions: DocumentFieldAction = {
|
|
|
163
165
|
documentIsNew,
|
|
164
166
|
assistSupported,
|
|
165
167
|
imageCaptionAction,
|
|
168
|
+
translateAction,
|
|
166
169
|
])
|
|
167
170
|
|
|
168
171
|
const instructionsLength = instructions?.length || 0
|
|
@@ -185,12 +188,14 @@ export const assistFieldActions: DocumentFieldAction = {
|
|
|
185
188
|
type: 'group',
|
|
186
189
|
icon: SparklesIcon,
|
|
187
190
|
title: pluginTitleShort,
|
|
188
|
-
children: [
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
children: [
|
|
192
|
+
runInstructionsGroup,
|
|
193
|
+
translateAction,
|
|
194
|
+
assistSupported && manageInstructionsItem,
|
|
195
|
+
].filter((c): c is DocumentFieldActionItem | DocumentFieldActionGroup => !!c),
|
|
191
196
|
expanded: false,
|
|
192
197
|
renderAsButton: true,
|
|
193
|
-
hidden: !assistSupported && !imageCaptionAction,
|
|
198
|
+
hidden: !assistSupported && !imageCaptionAction && !translateAction,
|
|
194
199
|
}),
|
|
195
200
|
[
|
|
196
201
|
//documentIsNew,
|
|
@@ -198,6 +203,7 @@ export const assistFieldActions: DocumentFieldAction = {
|
|
|
198
203
|
manageInstructionsItem,
|
|
199
204
|
assistSupported,
|
|
200
205
|
imageCaptionAction,
|
|
206
|
+
translateAction,
|
|
201
207
|
]
|
|
202
208
|
)
|
|
203
209
|
|
|
@@ -216,7 +222,7 @@ export const assistFieldActions: DocumentFieldAction = {
|
|
|
216
222
|
)
|
|
217
223
|
|
|
218
224
|
// If there are no instructions, we don't want to render the group
|
|
219
|
-
if (instructionsLength === 0 && !imageCaptionAction) {
|
|
225
|
+
if (instructionsLength === 0 && !imageCaptionAction && !translateAction) {
|
|
220
226
|
return emptyAction
|
|
221
227
|
}
|
|
222
228
|
|