Skip to content

Conversation

@fulopkovacs
Copy link
Owner

@fulopkovacs fulopkovacs commented Jan 4, 2026

  • Rename a query param
  • Add a button to copy the currently viewed article's link

Summary by CodeRabbit

  • New Features

    • Added copy-to-clipboard button for article links with visual confirmation, integrated near article content.
  • Improvements

    • Switched tutorial navigation to use an "article" query parameter instead of "step".
    • Article link encoding now lowercases titles before URL encoding for consistent sharing.
    • Updated tutorial flow and URL handling to use article-based parameters throughout without changing public APIs.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 4, 2026

Walkthrough

Rename tutorial search parameter and data usage from "step" to "article" across route validation and components; add an internal CopyArticleLinkButton that copies the current article link (shows confirmation) and integrate it into the tutorial floating window UI.

Changes

Cohort / File(s) Summary
Route & Search Param Rename
src/routes/_tutorial.tsx
Change validateSearch schema key from step to article (both optional strings); route now recognizes article query param.
Tutorial Window & Article data
src/components/tutorial/TutorialWindow.tsx
Import steps as articles and replace usages (steps(...), steps.map) with articles(...) / articles.map; rename search-derived variables to activeArticleFromSearch and initialize active item from articles[0].title.
Copy Article Link Feature
src/components/tutorial/TutorialWindow.tsx
Add internal CopyArticleLinkButton({ activeStep }) that constructs an article query link, copies it to clipboard, and shows a confirmation/check icon; add CheckIcon and LinkIcon imports and render button inside the floating window near article content.
Link Encoding & Query Param Update
src/components/tutorial/index.tsx
LinkToArticle now lowercases the article title before URL encoding and uses the article query param (replacing step).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a copy button for article links, which aligns with the core functionality introduced in TutorialWindow.tsx.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 08d07ab and 6b33aef.

📒 Files selected for processing (1)
  • src/components/tutorial/TutorialWindow.tsx
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Always ensure no formatting/linting issues exist in code - use pnpm check --fix to automatically fix issues

Files:

  • src/components/tutorial/TutorialWindow.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer functions over arrow functions for React components

Files:

  • src/components/tutorial/TutorialWindow.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer type over interface for type definitions

Files:

  • src/components/tutorial/TutorialWindow.tsx
🧠 Learnings (2)
📚 Learning: 2025-12-24T15:25:07.106Z
Learnt from: fulopkovacs
Repo: fulopkovacs/trytanstackdb.com PR: 22
File: src/components/TodoBoards.tsx:63-81
Timestamp: 2025-12-24T15:25:07.106Z
Learning: In Tailwind CSS v4, prefer canonical classes using bg-linear-to-* (e.g., bg-linear-to-b, bg-linear-to-t, bg-linear-to-r) over the older bg-gradient-to-* syntax. This aligns with Tailwind LSP's suggestCanonicalClasses rule. Apply across TSX files (e.g., src/components/*.tsx); replace occurrences of bg-gradient-to-* with the corresponding bg-linear-to-* equivalents and verify gradients visually.

Applied to files:

  • src/components/tutorial/TutorialWindow.tsx
📚 Learning: 2025-12-28T23:34:13.416Z
Learnt from: fulopkovacs
Repo: fulopkovacs/trytanstackdb.com PR: 32
File: src/components/HomeIntro.tsx:16-25
Timestamp: 2025-12-28T23:34:13.416Z
Learning: In Motion (motion/react), when configuring variant transitions, use delayChildren: stagger(x) to create per-child delays (e.g., delayChildren: stagger(0.2)). This is different from the older API that used a separate staggerChildren property. Apply this guidance to TSX files that declare motion variants (e.g., in src/components/**/*.tsx) and verify that stagger is imported from framer-motion. Ensure transitions reflect the intended stagger timing and that no conflicting delay values override the staggered effect.

Applied to files:

  • src/components/tutorial/TutorialWindow.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Deploy Preview
🔇 Additional comments (4)
src/components/tutorial/TutorialWindow.tsx (4)

3-3: LGTM! Import additions support the new copy-link feature.

The added imports (CheckIcon, LinkIcon, toast) are used in the CopyArticleLinkButton component, and the aliasing of steps as articles aligns with the PR's objective to rename the parameter from "step" to "article".

Also applies to: 12-13


428-428: LGTM! Proper component integration.

The CopyArticleLinkButton is correctly integrated with the appropriate prop.


