@sanity/assist 5.0.4 → 6.0.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/LICENSE +1 -1
- package/README.md +28 -254
- package/dist/index.d.ts +322 -410
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3205 -2670
- package/dist/index.js.map +1 -1
- package/package.json +41 -81
- package/dist/index.cjs +0 -4239
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -791
- package/sanity.json +0 -8
- package/src/_lib/connector/ConnectFromRegion.tsx +0 -25
- package/src/_lib/connector/ConnectToRegion.tsx +0 -23
- package/src/_lib/connector/ConnectorRegion.tsx +0 -24
- package/src/_lib/connector/ConnectorsProvider.tsx +0 -20
- package/src/_lib/connector/ConnectorsStore.ts +0 -122
- package/src/_lib/connector/ConnectorsStoreContext.ts +0 -5
- package/src/_lib/connector/helpers.ts +0 -5
- package/src/_lib/connector/index.ts +0 -9
- package/src/_lib/connector/mapConnectorToLine.ts +0 -83
- package/src/_lib/connector/types.ts +0 -56
- package/src/_lib/connector/useConnectorsStore.ts +0 -14
- package/src/_lib/connector/useRegionRects.ts +0 -142
- package/src/_lib/fixedListenQuery.ts +0 -101
- package/src/_lib/form/DocumentForm.tsx +0 -201
- package/src/_lib/form/constants.ts +0 -1
- package/src/_lib/form/helpers.ts +0 -32
- package/src/_lib/form/index.ts +0 -1
- package/src/_lib/randomKey.ts +0 -29
- package/src/_lib/useListeningQuery.ts +0 -62
- package/src/_lib/usePrevious.ts +0 -9
- package/src/assistConnectors/AssistConnectorsOverlay.tsx +0 -133
- package/src/assistConnectors/ConnectorPath.tsx +0 -63
- package/src/assistConnectors/draw/arrowPath.ts +0 -9
- package/src/assistConnectors/draw/connectorPath.ts +0 -142
- package/src/assistConnectors/index.ts +0 -1
- package/src/assistDocument/AssistDocumentContext.tsx +0 -51
- package/src/assistDocument/AssistDocumentContextProvider.tsx +0 -17
- package/src/assistDocument/AssistDocumentInput.tsx +0 -61
- package/src/assistDocument/AssistDocumentLayout.tsx +0 -12
- package/src/assistDocument/RequestRunInstructionProvider.tsx +0 -61
- package/src/assistDocument/components/AssistDocumentForm.tsx +0 -287
- package/src/assistDocument/components/AssistTypeContext.tsx +0 -7
- package/src/assistDocument/components/FieldRefPreview.tsx +0 -26
- package/src/assistDocument/components/InstructionsArrayField.tsx +0 -8
- package/src/assistDocument/components/InstructionsArrayInput.tsx +0 -27
- package/src/assistDocument/components/SelectedFieldContext.tsx +0 -10
- package/src/assistDocument/components/generic/HiddenFieldTitle.tsx +0 -5
- package/src/assistDocument/components/helpers.ts +0 -21
- package/src/assistDocument/components/instruction/BackToInstructionsLink.tsx +0 -32
- package/src/assistDocument/components/instruction/FieldRefInput.tsx +0 -54
- package/src/assistDocument/components/instruction/InstructionInput.tsx +0 -89
- package/src/assistDocument/components/instruction/InstructionOutputField.tsx +0 -46
- package/src/assistDocument/components/instruction/InstructionOutputInput.tsx +0 -206
- package/src/assistDocument/components/instruction/PromptInput.tsx +0 -59
- package/src/assistDocument/components/instruction/appearance/IconInput.tsx +0 -46
- package/src/assistDocument/components/instruction/appearance/InstructionVisibility.tsx +0 -37
- package/src/assistDocument/hooks/useAssistDocumentContextValue.tsx +0 -127
- package/src/assistDocument/hooks/useDocumentState.ts +0 -6
- package/src/assistDocument/hooks/useInstructionToaster.tsx +0 -75
- package/src/assistDocument/hooks/useStudioAssistDocument.ts +0 -99
- package/src/assistDocument/index.ts +0 -1
- package/src/assistFormComponents/AssistField.tsx +0 -63
- package/src/assistFormComponents/AssistFormBlock.tsx +0 -31
- package/src/assistFormComponents/AssistInlineFormBlock.tsx +0 -13
- package/src/assistFormComponents/AssistItem.tsx +0 -21
- package/src/assistFormComponents/validation/listItem.tsx +0 -63
- package/src/assistFormComponents/validation/validationList.tsx +0 -90
- package/src/assistInspector/AssistInspector.tsx +0 -419
- package/src/assistInspector/FieldAutocomplete.tsx +0 -146
- package/src/assistInspector/InstructionTaskHistoryButton.tsx +0 -262
- package/src/assistInspector/constants.ts +0 -1
- package/src/assistInspector/helpers.ts +0 -211
- package/src/assistInspector/index.ts +0 -27
- package/src/assistLayout/AiAssistanceConfigContext.tsx +0 -32
- package/src/assistLayout/AiAssistanceConfigProvider.tsx +0 -98
- package/src/assistLayout/AssistLayout.tsx +0 -39
- package/src/assistLayout/RunInstructionProvider.tsx +0 -278
- package/src/assistLayout/fieldRefCache.tsx +0 -34
- package/src/assistTypes.ts +0 -83
- package/src/components/AssistFeatureBadge.tsx +0 -9
- package/src/components/FadeInContent.tsx +0 -40
- package/src/components/HideReferenceChangedBannerInput.tsx +0 -25
- package/src/components/ImageContext.tsx +0 -85
- package/src/components/SafeValueInput.tsx +0 -74
- package/src/components/TimeAgo.tsx +0 -18
- package/src/constants.ts +0 -20
- package/src/fieldActions/PrivateIcon.tsx +0 -20
- package/src/fieldActions/assistFieldActions.tsx +0 -320
- package/src/fieldActions/customFieldActions.tsx +0 -333
- package/src/fieldActions/generateCaptionActions.tsx +0 -77
- package/src/fieldActions/generateImageActions.tsx +0 -58
- package/src/fieldActions/useUserInput.ts +0 -107
- package/src/globals.d.ts +0 -4
- package/src/helpers/assistSupported.ts +0 -49
- package/src/helpers/conditionalMembers.test.ts +0 -319
- package/src/helpers/conditionalMembers.ts +0 -134
- package/src/helpers/ids.test.ts +0 -28
- package/src/helpers/ids.ts +0 -23
- package/src/helpers/misc.ts +0 -25
- package/src/helpers/styleguide.ts +0 -24
- package/src/helpers/typeUtils.ts +0 -60
- package/src/helpers/useAssistSupported.ts +0 -8
- package/src/index.ts +0 -26
- package/src/onboarding/FirstAssistedPathProvider.tsx +0 -30
- package/src/onboarding/InspectorOnboarding.tsx +0 -47
- package/src/onboarding/onboardingStore.ts +0 -32
- package/src/plugin.tsx +0 -162
- package/src/presence/AiFieldPresence.tsx +0 -28
- package/src/presence/AssistAvatar.tsx +0 -96
- package/src/presence/AssistDocumentPresence.tsx +0 -50
- package/src/presence/useAssistPresence.ts +0 -64
- package/src/schemas/assistDocumentSchema.tsx +0 -497
- package/src/schemas/contextDocumentSchema.tsx +0 -57
- package/src/schemas/index.ts +0 -69
- package/src/schemas/serialize/SchemTypeTool.tsx +0 -103
- package/src/schemas/serialize/schemaUtils.ts +0 -38
- package/src/schemas/serialize/serializeSchema.test.ts +0 -819
- package/src/schemas/serialize/serializeSchema.ts +0 -224
- package/src/schemas/serializedSchemaTypeSchema.ts +0 -60
- package/src/schemas/typeDefExtensions.ts +0 -127
- package/src/translate/FieldTranslationProvider.tsx +0 -382
- package/src/translate/getLanguageParams.ts +0 -26
- package/src/translate/languageStore.ts +0 -18
- package/src/translate/paths.test.ts +0 -181
- package/src/translate/paths.ts +0 -183
- package/src/translate/translateActions.tsx +0 -205
- package/src/translate/types.ts +0 -197
- package/src/types.ts +0 -220
- package/src/useApiClient.ts +0 -338
- package/v2-incompatible.js +0 -11
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CheckmarkCircleIcon,
|
|
3
|
-
ClockIcon,
|
|
4
|
-
CloseCircleIcon,
|
|
5
|
-
ErrorOutlineIcon,
|
|
6
|
-
SyncIcon,
|
|
7
|
-
} from '@sanity/icons'
|
|
8
|
-
import {
|
|
9
|
-
Box,
|
|
10
|
-
Button,
|
|
11
|
-
Card,
|
|
12
|
-
Flex,
|
|
13
|
-
Popover,
|
|
14
|
-
Spinner,
|
|
15
|
-
Stack,
|
|
16
|
-
Text,
|
|
17
|
-
useClickOutside,
|
|
18
|
-
useGlobalKeyDown,
|
|
19
|
-
useLayer,
|
|
20
|
-
} from '@sanity/ui'
|
|
21
|
-
import {createElement, type ForwardedRef, forwardRef, useCallback, useMemo, useState} from 'react'
|
|
22
|
-
import {StatusButton, type StatusButtonProps, typed, useClient} from 'sanity'
|
|
23
|
-
import {keyframes, styled} from 'styled-components'
|
|
24
|
-
|
|
25
|
-
import {TimeAgo} from '../components/TimeAgo'
|
|
26
|
-
import {maxHistoryVisibilityMs, pluginTitle} from '../constants'
|
|
27
|
-
import {assistTasksStatusId} from '../helpers/ids'
|
|
28
|
-
import {getInstructionTitle} from '../helpers/misc'
|
|
29
|
-
import type {AssistTasksStatus, InstructionTask, StudioInstruction, TaskEndedReason} from '../types'
|
|
30
|
-
|
|
31
|
-
export interface InstructionTaskHistoryButtonProps {
|
|
32
|
-
documentId?: string
|
|
33
|
-
tasks: InstructionTask[] | undefined
|
|
34
|
-
instructions: StudioInstruction[] | undefined
|
|
35
|
-
showTitles: boolean
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface CancelableInstructionTask extends InstructionTask {
|
|
39
|
-
cancel: () => void
|
|
40
|
-
title?: string
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const rotate = keyframes`
|
|
44
|
-
0% {
|
|
45
|
-
transform: rotate(0);
|
|
46
|
-
}
|
|
47
|
-
100% {
|
|
48
|
-
transform: rotate(360deg);
|
|
49
|
-
}
|
|
50
|
-
`
|
|
51
|
-
|
|
52
|
-
const SyncSpinningIcon = styled(SyncIcon)`
|
|
53
|
-
animation: ${rotate} 1s linear infinite;
|
|
54
|
-
`
|
|
55
|
-
|
|
56
|
-
const TASK_CONFIG = {
|
|
57
|
-
aborted: {
|
|
58
|
-
title: 'Canceled',
|
|
59
|
-
icon: CloseCircleIcon,
|
|
60
|
-
tone: 'transparent',
|
|
61
|
-
},
|
|
62
|
-
error: {
|
|
63
|
-
title: 'Error',
|
|
64
|
-
icon: ErrorOutlineIcon,
|
|
65
|
-
tone: 'critical',
|
|
66
|
-
},
|
|
67
|
-
success: {
|
|
68
|
-
title: 'Completed',
|
|
69
|
-
icon: CheckmarkCircleIcon,
|
|
70
|
-
tone: 'positive',
|
|
71
|
-
},
|
|
72
|
-
timeout: {
|
|
73
|
-
title: 'Timeout',
|
|
74
|
-
icon: ClockIcon,
|
|
75
|
-
tone: 'caution',
|
|
76
|
-
},
|
|
77
|
-
} as const
|
|
78
|
-
|
|
79
|
-
export function InstructionTaskHistoryButton(props: InstructionTaskHistoryButtonProps) {
|
|
80
|
-
const {tasks, instructions, documentId, showTitles} = props
|
|
81
|
-
|
|
82
|
-
const client = useClient({apiVersion: '2023-01-01'})
|
|
83
|
-
const cancelRun = useCallback(
|
|
84
|
-
(taskKey: string) => {
|
|
85
|
-
if (!documentId) {
|
|
86
|
-
return
|
|
87
|
-
}
|
|
88
|
-
const statusDocId = assistTasksStatusId(documentId)
|
|
89
|
-
const basePath = `${typed<keyof AssistTasksStatus>('tasks')}[_key=="${taskKey}"]`
|
|
90
|
-
client
|
|
91
|
-
.patch(statusDocId)
|
|
92
|
-
.set({
|
|
93
|
-
[`${basePath}.${typed<keyof InstructionTask>('ended')}`]: new Date().toISOString(),
|
|
94
|
-
[`${basePath}.${typed<keyof InstructionTask>('reason')}`]:
|
|
95
|
-
typed<TaskEndedReason>('aborted'),
|
|
96
|
-
})
|
|
97
|
-
.commit()
|
|
98
|
-
.catch(console.error)
|
|
99
|
-
},
|
|
100
|
-
[client, documentId],
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
const titledTasks = useMemo(() => {
|
|
104
|
-
const t =
|
|
105
|
-
tasks
|
|
106
|
-
?.filter(
|
|
107
|
-
(task) =>
|
|
108
|
-
task.started &&
|
|
109
|
-
new Date().getTime() - new Date(task.started).getTime() < maxHistoryVisibilityMs,
|
|
110
|
-
)
|
|
111
|
-
.map((task): CancelableInstructionTask => {
|
|
112
|
-
const instruction = instructions?.find((i) => i._key === task.instructionKey)
|
|
113
|
-
return {
|
|
114
|
-
...task,
|
|
115
|
-
title: showTitles ? (task.title ?? getInstructionTitle(instruction)) : undefined,
|
|
116
|
-
cancel: () => cancelRun(task._key),
|
|
117
|
-
}
|
|
118
|
-
}) ?? []
|
|
119
|
-
t.sort((a, b) => new Date(b.started ?? '').getTime() - new Date(a.started ?? '').getTime())
|
|
120
|
-
return t
|
|
121
|
-
}, [tasks, instructions, cancelRun, showTitles])
|
|
122
|
-
|
|
123
|
-
// const id = useId()
|
|
124
|
-
|
|
125
|
-
const isRunning = useMemo(() => titledTasks.some((r) => !r.ended), [titledTasks])
|
|
126
|
-
const hasErrors = useMemo(
|
|
127
|
-
() => titledTasks.some((r) => r.reason === 'error' || r.reason === 'timeout'),
|
|
128
|
-
[titledTasks],
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
const [open, setOpen] = useState(false)
|
|
132
|
-
|
|
133
|
-
const toggleOpen = useCallback(() => setOpen((v) => !v), [])
|
|
134
|
-
|
|
135
|
-
const [button, setButton] = useState<HTMLButtonElement | null>(null)
|
|
136
|
-
const [popover, setPopover] = useState<HTMLDivElement | null>(null)
|
|
137
|
-
|
|
138
|
-
const handleClickOutside = useCallback(() => {
|
|
139
|
-
setOpen(false)
|
|
140
|
-
}, [])
|
|
141
|
-
|
|
142
|
-
useClickOutside(handleClickOutside, [button, popover])
|
|
143
|
-
|
|
144
|
-
const handleEscape = useCallback(() => {
|
|
145
|
-
setOpen(false)
|
|
146
|
-
button?.focus()
|
|
147
|
-
}, [button])
|
|
148
|
-
|
|
149
|
-
return (
|
|
150
|
-
<Popover
|
|
151
|
-
constrainSize
|
|
152
|
-
content={<TaskList onEscape={handleEscape} tasks={titledTasks} />}
|
|
153
|
-
open={open && !!titledTasks?.length}
|
|
154
|
-
placement="top"
|
|
155
|
-
portal
|
|
156
|
-
ref={setPopover}
|
|
157
|
-
width={0}
|
|
158
|
-
>
|
|
159
|
-
<TaskStatusButton
|
|
160
|
-
disabled={!titledTasks?.length}
|
|
161
|
-
hasErrors={hasErrors}
|
|
162
|
-
isRunning={isRunning}
|
|
163
|
-
onClick={toggleOpen}
|
|
164
|
-
ref={setButton}
|
|
165
|
-
selected={open}
|
|
166
|
-
/>
|
|
167
|
-
</Popover>
|
|
168
|
-
)
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const TASK_STATUS_BUTTON_TOOLTIP_PROPS: StatusButtonProps['tooltipProps'] = {
|
|
172
|
-
placement: 'top',
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const TaskStatusButton = forwardRef(function TaskStatusButton(
|
|
176
|
-
props: {
|
|
177
|
-
disabled: boolean
|
|
178
|
-
hasErrors: boolean
|
|
179
|
-
isRunning: boolean
|
|
180
|
-
onClick: () => void
|
|
181
|
-
selected: boolean
|
|
182
|
-
},
|
|
183
|
-
ref: ForwardedRef<HTMLButtonElement>,
|
|
184
|
-
) {
|
|
185
|
-
const {disabled, hasErrors, isRunning, onClick, selected} = props
|
|
186
|
-
|
|
187
|
-
return (
|
|
188
|
-
<StatusButton
|
|
189
|
-
label={`${pluginTitle} status`}
|
|
190
|
-
aria-label={`${pluginTitle} status`}
|
|
191
|
-
icon={isRunning ? SyncSpinningIcon : hasErrors ? ErrorOutlineIcon : CheckmarkCircleIcon}
|
|
192
|
-
mode="bleed"
|
|
193
|
-
onClick={onClick}
|
|
194
|
-
tone={hasErrors ? 'critical' : undefined}
|
|
195
|
-
disabled={disabled}
|
|
196
|
-
ref={ref}
|
|
197
|
-
selected={selected}
|
|
198
|
-
tooltipProps={TASK_STATUS_BUTTON_TOOLTIP_PROPS}
|
|
199
|
-
/>
|
|
200
|
-
)
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
function TaskList(props: {onEscape: () => void; tasks: CancelableInstructionTask[]}) {
|
|
204
|
-
const {onEscape, tasks} = props
|
|
205
|
-
|
|
206
|
-
const {isTopLayer} = useLayer()
|
|
207
|
-
|
|
208
|
-
useGlobalKeyDown(
|
|
209
|
-
useCallback(
|
|
210
|
-
(event) => {
|
|
211
|
-
if (isTopLayer && event.key === 'Escape') {
|
|
212
|
-
onEscape()
|
|
213
|
-
}
|
|
214
|
-
},
|
|
215
|
-
[isTopLayer, onEscape],
|
|
216
|
-
),
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
return (
|
|
220
|
-
<Stack padding={1} space={1}>
|
|
221
|
-
{tasks.map((task) => (
|
|
222
|
-
<TaskItem key={task._key} task={task} />
|
|
223
|
-
))}
|
|
224
|
-
</Stack>
|
|
225
|
-
)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function TaskItem(props: {task: CancelableInstructionTask}) {
|
|
229
|
-
const {task} = props
|
|
230
|
-
|
|
231
|
-
const taskType = task.reason && TASK_CONFIG[task.reason]
|
|
232
|
-
return (
|
|
233
|
-
<Card radius={2} tone={taskType && taskType?.tone}>
|
|
234
|
-
<Flex align="center" gap={1}>
|
|
235
|
-
<Flex align="flex-start" flex={1} gap={3} padding={3}>
|
|
236
|
-
<Box flex="none">
|
|
237
|
-
<Text size={1}>
|
|
238
|
-
{taskType && createElement(taskType.icon)}
|
|
239
|
-
{!task.reason && <Spinner />}
|
|
240
|
-
</Text>
|
|
241
|
-
</Box>
|
|
242
|
-
<Stack flex={1} space={2}>
|
|
243
|
-
<Text size={1} weight="medium">
|
|
244
|
-
{taskType ? taskType.title : 'Running'}
|
|
245
|
-
{task.title && <>: {task.title}</>}
|
|
246
|
-
</Text>
|
|
247
|
-
{task.message ? <Text size={1}>{task.message}</Text> : null}
|
|
248
|
-
<Text muted size={1}>
|
|
249
|
-
<TimeAgo date={task.ended ?? task.started} />
|
|
250
|
-
</Text>
|
|
251
|
-
</Stack>
|
|
252
|
-
</Flex>
|
|
253
|
-
|
|
254
|
-
{!task.ended && (
|
|
255
|
-
<Box flex="none" padding={1}>
|
|
256
|
-
<Button fontSize={1} mode="bleed" onClick={task.cancel} text="Cancel" />
|
|
257
|
-
</Box>
|
|
258
|
-
)}
|
|
259
|
-
</Flex>
|
|
260
|
-
</Card>
|
|
261
|
-
)
|
|
262
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const aiInspectorId = 'ai-assistance'
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BlockContentIcon,
|
|
3
|
-
BlockquoteIcon,
|
|
4
|
-
DocumentIcon,
|
|
5
|
-
ImageIcon,
|
|
6
|
-
LinkIcon,
|
|
7
|
-
OlistIcon,
|
|
8
|
-
StringIcon,
|
|
9
|
-
} from '@sanity/icons'
|
|
10
|
-
import {extractWithPath} from '@sanity/mutator'
|
|
11
|
-
import {type ComponentType, useMemo} from 'react'
|
|
12
|
-
import {
|
|
13
|
-
type ArraySchemaType,
|
|
14
|
-
isKeySegment,
|
|
15
|
-
isObjectSchemaType,
|
|
16
|
-
type ObjectSchemaType,
|
|
17
|
-
type Path,
|
|
18
|
-
pathToString,
|
|
19
|
-
type SanityDocumentLike,
|
|
20
|
-
type SchemaType,
|
|
21
|
-
stringToPath,
|
|
22
|
-
} from 'sanity'
|
|
23
|
-
import {type PaneRouterContextValue, usePaneRouter} from 'sanity/structure'
|
|
24
|
-
import {isAssistSupported} from '../helpers/assistSupported'
|
|
25
|
-
import {isPortableTextArray, isType} from '../helpers/typeUtils'
|
|
26
|
-
import {type AssistInspectorRouteParams, documentRootKey} from '../types'
|
|
27
|
-
import {useAiAssistanceConfig} from '../assistLayout/AiAssistanceConfigContext'
|
|
28
|
-
|
|
29
|
-
export interface FieldRef {
|
|
30
|
-
key: string
|
|
31
|
-
path: Path
|
|
32
|
-
title: string
|
|
33
|
-
schemaType: SchemaType
|
|
34
|
-
icon: ComponentType
|
|
35
|
-
synthetic?: boolean
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const maxDepth = 6
|
|
39
|
-
|
|
40
|
-
export function getTypeIcon(schemaType: SchemaType): ComponentType {
|
|
41
|
-
let t: SchemaType | undefined = schemaType
|
|
42
|
-
|
|
43
|
-
while (t) {
|
|
44
|
-
if (t.icon) return t.icon
|
|
45
|
-
t = t.type
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (isType(schemaType, 'slug')) return LinkIcon
|
|
49
|
-
if (isType(schemaType, 'image')) return ImageIcon
|
|
50
|
-
if (schemaType.jsonType === 'array' && isPortableTextArray(schemaType)) return BlockContentIcon
|
|
51
|
-
|
|
52
|
-
if (schemaType.jsonType === 'array') return OlistIcon
|
|
53
|
-
if (schemaType.jsonType === 'object') return BlockquoteIcon
|
|
54
|
-
if (schemaType.jsonType === 'string') return StringIcon
|
|
55
|
-
|
|
56
|
-
return DocumentIcon
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function asFieldRefsByTypePath(fieldRefs: FieldRef[]): Record<string, FieldRef | undefined> {
|
|
60
|
-
const lookup: Record<string, FieldRef | undefined> = fieldRefs.reduce(
|
|
61
|
-
(acc, ref) => ({...acc, [ref.key]: ref}),
|
|
62
|
-
{},
|
|
63
|
-
)
|
|
64
|
-
return lookup
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function getDocumentFieldRef(schemaType: ObjectSchemaType): FieldRef {
|
|
68
|
-
return {
|
|
69
|
-
key: documentRootKey,
|
|
70
|
-
icon: schemaType.icon ?? DocumentIcon,
|
|
71
|
-
title: `The entire document`,
|
|
72
|
-
path: [],
|
|
73
|
-
schemaType: schemaType,
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function getFieldRefs(
|
|
78
|
-
schemaType: ObjectSchemaType,
|
|
79
|
-
parent?: FieldRef,
|
|
80
|
-
depth = 0,
|
|
81
|
-
): FieldRef[] {
|
|
82
|
-
if (depth >= maxDepth) {
|
|
83
|
-
return []
|
|
84
|
-
}
|
|
85
|
-
return schemaType.fields
|
|
86
|
-
.filter((f) => !f.name.startsWith('_'))
|
|
87
|
-
.flatMap((field) => {
|
|
88
|
-
const path: Path = parent ? [...parent.path, field.name] : [field.name]
|
|
89
|
-
const title = field.type.title ?? field.name
|
|
90
|
-
const fieldRef: FieldRef = {
|
|
91
|
-
key: patchableKey(pathToString(path)),
|
|
92
|
-
path,
|
|
93
|
-
title: parent ? [parent.title, title].join(' / ') : title,
|
|
94
|
-
schemaType: field.type,
|
|
95
|
-
icon: getTypeIcon(field.type),
|
|
96
|
-
}
|
|
97
|
-
const fields =
|
|
98
|
-
field.type.jsonType === 'object' ? getFieldRefs(field.type, fieldRef, depth + 1) : []
|
|
99
|
-
|
|
100
|
-
const syntheticFields =
|
|
101
|
-
field.type.jsonType === 'array' ? getSyntheticFields(field.type, fieldRef, depth + 1) : []
|
|
102
|
-
if (!isAssistSupported(field.type)) {
|
|
103
|
-
return [...fields, ...syntheticFields]
|
|
104
|
-
}
|
|
105
|
-
return [fieldRef, ...fields, ...syntheticFields]
|
|
106
|
-
})
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function getSyntheticFields(schemaType: ArraySchemaType, parent?: FieldRef, depth = 0) {
|
|
110
|
-
if (depth >= maxDepth) {
|
|
111
|
-
return []
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return schemaType.of
|
|
115
|
-
.filter((itemType) => !isType(itemType, 'block'))
|
|
116
|
-
.flatMap((itemType) => {
|
|
117
|
-
const segment = {_key: itemType.name}
|
|
118
|
-
const title = itemType.title ?? itemType.name
|
|
119
|
-
const path: Path = parent ? [...parent.path, segment] : [segment]
|
|
120
|
-
const fieldRef: FieldRef = {
|
|
121
|
-
key: patchableKey(pathToString(path)),
|
|
122
|
-
path,
|
|
123
|
-
title: parent ? [parent.title, title].join(' / ') : title,
|
|
124
|
-
schemaType: itemType,
|
|
125
|
-
icon: getTypeIcon(itemType),
|
|
126
|
-
synthetic: true,
|
|
127
|
-
}
|
|
128
|
-
const fields =
|
|
129
|
-
itemType.jsonType === 'object' ? getFieldRefs(itemType, fieldRef, depth + 1) : []
|
|
130
|
-
|
|
131
|
-
if (!isAssistSupported(itemType)) {
|
|
132
|
-
return fields
|
|
133
|
-
}
|
|
134
|
-
return [fieldRef, ...fields]
|
|
135
|
-
})
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function getTypePath(doc: SanityDocumentLike, pathString: string) {
|
|
139
|
-
if (!pathString) {
|
|
140
|
-
return undefined
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const path = stringToPath(pathString)
|
|
144
|
-
const currentPath: Path = []
|
|
145
|
-
let valid = true
|
|
146
|
-
const syntheticPath = path.map((segment) => {
|
|
147
|
-
currentPath.push(segment)
|
|
148
|
-
|
|
149
|
-
if (isKeySegment(segment)) {
|
|
150
|
-
const match = extractWithPath(pathToString(currentPath), doc)[0]
|
|
151
|
-
const value = match?.value
|
|
152
|
-
if (match && value && typeof value === 'object' && '_type' in value) {
|
|
153
|
-
return {_key: value._type as string}
|
|
154
|
-
}
|
|
155
|
-
valid = false
|
|
156
|
-
}
|
|
157
|
-
return segment
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
return valid ? patchableKey(pathToString(syntheticPath)) : undefined
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* mutator crashes if path contains certain letters
|
|
165
|
-
* @param pathKey
|
|
166
|
-
*/
|
|
167
|
-
function patchableKey(pathKey: string) {
|
|
168
|
-
return pathKey.replace(/[=]=/g, ':').replace(/[[\]]/g, '|').replace(/"/g, '')
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export function useTypePath(doc: SanityDocumentLike, pathString: string) {
|
|
172
|
-
return useMemo(() => getTypePath(doc, pathString), [doc, pathString])
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export function useSelectedField(
|
|
176
|
-
documentSchemaType?: SchemaType,
|
|
177
|
-
path?: string,
|
|
178
|
-
): FieldRef | undefined {
|
|
179
|
-
const {getFieldRefs} = useAiAssistanceConfig()
|
|
180
|
-
|
|
181
|
-
const selectableFields = useMemo(
|
|
182
|
-
() =>
|
|
183
|
-
documentSchemaType && isObjectSchemaType(documentSchemaType)
|
|
184
|
-
? [getDocumentFieldRef(documentSchemaType), ...getFieldRefs(documentSchemaType.name)]
|
|
185
|
-
: [],
|
|
186
|
-
[documentSchemaType, getFieldRefs],
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
return useMemo(() => {
|
|
190
|
-
return path ? selectableFields?.find((f) => f.key === path) : undefined
|
|
191
|
-
}, [selectableFields, path])
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export function getFieldTitle(field?: FieldRef) {
|
|
195
|
-
const schemaType = field?.schemaType
|
|
196
|
-
return field?.title ?? schemaType?.title ?? schemaType?.name ?? 'Untitled'
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export type AiPaneRouter = Omit<PaneRouterContextValue, 'setParams' | 'params'> & {
|
|
200
|
-
params: AssistInspectorRouteParams
|
|
201
|
-
setParams: (p: Record<keyof AssistInspectorRouteParams, string | undefined>) => void
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
export function useAiPaneRouter() {
|
|
205
|
-
const paneRouter = usePaneRouter()
|
|
206
|
-
|
|
207
|
-
return useMemo(
|
|
208
|
-
() => ({...paneRouter, params: paneRouter.params ?? {}}) as AiPaneRouter,
|
|
209
|
-
[paneRouter],
|
|
210
|
-
)
|
|
211
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import {SparklesIcon} from '@sanity/icons'
|
|
2
|
-
import {DocumentInspector, typed} from 'sanity'
|
|
3
|
-
|
|
4
|
-
import {pluginTitle} from '../constants'
|
|
5
|
-
import {AssistInspectorRouteParams, fieldPathParam, instructionParam} from '../types'
|
|
6
|
-
import {AssistInspectorWrapper} from './AssistInspector'
|
|
7
|
-
import {aiInspectorId} from './constants'
|
|
8
|
-
|
|
9
|
-
export const assistInspector: DocumentInspector = {
|
|
10
|
-
name: aiInspectorId,
|
|
11
|
-
useMenuItem: () => ({
|
|
12
|
-
icon: SparklesIcon,
|
|
13
|
-
title: pluginTitle,
|
|
14
|
-
hidden: true,
|
|
15
|
-
showAsAction: false,
|
|
16
|
-
}),
|
|
17
|
-
component: AssistInspectorWrapper,
|
|
18
|
-
onClose({params}) {
|
|
19
|
-
return {
|
|
20
|
-
params: typed<AssistInspectorRouteParams>({
|
|
21
|
-
...params,
|
|
22
|
-
[fieldPathParam]: undefined,
|
|
23
|
-
[instructionParam]: undefined,
|
|
24
|
-
}) as typeof params,
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import {createContext, useContext} from 'react'
|
|
2
|
-
|
|
3
|
-
import {AssistPluginConfig} from '../plugin'
|
|
4
|
-
import {InstructStatus} from '../useApiClient'
|
|
5
|
-
import {SerializedSchemaType} from '../types'
|
|
6
|
-
import {FieldRef} from '../assistInspector/helpers'
|
|
7
|
-
|
|
8
|
-
export interface AiAssistanceConfigContextValue {
|
|
9
|
-
config: AssistPluginConfig
|
|
10
|
-
status?: InstructStatus
|
|
11
|
-
statusLoading: boolean
|
|
12
|
-
initLoading: boolean
|
|
13
|
-
init: () => void
|
|
14
|
-
error?: Error
|
|
15
|
-
serializedTypes: SerializedSchemaType[]
|
|
16
|
-
getFieldRefs: (documentType: string) => FieldRef[]
|
|
17
|
-
getFieldRefsByTypePath: (documentType: string) => Record<string, FieldRef | undefined>
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const AiAssistanceConfigContext = createContext<AiAssistanceConfigContextValue>({} as any)
|
|
21
|
-
|
|
22
|
-
export function useAiAssistanceConfig() {
|
|
23
|
-
const context = useContext(AiAssistanceConfigContext)
|
|
24
|
-
if (!context) {
|
|
25
|
-
throw new Error('Missing AiAssistanceConfigContext')
|
|
26
|
-
}
|
|
27
|
-
return context
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function useSerializedTypes() {
|
|
31
|
-
return useAiAssistanceConfig().serializedTypes
|
|
32
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import {ReactNode, useCallback, useEffect, useMemo, useState} from 'react'
|
|
2
|
-
import {AssistPluginConfig} from '../plugin'
|
|
3
|
-
import {InstructStatus, useApiClient, useGetInstructStatus, useInitInstruct} from '../useApiClient'
|
|
4
|
-
import {type ObjectSchemaType, Schema, useSchema} from 'sanity'
|
|
5
|
-
import {serializeSchema} from '../schemas/serialize/serializeSchema'
|
|
6
|
-
import {
|
|
7
|
-
AiAssistanceConfigContext,
|
|
8
|
-
AiAssistanceConfigContextValue,
|
|
9
|
-
} from './AiAssistanceConfigContext'
|
|
10
|
-
import {createFieldRefCache} from './fieldRefCache'
|
|
11
|
-
|
|
12
|
-
export function AiAssistanceConfigProvider(props: {
|
|
13
|
-
children?: ReactNode
|
|
14
|
-
config: AssistPluginConfig
|
|
15
|
-
}) {
|
|
16
|
-
const [status, setStatus] = useState<InstructStatus | undefined>()
|
|
17
|
-
const [error, setError] = useState<Error | undefined>()
|
|
18
|
-
|
|
19
|
-
const apiClient = useApiClient(props.config?.__customApiClient)
|
|
20
|
-
const {getInstructStatus, loading: statusLoading} = useGetInstructStatus(apiClient)
|
|
21
|
-
const {initInstruct, loading: initLoading} = useInitInstruct(apiClient)
|
|
22
|
-
|
|
23
|
-
const schema = useSchema()
|
|
24
|
-
const serializedTypes = useMemo(() => serializeSchema(schema, {leanFormat: true}), [schema])
|
|
25
|
-
const {getFieldRefs, getFieldRefsByTypePath} = useFieldRefGetters(schema)
|
|
26
|
-
|
|
27
|
-
useEffect(() => {
|
|
28
|
-
getInstructStatus()
|
|
29
|
-
.then((s) => setStatus(s))
|
|
30
|
-
.catch((e) => {
|
|
31
|
-
console.error(e)
|
|
32
|
-
setError(e as Error)
|
|
33
|
-
})
|
|
34
|
-
}, [getInstructStatus])
|
|
35
|
-
|
|
36
|
-
const init = useCallback(async () => {
|
|
37
|
-
setError(undefined)
|
|
38
|
-
try {
|
|
39
|
-
await initInstruct()
|
|
40
|
-
const status = await getInstructStatus()
|
|
41
|
-
setStatus(status)
|
|
42
|
-
} catch (e) {
|
|
43
|
-
console.error('Failed to init ai assistance', e)
|
|
44
|
-
setError(e as Error)
|
|
45
|
-
}
|
|
46
|
-
}, [initInstruct, getInstructStatus, setStatus])
|
|
47
|
-
|
|
48
|
-
const {config, children} = props
|
|
49
|
-
const context = useMemo<AiAssistanceConfigContextValue>(() => {
|
|
50
|
-
return {
|
|
51
|
-
config,
|
|
52
|
-
status,
|
|
53
|
-
statusLoading,
|
|
54
|
-
init,
|
|
55
|
-
initLoading,
|
|
56
|
-
error,
|
|
57
|
-
serializedTypes,
|
|
58
|
-
getFieldRefs,
|
|
59
|
-
getFieldRefsByTypePath,
|
|
60
|
-
}
|
|
61
|
-
}, [
|
|
62
|
-
config,
|
|
63
|
-
status,
|
|
64
|
-
init,
|
|
65
|
-
statusLoading,
|
|
66
|
-
initLoading,
|
|
67
|
-
error,
|
|
68
|
-
serializedTypes,
|
|
69
|
-
getFieldRefs,
|
|
70
|
-
getFieldRefsByTypePath,
|
|
71
|
-
])
|
|
72
|
-
|
|
73
|
-
return (
|
|
74
|
-
<AiAssistanceConfigContext.Provider value={context}>
|
|
75
|
-
{children}
|
|
76
|
-
</AiAssistanceConfigContext.Provider>
|
|
77
|
-
)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function useFieldRefGetters(schema: Schema) {
|
|
81
|
-
return useMemo(() => {
|
|
82
|
-
const getForSchemaType = createFieldRefCache()
|
|
83
|
-
|
|
84
|
-
function getRefsForType(documentType: string) {
|
|
85
|
-
const schemaType = schema.get(documentType) as ObjectSchemaType | undefined
|
|
86
|
-
if (!schemaType) {
|
|
87
|
-
throw new Error(`Schema type "${documentType}" not found`)
|
|
88
|
-
}
|
|
89
|
-
return getForSchemaType(schemaType)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
getFieldRefs: (documentType: string) => getRefsForType(documentType).fieldRefs,
|
|
94
|
-
getFieldRefsByTypePath: (documentType: string) =>
|
|
95
|
-
getRefsForType(documentType).fieldRefsByTypePath,
|
|
96
|
-
}
|
|
97
|
-
}, [schema])
|
|
98
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import {ThemeProvider} from '@sanity/ui'
|
|
2
|
-
import {useState} from 'react'
|
|
3
|
-
import {LayoutProps} from 'sanity'
|
|
4
|
-
|
|
5
|
-
import {Connector, ConnectorsProvider} from '../_lib/connector'
|
|
6
|
-
import {AssistConnectorsOverlay} from '../assistConnectors'
|
|
7
|
-
import {AssistPluginConfig} from '../plugin'
|
|
8
|
-
import {FieldTranslationProvider} from '../translate/FieldTranslationProvider'
|
|
9
|
-
import {StudioInstruction} from '../types'
|
|
10
|
-
import {RunInstructionRequest} from '../useApiClient'
|
|
11
|
-
import {RunInstructionProvider} from './RunInstructionProvider'
|
|
12
|
-
import {AiAssistanceConfigProvider} from './AiAssistanceConfigProvider'
|
|
13
|
-
|
|
14
|
-
export interface AIStudioLayoutProps extends LayoutProps {
|
|
15
|
-
config: AssistPluginConfig
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export type RunInstructionArgs = Omit<RunInstructionRequest, 'instructionKey' | 'userText'> & {
|
|
19
|
-
instruction: StudioInstruction
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function AssistLayout(props: AIStudioLayoutProps) {
|
|
23
|
-
const [connectors, setConnectors] = useState<Connector[]>([])
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<AiAssistanceConfigProvider config={props.config}>
|
|
27
|
-
<RunInstructionProvider>
|
|
28
|
-
<FieldTranslationProvider>
|
|
29
|
-
<ConnectorsProvider onConnectorsChange={setConnectors}>
|
|
30
|
-
{props.renderDefault(props)}
|
|
31
|
-
<ThemeProvider tone="default">
|
|
32
|
-
<AssistConnectorsOverlay connectors={connectors} />
|
|
33
|
-
</ThemeProvider>
|
|
34
|
-
</ConnectorsProvider>
|
|
35
|
-
</FieldTranslationProvider>
|
|
36
|
-
</RunInstructionProvider>
|
|
37
|
-
</AiAssistanceConfigProvider>
|
|
38
|
-
)
|
|
39
|
-
}
|