@ai-stack/payloadcms 3.68.0-beta.3 → 3.68.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/core/streamObject.js +3 -0
- package/dist/ai/core/streamObject.js.map +1 -1
- package/dist/ai/core/types.d.ts +3 -0
- package/dist/ai/core/types.js.map +1 -1
- package/dist/ai/prompts.d.ts +1 -2
- package/dist/ai/prompts.js +0 -110
- package/dist/ai/prompts.js.map +1 -1
- package/dist/ai/providers/registry.js +0 -1
- package/dist/ai/providers/registry.js.map +1 -1
- package/dist/ai/utils/filterEditorSchemaByNodes.d.ts +9 -0
- package/dist/ai/utils/filterEditorSchemaByNodes.js +30 -3
- package/dist/ai/utils/filterEditorSchemaByNodes.js.map +1 -1
- package/dist/ai/utils/nodeToSchemaMap.d.ts +22 -0
- package/dist/ai/utils/nodeToSchemaMap.js +72 -0
- package/dist/ai/utils/nodeToSchemaMap.js.map +1 -0
- package/dist/collections/AIJobs.js +1 -1
- package/dist/collections/AIJobs.js.map +1 -1
- package/dist/collections/AISettings.js +3 -3
- package/dist/collections/AISettings.js.map +1 -1
- package/dist/endpoints/fetchVoices.js +41 -24
- package/dist/endpoints/fetchVoices.js.map +1 -1
- package/dist/endpoints/index.js +108 -6
- package/dist/endpoints/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/payload-ai.d.ts +7 -4
- package/dist/plugin.js +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/types.d.ts +34 -2
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/ui/Compose/Compose.js +23 -6
- package/dist/ui/Compose/Compose.js.map +1 -1
- package/dist/ui/Compose/Compose.jsx +23 -4
- package/dist/ui/Compose/hooks/useGenerate.js +21 -72
- package/dist/ui/Compose/hooks/useGenerate.js.map +1 -1
- package/dist/ui/Compose/hooks/useGenerateUpload.d.ts +3 -3
- package/dist/ui/Compose/hooks/useGenerateUpload.js +37 -10
- package/dist/ui/Compose/hooks/useGenerateUpload.js.map +1 -1
- package/dist/ui/Compose/hooks/useStreamingUpdate.js.map +1 -1
- package/dist/ui/VoicesFetcher/index.js +33 -61
- package/dist/ui/VoicesFetcher/index.js.map +1 -1
- package/dist/ui/VoicesFetcher/index.jsx +31 -37
- package/dist/utilities/buildSmartPrompt.js +4 -6
- package/dist/utilities/buildSmartPrompt.js.map +1 -1
- package/dist/utilities/encryption.js +2 -1
- package/dist/utilities/encryption.js.map +1 -1
- package/dist/utilities/seedProperties.js +7 -24
- package/dist/utilities/seedProperties.js.map +1 -1
- package/dist/utilities/setSafeLexicalState.js +1 -2
- package/dist/utilities/setSafeLexicalState.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import { toast, useConfig, useDocumentInfo, useForm, useLocale } from '@payloadcms/ui';
|
|
1
|
+
import { toast, useConfig, useDocumentInfo, useField, useForm, useLocale } from '@payloadcms/ui';
|
|
2
2
|
import { useCallback, useState } from 'react';
|
|
3
3
|
import { PLUGIN_AI_JOBS_TABLE, PLUGIN_API_ENDPOINT_GENERATE_UPLOAD } from '../../../defaults.js';
|
|
4
|
+
import { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js';
|
|
4
5
|
import { useHistory } from './useHistory.js';
|
|
5
|
-
export const useGenerateUpload = ({ instructionIdRef
|
|
6
|
+
export const useGenerateUpload = ({ instructionIdRef })=>{
|
|
6
7
|
const { config } = useConfig();
|
|
7
8
|
const { routes: { api }, serverURL } = config;
|
|
8
9
|
const { id: documentId, collectionSlug } = useDocumentInfo();
|
|
9
10
|
const localFromContext = useLocale();
|
|
10
11
|
const { getData } = useForm();
|
|
11
12
|
const { set: setHistory } = useHistory();
|
|
13
|
+
const { field, path: pathFromContext } = useFieldProps();
|
|
14
|
+
const { setValue } = useField({
|
|
15
|
+
path: pathFromContext ?? ''
|
|
16
|
+
});
|
|
12
17
|
// Async job UI state
|
|
13
18
|
const [jobStatus, setJobStatus] = useState(undefined);
|
|
14
19
|
const [jobProgress, setJobProgress] = useState(0);
|
|
@@ -36,7 +41,6 @@ export const useGenerateUpload = ({ instructionIdRef, setValue })=>{
|
|
|
36
41
|
const json = await uploadResponse.json();
|
|
37
42
|
const { job, result } = json || {};
|
|
38
43
|
if (result) {
|
|
39
|
-
// Set the upload ID
|
|
40
44
|
setValue(result?.id);
|
|
41
45
|
setHistory(result?.id);
|
|
42
46
|
// Show toast to prompt user to save
|
|
@@ -66,11 +70,34 @@ export const useGenerateUpload = ({ instructionIdRef, setValue })=>{
|
|
|
66
70
|
setJobProgress(progress ?? 0);
|
|
67
71
|
// When result present, set field and finish
|
|
68
72
|
if (status === 'completed' && result_id) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
let valueToSet = result_id;
|
|
74
|
+
// Attempt to fetch full document for immediate preview
|
|
75
|
+
if (field && 'relationTo' in field && typeof field.relationTo === 'string') {
|
|
76
|
+
let attempts = 0;
|
|
77
|
+
const maxAttempts = 3;
|
|
78
|
+
while(attempts < maxAttempts){
|
|
79
|
+
try {
|
|
80
|
+
const docRes = await fetch(`${serverURL}${api}/${field.relationTo}/${result_id}`, {
|
|
81
|
+
credentials: 'include'
|
|
82
|
+
});
|
|
83
|
+
if (docRes.ok) {
|
|
84
|
+
const doc = await docRes.json();
|
|
85
|
+
// Verify we have a URL for preview
|
|
86
|
+
if (doc && doc.url) {
|
|
87
|
+
valueToSet = doc;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch (e) {
|
|
92
|
+
console.error('Failed to fetch generated document for preview:', e);
|
|
93
|
+
}
|
|
94
|
+
attempts++;
|
|
95
|
+
if (attempts < maxAttempts) {
|
|
96
|
+
await new Promise((resolve)=>setTimeout(resolve, 500));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
setValue(valueToSet);
|
|
74
101
|
setHistory(result_id);
|
|
75
102
|
setIsJobActive(false);
|
|
76
103
|
return;
|
|
@@ -80,7 +107,7 @@ export const useGenerateUpload = ({ instructionIdRef, setValue })=>{
|
|
|
80
107
|
throw new Error('Video generation failed');
|
|
81
108
|
}
|
|
82
109
|
}
|
|
83
|
-
} catch (
|
|
110
|
+
} catch (_) {
|
|
84
111
|
// silent retry
|
|
85
112
|
}
|
|
86
113
|
attempts += 1;
|
|
@@ -105,7 +132,7 @@ export const useGenerateUpload = ({ instructionIdRef, setValue })=>{
|
|
|
105
132
|
getData,
|
|
106
133
|
localFromContext?.code,
|
|
107
134
|
instructionIdRef,
|
|
108
|
-
setValue,
|
|
135
|
+
// setValue,
|
|
109
136
|
documentId,
|
|
110
137
|
collectionSlug,
|
|
111
138
|
serverURL,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/ui/Compose/hooks/useGenerateUpload.ts"],"sourcesContent":["import { toast, useConfig, useDocumentInfo, useForm, useLocale } from '@payloadcms/ui'\nimport { useCallback, useState } from 'react'\n\nimport type { GenerateTextarea } from '../../../types.js'\n\nimport { PLUGIN_AI_JOBS_TABLE, PLUGIN_API_ENDPOINT_GENERATE_UPLOAD } from '../../../defaults.js'\nimport { useHistory } from './useHistory.js'\n\ntype UseGenerateUploadParams = {\n instructionIdRef: React.MutableRefObject<string>\n setValue: (value: any) => void\n}\n\nexport const useGenerateUpload = ({ instructionIdRef, setValue }: UseGenerateUploadParams) => {\n const { config } = useConfig()\n const {\n routes: { api },\n serverURL,\n } = config\n const { id: documentId, collectionSlug } = useDocumentInfo()\n const localFromContext = useLocale()\n const { getData } = useForm()\n const { set: setHistory } = useHistory()\n\n // Async job UI state\n const [jobStatus, setJobStatus] = useState<string | undefined>(undefined)\n const [jobProgress, setJobProgress] = useState<number>(0)\n const [isJobActive, setIsJobActive] = useState<boolean>(false)\n\n const generateUpload = useCallback(async () => {\n const doc = getData()\n const currentInstructionId = instructionIdRef.current\n\n return fetch(`${serverURL}${api}${PLUGIN_API_ENDPOINT_GENERATE_UPLOAD}`, {\n body: JSON.stringify({\n collectionSlug: collectionSlug ?? '',\n doc,\n documentId,\n locale: localFromContext?.code,\n options: {\n instructionId: currentInstructionId,\n },\n } satisfies Parameters<GenerateTextarea>[0]),\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n .then(async (uploadResponse) => {\n if (uploadResponse.ok) {\n const json = await uploadResponse.json()\n const { job, result } = json || {}\n if (result) {\n // 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(`${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 }, [\n getData,\n localFromContext?.code,\n instructionIdRef,\n setValue,\n documentId,\n collectionSlug,\n serverURL,\n api,\n setHistory,\n ])\n\n return {\n generateUpload,\n isJobActive,\n jobProgress,\n jobStatus,\n }\n}\n"],"names":["toast","useConfig","useDocumentInfo","useForm","useLocale","useCallback","useState","PLUGIN_AI_JOBS_TABLE","PLUGIN_API_ENDPOINT_GENERATE_UPLOAD","useHistory","useGenerateUpload","instructionIdRef","setValue","config","routes","api","serverURL","id","documentId","collectionSlug","localFromContext","getData","set","setHistory","jobStatus","setJobStatus","undefined","jobProgress","setJobProgress","isJobActive","setIsJobActive","generateUpload","doc","currentInstructionId","current","fetch","body","JSON","stringify","locale","code","options","instructionId","credentials","headers","method","then","uploadResponse","ok","json","job","result","success","cancelled","attempts","maxAttempts","poll","res","jobDoc","progress","result_id","status","setTimeout","Error","e","errors","errStr","map","error","message","join","catch","console"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/ui/Compose/hooks/useGenerateUpload.ts"],"sourcesContent":["import { toast, useConfig, useDocumentInfo, useField, useForm, useLocale } from '@payloadcms/ui'\nimport { type RefObject, useCallback, useState } from 'react'\n\nimport type { GenerateTextarea } from '../../../types.js'\n\nimport { PLUGIN_AI_JOBS_TABLE, PLUGIN_API_ENDPOINT_GENERATE_UPLOAD } from '../../../defaults.js'\nimport { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js'\nimport { useHistory } from './useHistory.js'\n\ntype UseGenerateUploadParams = {\n instructionIdRef: RefObject<string>\n}\n\nexport const useGenerateUpload = ({ instructionIdRef }: UseGenerateUploadParams) => {\n const { config } = useConfig()\n const {\n routes: { api },\n serverURL,\n } = config\n const { id: documentId, collectionSlug } = useDocumentInfo()\n const localFromContext = useLocale()\n const { getData } = useForm()\n const { set: setHistory } = useHistory()\n\n const { field, path: pathFromContext } = useFieldProps()\n const { setValue } = useField<any>({\n path: pathFromContext ?? '',\n })\n\n // Async job UI state\n const [jobStatus, setJobStatus] = useState<string | undefined>(undefined)\n const [jobProgress, setJobProgress] = useState<number>(0)\n const [isJobActive, setIsJobActive] = useState<boolean>(false)\n\n const generateUpload = useCallback(async () => {\n const doc = getData()\n const currentInstructionId = instructionIdRef.current\n\n return fetch(`${serverURL}${api}${PLUGIN_API_ENDPOINT_GENERATE_UPLOAD}`, {\n body: JSON.stringify({\n collectionSlug: collectionSlug ?? '',\n doc,\n documentId,\n locale: localFromContext?.code,\n options: {\n instructionId: currentInstructionId,\n },\n } satisfies Parameters<GenerateTextarea>[0]),\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n .then(async (uploadResponse) => {\n if (uploadResponse.ok) {\n const json = await uploadResponse.json()\n const { job, result } = json || {}\n if (result) {\n setValue(result?.id)\n setHistory(result?.id)\n\n // Show toast to prompt user to save\n toast.success('Image generated successfully! Click Save to see the preview.')\n\n return uploadResponse\n }\n\n // Async job: poll AI Jobs collection for status/progress/result_id\n if (job && job.id) {\n setIsJobActive(true)\n const cancelled = false\n let attempts = 0\n const maxAttempts = 600 // up to ~10 minutes @ 1s\n\n // Basic in-hook state via closure variables; UI will re-render off fetches below\n const poll = async (): Promise<void> => {\n if (cancelled) {\n return\n }\n try {\n const res = await fetch(`${serverURL}${api}/${PLUGIN_AI_JOBS_TABLE}/${job.id}`, {\n credentials: 'include',\n })\n if (res.ok) {\n const jobDoc = await res.json()\n const { progress, result_id, status } = jobDoc || {}\n setJobStatus(status)\n setJobProgress(progress ?? 0)\n // When result present, set field and finish\n if (status === 'completed' && result_id) {\n let valueToSet = result_id\n\n // Attempt to fetch full document for immediate preview\n if (field && 'relationTo' in field && typeof field.relationTo === 'string') {\n let attempts = 0\n const maxAttempts = 3\n while (attempts < maxAttempts) {\n try {\n const docRes = await fetch(\n `${serverURL}${api}/${field.relationTo}/${result_id}`,\n {\n credentials: 'include',\n },\n )\n if (docRes.ok) {\n const doc = await docRes.json()\n // Verify we have a URL for preview\n if (doc && doc.url) {\n valueToSet = doc\n break\n }\n }\n } catch (e) {\n console.error('Failed to fetch generated document for preview:', e)\n }\n attempts++\n if (attempts < maxAttempts) {\n await new Promise((resolve) => setTimeout(resolve, 500))\n }\n }\n }\n\n setValue(valueToSet)\n setHistory(result_id)\n setIsJobActive(false)\n return\n }\n if (status === 'failed') {\n setIsJobActive(false)\n throw new Error('Video generation failed')\n }\n }\n } catch (_) {\n // silent retry\n }\n\n attempts += 1\n if (!cancelled && attempts < maxAttempts) {\n setTimeout(poll, 1000)\n }\n }\n setTimeout(poll, 1000)\n return uploadResponse\n }\n\n throw new Error('generateUpload: Unexpected response')\n } else {\n const { errors = [] } = await uploadResponse.json()\n const errStr = errors.map((error: any) => error.message).join(', ')\n throw new Error(errStr)\n }\n })\n .catch((error) => {\n toast.error(`Failed to generate: ${error.message}`)\n console.error(\n 'Error generating or setting your upload, please set it manually if its saved in your media files.',\n error,\n )\n })\n }, [\n getData,\n localFromContext?.code,\n instructionIdRef,\n // setValue,\n documentId,\n collectionSlug,\n serverURL,\n api,\n setHistory,\n ])\n\n return {\n generateUpload,\n isJobActive,\n jobProgress,\n jobStatus,\n }\n}\n"],"names":["toast","useConfig","useDocumentInfo","useField","useForm","useLocale","useCallback","useState","PLUGIN_AI_JOBS_TABLE","PLUGIN_API_ENDPOINT_GENERATE_UPLOAD","useFieldProps","useHistory","useGenerateUpload","instructionIdRef","config","routes","api","serverURL","id","documentId","collectionSlug","localFromContext","getData","set","setHistory","field","path","pathFromContext","setValue","jobStatus","setJobStatus","undefined","jobProgress","setJobProgress","isJobActive","setIsJobActive","generateUpload","doc","currentInstructionId","current","fetch","body","JSON","stringify","locale","code","options","instructionId","credentials","headers","method","then","uploadResponse","ok","json","job","result","success","cancelled","attempts","maxAttempts","poll","res","jobDoc","progress","result_id","status","valueToSet","relationTo","docRes","url","e","console","error","Promise","resolve","setTimeout","Error","_","errors","errStr","map","message","join","catch"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/ui/Compose/hooks/useStreamingUpdate.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../../../../src/ui/Compose/hooks/useStreamingUpdate.ts"],"sourcesContent":["import type { LexicalEditor } from 'lexical'\n\nimport { useForm } from '@payloadcms/ui'\nimport { useEffect, useRef } from 'react'\n\nimport { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js'\nimport { setSafeLexicalState } from '../../../utilities/setSafeLexicalState.js'\n\ntype UseStreamingUpdateParams = {\n editor: LexicalEditor\n isLoading: boolean\n object: any\n}\n\nexport const useStreamingUpdate = ({ editor, isLoading, object }: UseStreamingUpdateParams) => {\n const { field, path: pathFromContext } = useFieldProps()\n const { dispatchFields } = useForm()\n\n // Ref for latest object to avoid effect re-runs during high-frequency streaming\n const objectRef = useRef(object)\n objectRef.current = object\n\n useEffect(() => {\n // Only run the animation loop while loading (streaming)\n if (!isLoading) {\n return\n }\n\n let reqId: number\n\n const loop = () => {\n const currentObject = objectRef.current\n\n if (currentObject) {\n if (field?.type === 'richText') {\n setSafeLexicalState(currentObject, editor)\n } else if (field && 'name' in field && currentObject[field.name]) {\n // Use dispatchFields for high-frequency streaming updates to avoid re-renders\n dispatchFields({\n type: 'UPDATE',\n path: pathFromContext ?? '',\n value: currentObject[field.name],\n } as any)\n }\n }\n\n // Continue loop\n reqId = requestAnimationFrame(loop)\n }\n\n // Start loop\n loop()\n\n return () => {\n cancelAnimationFrame(reqId)\n }\n }, [isLoading, editor, field, dispatchFields, pathFromContext])\n}\n"],"names":["useForm","useEffect","useRef","useFieldProps","setSafeLexicalState","useStreamingUpdate","editor","isLoading","object","field","path","pathFromContext","dispatchFields","objectRef","current","reqId","loop","currentObject","type","name","value","requestAnimationFrame","cancelAnimationFrame"],"mappings":"AAEA,SAASA,OAAO,QAAQ,iBAAgB;AACxC,SAASC,SAAS,EAAEC,MAAM,QAAQ,QAAO;AAEzC,SAASC,aAAa,QAAQ,oDAAmD;AACjF,SAASC,mBAAmB,QAAQ,4CAA2C;AAQ/E,OAAO,MAAMC,qBAAqB,CAAC,EAAEC,MAAM,EAAEC,SAAS,EAAEC,MAAM,EAA4B;IACxF,MAAM,EAAEC,KAAK,EAAEC,MAAMC,eAAe,EAAE,GAAGR;IACzC,MAAM,EAAES,cAAc,EAAE,GAAGZ;IAE3B,gFAAgF;IAChF,MAAMa,YAAYX,OAAOM;IACzBK,UAAUC,OAAO,GAAGN;IAEpBP,UAAU;QACR,wDAAwD;QACxD,IAAI,CAACM,WAAW;YACd;QACF;QAEA,IAAIQ;QAEJ,MAAMC,OAAO;YACX,MAAMC,gBAAgBJ,UAAUC,OAAO;YAEvC,IAAIG,eAAe;gBACjB,IAAIR,OAAOS,SAAS,YAAY;oBAC9Bd,oBAAoBa,eAAeX;gBACrC,OAAO,IAAIG,SAAS,UAAUA,SAASQ,aAAa,CAACR,MAAMU,IAAI,CAAC,EAAE;oBAChE,8EAA8E;oBAC9EP,eAAe;wBACbM,MAAM;wBACNR,MAAMC,mBAAmB;wBACzBS,OAAOH,aAAa,CAACR,MAAMU,IAAI,CAAC;oBAClC;gBACF;YACF;YAEA,gBAAgB;YAChBJ,QAAQM,sBAAsBL;QAChC;QAEA,aAAa;QACbA;QAEA,OAAO;YACLM,qBAAqBP;QACvB;IACF,GAAG;QAACR;QAAWD;QAAQG;QAAOG;QAAgBD;KAAgB;AAChE,EAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Button,
|
|
3
|
+
import { Button, toast, useField } from '@payloadcms/ui';
|
|
4
4
|
import React, { useCallback, useState } from 'react';
|
|
5
5
|
import { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js';
|
|
6
6
|
/**
|
|
@@ -9,87 +9,58 @@ import { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js';
|
|
|
9
9
|
* SECURE: API key is never exposed to the client
|
|
10
10
|
*/ export const VoicesFetcher = ({ path })=>{
|
|
11
11
|
const [loading, setLoading] = useState(false);
|
|
12
|
-
const { dispatchFields } = useForm();
|
|
13
12
|
// Get the parent path (the block path)
|
|
14
13
|
const fieldPath = path || '';
|
|
15
14
|
const blockPath = fieldPath.split('.').slice(0, -1).join('.');
|
|
16
15
|
const voicesPath = `${blockPath}.voices`;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
const { setValue } = useField({
|
|
17
|
+
path: voicesPath
|
|
18
|
+
});
|
|
20
19
|
const fetchVoices = useCallback(async ()=>{
|
|
21
20
|
setLoading(true);
|
|
21
|
+
const controller = new AbortController();
|
|
22
|
+
const timeoutId = setTimeout(()=>controller.abort(), 30000) // 30s timeout
|
|
23
|
+
;
|
|
22
24
|
try {
|
|
23
25
|
// Call server endpoint - it will read the API key from the database
|
|
24
26
|
const response = await fetch(`/api${PLUGIN_API_ENDPOINT_FETCH_VOICES}`, {
|
|
25
27
|
headers: {
|
|
26
28
|
'Content-Type': 'application/json'
|
|
27
29
|
},
|
|
28
|
-
method: 'POST'
|
|
30
|
+
method: 'POST',
|
|
31
|
+
signal: controller.signal
|
|
29
32
|
});
|
|
33
|
+
clearTimeout(timeoutId);
|
|
30
34
|
if (!response.ok) {
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
let errorMessage = 'Failed to fetch voices';
|
|
36
|
+
try {
|
|
37
|
+
const error = await response.json();
|
|
38
|
+
errorMessage = error.message || errorMessage;
|
|
39
|
+
} catch (e) {
|
|
40
|
+
// If response is not JSON (e.g. 504 Gateway Timeout HTML), use status text
|
|
41
|
+
errorMessage = `Error ${response.status}: ${response.statusText}`;
|
|
42
|
+
}
|
|
43
|
+
throw new Error(errorMessage);
|
|
33
44
|
}
|
|
34
45
|
const data = await response.json();
|
|
35
46
|
const voices = data.voices || [];
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
path: voicesPath,
|
|
41
|
-
rowIndex: i
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
// Add new rows for each voice using ADD_ROW action
|
|
45
|
-
for (const voice of voices){
|
|
46
|
-
dispatchFields({
|
|
47
|
-
type: 'ADD_ROW',
|
|
48
|
-
path: voicesPath,
|
|
49
|
-
subFieldState: {
|
|
50
|
-
id: {
|
|
51
|
-
initialValue: voice.id,
|
|
52
|
-
valid: true,
|
|
53
|
-
value: voice.id
|
|
54
|
-
},
|
|
55
|
-
name: {
|
|
56
|
-
initialValue: voice.name,
|
|
57
|
-
valid: true,
|
|
58
|
-
value: voice.name
|
|
59
|
-
},
|
|
60
|
-
category: {
|
|
61
|
-
initialValue: voice.category || 'premade',
|
|
62
|
-
valid: true,
|
|
63
|
-
value: voice.category || 'premade'
|
|
64
|
-
},
|
|
65
|
-
enabled: {
|
|
66
|
-
initialValue: voice.enabled !== false,
|
|
67
|
-
valid: true,
|
|
68
|
-
value: voice.enabled !== false
|
|
69
|
-
},
|
|
70
|
-
labels: {
|
|
71
|
-
initialValue: voice.labels || {},
|
|
72
|
-
valid: true,
|
|
73
|
-
value: voice.labels || {}
|
|
74
|
-
},
|
|
75
|
-
preview_url: {
|
|
76
|
-
initialValue: voice.preview_url || '',
|
|
77
|
-
valid: true,
|
|
78
|
-
value: voice.preview_url || ''
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
alert(`Successfully fetched ${voices.length} voices!`);
|
|
47
|
+
// Replace the entire array value at once
|
|
48
|
+
// This is much more performant than dispatching ADD_ROW actions in a loop
|
|
49
|
+
setValue(voices);
|
|
50
|
+
toast.success(`Successfully fetched ${voices.length} voices!`);
|
|
84
51
|
} catch (error) {
|
|
85
|
-
|
|
52
|
+
const msg = error instanceof Error ? error.message : 'Failed to fetch voices';
|
|
53
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
54
|
+
toast.error('Request timed out. Please try again.');
|
|
55
|
+
} else {
|
|
56
|
+
toast.error(`Error: ${msg}`);
|
|
57
|
+
}
|
|
86
58
|
} finally{
|
|
87
59
|
setLoading(false);
|
|
60
|
+
clearTimeout(timeoutId);
|
|
88
61
|
}
|
|
89
62
|
}, [
|
|
90
|
-
|
|
91
|
-
dispatchFields,
|
|
92
|
-
voicesPath
|
|
63
|
+
setValue
|
|
93
64
|
]);
|
|
94
65
|
return /*#__PURE__*/ _jsxs("div", {
|
|
95
66
|
style: {
|
|
@@ -99,9 +70,10 @@ import { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js';
|
|
|
99
70
|
/*#__PURE__*/ _jsx(Button, {
|
|
100
71
|
buttonStyle: "secondary",
|
|
101
72
|
disabled: loading,
|
|
73
|
+
margin: false,
|
|
102
74
|
onClick: fetchVoices,
|
|
103
75
|
size: "medium",
|
|
104
|
-
children: loading ? 'Fetching Voices...' : 'Fetch Voices
|
|
76
|
+
children: loading ? 'Fetching Voices...' : 'Fetch Voices'
|
|
105
77
|
}),
|
|
106
78
|
/*#__PURE__*/ _jsx("p", {
|
|
107
79
|
style: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/VoicesFetcher/index.tsx"],"sourcesContent":["'use client'\n\nimport type { FieldClientComponent } from 'payload'\n\nimport { Button,
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/VoicesFetcher/index.tsx"],"sourcesContent":["'use client'\n\nimport type { FieldClientComponent } from 'payload'\n\nimport { Button, toast, useField, useFormFields } from '@payloadcms/ui'\nimport React, { useCallback, useState } from 'react'\n\nimport { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js'\n\ninterface Voice {\n category?: string\n enabled?: boolean\n id: string\n labels?: Record<string, unknown>\n name: string\n preview_url?: string\n}\n\n/**\n * VoicesFetcher Component\n * Fetches voices from ElevenLabs API (server-side) and populates the voices array field\n * SECURE: API key is never exposed to the client\n */\nexport const VoicesFetcher: FieldClientComponent = ({ path }) => {\n const [loading, setLoading] = useState(false)\n\n // Get the parent path (the block path)\n const fieldPath = (path as string) || ''\n const blockPath = fieldPath.split('.').slice(0, -1).join('.')\n const voicesPath = `${blockPath}.voices`\n\n const { setValue } = useField<Voice[]>({ path: voicesPath })\n\n const fetchVoices = useCallback(async () => {\n setLoading(true)\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), 30000) // 30s timeout\n\n try {\n // Call server endpoint - it will read the API key from the database\n const response = await fetch(`/api${PLUGIN_API_ENDPOINT_FETCH_VOICES}`, {\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n signal: controller.signal,\n })\n\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n let errorMessage = 'Failed to fetch voices'\n try {\n const error = await response.json()\n errorMessage = error.message || errorMessage\n } catch (e) {\n // If response is not JSON (e.g. 504 Gateway Timeout HTML), use status text\n errorMessage = `Error ${response.status}: ${response.statusText}`\n }\n throw new Error(errorMessage)\n }\n\n const data = await response.json()\n const voices: Voice[] = data.voices || []\n\n // Replace the entire array value at once\n // This is much more performant than dispatching ADD_ROW actions in a loop\n setValue(voices)\n\n toast.success(`Successfully fetched ${voices.length} voices!`)\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : 'Failed to fetch voices'\n if (error instanceof Error && error.name === 'AbortError') {\n toast.error('Request timed out. Please try again.')\n } else {\n toast.error(`Error: ${msg}`)\n }\n } finally {\n setLoading(false)\n clearTimeout(timeoutId)\n }\n }, [setValue])\n\n return (\n <div style={{ marginBottom: '20px' }}>\n <Button buttonStyle=\"secondary\" disabled={loading} margin={false} onClick={fetchVoices} size=\"medium\">\n {loading ? 'Fetching Voices...' : 'Fetch Voices'}\n </Button>\n <p style={{ color: 'var(--theme-elevation-600)', fontSize: '13px', marginTop: '8px' }}>\n This will fetch all available voices from your ElevenLabs account. Make sure you have saved\n your API key in the Setup tab first.\n </p>\n </div>\n )\n}\n"],"names":["Button","toast","useField","React","useCallback","useState","PLUGIN_API_ENDPOINT_FETCH_VOICES","VoicesFetcher","path","loading","setLoading","fieldPath","blockPath","split","slice","join","voicesPath","setValue","fetchVoices","controller","AbortController","timeoutId","setTimeout","abort","response","fetch","headers","method","signal","clearTimeout","ok","errorMessage","error","json","message","e","status","statusText","Error","data","voices","success","length","msg","name","div","style","marginBottom","buttonStyle","disabled","margin","onClick","size","p","color","fontSize","marginTop"],"mappings":"AAAA;;AAIA,SAASA,MAAM,EAAEC,KAAK,EAAEC,QAAQ,QAAuB,iBAAgB;AACvE,OAAOC,SAASC,WAAW,EAAEC,QAAQ,QAAQ,QAAO;AAEpD,SAASC,gCAAgC,QAAQ,oBAAmB;AAWpE;;;;CAIC,GACD,OAAO,MAAMC,gBAAsC,CAAC,EAAEC,IAAI,EAAE;IAC1D,MAAM,CAACC,SAASC,WAAW,GAAGL,SAAS;IAEvC,uCAAuC;IACvC,MAAMM,YAAY,AAACH,QAAmB;IACtC,MAAMI,YAAYD,UAAUE,KAAK,CAAC,KAAKC,KAAK,CAAC,GAAG,CAAC,GAAGC,IAAI,CAAC;IACzD,MAAMC,aAAa,CAAC,EAAEJ,UAAU,OAAO,CAAC;IAExC,MAAM,EAAEK,QAAQ,EAAE,GAAGf,SAAkB;QAAEM,MAAMQ;IAAW;IAE1D,MAAME,cAAcd,YAAY;QAC9BM,WAAW;QAEX,MAAMS,aAAa,IAAIC;QACvB,MAAMC,YAAYC,WAAW,IAAMH,WAAWI,KAAK,IAAI,OAAO,cAAc;;QAE5E,IAAI;YACF,oEAAoE;YACpE,MAAMC,WAAW,MAAMC,MAAM,CAAC,IAAI,EAAEnB,iCAAiC,CAAC,EAAE;gBACtEoB,SAAS;oBACP,gBAAgB;gBAClB;gBACAC,QAAQ;gBACRC,QAAQT,WAAWS,MAAM;YAC3B;YAEAC,aAAaR;YAEb,IAAI,CAACG,SAASM,EAAE,EAAE;gBAChB,IAAIC,eAAe;gBACnB,IAAI;oBACF,MAAMC,QAAQ,MAAMR,SAASS,IAAI;oBACjCF,eAAeC,MAAME,OAAO,IAAIH;gBAClC,EAAE,OAAOI,GAAG;oBACV,2EAA2E;oBAC3EJ,eAAe,CAAC,MAAM,EAAEP,SAASY,MAAM,CAAC,EAAE,EAAEZ,SAASa,UAAU,CAAC,CAAC;gBACnE;gBACA,MAAM,IAAIC,MAAMP;YAClB;YAEA,MAAMQ,OAAO,MAAMf,SAASS,IAAI;YAChC,MAAMO,SAAkBD,KAAKC,MAAM,IAAI,EAAE;YAEzC,yCAAyC;YACzC,0EAA0E;YAC1EvB,SAASuB;YAETvC,MAAMwC,OAAO,CAAC,CAAC,qBAAqB,EAAED,OAAOE,MAAM,CAAC,QAAQ,CAAC;QAC/D,EAAE,OAAOV,OAAgB;YACvB,MAAMW,MAAMX,iBAAiBM,QAAQN,MAAME,OAAO,GAAG;YACrD,IAAIF,iBAAiBM,SAASN,MAAMY,IAAI,KAAK,cAAc;gBACzD3C,MAAM+B,KAAK,CAAC;YACd,OAAO;gBACL/B,MAAM+B,KAAK,CAAC,CAAC,OAAO,EAAEW,IAAI,CAAC;YAC7B;QACF,SAAU;YACRjC,WAAW;YACXmB,aAAaR;QACf;IACF,GAAG;QAACJ;KAAS;IAEb,qBACE,MAAC4B;QAAIC,OAAO;YAAEC,cAAc;QAAO;;0BACjC,KAAC/C;gBAAOgD,aAAY;gBAAYC,UAAUxC;gBAASyC,QAAQ;gBAAOC,SAASjC;gBAAakC,MAAK;0BAC1F3C,UAAU,uBAAuB;;0BAEpC,KAAC4C;gBAAEP,OAAO;oBAAEQ,OAAO;oBAA8BC,UAAU;oBAAQC,WAAW;gBAAM;0BAAG;;;;AAM7F,EAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { Button,
|
|
2
|
+
import { Button, toast, useField } from '@payloadcms/ui';
|
|
3
3
|
import React, { useCallback, useState } from 'react';
|
|
4
4
|
import { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js';
|
|
5
5
|
/**
|
|
@@ -9,18 +9,15 @@ import { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js';
|
|
|
9
9
|
*/
|
|
10
10
|
export const VoicesFetcher = ({ path }) => {
|
|
11
11
|
const [loading, setLoading] = useState(false);
|
|
12
|
-
const { dispatchFields } = useForm();
|
|
13
12
|
// Get the parent path (the block path)
|
|
14
13
|
const fieldPath = path || '';
|
|
15
14
|
const blockPath = fieldPath.split('.').slice(0, -1).join('.');
|
|
16
15
|
const voicesPath = `${blockPath}.voices`;
|
|
17
|
-
|
|
18
|
-
const voicesField = useFormFields(([fields]) => fields[voicesPath]);
|
|
19
|
-
const currentRowCount = voicesField && 'rows' in voicesField && Array.isArray(voicesField.rows)
|
|
20
|
-
? voicesField.rows.length
|
|
21
|
-
: 0;
|
|
16
|
+
const { setValue } = useField({ path: voicesPath });
|
|
22
17
|
const fetchVoices = useCallback(async () => {
|
|
23
18
|
setLoading(true);
|
|
19
|
+
const controller = new AbortController();
|
|
20
|
+
const timeoutId = setTimeout(() => controller.abort(), 30000); // 30s timeout
|
|
24
21
|
try {
|
|
25
22
|
// Call server endpoint - it will read the API key from the database
|
|
26
23
|
const response = await fetch(`/api${PLUGIN_API_ENDPOINT_FETCH_VOICES}`, {
|
|
@@ -28,48 +25,45 @@ export const VoicesFetcher = ({ path }) => {
|
|
|
28
25
|
'Content-Type': 'application/json',
|
|
29
26
|
},
|
|
30
27
|
method: 'POST',
|
|
28
|
+
signal: controller.signal,
|
|
31
29
|
});
|
|
30
|
+
clearTimeout(timeoutId);
|
|
32
31
|
if (!response.ok) {
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
let errorMessage = 'Failed to fetch voices';
|
|
33
|
+
try {
|
|
34
|
+
const error = await response.json();
|
|
35
|
+
errorMessage = error.message || errorMessage;
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
// If response is not JSON (e.g. 504 Gateway Timeout HTML), use status text
|
|
39
|
+
errorMessage = `Error ${response.status}: ${response.statusText}`;
|
|
40
|
+
}
|
|
41
|
+
throw new Error(errorMessage);
|
|
35
42
|
}
|
|
36
43
|
const data = await response.json();
|
|
37
44
|
const voices = data.voices || [];
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
path: voicesPath,
|
|
43
|
-
rowIndex: i,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
// Add new rows for each voice using ADD_ROW action
|
|
47
|
-
for (const voice of voices) {
|
|
48
|
-
dispatchFields({
|
|
49
|
-
type: 'ADD_ROW',
|
|
50
|
-
path: voicesPath,
|
|
51
|
-
subFieldState: {
|
|
52
|
-
id: { initialValue: voice.id, valid: true, value: voice.id },
|
|
53
|
-
name: { initialValue: voice.name, valid: true, value: voice.name },
|
|
54
|
-
category: { initialValue: voice.category || 'premade', valid: true, value: voice.category || 'premade' },
|
|
55
|
-
enabled: { initialValue: voice.enabled !== false, valid: true, value: voice.enabled !== false },
|
|
56
|
-
labels: { initialValue: voice.labels || {}, valid: true, value: voice.labels || {} },
|
|
57
|
-
preview_url: { initialValue: voice.preview_url || '', valid: true, value: voice.preview_url || '' },
|
|
58
|
-
},
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
alert(`Successfully fetched ${voices.length} voices!`);
|
|
45
|
+
// Replace the entire array value at once
|
|
46
|
+
// This is much more performant than dispatching ADD_ROW actions in a loop
|
|
47
|
+
setValue(voices);
|
|
48
|
+
toast.success(`Successfully fetched ${voices.length} voices!`);
|
|
62
49
|
}
|
|
63
50
|
catch (error) {
|
|
64
|
-
|
|
51
|
+
const msg = error instanceof Error ? error.message : 'Failed to fetch voices';
|
|
52
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
53
|
+
toast.error('Request timed out. Please try again.');
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
toast.error(`Error: ${msg}`);
|
|
57
|
+
}
|
|
65
58
|
}
|
|
66
59
|
finally {
|
|
67
60
|
setLoading(false);
|
|
61
|
+
clearTimeout(timeoutId);
|
|
68
62
|
}
|
|
69
|
-
}, [
|
|
63
|
+
}, [setValue]);
|
|
70
64
|
return (<div style={{ marginBottom: '20px' }}>
|
|
71
|
-
<Button buttonStyle="secondary" disabled={loading} onClick={fetchVoices} size="medium">
|
|
72
|
-
{loading ? 'Fetching Voices...' : 'Fetch Voices
|
|
65
|
+
<Button buttonStyle="secondary" disabled={loading} margin={false} onClick={fetchVoices} size="medium">
|
|
66
|
+
{loading ? 'Fetching Voices...' : 'Fetch Voices'}
|
|
73
67
|
</Button>
|
|
74
68
|
<p style={{ color: 'var(--theme-elevation-600)', fontSize: '13px', marginTop: '8px' }}>
|
|
75
69
|
This will fetch all available voices from your ElevenLabs account. Make sure you have saved
|
|
@@ -92,7 +92,7 @@ import { getFieldBySchemaPath } from './getFieldBySchemaPath.js';
|
|
|
92
92
|
/**
|
|
93
93
|
* Build a smart contextual prompt based on field metadata.
|
|
94
94
|
* This is used as a fallback when the user hasn't set a custom prompt.
|
|
95
|
-
*
|
|
95
|
+
*
|
|
96
96
|
* @param context - The context containing schema path and document data
|
|
97
97
|
* @returns A contextual prompt string that can be used for AI generation
|
|
98
98
|
*/ export const buildSmartPrompt = (context)=>{
|
|
@@ -105,18 +105,16 @@ import { getFieldBySchemaPath } from './getFieldBySchemaPath.js';
|
|
|
105
105
|
const parts = [];
|
|
106
106
|
// Use description as primary guidance if available
|
|
107
107
|
if (description) {
|
|
108
|
-
parts.push(description);
|
|
109
|
-
} else {
|
|
110
|
-
// Fall back to type-based guidance, prefer label over name for better context
|
|
111
|
-
parts.push(getTypeGuidance(type, label || name));
|
|
108
|
+
parts.push(`Field description for user: ${description}\n`);
|
|
112
109
|
}
|
|
110
|
+
parts.push(getTypeGuidance(type, label || name));
|
|
113
111
|
// Add parent context if nested
|
|
114
112
|
const parentPhrase = getParentContextPhrase(parentContext);
|
|
115
113
|
if (parentPhrase) {
|
|
116
114
|
parts.push(parentPhrase);
|
|
117
115
|
}
|
|
118
116
|
// Add document title context if available
|
|
119
|
-
const title = documentData?.title;
|
|
117
|
+
const title = documentData?.title || documentData?.name;
|
|
120
118
|
if (title && typeof title === 'string') {
|
|
121
119
|
parts.push(`in the context of "${title}"`);
|
|
122
120
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/buildSmartPrompt.ts"],"sourcesContent":["'use strict'\n\nimport type { Field, Payload } from 'payload'\n\nimport { getFieldBySchemaPath } from './getFieldBySchemaPath.js'\n\nexport interface SmartPromptContext {\n /** The document data for template interpolation */\n documentData?: Record<string, unknown>\n /** The Payload instance to access collection config */\n payload: Payload\n /** The schema path like 'array-test-cases.teamMembers.contact.email' */\n schemaPath: string\n}\n\ninterface FieldInfo {\n /** The field configuration */\n field: Field | null\n /** Human-readable field label */\n label: string\n /** Field name from the path */\n name: string\n /** Parent field name if nested (e.g., 'teamMembers' for 'teamMembers.name') */\n parentContext: null | string\n /** The field type */\n type: string\n}\n\n/**\n * Extract field information from a schema path\n */\nconst getFieldInfo = (schemaPath: string, payload: Payload): FieldInfo => {\n const parts = schemaPath.split('.')\n const collectionSlug = parts[0]\n const fieldPath = parts.slice(1)\n const fieldName = fieldPath[fieldPath.length - 1] || ''\n
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/buildSmartPrompt.ts"],"sourcesContent":["'use strict'\n\nimport type { Field, Payload } from 'payload'\n\nimport { getFieldBySchemaPath } from './getFieldBySchemaPath.js'\n\nexport interface SmartPromptContext {\n /** The document data for template interpolation */\n documentData?: Record<string, unknown>\n /** The Payload instance to access collection config */\n payload: Payload\n /** The schema path like 'array-test-cases.teamMembers.contact.email' */\n schemaPath: string\n}\n\ninterface FieldInfo {\n /** The field configuration */\n field: Field | null\n /** Human-readable field label */\n label: string\n /** Field name from the path */\n name: string\n /** Parent field name if nested (e.g., 'teamMembers' for 'teamMembers.name') */\n parentContext: null | string\n /** The field type */\n type: string\n}\n\n/**\n * Extract field information from a schema path\n */\nconst getFieldInfo = (schemaPath: string, payload: Payload): FieldInfo => {\n const parts = schemaPath.split('.')\n const collectionSlug = parts[0]\n const fieldPath = parts.slice(1)\n const fieldName = fieldPath[fieldPath.length - 1] || ''\n\n // Get parent context (e.g., 'teamMembers' for 'teamMembers.name')\n let parentContext: null | string = null\n if (fieldPath.length > 1) {\n parentContext = fieldPath[fieldPath.length - 2]\n }\n\n // Try to get the actual field configuration from the collection\n let field: Field | null = null\n const collection = payload.config.collections.find((c) => c.slug === collectionSlug)\n if (collection) {\n field = getFieldBySchemaPath(collection, schemaPath)\n }\n\n return {\n name: fieldName,\n type: field?.type || 'text',\n field,\n label: (field as { label?: string })?.label || fieldName,\n parentContext,\n }\n}\n\n/**\n * Humanize a camelCase or snake_case field name\n * e.g., 'teamMembers' -> 'team members', 'first_name' -> 'first name'\n */\nconst humanize = (str: string): string => {\n return str\n .replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase to spaces\n .replace(/[_-]/g, ' ') // underscores/dashes to spaces\n .toLowerCase()\n .trim()\n}\n\n/**\n * Get a description snippet from field admin config\n */\nconst getFieldDescription = (field: Field | null): null | string => {\n if (!field) {\n return null\n }\n const admin = (field as { admin?: { description?: string } }).admin\n if (admin?.description && typeof admin.description === 'string') {\n return admin.description\n }\n return null\n}\n\n/**\n * Build type-specific prompt guidance\n */\nconst getTypeGuidance = (type: string, fieldName: string): string => {\n const nameHint = humanize(fieldName)\n\n switch (type) {\n case 'code':\n return `Generate code for ${nameHint}`\n case 'date':\n return `Generate an appropriate date for ${nameHint}`\n case 'email':\n return `Generate a valid professional email address`\n case 'json':\n return `Generate valid JSON data for ${nameHint}`\n case 'number':\n return `Generate an appropriate numeric value for ${nameHint}`\n case 'select':\n return `Select an appropriate option for ${nameHint}`\n case 'text':\n return `Generate appropriate text for ${nameHint}`\n case 'textarea':\n return `Write detailed content for ${nameHint}`\n case 'upload':\n // Explicit image generation instruction for multimodal models\n return `Generate an image of ${nameHint}`\n default:\n return `Generate content for ${nameHint}`\n }\n}\n\n/**\n * Build context from parent field name using generic humanization.\n * Works universally for any collection structure.\n */\nconst getParentContextPhrase = (parentContext: null | string): string => {\n if (!parentContext) {\n return ''\n }\n\n const humanized = humanize(parentContext)\n\n // Use singular form if the name ends with 's' (common for arrays)\n // e.g., \"teamMembers\" → \"team member\", \"products\" → \"product\"\n if (humanized.endsWith('s') && humanized.length > 2) {\n return `for a ${humanized.slice(0, -1)} entry`\n }\n\n return `for ${humanized}`\n}\n\n/**\n * Build a smart contextual prompt based on field metadata.\n * This is used as a fallback when the user hasn't set a custom prompt.\n *\n * @param context - The context containing schema path and document data\n * @returns A contextual prompt string that can be used for AI generation\n */\nexport const buildSmartPrompt = (context: SmartPromptContext): string => {\n const { documentData, payload, schemaPath } = context\n\n const fieldInfo = getFieldInfo(schemaPath, payload)\n const { name, type, field, label, parentContext } = fieldInfo\n\n // Start with the field's own description if available\n const description = getFieldDescription(field)\n\n // Build the prompt components\n const parts: string[] = []\n\n // Use description as primary guidance if available\n if (description) {\n parts.push(`Field description for user: ${description}\\n`)\n }\n\n parts.push(getTypeGuidance(type, label || name))\n\n // Add parent context if nested\n const parentPhrase = getParentContextPhrase(parentContext)\n if (parentPhrase) {\n parts.push(parentPhrase)\n }\n\n // Add document title context if available\n const title = documentData?.title || documentData?.name\n\n if (title && typeof title === 'string') {\n parts.push(`in the context of \"${title}\"`)\n }\n\n // Build the final prompt\n let prompt = parts.join(' ')\n\n // Ensure first letter is capitalized\n prompt = prompt.charAt(0).toUpperCase() + prompt.slice(1)\n\n // Add instruction suffix for clarity\n if (!prompt.endsWith('.')) {\n prompt += '.'\n }\n\n return prompt\n}\n\n/**\n * Check if a prompt template is empty and should be replaced with a smart prompt.\n * Only triggers when the prompt is completely empty or whitespace-only.\n */\nexport const isGenericPrompt = (template: null | string | undefined): boolean => {\n if (!template) {\n return true\n }\n return template.trim() === ''\n}\n"],"names":["getFieldBySchemaPath","getFieldInfo","schemaPath","payload","parts","split","collectionSlug","fieldPath","slice","fieldName","length","parentContext","field","collection","config","collections","find","c","slug","name","type","label","humanize","str","replace","toLowerCase","trim","getFieldDescription","admin","description","getTypeGuidance","nameHint","getParentContextPhrase","humanized","endsWith","buildSmartPrompt","context","documentData","fieldInfo","push","parentPhrase","title","prompt","join","charAt","toUpperCase","isGenericPrompt","template"],"mappings":"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"}
|
|
@@ -17,7 +17,8 @@ export function encrypt(text, secret) {
|
|
|
17
17
|
encrypted,
|
|
18
18
|
cipher.final()
|
|
19
19
|
]);
|
|
20
|
-
|
|
20
|
+
const result = iv.toString('hex') + ':' + encrypted.toString('hex');
|
|
21
|
+
return result;
|
|
21
22
|
}
|
|
22
23
|
export function decrypt(text, secret) {
|
|
23
24
|
if (!text) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/encryption.ts"],"sourcesContent":["import crypto from 'crypto'\n\nconst algorithm = 'aes-256-cbc'\nconst ivLength = 16\n\nexport function encrypt(text: string, secret: string): string {\n if (!text) {return text}\n if (!secret) {throw new Error('No secret provided for encryption')}\n\n // Ensure secret is 32 bytes\n const key = crypto.createHash('sha256').update(secret).digest()\n const iv = crypto.randomBytes(ivLength)\n const cipher = crypto.createCipheriv(algorithm, key, iv)\n let encrypted = cipher.update(text)\n encrypted = Buffer.concat([encrypted, cipher.final()])\n
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/encryption.ts"],"sourcesContent":["import crypto from 'crypto'\n\nconst algorithm = 'aes-256-cbc'\nconst ivLength = 16\n\nexport function encrypt(text: string, secret: string): string {\n if (!text) {\n return text\n }\n if (!secret) {\n throw new Error('No secret provided for encryption')\n }\n\n // Ensure secret is 32 bytes\n const key = crypto.createHash('sha256').update(secret).digest()\n const iv = crypto.randomBytes(ivLength)\n const cipher = crypto.createCipheriv(algorithm, key, iv)\n let encrypted = cipher.update(text)\n encrypted = Buffer.concat([encrypted, cipher.final()])\n const result = iv.toString('hex') + ':' + encrypted.toString('hex')\n\n return result\n}\n\nexport function decrypt(text: string, secret: string): string {\n if (!text) {\n return text\n }\n if (!secret) {\n throw new Error('No secret provided for decryption')\n }\n\n try {\n const textParts = text.split(':')\n const iv = Buffer.from(textParts.shift()!, 'hex')\n const encryptedText = Buffer.from(textParts.join(':'), 'hex')\n const key = crypto.createHash('sha256').update(secret).digest()\n const decipher = crypto.createDecipheriv(algorithm, key, iv)\n let decrypted = decipher.update(encryptedText)\n decrypted = Buffer.concat([decrypted, decipher.final()])\n return decrypted.toString()\n } catch (e) {\n // If decryption fails, return original text (might be already plain or invalid)\n return text\n }\n}\n"],"names":["crypto","algorithm","ivLength","encrypt","text","secret","Error","key","createHash","update","digest","iv","randomBytes","cipher","createCipheriv","encrypted","Buffer","concat","final","result","toString","decrypt","textParts","split","from","shift","encryptedText","join","decipher","createDecipheriv","decrypted","e"],"mappings":"AAAA,OAAOA,YAAY,SAAQ;AAE3B,MAAMC,YAAY;AAClB,MAAMC,WAAW;AAEjB,OAAO,SAASC,QAAQC,IAAY,EAAEC,MAAc;IAClD,IAAI,CAACD,MAAM;QACT,OAAOA;IACT;IACA,IAAI,CAACC,QAAQ;QACX,MAAM,IAAIC,MAAM;IAClB;IAEA,4BAA4B;IAC5B,MAAMC,MAAMP,OAAOQ,UAAU,CAAC,UAAUC,MAAM,CAACJ,QAAQK,MAAM;IAC7D,MAAMC,KAAKX,OAAOY,WAAW,CAACV;IAC9B,MAAMW,SAASb,OAAOc,cAAc,CAACb,WAAWM,KAAKI;IACrD,IAAII,YAAYF,OAAOJ,MAAM,CAACL;IAC9BW,YAAYC,OAAOC,MAAM,CAAC;QAACF;QAAWF,OAAOK,KAAK;KAAG;IACrD,MAAMC,SAASR,GAAGS,QAAQ,CAAC,SAAS,MAAML,UAAUK,QAAQ,CAAC;IAE7D,OAAOD;AACT;AAEA,OAAO,SAASE,QAAQjB,IAAY,EAAEC,MAAc;IAClD,IAAI,CAACD,MAAM;QACT,OAAOA;IACT;IACA,IAAI,CAACC,QAAQ;QACX,MAAM,IAAIC,MAAM;IAClB;IAEA,IAAI;QACF,MAAMgB,YAAYlB,KAAKmB,KAAK,CAAC;QAC7B,MAAMZ,KAAKK,OAAOQ,IAAI,CAACF,UAAUG,KAAK,IAAK;QAC3C,MAAMC,gBAAgBV,OAAOQ,IAAI,CAACF,UAAUK,IAAI,CAAC,MAAM;QACvD,MAAMpB,MAAMP,OAAOQ,UAAU,CAAC,UAAUC,MAAM,CAACJ,QAAQK,MAAM;QAC7D,MAAMkB,WAAW5B,OAAO6B,gBAAgB,CAAC5B,WAAWM,KAAKI;QACzD,IAAImB,YAAYF,SAASnB,MAAM,CAACiB;QAChCI,YAAYd,OAAOC,MAAM,CAAC;YAACa;YAAWF,SAASV,KAAK;SAAG;QACvD,OAAOY,UAAUV,QAAQ;IAC3B,EAAE,OAAOW,GAAG;QACV,gFAAgF;QAChF,OAAO3B;IACT;AACF"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { defaultSeedPrompts } from '../ai/prompts.js';
|
|
2
1
|
import { PLUGIN_INSTRUCTIONS_TABLE } from '../defaults.js';
|
|
3
2
|
import { updateFieldsConfig } from './updateFieldsConfig.js';
|
|
4
3
|
export const seedProperties = async ({ enabledCollections, req })=>{
|
|
@@ -17,7 +16,7 @@ export const seedProperties = async ({ enabledCollections, req })=>{
|
|
|
17
16
|
// Use the side-effect of getting schemaPathMap from it
|
|
18
17
|
const { schemaPathMap } = updateFieldsConfig(collectionConfig);
|
|
19
18
|
for (const [schemaPath, fieldInfo] of Object.entries(schemaPathMap)){
|
|
20
|
-
const { type, custom,
|
|
19
|
+
const { type, custom, relationTo } = fieldInfo;
|
|
21
20
|
// Check if instruction already exists
|
|
22
21
|
const existingInstruction = await payload.find({
|
|
23
22
|
collection: PLUGIN_INSTRUCTIONS_TABLE,
|
|
@@ -62,25 +61,9 @@ export const seedProperties = async ({ enabledCollections, req })=>{
|
|
|
62
61
|
}
|
|
63
62
|
continue;
|
|
64
63
|
}
|
|
65
|
-
//
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
fieldSchemaPaths: {},
|
|
69
|
-
fieldType: type,
|
|
70
|
-
path: schemaPath
|
|
71
|
-
});
|
|
72
|
-
if (!seeded || typeof seeded !== 'object') {
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
let prompt = 'prompt' in seeded ? seeded.prompt : '';
|
|
76
|
-
let system = 'system' in seeded ? seeded.system : '';
|
|
77
|
-
// Override with custom prompts if defined
|
|
78
|
-
if (custom?.ai?.prompt) {
|
|
79
|
-
prompt = custom.ai.prompt;
|
|
80
|
-
}
|
|
81
|
-
if (custom?.ai?.system) {
|
|
82
|
-
system = custom.ai.system;
|
|
83
|
-
}
|
|
64
|
+
// Use custom prompts if provided, otherwise leave empty
|
|
65
|
+
const prompt = custom?.ai?.prompt || '';
|
|
66
|
+
const system = custom?.ai?.system || '';
|
|
84
67
|
// Determine model-id based on field type
|
|
85
68
|
let modelId = 'text';
|
|
86
69
|
if (type === 'richText') {
|
|
@@ -97,13 +80,13 @@ export const seedProperties = async ({ enabledCollections, req })=>{
|
|
|
97
80
|
await payload.create({
|
|
98
81
|
collection: PLUGIN_INSTRUCTIONS_TABLE,
|
|
99
82
|
data: {
|
|
100
|
-
prompt,
|
|
101
|
-
system,
|
|
102
83
|
disabled: false,
|
|
103
84
|
'field-type': type,
|
|
104
85
|
'model-id': modelId,
|
|
86
|
+
prompt,
|
|
105
87
|
'relation-to': relationTo,
|
|
106
|
-
'schema-path': schemaPath
|
|
88
|
+
'schema-path': schemaPath,
|
|
89
|
+
system
|
|
107
90
|
},
|
|
108
91
|
overrideAccess: true
|
|
109
92
|
});
|