@ai-stack/payloadcms 3.68.0 → 3.76.0-beta.0
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 +5 -0
- package/dist/ai/core/media/image/handlers/multimodal.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 +11 -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 +34 -24
- 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/AISettings.js +47 -20
- package/dist/collections/AISettings.js.map +1 -1
- package/dist/collections/Instructions.js +37 -0
- 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 +41 -24
- package/dist/endpoints/fetchVoices.js.map +1 -1
- package/dist/endpoints/index.js +194 -16
- package/dist/endpoints/index.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 +16 -32
- 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 +21 -2
- 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 +34 -5
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/ui/AIConfigDashboard/index.js +198 -22
- package/dist/ui/AIConfigDashboard/index.js.map +1 -1
- package/dist/ui/AIConfigDashboard/index.jsx +159 -13
- 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/itemsMap.js +12 -6
- package/dist/ui/Compose/hooks/menu/itemsMap.js.map +1 -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/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 +150 -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/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/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 +8 -1
- package/dist/utilities/updateFieldsConfig.js.map +1 -1
- package/package.json +35 -33
- 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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/ui/Compose/hooks/useGenerateUpload.ts"],"sourcesContent":["import { toast, useConfig, useDocumentInfo, useField, useForm, useLocale } from '@payloadcms/ui'\nimport { type RefObject, useCallback, useState } from 'react'\n\nimport type { GenerateTextarea } from '../../../types.js'\n\nimport { PLUGIN_AI_JOBS_TABLE, PLUGIN_API_ENDPOINT_GENERATE_UPLOAD } from '../../../defaults.js'\nimport { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js'\nimport { useHistory } from './useHistory.js'\n\ntype UseGenerateUploadParams = {\n instructionIdRef: RefObject<string>\n}\n\nexport const useGenerateUpload = ({ instructionIdRef }: UseGenerateUploadParams) => {\n const { config } = useConfig()\n const {\n routes: { api },\n serverURL,\n } = config\n const { id: documentId, collectionSlug } = useDocumentInfo()\n const localFromContext = useLocale()\n const { getData } = useForm()\n const { set: setHistory } = useHistory()\n\n const { field, path: pathFromContext } = useFieldProps()\n const { setValue } = useField<any>({\n path: pathFromContext ?? '',\n })\n\n // Async job UI state\n const [jobStatus, setJobStatus] = useState<string | undefined>(undefined)\n const [jobProgress, setJobProgress] = useState<number>(0)\n const [isJobActive, setIsJobActive] = useState<boolean>(false)\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 json = await uploadResponse.json()\n const { job, result } = json || {}\n if (result) {\n setValue(result?.id)\n setHistory(result?.id)\n\n // Show toast to prompt user to save\n toast.success('Image generated successfully! Click Save to see the preview.')\n\n return uploadResponse\n }\n\n // Async job: poll AI Jobs collection for status/progress/result_id\n if (job && job.id) {\n setIsJobActive(true)\n const cancelled = false\n let attempts = 0\n const maxAttempts = 600 // up to ~10 minutes @ 1s\n\n // Basic in-hook state via closure variables; UI will re-render off fetches below\n const poll = async (): Promise<void> => {\n if (cancelled) {\n return\n }\n try {\n const res = await fetch(`${serverURL}${api}/${PLUGIN_AI_JOBS_TABLE}/${job.id}`, {\n credentials: 'include',\n })\n if (res.ok) {\n const jobDoc = await res.json()\n const { progress, result_id, status } = jobDoc || {}\n setJobStatus(status)\n setJobProgress(progress ?? 0)\n // When result present, set field and finish\n if (status === 'completed' && result_id) {\n let valueToSet = result_id\n\n // Attempt to fetch full document for immediate preview\n if (field && 'relationTo' in field && typeof field.relationTo === 'string') {\n let attempts = 0\n const maxAttempts = 3\n while (attempts < maxAttempts) {\n try {\n const docRes = await fetch(\n `${serverURL}${api}/${field.relationTo}/${result_id}`,\n {\n credentials: 'include',\n },\n )\n if (docRes.ok) {\n const doc = await docRes.json()\n // Verify we have a URL for preview\n if (doc && doc.url) {\n valueToSet = doc\n break\n }\n }\n } catch (e) {\n console.error('Failed to fetch generated document for preview:', e)\n }\n attempts++\n if (attempts < maxAttempts) {\n await new Promise((resolve) => setTimeout(resolve, 500))\n }\n }\n }\n\n setValue(valueToSet)\n setHistory(result_id)\n setIsJobActive(false)\n return\n }\n if (status === 'failed') {\n setIsJobActive(false)\n throw new Error('Video generation failed')\n }\n }\n } catch (_) {\n // silent retry\n }\n\n attempts += 1\n if (!cancelled && attempts < maxAttempts) {\n setTimeout(poll, 1000)\n }\n }\n setTimeout(poll, 1000)\n return uploadResponse\n }\n\n throw new Error('generateUpload: Unexpected response')\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 })\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 }, [\n getData,\n localFromContext?.code,\n instructionIdRef,\n // setValue,\n documentId,\n collectionSlug,\n serverURL,\n api,\n setHistory,\n ])\n\n return {\n generateUpload,\n isJobActive,\n jobProgress,\n jobStatus,\n }\n}\n"],"names":["toast","useConfig","useDocumentInfo","useField","useForm","useLocale","useCallback","useState","PLUGIN_AI_JOBS_TABLE","PLUGIN_API_ENDPOINT_GENERATE_UPLOAD","useFieldProps","useHistory","useGenerateUpload","instructionIdRef","config","routes","api","serverURL","id","documentId","collectionSlug","localFromContext","getData","set","setHistory","field","path","pathFromContext","setValue","jobStatus","setJobStatus","undefined","jobProgress","setJobProgress","isJobActive","setIsJobActive","generateUpload","doc","currentInstructionId","current","fetch","body","JSON","stringify","locale","code","options","instructionId","credentials","headers","method","then","uploadResponse","ok","json","job","result","success","cancelled","attempts","maxAttempts","poll","res","jobDoc","progress","result_id","status","valueToSet","relationTo","docRes","url","e","console","error","Promise","resolve","setTimeout","Error","_","errors","errStr","map","message","join","catch"],"mappings":"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"}
|
|
@@ -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":"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"}
|
|
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":"AAAA;AAEA,SAASA,eAAe,EAAEC,OAAO,QAAQ,iBAAgB;AACzD,SAASC,WAAW,EAAEC,SAAS,EAAEC,MAAM,QAAQ,QAAO;AACtD,SAASC,cAAc,QAAQ,iBAAgB;AAE/C,SAASC,WAAW,QAAQ,uBAAsB;AAClD,SAASC,aAAa,QAAQ,oDAAmD;AAEjF,MAAMC,cAAc,CAAC,EAAEF,YAAY,eAAe,CAAC;AACnD,MAAMG,mBAAmB;AASzB,yEAAyE;AACzE,IAAIC,qBAA0C;AAE9C,OAAO,MAAMC,aAAa;IACxB,MAAM,EAAEC,EAAE,EAAE,GAAGZ;IACf,MAAM,EAAEa,IAAI,EAAEC,UAAU,EAAE,GAAGP;IAC7B,MAAM,EAAEQ,OAAO,EAAE,GAAGd;IAEpB,MAAMe,WAAW,CAAC,EAAEJ,GAAG,CAAC,EAAEE,WAAW,CAAC;IAEtC,MAAMG,mBAAmBf,YAAY;QACnC,4BAA4B;QAC5B,IAAIQ,oBAAoB;YACtB,OAAOA;QACT;QAEA,IAAI;YACF,IAAI,OAAOQ,iBAAiB,aAAa;gBACvC,sBAAsB;gBACtB,MAAMC,SAASD,aAAaE,OAAO,CAACZ;gBACpCE,qBAAqBS,SAASE,KAAKC,KAAK,CAACH,UAAU,CAAC;gBACpD,OAAOT;YACT;YACA,OAAO,CAAC;QACV,EAAE,OAAOa,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;QAC3BlB,qBAAqBkB;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;oBACE,IAAI;wBACFd,aAAae,OAAO,CAACzB,aAAaa,KAAKa,SAAS,CAACN;oBACnD,EAAE,OAAOL,GAAG;wBACVC,QAAQW,IAAI,CAAC,0CAA0CZ;oBACzD;gBACF,GACA;oBAAEa,SAAS;gBAAK;YAEpB,OAAO;gBACL,oDAAoD;gBACpD,IAAI;oBACFlB,aAAae,OAAO,CAACzB,aAAaa,KAAKa,SAAS,CAACN;gBACnD,EAAE,OAAOL,GAAG;oBACVC,QAAQW,IAAI,CAAC,0CAA0CZ;gBACzD;YACF;YACAG,aAAaG,OAAO,GAAG;QACzB,GAAG;IACL,GAAG,EAAE;IAEL,uBAAuB;IACvB1B,UAAU;QACR,MAAMkC,sBAAsB,CAACd;YAC3B,IAAIA,EAAEe,GAAG,KAAK9B,eAAee,EAAEgB,QAAQ,EAAE;gBACvC,IAAI;oBACF7B,qBAAqBW,KAAKC,KAAK,CAACC,EAAEgB,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,eAAe1C,YAAY;QAC/B,MAAM2C,gBAAgB;YAAE,GAAG5B,kBAAkB;QAAC;QAC9C,IAAI6B,aAAa;QACjBC,OAAOC,IAAI,CAACH,eAAeI,OAAO,CAAC,CAACC;YAClC,IAAI,CAACA,EAAEC,UAAU,CAACvC,IAAIwC,cAAc,KAAK;gBACvC,OAAOP,aAAa,CAACK,EAAE;gBACvBJ,aAAa;YACf;QACF;QAEA,IAAIA,YAAY;YACdnB,mBAAmBkB;QACrB;IACF,GAAG;QAACjC;QAAIK;QAAkBU;KAAmB;IAE7CxB,UAAU;QACR,kFAAkF;QAClFyC;QAEA,MAAMC,gBAAgB5B;QACtB,MAAM,EAAEoC,YAAY,EAAEC,OAAO,EAAE,GAAGT,aAAa,CAAC7B,SAAS,IAAI;YAC3DqC,cAAc,CAAC;YACfC,SAAS,EAAE;QACb;QAEA,IAAIC,WAAWF;QACf,IAAIG,iBAAiB;QACrB,MAAMC,kBAAkB;eAAIH;SAAQ;QAEpC,IAAID,gBAAgB,CAAC,GAAG;YACtBE,WAAW;YAEX,sEAAsE;YACtE,6DAA6D;YAC7D,IAAI;gBACF,MAAMG,OAAO3C;gBACb,mEAAmE;gBACnE,uCAAuC;gBACvC,IAAIF,MAAM;oBACR,MAAM8C,QAAQtD,eAAeqD,MAAM7C;oBACnC,IAAI8C,OAAO;wBACTF,eAAe,CAACF,SAAS,GAAGI;wBAC5BH,iBAAiB;oBACnB;gBACF;YACF,EAAE,OAAOjC,GAAG;YACV,wCAAwC;YAC1C;QACF;QAEA,IAAIiC,gBAAgB;YAClB,MAAM5B,mBAAmB;gBACvB,GAAGiB,aAAa;gBAChB,CAAC7B,SAAS,EAAE;oBAAEqC,cAAcE;oBAAUD,SAASG;gBAAgB;YACjE;YACA9B,mBAAmBC;QACrB;IACF,GAAG;QAACZ;QAAUD;QAASF;QAAM+B;QAAc3B;QAAkBU;KAAmB;IAEhF,MAAMiC,MAAM1D,YACV,CAACwD;QACC,MAAMb,gBAAgB5B;QACtB,MAAM,EAAEoC,YAAY,EAAEC,OAAO,EAAE,GAAGT,aAAa,CAAC7B,SAAS,IAAI;YAC3DqC,cAAc,CAAC;YACfC,SAAS,EAAE;QACb;QAEA,qDAAqD;QACrD,IAAIO,aAAa;eAAIP,QAAQQ,KAAK,CAAC,GAAGT,eAAe;YAAIK;SAAK;QAE9D,2BAA2B;QAC3B,IAAIG,WAAWE,MAAM,GAAGtD,kBAAkB;YACxCoD,aAAaA,WAAWC,KAAK,CAACD,WAAWE,MAAM,GAAGtD;QACpD;QAEA,MAAMmB,mBAAmB;YACvB,GAAGiB,aAAa;YAChB,CAAC7B,SAAS,EAAE;gBAAEqC,cAAcQ,WAAWE,MAAM,GAAG;gBAAGT,SAASO;YAAW;QACzE;QACAlC,mBAAmBC;QACnB,OAAO8B;IACT,GACA;QAAC1C;QAAUC;QAAkBU;KAAmB;IAGlD,MAAMqC,OAAO9D,YAAY;QACvB,MAAM2C,gBAAgB5B;QACtB,MAAM,EAAEoC,YAAY,EAAEC,OAAO,EAAE,GAAGT,aAAa,CAAC7B,SAAS,IAAI;YAAEqC,cAAc,CAAC;YAAGC,SAAS,EAAE;QAAC;QAC7F,IAAID,eAAe,GAAG;YACpB,MAAME,WAAWF,eAAe;YAChC,MAAMd,WAAWe,OAAO,CAACC,SAAS;YAClC,MAAM3B,mBAAmB;gBACvB,GAAGiB,aAAa;gBAChB,CAAC7B,SAAS,EAAE;oBAAEqC,cAAcE;oBAAUD;gBAAQ;YAChD;YACA3B,mBAAmBC;YACnB,OAAOW;QACT;QACA,OAAO0B;IACT,GAAG;QAACjD;QAAUC;QAAkBU;KAAmB;IAEnD,MAAMuC,OAAOhE,YAAY;QACvB,MAAM2C,gBAAgB5B;QACtB,MAAM,EAAEoC,YAAY,EAAEC,OAAO,EAAE,GAAGT,aAAa,CAAC7B,SAAS,IAAI;YAAEqC,cAAc,CAAC;YAAGC,SAAS,EAAE;QAAC;QAC7F,IAAID,eAAeC,QAAQS,MAAM,GAAG,GAAG;YACrC,MAAMR,WAAWF,eAAe;YAChC,MAAMd,WAAWe,OAAO,CAACC,SAAS;YAClC,MAAM3B,mBAAmB;gBACvB,GAAGiB,aAAa;gBAChB,CAAC7B,SAAS,EAAE;oBAAEqC,cAAcE;oBAAUD;gBAAQ;YAChD;YACA3B,mBAAmBC;YACnB,OAAOW;QACT;QACA,OAAO0B;IACT,GAAG;QAACjD;QAAUC;QAAkBU;KAAmB;IAEnD,MAAMwC,wBAAwBjE,YAAY;QACxC,MAAM2C,gBAAgB5B;QACtB,OAAO4B,aAAa,CAAC7B,SAAS,IAAI;YAAEqC,cAAc,CAAC;YAAGC,SAAS,EAAE;QAAC;IACpE,GAAG;QAACrC;QAAkBD;KAAS;IAE/B,MAAMoD,eAAeD;IAErB,MAAME,UAAUD,aAAaf,YAAY,GAAG;IAC5C,MAAMiB,UAAUF,aAAaf,YAAY,GAAGe,aAAad,OAAO,CAACS,MAAM,GAAG;IAE1E,oFAAoF;IACpF,qEAAqE;IAErE,OAAO;QACLO;QACAD;QACAH;QACAN;QACAI;IACF;AACF,EAAC"}
|
|
@@ -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"}
|
|
@@ -6,27 +6,47 @@ export const DynamicVoiceSelect = (props)=>{
|
|
|
6
6
|
const { name, path } = props;
|
|
7
7
|
// Get provider from siblings
|
|
8
8
|
const parentPath = path.split('.').slice(0, -1).join('.');
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
9
|
+
const providerPath = `${parentPath}.provider`;
|
|
10
|
+
// Use useFormFields to get the provider field value - this will re-render when provider changes
|
|
11
|
+
const providerField = useFormFields(([fields])=>fields[providerPath]);
|
|
12
|
+
const provider = providerField?.value || '';
|
|
12
13
|
const { setValue, value } = useField({
|
|
13
14
|
path
|
|
14
15
|
});
|
|
15
16
|
const [aiSettings, setAiSettings] = useState(null);
|
|
17
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
18
|
+
// Fetch AI Settings - re-fetch when provider changes to ensure we have latest voices
|
|
16
19
|
useEffect(()=>{
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
const fetchSettings = async ()=>{
|
|
21
|
+
setIsLoading(true);
|
|
22
|
+
try {
|
|
23
|
+
const response = await fetch('/api/globals/ai-settings?depth=1');
|
|
24
|
+
if (response.ok) {
|
|
25
|
+
const data = await response.json();
|
|
26
|
+
setAiSettings(data);
|
|
27
|
+
}
|
|
28
|
+
} catch (err) {
|
|
29
|
+
console.error('Error fetching AI settings:', err);
|
|
30
|
+
} finally{
|
|
31
|
+
setIsLoading(false);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
void fetchSettings();
|
|
35
|
+
}, [
|
|
36
|
+
provider
|
|
37
|
+
]) // Re-fetch when provider changes to ensure we have the latest voices
|
|
38
|
+
;
|
|
19
39
|
const voices = useMemo(()=>{
|
|
20
|
-
if (!provider || !aiSettings) {
|
|
40
|
+
if (!provider || !aiSettings?.providers) {
|
|
21
41
|
return [];
|
|
22
42
|
}
|
|
23
|
-
|
|
24
|
-
|
|
43
|
+
// Find the provider block matching the selected provider
|
|
44
|
+
const providerBlock = aiSettings.providers.find((p)=>p.blockType === provider && p.enabled !== false);
|
|
45
|
+
if (!providerBlock?.voices) {
|
|
25
46
|
return [];
|
|
26
47
|
}
|
|
27
|
-
// Get voices from provider block
|
|
28
|
-
|
|
29
|
-
return voicesArray.filter((v)=>v.enabled !== false).map((v)=>({
|
|
48
|
+
// Get enabled voices from provider block
|
|
49
|
+
return providerBlock.voices.filter((v)=>v.enabled !== false).map((v)=>({
|
|
30
50
|
label: v.name || v.id,
|
|
31
51
|
value: v.id
|
|
32
52
|
}));
|
|
@@ -34,6 +54,19 @@ export const DynamicVoiceSelect = (props)=>{
|
|
|
34
54
|
provider,
|
|
35
55
|
aiSettings
|
|
36
56
|
]);
|
|
57
|
+
// Clear voice selection when provider changes and current voice is not available
|
|
58
|
+
useEffect(()=>{
|
|
59
|
+
if (value && voices.length > 0) {
|
|
60
|
+
const voiceExists = voices.some((v)=>v.value === value);
|
|
61
|
+
if (!voiceExists) {
|
|
62
|
+
setValue('');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}, [
|
|
66
|
+
voices,
|
|
67
|
+
value,
|
|
68
|
+
setValue
|
|
69
|
+
]);
|
|
37
70
|
if (!provider) {
|
|
38
71
|
return /*#__PURE__*/ _jsxs("div", {
|
|
39
72
|
className: "field-type text",
|
|
@@ -53,6 +86,25 @@ export const DynamicVoiceSelect = (props)=>{
|
|
|
53
86
|
]
|
|
54
87
|
});
|
|
55
88
|
}
|
|
89
|
+
if (isLoading) {
|
|
90
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
91
|
+
className: "field-type text",
|
|
92
|
+
children: [
|
|
93
|
+
/*#__PURE__*/ _jsx("label", {
|
|
94
|
+
className: "field-label",
|
|
95
|
+
htmlFor: path,
|
|
96
|
+
children: "Voice"
|
|
97
|
+
}),
|
|
98
|
+
/*#__PURE__*/ _jsx("p", {
|
|
99
|
+
style: {
|
|
100
|
+
color: 'var(--theme-elevation-600)',
|
|
101
|
+
fontSize: '13px'
|
|
102
|
+
},
|
|
103
|
+
children: "Loading voices..."
|
|
104
|
+
})
|
|
105
|
+
]
|
|
106
|
+
});
|
|
107
|
+
}
|
|
56
108
|
if (voices.length === 0) {
|
|
57
109
|
return /*#__PURE__*/ _jsxs("div", {
|
|
58
110
|
className: "field-type text",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/DynamicVoiceSelect/index.tsx"],"sourcesContent":["'use client'\n\nimport { SelectInput, useField, useFormFields } from '@payloadcms/ui'\nimport React, { useEffect, useMemo, useState } from 'react'\n\ntype Props = {\n name: string\n path: string\n}\n\nexport const DynamicVoiceSelect: React.FC<Props> = (props) => {\n const { name, path } = props\n\n // Get provider from siblings\n const parentPath = path.split('.').slice(0, -1).join('.')\n //
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/DynamicVoiceSelect/index.tsx"],"sourcesContent":["'use client'\n\nimport { SelectInput, useField, useFormFields } from '@payloadcms/ui'\nimport React, { useEffect, useMemo, useState } from 'react'\n\ntype Props = {\n name: string\n path: string\n}\n\ninterface Voice {\n category?: string\n enabled?: boolean\n id: string\n labels?: Record<string, unknown>\n name: string\n preview_url?: string\n}\n\ninterface ProviderBlock {\n blockType: string\n enabled?: boolean\n voices?: Voice[]\n}\n\nexport const DynamicVoiceSelect: React.FC<Props> = (props) => {\n const { name, path } = props\n\n // Get provider from siblings\n const parentPath = path.split('.').slice(0, -1).join('.')\n const providerPath = `${parentPath}.provider`\n\n // Use useFormFields to get the provider field value - this will re-render when provider changes\n const providerField = useFormFields(([fields]) => fields[providerPath])\n const provider = (providerField?.value as string) || ''\n\n const { setValue, value } = useField<string>({ path })\n const [aiSettings, setAiSettings] = useState<{ providers?: ProviderBlock[] } | null>(null)\n const [isLoading, setIsLoading] = useState(true)\n\n // Fetch AI Settings - re-fetch when provider changes to ensure we have latest voices\n useEffect(() => {\n const fetchSettings = async () => {\n setIsLoading(true)\n try {\n const response = await fetch('/api/globals/ai-settings?depth=1')\n if (response.ok) {\n const data = await response.json()\n setAiSettings(data)\n }\n } catch (err) {\n console.error('Error fetching AI settings:', err)\n } finally {\n setIsLoading(false)\n }\n }\n\n void fetchSettings()\n }, [provider]) // Re-fetch when provider changes to ensure we have the latest voices\n\n const voices = useMemo(() => {\n if (!provider || !aiSettings?.providers) {\n return []\n }\n\n // Find the provider block matching the selected provider\n const providerBlock = aiSettings.providers.find(\n (p: ProviderBlock) => p.blockType === provider && p.enabled !== false,\n )\n\n if (!providerBlock?.voices) {\n return []\n }\n\n // Get enabled voices from provider block\n return providerBlock.voices\n .filter((v: Voice) => v.enabled !== false)\n .map((v: Voice) => ({\n label: v.name || v.id,\n value: v.id,\n }))\n }, [provider, aiSettings])\n\n // Clear voice selection when provider changes and current voice is not available\n useEffect(() => {\n if (value && voices.length > 0) {\n const voiceExists = voices.some((v) => v.value === value)\n if (!voiceExists) {\n setValue('')\n }\n }\n }, [voices, value, setValue])\n\n if (!provider) {\n return (\n <div className=\"field-type text\">\n <label className=\"field-label\" htmlFor={path}>\n Voice\n </label>\n <p style={{ color: 'var(--theme-elevation-600)', fontSize: '13px' }}>\n Please select a provider first.\n </p>\n </div>\n )\n }\n\n if (isLoading) {\n return (\n <div className=\"field-type text\">\n <label className=\"field-label\" htmlFor={path}>\n Voice\n </label>\n <p style={{ color: 'var(--theme-elevation-600)', fontSize: '13px' }}>Loading voices...</p>\n </div>\n )\n }\n\n if (voices.length === 0) {\n return (\n <div className=\"field-type text\">\n <label className=\"field-label\" htmlFor={path}>\n Voice\n </label>\n <p style={{ color: 'var(--theme-elevation-600)', fontSize: '13px' }}>\n No voices available. Please configure voices in AI Settings for {provider}.\n </p>\n </div>\n )\n }\n\n return (\n <div className=\"field-type select\">\n <label className=\"field-label\" htmlFor={path}>\n Voice\n </label>\n <SelectInput\n name={name}\n onChange={(option) => {\n if (option && typeof option === 'object' && 'value' in option) {\n setValue(option.value as string)\n } else {\n setValue(option)\n }\n }}\n options={voices}\n path={path}\n value={value}\n />\n </div>\n )\n}\n\n"],"names":["SelectInput","useField","useFormFields","React","useEffect","useMemo","useState","DynamicVoiceSelect","props","name","path","parentPath","split","slice","join","providerPath","providerField","fields","provider","value","setValue","aiSettings","setAiSettings","isLoading","setIsLoading","fetchSettings","response","fetch","ok","data","json","err","console","error","voices","providers","providerBlock","find","p","blockType","enabled","filter","v","map","label","id","length","voiceExists","some","div","className","htmlFor","style","color","fontSize","onChange","option","options"],"mappings":"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"}
|
|
@@ -5,34 +5,59 @@ export const DynamicVoiceSelect = (props) => {
|
|
|
5
5
|
const { name, path } = props;
|
|
6
6
|
// Get provider from siblings
|
|
7
7
|
const parentPath = path.split('.').slice(0, -1).join('.');
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
8
|
+
const providerPath = `${parentPath}.provider`;
|
|
9
|
+
// Use useFormFields to get the provider field value - this will re-render when provider changes
|
|
10
|
+
const providerField = useFormFields(([fields]) => fields[providerPath]);
|
|
11
|
+
const provider = providerField?.value || '';
|
|
11
12
|
const { setValue, value } = useField({ path });
|
|
12
13
|
const [aiSettings, setAiSettings] = useState(null);
|
|
14
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
15
|
+
// Fetch AI Settings - re-fetch when provider changes to ensure we have latest voices
|
|
13
16
|
useEffect(() => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
const fetchSettings = async () => {
|
|
18
|
+
setIsLoading(true);
|
|
19
|
+
try {
|
|
20
|
+
const response = await fetch('/api/globals/ai-settings?depth=1');
|
|
21
|
+
if (response.ok) {
|
|
22
|
+
const data = await response.json();
|
|
23
|
+
setAiSettings(data);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
console.error('Error fetching AI settings:', err);
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
setIsLoading(false);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
void fetchSettings();
|
|
34
|
+
}, [provider]); // Re-fetch when provider changes to ensure we have the latest voices
|
|
19
35
|
const voices = useMemo(() => {
|
|
20
|
-
if (!provider || !aiSettings) {
|
|
36
|
+
if (!provider || !aiSettings?.providers) {
|
|
21
37
|
return [];
|
|
22
38
|
}
|
|
23
|
-
|
|
24
|
-
|
|
39
|
+
// Find the provider block matching the selected provider
|
|
40
|
+
const providerBlock = aiSettings.providers.find((p) => p.blockType === provider && p.enabled !== false);
|
|
41
|
+
if (!providerBlock?.voices) {
|
|
25
42
|
return [];
|
|
26
43
|
}
|
|
27
|
-
// Get voices from provider block
|
|
28
|
-
|
|
29
|
-
return voicesArray
|
|
44
|
+
// Get enabled voices from provider block
|
|
45
|
+
return providerBlock.voices
|
|
30
46
|
.filter((v) => v.enabled !== false)
|
|
31
47
|
.map((v) => ({
|
|
32
48
|
label: v.name || v.id,
|
|
33
49
|
value: v.id,
|
|
34
50
|
}));
|
|
35
51
|
}, [provider, aiSettings]);
|
|
52
|
+
// Clear voice selection when provider changes and current voice is not available
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (value && voices.length > 0) {
|
|
55
|
+
const voiceExists = voices.some((v) => v.value === value);
|
|
56
|
+
if (!voiceExists) {
|
|
57
|
+
setValue('');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}, [voices, value, setValue]);
|
|
36
61
|
if (!provider) {
|
|
37
62
|
return (<div className="field-type text">
|
|
38
63
|
<label className="field-label" htmlFor={path}>
|
|
@@ -43,6 +68,14 @@ export const DynamicVoiceSelect = (props) => {
|
|
|
43
68
|
</p>
|
|
44
69
|
</div>);
|
|
45
70
|
}
|
|
71
|
+
if (isLoading) {
|
|
72
|
+
return (<div className="field-type text">
|
|
73
|
+
<label className="field-label" htmlFor={path}>
|
|
74
|
+
Voice
|
|
75
|
+
</label>
|
|
76
|
+
<p style={{ color: 'var(--theme-elevation-600)', fontSize: '13px' }}>Loading voices...</p>
|
|
77
|
+
</div>);
|
|
78
|
+
}
|
|
46
79
|
if (voices.length === 0) {
|
|
47
80
|
return (<div className="field-type text">
|
|
48
81
|
<label className="field-label" htmlFor={path}>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useField } from '@payloadcms/ui';
|
|
3
|
+
import { Button, useField } from '@payloadcms/ui';
|
|
4
4
|
import React, { useState } from 'react';
|
|
5
5
|
export const EncryptedTextField = ({ label, path, required })=>{
|
|
6
6
|
const { setValue, value } = useField({
|
|
@@ -48,13 +48,13 @@ export const EncryptedTextField = ({ label, path, required })=>{
|
|
|
48
48
|
})
|
|
49
49
|
]
|
|
50
50
|
}),
|
|
51
|
-
/*#__PURE__*/ _jsx(
|
|
52
|
-
|
|
51
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
52
|
+
buttonStyle: "secondary",
|
|
53
53
|
onClick: ()=>{
|
|
54
54
|
setValue('');
|
|
55
55
|
setIsEditing(true);
|
|
56
56
|
},
|
|
57
|
-
|
|
57
|
+
size: "medium",
|
|
58
58
|
children: "Change"
|
|
59
59
|
})
|
|
60
60
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/EncryptedTextField/index.tsx"],"sourcesContent":["'use client'\n\nimport { useField } from '@payloadcms/ui'\nimport React, { useState } from 'react'\n\ntype Props = {\n label?: string\n path: string\n required?: boolean\n}\n\nexport const EncryptedTextField: React.FC<Props> = ({ label, path, required }) => {\n const { setValue, value } = useField<string>({ path })\n const [isEditing, setIsEditing] = useState(!value)\n\n const isMasked = typeof value === 'string' && value.startsWith('sk-') && value.includes('****')\n\n return (\n <div className=\"field-type text\">\n <label className=\"field-label\">\n {label || 'API Key'}\n {required && <span className=\"required\">*</span>}\n </label>\n\n {!isEditing && isMasked ? (\n <div style={{ alignItems: 'center', display: 'flex', gap: '10px' }}>\n <div\n style={{\n background: 'var(--theme-elevation-100)',\n borderRadius: '4px',\n flexGrow: 1,\n fontFamily: 'monospace',\n padding: '8px 12px',\n }}\n >\n {value}\n <span\n style={{ color: 'var(--theme-success-500)', fontSize: '0.8em', marginLeft: '10px' }}\n >\n ✓ Configured\n </span>\n </div>\n <
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/EncryptedTextField/index.tsx"],"sourcesContent":["'use client'\n\nimport { Button, useField } from '@payloadcms/ui'\nimport React, { useState } from 'react'\n\ntype Props = {\n label?: string\n path: string\n required?: boolean\n}\n\nexport const EncryptedTextField: React.FC<Props> = ({ label, path, required }) => {\n const { setValue, value } = useField<string>({ path })\n const [isEditing, setIsEditing] = useState(!value)\n\n const isMasked = typeof value === 'string' && value.startsWith('sk-') && value.includes('****')\n\n return (\n <div className=\"field-type text\">\n <label className=\"field-label\">\n {label || 'API Key'}\n {required && <span className=\"required\">*</span>}\n </label>\n\n {!isEditing && isMasked ? (\n <div style={{ alignItems: 'center', display: 'flex', gap: '10px' }}>\n <div\n style={{\n background: 'var(--theme-elevation-100)',\n borderRadius: '4px',\n flexGrow: 1,\n fontFamily: 'monospace',\n padding: '8px 12px',\n }}\n >\n {value}\n <span\n style={{ color: 'var(--theme-success-500)', fontSize: '0.8em', marginLeft: '10px' }}\n >\n ✓ Configured\n </span>\n </div>\n <Button\n buttonStyle=\"secondary\"\n onClick={() => {\n setValue('')\n setIsEditing(true)\n }}\n size=\"medium\"\n >\n Change\n </Button>\n </div>\n ) : (\n <input\n onChange={(e) => setValue(e.target.value)}\n placeholder=\"sk-...\"\n style={{ width: '100%' }}\n type=\"password\"\n value={value || ''}\n />\n )}\n </div>\n )\n}\n"],"names":["Button","useField","React","useState","EncryptedTextField","label","path","required","setValue","value","isEditing","setIsEditing","isMasked","startsWith","includes","div","className","span","style","alignItems","display","gap","background","borderRadius","flexGrow","fontFamily","padding","color","fontSize","marginLeft","buttonStyle","onClick","size","input","onChange","e","target","placeholder","width","type"],"mappings":"AAAA;;AAEA,SAASA,MAAM,EAAEC,QAAQ,QAAQ,iBAAgB;AACjD,OAAOC,SAASC,QAAQ,QAAQ,QAAO;AAQvC,OAAO,MAAMC,qBAAsC,CAAC,EAAEC,KAAK,EAAEC,IAAI,EAAEC,QAAQ,EAAE;IAC3E,MAAM,EAAEC,QAAQ,EAAEC,KAAK,EAAE,GAAGR,SAAiB;QAAEK;IAAK;IACpD,MAAM,CAACI,WAAWC,aAAa,GAAGR,SAAS,CAACM;IAE5C,MAAMG,WAAW,OAAOH,UAAU,YAAYA,MAAMI,UAAU,CAAC,UAAUJ,MAAMK,QAAQ,CAAC;IAExF,qBACE,MAACC;QAAIC,WAAU;;0BACb,MAACX;gBAAMW,WAAU;;oBACdX,SAAS;oBACTE,0BAAY,KAACU;wBAAKD,WAAU;kCAAW;;;;YAGzC,CAACN,aAAaE,yBACb,MAACG;gBAAIG,OAAO;oBAAEC,YAAY;oBAAUC,SAAS;oBAAQC,KAAK;gBAAO;;kCAC/D,MAACN;wBACCG,OAAO;4BACLI,YAAY;4BACZC,cAAc;4BACdC,UAAU;4BACVC,YAAY;4BACZC,SAAS;wBACX;;4BAECjB;0CACD,KAACQ;gCACCC,OAAO;oCAAES,OAAO;oCAA4BC,UAAU;oCAASC,YAAY;gCAAO;0CACnF;;;;kCAIH,KAAC7B;wBACC8B,aAAY;wBACZC,SAAS;4BACPvB,SAAS;4BACTG,aAAa;wBACf;wBACAqB,MAAK;kCACN;;;+BAKH,KAACC;gBACCC,UAAU,CAACC,IAAM3B,SAAS2B,EAAEC,MAAM,CAAC3B,KAAK;gBACxC4B,aAAY;gBACZnB,OAAO;oBAAEoB,OAAO;gBAAO;gBACvBC,MAAK;gBACL9B,OAAOA,SAAS;;;;AAK1B,EAAC"}
|