@@ -19,7 +19,9 @@ import {useRouter} from 'next/router';
1919import { IconCanary } from './Icon/IconCanary' ;
2020import { IconExperimental } from './Icon/IconExperimental' ;
2121import { IconCopy } from './Icon/IconCopy' ;
22- import { Button } from './Button' ;
22+ import { IconChevron } from './Icon/IconChevron' ;
23+ import { Menu } from '@headlessui/react' ;
24+ import { IconNewPage } from './Icon/IconNewPage' ;
2325
2426interface PageHeadingProps {
2527 title : string ;
@@ -35,15 +37,21 @@ function CopyAsMarkdownButton() {
3537 const { asPath} = useRouter ( ) ;
3638 const [ copied , setCopied ] = useState ( false ) ;
3739
40+ const cleanPath = asPath . split ( / [ ? # ] / ) [ 0 ] ;
41+ const mdPath = cleanPath + '.md' ;
42+ const fullMdUrl =
43+ typeof window !== 'undefined'
44+ ? `${ window . location . origin } ${ mdPath } `
45+ : mdPath ;
46+
3847 useEffect ( ( ) => {
3948 if ( ! copied ) return ;
4049 const timer = setTimeout ( ( ) => setCopied ( false ) , 2000 ) ;
4150 return ( ) => clearTimeout ( timer ) ;
4251 } , [ copied ] ) ;
4352
4453 async function fetchPageBlob ( ) {
45- const cleanPath = asPath . split ( / [ ? # ] / ) [ 0 ] ;
46- const res = await fetch ( cleanPath + '.md' ) ;
54+ const res = await fetch ( mdPath ) ;
4755 if ( ! res . ok ) throw new Error ( 'Failed to fetch' ) ;
4856 const text = await res . text ( ) ;
4957 return new Blob ( [ text ] , { type : 'text/plain' } ) ;
@@ -62,17 +70,89 @@ function CopyAsMarkdownButton() {
6270 }
6371
6472 return (
65- < Button onClick = { handleCopy } className = "text-sm py-1 px-3" >
66- < IconCopy className = "w-3.5 h-3.5 me-1.5" />
67- { copied ? (
68- 'Copied!'
69- ) : (
70- < >
71- < span className = "hidden sm:inline" > Copy page</ span >
72- < span className = "sm:hidden" > Copy</ span >
73- </ >
74- ) }
75- </ Button >
73+ < div className = "relative inline-flex items-center rounded-full text-primary dark:text-primary-dark shadow-secondary-button-stroke dark:shadow-secondary-button-stroke-dark" >
74+ < button
75+ onMouseDown = { ( e ) => {
76+ e . preventDefault ( ) ;
77+ e . stopPropagation ( ) ;
78+ } }
79+ onClick = { handleCopy }
80+ disabled = { copied }
81+ className = "text-sm font-bold leading-tight py-1 p-3 inline-flex items-center rounded-s-full outline-none hover:bg-gray-40/5 active:bg-gray-40/10 hover:dark:bg-gray-60/2 active:dark:bg-gray-60/10 disabled:opacity-50 disabled:cursor-not-allowed" >
82+ < IconCopy className = "w-3.5 h-3.5 me-1.5" />
83+ { copied ? (
84+ 'Copied!'
85+ ) : (
86+ < >
87+ < span className = "hidden sm:inline" > Copy page</ span >
88+ < span className = "sm:hidden" > Copy</ span >
89+ </ >
90+ ) }
91+ </ button >
92+
93+ < span
94+ className = "w-px h-4 bg-gray-40/30 dark:bg-gray-60/30"
95+ aria-hidden = "true"
96+ />
97+
98+ { /* @ts -ignore */ }
99+ < Menu >
100+ { /* @ts -ignore */ }
101+ < Menu . Button as = { React . Fragment } >
102+ < button
103+ onMouseDown = { ( e ) => {
104+ e . preventDefault ( ) ;
105+ e . stopPropagation ( ) ;
106+ } }
107+ className = "py-1 ps-2 pe-3 inline-flex items-center rounded-e-full outline-none hover:bg-gray-40/5 active:bg-gray-40/10 hover:dark:bg-gray-60/2 active:dark:bg-gray-60/10" >
108+ < IconChevron displayDirection = "down" className = "w-3.5 h-3.5" />
109+ </ button >
110+ </ Menu . Button >
111+
112+ { /* @ts -ignore */ }
113+ < Menu . Items
114+ transition
115+ className = "absolute end-0 top-full mt-2 p-1 w-48 rounded-lg bg-white dark:bg-wash-dark border border-border dark:border-border-dark shadow-sm z-50 focus:outline-none" >
116+ { /* @ts -ignore */ }
117+ < Menu . Item >
118+ < a
119+ href = { mdPath }
120+ target = "_blank"
121+ rel = "noreferrer"
122+ className = "w-full font-bold text-start px-4 py-0.5 text-sm rounded-md inline-flex items-center justify-between hover:bg-gray-40/5 active:bg-gray-40/10 hover:dark:bg-gray-60/2 active:dark:bg-gray-60/10" >
123+ View markdown
124+ < IconNewPage className = "w-3 h-3" />
125+ </ a >
126+ </ Menu . Item >
127+ { /* @ts -ignore */ }
128+ < Menu . Item >
129+ < a
130+ href = { `https://chatgpt.com/?q=${ encodeURIComponent (
131+ `Read from this URL: ${ fullMdUrl } and explain it to me.`
132+ ) } `}
133+ target = "_blank"
134+ rel = "noreferrer"
135+ className = "w-full font-bold text-start px-4 py-0.5 text-sm rounded-md inline-flex items-center justify-between hover:bg-gray-40/5 active:bg-gray-40/10 hover:dark:bg-gray-60/2 active:dark:bg-gray-60/10" >
136+ Open in ChatGPT
137+ < IconNewPage className = "w-3 h-3" />
138+ </ a >
139+ </ Menu . Item >
140+ { /* @ts -ignore */ }
141+ < Menu . Item >
142+ < a
143+ href = { `https://claude.ai/new?q=${ encodeURIComponent (
144+ `Read from this URL: ${ fullMdUrl } and explain it to me.`
145+ ) } `}
146+ target = "_blank"
147+ rel = "noreferrer"
148+ className = "w-full font-bold text-start px-4 py-0.5 text-sm rounded-md inline-flex items-center justify-between hover:bg-gray-40/5 active:bg-gray-40/10 hover:dark:bg-gray-60/2 active:dark:bg-gray-60/10" >
149+ Open in Claude
150+ < IconNewPage className = "w-3 h-3" />
151+ </ a >
152+ </ Menu . Item >
153+ </ Menu . Items >
154+ </ Menu >
155+ </ div >
76156 ) ;
77157}
78158
0 commit comments