diff --git a/src/components/AskAI/AskAI.tsx b/src/components/AskAI/AskAI.tsx new file mode 100644 index 00000000..b6bed6f2 --- /dev/null +++ b/src/components/AskAI/AskAI.tsx @@ -0,0 +1,101 @@ +'use client'; + +import { useCallback, useEffect, useRef } from 'react'; + +const COOKBOOK_APP_ID = '0117f014f27a56633c30024d41166aa2'; +const WIDGET_CSS = 'https://bb-chat-widget.s3.us-east-1.amazonaws.com/assets/style.css'; +const WIDGET_JS = 'https://bb-chat-widget.s3.us-east-1.amazonaws.com/assets/index.js'; +const WIDGET_ID = 'bytebellai'; + +type AskAIProps = { + className?: string; +}; + +export const AskAI = ({ className }: AskAIProps) => { + const hostRef = useRef(null); + const launcherRef = useRef(null); + + useEffect(() => { + if (!hostRef.current) return; + + // --- 1. Create (or reuse) the shadow root + const shadow = hostRef.current.shadowRoot ?? hostRef.current.attachShadow({ mode: 'open' }); + + // --- 2. to the widget’s CSS (stays scoped to this shadow root) + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = WIDGET_CSS; + shadow.appendChild(link); + + // --- 3. The container the script looks for + const container = document.createElement('div'); + container.id = WIDGET_ID; + container.dataset.appId = COOKBOOK_APP_ID; + // Prefer top-right so it sits with the navbar instead of bottom-right default + container.dataset.position = 'top-right'; + // Fine-tune placement near the navbar area + container.dataset.top = '12px'; + container.dataset.right = '16px'; + shadow.appendChild(container); + + /* --- 4. Patch document.getElementById just once + so that “bytebellai” resolves to the shadow-root element. */ + const originalGetById = document.getElementById.bind(document); + document.getElementById = (id: string) => (id === WIDGET_ID ? container : originalGetById(id)); + + // --- 5. Load the widget script after everything’s ready + const script = document.createElement('script'); + script.src = WIDGET_JS; + script.async = true; + shadow.appendChild(script); + + // Observe the shadow DOM to capture the widget launcher button when it mounts + const observer = new MutationObserver(() => { + if (launcherRef.current) return; + const candidate = shadow.querySelector('button, [role="button"]') as HTMLElement | null; + if (candidate) { + launcherRef.current = candidate; + candidate.style.opacity = '0'; + candidate.style.pointerEvents = 'none'; + } + }); + observer.observe(shadow as unknown as Node, { childList: true, subtree: true }); + + // --- 6. Cleanup on unmount + return () => { + shadow.innerHTML = ''; + document.getElementById = originalGetById; // restore original API + observer.disconnect(); + }; + }, []); + + const handleOpen = useCallback(() => { + if (launcherRef.current) { + launcherRef.current.click(); + return; + } + const fallback = hostRef.current?.shadowRoot?.querySelector('button, [role="button"]') as HTMLElement | null; + if (fallback) { + fallback.click(); + return; + } + + // Last-resort: broadcast a generic open event used by some widgets + try { + window.dispatchEvent(new CustomEvent('bb:open')); + } catch {} + }, []); + + return ( +
+ + {/* Keep the widget host out of normal layout */} +
+
+ ); +}; diff --git a/src/components/AskAI/index.ts b/src/components/AskAI/index.ts new file mode 100644 index 00000000..de2d9a0c --- /dev/null +++ b/src/components/AskAI/index.ts @@ -0,0 +1 @@ +export * from './AskAI'; diff --git a/src/components/AskCookbook/AskCookbook.tsx b/src/components/AskCookbook/AskCookbook.tsx deleted file mode 100644 index f37557bf..00000000 --- a/src/components/AskCookbook/AskCookbook.tsx +++ /dev/null @@ -1,51 +0,0 @@ -'use client'; - -import { useEffect, useRef } from 'react'; - -const COOKBOOK_APP_ID = '0117f014f27a56633c30024d41166aa2'; -const WIDGET_CSS = 'https://bb-chat-widget.s3.us-east-1.amazonaws.com/assets/style.css'; -const WIDGET_JS = 'https://bb-chat-widget.s3.us-east-1.amazonaws.com/assets/index.js'; -const WIDGET_ID = 'bytebellai'; - -export const AskCookbook = () => { - const hostRef = useRef(null); - - useEffect(() => { - if (!hostRef.current) return; - - // --- 1. Create (or reuse) the shadow root - const shadow = hostRef.current.shadowRoot ?? hostRef.current.attachShadow({ mode: 'open' }); - - // --- 2. to the widget’s CSS (stays scoped to this shadow root) - const link = document.createElement('link'); - link.rel = 'stylesheet'; - link.href = WIDGET_CSS; - shadow.appendChild(link); - - // --- 3. The container the script looks for - const container = document.createElement('div'); - container.id = WIDGET_ID; - container.dataset.appId = COOKBOOK_APP_ID; - shadow.appendChild(container); - - /* --- 4. Patch document.getElementById just once - so that “bytebellai” resolves to the shadow-root element. */ - const originalGetById = document.getElementById.bind(document); - document.getElementById = (id: string) => (id === WIDGET_ID ? container : originalGetById(id)); - - // --- 5. Load the widget script after everything’s ready - const script = document.createElement('script'); - script.src = WIDGET_JS; - script.async = true; - shadow.appendChild(script); - - // --- 6. Cleanup on unmount - return () => { - shadow.innerHTML = ''; - document.getElementById = originalGetById; // restore original API - }; - }, []); - - /* The host div is empty; all real content lives in its shadow root. */ - return
; -}; diff --git a/src/components/AskCookbook/ask-cookbook.d.ts b/src/components/AskCookbook/ask-cookbook.d.ts deleted file mode 100644 index 55ec0a60..00000000 --- a/src/components/AskCookbook/ask-cookbook.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Solves the following error: "Cannot find module ... or its corresponding type declarations. There are types at ..., but this result could not be resolved under your current 'moduleResolution' setting. Consider updating to 'node16', 'nodenext', or 'bundler'. [2307]" -declare module "@cookbookdev/docsbot/react" { - export { default } from "@cookbookdev/docsbot/dist/react/index.d.ts" -} diff --git a/src/components/AskCookbook/index.ts b/src/components/AskCookbook/index.ts deleted file mode 100644 index 17887677..00000000 --- a/src/components/AskCookbook/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './AskCookbook'; diff --git a/src/components/index.ts b/src/components/index.ts index fbe90f0a..d6ec2511 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -12,7 +12,7 @@ export { Logo, LogoMobile } from './Logo'; export * from './VersionFetcher'; export { PropertyInfo } from './PropertyInfo'; export * from './EcosystemMap'; -export * from './AskCookbook'; +export * from './AskAI'; export * from './QuickStartCard'; export { NetworkTabs } from './NetworkTabs'; export * from './OfficeHoursCard'; diff --git a/src/providers/DocsProviders.tsx b/src/providers/DocsProviders.tsx index 9265c070..40bc643d 100644 --- a/src/providers/DocsProviders.tsx +++ b/src/providers/DocsProviders.tsx @@ -1,7 +1,7 @@ 'use client'; import { Layout, Navbar } from 'nextra-theme-docs'; -import { AskCookbook } from '../components'; +import { AskAI } from '../components'; import { Logo, LogoMobile } from '../components/Logo'; import React, { useState, useEffect } from 'react'; import { Footer } from '../components/Footer/Footer'; @@ -39,16 +39,18 @@ export default function DocsProviders({ children, pageMap }) { children={ <>
- Support - + +
+ +
} @@ -59,22 +61,24 @@ export default function DocsProviders({ children, pageMap }) { logo={} logoLink='/' /* Remove excessive horizontal padding on small/medium screens; restore on large */ - className='flex items-center justify-between w-full dark:bg-neutral-900 bg-neutral-100 px-2 lg:px-4' + className='flex items-center w-full dark:bg-neutral-900 bg-neutral-100 px-2 lg:px-4' children={ -
-
- +
+
+ + Support + + +
+ +
+ {isHomepage && }
- - Support - - - {isHomepage && }
} />