@ai-stack/payloadcms 3.2.21-beta → 3.2.23-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +186 -160
- package/dist/ai/models/anthropic/index.js.map +1 -1
- package/dist/ai/models/elevenLabs/index.js.map +1 -1
- package/dist/ai/models/google/generateImage.d.ts +9 -0
- package/dist/ai/models/google/generateImage.js +27 -0
- package/dist/ai/models/google/generateImage.js.map +1 -0
- package/dist/ai/models/google/index.d.ts +2 -0
- package/dist/ai/models/google/index.js +201 -0
- package/dist/ai/models/google/index.js.map +1 -0
- package/dist/ai/models/index.js +2 -0
- package/dist/ai/models/index.js.map +1 -1
- package/dist/ai/models/openai/index.js.map +1 -1
- package/dist/ai/schemas/lexicalJsonSchema.js.map +1 -1
- package/dist/ai/utils/editImagesWithOpenAI.js.map +1 -1
- package/dist/ai/utils/filterEditorSchemaByNodes.js +2 -1
- package/dist/ai/utils/filterEditorSchemaByNodes.js.map +1 -1
- package/dist/ai/utils/generateFileNameByPrompt.js.map +1 -1
- package/dist/defaults.js.map +1 -1
- package/dist/endpoints/index.js.map +1 -1
- package/dist/fields/ComposeField/ComposeField.js.map +1 -1
- package/dist/init.js.map +1 -1
- package/dist/libraries/handlebars/helpers.js.map +1 -1
- package/dist/providers/InstructionsProvider/InstructionsProvider.js.map +1 -1
- package/dist/providers/InstructionsProvider/useInstructions.js.map +1 -1
- package/dist/ui/Compose/Compose.js.map +1 -1
- package/dist/ui/Compose/UndoRedoActions.js.map +1 -1
- package/dist/ui/Compose/hooks/menu/TranslateMenu.js +1 -2
- package/dist/ui/Compose/hooks/menu/TranslateMenu.js.map +1 -1
- package/dist/ui/Compose/hooks/menu/TranslateMenu.jsx +1 -1
- package/dist/ui/Compose/hooks/menu/useMenu.js.map +1 -1
- package/dist/ui/Compose/hooks/useActiveFieldTracking.js +98 -21
- package/dist/ui/Compose/hooks/useActiveFieldTracking.js.map +1 -1
- package/dist/ui/Compose/hooks/useGenerate.js.map +1 -1
- package/dist/ui/Compose/hooks/useHistory.js.map +1 -1
- package/dist/utilities/fieldToJsonSchema.js.map +1 -1
- package/dist/utilities/updateFieldsConfig.js.map +1 -1
- package/package.json +74 -38
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useEffect } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Allowed field type classes that should show the active state
|
|
5
|
+
*/ const ALLOWED_FIELD_TYPES = [
|
|
6
|
+
'upload',
|
|
7
|
+
'text',
|
|
8
|
+
'textarea',
|
|
9
|
+
'rich-text-lexical'
|
|
10
|
+
];
|
|
3
11
|
let currentContainer = null;
|
|
12
|
+
let rafId = null // Track RAF to cancel if needed
|
|
13
|
+
;
|
|
4
14
|
/**
|
|
5
15
|
* Safely escape CSS selector values
|
|
6
16
|
*/ const cssEscape = (value)=>{
|
|
@@ -21,30 +31,62 @@ let currentContainer = null;
|
|
|
21
31
|
const control = document.querySelector(selector);
|
|
22
32
|
return control?.closest('.field-type') ?? null;
|
|
23
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* Check if a container has one of the allowed field type classes
|
|
36
|
+
*/ const isAllowedFieldType = (container)=>{
|
|
37
|
+
return ALLOWED_FIELD_TYPES.some((type)=>container.classList.contains(type) || container.classList.contains(`field-type-${type}`));
|
|
38
|
+
};
|
|
24
39
|
/**
|
|
25
40
|
* Resolve the .field-type container for a given event target
|
|
41
|
+
* Only returns containers that match allowed field types
|
|
26
42
|
*/ const resolveContainerFromTarget = (target)=>{
|
|
27
43
|
if (!(target instanceof HTMLElement)) {
|
|
28
44
|
return null;
|
|
29
45
|
}
|
|
30
46
|
// Check for direct parent first
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
47
|
+
let container = target.closest('.field-type');
|
|
48
|
+
// If not found, fall back to React Select logic
|
|
49
|
+
if (!container) {
|
|
50
|
+
container = findContainerFromReactSelect(target);
|
|
34
51
|
}
|
|
35
|
-
//
|
|
36
|
-
|
|
52
|
+
// Only return if it's an allowed field type
|
|
53
|
+
if (container && isAllowedFieldType(container)) {
|
|
54
|
+
return container;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
37
57
|
};
|
|
38
58
|
/**
|
|
39
59
|
* Update the active container and toggle CSS class
|
|
60
|
+
* - Avoids acting on disconnected nodes
|
|
61
|
+
* - Avoids redundant class work
|
|
40
62
|
*/ const setActiveContainer = (next)=>{
|
|
63
|
+
// Normalize both references against disconnected nodes
|
|
64
|
+
if (currentContainer && !currentContainer.isConnected) {
|
|
65
|
+
currentContainer = null;
|
|
66
|
+
}
|
|
67
|
+
if (next && !next.isConnected) {
|
|
68
|
+
next = null;
|
|
69
|
+
}
|
|
41
70
|
if (currentContainer === next) {
|
|
42
71
|
return;
|
|
43
72
|
}
|
|
44
73
|
currentContainer?.classList.remove('ai-plugin-active');
|
|
45
|
-
next
|
|
74
|
+
if (next) {
|
|
75
|
+
next.classList.add('ai-plugin-active');
|
|
76
|
+
}
|
|
46
77
|
currentContainer = next;
|
|
47
78
|
};
|
|
79
|
+
const clearActiveContainer = ()=>{
|
|
80
|
+
if (currentContainer) {
|
|
81
|
+
currentContainer.classList.remove('ai-plugin-active');
|
|
82
|
+
currentContainer = null;
|
|
83
|
+
}
|
|
84
|
+
// Cancel any pending RAF
|
|
85
|
+
if (rafId !== null) {
|
|
86
|
+
cancelAnimationFrame(rafId);
|
|
87
|
+
rafId = null;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
48
90
|
const isInteractiveElement = (element)=>{
|
|
49
91
|
const tagName = element.tagName.toLowerCase();
|
|
50
92
|
const interactiveTags = [
|
|
@@ -79,15 +121,16 @@ const isInteractiveElement = (element)=>{
|
|
|
79
121
|
if (!(target instanceof HTMLElement)) {
|
|
80
122
|
return;
|
|
81
123
|
}
|
|
124
|
+
// Early exit if we're already inside the current container
|
|
125
|
+
if (currentContainer?.isConnected && currentContainer.contains(target)) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
82
128
|
// Only activate if the focused element is actually interactive
|
|
83
129
|
if (!isInteractiveElement(target)) {
|
|
84
130
|
return;
|
|
85
131
|
}
|
|
86
132
|
const container = resolveContainerFromTarget(target);
|
|
87
|
-
|
|
88
|
-
if (container) {
|
|
89
|
-
setActiveContainer(container);
|
|
90
|
-
}
|
|
133
|
+
setActiveContainer(container);
|
|
91
134
|
};
|
|
92
135
|
/**
|
|
93
136
|
* Handle pointer/mouse events - only switch when clicking a different .field-type
|
|
@@ -96,11 +139,12 @@ const isInteractiveElement = (element)=>{
|
|
|
96
139
|
if (!(target instanceof HTMLElement)) {
|
|
97
140
|
return;
|
|
98
141
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
setActiveContainer(container);
|
|
142
|
+
// Early exit if clicking within current container
|
|
143
|
+
if (currentContainer?.isConnected && currentContainer.contains(target)) {
|
|
144
|
+
return;
|
|
103
145
|
}
|
|
146
|
+
const container = resolveContainerFromTarget(target);
|
|
147
|
+
setActiveContainer(container);
|
|
104
148
|
};
|
|
105
149
|
/**
|
|
106
150
|
* Handle keyboard navigation (Tab key)
|
|
@@ -108,11 +152,25 @@ const isInteractiveElement = (element)=>{
|
|
|
108
152
|
if (e.key !== 'Tab') {
|
|
109
153
|
return;
|
|
110
154
|
}
|
|
111
|
-
|
|
155
|
+
// Cancel any pending RAF to prevent queuing
|
|
156
|
+
if (rafId !== null) {
|
|
157
|
+
cancelAnimationFrame(rafId);
|
|
158
|
+
}
|
|
159
|
+
// Defer until after focus has shifted
|
|
160
|
+
rafId = requestAnimationFrame(()=>{
|
|
161
|
+
rafId = null;
|
|
112
162
|
const container = resolveContainerFromTarget(document.activeElement);
|
|
113
163
|
setActiveContainer(container);
|
|
114
164
|
});
|
|
115
165
|
};
|
|
166
|
+
/**
|
|
167
|
+
* Handle visibility changes to properly cleanup when page is hidden
|
|
168
|
+
*/ const onVisibilityChange = ()=>{
|
|
169
|
+
if (typeof document !== 'undefined' && document.hidden) {
|
|
170
|
+
// Clear active state and cancel pending operations
|
|
171
|
+
clearActiveContainer();
|
|
172
|
+
}
|
|
173
|
+
};
|
|
116
174
|
/**
|
|
117
175
|
* Initialize document-level listeners to track the active field container.
|
|
118
176
|
* When a container is active, it receives the 'ai-plugin-active' class.
|
|
@@ -126,18 +184,37 @@ const isInteractiveElement = (element)=>{
|
|
|
126
184
|
pluginWindow.__aiComposeTrackingCount = (pluginWindow.__aiComposeTrackingCount ?? 0) + 1;
|
|
127
185
|
// Initialize listeners only once
|
|
128
186
|
if (!pluginWindow.__aiComposeTracking) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
187
|
+
const controller = new AbortController();
|
|
188
|
+
pluginWindow.__aiComposeTrackingController = controller;
|
|
189
|
+
// Use capture for early handling
|
|
190
|
+
document.addEventListener('focusin', onFocusIn, {
|
|
191
|
+
capture: true,
|
|
192
|
+
signal: controller.signal
|
|
193
|
+
});
|
|
194
|
+
document.addEventListener('pointerdown', onPointerDown, {
|
|
195
|
+
capture: true,
|
|
196
|
+
passive: true,
|
|
197
|
+
signal: controller.signal
|
|
198
|
+
});
|
|
199
|
+
document.addEventListener('keydown', onKeyDown, {
|
|
200
|
+
capture: true,
|
|
201
|
+
signal: controller.signal
|
|
202
|
+
});
|
|
203
|
+
document.addEventListener('visibilitychange', onVisibilityChange, {
|
|
204
|
+
signal: controller.signal
|
|
205
|
+
});
|
|
132
206
|
pluginWindow.__aiComposeTracking = true;
|
|
133
207
|
}
|
|
134
208
|
return ()=>{
|
|
135
209
|
// Decrement and cleanup when the last user unmounts
|
|
136
210
|
pluginWindow.__aiComposeTrackingCount = (pluginWindow.__aiComposeTrackingCount ?? 1) - 1;
|
|
137
211
|
if ((pluginWindow.__aiComposeTrackingCount ?? 0) <= 0) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
212
|
+
// Atomically remove all listeners
|
|
213
|
+
pluginWindow.__aiComposeTrackingController?.abort();
|
|
214
|
+
pluginWindow.__aiComposeTrackingController = undefined;
|
|
215
|
+
// Clear active state and cancel pending operations
|
|
216
|
+
clearActiveContainer();
|
|
217
|
+
// Reset all state
|
|
141
218
|
pluginWindow.__aiComposeTracking = false;
|
|
142
219
|
pluginWindow.__aiComposeTrackingCount = 0;
|
|
143
220
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/ui/Compose/hooks/useActiveFieldTracking.ts"],"sourcesContent":["'use client'\n\nimport { useEffect } from 'react'\n\nlet currentContainer: HTMLElement | null = null\n\n/**\n * Safely escape CSS selector values\n */\nconst cssEscape = (value: string): string => {\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {\n return CSS.escape(value)\n }\n return value.replace(/([ #;?%&,.+*~':\"!^$[\\]()=>|/@])/g, '\\\\$1')\n}\n\n/**\n * Find container from React Select dropdown elements\n */\nconst findContainerFromReactSelect = (target: HTMLElement): HTMLElement | null => {\n const listbox = target.closest<HTMLElement>('[role=\"listbox\"]')\n if (!listbox?.id) {\n return null\n }\n\n const id = cssEscape(listbox.id)\n const selector = `[aria-controls=\"${id}\"], [aria-owns=\"${id}\"]`\n const control = document.querySelector<HTMLElement>(selector)\n\n return control?.closest<HTMLElement>('.field-type') ?? null\n}\n\n/**\n * Resolve the .field-type container for a given event target\n */\nconst resolveContainerFromTarget = (target: EventTarget | null): HTMLElement | null => {\n if (!(target instanceof HTMLElement)) {\n return null\n }\n\n // Check for direct parent first\n const direct = target.closest<HTMLElement>('.field-type')\n if (direct) {\n return direct\n }\n\n // Fall back to React Select logic\n return findContainerFromReactSelect(target)\n}\n\n/**\n * Update the active container and toggle CSS class\n */\nconst setActiveContainer = (next: HTMLElement | null): void => {\n if (currentContainer === next) {\n return\n }\n\n currentContainer?.classList.remove('ai-plugin-active')\n next?.classList.add('ai-plugin-active')\n currentContainer = next\n}\n\nconst isInteractiveElement = (element: HTMLElement): boolean => {\n const tagName = element.tagName.toLowerCase()\n const interactiveTags = ['input', 'textarea', 'select', 'button']\n\n if (interactiveTags.includes(tagName)) {\n return true\n }\n\n // Check for contenteditable\n if (element.isContentEditable) {\n return true\n }\n\n // Check for elements with role=\"textbox\" or role=\"combobox\" (React Select)\n const role = element.getAttribute('role')\n if (role && ['combobox', 'listbox', 'searchbox', 'textbox'].includes(role)) {\n return true\n }\n\n return false\n}\n\n/**\n * Handle focus events - only activate if focus is on an interactive element within .field-type\n */\nconst onFocusIn = (e: FocusEvent): void => {\n const target = e.target\n if (!(target instanceof HTMLElement)) {\n return\n }\n\n // Only activate if the focused element is actually interactive\n if (!isInteractiveElement(target)) {\n return\n }\n\n const container = resolveContainerFromTarget(target)\n // Only update if we found a new container\n if (container) {\n setActiveContainer(container)\n }\n}\n\n/**\n * Handle pointer/mouse events - only switch when clicking a different .field-type\n */\nconst onPointerDown = (e: PointerEvent): void => {\n const target = e.target\n if (!(target instanceof HTMLElement)) {\n return\n }\n\n const container = resolveContainerFromTarget(target)\n\n // Only update if we found a container (keeps last active if clicking elsewhere)\n if (container) {\n setActiveContainer(container)\n }\n}\n\n/**\n * Handle keyboard navigation (Tab key)\n */\nconst onKeyDown = (e: KeyboardEvent): void => {\n if (e.key !== 'Tab') {\n return\n }\n\n requestAnimationFrame(() => {\n const container = resolveContainerFromTarget(document.activeElement)\n setActiveContainer(container)\n })\n}\n\n/**\n * Initialize document-level listeners to track the active field container.\n * When a container is active, it receives the 'ai-plugin-active' class.\n */\nexport const useActiveFieldTracking = (): void => {\n useEffect(() => {\n if (typeof window === 'undefined') {\n return\n }\n\n const pluginWindow = window as {\n __aiComposeTracking?: boolean\n __aiComposeTrackingCount?: number\n } & Window\n\n // Track number of mounted users of the hook\n pluginWindow.__aiComposeTrackingCount = (pluginWindow.__aiComposeTrackingCount ?? 0) + 1\n\n // Initialize listeners only once\n if (!pluginWindow.__aiComposeTracking) {\n document.addEventListener('focusin', onFocusIn, true)\n document.addEventListener('pointerdown', onPointerDown, true)\n document.addEventListener('keydown', onKeyDown, true)\n pluginWindow.__aiComposeTracking = true\n }\n\n return () => {\n // Decrement and cleanup when the last user unmounts\n pluginWindow.__aiComposeTrackingCount = (pluginWindow.__aiComposeTrackingCount ?? 1) - 1\n if ((pluginWindow.__aiComposeTrackingCount ?? 0) <= 0) {\n document.removeEventListener('focusin', onFocusIn, true)\n document.removeEventListener('pointerdown', onPointerDown, true)\n document.removeEventListener('keydown', onKeyDown, true)\n pluginWindow.__aiComposeTracking = false\n pluginWindow.__aiComposeTrackingCount = 0\n }\n }\n }, [])\n}\n"],"names":["useEffect","currentContainer","cssEscape","value","CSS","escape","replace","findContainerFromReactSelect","target","listbox","closest","id","selector","control","document","querySelector","resolveContainerFromTarget","HTMLElement","direct","setActiveContainer","next","classList","remove","add","isInteractiveElement","element","tagName","toLowerCase","interactiveTags","includes","isContentEditable","role","getAttribute","onFocusIn","e","container","onPointerDown","onKeyDown","key","requestAnimationFrame","activeElement","useActiveFieldTracking","window","pluginWindow","__aiComposeTrackingCount","__aiComposeTracking","addEventListener","removeEventListener"],"mappings":"AAAA;AAEA,SAASA,SAAS,QAAQ,QAAO;AAEjC,IAAIC,mBAAuC;AAE3C;;CAEC,GACD,MAAMC,YAAY,CAACC;IACjB,IAAI,OAAOC,QAAQ,eAAe,OAAOA,IAAIC,MAAM,KAAK,YAAY;QAClE,OAAOD,IAAIC,MAAM,CAACF;IACpB;IACA,OAAOA,MAAMG,OAAO,CAAC,oCAAoC;AAC3D;AAEA;;CAEC,GACD,MAAMC,+BAA+B,CAACC;IACpC,MAAMC,UAAUD,OAAOE,OAAO,CAAc;IAC5C,IAAI,CAACD,SAASE,IAAI;QAChB,OAAO;IACT;IAEA,MAAMA,KAAKT,UAAUO,QAAQE,EAAE;IAC/B,MAAMC,WAAW,CAAC,gBAAgB,EAAED,GAAG,gBAAgB,EAAEA,GAAG,EAAE,CAAC;IAC/D,MAAME,UAAUC,SAASC,aAAa,CAAcH;IAEpD,OAAOC,SAASH,QAAqB,kBAAkB;AACzD;AAEA;;CAEC,GACD,MAAMM,6BAA6B,CAACR;IAClC,IAAI,CAAEA,CAAAA,kBAAkBS,WAAU,GAAI;QACpC,OAAO;IACT;IAEA,gCAAgC;IAChC,MAAMC,SAASV,OAAOE,OAAO,CAAc;IAC3C,IAAIQ,QAAQ;QACV,OAAOA;IACT;IAEA,kCAAkC;IAClC,OAAOX,6BAA6BC;AACtC;AAEA;;CAEC,GACD,MAAMW,qBAAqB,CAACC;IAC1B,IAAInB,qBAAqBmB,MAAM;QAC7B;IACF;IAEAnB,kBAAkBoB,UAAUC,OAAO;IACnCF,MAAMC,UAAUE,IAAI;IACpBtB,mBAAmBmB;AACrB;AAEA,MAAMI,uBAAuB,CAACC;IAC5B,MAAMC,UAAUD,QAAQC,OAAO,CAACC,WAAW;IAC3C,MAAMC,kBAAkB;QAAC;QAAS;QAAY;QAAU;KAAS;IAEjE,IAAIA,gBAAgBC,QAAQ,CAACH,UAAU;QACrC,OAAO;IACT;IAEA,4BAA4B;IAC5B,IAAID,QAAQK,iBAAiB,EAAE;QAC7B,OAAO;IACT;IAEA,2EAA2E;IAC3E,MAAMC,OAAON,QAAQO,YAAY,CAAC;IAClC,IAAID,QAAQ;QAAC;QAAY;QAAW;QAAa;KAAU,CAACF,QAAQ,CAACE,OAAO;QAC1E,OAAO;IACT;IAEA,OAAO;AACT;AAEA;;CAEC,GACD,MAAME,YAAY,CAACC;IACjB,MAAM1B,SAAS0B,EAAE1B,MAAM;IACvB,IAAI,CAAEA,CAAAA,kBAAkBS,WAAU,GAAI;QACpC;IACF;IAEA,+DAA+D;IAC/D,IAAI,CAACO,qBAAqBhB,SAAS;QACjC;IACF;IAEA,MAAM2B,YAAYnB,2BAA2BR;IAC7C,0CAA0C;IAC1C,IAAI2B,WAAW;QACbhB,mBAAmBgB;IACrB;AACF;AAEA;;CAEC,GACD,MAAMC,gBAAgB,CAACF;IACrB,MAAM1B,SAAS0B,EAAE1B,MAAM;IACvB,IAAI,CAAEA,CAAAA,kBAAkBS,WAAU,GAAI;QACpC;IACF;IAEA,MAAMkB,YAAYnB,2BAA2BR;IAE7C,gFAAgF;IAChF,IAAI2B,WAAW;QACbhB,mBAAmBgB;IACrB;AACF;AAEA;;CAEC,GACD,MAAME,YAAY,CAACH;IACjB,IAAIA,EAAEI,GAAG,KAAK,OAAO;QACnB;IACF;IAEAC,sBAAsB;QACpB,MAAMJ,YAAYnB,2BAA2BF,SAAS0B,aAAa;QACnErB,mBAAmBgB;IACrB;AACF;AAEA;;;CAGC,GACD,OAAO,MAAMM,yBAAyB;IACpCzC,UAAU;QACR,IAAI,OAAO0C,WAAW,aAAa;YACjC;QACF;QAEA,MAAMC,eAAeD;QAKrB,4CAA4C;QAC5CC,aAAaC,wBAAwB,GAAG,AAACD,CAAAA,aAAaC,wBAAwB,IAAI,CAAA,IAAK;QAEvF,iCAAiC;QACjC,IAAI,CAACD,aAAaE,mBAAmB,EAAE;YACrC/B,SAASgC,gBAAgB,CAAC,WAAWb,WAAW;YAChDnB,SAASgC,gBAAgB,CAAC,eAAeV,eAAe;YACxDtB,SAASgC,gBAAgB,CAAC,WAAWT,WAAW;YAChDM,aAAaE,mBAAmB,GAAG;QACrC;QAEA,OAAO;YACL,oDAAoD;YACpDF,aAAaC,wBAAwB,GAAG,AAACD,CAAAA,aAAaC,wBAAwB,IAAI,CAAA,IAAK;YACvF,IAAI,AAACD,CAAAA,aAAaC,wBAAwB,IAAI,CAAA,KAAM,GAAG;gBACrD9B,SAASiC,mBAAmB,CAAC,WAAWd,WAAW;gBACnDnB,SAASiC,mBAAmB,CAAC,eAAeX,eAAe;gBAC3DtB,SAASiC,mBAAmB,CAAC,WAAWV,WAAW;gBACnDM,aAAaE,mBAAmB,GAAG;gBACnCF,aAAaC,wBAAwB,GAAG;YAC1C;QACF;IACF,GAAG,EAAE;AACP,EAAC"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/ui/Compose/hooks/useActiveFieldTracking.ts"],"sourcesContent":["'use client'\n\nimport { useEffect } from 'react'\n\n/**\n * Allowed field type classes that should show the active state\n */\nconst ALLOWED_FIELD_TYPES = ['upload', 'text', 'textarea', 'rich-text-lexical']\n\nlet currentContainer: HTMLElement | null = null\nlet rafId: null | number = null // Track RAF to cancel if needed\n\n/**\n * Safely escape CSS selector values\n */\nconst cssEscape = (value: string): string => {\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {\n return CSS.escape(value)\n }\n return value.replace(/([ #;?%&,.+*~':\"!^$[\\]()=>|/@])/g, '\\\\$1')\n}\n\n/**\n * Find container from React Select dropdown elements\n */\nconst findContainerFromReactSelect = (target: HTMLElement): HTMLElement | null => {\n const listbox = target.closest<HTMLElement>('[role=\"listbox\"]')\n if (!listbox?.id) {\n return null\n }\n\n const id = cssEscape(listbox.id)\n const selector = `[aria-controls=\"${id}\"], [aria-owns=\"${id}\"]`\n const control = document.querySelector<HTMLElement>(selector)\n\n return control?.closest<HTMLElement>('.field-type') ?? null\n}\n\n/**\n * Check if a container has one of the allowed field type classes\n */\nconst isAllowedFieldType = (container: HTMLElement): boolean => {\n return ALLOWED_FIELD_TYPES.some(\n (type) =>\n container.classList.contains(type) || container.classList.contains(`field-type-${type}`),\n )\n}\n\n/**\n * Resolve the .field-type container for a given event target\n * Only returns containers that match allowed field types\n */\nconst resolveContainerFromTarget = (target: EventTarget | null): HTMLElement | null => {\n if (!(target instanceof HTMLElement)) {\n return null\n }\n\n // Check for direct parent first\n let container = target.closest<HTMLElement>('.field-type')\n\n // If not found, fall back to React Select logic\n if (!container) {\n container = findContainerFromReactSelect(target)\n }\n\n // Only return if it's an allowed field type\n if (container && isAllowedFieldType(container)) {\n return container\n }\n\n return null\n}\n\n/**\n * Update the active container and toggle CSS class\n * - Avoids acting on disconnected nodes\n * - Avoids redundant class work\n */\nconst setActiveContainer = (next: HTMLElement | null): void => {\n // Normalize both references against disconnected nodes\n if (currentContainer && !currentContainer.isConnected) {\n currentContainer = null\n }\n if (next && !next.isConnected) {\n next = null\n }\n\n if (currentContainer === next) {\n return\n }\n\n currentContainer?.classList.remove('ai-plugin-active')\n if (next) {\n next.classList.add('ai-plugin-active')\n }\n currentContainer = next\n}\n\nconst clearActiveContainer = (): void => {\n if (currentContainer) {\n currentContainer.classList.remove('ai-plugin-active')\n currentContainer = null\n }\n\n // Cancel any pending RAF\n if (rafId !== null) {\n cancelAnimationFrame(rafId)\n rafId = null\n }\n}\n\nconst isInteractiveElement = (element: HTMLElement): boolean => {\n const tagName = element.tagName.toLowerCase()\n const interactiveTags = ['input', 'textarea', 'select', 'button']\n\n if (interactiveTags.includes(tagName)) {\n return true\n }\n\n // Check for contenteditable\n if (element.isContentEditable) {\n return true\n }\n\n // Check for elements with role=\"textbox\" or role=\"combobox\" (React Select)\n const role = element.getAttribute('role')\n if (role && ['combobox', 'listbox', 'searchbox', 'textbox'].includes(role)) {\n return true\n }\n\n return false\n}\n\n/**\n * Handle focus events - only activate if focus is on an interactive element within .field-type\n */\nconst onFocusIn = (e: FocusEvent): void => {\n const target = e.target\n if (!(target instanceof HTMLElement)) {\n return\n }\n\n // Early exit if we're already inside the current container\n if (currentContainer?.isConnected && currentContainer.contains(target)) {\n return\n }\n\n // Only activate if the focused element is actually interactive\n if (!isInteractiveElement(target)) {\n return\n }\n\n const container = resolveContainerFromTarget(target)\n setActiveContainer(container)\n}\n\n/**\n * Handle pointer/mouse events - only switch when clicking a different .field-type\n */\nconst onPointerDown = (e: PointerEvent): void => {\n const target = e.target\n if (!(target instanceof HTMLElement)) {\n return\n }\n\n // Early exit if clicking within current container\n if (currentContainer?.isConnected && currentContainer.contains(target)) {\n return\n }\n\n const container = resolveContainerFromTarget(target)\n setActiveContainer(container)\n}\n\n/**\n * Handle keyboard navigation (Tab key)\n */\nconst onKeyDown = (e: KeyboardEvent): void => {\n if (e.key !== 'Tab') {\n return\n }\n\n // Cancel any pending RAF to prevent queuing\n if (rafId !== null) {\n cancelAnimationFrame(rafId)\n }\n\n // Defer until after focus has shifted\n rafId = requestAnimationFrame(() => {\n rafId = null\n const container = resolveContainerFromTarget(document.activeElement)\n setActiveContainer(container)\n })\n}\n\n/**\n * Handle visibility changes to properly cleanup when page is hidden\n */\nconst onVisibilityChange = (): void => {\n if (typeof document !== 'undefined' && document.hidden) {\n // Clear active state and cancel pending operations\n clearActiveContainer()\n }\n}\n\n/**\n * Initialize document-level listeners to track the active field container.\n * When a container is active, it receives the 'ai-plugin-active' class.\n */\nexport const useActiveFieldTracking = (): void => {\n useEffect(() => {\n if (typeof window === 'undefined') {\n return\n }\n\n const pluginWindow = window as {\n __aiComposeTracking?: boolean\n __aiComposeTrackingController?: AbortController\n __aiComposeTrackingCount?: number\n } & Window\n\n // Track number of mounted users of the hook\n pluginWindow.__aiComposeTrackingCount = (pluginWindow.__aiComposeTrackingCount ?? 0) + 1\n\n // Initialize listeners only once\n if (!pluginWindow.__aiComposeTracking) {\n const controller = new AbortController()\n pluginWindow.__aiComposeTrackingController = controller\n\n // Use capture for early handling\n document.addEventListener('focusin', onFocusIn, {\n capture: true,\n signal: controller.signal,\n })\n document.addEventListener('pointerdown', onPointerDown, {\n capture: true,\n passive: true,\n signal: controller.signal,\n })\n document.addEventListener('keydown', onKeyDown, {\n capture: true,\n signal: controller.signal,\n })\n document.addEventListener('visibilitychange', onVisibilityChange, {\n signal: controller.signal,\n })\n\n pluginWindow.__aiComposeTracking = true\n }\n\n return () => {\n // Decrement and cleanup when the last user unmounts\n pluginWindow.__aiComposeTrackingCount = (pluginWindow.__aiComposeTrackingCount ?? 1) - 1\n\n if ((pluginWindow.__aiComposeTrackingCount ?? 0) <= 0) {\n // Atomically remove all listeners\n pluginWindow.__aiComposeTrackingController?.abort()\n pluginWindow.__aiComposeTrackingController = undefined\n\n // Clear active state and cancel pending operations\n clearActiveContainer()\n\n // Reset all state\n pluginWindow.__aiComposeTracking = false\n pluginWindow.__aiComposeTrackingCount = 0\n }\n }\n }, [])\n}\n"],"names":["useEffect","ALLOWED_FIELD_TYPES","currentContainer","rafId","cssEscape","value","CSS","escape","replace","findContainerFromReactSelect","target","listbox","closest","id","selector","control","document","querySelector","isAllowedFieldType","container","some","type","classList","contains","resolveContainerFromTarget","HTMLElement","setActiveContainer","next","isConnected","remove","add","clearActiveContainer","cancelAnimationFrame","isInteractiveElement","element","tagName","toLowerCase","interactiveTags","includes","isContentEditable","role","getAttribute","onFocusIn","e","onPointerDown","onKeyDown","key","requestAnimationFrame","activeElement","onVisibilityChange","hidden","useActiveFieldTracking","window","pluginWindow","__aiComposeTrackingCount","__aiComposeTracking","controller","AbortController","__aiComposeTrackingController","addEventListener","capture","signal","passive","abort","undefined"],"mappings":"AAAA;AAEA,SAASA,SAAS,QAAQ,QAAO;AAEjC;;CAEC,GACD,MAAMC,sBAAsB;IAAC;IAAU;IAAQ;IAAY;CAAoB;AAE/E,IAAIC,mBAAuC;AAC3C,IAAIC,QAAuB,KAAK,gCAAgC;;AAEhE;;CAEC,GACD,MAAMC,YAAY,CAACC;IACjB,IAAI,OAAOC,QAAQ,eAAe,OAAOA,IAAIC,MAAM,KAAK,YAAY;QAClE,OAAOD,IAAIC,MAAM,CAACF;IACpB;IACA,OAAOA,MAAMG,OAAO,CAAC,oCAAoC;AAC3D;AAEA;;CAEC,GACD,MAAMC,+BAA+B,CAACC;IACpC,MAAMC,UAAUD,OAAOE,OAAO,CAAc;IAC5C,IAAI,CAACD,SAASE,IAAI;QAChB,OAAO;IACT;IAEA,MAAMA,KAAKT,UAAUO,QAAQE,EAAE;IAC/B,MAAMC,WAAW,CAAC,gBAAgB,EAAED,GAAG,gBAAgB,EAAEA,GAAG,EAAE,CAAC;IAC/D,MAAME,UAAUC,SAASC,aAAa,CAAcH;IAEpD,OAAOC,SAASH,QAAqB,kBAAkB;AACzD;AAEA;;CAEC,GACD,MAAMM,qBAAqB,CAACC;IAC1B,OAAOlB,oBAAoBmB,IAAI,CAC7B,CAACC,OACCF,UAAUG,SAAS,CAACC,QAAQ,CAACF,SAASF,UAAUG,SAAS,CAACC,QAAQ,CAAC,CAAC,WAAW,EAAEF,KAAK,CAAC;AAE7F;AAEA;;;CAGC,GACD,MAAMG,6BAA6B,CAACd;IAClC,IAAI,CAAEA,CAAAA,kBAAkBe,WAAU,GAAI;QACpC,OAAO;IACT;IAEA,gCAAgC;IAChC,IAAIN,YAAYT,OAAOE,OAAO,CAAc;IAE5C,gDAAgD;IAChD,IAAI,CAACO,WAAW;QACdA,YAAYV,6BAA6BC;IAC3C;IAEA,4CAA4C;IAC5C,IAAIS,aAAaD,mBAAmBC,YAAY;QAC9C,OAAOA;IACT;IAEA,OAAO;AACT;AAEA;;;;CAIC,GACD,MAAMO,qBAAqB,CAACC;IAC1B,uDAAuD;IACvD,IAAIzB,oBAAoB,CAACA,iBAAiB0B,WAAW,EAAE;QACrD1B,mBAAmB;IACrB;IACA,IAAIyB,QAAQ,CAACA,KAAKC,WAAW,EAAE;QAC7BD,OAAO;IACT;IAEA,IAAIzB,qBAAqByB,MAAM;QAC7B;IACF;IAEAzB,kBAAkBoB,UAAUO,OAAO;IACnC,IAAIF,MAAM;QACRA,KAAKL,SAAS,CAACQ,GAAG,CAAC;IACrB;IACA5B,mBAAmByB;AACrB;AAEA,MAAMI,uBAAuB;IAC3B,IAAI7B,kBAAkB;QACpBA,iBAAiBoB,SAAS,CAACO,MAAM,CAAC;QAClC3B,mBAAmB;IACrB;IAEA,yBAAyB;IACzB,IAAIC,UAAU,MAAM;QAClB6B,qBAAqB7B;QACrBA,QAAQ;IACV;AACF;AAEA,MAAM8B,uBAAuB,CAACC;IAC5B,MAAMC,UAAUD,QAAQC,OAAO,CAACC,WAAW;IAC3C,MAAMC,kBAAkB;QAAC;QAAS;QAAY;QAAU;KAAS;IAEjE,IAAIA,gBAAgBC,QAAQ,CAACH,UAAU;QACrC,OAAO;IACT;IAEA,4BAA4B;IAC5B,IAAID,QAAQK,iBAAiB,EAAE;QAC7B,OAAO;IACT;IAEA,2EAA2E;IAC3E,MAAMC,OAAON,QAAQO,YAAY,CAAC;IAClC,IAAID,QAAQ;QAAC;QAAY;QAAW;QAAa;KAAU,CAACF,QAAQ,CAACE,OAAO;QAC1E,OAAO;IACT;IAEA,OAAO;AACT;AAEA;;CAEC,GACD,MAAME,YAAY,CAACC;IACjB,MAAMjC,SAASiC,EAAEjC,MAAM;IACvB,IAAI,CAAEA,CAAAA,kBAAkBe,WAAU,GAAI;QACpC;IACF;IAEA,2DAA2D;IAC3D,IAAIvB,kBAAkB0B,eAAe1B,iBAAiBqB,QAAQ,CAACb,SAAS;QACtE;IACF;IAEA,+DAA+D;IAC/D,IAAI,CAACuB,qBAAqBvB,SAAS;QACjC;IACF;IAEA,MAAMS,YAAYK,2BAA2Bd;IAC7CgB,mBAAmBP;AACrB;AAEA;;CAEC,GACD,MAAMyB,gBAAgB,CAACD;IACrB,MAAMjC,SAASiC,EAAEjC,MAAM;IACvB,IAAI,CAAEA,CAAAA,kBAAkBe,WAAU,GAAI;QACpC;IACF;IAEA,kDAAkD;IAClD,IAAIvB,kBAAkB0B,eAAe1B,iBAAiBqB,QAAQ,CAACb,SAAS;QACtE;IACF;IAEA,MAAMS,YAAYK,2BAA2Bd;IAC7CgB,mBAAmBP;AACrB;AAEA;;CAEC,GACD,MAAM0B,YAAY,CAACF;IACjB,IAAIA,EAAEG,GAAG,KAAK,OAAO;QACnB;IACF;IAEA,4CAA4C;IAC5C,IAAI3C,UAAU,MAAM;QAClB6B,qBAAqB7B;IACvB;IAEA,sCAAsC;IACtCA,QAAQ4C,sBAAsB;QAC5B5C,QAAQ;QACR,MAAMgB,YAAYK,2BAA2BR,SAASgC,aAAa;QACnEtB,mBAAmBP;IACrB;AACF;AAEA;;CAEC,GACD,MAAM8B,qBAAqB;IACzB,IAAI,OAAOjC,aAAa,eAAeA,SAASkC,MAAM,EAAE;QACtD,mDAAmD;QACnDnB;IACF;AACF;AAEA;;;CAGC,GACD,OAAO,MAAMoB,yBAAyB;IACpCnD,UAAU;QACR,IAAI,OAAOoD,WAAW,aAAa;YACjC;QACF;QAEA,MAAMC,eAAeD;QAMrB,4CAA4C;QAC5CC,aAAaC,wBAAwB,GAAG,AAACD,CAAAA,aAAaC,wBAAwB,IAAI,CAAA,IAAK;QAEvF,iCAAiC;QACjC,IAAI,CAACD,aAAaE,mBAAmB,EAAE;YACrC,MAAMC,aAAa,IAAIC;YACvBJ,aAAaK,6BAA6B,GAAGF;YAE7C,iCAAiC;YACjCxC,SAAS2C,gBAAgB,CAAC,WAAWjB,WAAW;gBAC9CkB,SAAS;gBACTC,QAAQL,WAAWK,MAAM;YAC3B;YACA7C,SAAS2C,gBAAgB,CAAC,eAAef,eAAe;gBACtDgB,SAAS;gBACTE,SAAS;gBACTD,QAAQL,WAAWK,MAAM;YAC3B;YACA7C,SAAS2C,gBAAgB,CAAC,WAAWd,WAAW;gBAC9Ce,SAAS;gBACTC,QAAQL,WAAWK,MAAM;YAC3B;YACA7C,SAAS2C,gBAAgB,CAAC,oBAAoBV,oBAAoB;gBAChEY,QAAQL,WAAWK,MAAM;YAC3B;YAEAR,aAAaE,mBAAmB,GAAG;QACrC;QAEA,OAAO;YACL,oDAAoD;YACpDF,aAAaC,wBAAwB,GAAG,AAACD,CAAAA,aAAaC,wBAAwB,IAAI,CAAA,IAAK;YAEvF,IAAI,AAACD,CAAAA,aAAaC,wBAAwB,IAAI,CAAA,KAAM,GAAG;gBACrD,kCAAkC;gBAClCD,aAAaK,6BAA6B,EAAEK;gBAC5CV,aAAaK,6BAA6B,GAAGM;gBAE7C,mDAAmD;gBACnDjC;gBAEA,kBAAkB;gBAClBsB,aAAaE,mBAAmB,GAAG;gBACnCF,aAAaC,wBAAwB,GAAG;YAC1C;QACF;IACF,GAAG,EAAE;AACP,EAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/ui/Compose/hooks/useGenerate.ts"],"sourcesContent":["import { experimental_useObject as useObject } from '@ai-sdk/react'\nimport { useEditorConfigContext } from '@payloadcms/richtext-lexical/client'\nimport { toast, useConfig, useDocumentInfo, useField, useForm, useLocale } from '@payloadcms/ui'\nimport { jsonSchema } from 'ai'\nimport { useCallback, useEffect, useMemo, useRef } from 'react'\n\nimport type { ActionMenuItems, GenerateTextarea } from '../../../types.js'\n\nimport {\n PLUGIN_API_ENDPOINT_GENERATE,\n PLUGIN_API_ENDPOINT_GENERATE_UPLOAD,\n PLUGIN_INSTRUCTIONS_TABLE,\n PLUGIN_NAME,\n} from '../../../defaults.js'\nimport { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js'\nimport { editorSchemaValidator } from '../../../utilities/editorSchemaValidator.js'\nimport { fieldToJsonSchema } from '../../../utilities/fieldToJsonSchema.js'\nimport { setSafeLexicalState } from '../../../utilities/setSafeLexicalState.js'\nimport { useHistory } from './useHistory.js'\n\ntype ActionCallbackParams = { action: ActionMenuItems; params?: unknown }\n\nexport const useGenerate = ({ instructionId }: { instructionId: string }) => {\n // Create a ref to hold the current instructionId\n const instructionIdRef = useRef(instructionId)\n\n // Update the ref whenever instructionId changes\n useEffect(() => {\n instructionIdRef.current = instructionId\n }, [instructionId])\n\n const { field, path: pathFromContext } = useFieldProps()\n const editorConfigContext = useEditorConfigContext()\n\n const { editor } = editorConfigContext\n\n const { config } = useConfig()\n const {\n routes: { api },\n serverURL,\n } = config\n\n const { setValue } = useField<any>({\n path: pathFromContext ?? '',\n })\n\n const { set: setHistory } = useHistory()\n\n const { getData } = useForm()\n const { id: documentId, collectionSlug } = useDocumentInfo()\n\n const localFromContext = useLocale()\n const {\n config: { collections },\n } = useConfig()\n\n const collection = collections.find((collection) => collection.slug === PLUGIN_INSTRUCTIONS_TABLE)\n const { custom: { [PLUGIN_NAME]: { editorConfig = {} } = {} } = {} } = collection?.admin ?? {}\n const { schema: editorSchema = {} } = editorConfig\n\n const memoizedValidator = useMemo(() => {\n return editorSchemaValidator(editorSchema)\n }, [editorSchema])\n\n const memoizedSchema = useMemo(\n () =>\n jsonSchema(editorSchema, {\n validate: (value) => {\n const isValid = memoizedValidator(value)\n\n if (isValid) {\n return {\n success: true,\n value,\n }\n } else {\n return {\n error: new Error('Invalid schema'),\n success: false,\n }\n }\n },\n }),\n [memoizedValidator],\n )\n\n // Active JSON schema for useObject based on field type\n const activeSchema = useMemo(() => {\n const f = field as any\n const fieldType = f?.type as string | undefined\n if (fieldType === 'richText') {\n return memoizedSchema\n }\n if (f && f.name && fieldType) {\n const schemaJson = fieldToJsonSchema(f)\n if (schemaJson && Object.keys(schemaJson).length > 0) {\n return jsonSchema(schemaJson)\n }\n }\n return undefined\n }, [field, memoizedSchema])\n\n const {\n isLoading: loadingObject,\n object,\n stop: objectStop,\n submit,\n } = useObject({\n api: `/api${PLUGIN_API_ENDPOINT_GENERATE}`,\n onError: (error: any) => {\n toast.error(`Failed to generate: ${error.message}`)\n console.error('Error generating object:', error)\n },\n onFinish: (result) => {\n if (result.object && field) {\n if (field.type === 'richText') {\n setHistory(result.object)\n setValue(result.object)\n } else if ('name' in field) {\n setHistory(result.object[field.name])\n setValue(result.object[field.name])\n }\n } else {\n console.log('onFinish: result, field ', result, field)\n }\n },\n schema: activeSchema as any,\n })\n\n useEffect(() => {\n if (!object) {\n return\n }\n\n requestAnimationFrame(() => {\n if (field?.type === 'richText') {\n setSafeLexicalState(object, editor)\n } else if (field && 'name' in field && object[field.name]) {\n setValue(object[field.name])\n }\n })\n }, [object, editor, field])\n\n const streamObject = useCallback(\n ({ action = 'Compose', params }: ActionCallbackParams) => {\n const doc = getData()\n\n const currentInstructionId = instructionIdRef.current\n\n const options = {\n action,\n actionParams: params,\n instructionId: currentInstructionId,\n }\n\n submit({\n allowedEditorNodes: Array.from(editor?._nodes?.keys() || []),\n doc: {\n ...doc,\n id: documentId,\n },\n locale: localFromContext?.code,\n options,\n })\n },\n [localFromContext?.code, instructionIdRef, documentId],\n )\n\n const generateUpload = useCallback(async () => {\n const doc = getData()\n const currentInstructionId = instructionIdRef.current\n\n return fetch(`${serverURL}${api}${PLUGIN_API_ENDPOINT_GENERATE_UPLOAD}`, {\n body: JSON.stringify({\n collectionSlug: collectionSlug ?? '',\n doc,\n documentId,\n locale: localFromContext?.code,\n options: {\n instructionId: currentInstructionId,\n },\n } satisfies Parameters<GenerateTextarea>[0]),\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n .then(async (uploadResponse) => {\n if (uploadResponse.ok) {\n const { result } = await uploadResponse.json()\n if (!result) {\n throw new Error('generateUpload: Something went wrong')\n }\n\n setValue(result?.id)\n setHistory(result?.id)\n console.log('Image updated...', result)\n } else {\n const { errors = [] } = await uploadResponse.json()\n const errStr = errors.map((error: any) => error.message).join(', ')\n throw new Error(errStr)\n }\n return uploadResponse\n })\n .catch((error) => {\n toast.error(`Failed to generate: ${error.message}`)\n console.error(\n 'Error generating or setting your upload, please set it manually if its saved in your media files.',\n error,\n )\n })\n }, [getData, localFromContext?.code, instructionIdRef, setValue, documentId, collectionSlug])\n\n const generate = useCallback(\n async (options?: ActionCallbackParams) => {\n if ((field as any)?.type === 'upload') {\n return generateUpload()\n }\n // All supported types use structured object generation when schema is provided server-side\n return streamObject(options ?? { action: 'Compose' })\n },\n [generateUpload, streamObject, field],\n )\n\n const stop = useCallback(() => {\n console.log('Stopping...')\n objectStop()\n }, [objectStop])\n\n return {\n generate,\n isLoading: loadingObject,\n stop,\n }\n}\n"],"names":["experimental_useObject","useObject","useEditorConfigContext","toast","useConfig","useDocumentInfo","useField","useForm","useLocale","jsonSchema","useCallback","useEffect","useMemo","useRef","PLUGIN_API_ENDPOINT_GENERATE","PLUGIN_API_ENDPOINT_GENERATE_UPLOAD","PLUGIN_INSTRUCTIONS_TABLE","PLUGIN_NAME","useFieldProps","editorSchemaValidator","fieldToJsonSchema","setSafeLexicalState","useHistory","useGenerate","instructionId","instructionIdRef","current","field","path","pathFromContext","editorConfigContext","editor","config","routes","api","serverURL","setValue","set","setHistory","getData","id","documentId","collectionSlug","localFromContext","collections","collection","find","slug","custom","editorConfig","admin","schema","editorSchema","memoizedValidator","memoizedSchema","validate","value","isValid","success","error","Error","activeSchema","f","fieldType","type","name","schemaJson","Object","keys","length","undefined","isLoading","loadingObject","object","stop","objectStop","submit","onError","message","console","onFinish","result","log","requestAnimationFrame","streamObject","action","params","doc","currentInstructionId","options","actionParams","allowedEditorNodes","Array","from","_nodes","locale","code","generateUpload","fetch","body","JSON","stringify","credentials","headers","method","then","uploadResponse","ok","json","errors","errStr","map","join","catch","generate"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/ui/Compose/hooks/useGenerate.ts"],"sourcesContent":["import { experimental_useObject as useObject } from '@ai-sdk/react'\nimport { useEditorConfigContext } from '@payloadcms/richtext-lexical/client'\nimport { toast, useConfig, useDocumentInfo, useField, useForm, useLocale } from '@payloadcms/ui'\nimport { jsonSchema } from 'ai'\nimport { useCallback, useEffect, useMemo, useRef } from 'react'\n\nimport type { ActionMenuItems, GenerateTextarea } from '../../../types.js'\n\nimport {\n PLUGIN_API_ENDPOINT_GENERATE,\n PLUGIN_API_ENDPOINT_GENERATE_UPLOAD,\n PLUGIN_INSTRUCTIONS_TABLE,\n PLUGIN_NAME,\n} from '../../../defaults.js'\nimport { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js'\nimport { editorSchemaValidator } from '../../../utilities/editorSchemaValidator.js'\nimport { fieldToJsonSchema } from '../../../utilities/fieldToJsonSchema.js'\nimport { setSafeLexicalState } from '../../../utilities/setSafeLexicalState.js'\nimport { useHistory } from './useHistory.js'\n\ntype ActionCallbackParams = { action: ActionMenuItems; params?: unknown }\n\nexport const useGenerate = ({ instructionId }: { instructionId: string }) => {\n // Create a ref to hold the current instructionId\n const instructionIdRef = useRef(instructionId)\n\n // Update the ref whenever instructionId changes\n useEffect(() => {\n instructionIdRef.current = instructionId\n }, [instructionId])\n\n const { field, path: pathFromContext } = useFieldProps()\n const editorConfigContext = useEditorConfigContext()\n\n const { editor } = editorConfigContext\n\n const { config } = useConfig()\n const {\n routes: { api },\n serverURL,\n } = config\n\n const { setValue } = useField<any>({\n path: pathFromContext ?? '',\n })\n\n const { set: setHistory } = useHistory()\n\n const { getData } = useForm()\n const { id: documentId, collectionSlug } = useDocumentInfo()\n\n const localFromContext = useLocale()\n const {\n config: { collections },\n } = useConfig()\n\n const collection = collections.find((collection) => collection.slug === PLUGIN_INSTRUCTIONS_TABLE)\n const { custom: { [PLUGIN_NAME]: { editorConfig = {} } = {} } = {} } = collection?.admin ?? {}\n const { schema: editorSchema = {} } = editorConfig\n\n const memoizedValidator = useMemo(() => {\n return editorSchemaValidator(editorSchema)\n }, [editorSchema])\n\n const memoizedSchema = useMemo(\n () =>\n jsonSchema(editorSchema, {\n validate: (value) => {\n const isValid = memoizedValidator(value)\n\n if (isValid) {\n return {\n success: true,\n value,\n }\n } else {\n return {\n error: new Error('Invalid schema'),\n success: false,\n }\n }\n },\n }),\n [memoizedValidator],\n )\n\n // Active JSON schema for useObject based on field type\n const activeSchema = useMemo(() => {\n const f = field as any\n const fieldType = f?.type as string | undefined\n if (fieldType === 'richText') {\n return memoizedSchema\n }\n if (f && f.name && fieldType) {\n const schemaJson = fieldToJsonSchema(f)\n if (schemaJson && Object.keys(schemaJson).length > 0) {\n return jsonSchema(schemaJson)\n }\n }\n return undefined\n }, [field, memoizedSchema])\n\n const {\n isLoading: loadingObject,\n object,\n stop: objectStop,\n submit,\n } = useObject({\n api: `/api${PLUGIN_API_ENDPOINT_GENERATE}`,\n onError: (error: any) => {\n toast.error(`Failed to generate: ${error.message}`)\n console.error('Error generating object:', error)\n },\n onFinish: (result) => {\n if (result.object && field) {\n if (field.type === 'richText') {\n setHistory(result.object)\n setValue(result.object)\n } else if ('name' in field) {\n setHistory(result.object[field.name])\n setValue(result.object[field.name])\n }\n } else {\n console.log('onFinish: result, field ', result, field)\n }\n },\n schema: activeSchema as any,\n })\n\n useEffect(() => {\n if (!object) {\n return\n }\n\n requestAnimationFrame(() => {\n if (field?.type === 'richText') {\n setSafeLexicalState(object, editor)\n } else if (field && 'name' in field && object[field.name]) {\n setValue(object[field.name])\n }\n })\n }, [object, editor, field])\n\n const streamObject = useCallback(\n ({ action = 'Compose', params }: ActionCallbackParams) => {\n const doc = getData()\n\n const currentInstructionId = instructionIdRef.current\n\n const options = {\n action,\n actionParams: params,\n instructionId: currentInstructionId,\n }\n\n submit({\n allowedEditorNodes: Array.from(editor?._nodes?.keys() || []),\n doc: {\n ...doc,\n id: documentId,\n },\n locale: localFromContext?.code,\n options,\n })\n },\n [localFromContext?.code, instructionIdRef, documentId],\n )\n\n const generateUpload = useCallback(async () => {\n const doc = getData()\n const currentInstructionId = instructionIdRef.current\n\n return fetch(`${serverURL}${api}${PLUGIN_API_ENDPOINT_GENERATE_UPLOAD}`, {\n body: JSON.stringify({\n collectionSlug: collectionSlug ?? '',\n doc,\n documentId,\n locale: localFromContext?.code,\n options: {\n instructionId: currentInstructionId,\n },\n } satisfies Parameters<GenerateTextarea>[0]),\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n .then(async (uploadResponse) => {\n if (uploadResponse.ok) {\n const { result } = await uploadResponse.json()\n if (!result) {\n throw new Error('generateUpload: Something went wrong')\n }\n\n setValue(result?.id)\n setHistory(result?.id)\n console.log('Image updated...', result)\n } else {\n const { errors = [] } = await uploadResponse.json()\n const errStr = errors.map((error: any) => error.message).join(', ')\n throw new Error(errStr)\n }\n return uploadResponse\n })\n .catch((error) => {\n toast.error(`Failed to generate: ${error.message}`)\n console.error(\n 'Error generating or setting your upload, please set it manually if its saved in your media files.',\n error,\n )\n })\n }, [getData, localFromContext?.code, instructionIdRef, setValue, documentId, collectionSlug])\n\n const generate = useCallback(\n async (options?: ActionCallbackParams) => {\n if ((field as any)?.type === 'upload') {\n return generateUpload()\n }\n // All supported types use structured object generation when schema is provided server-side\n return streamObject(options ?? { action: 'Compose' })\n },\n [generateUpload, streamObject, field],\n )\n\n const stop = useCallback(() => {\n console.log('Stopping...')\n objectStop()\n }, [objectStop])\n\n return {\n generate,\n isLoading: loadingObject,\n stop,\n }\n}\n"],"names":["experimental_useObject","useObject","useEditorConfigContext","toast","useConfig","useDocumentInfo","useField","useForm","useLocale","jsonSchema","useCallback","useEffect","useMemo","useRef","PLUGIN_API_ENDPOINT_GENERATE","PLUGIN_API_ENDPOINT_GENERATE_UPLOAD","PLUGIN_INSTRUCTIONS_TABLE","PLUGIN_NAME","useFieldProps","editorSchemaValidator","fieldToJsonSchema","setSafeLexicalState","useHistory","useGenerate","instructionId","instructionIdRef","current","field","path","pathFromContext","editorConfigContext","editor","config","routes","api","serverURL","setValue","set","setHistory","getData","id","documentId","collectionSlug","localFromContext","collections","collection","find","slug","custom","editorConfig","admin","schema","editorSchema","memoizedValidator","memoizedSchema","validate","value","isValid","success","error","Error","activeSchema","f","fieldType","type","name","schemaJson","Object","keys","length","undefined","isLoading","loadingObject","object","stop","objectStop","submit","onError","message","console","onFinish","result","log","requestAnimationFrame","streamObject","action","params","doc","currentInstructionId","options","actionParams","allowedEditorNodes","Array","from","_nodes","locale","code","generateUpload","fetch","body","JSON","stringify","credentials","headers","method","then","uploadResponse","ok","json","errors","errStr","map","join","catch","generate"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/ui/Compose/hooks/useHistory.ts"],"sourcesContent":["'use client'\n\nimport { useDocumentInfo, useField } from '@payloadcms/ui'\nimport { useCallback, useEffect } from 'react'\n\nimport { PLUGIN_NAME } from '../../../defaults.js'\nimport { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js'\n\nconst STORAGE_KEY = `${PLUGIN_NAME}-fields-history`\n\ninterface HistoryState {\n [path: string]: {\n currentIndex: number\n history: any[]\n }\n}\n\nexport const useHistory = () => {\n const { id } = useDocumentInfo()\n const { path: pathFromContext, schemaPath } = useFieldProps()\n const { value: currentFieldValue } = useField<string>({\n path: pathFromContext ?? '',\n })\n\n const fieldKey = `${id}.${schemaPath}`\n\n const getLatestHistory = useCallback((): HistoryState => {\n try {\n // This condition is applied, as it was somehow triggering on server side\n if (typeof localStorage !== 'undefined') {\n return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}')\n }\n return {}\n } catch (e) {\n console.error('Error parsing history:', e)\n return {}\n }\n }, [])\n\n const saveToLocalStorage = useCallback((newGlobalHistory: HistoryState) => {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(newGlobalHistory))\n }\n }, [])\n\n // Clear previous history\n const clearHistory = useCallback(() => {\n const latestHistory = { ...getLatestHistory() }\n Object.keys(latestHistory).forEach((k) => {\n if (!k.startsWith(id?.toString() ?? '')) {\n delete latestHistory[k]\n }\n })\n saveToLocalStorage(latestHistory)\n }, [id, fieldKey, getLatestHistory, saveToLocalStorage])\n\n useEffect(() => {\n // This is applied to clear out the document history which is not currently in use\n clearHistory()\n\n const latestHistory = getLatestHistory()\n const { currentIndex, history } = latestHistory[fieldKey] || {\n currentIndex: -1,\n history: [],\n }\n\n let newIndex = currentIndex\n if (currentIndex == -1) {\n newIndex = 0\n if (currentFieldValue) {\n history[newIndex] = currentFieldValue\n }\n }\n\n const newGlobalHistory = {\n ...latestHistory,\n [fieldKey]: { currentIndex: newIndex, history },\n }\n\n saveToLocalStorage(newGlobalHistory)\n }, [fieldKey])\n\n const set = useCallback(\n (data: any) => {\n const latestHistory = getLatestHistory()\n const { currentIndex, history } = latestHistory[fieldKey] || {\n currentIndex: -1,\n history: [],\n }\n const newHistory = [...history.slice(0, currentIndex + 1), data]\n const newGlobalHistory = {\n ...latestHistory,\n [fieldKey]: { currentIndex: newHistory.length - 1, history: newHistory },\n }\n saveToLocalStorage(newGlobalHistory)\n return data\n },\n [fieldKey, getLatestHistory, saveToLocalStorage],\n )\n\n const undo = useCallback(() => {\n const latestHistory = getLatestHistory()\n const { currentIndex, history } = latestHistory[fieldKey] || { currentIndex: -1, history: [] }\n if (currentIndex > 0) {\n const newIndex = currentIndex - 1\n const newValue = history[newIndex]\n const newGlobalHistory = {\n ...latestHistory,\n [fieldKey]: { currentIndex: newIndex, history },\n }\n saveToLocalStorage(newGlobalHistory)\n return newValue\n }\n return undefined\n }, [fieldKey, getLatestHistory, saveToLocalStorage])\n\n const redo = useCallback(() => {\n const latestHistory = getLatestHistory()\n const { currentIndex, history } = latestHistory[fieldKey] || { currentIndex: -1, history: [] }\n if (currentIndex < history.length - 1) {\n const newIndex = currentIndex + 1\n const newValue = history[newIndex]\n const newGlobalHistory = {\n ...latestHistory,\n [fieldKey]: { currentIndex: newIndex, history },\n }\n saveToLocalStorage(newGlobalHistory)\n return newValue\n }\n return undefined\n }, [fieldKey, getLatestHistory, saveToLocalStorage])\n\n const getLatestFieldHistory = useCallback(() => {\n const latestHistory = getLatestHistory()\n return latestHistory[fieldKey] || { currentIndex: -1, history: [] }\n }, [getLatestHistory, fieldKey])\n\n const fieldHistory = getLatestFieldHistory()\n\n const canUndo = fieldHistory.currentIndex > 0\n const canRedo = fieldHistory.currentIndex < fieldHistory.history.length - 1\n const currentValue = fieldHistory.history[fieldHistory.currentIndex]\n\n return {\n canRedo,\n canUndo,\n currentValue,\n redo,\n set,\n undo,\n }\n}\n"],"names":["useDocumentInfo","useField","useCallback","useEffect","PLUGIN_NAME","useFieldProps","STORAGE_KEY","useHistory","id","path","pathFromContext","schemaPath","value","currentFieldValue","fieldKey","getLatestHistory","localStorage","JSON","parse","getItem","e","console","error","saveToLocalStorage","newGlobalHistory","setItem","stringify","clearHistory","latestHistory","Object","keys","forEach","k","startsWith","toString","currentIndex","history","newIndex","set","data","newHistory","slice","length","undo","newValue","undefined","redo","getLatestFieldHistory","fieldHistory","canUndo","canRedo","currentValue"],"mappings":"AAAA;AAEA,SAASA,eAAe,EAAEC,QAAQ,QAAQ,iBAAgB;AAC1D,SAASC,WAAW,EAAEC,SAAS,QAAQ,QAAO;AAE9C,SAASC,WAAW,QAAQ,uBAAsB;AAClD,SAASC,aAAa,QAAQ,oDAAmD;AAEjF,MAAMC,cAAc,
|
|
1
|
+
{"version":3,"sources":["../../../../src/ui/Compose/hooks/useHistory.ts"],"sourcesContent":["'use client'\n\nimport { useDocumentInfo, useField } from '@payloadcms/ui'\nimport { useCallback, useEffect } from 'react'\n\nimport { PLUGIN_NAME } from '../../../defaults.js'\nimport { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js'\n\nconst STORAGE_KEY = `${PLUGIN_NAME}-fields-history`\n\ninterface HistoryState {\n [path: string]: {\n currentIndex: number\n history: any[]\n }\n}\n\nexport const useHistory = () => {\n const { id } = useDocumentInfo()\n const { path: pathFromContext, schemaPath } = useFieldProps()\n const { value: currentFieldValue } = useField<string>({\n path: pathFromContext ?? '',\n })\n\n const fieldKey = `${id}.${schemaPath}`\n\n const getLatestHistory = useCallback((): HistoryState => {\n try {\n // This condition is applied, as it was somehow triggering on server side\n if (typeof localStorage !== 'undefined') {\n return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}')\n }\n return {}\n } catch (e) {\n console.error('Error parsing history:', e)\n return {}\n }\n }, [])\n\n const saveToLocalStorage = useCallback((newGlobalHistory: HistoryState) => {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(newGlobalHistory))\n }\n }, [])\n\n // Clear previous history\n const clearHistory = useCallback(() => {\n const latestHistory = { ...getLatestHistory() }\n Object.keys(latestHistory).forEach((k) => {\n if (!k.startsWith(id?.toString() ?? '')) {\n delete latestHistory[k]\n }\n })\n saveToLocalStorage(latestHistory)\n }, [id, fieldKey, getLatestHistory, saveToLocalStorage])\n\n useEffect(() => {\n // This is applied to clear out the document history which is not currently in use\n clearHistory()\n\n const latestHistory = getLatestHistory()\n const { currentIndex, history } = latestHistory[fieldKey] || {\n currentIndex: -1,\n history: [],\n }\n\n let newIndex = currentIndex\n if (currentIndex == -1) {\n newIndex = 0\n if (currentFieldValue) {\n history[newIndex] = currentFieldValue\n }\n }\n\n const newGlobalHistory = {\n ...latestHistory,\n [fieldKey]: { currentIndex: newIndex, history },\n }\n\n saveToLocalStorage(newGlobalHistory)\n }, [fieldKey])\n\n const set = useCallback(\n (data: any) => {\n const latestHistory = getLatestHistory()\n const { currentIndex, history } = latestHistory[fieldKey] || {\n currentIndex: -1,\n history: [],\n }\n const newHistory = [...history.slice(0, currentIndex + 1), data]\n const newGlobalHistory = {\n ...latestHistory,\n [fieldKey]: { currentIndex: newHistory.length - 1, history: newHistory },\n }\n saveToLocalStorage(newGlobalHistory)\n return data\n },\n [fieldKey, getLatestHistory, saveToLocalStorage],\n )\n\n const undo = useCallback(() => {\n const latestHistory = getLatestHistory()\n const { currentIndex, history } = latestHistory[fieldKey] || { currentIndex: -1, history: [] }\n if (currentIndex > 0) {\n const newIndex = currentIndex - 1\n const newValue = history[newIndex]\n const newGlobalHistory = {\n ...latestHistory,\n [fieldKey]: { currentIndex: newIndex, history },\n }\n saveToLocalStorage(newGlobalHistory)\n return newValue\n }\n return undefined\n }, [fieldKey, getLatestHistory, saveToLocalStorage])\n\n const redo = useCallback(() => {\n const latestHistory = getLatestHistory()\n const { currentIndex, history } = latestHistory[fieldKey] || { currentIndex: -1, history: [] }\n if (currentIndex < history.length - 1) {\n const newIndex = currentIndex + 1\n const newValue = history[newIndex]\n const newGlobalHistory = {\n ...latestHistory,\n [fieldKey]: { currentIndex: newIndex, history },\n }\n saveToLocalStorage(newGlobalHistory)\n return newValue\n }\n return undefined\n }, [fieldKey, getLatestHistory, saveToLocalStorage])\n\n const getLatestFieldHistory = useCallback(() => {\n const latestHistory = getLatestHistory()\n return latestHistory[fieldKey] || { currentIndex: -1, history: [] }\n }, [getLatestHistory, fieldKey])\n\n const fieldHistory = getLatestFieldHistory()\n\n const canUndo = fieldHistory.currentIndex > 0\n const canRedo = fieldHistory.currentIndex < fieldHistory.history.length - 1\n const currentValue = fieldHistory.history[fieldHistory.currentIndex]\n\n return {\n canRedo,\n canUndo,\n currentValue,\n redo,\n set,\n undo,\n }\n}\n"],"names":["useDocumentInfo","useField","useCallback","useEffect","PLUGIN_NAME","useFieldProps","STORAGE_KEY","useHistory","id","path","pathFromContext","schemaPath","value","currentFieldValue","fieldKey","getLatestHistory","localStorage","JSON","parse","getItem","e","console","error","saveToLocalStorage","newGlobalHistory","setItem","stringify","clearHistory","latestHistory","Object","keys","forEach","k","startsWith","toString","currentIndex","history","newIndex","set","data","newHistory","slice","length","undo","newValue","undefined","redo","getLatestFieldHistory","fieldHistory","canUndo","canRedo","currentValue"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/fieldToJsonSchema.ts"],"sourcesContent":["/**\n * fieldToJsonSchema\n * Convert a Payload Field (server or client) into a minimal JSON Schema object,\n * wrapped as { type: 'object', properties: { [name]: valueSchema }, required: [...] }\n *\n * Supported types:\n * - text, textarea, select, number, date, code, email, json\n * Arrays are emitted only when field.hasMany is true and the field type supports hasMany\n * (text, textarea, select; for others only if your config truly sets hasMany).\n */\n\nexport type JsonSchema = Record<string, any>;\n\n// Narrowed minimal typing to avoid anywhere possible without pulling the full payload types at runtime.\n// In a Payload project, you can import type { Field, ClientField } from 'payload' and use those instead.\ntype BaseField = {\n admin?: {\n description?: unknown;\n language?: unknown;\n };\n hasMany?: boolean;\n // json\n jsonSchema?: unknown;\n max?: number;\n maxRows?: number;\n // number\n min?: number;\n // text/textarea\n minRows?: number;\n name?: string;\n // select\n options?: Array<\n | {\n label?: unknown;\n value: number | string;\n }\n | number\n | string\n >;\n required?: boolean;\n schema?: unknown;\n type?: string;\n typescriptSchema?: unknown;\n};\n\nfunction isString(s: unknown): s is string {\n return typeof s === 'string';\n}\n\nfunction isPlainObject(o: unknown): o is Record<string, unknown> {\n return !!o && typeof o === 'object' && !Array.isArray(o);\n}\n\nfunction getDescription(field: BaseField): string | undefined {\n const d = field?.admin?.description;\n return typeof d === 'string' ? d : undefined;\n}\n\nfunction stringWithDescription(field: BaseField) {\n const out: Record<string, any> = { type: 'string' };\n const description = getDescription(field);\n if (description) {out.description = description;}\n return out;\n}\n\nfunction numberWithBounds(field: BaseField) {\n const out: Record<string, any> = { type: 'number' };\n if (typeof field.min === 'number') {out.minimum = field.min;}\n if (typeof field.max === 'number') {out.maximum = field.max;}\n const description = getDescription(field);\n if (description) {out.description = description;}\n return out;\n}\n\nfunction dateSchema(field: BaseField) {\n const out: Record<string, any> = { type: 'string', format: 'date-time' };\n const description = getDescription(field);\n if (description) {out.description = description;}\n return out;\n}\n\nfunction codeSchema(field: BaseField) {\n const out: Record<string, any> = { type: 'string' };\n let description = getDescription(field);\n const lang = field?.admin?.language;\n if (typeof lang === 'string' && lang.trim()) {\n description = description ? `${description} (language: ${lang})` : `language: ${lang}`;\n }\n if (description) {out.description = description;}\n return out;\n}\n\nfunction emailSchema(field: BaseField) {\n const out: Record<string, any> = { type: 'string', format: 'email' };\n const description = getDescription(field);\n if (description) {out.description = description;}\n return out;\n}\n\nfunction jsonValueSchema(field: BaseField) {\n // Prefer a provided JSON Schema object\n if (isPlainObject(field.jsonSchema)) {return field.jsonSchema as object;}\n if (isPlainObject(field.schema)) {return field.schema as object;}\n\n // typescriptSchema cannot be executed here; default to object\n return { type: 'object' };\n}\n\nfunction normalizeSelectOptions(field: BaseField): { values: Array<number | string>; valueType: 'number' | 'string' } {\n const raw = field.options || [];\n const values: Array<number | string> = [];\n\n for (const opt of raw) {\n if (typeof opt === 'string' || typeof opt === 'number') {\n values.push(opt);\n } else if (isPlainObject(opt) && ('value' in opt)) {\n const v = (opt as any).value;\n if (typeof v === 'string' || typeof v === 'number') {\n values.push(v);\n }\n }\n }\n\n // Infer primitive type\n const allNumbers = values.length > 0 && values.every((v) => typeof v === 'number');\n const valueType: 'number' | 'string' = allNumbers ? 'number' : 'string';\n return { values, valueType };\n}\n\nfunction supportsHasMany(fieldType: string | undefined): boolean {\n // Out of the box: text, textarea, select support hasMany\n // Others can be arrays only if your config truly sets hasMany; we return boolean based on type here.\n return fieldType === 'text' || fieldType === 'textarea' || fieldType === 'select';\n}\n\nexport function fieldToJsonSchema(\n fieldInput: BaseField,\n opts?: { nameOverride?: string; wrapObject?: boolean },\n): JsonSchema {\n const field: BaseField = fieldInput || {};\n const name = isString(opts?.nameOverride) && opts?.nameOverride.length ? opts.nameOverride : (field.name || 'value');\n const type = field.type;\n\n let valueSchema: null | Record<string, any> = null;\n\n switch (type) {\n case 'code': {\n const base = codeSchema(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base;\n }\n break;\n }\n case 'date': {\n const base = dateSchema(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base;\n }\n break;\n }\n\n case 'email': {\n const base = emailSchema(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base;\n }\n break;\n }\n\n case 'json': {\n const base = jsonValueSchema(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base as Record<string, any>;\n }\n break;\n }\n\n case 'number': {\n const base = numberWithBounds(field);\n if (field.hasMany) {\n // Respect hasMany only if truly configured; Payload rarely uses hasMany for number, but allow if present.\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base;\n }\n break;\n }\n\n case 'select': {\n const { values, valueType } = normalizeSelectOptions(field);\n const baseSingle: Record<string, any> = { type: valueType, enum: values };\n const description = getDescription(field);\n if (description) {baseSingle.description = description;}\n\n if (field.hasMany && supportsHasMany(type)) {\n valueSchema = {\n type: 'array',\n items: { type: valueType, enum: values },\n ...(description ? { description } : {}),\n };\n } else {\n valueSchema = baseSingle;\n }\n break;\n }\n\n case 'text':\n case 'textarea': {\n const base = stringWithDescription(field);\n if (field.hasMany && supportsHasMany(type)) {\n const arr: Record<string, any> = {\n type: 'array',\n items: { type: 'string' },\n };\n if (typeof field.minRows === 'number') {arr.minItems = field.minRows;}\n if (typeof field.maxRows === 'number') {arr.maxItems = field.maxRows;}\n if (base.description) {arr.description = base.description;}\n valueSchema = arr;\n } else {\n valueSchema = base;\n }\n break;\n }\n\n default: {\n // Unsupported type: return null to allow caller to decide how to proceed (e.g., no schema)\n valueSchema = null;\n break;\n }\n }\n\n const wrap = opts?.wrapObject !== false;\n\n if (!wrap) {\n return (valueSchema || {}) as JsonSchema;\n }\n\n if (!valueSchema) {\n // Return undefined-like schema if not supported; caller may choose to skip passing schema\n return {} as JsonSchema;\n }\n\n const schema: JsonSchema = {\n type: 'object',\n additionalProperties: false,\n properties: {\n [name]: valueSchema,\n },\n required: [name]\n };\n\n return schema;\n}\n"],"names":["isString","s","isPlainObject","o","Array","isArray","getDescription","field","d","admin","description","undefined","stringWithDescription","out","type","numberWithBounds","min","minimum","max","maximum","dateSchema","format","codeSchema","lang","language","trim","emailSchema","jsonValueSchema","jsonSchema","schema","normalizeSelectOptions","raw","options","values","opt","push","v","value","allNumbers","length","every","valueType","supportsHasMany","fieldType","fieldToJsonSchema","fieldInput","opts","name","nameOverride","valueSchema","base","hasMany","items","baseSingle","enum","arr","minRows","minItems","maxRows","maxItems","wrap","wrapObject","additionalProperties","properties","required"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/fieldToJsonSchema.ts"],"sourcesContent":["/**\n * fieldToJsonSchema\n * Convert a Payload Field (server or client) into a minimal JSON Schema object,\n * wrapped as { type: 'object', properties: { [name]: valueSchema }, required: [...] }\n *\n * Supported types:\n * - text, textarea, select, number, date, code, email, json\n * Arrays are emitted only when field.hasMany is true and the field type supports hasMany\n * (text, textarea, select; for others only if your config truly sets hasMany).\n */\n\nexport type JsonSchema = Record<string, any>;\n\n// Narrowed minimal typing to avoid anywhere possible without pulling the full payload types at runtime.\n// In a Payload project, you can import type { Field, ClientField } from 'payload' and use those instead.\ntype BaseField = {\n admin?: {\n description?: unknown;\n language?: unknown;\n };\n hasMany?: boolean;\n // json\n jsonSchema?: unknown;\n max?: number;\n maxRows?: number;\n // number\n min?: number;\n // text/textarea\n minRows?: number;\n name?: string;\n // select\n options?: Array<\n | {\n label?: unknown;\n value: number | string;\n }\n | number\n | string\n >;\n required?: boolean;\n schema?: unknown;\n type?: string;\n typescriptSchema?: unknown;\n};\n\nfunction isString(s: unknown): s is string {\n return typeof s === 'string';\n}\n\nfunction isPlainObject(o: unknown): o is Record<string, unknown> {\n return !!o && typeof o === 'object' && !Array.isArray(o);\n}\n\nfunction getDescription(field: BaseField): string | undefined {\n const d = field?.admin?.description;\n return typeof d === 'string' ? d : undefined;\n}\n\nfunction stringWithDescription(field: BaseField) {\n const out: Record<string, any> = { type: 'string' };\n const description = getDescription(field);\n if (description) {out.description = description;}\n return out;\n}\n\nfunction numberWithBounds(field: BaseField) {\n const out: Record<string, any> = { type: 'number' };\n if (typeof field.min === 'number') {out.minimum = field.min;}\n if (typeof field.max === 'number') {out.maximum = field.max;}\n const description = getDescription(field);\n if (description) {out.description = description;}\n return out;\n}\n\nfunction dateSchema(field: BaseField) {\n const out: Record<string, any> = { type: 'string', format: 'date-time' };\n const description = getDescription(field);\n if (description) {out.description = description;}\n return out;\n}\n\nfunction codeSchema(field: BaseField) {\n const out: Record<string, any> = { type: 'string' };\n let description = getDescription(field);\n const lang = field?.admin?.language;\n if (typeof lang === 'string' && lang.trim()) {\n description = description ? `${description} (language: ${lang})` : `language: ${lang}`;\n }\n if (description) {out.description = description;}\n return out;\n}\n\nfunction emailSchema(field: BaseField) {\n const out: Record<string, any> = { type: 'string', format: 'email' };\n const description = getDescription(field);\n if (description) {out.description = description;}\n return out;\n}\n\nfunction jsonValueSchema(field: BaseField) {\n // Prefer a provided JSON Schema object\n if (isPlainObject(field.jsonSchema)) {return field.jsonSchema as object;}\n if (isPlainObject(field.schema)) {return field.schema as object;}\n\n // typescriptSchema cannot be executed here; default to object\n return { type: 'object' };\n}\n\nfunction normalizeSelectOptions(field: BaseField): { values: Array<number | string>; valueType: 'number' | 'string' } {\n const raw = field.options || [];\n const values: Array<number | string> = [];\n\n for (const opt of raw) {\n if (typeof opt === 'string' || typeof opt === 'number') {\n values.push(opt);\n } else if (isPlainObject(opt) && ('value' in opt)) {\n const v = (opt as any).value;\n if (typeof v === 'string' || typeof v === 'number') {\n values.push(v);\n }\n }\n }\n\n // Infer primitive type\n const allNumbers = values.length > 0 && values.every((v) => typeof v === 'number');\n const valueType: 'number' | 'string' = allNumbers ? 'number' : 'string';\n return { values, valueType };\n}\n\nfunction supportsHasMany(fieldType: string | undefined): boolean {\n // Out of the box: text, textarea, select support hasMany\n // Others can be arrays only if your config truly sets hasMany; we return boolean based on type here.\n return fieldType === 'text' || fieldType === 'textarea' || fieldType === 'select';\n}\n\nexport function fieldToJsonSchema(\n fieldInput: BaseField,\n opts?: { nameOverride?: string; wrapObject?: boolean },\n): JsonSchema {\n const field: BaseField = fieldInput || {};\n const name = isString(opts?.nameOverride) && opts?.nameOverride.length ? opts.nameOverride : (field.name || 'value');\n const type = field.type;\n\n let valueSchema: null | Record<string, any> = null;\n\n switch (type) {\n case 'code': {\n const base = codeSchema(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base;\n }\n break;\n }\n case 'date': {\n const base = dateSchema(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base;\n }\n break;\n }\n\n case 'email': {\n const base = emailSchema(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base;\n }\n break;\n }\n\n case 'json': {\n const base = jsonValueSchema(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base as Record<string, any>;\n }\n break;\n }\n\n case 'number': {\n const base = numberWithBounds(field);\n if (field.hasMany) {\n // Respect hasMany only if truly configured; Payload rarely uses hasMany for number, but allow if present.\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base;\n }\n break;\n }\n\n case 'select': {\n const { values, valueType } = normalizeSelectOptions(field);\n const baseSingle: Record<string, any> = { type: valueType, enum: values };\n const description = getDescription(field);\n if (description) {baseSingle.description = description;}\n\n if (field.hasMany && supportsHasMany(type)) {\n valueSchema = {\n type: 'array',\n items: { type: valueType, enum: values },\n ...(description ? { description } : {}),\n };\n } else {\n valueSchema = baseSingle;\n }\n break;\n }\n\n case 'text':\n case 'textarea': {\n const base = stringWithDescription(field);\n if (field.hasMany && supportsHasMany(type)) {\n const arr: Record<string, any> = {\n type: 'array',\n items: { type: 'string' },\n };\n if (typeof field.minRows === 'number') {arr.minItems = field.minRows;}\n if (typeof field.maxRows === 'number') {arr.maxItems = field.maxRows;}\n if (base.description) {arr.description = base.description;}\n valueSchema = arr;\n } else {\n valueSchema = base;\n }\n break;\n }\n\n default: {\n // Unsupported type: return null to allow caller to decide how to proceed (e.g., no schema)\n valueSchema = null;\n break;\n }\n }\n\n const wrap = opts?.wrapObject !== false;\n\n if (!wrap) {\n return (valueSchema || {}) as JsonSchema;\n }\n\n if (!valueSchema) {\n // Return undefined-like schema if not supported; caller may choose to skip passing schema\n return {} as JsonSchema;\n }\n\n const schema: JsonSchema = {\n type: 'object',\n additionalProperties: false,\n properties: {\n [name]: valueSchema,\n },\n required: [name]\n };\n\n return schema;\n}\n"],"names":["isString","s","isPlainObject","o","Array","isArray","getDescription","field","d","admin","description","undefined","stringWithDescription","out","type","numberWithBounds","min","minimum","max","maximum","dateSchema","format","codeSchema","lang","language","trim","emailSchema","jsonValueSchema","jsonSchema","schema","normalizeSelectOptions","raw","options","values","opt","push","v","value","allNumbers","length","every","valueType","supportsHasMany","fieldType","fieldToJsonSchema","fieldInput","opts","name","nameOverride","valueSchema","base","hasMany","items","baseSingle","enum","arr","minRows","minItems","maxRows","maxItems","wrap","wrapObject","additionalProperties","properties","required"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/updateFieldsConfig.ts"],"sourcesContent":["import type { CollectionConfig, GlobalConfig } from 'payload'\n\ninterface UpdateFieldsConfig {\n schemaPathMap: Record<string, string>\n updatedCollectionConfig: CollectionConfig | GlobalConfig\n}\n\nexport const updateFieldsConfig = (collectionConfig: CollectionConfig | GlobalConfig): UpdateFieldsConfig => {\n let schemaPathMap = {}\n\n function updateField(field: any, parentPath = ''): any {\n const currentPath = parentPath ? `${parentPath}.${field.name}` : field.name\n const currentSchemaPath = `${collectionConfig.slug}.${currentPath}`\n\n // Disabled fields/ field types\n if (\n field.admin?.disabled ||\n field.admin?.readOnly ||\n field.admin?.hidden ||\n field.type === 'row'\n ) {\n return field\n }\n\n // Map field path for global fieldInstructionsMap to load related instructions\n // This is done due to save extra API call to get instructions when Field components are loaded in admin\n // Doing is will only call instructions data when user clicks on settings\n if (['richText', 'text', 'textarea', 'upload'].includes(field.type)) {\n schemaPathMap = {\n ...schemaPathMap,\n [currentSchemaPath]: {\n type: field.type,\n label: field.label || field.name,\n relationTo: field.relationTo,\n },\n }\n }\n\n // Inject AI actions, richText is not included here as it has to be explicitly defined by user\n if (['text', 'textarea', 'upload'].includes(field.type)) {\n let customField = {}\n\n // Custom fields don't fully adhere to the Payload schema, making it difficult to\n // determine which components support injecting ComposeField as a Description.\n if (field.admin?.components?.Field || field.admin?.components?.Description) {\n // TODO: If a field already provides its own Description, we still inject our ComposeField\n // by overriding Description. If you need both, consider composing your own wrapper.\n customField = {}\n }\n\n return {\n ...field,\n admin: {\n ...field.admin,\n components: {\n ...(field.admin?.components || {}),\n Description: {\n clientProps: {\n schemaPath: currentSchemaPath,\n },\n path: '@ai-stack/payloadcms/fields#ComposeField',\n },\n ...customField,\n },\n },\n }\n }\n\n if (field.fields) {\n return {\n ...field,\n fields: field.fields.map((subField: any) => updateField(subField, currentPath)),\n }\n }\n\n if (field.tabs) {\n return {\n ...field,\n tabs: field.tabs.map((tab: any) => {\n return {\n ...tab,\n // Tabs are a UI construct and should not add to the schema path\n fields: (tab.fields || []).map((subField: any) => updateField(subField, parentPath)),\n }\n }),\n }\n }\n\n if (field.blocks) {\n return {\n ...field,\n blocks: field.blocks.map((block: any) => ({\n ...block,\n fields: block.fields.map((subField: any) =>\n updateField(subField, `${currentPath}.${block.slug}`),\n ),\n })),\n }\n }\n\n return field\n }\n\n const updatedCollectionConfig = {\n ...collectionConfig,\n fields: collectionConfig.fields.map((field) => updateField(field)),\n }\n\n return {\n schemaPathMap,\n updatedCollectionConfig,\n }\n}\n"],"names":["updateFieldsConfig","collectionConfig","schemaPathMap","updateField","field","parentPath","currentPath","name","currentSchemaPath","slug","admin","disabled","readOnly","hidden","type","includes","label","relationTo","customField","components","Field","Description","clientProps","schemaPath","path","fields","map","subField","tabs","tab","blocks","block","updatedCollectionConfig"],"mappings":"AAOA,OAAO,MAAMA,qBAAqB,CAACC;IACjC,IAAIC,gBAAgB,CAAC;IAErB,SAASC,YAAYC,KAAU,EAAEC,aAAa,EAAE;QAC9C,MAAMC,cAAcD,aAAa,
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/updateFieldsConfig.ts"],"sourcesContent":["import type { CollectionConfig, GlobalConfig } from 'payload'\n\ninterface UpdateFieldsConfig {\n schemaPathMap: Record<string, string>\n updatedCollectionConfig: CollectionConfig | GlobalConfig\n}\n\nexport const updateFieldsConfig = (collectionConfig: CollectionConfig | GlobalConfig): UpdateFieldsConfig => {\n let schemaPathMap = {}\n\n function updateField(field: any, parentPath = ''): any {\n const currentPath = parentPath ? `${parentPath}.${field.name}` : field.name\n const currentSchemaPath = `${collectionConfig.slug}.${currentPath}`\n\n // Disabled fields/ field types\n if (\n field.admin?.disabled ||\n field.admin?.readOnly ||\n field.admin?.hidden ||\n field.type === 'row'\n ) {\n return field\n }\n\n // Map field path for global fieldInstructionsMap to load related instructions\n // This is done due to save extra API call to get instructions when Field components are loaded in admin\n // Doing is will only call instructions data when user clicks on settings\n if (['richText', 'text', 'textarea', 'upload'].includes(field.type)) {\n schemaPathMap = {\n ...schemaPathMap,\n [currentSchemaPath]: {\n type: field.type,\n label: field.label || field.name,\n relationTo: field.relationTo,\n },\n }\n }\n\n // Inject AI actions, richText is not included here as it has to be explicitly defined by user\n if (['text', 'textarea', 'upload'].includes(field.type)) {\n let customField = {}\n\n // Custom fields don't fully adhere to the Payload schema, making it difficult to\n // determine which components support injecting ComposeField as a Description.\n if (field.admin?.components?.Field || field.admin?.components?.Description) {\n // TODO: If a field already provides its own Description, we still inject our ComposeField\n // by overriding Description. If you need both, consider composing your own wrapper.\n customField = {}\n }\n\n return {\n ...field,\n admin: {\n ...field.admin,\n components: {\n ...(field.admin?.components || {}),\n Description: {\n clientProps: {\n schemaPath: currentSchemaPath,\n },\n path: '@ai-stack/payloadcms/fields#ComposeField',\n },\n ...customField,\n },\n },\n }\n }\n\n if (field.fields) {\n return {\n ...field,\n fields: field.fields.map((subField: any) => updateField(subField, currentPath)),\n }\n }\n\n if (field.tabs) {\n return {\n ...field,\n tabs: field.tabs.map((tab: any) => {\n return {\n ...tab,\n // Tabs are a UI construct and should not add to the schema path\n fields: (tab.fields || []).map((subField: any) => updateField(subField, parentPath)),\n }\n }),\n }\n }\n\n if (field.blocks) {\n return {\n ...field,\n blocks: field.blocks.map((block: any) => ({\n ...block,\n fields: block.fields.map((subField: any) =>\n updateField(subField, `${currentPath}.${block.slug}`),\n ),\n })),\n }\n }\n\n return field\n }\n\n const updatedCollectionConfig = {\n ...collectionConfig,\n fields: collectionConfig.fields.map((field) => updateField(field)),\n }\n\n return {\n schemaPathMap,\n updatedCollectionConfig,\n }\n}\n"],"names":["updateFieldsConfig","collectionConfig","schemaPathMap","updateField","field","parentPath","currentPath","name","currentSchemaPath","slug","admin","disabled","readOnly","hidden","type","includes","label","relationTo","customField","components","Field","Description","clientProps","schemaPath","path","fields","map","subField","tabs","tab","blocks","block","updatedCollectionConfig"],"mappings":"AAOA,OAAO,MAAMA,qBAAqB,CAACC;IACjC,IAAIC,gBAAgB,CAAC;IAErB,SAASC,YAAYC,KAAU,EAAEC,aAAa,EAAE;QAC9C,MAAMC,cAAcD,aAAa,CAAC,EAAEA,WAAW,CAAC,EAAED,MAAMG,IAAI,CAAC,CAAC,GAAGH,MAAMG,IAAI;QAC3E,MAAMC,oBAAoB,CAAC,EAAEP,iBAAiBQ,IAAI,CAAC,CAAC,EAAEH,YAAY,CAAC;QAEnE,+BAA+B;QAC/B,IACEF,MAAMM,KAAK,EAAEC,YACbP,MAAMM,KAAK,EAAEE,YACbR,MAAMM,KAAK,EAAEG,UACbT,MAAMU,IAAI,KAAK,OACf;YACA,OAAOV;QACT;QAEA,8EAA8E;QAC9E,wGAAwG;QACxG,yEAAyE;QACzE,IAAI;YAAC;YAAY;YAAQ;YAAY;SAAS,CAACW,QAAQ,CAACX,MAAMU,IAAI,GAAG;YACnEZ,gBAAgB;gBACd,GAAGA,aAAa;gBAChB,CAACM,kBAAkB,EAAE;oBACnBM,MAAMV,MAAMU,IAAI;oBAChBE,OAAOZ,MAAMY,KAAK,IAAIZ,MAAMG,IAAI;oBAChCU,YAAYb,MAAMa,UAAU;gBAC9B;YACF;QACF;QAEA,8FAA8F;QAC9F,IAAI;YAAC;YAAQ;YAAY;SAAS,CAACF,QAAQ,CAACX,MAAMU,IAAI,GAAG;YACvD,IAAII,cAAc,CAAC;YAEnB,iFAAiF;YACjF,8EAA8E;YAC9E,IAAId,MAAMM,KAAK,EAAES,YAAYC,SAAShB,MAAMM,KAAK,EAAES,YAAYE,aAAa;gBAC1E,0FAA0F;gBAC1F,oFAAoF;gBACpFH,cAAc,CAAC;YACjB;YAEA,OAAO;gBACL,GAAGd,KAAK;gBACRM,OAAO;oBACL,GAAGN,MAAMM,KAAK;oBACdS,YAAY;wBACV,GAAIf,MAAMM,KAAK,EAAES,cAAc,CAAC,CAAC;wBACjCE,aAAa;4BACXC,aAAa;gCACXC,YAAYf;4BACd;4BACAgB,MAAM;wBACR;wBACA,GAAGN,WAAW;oBAChB;gBACF;YACF;QACF;QAEA,IAAId,MAAMqB,MAAM,EAAE;YAChB,OAAO;gBACL,GAAGrB,KAAK;gBACRqB,QAAQrB,MAAMqB,MAAM,CAACC,GAAG,CAAC,CAACC,WAAkBxB,YAAYwB,UAAUrB;YACpE;QACF;QAEA,IAAIF,MAAMwB,IAAI,EAAE;YACd,OAAO;gBACL,GAAGxB,KAAK;gBACRwB,MAAMxB,MAAMwB,IAAI,CAACF,GAAG,CAAC,CAACG;oBACpB,OAAO;wBACL,GAAGA,GAAG;wBACN,gEAAgE;wBAChEJ,QAAQ,AAACI,CAAAA,IAAIJ,MAAM,IAAI,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC,WAAkBxB,YAAYwB,UAAUtB;oBAC1E;gBACF;YACF;QACF;QAEA,IAAID,MAAM0B,MAAM,EAAE;YAChB,OAAO;gBACL,GAAG1B,KAAK;gBACR0B,QAAQ1B,MAAM0B,MAAM,CAACJ,GAAG,CAAC,CAACK,QAAgB,CAAA;wBACxC,GAAGA,KAAK;wBACRN,QAAQM,MAAMN,MAAM,CAACC,GAAG,CAAC,CAACC,WACxBxB,YAAYwB,UAAU,CAAC,EAAErB,YAAY,CAAC,EAAEyB,MAAMtB,IAAI,CAAC,CAAC;oBAExD,CAAA;YACF;QACF;QAEA,OAAOL;IACT;IAEA,MAAM4B,0BAA0B;QAC9B,GAAG/B,gBAAgB;QACnBwB,QAAQxB,iBAAiBwB,MAAM,CAACC,GAAG,CAAC,CAACtB,QAAUD,YAAYC;IAC7D;IAEA,OAAO;QACLF;QACA8B;IACF;AACF,EAAC"}
|