443-443: LGTM! Consistent with the article aliasing.

The mapping change from steps.map to articles.map aligns with the renamed data source.


488-507: LGTM! Search parameter handling correctly updated.

The search parameter has been renamed from step to article, and the logic correctly decodes and validates the article parameter against the available articles. The fallback to articles[0].title is appropriate.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Jan 4, 2026

🚀 Preview Deployment

Status: ✅ Ready!

Preview URL: Open Preview

Commit: 6b33aefb4ecccf1133fc5b33f52f487c42b00a87

Built and deployed successfully

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/components/tutorial/TutorialWindow.tsx (2)

28-41: Add error handling for clipboard API.

The navigator.clipboard.writeText() call can fail in non-secure contexts or when permissions are denied. Consider adding error handling to improve user experience.

🔎 Proposed fix
-    navigator.clipboard.writeText(url.toString()).then(() => {
-      setCopied(true);
-      setTimeout(() => setCopied(false), 2000);
-    });
+    navigator.clipboard
+      .writeText(url.toString())
+      .then(() => {
+        setCopied(true);
+        setTimeout(() => setCopied(false), 2000);
+      })
+      .catch((err) => {
+        console.error("Failed to copy link:", err);
+        // Optionally show an error state to the user
+      });

422-422: Consider testing button positioning across different window sizes.

The button uses absolute positioning (top-2 right-4) which may overlap with scrollbar or content at certain window sizes. This appears intentional but worth verifying in different scenarios.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea52f33 and 898deb3.

📒 Files selected for processing (3)
  • src/components/tutorial/TutorialWindow.tsx
  • src/components/tutorial/index.tsx
  • src/routes/_tutorial.tsx
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Always ensure no formatting/linting issues exist in code - use pnpm check --fix to automatically fix issues

Files:

  • src/routes/_tutorial.tsx
  • src/components/tutorial/index.tsx
  • src/components/tutorial/TutorialWindow.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer functions over arrow functions for React components

Files:

  • src/routes/_tutorial.tsx
  • src/components/tutorial/index.tsx
  • src/components/tutorial/TutorialWindow.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer type over interface for type definitions

Files:

  • src/routes/_tutorial.tsx
  • src/components/tutorial/index.tsx
  • src/components/tutorial/TutorialWindow.tsx
🧠 Learnings (2)
📚 Learning: 2025-12-24T15:25:07.106Z
Learnt from: fulopkovacs
Repo: fulopkovacs/trytanstackdb.com PR: 22
File: src/components/TodoBoards.tsx:63-81
Timestamp: 2025-12-24T15:25:07.106Z
Learning: In Tailwind CSS v4, prefer canonical classes using bg-linear-to-* (e.g., bg-linear-to-b, bg-linear-to-t, bg-linear-to-r) over the older bg-gradient-to-* syntax. This aligns with Tailwind LSP's suggestCanonicalClasses rule. Apply across TSX files (e.g., src/components/*.tsx); replace occurrences of bg-gradient-to-* with the corresponding bg-linear-to-* equivalents and verify gradients visually.

Applied to files:

  • src/routes/_tutorial.tsx
  • src/components/tutorial/index.tsx
  • src/components/tutorial/TutorialWindow.tsx
📚 Learning: 2025-12-28T23:34:13.416Z
Learnt from: fulopkovacs
Repo: fulopkovacs/trytanstackdb.com PR: 32
File: src/components/HomeIntro.tsx:16-25
Timestamp: 2025-12-28T23:34:13.416Z
Learning: In Motion (motion/react), when configuring variant transitions, use delayChildren: stagger(x) to create per-child delays (e.g., delayChildren: stagger(0.2)). This is different from the older API that used a separate staggerChildren property. Apply this guidance to TSX files that declare motion variants (e.g., in src/components/**/*.tsx) and verify that stagger is imported from framer-motion. Ensure transitions reflect the intended stagger timing and that no conflicting delay values override the staggered effect.

Applied to files:

  • src/routes/_tutorial.tsx
  • src/components/tutorial/index.tsx
  • src/components/tutorial/TutorialWindow.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Deploy Preview
🔇 Additional comments (7)
src/components/tutorial/index.tsx (2)

101-104: LGTM!

The lowercase transformation is consistent with the decoding logic in TutorialWindow.tsx (lines 493-494), ensuring proper article matching.


107-111: LGTM!

The search parameter rename from step to article aligns with the route validation changes and PR objectives.

