({name, url}: MarkdownMenuProps)
| 14 | } |
| 15 | |
| 16 | export function MarkdownMenu({name, url}: MarkdownMenuProps) { |
| 17 | let mdUrl = (url ?? '').replace(/\.html?$/i, '') + '.md'; |
| 18 | let [isCopied, setIsCopied] = useState(false); |
| 19 | let [isPending, setPending] = useState(false); |
| 20 | let timeout = useRef<ReturnType<typeof setTimeout> | null>(null); |
| 21 | |
| 22 | let pageUrl = |
| 23 | typeof window !== 'undefined' && url ? new URL(url, window.location.origin).href : (url ?? ''); |
| 24 | let fullMdUrl = |
| 25 | typeof window !== 'undefined' && mdUrl ? new URL(mdUrl, window.location.origin).href : mdUrl; |
| 26 | let library = url ? getLibraryLabel(getLibraryFromUrl(name)) : ''; |
| 27 | let aiPrompt = `Answer questions about the following ${library} documentation page: ${pageUrl}\nMarkdown source: ${fullMdUrl}`; |
| 28 | let chatGptUrl = `https://chatgpt.com/?q=${encodeURIComponent(aiPrompt)}`; |
| 29 | let claudeUrl = `https://claude.ai/new?q=${encodeURIComponent(aiPrompt)}`; |
| 30 | |
| 31 | useEffect(() => { |
| 32 | return () => { |
| 33 | if (timeout.current) { |
| 34 | clearTimeout(timeout.current); |
| 35 | } |
| 36 | }; |
| 37 | }, []); |
| 38 | |
| 39 | let handleCopy = useCallback(async () => { |
| 40 | if (timeout.current) { |
| 41 | clearTimeout(timeout.current); |
| 42 | } |
| 43 | if (typeof navigator !== 'undefined' && navigator.clipboard) { |
| 44 | try { |
| 45 | setPending(true); |
| 46 | await navigator.clipboard.write([ |
| 47 | new ClipboardItem({ |
| 48 | ['text/plain']: fetch(mdUrl).then(res => res.text()) |
| 49 | }) |
| 50 | ]); |
| 51 | setIsCopied(true); |
| 52 | timeout.current = setTimeout(() => setIsCopied(false), 2000); |
| 53 | } catch { |
| 54 | ToastQueue.negative('Failed to copy markdown.'); |
| 55 | } finally { |
| 56 | setPending(false); |
| 57 | } |
| 58 | } |
| 59 | }, [mdUrl]); |
| 60 | |
| 61 | return ( |
| 62 | <div |
| 63 | className={style({ |
| 64 | display: 'flex', |
| 65 | justifyContent: 'space-between', |
| 66 | paddingX: 4, |
| 67 | paddingBottom: 16 |
| 68 | })}> |
| 69 | <ActionButton isQuiet size="M" onPress={handleCopy} isPending={isPending}> |
| 70 | {isCopied ? <CheckmarkCircle /> : <Copy />} |
| 71 | <Text>Copy for LLM</Text> |
| 72 | </ActionButton> |
| 73 | <MenuTrigger align="end"> |
nothing calls this directly
no test coverage detected