diff --git a/.claude/agents/docs-reviewer.md b/.claude/agents/docs-reviewer.md
new file mode 100644
index 000000000..af0a856e4
--- /dev/null
+++ b/.claude/agents/docs-reviewer.md
@@ -0,0 +1,28 @@
+---
+name: docs-reviewer
+description: "Lean docs reviewer that dispatches reviews docs for a particular skill."
+model: opus
+color: cyan
+---
+
+You are a direct, critical, expert reviewer for React documentation.
+
+Your role is to use given skills to validate given doc pages for consistency, correctness, and adherence to established patterns.
+
+Complete this process:
+
+## Phase 1: Task Creation
+1. CRITICAL: Read the skill requested.
+2. Understand the skill's requirements.
+3. Create a task list to validate skills requirements.
+
+## Phase 2: Validate
+
+1. Read the docs files given.
+2. Review each file with the task list to verify.
+
+## Phase 3: Respond
+
+You must respond with a checklist of the issues you identified, and line number.
+
+DO NOT respond with passed validations, ONLY respond with the problems.
diff --git a/.claude/settings.json b/.claude/settings.json
new file mode 100644
index 000000000..111403183
--- /dev/null
+++ b/.claude/settings.json
@@ -0,0 +1,32 @@
+{
+ "skills": {
+ "suggest": [
+ {
+ "pattern": "src/content/learn/**/*.md",
+ "skill": "docs-writer-learn"
+ },
+ {
+ "pattern": "src/content/reference/**/*.md",
+ "skill": "docs-writer-reference"
+ }
+ ]
+ },
+ "permissions": {
+ "allow": [
+ "Skill(docs-voice)",
+ "Skill(docs-components)",
+ "Skill(docs-sandpack)",
+ "Skill(docs-rsc-sandpack)",
+ "Skill(docs-writer-learn)",
+ "Skill(docs-writer-reference)",
+ "Bash(yarn lint:*)",
+ "Bash(yarn lint-heading-ids:*)",
+ "Bash(yarn lint:fix:*)",
+ "Bash(yarn tsc:*)",
+ "Bash(yarn check-all:*)",
+ "Bash(yarn fix-headings:*)",
+ "Bash(yarn deadlinks:*)",
+ "Bash(yarn prettier:diff:*)"
+ ]
+ }
+}
diff --git a/.claude/skills/docs-components/SKILL.md b/.claude/skills/docs-components/SKILL.md
new file mode 100644
index 000000000..4b75f27a1
--- /dev/null
+++ b/.claude/skills/docs-components/SKILL.md
@@ -0,0 +1,518 @@
+---
+name: docs-components
+description: Comprehensive MDX component patterns (Note, Pitfall, DeepDive, Recipes, etc.) for all documentation types. Authoritative source for component usage, examples, and heading conventions.
+---
+
+# MDX Component Patterns
+
+## Quick Reference
+
+### Component Decision Tree
+
+| Need | Component |
+|------|-----------|
+| Helpful tip or terminology | `
Likes: {count}
+
+
+ {message}
+
+
);
}
diff --git a/src/components/PageHeading.tsx b/src/components/PageHeading.tsx
index ee92f5e55..f2ac2606d 100644
--- a/src/components/PageHeading.tsx
+++ b/src/components/PageHeading.tsx
@@ -14,8 +14,15 @@ import Tag from 'components/Tag';
import {H1} from './MDX/Heading';
import type {RouteTag, RouteItem} from './Layout/getRouteMeta';
import * as React from 'react';
+import {useState, useEffect} from 'react';
+import {useRouter} from 'next/router';
import {IconCanary} from './Icon/IconCanary';
import {IconExperimental} from './Icon/IconExperimental';
+<<<<<<< HEAD
+=======
+import {IconCopy} from './Icon/IconCopy';
+import {Button} from './Button';
+>>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a
interface PageHeadingProps {
title: string;
@@ -27,6 +34,51 @@ interface PageHeadingProps {
breadcrumbs: RouteItem[];
}
+function CopyAsMarkdownButton() {
+ const {asPath} = useRouter();
+ const [copied, setCopied] = useState(false);
+
+ useEffect(() => {
+ if (!copied) return;
+ const timer = setTimeout(() => setCopied(false), 2000);
+ return () => clearTimeout(timer);
+ }, [copied]);
+
+ async function fetchPageBlob() {
+ const cleanPath = asPath.split(/[?#]/)[0];
+ const res = await fetch(cleanPath + '.md');
+ if (!res.ok) throw new Error('Failed to fetch');
+ const text = await res.text();
+ return new Blob([text], {type: 'text/plain'});
+ }
+
+ async function handleCopy() {
+ try {
+ await navigator.clipboard.write([
+ // Don't wait for the blob, or Safari will refuse clipboard access
+ new ClipboardItem({'text/plain': fetchPageBlob()}),
+ ]);
+ setCopied(true);
+ } catch {
+ // Silently fail
+ }
+ }
+
+ return (
+
+ );
+}
+
function PageHeading({
title,
status,
@@ -37,7 +89,12 @@ function PageHeading({
return (
+
+
+
+
e.stopPropagation()}
/>
setIsActive(true)}
/>
>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a
/>
);
}
@@ -76,9 +81,15 @@ export default function Image() {
ุจู
ุฌุฑุฏ ุฃู ูุชู
ุชุตููุฑ ุงูู
ููู ูุฃูู ู
ุฑุฉุ ูู
ููู ุชูุดูุท ุนู
ููุงุช ุงูุชุตููุฑ ุงูุฃุฎุฑู ุนู ุทุฑูู ุชุญุฏูุซ ุญุงูุชู ุจุงุณุชุฎุฏุงู
ุฏุงูุฉ [`set`](/reference/react/useState#setstate). ุชุญุฏูุซ ุญุงูุฉ ุงูู
ููู ุงูุฎุงุต ุจู ูุถุน ุชููุงุฆููุง ุนู
ููุฉ ุชุตููุฑ ูู ูุงุฆู
ุฉ ุงูุงูุชุธุงุฑ. (ูู
ููู ุชุฎูู ูุฐู ุงูุนู
ููุงุช ุนูู ุฃููุง ุทูุจุงุช ู
ู ุฒุจูู ุงูู
ุทุนู
ููุญุตูู ุนูู ุงูุดุงู ุฃู ุงูุญูููุงุช ุฃู ุฃู ุดูุก ุขุฎุฑ ุจุนุฏ ุทูุจู ุงูุฃููุ ุงุนุชู
ุงุฏูุง ุนูู ุญุงูุฉ ุนุทุดู ุฃู ุฌูุนู.)
>>>>>> abe931a8cb3aee3e8b15ef7e187214789164162a
/>
);
}
@@ -130,8 +150,13 @@ img { margin: 0 10px 10px 0; }
| {food.name} | diff --git a/src/content/learn/state-a-components-memory.md b/src/content/learn/state-a-components-memory.md index 0efe1191d..0637dd173 100644 --- a/src/content/learn/state-a-components-memory.md +++ b/src/content/learn/state-a-components-memory.md @@ -40,14 +40,14 @@ export default function Gallery() { Next