Skip to content

Commit 896d89e

Browse files
Merge branch 'production' into patricia/pcx-fix-turnstile-faq
2 parents 95f0002 + e7a514b commit 896d89e

File tree

19 files changed

+828
-23
lines changed

19 files changed

+828
-23
lines changed
386 KB
Loading

src/components/CopyPageButton.tsx

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ import {
1010
} from "@floating-ui/react";
1111
import { useState } from "react";
1212
import {
13-
PiDotsThreeOutlineFill,
14-
PiClipboardTextLight,
13+
PiTriangleFill,
14+
PiCopyDuotone,
1515
PiArrowSquareOutLight,
1616
PiCheckCircleLight,
1717
PiXCircleLight,
1818
PiChatCircleLight,
1919
} from "react-icons/pi";
20+
import ClaudeIcon from "./icons/ClaudeIcon";
21+
import ChatGPTIcon from "./icons/ChatGPTIcon";
2022
import { track } from "~/util/zaraz";
2123

2224
type CopyState = "idle" | "success" | "error";
@@ -56,6 +58,17 @@ export default function CopyPageButton() {
5658
window.open(docsAIUrl, "_blank");
5759
};
5860

61+
const handleExternalAI = (url: string, vendor: string) => {
62+
const externalAIURL = url;
63+
const indexMdUrl = new URL("index.md", window.location.href).toString();
64+
const prompt = `Read this page from the Cloudflare docs: ${encodeURIComponent(indexMdUrl)} and answer questions about the content.`;
65+
track("clicked copy page button", {
66+
value: "docs ai",
67+
label: vendor,
68+
});
69+
window.open(`${externalAIURL}${prompt}`, "_blank");
70+
};
71+
5972
const handleCopyMarkdown = async () => {
6073
const markdownUrl = new URL("index.md", window.location.href).toString();
6174
try {
@@ -88,18 +101,25 @@ export default function CopyPageButton() {
88101
};
89102

90103
const options = [
91-
{
92-
label: "Copy Page as Markdown",
93-
description: "Copy the raw Markdown content to clipboard",
94-
icon: PiClipboardTextLight,
95-
onClick: handleCopyMarkdown,
96-
},
97104
{
98105
label: "View Page as Markdown",
99106
description: "Open the Markdown file in a new tab",
100107
icon: PiArrowSquareOutLight,
101108
onClick: handleViewMarkdown,
102109
},
110+
{
111+
label: "Open in Claude",
112+
description: "Ask Claude about this page",
113+
icon: ClaudeIcon,
114+
onClick: () => handleExternalAI("https://claude.ai/new?q=", "claude"),
115+
},
116+
{
117+
label: "Open in ChatGPT",
118+
description: "Ask ChatGPT about this page",
119+
icon: ChatGPTIcon,
120+
onClick: () =>
121+
handleExternalAI("https://chat.openai.com/?prompt=", "chatgpt"),
122+
},
103123
{
104124
label: "Ask Docs AI",
105125
description: "Open our Docs AI assistant in a new tab",
@@ -129,21 +149,29 @@ export default function CopyPageButton() {
129149

130150
return (
131151
<>
132-
<span>Page options</span>
133-
<PiDotsThreeOutlineFill />
152+
<PiCopyDuotone />
153+
<span>Copy page</span>
134154
</>
135155
);
136156
};
137157

138158
return (
139159
<>
140-
<button
141-
ref={refs.setReference}
142-
{...getReferenceProps()}
143-
className="inline-flex min-h-8 min-w-32 cursor-pointer items-center justify-center gap-2 rounded-sm border border-(--sl-color-hairline) bg-transparent px-3 text-sm text-black hover:bg-(--sl-color-bg-nav)"
144-
>
145-
{getButtonContent()}
146-
</button>
160+
<div className="flex justify-end">
161+
<button
162+
onClick={handleCopyMarkdown}
163+
className="inline-flex min-h-8 min-w-32 cursor-pointer items-center justify-center gap-2 rounded-l-sm border border-(--sl-color-hairline) bg-transparent px-3 text-sm text-black transition-colors duration-300 hover:bg-[var(--color-cl1-gray-9)] dark:hover:bg-[var(--color-cl1-gray-2)]"
164+
>
165+
{getButtonContent()}
166+
</button>
167+
<button
168+
ref={refs.setReference}
169+
{...getReferenceProps()}
170+
className="inline-flex min-h-8 w-8 cursor-pointer items-center justify-center rounded-r-sm border-t border-r border-b border-(--sl-color-hairline) bg-transparent text-sm text-black transition-colors duration-300 hover:bg-[var(--color-cl1-gray-9)] dark:hover:bg-[var(--color-cl1-gray-2)]"
171+
>
172+
<PiTriangleFill className="rotate-180 text-xs" />
173+
</button>
174+
</div>
147175
{isOpen && (
148176
<FloatingPortal>
149177
<ul
@@ -156,7 +184,7 @@ export default function CopyPageButton() {
156184
<li key={label}>
157185
<button
158186
onClick={onClick}
159-
className="relative block w-full cursor-pointer bg-transparent px-3 py-2 text-left text-black no-underline hover:bg-(--sl-color-bg-nav)"
187+
className="relative block w-full cursor-pointer bg-transparent px-3 py-2 text-left text-black no-underline hover:bg-[var(--color-cl1-gray-9)] dark:hover:bg-[var(--color-cl1-gray-2)]"
160188
>
161189
<div className="flex items-center gap-2 text-sm">
162190
<Icon />
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export default function ChatGPTIcon({
2+
className = "",
3+
}: {
4+
className?: string;
5+
}) {
6+
return (
7+
<svg
8+
className={`h-4 w-4 ${className}`}
9+
data-testid="geist-icon"
10+
height="16"
11+
strokeLinejoin="round"
12+
viewBox="0 0 16 16"
13+
width="16"
14+
style={{ color: "currentcolor" }}
15+
>
16+
<path
17+
d="M14.9449 6.54871C15.3128 5.45919 15.1861 4.26567 14.5978 3.27464C13.7131 1.75461 11.9345 0.972595 10.1974 1.3406C9.42464 0.481584 8.3144 -0.00692594 7.15045 7.42132e-05C5.37487 -0.00392587 3.79946 1.1241 3.2532 2.79113C2.11256 3.02164 1.12799 3.72615 0.551837 4.72468C-0.339497 6.24071 -0.1363 8.15175 1.05451 9.45178C0.686626 10.5413 0.813308 11.7348 1.40162 12.7258C2.28637 14.2459 4.06498 15.0279 5.80204 14.6599C6.5743 15.5189 7.68504 16.0074 8.849 15.9999C10.6256 16.0044 12.2015 14.8754 12.7478 13.2069C13.8884 12.9764 14.873 12.2718 15.4491 11.2733C16.3394 9.75728 16.1357 7.84774 14.9454 6.54771L14.9449 6.54871ZM8.85001 14.9544C8.13907 14.9554 7.45043 14.7099 6.90468 14.2604C6.92951 14.2474 6.97259 14.2239 7.00046 14.2069L10.2293 12.3668C10.3945 12.2743 10.4959 12.1008 10.4949 11.9133V7.42173L11.8595 8.19925C11.8742 8.20625 11.8838 8.22025 11.8858 8.23625V11.9558C11.8838 13.6099 10.5263 14.9509 8.85001 14.9544ZM2.32133 12.2028C1.9651 11.5958 1.8369 10.8843 1.95902 10.1938C1.98284 10.2078 2.02489 10.2333 2.05479 10.2503L5.28366 12.0903C5.44733 12.1848 5.65003 12.1848 5.81421 12.0903L9.75604 9.84429V11.3993C9.75705 11.4153 9.74945 11.4308 9.73678 11.4408L6.47295 13.3004C5.01915 14.1264 3.1625 13.6354 2.32184 12.2028H2.32133ZM1.47155 5.24819C1.82626 4.64017 2.38619 4.17516 3.05305 3.93366C3.05305 3.96116 3.05152 4.00966 3.05152 4.04366V7.72424C3.05051 7.91124 3.15186 8.08475 3.31654 8.17725L7.25838 10.4228L5.89376 11.2003C5.88008 11.2093 5.86285 11.2108 5.84765 11.2043L2.58331 9.34327C1.13255 8.51426 0.63494 6.68272 1.47104 5.24869L1.47155 5.24819ZM12.6834 7.82274L8.74157 5.57669L10.1062 4.79968C10.1199 4.79068 10.1371 4.78918 10.1523 4.79568L13.4166 6.65522C14.8699 7.48373 15.3681 9.31827 14.5284 10.7523C14.1732 11.3593 13.6138 11.8243 12.9474 12.0663V8.27575C12.9489 8.08875 12.8481 7.91574 12.6839 7.82274H12.6834ZM14.0414 5.8057C14.0176 5.7912 13.9756 5.7662 13.9457 5.7492L10.7168 3.90916C10.5531 3.81466 10.3504 3.81466 10.1863 3.90916L6.24442 6.15521V4.60017C6.2434 4.58417 6.251 4.56867 6.26367 4.55867L9.52751 2.70063C10.9813 1.87311 12.84 2.36563 13.6781 3.80066C14.0323 4.40667 14.1605 5.11618 14.0404 5.8057H14.0414ZM5.50257 8.57726L4.13744 7.79974C4.12275 7.79274 4.11312 7.77874 4.11109 7.76274V4.04316C4.11211 2.38713 5.47368 1.0451 7.15197 1.0461C7.86189 1.0461 8.54902 1.2921 9.09476 1.74011C9.06993 1.75311 9.02737 1.77661 8.99899 1.79361L5.77012 3.63365C5.60493 3.72615 5.50358 3.89916 5.50459 4.08666L5.50257 8.57626V8.57726ZM6.24391 7.00022L7.99972 5.9997L9.75553 6.99972V9.00027L7.99972 10.0003L6.24391 9.00027V7.00022Z"
18+
fill="currentColor"
19+
/>
20+
</svg>
21+
);
22+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
export default function ClaudeIcon({ className = "" }: { className?: string }) {
2+
return (
3+
<svg
4+
width="16"
5+
height="16"
6+
viewBox="0 0 12 12"
7+
fill="none"
8+
xmlns="http://www.w3.org/2000/svg"
9+
className={`h-4 w-4 ${className}`}
10+
>
11+
<g clipPath="url(#clip0_2002_2)">
12+
<path
13+
fillRule="evenodd"
14+
clipRule="evenodd"
15+
d="M2.3545 7.9775L4.7145 6.654L4.7545 6.539L4.7145 6.475H4.6L4.205 6.451L2.856 6.4145L1.6865 6.366L0.5535 6.305L0.268 6.2445L0 5.892L0.0275 5.716L0.2675 5.5555L0.6105 5.5855L1.3705 5.637L2.5095 5.716L3.3355 5.7645L4.56 5.892H4.7545L4.782 5.8135L4.715 5.7645L4.6635 5.716L3.4845 4.918L2.2085 4.074L1.5405 3.588L1.1785 3.3425L0.9965 3.1115L0.9175 2.6075L1.2455 2.2465L1.686 2.2765L1.7985 2.307L2.245 2.65L3.199 3.388L4.4445 4.3045L4.627 4.4565L4.6995 4.405L4.709 4.3685L4.627 4.2315L3.9495 3.0085L3.2265 1.7635L2.9045 1.2475L2.8195 0.938C2.78711 0.819128 2.76965 0.696687 2.7675 0.5735L3.1415 0.067L3.348 0L3.846 0.067L4.056 0.249L4.366 0.956L4.867 2.0705L5.6445 3.5855L5.8725 4.0345L5.994 4.4505L6.0395 4.578H6.1185V4.505L6.1825 3.652L6.301 2.6045L6.416 1.257L6.456 0.877L6.644 0.422L7.0175 0.176L7.3095 0.316L7.5495 0.6585L7.516 0.8805L7.373 1.806L7.0935 3.2575L6.9115 4.2285H7.0175L7.139 4.1075L7.6315 3.4545L8.4575 2.4225L8.8225 2.0125L9.2475 1.5605L9.521 1.345H10.0375L10.4175 1.9095L10.2475 2.4925L9.7155 3.166L9.275 3.737L8.643 4.587L8.248 5.267L8.2845 5.322L8.3785 5.312L9.8065 5.009L10.578 4.869L11.4985 4.7115L11.915 4.9055L11.9605 5.103L11.7965 5.5065L10.812 5.7495L9.6575 5.9805L7.938 6.387L7.917 6.402L7.9415 6.4325L8.716 6.5055L9.047 6.5235H9.858L11.368 6.636L11.763 6.897L12 7.216L11.9605 7.4585L11.353 7.7685L10.533 7.574L8.6185 7.119L7.9625 6.9545H7.8715V7.0095L8.418 7.5435L9.421 8.4485L10.6755 9.6135L10.739 9.9025L10.578 10.13L10.408 10.1055L9.3055 9.277L8.88 8.9035L7.917 8.0935H7.853V8.1785L8.075 8.503L9.2475 10.2635L9.3085 10.8035L9.2235 10.98L8.9195 11.0865L8.5855 11.0255L7.8985 10.063L7.191 8.9795L6.6195 8.008L6.5495 8.048L6.2125 11.675L6.0545 11.86L5.69 12L5.3865 11.7695L5.2255 11.396L5.3865 10.658L5.581 9.696L5.7385 8.931L5.8815 7.981L5.9665 7.665L5.9605 7.644L5.8905 7.653L5.1735 8.6365L4.0835 10.109L3.2205 11.0315L3.0135 11.1135L2.655 10.9285L2.6885 10.5975L2.889 10.303L4.083 8.785L4.803 7.844L5.268 7.301L5.265 7.222H5.2375L2.066 9.28L1.501 9.353L1.2575 9.125L1.288 8.752L1.4035 8.6305L2.3575 7.9745L2.3545 7.9775Z"
16+
fill="currentColor"
17+
/>
18+
</g>
19+
<defs>
20+
<clipPath id="clip0_2002_2">
21+
<rect width="12" height="12" fill="white" />
22+
</clipPath>
23+
</defs>
24+
</svg>
25+
);
26+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
---
2+
import { z } from "astro:schema";
3+
import { Code } from "@astrojs/starlight/components";
4+
import Details from "~/components/Details.astro";
5+
6+
type Props = z.infer<typeof props>;
7+
8+
const props = z.object({
9+
name: z.string(),
10+
lora: z.boolean(),
11+
});
12+
13+
const { name } = props.parse(Astro.props);
14+
15+
const worker = `
16+
export default {
17+
async fetch(request, env, ctx): Promise<Response> {
18+
const resp = await env.AI.run("${name}", {
19+
encoding: "linear16",
20+
sample_rate: "16000"
21+
}, {
22+
websocket: true
23+
});
24+
return resp;
25+
},
26+
} satisfies ExportedHandler<Env>;
27+
`;
28+
29+
const deployWorker = `
30+
npx wrangler deploy
31+
`;
32+
33+
const clientScript = `
34+
const ws = new WebSocket('wss://<your-worker-url.com>');
35+
36+
ws.onopen = () => {
37+
console.log('Connected to WebSocket');
38+
39+
// Generate and send random audio bytes
40+
// You can replace this part with a function
41+
// that reads from your mic or other audio source
42+
const audioData = generateRandomAudio();
43+
ws.send(audioData);
44+
console.log('Audio data sent');
45+
};
46+
47+
ws.onmessage = (event) => {
48+
// Transcription will be received here
49+
// Add your custom logic to parse the data
50+
console.log('Received:', event.data);
51+
};
52+
53+
ws.onerror = (error) => {
54+
console.error('WebSocket error:', error);
55+
};
56+
57+
ws.onclose = () => {
58+
console.log('WebSocket closed');
59+
};
60+
61+
// Generate random audio data (1 second of noise at 44.1kHz, mono)
62+
function generateRandomAudio() {
63+
const sampleRate = 44100;
64+
const duration = 1;
65+
const numSamples = sampleRate * duration;
66+
const buffer = new ArrayBuffer(numSamples * 2);
67+
const view = new Int16Array(buffer);
68+
69+
for (let i = 0; i < numSamples; i++) {
70+
view[i] = Math.floor(Math.random() * 65536 - 32768);
71+
}
72+
73+
return buffer;
74+
}
75+
`;
76+
77+
---
78+
79+
<>
80+
<Details header="Step 1: Create a Worker that establishes a WebSocket connection">
81+
<Code code={worker} lang="ts" />
82+
</Details>
83+
84+
<Details header="Step 2: Deploy your Worker">
85+
<Code code={deployWorker} lang="sh" />
86+
</Details>
87+
88+
<Details header="Step 3: Write a client script to connect to your Worker and send audio">
89+
<Code code={clientScript} lang="js" />
90+
</Details>
91+
</>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
title: Fine-grained Permissioning for Access for Apps, IdPs, & Targets now in Public Beta
3+
description: Expanding Role-Based-Access-Control with resource-level controls in Cloudflare Zero Trust.
4+
products:
5+
- fundamentals
6+
date: 2025-10-02
7+
---
8+
9+
import { Aside } from '@astrojs/starlight/components';
10+
11+
12+
Fine-grained permissions for **Access Applications, Identity Providers (IdPs), and Targets** is now available in Public Beta. This expands our RBAC model beyond account & zone-scoped roles, enabling administrators to grant permissions scoped to individual resources.
13+
14+
### What's New
15+
- **[Access Applications](https://developers.cloudflare.com/cloudflare-one/applications/)**: Grant admin permissions to specific Access Applications.
16+
- **[Identity Providers](https://developers.cloudflare.com/cloudflare-one/identity/)**: Grant admin permissions to individual Identity Providers.
17+
- **[Targets](https://developers.cloudflare.com/cloudflare-one/applications/non-http/infrastructure-apps/#1-add-a-target)**: Grant admin rights to specific Targets
18+
19+
![Updated Permissions Policy UX](~/assets/images/changelog/fundamentals/2025-10-01-fine-grained-permissioning-ux.png)
20+
21+
<Aside>
22+
23+
During the public beta, members must also be assigned an account-scoped, read only role to view resources in the dashboard. This restriction will be lifted in a future release.
24+
- **Account Read Only** plus a fine-grained permission for a specific App, IdP, or Target
25+
- **Cloudflare Zero Trust Read Only** plus fine-grained permission for a specific App, IdP, or Target
26+
27+
</Aside>
28+
29+
For more info:
30+
31+
- [Get started with Cloudflare Permissioning](/fundamentals/manage-members/roles/)
32+
- [Manage Member Permissioning via the UI & API](/fundamentals/manage-members/manage)
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
---
2+
title: New Deepgram Flux model available on Workers AI
3+
description: Partner voice activity detection model
4+
date: 2025-10-02
5+
---
6+
7+
Deepgram's newest Flux model [`@cf/deepgram/flux`](/workers-ai/models/flux/) is now available on Workers AI, hosted directly on Cloudflare's infrastructure. We're excited to be a launch partner with Deepgram and offer their new Speech Recognition model built specifically for enabling voice agents. Check out [Deepgram's blog](https://deepgram.com/flux) for more details on the release.
8+
9+
The Flux model can be used in conjunction with Deepgram's speech-to-text model [`@cf/deepgram/nova-3`](/workers-ai/models/nova-3/) and text-to-speech model [`@cf/deepgram/aura-1`](/workers-ai/models/aura-1/) to build end-to-end voice agents. Having Deepgram on Workers AI takes advantage of our edge GPU infrastructure, for ultra low latency voice AI applications.
10+
11+
## Promotional Pricing
12+
For the month of October 2025, Deepgram's Flux model will be free to use on Workers AI. Official pricing will be announced soon and charged after the promotional pricing period ends on October 31, 2025. Check out the [model page](/workers-ai/models/flux/) for pricing details in the future.
13+
14+
15+
## Example Usage
16+
17+
The new Flux model is WebSocket only as it requires live bi-directional streaming in order to recognize speech activity.
18+
19+
1. Create a worker that establishes a websocket connection with `@cf/deepgram/flux`
20+
21+
```js
22+
export default {
23+
async fetch(request, env, ctx): Promise<Response> {
24+
const resp = await env.AI.run("@cf/deepgram/flux", {
25+
encoding: "linear16",
26+
sample_rate: "16000"
27+
}, {
28+
websocket: true
29+
});
30+
return resp;
31+
},
32+
} satisfies ExportedHandler<Env>;
33+
```
34+
35+
2. Deploy your worker
36+
```bash
37+
npx wrangler deploy
38+
```
39+
40+
3. Write a client script to connect to your worker and start sending random audio bytes to it
41+
```js
42+
const ws = new WebSocket('wss://<your-worker-url.com>');
43+
44+
ws.onopen = () => {
45+
console.log('Connected to WebSocket');
46+
47+
// Generate and send random audio bytes
48+
// You can replace this part with a function
49+
// that reads from your mic or other audio source
50+
const audioData = generateRandomAudio();
51+
ws.send(audioData);
52+
console.log('Audio data sent');
53+
};
54+
55+
ws.onmessage = (event) => {
56+
// Transcription will be received here
57+
// Add your custom logic to parse the data
58+
console.log('Received:', event.data);
59+
};
60+
61+
ws.onerror = (error) => {
62+
console.error('WebSocket error:', error);
63+
};
64+
65+
ws.onclose = () => {
66+
console.log('WebSocket closed');
67+
};
68+
69+
// Generate random audio data (1 second of noise at 44.1kHz, mono)
70+
function generateRandomAudio() {
71+
const sampleRate = 44100;
72+
const duration = 1;
73+
const numSamples = sampleRate * duration;
74+
const buffer = new ArrayBuffer(numSamples * 2);
75+
const view = new Int16Array(buffer);
76+
77+
for (let i = 0; i < numSamples; i++) {
78+
view[i] = Math.floor(Math.random() * 65536 - 32768);
79+
}
80+
81+
return buffer;
82+
}
83+
```

0 commit comments

Comments
 (0)