@@ -58,22 +58,26 @@ export function OpenDocsAssistant(props: { type: AIActionType; trademark: boolea
58
58
const useCopiedStore = create < {
59
59
copied : boolean ;
60
60
setCopied : ( copied : boolean ) => void ;
61
+ loading : boolean ;
62
+ setLoading : ( loading : boolean ) => void ;
61
63
} > ( ( set ) => ( {
62
64
copied : false ,
63
65
setCopied : ( copied : boolean ) => set ( { copied } ) ,
66
+ loading : false ,
67
+ setLoading : ( loading : boolean ) => set ( { loading } ) ,
64
68
} ) ) ;
65
69
66
70
/**
67
71
* Copies the markdown version of the page to the clipboard.
68
72
*/
69
73
export function CopyMarkdown ( props : {
70
- markdown : string ;
74
+ markdownPageUrl : string ;
71
75
type : AIActionType ;
72
76
isDefaultAction ?: boolean ;
73
77
} ) {
74
- const { markdown , type, isDefaultAction } = props ;
78
+ const { markdownPageUrl , type, isDefaultAction } = props ;
75
79
const language = useLanguage ( ) ;
76
- const { copied, setCopied } = useCopiedStore ( ) ;
80
+ const { copied, setCopied, loading , setLoading } = useCopiedStore ( ) ;
77
81
const timeoutRef = useRef < ReturnType < typeof setTimeout > | null > ( null ) ;
78
82
79
83
// Close the dropdown menu manually after the copy button is clicked
@@ -88,6 +92,16 @@ export function CopyMarkdown(props: {
88
92
document . dispatchEvent ( new KeyboardEvent ( 'keydown' , { key : 'Escape' , bubbles : true } ) ) ;
89
93
} ;
90
94
95
+ // Fetch the markdown from the page
96
+ const fetchMarkdown = async ( ) => {
97
+ setLoading ( true ) ;
98
+
99
+ return fetch ( markdownPageUrl )
100
+ . then ( ( res ) => res . text ( ) )
101
+ . finally ( ( ) => setLoading ( false ) ) ;
102
+ } ;
103
+
104
+ // Reset the copied state when the component unmounts
91
105
useEffect ( ( ) => {
92
106
return ( ) => {
93
107
if ( timeoutRef . current ) {
@@ -96,21 +110,17 @@ export function CopyMarkdown(props: {
96
110
} ;
97
111
} , [ ] ) ;
98
112
99
- const onClick = ( e : React . MouseEvent ) => {
113
+ const onClick = async ( e : React . MouseEvent ) => {
100
114
// Prevent default behavior for non-default actions to avoid closing the dropdown.
101
115
// This allows showing transient UI (e.g., a "copied" state) inside the menu item.
102
116
// Default action buttons are excluded from this behavior.
103
117
if ( ! isDefaultAction ) {
104
118
e . preventDefault ( ) ;
105
119
}
106
120
107
- // Cancel any pending timeout
108
- if ( timeoutRef . current ) {
109
- clearTimeout ( timeoutRef . current ) ;
110
- }
121
+ const markdown = await fetchMarkdown ( ) ;
111
122
112
123
navigator . clipboard . writeText ( markdown ) ;
113
-
114
124
setCopied ( true ) ;
115
125
116
126
// Reset the copied state after 2 seconds
@@ -132,6 +142,7 @@ export function CopyMarkdown(props: {
132
142
shortLabel = { copied ? tString ( language , 'code_copied' ) : tString ( language , 'code_copy' ) }
133
143
description = { tString ( language , 'copy_page_markdown' ) }
134
144
onClick = { onClick }
145
+ loading = { loading }
135
146
/>
136
147
) ;
137
148
}
@@ -149,7 +160,7 @@ export function ViewAsMarkdown(props: { markdownPageUrl: string; type: AIActionT
149
160
icon = { < MarkdownIcon className = "size-4 fill-current" /> }
150
161
label = { tString ( language , 'view_page_markdown' ) }
151
162
description = { tString ( language , 'view_page_plaintext' ) }
152
- href = { ` ${ markdownPageUrl } .md` }
163
+ href = { markdownPageUrl }
153
164
/>
154
165
) ;
155
166
}
@@ -200,21 +211,24 @@ function AIActionWrapper(props: {
200
211
description ?: string ;
201
212
href ?: string ;
202
213
disabled ?: boolean ;
214
+ loading ?: boolean ;
203
215
} ) {
204
- const { type, icon, label, shortLabel, onClick, href, description, disabled } = props ;
216
+ const { type, icon, label, shortLabel, onClick, href, description, disabled, loading } = props ;
205
217
206
218
if ( type === 'button' ) {
207
219
return (
208
220
< Button
209
- icon = { icon }
221
+ icon = {
222
+ loading ? < Icon icon = "spinner-third" className = "size-4 animate-spin" /> : icon
223
+ }
210
224
size = "xsmall"
211
225
variant = "secondary"
212
226
label = { shortLabel || label }
213
227
className = "hover:!scale-100 !shadow-none !rounded-r-none border-r-0 bg-tint-base text-sm"
214
228
onClick = { onClick }
215
229
href = { href }
216
230
target = { href ? '_blank' : undefined }
217
- disabled = { disabled }
231
+ disabled = { disabled || loading }
218
232
/>
219
233
) ;
220
234
}
0 commit comments