@ai-stack/payloadcms 3.68.0-beta.2 → 3.68.0-beta.4

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 (76) hide show
  1. package/dist/ai/core/media/image/generateImage.js +2 -6
  2. package/dist/ai/core/media/image/generateImage.js.map +1 -1
  3. package/dist/ai/prompts.d.ts +1 -2
  4. package/dist/ai/prompts.js +0 -110
  5. package/dist/ai/prompts.js.map +1 -1
  6. package/dist/ai/providers/blocks/elevenlabs.js +1 -1
  7. package/dist/ai/providers/blocks/elevenlabs.js.map +1 -1
  8. package/dist/ai/providers/blocks/google.js +9 -5
  9. package/dist/ai/providers/blocks/google.js.map +1 -1
  10. package/dist/ai/providers/blocks/openai.js +1 -1
  11. package/dist/ai/providers/blocks/openai.js.map +1 -1
  12. package/dist/ai/providers/registry.js +0 -1
  13. package/dist/ai/providers/registry.js.map +1 -1
  14. package/dist/ai/utils/filterEditorSchemaByNodes.d.ts +9 -0
  15. package/dist/ai/utils/filterEditorSchemaByNodes.js +30 -3
  16. package/dist/ai/utils/filterEditorSchemaByNodes.js.map +1 -1
  17. package/dist/ai/utils/nodeToSchemaMap.d.ts +22 -0
  18. package/dist/ai/utils/nodeToSchemaMap.js +72 -0
  19. package/dist/ai/utils/nodeToSchemaMap.js.map +1 -0
  20. package/dist/collections/AISettings.js +44 -17
  21. package/dist/collections/AISettings.js.map +1 -1
  22. package/dist/defaults.d.ts +1 -0
  23. package/dist/defaults.js +8 -0
  24. package/dist/defaults.js.map +1 -1
  25. package/dist/endpoints/fetchFields.js +10 -0
  26. package/dist/endpoints/fetchFields.js.map +1 -1
  27. package/dist/endpoints/index.js +12 -0
  28. package/dist/endpoints/index.js.map +1 -1
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.js +1 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/plugin.js +16 -32
  33. package/dist/plugin.js.map +1 -1
  34. package/dist/providers/InstructionsProvider/InstructionsProvider.js +44 -15
  35. package/dist/providers/InstructionsProvider/InstructionsProvider.js.map +1 -1
  36. package/dist/providers/InstructionsProvider/InstructionsProvider.jsx +36 -16
  37. package/dist/providers/InstructionsProvider/context.d.ts +3 -0
  38. package/dist/providers/InstructionsProvider/context.js +2 -0
  39. package/dist/providers/InstructionsProvider/context.js.map +1 -1
  40. package/dist/providers/InstructionsProvider/useInstructions.js +3 -1
  41. package/dist/providers/InstructionsProvider/useInstructions.js.map +1 -1
  42. package/dist/types.d.ts +1 -5
  43. package/dist/types.js.map +1 -1
  44. package/dist/ui/AIConfigDashboard/index.js +198 -22
  45. package/dist/ui/AIConfigDashboard/index.js.map +1 -1
  46. package/dist/ui/AIConfigDashboard/index.jsx +159 -13
  47. package/dist/ui/Compose/Compose.js +21 -2
  48. package/dist/ui/Compose/Compose.js.map +1 -1
  49. package/dist/ui/Compose/Compose.jsx +21 -2
  50. package/dist/ui/Compose/hooks/useGenerate.js +26 -174
  51. package/dist/ui/Compose/hooks/useGenerate.js.map +1 -1
  52. package/dist/ui/Compose/hooks/useGenerateUpload.d.ts +11 -0
  53. package/dist/ui/Compose/hooks/useGenerateUpload.js +150 -0
  54. package/dist/ui/Compose/hooks/useGenerateUpload.js.map +1 -0
  55. package/dist/ui/Compose/hooks/useStreamingUpdate.d.ts +8 -0
  56. package/dist/ui/Compose/hooks/useStreamingUpdate.js +48 -0
  57. package/dist/ui/Compose/hooks/useStreamingUpdate.js.map +1 -0
  58. package/dist/ui/EncryptedTextField/index.js +4 -4
  59. package/dist/ui/EncryptedTextField/index.js.map +1 -1
  60. package/dist/ui/EncryptedTextField/index.jsx +4 -4
  61. package/dist/utilities/buildSmartPrompt.js +4 -6
  62. package/dist/utilities/buildSmartPrompt.js.map +1 -1
  63. package/dist/utilities/encryption.js +2 -1
  64. package/dist/utilities/encryption.js.map +1 -1
  65. package/dist/utilities/seedProperties.d.ts +7 -0
  66. package/dist/utilities/seedProperties.js +100 -0
  67. package/dist/utilities/seedProperties.js.map +1 -0
  68. package/dist/utilities/setSafeLexicalState.js +79 -6
  69. package/dist/utilities/setSafeLexicalState.js.map +1 -1
  70. package/dist/utilities/updateFieldsConfig.d.ts +1 -1
  71. package/dist/utilities/updateFieldsConfig.js +1 -0
  72. package/dist/utilities/updateFieldsConfig.js.map +1 -1
  73. package/package.json +2 -1
  74. package/dist/init.d.ts +0 -7
  75. package/dist/init.js +0 -152
  76. package/dist/init.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/AIConfigDashboard/index.tsx"],"sourcesContent":["'use client'\n\nimport React from 'react'\n\nexport const AIConfigDashboard: React.FC = () => {\n return (\n <div\n style={{\n alignItems: 'center',\n background: 'var(--theme-elevation-50)',\n border: '1px solid var(--theme-elevation-150)',\n borderRadius: '8px',\n display: 'flex',\n justifyContent: 'space-between',\n marginBottom: '20px',\n padding: '20px',\n }}\n >\n <div>\n <h4 style={{ margin: '0 0 5px 0' }}>AI Configuration</h4>\n <p style={{ color: 'var(--theme-elevation-500)', fontSize: '14px', margin: '0' }}>\n Manage your AI providers, API keys, and default models.\n </p>\n </div>\n <a href=\"/admin/globals/ai-settings\">\n <button className=\"btn btn--style-primary btn--size-small\">Manage AI Settings</button>\n </a>\n </div>\n )\n}\n"],"names":["React","AIConfigDashboard","div","style","alignItems","background","border","borderRadius","display","justifyContent","marginBottom","padding","h4","margin","p","color","fontSize","a","href","button","className"],"mappings":"AAAA;;AAEA,OAAOA,WAAW,QAAO;AAEzB,OAAO,MAAMC,oBAA8B;IACzC,qBACE,MAACC;QACCC,OAAO;YACLC,YAAY;YACZC,YAAY;YACZC,QAAQ;YACRC,cAAc;YACdC,SAAS;YACTC,gBAAgB;YAChBC,cAAc;YACdC,SAAS;QACX;;0BAEA,MAACT;;kCACC,KAACU;wBAAGT,OAAO;4BAAEU,QAAQ;wBAAY;kCAAG;;kCACpC,KAACC;wBAAEX,OAAO;4BAAEY,OAAO;4BAA8BC,UAAU;4BAAQH,QAAQ;wBAAI;kCAAG;;;;0BAIpF,KAACI;gBAAEC,MAAK;0BACN,cAAA,KAACC;oBAAOC,WAAU;8BAAyC;;;;;AAInE,EAAC"}
