strapi-content-embeddings 0.1.8 → 0.2.0
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/_chunks/{App-BfvnOBS9.js → App-ByRBbkZn.js} +2 -1
- package/dist/_chunks/App-ByRBbkZn.js.map +1 -0
- package/dist/_chunks/{App-sRU0Nh3x.mjs → App-MjsTrWRS.mjs} +2 -1
- package/dist/_chunks/App-MjsTrWRS.mjs.map +1 -0
- package/dist/_chunks/en-B4KWt_jN.js +1 -0
- package/dist/_chunks/en-B4KWt_jN.js.map +1 -0
- package/dist/_chunks/en-Byx4XI2L.mjs +1 -0
- package/dist/_chunks/en-Byx4XI2L.mjs.map +1 -0
- package/dist/_chunks/{index-DkNKkHgk.js → index-TWbcT-zJ.js} +111 -42
- package/dist/_chunks/index-TWbcT-zJ.js.map +1 -0
- package/dist/_chunks/{index-C58A29qR.mjs → index-ifqYByO5.mjs} +112 -43
- package/dist/_chunks/index-ifqYByO5.mjs.map +1 -0
- package/dist/admin/index.js +2 -1
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/index.mjs +2 -1
- package/dist/admin/index.mjs.map +1 -0
- package/dist/server/index.js +57 -59
- package/dist/server/index.js.map +1 -0
- package/dist/server/index.mjs +57 -59
- package/dist/server/index.mjs.map +1 -0
- package/dist/server/src/services/embeddings.d.ts +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-TWbcT-zJ.js","sources":["../../admin/src/pluginId.ts","../../admin/src/utils/getTranslation.ts","../../admin/src/components/Initializer.tsx","../../admin/src/components/custom/RobotIcon.tsx","../../admin/src/components/PluginIcon.tsx","../../admin/src/components/custom/MarkdownEditor.tsx","../../admin/src/components/custom/EmbeddingsModal.tsx","../../admin/src/components/custom/EmbeddingsWidget.tsx","../../admin/src/index.tsx"],"sourcesContent":["export const PLUGIN_ID = 'strapi-content-embeddings';\n","import { PLUGIN_ID } from '../pluginId';\n\nconst getTranslation = (id: string) => `${PLUGIN_ID}.${id}`;\n\nexport { getTranslation };\n","import { useEffect, useRef } from 'react';\n\nimport { PLUGIN_ID } from '../pluginId';\n\ntype InitializerProps = {\n setPlugin: (id: string) => void;\n};\n\nconst Initializer = ({ setPlugin }: InitializerProps) => {\n const ref = useRef(setPlugin);\n\n useEffect(() => {\n ref.current(PLUGIN_ID);\n }, []);\n\n return null;\n};\n\nexport { Initializer };\n","import React from \"react\";\n\ninterface RobotIconProps {\n height?: string | number;\n width?: string | number;\n}\n\nexport function RobotIcon({ height = 48, width = 48 }: RobotIconProps) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n height={height}\n viewBox=\"0 -960 960 960\"\n width={width}\n fill=\"currentColor\"\n >\n <path d=\"M160-120v-220q0-24.75 17.625-42.375T220-400h520q24.75 0 42.375 17.625T800-340v220H160Zm200-320q-83 0-141.5-58.5T160-640q0-83 58.5-141.5T360-840h240q83 0 141.5 58.5T800-640q0 83-58.5 141.5T600-440H360ZM220-180h520v-160H220v160Zm140-320h240q58.333 0 99.167-40.765 40.833-40.764 40.833-99Q740-698 699.167-739 658.333-780 600-780H360q-58.333 0-99.167 40.765-40.833 40.764-40.833 99Q220-582 260.833-541q40.834 41 99.167 41Zm.175-110q12.825 0 21.325-8.675 8.5-8.676 8.5-21.5 0-12.825-8.675-21.325-8.676-8.5-21.5-8.5-12.825 0-21.325 8.675-8.5 8.676-8.5 21.5 0 12.825 8.675 21.325 8.676 8.5 21.5 8.5Zm240 0q12.825 0 21.325-8.675 8.5-8.676 8.5-21.5 0-12.825-8.675-21.325-8.676-8.5-21.5-8.5-12.825 0-21.325 8.675-8.5 8.676-8.5 21.5 0 12.825 8.675 21.325 8.676 8.5 21.5 8.5ZM480-180Zm0-460Z\" />\n </svg>\n );\n}\n","import { RobotIcon } from './custom/RobotIcon';\n\nconst PluginIcon = () => <RobotIcon height={24} width={24} />;\n\nexport { PluginIcon };\n","import { useState } from 'react';\nimport { Box } from '@strapi/design-system';\nimport styled, { createGlobalStyle } from 'styled-components';\nimport {\n MDXEditor,\n headingsPlugin,\n listsPlugin,\n quotePlugin,\n thematicBreakPlugin,\n markdownShortcutPlugin,\n linkPlugin,\n linkDialogPlugin,\n toolbarPlugin,\n BoldItalicUnderlineToggles,\n BlockTypeSelect,\n CreateLink,\n ListsToggle,\n UndoRedo,\n Separator,\n} from '@mdxeditor/editor';\n\ninterface MarkdownEditorProps {\n content: string;\n onChange: (content: string) => void;\n height?: number;\n}\n\nconst MDXEditorStyles = createGlobalStyle`\n /* MDXEditor CSS Variables */\n :root {\n --mdx-spacing-0_5: 0.125rem;\n --mdx-spacing-1: 0.25rem;\n --mdx-spacing-1_5: 0.375rem;\n --mdx-spacing-2: 0.5rem;\n --mdx-spacing-3: 0.75rem;\n --mdx-spacing-4: 1rem;\n --mdx-spacing-36: 9rem;\n --mdx-radius-base: 0.25rem;\n --mdx-radius-medium: 0.375rem;\n --mdx-text-sm: 0.875rem;\n --mdx-baseBg: #f6f6f9;\n --mdx-baseBgActive: #e8e8ec;\n --mdx-basePageBg: #ffffff;\n --mdx-baseBorder: #dcdce4;\n --mdx-baseBorderHover: #b9bbc6;\n --mdx-baseBase: #e0e1e6;\n --mdx-baseTextContrast: #1c2024;\n --mdx-accentText: #4945ff;\n }\n\n /* Toolbar Root - critical for horizontal layout */\n [class*=\"_toolbarRoot\"] {\n z-index: 2;\n display: flex !important;\n flex-direction: row !important;\n flex-wrap: wrap !important;\n gap: var(--mdx-spacing-1);\n border-radius: var(--mdx-radius-medium);\n padding: var(--mdx-spacing-1_5);\n align-items: center !important;\n overflow-x: auto;\n position: sticky;\n top: 0;\n background-color: var(--mdx-baseBg) !important;\n border-bottom: 1px solid var(--mdx-baseBorder);\n width: 100%;\n }\n\n [class*=\"_toolbarRoot\"] div[role='separator'] {\n margin: var(--mdx-spacing-2) var(--mdx-spacing-1);\n border-left: 1px solid var(--mdx-baseBorder);\n border-right: 1px solid var(--mdx-baseBase);\n height: var(--mdx-spacing-4);\n }\n\n [class*=\"_toolbarRoot\"] svg {\n color: var(--mdx-baseTextContrast);\n display: block;\n }\n\n /* Toolbar button groups */\n [class*=\"_toolbarGroupOfGroups\"] {\n display: flex;\n margin: 0 var(--mdx-spacing-1);\n }\n\n [class*=\"_toolbarToggleSingleGroup\"] {\n display: flex;\n align-items: center;\n white-space: nowrap;\n }\n\n /* Toolbar buttons and toggle items */\n [class*=\"_toolbarToggleItem\"],\n [class*=\"_toolbarButton\"] {\n border: 0;\n background-color: transparent;\n font-size: inherit;\n appearance: none;\n box-sizing: border-box;\n cursor: pointer;\n padding: var(--mdx-spacing-0_5);\n border-radius: var(--mdx-radius-base);\n }\n\n [class*=\"_toolbarToggleItem\"]:hover,\n [class*=\"_toolbarButton\"]:hover {\n background-color: var(--mdx-baseBgActive);\n }\n\n [class*=\"_toolbarToggleItem\"][data-state='on'],\n [class*=\"_toolbarButton\"][data-state='on'],\n [class*=\"_toolbarToggleItem\"]:active,\n [class*=\"_toolbarButton\"]:active {\n color: var(--mdx-baseTextContrast);\n background-color: var(--mdx-baseBgActive);\n }\n\n /* Block type select dropdown */\n [class*=\"_toolbarNodeKindSelectTrigger\"],\n [class*=\"_selectTrigger\"] {\n border: 0;\n display: flex;\n color: inherit;\n align-items: center;\n width: var(--mdx-spacing-36);\n padding: var(--mdx-spacing-0_5) var(--mdx-spacing-1);\n padding-inline-start: var(--mdx-spacing-2);\n border-radius: var(--mdx-radius-medium);\n white-space: nowrap;\n font-size: var(--mdx-text-sm);\n background-color: var(--mdx-basePageBg);\n margin: 0 var(--mdx-spacing-1);\n cursor: pointer;\n }\n\n /* Dropdown containers */\n [class*=\"_toolbarNodeKindSelectContainer\"],\n [class*=\"_selectContainer\"] {\n filter: drop-shadow(0 2px 2px rgb(0 0 0 / 0.2));\n z-index: 100;\n width: var(--mdx-spacing-36);\n border-radius: var(--mdx-radius-base);\n background-color: var(--mdx-basePageBg);\n font-size: var(--mdx-text-sm);\n }\n\n /* Select items */\n [class*=\"_toolbarNodeKindSelectItem\"],\n [class*=\"_selectItem\"] {\n cursor: pointer;\n display: flex;\n padding: var(--mdx-spacing-2);\n }\n\n [class*=\"_toolbarNodeKindSelectItem\"][data-highlighted],\n [class*=\"_selectItem\"][data-highlighted],\n [class*=\"_toolbarNodeKindSelectItem\"][data-state='checked'],\n [class*=\"_selectItem\"][data-state='checked'] {\n background-color: var(--mdx-baseBg);\n outline: none;\n }\n\n /* Dropdown arrow */\n [class*=\"_selectDropdownArrow\"] {\n margin-left: auto;\n display: flex;\n align-items: center;\n }\n\n /* Content editable area */\n [class*=\"_contentEditable\"] {\n box-sizing: border-box;\n width: 100%;\n color: var(--mdx-baseTextContrast);\n padding: var(--mdx-spacing-3);\n min-height: 200px;\n outline: none;\n font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n font-size: 14px;\n line-height: 1.6;\n }\n\n [class*=\"_contentEditable\"]:focus {\n outline: none;\n }\n\n /* Placeholder positioning - ensure it's at the top */\n [class*=\"_contentEditable\"] {\n position: relative;\n }\n\n [class*=\"_contentEditable\"][data-placeholder]::before {\n position: absolute;\n top: var(--mdx-spacing-3);\n left: var(--mdx-spacing-3);\n color: #a5a5ba;\n pointer-events: none;\n }\n\n /* MDXEditor/Lexical placeholder styles */\n [class*=\"_placeholder\"],\n [class*=\"ContentEditable__placeholder\"],\n [class*=\"editor-placeholder\"] {\n position: absolute !important;\n top: var(--mdx-spacing-3) !important;\n left: var(--mdx-spacing-3) !important;\n color: #a5a5ba;\n pointer-events: none;\n overflow: hidden;\n text-overflow: ellipsis;\n user-select: none;\n display: inline-block;\n }\n\n /* Editor root wrapper needs relative positioning for placeholder */\n [class*=\"_rootContentEditableWrapper\"],\n [class*=\"_editorWrapper\"] {\n position: relative;\n display: flex;\n flex-direction: column;\n }\n\n /* Heading styles */\n [class*=\"_contentEditable\"] h1 {\n font-size: 1.75rem;\n font-weight: 600;\n margin: 0 0 1rem;\n }\n\n [class*=\"_contentEditable\"] h2 {\n font-size: 1.5rem;\n font-weight: 600;\n margin: 1rem 0 0.75rem;\n }\n\n [class*=\"_contentEditable\"] h3 {\n font-size: 1.25rem;\n font-weight: 600;\n margin: 1rem 0 0.5rem;\n }\n\n /* Paragraph and list styles */\n [class*=\"_contentEditable\"] p {\n margin: 0 0 1rem;\n }\n\n [class*=\"_contentEditable\"] ul,\n [class*=\"_contentEditable\"] ol {\n margin: 0 0 1rem;\n padding-left: 1.5rem;\n }\n\n [class*=\"_contentEditable\"] li {\n margin: 0.25rem 0;\n }\n\n /* Code styles */\n [class*=\"_contentEditable\"] code {\n background: #f0f0f5;\n padding: 0.2em 0.4em;\n border-radius: 3px;\n font-family: \"Monaco\", \"Menlo\", monospace;\n font-size: 0.9em;\n }\n\n [class*=\"_contentEditable\"] pre {\n background: #2d2d2d;\n color: #f8f8f2;\n padding: 1rem;\n border-radius: 4px;\n overflow-x: auto;\n margin: 0 0 1rem;\n }\n\n [class*=\"_contentEditable\"] pre code {\n background: none;\n padding: 0;\n }\n\n /* Blockquote */\n [class*=\"_contentEditable\"] blockquote {\n border-left: 3px solid #dcdce4;\n margin: 0 0 1rem;\n padding-left: 1rem;\n color: #666;\n }\n\n /* Links */\n [class*=\"_contentEditable\"] a {\n color: #4945ff;\n text-decoration: underline;\n }\n\n /* Horizontal rule */\n [class*=\"_contentEditable\"] hr {\n border: none;\n border-top: 1px solid #dcdce4;\n margin: 1.5rem 0;\n }\n\n /* Editor root */\n [class*=\"_editorRoot\"] {\n font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n color: var(--mdx-baseTextContrast);\n background: var(--mdx-basePageBg);\n }\n\n /* Link dialog */\n [class*=\"_linkDialogPopoverContent\"] {\n display: flex;\n flex-direction: column;\n gap: var(--mdx-spacing-2);\n padding: var(--mdx-spacing-3);\n background-color: var(--mdx-basePageBg);\n border-radius: var(--mdx-radius-medium);\n box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);\n z-index: 100;\n }\n\n [class*=\"_linkDialogInputWrapper\"] {\n display: flex;\n gap: var(--mdx-spacing-1);\n }\n\n [class*=\"_linkDialogInputWrapper\"] input {\n flex: 1;\n padding: var(--mdx-spacing-1) var(--mdx-spacing-2);\n border: 1px solid var(--mdx-baseBorder);\n border-radius: var(--mdx-radius-base);\n font-size: var(--mdx-text-sm);\n }\n\n [class*=\"_linkDialogInputWrapper\"] button {\n padding: var(--mdx-spacing-1) var(--mdx-spacing-2);\n background-color: var(--mdx-accentText);\n color: white;\n border: none;\n border-radius: var(--mdx-radius-base);\n cursor: pointer;\n }\n\n /* Popover positioning */\n [data-radix-popper-content-wrapper] {\n z-index: 100 !important;\n }\n`;\n\nconst EditorWrapper = styled(Box)<{ $isFocused: boolean }>`\n border: 1px solid ${({ $isFocused }) => ($isFocused ? '#4945ff' : '#dcdce4')};\n border-radius: 4px;\n overflow: hidden;\n background: #fff;\n transition: border-color 0.2s, box-shadow 0.2s;\n box-shadow: ${({ $isFocused }) => ($isFocused ? '0 0 0 2px rgba(73, 69, 255, 0.2)' : 'none')};\n`;\n\n// Toolbar contents component defined outside to prevent re-renders\nfunction ToolbarContents() {\n return (\n <>\n <UndoRedo />\n <Separator />\n <BlockTypeSelect />\n <Separator />\n <BoldItalicUnderlineToggles />\n <Separator />\n <CreateLink />\n <Separator />\n <ListsToggle />\n </>\n );\n}\n\nexport function MarkdownEditor({ content, onChange, height = 300 }: Readonly<MarkdownEditorProps>) {\n const [isFocused, setIsFocused] = useState(false);\n\n return (\n <>\n <MDXEditorStyles />\n <EditorWrapper\n $isFocused={isFocused}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n >\n <div style={{ minHeight: `${height}px` }}>\n <MDXEditor\n markdown={content}\n onChange={onChange}\n placeholder=\"Write your content here...\"\n plugins={[\n headingsPlugin(),\n listsPlugin(),\n quotePlugin(),\n thematicBreakPlugin(),\n linkPlugin(),\n linkDialogPlugin(),\n markdownShortcutPlugin(),\n toolbarPlugin({\n toolbarContents: ToolbarContents,\n }),\n ]}\n />\n </div>\n </EditorWrapper>\n </>\n );\n}\n","import React, { useState, useEffect, useCallback } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport styled from \"styled-components\";\nimport qs from \"qs\";\nimport {\n unstable_useContentManagerContext as useContentManagerContext,\n useFetchClient,\n useNotification,\n} from \"@strapi/strapi/admin\";\nimport {\n Button,\n Typography,\n Box,\n Modal,\n Field,\n TextInput,\n Loader,\n Flex,\n SingleSelect,\n SingleSelectOption,\n} from \"@strapi/design-system\";\nimport { Plus, Eye } from \"@strapi/icons\";\nimport { PLUGIN_ID } from \"../../pluginId\";\nimport { MarkdownEditor } from \"./MarkdownEditor\";\n\nconst StyledTypography = styled(Typography)`\n display: block;\n margin-top: 1rem;\n margin-bottom: 0.5rem;\n`;\n\nconst CHUNK_SIZE = 4000; // Content over this will be auto-chunked\n\ninterface ExistingEmbedding {\n documentId: string;\n title: string;\n content?: string;\n}\n\ninterface TextFieldOption {\n name: string;\n label: string;\n value: string;\n charCount: number;\n}\n\nexport function EmbeddingsModal() {\n const { post, get } = useFetchClient();\n const { toggleNotification } = useNotification();\n const navigate = useNavigate();\n\n // Access content manager context\n const context = useContentManagerContext();\n const { form, id, slug, collectionType } = context;\n\n const modifiedValues = form?.values || {};\n\n const [isVisible, setIsVisible] = useState(false);\n const [title, setTitle] = useState(\"\");\n const [content, setContent] = useState(\"\");\n const [fieldName, setFieldName] = useState(\"\");\n const [availableFields, setAvailableFields] = useState<TextFieldOption[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [isCheckingExisting, setIsCheckingExisting] = useState(true);\n const [existingEmbedding, setExistingEmbedding] = useState<ExistingEmbedding | null>(null);\n\n // Check for existing embedding when component mounts or id changes\n useEffect(() => {\n async function checkExistingEmbedding() {\n if (!id || !slug) {\n setIsCheckingExisting(false);\n return;\n }\n\n try {\n // Query embeddings filtered by metadata containing this documentId\n const query = qs.stringify({\n filters: {\n metadata: { $containsi: id },\n },\n });\n\n const response = await get(`/${PLUGIN_ID}/embeddings/find?${query}`);\n\n // Handle response - could be { data: [...] } or { data: { data: [...] } }\n const embeddings = response.data?.data || response.data || [];\n\n if (Array.isArray(embeddings) && embeddings.length > 0) {\n // Found existing embedding for this content\n setExistingEmbedding({\n documentId: embeddings[0].documentId,\n title: embeddings[0].title,\n content: embeddings[0].content,\n });\n }\n } catch (error) {\n console.error(\"Failed to check for existing embedding:\", error);\n } finally {\n setIsCheckingExisting(false);\n }\n }\n\n checkExistingEmbedding();\n }, [id, slug, get]);\n\n // Extract text value from a field (handles strings, blocks, and dynamic zones)\n const extractTextFromField = useCallback((value: any, depth: number = 0): string => {\n if (!value || depth > 5) return \"\"; // Prevent infinite recursion\n\n // Handle string values\n if (typeof value === \"string\") {\n return value.trim();\n }\n\n // Handle arrays (could be blocks, dynamic zones, or repeatable components)\n if (Array.isArray(value)) {\n const texts: string[] = [];\n\n for (const item of value) {\n // Check if it's a dynamic zone component (has __component property)\n if (item && typeof item === \"object\" && item.__component) {\n // Extract text from all fields in the component\n for (const [key, fieldValue] of Object.entries(item)) {\n if (key === \"__component\" || key === \"id\") continue;\n const extracted = extractTextFromField(fieldValue, depth + 1);\n if (extracted) texts.push(extracted);\n }\n }\n // Check if it's a blocks format item (rich text)\n else if (item && item.children) {\n const blockText = item.children\n .map((child: any) => child.text || \"\")\n .join(\"\");\n if (blockText) texts.push(blockText);\n }\n // Recursively handle nested arrays/objects\n else if (item && typeof item === \"object\") {\n const extracted = extractTextFromField(item, depth + 1);\n if (extracted) texts.push(extracted);\n }\n }\n\n return texts.join(\"\\n\\n\").trim();\n }\n\n // Handle objects (could be a component or relation)\n if (typeof value === \"object\") {\n const texts: string[] = [];\n\n for (const [key, fieldValue] of Object.entries(value)) {\n // Skip metadata fields\n if ([\"id\", \"__component\", \"documentId\", \"createdAt\", \"updatedAt\"].includes(key)) continue;\n const extracted = extractTextFromField(fieldValue, depth + 1);\n if (extracted) texts.push(extracted);\n }\n\n return texts.join(\"\\n\\n\").trim();\n }\n\n return \"\";\n }, []);\n\n // Check if a value is a dynamic zone\n const isDynamicZone = (value: any): boolean => {\n return Array.isArray(value) && value.length > 0 && value[0]?.__component;\n };\n\n // Detect all available text fields from form values\n const detectTextFields = useCallback((): TextFieldOption[] => {\n if (!modifiedValues) return [];\n\n const fields: TextFieldOption[] = [];\n\n // Check all fields in form values\n for (const [name, value] of Object.entries(modifiedValues)) {\n // Skip non-content fields\n if ([\"id\", \"documentId\", \"createdAt\", \"updatedAt\", \"publishedAt\", \"locale\", \"localizations\"].includes(name)) {\n continue;\n }\n\n const textValue = extractTextFromField(value);\n if (textValue && textValue.length > 0) {\n // Create a readable label from field name\n let label = name\n .replace(/([A-Z])/g, \" $1\")\n .replace(/^./, (str) => str.toUpperCase())\n .trim();\n\n // Add indicator for dynamic zones\n if (isDynamicZone(value)) {\n const componentCount = (value as any[]).length;\n label += ` (${componentCount} component${componentCount > 1 ? \"s\" : \"\"})`;\n }\n\n fields.push({\n name,\n label,\n value: textValue,\n charCount: textValue.length,\n });\n }\n }\n\n // Sort by character count (longest first) to prioritize main content\n fields.sort((a, b) => b.charCount - a.charCount);\n\n return fields;\n }, [modifiedValues, extractTextFromField]);\n\n // Update available fields when form values change\n useEffect(() => {\n const fields = detectTextFields();\n setAvailableFields(fields);\n\n // Auto-select first field if none selected\n if (fields.length > 0 && !fieldName) {\n setFieldName(fields[0].name);\n setContent(fields[0].value);\n }\n }, [detectTextFields, fieldName]);\n\n // Handle field selection change\n const handleFieldChange = (selectedFieldName: string) => {\n setFieldName(selectedFieldName);\n const selectedField = availableFields.find(f => f.name === selectedFieldName);\n if (selectedField) {\n setContent(selectedField.value);\n }\n };\n\n const contentLength = content.length;\n const willChunk = contentLength > CHUNK_SIZE;\n const estimatedChunks = willChunk ? Math.ceil(contentLength / (CHUNK_SIZE - 200)) : 1;\n // Check if content is saved (has an id) - don't require publish\n const isSaved = !!id;\n\n // Auto-generate metadata from collection context\n function generateMetadata(): Record<string, any> {\n return {\n source: \"content-manager\",\n collectionType: slug || collectionType || \"unknown\",\n fieldName: fieldName || \"content\",\n documentId: id,\n updatedAt: new Date().toISOString(),\n };\n }\n\n const isValid = title.trim() && content.trim(); // No length limit - auto-chunks if needed\n\n function handleOpenCreate() {\n setTitle(\"\");\n // Refresh available fields and select first one\n const fields = detectTextFields();\n setAvailableFields(fields);\n if (fields.length > 0) {\n setFieldName(fields[0].name);\n setContent(fields[0].value);\n }\n setIsVisible(true);\n }\n\n async function handleSubmit(e: React.FormEvent) {\n e.preventDefault();\n\n if (!title.trim()) {\n toggleNotification({\n type: \"warning\",\n message: \"Embeddings title is required\",\n });\n return;\n }\n\n if (!content.trim()) {\n toggleNotification({\n type: \"warning\",\n message: \"Embeddings content is required\",\n });\n return;\n }\n\n setIsLoading(true);\n\n try {\n const contentToEmbed = content.trim();\n const shouldChunk = contentToEmbed.length > CHUNK_SIZE;\n const chunks = shouldChunk ? Math.ceil(contentToEmbed.length / CHUNK_SIZE) : 1;\n\n if (shouldChunk) {\n console.log(`Creating chunked embedding: ${contentToEmbed.length} chars (~${chunks} parts)`);\n }\n\n const result = await post(`/${PLUGIN_ID}/embeddings/create-embedding`, {\n data: {\n title: title.trim(),\n content: contentToEmbed,\n collectionType: slug || collectionType,\n fieldName,\n metadata: generateMetadata(),\n autoChunk: shouldChunk,\n },\n });\n\n const responseData = result?.data || result;\n\n if (responseData?.documentId) {\n setExistingEmbedding({\n documentId: responseData.documentId,\n title: responseData.title,\n content: responseData.content,\n });\n }\n setIsVisible(false);\n\n const message = shouldChunk\n ? `Embedding created and chunked into ${chunks} parts`\n : \"Embedding created successfully\";\n toggleNotification({ type: \"success\", message });\n } catch (error: any) {\n console.error(\"Failed to create embedding:\", error);\n toggleNotification({\n type: \"danger\",\n message: error.message || \"Failed to create embedding\",\n });\n } finally {\n setIsLoading(false);\n }\n }\n\n function handleViewEmbedding() {\n if (existingEmbedding?.documentId) {\n navigate(`/plugins/${PLUGIN_ID}/embeddings/${existingEmbedding.documentId}`);\n }\n }\n\n // Don't render if not in edit view context\n if (!form || !id) {\n return null;\n }\n\n // Show loading state while checking for existing embedding\n if (isCheckingExisting) {\n return (\n <Box paddingTop={2}>\n <Loader small>Checking embeddings...</Loader>\n </Box>\n );\n }\n\n const submitButtonText = isLoading ? \"Creating...\" : \"Create Embedding\";\n\n return (\n <Box paddingTop={2}>\n {existingEmbedding ? (\n <Button onClick={handleViewEmbedding} startIcon={<Eye />} fullWidth>\n View Embedding\n </Button>\n ) : (\n <Button\n onClick={handleOpenCreate}\n startIcon={<Plus />}\n disabled={!isSaved}\n fullWidth\n >\n Create Embedding\n </Button>\n )}\n\n {!isSaved && !existingEmbedding && (\n <Typography variant=\"pi\" textColor=\"neutral600\" style={{ display: \"block\", marginTop: \"0.5rem\" }}>\n Save content first to create embedding\n </Typography>\n )}\n\n <Modal.Root open={isVisible} onOpenChange={setIsVisible}>\n <Modal.Content>\n <Modal.Header>\n <Modal.Title>Create Embedding from Content</Modal.Title>\n </Modal.Header>\n <Modal.Body>\n <Box>\n <Box marginBottom={4}>\n <Field.Root>\n <Field.Label>Title</Field.Label>\n <TextInput\n placeholder=\"Enter embedding title\"\n value={title}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setTitle(e.target.value)\n }\n />\n </Field.Root>\n </Box>\n\n {availableFields.length > 0 && (\n <Box marginBottom={4}>\n <Field.Root>\n <Field.Label>Source Field</Field.Label>\n <Field.Hint>Select which field to use for the embedding content</Field.Hint>\n </Field.Root>\n <SingleSelect\n value={fieldName}\n onChange={(value: string) => handleFieldChange(value)}\n placeholder=\"Select a field\"\n >\n {availableFields.map((field) => (\n <SingleSelectOption key={field.name} value={field.name}>\n {field.label} ({field.charCount.toLocaleString()} chars)\n </SingleSelectOption>\n ))}\n </SingleSelect>\n </Box>\n )}\n\n <Box marginBottom={4}>\n <Flex justifyContent=\"space-between\" alignItems=\"center\">\n <Field.Root>\n <Field.Label>Content Preview</Field.Label>\n </Field.Root>\n <Typography variant=\"pi\" textColor=\"neutral600\">\n {contentLength.toLocaleString()} characters\n {willChunk && (\n <Typography textColor=\"primary600\"> (~{estimatedChunks} chunks)</Typography>\n )}\n </Typography>\n </Flex>\n <MarkdownEditor\n content={content}\n onChange={setContent}\n height={200}\n />\n </Box>\n </Box>\n </Modal.Body>\n <Modal.Footer>\n <Modal.Close>\n <Button variant=\"tertiary\">Cancel</Button>\n </Modal.Close>\n <Button\n onClick={handleSubmit}\n disabled={isLoading || !isValid}\n loading={isLoading}\n >\n {submitButtonText}\n </Button>\n </Modal.Footer>\n </Modal.Content>\n </Modal.Root>\n </Box>\n );\n}\n","import React from \"react\";\nimport { Box } from \"@strapi/design-system\";\nimport { EmbeddingsModal } from \"./EmbeddingsModal\";\n\nexport function EmbeddingsWidget() {\n return (\n <Box>\n <EmbeddingsModal />\n </Box>\n );\n}\n","import React from 'react';\nimport { getTranslation } from './utils/getTranslation';\nimport { PLUGIN_ID } from './pluginId';\nimport { Initializer } from './components/Initializer';\nimport { PluginIcon } from './components/PluginIcon';\nimport { EmbeddingsWidget } from './components/custom/EmbeddingsWidget';\n\nexport default {\n register(app: any) {\n app.addMenuLink({\n to: `plugins/${PLUGIN_ID}`,\n icon: PluginIcon,\n intlLabel: {\n id: `${PLUGIN_ID}.plugin.name`,\n defaultMessage: PLUGIN_ID,\n },\n Component: async () => {\n const { App } = await import('./pages/App');\n return App;\n },\n });\n\n app.registerPlugin({\n id: PLUGIN_ID,\n initializer: Initializer,\n isReady: false,\n name: PLUGIN_ID,\n });\n },\n\n bootstrap(app: any) {\n app.getPlugin('content-manager').injectComponent('editView', 'right-links', {\n name: \"open-ai-embeddings\",\n Component: () => <EmbeddingsWidget />\n })\n },\n\n async registerTrads(app: any) {\n const { locales } = app;\n\n const importedTranslations = await Promise.all(\n (locales as string[]).map((locale) => {\n return import(`./translations/${locale}.json`)\n .then(({ default: data }) => {\n return {\n data: getTranslation(data),\n locale,\n };\n })\n .catch(() => {\n return {\n data: {},\n locale,\n };\n });\n })\n );\n\n return importedTranslations;\n },\n};\n"],"names":["useRef","useEffect","jsx","createGlobalStyle","styled","Box","jsxs","Fragment","UndoRedo","Separator","BlockTypeSelect","BoldItalicUnderlineToggles","CreateLink","ListsToggle","useState","MDXEditor","headingsPlugin","listsPlugin","quotePlugin","thematicBreakPlugin","linkPlugin","linkDialogPlugin","markdownShortcutPlugin","toolbarPlugin","Typography","useFetchClient","useNotification","useNavigate","useContentManagerContext","qs","useCallback","Loader","Button","Eye","Plus","Modal","Field","TextInput","SingleSelect","SingleSelectOption","Flex"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useRef, useEffect, useState, useCallback } from "react";
|
|
3
|
-
import { Box, Typography, Loader, Button, Modal, Field, TextInput } from "@strapi/design-system";
|
|
3
|
+
import { Box, Typography, Loader, Button, Modal, Field, TextInput, SingleSelect, SingleSelectOption, Flex } from "@strapi/design-system";
|
|
4
4
|
import { useNavigate } from "react-router-dom";
|
|
5
5
|
import styled, { createGlobalStyle } from "styled-components";
|
|
6
6
|
import qs from "qs";
|
|
@@ -421,7 +421,7 @@ function MarkdownEditor({ content, onChange, height = 300 }) {
|
|
|
421
421
|
)
|
|
422
422
|
] });
|
|
423
423
|
}
|
|
424
|
-
|
|
424
|
+
styled(Typography)`
|
|
425
425
|
display: block;
|
|
426
426
|
margin-top: 1rem;
|
|
427
427
|
margin-bottom: 0.5rem;
|
|
@@ -438,6 +438,7 @@ function EmbeddingsModal() {
|
|
|
438
438
|
const [title, setTitle] = useState("");
|
|
439
439
|
const [content, setContent] = useState("");
|
|
440
440
|
const [fieldName, setFieldName] = useState("");
|
|
441
|
+
const [availableFields, setAvailableFields] = useState([]);
|
|
441
442
|
const [isLoading, setIsLoading] = useState(false);
|
|
442
443
|
const [isCheckingExisting, setIsCheckingExisting] = useState(true);
|
|
443
444
|
const [existingEmbedding, setExistingEmbedding] = useState(null);
|
|
@@ -470,37 +471,84 @@ function EmbeddingsModal() {
|
|
|
470
471
|
}
|
|
471
472
|
checkExistingEmbedding();
|
|
472
473
|
}, [id, slug, get]);
|
|
473
|
-
const
|
|
474
|
-
if (!
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
}
|
|
487
|
-
return "";
|
|
488
|
-
}).join("\n\n");
|
|
489
|
-
if (text.trim()) {
|
|
490
|
-
setFieldName(name);
|
|
491
|
-
return text;
|
|
474
|
+
const extractTextFromField = useCallback((value, depth = 0) => {
|
|
475
|
+
if (!value || depth > 5) return "";
|
|
476
|
+
if (typeof value === "string") {
|
|
477
|
+
return value.trim();
|
|
478
|
+
}
|
|
479
|
+
if (Array.isArray(value)) {
|
|
480
|
+
const texts = [];
|
|
481
|
+
for (const item of value) {
|
|
482
|
+
if (item && typeof item === "object" && item.__component) {
|
|
483
|
+
for (const [key, fieldValue] of Object.entries(item)) {
|
|
484
|
+
if (key === "__component" || key === "id") continue;
|
|
485
|
+
const extracted = extractTextFromField(fieldValue, depth + 1);
|
|
486
|
+
if (extracted) texts.push(extracted);
|
|
492
487
|
}
|
|
488
|
+
} else if (item && item.children) {
|
|
489
|
+
const blockText = item.children.map((child) => child.text || "").join("");
|
|
490
|
+
if (blockText) texts.push(blockText);
|
|
491
|
+
} else if (item && typeof item === "object") {
|
|
492
|
+
const extracted = extractTextFromField(item, depth + 1);
|
|
493
|
+
if (extracted) texts.push(extracted);
|
|
493
494
|
}
|
|
494
495
|
}
|
|
496
|
+
return texts.join("\n\n").trim();
|
|
497
|
+
}
|
|
498
|
+
if (typeof value === "object") {
|
|
499
|
+
const texts = [];
|
|
500
|
+
for (const [key, fieldValue] of Object.entries(value)) {
|
|
501
|
+
if (["id", "__component", "documentId", "createdAt", "updatedAt"].includes(key)) continue;
|
|
502
|
+
const extracted = extractTextFromField(fieldValue, depth + 1);
|
|
503
|
+
if (extracted) texts.push(extracted);
|
|
504
|
+
}
|
|
505
|
+
return texts.join("\n\n").trim();
|
|
495
506
|
}
|
|
496
507
|
return "";
|
|
497
|
-
}, [
|
|
508
|
+
}, []);
|
|
509
|
+
const isDynamicZone = (value) => {
|
|
510
|
+
return Array.isArray(value) && value.length > 0 && value[0]?.__component;
|
|
511
|
+
};
|
|
512
|
+
const detectTextFields = useCallback(() => {
|
|
513
|
+
if (!modifiedValues) return [];
|
|
514
|
+
const fields = [];
|
|
515
|
+
for (const [name, value] of Object.entries(modifiedValues)) {
|
|
516
|
+
if (["id", "documentId", "createdAt", "updatedAt", "publishedAt", "locale", "localizations"].includes(name)) {
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
const textValue = extractTextFromField(value);
|
|
520
|
+
if (textValue && textValue.length > 0) {
|
|
521
|
+
let label = name.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).trim();
|
|
522
|
+
if (isDynamicZone(value)) {
|
|
523
|
+
const componentCount = value.length;
|
|
524
|
+
label += ` (${componentCount} component${componentCount > 1 ? "s" : ""})`;
|
|
525
|
+
}
|
|
526
|
+
fields.push({
|
|
527
|
+
name,
|
|
528
|
+
label,
|
|
529
|
+
value: textValue,
|
|
530
|
+
charCount: textValue.length
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
fields.sort((a, b) => b.charCount - a.charCount);
|
|
535
|
+
return fields;
|
|
536
|
+
}, [modifiedValues, extractTextFromField]);
|
|
498
537
|
useEffect(() => {
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
538
|
+
const fields = detectTextFields();
|
|
539
|
+
setAvailableFields(fields);
|
|
540
|
+
if (fields.length > 0 && !fieldName) {
|
|
541
|
+
setFieldName(fields[0].name);
|
|
542
|
+
setContent(fields[0].value);
|
|
502
543
|
}
|
|
503
|
-
}, [
|
|
544
|
+
}, [detectTextFields, fieldName]);
|
|
545
|
+
const handleFieldChange = (selectedFieldName) => {
|
|
546
|
+
setFieldName(selectedFieldName);
|
|
547
|
+
const selectedField = availableFields.find((f) => f.name === selectedFieldName);
|
|
548
|
+
if (selectedField) {
|
|
549
|
+
setContent(selectedField.value);
|
|
550
|
+
}
|
|
551
|
+
};
|
|
504
552
|
const contentLength = content.length;
|
|
505
553
|
const willChunk = contentLength > CHUNK_SIZE;
|
|
506
554
|
const estimatedChunks = willChunk ? Math.ceil(contentLength / (CHUNK_SIZE - 200)) : 1;
|
|
@@ -517,9 +565,11 @@ function EmbeddingsModal() {
|
|
|
517
565
|
const isValid = title.trim() && content.trim();
|
|
518
566
|
function handleOpenCreate() {
|
|
519
567
|
setTitle("");
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
568
|
+
const fields = detectTextFields();
|
|
569
|
+
setAvailableFields(fields);
|
|
570
|
+
if (fields.length > 0) {
|
|
571
|
+
setFieldName(fields[0].name);
|
|
572
|
+
setContent(fields[0].value);
|
|
523
573
|
}
|
|
524
574
|
setIsVisible(true);
|
|
525
575
|
}
|
|
@@ -605,16 +655,6 @@ function EmbeddingsModal() {
|
|
|
605
655
|
/* @__PURE__ */ jsx(Modal.Root, { open: isVisible, onOpenChange: setIsVisible, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
|
|
606
656
|
/* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: "Create Embedding from Content" }) }),
|
|
607
657
|
/* @__PURE__ */ jsx(Modal.Body, { children: /* @__PURE__ */ jsxs(Box, { children: [
|
|
608
|
-
/* @__PURE__ */ jsxs(StyledTypography, { variant: "omega", textColor: "neutral600", children: [
|
|
609
|
-
"Content: ",
|
|
610
|
-
contentLength,
|
|
611
|
-
" characters",
|
|
612
|
-
willChunk && /* @__PURE__ */ jsxs(Typography, { textColor: "primary600", children: [
|
|
613
|
-
" (will create ~",
|
|
614
|
-
estimatedChunks,
|
|
615
|
-
" embeddings)"
|
|
616
|
-
] })
|
|
617
|
-
] }),
|
|
618
658
|
/* @__PURE__ */ jsx(Box, { marginBottom: 4, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
|
|
619
659
|
/* @__PURE__ */ jsx(Field.Label, { children: "Title" }),
|
|
620
660
|
/* @__PURE__ */ jsx(
|
|
@@ -626,10 +666,38 @@ function EmbeddingsModal() {
|
|
|
626
666
|
}
|
|
627
667
|
)
|
|
628
668
|
] }) }),
|
|
629
|
-
/* @__PURE__ */ jsxs(Box, { marginBottom: 4, children: [
|
|
669
|
+
availableFields.length > 0 && /* @__PURE__ */ jsxs(Box, { marginBottom: 4, children: [
|
|
630
670
|
/* @__PURE__ */ jsxs(Field.Root, { children: [
|
|
631
|
-
/* @__PURE__ */ jsx(Field.Label, { children: "
|
|
632
|
-
/* @__PURE__ */ jsx(Field.Hint, { children:
|
|
671
|
+
/* @__PURE__ */ jsx(Field.Label, { children: "Source Field" }),
|
|
672
|
+
/* @__PURE__ */ jsx(Field.Hint, { children: "Select which field to use for the embedding content" })
|
|
673
|
+
] }),
|
|
674
|
+
/* @__PURE__ */ jsx(
|
|
675
|
+
SingleSelect,
|
|
676
|
+
{
|
|
677
|
+
value: fieldName,
|
|
678
|
+
onChange: (value) => handleFieldChange(value),
|
|
679
|
+
placeholder: "Select a field",
|
|
680
|
+
children: availableFields.map((field) => /* @__PURE__ */ jsxs(SingleSelectOption, { value: field.name, children: [
|
|
681
|
+
field.label,
|
|
682
|
+
" (",
|
|
683
|
+
field.charCount.toLocaleString(),
|
|
684
|
+
" chars)"
|
|
685
|
+
] }, field.name))
|
|
686
|
+
}
|
|
687
|
+
)
|
|
688
|
+
] }),
|
|
689
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 4, children: [
|
|
690
|
+
/* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
|
|
691
|
+
/* @__PURE__ */ jsx(Field.Root, { children: /* @__PURE__ */ jsx(Field.Label, { children: "Content Preview" }) }),
|
|
692
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
|
|
693
|
+
contentLength.toLocaleString(),
|
|
694
|
+
" characters",
|
|
695
|
+
willChunk && /* @__PURE__ */ jsxs(Typography, { textColor: "primary600", children: [
|
|
696
|
+
" (~",
|
|
697
|
+
estimatedChunks,
|
|
698
|
+
" chunks)"
|
|
699
|
+
] })
|
|
700
|
+
] })
|
|
633
701
|
] }),
|
|
634
702
|
/* @__PURE__ */ jsx(
|
|
635
703
|
MarkdownEditor,
|
|
@@ -669,7 +737,7 @@ const index = {
|
|
|
669
737
|
defaultMessage: PLUGIN_ID
|
|
670
738
|
},
|
|
671
739
|
Component: async () => {
|
|
672
|
-
const { App } = await import("./App-
|
|
740
|
+
const { App } = await import("./App-MjsTrWRS.mjs");
|
|
673
741
|
return App;
|
|
674
742
|
}
|
|
675
743
|
});
|
|
@@ -712,3 +780,4 @@ export {
|
|
|
712
780
|
RobotIcon as R,
|
|
713
781
|
index as i
|
|
714
782
|
};
|
|
783
|
+
//# sourceMappingURL=index-ifqYByO5.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-ifqYByO5.mjs","sources":["../../admin/src/pluginId.ts","../../admin/src/utils/getTranslation.ts","../../admin/src/components/Initializer.tsx","../../admin/src/components/custom/RobotIcon.tsx","../../admin/src/components/PluginIcon.tsx","../../admin/src/components/custom/MarkdownEditor.tsx","../../admin/src/components/custom/EmbeddingsModal.tsx","../../admin/src/components/custom/EmbeddingsWidget.tsx","../../admin/src/index.tsx"],"sourcesContent":["export const PLUGIN_ID = 'strapi-content-embeddings';\n","import { PLUGIN_ID } from '../pluginId';\n\nconst getTranslation = (id: string) => `${PLUGIN_ID}.${id}`;\n\nexport { getTranslation };\n","import { useEffect, useRef } from 'react';\n\nimport { PLUGIN_ID } from '../pluginId';\n\ntype InitializerProps = {\n setPlugin: (id: string) => void;\n};\n\nconst Initializer = ({ setPlugin }: InitializerProps) => {\n const ref = useRef(setPlugin);\n\n useEffect(() => {\n ref.current(PLUGIN_ID);\n }, []);\n\n return null;\n};\n\nexport { Initializer };\n","import React from \"react\";\n\ninterface RobotIconProps {\n height?: string | number;\n width?: string | number;\n}\n\nexport function RobotIcon({ height = 48, width = 48 }: RobotIconProps) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n height={height}\n viewBox=\"0 -960 960 960\"\n width={width}\n fill=\"currentColor\"\n >\n <path d=\"M160-120v-220q0-24.75 17.625-42.375T220-400h520q24.75 0 42.375 17.625T800-340v220H160Zm200-320q-83 0-141.5-58.5T160-640q0-83 58.5-141.5T360-840h240q83 0 141.5 58.5T800-640q0 83-58.5 141.5T600-440H360ZM220-180h520v-160H220v160Zm140-320h240q58.333 0 99.167-40.765 40.833-40.764 40.833-99Q740-698 699.167-739 658.333-780 600-780H360q-58.333 0-99.167 40.765-40.833 40.764-40.833 99Q220-582 260.833-541q40.834 41 99.167 41Zm.175-110q12.825 0 21.325-8.675 8.5-8.676 8.5-21.5 0-12.825-8.675-21.325-8.676-8.5-21.5-8.5-12.825 0-21.325 8.675-8.5 8.676-8.5 21.5 0 12.825 8.675 21.325 8.676 8.5 21.5 8.5Zm240 0q12.825 0 21.325-8.675 8.5-8.676 8.5-21.5 0-12.825-8.675-21.325-8.676-8.5-21.5-8.5-12.825 0-21.325 8.675-8.5 8.676-8.5 21.5 0 12.825 8.675 21.325 8.676 8.5 21.5 8.5ZM480-180Zm0-460Z\" />\n </svg>\n );\n}\n","import { RobotIcon } from './custom/RobotIcon';\n\nconst PluginIcon = () => <RobotIcon height={24} width={24} />;\n\nexport { PluginIcon };\n","import { useState } from 'react';\nimport { Box } from '@strapi/design-system';\nimport styled, { createGlobalStyle } from 'styled-components';\nimport {\n MDXEditor,\n headingsPlugin,\n listsPlugin,\n quotePlugin,\n thematicBreakPlugin,\n markdownShortcutPlugin,\n linkPlugin,\n linkDialogPlugin,\n toolbarPlugin,\n BoldItalicUnderlineToggles,\n BlockTypeSelect,\n CreateLink,\n ListsToggle,\n UndoRedo,\n Separator,\n} from '@mdxeditor/editor';\n\ninterface MarkdownEditorProps {\n content: string;\n onChange: (content: string) => void;\n height?: number;\n}\n\nconst MDXEditorStyles = createGlobalStyle`\n /* MDXEditor CSS Variables */\n :root {\n --mdx-spacing-0_5: 0.125rem;\n --mdx-spacing-1: 0.25rem;\n --mdx-spacing-1_5: 0.375rem;\n --mdx-spacing-2: 0.5rem;\n --mdx-spacing-3: 0.75rem;\n --mdx-spacing-4: 1rem;\n --mdx-spacing-36: 9rem;\n --mdx-radius-base: 0.25rem;\n --mdx-radius-medium: 0.375rem;\n --mdx-text-sm: 0.875rem;\n --mdx-baseBg: #f6f6f9;\n --mdx-baseBgActive: #e8e8ec;\n --mdx-basePageBg: #ffffff;\n --mdx-baseBorder: #dcdce4;\n --mdx-baseBorderHover: #b9bbc6;\n --mdx-baseBase: #e0e1e6;\n --mdx-baseTextContrast: #1c2024;\n --mdx-accentText: #4945ff;\n }\n\n /* Toolbar Root - critical for horizontal layout */\n [class*=\"_toolbarRoot\"] {\n z-index: 2;\n display: flex !important;\n flex-direction: row !important;\n flex-wrap: wrap !important;\n gap: var(--mdx-spacing-1);\n border-radius: var(--mdx-radius-medium);\n padding: var(--mdx-spacing-1_5);\n align-items: center !important;\n overflow-x: auto;\n position: sticky;\n top: 0;\n background-color: var(--mdx-baseBg) !important;\n border-bottom: 1px solid var(--mdx-baseBorder);\n width: 100%;\n }\n\n [class*=\"_toolbarRoot\"] div[role='separator'] {\n margin: var(--mdx-spacing-2) var(--mdx-spacing-1);\n border-left: 1px solid var(--mdx-baseBorder);\n border-right: 1px solid var(--mdx-baseBase);\n height: var(--mdx-spacing-4);\n }\n\n [class*=\"_toolbarRoot\"] svg {\n color: var(--mdx-baseTextContrast);\n display: block;\n }\n\n /* Toolbar button groups */\n [class*=\"_toolbarGroupOfGroups\"] {\n display: flex;\n margin: 0 var(--mdx-spacing-1);\n }\n\n [class*=\"_toolbarToggleSingleGroup\"] {\n display: flex;\n align-items: center;\n white-space: nowrap;\n }\n\n /* Toolbar buttons and toggle items */\n [class*=\"_toolbarToggleItem\"],\n [class*=\"_toolbarButton\"] {\n border: 0;\n background-color: transparent;\n font-size: inherit;\n appearance: none;\n box-sizing: border-box;\n cursor: pointer;\n padding: var(--mdx-spacing-0_5);\n border-radius: var(--mdx-radius-base);\n }\n\n [class*=\"_toolbarToggleItem\"]:hover,\n [class*=\"_toolbarButton\"]:hover {\n background-color: var(--mdx-baseBgActive);\n }\n\n [class*=\"_toolbarToggleItem\"][data-state='on'],\n [class*=\"_toolbarButton\"][data-state='on'],\n [class*=\"_toolbarToggleItem\"]:active,\n [class*=\"_toolbarButton\"]:active {\n color: var(--mdx-baseTextContrast);\n background-color: var(--mdx-baseBgActive);\n }\n\n /* Block type select dropdown */\n [class*=\"_toolbarNodeKindSelectTrigger\"],\n [class*=\"_selectTrigger\"] {\n border: 0;\n display: flex;\n color: inherit;\n align-items: center;\n width: var(--mdx-spacing-36);\n padding: var(--mdx-spacing-0_5) var(--mdx-spacing-1);\n padding-inline-start: var(--mdx-spacing-2);\n border-radius: var(--mdx-radius-medium);\n white-space: nowrap;\n font-size: var(--mdx-text-sm);\n background-color: var(--mdx-basePageBg);\n margin: 0 var(--mdx-spacing-1);\n cursor: pointer;\n }\n\n /* Dropdown containers */\n [class*=\"_toolbarNodeKindSelectContainer\"],\n [class*=\"_selectContainer\"] {\n filter: drop-shadow(0 2px 2px rgb(0 0 0 / 0.2));\n z-index: 100;\n width: var(--mdx-spacing-36);\n border-radius: var(--mdx-radius-base);\n background-color: var(--mdx-basePageBg);\n font-size: var(--mdx-text-sm);\n }\n\n /* Select items */\n [class*=\"_toolbarNodeKindSelectItem\"],\n [class*=\"_selectItem\"] {\n cursor: pointer;\n display: flex;\n padding: var(--mdx-spacing-2);\n }\n\n [class*=\"_toolbarNodeKindSelectItem\"][data-highlighted],\n [class*=\"_selectItem\"][data-highlighted],\n [class*=\"_toolbarNodeKindSelectItem\"][data-state='checked'],\n [class*=\"_selectItem\"][data-state='checked'] {\n background-color: var(--mdx-baseBg);\n outline: none;\n }\n\n /* Dropdown arrow */\n [class*=\"_selectDropdownArrow\"] {\n margin-left: auto;\n display: flex;\n align-items: center;\n }\n\n /* Content editable area */\n [class*=\"_contentEditable\"] {\n box-sizing: border-box;\n width: 100%;\n color: var(--mdx-baseTextContrast);\n padding: var(--mdx-spacing-3);\n min-height: 200px;\n outline: none;\n font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n font-size: 14px;\n line-height: 1.6;\n }\n\n [class*=\"_contentEditable\"]:focus {\n outline: none;\n }\n\n /* Placeholder positioning - ensure it's at the top */\n [class*=\"_contentEditable\"] {\n position: relative;\n }\n\n [class*=\"_contentEditable\"][data-placeholder]::before {\n position: absolute;\n top: var(--mdx-spacing-3);\n left: var(--mdx-spacing-3);\n color: #a5a5ba;\n pointer-events: none;\n }\n\n /* MDXEditor/Lexical placeholder styles */\n [class*=\"_placeholder\"],\n [class*=\"ContentEditable__placeholder\"],\n [class*=\"editor-placeholder\"] {\n position: absolute !important;\n top: var(--mdx-spacing-3) !important;\n left: var(--mdx-spacing-3) !important;\n color: #a5a5ba;\n pointer-events: none;\n overflow: hidden;\n text-overflow: ellipsis;\n user-select: none;\n display: inline-block;\n }\n\n /* Editor root wrapper needs relative positioning for placeholder */\n [class*=\"_rootContentEditableWrapper\"],\n [class*=\"_editorWrapper\"] {\n position: relative;\n display: flex;\n flex-direction: column;\n }\n\n /* Heading styles */\n [class*=\"_contentEditable\"] h1 {\n font-size: 1.75rem;\n font-weight: 600;\n margin: 0 0 1rem;\n }\n\n [class*=\"_contentEditable\"] h2 {\n font-size: 1.5rem;\n font-weight: 600;\n margin: 1rem 0 0.75rem;\n }\n\n [class*=\"_contentEditable\"] h3 {\n font-size: 1.25rem;\n font-weight: 600;\n margin: 1rem 0 0.5rem;\n }\n\n /* Paragraph and list styles */\n [class*=\"_contentEditable\"] p {\n margin: 0 0 1rem;\n }\n\n [class*=\"_contentEditable\"] ul,\n [class*=\"_contentEditable\"] ol {\n margin: 0 0 1rem;\n padding-left: 1.5rem;\n }\n\n [class*=\"_contentEditable\"] li {\n margin: 0.25rem 0;\n }\n\n /* Code styles */\n [class*=\"_contentEditable\"] code {\n background: #f0f0f5;\n padding: 0.2em 0.4em;\n border-radius: 3px;\n font-family: \"Monaco\", \"Menlo\", monospace;\n font-size: 0.9em;\n }\n\n [class*=\"_contentEditable\"] pre {\n background: #2d2d2d;\n color: #f8f8f2;\n padding: 1rem;\n border-radius: 4px;\n overflow-x: auto;\n margin: 0 0 1rem;\n }\n\n [class*=\"_contentEditable\"] pre code {\n background: none;\n padding: 0;\n }\n\n /* Blockquote */\n [class*=\"_contentEditable\"] blockquote {\n border-left: 3px solid #dcdce4;\n margin: 0 0 1rem;\n padding-left: 1rem;\n color: #666;\n }\n\n /* Links */\n [class*=\"_contentEditable\"] a {\n color: #4945ff;\n text-decoration: underline;\n }\n\n /* Horizontal rule */\n [class*=\"_contentEditable\"] hr {\n border: none;\n border-top: 1px solid #dcdce4;\n margin: 1.5rem 0;\n }\n\n /* Editor root */\n [class*=\"_editorRoot\"] {\n font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n color: var(--mdx-baseTextContrast);\n background: var(--mdx-basePageBg);\n }\n\n /* Link dialog */\n [class*=\"_linkDialogPopoverContent\"] {\n display: flex;\n flex-direction: column;\n gap: var(--mdx-spacing-2);\n padding: var(--mdx-spacing-3);\n background-color: var(--mdx-basePageBg);\n border-radius: var(--mdx-radius-medium);\n box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);\n z-index: 100;\n }\n\n [class*=\"_linkDialogInputWrapper\"] {\n display: flex;\n gap: var(--mdx-spacing-1);\n }\n\n [class*=\"_linkDialogInputWrapper\"] input {\n flex: 1;\n padding: var(--mdx-spacing-1) var(--mdx-spacing-2);\n border: 1px solid var(--mdx-baseBorder);\n border-radius: var(--mdx-radius-base);\n font-size: var(--mdx-text-sm);\n }\n\n [class*=\"_linkDialogInputWrapper\"] button {\n padding: var(--mdx-spacing-1) var(--mdx-spacing-2);\n background-color: var(--mdx-accentText);\n color: white;\n border: none;\n border-radius: var(--mdx-radius-base);\n cursor: pointer;\n }\n\n /* Popover positioning */\n [data-radix-popper-content-wrapper] {\n z-index: 100 !important;\n }\n`;\n\nconst EditorWrapper = styled(Box)<{ $isFocused: boolean }>`\n border: 1px solid ${({ $isFocused }) => ($isFocused ? '#4945ff' : '#dcdce4')};\n border-radius: 4px;\n overflow: hidden;\n background: #fff;\n transition: border-color 0.2s, box-shadow 0.2s;\n box-shadow: ${({ $isFocused }) => ($isFocused ? '0 0 0 2px rgba(73, 69, 255, 0.2)' : 'none')};\n`;\n\n// Toolbar contents component defined outside to prevent re-renders\nfunction ToolbarContents() {\n return (\n <>\n <UndoRedo />\n <Separator />\n <BlockTypeSelect />\n <Separator />\n <BoldItalicUnderlineToggles />\n <Separator />\n <CreateLink />\n <Separator />\n <ListsToggle />\n </>\n );\n}\n\nexport function MarkdownEditor({ content, onChange, height = 300 }: Readonly<MarkdownEditorProps>) {\n const [isFocused, setIsFocused] = useState(false);\n\n return (\n <>\n <MDXEditorStyles />\n <EditorWrapper\n $isFocused={isFocused}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n >\n <div style={{ minHeight: `${height}px` }}>\n <MDXEditor\n markdown={content}\n onChange={onChange}\n placeholder=\"Write your content here...\"\n plugins={[\n headingsPlugin(),\n listsPlugin(),\n quotePlugin(),\n thematicBreakPlugin(),\n linkPlugin(),\n linkDialogPlugin(),\n markdownShortcutPlugin(),\n toolbarPlugin({\n toolbarContents: ToolbarContents,\n }),\n ]}\n />\n </div>\n </EditorWrapper>\n </>\n );\n}\n","import React, { useState, useEffect, useCallback } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport styled from \"styled-components\";\nimport qs from \"qs\";\nimport {\n unstable_useContentManagerContext as useContentManagerContext,\n useFetchClient,\n useNotification,\n} from \"@strapi/strapi/admin\";\nimport {\n Button,\n Typography,\n Box,\n Modal,\n Field,\n TextInput,\n Loader,\n Flex,\n SingleSelect,\n SingleSelectOption,\n} from \"@strapi/design-system\";\nimport { Plus, Eye } from \"@strapi/icons\";\nimport { PLUGIN_ID } from \"../../pluginId\";\nimport { MarkdownEditor } from \"./MarkdownEditor\";\n\nconst StyledTypography = styled(Typography)`\n display: block;\n margin-top: 1rem;\n margin-bottom: 0.5rem;\n`;\n\nconst CHUNK_SIZE = 4000; // Content over this will be auto-chunked\n\ninterface ExistingEmbedding {\n documentId: string;\n title: string;\n content?: string;\n}\n\ninterface TextFieldOption {\n name: string;\n label: string;\n value: string;\n charCount: number;\n}\n\nexport function EmbeddingsModal() {\n const { post, get } = useFetchClient();\n const { toggleNotification } = useNotification();\n const navigate = useNavigate();\n\n // Access content manager context\n const context = useContentManagerContext();\n const { form, id, slug, collectionType } = context;\n\n const modifiedValues = form?.values || {};\n\n const [isVisible, setIsVisible] = useState(false);\n const [title, setTitle] = useState(\"\");\n const [content, setContent] = useState(\"\");\n const [fieldName, setFieldName] = useState(\"\");\n const [availableFields, setAvailableFields] = useState<TextFieldOption[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [isCheckingExisting, setIsCheckingExisting] = useState(true);\n const [existingEmbedding, setExistingEmbedding] = useState<ExistingEmbedding | null>(null);\n\n // Check for existing embedding when component mounts or id changes\n useEffect(() => {\n async function checkExistingEmbedding() {\n if (!id || !slug) {\n setIsCheckingExisting(false);\n return;\n }\n\n try {\n // Query embeddings filtered by metadata containing this documentId\n const query = qs.stringify({\n filters: {\n metadata: { $containsi: id },\n },\n });\n\n const response = await get(`/${PLUGIN_ID}/embeddings/find?${query}`);\n\n // Handle response - could be { data: [...] } or { data: { data: [...] } }\n const embeddings = response.data?.data || response.data || [];\n\n if (Array.isArray(embeddings) && embeddings.length > 0) {\n // Found existing embedding for this content\n setExistingEmbedding({\n documentId: embeddings[0].documentId,\n title: embeddings[0].title,\n content: embeddings[0].content,\n });\n }\n } catch (error) {\n console.error(\"Failed to check for existing embedding:\", error);\n } finally {\n setIsCheckingExisting(false);\n }\n }\n\n checkExistingEmbedding();\n }, [id, slug, get]);\n\n // Extract text value from a field (handles strings, blocks, and dynamic zones)\n const extractTextFromField = useCallback((value: any, depth: number = 0): string => {\n if (!value || depth > 5) return \"\"; // Prevent infinite recursion\n\n // Handle string values\n if (typeof value === \"string\") {\n return value.trim();\n }\n\n // Handle arrays (could be blocks, dynamic zones, or repeatable components)\n if (Array.isArray(value)) {\n const texts: string[] = [];\n\n for (const item of value) {\n // Check if it's a dynamic zone component (has __component property)\n if (item && typeof item === \"object\" && item.__component) {\n // Extract text from all fields in the component\n for (const [key, fieldValue] of Object.entries(item)) {\n if (key === \"__component\" || key === \"id\") continue;\n const extracted = extractTextFromField(fieldValue, depth + 1);\n if (extracted) texts.push(extracted);\n }\n }\n // Check if it's a blocks format item (rich text)\n else if (item && item.children) {\n const blockText = item.children\n .map((child: any) => child.text || \"\")\n .join(\"\");\n if (blockText) texts.push(blockText);\n }\n // Recursively handle nested arrays/objects\n else if (item && typeof item === \"object\") {\n const extracted = extractTextFromField(item, depth + 1);\n if (extracted) texts.push(extracted);\n }\n }\n\n return texts.join(\"\\n\\n\").trim();\n }\n\n // Handle objects (could be a component or relation)\n if (typeof value === \"object\") {\n const texts: string[] = [];\n\n for (const [key, fieldValue] of Object.entries(value)) {\n // Skip metadata fields\n if ([\"id\", \"__component\", \"documentId\", \"createdAt\", \"updatedAt\"].includes(key)) continue;\n const extracted = extractTextFromField(fieldValue, depth + 1);\n if (extracted) texts.push(extracted);\n }\n\n return texts.join(\"\\n\\n\").trim();\n }\n\n return \"\";\n }, []);\n\n // Check if a value is a dynamic zone\n const isDynamicZone = (value: any): boolean => {\n return Array.isArray(value) && value.length > 0 && value[0]?.__component;\n };\n\n // Detect all available text fields from form values\n const detectTextFields = useCallback((): TextFieldOption[] => {\n if (!modifiedValues) return [];\n\n const fields: TextFieldOption[] = [];\n\n // Check all fields in form values\n for (const [name, value] of Object.entries(modifiedValues)) {\n // Skip non-content fields\n if ([\"id\", \"documentId\", \"createdAt\", \"updatedAt\", \"publishedAt\", \"locale\", \"localizations\"].includes(name)) {\n continue;\n }\n\n const textValue = extractTextFromField(value);\n if (textValue && textValue.length > 0) {\n // Create a readable label from field name\n let label = name\n .replace(/([A-Z])/g, \" $1\")\n .replace(/^./, (str) => str.toUpperCase())\n .trim();\n\n // Add indicator for dynamic zones\n if (isDynamicZone(value)) {\n const componentCount = (value as any[]).length;\n label += ` (${componentCount} component${componentCount > 1 ? \"s\" : \"\"})`;\n }\n\n fields.push({\n name,\n label,\n value: textValue,\n charCount: textValue.length,\n });\n }\n }\n\n // Sort by character count (longest first) to prioritize main content\n fields.sort((a, b) => b.charCount - a.charCount);\n\n return fields;\n }, [modifiedValues, extractTextFromField]);\n\n // Update available fields when form values change\n useEffect(() => {\n const fields = detectTextFields();\n setAvailableFields(fields);\n\n // Auto-select first field if none selected\n if (fields.length > 0 && !fieldName) {\n setFieldName(fields[0].name);\n setContent(fields[0].value);\n }\n }, [detectTextFields, fieldName]);\n\n // Handle field selection change\n const handleFieldChange = (selectedFieldName: string) => {\n setFieldName(selectedFieldName);\n const selectedField = availableFields.find(f => f.name === selectedFieldName);\n if (selectedField) {\n setContent(selectedField.value);\n }\n };\n\n const contentLength = content.length;\n const willChunk = contentLength > CHUNK_SIZE;\n const estimatedChunks = willChunk ? Math.ceil(contentLength / (CHUNK_SIZE - 200)) : 1;\n // Check if content is saved (has an id) - don't require publish\n const isSaved = !!id;\n\n // Auto-generate metadata from collection context\n function generateMetadata(): Record<string, any> {\n return {\n source: \"content-manager\",\n collectionType: slug || collectionType || \"unknown\",\n fieldName: fieldName || \"content\",\n documentId: id,\n updatedAt: new Date().toISOString(),\n };\n }\n\n const isValid = title.trim() && content.trim(); // No length limit - auto-chunks if needed\n\n function handleOpenCreate() {\n setTitle(\"\");\n // Refresh available fields and select first one\n const fields = detectTextFields();\n setAvailableFields(fields);\n if (fields.length > 0) {\n setFieldName(fields[0].name);\n setContent(fields[0].value);\n }\n setIsVisible(true);\n }\n\n async function handleSubmit(e: React.FormEvent) {\n e.preventDefault();\n\n if (!title.trim()) {\n toggleNotification({\n type: \"warning\",\n message: \"Embeddings title is required\",\n });\n return;\n }\n\n if (!content.trim()) {\n toggleNotification({\n type: \"warning\",\n message: \"Embeddings content is required\",\n });\n return;\n }\n\n setIsLoading(true);\n\n try {\n const contentToEmbed = content.trim();\n const shouldChunk = contentToEmbed.length > CHUNK_SIZE;\n const chunks = shouldChunk ? Math.ceil(contentToEmbed.length / CHUNK_SIZE) : 1;\n\n if (shouldChunk) {\n console.log(`Creating chunked embedding: ${contentToEmbed.length} chars (~${chunks} parts)`);\n }\n\n const result = await post(`/${PLUGIN_ID}/embeddings/create-embedding`, {\n data: {\n title: title.trim(),\n content: contentToEmbed,\n collectionType: slug || collectionType,\n fieldName,\n metadata: generateMetadata(),\n autoChunk: shouldChunk,\n },\n });\n\n const responseData = result?.data || result;\n\n if (responseData?.documentId) {\n setExistingEmbedding({\n documentId: responseData.documentId,\n title: responseData.title,\n content: responseData.content,\n });\n }\n setIsVisible(false);\n\n const message = shouldChunk\n ? `Embedding created and chunked into ${chunks} parts`\n : \"Embedding created successfully\";\n toggleNotification({ type: \"success\", message });\n } catch (error: any) {\n console.error(\"Failed to create embedding:\", error);\n toggleNotification({\n type: \"danger\",\n message: error.message || \"Failed to create embedding\",\n });\n } finally {\n setIsLoading(false);\n }\n }\n\n function handleViewEmbedding() {\n if (existingEmbedding?.documentId) {\n navigate(`/plugins/${PLUGIN_ID}/embeddings/${existingEmbedding.documentId}`);\n }\n }\n\n // Don't render if not in edit view context\n if (!form || !id) {\n return null;\n }\n\n // Show loading state while checking for existing embedding\n if (isCheckingExisting) {\n return (\n <Box paddingTop={2}>\n <Loader small>Checking embeddings...</Loader>\n </Box>\n );\n }\n\n const submitButtonText = isLoading ? \"Creating...\" : \"Create Embedding\";\n\n return (\n <Box paddingTop={2}>\n {existingEmbedding ? (\n <Button onClick={handleViewEmbedding} startIcon={<Eye />} fullWidth>\n View Embedding\n </Button>\n ) : (\n <Button\n onClick={handleOpenCreate}\n startIcon={<Plus />}\n disabled={!isSaved}\n fullWidth\n >\n Create Embedding\n </Button>\n )}\n\n {!isSaved && !existingEmbedding && (\n <Typography variant=\"pi\" textColor=\"neutral600\" style={{ display: \"block\", marginTop: \"0.5rem\" }}>\n Save content first to create embedding\n </Typography>\n )}\n\n <Modal.Root open={isVisible} onOpenChange={setIsVisible}>\n <Modal.Content>\n <Modal.Header>\n <Modal.Title>Create Embedding from Content</Modal.Title>\n </Modal.Header>\n <Modal.Body>\n <Box>\n <Box marginBottom={4}>\n <Field.Root>\n <Field.Label>Title</Field.Label>\n <TextInput\n placeholder=\"Enter embedding title\"\n value={title}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setTitle(e.target.value)\n }\n />\n </Field.Root>\n </Box>\n\n {availableFields.length > 0 && (\n <Box marginBottom={4}>\n <Field.Root>\n <Field.Label>Source Field</Field.Label>\n <Field.Hint>Select which field to use for the embedding content</Field.Hint>\n </Field.Root>\n <SingleSelect\n value={fieldName}\n onChange={(value: string) => handleFieldChange(value)}\n placeholder=\"Select a field\"\n >\n {availableFields.map((field) => (\n <SingleSelectOption key={field.name} value={field.name}>\n {field.label} ({field.charCount.toLocaleString()} chars)\n </SingleSelectOption>\n ))}\n </SingleSelect>\n </Box>\n )}\n\n <Box marginBottom={4}>\n <Flex justifyContent=\"space-between\" alignItems=\"center\">\n <Field.Root>\n <Field.Label>Content Preview</Field.Label>\n </Field.Root>\n <Typography variant=\"pi\" textColor=\"neutral600\">\n {contentLength.toLocaleString()} characters\n {willChunk && (\n <Typography textColor=\"primary600\"> (~{estimatedChunks} chunks)</Typography>\n )}\n </Typography>\n </Flex>\n <MarkdownEditor\n content={content}\n onChange={setContent}\n height={200}\n />\n </Box>\n </Box>\n </Modal.Body>\n <Modal.Footer>\n <Modal.Close>\n <Button variant=\"tertiary\">Cancel</Button>\n </Modal.Close>\n <Button\n onClick={handleSubmit}\n disabled={isLoading || !isValid}\n loading={isLoading}\n >\n {submitButtonText}\n </Button>\n </Modal.Footer>\n </Modal.Content>\n </Modal.Root>\n </Box>\n );\n}\n","import React from \"react\";\nimport { Box } from \"@strapi/design-system\";\nimport { EmbeddingsModal } from \"./EmbeddingsModal\";\n\nexport function EmbeddingsWidget() {\n return (\n <Box>\n <EmbeddingsModal />\n </Box>\n );\n}\n","import React from 'react';\nimport { getTranslation } from './utils/getTranslation';\nimport { PLUGIN_ID } from './pluginId';\nimport { Initializer } from './components/Initializer';\nimport { PluginIcon } from './components/PluginIcon';\nimport { EmbeddingsWidget } from './components/custom/EmbeddingsWidget';\n\nexport default {\n register(app: any) {\n app.addMenuLink({\n to: `plugins/${PLUGIN_ID}`,\n icon: PluginIcon,\n intlLabel: {\n id: `${PLUGIN_ID}.plugin.name`,\n defaultMessage: PLUGIN_ID,\n },\n Component: async () => {\n const { App } = await import('./pages/App');\n return App;\n },\n });\n\n app.registerPlugin({\n id: PLUGIN_ID,\n initializer: Initializer,\n isReady: false,\n name: PLUGIN_ID,\n });\n },\n\n bootstrap(app: any) {\n app.getPlugin('content-manager').injectComponent('editView', 'right-links', {\n name: \"open-ai-embeddings\",\n Component: () => <EmbeddingsWidget />\n })\n },\n\n async registerTrads(app: any) {\n const { locales } = app;\n\n const importedTranslations = await Promise.all(\n (locales as string[]).map((locale) => {\n return import(`./translations/${locale}.json`)\n .then(({ default: data }) => {\n return {\n data: getTranslation(data),\n locale,\n };\n })\n .catch(() => {\n return {\n data: {},\n locale,\n };\n });\n })\n );\n\n return importedTranslations;\n },\n};\n"],"names":["useContentManagerContext"],"mappings":"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"}
|
package/dist/admin/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
|
package/dist/admin/index.mjs
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
package/dist/server/index.js
CHANGED
|
@@ -2070,12 +2070,12 @@ const embeddings = ({ strapi }) => ({
|
|
|
2070
2070
|
return chunks.length;
|
|
2071
2071
|
},
|
|
2072
2072
|
/**
|
|
2073
|
-
* Update
|
|
2074
|
-
*
|
|
2073
|
+
* Update a single chunk's content and embedding
|
|
2074
|
+
* Updates only the specified chunk without affecting other chunks in the group
|
|
2075
2075
|
*/
|
|
2076
2076
|
async updateChunkedEmbedding(id, data) {
|
|
2077
|
-
const { title, content, metadata
|
|
2078
|
-
|
|
2077
|
+
const { title, content, metadata } = data.data;
|
|
2078
|
+
this.getConfig();
|
|
2079
2079
|
const currentEntry = await strapi.documents(CONTENT_TYPE_UID$1).findOne({
|
|
2080
2080
|
documentId: id
|
|
2081
2081
|
});
|
|
@@ -2083,64 +2083,61 @@ const embeddings = ({ strapi }) => ({
|
|
|
2083
2083
|
throw new Error(`Embedding with id ${id} not found`);
|
|
2084
2084
|
}
|
|
2085
2085
|
const currentMetadata = currentEntry.metadata;
|
|
2086
|
-
const
|
|
2087
|
-
|
|
2088
|
-
const
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
if (
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
fieldName: currentEntry.fieldName || "content",
|
|
2119
|
-
metadata: preservedMetadata,
|
|
2120
|
-
related: originalRelated,
|
|
2121
|
-
autoChunk: true
|
|
2122
|
-
}
|
|
2123
|
-
});
|
|
2124
|
-
} else {
|
|
2125
|
-
const entity = await this.createEmbedding({
|
|
2126
|
-
data: {
|
|
2127
|
-
title: newTitle.replace(/\s*\[Part \d+\/\d+\]$/, ""),
|
|
2128
|
-
// Remove old part suffix
|
|
2086
|
+
const contentChanged = content !== void 0 && content !== currentEntry.content;
|
|
2087
|
+
console.log(`[updateChunkedEmbedding] Updating single chunk ${id}, contentChanged: ${contentChanged}`);
|
|
2088
|
+
const updateData = {};
|
|
2089
|
+
if (title !== void 0) {
|
|
2090
|
+
const currentTitle = currentEntry.title || "";
|
|
2091
|
+
const partMatch = currentTitle.match(/\s*\[Part \d+\/\d+\]$/);
|
|
2092
|
+
updateData.title = partMatch ? `${title}${partMatch[0]}` : title;
|
|
2093
|
+
}
|
|
2094
|
+
if (content !== void 0) {
|
|
2095
|
+
updateData.content = content;
|
|
2096
|
+
}
|
|
2097
|
+
if (metadata !== void 0) {
|
|
2098
|
+
updateData.metadata = {
|
|
2099
|
+
...currentMetadata,
|
|
2100
|
+
...metadata
|
|
2101
|
+
};
|
|
2102
|
+
if (contentChanged) {
|
|
2103
|
+
updateData.metadata.estimatedTokens = estimateTokens(updateData.content || currentEntry.content);
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
const updatedEntity = await strapi.documents(CONTENT_TYPE_UID$1).update({
|
|
2107
|
+
documentId: id,
|
|
2108
|
+
data: updateData
|
|
2109
|
+
});
|
|
2110
|
+
if (pluginManager.isInitialized() && (contentChanged || title !== void 0)) {
|
|
2111
|
+
try {
|
|
2112
|
+
console.log(`[updateChunkedEmbedding] Updating embedding in Neon for chunk ${id}`);
|
|
2113
|
+
await pluginManager.deleteEmbedding(id);
|
|
2114
|
+
const newContent = updateData.content || currentEntry.content;
|
|
2115
|
+
const result = await pluginManager.createEmbedding({
|
|
2116
|
+
id,
|
|
2117
|
+
title: updatedEntity.title || currentEntry.title,
|
|
2129
2118
|
content: newContent,
|
|
2130
2119
|
collectionType: currentEntry.collectionType || "standalone",
|
|
2131
|
-
fieldName: currentEntry.fieldName || "content"
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2120
|
+
fieldName: currentEntry.fieldName || "content"
|
|
2121
|
+
});
|
|
2122
|
+
await strapi.documents(CONTENT_TYPE_UID$1).update({
|
|
2123
|
+
documentId: id,
|
|
2124
|
+
data: {
|
|
2125
|
+
embeddingId: result.embeddingId,
|
|
2126
|
+
embedding: result.embedding
|
|
2127
|
+
}
|
|
2128
|
+
});
|
|
2129
|
+
console.log(`[updateChunkedEmbedding] Successfully updated embedding for chunk ${id}`);
|
|
2130
|
+
} catch (error) {
|
|
2131
|
+
console.error(`Failed to update vector store for ${id}:`, error);
|
|
2132
|
+
}
|
|
2143
2133
|
}
|
|
2134
|
+
const allChunks = await this.findRelatedChunks(id);
|
|
2135
|
+
return {
|
|
2136
|
+
entity: updatedEntity,
|
|
2137
|
+
chunks: allChunks,
|
|
2138
|
+
totalChunks: allChunks.length,
|
|
2139
|
+
wasChunked: allChunks.length > 1
|
|
2140
|
+
};
|
|
2144
2141
|
},
|
|
2145
2142
|
async updateEmbedding(id, data) {
|
|
2146
2143
|
const { title, content: rawContent, metadata, autoChunk } = data.data;
|
|
@@ -2551,3 +2548,4 @@ const index = {
|
|
|
2551
2548
|
middlewares
|
|
2552
2549
|
};
|
|
2553
2550
|
module.exports = index;
|
|
2551
|
+
//# sourceMappingURL=index.js.map
|