@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.
@@ -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 '../translate/FieldTranslationProvider'
16
+ import {useFieldTranslation} from './FieldTranslationProvider'
17
17
  import {useDraftDelayedTask} from '../assistDocument/RequestRunInstructionProvider'
18
- import {getLanguageParams} from '../translate/getLanguageParams'
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 & {documentIsAssistable?: boolean}
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 isDocumentLevel = props.path.length === 0
32
- const {schemaType, documentId, documentIsAssistable} = props
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
- isDocumentLevel &&
38
- ((!docTransTypes && isAssistSupported(schemaType)) ||
39
- docTransTypes?.includes(schemaType.name))
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
- languagePath && documentTranslationEnabled
60
- ? node({
61
- type: 'action',
62
- icon: translationApi.loading
63
- ? () => (
64
- <Box style={{height: 17}}>
65
- <Spinner style={{transform: 'translateY(6px)'}} />
66
- </Box>
67
- )
68
- : TranslateIcon,
69
- title: `Translate document`,
70
- onAction: () => {
71
- if (translationApi.loading || !languagePath || !documentId) {
72
- return
73
- }
74
- translate({languagePath, documentId: documentId ?? ''})
75
- },
76
- renderAsButton: true,
77
- disabled: translationApi.loading,
78
- })
79
- : undefined,
80
- [languagePath, translate, documentId, translationApi.loading, documentTranslationEnabled]
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: schemaType as ObjectSchemaType,
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
- schemaType,
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: 'Translate',
163
+ title: 'Translation',
131
164
  children: [translateDocumentAction, translateFieldsAction].filter(
132
165
  (c): c is DocumentFieldActionItem => !!c
133
166
  ),
@@ -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 {TranslationMap} from './translate/paths'
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