1
+ {"version":3,"sources":["../../../src/ui/AIConfigDashboard/index.tsx"],"sourcesContent":["'use client'\n\nimport { Button, toast, useConfig } from '@payloadcms/ui'\n// @ts-expect-error - Next.js types are not resolving correctly with nodenext but runtime is fine\nimport { useRouter } from 'next/navigation'\nimport React, { useContext, useEffect, useState } from 'react'\n\nimport { excludeCollections } from '../../defaults.js'\nimport { InstructionsContext } from '../../providers/InstructionsProvider/context.js'\n\nexport const AIConfigDashboard: React.FC = () => {\n const {\n config: {\n collections,\n routes: { admin: adminRoute, api: apiRoute },\n },\n } = useConfig()\n const router = useRouter()\n const { refresh, setEnabledCollections: setEnabledCollectionsInContext } =\n useContext(InstructionsContext)\n\n const [enabledCollections, setEnabledCollections] = useState<string[]>([])\n const [isLoading, setIsLoading] = useState(true)\n const [isSaving, setIsSaving] = useState(false)\n\n const availableCollections = collections.filter(\n (c) =>\n !excludeCollections.includes(c.slug) &&\n !(c.admin as unknown as { hidden?: ((args: any) => boolean) | boolean })?.hidden,\n )\n\n useEffect(() => {\n const fetchSettings = async () => {\n try {\n const response = await fetch(`${apiRoute}/globals/ai-settings`)\n if (response.ok) {\n const data = await response.json()\n // Handle both simple array and object wrapper if Payload wraps it\n const storedEnabled = data.enabledCollections || []\n setEnabledCollections(Array.isArray(storedEnabled) ? storedEnabled : [])\n }\n } catch (error) {\n console.error('Failed to fetch AI settings:', error)\n } finally {\n setIsLoading(false)\n }\n }\n\n fetchSettings().catch((e) => {\n console.log(e)\n })\n }, [apiRoute])\n\n const handleToggle = (slug: string) => {\n setEnabledCollections((prev) => {\n if (prev.includes(slug)) {\n return prev.filter((s) => s !== slug)\n }\n return [...prev, slug]\n })\n }\n\n const handleSave = async () => {\n setIsSaving(true)\n try {\n // First fetch current settings to get ID or just rely on global update behavior\n // We need to adhere to Payload's global update API\n const response = await fetch(`${apiRoute}/globals/ai-settings`, {\n body: JSON.stringify({\n enabledCollections,\n }),\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n if (response.ok) {\n toast.success('Settings saved successfully')\n if (setEnabledCollectionsInContext) {\n setEnabledCollectionsInContext(enabledCollections)\n }\n if (refresh) {\n await refresh()\n }\n router.refresh()\n } else {\n toast.error('Failed to save settings')\n }\n } catch (error) {\n console.error('Error saving settings:', error)\n toast.error('Error saving settings')\n } finally {\n setIsSaving(false)\n }\n }\n\n if (isLoading) {\n return <div style={{ padding: '20px', textAlign: 'center' }}>Loading AI configuration...</div>\n }\n\n return (\n <div\n style={{\n background: 'var(--theme-elevation-50)',\n // border: '1px solid var(--theme-elevation-150)',\n // borderRadius: '8px',\n marginBottom: '20px',\n overflow: 'hidden',\n }}\n >\n <div\n style={{\n alignItems: 'center',\n borderBottom: '1px solid var(--theme-elevation-150)',\n display: 'flex',\n justifyContent: 'space-between',\n padding: '20px',\n }}\n >\n <div>\n <h4 style={{ margin: '0 0 5px 0' }}>AI Configuration</h4>\n <p style={{ color: 'var(--theme-elevation-500)', fontSize: '14px', margin: '0' }}>\n Manage your AI providers, API keys, and enable AI for specific collections.\n </p>\n </div>\n <div style={{ display: 'flex', gap: '10px' }}>\n <Button buttonStyle=\"secondary\" el=\"link\" to={`${adminRoute}/globals/ai-settings`}>\n Settings\n </Button>\n <Button\n disabled={isSaving}\n onClick={handleSave}\n >\n {isSaving ? 'Saving...' : 'Save Changes'}\n </Button>\n </div>\n </div>\n\n <div style={{ padding: '20px' }}>\n <h5 style={{ marginBottom: '15px' }}>Enabled Collections</h5>\n <div\n style={{\n display: 'grid',\n gap: '15px',\n gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))',\n }}\n >\n {availableCollections.map((collection) => {\n const isEnabled = enabledCollections.includes(collection.slug)\n return (\n <button\n key={collection.slug}\n onClick={() => handleToggle(collection.slug)}\n style={{\n alignItems: 'center',\n background: isEnabled\n ? 'var(--theme-elevation-100)'\n : 'var(--theme-elevation-50)',\n border: `1px solid ${isEnabled ? 'var(--theme-text-success)' : 'var(--theme-elevation-200)'}`,\n borderRadius: '6px',\n cursor: 'pointer',\n display: 'flex',\n gap: '10px',\n padding: '10px 15px',\n textAlign: 'left',\n transition: 'all 0.2s ease',\n width: '100%',\n }}\n type=\"button\"\n >\n <div\n style={{\n alignItems: 'center',\n background: isEnabled\n ? 'var(--theme-text-success)'\n : 'var(--theme-elevation-200)',\n borderRadius: '12px',\n display: 'flex',\n height: '24px',\n justifyContent: isEnabled ? 'flex-end' : 'flex-start',\n padding: '2px',\n transition: 'all 0.2s ease',\n width: '44px',\n }}\n >\n <div\n style={{\n background: 'white',\n borderRadius: '50%',\n height: '20px',\n width: '20px',\n }}\n />\n </div>\n <span style={{ fontWeight: 500 }}>\n {typeof collection.labels?.singular === 'string'\n ? collection.labels.singular\n : collection.labels?.singular?.en || collection.slug}\n </span>\n </button>\n )\n })}\n </div>\n </div>\n </div>\n )\n}\n"],"names":["Button","toast","useConfig","useRouter","React","useContext","useEffect","useState","excludeCollections","InstructionsContext","AIConfigDashboard","config","collections","routes","admin","adminRoute","api","apiRoute","router","refresh","setEnabledCollections","setEnabledCollectionsInContext","enabledCollections","isLoading","setIsLoading","isSaving","setIsSaving","availableCollections","filter","c","includes","slug","hidden","fetchSettings","response","fetch","ok","data","json","storedEnabled","Array","isArray","error","console","catch","e","log","handleToggle","prev","s","handleSave","body","JSON","stringify","headers","method","success","div","style","padding","textAlign","background","marginBottom","overflow","alignItems","borderBottom","display","justifyContent","h4","margin","p","color","fontSize","gap","buttonStyle","el","to","disabled","onClick","h5","gridTemplateColumns","map","collection","isEnabled","button","border","borderRadius","cursor","transition","width","type","height","span","fontWeight","labels","singular","en"],"mappings":"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"}
@@ -1,24 +1,170 @@
1
1
  'use client';
