@ai-stack/payloadcms 3.68.0 → 3.76.0-beta.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/ai/core/media/image/generateImage.js +2 -6
- package/dist/ai/core/media/image/generateImage.js.map +1 -1
- package/dist/ai/core/media/image/handlers/multimodal.js +13 -28
- package/dist/ai/core/media/image/handlers/multimodal.js.map +1 -1
- package/dist/ai/core/media/image/handlers/standard.js +10 -7
- package/dist/ai/core/media/image/handlers/standard.js.map +1 -1
- package/dist/ai/core/media/speech/generateSpeech.js +8 -8
- package/dist/ai/core/media/speech/generateSpeech.js.map +1 -1
- package/dist/ai/core/media/types.d.ts +1 -1
- package/dist/ai/core/media/types.js.map +1 -1
- package/dist/ai/core/streamObject.js +3 -3
- package/dist/ai/core/streamObject.js.map +1 -1
- package/dist/ai/core/types.d.ts +3 -0
- package/dist/ai/core/types.js.map +1 -1
- package/dist/ai/prompts.d.ts +1 -2
- package/dist/ai/prompts.js +0 -110
- package/dist/ai/prompts.js.map +1 -1
- package/dist/ai/providers/blocks/anthropic.js +2 -1
- package/dist/ai/providers/blocks/anthropic.js.map +1 -1
- package/dist/ai/providers/blocks/elevenlabs.js +3 -2
- package/dist/ai/providers/blocks/elevenlabs.js.map +1 -1
- package/dist/ai/providers/blocks/fal.js +2 -1
- package/dist/ai/providers/blocks/fal.js.map +1 -1
- package/dist/ai/providers/blocks/google.js +12 -6
- package/dist/ai/providers/blocks/google.js.map +1 -1
- package/dist/ai/providers/blocks/openai-compatible.js +2 -1
- package/dist/ai/providers/blocks/openai-compatible.js.map +1 -1
- package/dist/ai/providers/blocks/openai.js +3 -2
- package/dist/ai/providers/blocks/openai.js.map +1 -1
- package/dist/ai/providers/blocks/xai.js +2 -1
- package/dist/ai/providers/blocks/xai.js.map +1 -1
- package/dist/ai/providers/icons.d.ts +7 -0
- package/dist/ai/providers/icons.js +9 -0
- package/dist/ai/providers/icons.js.map +1 -0
- package/dist/ai/providers/registry.js +36 -26
- package/dist/ai/providers/registry.js.map +1 -1
- package/dist/ai/utils/filterEditorSchemaByNodes.d.ts +9 -0
- package/dist/ai/utils/filterEditorSchemaByNodes.js +30 -3
- package/dist/ai/utils/filterEditorSchemaByNodes.js.map +1 -1
- package/dist/ai/utils/nodeToSchemaMap.d.ts +22 -0
- package/dist/ai/utils/nodeToSchemaMap.js +72 -0
- package/dist/ai/utils/nodeToSchemaMap.js.map +1 -0
- package/dist/collections/AIJobs.js +1 -1
- package/dist/collections/AIJobs.js.map +1 -1
- package/dist/collections/AIProviders.d.ts +2 -0
- package/dist/collections/{AISettings.js → AIProviders.js} +53 -25
- package/dist/collections/AIProviders.js.map +1 -0
- package/dist/collections/Instructions.js +45 -4
- package/dist/collections/Instructions.js.map +1 -1
- package/dist/defaults.d.ts +1 -0
- package/dist/defaults.js +8 -0
- package/dist/defaults.js.map +1 -1
- package/dist/endpoints/chat.d.ts +4 -0
- package/dist/endpoints/fetchFields.js +10 -0
- package/dist/endpoints/fetchFields.js.map +1 -1
- package/dist/endpoints/fetchVoices.js +42 -25
- package/dist/endpoints/fetchVoices.js.map +1 -1
- package/dist/endpoints/index.js +248 -35
- package/dist/endpoints/index.js.map +1 -1
- package/dist/exports/client.d.ts +1 -1
- package/dist/exports/client.js +1 -1
- package/dist/exports/client.js.map +1 -1
- package/dist/exports/fields.d.ts +1 -0
- package/dist/exports/fields.js +1 -0
- package/dist/exports/fields.js.map +1 -1
- package/dist/fields/ArrayComposeField/ArrayComposeField.d.ts +15 -0
- package/dist/fields/ArrayComposeField/ArrayComposeField.js +87 -0
- package/dist/fields/ArrayComposeField/ArrayComposeField.js.map +1 -0
- package/dist/fields/ArrayComposeField/ArrayComposeField.jsx +73 -0
- package/dist/fields/PromptEditorField/PromptEditorField.js +7 -2
- package/dist/fields/PromptEditorField/PromptEditorField.js.map +1 -1
- package/dist/fields/PromptEditorField/PromptEditorField.jsx +5 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/payload-ai.d.ts +152 -0
- package/dist/plugin.js +18 -34
- package/dist/plugin.js.map +1 -1
- package/dist/providers/InstructionsProvider/InstructionsProvider.js +47 -15
- package/dist/providers/InstructionsProvider/InstructionsProvider.js.map +1 -1
- package/dist/providers/InstructionsProvider/InstructionsProvider.jsx +39 -16
- package/dist/providers/InstructionsProvider/context.d.ts +3 -0
- package/dist/providers/InstructionsProvider/context.js +2 -0
- package/dist/providers/InstructionsProvider/context.js.map +1 -1
- package/dist/providers/InstructionsProvider/useInstructions.js +22 -3
- package/dist/providers/InstructionsProvider/useInstructions.js.map +1 -1
- package/dist/styles.d.ts +11 -0
- package/dist/types/handlebars-async-helpers.d.ts +1 -0
- package/dist/types/handlebars-dist-handlebars.d.ts +1 -0
- package/dist/types/react-mentions.d.ts +1 -0
- package/dist/types.d.ts +36 -7
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/ui/AIConfigDashboard/index.d.ts +1 -1
- package/dist/ui/AIConfigDashboard/index.js +201 -23
- package/dist/ui/AIConfigDashboard/index.js.map +1 -1
- package/dist/ui/AIConfigDashboard/index.jsx +166 -15
- package/dist/ui/Compose/Compose.d.ts +1 -0
- package/dist/ui/Compose/Compose.js +23 -4
- package/dist/ui/Compose/Compose.js.map +1 -1
- package/dist/ui/Compose/Compose.jsx +23 -4
- package/dist/ui/Compose/UndoRedoActions.d.ts +2 -2
- package/dist/ui/Compose/UndoRedoActions.js +8 -5
- package/dist/ui/Compose/UndoRedoActions.js.map +1 -1
- package/dist/ui/Compose/UndoRedoActions.jsx +6 -5
- package/dist/ui/Compose/compose.module.css +56 -16
- package/dist/ui/Compose/hooks/menu/TranslateMenu.d.ts +5 -0
- package/dist/ui/Compose/hooks/menu/TranslateMenu.js +45 -4
- package/dist/ui/Compose/hooks/menu/TranslateMenu.js.map +1 -1
- package/dist/ui/Compose/hooks/menu/TranslateMenu.jsx +41 -5
- package/dist/ui/Compose/hooks/menu/itemsMap.js +12 -6
- package/dist/ui/Compose/hooks/menu/itemsMap.js.map +1 -1
- package/dist/ui/Compose/hooks/menu/menu.module.scss +4 -1
- package/dist/ui/Compose/hooks/menu/useMenu.js +26 -15
- package/dist/ui/Compose/hooks/menu/useMenu.js.map +1 -1
- package/dist/ui/Compose/hooks/menu/useMenu.jsx +25 -12
- package/dist/ui/Compose/hooks/useActiveFieldTracking.js +34 -0
- package/dist/ui/Compose/hooks/useActiveFieldTracking.js.map +1 -1
- package/dist/ui/Compose/hooks/useGenerate.js +26 -174
- package/dist/ui/Compose/hooks/useGenerate.js.map +1 -1
- package/dist/ui/Compose/hooks/useGenerateUpload.d.ts +11 -0
- package/dist/ui/Compose/hooks/useGenerateUpload.js +156 -0
- package/dist/ui/Compose/hooks/useGenerateUpload.js.map +1 -0
- package/dist/ui/Compose/hooks/useHistory.d.ts +0 -1
- package/dist/ui/Compose/hooks/useHistory.js +65 -25
- package/dist/ui/Compose/hooks/useHistory.js.map +1 -1
- package/dist/ui/Compose/hooks/useStreamingUpdate.d.ts +8 -0
- package/dist/ui/Compose/hooks/useStreamingUpdate.js +48 -0
- package/dist/ui/Compose/hooks/useStreamingUpdate.js.map +1 -0
- package/dist/ui/ConfigDashboard/index.d.ts +2 -0
- package/dist/ui/ConfigDashboard/index.js +224 -0
- package/dist/ui/ConfigDashboard/index.js.map +1 -0
- package/dist/ui/ConfigDashboard/index.jsx +175 -0
- package/dist/ui/DynamicModelSelect/index.js +1 -1
- package/dist/ui/DynamicModelSelect/index.js.map +1 -1
- package/dist/ui/DynamicModelSelect/index.jsx +1 -1
- package/dist/ui/DynamicProviderSelect/index.js +1 -1
- package/dist/ui/DynamicProviderSelect/index.js.map +1 -1
- package/dist/ui/DynamicProviderSelect/index.jsx +1 -1
- package/dist/ui/DynamicVoiceSelect/index.js +63 -11
- package/dist/ui/DynamicVoiceSelect/index.js.map +1 -1
- package/dist/ui/DynamicVoiceSelect/index.jsx +47 -14
- package/dist/ui/EncryptedTextField/index.js +4 -4
- package/dist/ui/EncryptedTextField/index.js.map +1 -1
- package/dist/ui/EncryptedTextField/index.jsx +4 -4
- package/dist/ui/ProviderOptionsEditor/index.js +1 -1
- package/dist/ui/ProviderOptionsEditor/index.js.map +1 -1
- package/dist/ui/ProviderOptionsEditor/index.jsx +1 -1
- package/dist/ui/VoicesFetcher/index.js +34 -16
- package/dist/ui/VoicesFetcher/index.js.map +1 -1
- package/dist/ui/VoicesFetcher/index.jsx +32 -15
- package/dist/utilities/buildSmartPrompt.d.ts +22 -0
- package/dist/utilities/buildSmartPrompt.js +141 -0
- package/dist/utilities/buildSmartPrompt.js.map +1 -0
- package/dist/utilities/encryption.js +2 -1
- package/dist/utilities/encryption.js.map +1 -1
- package/dist/utilities/fieldToJsonSchema.js +32 -3
- package/dist/utilities/fieldToJsonSchema.js.map +1 -1
- package/dist/utilities/resolveImageReferences.d.ts +3 -1
- package/dist/utilities/resolveImageReferences.js +21 -2
- package/dist/utilities/resolveImageReferences.js.map +1 -1
- package/dist/utilities/seedProperties.d.ts +7 -0
- package/dist/utilities/seedProperties.js +100 -0
- package/dist/utilities/seedProperties.js.map +1 -0
- package/dist/utilities/setSafeLexicalState.js +79 -6
- package/dist/utilities/setSafeLexicalState.js.map +1 -1
- package/dist/utilities/updateFieldsConfig.d.ts +1 -1
- package/dist/utilities/updateFieldsConfig.js +9 -2
- package/dist/utilities/updateFieldsConfig.js.map +1 -1
- package/package.json +35 -33
- package/dist/collections/AISettings.d.ts +0 -2
- package/dist/collections/AISettings.js.map +0 -1
- package/dist/endpoints/chat.d.js +0 -3
- package/dist/endpoints/chat.d.js.map +0 -1
- package/dist/init.d.ts +0 -7
- package/dist/init.js +0 -135
- package/dist/init.js.map +0 -1
- package/dist/payload-ai.d.js +0 -3
- package/dist/payload-ai.d.js.map +0 -1
- package/dist/styles.d.js +0 -2
- package/dist/styles.d.js.map +0 -1
- package/dist/types/handlebars-async-helpers.d.js +0 -2
- package/dist/types/handlebars-async-helpers.d.js.map +0 -1
- package/dist/types/handlebars-dist-handlebars.d.js +0 -2
- package/dist/types/handlebars-dist-handlebars.d.js.map +0 -1
- package/dist/types/react-mentions.d.js +0 -2
- package/dist/types/react-mentions.d.js.map +0 -1
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useDocumentInfo,
|
|
2
|
+
import { useDocumentInfo, useForm } from '@payloadcms/ui';
|
|
3
3
|
import { useCallback, useEffect, useRef } from 'react';
|
|
4
|
+
import { getSiblingData } from 'payload/shared';
|
|
4
5
|
import { PLUGIN_NAME } from '../../../defaults.js';
|
|
5
6
|
import { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js';
|
|
6
7
|
const STORAGE_KEY = `${PLUGIN_NAME}-fields-history`;
|
|
8
|
+
const MAX_HISTORY_SIZE = 50;
|
|
7
9
|
// Global cache to prevent synchronous localStorage reads on every render
|
|
8
10
|
let globalHistoryCache = null;
|
|
9
11
|
export const useHistory = ()=>{
|
|
10
12
|
const { id } = useDocumentInfo();
|
|
11
|
-
const { path
|
|
12
|
-
const {
|
|
13
|
-
path: pathFromContext ?? ''
|
|
14
|
-
});
|
|
13
|
+
const { path, schemaPath } = useFieldProps();
|
|
14
|
+
const { getData } = useForm();
|
|
15
15
|
const fieldKey = `${id}.${schemaPath}`;
|
|
16
16
|
const getLatestHistory = useCallback(()=>{
|
|
17
17
|
// Return cache if available
|
|
@@ -43,21 +43,29 @@ export const useHistory = ()=>{
|
|
|
43
43
|
if (saveTimerRef.current) {
|
|
44
44
|
clearTimeout(saveTimerRef.current);
|
|
45
45
|
}
|
|
46
|
-
// Debounce the save operation by
|
|
46
|
+
// Debounce the save operation by 500ms
|
|
47
47
|
saveTimerRef.current = setTimeout(()=>{
|
|
48
48
|
// Use requestIdleCallback if available to avoid blocking the main thread
|
|
49
49
|
if (typeof requestIdleCallback !== 'undefined') {
|
|
50
50
|
requestIdleCallback(()=>{
|
|
51
|
-
|
|
51
|
+
try {
|
|
52
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(newGlobalHistory));
|
|
53
|
+
} catch (e) {
|
|
54
|
+
console.warn('Failed to save history to localStorage', e);
|
|
55
|
+
}
|
|
52
56
|
}, {
|
|
53
57
|
timeout: 2000
|
|
54
58
|
});
|
|
55
59
|
} else {
|
|
56
60
|
// Fallback for browsers without requestIdleCallback
|
|
57
|
-
|
|
61
|
+
try {
|
|
62
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(newGlobalHistory));
|
|
63
|
+
} catch (e) {
|
|
64
|
+
console.warn('Failed to save history to localStorage', e);
|
|
65
|
+
}
|
|
58
66
|
}
|
|
59
67
|
saveTimerRef.current = null;
|
|
60
|
-
},
|
|
68
|
+
}, 500);
|
|
61
69
|
}, []);
|
|
62
70
|
// Sync with other tabs
|
|
63
71
|
useEffect(()=>{
|
|
@@ -80,15 +88,18 @@ export const useHistory = ()=>{
|
|
|
80
88
|
const latestHistory = {
|
|
81
89
|
...getLatestHistory()
|
|
82
90
|
};
|
|
91
|
+
let hasChanges = false;
|
|
83
92
|
Object.keys(latestHistory).forEach((k)=>{
|
|
84
93
|
if (!k.startsWith(id?.toString() ?? '')) {
|
|
85
94
|
delete latestHistory[k];
|
|
95
|
+
hasChanges = true;
|
|
86
96
|
}
|
|
87
97
|
});
|
|
88
|
-
|
|
98
|
+
if (hasChanges) {
|
|
99
|
+
saveToLocalStorage(latestHistory);
|
|
100
|
+
}
|
|
89
101
|
}, [
|
|
90
102
|
id,
|
|
91
|
-
fieldKey,
|
|
92
103
|
getLatestHistory,
|
|
93
104
|
saveToLocalStorage
|
|
94
105
|
]);
|
|
@@ -101,22 +112,46 @@ export const useHistory = ()=>{
|
|
|
101
112
|
history: []
|
|
102
113
|
};
|
|
103
114
|
let newIndex = currentIndex;
|
|
115
|
+
let historyUpdated = false;
|
|
116
|
+
const newHistoryArray = [
|
|
117
|
+
...history
|
|
118
|
+
];
|
|
104
119
|
if (currentIndex == -1) {
|
|
105
120
|
newIndex = 0;
|
|
106
|
-
|
|
107
|
-
|
|
121
|
+
// Get initial value from form data instead of subscribing to useField
|
|
122
|
+
// This implementation avoids re-rendering on every keystroke
|
|
123
|
+
try {
|
|
124
|
+
const data = getData();
|
|
125
|
+
// We need to resolve the value from the data object using the path
|
|
126
|
+
// path might be 'group.subgroup.field'
|
|
127
|
+
if (path) {
|
|
128
|
+
const value = getSiblingData(data, path);
|
|
129
|
+
if (value) {
|
|
130
|
+
newHistoryArray[newIndex] = value;
|
|
131
|
+
historyUpdated = true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
} catch (e) {
|
|
135
|
+
// If we can't get the data, just ignore
|
|
108
136
|
}
|
|
109
137
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
138
|
+
if (historyUpdated) {
|
|
139
|
+
const newGlobalHistory = {
|
|
140
|
+
...latestHistory,
|
|
141
|
+
[fieldKey]: {
|
|
142
|
+
currentIndex: newIndex,
|
|
143
|
+
history: newHistoryArray
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
saveToLocalStorage(newGlobalHistory);
|
|
147
|
+
}
|
|
118
148
|
}, [
|
|
119
|
-
fieldKey
|
|
149
|
+
fieldKey,
|
|
150
|
+
getData,
|
|
151
|
+
path,
|
|
152
|
+
clearHistory,
|
|
153
|
+
getLatestHistory,
|
|
154
|
+
saveToLocalStorage
|
|
120
155
|
]);
|
|
121
156
|
const set = useCallback((data)=>{
|
|
122
157
|
const latestHistory = getLatestHistory();
|
|
@@ -124,10 +159,15 @@ export const useHistory = ()=>{
|
|
|
124
159
|
currentIndex: -1,
|
|
125
160
|
history: []
|
|
126
161
|
};
|
|
127
|
-
|
|
162
|
+
// Create new history array slice, appending new data
|
|
163
|
+
let newHistory = [
|
|
128
164
|
...history.slice(0, currentIndex + 1),
|
|
129
165
|
data
|
|
130
166
|
];
|
|
167
|
+
// Enforce Max History Size
|
|
168
|
+
if (newHistory.length > MAX_HISTORY_SIZE) {
|
|
169
|
+
newHistory = newHistory.slice(newHistory.length - MAX_HISTORY_SIZE);
|
|
170
|
+
}
|
|
131
171
|
const newGlobalHistory = {
|
|
132
172
|
...latestHistory,
|
|
133
173
|
[fieldKey]: {
|
|
@@ -205,11 +245,11 @@ export const useHistory = ()=>{
|
|
|
205
245
|
const fieldHistory = getLatestFieldHistory();
|
|
206
246
|
const canUndo = fieldHistory.currentIndex > 0;
|
|
207
247
|
const canRedo = fieldHistory.currentIndex < fieldHistory.history.length - 1;
|
|
208
|
-
|
|
248
|
+
// Note: We deliberately do not return currentValue to avoid subscription re-renders
|
|
249
|
+
// The consumers of this hook (UndoRedoActions) didn't use it anyway.
|
|
209
250
|
return {
|
|
210
251
|
canRedo,
|
|
211
252
|
canUndo,
|
|
212
|
-
currentValue,
|
|
213
253
|
redo,
|
|
214
254
|
set,
|
|
215
255
|
undo
|
|
@@ -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, useRef } 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\n// Global cache to prevent synchronous localStorage reads on every render\nlet globalHistoryCache: HistoryState | null = null\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 // Return cache if available\n if (globalHistoryCache) {\n return globalHistoryCache\n }\n\n try {\n if (typeof localStorage !== 'undefined') {\n // Read once, cache it\n const stored = localStorage.getItem(STORAGE_KEY)\n globalHistoryCache = stored ? JSON.parse(stored) : {}\n return globalHistoryCache!\n }\n return {}\n } catch (e) {\n console.error('Error parsing history:', e)\n return {}\n }\n }, [])\n\n // Debounce timer ref to prevent excessive localStorage writes\n const saveTimerRef = useRef<null | ReturnType<typeof setTimeout>>(null)\n\n const saveToLocalStorage = useCallback((newGlobalHistory: HistoryState) => {\n // Update cache immediately\n globalHistoryCache = newGlobalHistory\n\n if (typeof localStorage === 'undefined') {\n return\n }\n\n // Clear any pending save\n if (saveTimerRef.current) {\n clearTimeout(saveTimerRef.current)\n }\n\n // Debounce the save operation by 300ms\n saveTimerRef.current = setTimeout(() => {\n // Use requestIdleCallback if available to avoid blocking the main thread\n if (typeof requestIdleCallback !== 'undefined') {\n requestIdleCallback(\n () => {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(newGlobalHistory))\n },\n { timeout: 2000 },\n )\n } else {\n // Fallback for browsers without requestIdleCallback\n localStorage.setItem(STORAGE_KEY, JSON.stringify(newGlobalHistory))\n }\n saveTimerRef.current = null\n }, 300)\n }, [])\n\n // Sync with other tabs\n useEffect(() => {\n const handleStorageChange = (e: StorageEvent) => {\n if (e.key === STORAGE_KEY && e.newValue) {\n try {\n globalHistoryCache = JSON.parse(e.newValue)\n } catch (err) {\n // ignore parse error\n }\n }\n }\n\n window.addEventListener('storage', handleStorageChange)\n return () => {\n window.removeEventListener('storage', handleStorageChange)\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","useRef","PLUGIN_NAME","useFieldProps","STORAGE_KEY","globalHistoryCache","useHistory","id","path","pathFromContext","schemaPath","value","currentFieldValue","fieldKey","getLatestHistory","localStorage","stored","getItem","JSON","parse","e","console","error","saveTimerRef","saveToLocalStorage","newGlobalHistory","current","clearTimeout","setTimeout","requestIdleCallback","setItem","stringify","timeout","handleStorageChange","key","newValue","err","window","addEventListener","removeEventListener","clearHistory","latestHistory","Object","keys","forEach","k","startsWith","toString","currentIndex","history","newIndex","set","data","newHistory","slice","length","undo","undefined","redo","getLatestFieldHistory","fieldHistory","canUndo","canRedo","currentValue"],"mappings":"AAAA;AAEA,SAASA,eAAe,EAAEC,QAAQ,QAAQ,iBAAgB;AAC1D,SAASC,WAAW,EAAEC,SAAS,EAAEC,MAAM,QAAQ,QAAO;AAEtD,SAASC,WAAW,QAAQ,uBAAsB;AAClD,SAASC,aAAa,QAAQ,oDAAmD;AAEjF,MAAMC,cAAc,CAAC,EAAEF,YAAY,eAAe,CAAC;AASnD,yEAAyE;AACzE,IAAIG,qBAA0C;AAE9C,OAAO,MAAMC,aAAa;IACxB,MAAM,EAAEC,EAAE,EAAE,GAAGV;IACf,MAAM,EAAEW,MAAMC,eAAe,EAAEC,UAAU,EAAE,GAAGP;IAC9C,MAAM,EAAEQ,OAAOC,iBAAiB,EAAE,GAAGd,SAAiB;QACpDU,MAAMC,mBAAmB;IAC3B;IAEA,MAAMI,WAAW,CAAC,EAAEN,GAAG,CAAC,EAAEG,WAAW,CAAC;IAEtC,MAAMI,mBAAmBf,YAAY;QACnC,4BAA4B;QAC5B,IAAIM,oBAAoB;YACtB,OAAOA;QACT;QAEA,IAAI;YACF,IAAI,OAAOU,iBAAiB,aAAa;gBACvC,sBAAsB;gBACtB,MAAMC,SAASD,aAAaE,OAAO,CAACb;gBACpCC,qBAAqBW,SAASE,KAAKC,KAAK,CAACH,UAAU,CAAC;gBACpD,OAAOX;YACT;YACA,OAAO,CAAC;QACV,EAAE,OAAOe,GAAG;YACVC,QAAQC,KAAK,CAAC,0BAA0BF;YACxC,OAAO,CAAC;QACV;IACF,GAAG,EAAE;IAEL,8DAA8D;IAC9D,MAAMG,eAAetB,OAA6C;IAElE,MAAMuB,qBAAqBzB,YAAY,CAAC0B;QACtC,2BAA2B;QAC3BpB,qBAAqBoB;QAErB,IAAI,OAAOV,iBAAiB,aAAa;YACvC;QACF;QAEA,yBAAyB;QACzB,IAAIQ,aAAaG,OAAO,EAAE;YACxBC,aAAaJ,aAAaG,OAAO;QACnC;QAEA,uCAAuC;QACvCH,aAAaG,OAAO,GAAGE,WAAW;YAChC,yEAAyE;YACzE,IAAI,OAAOC,wBAAwB,aAAa;gBAC9CA,oBACE;oBACEd,aAAae,OAAO,CAAC1B,aAAac,KAAKa,SAAS,CAACN;gBACnD,GACA;oBAAEO,SAAS;gBAAK;YAEpB,OAAO;gBACL,oDAAoD;gBACpDjB,aAAae,OAAO,CAAC1B,aAAac,KAAKa,SAAS,CAACN;YACnD;YACAF,aAAaG,OAAO,GAAG;QACzB,GAAG;IACL,GAAG,EAAE;IAEL,uBAAuB;IACvB1B,UAAU;QACR,MAAMiC,sBAAsB,CAACb;YAC3B,IAAIA,EAAEc,GAAG,KAAK9B,eAAegB,EAAEe,QAAQ,EAAE;gBACvC,IAAI;oBACF9B,qBAAqBa,KAAKC,KAAK,CAACC,EAAEe,QAAQ;gBAC5C,EAAE,OAAOC,KAAK;gBACZ,qBAAqB;gBACvB;YACF;QACF;QAEAC,OAAOC,gBAAgB,CAAC,WAAWL;QACnC,OAAO;YACLI,OAAOE,mBAAmB,CAAC,WAAWN;QACxC;IACF,GAAG,EAAE;IAEL,yBAAyB;IACzB,MAAMO,eAAezC,YAAY;QAC/B,MAAM0C,gBAAgB;YAAE,GAAG3B,kBAAkB;QAAC;QAC9C4B,OAAOC,IAAI,CAACF,eAAeG,OAAO,CAAC,CAACC;YAClC,IAAI,CAACA,EAAEC,UAAU,CAACvC,IAAIwC,cAAc,KAAK;gBACvC,OAAON,aAAa,CAACI,EAAE;YACzB;QACF;QACArB,mBAAmBiB;IACrB,GAAG;QAAClC;QAAIM;QAAUC;QAAkBU;KAAmB;IAEvDxB,UAAU;QACR,kFAAkF;QAClFwC;QAEA,MAAMC,gBAAgB3B;QACtB,MAAM,EAAEkC,YAAY,EAAEC,OAAO,EAAE,GAAGR,aAAa,CAAC5B,SAAS,IAAI;YAC3DmC,cAAc,CAAC;YACfC,SAAS,EAAE;QACb;QAEA,IAAIC,WAAWF;QACf,IAAIA,gBAAgB,CAAC,GAAG;YACtBE,WAAW;YACX,IAAItC,mBAAmB;gBACrBqC,OAAO,CAACC,SAAS,GAAGtC;YACtB;QACF;QAEA,MAAMa,mBAAmB;YACvB,GAAGgB,aAAa;YAChB,CAAC5B,SAAS,EAAE;gBAAEmC,cAAcE;gBAAUD;YAAQ;QAChD;QAEAzB,mBAAmBC;IACrB,GAAG;QAACZ;KAAS;IAEb,MAAMsC,MAAMpD,YACV,CAACqD;QACC,MAAMX,gBAAgB3B;QACtB,MAAM,EAAEkC,YAAY,EAAEC,OAAO,EAAE,GAAGR,aAAa,CAAC5B,SAAS,IAAI;YAC3DmC,cAAc,CAAC;YACfC,SAAS,EAAE;QACb;QACA,MAAMI,aAAa;eAAIJ,QAAQK,KAAK,CAAC,GAAGN,eAAe;YAAII;SAAK;QAChE,MAAM3B,mBAAmB;YACvB,GAAGgB,aAAa;YAChB,CAAC5B,SAAS,EAAE;gBAAEmC,cAAcK,WAAWE,MAAM,GAAG;gBAAGN,SAASI;YAAW;QACzE;QACA7B,mBAAmBC;QACnB,OAAO2B;IACT,GACA;QAACvC;QAAUC;QAAkBU;KAAmB;IAGlD,MAAMgC,OAAOzD,YAAY;QACvB,MAAM0C,gBAAgB3B;QACtB,MAAM,EAAEkC,YAAY,EAAEC,OAAO,EAAE,GAAGR,aAAa,CAAC5B,SAAS,IAAI;YAAEmC,cAAc,CAAC;YAAGC,SAAS,EAAE;QAAC;QAC7F,IAAID,eAAe,GAAG;YACpB,MAAME,WAAWF,eAAe;YAChC,MAAMb,WAAWc,OAAO,CAACC,SAAS;YAClC,MAAMzB,mBAAmB;gBACvB,GAAGgB,aAAa;gBAChB,CAAC5B,SAAS,EAAE;oBAAEmC,cAAcE;oBAAUD;gBAAQ;YAChD;YACAzB,mBAAmBC;YACnB,OAAOU;QACT;QACA,OAAOsB;IACT,GAAG;QAAC5C;QAAUC;QAAkBU;KAAmB;IAEnD,MAAMkC,OAAO3D,YAAY;QACvB,MAAM0C,gBAAgB3B;QACtB,MAAM,EAAEkC,YAAY,EAAEC,OAAO,EAAE,GAAGR,aAAa,CAAC5B,SAAS,IAAI;YAAEmC,cAAc,CAAC;YAAGC,SAAS,EAAE;QAAC;QAC7F,IAAID,eAAeC,QAAQM,MAAM,GAAG,GAAG;YACrC,MAAML,WAAWF,eAAe;YAChC,MAAMb,WAAWc,OAAO,CAACC,SAAS;YAClC,MAAMzB,mBAAmB;gBACvB,GAAGgB,aAAa;gBAChB,CAAC5B,SAAS,EAAE;oBAAEmC,cAAcE;oBAAUD;gBAAQ;YAChD;YACAzB,mBAAmBC;YACnB,OAAOU;QACT;QACA,OAAOsB;IACT,GAAG;QAAC5C;QAAUC;QAAkBU;KAAmB;IAEnD,MAAMmC,wBAAwB5D,YAAY;QACxC,MAAM0C,gBAAgB3B;QACtB,OAAO2B,aAAa,CAAC5B,SAAS,IAAI;YAAEmC,cAAc,CAAC;YAAGC,SAAS,EAAE;QAAC;IACpE,GAAG;QAACnC;QAAkBD;KAAS;IAE/B,MAAM+C,eAAeD;IAErB,MAAME,UAAUD,aAAaZ,YAAY,GAAG;IAC5C,MAAMc,UAAUF,aAAaZ,YAAY,GAAGY,aAAaX,OAAO,CAACM,MAAM,GAAG;IAC1E,MAAMQ,eAAeH,aAAaX,OAAO,CAACW,aAAaZ,YAAY,CAAC;IAEpE,OAAO;QACLc;QACAD;QACAE;QACAL;QACAP;QACAK;IACF;AACF,EAAC"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/ui/Compose/hooks/useHistory.ts"],"sourcesContent":["'use client'\n\nimport { useDocumentInfo, useForm } from '@payloadcms/ui'\nimport { useCallback, useEffect, useRef } from 'react'\nimport { getSiblingData } from 'payload/shared'\n\nimport { PLUGIN_NAME } from '../../../defaults.js'\nimport { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js'\n\nconst STORAGE_KEY = `${PLUGIN_NAME}-fields-history`\nconst MAX_HISTORY_SIZE = 50\n\ninterface HistoryState {\n [path: string]: {\n currentIndex: number\n history: any[]\n }\n}\n\n// Global cache to prevent synchronous localStorage reads on every render\nlet globalHistoryCache: HistoryState | null = null\n\nexport const useHistory = () => {\n const { id } = useDocumentInfo()\n const { path, schemaPath } = useFieldProps()\n const { getData } = useForm()\n\n const fieldKey = `${id}.${schemaPath}`\n\n const getLatestHistory = useCallback((): HistoryState => {\n // Return cache if available\n if (globalHistoryCache) {\n return globalHistoryCache\n }\n\n try {\n if (typeof localStorage !== 'undefined') {\n // Read once, cache it\n const stored = localStorage.getItem(STORAGE_KEY)\n globalHistoryCache = stored ? JSON.parse(stored) : {}\n return globalHistoryCache!\n }\n return {}\n } catch (e) {\n console.error('Error parsing history:', e)\n return {}\n }\n }, [])\n\n // Debounce timer ref to prevent excessive localStorage writes\n const saveTimerRef = useRef<null | ReturnType<typeof setTimeout>>(null)\n\n const saveToLocalStorage = useCallback((newGlobalHistory: HistoryState) => {\n // Update cache immediately\n globalHistoryCache = newGlobalHistory\n\n if (typeof localStorage === 'undefined') {\n return\n }\n\n // Clear any pending save\n if (saveTimerRef.current) {\n clearTimeout(saveTimerRef.current)\n }\n\n // Debounce the save operation by 500ms\n saveTimerRef.current = setTimeout(() => {\n // Use requestIdleCallback if available to avoid blocking the main thread\n if (typeof requestIdleCallback !== 'undefined') {\n requestIdleCallback(\n () => {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(newGlobalHistory))\n } catch (e) {\n console.warn('Failed to save history to localStorage', e)\n }\n },\n { timeout: 2000 },\n )\n } else {\n // Fallback for browsers without requestIdleCallback\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(newGlobalHistory))\n } catch (e) {\n console.warn('Failed to save history to localStorage', e)\n }\n }\n saveTimerRef.current = null\n }, 500)\n }, [])\n\n // Sync with other tabs\n useEffect(() => {\n const handleStorageChange = (e: StorageEvent) => {\n if (e.key === STORAGE_KEY && e.newValue) {\n try {\n globalHistoryCache = JSON.parse(e.newValue)\n } catch (err) {\n // ignore parse error\n }\n }\n }\n\n window.addEventListener('storage', handleStorageChange)\n return () => {\n window.removeEventListener('storage', handleStorageChange)\n }\n }, [])\n\n // Clear previous history\n const clearHistory = useCallback(() => {\n const latestHistory = { ...getLatestHistory() }\n let hasChanges = false\n Object.keys(latestHistory).forEach((k) => {\n if (!k.startsWith(id?.toString() ?? '')) {\n delete latestHistory[k]\n hasChanges = true\n }\n })\n \n if (hasChanges) {\n saveToLocalStorage(latestHistory)\n }\n }, [id, 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 let historyUpdated = false\n const newHistoryArray = [...history]\n\n if (currentIndex == -1) {\n newIndex = 0\n \n // Get initial value from form data instead of subscribing to useField\n // This implementation avoids re-rendering on every keystroke\n try {\n const data = getData()\n // We need to resolve the value from the data object using the path\n // path might be 'group.subgroup.field'\n if (path) {\n const value = getSiblingData(data, path)\n if (value) {\n newHistoryArray[newIndex] = value\n historyUpdated = true\n }\n }\n } catch (e) {\n // If we can't get the data, just ignore\n }\n }\n\n if (historyUpdated) {\n const newGlobalHistory = {\n ...latestHistory,\n [fieldKey]: { currentIndex: newIndex, history: newHistoryArray },\n }\n saveToLocalStorage(newGlobalHistory)\n }\n }, [fieldKey, getData, path, clearHistory, getLatestHistory, saveToLocalStorage])\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 \n // Create new history array slice, appending new data\n let newHistory = [...history.slice(0, currentIndex + 1), data]\n \n // Enforce Max History Size\n if (newHistory.length > MAX_HISTORY_SIZE) {\n newHistory = newHistory.slice(newHistory.length - MAX_HISTORY_SIZE)\n }\n \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 \n // Note: We deliberately do not return currentValue to avoid subscription re-renders\n // The consumers of this hook (UndoRedoActions) didn't use it anyway.\n\n return {\n canRedo,\n canUndo,\n redo,\n set,\n undo,\n }\n}\n"],"names":["useDocumentInfo","useForm","useCallback","useEffect","useRef","getSiblingData","PLUGIN_NAME","useFieldProps","STORAGE_KEY","MAX_HISTORY_SIZE","globalHistoryCache","useHistory","id","path","schemaPath","getData","fieldKey","getLatestHistory","localStorage","stored","getItem","JSON","parse","e","console","error","saveTimerRef","saveToLocalStorage","newGlobalHistory","current","clearTimeout","setTimeout","requestIdleCallback","setItem","stringify","warn","timeout","handleStorageChange","key","newValue","err","window","addEventListener","removeEventListener","clearHistory","latestHistory","hasChanges","Object","keys","forEach","k","startsWith","toString","currentIndex","history","newIndex","historyUpdated","newHistoryArray","data","value","set","newHistory","slice","length","undo","undefined","redo","getLatestFieldHistory","fieldHistory","canUndo","canRedo"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LexicalEditor } from 'lexical';
|
|
2
|
+
type UseStreamingUpdateParams = {
|
|
3
|
+
editor: LexicalEditor;
|
|
4
|
+
isLoading: boolean;
|
|
5
|
+
object: any;
|
|
6
|
+
};
|
|
7
|
+
export declare const useStreamingUpdate: ({ editor, isLoading, object }: UseStreamingUpdateParams) => void;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { useForm } from '@payloadcms/ui';
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js';
|
|
4
|
+
import { setSafeLexicalState } from '../../../utilities/setSafeLexicalState.js';
|
|
5
|
+
export const useStreamingUpdate = ({ editor, isLoading, object })=>{
|
|
6
|
+
const { field, path: pathFromContext } = useFieldProps();
|
|
7
|
+
const { dispatchFields } = useForm();
|
|
8
|
+
// Ref for latest object to avoid effect re-runs during high-frequency streaming
|
|
9
|
+
const objectRef = useRef(object);
|
|
10
|
+
objectRef.current = object;
|
|
11
|
+
useEffect(()=>{
|
|
12
|
+
// Only run the animation loop while loading (streaming)
|
|
13
|
+
if (!isLoading) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
let reqId;
|
|
17
|
+
const loop = ()=>{
|
|
18
|
+
const currentObject = objectRef.current;
|
|
19
|
+
if (currentObject) {
|
|
20
|
+
if (field?.type === 'richText') {
|
|
21
|
+
setSafeLexicalState(currentObject, editor);
|
|
22
|
+
} else if (field && 'name' in field && currentObject[field.name]) {
|
|
23
|
+
// Use dispatchFields for high-frequency streaming updates to avoid re-renders
|
|
24
|
+
dispatchFields({
|
|
25
|
+
type: 'UPDATE',
|
|
26
|
+
path: pathFromContext ?? '',
|
|
27
|
+
value: currentObject[field.name]
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Continue loop
|
|
32
|
+
reqId = requestAnimationFrame(loop);
|
|
33
|
+
};
|
|
34
|
+
// Start loop
|
|
35
|
+
loop();
|
|
36
|
+
return ()=>{
|
|
37
|
+
cancelAnimationFrame(reqId);
|
|
38
|
+
};
|
|
39
|
+
}, [
|
|
40
|
+
isLoading,
|
|
41
|
+
editor,
|
|
42
|
+
field,
|
|
43
|
+
dispatchFields,
|
|
44
|
+
pathFromContext
|
|
45
|
+
]);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=useStreamingUpdate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/ui/Compose/hooks/useStreamingUpdate.ts"],"sourcesContent":["import type { LexicalEditor } from 'lexical'\n\nimport { useForm } from '@payloadcms/ui'\nimport { useEffect, useRef } from 'react'\n\nimport { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js'\nimport { setSafeLexicalState } from '../../../utilities/setSafeLexicalState.js'\n\ntype UseStreamingUpdateParams = {\n editor: LexicalEditor\n isLoading: boolean\n object: any\n}\n\nexport const useStreamingUpdate = ({ editor, isLoading, object }: UseStreamingUpdateParams) => {\n const { field, path: pathFromContext } = useFieldProps()\n const { dispatchFields } = useForm()\n\n // Ref for latest object to avoid effect re-runs during high-frequency streaming\n const objectRef = useRef(object)\n objectRef.current = object\n\n useEffect(() => {\n // Only run the animation loop while loading (streaming)\n if (!isLoading) {\n return\n }\n\n let reqId: number\n\n const loop = () => {\n const currentObject = objectRef.current\n\n if (currentObject) {\n if (field?.type === 'richText') {\n setSafeLexicalState(currentObject, editor)\n } else if (field && 'name' in field && currentObject[field.name]) {\n // Use dispatchFields for high-frequency streaming updates to avoid re-renders\n dispatchFields({\n type: 'UPDATE',\n path: pathFromContext ?? '',\n value: currentObject[field.name],\n } as any)\n }\n }\n\n // Continue loop\n reqId = requestAnimationFrame(loop)\n }\n\n // Start loop\n loop()\n\n return () => {\n cancelAnimationFrame(reqId)\n }\n }, [isLoading, editor, field, dispatchFields, pathFromContext])\n}\n"],"names":["useForm","useEffect","useRef","useFieldProps","setSafeLexicalState","useStreamingUpdate","editor","isLoading","object","field","path","pathFromContext","dispatchFields","objectRef","current","reqId","loop","currentObject","type","name","value","requestAnimationFrame","cancelAnimationFrame"],"mappings":"AAEA,SAASA,OAAO,QAAQ,iBAAgB;AACxC,SAASC,SAAS,EAAEC,MAAM,QAAQ,QAAO;AAEzC,SAASC,aAAa,QAAQ,oDAAmD;AACjF,SAASC,mBAAmB,QAAQ,4CAA2C;AAQ/E,OAAO,MAAMC,qBAAqB,CAAC,EAAEC,MAAM,EAAEC,SAAS,EAAEC,MAAM,EAA4B;IACxF,MAAM,EAAEC,KAAK,EAAEC,MAAMC,eAAe,EAAE,GAAGR;IACzC,MAAM,EAAES,cAAc,EAAE,GAAGZ;IAE3B,gFAAgF;IAChF,MAAMa,YAAYX,OAAOM;IACzBK,UAAUC,OAAO,GAAGN;IAEpBP,UAAU;QACR,wDAAwD;QACxD,IAAI,CAACM,WAAW;YACd;QACF;QAEA,IAAIQ;QAEJ,MAAMC,OAAO;YACX,MAAMC,gBAAgBJ,UAAUC,OAAO;YAEvC,IAAIG,eAAe;gBACjB,IAAIR,OAAOS,SAAS,YAAY;oBAC9Bd,oBAAoBa,eAAeX;gBACrC,OAAO,IAAIG,SAAS,UAAUA,SAASQ,aAAa,CAACR,MAAMU,IAAI,CAAC,EAAE;oBAChE,8EAA8E;oBAC9EP,eAAe;wBACbM,MAAM;wBACNR,MAAMC,mBAAmB;wBACzBS,OAAOH,aAAa,CAACR,MAAMU,IAAI,CAAC;oBAClC;gBACF;YACF;YAEA,gBAAgB;YAChBJ,QAAQM,sBAAsBL;QAChC;QAEA,aAAa;QACbA;QAEA,OAAO;YACLM,qBAAqBP;QACvB;IACF,GAAG;QAACR;QAAWD;QAAQG;QAAOG;QAAgBD;KAAgB;AAChE,EAAC"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Button, toast, useConfig } from '@payloadcms/ui';
|
|
4
|
+
// @ts-expect-error - Next.js types are not resolving correctly with nodenext but runtime is fine
|
|
5
|
+
import { useRouter } from 'next/navigation';
|
|
6
|
+
import React, { use, useEffect, useState } from 'react';
|
|
7
|
+
import { excludeCollections } from '../../defaults.js';
|
|
8
|
+
import { InstructionsContext } from '../../providers/InstructionsProvider/context.js';
|
|
9
|
+
export const ConfigDashboard = ()=>{
|
|
10
|
+
const { config: { collections, routes: { admin: adminRoute, api: apiRoute } } } = useConfig();
|
|
11
|
+
const router = useRouter();
|
|
12
|
+
const { refresh, setEnabledCollections: setEnabledCollectionsInContext } = use(InstructionsContext);
|
|
13
|
+
const [enabledCollections, setEnabledCollections] = useState([]);
|
|
14
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
15
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
16
|
+
const availableCollections = collections.filter((c)=>!excludeCollections.includes(c.slug) && !c.admin?.hidden);
|
|
17
|
+
useEffect(()=>{
|
|
18
|
+
const fetchSettings = async ()=>{
|
|
19
|
+
try {
|
|
20
|
+
const response = await fetch(`${apiRoute}/globals/ai-providers`);
|
|
21
|
+
if (response.ok) {
|
|
22
|
+
const data = await response.json();
|
|
23
|
+
// Handle both simple array and object wrapper if Payload wraps it
|
|
24
|
+
const storedEnabled = data.enabledCollections || [];
|
|
25
|
+
setEnabledCollections(Array.isArray(storedEnabled) ? storedEnabled : []);
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error('Failed to fetch AI settings:', error);
|
|
29
|
+
} finally{
|
|
30
|
+
setIsLoading(false);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
fetchSettings().catch((e)=>{
|
|
34
|
+
console.log(e);
|
|
35
|
+
});
|
|
36
|
+
}, [
|
|
37
|
+
apiRoute
|
|
38
|
+
]);
|
|
39
|
+
const handleToggle = (slug)=>{
|
|
40
|
+
setEnabledCollections((prev)=>{
|
|
41
|
+
if (prev.includes(slug)) {
|
|
42
|
+
return prev.filter((s)=>s !== slug);
|
|
43
|
+
}
|
|
44
|
+
return [
|
|
45
|
+
...prev,
|
|
46
|
+
slug
|
|
47
|
+
];
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
const handleSave = async ()=>{
|
|
51
|
+
setIsSaving(true);
|
|
52
|
+
try {
|
|
53
|
+
// First fetch current settings to get ID or just rely on global update behavior
|
|
54
|
+
// We need to adhere to Payload's global update API
|
|
55
|
+
const response = await fetch(`${apiRoute}/globals/ai-providers`, {
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
enabledCollections
|
|
58
|
+
}),
|
|
59
|
+
headers: {
|
|
60
|
+
'Content-Type': 'application/json'
|
|
61
|
+
},
|
|
62
|
+
method: 'POST'
|
|
63
|
+
});
|
|
64
|
+
if (response.ok) {
|
|
65
|
+
toast.success('Settings saved successfully');
|
|
66
|
+
if (setEnabledCollectionsInContext) {
|
|
67
|
+
setEnabledCollectionsInContext(enabledCollections);
|
|
68
|
+
}
|
|
69
|
+
if (refresh) {
|
|
70
|
+
await refresh();
|
|
71
|
+
}
|
|
72
|
+
router.refresh();
|
|
73
|
+
} else {
|
|
74
|
+
toast.error('Failed to save settings');
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('Error saving settings:', error);
|
|
78
|
+
toast.error('Error saving settings');
|
|
79
|
+
} finally{
|
|
80
|
+
setIsSaving(false);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
if (isLoading) {
|
|
84
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
85
|
+
style: {
|
|
86
|
+
padding: '20px',
|
|
87
|
+
textAlign: 'center'
|
|
88
|
+
},
|
|
89
|
+
children: "Loading configuration..."
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
93
|
+
style: {
|
|
94
|
+
background: 'var(--theme-elevation-50)',
|
|
95
|
+
// border: '1px solid var(--theme-elevation-150)',
|
|
96
|
+
// borderRadius: '8px',
|
|
97
|
+
// borderBottom: '1px solid var(--theme-elevation-150)',
|
|
98
|
+
// borderTop: '1px solid var(--theme-elevation-150)',
|
|
99
|
+
marginBottom: '20px',
|
|
100
|
+
overflow: 'hidden'
|
|
101
|
+
},
|
|
102
|
+
children: [
|
|
103
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
104
|
+
style: {
|
|
105
|
+
alignItems: 'center',
|
|
106
|
+
borderBottom: '1px solid var(--theme-elevation-150)',
|
|
107
|
+
display: 'flex',
|
|
108
|
+
justifyContent: 'space-between',
|
|
109
|
+
padding: '8px var(--gutter-h)'
|
|
110
|
+
},
|
|
111
|
+
children: [
|
|
112
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
113
|
+
children: [
|
|
114
|
+
/*#__PURE__*/ _jsx("h2", {
|
|
115
|
+
style: {
|
|
116
|
+
margin: '0 0 5px 0'
|
|
117
|
+
},
|
|
118
|
+
children: "Let's configure your AI Plugin"
|
|
119
|
+
}),
|
|
120
|
+
/*#__PURE__*/ _jsx("p", {
|
|
121
|
+
style: {
|
|
122
|
+
color: 'var(--theme-elevation-500)',
|
|
123
|
+
fontSize: '14px',
|
|
124
|
+
margin: '0'
|
|
125
|
+
},
|
|
126
|
+
children: "Set up the provider → Choose the content → Refine the behavior."
|
|
127
|
+
})
|
|
128
|
+
]
|
|
129
|
+
}),
|
|
130
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
131
|
+
style: {
|
|
132
|
+
display: 'flex',
|
|
133
|
+
gap: '10px'
|
|
134
|
+
},
|
|
135
|
+
children: [
|
|
136
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
137
|
+
buttonStyle: "secondary",
|
|
138
|
+
el: "link",
|
|
139
|
+
to: `${adminRoute}/globals/ai-providers`,
|
|
140
|
+
children: "Providers"
|
|
141
|
+
}),
|
|
142
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
143
|
+
disabled: isSaving,
|
|
144
|
+
onClick: handleSave,
|
|
145
|
+
children: isSaving ? 'Saving...' : 'Save Changes'
|
|
146
|
+
})
|
|
147
|
+
]
|
|
148
|
+
})
|
|
149
|
+
]
|
|
150
|
+
}),
|
|
151
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
152
|
+
style: {
|
|
153
|
+
padding: '24px var(--gutter-h)'
|
|
154
|
+
},
|
|
155
|
+
children: [
|
|
156
|
+
/*#__PURE__*/ _jsx("h5", {
|
|
157
|
+
style: {
|
|
158
|
+
marginBottom: '15px'
|
|
159
|
+
},
|
|
160
|
+
children: "Select the collections where AI features should be available, toggle them on or off, and save your changes."
|
|
161
|
+
}),
|
|
162
|
+
/*#__PURE__*/ _jsx("div", {
|
|
163
|
+
style: {
|
|
164
|
+
display: 'grid',
|
|
165
|
+
gap: '15px',
|
|
166
|
+
gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))'
|
|
167
|
+
},
|
|
168
|
+
children: availableCollections.map((collection)=>{
|
|
169
|
+
const isEnabled = enabledCollections.includes(collection.slug);
|
|
170
|
+
return /*#__PURE__*/ _jsxs("button", {
|
|
171
|
+
onClick: ()=>handleToggle(collection.slug),
|
|
172
|
+
style: {
|
|
173
|
+
alignItems: 'center',
|
|
174
|
+
background: isEnabled ? 'var(--theme-elevation-100)' : 'var(--theme-elevation-50)',
|
|
175
|
+
border: `1px solid ${isEnabled ? 'var(--theme-text-success)' : 'var(--theme-elevation-200)'}`,
|
|
176
|
+
borderRadius: '6px',
|
|
177
|
+
cursor: 'pointer',
|
|
178
|
+
display: 'flex',
|
|
179
|
+
gap: '10px',
|
|
180
|
+
padding: '10px 15px',
|
|
181
|
+
textAlign: 'left',
|
|
182
|
+
transition: 'all 0.2s ease',
|
|
183
|
+
width: '100%'
|
|
184
|
+
},
|
|
185
|
+
type: "button",
|
|
186
|
+
children: [
|
|
187
|
+
/*#__PURE__*/ _jsx("div", {
|
|
188
|
+
style: {
|
|
189
|
+
alignItems: 'center',
|
|
190
|
+
background: isEnabled ? 'var(--theme-text-success)' : 'var(--theme-elevation-200)',
|
|
191
|
+
borderRadius: '12px',
|
|
192
|
+
display: 'flex',
|
|
193
|
+
height: '24px',
|
|
194
|
+
justifyContent: isEnabled ? 'flex-end' : 'flex-start',
|
|
195
|
+
padding: '2px',
|
|
196
|
+
transition: 'all 0.2s ease',
|
|
197
|
+
width: '44px'
|
|
198
|
+
},
|
|
199
|
+
children: /*#__PURE__*/ _jsx("div", {
|
|
200
|
+
style: {
|
|
201
|
+
background: 'white',
|
|
202
|
+
borderRadius: '50%',
|
|
203
|
+
height: '20px',
|
|
204
|
+
width: '20px'
|
|
205
|
+
}
|
|
206
|
+
})
|
|
207
|
+
}),
|
|
208
|
+
/*#__PURE__*/ _jsx("span", {
|
|
209
|
+
style: {
|
|
210
|
+
fontWeight: 500
|
|
211
|
+
},
|
|
212
|
+
children: typeof collection.labels?.singular === 'string' ? collection.labels.singular : collection.labels?.singular?.en || collection.slug
|
|
213
|
+
})
|
|
214
|
+
]
|
|
215
|
+
}, collection.slug);
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
]
|
|
219
|
+
})
|
|
220
|
+
]
|
|
221
|
+
});
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/ConfigDashboard/index.tsx"],"sourcesContent":["'use client'\n\nimport { Button, toast, useConfig } from '@payloadcms/ui'\n// @ts-expect-error - Next.js types are not resolving correctly with nodenext but runtime is fine\nimport { useRouter } from 'next/navigation'\nimport React, { use, useEffect, useState } from 'react'\n\nimport { excludeCollections } from '../../defaults.js'\nimport { InstructionsContext } from '../../providers/InstructionsProvider/context.js'\n\nexport const ConfigDashboard: React.FC = () => {\n const {\n config: {\n collections,\n routes: { admin: adminRoute, api: apiRoute },\n },\n } = useConfig()\n const router = useRouter()\n const { refresh, setEnabledCollections: setEnabledCollectionsInContext } =\n use(InstructionsContext)\n\n const [enabledCollections, setEnabledCollections] = useState<string[]>([])\n const [isLoading, setIsLoading] = useState(true)\n const [isSaving, setIsSaving] = useState(false)\n\n const availableCollections = collections.filter(\n (c) =>\n !excludeCollections.includes(c.slug) &&\n !(c.admin as unknown as { hidden?: ((args: any) => boolean) | boolean })?.hidden,\n )\n\n useEffect(() => {\n const fetchSettings = async () => {\n try {\n const response = await fetch(`${apiRoute}/globals/ai-providers`)\n if (response.ok) {\n const data = await response.json()\n // Handle both simple array and object wrapper if Payload wraps it\n const storedEnabled = data.enabledCollections || []\n setEnabledCollections(Array.isArray(storedEnabled) ? storedEnabled : [])\n }\n } catch (error) {\n console.error('Failed to fetch AI settings:', error)\n } finally {\n setIsLoading(false)\n }\n }\n\n fetchSettings().catch((e) => {\n console.log(e)\n })\n }, [apiRoute])\n\n const handleToggle = (slug: string) => {\n setEnabledCollections((prev) => {\n if (prev.includes(slug)) {\n return prev.filter((s) => s !== slug)\n }\n return [...prev, slug]\n })\n }\n\n const handleSave = async () => {\n setIsSaving(true)\n try {\n // First fetch current settings to get ID or just rely on global update behavior\n // We need to adhere to Payload's global update API\n const response = await fetch(`${apiRoute}/globals/ai-providers`, {\n body: JSON.stringify({\n enabledCollections,\n }),\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n if (response.ok) {\n toast.success('Settings saved successfully')\n if (setEnabledCollectionsInContext) {\n setEnabledCollectionsInContext(enabledCollections)\n }\n if (refresh) {\n await refresh()\n }\n router.refresh()\n } else {\n toast.error('Failed to save settings')\n }\n } catch (error) {\n console.error('Error saving settings:', error)\n toast.error('Error saving settings')\n } finally {\n setIsSaving(false)\n }\n }\n\n if (isLoading) {\n return <div style={{ padding: '20px', textAlign: 'center' }}>Loading configuration...</div>\n }\n\n return (\n <div\n style={{\n background: 'var(--theme-elevation-50)',\n // border: '1px solid var(--theme-elevation-150)',\n // borderRadius: '8px',\n // borderBottom: '1px solid var(--theme-elevation-150)',\n // borderTop: '1px solid var(--theme-elevation-150)',\n marginBottom: '20px',\n overflow: 'hidden',\n }}\n >\n <div\n style={{\n alignItems: 'center',\n borderBottom: '1px solid var(--theme-elevation-150)',\n display: 'flex',\n justifyContent: 'space-between',\n padding: '8px var(--gutter-h)',\n }}\n >\n <div>\n <h2 style={{ margin: '0 0 5px 0' }}>Let's configure your AI Plugin</h2>\n <p style={{ color: 'var(--theme-elevation-500)', fontSize: '14px', margin: '0' }}>\n Set up the provider → Choose the content → Refine the behavior.\n </p>\n </div>\n <div style={{ display: 'flex', gap: '10px' }}>\n <Button buttonStyle=\"secondary\" el=\"link\" to={`${adminRoute}/globals/ai-providers`}>\n Providers\n </Button>\n <Button disabled={isSaving} onClick={handleSave}>\n {isSaving ? 'Saving...' : 'Save Changes'}\n </Button>\n </div>\n </div>\n\n <div style={{ padding: '24px var(--gutter-h)' }}>\n <h5 style={{ marginBottom: '15px' }}>\n Select the collections where AI features should be available, toggle them on or off, and\n save your changes.\n </h5>\n <div\n style={{\n display: 'grid',\n gap: '15px',\n gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))',\n }}\n >\n {availableCollections.map((collection) => {\n const isEnabled = enabledCollections.includes(collection.slug)\n return (\n <button\n key={collection.slug}\n onClick={() => handleToggle(collection.slug)}\n style={{\n alignItems: 'center',\n background: isEnabled\n ? 'var(--theme-elevation-100)'\n : 'var(--theme-elevation-50)',\n border: `1px solid ${isEnabled ? 'var(--theme-text-success)' : 'var(--theme-elevation-200)'}`,\n borderRadius: '6px',\n cursor: 'pointer',\n display: 'flex',\n gap: '10px',\n padding: '10px 15px',\n textAlign: 'left',\n transition: 'all 0.2s ease',\n width: '100%',\n }}\n type=\"button\"\n >\n <div\n style={{\n alignItems: 'center',\n background: isEnabled\n ? 'var(--theme-text-success)'\n : 'var(--theme-elevation-200)',\n borderRadius: '12px',\n display: 'flex',\n height: '24px',\n justifyContent: isEnabled ? 'flex-end' : 'flex-start',\n padding: '2px',\n transition: 'all 0.2s ease',\n width: '44px',\n }}\n >\n <div\n style={{\n background: 'white',\n borderRadius: '50%',\n height: '20px',\n width: '20px',\n }}\n />\n </div>\n <span style={{ fontWeight: 500 }}>\n {typeof collection.labels?.singular === 'string'\n ? collection.labels.singular\n : collection.labels?.singular?.en || collection.slug}\n </span>\n </button>\n )\n })}\n </div>\n </div>\n </div>\n )\n}\n"],"names":["Button","toast","useConfig","useRouter","React","use","useEffect","useState","excludeCollections","InstructionsContext","ConfigDashboard","config","collections","routes","admin","adminRoute","api","apiRoute","router","refresh","setEnabledCollections","setEnabledCollectionsInContext","enabledCollections","isLoading","setIsLoading","isSaving","setIsSaving","availableCollections","filter","c","includes","slug","hidden","fetchSettings","response","fetch","ok","data","json","storedEnabled","Array","isArray","error","console","catch","e","log","handleToggle","prev","s","handleSave","body","JSON","stringify","headers","method","success","div","style","padding","textAlign","background","marginBottom","overflow","alignItems","borderBottom","display","justifyContent","h2","margin","p","color","fontSize","gap","buttonStyle","el","to","disabled","onClick","h5","gridTemplateColumns","map","collection","isEnabled","button","border","borderRadius","cursor","transition","width","type","height","span","fontWeight","labels","singular","en"],"mappings":"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"}
|