Skip to content

Commit c0bb361

Browse files
committed
[Docs Lab] Add Agents toolkit section to right sidebar
Add a new 'Agents toolkit' section in the RHS sidebar that surfaces AI/agent-related actions (setup your agent, copy page link, copy/view as Markdown, open in Claude, open in ChatGPT) directly alongside the table of contents, above the existing feedback prompt.
1 parent cea57cf commit c0bb361

2 files changed

Lines changed: 146 additions & 1 deletion

File tree

src/components/AgentsToolkit.tsx

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import {
2+
PiCopyDuotone,
3+
PiArrowSquareOutLight,
4+
PiLinkLight,
5+
PiPlugsConnectedLight,
6+
PiCheckCircleLight,
7+
} from "react-icons/pi";
8+
import { useState } from "react";
9+
import ClaudeIcon from "./icons/ClaudeIcon";
10+
import ChatGPTIcon from "./icons/ChatGPTIcon";
11+
import { track } from "~/util/zaraz";
12+
13+
type CopyFeedback = { key: string; state: "success" } | null;
14+
15+
export default function AgentsToolkit() {
16+
const [copyFeedback, setCopyFeedback] = useState<CopyFeedback>(null);
17+
18+
const showFeedback = (key: string) => {
19+
setCopyFeedback({ key, state: "success" });
20+
setTimeout(() => setCopyFeedback(null), 1500);
21+
};
22+
23+
const handleCopyPageLink = async () => {
24+
try {
25+
await navigator.clipboard.writeText(window.location.href);
26+
track("agents toolkit clicked", { value: "copy page link" });
27+
showFeedback("copy-link");
28+
} catch (error) {
29+
console.error("Failed to copy page link:", error);
30+
}
31+
};
32+
33+
const handleCopyMarkdown = async () => {
34+
const markdownUrl = new URL("index.md", window.location.href).toString();
35+
try {
36+
const clipboardItem = new ClipboardItem({
37+
["text/plain"]: fetch(markdownUrl)
38+
.then((r) => r.text())
39+
.then((t) => new Blob([t], { type: "text/plain" }))
40+
.catch((e) => {
41+
throw new Error(`Received ${e.message} for ${markdownUrl}`);
42+
}),
43+
});
44+
await navigator.clipboard.write([clipboardItem]);
45+
track("agents toolkit clicked", { value: "copy markdown" });
46+
showFeedback("copy-md");
47+
} catch (error) {
48+
console.error("Failed to copy Markdown:", error);
49+
}
50+
};
51+
52+
const handleViewMarkdown = () => {
53+
const markdownUrl = new URL("index.md", window.location.href).toString();
54+
track("agents toolkit clicked", { value: "view markdown" });
55+
window.open(markdownUrl, "_blank");
56+
};
57+
58+
const handleExternalAI = (url: string, vendor: string) => {
59+
const indexMdUrl = new URL("index.md", window.location.href).toString();
60+
const prompt = `Read this page from the Cloudflare docs: ${encodeURIComponent(indexMdUrl)} and answer questions about the content.`;
61+
track("agents toolkit clicked", { value: `${vendor} ai` });
62+
window.open(`${url}${prompt}`, "_blank");
63+
};
64+
65+
const handleViewAIOptions = () => {
66+
track("agents toolkit clicked", { value: "view ai options" });
67+
window.open("/style-guide/ai-tooling/", "_blank");
68+
};
69+
70+
const options = [
71+
{
72+
key: "ai-options",
73+
label: "Setup your agent",
74+
icon: PiPlugsConnectedLight,
75+
onClick: handleViewAIOptions,
76+
},
77+
{
78+
key: "copy-link",
79+
label: "Copy page link",
80+
icon: PiLinkLight,
81+
onClick: handleCopyPageLink,
82+
},
83+
{
84+
key: "copy-md",
85+
label: "Copy as Markdown",
86+
icon: PiCopyDuotone,
87+
onClick: handleCopyMarkdown,
88+
},
89+
{
90+
key: "view-md",
91+
label: "View as Markdown",
92+
icon: PiArrowSquareOutLight,
93+
onClick: handleViewMarkdown,
94+
},
95+
{
96+
key: "claude",
97+
label: "Open in Claude",
98+
icon: ClaudeIcon,
99+
onClick: () => handleExternalAI("https://claude.ai/new?q=", "claude"),
100+
},
101+
{
102+
key: "chatgpt",
103+
label: "Open in ChatGPT",
104+
icon: ChatGPTIcon,
105+
onClick: () =>
106+
handleExternalAI("https://chat.openai.com/?prompt=", "chatgpt"),
107+
},
108+
];
109+
110+
return (
111+
<div>
112+
<h3 className="mb-3 mt-0 text-[11px] font-semibold tracking-widest text-[var(--sl-color-text-accent)] uppercase">
113+
Agents toolkit
114+
</h3>
115+
<ul className="m-0 flex list-none flex-col gap-0 p-0">
116+
{options.map(({ key, label, icon: Icon, onClick }) => {
117+
const justCopied =
118+
copyFeedback?.key === key &&
119+
copyFeedback.state === "success";
120+
121+
return (
122+
<li key={key} className="m-0 p-0">
123+
<button
124+
onClick={onClick}
125+
className="group flex w-full cursor-pointer items-center gap-2.5 rounded-sm border-0 bg-transparent px-0 py-1 text-[13px] text-[var(--sl-color-gray-2)] shadow-none transition-colors duration-150 ease-out hover:text-[var(--sl-color-white)] focus-visible:ring-2 focus-visible:ring-[var(--sl-color-text-accent)] focus-visible:outline-none"
126+
>
127+
{justCopied ? (
128+
<PiCheckCircleLight className="h-3.5 w-3.5 shrink-0 text-green-500" />
129+
) : (
130+
<Icon className="h-3.5 w-3.5 shrink-0" />
131+
)}
132+
<span>{justCopied ? "Copied!" : label}</span>
133+
</button>
134+
</li>
135+
);
136+
})}
137+
</ul>
138+
</div>
139+
);
140+
}

src/components/overrides/TableOfContents.astro

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import Default from "@astrojs/starlight/components/TableOfContents.astro";
33
import { Icon } from "@astrojs/starlight/components";
44
import FeedbackPrompt from "../FeedbackPrompt.tsx";
5+
import AgentsToolkit from "../AgentsToolkit.tsx";
56
67
const tags = Astro.locals.starlightRoute.entry.data.tags;
78
const showFeedback = Astro.locals.starlightRoute.entry.data.feedback;
@@ -37,8 +38,12 @@ const { editUrl } = Astro.locals.starlightRoute;
3738
{
3839
(showFeedback || showActions) && (
3940
<div class="mt-6 flex flex-col gap-5 border-t border-[var(--sidebar-border)] pt-6">
41+
<div class="agents-toolkit-container">
42+
<AgentsToolkit client:idle />
43+
</div>
44+
4045
{showFeedback && (
41-
<div class="feedback-container">
46+
<div class="feedback-container border-t border-[var(--sidebar-border)] pt-5">
4247
<FeedbackPrompt client:idle />
4348
</div>
4449
)}

0 commit comments

Comments
 (0)