1- import React , { useState , useRef , useEffect } from ' react' ;
2- import styles from ' ./styles.module.css' ;
1+ import React , { useState , useRef , useEffect } from " react" ;
2+ import styles from " ./styles.module.css" ;
33
44interface MarkdownButtonProps {
55 markdownUrl : string ;
66}
77
8- export default function MarkdownButton ( { markdownUrl } : MarkdownButtonProps ) : React . JSX . Element {
8+ export default function MarkdownButton ( {
9+ markdownUrl,
10+ } : MarkdownButtonProps ) : React . JSX . Element {
911 const [ isOpen , setIsOpen ] = useState ( false ) ;
1012 const [ copied , setCopied ] = useState ( false ) ;
1113 const dropdownRef = useRef < HTMLDivElement > ( null ) ;
1214
1315 // Close dropdown when clicking outside
1416 useEffect ( ( ) => {
1517 function handleClickOutside ( event : MouseEvent ) {
16- if ( dropdownRef . current && ! dropdownRef . current . contains ( event . target as Node ) ) {
18+ if (
19+ dropdownRef . current &&
20+ ! dropdownRef . current . contains ( event . target as Node )
21+ ) {
1722 setIsOpen ( false ) ;
1823 }
1924 }
20- document . addEventListener ( ' mousedown' , handleClickOutside ) ;
21- return ( ) => document . removeEventListener ( ' mousedown' , handleClickOutside ) ;
25+ document . addEventListener ( " mousedown" , handleClickOutside ) ;
26+ return ( ) => document . removeEventListener ( " mousedown" , handleClickOutside ) ;
2227 } , [ ] ) ;
2328
2429 const handleCopyPage = async ( ) => {
2530 try {
2631 const response = await fetch ( markdownUrl ) ;
27- if ( ! response . ok ) throw new Error ( ' Failed to fetch' ) ;
32+ if ( ! response . ok ) throw new Error ( " Failed to fetch" ) ;
2833 const markdown = await response . text ( ) ;
2934 await navigator . clipboard . writeText ( markdown ) ;
3035 setCopied ( true ) ;
3136 setTimeout ( ( ) => setCopied ( false ) , 2000 ) ;
3237 } catch ( err ) {
33- console . error ( ' Failed to copy:' , err ) ;
38+ console . error ( " Failed to copy:" , err ) ;
3439 }
3540 setIsOpen ( false ) ;
3641 } ;
3742
3843 const handleViewMarkdown = ( ) => {
39- window . open ( markdownUrl , ' _blank' ) ;
44+ window . open ( markdownUrl , " _blank" ) ;
4045 setIsOpen ( false ) ;
4146 } ;
4247
@@ -59,18 +64,27 @@ export default function MarkdownButton({ markdownUrl }: MarkdownButtonProps): Re
5964 } ;
6065
6166 const handleOpenInClaude = ( ) => {
62- window . open ( `https://claude.ai/new?q=${ encodeURIComponent ( getPromptWithMarkdown ( ) ) } ` , '_blank' ) ;
67+ window . open (
68+ `https://claude.ai/new?q=${ encodeURIComponent ( getPromptWithMarkdown ( ) ) } ` ,
69+ "_blank" ,
70+ ) ;
6371 setIsOpen ( false ) ;
6472 } ;
6573
6674 const handleOpenInChatGPT = ( ) => {
6775 // ChatGPT has issues with .md URLs, so use HTML URL
68- window . open ( `https://chat.openai.com/?q=${ encodeURIComponent ( getPromptWithHtml ( ) ) } ` , '_blank' ) ;
76+ window . open (
77+ `https://chat.openai.com/?q=${ encodeURIComponent ( getPromptWithHtml ( ) ) } ` ,
78+ "_blank" ,
79+ ) ;
6980 setIsOpen ( false ) ;
7081 } ;
7182
7283 const handleOpenInPerplexity = ( ) => {
73- window . open ( `https://www.perplexity.ai/?q=${ encodeURIComponent ( getPromptWithMarkdown ( ) ) } ` , '_blank' ) ;
84+ window . open (
85+ `https://www.perplexity.ai/?q=${ encodeURIComponent ( getPromptWithMarkdown ( ) ) } ` ,
86+ "_blank" ,
87+ ) ;
7488 setIsOpen ( false ) ;
7589 } ;
7690
@@ -82,61 +96,99 @@ export default function MarkdownButton({ markdownUrl }: MarkdownButtonProps): Re
8296 type = "button"
8397 aria-expanded = { isOpen }
8498 aria-haspopup = "true"
99+ aria-label = "Copy page URL"
85100 >
86101 < CopyIcon />
87- < span > { copied ? ' Copied!' : ' Copy page' } </ span >
102+ < span data-nosnippet > { copied ? " Copied!" : " Copy page" } </ span >
88103 < ChevronIcon isOpen = { isOpen } />
89104 </ button >
90105
91106 { isOpen && (
92107 < >
93- < div className = { styles . mobileBackdrop } onClick = { ( ) => setIsOpen ( false ) } />
94- < div className = { styles . dropdownMenu } >
95- < button className = { styles . dropdownItem } onClick = { handleCopyPage } >
96- < CopyIcon />
97- < div className = { styles . dropdownItemText } >
98- < span className = { styles . dropdownItemTitle } > Copy page</ span >
99- < span className = { styles . dropdownItemDesc } > Copy page as Markdown for LLMs</ span >
100- </ div >
101- </ button >
102-
103- < button className = { styles . dropdownItem } onClick = { handleViewMarkdown } >
104- < MarkdownIcon />
105- < div className = { styles . dropdownItemText } >
106- < span className = { styles . dropdownItemTitle } > View as Markdown</ span >
107- < span className = { styles . dropdownItemDesc } > View this page as plain text</ span >
108- </ div >
109- </ button >
110-
111- < div className = { styles . dropdownDivider } />
112-
113- < button className = { styles . dropdownItem } onClick = { handleOpenInChatGPT } >
114- < ChatGPTIcon />
115- < div className = { styles . dropdownItemText } >
116- < span className = { styles . dropdownItemTitle } > Open in ChatGPT</ span >
117- < span className = { styles . dropdownItemDesc } > Ask questions about this page</ span >
118- </ div >
119- < ExternalIcon />
120- </ button >
121-
122- < button className = { styles . dropdownItem } onClick = { handleOpenInClaude } >
123- < ClaudeIcon />
124- < div className = { styles . dropdownItemText } >
125- < span className = { styles . dropdownItemTitle } > Open in Claude</ span >
126- < span className = { styles . dropdownItemDesc } > Ask questions about this page</ span >
127- </ div >
128- < ExternalIcon />
129- </ button >
130-
131- < button className = { styles . dropdownItem } onClick = { handleOpenInPerplexity } >
132- < PerplexityIcon />
133- < div className = { styles . dropdownItemText } >
134- < span className = { styles . dropdownItemTitle } > Open in Perplexity</ span >
135- < span className = { styles . dropdownItemDesc } > Ask questions about this page</ span >
136- </ div >
137- < ExternalIcon />
138- </ button >
139- </ div >
108+ < div
109+ className = { styles . mobileBackdrop }
110+ onClick = { ( ) => setIsOpen ( false ) }
111+ />
112+ < div className = { styles . dropdownMenu } >
113+ < button
114+ className = { styles . dropdownItem }
115+ onClick = { handleCopyPage }
116+ aria-label = "Copy page URL"
117+ >
118+ < CopyIcon />
119+ < div className = { styles . dropdownItemText } >
120+ < span className = { styles . dropdownItemTitle } data-nosnippet >
121+ Copy page
122+ </ span >
123+ < span className = { styles . dropdownItemDesc } >
124+ Copy page as Markdown for LLMs
125+ </ span >
126+ </ div >
127+ </ button >
128+
129+ < button
130+ className = { styles . dropdownItem }
131+ onClick = { handleViewMarkdown }
132+ >
133+ < MarkdownIcon />
134+ < div className = { styles . dropdownItemText } >
135+ < span className = { styles . dropdownItemTitle } >
136+ View as Markdown
137+ </ span >
138+ < span className = { styles . dropdownItemDesc } >
139+ View this page as plain text
140+ </ span >
141+ </ div >
142+ </ button >
143+
144+ < div className = { styles . dropdownDivider } />
145+
146+ < button
147+ className = { styles . dropdownItem }
148+ onClick = { handleOpenInChatGPT }
149+ >
150+ < ChatGPTIcon />
151+ < div className = { styles . dropdownItemText } >
152+ < span className = { styles . dropdownItemTitle } >
153+ Open in ChatGPT
154+ </ span >
155+ < span className = { styles . dropdownItemDesc } >
156+ Ask questions about this page
157+ </ span >
158+ </ div >
159+ < ExternalIcon />
160+ </ button >
161+
162+ < button
163+ className = { styles . dropdownItem }
164+ onClick = { handleOpenInClaude }
165+ >
166+ < ClaudeIcon />
167+ < div className = { styles . dropdownItemText } >
168+ < span className = { styles . dropdownItemTitle } > Open in Claude</ span >
169+ < span className = { styles . dropdownItemDesc } >
170+ Ask questions about this page
171+ </ span >
172+ </ div >
173+ < ExternalIcon />
174+ </ button >
175+
176+ < button
177+ className = { styles . dropdownItem }
178+ onClick = { handleOpenInPerplexity }
179+ >
180+ < PerplexityIcon />
181+ < div className = { styles . dropdownItemText } >
182+ < span className = { styles . dropdownItemTitle } >
183+ Open in Perplexity
184+ </ span >
185+ < span className = { styles . dropdownItemDesc } >
186+ Ask questions about this page
187+ </ span >
188+ </ div >
189+ < ExternalIcon />
190+ </ button >
191+ </ div >
140192 </ >
141193 ) }
142194 </ div >
@@ -145,7 +197,13 @@ export default function MarkdownButton({ markdownUrl }: MarkdownButtonProps): Re
145197
146198function CopyIcon ( ) {
147199 return (
148- < svg width = "16" height = "16" viewBox = "0 0 16 16" fill = "currentColor" aria-hidden = "true" >
200+ < svg
201+ width = "16"
202+ height = "16"
203+ viewBox = "0 0 16 16"
204+ fill = "currentColor"
205+ aria-hidden = "true"
206+ >
149207 < path d = "M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z" />
150208 < path d = "M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z" />
151209 </ svg >
@@ -154,7 +212,13 @@ function CopyIcon() {
154212
155213function MarkdownIcon ( ) {
156214 return (
157- < svg width = "16" height = "16" viewBox = "0 0 16 16" fill = "currentColor" aria-hidden = "true" >
215+ < svg
216+ width = "16"
217+ height = "16"
218+ viewBox = "0 0 16 16"
219+ fill = "currentColor"
220+ aria-hidden = "true"
221+ >
158222 < path d = "M14.85 3c.63 0 1.15.52 1.14 1.15v7.7c0 .63-.51 1.15-1.15 1.15H1.15C.52 13 0 12.48 0 11.84V4.15C0 3.52.52 3 1.15 3h13.7zM9 11V5H7l-1.5 2.25L4 5H2v6h2V8l1.5 2L7 8v3h2zm2.99.5L14.5 8H13V5h-2v3H9.5l2.49 3.5z" />
159223 </ svg >
160224 ) ;
@@ -168,7 +232,10 @@ function ChevronIcon({ isOpen }: { isOpen: boolean }) {
168232 viewBox = "0 0 12 12"
169233 fill = "currentColor"
170234 aria-hidden = "true"
171- style = { { transform : isOpen ? 'rotate(180deg)' : 'rotate(0deg)' , transition : 'transform 0.2s' } }
235+ style = { {
236+ transform : isOpen ? "rotate(180deg)" : "rotate(0deg)" ,
237+ transition : "transform 0.2s" ,
238+ } }
172239 >
173240 < path d = "M2.22 4.47a.75.75 0 011.06 0L6 7.19l2.72-2.72a.75.75 0 111.06 1.06l-3.25 3.25a.75.75 0 01-1.06 0L2.22 5.53a.75.75 0 010-1.06z" />
174241 </ svg >
@@ -177,7 +244,14 @@ function ChevronIcon({ isOpen }: { isOpen: boolean }) {
177244
178245function ExternalIcon ( ) {
179246 return (
180- < svg width = "12" height = "12" viewBox = "0 0 12 12" fill = "currentColor" aria-hidden = "true" className = { styles . externalIcon } >
247+ < svg
248+ width = "12"
249+ height = "12"
250+ viewBox = "0 0 12 12"
251+ fill = "currentColor"
252+ aria-hidden = "true"
253+ className = { styles . externalIcon }
254+ >
181255 < path d = "M3.5 3a.5.5 0 00-.5.5v5a.5.5 0 00.5.5h5a.5.5 0 00.5-.5V6.5a.5.5 0 011 0v2A1.5 1.5 0 018.5 10h-5A1.5 1.5 0 012 8.5v-5A1.5 1.5 0 013.5 2h2a.5.5 0 010 1h-2z" />
182256 < path d = "M7 1.5a.5.5 0 01.5-.5h3a.5.5 0 01.5.5v3a.5.5 0 01-1 0V2.707L6.354 6.354a.5.5 0 11-.708-.708L9.293 2H7.5a.5.5 0 01-.5-.5z" />
183257 </ svg >
@@ -186,24 +260,46 @@ function ExternalIcon() {
186260
187261function ChatGPTIcon ( ) {
188262 return (
189- < svg width = "16" height = "16" viewBox = "0 0 24 24" fill = "currentColor" aria-hidden = "true" >
263+ < svg
264+ width = "16"
265+ height = "16"
266+ viewBox = "0 0 24 24"
267+ fill = "currentColor"
268+ aria-hidden = "true"
269+ >
190270 < path d = "M22.282 9.821a5.985 5.985 0 0 0-.516-4.91 6.046 6.046 0 0 0-6.51-2.9A6.065 6.065 0 0 0 4.981 4.18a5.985 5.985 0 0 0-3.998 2.9 6.046 6.046 0 0 0 .743 7.097 5.98 5.98 0 0 0 .51 4.911 6.051 6.051 0 0 0 6.515 2.9A5.985 5.985 0 0 0 13.26 24a6.056 6.056 0 0 0 5.772-4.206 5.99 5.99 0 0 0 3.997-2.9 6.056 6.056 0 0 0-.747-7.073zM13.26 22.43a4.476 4.476 0 0 1-2.876-1.04l.141-.081 4.779-2.758a.795.795 0 0 0 .392-.681v-6.737l2.02 1.168a.071.071 0 0 1 .038.052v5.583a4.504 4.504 0 0 1-4.494 4.494zM3.6 18.304a4.47 4.47 0 0 1-.535-3.014l.142.085 4.783 2.759a.771.771 0 0 0 .78 0l5.843-3.369v2.332a.08.08 0 0 1-.033.062L9.74 19.95a4.5 4.5 0 0 1-6.14-1.646zM2.34 7.896a4.485 4.485 0 0 1 2.366-1.973V11.6a.766.766 0 0 0 .388.676l5.815 3.355-2.02 1.168a.076.076 0 0 1-.071 0l-4.83-2.786A4.504 4.504 0 0 1 2.34 7.896zm16.597 3.855l-5.833-3.387L15.119 7.2a.076.076 0 0 1 .071 0l4.83 2.791a4.494 4.494 0 0 1-.676 8.105v-5.678a.79.79 0 0 0-.407-.667zm2.01-3.023l-.141-.085-4.774-2.782a.776.776 0 0 0-.785 0L9.409 9.23V6.897a.066.066 0 0 1 .028-.061l4.83-2.787a4.5 4.5 0 0 1 6.68 4.66zm-12.64 4.135l-2.02-1.164a.08.08 0 0 1-.038-.057V6.075a4.5 4.5 0 0 1 7.375-3.453l-.142.08-4.778 2.758a.795.795 0 0 0-.393.681zm1.097-2.365l2.602-1.5 2.607 1.5v2.999l-2.597 1.5-2.607-1.5z" />
191271 </ svg >
192272 ) ;
193273}
194274
195275function ClaudeIcon ( ) {
196276 return (
197- < svg width = "16" height = "16" viewBox = "0 0 256 257" fill = "currentColor" aria-hidden = "true" >
277+ < svg
278+ width = "16"
279+ height = "16"
280+ viewBox = "0 0 256 257"
281+ fill = "currentColor"
282+ aria-hidden = "true"
283+ >
198284 < path d = "m50.228 170.321 50.357-28.257.843-2.463-.843-1.361h-2.462l-8.426-.518-28.775-.778-24.952-1.037-24.175-1.296-6.092-1.297L0 125.796l.583-3.759 5.12-3.434 7.324.648 16.202 1.101 24.304 1.685 17.629 1.037 26.118 2.722h4.148l.583-1.685-1.426-1.037-1.101-1.037-25.147-17.045-27.22-18.017-14.258-10.37-7.713-5.25-3.888-4.925-1.685-10.758 7-7.713 9.397.649 2.398.648 9.527 7.323 20.35 15.75L94.817 91.9l3.889 3.24 1.555-1.102.195-.777-1.75-2.917-14.453-26.118-15.425-26.572-6.87-11.018-1.814-6.61c-.648-2.723-1.102-4.991-1.102-7.778l7.972-10.823L71.42 0 82.05 1.426l4.472 3.888 6.61 15.101 10.694 23.786 16.591 32.34 4.861 9.592 2.592 8.879.973 2.722h1.685v-1.556l1.36-18.211 2.528-22.36 2.463-28.776.843-8.1 4.018-9.722 7.971-5.25 6.222 2.981 5.12 7.324-.713 4.73-3.046 19.768-5.962 30.98-3.889 20.739h2.268l2.593-2.593 10.499-13.934 17.628-22.036 7.778-8.749 9.073-9.657 5.833-4.601h11.018l8.1 12.055-3.628 12.443-11.342 14.388-9.398 12.184-13.48 18.147-8.426 14.518.778 1.166 2.01-.194 30.46-6.481 16.462-2.982 19.637-3.37 8.88 4.148.971 4.213-3.5 8.62-20.998 5.184-24.628 4.926-36.682 8.685-.454.324.519.648 16.526 1.555 7.065.389h17.304l32.21 2.398 8.426 5.574 5.055 6.805-.843 5.184-12.962 6.611-17.498-4.148-40.83-9.721-14-3.5h-1.944v1.167l11.666 11.406 21.387 19.314 26.767 24.887 1.36 6.157-3.434 4.86-3.63-.518-23.526-17.693-9.073-7.972-20.545-17.304h-1.36v1.814l4.73 6.935 25.017 37.59 1.296 11.536-1.814 3.76-6.481 2.268-7.13-1.297-14.647-20.544-15.1-23.138-12.185-20.739-1.49.843-7.194 77.448-3.37 3.953-7.778 2.981-6.48-4.925-3.436-7.972 3.435-15.749 4.148-20.544 3.37-16.333 3.046-20.285 1.815-6.74-.13-.454-1.49.194-15.295 20.999-23.267 31.433-18.406 19.702-4.407 1.75-7.648-3.954.713-7.064 4.277-6.286 25.47-32.405 15.36-20.092 9.917-11.6-.065-1.686h-.583L44.07 198.125l-12.055 1.555-5.185-4.86.648-7.972 2.463-2.593 20.35-13.999-.064.065Z" />
199285 </ svg >
200286 ) ;
201287}
202288
203289function PerplexityIcon ( ) {
204290 return (
205- < svg width = "16" height = "16" viewBox = "0 0 34 38" fill = "currentColor" aria-hidden = "true" >
206- < path fillRule = "evenodd" clipRule = "evenodd" d = "M5.12114 0.0400391L15.919 9.98864V9.98636V0.062995H18.0209V10.0332L28.8671 0.0400391V11.3829H33.3202V27.744H28.8808V37.8442L18.0209 28.303V37.9538H15.919V28.4604L5.13338 37.96V27.744H0.680176V11.3829H5.12114V0.0400391ZM14.3344 13.4592H2.78208V25.6677H5.13074V21.8167L14.3344 13.4592ZM7.23518 22.7379V33.3271L15.919 25.6786V14.8506L7.23518 22.7379ZM18.0814 25.5775V14.8404L26.7677 22.7282V27.744H26.7789V33.219L18.0814 25.5775ZM28.8808 25.6677H31.2183V13.4592H19.752L28.8808 21.7302V25.6677ZM26.7652 11.3829V4.81584L19.6374 11.3829H26.7652ZM14.3507 11.3829H7.22306V4.81584L14.3507 11.3829Z" />
291+ < svg
292+ width = "16"
293+ height = "16"
294+ viewBox = "0 0 34 38"
295+ fill = "currentColor"
296+ aria-hidden = "true"
297+ >
298+ < path
299+ fillRule = "evenodd"
300+ clipRule = "evenodd"
301+ d = "M5.12114 0.0400391L15.919 9.98864V9.98636V0.062995H18.0209V10.0332L28.8671 0.0400391V11.3829H33.3202V27.744H28.8808V37.8442L18.0209 28.303V37.9538H15.919V28.4604L5.13338 37.96V27.744H0.680176V11.3829H5.12114V0.0400391ZM14.3344 13.4592H2.78208V25.6677H5.13074V21.8167L14.3344 13.4592ZM7.23518 22.7379V33.3271L15.919 25.6786V14.8506L7.23518 22.7379ZM18.0814 25.5775V14.8404L26.7677 22.7282V27.744H26.7789V33.219L18.0814 25.5775ZM28.8808 25.6677H31.2183V13.4592H19.752L28.8808 21.7302V25.6677ZM26.7652 11.3829V4.81584L19.6374 11.3829H26.7652ZM14.3507 11.3829H7.22306V4.81584L14.3507 11.3829Z"
302+ />
207303 </ svg >
208304 ) ;
209305}
0 commit comments