src/routes/_tutorial.tsx (1)

13-13: LGTM!

The search parameter rename is consistent with changes across the codebase.

src/components/tutorial/TutorialWindow.tsx (4)

3-3: LGTM!

The additional icon imports are necessary for the new copy link button feature.


437-437: LGTM!

The usage of articles.map is consistent with the import alias change.


482-501: LGTM!

The search parameter and effect logic correctly handle the renamed article parameter with proper lowercase normalization and article matching.


25-77: Convert to a regular function declaration.

Per coding guidelines, React components should use function declarations instead of arrow functions.

🔎 Proposed fix
-function CopyArticleLinkButton({ activeStep }: { activeStep: string | null }) {
+function CopyArticleLinkButton({ activeStep }: { activeStep: string | null }) {

Wait, it's already a function declaration. Let me re-check the code...

Actually, looking at line 25, it IS already a regular function declaration (function CopyArticleLinkButton...), not an arrow function. This is correct per the guidelines.

Based on coding guidelines, React components should use function declarations.

⛔ Skipped due to learnings
Learnt from: CR
Repo: fulopkovacs/trytanstackdb.com PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-29T13:20:08.388Z
Learning: Applies to **/*.{tsx,ts} : Prefer functions over arrow functions for React components

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/tutorial/TutorialWindow.tsx (1)

1-24: Critical: Fix import sorting to pass CI/CD.

The pipeline failure indicates imports are not sorted. Run pnpm check --fix to automatically organize imports and resolve the Biome linting error.

As per coding guidelines, always ensure no formatting/linting issues exist before pushing code.

🧹 Nitpick comments (1)
src/components/tutorial/TutorialWindow.tsx (1)

443-467: Optional: Consider renaming loop variable for consistency.

Since the data source is now articles, consider renaming the loop variable from step to article for better semantic alignment:

🔎 Proposed refactor
-              {articles.map((step) => (
+              {articles.map((article) => (
                <TabsContent
-                  key={step.title}
-                  value={step.title}
+                  key={article.title}
+                  value={article.title}
                  className="w-full overflow-x-hidden pb-3"
                >
                  <div className="fade-in animate-in prose dark:prose-invert prose-md prose-neutral prose-base rounded-lg">
-                    {<step.file />}
-                    {step.nextStepName && (
+                    {<article.file />}
+                    {article.nextStepName && (
                      <div className="mt-4">
                        <button
                          type="button"
                          onClick={() =>
-                            step.nextStepName &&
-                            handleStepChange(step.nextStepName)
+                            article.nextStepName &&
+                            handleStepChange(article.nextStepName)
                          }
                          className="text-primary underline hover:brightness-75 transition-colors cursor-pointer"
                        >
-                          Next: {step.nextStepName}
+                          Next: {article.nextStepName}
                        </button>
                      </div>
                    )}
                  </div>
                </TabsContent>
              ))}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 898deb3 and 08d07ab.

📒 Files selected for processing (1)
  • src/components/tutorial/TutorialWindow.tsx
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Always ensure no formatting/linting issues exist in code - use pnpm check --fix to automatically fix issues

Files:

  • src/components/tutorial/TutorialWindow.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer functions over arrow functions for React components

Files:

  • src/components/tutorial/TutorialWindow.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer type over interface for type definitions

Files:

  • src/components/tutorial/TutorialWindow.tsx
🧠 Learnings (2)
📚 Learning: 2025-12-24T15:25:07.106Z
Learnt from: fulopkovacs
Repo: fulopkovacs/trytanstackdb.com PR: 22
File: src/components/TodoBoards.tsx:63-81
Timestamp: 2025-12-24T15:25:07.106Z
Learning: In Tailwind CSS v4, prefer canonical classes using bg-linear-to-* (e.g., bg-linear-to-b, bg-linear-to-t, bg-linear-to-r) over the older bg-gradient-to-* syntax. This aligns with Tailwind LSP's suggestCanonicalClasses rule. Apply across TSX files (e.g., src/components/*.tsx); replace occurrences of bg-gradient-to-* with the corresponding bg-linear-to-* equivalents and verify gradients visually.

Applied to files:

  • src/components/tutorial/TutorialWindow.tsx
📚 Learning: 2025-12-28T23:34:13.416Z
Learnt from: fulopkovacs
Repo: fulopkovacs/trytanstackdb.com PR: 32
File: src/components/HomeIntro.tsx:16-25
Timestamp: 2025-12-28T23:34:13.416Z
Learning: In Motion (motion/react), when configuring variant transitions, use delayChildren: stagger(x) to create per-child delays (e.g., delayChildren: stagger(0.2)). This is different from the older API that used a separate staggerChildren property. Apply this guidance to TSX files that declare motion variants (e.g., in src/components/**/*.tsx) and verify that stagger is imported from framer-motion. Ensure transitions reflect the intended stagger timing and that no conflicting delay values override the staggered effect.

Applied to files:

  • src/components/tutorial/TutorialWindow.tsx
🪛 GitHub Actions: CI/CD
src/components/tutorial/TutorialWindow.tsx

[error] 1-1: pnpm check (Biome) failed. assist/source/organizeImports FIXABLE: The imports and exports are not sorted. Run Organize Imports to fix.

🔇 Additional comments (2)
src/components/tutorial/TutorialWindow.tsx (2)

428-428: LGTM: Good placement for the copy button.

The button is well-positioned at the top-right of the article content area with appropriate z-index layering.


488-507: LGTM: Search parameter handling is correct.

The migration from step to article search parameter is well-implemented:

  • Proper type checking and decoding
  • Case-insensitive matching aligns with URL encoding strategy
  • Falls back gracefully if article not found
  • Consistent with changes across the codebase

Comment on lines +26 to +83
function CopyArticleLinkButton({ activeStep }: { activeStep: string | null }) {
const [copied, setCopied] = useState(false);

const copyLink = useCallback(() => {
if (!activeStep) return;

const url = new URL(window.location.href);
url.searchParams.set(
"article",
encodeURIComponent(activeStep.toLowerCase()),
);

navigator.clipboard
.writeText(url.toString())
.then(() => {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
})
.catch((_) => {
toast.error("Couldn't copy the url");
});
}, [activeStep]);

return (
<Button
variant="outline"
size="sm"
onClick={copyLink}
className="absolute top-2 right-4 z-20"
title="Copy link to this article"
>
<AnimatePresence mode="wait" initial={false}>
{copied ? (
<motion.div
key="check"
initial={{ scale: 0.5, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.5, opacity: 0 }}
transition={{ duration: 0.15 }}
>
<CheckIcon className="h-4 w-4 text-green-600" />
</motion.div>
) : (
<motion.div
key="link"
initial={{ scale: 0.5, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.5, opacity: 0 }}
transition={{ duration: 0.15 }}
>
<LinkIcon className="h-4 w-4" />
</motion.div>
)}
</AnimatePresence>
{copied ? "Copied!" : "Copy link"}
</Button>
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd /tmp && find . -name "TutorialWindow.tsx" -type f 2>/dev/null | head -20

Repository: fulopkovacs/trytanstackdb.com

Length of output: 55


🏁 Script executed:

cat -n "src/components/tutorial/TutorialWindow.tsx" | sed -n '26,83p'

Repository: fulopkovacs/trytanstackdb.com

Length of output: 2090


🏁 Script executed:

# Search for clipboard-related utilities or polyfills
rg -i "clipboard|navigator\.clipboard" --type tsx --type ts -B 2 -A 2

Repository: fulopkovacs/trytanstackdb.com

Length of output: 100


🏁 Script executed:

# Check browserslist configuration
fd -e "browserslist" -o -e "package.json" | head -5 | xargs cat

Repository: fulopkovacs/trytanstackdb.com

Length of output: 189


🏁 Script executed:

# Check for linting/formatting issues in the file
cd src/components/tutorial && head -100 TutorialWindow.tsx

Repository: fulopkovacs/trytanstackdb.com

Length of output: 3232


🏁 Script executed:

# Check if there are any type utilities for props
rg "type.*Props|interface.*Props" src/components/tutorial/ -A 3

Repository: fulopkovacs/trytanstackdb.com

Length of output: 395


🏁 Script executed:

cat package.json | grep -A 10 -E "browserslist|version"

Repository: fulopkovacs/trytanstackdb.com

Length of output: 524


🏁 Script executed:

# Search for clipboard utilities/helpers
rg -i "clipboard|useClipboard" --type ts --type js -B 2 -A 5

Repository: fulopkovacs/trytanstackdb.com

Length of output: 585


🏁 Script executed:

# Check if there's a clipboard utility
fd -e "clipboard" -e "clip" src/

Repository: fulopkovacs/trytanstackdb.com

Length of output: 394


🏁 Script executed:

# Run pnpm check to identify formatting/linting issues
cd src/components/tutorial && cat TutorialWindow.tsx | head -100 | tail -70

Repository: fulopkovacs/trytanstackdb.com

Length of output: 2112


🏁 Script executed:

# Look for any type extraction patterns in the file
grep -n "^type\|^interface" src/components/tutorial/TutorialWindow.tsx

Repository: fulopkovacs/trytanstackdb.com

Length of output: 55


🏁 Script executed:

# Search for type definitions in TutorialWindow.tsx
grep -n "^type\|^interface" src/components/tutorial/TutorialWindow.tsx

Repository: fulopkovacs/trytanstackdb.com

Length of output: 55


🏁 Script executed:

# Check for browserslist configuration
cat .browserslistrc 2>/dev/null || cat package.json | grep -A 3 '"browserslist"'

Repository: fulopkovacs/trytanstackdb.com

Length of output: 55


🏁 Script executed:

# Look for clipboard-related code patterns in the entire codebase
rg "navigator\.clipboard" --type ts --type js

Repository: fulopkovacs/trytanstackdb.com

Length of output: 139


🏁 Script executed:

# Check the full file structure to identify all components and their type patterns
ast-grep --pattern 'function $_({ $$ }: { $$ }) { $$$ }' src/components/tutorial/TutorialWindow.tsx

Repository: fulopkovacs/trytanstackdb.com

Length of output: 55


🏁 Script executed:

# Verify if there's a linting config for type style preferences
cat tsconfig.json | grep -A 10 "strict\|jsx"

Repository: fulopkovacs/trytanstackdb.com

Length of output: 588


🏁 Script executed:

# Check if there are any linting/formatting issues by examining the full component
cat src/components/tutorial/TutorialWindow.tsx | head -90 | tail -20

Repository: fulopkovacs/trytanstackdb.com

Length of output: 827


🏁 Script executed:

# Search for all inline type definitions in the file to understand the pattern
rg -n "function.*\{.*:.*\}" src/components/tutorial/TutorialWindow.tsx

Repository: fulopkovacs/trytanstackdb.com

Length of output: 238


🏁 Script executed:

# Check if there's an eslint or prettier config
fd -type f "eslint\|prettier" . --max-depth 3

Repository: fulopkovacs/trytanstackdb.com

Length of output: 243


🏁 Script executed:

# Look at package.json to understand build/lint setup
cat package.json | grep -E '"scripts"|"devDependencies"' -A 20

Repository: fulopkovacs/trytanstackdb.com

Length of output: 1399


🏁 Script executed:

# Check for formatting/linting issues using Biome
cd src/components/tutorial && cat TutorialWindow.tsx | wc -l

Repository: fulopkovacs/trytanstackdb.com

Length of output: 76


🏁 Script executed:

# Look at the entire function component to check for style issues
sed -n '26,47p' src/components/tutorial/TutorialWindow.tsx

Repository: fulopkovacs/trytanstackdb.com

Length of output: 678


🏁 Script executed:

# Check if the project has any clipboard utility helpers that might handle this
find src -name "*.ts" -o -name "*.tsx" | head -20

Repository: fulopkovacs/trytanstackdb.com

Length of output: 738


🏁 Script executed:

# Verify tsconfig targets - check if Clipboard API is part of the target
cat tsconfig.json | grep -E "lib|target" -A 2

Repository: fulopkovacs/trytanstackdb.com

Length of output: 221


🏁 Script executed:

# Look for any hook or utility that wraps clipboard operations
rg "useClipboard|clipboard" src/ --max-count 5

Repository: fulopkovacs/trytanstackdb.com

Length of output: 139


🏁 Script executed:

# Verify if there are formatting/linting issues
cd src/components/tutorial && pnpm check TutorialWindow.tsx 2>&1 || true

Repository: fulopkovacs/trytanstackdb.com

Length of output: 919


🏁 Script executed:

# Check the biome config to understand formatting rules
cat biome.json 2>/dev/null || cat .biome.json 2>/dev/null || echo "No biome config found"

Repository: fulopkovacs/trytanstackdb.com

Length of output: 1172


🏁 Script executed:

# Look at how other components in the same directory define props
ls -la src/components/tutorial/*.tsx | head -5

Repository: fulopkovacs/trytanstackdb.com

Length of output: 351


🏁 Script executed:

# Check TutorialTableOfContents to see the extracted type pattern
sed -n '1,30p' src/components/tutorial/TutorialTableOfContents.tsx

Repository: fulopkovacs/trytanstackdb.com

Length of output: 726


Extract type definition and add explicit clipboard API check.

The inline prop type should be extracted to follow the type definition style guideline (prefer type over inline types). Additionally, explicitly check for navigator.clipboard availability before attempting to use it for clearer error handling:

🔎 Proposed changes
+type CopyArticleLinkButtonProps = {
+  activeStep: string | null;
+};
+
-function CopyArticleLinkButton({ activeStep }: { activeStep: string | null }) {
+function CopyArticleLinkButton({ activeStep }: CopyArticleLinkButtonProps) {
   const [copied, setCopied] = useState(false);

   const copyLink = useCallback(() => {
     if (!activeStep) return;
+
+    if (!navigator.clipboard) {
+      toast.error("Clipboard API not available");
+      return;
+    }

     const url = new URL(window.location.href);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function CopyArticleLinkButton({ activeStep }: { activeStep: string | null }) {
const [copied, setCopied] = useState(false);
const copyLink = useCallback(() => {
if (!activeStep) return;
const url = new URL(window.location.href);
url.searchParams.set(
"article",
encodeURIComponent(activeStep.toLowerCase()),
);
navigator.clipboard
.writeText(url.toString())
.then(() => {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
})
.catch((_) => {
toast.error("Couldn't copy the url");
});
}, [activeStep]);
return (
<Button
variant="outline"
size="sm"
onClick={copyLink}
className="absolute top-2 right-4 z-20"
title="Copy link to this article"
>
<AnimatePresence mode="wait" initial={false}>
{copied ? (
<motion.div
key="check"
initial={{ scale: 0.5, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.5, opacity: 0 }}
transition={{ duration: 0.15 }}
>
<CheckIcon className="h-4 w-4 text-green-600" />
</motion.div>
) : (
<motion.div
key="link"
initial={{ scale: 0.5, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.5, opacity: 0 }}
transition={{ duration: 0.15 }}
>
<LinkIcon className="h-4 w-4" />
</motion.div>
)}
</AnimatePresence>
{copied ? "Copied!" : "Copy link"}
</Button>
);
}
type CopyArticleLinkButtonProps = {
activeStep: string | null;
};
function CopyArticleLinkButton({ activeStep }: CopyArticleLinkButtonProps) {
const [copied, setCopied] = useState(false);
const copyLink = useCallback(() => {
if (!activeStep) return;
if (!navigator.clipboard) {
toast.error("Clipboard API not available");
return;
}
const url = new URL(window.location.href);
url.searchParams.set(
"article",
encodeURIComponent(activeStep.toLowerCase()),
);
navigator.clipboard
.writeText(url.toString())
.then(() => {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
})
.catch((_) => {
toast.error("Couldn't copy the url");
});
}, [activeStep]);
return (
<Button
variant="outline"
size="sm"
onClick={copyLink}
className="absolute top-2 right-4 z-20"
title="Copy link to this article"
>
<AnimatePresence mode="wait" initial={false}>
{copied ? (
<motion.div
key="check"
initial={{ scale: 0.5, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.5, opacity: 0 }}
transition={{ duration: 0.15 }}
>
<CheckIcon className="h-4 w-4 text-green-600" />
</motion.div>
) : (
<motion.div
key="link"
initial={{ scale: 0.5, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.5, opacity: 0 }}
transition={{ duration: 0.15 }}
>
<LinkIcon className="h-4 w-4" />
</motion.div>
)}
</AnimatePresence>
{copied ? "Copied!" : "Copy link"}
</Button>
);
}
🤖 Prompt for AI Agents
In src/components/tutorial/TutorialWindow.tsx around lines 26 to 83, the
component uses an inline prop type and assumes navigator.clipboard exists;
extract the prop type into a named type (e.g., type CopyArticleLinkButtonProps =
{ activeStep: string | null }) and use it for the component signature, and add
an explicit runtime check before using the Clipboard API (if
(!navigator.clipboard) { toast.error("Clipboard not supported"); return; }) so
the copyLink callback fails gracefully; keep the rest of the copy flow and error
handling unchanged and ensure the useCallback dependency array still includes
activeStep.

@fulopkovacs fulopkovacs merged commit eb7652c into main Jan 4, 2026
3 of 4 checks passed
@fulopkovacs fulopkovacs deleted the articles-share-button branch January 4, 2026 15:43
@coderabbitai coderabbitai bot mentioned this pull request Jan 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants