Skip to content
53 changes: 53 additions & 0 deletions src/components/copyMarkdownButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import {Clipboard} from 'react-feather';
import Link from 'next/link';

import {usePlausibleEvent} from 'sentry-docs/hooks/usePlausibleEvent';
import ChatGPT from 'sentry-docs/icons/chatgpt';
import Chevron from 'sentry-docs/icons/Chevron';
import Claude from 'sentry-docs/icons/claude';
import ExternalLink from 'sentry-docs/icons/external-link';
import Markdown from 'sentry-docs/icons/Markdown';

interface CopyMarkdownButtonProps {
Expand Down Expand Up @@ -66,6 +69,16 @@ export function CopyMarkdownButton({pathname}: CopyMarkdownButtonProps) {
setIsOpen(false);
};

const handleOpenChatGPTClick = () => {
emit('Open in ChatGPT', {props: {page: pathname, source: 'chatgpt_link'}});
setIsOpen(false);
};

const handleOpenClaudeClick = () => {
emit('Open in Claude', {props: {page: pathname, source: 'claude_link'}});
setIsOpen(false);
};

const handleDropdownToggle = () => {
setIsOpen(!isOpen);
if (!isOpen) {
Expand Down Expand Up @@ -204,6 +217,46 @@ export function CopyMarkdownButton({pathname}: CopyMarkdownButtonProps) {
</div>
</div>
</Link>

<a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if using Link here is better since it's an external link

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's actually why I used a instead of Link. Isn't Next.js's Link for soft nav within the app?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it can be used for external links too as it'll render an anchor tag anyway, was just thinking since Link was already imported in the file, just for standardization but leave it as is :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I know. Here's what my thought was:

  • we def have an external link and that's not going to change;
  • we don't want the current behaviour to change as well (with the a tag);
  • Next.js's Link component probably checks in with the router before navigating (extra steps for external navigation) and it has a preloading mechanism built in for perf optimization on soft navs. It's definitely built for local soft navigation;
  • Next.js's Link component is third-party and prone to change;

The a approach seemed like the cleanest.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

100% aligned, great call

href={`https://chatgpt.com/?hint=search&q=${encodeURIComponent('Read from ' + window.location.href + ' so I can ask questions about its contents')}`}
target="_blank"
rel="noopener noreferrer"
className={`${dropdownItemClass} no-underline`}
onClick={handleOpenChatGPTClick}
>
<div className={iconContainerClass}>
<ChatGPT width={14} height={14} />
</div>
<div className="flex-1">
<div className="font-medium text-sm leading-5 text-gray-900 dark:text-[var(--foreground)]">
Open in ChatGPT <ExternalLink aria-hidden="true" />
</div>
<div className="text-xs leading-4 text-gray-500 dark:text-[var(--foreground-secondary)]">
Ask ChatGPT questions about this page
</div>
</div>
</a>

<a
href={`https://claude.ai/new?q=${encodeURIComponent('Read from ' + window.location.href + ' so I can ask questions about its contents')}`}
target="_blank"
rel="noopener noreferrer"
className={`${dropdownItemClass} no-underline`}
onClick={handleOpenClaudeClick}
>
<div className={iconContainerClass}>
<Claude width={14} height={14} />
</div>
<div className="flex-1">
<div className="font-medium text-sm leading-5 text-gray-900 dark:text-[var(--foreground)]">
Open in Claude <ExternalLink aria-hidden="true" />
</div>
<div className="text-xs leading-4 text-gray-500 dark:text-[var(--foreground-secondary)]">
Ask Claude questions about this page
</div>
</div>
</a>
</div>
</div>,
document.body
Expand Down
8 changes: 8 additions & 0 deletions src/hooks/usePlausibleEvent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ type PlausibleEventProps = {
page: string;
title: string;
};
['Open in ChatGPT']: {
page: string;
source: string;
};
['Open in Claude']: {
page: string;
source: string;
};
['Read Progress']: {
page: string;
readProgress: ReadProgressMilestone;
Expand Down
16 changes: 16 additions & 0 deletions src/icons/chatgpt.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
function ChatGPT({width = 16, height = 16, ...props}: React.SVGAttributes<SVGElement>) {
return (
<svg
fill="currentColor"
fillRule="evenodd"
height={height}
viewBox="0 0 24 24"
width={width}
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path d="M21.55 10.004a5.416 5.416 0 00-.478-4.501c-1.217-2.09-3.662-3.166-6.05-2.66A5.59 5.59 0 0010.831 1C8.39.995 6.224 2.546 5.473 4.838A5.553 5.553 0 001.76 7.496a5.487 5.487 0 00.691 6.5 5.416 5.416 0 00.477 4.502c1.217 2.09 3.662 3.165 6.05 2.66A5.586 5.586 0 0013.168 23c2.443.006 4.61-1.546 5.361-3.84a5.553 5.553 0 003.715-2.66 5.488 5.488 0 00-.693-6.497v.001zm-8.381 11.558a4.199 4.199 0 01-2.675-.954c.034-.018.093-.05.132-.074l4.44-2.53a.71.71 0 00.364-.623v-6.176l1.877 1.069c.02.01.033.029.036.05v5.115c-.003 2.274-1.87 4.118-4.174 4.123zM4.192 17.78a4.059 4.059 0 01-.498-2.763c.032.02.09.055.131.078l4.44 2.53c.225.13.504.13.73 0l5.42-3.088v2.138a.068.068 0 01-.027.057L9.9 19.288c-1.999 1.136-4.552.46-5.707-1.51h-.001zM3.023 8.216A4.15 4.15 0 015.198 6.41l-.002.151v5.06a.711.711 0 00.364.624l5.42 3.087-1.876 1.07a.067.067 0 01-.063.005l-4.489-2.559c-1.995-1.14-2.679-3.658-1.53-5.63h.001zm15.417 3.54l-5.42-3.088L14.896 7.6a.067.067 0 01.063-.006l4.489 2.557c1.998 1.14 2.683 3.662 1.529 5.633a4.163 4.163 0 01-2.174 1.807V12.38a.71.71 0 00-.363-.623zm1.867-2.773a6.04 6.04 0 00-.132-.078l-4.44-2.53a.731.731 0 00-.729 0l-5.42 3.088V7.325a.068.068 0 01.027-.057L14.1 4.713c2-1.137 4.555-.46 5.707 1.513.487.833.664 1.809.499 2.757h.001zm-11.741 3.81l-1.877-1.068a.065.065 0 01-.036-.051V6.559c.001-2.277 1.873-4.122 4.181-4.12.976 0 1.92.338 2.671.954-.034.018-.092.05-.131.073l-4.44 2.53a.71.71 0 00-.365.623l-.003 6.173v.002zm1.02-2.168L12 9.25l2.414 1.375v2.75L12 14.75l-2.415-1.375v-2.75z" />
</svg>
);
}
export default ChatGPT;
17 changes: 17 additions & 0 deletions src/icons/claude.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
function Claude({width = 16, height = 16, ...props}: React.SVGAttributes<SVGElement>) {
return (
<svg
fill="currentColor"
fillRule="evenodd"
height={height}
viewBox="0 0 24 24"
width={width}
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<title>Claude</title>
<path d="M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z" />
</svg>
);
}
export default Claude;
23 changes: 23 additions & 0 deletions src/icons/external-link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
function ExternalLink({
width = 14,
height = 14,
...props
}: React.SVGAttributes<SVGElement>) {
return (
<span className="icon icon-external-link">
<svg
viewBox="0 0 24 24"
width={width}
height={height}
className="ml-1 inline"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id control this as props in case we want to use the external link as the first item

{...props}
>
<path
fill="currentColor"
d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"
/>
</svg>
</span>
);
}
export default ExternalLink;
Loading