diff --git a/src/components/AITools/index.tsx b/src/components/AITools/index.tsx new file mode 100644 index 0000000000..c83d4e9f71 --- /dev/null +++ b/src/components/AITools/index.tsx @@ -0,0 +1,88 @@ +import React, { useState } from 'react'; +import styles from './styles.module.css'; + +// Derive the mirror URL for the current page. +// /help/docs/foo/bar → /help/llm/docs/foo/bar.md +function getMirrorUrl(): string | null { + if (typeof window === 'undefined') return null; + const match = window.location.pathname.match(/^(.*\/docs\/.+?)\/?$/); + if (!match) return null; + return `${window.location.origin}${match[1].replace('/docs/', '/llm/docs/')}.md`; +} + +async function getPageContent(): Promise { + const mirrorUrl = getMirrorUrl(); + if (mirrorUrl) { + try { + const res = await fetch(mirrorUrl); + if (res.ok) return await res.text(); + } catch { /* fall through */ } + } + + // Fall back to readable page text when the markdown mirror is unavailable. + const el = document.querySelector('article') || document.body; + return (el as HTMLElement).innerText.replace(/\s+/g, ' ').trim().slice(0, 4000); +} + +function getPrompt(): string { + const url = typeof window !== 'undefined' ? window.location.href : ''; + const mirrorUrl = getMirrorUrl(); + const source = mirrorUrl ? `the markdown mirror at ${mirrorUrl} and the source page at ${url}` : `the source page at ${url}`; + return encodeURIComponent(`Read ${source} so I can ask questions about its contents. If you cannot access the page, ask me to paste the copied page content.`); +} + +function CopyIcon() { + return ( + + + + + ); +} + +function ClaudeIcon() { + return ( + + ); +} + +function ChatGPTIcon() { + return ( + + ); +} + +export default function AITools(): JSX.Element { + const [copied, setCopied] = useState(false); + + const handleCopy = async () => { + try { + const text = await getPageContent(); + await navigator.clipboard.writeText(text); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch { /* clipboard not available */ } + }; + + return ( +
+
Use with AI
+ + + + Ask Claude + + + + Ask ChatGPT + +
+ ); +} diff --git a/src/components/AITools/styles.module.css b/src/components/AITools/styles.module.css new file mode 100644 index 0000000000..3a726ba056 --- /dev/null +++ b/src/components/AITools/styles.module.css @@ -0,0 +1,47 @@ +.aiTools { + border: 1px solid var(--ifm-color-emphasis-300); + border-radius: 8px; + padding: 10px 12px; + margin-bottom: 16px; +} + +.header { + font-size: 0.65rem; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--ifm-color-emphasis-600); + margin-bottom: 6px; +} + +.action { + display: flex; + align-items: center; + gap: 8px; + padding: 4px 0; + width: 100%; + background: none; + border: none; + cursor: pointer; + font-size: 0.75rem; + color: var(--ifm-color-emphasis-800); + text-decoration: none; + text-align: left; + line-height: 1.4; +} + +.action:hover { + color: var(--ifm-color-primary); + text-decoration: none; +} + +.action:disabled { + opacity: 0.6; + cursor: default; +} + +.icon { + flex: 0 0 14px; + height: 14px; + width: 14px; +} diff --git a/src/theme/DocItem/TOC/Desktop/index.tsx b/src/theme/DocItem/TOC/Desktop/index.tsx new file mode 100644 index 0000000000..70cd690113 --- /dev/null +++ b/src/theme/DocItem/TOC/Desktop/index.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import TOCDesktop from '@theme-original/DocItem/TOC/Desktop'; +import type TOCDesktopType from '@theme/DocItem/TOC/Desktop'; +import type { WrapperProps } from '@docusaurus/types'; +import AITools from '@site/src/components/AITools'; + +type Props = WrapperProps; + +export default function TOCDesktopWrapper(props: Props): JSX.Element { + return ( + <> + + + + ); +}