@ai-stack/payloadcms 3.2.24-beta → 3.68.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.
Files changed (284) hide show
  1. package/{LICENSE.md → LICENSE} +1 -1
  2. package/README.md +218 -229
  3. package/dist/access/checkAccess.d.ts +4 -0
  4. package/dist/access/checkAccess.js +20 -0
  5. package/dist/access/checkAccess.js.map +1 -0
  6. package/dist/ai/core/generateObject.d.ts +7 -0
  7. package/dist/ai/core/generateObject.js +35 -0
  8. package/dist/ai/core/generateObject.js.map +1 -0
  9. package/dist/ai/core/generateText.d.ts +7 -0
  10. package/dist/ai/core/generateText.js +31 -0
  11. package/dist/ai/core/generateText.js.map +1 -0
  12. package/dist/ai/core/index.d.ts +11 -0
  13. package/dist/ai/core/index.js +10 -0
  14. package/dist/ai/core/index.js.map +1 -0
  15. package/dist/ai/core/media/generateMedia.d.ts +7 -0
  16. package/dist/ai/core/media/generateMedia.js +50 -0
  17. package/dist/ai/core/media/generateMedia.js.map +1 -0
  18. package/dist/ai/core/media/image/generateImage.d.ts +6 -0
  19. package/dist/ai/core/media/image/generateImage.js +41 -0
  20. package/dist/ai/core/media/image/generateImage.js.map +1 -0
  21. package/dist/ai/core/media/image/handlers/multimodal.d.ts +7 -0
  22. package/dist/ai/core/media/image/handlers/multimodal.js +95 -0
  23. package/dist/ai/core/media/image/handlers/multimodal.js.map +1 -0
  24. package/dist/ai/core/media/image/handlers/standard.d.ts +7 -0
  25. package/dist/ai/core/media/image/handlers/standard.js +28 -0
  26. package/dist/ai/core/media/image/handlers/standard.js.map +1 -0
  27. package/dist/ai/core/media/image/index.d.ts +2 -0
  28. package/dist/ai/core/media/image/index.js +3 -0
  29. package/dist/ai/core/media/image/index.js.map +1 -0
  30. package/dist/ai/core/media/index.d.ts +2 -0
  31. package/dist/ai/core/media/index.js +3 -0
  32. package/dist/ai/core/media/index.js.map +1 -0
  33. package/dist/ai/core/media/speech/generateSpeech.d.ts +5 -0
  34. package/dist/ai/core/media/speech/generateSpeech.js +55 -0
  35. package/dist/ai/core/media/speech/generateSpeech.js.map +1 -0
  36. package/dist/ai/core/media/speech/index.d.ts +2 -0
  37. package/dist/ai/core/media/speech/index.js +3 -0
  38. package/dist/ai/core/media/speech/index.js.map +1 -0
  39. package/dist/ai/core/media/types.d.ts +74 -0
  40. package/dist/ai/core/media/types.js +5 -0
  41. package/dist/ai/core/media/types.js.map +1 -0
  42. package/dist/ai/core/media/utils.d.ts +11 -0
  43. package/dist/ai/core/media/utils.js +34 -0
  44. package/dist/ai/core/media/utils.js.map +1 -0
  45. package/dist/ai/core/media/video/generateVideo.d.ts +6 -0
  46. package/dist/ai/core/media/video/generateVideo.js +32 -0
  47. package/dist/ai/core/media/video/generateVideo.js.map +1 -0
  48. package/dist/ai/core/media/video/index.d.ts +2 -0
  49. package/dist/ai/core/media/video/index.js +3 -0
  50. package/dist/ai/core/media/video/index.js.map +1 -0
  51. package/dist/ai/core/streamObject.d.ts +7 -0
  52. package/dist/ai/core/streamObject.js +57 -0
  53. package/dist/ai/core/streamObject.js.map +1 -0
  54. package/dist/ai/core/streamText.d.ts +7 -0
  55. package/dist/ai/core/streamText.js +30 -0
  56. package/dist/ai/core/streamText.js.map +1 -0
  57. package/dist/ai/core/types.d.ts +85 -0
  58. package/dist/ai/core/types.js +5 -0
  59. package/dist/ai/core/types.js.map +1 -0
  60. package/dist/ai/index.d.ts +11 -0
  61. package/dist/ai/index.js +25 -0
  62. package/dist/ai/index.js.map +1 -0
  63. package/dist/ai/providers/blocks/anthropic.d.ts +2 -0
  64. package/dist/ai/providers/blocks/anthropic.js +222 -0
  65. package/dist/ai/providers/blocks/anthropic.js.map +1 -0
  66. package/dist/ai/providers/blocks/elevenlabs.d.ts +2 -0
  67. package/dist/ai/providers/blocks/elevenlabs.js +448 -0
  68. package/dist/ai/providers/blocks/elevenlabs.js.map +1 -0
  69. package/dist/ai/providers/blocks/fal.d.ts +2 -0
  70. package/dist/ai/providers/blocks/fal.js +311 -0
  71. package/dist/ai/providers/blocks/fal.js.map +1 -0
  72. package/dist/ai/providers/blocks/google.d.ts +2 -0
  73. package/dist/ai/providers/blocks/google.js +622 -0
  74. package/dist/ai/providers/blocks/google.js.map +1 -0
  75. package/dist/ai/providers/blocks/index.d.ts +2 -0
  76. package/dist/ai/providers/blocks/index.js +18 -0
  77. package/dist/ai/providers/blocks/index.js.map +1 -0
  78. package/dist/ai/providers/blocks/openai-compatible.d.ts +2 -0
  79. package/dist/ai/providers/blocks/openai-compatible.js +307 -0
  80. package/dist/ai/providers/blocks/openai-compatible.js.map +1 -0
  81. package/dist/ai/providers/blocks/openai.d.ts +2 -0
  82. package/dist/ai/providers/blocks/openai.js +599 -0
  83. package/dist/ai/providers/blocks/openai.js.map +1 -0
  84. package/dist/ai/providers/blocks/xai.d.ts +2 -0
  85. package/dist/ai/providers/blocks/xai.js +246 -0
  86. package/dist/ai/providers/blocks/xai.js.map +1 -0
  87. package/dist/ai/providers/index.d.ts +2 -0
  88. package/dist/ai/providers/index.js +6 -0
  89. package/dist/ai/providers/index.js.map +1 -0
  90. package/dist/ai/providers/registry.d.ts +40 -0
  91. package/dist/ai/providers/registry.js +256 -0
  92. package/dist/ai/providers/registry.js.map +1 -0
  93. package/dist/ai/providers/types.d.ts +115 -0
  94. package/dist/ai/providers/types.js +4 -0
  95. package/dist/ai/providers/types.js.map +1 -0
  96. package/dist/ai/utils/systemGenerate.d.ts +1 -1
  97. package/dist/ai/utils/systemGenerate.js +19 -19
  98. package/dist/ai/utils/systemGenerate.js.map +1 -1
  99. package/dist/collections/AIJobs.d.ts +2 -0
  100. package/dist/collections/AIJobs.js +81 -0
  101. package/dist/collections/AIJobs.js.map +1 -0
  102. package/dist/collections/AISettings.d.ts +2 -0
  103. package/dist/collections/AISettings.js +279 -0
  104. package/dist/collections/AISettings.js.map +1 -0
  105. package/dist/collections/Instructions.js +185 -37
  106. package/dist/collections/Instructions.js.map +1 -1
  107. package/dist/defaults.d.ts +3 -0
  108. package/dist/defaults.js +3 -0
  109. package/dist/defaults.js.map +1 -1
  110. package/dist/endpoints/buildPromptUtils.d.ts +19 -0
  111. package/dist/endpoints/buildPromptUtils.js +114 -0
  112. package/dist/endpoints/buildPromptUtils.js.map +1 -0
  113. package/dist/endpoints/chat.d.js +3 -0
  114. package/dist/endpoints/chat.d.js.map +1 -0
  115. package/dist/endpoints/fetchVoices.d.ts +2 -0
  116. package/dist/endpoints/fetchVoices.js +79 -0
  117. package/dist/endpoints/fetchVoices.js.map +1 -0
  118. package/dist/endpoints/index.js +253 -214
  119. package/dist/endpoints/index.js.map +1 -1
  120. package/dist/exports/client.d.ts +9 -0
  121. package/dist/exports/client.js +9 -0
  122. package/dist/exports/client.js.map +1 -1
  123. package/dist/fields/ComposeField/ComposeField.js +2 -2
  124. package/dist/fields/ComposeField/ComposeField.js.map +1 -1
  125. package/dist/fields/ComposeField/ComposeField.jsx +2 -2
  126. package/dist/fields/PromptEditorField/PromptEditorField.js +155 -14
  127. package/dist/fields/PromptEditorField/PromptEditorField.js.map +1 -1
  128. package/dist/fields/PromptEditorField/PromptEditorField.jsx +118 -3
  129. package/dist/index.d.ts +1 -0
  130. package/dist/index.js.map +1 -1
  131. package/dist/init.js +35 -13
  132. package/dist/init.js.map +1 -1
  133. package/dist/payload-ai.d.js +3 -0
  134. package/dist/payload-ai.d.js.map +1 -0
  135. package/dist/plugin.js +80 -9
  136. package/dist/plugin.js.map +1 -1
  137. package/dist/providers/InstructionsProvider/InstructionsProvider.js +35 -7
  138. package/dist/providers/InstructionsProvider/InstructionsProvider.js.map +1 -1
  139. package/dist/providers/InstructionsProvider/InstructionsProvider.jsx +27 -4
  140. package/dist/providers/InstructionsProvider/context.d.ts +1 -0
  141. package/dist/providers/InstructionsProvider/context.js +1 -0
  142. package/dist/providers/InstructionsProvider/context.js.map +1 -1
  143. package/dist/providers/InstructionsProvider/useInstructions.js +13 -6
  144. package/dist/providers/InstructionsProvider/useInstructions.js.map +1 -1
  145. package/dist/types.d.ts +7 -7
  146. package/dist/types.js.map +1 -1
  147. package/dist/ui/AIConfigDashboard/index.d.ts +2 -0
  148. package/dist/ui/AIConfigDashboard/index.js +46 -0
  149. package/dist/ui/AIConfigDashboard/index.js.map +1 -0
  150. package/dist/ui/AIConfigDashboard/index.jsx +24 -0
  151. package/dist/ui/ApiKeyStatusIndicator/index.d.ts +6 -0
  152. package/dist/ui/ApiKeyStatusIndicator/index.js +39 -0
  153. package/dist/ui/ApiKeyStatusIndicator/index.js.map +1 -0
  154. package/dist/ui/ApiKeyStatusIndicator/index.jsx +29 -0
  155. package/dist/ui/Compose/Compose.d.ts +1 -2
  156. package/dist/ui/Compose/Compose.js +116 -90
  157. package/dist/ui/Compose/Compose.js.map +1 -1
  158. package/dist/ui/Compose/Compose.jsx +111 -101
  159. package/dist/ui/Compose/ComposePlaceholder.d.ts +7 -0
  160. package/dist/ui/Compose/ComposePlaceholder.js +78 -0
  161. package/dist/ui/Compose/ComposePlaceholder.js.map +1 -0
  162. package/dist/ui/Compose/ComposePlaceholder.jsx +66 -0
  163. package/dist/ui/Compose/UndoRedoActions.js +3 -1
  164. package/dist/ui/Compose/UndoRedoActions.js.map +1 -1
  165. package/dist/ui/Compose/UndoRedoActions.jsx +2 -1
  166. package/dist/ui/Compose/compose.module.css +1 -1
  167. package/dist/ui/Compose/hooks/menu/itemsMap.js +1 -1
  168. package/dist/ui/Compose/hooks/menu/itemsMap.js.map +1 -1
  169. package/dist/ui/Compose/hooks/menu/useMenu.d.ts +2 -1
  170. package/dist/ui/Compose/hooks/menu/useMenu.js +2 -2
  171. package/dist/ui/Compose/hooks/menu/useMenu.js.map +1 -1
  172. package/dist/ui/Compose/hooks/menu/useMenu.jsx +2 -2
  173. package/dist/ui/Compose/hooks/useActiveFieldTracking.js +69 -10
  174. package/dist/ui/Compose/hooks/useActiveFieldTracking.js.map +1 -1
  175. package/dist/ui/Compose/hooks/useGenerate.d.ts +3 -0
  176. package/dist/ui/Compose/hooks/useGenerate.js +71 -11
  177. package/dist/ui/Compose/hooks/useGenerate.js.map +1 -1
  178. package/dist/ui/Compose/hooks/useHistory.js +52 -5
  179. package/dist/ui/Compose/hooks/useHistory.js.map +1 -1
  180. package/dist/ui/DynamicModelSelect/index.d.ts +7 -0
  181. package/dist/ui/DynamicModelSelect/index.js +231 -0
  182. package/dist/ui/DynamicModelSelect/index.js.map +1 -0
  183. package/dist/ui/DynamicModelSelect/index.jsx +207 -0
  184. package/dist/ui/DynamicProviderSelect/index.d.ts +7 -0
  185. package/dist/ui/DynamicProviderSelect/index.js +101 -0
  186. package/dist/ui/DynamicProviderSelect/index.js.map +1 -0
  187. package/dist/ui/DynamicProviderSelect/index.jsx +90 -0
  188. package/dist/ui/DynamicVoiceSelect/index.d.ts +7 -0
  189. package/dist/ui/DynamicVoiceSelect/index.js +104 -0
  190. package/dist/ui/DynamicVoiceSelect/index.js.map +1 -0
  191. package/dist/ui/DynamicVoiceSelect/index.jsx +69 -0
  192. package/dist/ui/EncryptedTextField/index.d.ts +8 -0
  193. package/dist/ui/EncryptedTextField/index.js +74 -0
  194. package/dist/ui/EncryptedTextField/index.js.map +1 -0
  195. package/dist/ui/EncryptedTextField/index.jsx +35 -0
  196. package/dist/ui/Icons/LottieAnimation.js +3 -1
  197. package/dist/ui/Icons/LottieAnimation.js.map +1 -1
  198. package/dist/ui/Icons/LottieAnimation.jsx +2 -1
  199. package/dist/ui/ModelRowLabel/index.d.ts +6 -0
  200. package/dist/ui/ModelRowLabel/index.js +41 -0
  201. package/dist/ui/ModelRowLabel/index.js.map +1 -0
  202. package/dist/ui/ModelRowLabel/index.jsx +26 -0
  203. package/dist/ui/ProviderOptionsEditor/index.d.ts +7 -0
  204. package/dist/ui/ProviderOptionsEditor/index.js +291 -0
  205. package/dist/ui/ProviderOptionsEditor/index.js.map +1 -0
  206. package/dist/ui/ProviderOptionsEditor/index.jsx +210 -0
  207. package/dist/ui/VoicesFetcher/index.d.ts +7 -0
  208. package/dist/ui/VoicesFetcher/index.js +72 -0
  209. package/dist/ui/VoicesFetcher/index.js.map +1 -0
  210. package/dist/ui/VoicesFetcher/index.jsx +56 -0
  211. package/dist/utilities/encryption.d.ts +2 -0
  212. package/dist/utilities/encryption.js +47 -0
  213. package/dist/utilities/encryption.js.map +1 -0
  214. package/dist/utilities/extractImageData.d.ts +9 -0
  215. package/dist/utilities/extractImageData.js +12 -2
  216. package/dist/utilities/extractImageData.js.map +1 -1
  217. package/dist/utilities/fetchImages.d.ts +14 -0
  218. package/dist/utilities/fetchImages.js +38 -0
  219. package/dist/utilities/fetchImages.js.map +1 -0
  220. package/dist/utilities/fieldToJsonSchema.d.ts +2 -1
  221. package/dist/utilities/fieldToJsonSchema.js +66 -3
  222. package/dist/utilities/fieldToJsonSchema.js.map +1 -1
  223. package/dist/utilities/getFieldBySchemaPath.js +15 -0
  224. package/dist/utilities/getFieldBySchemaPath.js.map +1 -1
  225. package/dist/utilities/getProviderOptionsFields.d.ts +16 -0
  226. package/dist/utilities/getProviderOptionsFields.js +80 -0
  227. package/dist/utilities/getProviderOptionsFields.js.map +1 -0
  228. package/dist/utilities/isPluginActivated.js +1 -2
  229. package/dist/utilities/isPluginActivated.js.map +1 -1
  230. package/dist/utilities/lexicalToHTML.js.map +1 -1
  231. package/dist/utilities/resolveImageReferences.d.ts +28 -0
  232. package/dist/utilities/resolveImageReferences.js +148 -0
  233. package/dist/utilities/resolveImageReferences.js.map +1 -0
  234. package/dist/utilities/schemaConverter.d.ts +3 -0
  235. package/dist/utilities/schemaConverter.js +93 -0
  236. package/dist/utilities/schemaConverter.js.map +1 -0
  237. package/dist/utilities/setSafeLexicalState.d.ts +1 -3
  238. package/dist/utilities/setSafeLexicalState.js +1 -1
  239. package/dist/utilities/setSafeLexicalState.js.map +1 -1
  240. package/package.json +19 -21
  241. package/dist/ai/models/anthropic/index.d.ts +0 -2
  242. package/dist/ai/models/anthropic/index.js +0 -129
  243. package/dist/ai/models/anthropic/index.js.map +0 -1
  244. package/dist/ai/models/elevenLabs/generateVoice.d.ts +0 -8
  245. package/dist/ai/models/elevenLabs/generateVoice.js +0 -20
  246. package/dist/ai/models/elevenLabs/generateVoice.js.map +0 -1
  247. package/dist/ai/models/elevenLabs/index.d.ts +0 -2
  248. package/dist/ai/models/elevenLabs/index.js +0 -133
  249. package/dist/ai/models/elevenLabs/index.js.map +0 -1
  250. package/dist/ai/models/elevenLabs/voices.d.ts +0 -8
  251. package/dist/ai/models/elevenLabs/voices.js +0 -24
  252. package/dist/ai/models/elevenLabs/voices.js.map +0 -1
  253. package/dist/ai/models/generateObject.d.ts +0 -11
  254. package/dist/ai/models/generateObject.js +0 -22
  255. package/dist/ai/models/generateObject.js.map +0 -1
  256. package/dist/ai/models/google/generateImage.d.ts +0 -9
  257. package/dist/ai/models/google/generateImage.js +0 -27
  258. package/dist/ai/models/google/generateImage.js.map +0 -1
  259. package/dist/ai/models/google/index.d.ts +0 -2
  260. package/dist/ai/models/google/index.js +0 -201
  261. package/dist/ai/models/google/index.js.map +0 -1
  262. package/dist/ai/models/index.d.ts +0 -2
  263. package/dist/ai/models/index.js +0 -13
  264. package/dist/ai/models/index.js.map +0 -1
  265. package/dist/ai/models/openai/generateImage.d.ts +0 -5
  266. package/dist/ai/models/openai/generateImage.js +0 -31
  267. package/dist/ai/models/openai/generateImage.js.map +0 -1
  268. package/dist/ai/models/openai/generateVoice.d.ts +0 -6
  269. package/dist/ai/models/openai/generateVoice.js +0 -19
  270. package/dist/ai/models/openai/generateVoice.js.map +0 -1
  271. package/dist/ai/models/openai/index.d.ts +0 -2
  272. package/dist/ai/models/openai/index.js +0 -428
  273. package/dist/ai/models/openai/index.js.map +0 -1
  274. package/dist/ai/models/openai/openai.d.ts +0 -1
  275. package/dist/ai/models/openai/openai.js +0 -8
  276. package/dist/ai/models/openai/openai.js.map +0 -1
  277. package/dist/ai/utils/editImagesWithOpenAI.d.ts +0 -10
  278. package/dist/ai/utils/editImagesWithOpenAI.js +0 -37
  279. package/dist/ai/utils/editImagesWithOpenAI.js.map +0 -1
  280. package/dist/types.d.js +0 -3
  281. package/dist/types.d.js.map +0 -1
  282. package/dist/utilities/getGenerationModels.d.ts +0 -2
  283. package/dist/utilities/getGenerationModels.js +0 -10
  284. package/dist/utilities/getGenerationModels.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/ui/Compose/hooks/useGenerate.ts"],"sourcesContent":["import { experimental_useObject as useObject } from '@ai-sdk/react'\nimport { useEditorConfigContext } from '@payloadcms/richtext-lexical/client'\nimport { toast, useConfig, useDocumentInfo, useField, useForm, useLocale } from '@payloadcms/ui'\nimport { jsonSchema } from 'ai'\nimport { useCallback, useEffect, useMemo, useRef } from 'react'\n\nimport type { ActionMenuItems, GenerateTextarea } from '../../../types.js'\n\nimport {\n PLUGIN_API_ENDPOINT_GENERATE,\n PLUGIN_API_ENDPOINT_GENERATE_UPLOAD,\n PLUGIN_INSTRUCTIONS_TABLE,\n PLUGIN_NAME,\n} from '../../../defaults.js'\nimport { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js'\nimport { editorSchemaValidator } from '../../../utilities/editorSchemaValidator.js'\nimport { fieldToJsonSchema } from '../../../utilities/fieldToJsonSchema.js'\nimport { setSafeLexicalState } from '../../../utilities/setSafeLexicalState.js'\nimport { useHistory } from './useHistory.js'\n\ntype ActionCallbackParams = { action: ActionMenuItems; params?: unknown }\n\nexport const useGenerate = ({ instructionId }: { instructionId: string }) => {\n // Create a ref to hold the current instructionId\n const instructionIdRef = useRef(instructionId)\n\n // Update the ref whenever instructionId changes\n useEffect(() => {\n instructionIdRef.current = instructionId\n }, [instructionId])\n\n const { field, path: pathFromContext } = useFieldProps()\n const editorConfigContext = useEditorConfigContext()\n\n const { editor } = editorConfigContext\n\n const { config } = useConfig()\n const {\n routes: { api },\n serverURL,\n } = config\n\n const { setValue } = useField<any>({\n path: pathFromContext ?? '',\n })\n\n const { set: setHistory } = useHistory()\n\n const { getData } = useForm()\n const { id: documentId, collectionSlug } = useDocumentInfo()\n\n const localFromContext = useLocale()\n const {\n config: { collections },\n } = useConfig()\n\n const collection = collections.find((collection) => collection.slug === PLUGIN_INSTRUCTIONS_TABLE)\n const { custom: { [PLUGIN_NAME]: { editorConfig = {} } = {} } = {} } = collection?.admin ?? {}\n const { schema: editorSchema = {} } = editorConfig\n\n const memoizedValidator = useMemo(() => {\n return editorSchemaValidator(editorSchema)\n }, [editorSchema])\n\n const memoizedSchema = useMemo(\n () =>\n jsonSchema(editorSchema, {\n validate: (value) => {\n const isValid = memoizedValidator(value)\n\n if (isValid) {\n return {\n success: true,\n value,\n }\n } else {\n return {\n error: new Error('Invalid schema'),\n success: false,\n }\n }\n },\n }),\n [memoizedValidator],\n )\n\n // Active JSON schema for useObject based on field type\n const activeSchema = useMemo(() => {\n const f = field as any\n const fieldType = f?.type as string | undefined\n if (fieldType === 'richText') {\n return memoizedSchema\n }\n if (f && f.name && fieldType) {\n const schemaJson = fieldToJsonSchema(f)\n if (schemaJson && Object.keys(schemaJson).length > 0) {\n return jsonSchema(schemaJson)\n }\n }\n return undefined\n }, [field, memoizedSchema])\n\n const {\n isLoading: loadingObject,\n object,\n stop: objectStop,\n submit,\n } = useObject({\n api: `/api${PLUGIN_API_ENDPOINT_GENERATE}`,\n onError: (error: any) => {\n toast.error(`Failed to generate: ${error.message}`)\n console.error('Error generating object:', error)\n },\n onFinish: (result) => {\n if (result.object && field) {\n if (field.type === 'richText') {\n setHistory(result.object)\n setValue(result.object)\n } else if ('name' in field) {\n setHistory(result.object[field.name])\n setValue(result.object[field.name])\n }\n } else {\n console.log('onFinish: result, field ', result, field)\n }\n },\n schema: activeSchema as any,\n })\n\n useEffect(() => {\n if (!object) {\n return\n }\n\n requestAnimationFrame(() => {\n if (field?.type === 'richText') {\n setSafeLexicalState(object, editor)\n } else if (field && 'name' in field && object[field.name]) {\n setValue(object[field.name])\n }\n })\n }, [object, editor, field])\n\n const streamObject = useCallback(\n ({ action = 'Compose', params }: ActionCallbackParams) => {\n const doc = getData()\n\n const currentInstructionId = instructionIdRef.current\n\n const options = {\n action,\n actionParams: params,\n instructionId: currentInstructionId,\n }\n\n submit({\n allowedEditorNodes: Array.from(editor?._nodes?.keys() || []),\n doc: {\n ...doc,\n id: documentId,\n },\n locale: localFromContext?.code,\n options,\n })\n },\n [localFromContext?.code, instructionIdRef, documentId],\n )\n\n const generateUpload = useCallback(async () => {\n const doc = getData()\n const currentInstructionId = instructionIdRef.current\n\n return fetch(`${serverURL}${api}${PLUGIN_API_ENDPOINT_GENERATE_UPLOAD}`, {\n body: JSON.stringify({\n collectionSlug: collectionSlug ?? '',\n doc,\n documentId,\n locale: localFromContext?.code,\n options: {\n instructionId: currentInstructionId,\n },\n } satisfies Parameters<GenerateTextarea>[0]),\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n .then(async (uploadResponse) => {\n if (uploadResponse.ok) {\n const { result } = await uploadResponse.json()\n if (!result) {\n throw new Error('generateUpload: Something went wrong')\n }\n\n setValue(result?.id)\n setHistory(result?.id)\n console.log('Image updated...', result)\n } else {\n const { errors = [] } = await uploadResponse.json()\n const errStr = errors.map((error: any) => error.message).join(', ')\n throw new Error(errStr)\n }\n return uploadResponse\n })\n .catch((error) => {\n toast.error(`Failed to generate: ${error.message}`)\n console.error(\n 'Error generating or setting your upload, please set it manually if its saved in your media files.',\n error,\n )\n })\n }, [getData, localFromContext?.code, instructionIdRef, setValue, documentId, collectionSlug])\n\n const generate = useCallback(\n async (options?: ActionCallbackParams) => {\n if ((field as any)?.type === 'upload') {\n return generateUpload()\n }\n // All supported types use structured object generation when schema is provided server-side\n return streamObject(options ?? { action: 'Compose' })\n },\n [generateUpload, streamObject, field],\n )\n\n const stop = useCallback(() => {\n console.log('Stopping...')\n objectStop()\n }, [objectStop])\n\n return {\n generate,\n isLoading: loadingObject,\n stop,\n }\n}\n"],"names":["experimental_useObject","useObject","useEditorConfigContext","toast","useConfig","useDocumentInfo","useField","useForm","useLocale","jsonSchema","useCallback","useEffect","useMemo","useRef","PLUGIN_API_ENDPOINT_GENERATE","PLUGIN_API_ENDPOINT_GENERATE_UPLOAD","PLUGIN_INSTRUCTIONS_TABLE","PLUGIN_NAME","useFieldProps","editorSchemaValidator","fieldToJsonSchema","setSafeLexicalState","useHistory","useGenerate","instructionId","instructionIdRef","current","field","path","pathFromContext","editorConfigContext","editor","config","routes","api","serverURL","setValue","set","setHistory","getData","id","documentId","collectionSlug","localFromContext","collections","collection","find","slug","custom","editorConfig","admin","schema","editorSchema","memoizedValidator","memoizedSchema","validate","value","isValid","success","error","Error","activeSchema","f","fieldType","type","name","schemaJson","Object","keys","length","undefined","isLoading","loadingObject","object","stop","objectStop","submit","onError","message","console","onFinish","result","log","requestAnimationFrame","streamObject","action","params","doc","currentInstructionId","options","actionParams","allowedEditorNodes","Array","from","_nodes","locale","code","generateUpload","fetch","body","JSON","stringify","credentials","headers","method","then","uploadResponse","ok","json","errors","errStr","map","join","catch","generate"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/ui/Compose/hooks/useGenerate.ts"],"sourcesContent":["import { experimental_useObject as useObject } from '@ai-sdk/react'\nimport { useEditorConfigContext } from '@payloadcms/richtext-lexical/client'\nimport { toast, useConfig, useDocumentInfo, useField, useForm, useLocale } from '@payloadcms/ui'\nimport { jsonSchema } from 'ai'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\n\nimport type { ActionMenuItems, GenerateTextarea } from '../../../types.js'\n\nimport {\n PLUGIN_AI_JOBS_TABLE,\n PLUGIN_API_ENDPOINT_GENERATE,\n PLUGIN_API_ENDPOINT_GENERATE_UPLOAD,\n PLUGIN_INSTRUCTIONS_TABLE,\n PLUGIN_NAME,\n} from '../../../defaults.js'\nimport { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js'\nimport { editorSchemaValidator } from '../../../utilities/editorSchemaValidator.js'\nimport { fieldToJsonSchema } from '../../../utilities/fieldToJsonSchema.js'\nimport { setSafeLexicalState } from '../../../utilities/setSafeLexicalState.js'\nimport { useHistory } from './useHistory.js'\n\ntype ActionCallbackParams = { action: ActionMenuItems; params?: unknown }\n\nexport const useGenerate = ({ instructionId }: { instructionId: string }) => {\n // Create a ref to hold the current instructionId\n const instructionIdRef = useRef(instructionId)\n\n // Update the ref whenever instructionId changes\n useEffect(() => {\n instructionIdRef.current = instructionId\n }, [instructionId])\n\n const { field, path: pathFromContext } = useFieldProps()\n const editorConfigContext = useEditorConfigContext()\n\n const { editor } = editorConfigContext\n\n const { config } = useConfig()\n const {\n routes: { api },\n serverURL,\n } = config\n\n const { setValue } = useField<any>({\n path: pathFromContext ?? '',\n })\n\n const { set: setHistory } = useHistory()\n\n // 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 { getData } = useForm()\n const { id: documentId, collectionSlug } = useDocumentInfo()\n\n const localFromContext = useLocale()\n \n // Reuse config from above instead of calling useConfig again\n const { collections } = config\n\n const collection = collections.find((collection) => collection.slug === PLUGIN_INSTRUCTIONS_TABLE)\n const { custom: { [PLUGIN_NAME]: { editorConfig = {} } = {} } = {} } = collection?.admin ?? {}\n const { schema: editorSchema = {} } = editorConfig\n\n const memoizedValidator = useMemo(() => {\n return editorSchemaValidator(editorSchema)\n }, [editorSchema])\n\n const memoizedSchema = useMemo(\n () =>\n jsonSchema(editorSchema, {\n validate: (value) => {\n const isValid = memoizedValidator(value)\n\n if (isValid) {\n return {\n success: true,\n value,\n }\n } else {\n return {\n error: new Error('Invalid schema'),\n success: false,\n }\n }\n },\n }),\n [memoizedValidator],\n )\n\n // Active JSON schema for useObject based on field type\n const activeSchema = useMemo(() => {\n const f = field as any\n const fieldType = f?.type as string | undefined\n if (fieldType === 'richText') {\n return memoizedSchema\n }\n if (f && f.name && fieldType) {\n const schemaJson = fieldToJsonSchema(f)\n if (schemaJson && Object.keys(schemaJson).length > 0) {\n return jsonSchema(schemaJson)\n }\n }\n return undefined\n }, [field, memoizedSchema])\n\n const {\n isLoading: loadingObject,\n object,\n stop: objectStop,\n submit,\n } = useObject({\n api: `/api${PLUGIN_API_ENDPOINT_GENERATE}`,\n onError: (error: any) => {\n toast.error(`Failed to generate: ${error.message}`)\n console.error('Error generating object:', error)\n },\n onFinish: (result) => {\n if (result.object && field) {\n if (field.type === 'richText') {\n setHistory(result.object)\n setValue(result.object)\n } else if ('name' in field) {\n setHistory(result.object[field.name])\n setValue(result.object[field.name])\n }\n } else {\n console.log('onFinish: result, field ', result, field)\n }\n },\n schema: activeSchema as any,\n })\n\n useEffect(() => {\n if (!object) {\n return\n }\n\n requestAnimationFrame(() => {\n if (field?.type === 'richText') {\n setSafeLexicalState(object, editor)\n } else if (field && 'name' in field && object[field.name]) {\n setValue(object[field.name])\n }\n })\n }, [object, editor, field, setValue])\n\n const streamObject = useCallback(\n ({ action = 'Compose', params }: ActionCallbackParams) => {\n const doc = getData()\n\n const currentInstructionId = instructionIdRef.current\n\n const options = {\n action,\n actionParams: params,\n instructionId: currentInstructionId,\n }\n\n submit({\n allowedEditorNodes: Array.from(editor?._nodes?.keys() || []),\n doc: {\n ...doc,\n id: documentId,\n },\n locale: localFromContext?.code,\n options,\n })\n },\n [localFromContext?.code, instructionIdRef, documentId],\n )\n\n const generateUpload = useCallback(async () => {\n const doc = getData()\n const currentInstructionId = instructionIdRef.current\n\n return fetch(`${serverURL}${api}${PLUGIN_API_ENDPOINT_GENERATE_UPLOAD}`, {\n body: JSON.stringify({\n collectionSlug: collectionSlug ?? '',\n doc,\n documentId,\n locale: localFromContext?.code,\n options: {\n instructionId: currentInstructionId,\n },\n } satisfies Parameters<GenerateTextarea>[0]),\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n .then(async (uploadResponse) => {\n if (uploadResponse.ok) {\n const json = await uploadResponse.json()\n const { job, result } = json || {}\n if (result) {\n // Set the upload ID\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(\n `${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 // Force upload field to refetch by clearing then setting the ID\n setValue(null)\n setTimeout(() => {\n setValue(result_id)\n }, 0)\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 (e) {\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 }, [getData, localFromContext?.code, instructionIdRef, setValue, documentId, collectionSlug])\n\n const generate = useCallback(\n async (options?: ActionCallbackParams) => {\n if ((field as any)?.type === 'upload') {\n return generateUpload()\n }\n // All supported types use structured object generation when schema is provided server-side\n return streamObject(options ?? { action: 'Compose' })\n },\n [generateUpload, streamObject, field],\n )\n\n const stop = useCallback(() => {\n console.log('Stopping...')\n objectStop()\n }, [objectStop])\n\n return {\n generate,\n isJobActive,\n isLoading: loadingObject,\n jobProgress,\n jobStatus,\n stop,\n }\n}\n"],"names":["experimental_useObject","useObject","useEditorConfigContext","toast","useConfig","useDocumentInfo","useField","useForm","useLocale","jsonSchema","useCallback","useEffect","useMemo","useRef","useState","PLUGIN_AI_JOBS_TABLE","PLUGIN_API_ENDPOINT_GENERATE","PLUGIN_API_ENDPOINT_GENERATE_UPLOAD","PLUGIN_INSTRUCTIONS_TABLE","PLUGIN_NAME","useFieldProps","editorSchemaValidator","fieldToJsonSchema","setSafeLexicalState","useHistory","useGenerate","instructionId","instructionIdRef","current","field","path","pathFromContext","editorConfigContext","editor","config","routes","api","serverURL","setValue","set","setHistory","jobStatus","setJobStatus","undefined","jobProgress","setJobProgress","isJobActive","setIsJobActive","getData","id","documentId","collectionSlug","localFromContext","collections","collection","find","slug","custom","editorConfig","admin","schema","editorSchema","memoizedValidator","memoizedSchema","validate","value","isValid","success","error","Error","activeSchema","f","fieldType","type","name","schemaJson","Object","keys","length","isLoading","loadingObject","object","stop","objectStop","submit","onError","message","console","onFinish","result","log","requestAnimationFrame","streamObject","action","params","doc","currentInstructionId","options","actionParams","allowedEditorNodes","Array","from","_nodes","locale","code","generateUpload","fetch","body","JSON","stringify","credentials","headers","method","then","uploadResponse","ok","json","job","cancelled","attempts","maxAttempts","poll","res","jobDoc","progress","result_id","status","setTimeout","e","errors","errStr","map","join","catch","generate"],"mappings":"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"}
@@ -1,9 +1,11 @@
1
1
  'use client';
2
2
  import { useDocumentInfo, useField } from '@payloadcms/ui';
3
- import { useCallback, useEffect } from 'react';
3
+ import { useCallback, useEffect, useRef } from 'react';
4
4
  import { PLUGIN_NAME } from '../../../defaults.js';
5
5
  import { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js';
6
6
  const STORAGE_KEY = `${PLUGIN_NAME}-fields-history`;
7
+ // Global cache to prevent synchronous localStorage reads on every render
8
+ let globalHistoryCache = null;
7
9
  export const useHistory = ()=>{
8
10
  const { id } = useDocumentInfo();
9
11
  const { path: pathFromContext, schemaPath } = useFieldProps();
@@ -12,10 +14,16 @@ export const useHistory = ()=>{
12
14
  });
13
15
  const fieldKey = `${id}.${schemaPath}`;
14
16
  const getLatestHistory = useCallback(()=>{
17
+ // Return cache if available
18
+ if (globalHistoryCache) {
19
+ return globalHistoryCache;
20
+ }
15
21
  try {
16
- // This condition is applied, as it was somehow triggering on server side
17
22
  if (typeof localStorage !== 'undefined') {
18
- return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}');
23
+ // Read once, cache it
24
+ const stored = localStorage.getItem(STORAGE_KEY);
25
+ globalHistoryCache = stored ? JSON.parse(stored) : {};
26
+ return globalHistoryCache;
19
27
  }
20
28
  return {};
21
29
  } catch (e) {
@@ -23,10 +31,49 @@ export const useHistory = ()=>{
23
31
  return {};
24
32
  }
25
33
  }, []);
34
+ // Debounce timer ref to prevent excessive localStorage writes
35
+ const saveTimerRef = useRef(null);
26
36
  const saveToLocalStorage = useCallback((newGlobalHistory)=>{
27
- if (typeof localStorage !== 'undefined') {
28
- localStorage.setItem(STORAGE_KEY, JSON.stringify(newGlobalHistory));
37
+ // Update cache immediately
38
+ globalHistoryCache = newGlobalHistory;
39
+ if (typeof localStorage === 'undefined') {
40
+ return;
41
+ }
42
+ // Clear any pending save
43
+ if (saveTimerRef.current) {
44
+ clearTimeout(saveTimerRef.current);
29
45
  }
46
+ // Debounce the save operation by 300ms
47
+ saveTimerRef.current = setTimeout(()=>{
48
+ // Use requestIdleCallback if available to avoid blocking the main thread
49
+ if (typeof requestIdleCallback !== 'undefined') {
50
+ requestIdleCallback(()=>{
51
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(newGlobalHistory));
52
+ }, {
53
+ timeout: 2000
54
+ });
55
+ } else {
56
+ // Fallback for browsers without requestIdleCallback
57
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(newGlobalHistory));
58
+ }
59
+ saveTimerRef.current = null;
60
+ }, 300);
61
+ }, []);
62
+ // Sync with other tabs
63
+ useEffect(()=>{
64
+ const handleStorageChange = (e)=>{
65
+ if (e.key === STORAGE_KEY && e.newValue) {
66
+ try {
67
+ globalHistoryCache = JSON.parse(e.newValue);
68
+ } catch (err) {
69
+ // ignore parse error
70
+ }
71
+ }
72
+ };
73
+ window.addEventListener('storage', handleStorageChange);
74
+ return ()=>{
75
+ window.removeEventListener('storage', handleStorageChange);
76
+ };
30
77
  }, []);
31
78
  // Clear previous history
32
79
  const clearHistory = useCallback(()=>{
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/ui/Compose/hooks/useHistory.ts"],"sourcesContent":["'use client'\n\nimport { useDocumentInfo, useField } from '@payloadcms/ui'\nimport { useCallback, useEffect } from 'react'\n\nimport { PLUGIN_NAME } from '../../../defaults.js'\nimport { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js'\n\nconst STORAGE_KEY = `${PLUGIN_NAME}-fields-history`\n\ninterface HistoryState {\n [path: string]: {\n currentIndex: number\n history: any[]\n }\n}\n\nexport const useHistory = () => {\n const { id } = useDocumentInfo()\n const { path: pathFromContext, schemaPath } = useFieldProps()\n const { value: currentFieldValue } = useField<string>({\n path: pathFromContext ?? '',\n })\n\n const fieldKey = `${id}.${schemaPath}`\n\n const getLatestHistory = useCallback((): HistoryState => {\n try {\n // This condition is applied, as it was somehow triggering on server side\n if (typeof localStorage !== 'undefined') {\n return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}')\n }\n return {}\n } catch (e) {\n console.error('Error parsing history:', e)\n return {}\n }\n }, [])\n\n const saveToLocalStorage = useCallback((newGlobalHistory: HistoryState) => {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(newGlobalHistory))\n }\n }, [])\n\n // Clear previous history\n const clearHistory = useCallback(() => {\n const latestHistory = { ...getLatestHistory() }\n Object.keys(latestHistory).forEach((k) => {\n if (!k.startsWith(id?.toString() ?? '')) {\n delete latestHistory[k]\n }\n })\n saveToLocalStorage(latestHistory)\n }, [id, fieldKey, getLatestHistory, saveToLocalStorage])\n\n useEffect(() => {\n // This is applied to clear out the document history which is not currently in use\n clearHistory()\n\n const latestHistory = getLatestHistory()\n const { currentIndex, history } = latestHistory[fieldKey] || {\n currentIndex: -1,\n history: [],\n }\n\n let newIndex = currentIndex\n if (currentIndex == -1) {\n newIndex = 0\n if (currentFieldValue) {\n history[newIndex] = currentFieldValue\n }\n }\n\n const newGlobalHistory = {\n ...latestHistory,\n [fieldKey]: { currentIndex: newIndex, history },\n }\n\n saveToLocalStorage(newGlobalHistory)\n }, [fieldKey])\n\n const set = useCallback(\n (data: any) => {\n const latestHistory = getLatestHistory()\n const { currentIndex, history } = latestHistory[fieldKey] || {\n currentIndex: -1,\n history: [],\n }\n const newHistory = [...history.slice(0, currentIndex + 1), data]\n const newGlobalHistory = {\n ...latestHistory,\n [fieldKey]: { currentIndex: newHistory.length - 1, history: newHistory },\n }\n saveToLocalStorage(newGlobalHistory)\n return data\n },\n [fieldKey, getLatestHistory, saveToLocalStorage],\n )\n\n const undo = useCallback(() => {\n const latestHistory = getLatestHistory()\n const { currentIndex, history } = latestHistory[fieldKey] || { currentIndex: -1, history: [] }\n if (currentIndex > 0) {\n const newIndex = currentIndex - 1\n const newValue = history[newIndex]\n const newGlobalHistory = {\n ...latestHistory,\n [fieldKey]: { currentIndex: newIndex, history },\n }\n saveToLocalStorage(newGlobalHistory)\n return newValue\n }\n return undefined\n }, [fieldKey, getLatestHistory, saveToLocalStorage])\n\n const redo = useCallback(() => {\n const latestHistory = getLatestHistory()\n const { currentIndex, history } = latestHistory[fieldKey] || { currentIndex: -1, history: [] }\n if (currentIndex < history.length - 1) {\n const newIndex = currentIndex + 1\n const newValue = history[newIndex]\n const newGlobalHistory = {\n ...latestHistory,\n [fieldKey]: { currentIndex: newIndex, history },\n }\n saveToLocalStorage(newGlobalHistory)\n return newValue\n }\n return undefined\n }, [fieldKey, getLatestHistory, saveToLocalStorage])\n\n const getLatestFieldHistory = useCallback(() => {\n const latestHistory = getLatestHistory()\n return latestHistory[fieldKey] || { currentIndex: -1, history: [] }\n }, [getLatestHistory, fieldKey])\n\n const fieldHistory = getLatestFieldHistory()\n\n const canUndo = fieldHistory.currentIndex > 0\n const canRedo = fieldHistory.currentIndex < fieldHistory.history.length - 1\n const currentValue = fieldHistory.history[fieldHistory.currentIndex]\n\n return {\n canRedo,\n canUndo,\n currentValue,\n redo,\n set,\n undo,\n }\n}\n"],"names":["useDocumentInfo","useField","useCallback","useEffect","PLUGIN_NAME","useFieldProps","STORAGE_KEY","useHistory","id","path","pathFromContext","schemaPath","value","currentFieldValue","fieldKey","getLatestHistory","localStorage","JSON","parse","getItem","e","console","error","saveToLocalStorage","newGlobalHistory","setItem","stringify","clearHistory","latestHistory","Object","keys","forEach","k","startsWith","toString","currentIndex","history","newIndex","set","data","newHistory","slice","length","undo","newValue","undefined","redo","getLatestFieldHistory","fieldHistory","canUndo","canRedo","currentValue"],"mappings":"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"}
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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"}
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ type Props = {
3
+ name: string;
4
+ path: string;
5
+ };
6
+ export declare const DynamicModelSelect: React.FC<Props>;
7
+ export {};
@@ -0,0 +1,231 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { SelectInput, useField, useFormFields } from '@payloadcms/ui';
4
+ import React, { useEffect, useMemo, useState } from 'react';
5
+ import { allProviderBlocks } from '../../ai/providers/blocks/index.js';
6
+ /**
7
+ * Find a field by name within a block's fields, searching through tabs
8
+ */ function findFieldInBlock(block, fieldName) {
9
+ const searchFields = (fields)=>{
10
+ for (const field of fields){
11
+ if ('name' in field && field.name === fieldName) {
12
+ return field;
13
+ }
14
+ if (field.type === 'tabs' && 'tabs' in field) {
15
+ for (const tab of field.tabs){
16
+ const found = searchFields(tab.fields);
17
+ if (found) {
18
+ return found;
19
+ }
20
+ }
21
+ }
22
+ if (field.type === 'group' && 'fields' in field) {
23
+ const found = searchFields(field.fields);
24
+ if (found) {
25
+ return found;
26
+ }
27
+ }
28
+ }
29
+ return undefined;
30
+ };
31
+ return searchFields(block.fields);
32
+ }
33
+ /**
34
+ * Infer use case from field path
35
+ * Handles both:
36
+ * - AISettings paths: 'defaults.text.model', 'defaults.image.model'
37
+ * - Instructions paths: 'text-settings.model', 'image-settings.model'
38
+ */ function inferUseCase(path) {
39
+ const pathParts = path.split('.');
40
+ const parentName = pathParts[pathParts.length - 2];
41
+ // AISettings: 'defaults.text.model' -> parentName is 'text'
42
+ // Direct use case names
43
+ if ([
44
+ 'image',
45
+ 'text',
46
+ 'tts',
47
+ 'video'
48
+ ].includes(parentName)) {
49
+ return parentName;
50
+ }
51
+ // Instructions: 'text-settings.model' -> parentName is 'text-settings'
52
+ if (parentName === 'image-settings') {
53
+ return 'image';
54
+ }
55
+ if (parentName === 'tts-settings') {
56
+ return 'tts';
57
+ }
58
+ if (parentName === 'text-settings' || parentName === 'richtext-settings') {
59
+ return 'text';
60
+ }
61
+ if (parentName === 'video-settings') {
62
+ return 'video';
63
+ }
64
+ // Default to text
65
+ return 'text';
66
+ }
67
+ export const DynamicModelSelect = (props)=>{
68
+ const { name, path } = props;
69
+ // Getting the 'provider' sibling field value
70
+ const parentPath = path.split('.').slice(0, -1).join('.');
71
+ const providerPath = `${parentPath}.provider`;
72
+ const providerField = useFormFields(([fields])=>fields[providerPath]);
73
+ const providerValue = providerField?.value;
74
+ // Get all form fields to search for live provider configuration (for AISettings context)
75
+ // We filter to only 'providers' fields to avoid unnecessary re-renders,
76
+ // but note that the selector runs on every change.
77
+ const formProviders = useFormFields(([fields])=>{
78
+ const providers = {};
79
+ if (fields && typeof fields === 'object') {
80
+ Object.keys(fields).forEach((key)=>{
81
+ if (key.startsWith('providers.')) {
82
+ providers[key] = fields[key];
83
+ }
84
+ });
85
+ }
86
+ return providers;
87
+ });
88
+ const { setValue, value } = useField({
89
+ path
90
+ });
91
+ // State to hold fetched providers data
92
+ const [providersData, setProvidersData] = useState([]);
93
+ // Fetch AI Settings global to get configured providers
94
+ useEffect(()=>{
95
+ const fetchSettings = async ()=>{
96
+ try {
97
+ const response = await fetch('/api/globals/ai-settings?depth=1');
98
+ if (response.ok) {
99
+ const data = await response.json();
100
+ if (data && data.providers) {
101
+ setProvidersData(data.providers);
102
+ }
103
+ }
104
+ } catch (error) {
105
+ console.error('Error fetching AI settings:', error);
106
+ }
107
+ };
108
+ fetchSettings().catch(console.error);
109
+ }, []);
110
+ const inferredUseCase = useMemo(()=>inferUseCase(path), [
111
+ path
112
+ ]);
113
+ const options = useMemo(()=>{
114
+ if (!providerValue) {
115
+ return [];
116
+ }
117
+ const optionsList = [];
118
+ // Strategy:
119
+ // 1. Try to find provider in LIVE form state (if editing AISettings)
120
+ // 2. If not found, try to find in FETCHED API data (if editing Instructions or saved AISettings)
121
+ // 3. Fall back to static defaults from block definitions
122
+ let foundInForm = false;
123
+ let foundInAPI = false;
124
+ // --- 1. Live Form Search ---
125
+ // Iterate through form fields to find the matching provider block
126
+ // We assume standard block structure: providers.0.blockType, etc.
127
+ // We search up to 20 providers to be safe (unlikely to have more)
128
+ for(let i = 0; i < 20; i++){
129
+ const typeKey = `providers.${i}.blockType`;
130
+ const typeField = formProviders[typeKey];
131
+ if (!typeField) {
132
+ break;
133
+ } // Stop if no more blocks (or gap)
134
+ if (typeof typeField === 'object' && 'value' in typeField && typeField.value === providerValue) {
135
+ foundInForm = true;
136
+ // Found the provider! Now iterate its models
137
+ // Models path: providers.0.models.0.id
138
+ for(let j = 0; j < 50; j++){
139
+ const idKey = `providers.${i}.models.${j}.id`;
140
+ const nameKey = `providers.${i}.models.${j}.name`;
141
+ const useCaseKey = `providers.${i}.models.${j}.useCase`;
142
+ const enabledKey = `providers.${i}.models.${j}.enabled`;
143
+ const idField = formProviders[idKey];
144
+ if (!idField) {
145
+ break;
146
+ } // Stop if no more models
147
+ const modelId = idField.value;
148
+ const modelName = formProviders[nameKey]?.value;
149
+ const modelUseCase = formProviders[useCaseKey]?.value;
150
+ const modelEnabled = formProviders[enabledKey]?.value;
151
+ // Check use case and enabled status (default to enabled if undefined)
152
+ if (modelUseCase === inferredUseCase && modelEnabled !== false) {
153
+ optionsList.push({
154
+ label: modelName || modelId,
155
+ value: modelId
156
+ });
157
+ }
158
+ }
159
+ break; // Stop searching providers
160
+ }
161
+ }
162
+ // --- 2. API Data Search (if not found in form) ---
163
+ if (!foundInForm) {
164
+ const userProviderBlock = providersData.find((p)=>p.blockType === providerValue);
165
+ if (userProviderBlock && userProviderBlock.models) {
166
+ foundInAPI = true;
167
+ userProviderBlock.models.forEach((m)=>{
168
+ if (m.useCase === inferredUseCase && m.enabled !== false) {
169
+ // Avoid duplicates
170
+ if (!optionsList.some((opt)=>opt.value === m.id)) {
171
+ optionsList.push({
172
+ label: m.name,
173
+ value: m.id
174
+ });
175
+ }
176
+ }
177
+ });
178
+ }
179
+ }
180
+ // --- 3. Static Defaults (if not found in form OR API) ---
181
+ // Note: We only fall back to static if we didn't find ANY configuration for this provider.
182
+ // If we found the provider but it had no models for this use case, we show empty list (correct).
183
+ if (!foundInForm && !foundInAPI) {
184
+ const staticBlock = allProviderBlocks.find((b)=>b.slug === providerValue);
185
+ if (staticBlock) {
186
+ // Search through tabs to find models field
187
+ const modelsField = findFieldInBlock(staticBlock, 'models');
188
+ const defaultModels = modelsField && 'defaultValue' in modelsField ? modelsField.defaultValue : [];
189
+ defaultModels.forEach((m)=>{
190
+ if (m.useCase === inferredUseCase && m.enabled !== false) {
191
+ optionsList.push({
192
+ label: m.name,
193
+ value: m.id
194
+ });
195
+ }
196
+ });
197
+ }
198
+ }
199
+ return optionsList;
200
+ }, [
201
+ providerValue,
202
+ providersData,
203
+ inferredUseCase,
204
+ formProviders
205
+ ]);
206
+ return /*#__PURE__*/ _jsxs("div", {
207
+ className: "field-type select",
208
+ children: [
209
+ /*#__PURE__*/ _jsx("label", {
210
+ className: "field-label",
211
+ htmlFor: path,
212
+ children: "Model"
213
+ }),
214
+ /*#__PURE__*/ _jsx(SelectInput, {
215
+ name: name,
216
+ onChange: (option)=>{
217
+ if (option && typeof option === 'object' && 'value' in option) {
218
+ setValue(option.value);
219
+ } else {
220
+ setValue(option);
221
+ }
222
+ },
223
+ options: options,
224
+ path: path,
225
+ value: value
226
+ })
227
+ ]
228
+ });
229
+ };
230
+
231
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/ui/DynamicModelSelect/index.tsx"],"sourcesContent":["'use client'\n\nimport { SelectInput, useField, useFormFields } from '@payloadcms/ui'\nimport React, { useEffect, useMemo, useState } from 'react'\n\nimport { allProviderBlocks } from '../../ai/providers/blocks/index.js'\n\ntype Props = {\n name: string\n path: string\n}\n\n/**\n * Find a field by name within a block's fields, searching through tabs\n */\nfunction findFieldInBlock(block: any, fieldName: string): any | undefined {\n const searchFields = (fields: any[]): any | undefined => {\n for (const field of fields) {\n if ('name' in field && field.name === fieldName) {\n return field\n }\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n const found = searchFields(tab.fields)\n if (found) {\n return found\n }\n }\n }\n if (field.type === 'group' && 'fields' in field) {\n const found = searchFields(field.fields)\n if (found) {\n return found\n }\n }\n }\n return undefined\n }\n \n return searchFields(block.fields)\n}\n\n/**\n * Infer use case from field path\n * Handles both:\n * - AISettings paths: 'defaults.text.model', 'defaults.image.model'\n * - Instructions paths: 'text-settings.model', 'image-settings.model'\n */\nfunction inferUseCase(path: string): string {\n const pathParts = path.split('.')\n const parentName = pathParts[pathParts.length - 2]\n \n // AISettings: 'defaults.text.model' -> parentName is 'text'\n // Direct use case names\n if (['image', 'text', 'tts', 'video'].includes(parentName)) {\n return parentName\n }\n \n // Instructions: 'text-settings.model' -> parentName is 'text-settings'\n if (parentName === 'image-settings') {\n return 'image'\n }\n if (parentName === 'tts-settings') {\n return 'tts'\n }\n if (parentName === 'text-settings' || parentName === 'richtext-settings') {\n return 'text'\n }\n if (parentName === 'video-settings') {\n return 'video'\n }\n \n // Default to text\n return 'text'\n}\n\nexport const DynamicModelSelect: React.FC<Props> = (props) => {\n const { name, path } = props\n\n // Getting the 'provider' sibling field value\n const parentPath = path.split('.').slice(0, -1).join('.')\n const providerPath = `${parentPath}.provider`\n\n const providerField = useFormFields(([fields]) => fields[providerPath])\n const providerValue = providerField?.value as string\n\n // Get all form fields to search for live provider configuration (for AISettings context)\n // We filter to only 'providers' fields to avoid unnecessary re-renders, \n // but note that the selector runs on every change.\n const formProviders = useFormFields(([fields]) => {\n const providers: Record<string, any> = {}\n if (fields && typeof fields === 'object') {\n Object.keys(fields).forEach((key) => {\n if (key.startsWith('providers.')) {\n providers[key] = fields[key]\n }\n })\n }\n return providers\n })\n\n const { setValue, value } = useField<string>({ path })\n\n // State to hold fetched providers data\n const [providersData, setProvidersData] = useState<any[]>([])\n\n // Fetch AI Settings global to get configured providers\n useEffect(() => {\n const fetchSettings = async () => {\n try {\n const response = await fetch('/api/globals/ai-settings?depth=1')\n if (response.ok) {\n const data = await response.json()\n if (data && data.providers) {\n setProvidersData(data.providers)\n }\n }\n } catch (error) {\n console.error('Error fetching AI settings:', error)\n }\n }\n\n fetchSettings().catch(console.error)\n }, [])\n\n const inferredUseCase = useMemo(() => inferUseCase(path), [path])\n\n const options = useMemo(() => {\n if (!providerValue) {\n return []\n }\n\n const optionsList: { label: string; value: string }[] = []\n\n // Strategy:\n // 1. Try to find provider in LIVE form state (if editing AISettings)\n // 2. If not found, try to find in FETCHED API data (if editing Instructions or saved AISettings)\n // 3. Fall back to static defaults from block definitions\n\n let foundInForm = false\n let foundInAPI = false\n\n // --- 1. Live Form Search ---\n // Iterate through form fields to find the matching provider block\n // We assume standard block structure: providers.0.blockType, etc.\n // We search up to 20 providers to be safe (unlikely to have more)\n for (let i = 0; i < 20; i++) {\n const typeKey = `providers.${i}.blockType`\n const typeField = formProviders[typeKey]\n \n if (!typeField) {break} // Stop if no more blocks (or gap)\n \n if (typeof typeField === 'object' && 'value' in typeField && typeField.value === providerValue) {\n foundInForm = true\n // Found the provider! Now iterate its models\n // Models path: providers.0.models.0.id\n for (let j = 0; j < 50; j++) {\n const idKey = `providers.${i}.models.${j}.id`\n const nameKey = `providers.${i}.models.${j}.name`\n const useCaseKey = `providers.${i}.models.${j}.useCase`\n const enabledKey = `providers.${i}.models.${j}.enabled`\n \n const idField = formProviders[idKey]\n if (!idField) {break} // Stop if no more models\n \n const modelId = (idField).value as string\n const modelName = (formProviders[nameKey])?.value as string\n const modelUseCase = (formProviders[useCaseKey])?.value as string\n const modelEnabled = (formProviders[enabledKey])?.value\n \n // Check use case and enabled status (default to enabled if undefined)\n if (modelUseCase === inferredUseCase && modelEnabled !== false) {\n optionsList.push({\n label: modelName || modelId,\n value: modelId,\n })\n }\n }\n break // Stop searching providers\n }\n }\n\n // --- 2. API Data Search (if not found in form) ---\n if (!foundInForm) {\n const userProviderBlock = providersData.find((p: any) => p.blockType === providerValue)\n\n if (userProviderBlock && userProviderBlock.models) {\n foundInAPI = true\n userProviderBlock.models.forEach((m: any) => {\n if (m.useCase === inferredUseCase && m.enabled !== false) {\n // Avoid duplicates\n if (!optionsList.some((opt) => opt.value === m.id)) {\n optionsList.push({\n label: m.name,\n value: m.id,\n })\n }\n }\n })\n }\n }\n\n // --- 3. Static Defaults (if not found in form OR API) ---\n // Note: We only fall back to static if we didn't find ANY configuration for this provider.\n // If we found the provider but it had no models for this use case, we show empty list (correct).\n if (!foundInForm && !foundInAPI) {\n const staticBlock = allProviderBlocks.find((b) => b.slug === providerValue)\n\n if (staticBlock) {\n // Search through tabs to find models field\n const modelsField = findFieldInBlock(staticBlock, 'models')\n const defaultModels =\n modelsField && 'defaultValue' in modelsField ? (modelsField.defaultValue as any[]) : []\n\n defaultModels.forEach((m) => {\n if (m.useCase === inferredUseCase && m.enabled !== false) {\n optionsList.push({\n label: m.name,\n value: m.id,\n })\n }\n })\n }\n }\n\n return optionsList\n }, [providerValue, providersData, inferredUseCase, formProviders])\n\n return (\n <div className=\"field-type select\">\n <label className=\"field-label\" htmlFor={path}>\n Model\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={options as any}\n path={path}\n value={value}\n />\n </div>\n )\n}\n\n"],"names":["SelectInput","useField","useFormFields","React","useEffect","useMemo","useState","allProviderBlocks","findFieldInBlock","block","fieldName","searchFields","fields","field","name","type","tab","tabs","found","undefined","inferUseCase","path","pathParts","split","parentName","length","includes","DynamicModelSelect","props","parentPath","slice","join","providerPath","providerField","providerValue","value","formProviders","providers","Object","keys","forEach","key","startsWith","setValue","providersData","setProvidersData","fetchSettings","response","fetch","ok","data","json","error","console","catch","inferredUseCase","options","optionsList","foundInForm","foundInAPI","i","typeKey","typeField","j","idKey","nameKey","useCaseKey","enabledKey","idField","modelId","modelName","modelUseCase","modelEnabled","push","label","userProviderBlock","find","p","blockType","models","m","useCase","enabled","some","opt","id","staticBlock","b","slug","modelsField","defaultModels","defaultValue","div","className","htmlFor","onChange","option"],"mappings":"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"}