@ai-stack/payloadcms 3.2.19-beta → 3.2.21-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/ai/models/anthropic/index.js +9 -15
  2. package/dist/ai/models/anthropic/index.js.map +1 -1
  3. package/dist/ai/models/generateObject.d.ts +11 -0
  4. package/dist/ai/models/generateObject.js +22 -0
  5. package/dist/ai/models/generateObject.js.map +1 -0
  6. package/dist/ai/models/openai/index.js +19 -17
  7. package/dist/ai/models/openai/index.js.map +1 -1
  8. package/dist/ai/prompts.js +1 -1
  9. package/dist/ai/prompts.js.map +1 -1
  10. package/dist/collections/Instructions.js +11 -0
  11. package/dist/collections/Instructions.js.map +1 -1
  12. package/dist/endpoints/fetchFields.js +1 -0
  13. package/dist/endpoints/fetchFields.js.map +1 -1
  14. package/dist/endpoints/index.js +55 -6
  15. package/dist/endpoints/index.js.map +1 -1
  16. package/dist/fields/ComposeField/ComposeField.js +8 -6
  17. package/dist/fields/ComposeField/ComposeField.js.map +1 -1
  18. package/dist/fields/ComposeField/ComposeField.jsx +32 -0
  19. package/dist/fields/LexicalEditor/ComposeFeatureComponent.d.ts +1 -1
  20. package/dist/fields/LexicalEditor/ComposeFeatureComponent.js +5 -2
  21. package/dist/fields/LexicalEditor/ComposeFeatureComponent.js.map +1 -1
  22. package/dist/fields/LexicalEditor/ComposeFeatureComponent.jsx +24 -0
  23. package/dist/fields/LexicalEditor/feature.client.jsx +21 -0
  24. package/dist/fields/PromptEditorField/PromptEditorField.jsx +42 -0
  25. package/dist/fields/SelectField/SelectField.jsx +38 -0
  26. package/dist/providers/FieldProvider/FieldProvider.d.ts +7 -4
  27. package/dist/providers/FieldProvider/FieldProvider.js +7 -6
  28. package/dist/providers/FieldProvider/FieldProvider.js.map +1 -1
  29. package/dist/providers/FieldProvider/FieldProvider.jsx +27 -0
  30. package/dist/providers/FieldProvider/useFieldProps.d.ts +1 -1
  31. package/dist/providers/FieldProvider/useFieldProps.js +2 -2
  32. package/dist/providers/FieldProvider/useFieldProps.js.map +1 -1
  33. package/dist/providers/InstructionsProvider/InstructionsProvider.jsx +45 -0
  34. package/dist/types.d.ts +4 -4
  35. package/dist/types.js.map +1 -1
  36. package/dist/ui/Compose/Compose.js +12 -81
  37. package/dist/ui/Compose/Compose.js.map +1 -1
  38. package/dist/ui/Compose/Compose.jsx +141 -0
  39. package/dist/ui/Compose/UndoRedoActions.jsx +34 -0
  40. package/dist/ui/Compose/compose.module.css +99 -12
  41. package/dist/ui/Compose/hooks/menu/Item.jsx +15 -0
  42. package/dist/ui/Compose/hooks/menu/TranslateMenu.jsx +58 -0
  43. package/dist/ui/Compose/hooks/menu/items.jsx +10 -0
  44. package/dist/ui/Compose/hooks/menu/useMenu.js +1 -1
  45. package/dist/ui/Compose/hooks/menu/useMenu.js.map +1 -1
  46. package/dist/ui/Compose/hooks/menu/useMenu.jsx +89 -0
  47. package/dist/ui/Compose/hooks/useActiveFieldTracking.d.ts +5 -0
  48. package/dist/ui/Compose/hooks/useActiveFieldTracking.js +148 -0
  49. package/dist/ui/Compose/hooks/useActiveFieldTracking.js.map +1 -0
  50. package/dist/ui/Compose/hooks/useGenerate.js +46 -79
  51. package/dist/ui/Compose/hooks/useGenerate.js.map +1 -1
  52. package/dist/ui/Icons/Icons.jsx +78 -0
  53. package/dist/ui/Icons/LottieAnimation.jsx +64 -0
  54. package/dist/utilities/extractPromptAttachments.d.ts +2 -2
  55. package/dist/utilities/extractPromptAttachments.js.map +1 -1
  56. package/dist/utilities/fieldToJsonSchema.d.ts +37 -0
  57. package/dist/utilities/fieldToJsonSchema.js +274 -0
  58. package/dist/utilities/fieldToJsonSchema.js.map +1 -0
  59. package/dist/utilities/getFieldBySchemaPath.d.ts +12 -1
  60. package/dist/utilities/getFieldBySchemaPath.js +63 -29
  61. package/dist/utilities/getFieldBySchemaPath.js.map +1 -1
  62. package/package.json +2 -1
  63. package/dist/ai/models/anthropic/generateRichText.d.ts +0 -1
  64. package/dist/ai/models/anthropic/generateRichText.js +0 -36
  65. package/dist/ai/models/anthropic/generateRichText.js.map +0 -1
  66. package/dist/ai/models/openai/generateRichText.d.ts +0 -1
  67. package/dist/ai/models/openai/generateRichText.js +0 -37
  68. package/dist/ai/models/openai/generateRichText.js.map +0 -1
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { JSONSchema } from 'openai/lib/jsonschema'\nimport type { ImageGenerateParams } from 'openai/resources/images'\nimport type {\n CollectionConfig,\n CollectionSlug,\n DataFromCollectionSlug,\n Endpoint,\n Field,\n File,\n GlobalConfig,\n GroupField,\n PayloadRequest,\n TypedCollection,\n} from 'payload'\nimport type { CSSProperties, MouseEventHandler } from 'react'\n\nimport type {PLUGIN_INSTRUCTIONS_TABLE} from \"./defaults.js\";\n\nexport interface PluginConfigAccess {\n /**\n * Control access to AI generation features (generate text, images, audio)\n * @default () => !!req.user (requires authentication)\n */\n generate?: ({ req }: { req: PayloadRequest }) => Promise<boolean> | boolean\n /**\n * Control access to AI settings/configuration\n * @default () => !!req.user (requires authentication)\n */\n settings?: ({ req }: { req: PayloadRequest }) => Promise<boolean> | boolean\n}\n\nexport interface PluginOptions {\n /**\n * Provide local tags to filter language options from the Translate Menu\n * Check for the available local tags,\n * visit: https://www.npmjs.com/package/locale-codes\n * Example: [\"en-US\", \"zh-SG\", \"zh-CN\", \"en\"]\n */\n enabledLanguages?: string[]\n}\n\nexport type PluginConfigMediaUploadFunction = (\n result: { data: Record<any, any>; file: File },\n {\n collection,\n request,\n }: {\n collection: CollectionSlug\n request: PayloadRequest\n },\n) => Promise<DataFromCollectionSlug<CollectionSlug>>\n\nexport interface PluginConfig {\n /**\n * Access control configuration for AI features\n * By default, all AI features require authentication\n */\n access?: PluginConfigAccess\n collections: {\n [key: CollectionSlug]: boolean\n }\n debugging?: boolean\n disableSponsorMessage?: boolean\n editorConfig?: { nodes: JSONSchema[] }\n fields?: Field[]\n generatePromptOnInit?: boolean\n generationModels?: ((defaultModels: GenerationModel[]) => GenerationModel[]) | GenerationModel[]\n globals?: {\n [key: GlobalConfig['slug']]: boolean\n }\n interfaceName?: string\n mediaUpload?: PluginConfigMediaUploadFunction\n options?: PluginOptions\n // Override the instructions collection config\n overrideInstructions?: Partial<CollectionConfig>\n promptFields?: PromptField[]\n /**\n * Custom action prompts for AI text generation\n * If not provided, uses default prompts\n * You can access default prompts by importing { defaultPrompts } from '@ai-stack/payloadcms'\n */\n prompts?: ActionPrompt[]\n /**\n * Custom seed prompt function for generating field-specific prompts\n * If not provided, uses default seed prompt function\n * You can access default seed prompts by importing { defaultSeedPrompts } from '@ai-stack/payloadcms'\n */\n seedPrompts?: SeedPromptFunction\n uploadCollectionSlug?: CollectionSlug\n}\n\nexport interface GenerationModel {\n fields: string[]\n generateText?: (prompt: string, system: string) => Promise<string>\n handler?: (prompt: string, options: any) => Promise<any> | Response\n id: string\n name: string\n output: 'audio' | 'file' | 'image' | 'json' | 'text' | 'video'\n settings?: GroupField\n supportsPromptOptimization?: boolean\n}\n\nexport interface GenerationConfig {\n models: GenerationModel[]\n provider: string\n}\n\nexport type GenerateTextarea<T = any> = (args: {\n collectionSlug: CollectionSlug\n doc: T\n documentId?: number | string\n locale?: string\n options?: any\n}) => Promise<string> | string\n\nexport interface Endpoints {\n textarea: Omit<Endpoint, 'root'>\n upload: Omit<Endpoint, 'root'>\n}\n\nexport type ActionMenuItems =\n | 'Compose'\n | 'Expand'\n | 'Proofread'\n | 'Rephrase'\n | 'Settings'\n | 'Simplify'\n | 'Summarize'\n | 'Tone'\n | 'Translate'\n\nexport type ActionPromptOptions = {\n layout?: string\n locale?: string\n prompt?: string\n systemPrompt?: string\n}\n\nexport type ActionPrompt = {\n layout?: (options?: ActionPromptOptions) => string\n name: ActionMenuItems\n system: (options: ActionPromptOptions) => string\n}\n\nexport type SeedPromptOptions = {\n fieldLabel: string\n fieldSchemaPaths: Record<string, any>\n fieldType: string\n path: string\n}\n\nexport type SeedPromptData = Omit<TypedCollection[typeof PLUGIN_INSTRUCTIONS_TABLE], 'createdAt' | 'id' | 'updatedAt'>\n\nexport type SeedPromptResult = {\n data?: SeedPromptData\n prompt: string\n system: string\n} | {\n data?: SeedPromptData\n} | false | undefined | void\n\nexport type SeedPromptFunction = (options: SeedPromptOptions) => Promise<SeedPromptResult> | SeedPromptResult\n\nexport type ActionMenuEvents =\n | 'onCompose'\n | 'onExpand'\n | 'onProofread'\n | 'onRephrase'\n | 'onSettings'\n | 'onSimplify'\n | 'onSummarize'\n | 'onTone'\n | 'onTranslate'\n\nexport type UseMenuEvents = {\n [key in ActionMenuEvents]?: (data?: unknown) => void\n}\n\nexport type UseMenuOptions = {\n isConfigAllowed: boolean\n}\n\nexport type BaseItemProps<T = any> = {\n children?: React.ReactNode\n disabled?: boolean\n hideIcon?: boolean\n isActive?: boolean\n isMenu?: boolean\n onClick: (data?: unknown) => void\n onMouseEnter?: MouseEventHandler<T> | undefined\n onMouseLeave?: MouseEventHandler<T> | undefined\n style?: CSSProperties | undefined\n title?: string\n}\n\nexport type ImageReference = {\n data: Blob\n name: string\n size: number\n type: string\n url: string\n}\n\nexport type GenerateImageParams = {\n images?: ImageReference[]\n size?: ImageGenerateParams['size']\n style?: ImageGenerateParams['style']\n version?: ImageGenerateParams['model']\n}\n\nexport type SerializedPromptField = {\n collections?: (CollectionSlug)[]\n name: string\n}\n\nexport type PromptFieldGetterContext = {\n collection: CollectionSlug\n type: string\n}\n\nexport type PromptField = {\n // If not provided, the value will be returned from the data object as-is\n getter?: (data: object, ctx: PromptFieldGetterContext) => Promise<string> | string\n} & SerializedPromptField\n"],"names":[],"mappings":"AA4NA,WAGyB"}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { JSONSchema } from 'openai/lib/jsonschema'\nimport type { ImageGenerateParams } from 'openai/resources/images'\nimport type {\n CollectionConfig,\n CollectionSlug,\n DataFromCollectionSlug,\n Endpoint,\n Field,\n File,\n GlobalConfig,\n GroupField,\n PayloadRequest,\n TypedCollection,\n} from 'payload'\nimport type { CSSProperties, MouseEventHandler } from 'react'\n\nimport type {PLUGIN_INSTRUCTIONS_TABLE} from \"./defaults.js\";\n\nexport interface PluginConfigAccess {\n /**\n * Control access to AI generation features (generate text, images, audio)\n * @default () => !!req.user (requires authentication)\n */\n generate?: ({ req }: { req: PayloadRequest }) => boolean | Promise<boolean>\n /**\n * Control access to AI settings/configuration\n * @default () => !!req.user (requires authentication)\n */\n settings?: ({ req }: { req: PayloadRequest }) => boolean | Promise<boolean>\n}\n\nexport interface PluginOptions {\n /**\n * Provide local tags to filter language options from the Translate Menu\n * Check for the available local tags,\n * visit: https://www.npmjs.com/package/locale-codes\n * Example: [\"en-US\", \"zh-SG\", \"zh-CN\", \"en\"]\n */\n enabledLanguages?: string[]\n}\n\nexport type PluginConfigMediaUploadFunction = (\n result: { data: Record<any, any>; file: File },\n {\n collection,\n request,\n }: {\n collection: CollectionSlug\n request: PayloadRequest\n },\n) => Promise<DataFromCollectionSlug<CollectionSlug>>\n\nexport interface PluginConfig {\n /**\n * Access control configuration for AI features\n * By default, all AI features require authentication\n */\n access?: PluginConfigAccess\n collections: {\n [key: CollectionSlug]: boolean\n }\n debugging?: boolean\n disableSponsorMessage?: boolean\n editorConfig?: { nodes: JSONSchema[] }\n fields?: Field[]\n generatePromptOnInit?: boolean\n generationModels?: ((defaultModels: GenerationModel[]) => GenerationModel[]) | GenerationModel[]\n globals?: {\n [key: GlobalConfig['slug']]: boolean\n }\n interfaceName?: string\n mediaUpload?: PluginConfigMediaUploadFunction\n options?: PluginOptions\n // Override the instructions collection config\n overrideInstructions?: Partial<CollectionConfig>\n promptFields?: PromptField[]\n /**\n * Custom action prompts for AI text generation\n * If not provided, uses default prompts\n * You can access default prompts by importing { defaultPrompts } from '@ai-stack/payloadcms'\n */\n prompts?: ActionPrompt[]\n /**\n * Custom seed prompt function for generating field-specific prompts\n * If not provided, uses default seed prompt function\n * You can access default seed prompts by importing { defaultSeedPrompts } from '@ai-stack/payloadcms'\n */\n seedPrompts?: SeedPromptFunction\n uploadCollectionSlug?: CollectionSlug\n}\n\nexport interface GenerationModel {\n fields: string[]\n generateText?: (prompt: string, system: string) => Promise<string>\n handler?: (prompt: string, options: any) => Promise<any> | Response\n id: string\n name: string\n output: 'audio' | 'file' | 'image' | 'json' | 'text' | 'video'\n settings?: GroupField\n supportsPromptOptimization?: boolean\n}\n\nexport interface GenerationConfig {\n models: GenerationModel[]\n provider: string\n}\n\nexport type GenerateTextarea<T = any> = (args: {\n collectionSlug: CollectionSlug\n doc: T\n documentId?: number | string\n locale?: string\n options?: any\n}) => Promise<string> | string\n\nexport interface Endpoints {\n textarea: Omit<Endpoint, 'root'>\n upload: Omit<Endpoint, 'root'>\n}\n\nexport type ActionMenuItems =\n | 'Compose'\n | 'Expand'\n | 'Proofread'\n | 'Rephrase'\n | 'Settings'\n | 'Simplify'\n | 'Summarize'\n | 'Tone'\n | 'Translate'\n\nexport type ActionPromptOptions = {\n layout?: string\n locale?: string\n prompt?: string\n systemPrompt?: string\n}\n\nexport type ActionPrompt = {\n layout?: (options?: ActionPromptOptions) => string\n name: ActionMenuItems\n system: (options: ActionPromptOptions) => string\n}\n\nexport type SeedPromptOptions = {\n fieldLabel: string\n fieldSchemaPaths: Record<string, any>\n fieldType: string\n path: string\n}\n\nexport type SeedPromptData = Omit<TypedCollection[typeof PLUGIN_INSTRUCTIONS_TABLE], 'createdAt' | 'id' | 'updatedAt'>\n\nexport type SeedPromptResult = {\n data?: SeedPromptData\n} | {\n data?: SeedPromptData\n prompt: string\n system: string\n} | false | undefined | void\n\nexport type SeedPromptFunction = (options: SeedPromptOptions) => Promise<SeedPromptResult> | SeedPromptResult\n\nexport type ActionMenuEvents =\n | 'onCompose'\n | 'onExpand'\n | 'onProofread'\n | 'onRephrase'\n | 'onSettings'\n | 'onSimplify'\n | 'onSummarize'\n | 'onTone'\n | 'onTranslate'\n\nexport type UseMenuEvents = {\n [key in ActionMenuEvents]?: (data?: unknown) => void\n}\n\nexport type UseMenuOptions = {\n isConfigAllowed: boolean\n}\n\nexport type BaseItemProps<T = any> = {\n children?: React.ReactNode\n disabled?: boolean\n hideIcon?: boolean\n isActive?: boolean\n isMenu?: boolean\n onClick: (data?: unknown) => void\n onMouseEnter?: MouseEventHandler<T> | undefined\n onMouseLeave?: MouseEventHandler<T> | undefined\n style?: CSSProperties | undefined\n title?: string\n}\n\nexport type ImageReference = {\n data: Blob\n name: string\n size: number\n type: string\n url: string\n}\n\nexport type GenerateImageParams = {\n images?: ImageReference[]\n size?: ImageGenerateParams['size']\n style?: ImageGenerateParams['style']\n version?: ImageGenerateParams['model']\n}\n\nexport type SerializedPromptField = {\n collections?: (CollectionSlug)[]\n name: string\n}\n\nexport type PromptFieldGetterContext = {\n collection: CollectionSlug\n type: string\n}\n\nexport type PromptField = {\n // If not provided, the value will be returned from the data object as-is\n getter?: (data: object, ctx: PromptFieldGetterContext) => Promise<string> | string\n} & SerializedPromptField\n"],"names":[],"mappings":"AA4NA,WAGyB"}
@@ -2,92 +2,24 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useEditorConfigContext } from '@payloadcms/richtext-lexical/client';
4
4
  import { Popup, useDocumentDrawer, useField } from '@payloadcms/ui';