2
- import React from 'react';
2
+ import { Button, toast, useConfig } from '@payloadcms/ui';
3
+ // @ts-expect-error - Next.js types are not resolving correctly with nodenext but runtime is fine
4
+ import { useRouter } from 'next/navigation';
5
+ import React, { useContext, useEffect, useState } from 'react';
6
+ import { excludeCollections } from '../../defaults.js';
7
+ import { InstructionsContext } from '../../providers/InstructionsProvider/context.js';
3
8
  export const AIConfigDashboard = () => {
9
+ const { config: { collections, routes: { admin: adminRoute, api: apiRoute }, }, } = useConfig();
10
+ const router = useRouter();
11
+ const { refresh, setEnabledCollections: setEnabledCollectionsInContext } = useContext(InstructionsContext);
12
+ const [enabledCollections, setEnabledCollections] = useState([]);
13
+ const [isLoading, setIsLoading] = useState(true);
14
+ const [isSaving, setIsSaving] = useState(false);
15
+ const availableCollections = collections.filter((c) => !excludeCollections.includes(c.slug) &&
16
+ !c.admin?.hidden);
17
+ useEffect(() => {
18
+ const fetchSettings = async () => {
19
+ try {
20
+ const response = await fetch(`${apiRoute}/globals/ai-settings`);
21
+ if (response.ok) {
22
+ const data = await response.json();
23
+ // Handle both simple array and object wrapper if Payload wraps it
24
+ const storedEnabled = data.enabledCollections || [];
25
+ setEnabledCollections(Array.isArray(storedEnabled) ? storedEnabled : []);
26
+ }
27
+ }
28
+ catch (error) {
29
+ console.error('Failed to fetch AI settings:', error);
30
+ }
31
+ finally {
32
+ setIsLoading(false);
33
+ }
34
+ };
35
+ fetchSettings().catch((e) => {
36
+ console.log(e);
37
+ });
38
+ }, [apiRoute]);
39
+ const handleToggle = (slug) => {
40
+ setEnabledCollections((prev) => {
41
+ if (prev.includes(slug)) {
42
+ return prev.filter((s) => s !== slug);
43
+ }
44
+ return [...prev, slug];
45
+ });
46
+ };
47
+ const handleSave = async () => {
48
+ setIsSaving(true);
49
+ try {
50
+ // First fetch current settings to get ID or just rely on global update behavior
51
+ // We need to adhere to Payload's global update API
52
+ const response = await fetch(`${apiRoute}/globals/ai-settings`, {
53
+ body: JSON.stringify({
54
+ enabledCollections,
55
+ }),
56
+ headers: {
57
+ 'Content-Type': 'application/json',
58
+ },
59
+ method: 'POST',
60
+ });
61
+ if (response.ok) {
62
+ toast.success('Settings saved successfully');
63
+ if (setEnabledCollectionsInContext) {
64
+ setEnabledCollectionsInContext(enabledCollections);
65
+ }
66
+ if (refresh) {
67
+ await refresh();
68
+ }
69
+ router.refresh();
70
+ }
71
+ else {
72
+ toast.error('Failed to save settings');
73
+ }
74
+ }
75
+ catch (error) {
76
+ console.error('Error saving settings:', error);
77
+ toast.error('Error saving settings');
78
+ }
79
+ finally {
80
+ setIsSaving(false);
81
+ }
82
+ };
83
+ if (isLoading) {
84
+ return <div style={{ padding: '20px', textAlign: 'center' }}>Loading AI configuration...</div>;
85
+ }
4
86
  return (<div style={{
5
- alignItems: 'center',
6
87
  background: 'var(--theme-elevation-50)',
7
- border: '1px solid var(--theme-elevation-150)',
8
- borderRadius: '8px',
88
+ // border: '1px solid var(--theme-elevation-150)',
89
+ // borderRadius: '8px',
90
+ marginBottom: '20px',
91
+ overflow: 'hidden',
92
+ }}>
93
+ <div style={{
94
+ alignItems: 'center',
95
+ borderBottom: '1px solid var(--theme-elevation-150)',
9
96
  display: 'flex',
10
97
  justifyContent: 'space-between',
11
- marginBottom: '20px',
12
98
  padding: '20px',
13
99
  }}>
14
- <div>
15
- <h4 style={{ margin: '0 0 5px 0' }}>AI Configuration</h4>
16
- <p style={{ color: 'var(--theme-elevation-500)', fontSize: '14px', margin: '0' }}>
17
- Manage your AI providers, API keys, and default models.
18
- </p>
100
+ <div>
101
+ <h4 style={{ margin: '0 0 5px 0' }}>AI Configuration</h4>
102
+ <p style={{ color: 'var(--theme-elevation-500)', fontSize: '14px', margin: '0' }}>
103
+ Manage your AI providers, API keys, and enable AI for specific collections.
104
+ </p>
105
+ </div>
106
+ <div style={{ display: 'flex', gap: '10px' }}>
107
+ <Button buttonStyle="secondary" el="link" to={`${adminRoute}/globals/ai-settings`}>
108
+ Settings
109
+ </Button>
110
+ <Button disabled={isSaving} onClick={handleSave}>
111
+ {isSaving ? 'Saving...' : 'Save Changes'}
112
+ </Button>
113
+ </div>
114
+ </div>
115
+
116
+ <div style={{ padding: '20px' }}>
117
+ <h5 style={{ marginBottom: '15px' }}>Enabled Collections</h5>
118
+ <div style={{
119
+ display: 'grid',
120
+ gap: '15px',
121
+ gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))',
122
+ }}>
123
+ {availableCollections.map((collection) => {
124
+ const isEnabled = enabledCollections.includes(collection.slug);
125
+ return (<button key={collection.slug} onClick={() => handleToggle(collection.slug)} style={{
126
+ alignItems: 'center',
127
+ background: isEnabled
128
+ ? 'var(--theme-elevation-100)'
129
+ : 'var(--theme-elevation-50)',
130
+ border: `1px solid ${isEnabled ? 'var(--theme-text-success)' : 'var(--theme-elevation-200)'}`,
131
+ borderRadius: '6px',
132
+ cursor: 'pointer',
133
+ display: 'flex',
134
+ gap: '10px',
135
+ padding: '10px 15px',
136
+ textAlign: 'left',
137
+ transition: 'all 0.2s ease',
138
+ width: '100%',
139
+ }} type="button">
140
+ <div style={{
141
+ alignItems: 'center',
142
+ background: isEnabled
143
+ ? 'var(--theme-text-success)'
144
+ : 'var(--theme-elevation-200)',
145
+ borderRadius: '12px',
146
+ display: 'flex',
147
+ height: '24px',
148
+ justifyContent: isEnabled ? 'flex-end' : 'flex-start',
149
+ padding: '2px',
150
+ transition: 'all 0.2s ease',
151
+ width: '44px',
152
+ }}>
153
+ <div style={{
154
+ background: 'white',
155
+ borderRadius: '50%',
156
+ height: '20px',
157
+ width: '20px',
158
+ }}/>
159
+ </div>
160
+ <span style={{ fontWeight: 500 }}>
161
+ {typeof collection.labels?.singular === 'string'
162
+ ? collection.labels.singular
163
+ : collection.labels?.singular?.en || collection.slug}
164
+ </span>
165
+ </button>);
166
+ })}
167
+ </div>
19
168
  </div>
