Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e4b44a0
Add CopyForLLMButton to copy markdown content for AI tools
cursoragent Jul 12, 2025
ecc01be
Add TypeScript types for react-feather icons
cursoragent Jul 12, 2025
84c9e60
Add ArrowDown icon to react-feather type definitions
cursoragent Jul 12, 2025
7d2b894
Add React and Node types to TypeScript configuration
cursoragent Jul 12, 2025
0649aff
Update TypeScript type dependencies to latest versions
cursoragent Jul 12, 2025
eee8060
Fix Sidebar type and add React/Next path aliases in tsconfig
cursoragent Jul 12, 2025
ac1f6ee
Improve markdown copy button with robust fallback and error handling
cursoragent Jul 12, 2025
9026f1b
Add Sentry error tracking to CopyForLLMButton component
cursoragent Jul 12, 2025
efbfd77
updating lock file
rahulchhabria Jul 12, 2025
7e4eb39
[getsentry/action-github-commit] Auto commit
getsantry[bot] Jul 12, 2025
d1b72ad
Checkpoint before follow-up message
cursoragent Jul 12, 2025
af2e61d
Remove unnecessary path mappings in tsconfig.json
cursoragent Jul 12, 2025
2817600
Improve clipboard copy with error handling and fallback mechanism
cursoragent Jul 12, 2025
7898dc2
[getsentry/action-github-commit] Auto commit
getsantry[bot] Jul 12, 2025
30bf5da
Adjust CopyForLLMButton scale to improve visual appearance
cursoragent Jul 12, 2025
2ef4d67
minor formating for mobile and desktop
rahulchhabria Jul 13, 2025
7f52ac7
Update React type definitions to version 19
cursoragent Jul 13, 2025
f90bbef
fix for: Bug: Uncleaned Timeout Causes React Warning
rahulchhabria Jul 13, 2025
25b4f4d
Merge branch 'cursor/add-copy-for-llm-button-54f6' of https://github.…
rahulchhabria Jul 13, 2025
ac4669a
[getsentry/action-github-commit] Auto commit
getsantry[bot] Jul 13, 2025
fcf6721
revert the downgrade
rahulchhabria Jul 13, 2025
da5007d
Merge branch 'cursor/add-copy-for-llm-button-54f6' of https://github.…
rahulchhabria Jul 13, 2025
20868a8
fixing issues with lock file
codyde Jul 13, 2025
7723d1e
bumping to next 15.1.8
codyde Jul 13, 2025
b0dda5c
fixing lockfiles
codyde Jul 13, 2025
2f90799
Fix TypeScript linting errors across components
codyde Jul 13, 2025
a31f49a
Fix remaining TypeScript property access errors
codyde Jul 13, 2025
72225e9
fixing lint... maybe?
codyde Jul 14, 2025
850cac3
Fix ESLint import sorting errors
codyde Jul 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added core
Binary file not shown.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
"mdx-bundler": "^10.0.1",
"mermaid": "^11.4.0",
"micromark": "^4.0.0",
"next": "15.1.7",
"next": "^15.1.8",
"next-mdx-remote": "^4.4.1",
"next-plausible": "^3.12.4",
"next-themes": "^0.3.0",
Expand Down Expand Up @@ -119,9 +119,9 @@
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.10",
"@types/dompurify": "3.0.5",
"@types/node": "^22",
"@types/react": "18.3.12",
"@types/react-dom": "18.3.1",
"@types/node": "22",
"@types/react": "19",
"@types/react-dom": "19",
"@types/ws": "^8.5.10",
"autoprefixer": "^10.4.17",
"concurrently": "^9.1.0",
Expand Down
15 changes: 15 additions & 0 deletions src/@types/react-feather.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
declare module 'react-feather' {
import {FC, SVGAttributes} from 'react';

export interface FeatherIconProps extends SVGAttributes<SVGElement> {
size?: number | string;
}

// We declare commonly used icons; others can be added as needed
export const Check: FC<FeatherIconProps>;
export const Clipboard: FC<FeatherIconProps>;
export const ArrowDown: FC<FeatherIconProps>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const icons: Record<string, FC<FeatherIconProps>>;
export default icons;
}
2 changes: 1 addition & 1 deletion src/clientUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ type ClickOutsideCallback = (event: MouseEvent) => void;

