@sanity/assist 5.0.0 → 5.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/dist/index.mjs DELETED
@@ -1,4328 +0,0 @@
1
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import { pathToString, getVersionFromId, getPublishedId, isVersionId, useEditState, useCurrentUser, useClient, typed, FormFieldHeaderText, PatchEvent, unset, isObjectSchemaType, stringToPath, isKeySegment, useSchema, getVersionId, getDraftId, useColorSchemeValue, isArraySchemaType, useFormCallbacks, useDocumentStore, useDocumentPresence, createPatchChannel, FormBuilder, fromMutationPatches, StatusButton, PresenceOverlay, VirtualizerScrollInstanceProvider, isDocumentSchemaType, useSyncState, set, useWorkspaceSchemaId, MemberFieldError, FormCallbacksProvider, FormInput, setIfMissing, insert, ObjectInputMember, isArrayOfObjectsSchemaType, defineType, defineField, defineArrayMember, definePlugin } from "sanity";
3
- import { useToast, useLayer, Dialog, Stack, Flex, Tooltip, Text, TextArea, Button, Badge, Popover, Card, Box, ErrorBoundary, focusFirstDescendant, Spinner, Container, Autocomplete, Breadcrumbs, useClickOutside, useGlobalKeyDown, useTheme, rgba, Radio, Checkbox, ThemeProvider, MenuButton, Menu, MenuItem, Switch, Label } from "@sanity/ui";
4
- import { useRef, useState, useEffect, useMemo, createContext, useContext, useCallback, useId, forwardRef, createElement, useReducer } from "react";
5
- import { useDocumentPane, usePaneRouter, DocumentInspectorHeader, DocumentPaneProvider } from "sanity/structure";
6
- import { minutesToMilliseconds, isAfter, addSeconds, formatDistanceToNow } from "date-fns";
7
- import { PlayIcon, DocumentIcon, LinkIcon, ImageIcon, BlockContentIcon, OlistIcon, BlockquoteIcon, StringIcon, SparklesIcon, ArrowRightIcon, CheckmarkIcon, SearchIcon, SyncIcon, ErrorOutlineIcon, CheckmarkCircleIcon, ClockIcon, CloseCircleIcon, RetryIcon, CloseIcon, icons, TranslateIcon, LockIcon, ControlsIcon, ArrowLeftIcon, TokenIcon, DocumentTextIcon, ThListIcon, CodeIcon, ComposeIcon } from "@sanity/icons";
8
- import { extractWithPath } from "@sanity/mutator";
9
- import { keyframes, styled } from "styled-components";
10
- import { tap, mergeMap, share, take, filter, distinctUntilChanged, catchError } from "rxjs/operators";
11
- import { get } from "lodash-es";
12
- import isEqual from "react-fast-compare";
13
- import { defer, throwError, of, partition, merge, switchMap, delay } from "rxjs";
14
- import { exhaustMapToWithTrailing } from "rxjs-exhaustmap-with-trailing";
15
- function hasOverflowScroll(el) {
16
- const overflow = getComputedStyle(el).overflow;
17
- return overflow.includes("auto") || overflow.includes("hidden") || overflow.includes("scroll");
18
- }
19
- function useRegionRects() {
20
- const ref = useRef(null), [relativeBoundsRect, setRelativeBoundsRect] = useState(null), [relativeElementRect, setRelativeElementRect] = useState(null), [boundsScroll, setBoundsScroll] = useState({ x: 0, y: 0 }), [scroll, setScroll] = useState({ x: 0, y: 0 }), boundsScrollXRef = useRef(0), boundsScrollYRef = useRef(0), elementScrollXRef = useRef(0), elementScrollYRef = useRef(0);
21
- useEffect(() => {
22
- const el = ref.current;
23
- if (!el) return;
24
- const scrollParents = [];
25
- let parent = el.parentElement;
26
- for (; parent && parent !== document.body; )
27
- hasOverflowScroll(parent) && scrollParents.push(parent), parent = parent.parentElement;
28
- function handleResize() {
29
- const boundsRect = scrollParents[0]?.getBoundingClientRect() || {
30
- x: 0,
31
- y: 0,
32
- width: window.innerWidth,
33
- height: window.innerHeight
34
- }, domRect = el.getBoundingClientRect();
35
- setRelativeBoundsRect({
36
- x: boundsRect.x + boundsScrollXRef.current,
37
- y: boundsRect.y + boundsScrollYRef.current,
38
- w: boundsRect.width,
39
- h: boundsRect.height
40
- }), setRelativeElementRect({
41
- x: domRect.x + elementScrollXRef.current,
42
- y: domRect.y + elementScrollYRef.current,
43
- w: domRect.width,
44
- h: domRect.height
45
- });
46
- }
47
- function handleScroll() {
48
- let scrollX = window.scrollX, scrollY = window.scrollY;
49
- for (const scrollParent2 of scrollParents)
50
- scrollX += scrollParent2.scrollLeft, scrollY += scrollParent2.scrollTop;
51
- const scrollParent = scrollParents[0];
52
- boundsScrollXRef.current = scrollX - (scrollParent?.scrollLeft || window.scrollX), boundsScrollYRef.current = scrollY - (scrollParent?.scrollTop || window.scrollY), setBoundsScroll({
53
- x: boundsScrollXRef.current,
54
- y: boundsScrollYRef.current
55
- }), elementScrollXRef.current = scrollX, elementScrollYRef.current = scrollY, setScroll({ x: scrollX, y: scrollY });
56
- }
57
- window.addEventListener("scroll", handleScroll, { passive: !0 });
58
- const ro = new ResizeObserver(handleResize);
59
- ro.observe(el);
60
- for (const scrollParent of scrollParents)
61
- scrollParent.addEventListener("scroll", handleScroll, { passive: !0 }), ro.observe(scrollParent);
62
- return handleScroll(), () => {
63
- ro.unobserve(el);
64
- for (const scrollParent of scrollParents)
65
- ro.unobserve(scrollParent), scrollParent.removeEventListener("scroll", handleScroll);
66
- ro.disconnect(), window.removeEventListener("scroll", handleScroll);
67
- };
68
- }, []);
69
- const bounds = useMemo(
70
- () => relativeBoundsRect && {
71
- x: relativeBoundsRect.x - boundsScroll.x,
72
- y: relativeBoundsRect.y - boundsScroll.y,
73
- w: relativeBoundsRect.w,
74
- h: relativeBoundsRect.h
75
- },
76
- [relativeBoundsRect, boundsScroll]
77
- ), element = useMemo(
78
- () => relativeElementRect && {
79
- x: relativeElementRect.x - scroll.x,
80
- y: relativeElementRect.y - scroll.y,
81
- w: relativeElementRect.w,
82
- h: relativeElementRect.h
83
- },
84
- [relativeElementRect, scroll]
85
- );
86
- return { bounds, element, ref };
87
- }
88
- function ConnectorRegion(props) {
89
- const { children, onRectsChange, ...restProps } = props, { bounds, element, ref } = useRegionRects();
90
- return useEffect(() => {
91
- onRectsChange?.(bounds && element ? { bounds, element } : null);
92
- }, [bounds, element, onRectsChange]), /* @__PURE__ */ jsx("div", { ...restProps, ref, children });
93
- }
94
- const ConnectorsStoreContext = createContext(null);
95
- function useConnectorsStore() {
96
- const store = useContext(ConnectorsStoreContext);
97
- if (!store)
98
- throw new Error("Missing connectors store context");
99
- return store;
100
- }
101
- function ConnectFromRegion(props) {
102
- const { children, _key: key, zIndex, ...restProps } = props, store = useConnectorsStore(), [rects, setRects] = useState(null);
103
- return useEffect(() => store.from.subscribe(key, { zIndex }), [key, store, zIndex]), useEffect(() => {
104
- rects && store.from.next(key, rects);
105
- }, [key, rects, store]), /* @__PURE__ */ jsx(ConnectorRegion, { ...restProps, onRectsChange: setRects, children });
106
- }
107
- function createConnectorsStore() {
108
- const configKeys = [], fieldKeys = [], channels = {
109
- from: /* @__PURE__ */ new Map(),
110
- to: /* @__PURE__ */ new Map()
111
- }, payloads = {
112
- from: /* @__PURE__ */ new Map(),
113
- to: /* @__PURE__ */ new Map()
114
- }, observers = [];
115
- function notifyObservers() {
116
- const connectors = [];
117
- for (const key of configKeys) {
118
- const toRects = channels.to.get(key), toPayload = payloads.from.get(key), fromRects = channels.from.get(key), fromPayload = payloads.from.get(key);
119
- toRects && fromRects && connectors.push({
120
- key,
121
- from: { ...fromRects, payload: fromPayload },
122
- to: { ...toRects, payload: toPayload }
123
- });
124
- }
125
- for (const observer of observers)
126
- observer(connectors);
127
- }
128
- return {
129
- to: {
130
- subscribe(key, payload) {
131
- return channels.to.set(key, null), payloads.to.set(key, payload), configKeys.push(key), () => {
132
- channels.to.delete(key), payloads.to.delete(key);
133
- const idx = configKeys.indexOf(key);
134
- idx > -1 && configKeys.splice(idx, 1), notifyObservers();
135
- };
136
- },
137
- next(key, rects) {
138
- channels.to.set(key, rects), fieldKeys.includes(key) && notifyObservers();
139
- }
140
- },
141
- connectors: {
142
- subscribe(observer) {
143
- return observers.push(observer), () => {
144
- const idx = observers.indexOf(observer);
145
- idx > -1 && observers.splice(idx, 1);
146
- };
147
- }
148
- },
149
- from: {
150
- subscribe(key, payload) {
151
- return channels.from.set(key, null), payloads.from.set(key, payload), fieldKeys.push(key), () => {
152
- channels.from.delete(key), payloads.from.delete(key);
153
- const idx = fieldKeys.indexOf(key);
154
- idx > -1 && fieldKeys.splice(idx, 1), notifyObservers();
155
- };
156
- },
157
- next(key, rects) {
158
- channels.from.set(key, rects), configKeys.includes(key) && notifyObservers();
159
- }
160
- }
161
- };
162
- }
163
- function ConnectorsProvider(props) {
164
- const { children, onConnectorsChange } = props, store = useMemo(() => createConnectorsStore(), []);
165
- return useEffect(
166
- () => onConnectorsChange && store.connectors.subscribe(onConnectorsChange),
167
- [onConnectorsChange, store]
168
- ), /* @__PURE__ */ jsx(ConnectorsStoreContext.Provider, { value: store, children });
169
- }
170
- function getConnectorLinePoint(options2, rect, bounds) {
171
- const centerY = rect.y + rect.h / 2, isAbove = rect.y + rect.h < bounds.y + options2.arrow.marginY, isBelow = rect.y > bounds.y + bounds.h - options2.arrow.marginY;
172
- return {
173
- bounds,
174
- x: rect.x,
175
- y: centerY,
176
- centerY,
177
- startY: rect.y + options2.path.marginY,
178
- endY: rect.y + rect.h - options2.path.marginY,
179
- isAbove,
180
- isBelow,
181
- outOfBounds: isAbove || isBelow
182
- };
183
- }
184
- function mapConnectorToLine(options2, connector) {
185
- const fromBounds = {
186
- y: connector.from.bounds.y + options2.arrow.threshold,
187
- // bottom: connector.from.bounds.y + connector.from.bounds.h - options.arrow.threshold,
188
- x: connector.from.bounds.x,
189
- // right: connector.from.bounds.x + connector.from.bounds.w,
190
- w: connector.from.bounds.w,
191
- h: connector.from.bounds.h - options2.arrow.threshold * 2
192
- }, from = getConnectorLinePoint(options2, connector.from.element, fromBounds);
193
- from.x = connector.from.element.x + connector.from.element.w;
194
- const fromBottom = fromBounds.y + fromBounds.h, toBounds = {
195
- y: connector.to.bounds.y + options2.arrow.threshold,
196
- // bottom: connector.to.bounds.y + connector.to.bounds.h - options.arrow.threshold,
197
- x: connector.to.bounds.x,
198
- // right: connector.to.bounds.x + connector.to.bounds.w,
199
- w: connector.to.bounds.w,
200
- h: connector.to.bounds.h - options2.arrow.threshold * 2
201
- }, toBottom = toBounds.y + toBounds.h, to = getConnectorLinePoint(options2, connector.to.element, toBounds), maxStartY = Math.max(to.startY, from.startY);
202
- return from.y = Math.min(maxStartY, from.endY), from.y < toBounds.y ? from.y = Math.min(toBounds.y, from.endY) : from.y > toBottom && (from.y = Math.max(toBottom, from.startY)), to.y = Math.min(maxStartY, to.endY), to.y < fromBounds.y ? to.y = Math.min(fromBounds.y, to.endY) : to.y > fromBottom && (to.y = Math.max(fromBottom, to.startY)), from.y = Math.min(Math.max(from.y, fromBounds.y), fromBottom), to.y = Math.min(Math.max(to.y, toBounds.y), toBottom), { from, to };
203
- }
204
- const assistFormId = "assist", assistDocumentIdPrefix = "sanity.assist.schemaType.", assistDocumentStatusIdPrefix = "sanity.assist.status.", assistSchemaIdPrefix = "sanity.assist.schema.", assistDocumentTypeName = "sanity.assist.schemaType.annotations", assistFieldTypeName = "sanity.assist.schemaType.field", instructionTypeName = "sanity.assist.instruction", promptTypeName = "sanity.assist.instruction.prompt", userInputTypeName = "sanity.assist.instruction.userInput", instructionContextTypeName = "sanity.assist.instruction.context", fieldReferenceTypeName = "sanity.assist.instruction.fieldRef", contextDocumentTypeName = "assist.instruction.context", assistTasksStatusTypeName = "sanity.assist.task.status", instructionTaskTypeName = "sanity.assist.instructionTask", fieldPresenceTypeName = "sanity.assist.instructionTask.presence", assistSerializedTypeName = "sanity.assist.serialized.type", assistSerializedFieldTypeName = "sanity.assist.serialized.field", outputFieldTypeName = "sanity.assist.output.field", outputTypeTypeName = "sanity.assist.output.type", fieldPathParam = "pathKey", instructionParam = "instruction", documentRootKey = "<document>";
205
- function usePathKey(path) {
206
- return useMemo(() => getPathKey(path), [path]);
207
- }
208
- function getPathKey(path) {
209
- return path.length ? Array.isArray(path) ? pathToString(path) : path : documentRootKey;
210
- }
211
- function getInstructionTitle(instruction2) {
212
- return instruction2?.title ?? "Untitled";
213
- }
214
- function isDefined(t) {
215
- return t != null;
216
- }
217
- function isPortableTextArray(type) {
218
- return type.of.find((t) => isType(t, "block"));
219
- }
220
- function isType(schemaType, typeName) {
221
- return schemaType.name === typeName ? !0 : schemaType.type ? isType(schemaType.type, typeName) : !1;
222
- }
223
- function isImage(schemaType) {
224
- return isType(schemaType, "image");
225
- }
226
- function getDescriptionFieldOption(schemaType) {
227
- if (!schemaType)
228
- return;
229
- const descriptionField = schemaType.options?.aiAssist?.imageDescriptionField;
230
- return typeof descriptionField == "string" ? {
231
- path: descriptionField,
232
- updateOnImageChange: !0
233
- } : descriptionField ? {
234
- path: descriptionField.path,
235
- updateOnImageChange: descriptionField.updateOnImageChange ?? !0
236
- } : getDescriptionFieldOption(schemaType.type);
237
- }
238
- function getImageInstructionFieldOption(schemaType) {
239
- return schemaType ? schemaType.options?.aiAssist?.imageInstructionField || getImageInstructionFieldOption(schemaType.type) : void 0;
240
- }
241
- function isSchemaAssistEnabled(type) {
242
- return !type.options?.aiAssist?.exclude;
243
- }
244
- function isAssistSupported(type) {
245
- return !isSchemaAssistEnabled(type) || isDisabled(type) ? !1 : type.jsonType === "array" ? !type.of.every((t) => isDisabled(t)) : type.jsonType === "object" ? !type.fields.every((field) => isDisabled(field.type)) || /* to allow attaching custom actions on fieldless images */
246
- isType(type, "image") : !0;
247
- }
248
- function isDisabled(type) {
249
- return !isSchemaAssistEnabled(type) || isUnsupportedType(type);
250
- }
251
- function isUnsupportedType(type) {
252
- return type.name === "sanity.imageCrop" || type.name === "sanity.imageHotspot" || isType(type, "globalDocumentReference") || isType(type, "reference") && !type?.options?.aiAssist?.embeddingsIndex || isType(type, "crossDatasetReference") || isType(type, "file");
253
- }
254
- const FirstAssistedPathContext = createContext(void 0);
255
- function FirstAssistedPathProvider(props) {
256
- const { members } = props, firstAssistedPath = useMemo(() => {
257
- const firstAssisted = members.find(
258
- (member) => member.kind === "field" && isAssistSupported(member.field.schemaType)
259
- );
260
- return firstAssisted?.field.path ? pathToString(firstAssisted?.field.path) : void 0;
261
- }, [members]);
262
- return /* @__PURE__ */ jsx(FirstAssistedPathContext.Provider, { value: firstAssistedPath, children: props.children });
263
- }
264
- const releaseAnnouncementUrl = "https://www.sanity.io/blog/sanity-ai-assist-announcement?utm_source=sanity-assist-plugin&utm_medium=organic_social&utm_campaign=ai-assist&utm_content=", instructionGuideUrl = "https://sanity.io/guides/getting-started-with-ai-assist-instructions?utm_source=sanity-assist-plugin&utm_medium=organic_social&utm_campaign=ai-assist&utm_content=", giveFeedbackUrl = "https://forms.gle/Kwz7CThxGeA2GiEU8", salesUrl = "https://www.sanity.io/contact/sales?utm_source=sanity-assist-plugin&utm_medium=organic_social&utm_campaign=ai-assist&utm_content=", packageName = "@sanity/assist", pluginTitle = "Sanity AI Assist", pluginTitleShort = "AI Assist", maxHistoryVisibilityMs = minutesToMilliseconds(30), illegalIdChars = /[^a-zA-Z0-9._-]/g;
265
- function assistDocumentId(documentType) {
266
- return `${assistDocumentIdPrefix}${documentType}`.replace(illegalIdChars, "_");
267
- }
268
- function assistTasksStatusId(documentId) {
269
- return isVersionId(documentId) ? `${assistDocumentStatusIdPrefix}${getVersionFromId(documentId)}.${getPublishedId(documentId)}` : `${assistDocumentStatusIdPrefix}${getPublishedId(documentId)}`;
270
- }
271
- function useDocumentState(id, docType) {
272
- const state = useEditState(id, docType);
273
- return state.draft || state.published;
274
- }
275
- function useStudioAssistDocument({
276
- documentId,
277
- schemaType,
278
- initDoc
279
- }) {
280
- const documentTypeName = schemaType.name, currentUser = useCurrentUser(), assistDocument = useDocumentState(
281
- assistDocumentId(documentTypeName),
282
- assistDocumentTypeName
283
- ), assistTasksStatus = useDocumentState(
284
- assistTasksStatusId(documentId ?? ""),
285
- assistTasksStatusTypeName
286
- ), client = useClient({ apiVersion: "2023-01-01" });
287
- return useEffect(() => {
288
- !assistDocument && initDoc && client.createIfNotExists({
289
- _id: assistDocumentId(documentTypeName),
290
- _type: assistDocumentTypeName
291
- }).catch(() => {
292
- });
293
- }, [client, assistDocument, documentTypeName, initDoc]), useMemo(() => {
294
- if (!assistDocument)
295
- return;
296
- const tasks = assistTasksStatus?.tasks ?? [], fields = (assistDocument?.fields ?? []).map((assistField) => ({
297
- ...assistField,
298
- tasks: tasks.filter((task) => task.path === assistField.path),
299
- instructions: assistField.instructions?.filter((p) => !p.userId || p.userId === currentUser?.id).map((instruction2) => asStudioInstruction(instruction2, tasks))
300
- }));
301
- return typed({
302
- ...assistDocument,
303
- tasks: tasks?.map((task) => {
304
- const instruction2 = fields.find((f) => f.path === task.path)?.instructions?.find((i) => i._key === task.instructionKey);
305
- return {
306
- ...task,
307
- instruction: instruction2
308
- };
309
- }),
310
- fields
311
- });
312
- }, [assistDocument, assistTasksStatus, currentUser]);
313
- }
314
- function asStudioInstruction(instruction2, run) {
315
- return {
316
- ...instruction2,
317
- tasks: run.filter((task) => task.instructionKey === instruction2._key).filter(
318
- (task) => task.started && (/* @__PURE__ */ new Date()).getTime() - new Date(task.started).getTime() < maxHistoryVisibilityMs
319
- )
320
- };
321
- }
322
- const NO_TASKS = [];
323
- function useInstructionToaster(documentId, documentSchemaType) {
324
- const assistDocument = useStudioAssistDocument({ documentId, schemaType: documentSchemaType }), assistDocLoaded = !!assistDocument, currentUser = useCurrentUser(), toast = useToast(), tasks = assistDocument?.tasks, previousTasks = useRef("initial");
325
- useEffect(() => {
326
- if (assistDocLoaded) {
327
- if (previousTasks.current !== "initial") {
328
- const prevTaskByKey = Object.fromEntries(
329
- (previousTasks.current ?? NO_TASKS).map((run) => [run._key, run])
330
- );
331
- tasks?.filter((task) => task.startedByUserId === currentUser?.id).filter((task) => {
332
- const prevTask = prevTaskByKey[task._key];
333
- return !prevTask && task.ended || !prevTask?.ended && task.ended;
334
- }).filter((task) => task.ended && isAfter(addSeconds(new Date(task.ended), 30), /* @__PURE__ */ new Date()))?.forEach((task) => {
335
- const title = task.title ?? getInstructionTitle(task.instruction);
336
- task.reason === "error" ? toast.push({
337
- title: `Failed: ${title}`,
338
- status: "error",
339
- description: `Instruction failed. ${task.message ?? ""}`,
340
- closable: !0,
341
- duration: 1e4
342
- }) : task.reason === "timeout" ? toast.push({
343
- title: `Timeout: ${title}`,
344
- status: "error",
345
- description: "Instruction timed out.",
346
- closable: !0
347
- }) : task.reason === "success" ? toast.push({
348
- title: `Success: ${title}`,
349
- status: "success",
350
- description: "Instruction completed.",
351
- closable: !0
352
- }) : task.reason === "aborted" && toast.push({
353
- title: `Canceled: ${title}`,
354
- status: "warning",
355
- description: "Instruction canceled.",
356
- closable: !0
357
- });
358
- });
359
- }
360
- previousTasks.current = tasks;
361
- }
362
- }, [tasks, previousTasks, toast, currentUser, assistDocLoaded]);
363
- }
364
- function AssistDocumentInputWrapper(props) {
365
- if (!isType(props.schemaType, "document") && props.id !== "root" && props.id !== assistFormId)
366
- return /* @__PURE__ */ jsx(AssistInput, { ...props });
367
- const documentId = props.value?._id;
368
- return documentId ? /* @__PURE__ */ jsx(AssistDocumentInput, { ...props, documentId }) : props.renderDefault(props);
369
- }
370
- function AssistDocumentInput({ documentId, ...props }) {
371
- useInstructionToaster(documentId, props.schemaType);
372
- const schemaType = useMemo(() => props.schemaType.name !== assistDocumentTypeName ? props.schemaType : {
373
- ...props.schemaType,
374
- type: {
375
- ...props.schemaType.type,
376
- // compatability with i18nArrays plugin that requires this to be document
377
- name: "document"
378
- }
379
- }, [props.schemaType]);
380
- return /* @__PURE__ */ jsx(FirstAssistedPathProvider, { members: props.members, children: props.renderDefault({ ...props, schemaType }) });
381
- }
382
- function AssistInput(props) {
383
- const { zIndex } = useLayer(), { paneKey } = useDocumentPane(), pathKey = usePathKey(props.path);
384
- return /* @__PURE__ */ jsx(ConnectFromRegion, { _key: `${paneKey}_${pathKey}`, zIndex, style: { minWidth: 0 }, children: props.renderDefault(props) });
385
- }
386
- const AssistDocumentContext = createContext(
387
- void 0
388
- );
389
- function useAssistDocumentContext() {
390
- const context = useContext(AssistDocumentContext);
391
- if (!context)
392
- throw new Error("AssistDocumentContext value is missing");
393
- return context;
394
- }
395
- const AiAssistanceConfigContext = createContext({});
396
- function useAiAssistanceConfig() {
397
- const context = useContext(AiAssistanceConfigContext);
398
- if (!context)
399
- throw new Error("Missing AiAssistanceConfigContext");
400
- return context;
401
- }
402
- function useSerializedTypes() {
403
- return useAiAssistanceConfig().serializedTypes;
404
- }
405
- const basePath = "/assist/tasks/instruction", API_VERSION_WITH_EXTENDED_TYPES = "2025-04-01";
406
- function canUseAssist(status) {
407
- return status?.enabled && status.initialized && status.validToken;
408
- }
409
- function useApiClient(customApiClient) {
410
- const client = useClient({ apiVersion: API_VERSION_WITH_EXTENDED_TYPES });
411
- return useMemo(
412
- () => customApiClient ? customApiClient(client) : client,
413
- [client, customApiClient]
414
- );
415
- }
416
- function useTranslate(apiClient) {
417
- const [loading, setLoading] = useState(!1), user = useCurrentUser(), types = useSerializedTypes(), toast = useToast(), translate = useCallback(
418
- ({
419
- documentId,
420
- languagePath,
421
- styleguide,
422
- translatePath,
423
- fieldLanguageMap,
424
- conditionalMembers
425
- }) => {
426
- setLoading(!0);
427
- async function run() {
428
- return apiClient.request({
429
- method: "POST",
430
- url: `/assist/tasks/translate/${apiClient.config().dataset}?projectId=${apiClient.config().projectId}`,
431
- body: {
432
- documentId,
433
- types,
434
- languagePath,
435
- userStyleguide: await styleguide(),
436
- fieldLanguageMap,
437
- conditionalMembers,
438
- translatePath: translatePath.length === 0 ? documentRootKey : pathToString(translatePath),
439
- userId: user?.id
440
- }
441
- });
442
- }
443
- return run().catch((e) => {
444
- throw toast.push({
445
- status: "error",
446
- title: "Translate failed",
447
- description: e.message
448
- }), setLoading(!1), e;
449
- }).finally(() => {
450
- setTimeout(() => {
451
- setLoading(!1);
452
- }, 2e3);
453
- });
454
- },
455
- [setLoading, apiClient, toast, user, types]
456
- );
457
- return useMemo(
458
- () => ({
459
- translate,
460
- loading
461
- }),
462
- [translate, loading]
463
- );
464
- }
465
- function useGenerateCaption(apiClient) {
466
- const [loading, setLoading] = useState(!1), user = useCurrentUser(), types = useSerializedTypes(), toast = useToast(), generateCaption = useCallback(
467
- ({ path, documentId }) => (setLoading(!0), apiClient.request({
468
- method: "POST",
469
- url: `/assist/tasks/generate-caption/${apiClient.config().dataset}?projectId=${apiClient.config().projectId}`,
470
- body: {
471
- path,
472
- documentId,
473
- types,
474
- userId: user?.id
475
- }
476
- }).catch((e) => {
477
- throw toast.push({
478
- status: "error",
479
- title: "Generate image description failed",
480
- description: e.message
481
- }), setLoading(!1), e;
482
- }).finally(() => {
483
- setTimeout(() => {
484
- setLoading(!1);
485
- }, 2e3);
486
- })),
487
- [setLoading, apiClient, toast, user, types]
488
- );
489
- return useMemo(
490
- () => ({
491
- generateCaption,
492
- loading
493
- }),
494
- [generateCaption, loading]
495
- );
496
- }
497
- function useGenerateImage(apiClient) {
498
- const [loading, setLoading] = useState(!1), user = useCurrentUser(), types = useSerializedTypes(), toast = useToast(), generateImage = useCallback(
499
- ({ path, documentId }) => (setLoading(!0), apiClient.request({
500
- method: "POST",
501
- url: `/assist/tasks/generate-image/${apiClient.config().dataset}?projectId=${apiClient.config().projectId}`,
502
- body: {
503
- path,
504
- documentId,
505
- types,
506
- userId: user?.id
507
- }
508
- }).catch((e) => {
509
- throw toast.push({
510
- status: "error",
511
- title: "Generate image from prompt failed",
512
- description: e.message
513
- }), setLoading(!1), e;
514
- }).finally(() => {
515
- setTimeout(() => {
516
- setLoading(!1);
517
- }, 2e3);
518
- })),
519
- [setLoading, apiClient, toast, user, types]
520
- );
521
- return useMemo(
522
- () => ({
523
- generateImage,
524
- loading
525
- }),
526
- [generateImage, loading]
527
- );
528
- }
529
- function useGetInstructStatus(apiClient) {
530
- const [loading, setLoading] = useState(!0), getInstructStatus = useCallback(async () => {
531
- setLoading(!0);
532
- const projectId = apiClient.config().projectId;
533
- try {
534
- return await apiClient.request({
535
- method: "GET",
536
- url: `${basePath}/${apiClient.config().dataset}/status?projectId=${projectId}`
537
- });
538
- } finally {
539
- setLoading(!1);
540
- }
541
- }, [setLoading, apiClient]);
542
- return {
543
- loading,
544
- getInstructStatus
545
- };
546
- }
547
- function useInitInstruct(apiClient) {
548
- const [loading, setLoading] = useState(!1), initInstruct = useCallback(() => (setLoading(!0), apiClient.request({
549
- method: "GET",
550
- url: `${basePath}/${apiClient.config().dataset}/init?projectId=${apiClient.config().projectId}`
551
- }).finally(() => {
552
- setLoading(!1);
553
- })), [setLoading, apiClient]);
554
- return {
555
- loading,
556
- initInstruct
557
- };
558
- }
559
- function useRunInstructionApi(apiClient) {
560
- const toast = useToast(), [loading, setLoading] = useState(!1), user = useCurrentUser(), types = useSerializedTypes(), {
561
- config: { assist: assistConfig }
562
- } = useAiAssistanceConfig(), runInstruction = useCallback(
563
- (request) => {
564
- if (!user) {
565
- toast.push({
566
- status: "error",
567
- title: "Unable to get user for instruction."
568
- });
569
- return;
570
- }
571
- setLoading(!0);
572
- const { timeZone, locale } = Intl.DateTimeFormat().resolvedOptions(), defaultLocaleSettings = { timeZone, locale }, localeSettings = assistConfig?.localeSettings?.({ user, defaultSettings: defaultLocaleSettings }) ?? defaultLocaleSettings;
573
- return apiClient.request({
574
- method: "POST",
575
- url: `${basePath}/${apiClient.config().dataset}?projectId=${apiClient.config().projectId}`,
576
- body: {
577
- ...request,
578
- types,
579
- userId: user?.id,
580
- localeSettings,
581
- maxPathDepth: assistConfig?.maxPathDepth
582
- }
583
- }).catch((e) => {
584
- throw toast.push({
585
- status: "error",
586
- title: "Instruction failed",
587
- description: e.message
588
- }), e;
589
- }).finally(() => {
590
- setLoading(!1);
591
- });
592
- },
593
- [apiClient, types, user, toast, assistConfig]
594
- );
595
- return useMemo(
596
- () => ({
597
- runInstruction,
598
- loading
599
- }),
600
- [runInstruction, loading]
601
- );
602
- }
603
- const NO_INPUT = {}, RunInstructionContext = createContext({
604
- runInstruction: () => {
605
- },
606
- getUserInput: async () => {
607
- },
608
- instructionLoading: !1
609
- });
610
- function useRunInstruction() {
611
- return useContext(RunInstructionContext);
612
- }
613
- function isUserInputBlock(block) {
614
- return block._type === userInputTypeName;
615
- }
616
- function RunInstructionProvider(props) {
617
- const { config } = useAiAssistanceConfig(), apiClient = useApiClient(config?.__customApiClient), { runInstruction: runInstructionRequest, loading } = useRunInstructionApi(apiClient), id = useId(), [inputs, setInputs] = useState(NO_INPUT), [runRequest, setRunRequest] = useState(), [resolveUserInput, setResolveUserInput] = useState(), getUserInput = useCallback(async ({ title, inputs: inputs2 }) => {
618
- const userInputBlocks = inputs2.map((input, i) => ({
619
- _type: userInputTypeName,
620
- _key: input.id ?? `${i}`,
621
- message: input.title,
622
- description: input.description
623
- }));
624
- if (userInputBlocks.length)
625
- return setRunRequest({ dialogTitle: title, userInputBlocks }), new Promise((resolve) => {
626
- setResolveUserInput(() => resolve);
627
- });
628
- }, []), runInstruction = useCallback(
629
- (req) => {
630
- if (loading)
631
- return;
632
- const { instruction: instruction2, ...request } = req, instructionKey = instruction2._key, userInputBlocks = instruction2?.prompt?.flatMap(
633
- (block) => block._type === "block" ? block.children.filter(isUserInputBlock) : [block]
634
- ).filter(isUserInputBlock);
635
- if (!userInputBlocks?.length) {
636
- runInstructionRequest({
637
- ...request,
638
- instructionKey,
639
- userTexts: void 0
640
- });
641
- return;
642
- }
643
- setRunRequest({
644
- ...req,
645
- userInputBlocks
646
- });
647
- },
648
- [runInstructionRequest, loading]
649
- ), close = useCallback(() => {
650
- setRunRequest(void 0), setInputs(NO_INPUT), resolveUserInput && resolveUserInput(void 0), setResolveUserInput(void 0);
651
- }, [resolveUserInput]), runWithInput = useCallback(() => {
652
- if (runRequest)
653
- if ("instruction" in runRequest) {
654
- const { instruction: instruction2, userTexts, ...request } = runRequest;
655
- runInstructionRequest({
656
- ...request,
657
- instructionKey: instruction2._key,
658
- userTexts: Object.entries(inputs).map(([key, value]) => ({
659
- blockKey: key,
660
- userInput: value
661
- }))
662
- });
663
- } else {
664
- const userInputs = Object.values(inputs).map((input, i) => {
665
- const userInputBlock = runRequest.userInputBlocks[i];
666
- return {
667
- input: {
668
- id: userInputBlock._key,
669
- title: userInputBlock.message ?? "",
670
- description: userInputBlock.description
671
- },
672
- result: input
673
- };
674
- });
675
- resolveUserInput?.(userInputs), setResolveUserInput(void 0);
676
- }
677
- close();
678
- }, [close, runInstructionRequest, runRequest, inputs, resolveUserInput]), open = !!runRequest, runDisabled = useMemo(
679
- () => (runRequest?.userInputBlocks?.length ?? 0) > Object.entries(inputs).filter(([, value]) => !!value).length,
680
- [runRequest?.userInputBlocks, inputs]
681
- ), runButton = /* @__PURE__ */ jsx(
682
- Button,
683
- {
684
- text: "Run instruction",
685
- onClick: runWithInput,
686
- tone: "primary",
687
- icon: PlayIcon,
688
- style: { width: "100%" },
689
- disabled: runDisabled
690
- }
691
- ), contextValue = useMemo(
692
- () => ({ runInstruction, getUserInput, instructionLoading: loading }),
693
- [runInstruction, loading]
694
- );
695
- return /* @__PURE__ */ jsxs(RunInstructionContext.Provider, { value: contextValue, children: [
696
- open ? /* @__PURE__ */ jsx(
697
- Dialog,
698
- {
699
- id,
700
- open,
701
- onClose: close,
702
- width: 1,
703
- header: "dialogTitle" in runRequest ? runRequest.dialogTitle : getInstructionTitle(runRequest?.instruction),
704
- footer: /* @__PURE__ */ jsx(Flex, { justify: "space-between", padding: 2, flex: 1, children: runDisabled ? /* @__PURE__ */ jsx(
705
- Tooltip,
706
- {
707
- content: /* @__PURE__ */ jsx(Flex, { padding: 2, children: /* @__PURE__ */ jsx(Text, { children: "Unable to run instruction. All fields must have a value." }) }),
708
- placement: "top",
709
- children: /* @__PURE__ */ jsx(Flex, { flex: 1, children: runButton })
710
- }
711
- ) : runButton }),
712
- children: /* @__PURE__ */ jsx(Stack, { padding: 4, space: 2, children: runRequest?.userInputBlocks?.map((block, i) => /* @__PURE__ */ jsx(
713
- UserInput,
714
- {
715
- block,
716
- autoFocus: i === 0,
717
- inputs,
718
- setInputs
719
- },
720
- block._key
721
- )) })
722
- }
723
- ) : null,
724
- props.children
725
- ] });
726
- }
727
- function UserInput(props) {
728
- const { block, autoFocus, setInputs, inputs } = props, key = block._key, textAreaRef = useRef(null), onChange = useCallback(
729
- (e) => {
730
- setInputs((current) => ({
731
- ...current,
732
- [key]: (e.currentTarget ?? e.target).value
733
- }));
734
- },
735
- [key, setInputs]
736
- ), value = useMemo(() => inputs[key], [inputs, key]);
737
- return useEffect(() => {
738
- autoFocus && setTimeout(() => textAreaRef.current?.focus(), 0);
739
- }, [autoFocus]), /* @__PURE__ */ jsxs(Stack, { padding: 2, space: 3, children: [
740
- /* @__PURE__ */ jsx(
741
- FormFieldHeaderText,
742
- {
743
- title: block?.message ?? "Provide more context",
744
- description: block.description
745
- }
746
- ),
747
- /* @__PURE__ */ jsx(
748
- TextArea,
749
- {
750
- ref: textAreaRef,
751
- rows: 4,
752
- value,
753
- onChange,
754
- style: { resize: "vertical" }
755
- }
756
- )
757
- ] });
758
- }
759
- function isDocAssistable(documentSchemaType, published, draft) {
760
- return !!(documentSchemaType.liveEdit ? published : draft);
761
- }
762
- function useRequestRunInstruction(args) {
763
- const { runInstruction, instructionLoading } = useRunInstruction(), requestRunInstruction = useDraftDelayedTask({
764
- ...args,
765
- task: runInstruction
766
- });
767
- return {
768
- instructionLoading,
769
- requestRunInstruction
770
- };
771
- }
772
- function useDraftDelayedTask(args) {
773
- const { documentOnChange, isDocAssistable: isDocAssistable2, task } = args, [queuedArgs, setQueuedArgs] = useState(void 0);
774
- return useEffect(() => {
775
- queuedArgs && isDocAssistable2 && (task(queuedArgs), setQueuedArgs(void 0));
776
- }, [queuedArgs, isDocAssistable2, task]), useCallback(
777
- (taskArgs) => {
778
- documentOnChange(PatchEvent.from([unset(["_force_document_creation"])])), setQueuedArgs(taskArgs);
779
- },
780
- [setQueuedArgs, documentOnChange]
781
- );
782
- }
783
- const maxDepth = 6;
784
- function getTypeIcon(schemaType) {
785
- let t = schemaType;
786
- for (; t; ) {
787
- if (t.icon) return t.icon;
788
- t = t.type;
789
- }
790
- return isType(schemaType, "slug") ? LinkIcon : isType(schemaType, "image") ? ImageIcon : schemaType.jsonType === "array" && isPortableTextArray(schemaType) ? BlockContentIcon : schemaType.jsonType === "array" ? OlistIcon : schemaType.jsonType === "object" ? BlockquoteIcon : schemaType.jsonType === "string" ? StringIcon : DocumentIcon;
791
- }
792
- function asFieldRefsByTypePath(fieldRefs) {
793
- return fieldRefs.reduce(
794
- (acc, ref) => ({ ...acc, [ref.key]: ref }),
795
- {}
796
- );
797
- }
798
- function getDocumentFieldRef(schemaType) {
799
- return {
800
- key: documentRootKey,
801
- icon: schemaType.icon ?? DocumentIcon,
802
- title: "The entire document",
803
- path: [],
804
- schemaType
805
- };
806
- }
807
- function getFieldRefs(schemaType, parent, depth = 0) {
808
- return depth >= maxDepth ? [] : schemaType.fields.filter((f) => !f.name.startsWith("_")).flatMap((field) => {
809
- const path = parent ? [...parent.path, field.name] : [field.name], title = field.type.title ?? field.name, fieldRef = {
810
- key: patchableKey(pathToString(path)),
811
- path,
812
- title: parent ? [parent.title, title].join(" / ") : title,
813
- schemaType: field.type,
814
- icon: getTypeIcon(field.type)
815
- }, fields = field.type.jsonType === "object" ? getFieldRefs(field.type, fieldRef, depth + 1) : [], syntheticFields = field.type.jsonType === "array" ? getSyntheticFields(field.type, fieldRef, depth + 1) : [];
816
- return isAssistSupported(field.type) ? [fieldRef, ...fields, ...syntheticFields] : [...fields, ...syntheticFields];
817
- });
818
- }
819
- function getSyntheticFields(schemaType, parent, depth = 0) {
820
- return depth >= maxDepth ? [] : schemaType.of.filter((itemType) => !isType(itemType, "block")).flatMap((itemType) => {
821
- const segment = { _key: itemType.name }, title = itemType.title ?? itemType.name, path = parent ? [...parent.path, segment] : [segment], fieldRef = {
822
- key: patchableKey(pathToString(path)),
823
- path,
824
- title: parent ? [parent.title, title].join(" / ") : title,
825
- schemaType: itemType,
826
- icon: getTypeIcon(itemType),
827
- synthetic: !0
828
- }, fields = itemType.jsonType === "object" ? getFieldRefs(itemType, fieldRef, depth + 1) : [];
829
- return isAssistSupported(itemType) ? [fieldRef, ...fields] : fields;
830
- });
831
- }
832
- function getTypePath(doc, pathString) {
833
- if (!pathString)
834
- return;
835
- const path = stringToPath(pathString), currentPath = [];
836
- let valid = !0;
837
- const syntheticPath = path.map((segment) => {
838
- if (currentPath.push(segment), isKeySegment(segment)) {
839
- const match = extractWithPath(pathToString(currentPath), doc)[0], value = match?.value;
840
- if (match && value && typeof value == "object" && "_type" in value)
841
- return { _key: value._type };
842
- valid = !1;
843
- }
844
- return segment;
845
- });
846
- return valid ? patchableKey(pathToString(syntheticPath)) : void 0;
847
- }
848
- function patchableKey(pathKey) {
849
- return pathKey.replace(/[=]=/g, ":").replace(/[[\]]/g, "|").replace(/"/g, "");
850
- }
851
- function useTypePath(doc, pathString) {
852
- return useMemo(() => getTypePath(doc, pathString), [doc, pathString]);
853
- }
854
- function useSelectedField(documentSchemaType, path) {
855
- const { getFieldRefs: getFieldRefs2 } = useAiAssistanceConfig(), selectableFields = useMemo(
856
- () => documentSchemaType && isObjectSchemaType(documentSchemaType) ? [getDocumentFieldRef(documentSchemaType), ...getFieldRefs2(documentSchemaType.name)] : [],
857
- [documentSchemaType, getFieldRefs2]
858
- );
859
- return useMemo(() => path ? selectableFields?.find((f) => f.key === path) : void 0, [selectableFields, path]);
860
- }
861
- function getFieldTitle(field) {
862
- const schemaType = field?.schemaType;
863
- return field?.title ?? schemaType?.title ?? schemaType?.name ?? "Untitled";
864
- }
865
- function useAiPaneRouter() {
866
- const paneRouter = usePaneRouter();
867
- return useMemo(
868
- () => ({ ...paneRouter, params: paneRouter.params ?? {} }),
869
- [paneRouter]
870
- );
871
- }
872
- function useAssistDocumentContextValue(documentId, documentType) {
873
- const schema = useSchema(), { getFieldRefs: getFieldRefs2, getFieldRefsByTypePath } = useAiAssistanceConfig(), documentSchemaType = useMemo(() => {
874
- const schemaType = schema.get(documentType);
875
- if (!schemaType)
876
- throw new Error(`Schema type "${documentType}" not found`);
877
- return schemaType;
878
- }, [documentType, schema]), { fieldRefs, fieldRefsByTypePath } = useMemo(() => ({
879
- fieldRefs: getFieldRefs2(documentType),
880
- fieldRefsByTypePath: getFieldRefsByTypePath(documentType)
881
- }), [getFieldRefs2, getFieldRefsByTypePath, documentType]), {
882
- openInspector,
883
- closeInspector,
884
- inspector,
885
- onChange: documentOnChange,
886
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
887
- // @ts-ignore this is a valid option available in `corel` - Remove after corel is merged to next
888
- selectedReleaseId,
889
- editState
890
- } = useDocumentPane(), { draft, published, version } = editState || {}, assistableDocumentId = selectedReleaseId ? getVersionId(documentId, selectedReleaseId) : documentSchemaType.liveEdit ? documentId : getDraftId(documentId), documentIsNew = selectedReleaseId ? !version?._id : !draft?._id && !published?._id, documentIsAssistable = selectedReleaseId ? !!version : isDocAssistable(documentSchemaType, published, draft), { params } = useAiPaneRouter(), selectedPath = params[fieldPathParam], assistDocument = useStudioAssistDocument({
891
- documentId: assistableDocumentId,
892
- schemaType: documentSchemaType
893
- }), { syntheticTasks, addSyntheticTask, removeSyntheticTask } = useSyntheticTasks(assistableDocumentId);
894
- return useMemo(() => {
895
- const base = {
896
- assistableDocumentId,
897
- documentSchemaType,
898
- documentIsNew,
899
- documentIsAssistable,
900
- openInspector,
901
- closeInspector,
902
- inspector,
903
- documentOnChange,
904
- selectedPath,
905
- syntheticTasks,
906
- addSyntheticTask,
907
- removeSyntheticTask,
908
- fieldRefs,
909
- fieldRefsByTypePath
910
- };
911
- return assistDocument ? {
912
- ...base,
913
- loading: !1,
914
- assistDocument
915
- } : { ...base, loading: !0, assistDocument: void 0 };
916
- }, [
917
- assistDocument,
918
- documentIsAssistable,
919
- assistableDocumentId,
920
- documentSchemaType,
921
- documentIsNew,
922
- openInspector,
923
- closeInspector,
924
- inspector,
925
- documentOnChange,
926
- selectedPath,
927
- syntheticTasks,
928
- addSyntheticTask,
929
- removeSyntheticTask,
930
- fieldRefs,
931
- fieldRefsByTypePath
932
- ]);
933
- }
934
- function useSyntheticTasks(assistableDocumentId) {
935
- const [syntheticTasks, setSyntheticTasks] = useState(() => []), addSyntheticTask = useCallback((task) => {
936
- setSyntheticTasks((current) => [...current, task]);
937
- }, []), removeSyntheticTask = useCallback((task) => {
938
- setSyntheticTasks((current) => current.filter((t) => task._key !== t._key));
939
- }, []);
940
- return useEffect(() => {
941
- setSyntheticTasks([]);
942
- }, [assistableDocumentId]), {
943
- syntheticTasks,
944
- addSyntheticTask,
945
- removeSyntheticTask
946
- };
947
- }
948
- function AssistDocumentContextProvider(props) {
949
- const { documentId, documentType } = props, value = useAssistDocumentContextValue(documentId, documentType);
950
- return /* @__PURE__ */ jsx(AssistDocumentContext.Provider, { value, children: props.children });
951
- }
952
- function AssistDocumentLayout(props) {
953
- const { documentId, documentType } = props;
954
- return /* @__PURE__ */ jsx(AssistDocumentContextProvider, { documentType, documentId, children: props.renderDefault(props) });
955
- }
956
- function AssistFeatureBadge() {
957
- return /* @__PURE__ */ jsx(Badge, { fontSize: 0, style: { margin: "-2px 0" }, tone: "primary", children: "Beta" });
958
- }
959
- function AssistOnboardingPopover(props) {
960
- const { dismiss } = props;
961
- return /* @__PURE__ */ jsx(
962
- Popover,
963
- {
964
- content: /* @__PURE__ */ jsx(AssistIntroCard, { dismiss }),
965
- open: !0,
966
- portal: !0,
967
- placeholder: "bottom",
968
- tone: "default",
969
- width: 0,
970
- children: /* @__PURE__ */ jsx(Card, { radius: 2, shadow: 2, style: { padding: 2, lineHeight: 0 }, children: /* @__PURE__ */ jsx(Button, { disabled: !0, fontSize: 1, icon: SparklesIcon, mode: "bleed", padding: 2 }) })
971
- }
972
- );
973
- }
974
- function AssistIntroCard(props) {
975
- const buttonRef = useRef(null);
976
- return /* @__PURE__ */ jsxs(Stack, { as: "section", padding: 3, space: 3, children: [
977
- /* @__PURE__ */ jsxs(Stack, { padding: 2, space: 4, children: [
978
- /* @__PURE__ */ jsxs(Flex, { gap: 2, align: "center", children: [
979
- /* @__PURE__ */ jsx(Text, { as: "h1", size: 1, weight: "semibold", children: pluginTitle }),
980
- /* @__PURE__ */ jsx("div", { "aria-hidden": !0, style: { margin: "-3px 0", lineHeight: 0 }, children: /* @__PURE__ */ jsx(AssistFeatureBadge, {}) })
981
- ] }),
982
- /* @__PURE__ */ jsx(Stack, { space: 3, children: /* @__PURE__ */ jsxs(Text, { as: "p", muted: !0, size: 1, children: [
983
- "Manage reusable AI instructions to boost your content creation and reduce amount of repetitive chores.",
984
- " ",
985
- /* @__PURE__ */ jsxs("a", { href: releaseAnnouncementUrl, target: "_blank", rel: "noreferrer", children: [
986
- "Learn more ",
987
- /* @__PURE__ */ jsx(ArrowRightIcon, {})
988
- ] })
989
- ] }) })
990
- ] }),
991
- /* @__PURE__ */ jsx(
992
- Button,
993
- {
994
- fontSize: 1,
995
- icon: CheckmarkIcon,
996
- onClick: props.dismiss,
997
- padding: 3,
998
- ref: buttonRef,
999
- text: "Ok, good to know!",
1000
- tone: "primary"
1001
- }
1002
- )
1003
- ] });
1004
- }
1005
- const inspectorOnboardingKey = "sanityStudio:assist:inspector:onboarding:dismissed", fieldOnboardingKey = "sanityStudio:assist:field:onboarding:dismissed";
1006
- function isFeatureOnboardingDismissed(featureKey) {
1007
- return typeof localStorage > "u" ? !1 : localStorage.getItem(featureKey) === "true";
1008
- }
1009
- function dismissFeatureOnboarding(featureKey) {
1010
- typeof localStorage > "u" || localStorage.setItem(featureKey, "true");
1011
- }
1012
- function useOnboardingFeature(featureKey) {
1013
- const [showOnboarding, setShowOnboarding] = useState(
1014
- () => !isFeatureOnboardingDismissed(featureKey)
1015
- ), dismissOnboarding = useCallback(() => {
1016
- setShowOnboarding(!1), dismissFeatureOnboarding(featureKey);
1017
- }, [setShowOnboarding, featureKey]);
1018
- return { showOnboarding, dismissOnboarding };
1019
- }
1020
- const fadeIn = keyframes`
1021
- 0% {
1022
- opacity: 0;
1023
- transform: scale(0.75);
1024
- }
1025
- 40% {
1026
- opacity: 0;
1027
- transform: scale(0.75);
1028
- }
1029
- 100% {
1030
- opacity: 1;
1031
- transform: scale(1);
1032
- }
1033
- `, FadeInDiv = styled.div`
1034
- animation-name: ${fadeIn};
1035
- animation-timing-function: ease-in-out;
1036
- `, FadeInContent = forwardRef(function({
1037
- children,
1038
- durationMs = 250
1039
- }, ref) {
1040
- return /* @__PURE__ */ jsx(FadeInDiv, { ref, style: { animationDuration: `${durationMs}ms` }, children });
1041
- }), purple = {
1042
- 400: {
1043
- hex: "#b087f7"
1044
- },
1045
- 500: {
1046
- hex: "#8f57ef"
1047
- },
1048
- 600: {
1049
- hex: "#721fe5"
1050
- }
1051
- }, Root = styled.span`
1052
- display: block;
1053
- width: 25px;
1054
- height: 25px;
1055
- position: relative;
1056
- `, dash = keyframes`
1057
- 0% {
1058
- transform: rotate(0);
1059
- }
1060
- 100% {
1061
- transform: rotate(43deg);
1062
- }
1063
- `, Outline = styled.svg`
1064
- display: block;
1065
- position: absolute;
1066
- top: 0;
1067
- left: 0;
1068
-
1069
- & > circle {
1070
- stroke: var(--ai-avatar-stroke-color);
1071
- stroke-width: 1.5px;
1072
- stroke-linecap: round;
1073
- transform-origin: center;
1074
- animation: ${dash} 500ms ease-in-out infinite;
1075
- transition: stroke-dasharray 200ms ease-in-out;
1076
-
1077
- stroke-dasharray: 2.34px 0;
1078
-
1079
- [data-state='active'] > & {
1080
- stroke-dasharray: 2px 2.34px;
1081
- }
1082
- }
1083
- `, IconDisc = styled.span`
1084
- background: var(--ai-avatar-disc-color);
1085
- color: white;
1086
- width: 21px;
1087
- height: 21px;
1088
- display: flex;
1089
- align-items: center;
1090
- justify-content: center;
1091
- border-radius: 10.5px;
1092
- position: absolute;
1093
- top: 2px;
1094
- left: 2px;
1095
- `;
1096
- function AssistAvatar(props) {
1097
- const { state = "present" } = props, scheme = useColorSchemeValue(), style = useMemo(() => scheme === "dark" ? {
1098
- "--ai-avatar-stroke-color": purple[400].hex,
1099
- "--ai-avatar-disc-color": purple[600].hex
1100
- } : {
1101
- "--ai-avatar-stroke-color": purple[500].hex,
1102
- "--ai-avatar-disc-color": purple[600].hex
1103
- }, [scheme]);
1104
- return /* @__PURE__ */ jsxs(Root, { "data-state": state, style, children: [
1105
- /* @__PURE__ */ jsx(
1106
- Outline,
1107
- {
1108
- width: "25",
1109
- height: "25",
1110
- viewBox: "0 0 25 25",
1111
- fill: "none",
1112
- xmlns: "http://www.w3.org/2000/svg",
1113
- children: /* @__PURE__ */ jsx("circle", { cx: "12.5", cy: "12.5", r: "11.75" })
1114
- }
1115
- ),
1116
- /* @__PURE__ */ jsx(IconDisc, { children: /* @__PURE__ */ jsx(Text, { as: "span", size: 0, style: { color: "inherit" }, children: /* @__PURE__ */ jsx(SparklesIcon, { style: { color: "inherit" } }) }) })
1117
- ] });
1118
- }
1119
- function AiFieldPresence(props) {
1120
- return /* @__PURE__ */ jsx(Card, { style: { position: "relative", background: "transparent" }, contentEditable: !1, children: /* @__PURE__ */ jsx(
1121
- Tooltip,
1122
- {
1123
- placement: "left",
1124
- content: /* @__PURE__ */ jsx(Card, { padding: 3, border: !0, children: /* @__PURE__ */ jsx(Flex, { align: "center", children: /* @__PURE__ */ jsx(Text, { size: 1, children: "Running instruction..." }) }) }),
1125
- children: /* @__PURE__ */ jsx(FadeInContent, { durationMs: 300, children: /* @__PURE__ */ jsx(AssistAvatar, { state: "active" }) })
1126
- }
1127
- ) });
1128
- }
1129
- const NO_PRESENCE = [];
1130
- function useAssistPresence(path, showFocusWithin) {
1131
- const context = useAssistDocumentContext(), tasks = (context && "assistDocument" in context ? context.assistDocument : void 0)?.tasks;
1132
- return useMemo(() => {
1133
- const activePresence = tasks?.filter((task) => !task.ended)?.flatMap((task) => task.presence ?? [])?.filter(
1134
- (p) => p.started && (/* @__PURE__ */ new Date()).getTime() - new Date(p.started).getTime() < maxHistoryVisibilityMs
1135
- ).filter((presence) => {
1136
- if (!presence.path || !path.length)
1137
- return !1;
1138
- const statusPath = stringToPath(presence.path);
1139
- return !showFocusWithin && statusPath.length !== path.length ? !1 : path.every((pathSegment, i) => {
1140
- const statusSegment = statusPath[i];
1141
- return typeof pathSegment == "string" ? pathSegment === statusSegment : isKeySegment(pathSegment) && isKeySegment(statusSegment) ? pathSegment._key === statusSegment._key : !1;
1142
- });
1143
- });
1144
- return activePresence?.length ? activePresence.map((status) => aiPresence(status, path)) : NO_PRESENCE;
1145
- }, [showFocusWithin, tasks, path]);
1146
- }
1147
- function aiPresence(presence, path, title) {
1148
- return {
1149
- user: {
1150
- id: `sanity-assistant_${presence._key}`,
1151
- displayName: pluginTitle
1152
- },
1153
- path,
1154
- sessionId: "not-available",
1155
- lastActiveAt: presence?.started ?? (/* @__PURE__ */ new Date()).toISOString()
1156
- };
1157
- }
1158
- function AssistFieldWrapper(props) {
1159
- const { schemaType } = props;
1160
- return !useMemo(() => isAssistSupported(schemaType), [schemaType]) || schemaType.name.startsWith("sanity.") || schemaType.name === contextDocumentTypeName ? props.renderDefault(props) : !isType(props.schemaType, "document") && props.inputId !== "root" && props.inputId !== assistFormId ? /* @__PURE__ */ jsx(AssistField, { ...props, children: props.children }) : props.renderDefault(props);
1161
- }
1162
- function AssistField(props) {
1163
- const { path } = props, isPortableText = useMemo(
1164
- () => !!(isArraySchemaType(props.schemaType) && isPortableTextArray(props.schemaType)),
1165
- [props.schemaType]
1166
- ), presence = useAssistPresence(props.path, isPortableText), firstAssistedPath = useContext(FirstAssistedPathContext), isFirstAssisted = useMemo(
1167
- () => pathToString(path) === firstAssistedPath,
1168
- [path, firstAssistedPath]
1169
- ), { showOnboarding, dismissOnboarding } = useOnboardingFeature(fieldOnboardingKey), singlePresence = presence[0], actions = /* @__PURE__ */ jsxs(Flex, { gap: 2, align: "center", justify: "space-between", children: [
1170
- singlePresence && /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(AiFieldPresence, { presence: singlePresence }) }),
1171
- isFirstAssisted && showOnboarding && /* @__PURE__ */ jsx(AssistOnboardingPopover, { dismiss: dismissOnboarding })
1172
- ] });
1173
- return props.renderDefault({
1174
- ...props,
1175
- // When showing the onboarding, prevent default field actions from being rendered
1176
- actions: isFirstAssisted && showOnboarding ? [] : props.actions,
1177
- // Render presence (and possibly onboarding) in the internal slot (between presence and the field actions)
1178
- // eslint-disable-next-line camelcase
1179
- __internal_slot: actions
1180
- });
1181
- }
1182
- const WrapPreCard = styled(Card)`
1183
- & pre {
1184
- white-space: pre-wrap !important;
1185
- }
1186
- `;
1187
- function SafeValueInput(props) {
1188
- return /* @__PURE__ */ jsx(ErrorWrapper, { onChange: props.onChange, children: /* @__PURE__ */ jsx(PteValueFixer, { ...props }) });
1189
- }
1190
- function ErrorWrapper(props) {
1191
- const { onChange } = props, [error, setError] = useState(), catchError2 = useCallback((params) => {
1192
- setError(params.error);
1193
- }, []), unsetValue = useCallback(() => {
1194
- onChange(PatchEvent.from(unset())), setError(void 0);
1195
- }, [onChange]), dismiss = useCallback(() => setError(void 0), []), catcher = /* @__PURE__ */ jsx(ErrorBoundary, { onCatch: catchError2, children: props.children });
1196
- return error ? /* @__PURE__ */ jsx(Card, { border: !0, tone: "critical", padding: 2, contentEditable: !1, children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
1197
- /* @__PURE__ */ jsx(Text, { muted: !0, weight: "semibold", children: "An error occurred." }),
1198
- /* @__PURE__ */ jsx(WrapPreCard, { flex: 1, padding: 2, tone: "critical", border: !0, children: catcher }),
1199
- /* @__PURE__ */ jsxs(Flex, { width: "fill", flex: 1, gap: 3, children: [
1200
- /* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsx(Button, { text: "Dismiss", onClick: dismiss, tone: "primary" }) }),
1201
- /* @__PURE__ */ jsx(Button, { text: "Unset value", onClick: unsetValue, tone: "critical" })
1202
- ] })
1203
- ] }) }) : catcher;
1204
- }
1205
- function PteValueFixer(props) {
1206
- const isPortableText = useMemo(
1207
- () => isArraySchemaType(props.schemaType) && isPortableTextArray(props.schemaType),
1208
- [props.schemaType]
1209
- ), value = props.value;
1210
- return isPortableText && value && !value.length ? props.renderDefault({ ...props, value: void 0 }) : props.renderDefault(props);
1211
- }
1212
- function AssistFormBlock(props) {
1213
- const presence = useAssistPresence(props.path, !0), { onChange } = useFormCallbacks(), key = props.value._key, localOnChange = useCallback(
1214
- (patchEvent) => {
1215
- key && onChange(PatchEvent.from(patchEvent).prefixAll({ _key: key }));
1216
- },
1217
- [onChange, key]
1218
- ), singlePresence = presence[0];
1219
- return /* @__PURE__ */ jsx(ErrorWrapper, { onChange: localOnChange, children: /* @__PURE__ */ jsxs(Flex, { align: "center", justify: "space-between", children: [
1220
- /* @__PURE__ */ jsx(Box, { flex: 1, children: props.renderDefault(props) }),
1221
- singlePresence && /* @__PURE__ */ jsx(AiFieldPresence, { presence: singlePresence })
1222
- ] }) });
1223
- }
1224
- const InlineBlockValueContext = createContext(void 0);
1225
- function AssistInlineFormBlock(props) {
1226
- return /* @__PURE__ */ jsx(InlineBlockValueContext.Provider, { value: props.value, children: /* @__PURE__ */ jsx(Fragment, { children: props.renderDefault(props) }) });
1227
- }
1228
- function AssistItem(props) {
1229
- const { path } = props, presence = useAssistPresence(path, !0);
1230
- return /* @__PURE__ */ jsxs(Flex, { align: "center", width: "fill", style: { position: "relative" }, children: [
1231
- /* @__PURE__ */ jsx(Box, { flex: 1, children: props.renderDefault({ ...props }) }),
1232
- presence.map((pre) => /* @__PURE__ */ jsx(Box, { style: { position: "absolute", right: 35 }, children: /* @__PURE__ */ jsx(AiFieldPresence, { presence: pre }) }, pre.user.id))
1233
- ] });
1234
- }
1235
- const preventDefault = (ev) => ev.preventDefault();
1236
- function DocumentForm(props) {
1237
- const {
1238
- collapsedFieldSets,
1239
- collapsedPaths,
1240
- displayed: value,
1241
- documentId,
1242
- documentType,
1243
- editState,
1244
- formState,
1245
- onBlur,
1246
- onChange,
1247
- onFocus,
1248
- onPathOpen,
1249
- onSetActiveFieldGroup,
1250
- onSetCollapsedFieldSet,
1251
- onSetCollapsedPath,
1252
- ready,
1253
- validation
1254
- } = useDocumentPane(), documentStore = useDocumentStore(), presence = useDocumentPresence(documentId), patchChannel = useMemo(() => createPatchChannel(), []), isLocked = editState?.transactionSyncLock?.enabled;
1255
- useEffect(() => {
1256
- const sub = documentStore.pair.documentEvents(documentId, documentType).pipe(
1257
- tap((event) => {
1258
- event.type === "mutation" && patchChannel.publish(prepareMutationEvent(event)), event.type === "rebase" && patchChannel.publish(prepareRebaseEvent(event));
1259
- })
1260
- ).subscribe();
1261
- return () => {
1262
- sub.unsubscribe();
1263
- };
1264
- }, [documentId, documentStore, documentType, patchChannel]);
1265
- const hasRev = !!value?._rev;
1266
- useEffect(() => {
1267
- hasRev && patchChannel.publish({
1268
- type: "mutation",
1269
- patches: [],
1270
- snapshot: value
1271
- });
1272
- }, [hasRev]);
1273
- const formRef = useRef(null);
1274
- return useEffect(() => {
1275
- focusFirstDescendant(formRef.current);
1276
- }, []), isLocked ? /* @__PURE__ */ jsx(Box, { as: "form", ...props, ref: formRef, children: /* @__PURE__ */ jsx(
1277
- Flex,
1278
- {
1279
- align: "center",
1280
- direction: "column",
1281
- height: "fill",
1282
- justify: "center",
1283
- padding: 3,
1284
- sizing: "border",
1285
- children: /* @__PURE__ */ jsx(Text, { size: 1, children: "Please hold tight while the document is synced. This usually happens right after the document has been published, and it shouldn\u2019t take more than a few seconds" })
1286
- }
1287
- ) }) : /* @__PURE__ */ jsx(Box, { as: "form", ...props, onSubmit: preventDefault, ref: formRef, children: ready ? formState === null ? /* @__PURE__ */ jsx(
1288
- Flex,
1289
- {
1290
- align: "center",
1291
- direction: "column",
1292
- height: "fill",
1293
- justify: "center",
1294
- padding: 3,
1295
- sizing: "border",
1296
- children: /* @__PURE__ */ jsx(Text, { size: 1, children: "This form is hidden" })
1297
- }
1298
- ) : /* @__PURE__ */ jsx(
1299
- FormBuilder,
1300
- {
1301
- __internal_patchChannel: patchChannel,
1302
- collapsedFieldSets,
1303
- collapsedPaths,
1304
- focusPath: formState.focusPath,
1305
- changed: formState.changed,
1306
- focused: formState.focused,
1307
- groups: formState.groups,
1308
- id: assistFormId,
1309
- members: formState.members,
1310
- onChange,
1311
- onFieldGroupSelect: onSetActiveFieldGroup,
1312
- onPathBlur: onBlur,
1313
- onPathFocus: onFocus,
1314
- onPathOpen,
1315
- onSetFieldSetCollapsed: onSetCollapsedFieldSet,
1316
- onSetPathCollapsed: onSetCollapsedPath,
1317
- presence,
1318
- readOnly: formState.readOnly,
1319
- schemaType: formState.schemaType,
1320
- validation,
1321
- value: formState.value
1322
- }
1323
- ) : /* @__PURE__ */ jsxs(
1324
- Flex,
1325
- {
1326
- align: "center",
1327
- direction: "column",
1328
- height: "fill",
1329
- justify: "center",
1330
- padding: 3,
1331
- sizing: "border",
1332
- children: [
1333
- /* @__PURE__ */ jsx(Spinner, { muted: !0 }),
1334
- /* @__PURE__ */ jsx(Box, { marginTop: 3, children: /* @__PURE__ */ jsx(Text, { align: "center", muted: !0, size: 1, children: "Loading document" }) })
1335
- ]
1336
- }
1337
- ) });
1338
- }
1339
- function prepareMutationEvent(event) {
1340
- const patches = event.mutations.map((mut) => mut.patch).filter(Boolean);
1341
- return {
1342
- type: "mutation",
1343
- snapshot: event.document,
1344
- patches: fromMutationPatches(event.origin, patches)
1345
- };
1346
- }
1347
- function prepareRebaseEvent(event) {
1348
- const remotePatches = event.remoteMutations.map((mut) => mut.patch).filter(Boolean), localPatches = event.localMutations.map((mut) => mut.patch).filter(Boolean);
1349
- return {
1350
- type: "rebase",
1351
- snapshot: event.document,
1352
- patches: fromMutationPatches("remote", remotePatches).concat(
1353
- fromMutationPatches("local", localPatches)
1354
- )
1355
- };
1356
- }
1357
- const AssistTypeContext = createContext({}), DEFAULT_MAX_DEPTH$1 = 8, ABSOLUTE_MAX_DEPTH$1 = 50;
1358
- function getConditionalMembers(docState, maxDepth2 = DEFAULT_MAX_DEPTH$1) {
1359
- return [{
1360
- path: "",
1361
- hidden: !1,
1362
- readOnly: !!docState.readOnly,
1363
- conditional: isConditional(docState.schemaType)
1364
- }, ...extractConditionalPaths(docState, Math.min(maxDepth2, ABSOLUTE_MAX_DEPTH$1))].filter((v) => v.conditional).map(({ conditional, ...state }) => ({ ...state }));
1365
- }
1366
- function isConditional(schemaType) {
1367
- return typeof schemaType.hidden == "function" || typeof schemaType.readOnly == "function";
1368
- }
1369
- function conditionalState(memberState) {
1370
- return {
1371
- path: pathToString(memberState.path),
1372
- readOnly: !!memberState.readOnly,
1373
- // Use actual form state readOnly value
1374
- hidden: !1,
1375
- // If it's in form state members, it's not hidden
1376
- conditional: isConditional(memberState.schemaType)
1377
- };
1378
- }
1379
- function extractConditionalPaths(node, maxDepth2) {
1380
- return node.path.length >= maxDepth2 ? [] : node.members.reduce((acc, member) => {
1381
- if (member.kind === "error")
1382
- return acc;
1383
- if (member.kind === "field") {
1384
- const schemaType = member.field.schemaType;
1385
- if (schemaType.jsonType === "object") {
1386
- const innerFields = member.field.readOnly ? [] : extractConditionalPaths(member.field, maxDepth2);
1387
- return [...acc, conditionalState(member.field), ...innerFields];
1388
- } else if (schemaType.jsonType === "array") {
1389
- const array = member.field;
1390
- let arrayPaths = [];
1391
- const isObjectsArray = array.members.some(
1392
- (m) => m.kind === "item" && isObjectSchemaType(m.item.schemaType)
1393
- );
1394
- if (!array.readOnly)
1395
- for (const arrayMember of array.members) {
1396
- if (arrayMember.kind === "error")
1397
- continue;
1398
- const innerFields = isObjectsArray && !arrayMember.item.readOnly ? extractConditionalPaths(arrayMember.item, maxDepth2) : [];
1399
- arrayPaths = [...arrayPaths, conditionalState(arrayMember.item), ...innerFields];
1400
- }
1401
- return [...acc, conditionalState(array), ...arrayPaths];
1402
- }
1403
- return [...acc, conditionalState(member.field)];
1404
- } else if (member.kind === "fieldSet") {
1405
- const conditionalFieldset = !!node.schemaType?.fieldsets?.some(
1406
- (f) => !f.single && f.name === member.fieldSet.name && typeof f.hidden == "function"
1407
- ), innerFields = extractConditionalPaths(member.fieldSet, maxDepth2).map((f) => ({
1408
- ...f,
1409
- // if fieldset is conditional, visible fields must also be considered conditional
1410
- conditional: conditionalFieldset || f.conditional
1411
- }));
1412
- return [...acc, ...innerFields];
1413
- }
1414
- return acc;
1415
- }, []);
1416
- }
1417
- const SparklesIllustration = styled(SparklesIcon)({
1418
- fontSize: "3.125em",
1419
- "& path": {
1420
- strokeWidth: "0.6px !important"
1421
- }
1422
- });
1423
- function InspectorOnboarding(props) {
1424
- const { onDismiss } = props;
1425
- return /* @__PURE__ */ jsx(Box, { padding: 4, children: /* @__PURE__ */ jsx(Container, { width: 0, children: /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
1426
- /* @__PURE__ */ jsx("div", { style: { textAlign: "center" }, children: /* @__PURE__ */ jsx(SparklesIllustration, {}) }),
1427
- /* @__PURE__ */ jsx(Text, { align: "center", size: 1, children: "Create reusable AI instructions that can be applied across all documents of a certain type." }),
1428
- /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, justify: "center", children: [
1429
- /* @__PURE__ */ jsx(
1430
- Button,
1431
- {
1432
- as: "a",
1433
- href: releaseAnnouncementUrl,
1434
- rel: "noreferrer",
1435
- target: "_blank",
1436
- fontSize: 1,
1437
- mode: "default",
1438
- onClick: onDismiss,
1439
- padding: 2,
1440
- text: "Learn more",
1441
- tone: "primary"
1442
- }
1443
- ),
1444
- /* @__PURE__ */ jsx(Button, { fontSize: 1, mode: "bleed", onClick: onDismiss, padding: 2, text: "Dismiss" })
1445
- ] })
1446
- ] }) }) });
1447
- }
1448
- function FieldAutocomplete(props) {
1449
- const { id, schemaType, fieldPath, onSelect, includeDocument, filter: filter2 } = props, { getFieldRefs: getFieldRefs2 } = useAiAssistanceConfig(), fieldRefs = useMemo(() => {
1450
- const refs = getFieldRefs2(schemaType.name);
1451
- return includeDocument ? [getDocumentFieldRef(schemaType), ...refs] : refs;
1452
- }, [schemaType, includeDocument, getFieldRefs2]), currentField = useMemo(
1453
- () => fieldRefs.find((f) => f.key === fieldPath),
1454
- [fieldPath, fieldRefs]
1455
- ), autocompleteOptions = useMemo(
1456
- () => fieldRefs.filter((field) => filter2 ? filter2(field) : !0).filter((f) => !isType(f.schemaType, "reference")).map((field) => ({ value: field.key, field })),
1457
- [fieldRefs, filter2]
1458
- ), renderOption = useCallback((option) => {
1459
- const { value, field } = option;
1460
- return value ? isType(field.schemaType, "document") ? /* @__PURE__ */ jsx(Card, { as: "button", padding: 3, radius: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: "The entire document" }) }) : /* @__PURE__ */ jsx(Card, { as: "button", padding: 3, radius: 1, children: /* @__PURE__ */ jsxs(Flex, { gap: 3, children: [
1461
- /* @__PURE__ */ jsx(Text, { size: 1, children: createElement(field.icon) }),
1462
- /* @__PURE__ */ jsx(FieldTitle, { field })
1463
- ] }) }) : /* @__PURE__ */ jsx(Card, { as: "button", padding: 3, radius: 1, children: /* @__PURE__ */ jsx(Text, { accent: !0, size: 1, children: option.value }) });
1464
- }, []), renderValue = useCallback((value, option) => option?.field.title ?? value, []), filterOption = useCallback((query, option) => {
1465
- const lQuery = query.toLowerCase();
1466
- return option?.value?.toLowerCase().includes(lQuery) || option?.field?.title?.toLowerCase().includes(lQuery);
1467
- }, []);
1468
- return /* @__PURE__ */ jsx(
1469
- Autocomplete,
1470
- {
1471
- fontSize: 1,
1472
- icon: currentField ? currentField.icon : SearchIcon,
1473
- onChange: onSelect,
1474
- openButton: !0,
1475
- id,
1476
- options: autocompleteOptions,
1477
- placeholder: "Search for a field",
1478
- radius: 2,
1479
- renderOption,
1480
- renderValue,
1481
- value: currentField?.key,
1482
- filterOption
1483
- }
1484
- );
1485
- }
1486
- function FieldTitle(props) {
1487
- const splitTitle = props.field.title.split("/");
1488
- return /* @__PURE__ */ jsx(Box, { flex: "none", children: /* @__PURE__ */ jsxs(
1489
- Breadcrumbs,
1490
- {
1491
- style: {
1492
- display: "flex",
1493
- flexWrap: "wrap",
1494
- alignItems: "center",
1495
- marginTop: "-4px"
1496
- },
1497
- separator: /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: "/" }) }),
1498
- space: 1,
1499
- children: [
1500
- splitTitle.slice(0, splitTitle.length - 1).map((pt, i) => (
1501
- // eslint-disable-next-line react/no-array-index-key
1502
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: pt.trim() }) }, i)
1503
- )),
1504
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: splitTitle[splitTitle.length - 1] }) })
1505
- ]
1506
- }
1507
- ) });
1508
- }
1509
- function useInterval(ms) {
1510
- const [tick, update] = useReducer((n) => n + 1, 0);
1511
- return useEffect(() => {
1512
- const i = setInterval(update, ms);
1513
- return () => clearInterval(i);
1514
- }, [ms]), tick;
1515
- }
1516
- function TimeAgo({ date }) {
1517
- useInterval(1e3);
1518
- const timeSince = formatDistanceToNow(date ? new Date(date) : /* @__PURE__ */ new Date());
1519
- return /* @__PURE__ */ jsxs("span", { title: timeSince, children: [
1520
- timeSince,
1521
- " ago"
1522
- ] });
1523
- }
1524
- const rotate = keyframes`
1525
- 0% {
1526
- transform: rotate(0);
1527
- }
1528
- 100% {
1529
- transform: rotate(360deg);
1530
- }
1531
- `, SyncSpinningIcon = styled(SyncIcon)`
1532
- animation: ${rotate} 1s linear infinite;
1533
- `, TASK_CONFIG = {
1534
- aborted: {
1535
- title: "Canceled",
1536
- icon: CloseCircleIcon,
1537
- tone: "transparent"
1538
- },
1539
- error: {
1540
- title: "Error",
1541
- icon: ErrorOutlineIcon,
1542
- tone: "critical"
1543
- },
1544
- success: {
1545
- title: "Completed",
1546
- icon: CheckmarkCircleIcon,
1547
- tone: "positive"
1548
- },
1549
- timeout: {
1550
- title: "Timeout",
1551
- icon: ClockIcon,
1552
- tone: "caution"
1553
- }
1554
- };
1555
- function InstructionTaskHistoryButton(props) {
1556
- const { tasks, instructions, documentId, showTitles } = props, client = useClient({ apiVersion: "2023-01-01" }), cancelRun = useCallback(
1557
- (taskKey) => {
1558
- if (!documentId)
1559
- return;
1560
- const statusDocId = assistTasksStatusId(documentId), basePath2 = `${typed("tasks")}[_key=="${taskKey}"]`;
1561
- client.patch(statusDocId).set({
1562
- [`${basePath2}.${typed("ended")}`]: (/* @__PURE__ */ new Date()).toISOString(),
1563
- [`${basePath2}.${typed("reason")}`]: typed("aborted")
1564
- }).commit().catch(console.error);
1565
- },
1566
- [client, documentId]
1567
- ), titledTasks = useMemo(() => {
1568
- const t = tasks?.filter(
1569
- (task) => task.started && (/* @__PURE__ */ new Date()).getTime() - new Date(task.started).getTime() < maxHistoryVisibilityMs
1570
- ).map((task) => {
1571
- const instruction2 = instructions?.find((i) => i._key === task.instructionKey);
1572
- return {
1573
- ...task,
1574
- title: showTitles ? task.title ?? getInstructionTitle(instruction2) : void 0,
1575
- cancel: () => cancelRun(task._key)
1576
- };
1577
- }) ?? [];
1578
- return t.sort((a, b) => new Date(b.started ?? "").getTime() - new Date(a.started ?? "").getTime()), t;
1579
- }, [tasks, instructions, cancelRun, showTitles]), isRunning = useMemo(() => titledTasks.some((r) => !r.ended), [titledTasks]), hasErrors = useMemo(
1580
- () => titledTasks.some((r) => r.reason === "error" || r.reason === "timeout"),
1581
- [titledTasks]
1582
- ), [open, setOpen] = useState(!1), toggleOpen = useCallback(() => setOpen((v) => !v), []), [button, setButton] = useState(null), [popover, setPopover] = useState(null), handleClickOutside = useCallback(() => {
1583
- setOpen(!1);
1584
- }, []);
1585
- useClickOutside(handleClickOutside, [button, popover]);
1586
- const handleEscape = useCallback(() => {
1587
- setOpen(!1), button?.focus();
1588
- }, [button]);
1589
- return /* @__PURE__ */ jsx(
1590
- Popover,
1591
- {
1592
- constrainSize: !0,
1593
- content: /* @__PURE__ */ jsx(TaskList, { onEscape: handleEscape, tasks: titledTasks }),
1594
- open: open && !!titledTasks?.length,
1595
- placement: "top",
1596
- portal: !0,
1597
- ref: setPopover,
1598
- width: 0,
1599
- children: /* @__PURE__ */ jsx(
1600
- TaskStatusButton,
1601
- {
1602
- disabled: !titledTasks?.length,
1603
- hasErrors,
1604
- isRunning,
1605
- onClick: toggleOpen,
1606
- ref: setButton,
1607
- selected: open
1608
- }
1609
- )
1610
- }
1611
- );
1612
- }
1613
- const TASK_STATUS_BUTTON_TOOLTIP_PROPS = {
1614
- placement: "top"
1615
- }, TaskStatusButton = forwardRef(function(props, ref) {
1616
- const { disabled, hasErrors, isRunning, onClick, selected } = props;
1617
- return /* @__PURE__ */ jsx(
1618
- StatusButton,
1619
- {
1620
- label: `${pluginTitle} status`,
1621
- "aria-label": `${pluginTitle} status`,
1622
- icon: isRunning ? SyncSpinningIcon : hasErrors ? ErrorOutlineIcon : CheckmarkCircleIcon,
1623
- mode: "bleed",
1624
- onClick,
1625
- tone: hasErrors ? "critical" : void 0,
1626
- disabled,
1627
- ref,
1628
- selected,
1629
- tooltipProps: TASK_STATUS_BUTTON_TOOLTIP_PROPS
1630
- }
1631
- );
1632
- });
1633
- function TaskList(props) {
1634
- const { onEscape, tasks } = props, { isTopLayer } = useLayer();
1635
- return useGlobalKeyDown(
1636
- useCallback(
1637
- (event) => {
1638
- isTopLayer && event.key === "Escape" && onEscape();
1639
- },
1640
- [isTopLayer, onEscape]
1641
- )
1642
- ), /* @__PURE__ */ jsx(Stack, { padding: 1, space: 1, children: tasks.map((task) => /* @__PURE__ */ jsx(TaskItem, { task }, task._key)) });
1643
- }
1644
- function TaskItem(props) {
1645
- const { task } = props, taskType = task.reason && TASK_CONFIG[task.reason];
1646
- return /* @__PURE__ */ jsx(Card, { radius: 2, tone: taskType && taskType?.tone, children: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 1, children: [
1647
- /* @__PURE__ */ jsxs(Flex, { align: "flex-start", flex: 1, gap: 3, padding: 3, children: [
1648
- /* @__PURE__ */ jsx(Box, { flex: "none", children: /* @__PURE__ */ jsxs(Text, { size: 1, children: [
1649
- taskType && createElement(taskType.icon),
1650
- !task.reason && /* @__PURE__ */ jsx(Spinner, {})
1651
- ] }) }),
1652
- /* @__PURE__ */ jsxs(Stack, { flex: 1, space: 2, children: [
1653
- /* @__PURE__ */ jsxs(Text, { size: 1, weight: "medium", children: [
1654
- taskType ? taskType.title : "Running",
1655
- task.title && /* @__PURE__ */ jsxs(Fragment, { children: [
1656
- ": ",
1657
- task.title
1658
- ] })
1659
- ] }),
1660
- task.message ? /* @__PURE__ */ jsx(Text, { size: 1, children: task.message }) : null,
1661
- /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: /* @__PURE__ */ jsx(TimeAgo, { date: task.ended ?? task.started }) })
1662
- ] })
1663
- ] }),
1664
- !task.ended && /* @__PURE__ */ jsx(Box, { flex: "none", padding: 1, children: /* @__PURE__ */ jsx(Button, { fontSize: 1, mode: "bleed", onClick: task.cancel, text: "Cancel" }) })
1665
- ] }) });
1666
- }
1667
- const CardWithShadowBelow = styled(Card)`
1668
- position: relative;
1669
-
1670
- &:after {
1671
- content: '';
1672
- display: block;
1673
- position: absolute;
1674
- left: 0;
1675
- right: 0;
1676
- bottom: -1px;
1677
- border-bottom: 1px solid var(--card-border-color);
1678
- opacity: 0.5;
1679
- z-index: 100;
1680
- }
1681
- `, CardWithShadowAbove = styled(Card)`
1682
- position: relative;
1683
-
1684
- &:after {
1685
- content: '';
1686
- display: block;
1687
- position: absolute;
1688
- left: 0;
1689
- right: 0;
1690
- top: -1px;
1691
- border-top: 1px solid var(--card-border-color);
1692
- opacity: 0.5;
1693
- z-index: 100;
1694
- }
1695
- `;
1696
- function AssistInspectorWrapper(props) {
1697
- const context = useAiAssistanceConfig();
1698
- if (context.statusLoading)
1699
- return /* @__PURE__ */ jsx(Flex, { align: "center", height: "fill", justify: "center", padding: 4, sizing: "border", children: /* @__PURE__ */ jsxs(Stack, { space: 3, style: { textAlign: "center" }, children: [
1700
- /* @__PURE__ */ jsx(Spinner, { muted: !0 }),
1701
- /* @__PURE__ */ jsxs(Text, { muted: !0, size: 1, children: [
1702
- "Loading ",
1703
- pluginTitle,
1704
- "..."
1705
- ] })
1706
- ] }) });
1707
- const status = context.status;
1708
- return status?.enabled ? !status?.initialized || !status.validToken ? /* @__PURE__ */ jsxs(Flex, { direction: "column", height: "fill", children: [
1709
- /* @__PURE__ */ jsx(
1710
- DocumentInspectorHeader,
1711
- {
1712
- closeButtonLabel: "Close",
1713
- onClose: props.onClose,
1714
- title: pluginTitle
1715
- }
1716
- ),
1717
- /* @__PURE__ */ jsxs(Stack, { padding: 4, space: 3, children: [
1718
- context.error ? /* @__PURE__ */ jsxs(Text, { size: 1, weight: "semibold", children: [
1719
- "Failed to start ",
1720
- pluginTitle
1721
- ] }) : null,
1722
- !context.error && !status?.initialized ? /* @__PURE__ */ jsxs(Text, { size: 1, weight: "semibold", children: [
1723
- pluginTitle,
1724
- " is not enabled"
1725
- ] }) : null,
1726
- !context.error && status?.initialized && !status.validToken ? /* @__PURE__ */ jsxs(Fragment, { children: [
1727
- /* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: "Invalid token" }),
1728
- /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: "The token used by the AI Assistant is not valid and has to be regenerated." })
1729
- ] }) : null,
1730
- context.error && /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: "Something went wrong. See console for details." }),
1731
- !context.error && !status?.initialized && /* @__PURE__ */ jsxs(Text, { size: 1, muted: !0, children: [
1732
- "Please enable ",
1733
- pluginTitle,
1734
- "."
1735
- ] }),
1736
- /* @__PURE__ */ jsx(
1737
- Button,
1738
- {
1739
- fontSize: 1,
1740
- icon: context.initLoading ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Spinner, {}) }) : context.error ? RetryIcon : void 0,
1741
- text: context.error ? "Retry" : status?.initialized && !status.validToken ? `Restore ${pluginTitle}` : `Enable ${pluginTitle} now`,
1742
- tone: "primary",
1743
- onClick: context.init,
1744
- disabled: context.initLoading
1745
- }
1746
- )
1747
- ] })
1748
- ] }) : /* @__PURE__ */ jsx(AssistInspector, { ...props }) : /* @__PURE__ */ jsxs(Flex, { direction: "column", height: "fill", children: [
1749
- /* @__PURE__ */ jsx(
1750
- DocumentInspectorHeader,
1751
- {
1752
- closeButtonLabel: "Close",
1753
- onClose: props.onClose,
1754
- title: pluginTitle
1755
- }
1756
- ),
1757
- /* @__PURE__ */ jsxs(Stack, { flex: 1, overflow: "auto", padding: 4, space: 3, children: [
1758
- /* @__PURE__ */ jsxs(Text, { as: "p", size: 1, weight: "semibold", children: [
1759
- pluginTitle,
1760
- " is not available"
1761
- ] }),
1762
- /* @__PURE__ */ jsxs(Text, { as: "p", muted: !0, size: 1, children: [
1763
- "Please get in touch with a Sanity account manager or",
1764
- " ",
1765
- /* @__PURE__ */ jsx("a", { href: salesUrl, target: "_blank", rel: "noreferrer", children: "contact our sales team" }),
1766
- " ",
1767
- "to get started with ",
1768
- pluginTitle,
1769
- ".",
1770
- " ",
1771
- /* @__PURE__ */ jsx("a", { href: releaseAnnouncementUrl, target: "_blank", rel: "noreferrer", children: "Learn more \u2192" })
1772
- ] })
1773
- ] })
1774
- ] });
1775
- }
1776
- function AssistInspector(props) {
1777
- const { params } = useAiPaneRouter(), boundary = useRef(null), pathKey = params?.[fieldPathParam], instructionKey = params?.[instructionParam], documentPane = useDocumentPane(), {
1778
- documentId,
1779
- documentType,
1780
- value: docValue,
1781
- schemaType,
1782
- onChange: documentOnChange,
1783
- formState
1784
- } = documentPane, { assistableDocumentId, documentIsAssistable } = useAssistDocumentContext(), formStateRef = useRef(formState);
1785
- formStateRef.current = formState;
1786
- const { instructionLoading, requestRunInstruction } = useRequestRunInstruction({
1787
- documentOnChange,
1788
- isDocAssistable: documentIsAssistable
1789
- }), typePath = useTypePath(docValue, pathKey ?? ""), selectedField = useSelectedField(schemaType, typePath), aiDocId = assistDocumentId(documentType), assistDocument = useStudioAssistDocument({ documentId, schemaType, initDoc: !0 }), instruction2 = assistDocument?.fields?.find((f) => f.path === typePath)?.instructions?.find((i) => i._key === instructionKey), tasks = useMemo(
1790
- () => assistDocument?.tasks?.filter((i) => !instructionKey || i.instructionKey === instructionKey),
1791
- [assistDocument?.tasks, instructionKey]
1792
- ), instructions = useMemo(
1793
- () => assistDocument?.fields?.flatMap((f) => f.instructions ?? []),
1794
- [assistDocument?.fields]
1795
- ), promptValue = instruction2?.prompt, isEmptyPrompt = useMemo(() => {
1796
- if (!promptValue?.length)
1797
- return !0;
1798
- const children = promptValue[0]?.children;
1799
- return promptValue.length == 1 && children?.length === 1 && !children?.[0]?.text?.length;
1800
- }, [promptValue]), paneNode = useMemo(
1801
- () => ({
1802
- type: "document",
1803
- id: aiDocId,
1804
- title: pluginTitle,
1805
- options: {
1806
- id: aiDocId,
1807
- type: assistDocumentTypeName
1808
- }
1809
- }),
1810
- [aiDocId]
1811
- ), runCurrentInstruction = useCallback(
1812
- () => instruction2 && pathKey && typePath && requestRunInstruction({
1813
- documentId: assistableDocumentId,
1814
- path: pathKey,
1815
- typePath,
1816
- assistDocumentId: assistDocumentId(documentType),
1817
- instruction: instruction2,
1818
- conditionalMembers: formStateRef.current ? getConditionalMembers(formStateRef.current) : []
1819
- }),
1820
- [pathKey, instruction2, typePath, documentType, assistableDocumentId, requestRunInstruction]
1821
- ), Region = useCallback((_props) => /* @__PURE__ */ jsx("div", { ..._props, style: { height: "100%", flex: 1, overflow: "auto" } }), []), assistTypeContext = useMemo(() => ({ typePath, documentType }), [typePath, documentType]);
1822
- return !documentId || !schemaType || schemaType.jsonType !== "object" ? /* @__PURE__ */ jsx(Card, { flex: 1, padding: 4, children: /* @__PURE__ */ jsx(Text, { children: "Document not ready yet." }) }) : /* @__PURE__ */ jsxs(
1823
- Flex,
1824
- {
1825
- ref: boundary,
1826
- direction: "column",
1827
- height: "fill",
1828
- overflow: "hidden",
1829
- sizing: "border",
1830
- style: { lineHeight: 0 },
1831
- children: [
1832
- /* @__PURE__ */ jsx(
1833
- AiInspectorHeader,
1834
- {
1835
- onClose: props.onClose,
1836
- field: selectedField,
1837
- fieldTitle: getFieldTitle(selectedField)
1838
- }
1839
- ),
1840
- /* @__PURE__ */ jsx(Card, { as: Region, flex: 1, overflow: "auto", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", style: { minHeight: "100%" }, children: [
1841
- /* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsx(PresenceOverlay, { children: /* @__PURE__ */ jsx(Box, { padding: 4, children: selectedField && /* @__PURE__ */ jsx(AssistTypeContext.Provider, { value: assistTypeContext, children: /* @__PURE__ */ jsx(
1842
- VirtualizerScrollInstanceProvider,
1843
- {
1844
- scrollElement: boundary.current,
1845
- containerElement: boundary,
1846
- children: /* @__PURE__ */ jsx(
1847
- DocumentPaneProvider,
1848
- {
1849
- paneKey: documentPane.paneKey,
1850
- index: documentPane.index,
1851
- itemId: "ai",
1852
- pane: paneNode,
1853
- forcedVersion: {
1854
- isReleaseLocked: !1,
1855
- selectedPerspectiveName: "published",
1856
- selectedReleaseId: void 0
1857
- },
1858
- children: /* @__PURE__ */ jsx(DocumentForm, {})
1859
- }
1860
- )
1861
- }
1862
- ) }) }) }) }),
1863
- /* @__PURE__ */ jsx(Box, { flex: "none", padding: 4, children: /* @__PURE__ */ jsxs(Text, { muted: !0, size: 1, children: [
1864
- "How is Sanity AI Assist working for you?",
1865
- " ",
1866
- /* @__PURE__ */ jsxs(
1867
- "a",
1868
- {
1869
- href: giveFeedbackUrl,
1870
- target: "_blank",
1871
- rel: "noreferrer",
1872
- style: { whiteSpace: "nowrap" },
1873
- children: [
1874
- "Let us know ",
1875
- /* @__PURE__ */ jsx(ArrowRightIcon, {})
1876
- ]
1877
- }
1878
- )
1879
- ] }) })
1880
- ] }) }),
1881
- /* @__PURE__ */ jsx(CardWithShadowAbove, { flex: "none", paddingX: 4, paddingY: 3, style: { justifySelf: "flex-end" }, children: /* @__PURE__ */ jsxs(Flex, { gap: 2, flex: 1, justify: "flex-end", children: [
1882
- schemaType?.name && pathKey && instructionKey && /* @__PURE__ */ jsx(Stack, { flex: 1, children: /* @__PURE__ */ jsx(
1883
- Button,
1884
- {
1885
- mode: "ghost",
1886
- disabled: isEmptyPrompt || instructionLoading,
1887
- fontSize: 1,
1888
- icon: instructionLoading ? /* @__PURE__ */ jsx(Spinner, { style: { marginTop: 3 } }) : PlayIcon,
1889
- onClick: runCurrentInstruction,
1890
- padding: 3,
1891
- text: "Run instruction"
1892
- }
1893
- ) }),
1894
- /* @__PURE__ */ jsx(
1895
- InstructionTaskHistoryButton,
1896
- {
1897
- documentId: assistableDocumentId,
1898
- tasks,
1899
- instructions,
1900
- showTitles: !instructionKey
1901
- }
1902
- )
1903
- ] }) })
1904
- ]
1905
- }
1906
- );
1907
- }
1908
- function AiInspectorHeader(props) {
1909
- const { onClose, field, fieldTitle } = props, { showOnboarding, dismissOnboarding } = useOnboardingFeature(inspectorOnboardingKey);
1910
- return /* @__PURE__ */ jsxs(CardWithShadowBelow, { flex: "none", padding: 2, children: [
1911
- /* @__PURE__ */ jsxs(Flex, { flex: 1, align: "center", children: [
1912
- /* @__PURE__ */ jsx(Flex, { flex: 1, padding: 3, gap: 2, align: "center", children: /* @__PURE__ */ jsxs(Flex, { gap: 1, align: "center", wrap: "wrap", style: { marginTop: "-4px" }, children: [
1913
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: "AI instructions for" }) }),
1914
- /* @__PURE__ */ jsx(Card, { radius: 2, border: !0, padding: 1, marginTop: 1, children: field ? /* @__PURE__ */ jsx(FieldTitle, { field }) : /* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: fieldTitle }) })
1915
- ] }) }),
1916
- /* @__PURE__ */ jsx(Box, { flex: "none", children: /* @__PURE__ */ jsx(Button, { fontSize: 1, icon: CloseIcon, mode: "bleed", onClick: onClose }) })
1917
- ] }),
1918
- showOnboarding && /* @__PURE__ */ jsx(InspectorOnboarding, { onDismiss: dismissOnboarding })
1919
- ] });
1920
- }
1921
- const aiInspectorId = "ai-assistance", assistInspector = {
1922
- name: aiInspectorId,
1923
- useMenuItem: () => ({
1924
- icon: SparklesIcon,
1925
- title: pluginTitle,
1926
- hidden: !0,
1927
- showAsAction: !1
1928
- }),
1929
- component: AssistInspectorWrapper,
1930
- onClose({ params }) {
1931
- return {
1932
- params: typed({
1933
- ...params,
1934
- [fieldPathParam]: void 0,
1935
- [instructionParam]: void 0
1936
- })
1937
- };
1938
- }
1939
- };
1940
- function arrowPath(options2, x, y, dir) {
1941
- return [
1942
- `M ${x - options2.arrow.size} ${y - options2.arrow.size * dir} `,
1943
- `L ${x} ${y}`,
1944
- `L ${x + options2.arrow.size} ${y - options2.arrow.size * dir}`
1945
- ].join("");
1946
- }
1947
- function moveTo(x, y) {
1948
- return `M${x} ${y}`;
1949
- }
1950
- function lineTo(x, y) {
1951
- return `L${x} ${y}`;
1952
- }
1953
- function join(strings, delim = "") {
1954
- return strings.join(delim);
1955
- }
1956
- function quadCurve(x1, y1, x, y) {
1957
- return `Q${x1} ${y1} ${x} ${y}`;
1958
- }
1959
- function drawConnectorPath(options2, line) {
1960
- const { cornerRadius } = options2.path, { from, to } = line, { x: fromX, y: fromY } = from, { x: _toX, y: toY } = to, toX = _toX - 1, dividerX = to.bounds.x + options2.divider.offsetX, fromPathX = from.isAbove || from.isBelow ? fromX + options2.arrow.marginX : fromX, r0 = Math.min(cornerRadius, Math.abs(fromPathX - dividerX) / 2), r1 = Math.min(cornerRadius, Math.abs(fromY - toY) / 2), cmds = [];
1961
- return from.isAbove ? cmds.push(
1962
- moveTo(
1963
- fromX + options2.arrow.marginX,
1964
- fromY - options2.arrow.threshold + options2.arrow.marginY
1965
- ),
1966
- lineTo(fromX + options2.arrow.marginX, fromY - r0),
1967
- quadCurve(fromX + options2.arrow.marginX, fromY, fromX + options2.arrow.marginX + r0, fromY)
1968
- ) : from.isBelow ? cmds.push(
1969
- moveTo(
1970
- fromX + options2.arrow.marginX,
1971
- fromY + options2.arrow.threshold - options2.arrow.marginY
1972
- ),
1973
- lineTo(fromX + options2.arrow.marginX, fromY + r0),
1974
- quadCurve(fromX + options2.arrow.marginX, fromY, fromX + options2.arrow.marginX + r0, fromY)
1975
- ) : cmds.push(moveTo(fromX, fromY)), to.isAbove ? fromY < to.bounds.y ? cmds.push(
1976
- lineTo(dividerX - r1, fromY),
1977
- quadCurve(dividerX, fromY, dividerX, fromY + r1),
1978
- lineTo(dividerX, toY - r1),
1979
- quadCurve(dividerX, toY, dividerX + r1, toY),
1980
- lineTo(dividerX - cornerRadius, toY),
1981
- quadCurve(dividerX, toY, dividerX, toY - cornerRadius),
1982
- lineTo(dividerX, toY - options2.arrow.threshold + options2.arrow.marginY)
1983
- ) : cmds.push(
1984
- lineTo(dividerX - cornerRadius, fromY),
1985
- quadCurve(dividerX, fromY, dividerX, fromY - cornerRadius),
1986
- lineTo(dividerX, toY - options2.arrow.threshold + options2.arrow.marginY)
1987
- ) : to.isBelow ? fromY > to.bounds.y + to.bounds.h ? cmds.push(
1988
- lineTo(dividerX - options2.arrow.marginX - r1, fromY),
1989
- quadCurve(
1990
- dividerX - options2.arrow.marginX,
1991
- fromY,
1992
- dividerX - options2.arrow.marginX,
1993
- fromY - r1
1994
- ),
1995
- lineTo(dividerX - options2.arrow.marginX, toY + r1),
1996
- quadCurve(
1997
- dividerX - options2.arrow.marginX,
1998
- toY,
1999
- dividerX - options2.arrow.marginX + r1,
2000
- toY
2001
- ),
2002
- lineTo(dividerX - cornerRadius, toY),
2003
- quadCurve(dividerX, toY, dividerX, toY + cornerRadius),
2004
- lineTo(dividerX, toY + options2.arrow.threshold - options2.arrow.marginY)
2005
- ) : cmds.push(
2006
- lineTo(dividerX - cornerRadius, fromY),
2007
- quadCurve(dividerX, fromY, dividerX, fromY + cornerRadius),
2008
- lineTo(dividerX, toY + options2.arrow.threshold - options2.arrow.marginY)
2009
- ) : fromY < toY ? cmds.push(
2010
- lineTo(dividerX - r0, fromY),
2011
- quadCurve(dividerX, fromY, dividerX, fromY + r1),
2012
- lineTo(dividerX, toY - r1),
2013
- quadCurve(dividerX, toY, dividerX + r1, toY),
2014
- lineTo(toX, toY)
2015
- ) : cmds.push(
2016
- lineTo(dividerX - Math.min(r0, r1), fromY),
2017
- quadCurve(dividerX, fromY, dividerX, fromY - Math.min(r0, r1)),
2018
- lineTo(dividerX, toY + r1),
2019
- quadCurve(dividerX, toY, dividerX + r1, toY),
2020
- lineTo(toX, toY)
2021
- ), join(cmds);
2022
- }
2023
- function ConnectorPath(props) {
2024
- const { from, options: options2, to } = props, { strokeWidth } = options2.path, theme = useTheme(), line = useMemo(() => mapConnectorToLine(options2, { from, to }), [from, options2, to]);
2025
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2026
- /* @__PURE__ */ jsx(
2027
- "path",
2028
- {
2029
- d: drawConnectorPath(options2, line),
2030
- stroke: theme.sanity.color.base.bg,
2031
- strokeWidth: strokeWidth + 4
2032
- }
2033
- ),
2034
- /* @__PURE__ */ jsx(
2035
- "path",
2036
- {
2037
- d: drawConnectorPath(options2, line),
2038
- stroke: rgba(theme.sanity.color.base.border, 0.5),
2039
- strokeWidth
2040
- }
2041
- ),
2042
- line.from.isAbove && /* @__PURE__ */ jsx(
2043
- "path",
2044
- {
2045
- d: arrowPath(
2046
- options2,
2047
- line.from.x + options2.arrow.marginX,
2048
- line.from.bounds.y - options2.arrow.threshold + options2.arrow.marginY,
2049
- -1
2050
- ),
2051
- stroke: theme.sanity.color.base.border,
2052
- strokeWidth
2053
- }
2054
- ),
2055
- line.from.isBelow && /* @__PURE__ */ jsx(
2056
- "path",
2057
- {
2058
- d: arrowPath(
2059
- options2,
2060
- line.from.x + options2.arrow.marginX,
2061
- line.from.bounds.y + line.from.bounds.h + options2.arrow.threshold - options2.arrow.marginY,
2062
- 1
2063
- ),
2064
- stroke: theme.sanity.color.base.border,
2065
- strokeWidth
2066
- }
2067
- )
2068
- ] });
2069
- }
2070
- const DEBUG = !1, options = {
2071
- arrow: {
2072
- marginX: 10.5,
2073
- marginY: 5,
2074
- size: 4,
2075
- threshold: 16.5
2076
- },
2077
- divider: {
2078
- offsetX: -10.5
2079
- },
2080
- path: {
2081
- cornerRadius: 3,
2082
- marginY: 10.5,
2083
- strokeWidth: 1
2084
- }
2085
- };
2086
- function AssistConnectorsOverlay(props) {
2087
- const { connectors } = props, [, setRedraw] = useState(!1);
2088
- return useEffect(() => {
2089
- setRedraw(!0);
2090
- }, []), /* @__PURE__ */ jsxs(Fragment, { children: [
2091
- /* @__PURE__ */ jsx(
2092
- "svg",
2093
- {
2094
- fill: "none",
2095
- width: window.innerWidth,
2096
- height: window.innerHeight,
2097
- style: {
2098
- position: "absolute",
2099
- top: 0,
2100
- left: 0,
2101
- width: "100%",
2102
- height: "100%",
2103
- pointerEvents: "none",
2104
- zIndex: 150
2105
- // zIndex,
2106
- },
2107
- children: connectors.map((connector) => /* @__PURE__ */ jsx(
2108
- ConnectorPath,
2109
- {
2110
- from: connector.from,
2111
- options,
2112
- to: connector.to
2113
- },
2114
- connector.key
2115
- ))
2116
- }
2117
- ),
2118
- DEBUG
2119
- ] });
2120
- }
2121
- function validateStyleguide(styleguide) {
2122
- if (styleguide && styleguide.length > 2e3)
2123
- throw new Error(
2124
- `[${packageName}]: \`translate.styleguide\` value is too long. It must be 2000 characters or less, but was ${styleguide.length} characters`
2125
- );
2126
- return styleguide;
2127
- }
2128
- function createStyleGuideResolver(styleguide, context) {
2129
- return async () => {
2130
- if (typeof styleguide != "function")
2131
- return styleguide;
2132
- const styleguideResult = await styleguide(context);
2133
- return validateStyleguide(styleguideResult);
2134
- };
2135
- }
2136
- const getLanguageParams = (select, document2) => {
2137
- if (!select || !document2)
2138
- return {};
2139
- const selection = select || {}, selectedValue = {};
2140
- for (const [key, path] of Object.entries(selection)) {
2141
- let value = get(document2, path);
2142
- Array.isArray(value) && (value = value.filter(
2143
- (item) => typeof item == "object" ? item?._type !== "reference" || "_ref" in item : !0
2144
- )), selectedValue[key] = value;
2145
- }
2146
- return selectedValue;
2147
- }, toFieldLanguagesKeyPrefix = "sanityStudio:assist:field-languages:from:";
2148
- function getPreferredToFieldLanguages(fromLanguageId) {
2149
- if (typeof localStorage > "u")
2150
- return [];
2151
- const value = localStorage.getItem(`${toFieldLanguagesKeyPrefix}${fromLanguageId}`);
2152
- return value ? JSON.parse(value) : [];
2153
- }
2154
- function setPreferredToFieldLanguages(fromLanguageId, languageIds) {
2155
- typeof localStorage > "u" || localStorage.setItem(`${toFieldLanguagesKeyPrefix}${fromLanguageId}`, JSON.stringify(languageIds));
2156
- }
2157
- const DEFAULT_MAX_DEPTH = 6, ABSOLUTE_MAX_DEPTH = 50;
2158
- function getDocumentMembersFlat(doc, schemaType, maxDepth2 = DEFAULT_MAX_DEPTH) {
2159
- return isDocumentSchemaType(schemaType) ? extractPaths(doc, schemaType, [], Math.min(maxDepth2, ABSOLUTE_MAX_DEPTH)) : (console.error("Schema type is not a document"), []);
2160
- }
2161
- function extractPaths(doc, schemaType, path, maxDepth2) {
2162
- return path.length >= maxDepth2 ? [] : schemaType.fields.reduce((acc, field) => {
2163
- const fieldPath = [...path, field.name], fieldSchema = field.type, { value } = extractWithPath(pathToString(fieldPath), doc)[0] ?? {};
2164
- if (!value)
2165
- return acc;
2166
- const thisFieldWithPath = {
2167
- path: fieldPath,
2168
- name: field.name,
2169
- schemaType: fieldSchema,
2170
- value
2171
- };
2172
- if (fieldSchema.jsonType === "object") {
2173
- const innerFields = extractPaths(doc, fieldSchema, fieldPath, maxDepth2);
2174
- return [...acc, thisFieldWithPath, ...innerFields];
2175
- } else if (fieldSchema.jsonType === "array" && fieldSchema.of.length && fieldSchema.of.some((item) => "fields" in item) && // no reason to drill into arrays if the item fields will be culled by maxDepth, ie we need 1 extra path headroom
2176
- path.length + 1 < maxDepth2) {
2177
- const { value: arrayValue } = extractWithPath(pathToString(fieldPath), doc)[0] ?? {};
2178
- let arrayPaths = [];
2179
- if (arrayValue?.length)
2180
- for (const item of arrayValue) {
2181
- const itemPath = [...fieldPath, { _key: item._key }];
2182
- let itemSchema = fieldSchema.of.find((t) => t.name === item._type);
2183
- if (item._type || (itemSchema = fieldSchema.of[0], console.warn(
2184
- "Array item is missing _type - using the first defined type in the array.of schema",
2185
- {
2186
- itemPath,
2187
- item,
2188
- itemSchema
2189
- }
2190
- )), item._key && itemSchema) {
2191
- const innerFields = extractPaths(
2192
- doc,
2193
- itemSchema,
2194
- itemPath,
2195
- maxDepth2
2196
- ), arrayMember = {
2197
- path: itemPath,
2198
- name: item._key,
2199
- schemaType: itemSchema,
2200
- value: item
2201
- };
2202
- arrayPaths = [...arrayPaths, arrayMember, ...innerFields];
2203
- }
2204
- }
2205
- return [...acc, thisFieldWithPath, ...arrayPaths];
2206
- }
2207
- return [...acc, thisFieldWithPath];
2208
- }, []);
2209
- }
2210
- const defaultLanguageOutputs = function(member, enclosingType, translateFromLanguageId, translateToLanguageIds) {
2211
- if (member.schemaType.jsonType === "object" && member.schemaType.name.startsWith("internationalizedArray")) {
2212
- const pathEnd = member.path.slice(-1);
2213
- return (isKeySegment(pathEnd[0]) ? pathEnd[0]._key : null) === translateFromLanguageId ? translateToLanguageIds.map((translateToId) => ({
2214
- id: translateToId,
2215
- outputPath: [...member.path.slice(0, -1), { _key: translateToId }]
2216
- })) : void 0;
2217
- }
2218
- if (enclosingType.jsonType === "object" && enclosingType.name.startsWith("locale"))
2219
- return translateFromLanguageId === member.name ? translateToLanguageIds.map((translateToId) => ({
2220
- id: translateToId,
2221
- outputPath: [...member.path.slice(0, -1), translateToId]
2222
- })) : void 0;
2223
- };
2224
- function getFieldLanguageMap(documentSchema, documentMembers, translateFromLanguageId, outputLanguageIds, langFn) {
2225
- const translationMaps = [];
2226
- for (const member of documentMembers) {
2227
- const parentPath = member.path.slice(0, -1), enclosingType = documentMembers.find((m) => pathToString(m.path) === pathToString(parentPath))?.schemaType ?? documentSchema, translations = langFn(
2228
- member,
2229
- enclosingType,
2230
- translateFromLanguageId,
2231
- outputLanguageIds
2232
- )?.filter((translation) => translation.id !== translateFromLanguageId);
2233
- translations && translationMaps.push({
2234
- inputLanguageId: translateFromLanguageId,
2235
- inputPath: member.path,
2236
- outputs: translations
2237
- });
2238
- }
2239
- return translationMaps;
2240
- }
2241
- const FieldTranslationContext = createContext({
2242
- openFieldTranslation: () => {
2243
- },
2244
- translationLoading: !1
2245
- });
2246
- function useFieldTranslation() {
2247
- return useContext(FieldTranslationContext);
2248
- }
2249
- function hasValuesToTranslate(fieldLanguageMaps, fromLanguage, basePath2) {
2250
- return fieldLanguageMaps?.some(
2251
- (map) => map.inputLanguageId === fromLanguage?.id && map.inputPath && pathToString(map.inputPath).startsWith(pathToString(basePath2))
2252
- );
2253
- }
2254
- function FieldTranslationProvider(props) {
2255
- const { config: assistConfig } = useAiAssistanceConfig(), apiClient = useApiClient(assistConfig.__customApiClient), styleguide = assistConfig.translate?.styleguide, config = assistConfig.translate?.field, { translate: runTranslate } = useTranslate(apiClient), [dialogOpen, setDialogOpen] = useState(!1), [fieldTranslationParams, setFieldTranslationParams] = useState(), [languages, setLanguages] = useState(), [fromLanguage, setFromLanguage] = useState(void 0), [toLanguages, setToLanguages] = useState(void 0), [fieldLanguageMaps, setFieldLanguageMaps] = useState(), close = useCallback(() => {
2256
- setDialogOpen(!1), setLanguages(void 0), setFieldTranslationParams(void 0);
2257
- }, []), languageClient = useClient({
2258
- apiVersion: config?.apiVersion ?? API_VERSION_WITH_EXTENDED_TYPES
2259
- }), documentId = fieldTranslationParams?.document?._id, id = useId(), selectFromLanguage = useCallback(
2260
- (from, languages2, params) => {
2261
- const { document: document2, documentSchema } = params ?? {};
2262
- if (setFromLanguage(from), !document2 || !documentSchema || !params || !languages2) {
2263
- setFieldLanguageMaps(void 0);
2264
- return;
2265
- }
2266
- const preferred = getPreferredToFieldLanguages(from.id), allToLanguages = languages2.filter((l) => l.id !== from?.id), filteredToLanguages = allToLanguages.filter(
2267
- (l) => !preferred.length || preferred.includes(l.id)
2268
- );
2269
- setToLanguages(filteredToLanguages);
2270
- const fromId = from?.id, allToIds = allToLanguages?.map((l) => l.id) ?? [], docMembers = getDocumentMembersFlat(document2, documentSchema, config?.maxPathDepth);
2271
- if (fromId && allToIds?.length) {
2272
- const transMap = getFieldLanguageMap(
2273
- documentSchema,
2274
- docMembers,
2275
- fromId,
2276
- allToIds.filter((toId) => fromId !== toId),
2277
- config?.translationOutputs ?? defaultLanguageOutputs
2278
- );
2279
- setFieldLanguageMaps(transMap);
2280
- } else
2281
- setFieldLanguageMaps(void 0);
2282
- },
2283
- [config]
2284
- ), toggleToLanguage = useCallback(
2285
- (toggledLang, toLanguages2, languages2) => {
2286
- if (!languages2 || !fromLanguage)
2287
- return;
2288
- const wasSelected = !!toLanguages2?.find((l) => l.id === toggledLang.id), newToLangs = languages2.filter(
2289
- (anyLang) => !!toLanguages2?.find(
2290
- (selectedLang) => toggledLang.id !== selectedLang.id && selectedLang.id === anyLang.id
2291
- ) || toggledLang.id === anyLang.id && !wasSelected
2292
- );
2293
- setToLanguages(newToLangs), setPreferredToFieldLanguages(
2294
- fromLanguage.id,
2295
- newToLangs.map((l) => l.id)
2296
- );
2297
- },
2298
- [fromLanguage]
2299
- ), openFieldTranslation = useCallback(
2300
- async (params) => {
2301
- setDialogOpen(!0);
2302
- const languageParams = getLanguageParams(config?.selectLanguageParams, params.document), languages2 = await (typeof config?.languages == "function" ? config?.languages(languageClient, languageParams) : Promise.resolve(config?.languages));
2303
- setLanguages(languages2), setFieldTranslationParams(params);
2304
- const fromLanguage2 = languages2?.[0];
2305
- fromLanguage2 ? selectFromLanguage(fromLanguage2, languages2, params) : console.error("No languages available for selected language params", languageParams);
2306
- },
2307
- [selectFromLanguage, config, languageClient]
2308
- ), contextValue = useMemo(() => ({
2309
- openFieldTranslation,
2310
- translationLoading: !1
2311
- }), [openFieldTranslation]), runDisabled = !fromLanguage || !toLanguages?.length || !fieldLanguageMaps?.length || !documentId || !hasValuesToTranslate(fieldLanguageMaps, fromLanguage, fieldTranslationParams.translatePath), onRunTranslation = useCallback(() => {
2312
- const translatePath = fieldTranslationParams?.translatePath;
2313
- fieldLanguageMaps && documentId && translatePath && runTranslate({
2314
- documentId,
2315
- translatePath,
2316
- styleguide: createStyleGuideResolver(styleguide, {
2317
- client: languageClient,
2318
- documentId,
2319
- schemaType: fieldTranslationParams?.documentSchema,
2320
- translatePath
2321
- }),
2322
- fieldLanguageMap: fieldLanguageMaps.map((map) => ({
2323
- ...map,
2324
- // eslint-disable-next-line max-nested-callbacks
2325
- outputs: map.outputs.filter((out) => !!toLanguages?.find((l) => l.id === out.id))
2326
- })),
2327
- conditionalMembers: fieldTranslationParams?.conditionalMembers
2328
- }), close();
2329
- }, [
2330
- fieldLanguageMaps,
2331
- documentId,
2332
- runTranslate,
2333
- styleguide,
2334
- close,
2335
- toLanguages,
2336
- fieldTranslationParams?.translatePath,
2337
- fieldTranslationParams?.conditionalMembers,
2338
- fieldTranslationParams?.documentSchema,
2339
- languageClient
2340
- ]), runButton = /* @__PURE__ */ jsx(
2341
- Button,
2342
- {
2343
- text: "Translate",
2344
- tone: "primary",
2345
- icon: PlayIcon,
2346
- style: { width: "100%" },
2347
- disabled: runDisabled,
2348
- onClick: onRunTranslation
2349
- }
2350
- );
2351
- return /* @__PURE__ */ jsxs(FieldTranslationContext.Provider, { value: contextValue, children: [
2352
- dialogOpen ? /* @__PURE__ */ jsx(
2353
- Dialog,
2354
- {
2355
- id,
2356
- width: 1,
2357
- open: dialogOpen,
2358
- onClose: close,
2359
- header: "Translate fields",
2360
- footer: /* @__PURE__ */ jsx(Flex, { justify: "space-between", padding: 2, flex: 1, children: runDisabled ? /* @__PURE__ */ jsx(
2361
- Tooltip,
2362
- {
2363
- content: /* @__PURE__ */ jsx(Flex, { padding: 2, children: /* @__PURE__ */ jsx(Text, { children: "There is nothing to translate in the selected from-language." }) }),
2364
- placement: "top",
2365
- children: /* @__PURE__ */ jsx(Flex, { flex: 1, children: runButton })
2366
- }
2367
- ) : runButton }),
2368
- children: languages ? /* @__PURE__ */ jsxs(Flex, { padding: 4, gap: 5, align: "flex-start", justify: "center", children: [
2369
- /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
2370
- /* @__PURE__ */ jsx(Box, { marginBottom: 2, children: /* @__PURE__ */ jsx(Text, { weight: "semibold", children: "From" }) }),
2371
- languages?.map((radioLanguage) => /* @__PURE__ */ jsx(
2372
- FromLanguageRadio,
2373
- {
2374
- radioLanguage,
2375
- fromLanguage,
2376
- selectFromLanguage,
2377
- languages,
2378
- fieldTranslationParams
2379
- },
2380
- radioLanguage.id
2381
- ))
2382
- ] }),
2383
- /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
2384
- /* @__PURE__ */ jsx(Box, { marginBottom: 2, children: /* @__PURE__ */ jsx(Text, { weight: "semibold", children: "To" }) }),
2385
- languages.map((checkboxLanguage) => /* @__PURE__ */ jsx(
2386
- ToLanguageCheckbox,
2387
- {
2388
- checkboxLanguage,
2389
- fromLanguage,
2390
- toLanguages,
2391
- toggleToLanguage,
2392
- languages
2393
- },
2394
- checkboxLanguage.id
2395
- ))
2396
- ] })
2397
- ] }) : /* @__PURE__ */ jsxs(Flex, { padding: 4, gap: 2, align: "flex-start", justify: "center", children: [
2398
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Spinner, {}) }),
2399
- /* @__PURE__ */ jsx(Text, { children: "Loading languages..." })
2400
- ] })
2401
- }
2402
- ) : null,
2403
- props.children
2404
- ] });
2405
- }
2406
- function ToLanguageCheckbox(props) {
2407
- const { checkboxLanguage, fromLanguage, toLanguages, toggleToLanguage, languages } = props, langId = checkboxLanguage.id, onChange = useCallback(
2408
- () => toggleToLanguage(checkboxLanguage, toLanguages, languages),
2409
- [toggleToLanguage, checkboxLanguage, toLanguages, languages]
2410
- );
2411
- return /* @__PURE__ */ jsxs(
2412
- Flex,
2413
- {
2414
- gap: 3,
2415
- align: "center",
2416
- as: "label",
2417
- style: langId === fromLanguage?.id ? { opacity: 0.5 } : void 0,
2418
- children: [
2419
- /* @__PURE__ */ jsx(
2420
- Checkbox,
2421
- {
2422
- name: "toLang",
2423
- value: langId,
2424
- checked: langId !== fromLanguage?.id && !!toLanguages?.find((tl) => tl.id === langId),
2425
- onChange,
2426
- disabled: langId === fromLanguage?.id
2427
- }
2428
- ),
2429
- /* @__PURE__ */ jsx(Text, { muted: langId === fromLanguage?.id, children: checkboxLanguage.title ?? langId })
2430
- ]
2431
- },
2432
- langId
2433
- );
2434
- }
2435
- function FromLanguageRadio(props) {
2436
- const { languages, radioLanguage, selectFromLanguage, fromLanguage, fieldTranslationParams } = props, langId = radioLanguage.id, onChange = useCallback(
2437
- () => selectFromLanguage(radioLanguage, languages, fieldTranslationParams),
2438
- [selectFromLanguage, radioLanguage, languages, fieldTranslationParams]
2439
- );
2440
- return /* @__PURE__ */ jsxs(Flex, { gap: 3, align: "center", as: "label", children: [
2441
- /* @__PURE__ */ jsx(
2442
- Radio,
2443
- {
2444
- name: "fromLang",
2445
- value: langId,
2446
- checked: langId === fromLanguage?.id,
2447
- onChange
2448
- }
2449
- ),
2450
- /* @__PURE__ */ jsx(Text, { children: radioLanguage.title ?? radioLanguage.id })
2451
- ] }, langId);
2452
- }
2453
- const hiddenTypes = [
2454
- "any",
2455
- "array",
2456
- "block",
2457
- "boolean",
2458
- "crossDatasetReference",
2459
- "date",
2460
- "datetime",
2461
- "document",
2462
- "email",
2463
- "file",
2464
- "globalDocumentReference",
2465
- "image",
2466
- "number",
2467
- "object",
2468
- "reference",
2469
- "span",
2470
- "string",
2471
- "text",
2472
- "url",
2473
- "slug",
2474
- "geopoint",
2475
- "sanity.assetSourceData",
2476
- "sanity.imageAsset",
2477
- "sanity.fileAsset",
2478
- "sanity.imageCrop",
2479
- "sanity.imageHotspot",
2480
- "sanity.imageMetadata",
2481
- "sanity.imageDimensions",
2482
- "sanity.imagePalette",
2483
- "sanity.imagePaletteSwatch",
2484
- assistSerializedTypeName,
2485
- assistSerializedFieldTypeName,
2486
- "sanity-agent.job.document"
2487
- ], inlineTypes = ["document", "object", "image", "file"];
2488
- function serializeSchema(schema, options2) {
2489
- const list = schema.getTypeNames().filter((t) => !(hiddenTypes.includes(t) || t.startsWith("sanity."))).map((t) => schema.get(t)).filter((t) => !!t).map((t) => getSchemaStub(t, schema, options2)).filter((t) => !("to" in t && t.to && !t.to.length || "of" in t && t.of && !t.of.length || "fields" in t && t.fields && !t.fields.length));
2490
- return list.sort((a, b) => (a?.name ?? "").localeCompare(b?.name ?? "")), list;
2491
- }
2492
- function getSchemaStub(schemaType, schema, options2) {
2493
- if (!schemaType.type?.name)
2494
- throw console.error("Missing type name", schemaType.type), new Error("Type is missing name!");
2495
- const baseSchema = {
2496
- // we dont need type or id when we send using POST, so leave these out to save bandwidth
2497
- ...options2?.leanFormat ? {} : { _id: `${assistSchemaIdPrefix}${schemaType.name}`, _type: assistSerializedTypeName },
2498
- name: schemaType.name,
2499
- title: schemaType.title,
2500
- type: schemaType.type.name,
2501
- ...getBaseFields(schema, schemaType, schemaType.type.name, options2)
2502
- };
2503
- return removeUndef(baseSchema);
2504
- }
2505
- function getBaseFields(schema, type, typeName, options2) {
2506
- const schemaOptions = removeUndef({
2507
- imagePromptField: type.options?.aiAssist?.imageInstructionField,
2508
- embeddingsIndex: type.options?.aiAssist?.embeddingsIndex
2509
- });
2510
- return removeUndef({
2511
- options: Object.keys(schemaOptions).length ? schemaOptions : void 0,
2512
- values: Array.isArray(type?.options?.list) ? type?.options?.list.map(
2513
- (v) => typeof v == "string" ? v : v.value ?? `${v.title}`
2514
- ) : void 0,
2515
- of: "of" in type && typeName === "array" ? arrayOf(type, schema, options2) : void 0,
2516
- to: "to" in type && typeName === "reference" ? refToTypeNames(type) : void 0,
2517
- fields: "fields" in type && inlineTypes.includes(typeName) ? serializeFields(schema, type, options2) : void 0,
2518
- annotations: typeName === "block" && "fields" in type ? serializeAnnotations(type, schema, options2) : void 0,
2519
- inlineOf: typeName === "block" && "fields" in type ? serializeInlineOf(type, schema, options2) : void 0,
2520
- hidden: typeof type.hidden == "function" ? "function" : type.hidden ? !0 : void 0,
2521
- readOnly: typeof type.readOnly == "function" ? "function" : type.readOnly ? !0 : void 0
2522
- });
2523
- }
2524
- function serializeFields(schema, schemaType, options2) {
2525
- return (schemaType.fieldsets ? schemaType.fieldsets.flatMap(
2526
- (fs) => fs.single ? fs.field : fs.fields.map((f) => ({
2527
- ...f,
2528
- type: {
2529
- ...f.type,
2530
- // if fieldset is (conditionally) hidden, the field must be considered the same way
2531
- // ie, if the field does not show up in conditionalMembers, it is hidden
2532
- // regardless of weather or not it is the field function or the fieldset function that hides it
2533
- hidden: typeof fs.hidden == "function" ? fs.hidden : fs.hidden ? !0 : f.type.hidden
2534
- }
2535
- }))
2536
- ) : schemaType.fields).filter((f) => !["sanity.imageHotspot", "sanity.imageCrop"].includes(f.type?.name ?? "")).filter((f) => isAssistSupported(f.type)).map((field) => serializeMember(schema, field.type, field.name, options2));
2537
- }
2538
- function serializeMember(schema, type, name, options2) {
2539
- const typeName = schema.get(type?.name) ? type.name : type.type?.name ?? "";
2540
- return removeUndef({
2541
- ...options2?.leanFormat ? {} : { _type: assistSerializedFieldTypeName },
2542
- name,
2543
- type: typeName,
2544
- title: type.title,
2545
- ...getBaseFields(schema, type, typeName, options2)
2546
- });
2547
- }
2548
- function serializeInlineOf(blockSchemaType, schema, options2) {
2549
- const childrenType = blockSchemaType.fields.find((f) => f.name === "children")?.type;
2550
- if (!(!childrenType || !isArraySchemaType(childrenType)))
2551
- return arrayOf(
2552
- {
2553
- of: childrenType.of.filter((t) => !isType(t, "span"))
2554
- },
2555
- schema,
2556
- options2
2557
- );
2558
- }
2559
- function serializeAnnotations(blockSchemaType, schema, options2) {
2560
- const marksType = blockSchemaType.fields.find((f) => f.name === "markDefs")?.type;
2561
- if (!(!marksType || !isArraySchemaType(marksType)))
2562
- return arrayOf(marksType, schema, options2);
2563
- }
2564
- function arrayOf(arrayType, schema, options2) {
2565
- return arrayType.of.filter((type) => isAssistSupported(type)).map((t) => serializeMember(schema, t, t.name, options2));
2566
- }
2567
- function refToTypeNames(type) {
2568
- return type.to.map((t) => ({
2569
- type: typed(t.name)
2570
- }));
2571
- }
2572
- function removeUndef(obj) {
2573
- return Object.keys(obj).forEach((key) => obj[key] === void 0 ? delete obj[key] : {}), obj;
2574
- }
2575
- function createFieldRefCache() {
2576
- const byType = {};
2577
- function getRefsForType(schemaType) {
2578
- const documentType = schemaType.name, cached = byType[documentType];
2579
- if (cached) return cached;
2580
- const fieldRefs = getFieldRefs(schemaType), fieldRefsByTypePath = asFieldRefsByTypePath(fieldRefs), refs = {
2581
- fieldRefs,
2582
- fieldRefsByTypePath
2583
- };
2584
- return byType[documentType] = refs, refs;
2585
- }
2586
- return getRefsForType;
2587
- }
2588
- function AiAssistanceConfigProvider(props) {
2589
- const [status, setStatus] = useState(), [error, setError] = useState(), apiClient = useApiClient(props.config?.__customApiClient), { getInstructStatus, loading: statusLoading } = useGetInstructStatus(apiClient), { initInstruct, loading: initLoading } = useInitInstruct(apiClient), schema = useSchema(), serializedTypes = useMemo(() => serializeSchema(schema, { leanFormat: !0 }), [schema]), { getFieldRefs: getFieldRefs2, getFieldRefsByTypePath } = useFieldRefGetters(schema);
2590
- useEffect(() => {
2591
- getInstructStatus().then((s) => setStatus(s)).catch((e) => {
2592
- console.error(e), setError(e);
2593
- });
2594
- }, [getInstructStatus]);
2595
- const init = useCallback(async () => {
2596
- setError(void 0);
2597
- try {
2598
- await initInstruct();
2599
- const status2 = await getInstructStatus();
2600
- setStatus(status2);
2601
- } catch (e) {
2602
- console.error("Failed to init ai assistance", e), setError(e);
2603
- }
2604
- }, [initInstruct, getInstructStatus, setStatus]), { config, children } = props, context = useMemo(() => ({
2605
- config,
2606
- status,
2607
- statusLoading,
2608
- init,
2609
- initLoading,
2610
- error,
2611
- serializedTypes,
2612
- getFieldRefs: getFieldRefs2,
2613
- getFieldRefsByTypePath
2614
- }), [
2615
- config,
2616
- status,
2617
- init,
2618
- statusLoading,
2619
- initLoading,
2620
- error,
2621
- serializedTypes,
2622
- getFieldRefs2,
2623
- getFieldRefsByTypePath
2624
- ]);
2625
- return /* @__PURE__ */ jsx(AiAssistanceConfigContext.Provider, { value: context, children });
2626
- }
2627
- function useFieldRefGetters(schema) {
2628
- return useMemo(() => {
2629
- const getForSchemaType = createFieldRefCache();
2630
- function getRefsForType(documentType) {
2631
- const schemaType = schema.get(documentType);
2632
- if (!schemaType)
2633
- throw new Error(`Schema type "${documentType}" not found`);
2634
- return getForSchemaType(schemaType);
2635
- }
2636
- return {
2637
- getFieldRefs: (documentType) => getRefsForType(documentType).fieldRefs,
2638
- getFieldRefsByTypePath: (documentType) => getRefsForType(documentType).fieldRefsByTypePath
2639
- };
2640
- }, [schema]);
2641
- }
2642
- function AssistLayout(props) {
2643
- const [connectors, setConnectors] = useState([]);
2644
- return /* @__PURE__ */ jsx(AiAssistanceConfigProvider, { config: props.config, children: /* @__PURE__ */ jsx(RunInstructionProvider, { children: /* @__PURE__ */ jsx(FieldTranslationProvider, { children: /* @__PURE__ */ jsxs(ConnectorsProvider, { onConnectorsChange: setConnectors, children: [
2645
- props.renderDefault(props),
2646
- /* @__PURE__ */ jsx(ThemeProvider, { tone: "default", children: /* @__PURE__ */ jsx(AssistConnectorsOverlay, { connectors }) })
2647
- ] }) }) }) });
2648
- }
2649
- const ImageContext = createContext({});
2650
- function ImageContextProvider(props) {
2651
- const { schemaType, path, value, readOnly } = props, assetRef = value?.asset?._ref, { selectedReleaseId } = useDocumentPane(), [assetRefState, setAssetRefState] = useState(assetRef), { assistableDocumentId, documentSchemaType } = useAssistDocumentContext(), { config, status } = useAiAssistanceConfig(), apiClient = useApiClient(config?.__customApiClient), { generateCaption } = useGenerateCaption(apiClient), { isSyncing } = useSyncState(
2652
- getPublishedId(assistableDocumentId),
2653
- documentSchemaType.name,
2654
- selectedReleaseId
2655
- ), isShowingOlderRevision = !!usePaneRouter().params?.rev;
2656
- useEffect(() => {
2657
- const descriptionField = getDescriptionFieldOption(schemaType);
2658
- assetRef && assistableDocumentId && descriptionField?.updateOnImageChange && assetRef !== assetRefState && !isSyncing && !isShowingOlderRevision && !readOnly && (setAssetRefState(assetRef), canUseAssist(status) && generateCaption({
2659
- path: pathToString([...path, descriptionField.path]),
2660
- documentId: assistableDocumentId
2661
- }));
2662
- }, [
2663
- schemaType,
2664
- path,
2665
- assetRef,
2666
- assetRefState,
2667
- assistableDocumentId,
2668
- generateCaption,
2669
- isSyncing,
2670
- status,
2671
- readOnly,
2672
- isShowingOlderRevision
2673
- ]);
2674
- const context = useMemo(() => {
2675
- const descriptionField = getDescriptionFieldOption(schemaType), imageInstructionField = getImageInstructionFieldOption(schemaType);
2676
- return {
2677
- imageDescriptionPath: descriptionField?.path ? pathToString([...path, descriptionField.path]) : void 0,
2678
- imageInstructionPath: imageInstructionField ? pathToString([...path, imageInstructionField]) : void 0,
2679
- assetRef
2680
- };
2681
- }, [schemaType, path, assetRef]);
2682
- return /* @__PURE__ */ jsx(ImageContext.Provider, { value: context, children: props.renderDefault(props) });
2683
- }
2684
- function IconInput(props) {
2685
- const { value, onChange } = props, id = useId(), items = useMemo(
2686
- () => Object.entries(icons).map(([key, icon]) => /* @__PURE__ */ jsx(IconItem, { iconKey: key, icon, onChange }, key)),
2687
- [onChange]
2688
- ), selectedIcon = useMemo(() => getIcon(value), [value]);
2689
- return /* @__PURE__ */ jsx(
2690
- MenuButton,
2691
- {
2692
- button: /* @__PURE__ */ jsx(Button, { icon: selectedIcon, title: "Select icon", padding: 3, mode: "ghost", radius: 1 }),
2693
- id,
2694
- menu: /* @__PURE__ */ jsx(Menu, { style: { maxHeight: 300 }, children: items }),
2695
- popover: { portal: !0 }
2696
- }
2697
- );
2698
- }
2699
- function IconItem({
2700
- icon,
2701
- iconKey: key,
2702
- onChange
2703
- }) {
2704
- const onClick = useCallback(() => onChange(set(key)), [onChange, key]);
2705
- return /* @__PURE__ */ jsx(MenuItem, { icon, title: key, text: key, onClick });
2706
- }
2707
- function getIcon(iconName) {
2708
- return Object.entries(icons).find(([key]) => key === iconName)?.[1] ?? icons.sparkles;
2709
- }
2710
- function useAssistSupported(path, schemaType) {
2711
- return useMemo(() => isAssistSupported(schemaType), [schemaType]);
2712
- }
2713
- const translateActions = {
2714
- name: "sanity-assist-translate",
2715
- useAction(props) {
2716
- const { config, status } = useAiAssistanceConfig(), apiClient = useApiClient(config?.__customApiClient), client = useClient({ apiVersion: API_VERSION_WITH_EXTENDED_TYPES }), {
2717
- schemaType: fieldSchemaType,
2718
- path,
2719
- documentId,
2720
- documentSchemaType,
2721
- documentIsAssistable
2722
- } = props, isDocumentLevel = path.length === 0, readOnly = fieldSchemaType.readOnly === !0, docTransTypes = config.translate?.document?.documentTypes, options2 = fieldSchemaType?.options, addFieldAction = isDocumentLevel || options2?.aiAssist?.translateAction, fieldTransEnabled = addFieldAction && documentSchemaType && config.translate?.field?.documentTypes?.includes(documentSchemaType.name), documentTranslationEnabled = addFieldAction && documentSchemaType && (!docTransTypes && isAssistSupported(fieldSchemaType) || docTransTypes?.includes(documentSchemaType.name));
2723
- if (documentSchemaType && (documentTranslationEnabled || fieldTransEnabled)) {
2724
- const { value: documentValue, onChange: documentOnChange, formState } = useDocumentPane(), docRef = useRef(documentValue);
2725
- docRef.current = documentValue;
2726
- const formStateRef = useRef(formState);
2727
- formStateRef.current = formState;
2728
- const translationApi = useTranslate(apiClient), translate = useDraftDelayedTask({
2729
- documentOnChange,
2730
- isDocAssistable: documentIsAssistable ?? !1,
2731
- task: translationApi.translate
2732
- }), styleguide = config.translate?.styleguide, languagePath = config.translate?.document?.languageField, translateDocumentAction = useMemo(() => {
2733
- if (!languagePath || !documentTranslationEnabled)
2734
- return;
2735
- const title = path.length ? "Translate" : "Translate document";
2736
- return {
2737
- type: "action",
2738
- icon: translationApi.loading ? () => /* @__PURE__ */ jsx(Box, { style: { height: 17 }, children: /* @__PURE__ */ jsx(Spinner, { style: { transform: "translateY(6px)" } }) }) : TranslateIcon,
2739
- title,
2740
- onAction: () => {
2741
- translationApi.loading || !languagePath || !documentId || translate({
2742
- languagePath,
2743
- translatePath: path,
2744
- styleguide: createStyleGuideResolver(styleguide, {
2745
- client,
2746
- documentId,
2747
- schemaType: documentSchemaType
2748
- }),
2749
- documentId: documentId ?? "",
2750
- conditionalMembers: formStateRef.current ? getConditionalMembers(formStateRef.current) : []
2751
- });
2752
- },
2753
- renderAsButton: !0,
2754
- disabled: translationApi.loading || readOnly
2755
- };
2756
- }, [
2757
- languagePath,
2758
- translate,
2759
- styleguide,
2760
- documentId,
2761
- translationApi.loading,
2762
- documentTranslationEnabled,
2763
- path,
2764
- readOnly,
2765
- client,
2766
- documentSchemaType
2767
- ]), fieldTranslate = useFieldTranslation(), openFieldTranslation = useDraftDelayedTask({
2768
- documentOnChange,
2769
- isDocAssistable: documentIsAssistable ?? !1,
2770
- task: fieldTranslate.openFieldTranslation
2771
- }), maxDepth2 = config.translate?.field?.maxPathDepth, translateFieldsAction = useMemo(
2772
- () => fieldTransEnabled ? {
2773
- type: "action",
2774
- icon: fieldTranslate.translationLoading ? () => /* @__PURE__ */ jsx(Box, { style: { height: 17 }, children: /* @__PURE__ */ jsx(Spinner, { style: { transform: "translateY(6px)" } }) }) : TranslateIcon,
2775
- title: "Translate fields...",
2776
- onAction: () => {
2777
- fieldTranslate.translationLoading || !documentId || (formStateRef.current && getConditionalMembers(formStateRef.current), openFieldTranslation({
2778
- document: {
2779
- ...docRef.current,
2780
- _id: documentId
2781
- },
2782
- documentSchema: documentSchemaType,
2783
- translatePath: path,
2784
- conditionalMembers: formStateRef.current ? getConditionalMembers(formStateRef.current, maxDepth2) : []
2785
- }));
2786
- },
2787
- renderAsButton: !0,
2788
- disabled: fieldTranslate.translationLoading || readOnly
2789
- } : void 0,
2790
- [
2791
- openFieldTranslation,
2792
- documentSchemaType,
2793
- documentId,
2794
- fieldTranslate.translationLoading,
2795
- fieldTransEnabled,
2796
- path,
2797
- readOnly,
2798
- maxDepth2
2799
- ]
2800
- );
2801
- return useMemo(() => {
2802
- if (status?.initialized)
2803
- return {
2804
- type: "group",
2805
- icon: () => null,
2806
- title: "Translation",
2807
- children: [translateDocumentAction, translateFieldsAction].filter(
2808
- (c) => !!c
2809
- ),
2810
- expanded: !0
2811
- };
2812
- }, [translateDocumentAction, translateFieldsAction, status]);
2813
- }
2814
- }
2815
- }, generateCaptionsActions = {
2816
- name: "sanity-assist-generate-captions",
2817
- useAction(props) {
2818
- const pathKey = usePathKey(props.path), { openInspector } = useDocumentPane(), { config, status } = useAiAssistanceConfig(), apiClient = useApiClient(config?.__customApiClient), { generateCaption, loading } = useGenerateCaption(apiClient), imageContext = useContext(ImageContext);
2819
- if (imageContext && pathKey === imageContext?.imageDescriptionPath) {
2820
- const { assistableDocumentId } = useAssistDocumentContext();
2821
- return useMemo(() => ({
2822
- type: "action",
2823
- icon: loading ? () => /* @__PURE__ */ jsx(Box, { style: { height: 17 }, children: /* @__PURE__ */ jsx(Spinner, { style: { transform: "translateY(6px)" } }) }) : ImageIcon,
2824
- title: "Generate image description",
2825
- onAction: () => {
2826
- if (!loading) {
2827
- if (!canUseAssist(status)) {
2828
- openInspector(aiInspectorId, {
2829
- [fieldPathParam]: pathKey,
2830
- [instructionParam]: void 0
2831
- });
2832
- return;
2833
- }
2834
- generateCaption({ path: pathKey, documentId: assistableDocumentId });
2835
- }
2836
- },
2837
- renderAsButton: !0,
2838
- disabled: loading,
2839
- hidden: !imageContext.assetRef
2840
- }), [
2841
- generateCaption,
2842
- pathKey,
2843
- assistableDocumentId,
2844
- loading,
2845
- imageContext,
2846
- status,
2847
- openInspector
2848
- ]);
2849
- }
2850
- }
2851
- }, generateImagActions = {
2852
- name: "sanity-assist-generate-image",
2853
- useAction(props) {
2854
- const pathKey = usePathKey(props.path), { config } = useAiAssistanceConfig(), apiClient = useApiClient(config?.__customApiClient), { generateImage, loading } = useGenerateImage(apiClient), imageContext = useContext(ImageContext);
2855
- if (imageContext && pathKey === imageContext?.imageInstructionPath) {
2856
- const { assistableDocumentId } = useAssistDocumentContext();
2857
- return useMemo(() => ({
2858
- type: "action",
2859
- icon: loading ? () => /* @__PURE__ */ jsx(Box, { style: { height: 17 }, children: /* @__PURE__ */ jsx(Spinner, { style: { transform: "translateY(6px)" } }) }) : ImageIcon,
2860
- title: "Generate image from prompt",
2861
- onAction: () => {
2862
- loading || generateImage({ path: pathKey, documentId: assistableDocumentId });
2863
- },
2864
- renderAsButton: !0,
2865
- disabled: loading
2866
- }), [generateImage, pathKey, assistableDocumentId, loading]);
2867
- }
2868
- }
2869
- };
2870
- function PrivateIcon() {
2871
- return /* @__PURE__ */ jsx(
2872
- Tooltip,
2873
- {
2874
- content: /* @__PURE__ */ jsx(Text, { size: 1, style: { whiteSpace: "nowrap" }, children: "Only visible to you" }),
2875
- fallbackPlacements: ["bottom"],
2876
- padding: 2,
2877
- placement: "top",
2878
- portal: !0,
2879
- children: /* @__PURE__ */ jsx(LockIcon, {})
2880
- }
2881
- );
2882
- }
2883
- function getRandomValues(typedArray) {
2884
- const crypto = typeof window < "u" && "crypto" in window ? window.crypto : globalThis.crypto;
2885
- if (!crypto || !crypto.getRandomValues)
2886
- throw new Error("WebCrypto not available in this environment");
2887
- return crypto.getRandomValues(typedArray);
2888
- }
2889
- function whatwgRNG(length = 16) {
2890
- const rnds8 = new Uint8Array(length);
2891
- return getRandomValues(rnds8), rnds8;
2892
- }
2893
- const getByteHexTable = /* @__PURE__ */ (() => {
2894
- let table;
2895
- return () => {
2896
- if (table)
2897
- return table;
2898
- table = [];
2899
- for (let i = 0; i < 256; ++i)
2900
- table[i] = (i + 256).toString(16).substring(1);
2901
- return table;
2902
- };
2903
- })();
2904
- function randomKey(length) {
2905
- const table = getByteHexTable();
2906
- return whatwgRNG(length).reduce((str, n) => str + table[n], "").slice(0, length);
2907
- }
2908
- function defineAssistFieldAction(action) {
2909
- return {
2910
- ...action,
2911
- type: "action"
2912
- };
2913
- }
2914
- function defineFieldActionDivider() {
2915
- return {
2916
- type: "divider"
2917
- };
2918
- }
2919
- function defineAssistFieldActionGroup(group) {
2920
- return {
2921
- ...group,
2922
- type: "group"
2923
- };
2924
- }
2925
- function useCustomFieldActions(props) {
2926
- const {
2927
- config: { fieldActions }
2928
- } = useAiAssistanceConfig(), { addSyntheticTask, removeSyntheticTask } = useAssistDocumentContext(), schemaId = useWorkspaceSchemaId(), { push: pushToast } = useToast(), configActions = fieldActions?.useFieldActions?.({
2929
- ...props,
2930
- schemaId,
2931
- path: props.path
2932
- });
2933
- return useMemo(() => {
2934
- const title = fieldActions?.title, customActions = configActions?.filter(isDefined).map((node) => createSafeNode({
2935
- node,
2936
- pushToast,
2937
- addSyntheticTask,
2938
- removeSyntheticTask
2939
- })).filter(isDefined), onlyGroups = customActions?.length && customActions?.every((node) => node.type === "group");
2940
- return (customActions?.length ? onlyGroups ? customActions : [
2941
- {
2942
- type: "group",
2943
- title: title || "Custom actions",
2944
- children: customActions,
2945
- expanded: !0
2946
- }
2947
- ] : []) ?? [];
2948
- }, [configActions, fieldActions, pushToast]);
2949
- }
2950
- function createSafeNode(args) {
2951
- const { node } = args;
2952
- switch (node.type) {
2953
- case "action":
2954
- return createSafeAction({ ...args, action: node });
2955
- case "group":
2956
- const children = node.children?.filter(isDefined).map((child) => createSafeNode({ ...args, node: child })).filter(isDefined);
2957
- return children?.length ? {
2958
- ...node,
2959
- renderAsButton: !1,
2960
- expanded: !0,
2961
- children
2962
- } : void 0;
2963
- case "divider":
2964
- default:
2965
- return node;
2966
- }
2967
- }
2968
- function createSafeAction(args) {
2969
- const { action, pushToast, addSyntheticTask, removeSyntheticTask } = args;
2970
- return {
2971
- ...action,
2972
- onAction: () => {
2973
- async function runAction() {
2974
- const task = {
2975
- _type: instructionTaskTypeName,
2976
- _key: randomKey(12),
2977
- started: (/* @__PURE__ */ new Date()).toISOString(),
2978
- presence: [
2979
- {
2980
- _type: fieldPresenceTypeName,
2981
- _key: randomKey(12),
2982
- path: documentRootKey,
2983
- started: (/* @__PURE__ */ new Date()).toISOString()
2984
- }
2985
- ]
2986
- };
2987
- try {
2988
- addSyntheticTask(task);
2989
- const actionResult = action.onAction?.();
2990
- actionResult instanceof Promise && await actionResult;
2991
- } catch (err) {
2992
- console.error("Failed to execute action", action, err), pushToast({
2993
- title: "Failed to execute action",
2994
- description: err?.message,
2995
- status: "error"
2996
- });
2997
- } finally {
2998
- removeSyntheticTask(task);
2999
- }
3000
- }
3001
- runAction();
3002
- },
3003
- renderAsButton: !1,
3004
- selected: !1
3005
- };
3006
- }
3007
- const assistFieldActions = {
3008
- name: "sanity-assist-actions",
3009
- useAction(props) {
3010
- const { schemaType } = props, {
3011
- assistDocument,
3012
- documentIsNew,
3013
- documentIsAssistable,
3014
- openInspector,
3015
- closeInspector,
3016
- inspector,
3017
- documentOnChange,
3018
- documentSchemaType,
3019
- selectedPath,
3020
- assistableDocumentId,
3021
- fieldRefsByTypePath
3022
- } = useAssistDocumentContext(), { value: docValue, formState } = useDocumentPane(), docValueRef = useRef(docValue), formStateRef = useRef(formState);
3023
- formStateRef.current = formState;
3024
- const currentUser = useCurrentUser(), isHidden = !assistDocument, pathKey = usePathKey(props.path), typePath = useTypePath(docValue, pathKey), assistDocumentId2 = assistDocument?._id, { requestRunInstruction } = useRequestRunInstruction({
3025
- documentOnChange,
3026
- isDocAssistable: documentIsAssistable ?? !1
3027
- }), isSelectable = !!useSelectedField(documentSchemaType, typePath), assistSupported = useAssistSupported(props.path, schemaType) && isSelectable && isSchemaAssistEnabled(documentSchemaType) && schemaType.readOnly !== !0, fieldAssist = useMemo(
3028
- () => (assistDocument?.fields ?? []).find(
3029
- (f) => f.path === typePath || pathKey === documentRootKey && f.path === pathKey
3030
- ),
3031
- [assistDocument?.fields, pathKey, typePath]
3032
- ), fieldAssistKey = fieldAssist?._key, isSelected = inspector?.name === aiInspectorId && pathKey === selectedPath, imageCaptionAction = generateCaptionsActions.useAction(props), imageGenAction = generateImagActions.useAction(props), translateAction = translateActions.useAction(
3033
- typed({
3034
- ...props,
3035
- documentId: assistableDocumentId,
3036
- documentIsAssistable,
3037
- documentSchemaType
3038
- })
3039
- ), manageInstructions = useCallback(
3040
- () => isSelected ? closeInspector(aiInspectorId) : openInspector(aiInspectorId, {
3041
- [fieldPathParam]: pathKey,
3042
- [instructionParam]: void 0
3043
- }),
3044
- [openInspector, closeInspector, isSelected, pathKey]
3045
- ), onInstructionAction = useCallback(
3046
- (instruction2) => {
3047
- !pathKey || !fieldAssistKey || !assistDocumentId2 || !assistableDocumentId || requestRunInstruction({
3048
- documentId: assistableDocumentId,
3049
- assistDocumentId: assistDocumentId2,
3050
- path: pathKey,
3051
- typePath,
3052
- instruction: instruction2,
3053
- conditionalMembers: formStateRef.current ? getConditionalMembers(formStateRef.current) : []
3054
- });
3055
- },
3056
- [
3057
- requestRunInstruction,
3058
- assistableDocumentId,
3059
- pathKey,
3060
- typePath,
3061
- assistDocumentId2,
3062
- fieldAssistKey
3063
- ]
3064
- ), privateInstructions = useMemo(
3065
- () => fieldAssist?.instructions?.filter((i) => i.userId && i.userId === currentUser?.id) || [],
3066
- [fieldAssist?.instructions, currentUser]
3067
- ), sharedInstructions = useMemo(
3068
- () => fieldAssist?.instructions?.filter((i) => !i.userId) || [],
3069
- [fieldAssist?.instructions]
3070
- ), instructions = useMemo(
3071
- () => [...privateInstructions, ...sharedInstructions],
3072
- [privateInstructions, sharedInstructions]
3073
- ), runInstructionsGroup = useMemo(() => instructions?.length || imageCaptionAction || translateAction || imageGenAction ? {
3074
- type: "group",
3075
- icon: () => null,
3076
- title: "Run instructions",
3077
- children: [
3078
- ...instructions?.map(
3079
- (instruction2) => instructionItem({
3080
- instruction: instruction2,
3081
- isPrivate: !!(instruction2.userId && instruction2.userId === currentUser?.id),
3082
- onInstructionAction,
3083
- hidden: isHidden,
3084
- assistSupported
3085
- })
3086
- ) || [],
3087
- imageCaptionAction,
3088
- imageGenAction
3089
- ].filter((a) => !!a),
3090
- expanded: !0
3091
- } : void 0, [
3092
- instructions,
3093
- currentUser?.id,
3094
- onInstructionAction,
3095
- isHidden,
3096
- documentIsNew,
3097
- assistSupported,
3098
- imageCaptionAction,
3099
- translateAction,
3100
- imageGenAction
3101
- ]), getDocumentValue = useCallback(() => docValueRef.current, []), getConditionalPaths = useCallback(() => (formStateRef.current ? getConditionalMembers(formStateRef.current) : []).flatMap(
3102
- (cm) => {
3103
- const path = stringToPath(cm.path);
3104
- return path.some((s) => typeof s == "number") ? [] : {
3105
- ...cm,
3106
- path
3107
- };
3108
- }
3109
- ), []), parentSchemaType = useMemo(() => {
3110
- if (props.path.length) {
3111
- if (props.path.length === 1)
3112
- return documentSchemaType;
3113
- } else return;
3114
- const parentPath = props.path.slice(0, -1), typePath2 = getTypePath(docValueRef.current, pathToString(parentPath));
3115
- return typePath2 ? fieldRefsByTypePath[typePath2]?.schemaType : void 0;
3116
- }, [fieldRefsByTypePath, props.path, documentSchemaType]), customActions = useCustomFieldActions({
3117
- actionType: props.path.length ? "field" : "document",
3118
- documentIdForAction: assistableDocumentId,
3119
- schemaType,
3120
- documentSchemaType,
3121
- path: props.path,
3122
- getDocumentValue,
3123
- getConditionalPaths,
3124
- parentSchemaType
3125
- }), manageInstructionsItem = useMemo(
3126
- () => ({
3127
- type: "action",
3128
- icon: ControlsIcon,
3129
- title: "Manage instructions",
3130
- onAction: manageInstructions,
3131
- selected: isSelected
3132
- }),
3133
- [manageInstructions, isSelected]
3134
- ), group = useMemo(
3135
- () => ({
3136
- type: "group",
3137
- icon: SparklesIcon,
3138
- title: pluginTitleShort,
3139
- children: [
3140
- runInstructionsGroup,
3141
- translateAction,
3142
- ...customActions,
3143
- assistSupported && manageInstructionsItem
3144
- ].filter((c) => !!c).filter((c) => c.type === "group" ? c.children.length : !0),
3145
- expanded: !1,
3146
- renderAsButton: !0,
3147
- hidden: !assistSupported && !imageCaptionAction && !translateAction && !imageGenAction
3148
- }),
3149
- [
3150
- //documentIsNew,
3151
- runInstructionsGroup,
3152
- manageInstructionsItem,
3153
- assistSupported,
3154
- imageCaptionAction,
3155
- translateAction,
3156
- imageGenAction,
3157
- customActions
3158
- ]
3159
- ), emptyAction = useMemo(
3160
- () => ({
3161
- type: "action",
3162
- hidden: !assistSupported,
3163
- icon: SparklesIcon,
3164
- onAction: manageInstructions,
3165
- renderAsButton: !0,
3166
- title: pluginTitleShort,
3167
- selected: isSelected
3168
- }),
3169
- [assistSupported, manageInstructions, isSelected]
3170
- );
3171
- return !instructions?.length && !imageCaptionAction && !translateAction && !imageGenAction && !customActions.length ? emptyAction : group;
3172
- }
3173
- };
3174
- function instructionItem(props) {
3175
- const { hidden, isPrivate, onInstructionAction, assistSupported, instruction: instruction2 } = props;
3176
- return {
3177
- type: "action",
3178
- icon: getIcon(instruction2.icon),
3179
- iconRight: isPrivate ? PrivateIcon : void 0,
3180
- title: getInstructionTitle(instruction2),
3181
- onAction: () => onInstructionAction(instruction2),
3182
- disabled: !assistSupported,
3183
- hidden
3184
- };
3185
- }
3186
- function createAssistDocumentPresence(documentId) {
3187
- return function() {
3188
- return documentId ? /* @__PURE__ */ jsx(AssistDocumentPresence, {}) : null;
3189
- };
3190
- }
3191
- function AssistDocumentPresence() {
3192
- const { assistDocument, syntheticTasks } = useAssistDocumentContext(), anyPresence = useMemo(() => {
3193
- const anyPresence2 = [...assistDocument?.tasks ?? [], ...syntheticTasks ?? []].filter((run) => !run.ended && !run.reason)?.flatMap((run) => run.presence ?? []).find((f) => f.started && (/* @__PURE__ */ new Date()).getTime() - new Date(f.started).getTime() < 3e4);
3194
- if (anyPresence2)
3195
- return aiPresence(anyPresence2, []);
3196
- const anyRun = assistDocument?.tasks?.filter((run) => !run.ended && !run.reason)?.find((f) => f.started && (/* @__PURE__ */ new Date()).getTime() - new Date(f.started).getTime() < 3e4);
3197
- return anyRun ? aiPresence(
3198
- {
3199
- started: anyRun.started,
3200
- _key: anyRun._key
3201
- },
3202
- []
3203
- ) : void 0;
3204
- }, [assistDocument?.tasks, syntheticTasks]);
3205
- return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(Flex, { flex: 1, justify: "flex-end", children: /* @__PURE__ */ jsx(Flex, { gap: 2, align: "center", children: anyPresence && /* @__PURE__ */ jsx(AiFieldPresence, { presence: anyPresence }) }) }) });
3206
- }
3207
- function BackToInstructionListLink() {
3208
- const { openInspector } = useDocumentPane(), goBack = useCallback(
3209
- () => openInspector(aiInspectorId, { [instructionParam]: void 0 }),
3210
- [openInspector]
3211
- );
3212
- return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
3213
- Button,
3214
- {
3215
- as: "a",
3216
- fontSize: 1,
3217
- icon: ArrowLeftIcon,
3218
- mode: "bleed",
3219
- padding: 1,
3220
- space: 2,
3221
- onClick: goBack,
3222
- text: " Instructions",
3223
- textAlign: "left"
3224
- }
3225
- ) });
3226
- }
3227
- const SelectedFieldContext = createContext(void 0), SelectedFieldContextProvider = SelectedFieldContext.Provider, EMPTY_FIELDS = [];
3228
- function AssistDocumentForm(props) {
3229
- return props.readOnly ? /* @__PURE__ */ jsx(Card, { border: !0, tone: "caution", padding: 2, children: /* @__PURE__ */ jsx(Text, { size: 1, children: " You do not have sufficient permissions to manage instructions." }) }) : /* @__PURE__ */ jsx(AssistDocumentFormEditable, { ...props });
3230
- }
3231
- function AssistDocumentFormEditable(props) {
3232
- const { onChange } = props, value = props.value, id = value?._id, fields = value?.fields, { params, setParams } = useAiPaneRouter(), pathKey = params[fieldPathParam], { typePath, documentType: targetDocumentType } = useContext(AssistTypeContext), instruction2 = params[instructionParam], activeKey = useMemo(() => {
3233
- if (typePath)
3234
- return (fields ?? EMPTY_FIELDS).find((f) => f.path === typePath)?._key;
3235
- }, [fields, typePath]), activePath = useMemo(() => {
3236
- if (!activeKey)
3237
- return;
3238
- const base = ["fields", { _key: activeKey }];
3239
- return instruction2 ? [...base, "instructions", { _key: instruction2 }] : base;
3240
- }, [activeKey, instruction2]), schema = useSchema(), documentSchema = useMemo(() => {
3241
- if (targetDocumentType)
3242
- return schema.get(targetDocumentType);
3243
- }, [schema, targetDocumentType]), fieldSchema = useSelectedSchema(pathKey, documentSchema), context = useMemo(
3244
- () => ({
3245
- documentSchema,
3246
- fieldSchema: fieldSchema ?? documentSchema
3247
- }),
3248
- [fieldSchema, documentSchema]
3249
- ), title = value?.title;
3250
- useEffect(() => {
3251
- !title && documentSchema && !id?.startsWith("drafts.") && onChange(set(documentSchema.title ?? documentSchema.name, ["title"]));
3252
- }, [title, documentSchema, onChange, id]);
3253
- const { onPathOpen, ...formCallbacks } = useFormCallbacks(), newCallbacks = useMemo(
3254
- () => ({
3255
- ...formCallbacks,
3256
- onPathOpen: (path) => {
3257
- !instruction2 && path.length === 4 && path[2] === "instructions" ? (setParams(
3258
- typed({
3259
- ...params,
3260
- [instructionParam]: path[3]?._key
3261
- })
3262
- ), onPathOpen([])) : setTimeout(() => onPathOpen(path), 0);
3263
- }
3264
- }),
3265
- [formCallbacks, onPathOpen, params, setParams, instruction2]
3266
- );
3267
- useEffect(() => {
3268
- activePath && !instruction2 && onPathOpen([]);
3269
- }, [activePath, instruction2, onPathOpen]);
3270
- const fieldError = useMemo(() => {
3271
- const fieldError2 = props.members.find(
3272
- (m) => m.kind === "error" && m.fieldName === "fields"
3273
- );
3274
- if (fieldError2)
3275
- return /* @__PURE__ */ jsx(MemberFieldError, { member: fieldError2 });
3276
- }, [props.members]);
3277
- return /* @__PURE__ */ jsx(SelectedFieldContextProvider, { value: context, children: /* @__PURE__ */ jsxs(Stack, { space: 5, children: [
3278
- /* @__PURE__ */ jsx(
3279
- FieldsInitializer,
3280
- {
3281
- pathKey: typePath,
3282
- activePath,
3283
- fields,
3284
- documentSchema,
3285
- onChange
3286
- },
3287
- typePath
3288
- ),
3289
- instruction2 && /* @__PURE__ */ jsx(BackToInstructionListLink, {}),
3290
- activePath && !fieldError && /* @__PURE__ */ jsx(FormCallbacksProvider, { ...newCallbacks, children: /* @__PURE__ */ jsx("div", { style: { lineHeight: "1.25em" }, children: /* @__PURE__ */ jsx(FormInput, { ...props, absolutePath: activePath }) }) }),
3291
- fieldError,
3292
- !activePath && props.renderDefault(props)
3293
- ] }) });
3294
- }
3295
- function useSelectedSchema(fieldPath, documentSchema) {
3296
- return useMemo(() => {
3297
- if (!fieldPath)
3298
- return;
3299
- if (fieldPath === documentRootKey)
3300
- return documentSchema;
3301
- const path = stringToPath(fieldPath);
3302
- let currentSchema = documentSchema;
3303
- for (let i = 0; i < path.length; i++) {
3304
- const segment = path[i], field = currentSchema?.fields.find((f) => f.name === segment);
3305
- if (!field)
3306
- return;
3307
- if (i === path.length - 1)
3308
- return field.type;
3309
- if (field.type.jsonType !== "object")
3310
- return;
3311
- currentSchema = field.type;
3312
- }
3313
- return currentSchema;
3314
- }, [documentSchema, fieldPath]);
3315
- }
3316
- function FieldsInitializer({
3317
- pathKey,
3318
- activePath,
3319
- fields,
3320
- documentSchema,
3321
- onChange
3322
- }) {
3323
- const {
3324
- config: { __presets: presets }
3325
- } = useAiAssistanceConfig(), existingField = fields?.find((f) => f._key === pathKey), documentPresets = !!documentSchema?.name && presets?.[documentSchema?.name], missingPresetInstructions = useMemo(() => {
3326
- if (!documentPresets || !pathKey)
3327
- return;
3328
- const existingInstructions = existingField?.instructions;
3329
- return documentPresets.fields?.find((f) => f.path === pathKey)?.instructions?.filter(
3330
- (i) => !existingInstructions?.some((ei) => ei._key === i._key)
3331
- );
3332
- }, [documentPresets, pathKey, existingField]), initialized = useRef(!1);
3333
- return useEffect(() => {
3334
- if (initialized.current || !pathKey || existingField && !missingPresetInstructions?.length)
3335
- return;
3336
- let event = PatchEvent.from([setIfMissing([], ["fields"])]);
3337
- existingField || (event = event.append(
3338
- insert(
3339
- [
3340
- typed({
3341
- _key: pathKey,
3342
- _type: assistFieldTypeName,
3343
- path: pathKey,
3344
- instructions: []
3345
- })
3346
- ],
3347
- "after",
3348
- ["fields", -1]
3349
- )
3350
- )), existingField?.instructions?.length || (event = event.append([setIfMissing([], ["fields", { _key: pathKey }, "instructions"])])), missingPresetInstructions?.length && (event = event.append(
3351
- insert(
3352
- missingPresetInstructions.map(
3353
- (preset) => ({
3354
- ...preset,
3355
- _type: "sanity.assist.instruction",
3356
- prompt: preset.prompt?.map((p) => ({ markDefs: [], ...p }))
3357
- })
3358
- ),
3359
- "after",
3360
- ["fields", { _key: pathKey }, "instructions", -1]
3361
- )
3362
- )), onChange(event), initialized.current = !0;
3363
- }, [activePath, onChange, pathKey, existingField, missingPresetInstructions]), null;
3364
- }
3365
- function FieldRefPreview(props) {
3366
- const { actions } = props, documentSchema = useContext(SelectedFieldContext)?.documentSchema, path = useContext(InlineBlockValueContext)?.path ?? props.path, selectedField = useSelectedField(documentSchema, path);
3367
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, align: "center", style: { width: "100%" }, children: [
3368
- /* @__PURE__ */ jsx(Flex, { flex: 1, gap: 2, align: "center", paddingY: 3, paddingX: 1, children: /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { size: 1, textOverflow: "ellipsis", children: selectedField?.title ?? "Select field" }) }) }),
3369
- actions
3370
- ] });
3371
- }
3372
- function HiddenFieldTitle(props) {
3373
- return props.renderDefault({ ...props, title: "", level: props.level - 2, changed: !1 });
3374
- }
3375
- function InstructionVisibility(props) {
3376
- const { value, onChange } = props, user = useCurrentUser(), handleChange = useCallback(() => {
3377
- const newValue = value ? "" : user?.id ?? "";
3378
- onChange(newValue ? set(newValue) : unset());
3379
- }, [onChange, user, value]), id = useId();
3380
- return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(Flex, { gap: 2, align: "flex-start", children: [
3381
- /* @__PURE__ */ jsx("div", { style: { margin: "-3px 0" }, children: /* @__PURE__ */ jsx(
3382
- Switch,
3383
- {
3384
- ...props.elementProps,
3385
- id,
3386
- value: `${!value}`,
3387
- checked: !value,
3388
- onChange: handleChange,
3389
- disabled: props.elementProps.readOnly
3390
- }
3391
- ) }),
3392
- /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, weight: "medium", children: /* @__PURE__ */ jsx("label", { htmlFor: id, children: "Make visible to all Studio members" }) })
3393
- ] }) });
3394
- }
3395
- function FieldRefPathInput(props) {
3396
- const documentSchema = useContext(SelectedFieldContext)?.documentSchema, { typePath } = useContext(AssistTypeContext), ref = useRef(null), id = useId(), { onChange } = props;
3397
- useEffect(() => {
3398
- ref.current?.querySelector("input")?.focus();
3399
- }, []);
3400
- const onSelect = useCallback((path) => onChange(set(path)), [onChange]), filter2 = useCallback(
3401
- (field) => {
3402
- if (!field.key.includes("|") || !typePath)
3403
- return !0;
3404
- if (field.key.includes("|") && !typePath.includes("|"))
3405
- return !1;
3406
- const fieldSegments = field.key.split("."), lastArrayItemIndex = fieldSegments.findLastIndex((s) => s.includes("|")), mustStartWith = fieldSegments.slice(0, lastArrayItemIndex + 1).join(".");
3407
- return typePath.startsWith(mustStartWith);
3408
- },
3409
- [typePath]
3410
- );
3411
- return documentSchema ? /* @__PURE__ */ jsx(Box, { flex: 1, style: { minWidth: 300 }, ref, children: /* @__PURE__ */ jsx(
3412
- FieldAutocomplete,
3413
- {
3414
- id,
3415
- schemaType: documentSchema,
3416
- onSelect,
3417
- fieldPath: props.value,
3418
- filter: filter2
3419
- }
3420
- ) }) : props.renderDefault(props);
3421
- }
3422
- function findFieldMember(members, fieldName) {
3423
- return members.find(
3424
- (m) => m.kind === "field" && m.name === fieldName || m.kind === "error" && m.fieldName === fieldName
3425
- );
3426
- }
3427
- function findFieldsetMember(members, fieldsetName) {
3428
- return members.find(
3429
- (m) => m.kind === "fieldSet" && m.fieldSet.name === fieldsetName
3430
- );
3431
- }
3432
- function InstructionInput(props) {
3433
- return /* @__PURE__ */ jsxs(Stack, { space: [4, 4, 4, 5], children: [
3434
- /* @__PURE__ */ jsx(NameField, { ...props }),
3435
- /* @__PURE__ */ jsx(ShareField, { ...props }),
3436
- /* @__PURE__ */ jsx(ObjectMember, { fieldName: "prompt", ...props }),
3437
- /* @__PURE__ */ jsx(ObjectMember, { fieldName: "output", ...props })
3438
- ] });
3439
- }
3440
- function ObjectMember({ fieldName, ...props }) {
3441
- const member = findFieldMember(props.members, fieldName);
3442
- return member ? /* @__PURE__ */ jsx(ObjectInputMember, { ...props, member }) : null;
3443
- }
3444
- const NONE = [];
3445
- function NameField(props) {
3446
- const fieldsetMember = findFieldsetMember(props.members, "appearance"), titleId = useId(), members = fieldsetMember?.fieldSet.members ?? NONE, iconMember = findFieldMember(members, "icon"), titleMember = findFieldMember(members, "title"), titlePlaceholder = "Untitled", moddedTitleMember = useMemo(() => {
3447
- if (titleMember)
3448
- return titleMember.kind === "error" ? titleMember : {
3449
- ...titleMember,
3450
- field: {
3451
- ...titleMember?.field,
3452
- schemaType: {
3453
- ...titleMember?.field.schemaType,
3454
- placeholder: titlePlaceholder
3455
- }
3456
- }
3457
- };
3458
- }, [titleMember, titlePlaceholder]);
3459
- return /* @__PURE__ */ jsx(Stack, { space: 5, children: /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
3460
- /* @__PURE__ */ jsx(Flex, { gap: 1, children: /* @__PURE__ */ jsx(Text, { as: "label", weight: "semibold", size: 1, htmlFor: titleId, children: "Name" }) }),
3461
- /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: "How this instruction appears in menus" }),
3462
- /* @__PURE__ */ jsxs(Flex, { align: "center", children: [
3463
- iconMember && /* @__PURE__ */ jsx(Box, { flex: "none", children: /* @__PURE__ */ jsx(ObjectInputMember, { ...props, member: iconMember }) }),
3464
- moddedTitleMember && /* @__PURE__ */ jsx(Box, { flex: 1, style: { marginLeft: -1 }, children: /* @__PURE__ */ jsx(ObjectInputMember, { ...props, member: moddedTitleMember }) })
3465
- ] })
3466
- ] }) });
3467
- }
3468
- function ShareField(props) {
3469
- const members = findFieldsetMember(props.members, "appearance")?.fieldSet.members ?? NONE, visibilityMember = findFieldMember(members, "userId");
3470
- return /* @__PURE__ */ jsx(Fragment, { children: visibilityMember && /* @__PURE__ */ jsx(ObjectInputMember, { ...props, member: visibilityMember }) });
3471
- }
3472
- function InstructionOutputField(props) {
3473
- const { fieldSchema } = useContext(SelectedFieldContext) ?? {};
3474
- return !fieldSchema || !(isObjectSchemaType(fieldSchema) || isArrayOfObjectsSchemaType(fieldSchema)) ? null : /* @__PURE__ */ jsx(EnabledOutputField, { ...props, fieldSchema, children: props.children });
3475
- }
3476
- function EnabledOutputField({
3477
- fieldSchema,
3478
- ...props
3479
- }) {
3480
- const [open, setOpen] = useState(!!props.value?.length), onExpand = useCallback(() => setOpen(!0), []), onCollapse = useCallback(() => setOpen(!1), []);
3481
- return props.renderDefault({
3482
- ...props,
3483
- collapsible: !0,
3484
- onExpand,
3485
- onCollapse,
3486
- collapsed: !open,
3487
- level: 1,
3488
- title: isObjectSchemaType(fieldSchema) ? "Allowed fields" : "Allowed types"
3489
- });
3490
- }
3491
- function InstructionOutputInput(props) {
3492
- const { fieldSchema } = useContext(SelectedFieldContext) ?? {};
3493
- return fieldSchema ? isObjectSchemaType(fieldSchema) ? /* @__PURE__ */ jsx(ObjectOutputInput, { ...props, fieldSchema }) : isArrayOfObjectsSchemaType(fieldSchema) ? /* @__PURE__ */ jsx(ArrayOutputInput, { ...props, fieldSchema }) : null : null;
3494
- }
3495
- function useEmptySelectAllValue(value, allowedValues, onChange) {
3496
- useEffect(() => {
3497
- const validValues = value?.filter(
3498
- (v) => allowedValues.find(
3499
- (f) => f.name === (v._type === outputFieldTypeName ? v.relativePath : v.type)
3500
- )
3501
- ), valueLength = value?.length ?? 0, validLength = validValues?.length ?? 0;
3502
- (!validLength && valueLength || validLength >= allowedValues.length) && onChange(PatchEvent.from([unset()]));
3503
- }, [allowedValues, value, onChange]);
3504
- }
3505
- function ObjectOutputInput({
3506
- fieldSchema,
3507
- ...props
3508
- }) {
3509
- const { value, onChange } = props, fields = useMemo(
3510
- () => fieldSchema.fields.filter((field) => isAssistSupported(field.type)),
3511
- [fieldSchema.fields]
3512
- );
3513
- useEmptySelectAllValue(value, fields, onChange);
3514
- const onSelectChange = useCallback(
3515
- (checked, selectedValue) => {
3516
- if (checked)
3517
- if (value?.length)
3518
- onChange(PatchEvent.from(unset([{ _key: selectedValue }])));
3519
- else {
3520
- const items = fields.filter((f) => f.name !== selectedValue).map(
3521
- (field) => typed({
3522
- _key: field.name,
3523
- _type: "sanity.assist.output.field",
3524
- relativePath: field.name
3525
- })
3526
- );
3527
- onChange(PatchEvent.from([setIfMissing([]), insert(items, "after", [-1])]));
3528
- }
3529
- else {
3530
- const patchValue = {
3531
- _key: selectedValue,
3532
- _type: "sanity.assist.output.field",
3533
- relativePath: selectedValue
3534
- };
3535
- onChange(PatchEvent.from([setIfMissing([]), insert([patchValue], "after", [-1])]));
3536
- }
3537
- },
3538
- [onChange, value, fields]
3539
- );
3540
- return /* @__PURE__ */ jsx(Stack, { space: 2, children: fields.map((field) => /* @__PURE__ */ jsx(Flex, { align: "center", gap: 2, children: /* @__PURE__ */ jsx(
3541
- Selectable,
3542
- {
3543
- value: field.name,
3544
- title: field.type.title ?? field.name,
3545
- arrayValue: value,
3546
- onChange: onSelectChange
3547
- }
3548
- ) }, field.name)) });
3549
- }
3550
- function ArrayOutputInput({
3551
- fieldSchema,
3552
- ...props
3553
- }) {
3554
- const { value, onChange } = props, ofItems = useMemo(
3555
- () => fieldSchema.of.filter((itemType) => isAssistSupported(itemType)),
3556
- [fieldSchema.of]
3557
- );
3558
- useEmptySelectAllValue(value, ofItems, onChange);
3559
- const onSelectChange = useCallback(
3560
- (checked, selectedValue) => {
3561
- if (checked)
3562
- if (value?.length)
3563
- onChange(PatchEvent.from(unset([{ _key: selectedValue }])));
3564
- else {
3565
- const items = ofItems.filter((f) => f.name !== selectedValue).map(
3566
- (field) => typed({
3567
- _key: field.name,
3568
- _type: "sanity.assist.output.type",
3569
- type: field.name
3570
- })
3571
- );
3572
- onChange(PatchEvent.from([setIfMissing([]), insert(items, "after", [-1])]));
3573
- }
3574
- else {
3575
- const patchValue = {
3576
- _key: selectedValue,
3577
- _type: "sanity.assist.output.type",
3578
- type: selectedValue
3579
- };
3580
- onChange(PatchEvent.from([setIfMissing([]), insert([patchValue], "after", [-1])]));
3581
- }
3582
- },
3583
- [onChange, value, ofItems]
3584
- );
3585
- return /* @__PURE__ */ jsx(Stack, { space: 2, children: ofItems.map((itemType) => /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
3586
- Selectable,
3587
- {
3588
- value: itemType.name,
3589
- title: isType(itemType, "block") ? "Text" : itemType.title ?? itemType.name,
3590
- arrayValue: value,
3591
- onChange: onSelectChange
3592
- }
3593
- ) }, itemType.name)) });
3594
- }
3595
- function Selectable({
3596
- title,
3597
- arrayValue,
3598
- value,
3599
- onChange
3600
- }) {
3601
- const checked = !arrayValue?.length || !!arrayValue?.find((v) => v._key === value), handleChange = useCallback(() => onChange(checked, value), [onChange, checked, value]);
3602
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, align: "flex-start", children: [
3603
- /* @__PURE__ */ jsx(Checkbox, { checked, onChange: handleChange }),
3604
- /* @__PURE__ */ jsx(Card, { marginTop: 1, onClick: handleChange, children: /* @__PURE__ */ jsx(Text, { style: { cursor: "default" }, size: 1, children: title }) })
3605
- ] });
3606
- }
3607
- const PteMods = styled(Box)`
3608
- & [data-testid='pt-editor__toolbar-card'] > div > div:last-child {
3609
- display: none;
3610
- }
3611
- & [data-testid='pt-editor'] {
3612
- min-height: 300px;
3613
- }
3614
- & [data-testid='pt-editor'] .pt-inline-object * {
3615
- max-width: 400px;
3616
- }
3617
- `;
3618
- function PromptInput(props) {
3619
- return useOnlyInlineBlocks(props), /* @__PURE__ */ jsx(PteMods, { children: props.renderDefault(props) });
3620
- }
3621
- function useOnlyInlineBlocks(props) {
3622
- useEffect(() => {
3623
- let needsFix = !1;
3624
- const val = (props.value ?? []).map((block) => block._type === "block" ? block : (needsFix = !0, typed({
3625
- _key: randomKey(12),
3626
- _type: "block",
3627
- level: 0,
3628
- markDefs: [],
3629
- style: "normal",
3630
- children: [block]
3631
- })));
3632
- needsFix && props.onChange(set(val));
3633
- }, []);
3634
- }
3635
- function InstructionsArrayField(props) {
3636
- return props.renderDefault({
3637
- ...props,
3638
- title: " "
3639
- });
3640
- }
3641
- function InstructionsArrayInput(props) {
3642
- const user = useCurrentUser(), originalValue = props.value, originalMembers = props.members, value = useMemo(
3643
- () => (originalValue ?? []).filter((v) => !v.userId || v.userId === user?.id),
3644
- [originalValue, user]
3645
- ), members = useMemo(
3646
- () => (originalMembers ?? []).filter((v) => {
3647
- if (v.kind === "error")
3648
- return !0;
3649
- const value2 = v?.item?.value;
3650
- return !value2.userId || value2.userId === user?.id;
3651
- }),
3652
- [originalMembers, user]
3653
- );
3654
- return props.renderDefault({ ...props, value, members });
3655
- }
3656
- function HideReferenceChangedBannerInput(props) {
3657
- const ref = useRef(null);
3658
- return useEffect(() => {
3659
- const parent = ref.current?.closest('[data-testid="pane-content"]');
3660
- if (!parent)
3661
- return;
3662
- const style = document.createElement("style"), parentId = `id-${Math.random()}`.replace(".", "-");
3663
- parent.id = parentId, style.innerText = `
3664
- #${parentId} [data-testid="reference-changed-banner"] { display: none; }
3665
- `, parent.prepend(style);
3666
- }, [ref]), /* @__PURE__ */ jsx(Box, { ref, children: props.renderDefault(props) });
3667
- }
3668
- const contextDocumentSchema = defineType({
3669
- type: "document",
3670
- name: contextDocumentTypeName,
3671
- title: "AI context",
3672
- liveEdit: !0,
3673
- icon: TokenIcon,
3674
- components: {
3675
- input: HideReferenceChangedBannerInput
3676
- },
3677
- fields: [
3678
- defineField({
3679
- type: "string",
3680
- name: "title",
3681
- title: "Title"
3682
- }),
3683
- defineField({
3684
- name: "context",
3685
- type: "array",
3686
- title: "Context",
3687
- of: [
3688
- defineArrayMember({
3689
- type: "block",
3690
- styles: [{ title: "Normal", value: "normal" }],
3691
- lists: [],
3692
- marks: {
3693
- decorators: [],
3694
- annotations: []
3695
- }
3696
- })
3697
- ]
3698
- })
3699
- ],
3700
- preview: {
3701
- select: {
3702
- title: "title",
3703
- context: "context"
3704
- },
3705
- prepare({ title, context }) {
3706
- const words = context?.flatMap((block) => block?.children).flatMap((child) => child?.text?.split(" ")).filter(Boolean)?.length ?? 0;
3707
- return {
3708
- title,
3709
- subtitle: `Words: ${words}`,
3710
- media: DocumentTextIcon
3711
- };
3712
- }
3713
- }
3714
- }), fieldReference = defineType({
3715
- type: "object",
3716
- name: fieldReferenceTypeName,
3717
- title: "Field",
3718
- icon: ThListIcon,
3719
- fields: [
3720
- defineField({
3721
- type: "string",
3722
- name: "path",
3723
- title: "Field",
3724
- components: {
3725
- input: FieldRefPathInput
3726
- },
3727
- validation: (rule) => {
3728
- const getForSchemaType = createFieldRefCache();
3729
- return rule.custom((value, context) => {
3730
- if (!value)
3731
- return "Please select a field";
3732
- try {
3733
- const docId = context.document?._id;
3734
- if (!docId)
3735
- return "Field reference cannot be used outside document inspector context. Could not resolve document id.";
3736
- const targetDocType = docId.replace(new RegExp(`^${assistDocumentIdPrefix}`), ""), schema = context.schema.get(targetDocType);
3737
- if (!schema)
3738
- return `Field reference cannot be used outside document inspector context. Could not resolve schema: ${targetDocType}`;
3739
- const { fieldRefs } = getForSchemaType(schema);
3740
- return fieldRefs.find((r) => r.key === value) ? !0 : `Field with path "${value}" does not exist in the schema.`;
3741
- } catch (e) {
3742
- return console.error("Failed to resolve field reference", e), "Invalid field reference.";
3743
- }
3744
- });
3745
- }
3746
- })
3747
- ],
3748
- preview: {
3749
- select: {
3750
- path: "path"
3751
- },
3752
- prepare({ path }) {
3753
- return {
3754
- title: path,
3755
- path,
3756
- icon: CodeIcon
3757
- };
3758
- }
3759
- },
3760
- components: {
3761
- preview: FieldRefPreview
3762
- },
3763
- options: {
3764
- modal: {
3765
- type: "popover"
3766
- }
3767
- }
3768
- }), userInput = defineType({
3769
- type: "object",
3770
- name: userInputTypeName,
3771
- title: "User input",
3772
- icon: ComposeIcon,
3773
- fields: [
3774
- defineField({
3775
- type: "string",
3776
- name: "message",
3777
- title: "User input title",
3778
- placeholder: "Provide instruction text",
3779
- description: "The header above the user input text field",
3780
- validation: (rule) => rule.required()
3781
- }),
3782
- defineField({
3783
- type: "text",
3784
- rows: 3,
3785
- name: "description",
3786
- title: "User input description",
3787
- description: "The description above the user input text field"
3788
- })
3789
- ],
3790
- preview: {
3791
- select: {
3792
- title: "message"
3793
- }
3794
- },
3795
- options: {
3796
- modal: {
3797
- type: "popover",
3798
- width: 1
3799
- }
3800
- }
3801
- }), promptContext = defineType({
3802
- type: "object",
3803
- name: instructionContextTypeName,
3804
- title: contextDocumentSchema.title,
3805
- icon: contextDocumentSchema.icon,
3806
- fields: [
3807
- defineField({
3808
- type: "reference",
3809
- name: "reference",
3810
- to: [{ type: contextDocumentSchema.name }],
3811
- title: "Context",
3812
- description: "The referenced context will be inserted into the instruction",
3813
- validation: (rule) => rule.required(),
3814
- components: {
3815
- input: function(props) {
3816
- return /* @__PURE__ */ jsx(Box, { style: { maxWidth: 300 }, children: props.renderDefault(props) });
3817
- }
3818
- }
3819
- })
3820
- ],
3821
- preview: {
3822
- select: {
3823
- ref: "reference._ref",
3824
- title: "reference.title",
3825
- context: "reference.context"
3826
- },
3827
- prepare(select) {
3828
- return select.ref ? contextDocumentSchema?.preview?.prepare?.(select) ?? select : { title: "No reference selected", media: contextDocumentSchema.icon };
3829
- }
3830
- },
3831
- options: {
3832
- modal: {
3833
- type: "popover",
3834
- width: "auto"
3835
- }
3836
- }
3837
- }), prompt = defineType({
3838
- type: "array",
3839
- name: promptTypeName,
3840
- title: "Prompt",
3841
- of: [
3842
- defineArrayMember({
3843
- type: "block",
3844
- styles: [{ title: "Normal", value: "normal" }],
3845
- lists: [],
3846
- marks: {
3847
- decorators: [],
3848
- annotations: []
3849
- },
3850
- of: [
3851
- defineArrayMember({
3852
- type: fieldReference.name
3853
- }),
3854
- defineArrayMember({
3855
- type: promptContext.name
3856
- }),
3857
- defineArrayMember({
3858
- type: userInput.name
3859
- })
3860
- ]
3861
- })
3862
- /* defineArrayMember({
3863
- type: fieldReference.name,
3864
- }),
3865
- defineArrayMember({
3866
- type: promptContext.name,
3867
- }),
3868
- defineArrayMember({
3869
- type: userInput.name,
3870
- }),*/
3871
- ]
3872
- }), outputFieldType = defineType({
3873
- type: "object",
3874
- name: outputFieldTypeName,
3875
- title: "Output field",
3876
- fields: [
3877
- defineField({
3878
- type: "string",
3879
- name: "path",
3880
- title: "Path"
3881
- })
3882
- ]
3883
- }), outputTypeType = defineType({
3884
- type: "object",
3885
- name: outputTypeTypeName,
3886
- title: "Output type",
3887
- fields: [
3888
- defineField({
3889
- type: "string",
3890
- name: "type",
3891
- title: "Type"
3892
- })
3893
- ]
3894
- }), instruction = defineType({
3895
- type: "object",
3896
- name: instructionTypeName,
3897
- title: "Instruction",
3898
- fieldsets: [
3899
- { name: "appearance", title: "Appearance", options: { collapsible: !0, collapsed: !0 } }
3900
- ],
3901
- preview: {
3902
- select: {
3903
- icon: "icon",
3904
- title: "title",
3905
- userId: "userId"
3906
- },
3907
- prepare: ({ icon, title, userId }) => ({
3908
- title,
3909
- icon: icon ? icons[icon] : SparklesIcon,
3910
- userId
3911
- })
3912
- },
3913
- components: {
3914
- input: InstructionInput,
3915
- preview: (props) => /* @__PURE__ */ jsxs(Flex, { gap: 3, align: "center", padding: 2, children: [
3916
- props.icon && /* @__PURE__ */ jsx(Box, { flex: "none", children: /* @__PURE__ */ jsx(Text, { size: 1, children: createElement(props.icon) }) }),
3917
- /* @__PURE__ */ jsx(Stack, { flex: 1, space: 2, children: /* @__PURE__ */ jsx(Text, { size: 1, textOverflow: "ellipsis", weight: "medium", children: getInstructionTitle(props) }) }),
3918
- props.userId && /* @__PURE__ */ jsx(Text, { size: 1, children: /* @__PURE__ */ jsx(
3919
- Tooltip,
3920
- {
3921
- content: /* @__PURE__ */ jsx(Text, { size: 1, children: "Only visible to you" }),
3922
- padding: 2,
3923
- placement: "top",
3924
- portal: !0,
3925
- children: /* @__PURE__ */ jsx(LockIcon, {})
3926
- }
3927
- ) })
3928
- ] })
3929
- },
3930
- fields: [
3931
- defineField({
3932
- type: prompt.name,
3933
- name: "prompt",
3934
- title: "Instruction",
3935
- description: /* @__PURE__ */ jsxs(Fragment, { children: [
3936
- "Learn from",
3937
- " ",
3938
- /* @__PURE__ */ jsxs("a", { href: instructionGuideUrl, target: "_blank", rel: "noreferrer", children: [
3939
- "our instruction guide ",
3940
- /* @__PURE__ */ jsx(ArrowRightIcon, {})
3941
- ] })
3942
- ] }),
3943
- components: {
3944
- input: PromptInput
3945
- }
3946
- }),
3947
- defineField({
3948
- type: "string",
3949
- name: "icon",
3950
- title: "Icon",
3951
- fieldset: "appearance",
3952
- components: {
3953
- field: HiddenFieldTitle,
3954
- input: IconInput
3955
- }
3956
- }),
3957
- defineField({
3958
- type: "string",
3959
- name: "title",
3960
- title: "Title",
3961
- fieldset: "appearance",
3962
- components: {
3963
- field: HiddenFieldTitle
3964
- }
3965
- }),
3966
- defineField({
3967
- type: "string",
3968
- name: "userId",
3969
- title: "Visibility",
3970
- fieldset: "appearance",
3971
- components: {
3972
- field: HiddenFieldTitle,
3973
- input: InstructionVisibility
3974
- },
3975
- initialValue: (params, context) => context.currentUser?.id ?? "",
3976
- readOnly: (context) => !!(context.parent?.createdById && context.parent?.createdById !== context.currentUser?.id)
3977
- }),
3978
- defineField({
3979
- type: "string",
3980
- name: "createdById",
3981
- title: "Created by",
3982
- hidden: !0,
3983
- fieldset: "appearance",
3984
- initialValue: (params, context) => context.currentUser?.id ?? ""
3985
- }),
3986
- defineField({
3987
- type: "array",
3988
- name: "output",
3989
- title: "Output filter",
3990
- components: {
3991
- input: InstructionOutputInput,
3992
- field: InstructionOutputField
3993
- },
3994
- of: [
3995
- defineArrayMember({ type: outputFieldType.name }),
3996
- defineArrayMember({ type: outputTypeType.name })
3997
- ]
3998
- })
3999
- ]
4000
- }), fieldInstructions = defineType({
4001
- type: "object",
4002
- name: assistFieldTypeName,
4003
- title: "Field prompt",
4004
- /* components: {
4005
- input: FieldPromptInput,
4006
- },*/
4007
- fields: [
4008
- defineField({
4009
- type: "string",
4010
- name: "path",
4011
- title: "Path",
4012
- readOnly: !0,
4013
- hidden: !0
4014
- }),
4015
- defineField({
4016
- type: "array",
4017
- name: "instructions",
4018
- title: "Instructions",
4019
- of: [{ type: instruction.name }],
4020
- components: {
4021
- field: InstructionsArrayField,
4022
- input: InstructionsArrayInput
4023
- }
4024
- })
4025
- ],
4026
- preview: {
4027
- select: {
4028
- title: "path"
4029
- }
4030
- }
4031
- }), assistDocumentSchema = defineType({
4032
- //NOTE: this is a document type. Using object here ensures it does not appear in structure menus
4033
- type: "object",
4034
- liveEdit: !0,
4035
- name: assistDocumentTypeName,
4036
- title: "AI Document",
4037
- components: {
4038
- input: AssistDocumentForm,
4039
- field: (props) => props.renderDefault({ ...props, title: "" })
4040
- },
4041
- fields: [
4042
- defineField({
4043
- type: "string",
4044
- name: "title",
4045
- title: "Title"
4046
- }),
4047
- defineField({
4048
- type: "array",
4049
- name: "fields",
4050
- title: "Fields",
4051
- of: [{ type: fieldInstructions.name }]
4052
- })
4053
- ],
4054
- preview: {
4055
- select: {
4056
- title: "title"
4057
- }
4058
- }
4059
- }), instructionTask = defineType({
4060
- type: "object",
4061
- name: instructionTaskTypeName,
4062
- title: "Instruction task",
4063
- fields: [
4064
- defineField({
4065
- type: "string",
4066
- name: "path",
4067
- title: "Path"
4068
- }),
4069
- defineField({
4070
- type: "string",
4071
- name: "instructionKey",
4072
- title: "Instruction key"
4073
- }),
4074
- defineField({
4075
- type: "datetime",
4076
- name: "started",
4077
- title: "Started"
4078
- }),
4079
- defineField({
4080
- type: "datetime",
4081
- name: "updated",
4082
- title: "Updated"
4083
- }),
4084
- defineField({
4085
- type: "string",
4086
- name: "info",
4087
- title: "Info"
4088
- })
4089
- ]
4090
- }), documentInstructionStatus = defineType({
4091
- //NOTE: this is a document type. Using object here ensures it does not appear in structure menus
4092
- type: "object",
4093
- liveEdit: !0,
4094
- name: assistTasksStatusTypeName,
4095
- title: "Document instruction status",
4096
- fields: [
4097
- defineField({
4098
- type: "array",
4099
- name: "tasks",
4100
- title: "Tasks",
4101
- of: [{ type: instructionTask.name }]
4102
- })
4103
- ]
4104
- });
4105
- function excludeComments(type) {
4106
- const existingRender = type?.components?.field;
4107
- return {
4108
- ...type,
4109
- ..."components" in type ? {
4110
- components: {
4111
- ...type.components,
4112
- field: (props) => {
4113
- const newProps = { ...props, __internal_comments: void 0 };
4114
- return typeof existingRender == "function" ? existingRender(newProps) : props.renderDefault(newProps);
4115
- }
4116
- }
4117
- } : {},
4118
- ..."fields" in type ? {
4119
- // recursively disable comments in fields
4120
- fields: type.fields?.map((field) => excludeComments(field))
4121
- } : {},
4122
- ..."of" in type ? {
4123
- // recursively disable comments in array items
4124
- of: type.of?.map((arrayItemType) => excludeComments(arrayItemType))
4125
- } : {}
4126
- };
4127
- }
4128
- const instructionForm = [
4129
- fieldInstructions,
4130
- instruction,
4131
- fieldReference,
4132
- prompt,
4133
- userInput,
4134
- promptContext
4135
- ].map(excludeComments), schemaTypes = [
4136
- ...instructionForm,
4137
- outputFieldType,
4138
- outputTypeType,
4139
- assistDocumentSchema,
4140
- documentInstructionStatus,
4141
- instructionTask,
4142
- contextDocumentSchema
4143
- ], assist = definePlugin((config) => {
4144
- const configWithDefaults = config ?? {}, styleguide = configWithDefaults.translate?.styleguide || "", maxPathDepth = configWithDefaults.assist?.maxPathDepth, temperature = configWithDefaults.assist?.temperature;
4145
- if (typeof styleguide == "string" && validateStyleguide(styleguide), maxPathDepth !== void 0 && (maxPathDepth < 1 || maxPathDepth > 12))
4146
- throw new Error(
4147
- `[${packageName}]: \`assist.maxPathDepth\` must be be in the range [1,12] inclusive, but was ${maxPathDepth}`
4148
- );
4149
- if (temperature !== void 0 && (temperature < 0 || temperature > 1))
4150
- throw new Error(
4151
- `[${packageName}]: \`assist.maxPathDepth\` must be be in the range [0,1] inclusive, but was ${temperature}`
4152
- );
4153
- return {
4154
- name: packageName,
4155
- handlesGDR: !0,
4156
- schema: {
4157
- types: schemaTypes
4158
- },
4159
- i18n: {
4160
- bundles: [{}]
4161
- },
4162
- document: {
4163
- inspectors: (prev, context) => {
4164
- const documentType = context.documentType, docSchema = context.schema.get(documentType);
4165
- return docSchema && isSchemaAssistEnabled(docSchema) ? [...prev, assistInspector] : prev;
4166
- },
4167
- unstable_fieldActions: (prev, { documentType, schema }) => {
4168
- if (documentType === assistDocumentTypeName)
4169
- return [];
4170
- const docSchema = schema.get(documentType);
4171
- return docSchema && isSchemaAssistEnabled(docSchema) ? [...prev, assistFieldActions] : prev;
4172
- },
4173
- unstable_languageFilter: (prev, { documentId, schema, schemaType }) => {
4174
- if (schemaType === assistDocumentTypeName)
4175
- return [];
4176
- const docSchema = schema.get(schemaType);
4177
- return docSchema && isObjectSchemaType(docSchema) && isSchemaAssistEnabled(docSchema) ? [...prev, createAssistDocumentPresence(documentId)] : prev;
4178
- },
4179
- components: {
4180
- unstable_layout: AssistDocumentLayout
4181
- }
4182
- },
4183
- studio: {
4184
- components: {
4185
- layout: function(props) {
4186
- return /* @__PURE__ */ jsx(AssistLayout, { ...props, config: configWithDefaults });
4187
- }
4188
- }
4189
- },
4190
- form: {
4191
- components: {
4192
- input: AssistDocumentInputWrapper,
4193
- field: AssistFieldWrapper,
4194
- item: AssistItem,
4195
- block: AssistFormBlock,
4196
- inlineBlock: AssistInlineFormBlock
4197
- }
4198
- },
4199
- plugins: [
4200
- definePlugin({
4201
- name: `${packageName}/safe-value-input`,
4202
- form: { components: { input: SafeValueInput } }
4203
- })(),
4204
- definePlugin({
4205
- name: `${packageName}/generate-caption`,
4206
- form: {
4207
- components: {
4208
- input: (props) => {
4209
- const { schemaType } = props;
4210
- return isImage(schemaType) ? /* @__PURE__ */ jsx(ImageContextProvider, { ...props }) : props.renderDefault(props);
4211
- }
4212
- }
4213
- }
4214
- })()
4215
- ]
4216
- };
4217
- }), fetch = (client, query, params, options2) => defer(
4218
- () => (
4219
- // getVersionedClient(options.apiVersion)
4220
- client.observable.fetch(query, params, {
4221
- tag: options2.tag,
4222
- filterResponse: !0
4223
- })
4224
- )
4225
- ), listen = (client, query, params, options2) => defer(
4226
- () => (
4227
- // getVersionedClient(options.apiVersion)
4228
- client.listen(query, params, {
4229
- events: ["welcome", "mutation", "reconnect"],
4230
- includeResult: !1,
4231
- visibility: "query",
4232
- tag: options2.tag
4233
- })
4234
- )
4235
- );
4236
- function isWelcomeEvent(event) {
4237
- return event.type === "welcome";
4238
- }
4239
- const listenQuery = (client, query, params = {}, options2 = {}) => {
4240
- const fetchQuery = query, listenerQuery = query, fetchOnce$ = fetch(client, fetchQuery, params, options2), events$ = listen(client, listenerQuery, params, options2).pipe(
4241
- mergeMap((ev, i) => i === 0 && !isWelcomeEvent(ev) ? throwError(
4242
- new Error(
4243
- ev.type === "reconnect" ? "Could not establish EventSource connection" : `Received unexpected type of first event "${ev.type}"`
4244
- )
4245
- ) : of(ev)),
4246
- share()
4247
- ), [welcome$, mutationAndReconnect$] = partition(events$, isWelcomeEvent), isRelevantEvent = (event) => !options2.transitions || event.type !== "mutation" ? !0 : options2.transitions.includes(event.transition);
4248
- return merge(
4249
- welcome$.pipe(take(1)),
4250
- mutationAndReconnect$.pipe(
4251
- filter(isRelevantEvent),
4252
- switchMap((event) => merge(of(event), of(event).pipe(delay(options2.throttleTime || 1e3))))
4253
- )
4254
- ).pipe(exhaustMapToWithTrailing(fetchOnce$));
4255
- }, DEFAULT_PARAMS = {}, DEFAULT_OPTIONS = { apiVersion: "v2022-05-09" };
4256
- function useListeningQuery(query, params = DEFAULT_PARAMS, options2 = DEFAULT_OPTIONS) {
4257
- const [loading, setLoading] = useState(!0), [error, setError] = useState(!1), [data, setData] = useState(null), subscription = useRef(null), client = useClient({ apiVersion: "v2022-05-09" });
4258
- return useEffect(() => (subscription.current = listenQuery(client, query, params, options2).pipe(
4259
- distinctUntilChanged(isEqual),
4260
- catchError((err) => (console.error(err), setError(err), setLoading(!1), setData(null), err))
4261
- ).subscribe((documents) => {
4262
- setData((current) => isEqual(current, documents) ? current : documents), setLoading(!1), setError(!1);
4263
- }), () => subscription.current ? subscription.current.unsubscribe() : void 0), [query, params, options2, client]), { loading, error, data };
4264
- }
4265
- const NO_DATA = [], defaultTitle = "Sync schema";
4266
- function SchemaTypeTool() {
4267
- const schema = useSchema(), client = useClient({ apiVersion: "2023-01-01" }), [saving, setSaving] = useState(!1), [syncTitle, setSyncTitle] = useState(defaultTitle), { data } = useListeningQuery("*[_type==$type] | order(_type)", {
4268
- type: assistSerializedTypeName
4269
- }), types = useMemo(() => serializeSchema(schema), [schema]), storeTypes = useCallback(() => {
4270
- setSaving(!0);
4271
- let canSave = !0;
4272
- async function store() {
4273
- setSyncTitle(`Syncing 0/${types.length}`);
4274
- const transaction = client.transaction();
4275
- for (let i = 0; i < types.length && canSave; i++) {
4276
- const type = types[i];
4277
- await transaction.createOrReplace(type), i > 0 && i % 50 === 0 && (await transaction.commit(), transaction.reset(), setSyncTitle(`Syncing ${i}/${types.length}`));
4278
- }
4279
- await transaction.commit();
4280
- }
4281
- return store().catch(console.error).finally(() => {
4282
- setSaving(!1), setSyncTitle(defaultTitle);
4283
- }), () => {
4284
- canSave = !1, setSaving(!1), setSyncTitle(defaultTitle);
4285
- };
4286
- }, [types, client, setSaving, setSyncTitle]);
4287
- return /* @__PURE__ */ jsx(Card, { padding: 4, overflow: "auto", style: { height: "calc(100vh - 81px)" }, children: /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
4288
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(
4289
- Button,
4290
- {
4291
- icon: saving ? /* @__PURE__ */ jsx(Spinner, { style: { marginTop: 5 } }) : SyncIcon,
4292
- text: syncTitle,
4293
- disabled: saving,
4294
- onClick: storeTypes
4295
- }
4296
- ) }),
4297
- /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
4298
- /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
4299
- /* @__PURE__ */ jsx(Label, { children: "Studio schema" }),
4300
- /* @__PURE__ */ jsx("ul", { children: types.map((type) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(SchemaEntry, { schemaStub: type }) }, type.name)) })
4301
- ] }),
4302
- /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
4303
- /* @__PURE__ */ jsx(Label, { children: "Stored schema" }),
4304
- /* @__PURE__ */ jsx("ul", { children: (data ?? NO_DATA).map((type) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(SchemaEntry, { schemaStub: type }) }, type.name)) })
4305
- ] })
4306
- ] })
4307
- ] }) });
4308
- }
4309
- function SchemaEntry({ schemaStub }) {
4310
- const out = useMemo(() => JSON.stringify(schemaStub, null, 2), [schemaStub]);
4311
- return /* @__PURE__ */ jsx("pre", { children: out });
4312
- }
4313
- function useUserInput() {
4314
- const { getUserInput } = useRunInstruction();
4315
- return getUserInput;
4316
- }
4317
- export {
4318
- SchemaTypeTool,
4319
- assist,
4320
- contextDocumentTypeName,
4321
- defaultLanguageOutputs,
4322
- defineAssistFieldAction,
4323
- defineAssistFieldActionGroup,
4324
- defineFieldActionDivider,
4325
- isType,
4326
- useUserInput
4327
- };
4328
- //# sourceMappingURL=index.mjs.map