dioxus_docs_kit/components/copy_page.rs
1use dioxus::prelude::*;
2use dioxus_free_icons::{Icon, icons::ld_icons::*};
3
4/// "Copy page" button for MDX documentation pages.
5///
6/// Copies the page's raw Markdown source to the clipboard — the "copy page for
7/// LLMs" pattern. On success it swaps to a "Copied" state (check icon) for ~2
8/// seconds, then reverts.
9///
10/// The Markdown is handed to the clipboard via `document::eval` argument
11/// passing (`eval.send`), never string-interpolated into the script, so
12/// backticks and quotes in the source can't break the JS.
13///
14/// # Props
15///
16/// - `content`: Raw Markdown source of the current page.
17///
18/// # Stable public classes
19///
20/// Carries `dk-copy-page` so theme presets can target the button.
21#[component]
22pub fn CopyPageButton(content: String) -> Element {
23 #[allow(unused_mut)]
24 let mut copied = use_signal(|| false);
25
26 rsx! {
27 button {
28 class: "dk-copy-page btn btn-ghost btn-sm gap-1.5 opacity-60 hover:opacity-100 transition-all duration-150 hover:bg-base-content/10 shrink-0",
29 title: "Copy page as Markdown",
30 onclick: move |_| {
31 #[cfg(target_arch = "wasm32")]
32 {
33 let content = content.clone();
34 spawn(async move {
35 // Send the Markdown into the script as an argument rather
36 // than interpolating it — the source contains backticks
37 // and quotes that would otherwise break the JS.
38 let eval = document::eval(
39 "const text = await dioxus.recv();\n\
40 await navigator.clipboard.writeText(text);",
41 );
42 let _ = eval.send(content);
43 copied.set(true);
44 gloo_timers::future::TimeoutFuture::new(2000).await;
45 copied.set(false);
46 });
47 }
48 },
49 if copied() {
50 Icon { class: "size-4 text-success", icon: LdCheck }
51 span { class: "text-xs", "Copied!" }
52 } else {
53 Icon { class: "size-4", icon: LdCopy }
54 span { class: "text-xs", "Copy page" }
55 }
56 }
57 }
58}