interface UseClickOutsideOpts<E extends HTMLElement> {
handler: ClickOutsideCallback;
ref: React.RefObject<E>;
ref: React.RefObject<E | null>;
enabled?: boolean;
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/codeBlock/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export function cleanCodeSnippet(rawCodeSnippet: string, options?: CleanCopyOpti
* @param options - Configuration options for cleaning
*/
export function useCleanSnippetInClipboard(
codeRef: RefObject<HTMLElement>,
codeRef: RefObject<HTMLElement | null>,
options: CleanCopyOptions = {}
) {
const {cleanDiffMarkers = true, cleanBashPrompt = true, language = ''} = options;
Expand Down
12 changes: 6 additions & 6 deletions src/components/codeHighlights/codeHighlights.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import {Children, cloneElement, ReactElement, useEffect, useRef, useState} from 'react';
import {Children, cloneElement, isValidElement, ReactElement, useEffect, useRef, useState} from 'react';
import {Check, Clipboard} from 'react-feather';
import styled from '@emotion/styled';
import * as Sentry from '@sentry/nextjs';
Expand All @@ -19,20 +19,20 @@ export function makeHighlightBlocks(
let highlightElementGroupCounter = 0;

return items.reduce((arr: ChildrenItem[], child, index) => {
if (typeof child !== 'object') {
if (typeof child !== 'object' || !isValidElement(child)) {
arr.push(child);
return arr;
}

const element = child as ReactElement;
const classes = element.props.className;
const classes = (element.props as any)?.className;

const isCodeLine = classes && classes.includes('code-line');
if (!isCodeLine) {
const updatedChild = cloneElement(
child as ReactElement,
element.props,
makeHighlightBlocks((child as ReactElement).props.children, language)
element,
element.props as any,
makeHighlightBlocks((element.props as any)?.children, language)
);
arr.push(updatedChild);
return arr;
Expand Down
21 changes: 13 additions & 8 deletions src/components/codeKeywords/codeKeywords.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import {Children, cloneElement, ReactElement} from 'react';
import {Children, cloneElement, isValidElement, ReactElement} from 'react';

import {KeywordSelector} from './keywordSelector';
import {OrgAuthTokenCreator} from './orgAuthTokenCreator';
Expand All @@ -15,19 +15,24 @@ export function makeKeywordsClickable(children: React.ReactNode) {
const items = Children.toArray(children);

return items.reduce((arr: ChildrenItem[], child) => {
if (typeof child !== 'string') {
if (typeof child !== 'string' && isValidElement(child)) {
const element = child as ReactElement;
const updatedChild = cloneElement(
child as ReactElement,
element,
{},
makeKeywordsClickable((child as ReactElement).props.children)
makeKeywordsClickable((element.props as any)?.children)
);
arr.push(updatedChild);
return arr;
}
if (ORG_AUTH_TOKEN_REGEX.test(child)) {
makeOrgAuthTokenClickable(arr, child);
} else if (KEYWORDS_REGEX.test(child)) {
makeProjectKeywordsClickable(arr, child);
if (typeof child === 'string') {
if (ORG_AUTH_TOKEN_REGEX.test(child)) {
makeOrgAuthTokenClickable(arr, child);
} else if (KEYWORDS_REGEX.test(child)) {
makeProjectKeywordsClickable(arr, child);
} else {
arr.push(child);
}
} else {
arr.push(child);
}
Expand Down
7 changes: 6 additions & 1 deletion src/components/codeTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import {
Children,
isValidElement,
ReactElement,
ReactNode,
useContext,
Expand Down Expand Up @@ -45,7 +46,11 @@ const showSigninNote = (children: ReactNode) => {
if (typeof node === 'string') {
return KEYWORDS_REGEX.test(node) || ORG_AUTH_TOKEN_REGEX.test(node);
}
return showSigninNote((node as ReactElement).props.children);
if (isValidElement(node)) {
const element = node as ReactElement;
return showSigninNote((element.props as any)?.children);
}
return false;
});
};

Expand Down
8 changes: 4 additions & 4 deletions src/components/configValue.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {Fragment} from 'react';
import {Fragment, ReactElement} from 'react';

type Props = {
children: JSX.Element;
children: ReactElement;
location: string;
name: string;
};

const getDescriptiveLocation = (location: string): JSX.Element => {
const getDescriptiveLocation = (location: string): ReactElement => {
switch (location) {
case 'env':
return <Fragment>in System Environment</Fragment>;
Expand All @@ -29,7 +29,7 @@ const getDescriptiveLocation = (location: string): JSX.Element => {
}
};

export function ConfigValue({name, location, children}: Props): JSX.Element {
export function ConfigValue({name, location, children}: Props): ReactElement {
return (
<div style={{marginBottom: '2rem'}}>
<h4 style={{marginBottom: 0, fontWeight: 'bold'}}>
Expand Down
83 changes: 83 additions & 0 deletions src/components/copyForLLMButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
'use client';

import {useEffect, useRef, useState} from 'react';
import {Check, Clipboard} from 'react-feather';
import * as Sentry from '@sentry/nextjs';

async function tryWriteClipboard(text: string): Promise<boolean> {
try {
if (navigator?.clipboard) {
await navigator.clipboard.writeText(text);
return true;
}
} catch (err) {
Sentry.captureException(err);
}
return false;
}

interface Props {
/** Absolute path to the markdown version of this page (e.g. `/docs/page.md`) */
markdownPath: string;
}

export function CopyForLLMButton({markdownPath}: Props) {
const [copied, setCopied] = useState(false);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);

useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);

async function handleClick() {
let didCopy = false;

// First attempt: fetch markdown file and copy its contents
try {
const response = await fetch(markdownPath);
if (response.ok) {
const text = await response.text();
didCopy = await tryWriteClipboard(text);
}
} catch (err) {
// network error handled below in fallback
Sentry.captureException(err);
}

// Fallback: copy the markdown URL if first attempt failed
if (!didCopy) {
try {
didCopy = await tryWriteClipboard(window.location.origin + markdownPath);
} catch (err) {
Sentry.captureException(err);
}
}

// Visual feedback only when something was actually copied
if (didCopy) {
setCopied(true);
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => setCopied(false), 1200);
}
}

return (
<button
type="button"
onClick={handleClick}
title="Copy page content for LLM"
className="mr-2 float-right flex items-center gap-1 rounded px-2 py-1 text-xs hover:bg-[var(--gray-2)]"
style={{height: '25px'}}
data-mdast="ignore"
>
{copied ? <Check size={14} /> : <Clipboard size={14} />}
<span className="hidden sm:inline whitespace-nowrap">Copy for LLM</span>
</button>
);
}
2 changes: 2 additions & 0 deletions src/components/docPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import './type.scss';
import {Banner} from '../banner';
import {Breadcrumbs} from '../breadcrumbs';
import {CodeContextProvider} from '../codeContext';
import {CopyForLLMButton} from '../copyForLLMButton';
import {DocFeedback} from '../docFeedback';
import {GitHubCTA} from '../githubCTA';
import {Header} from '../header';
Expand Down Expand Up @@ -94,6 +95,7 @@ export function DocPage({
>
<Markdown className="flex p-0 flex-wrap" width={24} height={24} />
</Link>
<CopyForLLMButton markdownPath={`/${pathname}.md`} />
</div>
<div>
<hgroup>
Expand Down
2 changes: 1 addition & 1 deletion src/components/expandable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export function Expandable({
[emit, title]
);

function toggleIsExpanded(event: React.MouseEvent<HTMLDetailsElement>) {
function toggleIsExpanded(event: React.SyntheticEvent<HTMLDetailsElement>) {
const newVal = event.currentTarget.open;
setIsExpanded(newVal);

Expand Down
4 changes: 3 additions & 1 deletion src/components/sidebar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {ReactElement} from 'react';

import {
extractPlatforms,
getCurrentGuide,
Expand All @@ -22,7 +24,7 @@ const activeLinkSelector = `.${styles.sidebar} .toc-item .active`;

export const sidebarToggleId = styles['navbar-menu-toggle'];

export async function Sidebar({path, versions}: SidebarProps) {
export async function Sidebar({path, versions}: SidebarProps): Promise<ReactElement> {
const rootNode = await getDocsRootNode();

if (isDeveloperDocs) {
Expand Down
Loading
Loading