@ai-stack/payloadcms 3.76.0-beta.0 → 3.76.0-beta.1
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 +13 -33
- package/dist/ai/core/media/image/handlers/multimodal.js.map +1 -1
- package/dist/ai/core/media/image/handlers/standard.js +10 -7
- package/dist/ai/core/media/image/handlers/standard.js.map +1 -1
- package/dist/ai/core/media/speech/generateSpeech.js +8 -8
- package/dist/ai/core/media/speech/generateSpeech.js.map +1 -1
- package/dist/ai/core/media/types.d.ts +1 -1
- package/dist/ai/core/media/types.js.map +1 -1
- package/dist/ai/providers/blocks/google.js +1 -0
- package/dist/ai/providers/blocks/google.js.map +1 -1
- package/dist/ai/providers/registry.js +2 -2
- package/dist/ai/providers/registry.js.map +1 -1
- package/dist/collections/AIProviders.d.ts +2 -0
- package/dist/collections/{AISettings.js → AIProviders.js} +7 -6
- package/dist/collections/AIProviders.js.map +1 -0
- package/dist/collections/Instructions.js +8 -4
- package/dist/collections/Instructions.js.map +1 -1
- package/dist/defaults.js +1 -1
- package/dist/defaults.js.map +1 -1
- package/dist/endpoints/fetchFields.js +1 -1
- package/dist/endpoints/fetchFields.js.map +1 -1
- package/dist/endpoints/fetchVoices.js +1 -1
- package/dist/endpoints/fetchVoices.js.map +1 -1
- package/dist/endpoints/index.js +56 -21
- package/dist/endpoints/index.js.map +1 -1
- package/dist/exports/client.d.ts +1 -1
- package/dist/exports/client.js +1 -1
- package/dist/exports/client.js.map +1 -1
- package/dist/plugin.js +2 -2
- package/dist/plugin.js.map +1 -1
- package/dist/providers/InstructionsProvider/useInstructions.js +1 -1
- package/dist/providers/InstructionsProvider/useInstructions.js.map +1 -1
- package/dist/types.d.ts +4 -4
- package/dist/types.js.map +1 -1
- package/dist/ui/AIConfigDashboard/index.d.ts +1 -1
- package/dist/ui/AIConfigDashboard/index.js +16 -14
- package/dist/ui/AIConfigDashboard/index.js.map +1 -1
- package/dist/ui/AIConfigDashboard/index.jsx +18 -13
- package/dist/ui/Compose/hooks/menu/TranslateMenu.d.ts +5 -0
- package/dist/ui/Compose/hooks/menu/TranslateMenu.js +45 -4
- package/dist/ui/Compose/hooks/menu/TranslateMenu.js.map +1 -1
- package/dist/ui/Compose/hooks/menu/TranslateMenu.jsx +41 -5
- package/dist/ui/Compose/hooks/menu/menu.module.scss +4 -1
- package/dist/ui/Compose/hooks/useActiveFieldTracking.js +34 -0
- package/dist/ui/Compose/hooks/useActiveFieldTracking.js.map +1 -1
- package/dist/ui/Compose/hooks/useGenerateUpload.js +8 -2
- package/dist/ui/Compose/hooks/useGenerateUpload.js.map +1 -1
- package/dist/ui/ConfigDashboard/index.d.ts +2 -0
- package/dist/ui/ConfigDashboard/index.js +224 -0
- package/dist/ui/ConfigDashboard/index.js.map +1 -0
- package/dist/ui/ConfigDashboard/index.jsx +175 -0
- package/dist/ui/DynamicModelSelect/index.js +1 -1
- package/dist/ui/DynamicModelSelect/index.js.map +1 -1
- package/dist/ui/DynamicModelSelect/index.jsx +1 -1
- package/dist/ui/DynamicProviderSelect/index.js +1 -1
- package/dist/ui/DynamicProviderSelect/index.js.map +1 -1
- package/dist/ui/DynamicProviderSelect/index.jsx +1 -1
- package/dist/ui/DynamicVoiceSelect/index.js +1 -1
- package/dist/ui/DynamicVoiceSelect/index.js.map +1 -1
- package/dist/ui/DynamicVoiceSelect/index.jsx +1 -1
- package/dist/ui/ProviderOptionsEditor/index.js +1 -1
- package/dist/ui/ProviderOptionsEditor/index.js.map +1 -1
- package/dist/ui/ProviderOptionsEditor/index.jsx +1 -1
- package/dist/utilities/updateFieldsConfig.js +1 -1
- package/dist/utilities/updateFieldsConfig.js.map +1 -1
- package/package.json +1 -1
- package/dist/collections/AISettings.d.ts +0 -2
- package/dist/collections/AISettings.js.map +0 -1
package/dist/exports/client.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export { LexicalEditorFeatureClient } from '../fields/LexicalEditor/feature.client.js';
|
|
2
2
|
export { InstructionsContext } from '../providers/InstructionsProvider/context.js';
|
|
3
3
|
export { InstructionsProvider } from '../providers/InstructionsProvider/InstructionsProvider.js';
|
|
4
|
-
export { AIConfigDashboard } from '../ui/AIConfigDashboard/index.js';
|
|
5
4
|
export { ApiKeyStatusIndicator } from '../ui/ApiKeyStatusIndicator/index.js';
|
|
5
|
+
export { ConfigDashboard } from '../ui/ConfigDashboard/index.js';
|
|
6
6
|
export { DynamicModelSelect } from '../ui/DynamicModelSelect/index.js';
|
|
7
7
|
export { DynamicProviderSelect } from '../ui/DynamicProviderSelect/index.js';
|
|
8
8
|
export { DynamicVoiceSelect } from '../ui/DynamicVoiceSelect/index.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { LexicalEditorFeatureClient } from '../fields/LexicalEditor/feature.client.js'\nexport { InstructionsContext } from '../providers/InstructionsProvider/context.js'\nexport { InstructionsProvider } from '../providers/InstructionsProvider/InstructionsProvider.js'\nexport {
|
|
1
|
+
{"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { LexicalEditorFeatureClient } from '../fields/LexicalEditor/feature.client.js'\nexport { InstructionsContext } from '../providers/InstructionsProvider/context.js'\nexport { InstructionsProvider } from '../providers/InstructionsProvider/InstructionsProvider.js'\nexport { ApiKeyStatusIndicator } from '../ui/ApiKeyStatusIndicator/index.js'\nexport { ConfigDashboard } from '../ui/ConfigDashboard/index.js'\nexport { DynamicModelSelect } from '../ui/DynamicModelSelect/index.js'\nexport { DynamicProviderSelect } from '../ui/DynamicProviderSelect/index.js'\nexport { DynamicVoiceSelect } from '../ui/DynamicVoiceSelect/index.js'\nexport { EncryptedTextField } from '../ui/EncryptedTextField/index.js'\nexport { ModelRowLabel } from '../ui/ModelRowLabel/index.js'\nexport { ProviderOptionsEditor } from '../ui/ProviderOptionsEditor/index.js'\nexport { VoicesFetcher } from '../ui/VoicesFetcher/index.js'\n"],"names":["LexicalEditorFeatureClient","InstructionsContext","InstructionsProvider","ApiKeyStatusIndicator","ConfigDashboard","DynamicModelSelect","DynamicProviderSelect","DynamicVoiceSelect","EncryptedTextField","ModelRowLabel","ProviderOptionsEditor","VoicesFetcher"],"mappings":"AAAA,SAASA,0BAA0B,QAAQ,4CAA2C;AACtF,SAASC,mBAAmB,QAAQ,+CAA8C;AAClF,SAASC,oBAAoB,QAAQ,4DAA2D;AAChG,SAASC,qBAAqB,QAAQ,uCAAsC;AAC5E,SAASC,eAAe,QAAQ,iCAAgC;AAChE,SAASC,kBAAkB,QAAQ,oCAAmC;AACtE,SAASC,qBAAqB,QAAQ,uCAAsC;AAC5E,SAASC,kBAAkB,QAAQ,oCAAmC;AACtE,SAASC,kBAAkB,QAAQ,oCAAmC;AACtE,SAASC,aAAa,QAAQ,+BAA8B;AAC5D,SAASC,qBAAqB,QAAQ,uCAAsC;AAC5E,SAASC,aAAa,QAAQ,+BAA8B"}
|
package/dist/plugin.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { deepMerge } from 'payload/shared';
|
|
2
2
|
import { lexicalJsonSchema } from './ai/schemas/lexicalJsonSchema.js';
|
|
3
3
|
import { aiJobsCollection } from './collections/AIJobs.js';
|
|
4
|
-
import {
|
|
4
|
+
import { AIProvidersGlobal } from './collections/AIProviders.js';
|
|
5
5
|
import { instructionsCollection } from './collections/Instructions.js';
|
|
6
6
|
import { PLUGIN_NAME } from './defaults.js';
|
|
7
7
|
import { fetchFields } from './endpoints/fetchFields.js';
|
|
@@ -90,7 +90,7 @@ const payloadAiPlugin = (pluginConfig)=>(incomingConfig)=>{
|
|
|
90
90
|
];
|
|
91
91
|
const globals = [
|
|
92
92
|
...incomingConfig.globals ?? [],
|
|
93
|
-
|
|
93
|
+
AIProvidersGlobal
|
|
94
94
|
];
|
|
95
95
|
const { globals: globalsSlugs } = pluginConfig;
|
|
96
96
|
const { components: { providers = [] } = {} } = incomingConfig.admin || {};
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["import type { CollectionConfig, Config, GlobalConfig } from 'payload'\n\nimport { deepMerge } from 'payload/shared'\n\nimport type { PluginConfig } from './types.js'\n\nimport { lexicalJsonSchema } from './ai/schemas/lexicalJsonSchema.js'\nimport { aiJobsCollection } from './collections/AIJobs.js'\nimport { aiSettingsGlobal } from './collections/AISettings.js'\nimport { instructionsCollection } from './collections/Instructions.js'\nimport { PLUGIN_NAME } from './defaults.js'\nimport { fetchFields } from './endpoints/fetchFields.js'\nimport { fetchVoices } from './endpoints/fetchVoices.js'\nimport { endpoints } from './endpoints/index.js'\nimport { translations } from './translations/index.js'\nimport { isPluginActivated } from './utilities/isPluginActivated.js'\nimport { updateFieldsConfig } from './utilities/updateFieldsConfig.js'\n\nconst defaultPluginConfig: PluginConfig = {\n access: {\n generate: ({ req }) => !!req.user,\n settings: ({ req }) => !!req.user,\n },\n disableSponsorMessage: false,\n}\n\nconst sponsorMessage = `\n╔═══════════════════════════════════════════════════════════════╗\n║ THANK YOU FOR USING THE PAYLOAD AI PLUGIN! ║\n║ ║\n║ If this plugin makes your life easier, please ║\n║ consider supporting its development and maintenance: ║\n║ ║\n║ • Buy me a coffee: https://buymeacoffee.com/ashbuilds ║\n║ • Sponsor on GitHub: https://github.com/sponsors/ashbuilds ║\n║ ║\n║ Your support fuels continued improvements, ║\n║ new features, and more caffeinated coding sessions! ☕ ║\n║ ║\n║ Got feedback or need help? Submit an issue here: ║\n║ • https://github.com/ashbuilds/payload-ai/issues/new ║\n║ ║\n║ Thank you again, and happy building! ║\n╚═══════════════════════════════════════════════════════════════╝\n`\n\nconst securityMessage = `\n╔═══════════════════════════════════════════════════════════════╗\n║ SECURITY NOTICE ║\n║ ║\n║ The AI Plugin now requires authentication by default. ║\n║ All AI features are restricted to authenticated users. ║\n║ ║\n║ To customize access control, configure the 'access' option ║\n║ in your plugin settings. See documentation for details. ║\n║ ║\n║ If you need different access patterns, please configure ║\n║ them explicitly in your plugin configuration. ║\n╚═══════════════════════════════════════════════════════════════╝\n`\n\nconst payloadAiPlugin =\n (pluginConfig: PluginConfig) =>\n (incomingConfig: Config): Config => {\n pluginConfig = {\n ...defaultPluginConfig,\n ...pluginConfig,\n access: {\n ...defaultPluginConfig.access,\n ...pluginConfig.access,\n },\n }\n\n const isActivated = isPluginActivated(pluginConfig)\n let updatedConfig: Config = { ...incomingConfig }\n\n if (isActivated) {\n const Instructions = instructionsCollection(pluginConfig)\n const AIJobs = aiJobsCollection()\n // Inject editor schema to config, so that it can be accessed when /textarea endpoint will hit\n const lexicalSchema = lexicalJsonSchema(pluginConfig.editorConfig?.nodes)\n\n Instructions.admin = {\n ...Instructions.admin,\n }\n\n if (pluginConfig.debugging) {\n Instructions.admin.hidden = false\n }\n\n Instructions.admin.custom = {\n ...(Instructions.admin.custom || {}),\n [PLUGIN_NAME]: {\n editorConfig: {\n // Used in admin client for useObject hook\n schema: lexicalSchema,\n },\n },\n }\n\n const collections = [...(incomingConfig.collections ?? []), Instructions, AIJobs]\n const globals = [...(incomingConfig.globals ?? []), aiSettingsGlobal]\n const { globals: globalsSlugs } = pluginConfig\n\n const { components: { providers = [] } = {} } = incomingConfig.admin || {}\n const updatedProviders = [\n ...(providers ?? []),\n {\n path: '@ai-stack/payloadcms/client#InstructionsProvider',\n },\n ]\n\n incomingConfig.admin = {\n ...(incomingConfig.admin || {}),\n components: {\n ...(incomingConfig.admin?.components ?? {}),\n providers: updatedProviders,\n },\n }\n\n const pluginEndpoints = endpoints(pluginConfig)\n updatedConfig = {\n ...incomingConfig,\n collections: collections.map((collection) => {\n // Always inject fields, but they will be dynamically enabled/disabled by the InstructionsProvider\n const { updatedCollectionConfig } = updateFieldsConfig(collection)\n return updatedCollectionConfig as CollectionConfig\n }),\n endpoints: [\n ...(incomingConfig.endpoints ?? []),\n pluginEndpoints.textarea,\n pluginEndpoints.upload,\n ...(pluginEndpoints.videogenWebhook ? [pluginEndpoints.videogenWebhook] : []),\n fetchFields(pluginConfig),\n fetchVoices,\n ],\n globals: globals.map((global) => {\n if (globalsSlugs && globalsSlugs[global.slug]) {\n const { updatedCollectionConfig } = updateFieldsConfig(global)\n return updatedCollectionConfig as GlobalConfig\n }\n\n return global\n }),\n i18n: {\n ...(incomingConfig.i18n || {}),\n translations: {\n ...deepMerge(translations, incomingConfig.i18n?.translations ?? {}),\n },\n },\n }\n }\n\n updatedConfig.onInit = async (payload) => {\n if (incomingConfig.onInit) {\n await incomingConfig.onInit(payload)\n }\n\n if (!isActivated) {\n payload.logger.warn(`— AI Plugin: Not activated. Please verify your environment keys.`)\n return\n }\n\n if (!pluginConfig.disableSponsorMessage) {\n setTimeout(() => {\n payload.logger.info(securityMessage)\n }, 1000)\n setTimeout(() => {\n payload.logger.info(sponsorMessage)\n }, 3000)\n }\n\n // Inject AI capabilities with the abstraction layer\n ;(payload as any).ai = {\n // Core generation methods\n generateObject: async (args: any) => {\n const { generateObject } = await import('./ai/core/index.js')\n return generateObject({ ...args, payload })\n },\n\n generateText: async (args: any) => {\n const { generateText } = await import('./ai/core/index.js')\n return generateText({ ...args, payload })\n },\n\n generateMedia: async (args: any) => {\n const { generateMedia } = await import('./ai/core/index.js')\n return generateMedia({ ...args, payload })\n },\n\n // Streaming variants\n streamObject: async (args: any) => {\n const { streamObject } = await import('./ai/core/index.js')\n const result = await streamObject({ ...args, payload })\n return result.toTextStreamResponse()\n },\n\n streamText: async (args: any) => {\n const { streamText } = await import('./ai/core/index.js')\n return streamText({ ...args, payload })\n },\n\n // Helper utilities\n getModel: async (provider: string, modelId: string, type?: 'image' | 'text' | 'tts') => {\n const { getImageModel, getLanguageModel, getTTSModel } = await import(\n './ai/providers/registry.js'\n )\n if (type === 'image') {\n return getImageModel(payload, provider, modelId)\n }\n if (type === 'tts') {\n return getTTSModel(payload, provider, modelId)\n }\n return getLanguageModel(payload, provider, modelId)\n },\n\n getRegistry: async () => {\n const { getProviderRegistry } = await import('./ai/providers/registry.js')\n return getProviderRegistry(payload)\n },\n\n // Legacy method for backward compatibility\n /** @deprecated Use generateObject or generateText instead */\n generate: async (args: any) => {\n const { generate } = await import('./ai/index.js')\n return generate({ ...args, payload })\n },\n }\n }\n\n return updatedConfig\n }\n\nexport { payloadAiPlugin }\n"],"names":["deepMerge","lexicalJsonSchema","aiJobsCollection","aiSettingsGlobal","instructionsCollection","PLUGIN_NAME","fetchFields","fetchVoices","endpoints","translations","isPluginActivated","updateFieldsConfig","defaultPluginConfig","access","generate","req","user","settings","disableSponsorMessage","sponsorMessage","securityMessage","payloadAiPlugin","pluginConfig","incomingConfig","isActivated","updatedConfig","Instructions","AIJobs","lexicalSchema","editorConfig","nodes","admin","debugging","hidden","custom","schema","collections","globals","globalsSlugs","components","providers","updatedProviders","path","pluginEndpoints","map","collection","updatedCollectionConfig","textarea","upload","videogenWebhook","global","slug","i18n","onInit","payload","logger","warn","setTimeout","info","ai","generateObject","args","generateText","generateMedia","streamObject","result","toTextStreamResponse","streamText","getModel","provider","modelId","type","getImageModel","getLanguageModel","getTTSModel","getRegistry","getProviderRegistry"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["import type { CollectionConfig, Config, GlobalConfig } from 'payload'\n\nimport { deepMerge } from 'payload/shared'\n\nimport type { PluginConfig } from './types.js'\n\nimport { lexicalJsonSchema } from './ai/schemas/lexicalJsonSchema.js'\nimport { aiJobsCollection } from './collections/AIJobs.js'\nimport { AIProvidersGlobal } from './collections/AIProviders.js'\nimport { instructionsCollection } from './collections/Instructions.js'\nimport { PLUGIN_NAME } from './defaults.js'\nimport { fetchFields } from './endpoints/fetchFields.js'\nimport { fetchVoices } from './endpoints/fetchVoices.js'\nimport { endpoints } from './endpoints/index.js'\nimport { translations } from './translations/index.js'\nimport { isPluginActivated } from './utilities/isPluginActivated.js'\nimport { updateFieldsConfig } from './utilities/updateFieldsConfig.js'\n\nconst defaultPluginConfig: PluginConfig = {\n access: {\n generate: ({ req }) => !!req.user,\n settings: ({ req }) => !!req.user,\n },\n disableSponsorMessage: false,\n}\n\nconst sponsorMessage = `\n╔═══════════════════════════════════════════════════════════════╗\n║ THANK YOU FOR USING THE PAYLOAD AI PLUGIN! ║\n║ ║\n║ If this plugin makes your life easier, please ║\n║ consider supporting its development and maintenance: ║\n║ ║\n║ • Buy me a coffee: https://buymeacoffee.com/ashbuilds ║\n║ • Sponsor on GitHub: https://github.com/sponsors/ashbuilds ║\n║ ║\n║ Your support fuels continued improvements, ║\n║ new features, and more caffeinated coding sessions! ☕ ║\n║ ║\n║ Got feedback or need help? Submit an issue here: ║\n║ • https://github.com/ashbuilds/payload-ai/issues/new ║\n║ ║\n║ Thank you again, and happy building! ║\n╚═══════════════════════════════════════════════════════════════╝\n`\n\nconst securityMessage = `\n╔═══════════════════════════════════════════════════════════════╗\n║ SECURITY NOTICE ║\n║ ║\n║ The AI Plugin now requires authentication by default. ║\n║ All AI features are restricted to authenticated users. ║\n║ ║\n║ To customize access control, configure the 'access' option ║\n║ in your plugin settings. See documentation for details. ║\n║ ║\n║ If you need different access patterns, please configure ║\n║ them explicitly in your plugin configuration. ║\n╚═══════════════════════════════════════════════════════════════╝\n`\n\nconst payloadAiPlugin =\n (pluginConfig: PluginConfig) =>\n (incomingConfig: Config): Config => {\n pluginConfig = {\n ...defaultPluginConfig,\n ...pluginConfig,\n access: {\n ...defaultPluginConfig.access,\n ...pluginConfig.access,\n },\n }\n\n const isActivated = isPluginActivated(pluginConfig)\n let updatedConfig: Config = { ...incomingConfig }\n\n if (isActivated) {\n const Instructions = instructionsCollection(pluginConfig)\n const AIJobs = aiJobsCollection()\n // Inject editor schema to config, so that it can be accessed when /textarea endpoint will hit\n const lexicalSchema = lexicalJsonSchema(pluginConfig.editorConfig?.nodes)\n\n Instructions.admin = {\n ...Instructions.admin,\n }\n\n if (pluginConfig.debugging) {\n Instructions.admin.hidden = false\n }\n\n Instructions.admin.custom = {\n ...(Instructions.admin.custom || {}),\n [PLUGIN_NAME]: {\n editorConfig: {\n // Used in admin client for useObject hook\n schema: lexicalSchema,\n },\n },\n }\n\n const collections = [...(incomingConfig.collections ?? []), Instructions, AIJobs]\n const globals = [...(incomingConfig.globals ?? []), AIProvidersGlobal]\n const { globals: globalsSlugs } = pluginConfig\n\n const { components: { providers = [] } = {} } = incomingConfig.admin || {}\n const updatedProviders = [\n ...(providers ?? []),\n {\n path: '@ai-stack/payloadcms/client#InstructionsProvider',\n },\n ]\n\n incomingConfig.admin = {\n ...(incomingConfig.admin || {}),\n components: {\n ...(incomingConfig.admin?.components ?? {}),\n providers: updatedProviders,\n },\n }\n\n const pluginEndpoints = endpoints(pluginConfig)\n updatedConfig = {\n ...incomingConfig,\n collections: collections.map((collection) => {\n // Always inject fields, but they will be dynamically enabled/disabled by the InstructionsProvider\n const { updatedCollectionConfig } = updateFieldsConfig(collection)\n return updatedCollectionConfig as CollectionConfig\n }),\n endpoints: [\n ...(incomingConfig.endpoints ?? []),\n pluginEndpoints.textarea,\n pluginEndpoints.upload,\n ...(pluginEndpoints.videogenWebhook ? [pluginEndpoints.videogenWebhook] : []),\n fetchFields(pluginConfig),\n fetchVoices,\n ],\n globals: globals.map((global) => {\n if (globalsSlugs && globalsSlugs[global.slug]) {\n const { updatedCollectionConfig } = updateFieldsConfig(global)\n return updatedCollectionConfig as GlobalConfig\n }\n\n return global\n }),\n i18n: {\n ...(incomingConfig.i18n || {}),\n translations: {\n ...deepMerge(translations, incomingConfig.i18n?.translations ?? {}),\n },\n },\n }\n }\n\n updatedConfig.onInit = async (payload) => {\n if (incomingConfig.onInit) {\n await incomingConfig.onInit(payload)\n }\n\n if (!isActivated) {\n payload.logger.warn(`— AI Plugin: Not activated. Please verify your environment keys.`)\n return\n }\n\n if (!pluginConfig.disableSponsorMessage) {\n setTimeout(() => {\n payload.logger.info(securityMessage)\n }, 1000)\n setTimeout(() => {\n payload.logger.info(sponsorMessage)\n }, 3000)\n }\n\n // Inject AI capabilities with the abstraction layer\n ;(payload as any).ai = {\n // Core generation methods\n generateObject: async (args: any) => {\n const { generateObject } = await import('./ai/core/index.js')\n return generateObject({ ...args, payload })\n },\n\n generateText: async (args: any) => {\n const { generateText } = await import('./ai/core/index.js')\n return generateText({ ...args, payload })\n },\n\n generateMedia: async (args: any) => {\n const { generateMedia } = await import('./ai/core/index.js')\n return generateMedia({ ...args, payload })\n },\n\n // Streaming variants\n streamObject: async (args: any) => {\n const { streamObject } = await import('./ai/core/index.js')\n const result = await streamObject({ ...args, payload })\n return result.toTextStreamResponse()\n },\n\n streamText: async (args: any) => {\n const { streamText } = await import('./ai/core/index.js')\n return streamText({ ...args, payload })\n },\n\n // Helper utilities\n getModel: async (provider: string, modelId: string, type?: 'image' | 'text' | 'tts') => {\n const { getImageModel, getLanguageModel, getTTSModel } = await import(\n './ai/providers/registry.js'\n )\n if (type === 'image') {\n return getImageModel(payload, provider, modelId)\n }\n if (type === 'tts') {\n return getTTSModel(payload, provider, modelId)\n }\n return getLanguageModel(payload, provider, modelId)\n },\n\n getRegistry: async () => {\n const { getProviderRegistry } = await import('./ai/providers/registry.js')\n return getProviderRegistry(payload)\n },\n\n // Legacy method for backward compatibility\n /** @deprecated Use generateObject or generateText instead */\n generate: async (args: any) => {\n const { generate } = await import('./ai/index.js')\n return generate({ ...args, payload })\n },\n }\n }\n\n return updatedConfig\n }\n\nexport { payloadAiPlugin }\n"],"names":["deepMerge","lexicalJsonSchema","aiJobsCollection","AIProvidersGlobal","instructionsCollection","PLUGIN_NAME","fetchFields","fetchVoices","endpoints","translations","isPluginActivated","updateFieldsConfig","defaultPluginConfig","access","generate","req","user","settings","disableSponsorMessage","sponsorMessage","securityMessage","payloadAiPlugin","pluginConfig","incomingConfig","isActivated","updatedConfig","Instructions","AIJobs","lexicalSchema","editorConfig","nodes","admin","debugging","hidden","custom","schema","collections","globals","globalsSlugs","components","providers","updatedProviders","path","pluginEndpoints","map","collection","updatedCollectionConfig","textarea","upload","videogenWebhook","global","slug","i18n","onInit","payload","logger","warn","setTimeout","info","ai","generateObject","args","generateText","generateMedia","streamObject","result","toTextStreamResponse","streamText","getModel","provider","modelId","type","getImageModel","getLanguageModel","getTTSModel","getRegistry","getProviderRegistry"],"mappings":"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"}
|
|
@@ -23,7 +23,7 @@ const warnOnceOnMissingInstructions = (path)=>{
|
|
|
23
23
|
if (!warnedOnceOnNoInstructionId.has(path)) {
|
|
24
24
|
warnedOnceOnNoInstructionId.add(path);
|
|
25
25
|
// eslint-disable-next-line no-console
|
|
26
|
-
console.info(`[AI Plugin] There are no AI instructions for this field: ${path}
|
|
26
|
+
console.info(`[AI Plugin] There are no AI instructions for this field: ${path}.`);
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
29
|
export const useInstructions = (update = {})=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/providers/InstructionsProvider/useInstructions.ts"],"sourcesContent":["import { useDocumentInfo } from '@payloadcms/ui'\nimport { useContext, useEffect, useMemo, useState } from 'react'\n\nimport { PLUGIN_INSTRUCTIONS_TABLE } from '../../defaults.js'\nimport { handlebarsHelpers, handlebarsHelpersMap } from '../../libraries/handlebars/helpersMap.js'\nimport { InstructionsContext } from './context.js'\n\n/**\n * Normalize a schema path by removing array indices.\n * This allows fields inside arrays to match their instruction records.\n * \n * Example:\n * 'array-test-cases.keywords.0.keyword' -> 'array-test-cases.keywords.keyword'\n * 'characters.views.2.description' -> 'characters.views.description'\n * 'posts.title' -> 'posts.title' (no change)\n */\nconst normalizeSchemaPath = (path: string): string => {\n if (!path) {\n return path\n }\n // Remove numeric path segments (array indices) and new Lexical _index- based keys\n return path\n .split('.')\n .filter((segment) => !/^\\d+$/.test(segment) && !segment.startsWith('_index-'))\n .join('.')\n}\n\nconst warnedOnceOnNoInstructionId = new Set<string>()\nconst warnOnceOnMissingInstructions = (path: string) => {\n if (!warnedOnceOnNoInstructionId.has(path)) {\n warnedOnceOnNoInstructionId.add(path)\n // eslint-disable-next-line no-console\n console.info(`[AI Plugin] There are no AI instructions for this field: ${path}
|
|
1
|
+
{"version":3,"sources":["../../../src/providers/InstructionsProvider/useInstructions.ts"],"sourcesContent":["import { useDocumentInfo } from '@payloadcms/ui'\nimport { useContext, useEffect, useMemo, useState } from 'react'\n\nimport { PLUGIN_INSTRUCTIONS_TABLE } from '../../defaults.js'\nimport { handlebarsHelpers, handlebarsHelpersMap } from '../../libraries/handlebars/helpersMap.js'\nimport { InstructionsContext } from './context.js'\n\n/**\n * Normalize a schema path by removing array indices.\n * This allows fields inside arrays to match their instruction records.\n * \n * Example:\n * 'array-test-cases.keywords.0.keyword' -> 'array-test-cases.keywords.keyword'\n * 'characters.views.2.description' -> 'characters.views.description'\n * 'posts.title' -> 'posts.title' (no change)\n */\nconst normalizeSchemaPath = (path: string): string => {\n if (!path) {\n return path\n }\n // Remove numeric path segments (array indices) and new Lexical _index- based keys\n return path\n .split('.')\n .filter((segment) => !/^\\d+$/.test(segment) && !segment.startsWith('_index-'))\n .join('.')\n}\n\nconst warnedOnceOnNoInstructionId = new Set<string>()\nconst warnOnceOnMissingInstructions = (path: string) => {\n if (!warnedOnceOnNoInstructionId.has(path)) {\n warnedOnceOnNoInstructionId.add(path)\n // eslint-disable-next-line no-console\n console.info(`[AI Plugin] There are no AI instructions for this field: ${path}.`)\n }\n}\n\nexport const useInstructions = (\n update: {\n schemaPath?: unknown\n } = {},\n) => {\n const context = useContext(InstructionsContext)\n const { collectionSlug } = useDocumentInfo()\n const { activeCollection, debugging, hasInstructions, instructions, promptFields, setActiveCollection } = context\n\n const [schemaPath, setSchemaPath] = useState(update.schemaPath as string)\n\n useEffect(() => {\n if (update.schemaPath !== schemaPath) {\n setSchemaPath((update.schemaPath as string) ?? '')\n }\n }, [update.schemaPath, schemaPath])\n\n useEffect(() => {\n if (\n activeCollection !== collectionSlug &&\n collectionSlug !== PLUGIN_INSTRUCTIONS_TABLE &&\n typeof setActiveCollection === 'function'\n ) {\n setActiveCollection(collectionSlug ?? '')\n }\n }, [activeCollection, collectionSlug, setActiveCollection])\n\n const groupedFields = useMemo(() => {\n const result: Record<string, string[]> = {}\n\n for (const fullKey of Object.keys(instructions || {})) {\n const [collection, ...pathParts] = fullKey.split('.')\n const path = pathParts.join('.')\n if (!result[collection]) {\n result[collection] = []\n }\n result[collection].push(path)\n }\n\n return result\n }, [instructions])\n\n // Suggestions for prompt editor\n const promptEditorSuggestions = useMemo(() => {\n const activeFields = groupedFields[activeCollection as string] || []\n const suggestions: string[] = []\n\n // Build instruction lookup map once for O(1) access instead of O(n) search per field\n const instructionLookup = new Map(\n Object.entries(instructions).map(([key, value]) => {\n const path = key.split('.').slice(1).join('.')\n return [path, value]\n })\n )\n\n activeFields.forEach((f) => {\n const fieldInfo = instructionLookup.get(f)\n\n if (!fieldInfo) {return}\n\n if (fieldInfo.fieldType === 'upload') {\n return\n }\n\n const helpers = handlebarsHelpers.filter(\n (h) => (handlebarsHelpersMap as Record<string, { field?: string }>)[h]?.field === fieldInfo.fieldType,\n )\n\n if (helpers.length) {\n for (const helper of helpers) {\n suggestions.push(`${helper} ${f}`)\n }\n } else {\n suggestions.push(f)\n }\n })\n\n promptFields.forEach(({ name, collections }) => {\n if (!activeCollection) {return}\n\n if (!collections || collections.includes(activeCollection)) {\n suggestions.push(name)\n }\n })\n\n return suggestions\n }, [groupedFields, activeCollection, instructions, promptFields])\n\n // Normalize the schema path to handle array indices\n const normalizedSchemaPath = normalizeSchemaPath(schemaPath)\n const pathInstructions = instructions[normalizedSchemaPath]\n\n if (debugging && !pathInstructions && schemaPath && hasInstructions) {\n warnOnceOnMissingInstructions(schemaPath)\n }\n\n const isCollectionEnabled = context.enabledCollections?.includes(collectionSlug || activeCollection || '') ?? false\n\n return {\n ...context,\n ...(pathInstructions || {}),\n disabled: !isCollectionEnabled || (pathInstructions?.disabled ?? false),\n promptEditorSuggestions,\n }\n}\n\n"],"names":["useDocumentInfo","useContext","useEffect","useMemo","useState","PLUGIN_INSTRUCTIONS_TABLE","handlebarsHelpers","handlebarsHelpersMap","InstructionsContext","normalizeSchemaPath","path","split","filter","segment","test","startsWith","join","warnedOnceOnNoInstructionId","Set","warnOnceOnMissingInstructions","has","add","console","info","useInstructions","update","context","collectionSlug","activeCollection","debugging","hasInstructions","instructions","promptFields","setActiveCollection","schemaPath","setSchemaPath","groupedFields","result","fullKey","Object","keys","collection","pathParts","push","promptEditorSuggestions","activeFields","suggestions","instructionLookup","Map","entries","map","key","value","slice","forEach","f","fieldInfo","get","fieldType","helpers","h","field","length","helper","name","collections","includes","normalizedSchemaPath","pathInstructions","isCollectionEnabled","enabledCollections","disabled"],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import type { GenerateObjectResult, ModelMessage } from 'ai';
|
|
1
2
|
import type { JSONSchema } from 'openai/lib/jsonschema';
|
|
2
3
|
import type { ImageGenerateParams } from 'openai/resources/images';
|
|
3
4
|
import type { CollectionConfig, CollectionSlug, DataFromCollectionSlug, Endpoint, Field, File, GlobalConfig, GroupField, PayloadRequest, TypedCollection } from 'payload';
|
|
4
5
|
import type { CSSProperties, MouseEventHandler } from 'react';
|
|
5
|
-
import type { GenerateObjectResult, ModelMessage } from 'ai';
|
|
6
6
|
import type { MediaResult } from './ai/core/index.js';
|
|
7
|
-
import type { PLUGIN_INSTRUCTIONS_TABLE } from
|
|
7
|
+
import type { PLUGIN_INSTRUCTIONS_TABLE } from './defaults.js';
|
|
8
8
|
export interface PluginConfigAccess {
|
|
9
9
|
/**
|
|
10
10
|
* Control access to AI generation features (generate text, images, audio)
|
|
@@ -46,7 +46,6 @@ export interface PluginConfig {
|
|
|
46
46
|
nodes: JSONSchema[];
|
|
47
47
|
};
|
|
48
48
|
fields?: Field[];
|
|
49
|
-
generatePromptOnInit?: boolean;
|
|
50
49
|
generationModels?: ((defaultModels: GenerationModel[]) => GenerationModel[]) | GenerationModel[];
|
|
51
50
|
globals?: {
|
|
52
51
|
[key: GlobalConfig['slug']]: boolean;
|
|
@@ -132,6 +131,7 @@ export type UseMenuOptions = {
|
|
|
132
131
|
};
|
|
133
132
|
export type BaseItemProps<T = any> = {
|
|
134
133
|
children?: React.ReactNode;
|
|
134
|
+
className?: string;
|
|
135
135
|
disabled?: boolean;
|
|
136
136
|
hideIcon?: boolean;
|
|
137
137
|
isActive?: boolean;
|
|
@@ -194,7 +194,7 @@ export interface AfterGenerateArgs<T = any> {
|
|
|
194
194
|
}
|
|
195
195
|
export type AfterGenerateHook<T = any> = (args: AfterGenerateArgs<T>) => Promise<void> | void;
|
|
196
196
|
export interface AIFieldConfig {
|
|
197
|
+
[key: string]: unknown;
|
|
197
198
|
afterGenerate?: AfterGenerateHook[];
|
|
198
199
|
beforeGenerate?: BeforeGenerateHook[];
|
|
199
|
-
[key: string]: unknown;
|
|
200
200
|
}
|
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 {
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { GenerateObjectResult, ModelMessage } from 'ai'\nimport 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 { MediaResult } from './ai/core/index.js'\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: MediaResult,\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 debugging?: boolean\n disableSponsorMessage?: boolean\n editorConfig?: { nodes: JSONSchema[] }\n fields?: Field[]\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, fields will have empty prompts by default\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) => File | 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 fetchVoices?: Omit<Endpoint, 'root'>\n textarea: Omit<Endpoint, 'root'>\n upload: Omit<Endpoint, 'root'>\n videogenWebhook?: 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<\n TypedCollection[typeof PLUGIN_INSTRUCTIONS_TABLE],\n 'createdAt' | 'id' | 'updatedAt'\n>\n\nexport type SeedPromptResult =\n | {\n data?: SeedPromptData\n }\n | {\n data?: SeedPromptData\n prompt: string\n system: string\n }\n | false\n | undefined\n | void\n\nexport type SeedPromptFunction = (\n options: SeedPromptOptions,\n) => 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 className?: string\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\nexport interface BeforeGenerateArgs<T = any> {\n doc: T\n field: Field\n headers: Record<string, string>\n instructions: Record<string, unknown> // The instruction document\n messages?: ModelMessage[]\n payload: PayloadRequest['payload']\n prompt: string\n req: PayloadRequest\n system: string\n}\n\nexport type BeforeGenerateResult = {\n messages?: ModelMessage[]\n prompt?: string\n system?: string\n} | void\n\nexport type BeforeGenerateHook<T = any> = (\n args: BeforeGenerateArgs<T>,\n) => BeforeGenerateResult | Promise<BeforeGenerateResult>\n\nexport interface AfterGenerateArgs<T = any> {\n doc: T\n field: Field\n headers: Record<string, string>\n instructions: Record<string, unknown>\n payload: PayloadRequest['payload']\n req: PayloadRequest\n result: GenerateObjectResult<any> | MediaResult | string // depends on context\n}\n\nexport type AfterGenerateHook<T = any> = (args: AfterGenerateArgs<T>) => Promise<void> | void\n\n// Add to PluginConfig or a new interface if accessed via custom.ai\nexport interface AIFieldConfig {\n [key: string]: unknown\n afterGenerate?: AfterGenerateHook[]\n beforeGenerate?: BeforeGenerateHook[]\n}\n"],"names":[],"mappings":"AA6QA,mEAAmE;AACnE,WAIC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const ConfigDashboard: React.FC;
|
|
@@ -3,13 +3,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { Button, toast, useConfig } from '@payloadcms/ui';
|
|
4
4
|
// @ts-expect-error - Next.js types are not resolving correctly with nodenext but runtime is fine
|
|
5
5
|
import { useRouter } from 'next/navigation';
|
|
6
|
-
import React, {
|
|
6
|
+
import React, { use, useEffect, useState } from 'react';
|
|
7
7
|
import { excludeCollections } from '../../defaults.js';
|
|
8
8
|
import { InstructionsContext } from '../../providers/InstructionsProvider/context.js';
|
|
9
|
-
export const
|
|
9
|
+
export const ConfigDashboard = ()=>{
|
|
10
10
|
const { config: { collections, routes: { admin: adminRoute, api: apiRoute } } } = useConfig();
|
|
11
11
|
const router = useRouter();
|
|
12
|
-
const { refresh, setEnabledCollections: setEnabledCollectionsInContext } =
|
|
12
|
+
const { refresh, setEnabledCollections: setEnabledCollectionsInContext } = use(InstructionsContext);
|
|
13
13
|
const [enabledCollections, setEnabledCollections] = useState([]);
|
|
14
14
|
const [isLoading, setIsLoading] = useState(true);
|
|
15
15
|
const [isSaving, setIsSaving] = useState(false);
|
|
@@ -17,7 +17,7 @@ export const AIConfigDashboard = ()=>{
|
|
|
17
17
|
useEffect(()=>{
|
|
18
18
|
const fetchSettings = async ()=>{
|
|
19
19
|
try {
|
|
20
|
-
const response = await fetch(`${apiRoute}/globals/ai-
|
|
20
|
+
const response = await fetch(`${apiRoute}/globals/ai-providers`);
|
|
21
21
|
if (response.ok) {
|
|
22
22
|
const data = await response.json();
|
|
23
23
|
// Handle both simple array and object wrapper if Payload wraps it
|
|
@@ -52,7 +52,7 @@ export const AIConfigDashboard = ()=>{
|
|
|
52
52
|
try {
|
|
53
53
|
// First fetch current settings to get ID or just rely on global update behavior
|
|
54
54
|
// We need to adhere to Payload's global update API
|
|
55
|
-
const response = await fetch(`${apiRoute}/globals/ai-
|
|
55
|
+
const response = await fetch(`${apiRoute}/globals/ai-providers`, {
|
|
56
56
|
body: JSON.stringify({
|
|
57
57
|
enabledCollections
|
|
58
58
|
}),
|
|
@@ -86,7 +86,7 @@ export const AIConfigDashboard = ()=>{
|
|
|
86
86
|
padding: '20px',
|
|
87
87
|
textAlign: 'center'
|
|
88
88
|
},
|
|
89
|
-
children: "Loading
|
|
89
|
+
children: "Loading configuration..."
|
|
90
90
|
});
|
|
91
91
|
}
|
|
92
92
|
return /*#__PURE__*/ _jsxs("div", {
|
|
@@ -94,6 +94,8 @@ export const AIConfigDashboard = ()=>{
|
|
|
94
94
|
background: 'var(--theme-elevation-50)',
|
|
95
95
|
// border: '1px solid var(--theme-elevation-150)',
|
|
96
96
|
// borderRadius: '8px',
|
|
97
|
+
// borderBottom: '1px solid var(--theme-elevation-150)',
|
|
98
|
+
// borderTop: '1px solid var(--theme-elevation-150)',
|
|
97
99
|
marginBottom: '20px',
|
|
98
100
|
overflow: 'hidden'
|
|
99
101
|
},
|
|
@@ -104,16 +106,16 @@ export const AIConfigDashboard = ()=>{
|
|
|
104
106
|
borderBottom: '1px solid var(--theme-elevation-150)',
|
|
105
107
|
display: 'flex',
|
|
106
108
|
justifyContent: 'space-between',
|
|
107
|
-
padding: '
|
|
109
|
+
padding: '8px var(--gutter-h)'
|
|
108
110
|
},
|
|
109
111
|
children: [
|
|
110
112
|
/*#__PURE__*/ _jsxs("div", {
|
|
111
113
|
children: [
|
|
112
|
-
/*#__PURE__*/ _jsx("
|
|
114
|
+
/*#__PURE__*/ _jsx("h2", {
|
|
113
115
|
style: {
|
|
114
116
|
margin: '0 0 5px 0'
|
|
115
117
|
},
|
|
116
|
-
children: "AI
|
|
118
|
+
children: "Let's configure your AI Plugin"
|
|
117
119
|
}),
|
|
118
120
|
/*#__PURE__*/ _jsx("p", {
|
|
119
121
|
style: {
|
|
@@ -121,7 +123,7 @@ export const AIConfigDashboard = ()=>{
|
|
|
121
123
|
fontSize: '14px',
|
|
122
124
|
margin: '0'
|
|
123
125
|
},
|
|
124
|
-
children: "
|
|
126
|
+
children: "Set up the provider → Choose the content → Refine the behavior."
|
|
125
127
|
})
|
|
126
128
|
]
|
|
127
129
|
}),
|
|
@@ -134,8 +136,8 @@ export const AIConfigDashboard = ()=>{
|
|
|
134
136
|
/*#__PURE__*/ _jsx(Button, {
|
|
135
137
|
buttonStyle: "secondary",
|
|
136
138
|
el: "link",
|
|
137
|
-
to: `${adminRoute}/globals/ai-
|
|
138
|
-
children: "
|
|
139
|
+
to: `${adminRoute}/globals/ai-providers`,
|
|
140
|
+
children: "Providers"
|
|
139
141
|
}),
|
|
140
142
|
/*#__PURE__*/ _jsx(Button, {
|
|
141
143
|
disabled: isSaving,
|
|
@@ -148,14 +150,14 @@ export const AIConfigDashboard = ()=>{
|
|
|
148
150
|
}),
|
|
149
151
|
/*#__PURE__*/ _jsxs("div", {
|
|
150
152
|
style: {
|
|
151
|
-
padding: '
|
|
153
|
+
padding: '24px var(--gutter-h)'
|
|
152
154
|
},
|
|
153
155
|
children: [
|
|
154
156
|
/*#__PURE__*/ _jsx("h5", {
|
|
155
157
|
style: {
|
|
156
158
|
marginBottom: '15px'
|
|
157
159
|
},
|
|
158
|
-
children: "
|
|
160
|
+
children: "Select the collections where AI features should be available, toggle them on or off, and save your changes."
|
|
159
161
|
}),
|
|
160
162
|
/*#__PURE__*/ _jsx("div", {
|
|
161
163
|
style: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/AIConfigDashboard/index.tsx"],"sourcesContent":["'use client'\n\nimport { Button, toast, useConfig } from '@payloadcms/ui'\n// @ts-expect-error - Next.js types are not resolving correctly with nodenext but runtime is fine\nimport { useRouter } from 'next/navigation'\nimport React, { useContext, useEffect, useState } from 'react'\n\nimport { excludeCollections } from '../../defaults.js'\nimport { InstructionsContext } from '../../providers/InstructionsProvider/context.js'\n\nexport const AIConfigDashboard: React.FC = () => {\n const {\n config: {\n collections,\n routes: { admin: adminRoute, api: apiRoute },\n },\n } = useConfig()\n const router = useRouter()\n const { refresh, setEnabledCollections: setEnabledCollectionsInContext } =\n useContext(InstructionsContext)\n\n const [enabledCollections, setEnabledCollections] = useState<string[]>([])\n const [isLoading, setIsLoading] = useState(true)\n const [isSaving, setIsSaving] = useState(false)\n\n const availableCollections = collections.filter(\n (c) =>\n !excludeCollections.includes(c.slug) &&\n !(c.admin as unknown as { hidden?: ((args: any) => boolean) | boolean })?.hidden,\n )\n\n useEffect(() => {\n const fetchSettings = async () => {\n try {\n const response = await fetch(`${apiRoute}/globals/ai-settings`)\n if (response.ok) {\n const data = await response.json()\n // Handle both simple array and object wrapper if Payload wraps it\n const storedEnabled = data.enabledCollections || []\n setEnabledCollections(Array.isArray(storedEnabled) ? storedEnabled : [])\n }\n } catch (error) {\n console.error('Failed to fetch AI settings:', error)\n } finally {\n setIsLoading(false)\n }\n }\n\n fetchSettings().catch((e) => {\n console.log(e)\n })\n }, [apiRoute])\n\n const handleToggle = (slug: string) => {\n setEnabledCollections((prev) => {\n if (prev.includes(slug)) {\n return prev.filter((s) => s !== slug)\n }\n return [...prev, slug]\n })\n }\n\n const handleSave = async () => {\n setIsSaving(true)\n try {\n // First fetch current settings to get ID or just rely on global update behavior\n // We need to adhere to Payload's global update API\n const response = await fetch(`${apiRoute}/globals/ai-settings`, {\n body: JSON.stringify({\n enabledCollections,\n }),\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n if (response.ok) {\n toast.success('Settings saved successfully')\n if (setEnabledCollectionsInContext) {\n setEnabledCollectionsInContext(enabledCollections)\n }\n if (refresh) {\n await refresh()\n }\n router.refresh()\n } else {\n toast.error('Failed to save settings')\n }\n } catch (error) {\n console.error('Error saving settings:', error)\n toast.error('Error saving settings')\n } finally {\n setIsSaving(false)\n }\n }\n\n if (isLoading) {\n return <div style={{ padding: '20px', textAlign: 'center' }}>Loading AI configuration...</div>\n }\n\n return (\n <div\n style={{\n background: 'var(--theme-elevation-50)',\n // border: '1px solid var(--theme-elevation-150)',\n // borderRadius: '8px',\n marginBottom: '20px',\n overflow: 'hidden',\n }}\n >\n <div\n style={{\n alignItems: 'center',\n borderBottom: '1px solid var(--theme-elevation-150)',\n display: 'flex',\n justifyContent: 'space-between',\n padding: '20px',\n }}\n >\n <div>\n <h4 style={{ margin: '0 0 5px 0' }}>AI Configuration</h4>\n <p style={{ color: 'var(--theme-elevation-500)', fontSize: '14px', margin: '0' }}>\n Manage your AI providers, API keys, and enable AI for specific collections.\n </p>\n </div>\n <div style={{ display: 'flex', gap: '10px' }}>\n <Button buttonStyle=\"secondary\" el=\"link\" to={`${adminRoute}/globals/ai-settings`}>\n Settings\n </Button>\n <Button\n disabled={isSaving}\n onClick={handleSave}\n >\n {isSaving ? 'Saving...' : 'Save Changes'}\n </Button>\n </div>\n </div>\n\n <div style={{ padding: '20px' }}>\n <h5 style={{ marginBottom: '15px' }}>Enabled Collections</h5>\n <div\n style={{\n display: 'grid',\n gap: '15px',\n gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))',\n }}\n >\n {availableCollections.map((collection) => {\n const isEnabled = enabledCollections.includes(collection.slug)\n return (\n <button\n key={collection.slug}\n onClick={() => handleToggle(collection.slug)}\n style={{\n alignItems: 'center',\n background: isEnabled\n ? 'var(--theme-elevation-100)'\n : 'var(--theme-elevation-50)',\n border: `1px solid ${isEnabled ? 'var(--theme-text-success)' : 'var(--theme-elevation-200)'}`,\n borderRadius: '6px',\n cursor: 'pointer',\n display: 'flex',\n gap: '10px',\n padding: '10px 15px',\n textAlign: 'left',\n transition: 'all 0.2s ease',\n width: '100%',\n }}\n type=\"button\"\n >\n <div\n style={{\n alignItems: 'center',\n background: isEnabled\n ? 'var(--theme-text-success)'\n : 'var(--theme-elevation-200)',\n borderRadius: '12px',\n display: 'flex',\n height: '24px',\n justifyContent: isEnabled ? 'flex-end' : 'flex-start',\n padding: '2px',\n transition: 'all 0.2s ease',\n width: '44px',\n }}\n >\n <div\n style={{\n background: 'white',\n borderRadius: '50%',\n height: '20px',\n width: '20px',\n }}\n />\n </div>\n <span style={{ fontWeight: 500 }}>\n {typeof collection.labels?.singular === 'string'\n ? collection.labels.singular\n : collection.labels?.singular?.en || collection.slug}\n </span>\n </button>\n )\n })}\n </div>\n </div>\n </div>\n )\n}\n"],"names":["Button","toast","useConfig","useRouter","React","useContext","useEffect","useState","excludeCollections","InstructionsContext","AIConfigDashboard","config","collections","routes","admin","adminRoute","api","apiRoute","router","refresh","setEnabledCollections","setEnabledCollectionsInContext","enabledCollections","isLoading","setIsLoading","isSaving","setIsSaving","availableCollections","filter","c","includes","slug","hidden","fetchSettings","response","fetch","ok","data","json","storedEnabled","Array","isArray","error","console","catch","e","log","handleToggle","prev","s","handleSave","body","JSON","stringify","headers","method","success","div","style","padding","textAlign","background","marginBottom","overflow","alignItems","borderBottom","display","justifyContent","h4","margin","p","color","fontSize","gap","buttonStyle","el","to","disabled","onClick","h5","gridTemplateColumns","map","collection","isEnabled","button","border","borderRadius","cursor","transition","width","type","height","span","fontWeight","labels","singular","en"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/AIConfigDashboard/index.tsx"],"sourcesContent":["'use client'\n\nimport { Button, toast, useConfig } from '@payloadcms/ui'\n// @ts-expect-error - Next.js types are not resolving correctly with nodenext but runtime is fine\nimport { useRouter } from 'next/navigation'\nimport React, { use, useEffect, useState } from 'react'\n\nimport { excludeCollections } from '../../defaults.js'\nimport { InstructionsContext } from '../../providers/InstructionsProvider/context.js'\n\nexport const ConfigDashboard: React.FC = () => {\n const {\n config: {\n collections,\n routes: { admin: adminRoute, api: apiRoute },\n },\n } = useConfig()\n const router = useRouter()\n const { refresh, setEnabledCollections: setEnabledCollectionsInContext } =\n use(InstructionsContext)\n\n const [enabledCollections, setEnabledCollections] = useState<string[]>([])\n const [isLoading, setIsLoading] = useState(true)\n const [isSaving, setIsSaving] = useState(false)\n\n const availableCollections = collections.filter(\n (c) =>\n !excludeCollections.includes(c.slug) &&\n !(c.admin as unknown as { hidden?: ((args: any) => boolean) | boolean })?.hidden,\n )\n\n useEffect(() => {\n const fetchSettings = async () => {\n try {\n const response = await fetch(`${apiRoute}/globals/ai-providers`)\n if (response.ok) {\n const data = await response.json()\n // Handle both simple array and object wrapper if Payload wraps it\n const storedEnabled = data.enabledCollections || []\n setEnabledCollections(Array.isArray(storedEnabled) ? storedEnabled : [])\n }\n } catch (error) {\n console.error('Failed to fetch AI settings:', error)\n } finally {\n setIsLoading(false)\n }\n }\n\n fetchSettings().catch((e) => {\n console.log(e)\n })\n }, [apiRoute])\n\n const handleToggle = (slug: string) => {\n setEnabledCollections((prev) => {\n if (prev.includes(slug)) {\n return prev.filter((s) => s !== slug)\n }\n return [...prev, slug]\n })\n }\n\n const handleSave = async () => {\n setIsSaving(true)\n try {\n // First fetch current settings to get ID or just rely on global update behavior\n // We need to adhere to Payload's global update API\n const response = await fetch(`${apiRoute}/globals/ai-providers`, {\n body: JSON.stringify({\n enabledCollections,\n }),\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n if (response.ok) {\n toast.success('Settings saved successfully')\n if (setEnabledCollectionsInContext) {\n setEnabledCollectionsInContext(enabledCollections)\n }\n if (refresh) {\n await refresh()\n }\n router.refresh()\n } else {\n toast.error('Failed to save settings')\n }\n } catch (error) {\n console.error('Error saving settings:', error)\n toast.error('Error saving settings')\n } finally {\n setIsSaving(false)\n }\n }\n\n if (isLoading) {\n return <div style={{ padding: '20px', textAlign: 'center' }}>Loading configuration...</div>\n }\n\n return (\n <div\n style={{\n background: 'var(--theme-elevation-50)',\n // border: '1px solid var(--theme-elevation-150)',\n // borderRadius: '8px',\n // borderBottom: '1px solid var(--theme-elevation-150)',\n // borderTop: '1px solid var(--theme-elevation-150)',\n marginBottom: '20px',\n overflow: 'hidden',\n }}\n >\n <div\n style={{\n alignItems: 'center',\n borderBottom: '1px solid var(--theme-elevation-150)',\n display: 'flex',\n justifyContent: 'space-between',\n padding: '8px var(--gutter-h)',\n }}\n >\n <div>\n <h2 style={{ margin: '0 0 5px 0' }}>Let's configure your AI Plugin</h2>\n <p style={{ color: 'var(--theme-elevation-500)', fontSize: '14px', margin: '0' }}>\n Set up the provider → Choose the content → Refine the behavior.\n </p>\n </div>\n <div style={{ display: 'flex', gap: '10px' }}>\n <Button buttonStyle=\"secondary\" el=\"link\" to={`${adminRoute}/globals/ai-providers`}>\n Providers\n </Button>\n <Button disabled={isSaving} onClick={handleSave}>\n {isSaving ? 'Saving...' : 'Save Changes'}\n </Button>\n </div>\n </div>\n\n <div style={{ padding: '24px var(--gutter-h)' }}>\n <h5 style={{ marginBottom: '15px' }}>\n Select the collections where AI features should be available, toggle them on or off, and\n save your changes.\n </h5>\n <div\n style={{\n display: 'grid',\n gap: '15px',\n gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))',\n }}\n >\n {availableCollections.map((collection) => {\n const isEnabled = enabledCollections.includes(collection.slug)\n return (\n <button\n key={collection.slug}\n onClick={() => handleToggle(collection.slug)}\n style={{\n alignItems: 'center',\n background: isEnabled\n ? 'var(--theme-elevation-100)'\n : 'var(--theme-elevation-50)',\n border: `1px solid ${isEnabled ? 'var(--theme-text-success)' : 'var(--theme-elevation-200)'}`,\n borderRadius: '6px',\n cursor: 'pointer',\n display: 'flex',\n gap: '10px',\n padding: '10px 15px',\n textAlign: 'left',\n transition: 'all 0.2s ease',\n width: '100%',\n }}\n type=\"button\"\n >\n <div\n style={{\n alignItems: 'center',\n background: isEnabled\n ? 'var(--theme-text-success)'\n : 'var(--theme-elevation-200)',\n borderRadius: '12px',\n display: 'flex',\n height: '24px',\n justifyContent: isEnabled ? 'flex-end' : 'flex-start',\n padding: '2px',\n transition: 'all 0.2s ease',\n width: '44px',\n }}\n >\n <div\n style={{\n background: 'white',\n borderRadius: '50%',\n height: '20px',\n width: '20px',\n }}\n />\n </div>\n <span style={{ fontWeight: 500 }}>\n {typeof collection.labels?.singular === 'string'\n ? collection.labels.singular\n : collection.labels?.singular?.en || collection.slug}\n </span>\n </button>\n )\n })}\n </div>\n </div>\n </div>\n )\n}\n"],"names":["Button","toast","useConfig","useRouter","React","use","useEffect","useState","excludeCollections","InstructionsContext","ConfigDashboard","config","collections","routes","admin","adminRoute","api","apiRoute","router","refresh","setEnabledCollections","setEnabledCollectionsInContext","enabledCollections","isLoading","setIsLoading","isSaving","setIsSaving","availableCollections","filter","c","includes","slug","hidden","fetchSettings","response","fetch","ok","data","json","storedEnabled","Array","isArray","error","console","catch","e","log","handleToggle","prev","s","handleSave","body","JSON","stringify","headers","method","success","div","style","padding","textAlign","background","marginBottom","overflow","alignItems","borderBottom","display","justifyContent","h2","margin","p","color","fontSize","gap","buttonStyle","el","to","disabled","onClick","h5","gridTemplateColumns","map","collection","isEnabled","button","border","borderRadius","cursor","transition","width","type","height","span","fontWeight","labels","singular","en"],"mappings":"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"}
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import { Button, toast, useConfig } from '@payloadcms/ui';
|
|
3
3
|
// @ts-expect-error - Next.js types are not resolving correctly with nodenext but runtime is fine
|
|
4
4
|
import { useRouter } from 'next/navigation';
|
|
5
|
-
import React, {
|
|
5
|
+
import React, { use, useEffect, useState } from 'react';
|
|
6
6
|
import { excludeCollections } from '../../defaults.js';
|
|
7
7
|
import { InstructionsContext } from '../../providers/InstructionsProvider/context.js';
|
|
8
|
-
export const
|
|
8
|
+
export const ConfigDashboard = () => {
|
|
9
9
|
const { config: { collections, routes: { admin: adminRoute, api: apiRoute }, }, } = useConfig();
|
|
10
10
|
const router = useRouter();
|
|
11
|
-
const { refresh, setEnabledCollections: setEnabledCollectionsInContext } =
|
|
11
|
+
const { refresh, setEnabledCollections: setEnabledCollectionsInContext } = use(InstructionsContext);
|
|
12
12
|
const [enabledCollections, setEnabledCollections] = useState([]);
|
|
13
13
|
const [isLoading, setIsLoading] = useState(true);
|
|
14
14
|
const [isSaving, setIsSaving] = useState(false);
|
|
@@ -17,7 +17,7 @@ export const AIConfigDashboard = () => {
|
|
|
17
17
|
useEffect(() => {
|
|
18
18
|
const fetchSettings = async () => {
|
|
19
19
|
try {
|
|
20
|
-
const response = await fetch(`${apiRoute}/globals/ai-
|
|
20
|
+
const response = await fetch(`${apiRoute}/globals/ai-providers`);
|
|
21
21
|
if (response.ok) {
|
|
22
22
|
const data = await response.json();
|
|
23
23
|
// Handle both simple array and object wrapper if Payload wraps it
|
|
@@ -49,7 +49,7 @@ export const AIConfigDashboard = () => {
|
|
|
49
49
|
try {
|
|
50
50
|
// First fetch current settings to get ID or just rely on global update behavior
|
|
51
51
|
// We need to adhere to Payload's global update API
|
|
52
|
-
const response = await fetch(`${apiRoute}/globals/ai-
|
|
52
|
+
const response = await fetch(`${apiRoute}/globals/ai-providers`, {
|
|
53
53
|
body: JSON.stringify({
|
|
54
54
|
enabledCollections,
|
|
55
55
|
}),
|
|
@@ -81,12 +81,14 @@ export const AIConfigDashboard = () => {
|
|
|
81
81
|
}
|
|
82
82
|
};
|
|
83
83
|
if (isLoading) {
|
|
84
|
-
return <div style={{ padding: '20px', textAlign: 'center' }}>Loading
|
|
84
|
+
return <div style={{ padding: '20px', textAlign: 'center' }}>Loading configuration...</div>;
|
|
85
85
|
}
|
|
86
86
|
return (<div style={{
|
|
87
87
|
background: 'var(--theme-elevation-50)',
|
|
88
88
|
// border: '1px solid var(--theme-elevation-150)',
|
|
89
89
|
// borderRadius: '8px',
|
|
90
|
+
// borderBottom: '1px solid var(--theme-elevation-150)',
|
|
91
|
+
// borderTop: '1px solid var(--theme-elevation-150)',
|
|
90
92
|
marginBottom: '20px',
|
|
91
93
|
overflow: 'hidden',
|
|
92
94
|
}}>
|
|
@@ -95,17 +97,17 @@ export const AIConfigDashboard = () => {
|
|
|
95
97
|
borderBottom: '1px solid var(--theme-elevation-150)',
|
|
96
98
|
display: 'flex',
|
|
97
99
|
justifyContent: 'space-between',
|
|
98
|
-
padding: '
|
|
100
|
+
padding: '8px var(--gutter-h)',
|
|
99
101
|
}}>
|
|
100
102
|
<div>
|
|
101
|
-
<
|
|
103
|
+
<h2 style={{ margin: '0 0 5px 0' }}>Let's configure your AI Plugin</h2>
|
|
102
104
|
<p style={{ color: 'var(--theme-elevation-500)', fontSize: '14px', margin: '0' }}>
|
|
103
|
-
|
|
105
|
+
Set up the provider → Choose the content → Refine the behavior.
|
|
104
106
|
</p>
|
|
105
107
|
</div>
|
|
106
108
|
<div style={{ display: 'flex', gap: '10px' }}>
|
|
107
|
-
<Button buttonStyle="secondary" el="link" to={`${adminRoute}/globals/ai-
|
|
108
|
-
|
|
109
|
+
<Button buttonStyle="secondary" el="link" to={`${adminRoute}/globals/ai-providers`}>
|
|
110
|
+
Providers
|
|
109
111
|
</Button>
|
|
110
112
|
<Button disabled={isSaving} onClick={handleSave}>
|
|
111
113
|
{isSaving ? 'Saving...' : 'Save Changes'}
|
|
@@ -113,8 +115,11 @@ export const AIConfigDashboard = () => {
|
|
|
113
115
|
</div>
|
|
114
116
|
</div>
|
|
115
117
|
|
|
116
|
-
<div style={{ padding: '
|
|
117
|
-
<h5 style={{ marginBottom: '15px' }}>
|
|
118
|
+
<div style={{ padding: '24px var(--gutter-h)' }}>
|
|
119
|
+
<h5 style={{ marginBottom: '15px' }}>
|
|
120
|
+
Select the collections where AI features should be available, toggle them on or off, and
|
|
121
|
+
save your changes.
|
|
122
|
+
</h5>
|
|
118
123
|
<div style={{
|
|
119
124
|
display: 'grid',
|
|
120
125
|
gap: '15px',
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import locales from 'locale-codes';
|
|
3
|
-
import React, { memo, useState } from 'react';
|
|
3
|
+
import React, { memo, useEffect, useRef, useState } from 'react';
|
|
4
4
|
import { useInstructions } from '../../../../providers/InstructionsProvider/useInstructions.js';
|
|
5
5
|
import { Item } from './Item.js';
|
|
6
6
|
import { Translate } from './items.js';
|
|
7
7
|
import styles from './menu.module.scss';
|
|
8
8
|
export const TranslateMenu = ({ onClick })=>{
|
|
9
9
|
const [show, setShow] = useState(false);
|
|
10
|
+
const closeTimeoutRef = useRef(null);
|
|
10
11
|
const { enabledLanguages = [] } = useInstructions();
|
|
11
12
|
let filteredLocales = locales.all.filter((a)=>{
|
|
12
13
|
return a.tag && a.location;
|
|
@@ -16,11 +17,48 @@ export const TranslateMenu = ({ onClick })=>{
|
|
|
16
17
|
}
|
|
17
18
|
const [languages, setLanguages] = useState(filteredLocales);
|
|
18
19
|
const [inputFocus, setInputFocus] = useState(false);
|
|
20
|
+
useEffect(()=>{
|
|
21
|
+
if (!show) {
|
|
22
|
+
if (typeof window !== 'undefined') {
|
|
23
|
+
window.__AI_MENU_INTERACTIVE = false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return ()=>{
|
|
27
|
+
if (closeTimeoutRef.current) {
|
|
28
|
+
clearTimeout(closeTimeoutRef.current);
|
|
29
|
+
}
|
|
30
|
+
if (show && typeof window !== 'undefined') {
|
|
31
|
+
window.__AI_MENU_INTERACTIVE = false;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}, [
|
|
35
|
+
show
|
|
36
|
+
]);
|
|
19
37
|
return /*#__PURE__*/ _jsxs("div", {
|
|
20
|
-
className: styles.menu
|
|
38
|
+
className: `${styles.menu} ai-interactive-menu`,
|
|
39
|
+
"data-ai-interactive": "true",
|
|
40
|
+
onBlur: (e)=>{
|
|
41
|
+
// Only clear if focus moves outside the menu container
|
|
42
|
+
if (!e.currentTarget.contains(e.relatedTarget)) {
|
|
43
|
+
if (typeof window !== 'undefined') window.__AI_MENU_INTERACTIVE = false;
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
onFocus: ()=>{
|
|
47
|
+
if (typeof window !== 'undefined') window.__AI_MENU_INTERACTIVE = true;
|
|
48
|
+
},
|
|
49
|
+
onMouseEnter: ()=>{
|
|
50
|
+
if (closeTimeoutRef.current) {
|
|
51
|
+
clearTimeout(closeTimeoutRef.current);
|
|
52
|
+
closeTimeoutRef.current = null;
|
|
53
|
+
}
|
|
54
|
+
if (typeof window !== 'undefined') window.__AI_MENU_INTERACTIVE = true;
|
|
55
|
+
},
|
|
21
56
|
onMouseLeave: ()=>{
|
|
57
|
+
if (typeof window !== 'undefined') window.__AI_MENU_INTERACTIVE = false;
|
|
22
58
|
if (!inputFocus) {
|
|
23
|
-
|
|
59
|
+
closeTimeoutRef.current = setTimeout(()=>{
|
|
60
|
+
setShow(false);
|
|
61
|
+
}, 400);
|
|
24
62
|
}
|
|
25
63
|
},
|
|
26
64
|
children: [
|
|
@@ -49,7 +87,8 @@ export const TranslateMenu = ({ onClick })=>{
|
|
|
49
87
|
top: 0
|
|
50
88
|
},
|
|
51
89
|
children: /*#__PURE__*/ _jsx("input", {
|
|
52
|
-
className: styles.menuInput
|
|
90
|
+
className: `${styles.menuInput} ai-interactive-menu`,
|
|
91
|
+
"data-ai-interactive": "true",
|
|
53
92
|
onBlur: ()=>setInputFocus(false),
|
|
54
93
|
onChange: (event)=>{
|
|
55
94
|
const value = event.target.value;
|
|
@@ -64,6 +103,8 @@ export const TranslateMenu = ({ onClick })=>{
|
|
|
64
103
|
}),
|
|
65
104
|
languages.map((locale)=>{
|
|
66
105
|
return /*#__PURE__*/ _jsx(Item, {
|
|
106
|
+
className: "ai-interactive-menu",
|
|
107
|
+
"data-ai-interactive": "true",
|
|
67
108
|
onClick: ()=>{
|
|
68
109
|
onClick({
|
|
69
110
|
locale: locale.tag
|