diff --git a/packages/playground/website/src/components/site-error-modal/site-error-modal.tsx b/packages/playground/website/src/components/site-error-modal/site-error-modal.tsx index c0f531d05a..4bb86eb554 100644 --- a/packages/playground/website/src/components/site-error-modal/site-error-modal.tsx +++ b/packages/playground/website/src/components/site-error-modal/site-error-modal.tsx @@ -20,6 +20,7 @@ import type { } from './types'; import { getSiteErrorView } from './get-site-error-view'; import type { SiteInfo } from '../../lib/state/redux/slice-sites'; +import { useKapaAI } from './use-kapa-ai'; export function SiteErrorModal({ error, @@ -38,6 +39,7 @@ export function SiteErrorModal({ isSubmittingReport, handleSubmitReport, } = useErrorReporting(site); + const kapaAI = useKapaAI(); const helpers: PresentationHelpers = { deleteSite: () => { @@ -69,6 +71,9 @@ export function SiteErrorModal({ }); const detailText = formatErrorDetails(errorDetails); + const shouldShowKapaButton = + !isReporting && detailText && kapaAI.isEnabled(); + return ( )} - {view.actions.length || !view.isDeveloperError ? ( + {view.actions.length || !view.isDeveloperError || detailText ? (
+ {shouldShowKapaButton && ( + + )} {!view.isDeveloperError && !view.hideReportButton && !isReporting && diff --git a/packages/playground/website/src/components/site-error-modal/use-kapa-ai.ts b/packages/playground/website/src/components/site-error-modal/use-kapa-ai.ts new file mode 100644 index 0000000000..3211e709c2 --- /dev/null +++ b/packages/playground/website/src/components/site-error-modal/use-kapa-ai.ts @@ -0,0 +1,112 @@ +import { useCallback, useRef } from 'react'; + +const KAPA_WEBSITE_ID = 'a8b85529-1773-4710-b35f-c9ebc70ffcb6'; +const KAPA_SCRIPT_ID = 'kapa-widget-script'; + +declare global { + interface Window { + Kapa?: { + open: (options?: { + mode?: 'ai' | 'search'; + query?: string; + submit?: boolean; + }) => void; + close: () => void; + render: (options?: { onRender?: () => void }) => void; + unmount: () => void; + }; + } +} + +function loadKapaScript(): Promise { + return new Promise((resolve) => { + // Check if script already exists and loaded + if (document.getElementById(KAPA_SCRIPT_ID)) { + if (window.Kapa) { + resolve(); + } else { + // Script exists but not loaded yet, wait for it + const checkKapa = setInterval(() => { + if (window.Kapa) { + clearInterval(checkKapa); + resolve(); + } + }, 100); + setTimeout(() => clearInterval(checkKapa), 5000); + } + return; + } + + const script = document.createElement('script'); + script.id = KAPA_SCRIPT_ID; + script.src = 'https://widget.kapa.ai/kapa-widget.bundle.js'; + script.async = true; + script.setAttribute('data-website-id', KAPA_WEBSITE_ID); + script.setAttribute( + 'data-project-name', + 'WordPress Playground AI Assistant' + ); + script.setAttribute('data-project-color', '#3858e9'); + script.setAttribute( + 'data-project-logo', + 'https://wordpress.github.io/wordpress-playground/img/playground-logo.svg' + ); + script.setAttribute('data-button-hide', 'true'); + script.setAttribute('data-modal-z-index', '100001'); + script.setAttribute('data-scale-factor', '1.3'); + + script.onload = () => { + const checkKapa = setInterval(() => { + if (window.Kapa) { + clearInterval(checkKapa); + resolve(); + } + }, 100); + setTimeout(() => clearInterval(checkKapa), 5000); + }; + + document.body.appendChild(script); + }); +} + +export function useKapaAI() { + const hasSubmittedQuery = useRef(false); + const isEnabled = () => { + return ( + window.location.hostname === 'playground.wordpress.net' || + process.env.NODE_ENV === 'development' + ); + }; + + const openWithErrorMessage = useCallback(async (errorMessage: string) => { + if (!isEnabled()) { + return; + } + + await loadKapaScript(); + + if (window.Kapa) { + if (hasSubmittedQuery.current) { + window.Kapa.open(); + } else { + const urlParams = new URLSearchParams(window.location.search); + const hasExternalBlueprint = urlParams.has('blueprint-url'); + const contextPrefix = hasExternalBlueprint + ? '' + : `Given the URL query parameters ${window.location.search}, `; + + window.Kapa.open({ + mode: 'ai', + query: `${contextPrefix}Suggest a solution or troubleshooting steps for the following error: ${errorMessage}`, + submit: true, + }); + hasSubmittedQuery.current = true; + } + } + }, []); + + return { + isEnabled, + openWithErrorMessage, + }; +} diff --git a/packages/playground/website/vite.config.ts b/packages/playground/website/vite.config.ts index cc10eb0657..5b74e3b3cd 100644 --- a/packages/playground/website/vite.config.ts +++ b/packages/playground/website/vite.config.ts @@ -43,8 +43,8 @@ export default defineConfig(({ command, mode }) => { 'CORS_PROXY_URL' in process.env ? process.env.CORS_PROXY_URL : mode === 'production' - ? 'https://wordpress-playground-cors-proxy.net/?' - : '/cors-proxy/?'; + ? 'https://wordpress-playground-cors-proxy.net/?' + : '/cors-proxy/?'; return { // Split traffic from this server on dev so that the iframe content and