@ai-stack/payloadcms 3.68.0-beta.1 → 3.68.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/core/media/image/handlers/multimodal.js +5 -0
- package/dist/ai/core/media/image/handlers/multimodal.js.map +1 -1
- package/dist/ai/core/streamObject.js +0 -3
- package/dist/ai/core/streamObject.js.map +1 -1
- package/dist/ai/providers/blocks/anthropic.js +2 -1
- package/dist/ai/providers/blocks/anthropic.js.map +1 -1
- package/dist/ai/providers/blocks/elevenlabs.js +2 -1
- package/dist/ai/providers/blocks/elevenlabs.js.map +1 -1
- package/dist/ai/providers/blocks/fal.js +2 -1
- package/dist/ai/providers/blocks/fal.js.map +1 -1
- package/dist/ai/providers/blocks/google.js +2 -1
- package/dist/ai/providers/blocks/google.js.map +1 -1
- package/dist/ai/providers/blocks/openai-compatible.js +2 -1
- package/dist/ai/providers/blocks/openai-compatible.js.map +1 -1
- package/dist/ai/providers/blocks/openai.js +2 -1
- package/dist/ai/providers/blocks/openai.js.map +1 -1
- package/dist/ai/providers/blocks/xai.js +2 -1
- package/dist/ai/providers/blocks/xai.js.map +1 -1
- package/dist/ai/providers/icons.d.ts +7 -0
- package/dist/ai/providers/icons.js +9 -0
- package/dist/ai/providers/icons.js.map +1 -0
- package/dist/ai/providers/registry.js +34 -23
- package/dist/ai/providers/registry.js.map +1 -1
- package/dist/collections/Instructions.js +37 -0
- package/dist/collections/Instructions.js.map +1 -1
- package/dist/endpoints/chat.d.ts +4 -0
- package/dist/endpoints/index.js +86 -10
- package/dist/endpoints/index.js.map +1 -1
- package/dist/exports/fields.d.ts +1 -0
- package/dist/exports/fields.js +1 -0
- package/dist/exports/fields.js.map +1 -1
- package/dist/fields/ArrayComposeField/ArrayComposeField.d.ts +15 -0
- package/dist/fields/ArrayComposeField/ArrayComposeField.js +87 -0
- package/dist/fields/ArrayComposeField/ArrayComposeField.js.map +1 -0
- package/dist/fields/ArrayComposeField/ArrayComposeField.jsx +73 -0
- package/dist/fields/PromptEditorField/PromptEditorField.js +7 -2
- package/dist/fields/PromptEditorField/PromptEditorField.js.map +1 -1
- package/dist/fields/PromptEditorField/PromptEditorField.jsx +5 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/init.js +23 -6
- package/dist/init.js.map +1 -1
- package/dist/payload-ai.d.ts +149 -0
- package/dist/providers/InstructionsProvider/InstructionsProvider.js +3 -0
- package/dist/providers/InstructionsProvider/InstructionsProvider.js.map +1 -1
- package/dist/providers/InstructionsProvider/InstructionsProvider.jsx +3 -0
- package/dist/providers/InstructionsProvider/useInstructions.js +18 -1
- package/dist/providers/InstructionsProvider/useInstructions.js.map +1 -1
- package/dist/styles.d.ts +11 -0
- package/dist/types/handlebars-async-helpers.d.ts +1 -0
- package/dist/types/handlebars-dist-handlebars.d.ts +1 -0
- package/dist/types/react-mentions.d.ts +1 -0
- package/dist/ui/Compose/Compose.d.ts +1 -0
- package/dist/ui/Compose/Compose.js +2 -2
- package/dist/ui/Compose/Compose.js.map +1 -1
- package/dist/ui/Compose/Compose.jsx +2 -2
- package/dist/ui/Compose/UndoRedoActions.d.ts +2 -2
- package/dist/ui/Compose/UndoRedoActions.js +8 -5
- package/dist/ui/Compose/UndoRedoActions.js.map +1 -1
- package/dist/ui/Compose/UndoRedoActions.jsx +6 -5
- package/dist/ui/Compose/compose.module.css +56 -16
- package/dist/ui/Compose/hooks/menu/itemsMap.js +12 -6
- package/dist/ui/Compose/hooks/menu/itemsMap.js.map +1 -1
- package/dist/ui/Compose/hooks/menu/useMenu.js +26 -15
- package/dist/ui/Compose/hooks/menu/useMenu.js.map +1 -1
- package/dist/ui/Compose/hooks/menu/useMenu.jsx +25 -12
- package/dist/ui/Compose/hooks/useHistory.d.ts +0 -1
- package/dist/ui/Compose/hooks/useHistory.js +65 -25
- package/dist/ui/Compose/hooks/useHistory.js.map +1 -1
- package/dist/ui/DynamicVoiceSelect/index.js +63 -11
- package/dist/ui/DynamicVoiceSelect/index.js.map +1 -1
- package/dist/ui/DynamicVoiceSelect/index.jsx +47 -14
- package/dist/ui/VoicesFetcher/index.js +54 -8
- package/dist/ui/VoicesFetcher/index.js.map +1 -1
- package/dist/ui/VoicesFetcher/index.jsx +32 -9
- package/dist/utilities/buildSmartPrompt.d.ts +22 -0
- package/dist/utilities/buildSmartPrompt.js +143 -0
- package/dist/utilities/buildSmartPrompt.js.map +1 -0
- package/dist/utilities/resolveImageReferences.d.ts +3 -1
- package/dist/utilities/resolveImageReferences.js +21 -2
- package/dist/utilities/resolveImageReferences.js.map +1 -1
- package/dist/utilities/updateFieldsConfig.js +7 -1
- package/dist/utilities/updateFieldsConfig.js.map +1 -1
- package/package.json +3 -3
- package/dist/endpoints/chat.d.js +0 -3
- package/dist/endpoints/chat.d.js.map +0 -1
- package/dist/payload-ai.d.js +0 -3
- package/dist/payload-ai.d.js.map +0 -1
- package/dist/styles.d.js +0 -2
- package/dist/styles.d.js.map +0 -1
- package/dist/types/handlebars-async-helpers.d.js +0 -2
- package/dist/types/handlebars-async-helpers.d.js.map +0 -1
- package/dist/types/handlebars-dist-handlebars.d.js +0 -2
- package/dist/types/handlebars-dist-handlebars.d.js.map +0 -1
- package/dist/types/react-mentions.d.js +0 -2
- package/dist/types/react-mentions.d.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/exports/fields.ts"],"sourcesContent":["export { ComposeField } from '../fields/ComposeField/ComposeField.js'\nexport { PromptEditorField } from '../fields/PromptEditorField/PromptEditorField.js'\nexport { SelectField } from '../fields/SelectField/SelectField.js'\n"],"names":["ComposeField","PromptEditorField","SelectField"],"mappings":"AAAA,SAASA,YAAY,QAAQ,yCAAwC;AACrE,SAASC,iBAAiB,QAAQ,mDAAkD;AACpF,SAASC,WAAW,QAAQ,uCAAsC"}
|
|
1
|
+
{"version":3,"sources":["../../src/exports/fields.ts"],"sourcesContent":["export { ArrayComposeField } from '../fields/ArrayComposeField/ArrayComposeField.js'\nexport { ComposeField } from '../fields/ComposeField/ComposeField.js'\nexport { PromptEditorField } from '../fields/PromptEditorField/PromptEditorField.js'\nexport { SelectField } from '../fields/SelectField/SelectField.js'\n"],"names":["ArrayComposeField","ComposeField","PromptEditorField","SelectField"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,mDAAkD;AACpF,SAASC,YAAY,QAAQ,yCAAwC;AACrE,SAASC,iBAAiB,QAAQ,mDAAkD;AACpF,SAASC,WAAW,QAAQ,uCAAsC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ClientField } from 'payload';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
type ArrayComposeFieldProps = {
|
|
4
|
+
[key: string]: any;
|
|
5
|
+
field: ClientField;
|
|
6
|
+
path?: string;
|
|
7
|
+
schemaPath?: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* ArrayComposeField - A version of ComposeField specifically for array fields.
|
|
11
|
+
* Unlike regular fields, arrays don't have focus events, so this component
|
|
12
|
+
* renders the Compose button immediately (always visible).
|
|
13
|
+
*/
|
|
14
|
+
export declare const ArrayComposeField: (props: ArrayComposeFieldProps) => React.JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { FieldDescription, useDocumentInfo } from '@payloadcms/ui';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { createPortal } from 'react-dom';
|
|
6
|
+
import { FieldProvider } from '../../providers/FieldProvider/FieldProvider.js';
|
|
7
|
+
import { useInstructions } from '../../providers/InstructionsProvider/useInstructions.js';
|
|
8
|
+
import { Compose } from '../../ui/Compose/Compose.js';
|
|
9
|
+
import styles from '../../ui/Compose/compose.module.css';
|
|
10
|
+
/**
|
|
11
|
+
* ArrayComposeField - A version of ComposeField specifically for array fields.
|
|
12
|
+
* Unlike regular fields, arrays don't have focus events, so this component
|
|
13
|
+
* renders the Compose button immediately (always visible).
|
|
14
|
+
*/ export const ArrayComposeField = (props)=>{
|
|
15
|
+
const { collectionSlug } = useDocumentInfo();
|
|
16
|
+
const finalSchemaPath = props?.schemaPath ?? (collectionSlug ? `${collectionSlug}.${props?.path ?? ''}` : props?.path ?? '');
|
|
17
|
+
const { id: instructionId, disabled, hasInstructions, isConfigAllowed } = useInstructions({
|
|
18
|
+
schemaPath: finalSchemaPath
|
|
19
|
+
});
|
|
20
|
+
// Portal target state
|
|
21
|
+
const [portalTarget, setPortalTarget] = React.useState(null);
|
|
22
|
+
// State to track if the "Add Row" button is present in the DOM
|
|
23
|
+
const [isAddRowPresent, setIsAddRowPresent] = React.useState(true);
|
|
24
|
+
React.useEffect(()=>{
|
|
25
|
+
if (props.field && 'name' in props.field) {
|
|
26
|
+
const fieldId = `field-${props.field.name}`;
|
|
27
|
+
const fieldElement = document.getElementById(fieldId);
|
|
28
|
+
if (fieldElement) {
|
|
29
|
+
fieldElement.classList.add(styles.arrayFieldWrapper);
|
|
30
|
+
// Force relative position via inline style to prevent class application delays/overrides
|
|
31
|
+
fieldElement.style.position = 'relative';
|
|
32
|
+
setPortalTarget(fieldElement);
|
|
33
|
+
// Check initial state
|
|
34
|
+
const checkAddRow = ()=>{
|
|
35
|
+
const btn = fieldElement.querySelector('.array-field__add-row');
|
|
36
|
+
setIsAddRowPresent(!!btn);
|
|
37
|
+
};
|
|
38
|
+
checkAddRow();
|
|
39
|
+
// Observe for changes (e.g. when max rows reached and button is removed)
|
|
40
|
+
const observer = new MutationObserver(checkAddRow);
|
|
41
|
+
observer.observe(fieldElement, {
|
|
42
|
+
childList: true,
|
|
43
|
+
subtree: true
|
|
44
|
+
});
|
|
45
|
+
return ()=>observer.disconnect();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}, [
|
|
49
|
+
props.field
|
|
50
|
+
]);
|
|
51
|
+
const adminDescription = props?.field?.admin || {};
|
|
52
|
+
const description = 'description' in adminDescription ? adminDescription.description : '';
|
|
53
|
+
return /*#__PURE__*/ _jsxs(FieldProvider, {
|
|
54
|
+
context: {
|
|
55
|
+
field: props?.field,
|
|
56
|
+
path: props?.path ?? '',
|
|
57
|
+
schemaPath: finalSchemaPath
|
|
58
|
+
},
|
|
59
|
+
children: [
|
|
60
|
+
/*#__PURE__*/ _jsx("div", {
|
|
61
|
+
children: /*#__PURE__*/ _jsx(FieldDescription, {
|
|
62
|
+
description: description,
|
|
63
|
+
path: props?.path
|
|
64
|
+
})
|
|
65
|
+
}),
|
|
66
|
+
hasInstructions && instructionId && !disabled && portalTarget ? /*#__PURE__*/ createPortal(/*#__PURE__*/ _jsx("div", {
|
|
67
|
+
className: `
|
|
68
|
+
${styles.composePortal}
|
|
69
|
+
${isAddRowPresent ? styles.composePortalAbsolute : styles.composePortalStatic}
|
|
70
|
+
`,
|
|
71
|
+
children: /*#__PURE__*/ _jsx(Compose, {
|
|
72
|
+
descriptionProps: {
|
|
73
|
+
...props,
|
|
74
|
+
field: props?.field,
|
|
75
|
+
path: props?.path ?? '',
|
|
76
|
+
schemaPath: finalSchemaPath
|
|
77
|
+
},
|
|
78
|
+
forceVisible: true,
|
|
79
|
+
instructionId: instructionId,
|
|
80
|
+
isConfigAllowed: isConfigAllowed
|
|
81
|
+
})
|
|
82
|
+
}), portalTarget) : null
|
|
83
|
+
]
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
//# sourceMappingURL=ArrayComposeField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/fields/ArrayComposeField/ArrayComposeField.tsx"],"sourcesContent":["'use client'\n\nimport type { ClientField } from 'payload'\n\nimport { FieldDescription, useDocumentInfo } from '@payloadcms/ui'\nimport React from 'react'\nimport { createPortal } from 'react-dom'\n\nimport { FieldProvider } from '../../providers/FieldProvider/FieldProvider.js'\nimport { useInstructions } from '../../providers/InstructionsProvider/useInstructions.js'\nimport { Compose } from '../../ui/Compose/Compose.js'\nimport styles from '../../ui/Compose/compose.module.css'\n\ntype ArrayComposeFieldProps = {\n [key: string]: any\n field: ClientField\n path?: string\n schemaPath?: string\n}\n\n/**\n * ArrayComposeField - A version of ComposeField specifically for array fields.\n * Unlike regular fields, arrays don't have focus events, so this component\n * renders the Compose button immediately (always visible).\n */\nexport const ArrayComposeField = (props: ArrayComposeFieldProps) => {\n const { collectionSlug } = useDocumentInfo()\n\n const finalSchemaPath =\n props?.schemaPath ??\n (collectionSlug ? `${collectionSlug}.${props?.path ?? ''}` : (props?.path ?? ''))\n\n const {\n id: instructionId,\n disabled,\n hasInstructions,\n isConfigAllowed,\n } = useInstructions({\n schemaPath: finalSchemaPath,\n })\n\n // Portal target state\n const [portalTarget, setPortalTarget] = React.useState<HTMLElement | null>(null)\n\n // State to track if the \"Add Row\" button is present in the DOM\n const [isAddRowPresent, setIsAddRowPresent] = React.useState(true)\n\n React.useEffect(() => {\n if (props.field && 'name' in props.field) {\n const fieldId = `field-${props.field.name}`\n const fieldElement = document.getElementById(fieldId)\n \n if (fieldElement) {\n fieldElement.classList.add(styles.arrayFieldWrapper)\n // Force relative position via inline style to prevent class application delays/overrides\n fieldElement.style.position = 'relative'\n setPortalTarget(fieldElement)\n\n // Check initial state\n const checkAddRow = () => {\n const btn = fieldElement.querySelector('.array-field__add-row')\n setIsAddRowPresent(!!btn)\n }\n \n checkAddRow()\n\n // Observe for changes (e.g. when max rows reached and button is removed)\n const observer = new MutationObserver(checkAddRow)\n observer.observe(fieldElement, { childList: true, subtree: true })\n\n return () => observer.disconnect()\n }\n }\n }, [props.field])\n\n const adminDescription = props?.field?.admin || {}\n const description = 'description' in adminDescription ? adminDescription.description : ''\n\n return (\n <FieldProvider\n context={{\n field: props?.field,\n path: props?.path ?? '',\n schemaPath: finalSchemaPath,\n }}\n >\n <div>\n <FieldDescription description={description as any} path={props?.path as string} />\n </div>\n\n {/* Portal the Compose button to the bottom of the field wrapper */}\n {hasInstructions && instructionId && !disabled && portalTarget\n ? createPortal(\n <div \n className={`\n ${styles.composePortal} \n ${isAddRowPresent ? styles.composePortalAbsolute : styles.composePortalStatic}\n `}\n >\n <Compose\n descriptionProps={{\n ...props,\n field: props?.field,\n path: props?.path ?? '',\n schemaPath: finalSchemaPath,\n }}\n forceVisible={true}\n instructionId={instructionId}\n isConfigAllowed={isConfigAllowed}\n />\n </div>,\n portalTarget,\n )\n : null}\n </FieldProvider>\n )\n}\n"],"names":["FieldDescription","useDocumentInfo","React","createPortal","FieldProvider","useInstructions","Compose","styles","ArrayComposeField","props","collectionSlug","finalSchemaPath","schemaPath","path","id","instructionId","disabled","hasInstructions","isConfigAllowed","portalTarget","setPortalTarget","useState","isAddRowPresent","setIsAddRowPresent","useEffect","field","fieldId","name","fieldElement","document","getElementById","classList","add","arrayFieldWrapper","style","position","checkAddRow","btn","querySelector","observer","MutationObserver","observe","childList","subtree","disconnect","adminDescription","admin","description","context","div","className","composePortal","composePortalAbsolute","composePortalStatic","descriptionProps","forceVisible"],"mappings":"AAAA;;AAIA,SAASA,gBAAgB,EAAEC,eAAe,QAAQ,iBAAgB;AAClE,OAAOC,WAAW,QAAO;AACzB,SAASC,YAAY,QAAQ,YAAW;AAExC,SAASC,aAAa,QAAQ,iDAAgD;AAC9E,SAASC,eAAe,QAAQ,0DAAyD;AACzF,SAASC,OAAO,QAAQ,8BAA6B;AACrD,OAAOC,YAAY,sCAAqC;AASxD;;;;CAIC,GACD,OAAO,MAAMC,oBAAoB,CAACC;IAChC,MAAM,EAAEC,cAAc,EAAE,GAAGT;IAE3B,MAAMU,kBACJF,OAAOG,cACNF,CAAAA,iBAAiB,CAAC,EAAEA,eAAe,CAAC,EAAED,OAAOI,QAAQ,GAAG,CAAC,GAAIJ,OAAOI,QAAQ,EAAE;IAEjF,MAAM,EACJC,IAAIC,aAAa,EACjBC,QAAQ,EACRC,eAAe,EACfC,eAAe,EAChB,GAAGb,gBAAgB;QAClBO,YAAYD;IACd;IAEA,sBAAsB;IACtB,MAAM,CAACQ,cAAcC,gBAAgB,GAAGlB,MAAMmB,QAAQ,CAAqB;IAE3E,+DAA+D;IAC/D,MAAM,CAACC,iBAAiBC,mBAAmB,GAAGrB,MAAMmB,QAAQ,CAAC;IAE7DnB,MAAMsB,SAAS,CAAC;QACd,IAAIf,MAAMgB,KAAK,IAAI,UAAUhB,MAAMgB,KAAK,EAAE;YACxC,MAAMC,UAAU,CAAC,MAAM,EAAEjB,MAAMgB,KAAK,CAACE,IAAI,CAAC,CAAC;YAC3C,MAAMC,eAAeC,SAASC,cAAc,CAACJ;YAE7C,IAAIE,cAAc;gBAChBA,aAAaG,SAAS,CAACC,GAAG,CAACzB,OAAO0B,iBAAiB;gBACnD,yFAAyF;gBACzFL,aAAaM,KAAK,CAACC,QAAQ,GAAG;gBAC9Bf,gBAAgBQ;gBAEhB,sBAAsB;gBACtB,MAAMQ,cAAc;oBAClB,MAAMC,MAAMT,aAAaU,aAAa,CAAC;oBACvCf,mBAAmB,CAAC,CAACc;gBACvB;gBAEAD;gBAEA,yEAAyE;gBACzE,MAAMG,WAAW,IAAIC,iBAAiBJ;gBACtCG,SAASE,OAAO,CAACb,cAAc;oBAAEc,WAAW;oBAAMC,SAAS;gBAAK;gBAEhE,OAAO,IAAMJ,SAASK,UAAU;YAClC;QACF;IACF,GAAG;QAACnC,MAAMgB,KAAK;KAAC;IAEhB,MAAMoB,mBAAmBpC,OAAOgB,OAAOqB,SAAS,CAAC;IACjD,MAAMC,cAAc,iBAAiBF,mBAAmBA,iBAAiBE,WAAW,GAAG;IAEvF,qBACE,MAAC3C;QACC4C,SAAS;YACPvB,OAAOhB,OAAOgB;YACdZ,MAAMJ,OAAOI,QAAQ;YACrBD,YAAYD;QACd;;0BAEA,KAACsC;0BACC,cAAA,KAACjD;oBAAiB+C,aAAaA;oBAAoBlC,MAAMJ,OAAOI;;;YAIjEI,mBAAmBF,iBAAiB,CAACC,YAAYG,6BAC9ChB,2BACE,KAAC8C;gBACCC,WAAW,CAAC;gBACV,EAAE3C,OAAO4C,aAAa,CAAC;gBACvB,EAAE7B,kBAAkBf,OAAO6C,qBAAqB,GAAG7C,OAAO8C,mBAAmB,CAAC;cAChF,CAAC;0BAED,cAAA,KAAC/C;oBACCgD,kBAAkB;wBAChB,GAAG7C,KAAK;wBACRgB,OAAOhB,OAAOgB;wBACdZ,MAAMJ,OAAOI,QAAQ;wBACrBD,YAAYD;oBACd;oBACA4C,cAAc;oBACdxC,eAAeA;oBACfG,iBAAiBA;;gBAGrBC,gBAEF;;;AAGV,EAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { FieldDescription, useDocumentInfo } from '@payloadcms/ui';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { createPortal } from 'react-dom';
|
|
5
|
+
import { FieldProvider } from '../../providers/FieldProvider/FieldProvider.js';
|
|
6
|
+
import { useInstructions } from '../../providers/InstructionsProvider/useInstructions.js';
|
|
7
|
+
import { Compose } from '../../ui/Compose/Compose.js';
|
|
8
|
+
import styles from '../../ui/Compose/compose.module.css';
|
|
9
|
+
/**
|
|
10
|
+
* ArrayComposeField - A version of ComposeField specifically for array fields.
|
|
11
|
+
* Unlike regular fields, arrays don't have focus events, so this component
|
|
12
|
+
* renders the Compose button immediately (always visible).
|
|
13
|
+
*/
|
|
14
|
+
export const ArrayComposeField = (props) => {
|
|
15
|
+
const { collectionSlug } = useDocumentInfo();
|
|
16
|
+
const finalSchemaPath = props?.schemaPath ??
|
|
17
|
+
(collectionSlug ? `${collectionSlug}.${props?.path ?? ''}` : (props?.path ?? ''));
|
|
18
|
+
const { id: instructionId, disabled, hasInstructions, isConfigAllowed, } = useInstructions({
|
|
19
|
+
schemaPath: finalSchemaPath,
|
|
20
|
+
});
|
|
21
|
+
// Portal target state
|
|
22
|
+
const [portalTarget, setPortalTarget] = React.useState(null);
|
|
23
|
+
// State to track if the "Add Row" button is present in the DOM
|
|
24
|
+
const [isAddRowPresent, setIsAddRowPresent] = React.useState(true);
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
if (props.field && 'name' in props.field) {
|
|
27
|
+
const fieldId = `field-${props.field.name}`;
|
|
28
|
+
const fieldElement = document.getElementById(fieldId);
|
|
29
|
+
if (fieldElement) {
|
|
30
|
+
fieldElement.classList.add(styles.arrayFieldWrapper);
|
|
31
|
+
// Force relative position via inline style to prevent class application delays/overrides
|
|
32
|
+
fieldElement.style.position = 'relative';
|
|
33
|
+
setPortalTarget(fieldElement);
|
|
34
|
+
// Check initial state
|
|
35
|
+
const checkAddRow = () => {
|
|
36
|
+
const btn = fieldElement.querySelector('.array-field__add-row');
|
|
37
|
+
setIsAddRowPresent(!!btn);
|
|
38
|
+
};
|
|
39
|
+
checkAddRow();
|
|
40
|
+
// Observe for changes (e.g. when max rows reached and button is removed)
|
|
41
|
+
const observer = new MutationObserver(checkAddRow);
|
|
42
|
+
observer.observe(fieldElement, { childList: true, subtree: true });
|
|
43
|
+
return () => observer.disconnect();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}, [props.field]);
|
|
47
|
+
const adminDescription = props?.field?.admin || {};
|
|
48
|
+
const description = 'description' in adminDescription ? adminDescription.description : '';
|
|
49
|
+
return (<FieldProvider context={{
|
|
50
|
+
field: props?.field,
|
|
51
|
+
path: props?.path ?? '',
|
|
52
|
+
schemaPath: finalSchemaPath,
|
|
53
|
+
}}>
|
|
54
|
+
<div>
|
|
55
|
+
<FieldDescription description={description} path={props?.path}/>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
{/* Portal the Compose button to the bottom of the field wrapper */}
|
|
59
|
+
{hasInstructions && instructionId && !disabled && portalTarget
|
|
60
|
+
? createPortal(<div className={`
|
|
61
|
+
${styles.composePortal}
|
|
62
|
+
${isAddRowPresent ? styles.composePortalAbsolute : styles.composePortalStatic}
|
|
63
|
+
`}>
|
|
64
|
+
<Compose descriptionProps={{
|
|
65
|
+
...props,
|
|
66
|
+
field: props?.field,
|
|
67
|
+
path: props?.path ?? '',
|
|
68
|
+
schemaPath: finalSchemaPath,
|
|
69
|
+
}} forceVisible={true} instructionId={instructionId} isConfigAllowed={isConfigAllowed}/>
|
|
70
|
+
</div>, portalTarget)
|
|
71
|
+
: null}
|
|
72
|
+
</FieldProvider>);
|
|
73
|
+
};
|
|
@@ -156,8 +156,13 @@ export const PromptEditorField = (props)=>{
|
|
|
156
156
|
payloadValue
|
|
157
157
|
]);
|
|
158
158
|
const handleChange = useCallback((e)=>{
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
const newValue = e.target.value;
|
|
160
|
+
setLocalValue(newValue);
|
|
161
|
+
// Also update Payload value immediately to prevent loss when Save is clicked
|
|
162
|
+
setValue(newValue);
|
|
163
|
+
}, [
|
|
164
|
+
setValue
|
|
165
|
+
]);
|
|
161
166
|
const handleBlur = useCallback(()=>{
|
|
162
167
|
setValue(localValue);
|
|
163
168
|
}, [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/fields/PromptEditorField/PromptEditorField.tsx"],"sourcesContent":["'use client'\n\nimport type { TextareaFieldClientProps } from 'payload'\n\nimport { FieldDescription, FieldLabel, useConfig, useField } from '@payloadcms/ui'\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { Mention, MentionsInput } from 'react-mentions/dist/react-mentions.cjs.js'\n\nimport { useInstructions } from '../../providers/InstructionsProvider/useInstructions.js'\nimport { defaultStyle } from './defaultStyle.js'\n\nexport const PromptEditorField: React.FC<TextareaFieldClientProps> = (props) => {\n const { field, path: pathFromContext } = props\n const { setValue, value: payloadValue } = useField<string>({\n path: pathFromContext,\n })\n\n const [localValue, setLocalValue] = useState(payloadValue || '')\n const hasInitialized = useRef(false)\n\n const { activeCollection, promptEditorSuggestions } = useInstructions()\n const { config } = useConfig()\n\n const suggestions = useMemo(\n () =>\n promptEditorSuggestions.map((suggestion: string) => ({\n id: suggestion,\n display: suggestion,\n })),\n [promptEditorSuggestions],\n )\n\n // Extract document ID from URL if available (to get specific filenames)\n const [documentData, setDocumentData] = useState<null | Record<string, unknown>>(null)\n \n useEffect(() => {\n // Only run in browser\n if (typeof window === 'undefined') {\n return\n }\n\n // Allow time for verify window.location is stable (unlikely to change but good practice)\n const segments = window.location.pathname.split('/')\n const collectionsIndex = segments.indexOf('collections')\n \n if (collectionsIndex > -1 && segments.length > collectionsIndex + 2) {\n const urlCollectionSlug = segments[collectionsIndex + 1]\n const urlId = segments[collectionsIndex + 2]\n\n // Only fetch if we are editing instructions for the same collection we are viewing\n // and we haven't fetched yet (or ID changed)\n if (urlCollectionSlug === activeCollection && urlId && urlId !== 'create') {\n const fetchDocument = async () => {\n try {\n \n const response = await fetch(`${String(config.serverURL)}${String(config.routes.api)}/${String(urlCollectionSlug)}/${String(urlId)}`)\n if (response.ok) {\n const data = await response.json()\n setDocumentData(data)\n }\n } catch (_err) {\n // Ignore error\n }\n }\n void fetchDocument()\n }\n }\n }, [activeCollection, config])\n\n // Extract all upload fields from the current collection schema\n const imageFieldSuggestions = useMemo(() => {\n const suggestions: { display: string; id: string }[] = []\n \n // Use activeCollection from context which holds the target collection slug\n const targetSlug = activeCollection\n \n if (!targetSlug || !config?.collections) {\n return []\n }\n\n const collection = config.collections.find((c) => c.slug === targetSlug)\n if (!collection?.fields) {\n return []\n }\n\n const uploadFields: { hasMany: boolean; name: string }[] = []\n\n // Recursive function to find upload fields\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const findUploadFields = (fields: any[], prefix = ''): void => {\n for (const field of fields) {\n if (field.type === 'upload' && field.name) {\n const fieldPath = prefix ? `${prefix}.${String(field.name)}` : String(field.name)\n uploadFields.push({ name: fieldPath, hasMany: field.hasMany === true })\n }\n // Check nested fields in groups, arrays, etc.\n if (field.fields && Array.isArray(field.fields)) {\n const newPrefix = field.name ? (prefix ? `${prefix}.${String(field.name)}` : String(field.name)) : prefix\n findUploadFields(field.fields, newPrefix)\n }\n }\n }\n\n findUploadFields(collection.fields)\n \n // Add generic field names (base suggestions) - ONLY for single uploads (not hasMany arrays)\n uploadFields.forEach(({ name, hasMany }) => {\n // User requested to hide the array itself for hasMany fields\n if (!hasMany) {\n suggestions.push({ id: name, display: name })\n }\n })\n\n // If we have document data, add specific filename suggestions\n if (documentData) {\n uploadFields.forEach(({ name, hasMany }) => {\n const value = documentData[name] // Note: nested access logic simplified for now\n \n // Helper to extract filename from media doc (which might be ID or object)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const getFilename = (item: any): null | string => {\n if (typeof item === 'object' && item && (item.filename || item.name)) {\n return item.filename || item.name\n }\n // If it's just an ID, we can't show filename without populating. \n // Assuming compose view usually fetches with depth > 0 or we rely on what we have.\n return null\n }\n\n if (value) {\n if (hasMany && Array.isArray(value)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n value.forEach((item: any) => {\n const fname = getFilename(item)\n if (fname) {\n const suggestion = `${name}:${fname}`\n suggestions.push({ id: suggestion, display: suggestion })\n }\n })\n } else if (!hasMany) {\n // Single image - we already added the base name above.\n // We can optionally add the specific filename too if desired, \n // but user request focused on arrays.\n // Adding the specific filename option for Single images too as it's explicit.\n const fname = getFilename(value)\n if (fname) {\n const suggestion = `${name}:${fname}`\n suggestions.push({ id: suggestion, display: suggestion })\n }\n }\n }\n })\n }\n \n return suggestions\n }, [activeCollection, config, documentData])\n\n useEffect(() => {\n if (!hasInitialized.current || payloadValue === '') {\n setLocalValue(payloadValue || '')\n hasInitialized.current = true\n }\n }, [payloadValue])\n\n const handleChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setLocalValue(e.target.value)\n }, [])\n\n const handleBlur = useCallback(() => {\n setValue(localValue)\n }, [localValue, setValue])\n\n const displayTransform = useCallback((id: string) => `{{ ${id} }}`, [])\n const imageDisplayTransform = useCallback((id: string) => `@${id}`, [])\n\n return (\n <div className=\"field-type textarea\">\n <FieldLabel label={field.label} />\n <MentionsInput\n onBlur={handleBlur}\n onChange={handleChange}\n placeholder=\"Type {{ for fields }} or @imageField for images. For specific images use @imageField:filename.jpg\"\n style={defaultStyle}\n value={localValue}\n >\n <Mention\n data={suggestions}\n displayTransform={displayTransform}\n markup=\"{{__id__}}\"\n style={{\n backgroundColor: 'var(--theme-elevation-100)',\n padding: '2px 0',\n }}\n trigger=\"{\"\n />\n <Mention\n data={imageFieldSuggestions}\n displayTransform={imageDisplayTransform}\n markup=\"@__id__\"\n style={{\n backgroundColor: 'var(--theme-elevation-150)',\n padding: '2px 0',\n }}\n trigger=\"@\"\n />\n </MentionsInput>\n <FieldDescription description={field?.admin?.description} path=\"\" />\n </div>\n )\n}\n"],"names":["FieldDescription","FieldLabel","useConfig","useField","React","useCallback","useEffect","useMemo","useRef","useState","Mention","MentionsInput","useInstructions","defaultStyle","PromptEditorField","props","field","path","pathFromContext","setValue","value","payloadValue","localValue","setLocalValue","hasInitialized","activeCollection","promptEditorSuggestions","config","suggestions","map","suggestion","id","display","documentData","setDocumentData","window","segments","location","pathname","split","collectionsIndex","indexOf","length","urlCollectionSlug","urlId","fetchDocument","response","fetch","String","serverURL","routes","api","ok","data","json","_err","imageFieldSuggestions","targetSlug","collections","collection","find","c","slug","fields","uploadFields","findUploadFields","prefix","type","name","fieldPath","push","hasMany","Array","isArray","newPrefix","forEach","getFilename","item","filename","fname","current","handleChange","e","target","handleBlur","displayTransform","imageDisplayTransform","div","className","label","onBlur","onChange","placeholder","style","markup","backgroundColor","padding","trigger","description","admin"],"mappings":"AAAA;;AAIA,SAASA,gBAAgB,EAAEC,UAAU,EAAEC,SAAS,EAAEC,QAAQ,QAAQ,iBAAgB;AAClF,OAAOC,SAASC,WAAW,EAAEC,SAAS,EAAEC,OAAO,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAO;AAChF,SAASC,OAAO,EAAEC,aAAa,QAAQ,4CAA2C;AAElF,SAASC,eAAe,QAAQ,0DAAyD;AACzF,SAASC,YAAY,QAAQ,oBAAmB;AAEhD,OAAO,MAAMC,oBAAwD,CAACC;IACpE,MAAM,EAAEC,KAAK,EAAEC,MAAMC,eAAe,EAAE,GAAGH;IACzC,MAAM,EAAEI,QAAQ,EAAEC,OAAOC,YAAY,EAAE,GAAGlB,SAAiB;QACzDc,MAAMC;IACR;IAEA,MAAM,CAACI,YAAYC,cAAc,GAAGd,SAASY,gBAAgB;IAC7D,MAAMG,iBAAiBhB,OAAO;IAE9B,MAAM,EAAEiB,gBAAgB,EAAEC,uBAAuB,EAAE,GAAGd;IACtD,MAAM,EAAEe,MAAM,EAAE,GAAGzB;IAEnB,MAAM0B,cAAcrB,QAClB,IACEmB,wBAAwBG,GAAG,CAAC,CAACC,aAAwB,CAAA;gBACnDC,IAAID;gBACJE,SAASF;YACX,CAAA,IACF;QAACJ;KAAwB;IAG3B,wEAAwE;IACxE,MAAM,CAACO,cAAcC,gBAAgB,GAAGzB,SAAyC;IAEjFH,UAAU;QACR,sBAAsB;QACtB,IAAI,OAAO6B,WAAW,aAAa;YACjC;QACF;QAEA,yFAAyF;QACzF,MAAMC,WAAWD,OAAOE,QAAQ,CAACC,QAAQ,CAACC,KAAK,CAAC;QAChD,MAAMC,mBAAmBJ,SAASK,OAAO,CAAC;QAE1C,IAAID,mBAAmB,CAAC,KAAKJ,SAASM,MAAM,GAAGF,mBAAmB,GAAG;YACnE,MAAMG,oBAAoBP,QAAQ,CAACI,mBAAmB,EAAE;YACxD,MAAMI,QAAQR,QAAQ,CAACI,mBAAmB,EAAE;YAE5C,mFAAmF;YACnF,6CAA6C;YAC7C,IAAIG,sBAAsBlB,oBAAoBmB,SAASA,UAAU,UAAU;gBACxE,MAAMC,gBAAgB;oBACpB,IAAI;wBAEF,MAAMC,WAAW,MAAMC,MAAM,CAAC,EAAEC,OAAOrB,OAAOsB,SAAS,EAAE,EAAED,OAAOrB,OAAOuB,MAAM,CAACC,GAAG,EAAE,CAAC,EAAEH,OAAOL,mBAAmB,CAAC,EAAEK,OAAOJ,OAAO,CAAC;wBACpI,IAAIE,SAASM,EAAE,EAAE;4BACf,MAAMC,OAAO,MAAMP,SAASQ,IAAI;4BAChCpB,gBAAgBmB;wBAClB;oBACF,EAAE,OAAOE,MAAM;oBACb,eAAe;oBACjB;gBACF;gBACA,KAAKV;YACR;QACF;IACF,GAAG;QAACpB;QAAkBE;KAAO;IAE7B,+DAA+D;IAC/D,MAAM6B,wBAAwBjD,QAAQ;QACpC,MAAMqB,cAAiD,EAAE;QAEzD,2EAA2E;QAC3E,MAAM6B,aAAahC;QAEnB,IAAI,CAACgC,cAAc,CAAC9B,QAAQ+B,aAAa;YACvC,OAAO,EAAE;QACX;QAEA,MAAMC,aAAahC,OAAO+B,WAAW,CAACE,IAAI,CAAC,CAACC,IAAMA,EAAEC,IAAI,KAAKL;QAC7D,IAAI,CAACE,YAAYI,QAAQ;YACvB,OAAO,EAAE;QACX;QAEA,MAAMC,eAAqD,EAAE;QAE7D,2CAA2C;QAC3C,8DAA8D;QAC9D,MAAMC,mBAAmB,CAACF,QAAeG,SAAS,EAAE;YAClD,KAAK,MAAMlD,SAAS+C,OAAQ;gBAC1B,IAAI/C,MAAMmD,IAAI,KAAK,YAAYnD,MAAMoD,IAAI,EAAE;oBACzC,MAAMC,YAAYH,SAAS,CAAC,EAAEA,OAAO,CAAC,EAAElB,OAAOhC,MAAMoD,IAAI,EAAE,CAAC,GAAGpB,OAAOhC,MAAMoD,IAAI;oBAChFJ,aAAaM,IAAI,CAAC;wBAAEF,MAAMC;wBAAWE,SAASvD,MAAMuD,OAAO,KAAK;oBAAK;gBACvE;gBACA,8CAA8C;gBAC9C,IAAIvD,MAAM+C,MAAM,IAAIS,MAAMC,OAAO,CAACzD,MAAM+C,MAAM,GAAG;oBAC/C,MAAMW,YAAY1D,MAAMoD,IAAI,GAAIF,SAAS,CAAC,EAAEA,OAAO,CAAC,EAAElB,OAAOhC,MAAMoD,IAAI,EAAE,CAAC,GAAGpB,OAAOhC,MAAMoD,IAAI,IAAKF;oBACnGD,iBAAiBjD,MAAM+C,MAAM,EAAEW;gBACjC;YACF;QACF;QAEAT,iBAAiBN,WAAWI,MAAM;QAElC,4FAA4F;QAC5FC,aAAaW,OAAO,CAAC,CAAC,EAAEP,IAAI,EAAEG,OAAO,EAAE;YACrC,6DAA6D;YAC7D,IAAI,CAACA,SAAS;gBACZ3C,YAAY0C,IAAI,CAAC;oBAAEvC,IAAIqC;oBAAMpC,SAASoC;gBAAK;YAC7C;QACF;QAEA,8DAA8D;QAC9D,IAAInC,cAAc;YAChB+B,aAAaW,OAAO,CAAC,CAAC,EAAEP,IAAI,EAAEG,OAAO,EAAE;gBACrC,MAAMnD,QAAQa,YAAY,CAACmC,KAAK,CAAC,+CAA+C;;gBAEhF,0EAA0E;gBAC1E,8DAA8D;gBAC9D,MAAMQ,cAAc,CAACC;oBAClB,IAAI,OAAOA,SAAS,YAAYA,QAASA,CAAAA,KAAKC,QAAQ,IAAID,KAAKT,IAAI,AAAD,GAAI;wBACpE,OAAOS,KAAKC,QAAQ,IAAID,KAAKT,IAAI;oBACnC;oBACA,kEAAkE;oBAClE,mFAAmF;oBACnF,OAAO;gBACV;gBAEA,IAAIhD,OAAO;oBACR,IAAImD,WAAWC,MAAMC,OAAO,CAACrD,QAAQ;wBACnC,8DAA8D;wBAC9DA,MAAMuD,OAAO,CAAC,CAACE;4BACb,MAAME,QAAQH,YAAYC;4BAC1B,IAAIE,OAAO;gCACT,MAAMjD,aAAa,CAAC,EAAEsC,KAAK,CAAC,EAAEW,MAAM,CAAC;gCACrCnD,YAAY0C,IAAI,CAAC;oCAAEvC,IAAID;oCAAYE,SAASF;gCAAW;4BACzD;wBACF;oBACF,OAAO,IAAI,CAACyC,SAAS;wBACnB,uDAAuD;wBACvD,+DAA+D;wBAC/D,sCAAsC;wBACtC,8EAA8E;wBAC9E,MAAMQ,QAAQH,YAAYxD;wBAC1B,IAAI2D,OAAO;4BACP,MAAMjD,aAAa,CAAC,EAAEsC,KAAK,CAAC,EAAEW,MAAM,CAAC;4BACrCnD,YAAY0C,IAAI,CAAC;gCAAEvC,IAAID;gCAAYE,SAASF;4BAAW;wBAC3D;oBACF;gBACH;YACF;QACF;QAEA,OAAOF;IACT,GAAG;QAACH;QAAkBE;QAAQM;KAAa;IAE3C3B,UAAU;QACR,IAAI,CAACkB,eAAewD,OAAO,IAAI3D,iBAAiB,IAAI;YAClDE,cAAcF,gBAAgB;YAC9BG,eAAewD,OAAO,GAAG;QAC3B;IACF,GAAG;QAAC3D;KAAa;IAEjB,MAAM4D,eAAe5E,YAAY,CAAC6E;QAChC3D,cAAc2D,EAAEC,MAAM,CAAC/D,KAAK;IAC9B,GAAG,EAAE;IAEL,MAAMgE,aAAa/E,YAAY;QAC7Bc,SAASG;IACX,GAAG;QAACA;QAAYH;KAAS;IAEzB,MAAMkE,mBAAmBhF,YAAY,CAAC0B,KAAe,CAAC,GAAG,EAAEA,GAAG,GAAG,CAAC,EAAE,EAAE;IACtE,MAAMuD,wBAAwBjF,YAAY,CAAC0B,KAAe,CAAC,CAAC,EAAEA,GAAG,CAAC,EAAE,EAAE;IAEtE,qBACE,MAACwD;QAAIC,WAAU;;0BACb,KAACvF;gBAAWwF,OAAOzE,MAAMyE,KAAK;;0BAC9B,MAAC9E;gBACC+E,QAAQN;gBACRO,UAAUV;gBACVW,aAAY;gBACZC,OAAOhF;gBACPO,OAAOE;;kCAEP,KAACZ;wBACC2C,MAAMzB;wBACNyD,kBAAkBA;wBAClBS,QAAO;wBACPD,OAAO;4BACLE,iBAAiB;4BACjBC,SAAS;wBACX;wBACAC,SAAQ;;kCAEV,KAACvF;wBACC2C,MAAMG;wBACN6B,kBAAkBC;wBAClBQ,QAAO;wBACPD,OAAO;4BACLE,iBAAiB;4BACjBC,SAAS;wBACX;wBACAC,SAAQ;;;;0BAGZ,KAACjG;gBAAiBkG,aAAalF,OAAOmF,OAAOD;gBAAajF,MAAK;;;;AAGrE,EAAC"}
|
|
1
|
+
{"version":3,"sources":["../../../src/fields/PromptEditorField/PromptEditorField.tsx"],"sourcesContent":["'use client'\n\nimport type { TextareaFieldClientProps } from 'payload'\n\nimport { FieldDescription, FieldLabel, useConfig, useField } from '@payloadcms/ui'\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { Mention, MentionsInput } from 'react-mentions/dist/react-mentions.cjs.js'\n\nimport { useInstructions } from '../../providers/InstructionsProvider/useInstructions.js'\nimport { defaultStyle } from './defaultStyle.js'\n\nexport const PromptEditorField: React.FC<TextareaFieldClientProps> = (props) => {\n const { field, path: pathFromContext } = props\n const { setValue, value: payloadValue } = useField<string>({\n path: pathFromContext,\n })\n\n const [localValue, setLocalValue] = useState(payloadValue || '')\n const hasInitialized = useRef(false)\n\n const { activeCollection, promptEditorSuggestions } = useInstructions()\n const { config } = useConfig()\n\n const suggestions = useMemo(\n () =>\n promptEditorSuggestions.map((suggestion: string) => ({\n id: suggestion,\n display: suggestion,\n })),\n [promptEditorSuggestions],\n )\n\n // Extract document ID from URL if available (to get specific filenames)\n const [documentData, setDocumentData] = useState<null | Record<string, unknown>>(null)\n \n useEffect(() => {\n // Only run in browser\n if (typeof window === 'undefined') {\n return\n }\n\n // Allow time for verify window.location is stable (unlikely to change but good practice)\n const segments = window.location.pathname.split('/')\n const collectionsIndex = segments.indexOf('collections')\n \n if (collectionsIndex > -1 && segments.length > collectionsIndex + 2) {\n const urlCollectionSlug = segments[collectionsIndex + 1]\n const urlId = segments[collectionsIndex + 2]\n\n // Only fetch if we are editing instructions for the same collection we are viewing\n // and we haven't fetched yet (or ID changed)\n if (urlCollectionSlug === activeCollection && urlId && urlId !== 'create') {\n const fetchDocument = async () => {\n try {\n \n const response = await fetch(`${String(config.serverURL)}${String(config.routes.api)}/${String(urlCollectionSlug)}/${String(urlId)}`)\n if (response.ok) {\n const data = await response.json()\n setDocumentData(data)\n }\n } catch (_err) {\n // Ignore error\n }\n }\n void fetchDocument()\n }\n }\n }, [activeCollection, config])\n\n // Extract all upload fields from the current collection schema\n const imageFieldSuggestions = useMemo(() => {\n const suggestions: { display: string; id: string }[] = []\n \n // Use activeCollection from context which holds the target collection slug\n const targetSlug = activeCollection\n \n if (!targetSlug || !config?.collections) {\n return []\n }\n\n const collection = config.collections.find((c) => c.slug === targetSlug)\n if (!collection?.fields) {\n return []\n }\n\n const uploadFields: { hasMany: boolean; name: string }[] = []\n\n // Recursive function to find upload fields\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const findUploadFields = (fields: any[], prefix = ''): void => {\n for (const field of fields) {\n if (field.type === 'upload' && field.name) {\n const fieldPath = prefix ? `${prefix}.${String(field.name)}` : String(field.name)\n uploadFields.push({ name: fieldPath, hasMany: field.hasMany === true })\n }\n // Check nested fields in groups, arrays, etc.\n if (field.fields && Array.isArray(field.fields)) {\n const newPrefix = field.name ? (prefix ? `${prefix}.${String(field.name)}` : String(field.name)) : prefix\n findUploadFields(field.fields, newPrefix)\n }\n }\n }\n\n findUploadFields(collection.fields)\n \n // Add generic field names (base suggestions) - ONLY for single uploads (not hasMany arrays)\n uploadFields.forEach(({ name, hasMany }) => {\n // User requested to hide the array itself for hasMany fields\n if (!hasMany) {\n suggestions.push({ id: name, display: name })\n }\n })\n\n // If we have document data, add specific filename suggestions\n if (documentData) {\n uploadFields.forEach(({ name, hasMany }) => {\n const value = documentData[name] // Note: nested access logic simplified for now\n \n // Helper to extract filename from media doc (which might be ID or object)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const getFilename = (item: any): null | string => {\n if (typeof item === 'object' && item && (item.filename || item.name)) {\n return item.filename || item.name\n }\n // If it's just an ID, we can't show filename without populating. \n // Assuming compose view usually fetches with depth > 0 or we rely on what we have.\n return null\n }\n\n if (value) {\n if (hasMany && Array.isArray(value)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n value.forEach((item: any) => {\n const fname = getFilename(item)\n if (fname) {\n const suggestion = `${name}:${fname}`\n suggestions.push({ id: suggestion, display: suggestion })\n }\n })\n } else if (!hasMany) {\n // Single image - we already added the base name above.\n // We can optionally add the specific filename too if desired, \n // but user request focused on arrays.\n // Adding the specific filename option for Single images too as it's explicit.\n const fname = getFilename(value)\n if (fname) {\n const suggestion = `${name}:${fname}`\n suggestions.push({ id: suggestion, display: suggestion })\n }\n }\n }\n })\n }\n \n return suggestions\n }, [activeCollection, config, documentData])\n\n useEffect(() => {\n if (!hasInitialized.current || payloadValue === '') {\n setLocalValue(payloadValue || '')\n hasInitialized.current = true\n }\n }, [payloadValue])\n\n const handleChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {\n const newValue = e.target.value\n setLocalValue(newValue)\n // Also update Payload value immediately to prevent loss when Save is clicked\n setValue(newValue)\n }, [setValue])\n\n const handleBlur = useCallback(() => {\n setValue(localValue)\n }, [localValue, setValue])\n\n const displayTransform = useCallback((id: string) => `{{ ${id} }}`, [])\n const imageDisplayTransform = useCallback((id: string) => `@${id}`, [])\n\n return (\n <div className=\"field-type textarea\">\n <FieldLabel label={field.label} />\n <MentionsInput\n onBlur={handleBlur}\n onChange={handleChange}\n placeholder=\"Type {{ for fields }} or @imageField for images. For specific images use @imageField:filename.jpg\"\n style={defaultStyle}\n value={localValue}\n >\n <Mention\n data={suggestions}\n displayTransform={displayTransform}\n markup=\"{{__id__}}\"\n style={{\n backgroundColor: 'var(--theme-elevation-100)',\n padding: '2px 0',\n }}\n trigger=\"{\"\n />\n <Mention\n data={imageFieldSuggestions}\n displayTransform={imageDisplayTransform}\n markup=\"@__id__\"\n style={{\n backgroundColor: 'var(--theme-elevation-150)',\n padding: '2px 0',\n }}\n trigger=\"@\"\n />\n </MentionsInput>\n <FieldDescription description={field?.admin?.description} path=\"\" />\n </div>\n )\n}\n"],"names":["FieldDescription","FieldLabel","useConfig","useField","React","useCallback","useEffect","useMemo","useRef","useState","Mention","MentionsInput","useInstructions","defaultStyle","PromptEditorField","props","field","path","pathFromContext","setValue","value","payloadValue","localValue","setLocalValue","hasInitialized","activeCollection","promptEditorSuggestions","config","suggestions","map","suggestion","id","display","documentData","setDocumentData","window","segments","location","pathname","split","collectionsIndex","indexOf","length","urlCollectionSlug","urlId","fetchDocument","response","fetch","String","serverURL","routes","api","ok","data","json","_err","imageFieldSuggestions","targetSlug","collections","collection","find","c","slug","fields","uploadFields","findUploadFields","prefix","type","name","fieldPath","push","hasMany","Array","isArray","newPrefix","forEach","getFilename","item","filename","fname","current","handleChange","e","newValue","target","handleBlur","displayTransform","imageDisplayTransform","div","className","label","onBlur","onChange","placeholder","style","markup","backgroundColor","padding","trigger","description","admin"],"mappings":"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"}
|
|
@@ -133,8 +133,11 @@ export const PromptEditorField = (props) => {
|
|
|
133
133
|
}
|
|
134
134
|
}, [payloadValue]);
|
|
135
135
|
const handleChange = useCallback((e) => {
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
const newValue = e.target.value;
|
|
137
|
+
setLocalValue(newValue);
|
|
138
|
+
// Also update Payload value immediately to prevent loss when Save is clicked
|
|
139
|
+
setValue(newValue);
|
|
140
|
+
}, [setValue]);
|
|
138
141
|
const handleBlur = useCallback(() => {
|
|
139
142
|
setValue(localValue);
|
|
140
143
|
}, [localValue, setValue]);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export type { GenerateArgs } from './ai/index.js';
|
|
2
2
|
export { defaultPrompts, defaultSeedPrompts } from './ai/prompts.js';
|
|
3
3
|
export { PayloadAiPluginLexicalEditorFeature } from './fields/LexicalEditor/feature.server.js';
|
|
4
|
+
export type {} from './payload-ai.d.ts';
|
|
4
5
|
export { payloadAiPlugin } from './plugin.js';
|
|
6
|
+
export { fieldToJsonSchema } from './utilities/fieldToJsonSchema.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { defaultPrompts, defaultSeedPrompts } from './ai/prompts.js';
|
|
2
2
|
export { PayloadAiPluginLexicalEditorFeature } from './fields/LexicalEditor/feature.server.js';
|
|
3
3
|
export { payloadAiPlugin } from './plugin.js';
|
|
4
|
+
export { fieldToJsonSchema } from './utilities/fieldToJsonSchema.js';
|
|
4
5
|
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type { GenerateArgs } from './ai/index.js'\n\nexport { defaultPrompts, defaultSeedPrompts } from './ai/prompts.js'\nexport { PayloadAiPluginLexicalEditorFeature } from './fields/LexicalEditor/feature.server.js'\n\nexport { payloadAiPlugin } from './plugin.js'\n"],"names":["defaultPrompts","defaultSeedPrompts","PayloadAiPluginLexicalEditorFeature","payloadAiPlugin"],"mappings":"AAEA,SAASA,cAAc,EAAEC,kBAAkB,QAAQ,kBAAiB;
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type { GenerateArgs } from './ai/index.js'\n\nexport { defaultPrompts, defaultSeedPrompts } from './ai/prompts.js'\n\nexport { PayloadAiPluginLexicalEditorFeature } from './fields/LexicalEditor/feature.server.js'\n// Re-export to ensure payload.ai module augmentation is included\nexport type {} from './payload-ai.d.ts'\n\nexport { payloadAiPlugin } from './plugin.js'\nexport { fieldToJsonSchema } from './utilities/fieldToJsonSchema.js'\n"],"names":["defaultPrompts","defaultSeedPrompts","PayloadAiPluginLexicalEditorFeature","payloadAiPlugin","fieldToJsonSchema"],"mappings":"AAEA,SAASA,cAAc,EAAEC,kBAAkB,QAAQ,kBAAiB;AAEpE,SAASC,mCAAmC,QAAQ,2CAA0C;AAI9F,SAASC,eAAe,QAAQ,cAAa;AAC7C,SAASC,iBAAiB,QAAQ,mCAAkC"}
|
package/dist/init.js
CHANGED
|
@@ -20,6 +20,12 @@ const CAPABILITY_MAP = [
|
|
|
20
20
|
fields: [
|
|
21
21
|
'upload'
|
|
22
22
|
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'array',
|
|
26
|
+
fields: [
|
|
27
|
+
'array'
|
|
28
|
+
]
|
|
23
29
|
}
|
|
24
30
|
];
|
|
25
31
|
export const init = async (payload, fieldSchemaPaths, pluginConfig)=>{
|
|
@@ -37,6 +43,7 @@ export const init = async (payload, fieldSchemaPaths, pluginConfig)=>{
|
|
|
37
43
|
pagination: false,
|
|
38
44
|
select: {
|
|
39
45
|
'field-type': true,
|
|
46
|
+
'model-id': true,
|
|
40
47
|
'schema-path': true
|
|
41
48
|
}
|
|
42
49
|
});
|
|
@@ -66,7 +73,8 @@ export const init = async (payload, fieldSchemaPaths, pluginConfig)=>{
|
|
|
66
73
|
}
|
|
67
74
|
continue;
|
|
68
75
|
}
|
|
69
|
-
|
|
76
|
+
// Empty prompt - smart fallback will generate a contextual prompt at runtime
|
|
77
|
+
const generatedPrompt = '';
|
|
70
78
|
if ('prompt' in seed) {
|
|
71
79
|
// Prompt generation currently disabled during migration to AI SDK Providers
|
|
72
80
|
// TODO: Re-enable using a default provider from AI Settings if available
|
|
@@ -106,16 +114,25 @@ export const init = async (payload, fieldSchemaPaths, pluginConfig)=>{
|
|
|
106
114
|
};
|
|
107
115
|
}
|
|
108
116
|
} else {
|
|
109
|
-
|
|
110
|
-
|
|
117
|
+
const modelForId = CAPABILITY_MAP.find((a)=>a.fields.includes(fieldType));
|
|
118
|
+
if (instructions['field-type'] !== fieldType || !instructions['model-id']) {
|
|
119
|
+
payload.logger.warn(`— AI Plugin: Field config mismatch for ${path}! Updating...`);
|
|
120
|
+
const updateData = {
|
|
121
|
+
'field-type': fieldType
|
|
122
|
+
};
|
|
123
|
+
// Only set model-id if it's missing or we have a better default
|
|
124
|
+
if (!instructions['model-id'] && modelForId?.id) {
|
|
125
|
+
updateData['model-id'] = modelForId.id;
|
|
126
|
+
}
|
|
111
127
|
await payload.update({
|
|
112
128
|
id: instructions.id,
|
|
113
129
|
collection: PLUGIN_INSTRUCTIONS_TABLE,
|
|
114
|
-
data:
|
|
115
|
-
'field-type': fieldType
|
|
116
|
-
}
|
|
130
|
+
data: updateData
|
|
117
131
|
});
|
|
118
132
|
instructions['field-type'] = fieldType;
|
|
133
|
+
if (updateData['model-id']) {
|
|
134
|
+
instructions['model-id'] = updateData['model-id'];
|
|
135
|
+
}
|
|
119
136
|
}
|
|
120
137
|
fieldInstructionsMap[path] = {
|
|
121
138
|
id: instructions.id,
|
package/dist/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/init.ts"],"sourcesContent":["import type { Payload } from 'payload'\n\nimport type { PluginConfig } from './types.js'\n\nimport { defaultSeedPrompts } from './ai/prompts.js'\nimport { systemGenerate } from './ai/utils/systemGenerate.js'\nimport { PLUGIN_INSTRUCTIONS_TABLE } from './defaults.js'\n\n// Defined capabilities mapping for init\nconst CAPABILITY_MAP = [\n { id: 'text', fields: ['text', 'textarea'] },\n { id: 'richtext', fields: ['richText'] },\n { id: 'image', fields: ['upload'] },\n // TTS usually outputs to upload, but init logic maps field types to capabilities\n]\n\nexport const init = async (\n payload: Payload,\n fieldSchemaPaths: Record<string, { label: string; relationTo?: string; type: string }>,\n pluginConfig: PluginConfig,\n) => {\n if (!pluginConfig.generatePromptOnInit) {\n return\n }\n\n if (pluginConfig.debugging) {\n payload.logger.info(`— AI Plugin: Initializing...`)\n }\n\n const paths = Object.keys(fieldSchemaPaths)\n\n // Get all instructions for faster initialization\n const { docs: allInstructions } = await payload.find({\n collection: PLUGIN_INSTRUCTIONS_TABLE,\n depth: 0,\n pagination: false,\n select: {\n 'field-type': true,\n 'schema-path': true,\n },\n })\n\n const fieldInstructionsMap: Record<string, { fieldType: any; id: any }> = {}\n\n for (let i = 0; i < paths.length; i++) {\n const path = paths[i]\n const { type: fieldType, label: fieldLabel, relationTo } = fieldSchemaPaths[path]\n let instructions = allInstructions.find((entry) => entry['schema-path'] === path)\n\n if (!instructions) {\n let seed\n const seedOptions = {\n fieldLabel,\n fieldSchemaPaths,\n fieldType,\n path,\n }\n\n if (pluginConfig.seedPrompts) {seed = await pluginConfig.seedPrompts(seedOptions)}\n if (seed === undefined) {seed = await defaultSeedPrompts(seedOptions)}\n // Field should be ignored\n if (!seed) {\n if (pluginConfig.debugging) {\n payload.logger.info(`— AI Plugin: No seed prompt for ${path}, ignoring...`)\n }\n continue\n }\n\n const generatedPrompt: string | undefined = '
|
|
1
|
+
{"version":3,"sources":["../src/init.ts"],"sourcesContent":["import type { Payload } from 'payload'\n\nimport type { PluginConfig } from './types.js'\n\nimport { defaultSeedPrompts } from './ai/prompts.js'\nimport { systemGenerate } from './ai/utils/systemGenerate.js'\nimport { PLUGIN_INSTRUCTIONS_TABLE } from './defaults.js'\n\n// Defined capabilities mapping for init\nconst CAPABILITY_MAP = [\n { id: 'text', fields: ['text', 'textarea'] },\n { id: 'richtext', fields: ['richText'] },\n { id: 'image', fields: ['upload'] },\n { id: 'array', fields: ['array'] },\n // TTS usually outputs to upload, but init logic maps field types to capabilities\n]\n\nexport const init = async (\n payload: Payload,\n fieldSchemaPaths: Record<string, { label: string; relationTo?: string; type: string }>,\n pluginConfig: PluginConfig,\n) => {\n if (!pluginConfig.generatePromptOnInit) {\n return\n }\n\n if (pluginConfig.debugging) {\n payload.logger.info(`— AI Plugin: Initializing...`)\n }\n\n const paths = Object.keys(fieldSchemaPaths)\n\n // Get all instructions for faster initialization\n const { docs: allInstructions } = await payload.find({\n collection: PLUGIN_INSTRUCTIONS_TABLE,\n depth: 0,\n pagination: false,\n select: {\n 'field-type': true,\n 'model-id': true,\n 'schema-path': true,\n },\n })\n\n const fieldInstructionsMap: Record<string, { fieldType: any; id: any }> = {}\n\n for (let i = 0; i < paths.length; i++) {\n const path = paths[i]\n const { type: fieldType, label: fieldLabel, relationTo } = fieldSchemaPaths[path]\n let instructions = allInstructions.find((entry) => entry['schema-path'] === path)\n\n if (!instructions) {\n let seed\n const seedOptions = {\n fieldLabel,\n fieldSchemaPaths,\n fieldType,\n path,\n }\n\n if (pluginConfig.seedPrompts) {seed = await pluginConfig.seedPrompts(seedOptions)}\n if (seed === undefined) {seed = await defaultSeedPrompts(seedOptions)}\n // Field should be ignored\n if (!seed) {\n if (pluginConfig.debugging) {\n payload.logger.info(`— AI Plugin: No seed prompt for ${path}, ignoring...`)\n }\n continue\n }\n\n // Empty prompt - smart fallback will generate a contextual prompt at runtime\n const generatedPrompt: string | undefined = ''\n if ('prompt' in seed) {\n // Prompt generation currently disabled during migration to AI SDK Providers\n // TODO: Re-enable using a default provider from AI Settings if available\n /*\n generatedPrompt = await systemGenerate(\n {\n prompt: seed.prompt,\n system: seed.system,\n },\n undefined // No generateTextFn currently\n )\n */\n }\n\n const modelForId = CAPABILITY_MAP.find((a) => a.fields.includes(fieldType))\n\n const data = {\n 'model-id': modelForId?.id,\n prompt: generatedPrompt,\n ...seed.data, // allow to override data, but not the one below\n 'field-type': fieldType,\n 'relation-to': relationTo,\n 'schema-path': path,\n }\n\n payload.logger.info(\n {\n 'model-id': data['model-id'],\n prompt: generatedPrompt,\n ...seed.data,\n },\n `Prompt seeded for \"${path}\" field`,\n )\n\n instructions = (await payload\n .create({\n collection: PLUGIN_INSTRUCTIONS_TABLE,\n data,\n })\n .catch((err) => {\n payload.logger.error(err, '— AI Plugin: Error creating Compose settings-')\n })) as (typeof allInstructions)[0]\n\n if (instructions?.id) {\n fieldInstructionsMap[path] = {\n id: instructions.id,\n fieldType,\n }\n }\n } else {\n const modelForId = CAPABILITY_MAP.find((a) => a.fields.includes(fieldType))\n\n if (instructions['field-type'] !== fieldType || !instructions['model-id']) {\n payload.logger.warn(\n `— AI Plugin: Field config mismatch for ${path}! Updating...`,\n )\n const updateData: any = {\n 'field-type': fieldType,\n }\n \n // Only set model-id if it's missing or we have a better default\n if (!instructions['model-id'] && modelForId?.id) {\n updateData['model-id'] = modelForId.id\n }\n \n await payload.update({\n id: instructions.id,\n collection: PLUGIN_INSTRUCTIONS_TABLE,\n data: updateData,\n })\n instructions['field-type'] = fieldType\n if (updateData['model-id']) {\n instructions['model-id'] = updateData['model-id']\n }\n }\n\n fieldInstructionsMap[path] = {\n id: instructions.id,\n fieldType,\n }\n }\n }\n\n if (pluginConfig.debugging) {\n payload.logger.info(\n `— AI Plugin: Enabled fields map: ${JSON.stringify(fieldInstructionsMap, null, 2)}`,\n )\n payload.logger.info(`— AI Plugin: Initialized!`)\n }\n\n if (pluginConfig.generatePromptOnInit) {\n payload.logger.info(\n '\\n\\n-AI Plugin: Example prompts are added to get you started, Now go break some code 🚀🚀🚀\\n\\n',\n )\n }\n}\n"],"names":["defaultSeedPrompts","PLUGIN_INSTRUCTIONS_TABLE","CAPABILITY_MAP","id","fields","init","payload","fieldSchemaPaths","pluginConfig","generatePromptOnInit","debugging","logger","info","paths","Object","keys","docs","allInstructions","find","collection","depth","pagination","select","fieldInstructionsMap","i","length","path","type","fieldType","label","fieldLabel","relationTo","instructions","entry","seed","seedOptions","seedPrompts","undefined","generatedPrompt","modelForId","a","includes","data","prompt","create","catch","err","error","warn","updateData","update","JSON","stringify"],"mappings":"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"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// Global type definitions for @ai-stack/payloadcms
|
|
2
|
+
// This file augments the Payload types using inline type definitions
|
|
3
|
+
|
|
4
|
+
import type { GenerateObjectResult, ImagePart, JSONValue, ModelMessage } from 'ai'
|
|
5
|
+
import type { z } from 'zod'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Provider options compatible with AI SDK
|
|
9
|
+
*/
|
|
10
|
+
type ProviderOptions = Record<string, Record<string, JSONValue>>
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Base arguments for all generation methods
|
|
14
|
+
*/
|
|
15
|
+
interface PayloadGenerationBaseArgs {
|
|
16
|
+
extractAttachments?: boolean
|
|
17
|
+
maxTokens?: number
|
|
18
|
+
messages?: ModelMessage[]
|
|
19
|
+
model?: string
|
|
20
|
+
prompt: string
|
|
21
|
+
provider?: string
|
|
22
|
+
providerOptions?: ProviderOptions
|
|
23
|
+
system?: string
|
|
24
|
+
temperature?: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Arguments for generateObject - structured output generation
|
|
29
|
+
*/
|
|
30
|
+
interface PayloadGenerateObjectArgs extends PayloadGenerationBaseArgs {
|
|
31
|
+
images?: ImagePart[]
|
|
32
|
+
mode?: 'auto' | 'json' | 'tool'
|
|
33
|
+
schema?: Record<string, unknown> | z.ZodTypeAny
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Arguments for generateText - simple text generation
|
|
38
|
+
*/
|
|
39
|
+
interface PayloadGenerateTextArgs extends PayloadGenerationBaseArgs {
|
|
40
|
+
// No additional fields needed for basic text generation
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Arguments for generateMedia - image/video generation
|
|
45
|
+
*/
|
|
46
|
+
interface PayloadGenerateMediaArgs {
|
|
47
|
+
aspectRatio?: string
|
|
48
|
+
audioFormat?: string
|
|
49
|
+
callbackUrl?: string
|
|
50
|
+
duration?: number
|
|
51
|
+
fps?: number
|
|
52
|
+
images?: ImagePart[]
|
|
53
|
+
instructionId?: number | string
|
|
54
|
+
mode?: 'i2v' | 't2v'
|
|
55
|
+
model?: string
|
|
56
|
+
n?: number
|
|
57
|
+
prompt: string
|
|
58
|
+
provider?: string
|
|
59
|
+
providerOptions?: ProviderOptions
|
|
60
|
+
seed?: number
|
|
61
|
+
size?: { height: number; width: number }
|
|
62
|
+
speed?: number
|
|
63
|
+
voice?: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Result from generateMedia - can be immediate file or async job
|
|
68
|
+
*/
|
|
69
|
+
interface MediaResult {
|
|
70
|
+
// Immediate result (image generation)
|
|
71
|
+
file?: {
|
|
72
|
+
data: Buffer
|
|
73
|
+
mimetype: string
|
|
74
|
+
name: string
|
|
75
|
+
size: number
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Async job result (video generation)
|
|
79
|
+
jobId?: string
|
|
80
|
+
progress?: number
|
|
81
|
+
status?: 'completed' | 'failed' | 'queued' | 'running'
|
|
82
|
+
taskId?: string
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
declare module 'payload' {
|
|
86
|
+
interface BasePayload {
|
|
87
|
+
ai: {
|
|
88
|
+
/**
|
|
89
|
+
* @deprecated Use generateObject or generateText instead
|
|
90
|
+
* Legacy generate method for backward compatibility
|
|
91
|
+
*/
|
|
92
|
+
generate: (args: unknown) => Promise<unknown>
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Generate media (images or videos)
|
|
96
|
+
* @param args - Generation arguments including provider, model, prompt, and media options
|
|
97
|
+
* @returns Promise resolving to either a file or async job info
|
|
98
|
+
*/
|
|
99
|
+
generateMedia: (args: PayloadGenerateMediaArgs) => Promise<MediaResult>
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Generate structured output with schema validation
|
|
103
|
+
* @param args - Generation arguments including provider, model, prompt, and schema
|
|
104
|
+
* @returns Promise resolving to the generated object
|
|
105
|
+
*/
|
|
106
|
+
generateObject: <T = unknown>(args: PayloadGenerateObjectArgs) => Promise<GenerateObjectResult<T>>
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Generate simple text output
|
|
110
|
+
* @param args - Generation arguments including provider, model, and prompt
|
|
111
|
+
* @returns Promise resolving to the generated text
|
|
112
|
+
*/
|
|
113
|
+
generateText: (args: PayloadGenerateTextArgs) => Promise<string>
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get a specific model instance
|
|
117
|
+
* @param provider - Provider name (e.g., 'openai', 'anthropic')
|
|
118
|
+
* @param modelId - Model ID (e.g., 'gpt-4', 'claude-3')
|
|
119
|
+
* @param type - Model type ('text', 'image', or 'tts')
|
|
120
|
+
* @returns Promise resolving to the model instance
|
|
121
|
+
*/
|
|
122
|
+
getModel: (provider: string, modelId: string, type?: 'image' | 'text' | 'tts') => Promise<unknown>
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get the provider registry
|
|
126
|
+
* @returns Promise resolving to the provider registry
|
|
127
|
+
*/
|
|
128
|
+
getRegistry: () => Promise<Record<string, unknown>>
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Stream structured output with schema validation
|
|
132
|
+
* @param args - Generation arguments including provider, model, prompt, and schema
|
|
133
|
+
* @returns Response stream
|
|
134
|
+
*/
|
|
135
|
+
streamObject: <T = unknown>(
|
|
136
|
+
args: PayloadGenerateObjectArgs,
|
|
137
|
+
) => Promise<Response>
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Stream text output
|
|
141
|
+
* @param args - Generation arguments including provider, model, and prompt
|
|
142
|
+
* @returns Response stream
|
|
143
|
+
*/
|
|
144
|
+
streamText: (args: PayloadGenerateTextArgs) => Promise<Response>
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export {}
|
|
@@ -3,8 +3,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useAuth, useConfig, useDocumentDrawer } from '@payloadcms/ui';
|
|
4
4
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
5
5
|
import { PLUGIN_FETCH_FIELDS_ENDPOINT, PLUGIN_INSTRUCTIONS_TABLE } from '../../defaults.js';
|
|
6
|
+
import { useActiveFieldTracking } from '../../ui/Compose/hooks/useActiveFieldTracking.js';
|
|
6
7
|
import { InstructionsContext } from './context.js';
|
|
7
8
|
export const InstructionsProvider = ({ children })=>{
|
|
9
|
+
// Initialize field tracking globally so ai-plugin-active class is added on field focus
|
|
10
|
+
useActiveFieldTracking();
|
|
8
11
|
const [instructions, setInstructionsState] = useState({});
|
|
9
12
|
const [promptFields, setPromptFields] = useState([]);
|
|
10
13
|
const [activeCollection, setActiveCollection] = useState('');
|