diff --git a/docusaurus/src/components/ContributeLink/ContributeLink.jsx b/docusaurus/src/components/ContributeLink/ContributeLink.jsx
index f801c6bd38..d60da1b745 100644
--- a/docusaurus/src/components/ContributeLink/ContributeLink.jsx
+++ b/docusaurus/src/components/ContributeLink/ContributeLink.jsx
@@ -6,6 +6,7 @@ import {translate} from '@docusaurus/Translate';
import styles from './contribute-link.module.scss';
import Icon from '../Icon';
import CopyMarkdownButton from '../CopyMarkdownButton';
+import SendToAIButton from '../SendToAiButton';
export default function ContributeLink() {
const {siteConfig} = useDocusaurusContext();
@@ -37,6 +38,7 @@ export default function ContributeLink() {
{editThisPageMessage}
+
);
}
\ No newline at end of file
diff --git a/docusaurus/src/components/SendToAiButton.js b/docusaurus/src/components/SendToAiButton.js
new file mode 100644
index 0000000000..4fb726f3fd
--- /dev/null
+++ b/docusaurus/src/components/SendToAiButton.js
@@ -0,0 +1,389 @@
+import React, { useState, useCallback, useRef } from 'react';
+
+const SendToAIButton = ({ Icon }) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const [isLoading, setIsLoading] = useState(false);
+ const [showPromptModal, setShowPromptModal] = useState(false);
+ const [currentPrompt, setCurrentPrompt] = useState('');
+ const [currentAI, setCurrentAI] = useState('');
+ const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0 });
+ const buttonRef = useRef(null);
+ const textareaRef = useRef(null);
+
+ // Calculate dropdown position
+ const calculatePosition = useCallback(() => {
+ if (buttonRef.current) {
+ const rect = buttonRef.current.getBoundingClientRect();
+ setDropdownPosition({
+ top: rect.bottom + window.scrollY + 4,
+ left: rect.left + window.scrollX
+ });
+ }
+ }, []);
+
+ // Get current document info
+ const getCurrentDocId = () => {
+ if (typeof window === 'undefined') return null;
+ const path = window.location.pathname;
+ const segments = path.replace(/^\/|\/$/g, '').split('/');
+ return segments.length >= 2 ? segments.join('/') : null;
+ };
+
+ const getCurrentDocPath = () => {
+ if (typeof window === 'undefined') return null;
+ const path = window.location.pathname;
+ const cleanPath = path.replace(/^\/|\/$/g, '');
+ return cleanPath ? `docs/${cleanPath}.md` : null;
+ };
+
+ // Fetch markdown content
+ const fetchMarkdownContent = async () => {
+ const currentDocId = getCurrentDocId();
+ const currentDocPath = getCurrentDocPath();
+
+ if (!currentDocId && !currentDocPath) {
+ throw new Error('Could not determine document path');
+ }
+
+ const baseUrl = 'https://raw.githubusercontent.com/strapi/documentation/main/docusaurus';
+ const markdownUrl = currentDocPath
+ ? `${baseUrl}/${currentDocPath}`
+ : `${baseUrl}/docs/${currentDocId}.md`;
+
+ const response = await fetch(markdownUrl);
+ if (!response.ok) throw new Error(`Failed to fetch: ${response.status}`);
+ return await response.text();
+ };
+
+ // Build prompt
+ const buildPrompt = (markdownContent) => {
+ return `You're a Strapi documentation expert. Please use this content as context and get ready to answer my questions:
+
+---
+
+${markdownContent}
+
+---
+
+I'm ready for your questions about this Strapi documentation!`;
+ };
+
+ // Handle AI selection
+ const handleAIClick = useCallback(async (aiType) => {
+ setIsLoading(true);
+ setIsOpen(false);
+
+ try {
+ const markdownContent = await fetchMarkdownContent();
+ const prompt = buildPrompt(markdownContent);
+
+ setCurrentPrompt(prompt);
+ setCurrentAI(aiType);
+ setShowPromptModal(true);
+
+ } catch (error) {
+ console.error('Error fetching content:', error);
+ alert('Error: Could not fetch the markdown content');
+ } finally {
+ setIsLoading(false);
+ }
+ }, []);
+
+ // Handle copy from modal
+ const handleCopyFromModal = useCallback(async () => {
+ try {
+ await navigator.clipboard.writeText(currentPrompt);
+
+ // Open the AI tool
+ const url = currentAI === 'ChatGPT'
+ ? 'https://chat.openai.com/'
+ : 'https://claude.ai/chat';
+ window.open(url, '_blank');
+
+ // Close modal
+ setShowPromptModal(false);
+
+ // Show elegant notification instead of alert
+ const notification = document.createElement('div');
+ notification.style.cssText = `
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ background: #28a745;
+ color: white;
+ padding: 12px 16px;
+ border-radius: 6px;
+ z-index: 1000001;
+ font-family: inherit;
+ font-size: 14px;
+ font-weight: 500;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
+ max-width: 300px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ `;
+ notification.innerHTML = `
+
+ Prompt copied! Paste it in ${currentAI}
+ `;
+ document.body.appendChild(notification);
+
+ setTimeout(() => {
+ if (document.body.contains(notification)) {
+ document.body.removeChild(notification);
+ }
+ }, 4000);
+
+ } catch (error) {
+ console.error('Clipboard failed:', error);
+ // If clipboard fails, select all text for manual copy
+ if (textareaRef.current) {
+ textareaRef.current.select();
+ textareaRef.current.setSelectionRange(0, 99999); // For mobile
+
+ // Show warning notification instead of alert
+ const notification = document.createElement('div');
+ notification.style.cssText = `
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ background: #ffc107;
+ color: #212529;
+ padding: 12px 16px;
+ border-radius: 6px;
+ z-index: 1000001;
+ font-family: inherit;
+ font-size: 14px;
+ font-weight: 500;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
+ max-width: 300px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ `;
+ notification.innerHTML = `
+
+ Auto-copy failed. Please copy manually (Ctrl+C)
+ `;
+ document.body.appendChild(notification);
+
+ setTimeout(() => {
+ if (document.body.contains(notification)) {
+ document.body.removeChild(notification);
+ }
+ }, 5000);
+ }
+ }
+ }, [currentPrompt, currentAI]);
+
+ const handleToggle = useCallback((e) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (!isOpen) {
+ calculatePosition();
+ }
+ setIsOpen(prev => !prev);
+ }, [isOpen, calculatePosition]);
+
+ if (!getCurrentDocId()) return null;
+
+ return (
+ <>
+
+
+ {/* Dropdown */}
+ {isOpen && (
+
+
+
+
+ )}
+
+ {/* Prompt Modal */}
+ {showPromptModal && (
+
+
+
+
+ Copy this prompt to {currentAI}
+
+
+ The prompt below contains the documentation content. Copy it and paste it in {currentAI} to start chatting!
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ {isLoading && (
+
+ Loading...
+
+ )}
+ >
+ );
+};
+
+export default SendToAIButton;
\ No newline at end of file
diff --git a/docusaurus/src/scss/__index.scss b/docusaurus/src/scss/__index.scss
index 29f2637e01..e8bb17afc0 100644
--- a/docusaurus/src/scss/__index.scss
+++ b/docusaurus/src/scss/__index.scss
@@ -45,6 +45,7 @@
@use 'search.scss';
@use 'scene.scss';
@use 'sidebar.scss';
+@use 'send-to-ai.scss';
@use 'table.scss';
@use 'tabs.scss';
@use 'table-of-contents.scss';
diff --git a/docusaurus/src/scss/send-to-ai.scss b/docusaurus/src/scss/send-to-ai.scss
new file mode 100644
index 0000000000..b280158533
--- /dev/null
+++ b/docusaurus/src/scss/send-to-ai.scss
@@ -0,0 +1,268 @@
+/** Component: Send to AI Button */
+@use './mixins' as *;
+
+.send-to-ai-button {
+ position: relative;
+ display: inline-block;
+ // Force higher z-index to override navbar contexts
+ z-index: 10000;
+
+ &__trigger {
+ display: flex;
+ align-items: center;
+ padding: 0;
+ border: none;
+ border-radius: 0;
+ background-color: transparent;
+ color: var(--strapi-primary-600);
+ font-size: var(--strapi-font-size-xs);
+ font-weight: 500;
+ font-family: "SF Pro Text", "SF Pro Display", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+ cursor: pointer;
+ text-decoration: none;
+ transition: color var(--ifm-transition-fast, 0.15s) var(--ifm-transition-timing-default, ease-in-out);
+
+ &:hover {
+ text-decoration: none;
+
+ span {
+ text-decoration: underline;
+ }
+ }
+
+ &:disabled {
+ cursor: default;
+ opacity: 0.7;
+ }
+
+ // First icon (robot)
+ i:first-child,
+ span:first-child {
+ margin-right: 0.5rem;
+ position: relative;
+ top: -1px;
+ text-decoration: none !important;
+ }
+
+ // Last icon (caret)
+ i:last-child,
+ span:last-child {
+ margin-left: 0.25rem;
+ font-size: 10px;
+ position: relative;
+ top: -1px;
+ text-decoration: none !important;
+ transition: transform 0.2s ease;
+ }
+
+ &[aria-expanded="true"] {
+ i:last-child,
+ span:last-child {
+ transform: rotate(180deg);
+ }
+ }
+ }
+
+ &__dropdown {
+ position: absolute;
+ top: calc(100% + 4px);
+ left: 0;
+ right: auto;
+ background-color: var(--ifm-background-color);
+ border: 1px solid var(--strapi-neutral-200);
+ border-radius: 4px;
+ box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.15);
+ // Force highest z-index to be above navbar, search, and other components
+ z-index: 99999 !important;
+ min-width: 180px;
+ overflow: visible;
+ // Force positioning context
+ transform: translateZ(0);
+
+ // Animation
+ animation: dropdownFadeIn 0.15s ease-out;
+
+ // Ensure visibility
+ opacity: 1 !important;
+ visibility: visible !important;
+ display: block !important;
+ pointer-events: auto !important;
+ }
+
+ &__option {
+ display: flex !important;
+ align-items: center;
+ width: 100%;
+ padding: 12px 16px;
+ border: none;
+ background-color: transparent;
+ color: var(--strapi-neutral-800);
+ font-size: var(--strapi-font-size-sm);
+ font-weight: 500;
+ font-family: "SF Pro Text", "SF Pro Display", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+ cursor: pointer;
+ text-align: left;
+ transition: background-color 0.15s ease;
+ // Force visibility
+ opacity: 1 !important;
+ visibility: visible !important;
+ pointer-events: auto !important;
+ // Ensure proper stacking
+ position: relative;
+ z-index: 1;
+
+ &:hover {
+ background-color: var(--strapi-primary-100) !important;
+ color: var(--strapi-primary-700) !important;
+ }
+
+ &:active {
+ background-color: var(--strapi-primary-200) !important;
+ }
+
+ &:disabled {
+ cursor: default;
+ opacity: 0.5 !important;
+ }
+
+ // Icon in options
+ i, span:first-child {
+ margin-right: 10px;
+ text-decoration: none !important;
+ flex-shrink: 0;
+ display: inline-block !important;
+ }
+
+ // Text in options
+ span:last-child {
+ text-decoration: none;
+ white-space: nowrap;
+ display: inline-block !important;
+ }
+
+ // Add border between options
+ &:not(:last-child) {
+ border-bottom: 1px solid var(--strapi-neutral-150);
+ }
+
+ // Debug: force visible styles
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: transparent;
+ z-index: -1;
+ }
+ }
+
+ &__loading {
+ position: absolute;
+ top: calc(100% + 4px);
+ left: 0;
+ padding: 8px 12px;
+ background-color: var(--strapi-neutral-100);
+ border: 1px solid var(--strapi-neutral-200);
+ border-radius: 4px;
+ font-size: var(--strapi-font-size-xs);
+ color: var(--strapi-neutral-600);
+ white-space: nowrap;
+ z-index: 99999 !important;
+
+ // Loading animation
+ &::after {
+ content: '';
+ display: inline-block;
+ width: 12px;
+ height: 12px;
+ margin-left: 8px;
+ border: 2px solid var(--strapi-neutral-300);
+ border-top-color: var(--strapi-primary-600);
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+ }
+ }
+}
+
+// Portal styles for dropdowns that might be rendered outside normal flow
+.send-to-ai-dropdown-portal {
+ position: fixed !important;
+ z-index: 99999 !important;
+ pointer-events: auto !important;
+
+ .send-to-ai-button__dropdown {
+ position: relative !important;
+ top: 0 !important;
+ left: 0 !important;
+ z-index: 1 !important;
+ }
+}
+
+// Animations
+@keyframes dropdownFadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(-4px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+/** Dark mode */
+@include dark {
+ .send-to-ai-button {
+ &__trigger {
+ color: var(--strapi-primary-500);
+ }
+
+ &__dropdown {
+ background-color: var(--strapi-neutral-100) !important;
+ border-color: var(--strapi-neutral-200);
+ box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.4);
+ }
+
+ &__option {
+ color: var(--strapi-neutral-800) !important;
+
+ &:hover {
+ background-color: var(--strapi-primary-100) !important;
+ color: var(--strapi-primary-700) !important;
+ }
+
+ &:active {
+ background-color: var(--strapi-primary-200) !important;
+ }
+
+ &:not(:last-child) {
+ border-bottom-color: var(--strapi-neutral-200);
+ }
+ }
+
+ &__loading {
+ background-color: var(--strapi-neutral-100);
+ border-color: var(--strapi-neutral-200);
+ color: var(--strapi-neutral-600);
+ }
+ }
+
+ // Force dark mode dropdown visibility
+ .send-to-ai-dropdown-portal {
+ .send-to-ai-button__dropdown {
+ background-color: var(--strapi-neutral-100) !important;
+ }
+
+ .send-to-ai-button__option {
+ color: var(--strapi-neutral-800) !important;
+ }
+ }
+}
\ No newline at end of file