@sanity/assist 1.2.15-lang.2 → 1.2.15-lang.4
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 +142 -22
- package/dist/index.d.ts +60 -0
- package/dist/index.esm.js +269 -101
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +269 -101
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/assistDocument/AssistDocumentInput.tsx +22 -3
- package/src/components/ImageContext.tsx +11 -5
- package/src/components/SafeValueInput.tsx +4 -1
- package/src/fieldActions/assistFieldActions.tsx +13 -5
- package/src/fieldActions/generateImageActions.tsx +58 -0
- package/src/helpers/typeUtils.ts +11 -0
- package/src/plugin.tsx +9 -1
- package/src/schemas/typeDefExtensions.ts +62 -0
- package/src/translate/FieldTranslationProvider.tsx +53 -44
- package/src/translate/languageStore.ts +18 -0
- package/src/translate/paths.test.ts +4 -4
- package/src/translate/paths.ts +5 -5
- package/src/{fieldActions → translate}/translateActions.tsx +73 -40
- package/src/useApiClient.ts +66 -11
|
@@ -13,33 +13,50 @@ import {useAiAssistanceConfig} from '../assistLayout/AiAssistanceConfigContext'
|
|
|
13
13
|
import {Box, Spinner} from '@sanity/ui'
|
|
14
14
|
import {isAssistSupported} from '../helpers/assistSupported'
|
|
15
15
|
import {useDocumentPane} from 'sanity/desk'
|
|
16
|
-
import {useFieldTranslation} from '
|
|
16
|
+
import {useFieldTranslation} from './FieldTranslationProvider'
|
|
17
17
|
import {useDraftDelayedTask} from '../assistDocument/RequestRunInstructionProvider'
|
|
18
|
-
import {
|
|
18
|
+
import {AssistOptions} from '../schemas/typeDefExtensions'
|
|
19
|
+
import {extractWithPath} from '@sanity/mutator'
|
|
19
20
|
|
|
20
21
|
function node(node: DocumentFieldActionItem | DocumentFieldActionGroup) {
|
|
21
22
|
return node
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
export type TranslateProps = DocumentFieldActionProps & {
|
|
25
|
+
export type TranslateProps = DocumentFieldActionProps & {
|
|
26
|
+
documentIsAssistable?: boolean
|
|
27
|
+
documentSchemaType?: ObjectSchemaType
|
|
28
|
+
}
|
|
25
29
|
export const translateActions: DocumentFieldAction = {
|
|
26
30
|
name: 'sanity-assist-translate',
|
|
27
31
|
useAction(props: TranslateProps) {
|
|
28
32
|
const {config} = useAiAssistanceConfig()
|
|
29
33
|
const apiClient = useApiClient(config?.__customApiClient)
|
|
30
34
|
|
|
31
|
-
const
|
|
32
|
-
|
|
35
|
+
const {
|
|
36
|
+
schemaType: fieldSchemaType,
|
|
37
|
+
path,
|
|
38
|
+
documentId,
|
|
39
|
+
documentSchemaType,
|
|
40
|
+
documentIsAssistable,
|
|
41
|
+
} = props
|
|
42
|
+
const isDocumentLevel = path.length === 0
|
|
33
43
|
|
|
34
|
-
const fieldTransEnabled = config.translate?.field?.documentTypes?.includes(schemaType.name)
|
|
35
44
|
const docTransTypes = config.translate?.document?.documentTypes
|
|
45
|
+
const options = fieldSchemaType?.options as AssistOptions | undefined
|
|
46
|
+
const addFieldAction = isDocumentLevel || options?.aiWritingAssistance?.translateAction
|
|
47
|
+
|
|
48
|
+
const fieldTransEnabled =
|
|
49
|
+
addFieldAction &&
|
|
50
|
+
documentSchemaType &&
|
|
51
|
+
config.translate?.field?.documentTypes?.includes(documentSchemaType.name)
|
|
36
52
|
const documentTranslationEnabled =
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
53
|
+
addFieldAction &&
|
|
54
|
+
documentSchemaType &&
|
|
55
|
+
((!docTransTypes && isAssistSupported(fieldSchemaType)) ||
|
|
56
|
+
docTransTypes?.includes(documentSchemaType.name))
|
|
40
57
|
|
|
41
58
|
// these checks are stable (ie, does not change after mount), so not breaking rules of hooks
|
|
42
|
-
if (documentTranslationEnabled || fieldTransEnabled) {
|
|
59
|
+
if (documentSchemaType && (documentTranslationEnabled || fieldTransEnabled)) {
|
|
43
60
|
const {value: documentValue, onChange: documentOnChange} = useDocumentPane()
|
|
44
61
|
const docRef = useRef(documentValue)
|
|
45
62
|
|
|
@@ -52,33 +69,47 @@ export const translateActions: DocumentFieldAction = {
|
|
|
52
69
|
docRef.current = documentValue
|
|
53
70
|
const languagePath = config.translate?.document?.languageField
|
|
54
71
|
|
|
55
|
-
//const {value: languageId} = extractWithPath(languagePath, documentValue)[0] ?? {}
|
|
56
72
|
// if this is true, it is stable, and not breaking rules of hooks
|
|
57
|
-
const translateDocumentAction = useMemo(
|
|
58
|
-
()
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
const translateDocumentAction = useMemo(() => {
|
|
74
|
+
if (!languagePath || !documentTranslationEnabled) {
|
|
75
|
+
return undefined
|
|
76
|
+
}
|
|
77
|
+
const {value: languageId} = extractWithPath(languagePath, documentValue)[0] ?? {}
|
|
78
|
+
const title = path.length ? `Translate` : `Translate document`
|
|
79
|
+
return node({
|
|
80
|
+
type: 'action',
|
|
81
|
+
icon: translationApi.loading
|
|
82
|
+
? () => (
|
|
83
|
+
<Box style={{height: 17}}>
|
|
84
|
+
<Spinner style={{transform: 'translateY(6px)'}} />
|
|
85
|
+
</Box>
|
|
86
|
+
)
|
|
87
|
+
: TranslateIcon,
|
|
88
|
+
title,
|
|
89
|
+
onAction: () => {
|
|
90
|
+
if (translationApi.loading || !languagePath || !documentId) {
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
translate({
|
|
94
|
+
languagePath,
|
|
95
|
+
translatePath: path,
|
|
96
|
+
documentId: documentId ?? '',
|
|
97
|
+
})
|
|
98
|
+
},
|
|
99
|
+
renderAsButton: true,
|
|
100
|
+
disabled:
|
|
101
|
+
translationApi.loading || !languageId
|
|
102
|
+
? {reason: 'Language is not set for this document'}
|
|
103
|
+
: undefined,
|
|
104
|
+
})
|
|
105
|
+
}, [
|
|
106
|
+
languagePath,
|
|
107
|
+
translate,
|
|
108
|
+
documentId,
|
|
109
|
+
translationApi.loading,
|
|
110
|
+
documentTranslationEnabled,
|
|
111
|
+
path,
|
|
112
|
+
])
|
|
82
113
|
|
|
83
114
|
const fieldTranslate = useFieldTranslation()
|
|
84
115
|
const openFieldTranslation = useDraftDelayedTask({
|
|
@@ -99,14 +130,15 @@ export const translateActions: DocumentFieldAction = {
|
|
|
99
130
|
</Box>
|
|
100
131
|
)
|
|
101
132
|
: TranslateIcon,
|
|
102
|
-
title: `Translate fields
|
|
133
|
+
title: `Translate fields...`,
|
|
103
134
|
onAction: () => {
|
|
104
135
|
if (fieldTranslate.translationLoading || !documentId) {
|
|
105
136
|
return
|
|
106
137
|
}
|
|
107
138
|
openFieldTranslation({
|
|
108
139
|
document: docRef.current,
|
|
109
|
-
documentSchema:
|
|
140
|
+
documentSchema: documentSchemaType,
|
|
141
|
+
translatePath: path,
|
|
110
142
|
})
|
|
111
143
|
},
|
|
112
144
|
renderAsButton: true,
|
|
@@ -115,10 +147,11 @@ export const translateActions: DocumentFieldAction = {
|
|
|
115
147
|
: undefined,
|
|
116
148
|
[
|
|
117
149
|
openFieldTranslation,
|
|
118
|
-
|
|
150
|
+
documentSchemaType,
|
|
119
151
|
documentId,
|
|
120
152
|
fieldTranslate.translationLoading,
|
|
121
153
|
fieldTransEnabled,
|
|
154
|
+
path,
|
|
122
155
|
]
|
|
123
156
|
)
|
|
124
157
|
|
|
@@ -127,7 +160,7 @@ export const translateActions: DocumentFieldAction = {
|
|
|
127
160
|
return node({
|
|
128
161
|
type: 'group',
|
|
129
162
|
icon: () => null,
|
|
130
|
-
title: '
|
|
163
|
+
title: 'Translation',
|
|
131
164
|
children: [translateDocumentAction, translateFieldsAction].filter(
|
|
132
165
|
(c): c is DocumentFieldActionItem => !!c
|
|
133
166
|
),
|
package/src/useApiClient.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {useClient, useCurrentUser, useSchema} from 'sanity'
|
|
1
|
+
import {Path, pathToString, useClient, useCurrentUser, useSchema} from 'sanity'
|
|
2
2
|
import {useCallback, useMemo, useState} from 'react'
|
|
3
3
|
import {serializeSchema} from './schemas/serialize/serializeSchema'
|
|
4
4
|
import {useToast} from '@sanity/ui'
|
|
5
5
|
import {SanityClient} from '@sanity/client'
|
|
6
|
-
import {
|
|
6
|
+
import {FieldLanguageMap} from './translate/paths'
|
|
7
|
+
import {documentRootKey} from './types'
|
|
7
8
|
|
|
8
9
|
export interface UserTextInstance {
|
|
9
10
|
blockKey: string
|
|
@@ -26,6 +27,13 @@ export interface InstructStatus {
|
|
|
26
27
|
validToken: boolean
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
export interface TranslateRequest {
|
|
31
|
+
documentId: string
|
|
32
|
+
translatePath: Path
|
|
33
|
+
languagePath?: string
|
|
34
|
+
fieldLanguageMap?: FieldLanguageMap[]
|
|
35
|
+
}
|
|
36
|
+
|
|
29
37
|
const basePath = '/assist/tasks/instruction'
|
|
30
38
|
|
|
31
39
|
export function useApiClient(customApiClient?: (defaultClient: SanityClient) => SanityClient) {
|
|
@@ -44,15 +52,7 @@ export function useTranslate(apiClient: SanityClient) {
|
|
|
44
52
|
const toast = useToast()
|
|
45
53
|
|
|
46
54
|
const translate = useCallback(
|
|
47
|
-
({
|
|
48
|
-
documentId,
|
|
49
|
-
languagePath,
|
|
50
|
-
fieldLanguageMap,
|
|
51
|
-
}: {
|
|
52
|
-
documentId: string
|
|
53
|
-
languagePath?: string
|
|
54
|
-
fieldLanguageMap?: TranslationMap[]
|
|
55
|
-
}) => {
|
|
55
|
+
({documentId, languagePath, translatePath, fieldLanguageMap}: TranslateRequest) => {
|
|
56
56
|
setLoading(true)
|
|
57
57
|
|
|
58
58
|
return apiClient
|
|
@@ -66,6 +66,8 @@ export function useTranslate(apiClient: SanityClient) {
|
|
|
66
66
|
types,
|
|
67
67
|
languagePath,
|
|
68
68
|
fieldLanguageMap,
|
|
69
|
+
translatePath:
|
|
70
|
+
translatePath.length === 0 ? documentRootKey : pathToString(translatePath),
|
|
69
71
|
userId: user?.id,
|
|
70
72
|
},
|
|
71
73
|
})
|
|
@@ -151,6 +153,59 @@ export function useGenerateCaption(apiClient: SanityClient) {
|
|
|
151
153
|
)
|
|
152
154
|
}
|
|
153
155
|
|
|
156
|
+
export function useGenerateImage(apiClient: SanityClient) {
|
|
157
|
+
const [loading, setLoading] = useState(false)
|
|
158
|
+
const user = useCurrentUser()
|
|
159
|
+
const schema = useSchema()
|
|
160
|
+
const types = useMemo(() => serializeSchema(schema, {leanFormat: true}), [schema])
|
|
161
|
+
const toast = useToast()
|
|
162
|
+
|
|
163
|
+
const generateImage = useCallback(
|
|
164
|
+
({path, documentId}: {path: string; documentId: string}) => {
|
|
165
|
+
setLoading(true)
|
|
166
|
+
|
|
167
|
+
return apiClient
|
|
168
|
+
.request({
|
|
169
|
+
method: 'POST',
|
|
170
|
+
url: `/assist/tasks/generate-image/${apiClient.config().dataset}?projectId=${
|
|
171
|
+
apiClient.config().projectId
|
|
172
|
+
}`,
|
|
173
|
+
body: {
|
|
174
|
+
path,
|
|
175
|
+
documentId,
|
|
176
|
+
types,
|
|
177
|
+
userId: user?.id,
|
|
178
|
+
},
|
|
179
|
+
})
|
|
180
|
+
.catch((e) => {
|
|
181
|
+
toast.push({
|
|
182
|
+
status: 'error',
|
|
183
|
+
title: 'Generate image from prompt failed',
|
|
184
|
+
description: e.message,
|
|
185
|
+
})
|
|
186
|
+
setLoading(false)
|
|
187
|
+
throw e
|
|
188
|
+
})
|
|
189
|
+
.finally(() => {
|
|
190
|
+
// adding some artificial delay here
|
|
191
|
+
// server responds with 201 then proceeds; we dont need to allow spamming the button
|
|
192
|
+
setTimeout(() => {
|
|
193
|
+
setLoading(false)
|
|
194
|
+
}, 2000)
|
|
195
|
+
})
|
|
196
|
+
},
|
|
197
|
+
[setLoading, apiClient, toast, user, types]
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
return useMemo(
|
|
201
|
+
() => ({
|
|
202
|
+
generateImage,
|
|
203
|
+
loading,
|
|
204
|
+
}),
|
|
205
|
+
[generateImage, loading]
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
|
|
154
209
|
export function useGetInstructStatus(apiClient: SanityClient) {
|
|
155
210
|
const [loading, setLoading] = useState(true)
|
|
156
211
|
|