20
- <a href="/admin/globals/ai-settings">
21
- <button className="btn btn--style-primary btn--size-small">Manage AI Settings</button>
22
- </a>
23
169
  </div>);
24
170
  };
@@ -134,9 +134,28 @@ export const Compose = ({ descriptionProps, forceVisible, instructionId, isConfi
134
134
  path: pathFromContext
135
135
  });
136
136
  const setIfValueIsLexicalState = useCallback((val)=>{
137
- if (val && typeof val === 'object' && 'root' in val && lexicalEditor) {
138
- setSafeLexicalState(JSON.stringify(val), lexicalEditor);
137
+ // Prevent setting incomplete states during streaming
138
+ if (!val || typeof val !== 'object' || !('root' in val) || !lexicalEditor) {
139
+ return;
139
140
  }
141
+ // Validate that the state is complete before setting
142
+ // Check for common incomplete streaming states
143
+ if (!val.root || typeof val.root !== 'object' || Object.keys(val.root).length === 0) {
144
+ return;
145
+ }
146
+ if (val.root.type !== 'root') {
147
+ return;
148
+ }
149
+ if (!val.root.children || !Array.isArray(val.root.children) || val.root.children.length === 0) {
150
+ return;
151
+ }
152
+ // Check for invalid child types (common streaming issue)
153
+ const hasInvalidChildren = val.root.children.some((child)=>!child || !child.type || child.type === 'undefined' || child.type === '');
154
+ if (hasInvalidChildren) {
155
+ return;
156
+ }
157
+ // State looks valid, proceed
158
+ setSafeLexicalState(JSON.stringify(val), lexicalEditor);
140
159
  // DO NOT PROVIDE lexicalEditor as a dependency, it freaks out and does not update the editor after first undo/redo - revisit
141
160
  }, []);
142
161
  const popupRender = useCallback(({ close })=>{
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/Compose/Compose.tsx"],"sourcesContent":["'use client'\n\nimport type { ClientField } from 'payload'\nimport type { FC } from 'react'\n\nimport { useEditorConfigContext } from '@payloadcms/richtext-lexical/client'\nimport { Popup, useField } from '@payloadcms/ui'\nimport React, { useCallback, useMemo, useState } from 'react'\n\nimport { PLUGIN_INSTRUCTIONS_TABLE } from '../../defaults.js'\nimport { useInstructions } from '../../providers/InstructionsProvider/useInstructions.js'\nimport { setSafeLexicalState } from '../../utilities/setSafeLexicalState.js'\nimport { PluginIcon } from '../Icons/Icons.js'\nimport styles from './compose.module.css'\nimport { useMenu } from './hooks/menu/useMenu.js'\nimport { useActiveFieldTracking } from './hooks/useActiveFieldTracking.js'\nimport { useGenerate } from './hooks/useGenerate.js'\nimport { UndoRedoActions } from './UndoRedoActions.js'\n\nexport type ComposeProps = {\n descriptionProps?: {\n field: ClientField\n path: string\n schemaPath: string\n }\n forceVisible?: boolean\n instructionId: string\n isConfigAllowed: boolean\n}\n\nexport const Compose: FC<ComposeProps> = ({ descriptionProps, forceVisible, instructionId, isConfigAllowed }) => {\n const pathFromContext = descriptionProps?.path\n const { editor: lexicalEditor } = useEditorConfigContext()\n \n // Get global openDrawer from context\n const { openDrawer } = useInstructions()\n\n // Initialize global active-field tracking\n useActiveFieldTracking()\n\n const [isProcessing, setIsProcessing] = useState<boolean>(false)\n const { generate, isJobActive, isLoading, jobProgress, jobStatus, stop } = useGenerate({ instructionId })\n\n // Memoize menu event handlers to prevent recreation on every render\n const onCompose = useCallback(() => {\n console.log('Composing...')\n setIsProcessing(true)\n generate({\n action: 'Compose',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n }, [generate])\n\n const onExpand = useCallback(() => {\n console.log('Expanding...')\n generate({\n action: 'Expand',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n }, [generate])\n\n const onProofread = useCallback(() => {\n console.log('Proofreading...')\n generate({\n action: 'Proofread',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n }, [generate])\n\n const onRephrase = useCallback(() => {\n console.log('Rephrasing...')\n generate({\n action: 'Rephrase',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n }, [generate])\n\n const onSimplify = useCallback(() => {\n console.log('Simplifying...')\n generate({\n action: 'Simplify',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n }, [generate])\n\n const onSummarize = useCallback(() => {\n console.log('Summarizing...')\n generate({\n action: 'Summarize',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n }, [generate])\n\n const onTranslate = useCallback((data: unknown) => {\n console.log('Translating...')\n generate({\n action: 'Translate',\n params: data,\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n }, [generate])\n\n const handleOpenSettings = useCallback(() => {\n if (isConfigAllowed) {\n openDrawer(instructionId)\n }\n }, [isConfigAllowed, openDrawer, instructionId])\n\n const { ActiveComponent, Menu } = useMenu(\n {\n onCompose,\n onExpand,\n onProofread,\n onRephrase,\n onSettings: isConfigAllowed ? handleOpenSettings : undefined,\n onSimplify,\n onSummarize,\n onTranslate,\n },\n {\n isConfigAllowed,\n },\n )\n\n const { setValue } = useField<string>({\n path: pathFromContext,\n })\n\n const setIfValueIsLexicalState = useCallback((val: any) => {\n if (val && typeof val === 'object' && 'root' in val && lexicalEditor) {\n setSafeLexicalState(JSON.stringify(val), lexicalEditor)\n }\n\n // DO NOT PROVIDE lexicalEditor as a dependency, it freaks out and does not update the editor after first undo/redo - revisit\n }, [])\n\n const popupRender = useCallback(\n ({ close }: { close: () => void }) => {\n return <Menu isLoading={isProcessing || isLoading} onClose={close} />\n },\n [isProcessing, isLoading, Menu],\n )\n\n // Combine loading states to reduce re-renders\n const isAnyLoading = isProcessing || isLoading || isJobActive\n\n const memoizedPopup = useMemo(() => {\n return (\n <Popup\n button={<PluginIcon isLoading={isAnyLoading} />}\n render={popupRender}\n verticalAlign=\"bottom\"\n />\n )\n }, [popupRender, isAnyLoading])\n\n return (\n <label\n className={`payloadai-compose__actions ${styles.actions} ${forceVisible ? styles.actionsVisible : ''}`}\n onClick={(e) => e.preventDefault()}\n role=\"presentation\"\n >\n {memoizedPopup}\n <ActiveComponent\n isLoading={isProcessing || isLoading || isJobActive}\n loadingLabel={isJobActive ? (jobStatus === 'running' ? `Video ${Math.max(0, Math.min(100, Math.round(jobProgress ?? 0)))}%` : (jobStatus || 'Queued')) : undefined}\n stop={stop}\n />\n <UndoRedoActions\n onChange={(val) => {\n setValue(val)\n setIfValueIsLexicalState(val)\n }}\n />\n </label>\n )\n}\n"],"names":["useEditorConfigContext","Popup","useField","React","useCallback","useMemo","useState","useInstructions","setSafeLexicalState","PluginIcon","styles","useMenu","useActiveFieldTracking","useGenerate","UndoRedoActions","Compose","descriptionProps","forceVisible","instructionId","isConfigAllowed","pathFromContext","path","editor","lexicalEditor","openDrawer","isProcessing","setIsProcessing","generate","isJobActive","isLoading","jobProgress","jobStatus","stop","onCompose","console","log","action","catch","reason","error","finally","onExpand","onProofread","onRephrase","onSimplify","onSummarize","onTranslate","data","params","handleOpenSettings","ActiveComponent","Menu","onSettings","undefined","setValue","setIfValueIsLexicalState","val","JSON","stringify","popupRender","close","onClose","isAnyLoading","memoizedPopup","button","render","verticalAlign","label","className","actions","actionsVisible","onClick","e","preventDefault","role","loadingLabel","Math","max","min","round","onChange"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/ui/Compose/Compose.tsx"],"sourcesContent":["'use client'\n\nimport type { ClientField } from 'payload'\nimport type { FC } from 'react'\n\nimport { useEditorConfigContext } from '@payloadcms/richtext-lexical/client'\nimport { Popup, useField } from '@payloadcms/ui'\nimport React, { useCallback, useMemo, useState } from 'react'\n\nimport { PLUGIN_INSTRUCTIONS_TABLE } from '../../defaults.js'\nimport { useInstructions } from '../../providers/InstructionsProvider/useInstructions.js'\nimport { setSafeLexicalState } from '../../utilities/setSafeLexicalState.js'\nimport { PluginIcon } from '../Icons/Icons.js'\nimport styles from './compose.module.css'\nimport { useMenu } from './hooks/menu/useMenu.js'\nimport { useActiveFieldTracking } from './hooks/useActiveFieldTracking.js'\nimport { useGenerate } from './hooks/useGenerate.js'\nimport { UndoRedoActions } from './UndoRedoActions.js'\n\nexport type ComposeProps = {\n descriptionProps?: {\n field: ClientField\n path: string\n schemaPath: string\n }\n forceVisible?: boolean\n instructionId: string\n isConfigAllowed: boolean\n}\n\nexport const Compose: FC<ComposeProps> = ({ descriptionProps, forceVisible, instructionId, isConfigAllowed }) => {\n const pathFromContext = descriptionProps?.path\n const { editor: lexicalEditor } = useEditorConfigContext()\n \n // Get global openDrawer from context\n const { openDrawer } = useInstructions()\n\n // Initialize global active-field tracking\n useActiveFieldTracking()\n\n const [isProcessing, setIsProcessing] = useState<boolean>(false)\n const { generate, isJobActive, isLoading, jobProgress, jobStatus, stop } = useGenerate({ instructionId })\n\n // Memoize menu event handlers to prevent recreation on every render\n const onCompose = useCallback(() => {\n console.log('Composing...')\n setIsProcessing(true)\n generate({\n action: 'Compose',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n }, [generate])\n\n const onExpand = useCallback(() => {\n console.log('Expanding...')\n generate({\n action: 'Expand',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n }, [generate])\n\n const onProofread = useCallback(() => {\n console.log('Proofreading...')\n generate({\n action: 'Proofread',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n }, [generate])\n\n const onRephrase = useCallback(() => {\n console.log('Rephrasing...')\n generate({\n action: 'Rephrase',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n }, [generate])\n\n const onSimplify = useCallback(() => {\n console.log('Simplifying...')\n generate({\n action: 'Simplify',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n }, [generate])\n\n const onSummarize = useCallback(() => {\n console.log('Summarizing...')\n generate({\n action: 'Summarize',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n }, [generate])\n\n const onTranslate = useCallback((data: unknown) => {\n console.log('Translating...')\n generate({\n action: 'Translate',\n params: data,\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n }, [generate])\n\n const handleOpenSettings = useCallback(() => {\n if (isConfigAllowed) {\n openDrawer(instructionId)\n }\n }, [isConfigAllowed, openDrawer, instructionId])\n\n const { ActiveComponent, Menu } = useMenu(\n {\n onCompose,\n onExpand,\n onProofread,\n onRephrase,\n onSettings: isConfigAllowed ? handleOpenSettings : undefined,\n onSimplify,\n onSummarize,\n onTranslate,\n },\n {\n isConfigAllowed,\n },\n )\n\n const { setValue } = useField<string>({\n path: pathFromContext,\n })\n\n const setIfValueIsLexicalState = useCallback((val: any) => {\n // Prevent setting incomplete states during streaming\n if (!val || typeof val !== 'object' || !('root' in val) || !lexicalEditor) {\n return\n }\n\n // Validate that the state is complete before setting\n // Check for common incomplete streaming states\n if (!val.root || typeof val.root !== 'object' || Object.keys(val.root).length === 0) {\n return\n }\n\n if (val.root.type !== 'root') {\n return\n }\n\n if (!val.root.children || !Array.isArray(val.root.children) || val.root.children.length === 0) {\n return\n }\n\n // Check for invalid child types (common streaming issue)\n const hasInvalidChildren = val.root.children.some(\n (child: any) => !child || !child.type || child.type === 'undefined' || child.type === '',\n )\n\n if (hasInvalidChildren) {\n return\n }\n\n // State looks valid, proceed\n setSafeLexicalState(JSON.stringify(val), lexicalEditor)\n\n // DO NOT PROVIDE lexicalEditor as a dependency, it freaks out and does not update the editor after first undo/redo - revisit\n }, [])\n\n const popupRender = useCallback(\n ({ close }: { close: () => void }) => {\n return <Menu isLoading={isProcessing || isLoading} onClose={close} />\n },\n [isProcessing, isLoading, Menu],\n )\n\n // Combine loading states to reduce re-renders\n const isAnyLoading = isProcessing || isLoading || isJobActive\n\n const memoizedPopup = useMemo(() => {\n return (\n <Popup\n button={<PluginIcon isLoading={isAnyLoading} />}\n render={popupRender}\n verticalAlign=\"bottom\"\n />\n )\n }, [popupRender, isAnyLoading])\n\n return (\n <label\n className={`payloadai-compose__actions ${styles.actions} ${forceVisible ? styles.actionsVisible : ''}`}\n onClick={(e) => e.preventDefault()}\n role=\"presentation\"\n >\n {memoizedPopup}\n <ActiveComponent\n isLoading={isProcessing || isLoading || isJobActive}\n loadingLabel={isJobActive ? (jobStatus === 'running' ? `Video ${Math.max(0, Math.min(100, Math.round(jobProgress ?? 0)))}%` : (jobStatus || 'Queued')) : undefined}\n stop={stop}\n />\n <UndoRedoActions\n onChange={(val) => {\n setValue(val)\n setIfValueIsLexicalState(val)\n }}\n />\n </label>\n )\n}\n"],"names":["useEditorConfigContext","Popup","useField","React","useCallback","useMemo","useState","useInstructions","setSafeLexicalState","PluginIcon","styles","useMenu","useActiveFieldTracking","useGenerate","UndoRedoActions","Compose","descriptionProps","forceVisible","instructionId","isConfigAllowed","pathFromContext","path","editor","lexicalEditor","openDrawer","isProcessing","setIsProcessing","generate","isJobActive","isLoading","jobProgress","jobStatus","stop","onCompose","console","log","action","catch","reason","error","finally","onExpand","onProofread","onRephrase","onSimplify","onSummarize","onTranslate","data","params","handleOpenSettings","ActiveComponent","Menu","onSettings","undefined","setValue","setIfValueIsLexicalState","val","root","Object","keys","length","type","children","Array","isArray","hasInvalidChildren","some","child","JSON","stringify","popupRender","close","onClose","isAnyLoading","memoizedPopup","button","render","verticalAlign","label","className","actions","actionsVisible","onClick","e","preventDefault","role","loadingLabel","Math","max","min","round","onChange"],"mappings":"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"}
@@ -127,9 +127,28 @@ export const Compose = ({ descriptionProps, forceVisible, instructionId, isConfi
127
127
  path: pathFromContext,
128
128
  });
129
129
  const setIfValueIsLexicalState = useCallback((val) => {
130
- if (val && typeof val === 'object' && 'root' in val && lexicalEditor) {
131
- setSafeLexicalState(JSON.stringify(val), lexicalEditor);
130
+ // Prevent setting incomplete states during streaming
131
+ if (!val || typeof val !== 'object' || !('root' in val) || !lexicalEditor) {
132
+ return;
132
133
  }
134
+ // Validate that the state is complete before setting
135
+ // Check for common incomplete streaming states
136
+ if (!val.root || typeof val.root !== 'object' || Object.keys(val.root).length === 0) {
137
+ return;
138
+ }
139
+ if (val.root.type !== 'root') {
140
+ return;
141
+ }
142
+ if (!val.root.children || !Array.isArray(val.root.children) || val.root.children.length === 0) {
143
+ return;
144
+ }
145
+ // Check for invalid child types (common streaming issue)
146
+ const hasInvalidChildren = val.root.children.some((child) => !child || !child.type || child.type === 'undefined' || child.type === '');
147
+ if (hasInvalidChildren) {
148
+ return;
149
+ }
150
+ // State looks valid, proceed
151
+ setSafeLexicalState(JSON.stringify(val), lexicalEditor);
133
152
  // DO NOT PROVIDE lexicalEditor as a dependency, it freaks out and does not update the editor after first undo/redo - revisit
134
153
  }, []);
135
154
  const popupRender = useCallback(({ close }) => {
@@ -1,14 +1,14 @@
1
1
  import { experimental_useObject as useObject } from '@ai-sdk/react';
2
2
  import { useEditorConfigContext } from '@payloadcms/richtext-lexical/client';
3
- import { toast, useConfig, useDocumentInfo, useField, useForm, useLocale } from '@payloadcms/ui';
3
+ import { toast, useDocumentInfo, useField, useForm, useLocale } from '@payloadcms/ui';
4
4
  import { jsonSchema } from 'ai';
5
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
- import { PLUGIN_AI_JOBS_TABLE, PLUGIN_API_ENDPOINT_GENERATE, PLUGIN_API_ENDPOINT_GENERATE_UPLOAD, PLUGIN_INSTRUCTIONS_TABLE, PLUGIN_NAME } from '../../../defaults.js';
5
+ import { useCallback, useEffect, useRef } from 'react';
6
+ import { PLUGIN_API_ENDPOINT_GENERATE } from '../../../defaults.js';
7
7
  import { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js';
8
- import { editorSchemaValidator } from '../../../utilities/editorSchemaValidator.js';
9
- import { fieldToJsonSchema } from '../../../utilities/fieldToJsonSchema.js';
10
8
  import { setSafeLexicalState } from '../../../utilities/setSafeLexicalState.js';
9
+ import { useGenerateUpload } from './useGenerateUpload.js';
11
10
  import { useHistory } from './useHistory.js';
11
+ import { useStreamingUpdate } from './useStreamingUpdate.js';
12
12
  export const useGenerate = ({ instructionId })=>{
13
13
  // Create a ref to hold the current instructionId
14
14
  const instructionIdRef = useRef(instructionId);
@@ -20,66 +20,13 @@ export const useGenerate = ({ instructionId })=>{
20
20
  ]);
21
21
  const { field, path: pathFromContext } = useFieldProps();
22
22
  const editorConfigContext = useEditorConfigContext();
23
- const { editor } = editorConfigContext;
24
- const { config } = useConfig();
25
- const { routes: { api }, serverURL } = config;
26
23
  const { setValue } = useField({
27
24
  path: pathFromContext ?? ''
28
25
  });
29
26
  const { set: setHistory } = useHistory();
30
- // Async job UI state
31
- const [jobStatus, setJobStatus] = useState(undefined);
32
- const [jobProgress, setJobProgress] = useState(0);
33
- const [isJobActive, setIsJobActive] = useState(false);
34
27
  const { getData } = useForm();
35
- const { id: documentId, collectionSlug } = useDocumentInfo();
28
+ const { id: documentId } = useDocumentInfo();
36
29
  const localFromContext = useLocale();
37
- // Reuse config from above instead of calling useConfig again
38
- const { collections } = config;
39
- const collection = collections.find((collection)=>collection.slug === PLUGIN_INSTRUCTIONS_TABLE);
40
- const { custom: { [PLUGIN_NAME]: { editorConfig = {} } = {} } = {} } = collection?.admin ?? {};
41
- const { schema: editorSchema = {} } = editorConfig;
42
- const memoizedValidator = useMemo(()=>{
43
- return editorSchemaValidator(editorSchema);
44
- }, [
45
- editorSchema
46
- ]);
47
- const memoizedSchema = useMemo(()=>jsonSchema(editorSchema, {
48
- validate: (value)=>{
49
- const isValid = memoizedValidator(value);
50
- if (isValid) {
51
- return {
52
- success: true,
53
- value
54
- };
55
- } else {
56
- return {
57
- error: new Error('Invalid schema'),
58
- success: false
59
- };
60
- }
61
- }
62
- }), [
63
- memoizedValidator
64
- ]);
65
- // Active JSON schema for useObject based on field type
66
- const activeSchema = useMemo(()=>{
67
- const f = field;
68
- const fieldType = f?.type;
69
- if (fieldType === 'richText') {
70
- return memoizedSchema;
71
- }
72
- if (f && f.name && fieldType) {
73
- const schemaJson = fieldToJsonSchema(f);
74
- if (schemaJson && Object.keys(schemaJson).length > 0) {
75
- return jsonSchema(schemaJson);
76
- }
77
- }
78
- return undefined;
79
- }, [
80
- field,
81
- memoizedSchema
82
- ]);
83
30
  const { isLoading: loadingObject, object, stop: objectStop, submit } = useObject({
84
31
  api: `/api${PLUGIN_API_ENDPOINT_GENERATE}`,
85
32
  onError: (error)=>{
@@ -90,8 +37,8 @@ export const useGenerate = ({ instructionId })=>{
90
37
  if (result.object && field) {
91
38
  if (field.type === 'richText') {
92
39
  setHistory(result.object);
93
- setValue(result.object);
94
- } else if ('name' in field) {
40
+ setSafeLexicalState(result.object, editor);
41
+ } else if ('name' in field && result.object[field.name]) {
95
42
  setHistory(result.object[field.name]);
96
43
  setValue(result.object[field.name]);
97
44
  }
@@ -99,25 +46,23 @@ export const useGenerate = ({ instructionId })=>{
99
46
  console.log('onFinish: result, field ', result, field);
100
47
  }
101
48
  },
102
- schema: activeSchema
49
+ schema: jsonSchema({
50
+ type: 'object',
51
+ additionalProperties: true,
52
+ properties: {}
53
+ })
103
54
  });
104
- useEffect(()=>{
105
- if (!object) {
106
- return;
107
- }
108
- requestAnimationFrame(()=>{
109
- if (field?.type === 'richText') {
110
- setSafeLexicalState(object, editor);
111
- } else if (field && 'name' in field && object[field.name]) {
112
- setValue(object[field.name]);
113
- }
114
- });
115
- }, [
116
- object,
55
+ const { editor } = editorConfigContext;
56
+ // Hook: Handle high-frequency streaming updates
57
+ useStreamingUpdate({
117
58
  editor,
118
- field,
119
- setValue
120
- ]);
59
+ isLoading: loadingObject,
60
+ object
61
+ });
62
+ // Hook 2: Handle Upload generation and polling
63
+ const { generateUpload, isJobActive, jobProgress, jobStatus } = useGenerateUpload({
64
+ instructionIdRef
65
+ });
121
66
  const streamObject = useCallback(({ action = 'Compose', params })=>{
122
67
  const doc = getData();
123
68
  const currentInstructionId = instructionIdRef.current;
@@ -138,103 +83,10 @@ export const useGenerate = ({ instructionId })=>{
138
83
  }, [
139
84
  localFromContext?.code,
140
85
  instructionIdRef,
141
- documentId
142
- ]);
143
- const generateUpload = useCallback(async ()=>{
144
- const doc = getData();
145
- const currentInstructionId = instructionIdRef.current;
146
- return fetch(`${serverURL}${api}${PLUGIN_API_ENDPOINT_GENERATE_UPLOAD}`, {
147
- body: JSON.stringify({
148
- collectionSlug: collectionSlug ?? '',
149
- doc,
150
- documentId,
151
- locale: localFromContext?.code,
152
- options: {
153
- instructionId: currentInstructionId
154
- }
155
- }),
156
- credentials: 'include',
157
- headers: {
158
- 'Content-Type': 'application/json'
159
- },
160
- method: 'POST'
161
- }).then(async (uploadResponse)=>{
162
- if (uploadResponse.ok) {
163
- const json = await uploadResponse.json();
164
- const { job, result } = json || {};
165
- if (result) {
166
- // Set the upload ID
167
- setValue(result?.id);
168
- setHistory(result?.id);
169
- // Show toast to prompt user to save
170
- toast.success('Image generated successfully! Click Save to see the preview.');
171
- return uploadResponse;
172
- }
173
- // Async job: poll AI Jobs collection for status/progress/result_id
174
- if (job && job.id) {
175
- setIsJobActive(true);
176
- const cancelled = false;
177
- let attempts = 0;
178
- const maxAttempts = 600 // up to ~10 minutes @ 1s
179
- ;
180
- // Basic in-hook state via closure variables; UI will re-render off fetches below
181
- const poll = async ()=>{
182
- if (cancelled) {
183
- return;
184
- }
185
- try {
186
- const res = await fetch(`${serverURL}${api}/${PLUGIN_AI_JOBS_TABLE}/${job.id}`, {
187
- credentials: 'include'
188
- });
189
- if (res.ok) {
190
- const jobDoc = await res.json();
191
- const { progress, result_id, status } = jobDoc || {};
192
- setJobStatus(status);
193
- setJobProgress(progress ?? 0);
194
- // When result present, set field and finish
195
- if (status === 'completed' && result_id) {
196
- // Force upload field to refetch by clearing then setting the ID
197
- setValue(null);
198
- setTimeout(()=>{
199
- setValue(result_id);
200
- }, 0);
201
- setHistory(result_id);
202
- setIsJobActive(false);
203
- return;
204
- }
205
- if (status === 'failed') {
206
- setIsJobActive(false);
207
- throw new Error('Video generation failed');
208
- }
209
- }
210
- } catch (e) {
211
- // silent retry
212
- }
213
- attempts += 1;
214
- if (!cancelled && attempts < maxAttempts) {
215
- setTimeout(poll, 1000);
216
- }
217
- };
218
- setTimeout(poll, 1000);
219
- return uploadResponse;
220
- }
221
- throw new Error('generateUpload: Unexpected response');
222
- } else {
223
- const { errors = [] } = await uploadResponse.json();
224
- const errStr = errors.map((error)=>error.message).join(', ');
225
- throw new Error(errStr);
226
- }
227
- }).catch((error)=>{
228
- toast.error(`Failed to generate: ${error.message}`);
229
- console.error('Error generating or setting your upload, please set it manually if its saved in your media files.', error);
230
- });
231
- }, [
232
- getData,
233
- localFromContext?.code,
234
- instructionIdRef,
235
- setValue,
236
86
  documentId,
237
- collectionSlug
87
+ getData,
88
+ submit,
89
+ editor
238
90
  ]);
239
91
  const generate = useCallback(async (options)=>{
240
92
  if (field?.type === 'upload') {