5
- import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
5
+ import React, { useCallback, useMemo, useState } from 'react';
6
6
  import { PLUGIN_INSTRUCTIONS_TABLE } from '../../defaults.js';
7
7
  import { setSafeLexicalState } from '../../utilities/setSafeLexicalState.js';
8
8
  import { PluginIcon } from '../Icons/Icons.js';
9
9
  import styles from './compose.module.css';
10
10
  import { useMenu } from './hooks/menu/useMenu.js';
11
+ import { useActiveFieldTracking } from './hooks/useActiveFieldTracking.js';
11
12
  import { useGenerate } from './hooks/useGenerate.js';
12
13
  import { UndoRedoActions } from './UndoRedoActions.js';
13
- function findParentWithClass(element, className) {
14
- // Base case: if the element is null, or we've reached the top of the DOM
15
- if (!element || element === document.body) {
16
- return null;
17
- }
18
- // Check if the current element has the class we're looking for
19
- if (element.classList.contains(className)) {
20
- return element;
21
- }
22
- // Recursively call the function on the parent element
23
- return findParentWithClass(element.parentElement, className);
24
- }
25
14
  export const Compose = ({ descriptionProps, instructionId, isConfigAllowed })=>{
26
15
  const [DocumentDrawer, _, { closeDrawer, openDrawer }] = useDocumentDrawer({
27
16
  id: instructionId,
28
17
  collectionSlug: PLUGIN_INSTRUCTIONS_TABLE
29
18
  });
30
- const fieldType = descriptionProps?.field?.type;
31
19
  const pathFromContext = descriptionProps?.path;
32
- const schemaPath = descriptionProps?.schemaPath;
33
- const { editor: lexicalEditor, editorContainerRef } = useEditorConfigContext();
34
- // The below snippet is used to show/hide the action menu on AI-enabled fields
35
- const [input, setInput] = useState(null);
36
- const actionsRef = useRef(null);
37
- // Set input element for current field
38
- useEffect(()=>{
39
- if (!actionsRef.current) {
40
- return;
41
- }
42
- if (!pathFromContext) {
43
- return;
44
- }
45
- const fieldId = `field-${pathFromContext.replace(/\./g, '__')}`;
46
- const inputElement = document.getElementById(fieldId);
47
- if (!inputElement && fieldType === 'richText') {
48
- setInput(editorContainerRef.current);
49
- } else {
50
- actionsRef.current?.setAttribute('for', fieldId);
51
- setInput(inputElement);
52
- }
53
- }, [
54
- pathFromContext,
55
- schemaPath,
56
- actionsRef,
57
- editorContainerRef,
58
- fieldType
59
- ]);
60
- // Show or hide actions menu on field
61
- useEffect(()=>{
62
- if (!input || !actionsRef.current) {
63
- return;
64
- }
65
- actionsRef.current?.classList.add(styles.actions_hidden);
66
- // Create the handler function
67
- const clickHandler = (event)=>{
68
- document.querySelectorAll('.ai-plugin-active')?.forEach((element)=>{
69
- const actionElement = element.querySelector(`.${styles.actions}`);
70
- if (actionElement) {
71
- actionElement.classList.add(styles.actions_hidden);
72
- element.classList.remove('ai-plugin-active');
73
- }
74
- });
75
- actionsRef.current?.classList.remove(styles.actions_hidden);
76
- const parentWithClass = findParentWithClass(event.target, 'field-type');
77
- if (parentWithClass) {
78
- parentWithClass.classList.add('ai-plugin-active');
79
- }
80
- };
81
- // Add the event listener
82
- input?.addEventListener('click', clickHandler);
83
- // Clean up the event listener when the component unmounts or input changes
84
- return ()=>{
85
- input?.removeEventListener('click', clickHandler);
86
- };
87
- }, [
88
- input,
89
- actionsRef
90
- ]);
20
+ const { editor: lexicalEditor } = useEditorConfigContext();
21
+ // Initialize global active-field tracking
22
+ useActiveFieldTracking();
91
23
  const [isProcessing, setIsProcessing] = useState(false);
92
24
  const { generate, isLoading, stop } = useGenerate({
93
25
  instructionId
@@ -99,7 +31,7 @@ export const Compose = ({ descriptionProps, instructionId, isConfigAllowed })=>{
99
31
  generate({
100
32
  action: 'Compose'
101
33
  }).catch((reason)=>{
102
- console.error("Compose : ", reason);
34
+ console.error('Compose : ', reason);
103
35
  }).finally(()=>{
104
36
  setIsProcessing(false);
105
37
  });
@@ -109,7 +41,7 @@ export const Compose = ({ descriptionProps, instructionId, isConfigAllowed })=>{
109
41
  generate({
110
42
  action: 'Expand'
111
43
  }).catch((reason)=>{
112
- console.error("Compose : ", reason);
44
+ console.error('Compose : ', reason);
113
45
  }).finally(()=>{
114
46
  setIsProcessing(false);
115
47
  });
@@ -119,7 +51,7 @@ export const Compose = ({ descriptionProps, instructionId, isConfigAllowed })=>{
119
51
  generate({
120
52
  action: 'Proofread'
121
53
  }).catch((reason)=>{
122
- console.error("Compose : ", reason);
54
+ console.error('Compose : ', reason);
123
55
  }).finally(()=>{
124
56
  setIsProcessing(false);
125
57
  });
@@ -129,7 +61,7 @@ export const Compose = ({ descriptionProps, instructionId, isConfigAllowed })=>{
129
61
  generate({
130
62
  action: 'Rephrase'
131
63
  }).catch((reason)=>{
132
- console.error("Compose : ", reason);
64
+ console.error('Compose : ', reason);
133
65
  }).finally(()=>{
134
66
  setIsProcessing(false);
135
67
  });
@@ -140,7 +72,7 @@ export const Compose = ({ descriptionProps, instructionId, isConfigAllowed })=>{
140
72
  generate({
141
73
  action: 'Simplify'
142
74
  }).catch((reason)=>{
143
- console.error("Compose : ", reason);
75
+ console.error('Compose : ', reason);
144
76
  }).finally(()=>{
145
77
  setIsProcessing(false);
146
78
  });
@@ -150,7 +82,7 @@ export const Compose = ({ descriptionProps, instructionId, isConfigAllowed })=>{
150
82
  generate({
151
83
  action: 'Summarize'
152
84
  }).catch((reason)=>{
153
- console.error("Compose : ", reason);
85
+ console.error('Compose : ', reason);
154
86
  }).finally(()=>{
155
87
  setIsProcessing(false);
156
88
  });
@@ -161,7 +93,7 @@ export const Compose = ({ descriptionProps, instructionId, isConfigAllowed })=>{
161
93
  action: 'Translate',
162
94
  params: data
163
95
  }).catch((reason)=>{
164
- console.error("Compose : ", reason);
96
+ console.error('Compose : ', reason);
165
97
  }).finally(()=>{
166
98
  setIsProcessing(false);
167
99
  });
@@ -204,7 +136,6 @@ export const Compose = ({ descriptionProps, instructionId, isConfigAllowed })=>{
204
136
  return /*#__PURE__*/ _jsxs("label", {
205
137
  className: `payloadai-compose__actions ${styles.actions}`,
206
138
  onClick: (e)=>e.preventDefault(),
207
- ref: actionsRef,
208
139
  role: "presentation",
209
140
  children: [
210
141
  /*#__PURE__*/ _jsx(DocumentDrawer, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/Compose/Compose.tsx"],"sourcesContent":["'use client'\n\nimport type { ClientField } from 'payload'\nimport type { FC } from 'react'\n\nimport { useEditorConfigContext } from '@payloadcms/richtext-lexical/client'\nimport { FieldDescription, Popup, useDocumentDrawer, useField } from '@payloadcms/ui'\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'\n\nimport { PLUGIN_INSTRUCTIONS_TABLE } from '../../defaults.js'\nimport { setSafeLexicalState } from '../../utilities/setSafeLexicalState.js'\nimport { PluginIcon } from '../Icons/Icons.js'\nimport styles from './compose.module.css'\nimport { useMenu } from './hooks/menu/useMenu.js'\nimport { useGenerate } from './hooks/useGenerate.js'\nimport { UndoRedoActions } from './UndoRedoActions.js'\n\nfunction findParentWithClass(element: HTMLElement | null, className: string): HTMLElement | null {\n // Base case: if the element is null, or we've reached the top of the DOM\n if (!element || element === document.body) {\n return null\n }\n\n // Check if the current element has the class we're looking for\n if (element.classList.contains(className)) {\n return element\n }\n\n // Recursively call the function on the parent element\n return findParentWithClass(element.parentElement, className)\n}\n\ntype ComposeProps = {\n descriptionProps?: {\n field: ClientField\n path: string\n schemaPath: string\n }\n instructionId: string\n isConfigAllowed: boolean\n}\n\nexport const Compose: FC<ComposeProps> = ({ descriptionProps, instructionId, isConfigAllowed }) => {\n const [DocumentDrawer, _, { closeDrawer, openDrawer }] = useDocumentDrawer({\n id: instructionId,\n collectionSlug: PLUGIN_INSTRUCTIONS_TABLE,\n })\n\n const fieldType = descriptionProps?.field?.type\n const pathFromContext = descriptionProps?.path\n const schemaPath = descriptionProps?.schemaPath\n const { editor: lexicalEditor, editorContainerRef } = useEditorConfigContext()\n\n // The below snippet is used to show/hide the action menu on AI-enabled fields\n const [input, setInput] = useState<HTMLElement | null>(null)\n const actionsRef = useRef<HTMLLabelElement | null>(null)\n\n // Set input element for current field\n useEffect(() => {\n if (!actionsRef.current) {\n return\n }\n\n if (!pathFromContext) {\n return\n }\n\n const fieldId = `field-${pathFromContext.replace(/\\./g, '__')}`\n const inputElement = document.getElementById(fieldId)\n\n if (!inputElement && fieldType === 'richText') {\n setInput(editorContainerRef.current as HTMLElement | null)\n } else {\n actionsRef.current?.setAttribute('for', fieldId)\n setInput(inputElement)\n }\n }, [pathFromContext, schemaPath, actionsRef, editorContainerRef, fieldType])\n\n // Show or hide actions menu on field\n useEffect(() => {\n if (!input || !actionsRef.current) {\n return\n }\n\n actionsRef.current?.classList.add(styles.actions_hidden)\n\n // Create the handler function\n const clickHandler = (event: MouseEvent) => {\n document.querySelectorAll('.ai-plugin-active')?.forEach((element) => {\n const actionElement = (element as HTMLElement).querySelector(`.${styles.actions}`)\n if (actionElement) {\n actionElement.classList.add(styles.actions_hidden)\n element.classList.remove('ai-plugin-active')\n }\n })\n\n actionsRef.current?.classList.remove(styles.actions_hidden)\n const parentWithClass = findParentWithClass(event.target as HTMLElement, 'field-type')\n if (parentWithClass) {\n parentWithClass.classList.add('ai-plugin-active')\n }\n }\n\n // Add the event listener\n input?.addEventListener('click', clickHandler)\n\n // Clean up the event listener when the component unmounts or input changes\n return () => {\n input?.removeEventListener('click', clickHandler)\n }\n }, [input, actionsRef])\n\n const [isProcessing, setIsProcessing] = useState<boolean>(false)\n const { generate, isLoading, stop } = useGenerate({ instructionId })\n\n const { ActiveComponent, Menu } = useMenu(\n {\n onCompose: () => {\n console.log('Composing...')\n setIsProcessing(true)\n generate({\n action: 'Compose',\n }).catch((reason)=>{\n console.error(\"Compose : \",reason)\n }).finally(() => {\n setIsProcessing(false)\n })\n },\n onExpand: () => {\n console.log('Expanding...')\n generate({\n action: 'Expand',\n }).catch((reason)=>{\n console.error(\"Compose : \",reason)\n }).finally(() => {\n setIsProcessing(false)\n })\n },\n onProofread: () => {\n console.log('Proofreading...')\n generate({\n action: 'Proofread',\n }).catch((reason)=>{\n console.error(\"Compose : \",reason)\n }).finally(() => {\n setIsProcessing(false)\n })\n },\n onRephrase: () => {\n console.log('Rephrasing...')\n generate({\n action: 'Rephrase',\n }).catch((reason)=>{\n console.error(\"Compose : \",reason)\n }).finally(() => {\n setIsProcessing(false)\n })\n },\n onSettings: isConfigAllowed ? openDrawer : undefined,\n onSimplify: () => {\n console.log('Simplifying...')\n generate({\n action: 'Simplify',\n }).catch((reason)=>{\n console.error(\"Compose : \",reason)\n }).finally(() => {\n setIsProcessing(false)\n })\n },\n onSummarize: () => {\n console.log('Summarizing...')\n generate({\n action: 'Summarize',\n }).catch((reason)=>{\n console.error(\"Compose : \",reason)\n }).finally(() => {\n setIsProcessing(false)\n })\n },\n onTranslate: (data) => {\n console.log('Translating...')\n generate({\n action: 'Translate',\n params: data,\n }).catch((reason)=>{\n console.error(\"Compose : \",reason)\n }).finally(() => {\n setIsProcessing(false)\n })\n },\n },\n {\n isConfigAllowed,\n },\n )\n\n const { setValue } = useField<string>({\n path: pathFromContext,\n })\n\n const setIfValueIsLexicalState = useCallback((val: any) => {\n if (val && typeof val === 'object' && 'root' in val && lexicalEditor) {\n setSafeLexicalState(JSON.stringify(val), lexicalEditor)\n }\n\n // DO NOT PROVIDE lexicalEditor as a dependency, it freaks out and does not update the editor after first undo/redo\n }, [])\n\n const popupRender = useCallback(\n ({ close }: { close: () => void }) => {\n return <Menu isLoading={isProcessing || isLoading} onClose={close} />\n },\n [isProcessing, isLoading, Menu],\n )\n\n const memoizedPopup = useMemo(() => {\n return (\n <Popup\n button={<PluginIcon isLoading={isProcessing || isLoading} />}\n render={popupRender}\n verticalAlign=\"bottom\"\n />\n )\n }, [popupRender, isProcessing, isLoading])\n\n return (\n <label\n className={`payloadai-compose__actions ${styles.actions}`}\n onClick={(e) => e.preventDefault()}\n ref={actionsRef}\n role=\"presentation\"\n >\n <DocumentDrawer\n onSave={() => {\n closeDrawer()\n }}\n />\n {memoizedPopup}\n <ActiveComponent isLoading={isProcessing || isLoading} stop={stop} />\n <UndoRedoActions\n onChange={(val) => {\n setValue(val)\n setIfValueIsLexicalState(val)\n }}\n />\n </label>\n )\n}\n"],"names":["useEditorConfigContext","Popup","useDocumentDrawer","useField","React","useCallback","useEffect","useMemo","useRef","useState","PLUGIN_INSTRUCTIONS_TABLE","setSafeLexicalState","PluginIcon","styles","useMenu","useGenerate","UndoRedoActions","findParentWithClass","element","className","document","body","classList","contains","parentElement","Compose","descriptionProps","instructionId","isConfigAllowed","DocumentDrawer","_","closeDrawer","openDrawer","id","collectionSlug","fieldType","field","type","pathFromContext","path","schemaPath","editor","lexicalEditor","editorContainerRef","input","setInput","actionsRef","current","fieldId","replace","inputElement","getElementById","setAttribute","add","actions_hidden","clickHandler","event","querySelectorAll","forEach","actionElement","querySelector","actions","remove","parentWithClass","target","addEventListener","removeEventListener","isProcessing","setIsProcessing","generate","isLoading","stop","ActiveComponent","Menu","onCompose","console","log","action","catch","reason","error","finally","onExpand","onProofread","onRephrase","onSettings","undefined","onSimplify","onSummarize","onTranslate","data","params","setValue","setIfValueIsLexicalState","val","JSON","stringify","popupRender","close","onClose","memoizedPopup","button","render","verticalAlign","label","onClick","e","preventDefault","ref","role","onSave","onChange"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/ui/Compose/Compose.tsx"],"sourcesContent":["'use client'\n\nimport type { ClientField } from 'payload'\nimport type { FC } from 'react'\n\nimport { useEditorConfigContext } from '@payloadcms/richtext-lexical/client'\nimport { Popup, useDocumentDrawer, useField } from '@payloadcms/ui'\nimport React, { useCallback, useMemo, useState } from 'react'\n\nimport { PLUGIN_INSTRUCTIONS_TABLE } from '../../defaults.js'\nimport { setSafeLexicalState } from '../../utilities/setSafeLexicalState.js'\nimport { PluginIcon } from '../Icons/Icons.js'\nimport styles from './compose.module.css'\nimport { useMenu } from './hooks/menu/useMenu.js'\nimport { useActiveFieldTracking } from './hooks/useActiveFieldTracking.js'\nimport { useGenerate } from './hooks/useGenerate.js'\nimport { UndoRedoActions } from './UndoRedoActions.js'\n\ntype ComposeProps = {\n descriptionProps?: {\n field: ClientField\n path: string\n schemaPath: string\n }\n instructionId: string\n isConfigAllowed: boolean\n}\n\nexport const Compose: FC<ComposeProps> = ({ descriptionProps, instructionId, isConfigAllowed }) => {\n const [DocumentDrawer, _, { closeDrawer, openDrawer }] = useDocumentDrawer({\n id: instructionId,\n collectionSlug: PLUGIN_INSTRUCTIONS_TABLE,\n })\n\n const pathFromContext = descriptionProps?.path\n const { editor: lexicalEditor } = useEditorConfigContext()\n\n // Initialize global active-field tracking\n useActiveFieldTracking()\n\n const [isProcessing, setIsProcessing] = useState<boolean>(false)\n const { generate, isLoading, stop } = useGenerate({ instructionId })\n\n const { ActiveComponent, Menu } = useMenu(\n {\n onCompose: () => {\n console.log('Composing...')\n setIsProcessing(true)\n generate({\n action: 'Compose',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n },\n onExpand: () => {\n console.log('Expanding...')\n generate({\n action: 'Expand',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n },\n onProofread: () => {\n console.log('Proofreading...')\n generate({\n action: 'Proofread',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n },\n onRephrase: () => {\n console.log('Rephrasing...')\n generate({\n action: 'Rephrase',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n },\n onSettings: isConfigAllowed ? openDrawer : undefined,\n onSimplify: () => {\n console.log('Simplifying...')\n generate({\n action: 'Simplify',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n },\n onSummarize: () => {\n console.log('Summarizing...')\n generate({\n action: 'Summarize',\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n },\n onTranslate: (data) => {\n console.log('Translating...')\n generate({\n action: 'Translate',\n params: data,\n })\n .catch((reason) => {\n console.error('Compose : ', reason)\n })\n .finally(() => {\n setIsProcessing(false)\n })\n },\n },\n {\n isConfigAllowed,\n },\n )\n\n const { setValue } = useField<string>({\n path: pathFromContext,\n })\n\n const setIfValueIsLexicalState = useCallback((val: any) => {\n if (val && typeof val === 'object' && 'root' in val && lexicalEditor) {\n setSafeLexicalState(JSON.stringify(val), lexicalEditor)\n }\n\n // DO NOT PROVIDE lexicalEditor as a dependency, it freaks out and does not update the editor after first undo/redo\n }, [])\n\n const popupRender = useCallback(\n ({ close }: { close: () => void }) => {\n return <Menu isLoading={isProcessing || isLoading} onClose={close} />\n },\n [isProcessing, isLoading, Menu],\n )\n\n const memoizedPopup = useMemo(() => {\n return (\n <Popup\n button={<PluginIcon isLoading={isProcessing || isLoading} />}\n render={popupRender}\n verticalAlign=\"bottom\"\n />\n )\n }, [popupRender, isProcessing, isLoading])\n\n return (\n <label\n className={`payloadai-compose__actions ${styles.actions}`}\n onClick={(e) => e.preventDefault()}\n role=\"presentation\"\n >\n <DocumentDrawer\n onSave={() => {\n closeDrawer()\n }}\n />\n {memoizedPopup}\n <ActiveComponent isLoading={isProcessing || isLoading} stop={stop} />\n <UndoRedoActions\n onChange={(val) => {\n setValue(val)\n setIfValueIsLexicalState(val)\n }}\n />\n </label>\n )\n}\n"],"names":["useEditorConfigContext","Popup","useDocumentDrawer","useField","React","useCallback","useMemo","useState","PLUGIN_INSTRUCTIONS_TABLE","setSafeLexicalState","PluginIcon","styles","useMenu","useActiveFieldTracking","useGenerate","UndoRedoActions","Compose","descriptionProps","instructionId","isConfigAllowed","DocumentDrawer","_","closeDrawer","openDrawer","id","collectionSlug","pathFromContext","path","editor","lexicalEditor","isProcessing","setIsProcessing","generate","isLoading","stop","ActiveComponent","Menu","onCompose","console","log","action","catch","reason","error","finally","onExpand","onProofread","onRephrase","onSettings","undefined","onSimplify","onSummarize","onTranslate","data","params","setValue","setIfValueIsLexicalState","val","JSON","stringify","popupRender","close","onClose","memoizedPopup","button","render","verticalAlign","label","className","actions","onClick","e","preventDefault","role","onSave","onChange"],"mappings":"AAAA;;AAKA,SAASA,sBAAsB,QAAQ,sCAAqC;AAC5E,SAASC,KAAK,EAAEC,iBAAiB,EAAEC,QAAQ,QAAQ,iBAAgB;AACnE,OAAOC,SAASC,WAAW,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,QAAO;AAE7D,SAASC,yBAAyB,QAAQ,oBAAmB;AAC7D,SAASC,mBAAmB,QAAQ,yCAAwC;AAC5E,SAASC,UAAU,QAAQ,oBAAmB;AAC9C,OAAOC,YAAY,uBAAsB;AACzC,SAASC,OAAO,QAAQ,0BAAyB;AACjD,SAASC,sBAAsB,QAAQ,oCAAmC;AAC1E,SAASC,WAAW,QAAQ,yBAAwB;AACpD,SAASC,eAAe,QAAQ,uBAAsB;AAYtD,OAAO,MAAMC,UAA4B,CAAC,EAAEC,gBAAgB,EAAEC,aAAa,EAAEC,eAAe,EAAE;IAC5F,MAAM,CAACC,gBAAgBC,GAAG,EAAEC,WAAW,EAAEC,UAAU,EAAE,CAAC,GAAGrB,kBAAkB;QACzEsB,IAAIN;QACJO,gBAAgBjB;IAClB;IAEA,MAAMkB,kBAAkBT,kBAAkBU;IAC1C,MAAM,EAAEC,QAAQC,aAAa,EAAE,GAAG7B;IAElC,0CAA0C;IAC1Ca;IAEA,MAAM,CAACiB,cAAcC,gBAAgB,GAAGxB,SAAkB;IAC1D,MAAM,EAAEyB,QAAQ,EAAEC,SAAS,EAAEC,IAAI,EAAE,GAAGpB,YAAY;QAAEI;IAAc;IAElE,MAAM,EAAEiB,eAAe,EAAEC,IAAI,EAAE,GAAGxB,QAChC;QACEyB,WAAW;YACTC,QAAQC,GAAG,CAAC;YACZR,gBAAgB;YAChBC,SAAS;gBACPQ,QAAQ;YACV,GACGC,KAAK,CAAC,CAACC;gBACNJ,QAAQK,KAAK,CAAC,cAAcD;YAC9B,GACCE,OAAO,CAAC;gBACPb,gBAAgB;YAClB;QACJ;QACAc,UAAU;YACRP,QAAQC,GAAG,CAAC;YACZP,SAAS;gBACPQ,QAAQ;YACV,GACGC,KAAK,CAAC,CAACC;gBACNJ,QAAQK,KAAK,CAAC,cAAcD;YAC9B,GACCE,OAAO,CAAC;gBACPb,gBAAgB;YAClB;QACJ;QACAe,aAAa;YACXR,QAAQC,GAAG,CAAC;YACZP,SAAS;gBACPQ,QAAQ;YACV,GACGC,KAAK,CAAC,CAACC;gBACNJ,QAAQK,KAAK,CAAC,cAAcD;YAC9B,GACCE,OAAO,CAAC;gBACPb,gBAAgB;YAClB;QACJ;QACAgB,YAAY;YACVT,QAAQC,GAAG,CAAC;YACZP,SAAS;gBACPQ,QAAQ;YACV,GACGC,KAAK,CAAC,CAACC;gBACNJ,QAAQK,KAAK,CAAC,cAAcD;YAC9B,GACCE,OAAO,CAAC;gBACPb,gBAAgB;YAClB;QACJ;QACAiB,YAAY7B,kBAAkBI,aAAa0B;QAC3CC,YAAY;YACVZ,QAAQC,GAAG,CAAC;YACZP,SAAS;gBACPQ,QAAQ;YACV,GACGC,KAAK,CAAC,CAACC;gBACNJ,QAAQK,KAAK,CAAC,cAAcD;YAC9B,GACCE,OAAO,CAAC;gBACPb,gBAAgB;YAClB;QACJ;QACAoB,aAAa;YACXb,QAAQC,GAAG,CAAC;YACZP,SAAS;gBACPQ,QAAQ;YACV,GACGC,KAAK,CAAC,CAACC;gBACNJ,QAAQK,KAAK,CAAC,cAAcD;YAC9B,GACCE,OAAO,CAAC;gBACPb,gBAAgB;YAClB;QACJ;QACAqB,aAAa,CAACC;YACZf,QAAQC,GAAG,CAAC;YACZP,SAAS;gBACPQ,QAAQ;gBACRc,QAAQD;YACV,GACGZ,KAAK,CAAC,CAACC;gBACNJ,QAAQK,KAAK,CAAC,cAAcD;YAC9B,GACCE,OAAO,CAAC;gBACPb,gBAAgB;YAClB;QACJ;IACF,GACA;QACEZ;IACF;IAGF,MAAM,EAAEoC,QAAQ,EAAE,GAAGpD,SAAiB;QACpCwB,MAAMD;IACR;IAEA,MAAM8B,2BAA2BnD,YAAY,CAACoD;QAC5C,IAAIA,OAAO,OAAOA,QAAQ,YAAY,UAAUA,OAAO5B,eAAe;YACpEpB,oBAAoBiD,KAAKC,SAAS,CAACF,MAAM5B;QAC3C;IAEA,mHAAmH;IACrH,GAAG,EAAE;IAEL,MAAM+B,cAAcvD,YAClB,CAAC,EAAEwD,KAAK,EAAyB;QAC/B,qBAAO,KAACzB;YAAKH,WAAWH,gBAAgBG;YAAW6B,SAASD;;IAC9D,GACA;QAAC/B;QAAcG;QAAWG;KAAK;IAGjC,MAAM2B,gBAAgBzD,QAAQ;QAC5B,qBACE,KAACL;YACC+D,sBAAQ,KAACtD;gBAAWuB,WAAWH,gBAAgBG;;YAC/CgC,QAAQL;YACRM,eAAc;;IAGpB,GAAG;QAACN;QAAa9B;QAAcG;KAAU;IAEzC,qBACE,MAACkC;QACCC,WAAW,CAAC,2BAA2B,EAAEzD,OAAO0D,OAAO,EAAE;QACzDC,SAAS,CAACC,IAAMA,EAAEC,cAAc;QAChCC,MAAK;;0BAEL,KAACrD;gBACCsD,QAAQ;oBACNpD;gBACF;;YAEDyC;0BACD,KAAC5B;gBAAgBF,WAAWH,gBAAgBG;gBAAWC,MAAMA;;0BAC7D,KAACnB;gBACC4D,UAAU,CAAClB;oBACTF,SAASE;oBACTD,yBAAyBC;gBAC3B;;;;AAIR,EAAC"}
@@ -0,0 +1,141 @@
1
+ 'use client';
2
+ import { useEditorConfigContext } from '@payloadcms/richtext-lexical/client';
3
+ import { Popup, useDocumentDrawer, useField } from '@payloadcms/ui';
4
+ import React, { useCallback, useMemo, useState } from 'react';
5
+ import { PLUGIN_INSTRUCTIONS_TABLE } from '../../defaults.js';
6
+ import { setSafeLexicalState } from '../../utilities/setSafeLexicalState.js';
7
+ import { PluginIcon } from '../Icons/Icons.js';
8
+ import styles from './compose.module.css';
9
+ import { useMenu } from './hooks/menu/useMenu.js';
10
+ import { useActiveFieldTracking } from './hooks/useActiveFieldTracking.js';
11
+ import { useGenerate } from './hooks/useGenerate.js';
12
+ import { UndoRedoActions } from './UndoRedoActions.js';
13
+ export const Compose = ({ descriptionProps, instructionId, isConfigAllowed }) => {
14
+ const [DocumentDrawer, _, { closeDrawer, openDrawer }] = useDocumentDrawer({
15
+ id: instructionId,
16
+ collectionSlug: PLUGIN_INSTRUCTIONS_TABLE,
17
+ });
18
+ const pathFromContext = descriptionProps?.path;
19
+ const { editor: lexicalEditor } = useEditorConfigContext();
20
+ // Initialize global active-field tracking
21
+ useActiveFieldTracking();
22
+ const [isProcessing, setIsProcessing] = useState(false);
23
+ const { generate, isLoading, stop } = useGenerate({ instructionId });
24
+ const { ActiveComponent, Menu } = useMenu({
25
+ onCompose: () => {
26
+ console.log('Composing...');
27
+ setIsProcessing(true);
28
+ generate({
29
+ action: 'Compose',
30
+ })
31
+ .catch((reason) => {
32
+ console.error('Compose : ', reason);
33
+ })
34
+ .finally(() => {
35
+ setIsProcessing(false);
36
+ });
37
+ },
38
+ onExpand: () => {
39
+ console.log('Expanding...');
40
+ generate({
41
+ action: 'Expand',
42
+ })
43
+ .catch((reason) => {
44
+ console.error('Compose : ', reason);
45
+ })
46
+ .finally(() => {
47
+ setIsProcessing(false);
48
+ });
49
+ },
50
+ onProofread: () => {
51
+ console.log('Proofreading...');
52
+ generate({
53
+ action: 'Proofread',
54
+ })
55
+ .catch((reason) => {
56
+ console.error('Compose : ', reason);
57
+ })
58
+ .finally(() => {
59
+ setIsProcessing(false);
60
+ });
61
+ },
62
+ onRephrase: () => {
63
+ console.log('Rephrasing...');
64
+ generate({
65
+ action: 'Rephrase',
66
+ })
67
+ .catch((reason) => {
68
+ console.error('Compose : ', reason);
69
+ })
70
+ .finally(() => {
71
+ setIsProcessing(false);
72
+ });
73
+ },
74
+ onSettings: isConfigAllowed ? openDrawer : undefined,
75
+ onSimplify: () => {
76
+ console.log('Simplifying...');
77
+ generate({
78
+ action: 'Simplify',
79
+ })
80
+ .catch((reason) => {
81
+ console.error('Compose : ', reason);
82
+ })
83
+ .finally(() => {
84
+ setIsProcessing(false);
85
+ });
86
+ },
87
+ onSummarize: () => {
88
+ console.log('Summarizing...');
89
+ generate({
90
+ action: 'Summarize',
91
+ })
92
+ .catch((reason) => {
93
+ console.error('Compose : ', reason);
94
+ })
95
+ .finally(() => {
96
+ setIsProcessing(false);
97
+ });
98
+ },
99
+ onTranslate: (data) => {
100
+ console.log('Translating...');
101
+ generate({
102
+ action: 'Translate',
103
+ params: data,
104
+ })
105
+ .catch((reason) => {
106
+ console.error('Compose : ', reason);
107
+ })
108
+ .finally(() => {
109
+ setIsProcessing(false);
110
+ });
111
+ },
112
+ }, {
113
+ isConfigAllowed,
114
+ });
115
+ const { setValue } = useField({
116
+ path: pathFromContext,
117
+ });
118
+ const setIfValueIsLexicalState = useCallback((val) => {
119
+ if (val && typeof val === 'object' && 'root' in val && lexicalEditor) {
120
+ setSafeLexicalState(JSON.stringify(val), lexicalEditor);
121
+ }
122
+ // DO NOT PROVIDE lexicalEditor as a dependency, it freaks out and does not update the editor after first undo/redo
123
+ }, []);
124
+ const popupRender = useCallback(({ close }) => {
125
+ return <Menu isLoading={isProcessing || isLoading} onClose={close}/>;
126
+ }, [isProcessing, isLoading, Menu]);
127
+ const memoizedPopup = useMemo(() => {
128
+ return (<Popup button={<PluginIcon isLoading={isProcessing || isLoading}/>} render={popupRender} verticalAlign="bottom"/>);
129
+ }, [popupRender, isProcessing, isLoading]);
130
+ return (<label className={`payloadai-compose__actions ${styles.actions}`} onClick={(e) => e.preventDefault()} role="presentation">
131
+ <DocumentDrawer onSave={() => {
132
+ closeDrawer();
133
+ }}/>
134
+ {memoizedPopup}
135
+ <ActiveComponent isLoading={isProcessing || isLoading} stop={stop}/>
136
+ <UndoRedoActions onChange={(val) => {
137
+ setValue(val);
138
+ setIfValueIsLexicalState(val);
139
+ }}/>
140
+ </label>);
141
+ };
@@ -0,0 +1,34 @@
1
+ import React, { useCallback, useEffect, useState } from 'react';
2
+ import { useHistory } from './hooks/useHistory.js';
3
+ export const UndoRedoActions = ({ onChange }) => {
4
+ const { canRedo, canUndo, redo, undo } = useHistory();
5
+ const redoHistoryValue = useCallback((event) => {
6
+ event.stopPropagation();
7
+ const value = redo();
8
+ if (value) {
9
+ onChange(value);
10
+ }
11
+ }, [redo]);
12
+ const undoHistoryValue = useCallback((event) => {
13
+ event.stopPropagation();
14
+ const value = undo();
15
+ if (value) {
16
+ onChange(value);
17
+ }
18
+ }, [undo]);
19
+ // Delay rendering until the client-side hydration is complete
20
+ const [isMounted, setIsMounted] = useState(false);
21
+ useEffect(() => {
22
+ setIsMounted(true);
23
+ }, []);
24
+ if (!isMounted || (!canUndo && !canRedo))
25
+ return null;
26
+ return (<React.Fragment>
27
+ <button className={`btn btn--size-small btn--style-secondary ${!canUndo && 'btn--disabled'}`} disabled={!canUndo} onClick={undoHistoryValue} style={{ marginBlock: 0 }} type="button">
28
+ Undo
29
+ </button>
30
+ <button className={`btn btn--size-small btn--style-secondary ${!canRedo && 'btn--disabled'}`} disabled={!canRedo} onClick={redoHistoryValue} style={{ marginBlock: 0 }} type="button">
31
+ Redo
32
+ </button>
33
+ </React.Fragment>);
34
+ };
@@ -1,21 +1,108 @@
1
1
  .actions {
2
2
  position: relative;
3
- margin: 5px 0;
4
3
  display: flex;
5
4
  gap: 10px;
5
+ align-items: center;
6
+ opacity: 0;
7
+ visibility: hidden;
8
+ pointer-events: none;
9
+ padding: 0 5px;
10
+ margin: 0;
11
+ z-index: 1;
12
+ transform: scaleY(0) skewX(90deg) translateY(-5px);
13
+ transform-origin: top;
14
+ max-height: 0;
15
+
16
+ will-change: transform, opacity;
17
+ backface-visibility: hidden;
18
+ perspective: 1000px;
19
+
20
+ transition:
21
+ opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1),
22
+ transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
23
+ max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1),
24
+ visibility 0s linear 0.3s;
25
+ }
26
+
27
+ :global(.field-type.ai-plugin-active) .actions {
28
+ opacity: 1;
29
+ visibility: visible;
30
+ pointer-events: auto;
6
31
  padding: 5px 0;
7
- transition: all 0.3s ease;
32
+ margin: 5px 0;
8
33
  max-height: 100px;
9
- opacity: 1;
10
- align-items: center;
11
- z-index: auto;
34
+ transform: scaleY(1) skewX(0) translateY(0) translateZ(0);
35
+
36
+ transition:
37
+ opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1),
38
+ transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
39
+ max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1);
12
40
  }
13
41
 
14
- .actions_hidden {
15
- max-height: 0;
16
- overflow: hidden;
17
- padding: 0 5px;
18
- margin: 0;
42
+ :global(.field-type.ai-plugin-active) .actions {
43
+ animation: removeWillChange 0.3s forwards;
44
+ }
45
+
46
+ @keyframes removeWillChange {
47
+ to {
48
+ will-change: auto;
49
+ }
50
+ }
51
+
52
+ .actions > * {
19
53
  opacity: 0;
20
- z-index: 0;
21
- }
54
+ transform: translateY(-3px) translateZ(0);
55
+
56
+ will-change: transform, opacity;
57
+ backface-visibility: hidden;
58
+
59
+ transition:
60
+ opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1),
61
+ transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
62
+ }
63
+
64
+ :global(.field-type.ai-plugin-active) .actions > * {
65
+ opacity: 1;
66
+ transform: translateY(0) translateZ(0);
67
+ }
68
+
69
+ :global(.field-type.ai-plugin-active) .actions > *:nth-child(1) {
70
+ transition-delay: 0.05s;
71
+ animation: removeWillChange 0.25s 0.05s forwards;
72
+ }
73
+
74
+ :global(.field-type.ai-plugin-active) .actions > *:nth-child(2) {
75
+ transition-delay: 0.08s;
76
+ animation: removeWillChange 0.28s 0.08s forwards;
77
+ }
78
+
79
+ :global(.field-type.ai-plugin-active) .actions > *:nth-child(3) {
80
+ transition-delay: 0.11s;
81
+ animation: removeWillChange 0.31s 0.11s forwards;
82
+ }
83
+
84
+ :global(.field-type.ai-plugin-active) .actions > *:nth-child(4) {
85
+ transition-delay: 0.14s;
86
+ animation: removeWillChange 0.34s 0.14s forwards;
87
+ }
88
+
89
+ :global(.field-type.ai-plugin-active) .actions > *:nth-child(5) {
90
+ transition-delay: 0.17s;
91
+ animation: removeWillChange 0.37s 0.17s forwards;
92
+ }
93
+
94
+ @media (prefers-reduced-motion: no-preference) and (min-resolution: 120dpi) {
95
+ .actions,
96
+ .actions > * {
97
+ transition-duration: 0.2s;
98
+ }
99
+ }
100
+
101
+ @media (prefers-reduced-motion: reduce) {
102
+ .actions,
103
+ .actions > * {
104
+ transition-duration: 0.01ms !important;
105
+ animation-duration: 0.01ms !important;
106
+ will-change: auto !important;
107
+ }
108
+ }
@@ -0,0 +1,15 @@
1
+ import React, { memo } from 'react';
2
+ import { ArrowIcon } from '../../../Icons/Icons.js';
3
+ import styles from './menu.module.scss';
4
+ export const Item = memo(({ children, disabled, isActive, onClick, ...rest }) => (<span className={styles.generate_button + ' ' + (isActive ? styles.active : '')} data-disabled={disabled} onClick={!disabled && typeof onClick === 'function'
5
+ ? onClick
6
+ : undefined} onKeyDown={!disabled && typeof onClick === 'function'
7
+ ? onClick
8
+ : undefined} role="presentation" {...rest}>
9
+ {children}
10
+ </span>));
11
+ export const createMenuItem = (IconComponent, initialText) => memo(({ children, disabled, hideIcon, isMenu, onClick, ...rest }) => (<Item disabled={disabled} onClick={onClick} {...rest}>
12
+ {hideIcon || <IconComponent size={18}/>}
13
+ {children || <span className={styles.text}>{initialText}</span>}
14
+ {isMenu && <ArrowIcon size={18}/>}
15
+ </Item>));
@@ -0,0 +1,58 @@
1
+ import locales from 'locale-codes';
2
+ import React, { memo, useState } from 'react';
3
+ import { useInstructions } from '../../../../providers/InstructionsProvider/useInstructions.js';
4
+ import { Item } from './Item.js';
5
+ import { Translate } from './items.js';
6
+ import styles from './menu.module.scss';
7
+ export const TranslateMenu = ({ onClick }) => {
8
+ const [show, setShow] = useState(false);
9
+ const { enabledLanguages = [] } = useInstructions();
10
+ let filteredLocales = locales.all.filter((a) => {
11
+ return a.tag && a.location;
12
+ });
13
+ if (enabledLanguages?.length) {
14
+ filteredLocales = filteredLocales.filter((a) => enabledLanguages?.includes(a.tag));
15
+ }
16
+ const [languages, setLanguages] = useState(filteredLocales);
17
+ const [inputFocus, setInputFocus] = useState(false);
18
+ return (<div className={styles.menu} onMouseLeave={() => {
19
+ if (!inputFocus) {
20
+ setShow(false);
21
+ }
22
+ }}>
23
+ <Translate isActive={show} isMenu onClick={() => {
24
+ setShow(!show);
25
+ }} onMouseEnter={() => setShow(true)}/>
26
+ <div className={styles.hoverMenu} data-show={show}>
27
+ <div className={`${styles.menu} ${styles.subMenu}`} style={{
28
+ background: 'var(--popup-bg)',
29
+ // minHeight: '300px',
30
+ }}>
31
+ <Item onClick={() => { }} style={{
32
+ background: 'transparent',
33
+ padding: '0 0 5px 0',
34
+ position: 'sticky',
35
+ top: 0,
36
+ }}>
37
+ <input className={styles.menuInput} onBlur={() => setInputFocus(false)} onChange={(event) => {
38
+ const value = event.target.value;
39
+ setLanguages(filteredLocales.filter((l) => {
40
+ const lowerCaseValue = value.toLowerCase();
41
+ return (l.name.toLowerCase().startsWith(lowerCaseValue) ||
42
+ (l.location && l.location.toLowerCase().startsWith(lowerCaseValue)) ||
43
+ l.tag.toLowerCase().startsWith(lowerCaseValue));
44
+ }));
45
+ }} onFocus={() => setInputFocus(true)} placeholder="Search..."/>
46
+ </Item>
47
+ {languages.map((locale) => {
48
+ return (<Item key={locale.tag} onClick={() => {
49
+ onClick({ locale: locale.tag });
50
+ }}>
51
+ <span className={styles.ellipsis}>{`${locale.location} (${locale.tag})`}</span>
52
+ </Item>);
53
+ })}
54
+ </div>
55
+ </div>
56
+ </div>);
57
+ };
58
+ export const MemoizedTranslateMenu = memo(TranslateMenu);
@@ -0,0 +1,10 @@
1
+ import { DocsAddOnIcon, EditNoteIcon, SegmentIcon, SpellCheckIcon, StylusNoteIcon, SummarizeIcon, TranslateIcon, TuneIcon, } from '../../../Icons/Icons.js';
2
+ import { createMenuItem } from './Item.js';
3
+ export const Proofread = createMenuItem(SpellCheckIcon, 'Proofread');
4
+ export const Rephrase = createMenuItem(EditNoteIcon, 'Rephrase');
5
+ export const Translate = createMenuItem(TranslateIcon, 'Translate');
6
+ export const Expand = createMenuItem(DocsAddOnIcon, 'Expand');
7
+ export const Summarize = createMenuItem(SummarizeIcon, 'Summarize');
8
+ export const Simplify = createMenuItem(SegmentIcon, 'Simplify');
9
+ export const Compose = createMenuItem(StylusNoteIcon, 'Compose');
10
+ export const Settings = createMenuItem(TuneIcon, 'Settings');
@@ -19,7 +19,7 @@ const getActiveComponent = (ac)=>{
19
19
  }
20
20
  };
21
21
  export const useMenu = (menuEvents, options)=>{
22
- const { type: fieldType, path: pathFromContext } = useFieldProps();
22
+ const { field: { type: fieldType } = {}, path: pathFromContext } = useFieldProps();
23
23
  const field = useField({
24
24
  path: pathFromContext ?? ''
25
25
  });