Skip to content

Conversation

@MananTank
Copy link
Member

@MananTank MananTank commented Sep 18, 2025


PR-Codex overview

This PR introduces a new persistTokenSelections prop for the SwapWidget, allowing developers to control whether token selections persist in local storage. It also enhances the BridgeNetworkSelector and updates various components to improve functionality and organization.

Detailed summary

  • Added persistTokenSelections prop to SwapWidget.
  • Updated BridgeNetworkSelector to use useBridgeSupportedChains hook.
  • Refactored LeftSection to use CurrencySelector for currency selection.
  • Introduced SwapWidgetPlayground for better widget customization.
  • Enhanced TokenSelector and CurrencySelector components.
  • Added bridgeFeatureCards to metadata.
  • Updated routing and UI components for the new bridge functionality.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features

    • SwapWidget gains a persistTokenSelections prop to opt out of saving token choices.
    • Added a Swap Widget Playground with live preview, generated code view, theme/currency/prefill/branding controls, and UI/Code tabs.
    • Playground is integrated into site navigation and feature cards under a new Bridge section.
  • Chores

    • Playground now uses bridge-supported chains and improved TypeScript formatting for generated snippets.
    • Added a reusable currency selector for widget configuration.

@vercel
Copy link

vercel bot commented Sep 18, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
docs-v2 Ready Ready Preview Comment Sep 19, 2025 0:30am
nebula Ready Ready Preview Comment Sep 19, 2025 0:30am
thirdweb_playground Ready Ready Preview Comment Sep 19, 2025 0:30am
thirdweb-www Ready Ready Preview Comment Sep 19, 2025 0:30am
wallet-ui Ready Ready Preview Comment Sep 19, 2025 0:30am

@linear
Copy link

linear bot commented Sep 18, 2025

@changeset-bot
Copy link

changeset-bot bot commented Sep 18, 2025

🦋 Changeset detected

Latest commit: 32980f8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
thirdweb Patch
@thirdweb-dev/nebula Patch
@thirdweb-dev/wagmi-adapter Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@MananTank MananTank marked this pull request as ready for review September 18, 2025 19:22
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 18, 2025

Walkthrough

Adds a SwapWidget playground and navigation, introduces bridge-supported chain APIs/components, a prop to toggle SwapWidget token-persistence, multiple new playground UI/code components, selector/hook refactors, utility removals, and minor formatting/plugin and import-path adjustments.

Changes

Cohort / File(s) Summary
SwapWidget persistence toggle
packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx, .changeset/honest-hands-clap.md
Adds persistTokenSelections?: boolean to SwapWidgetProps. Token persistence (read/write last-used tokens in localStorage) is gated by this flag; initial token selection and persistence effects respect it. Adds a patch changeset file.
Swap Widget playground components & types
apps/playground-web/src/app/bridge/swap-widget/components/*, apps/playground-web/src/app/bridge/swap-widget/components/types.ts
New playground components: LeftSection, RightSection, SwapWidgetPlayground, CodeGen, and exported SwapWidgetPlaygroundOptions type. RightSection renders live preview and lazy code generation.
Playground route, metadata, nav & homepage
apps/playground-web/src/app/bridge/swap-widget/page.tsx, apps/playground-web/src/app/data/pages-metadata.ts, apps/playground-web/src/app/navLinks.ts, apps/playground-web/src/app/page.tsx
Adds Swap Widget page (PageLayout + ThirdwebProvider) with metadata, adds a Bridge feature card and sidebar submenu, and inserts a Bridge section on the homepage.
Network selectors & bridge chains hook
apps/playground-web/src/components/blocks/NetworkSelectors.tsx, apps/playground-web/src/hooks/chains.ts, apps/playground-web/src/lib/env.ts
Introduces useBridgeSupportedChains() fetching bridge-supported chains, adds BridgeNetworkSelector (single-chain selector) replacing previous multi/single variants, and re-exports isProd from env. Removes useAllChainsData and MultiNetworkSelector.
Currency selector & TokenSelector changes
apps/playground-web/src/components/blocks/CurrencySelector.tsx, apps/playground-web/src/components/ui/TokenSelector.tsx
Adds CurrencySelector component. Removes addNativeTokenIfMissing prop/logic from TokenSelector; token list now strictly uses tokensQuery results (no synthetic native token injection).
Playground wiring & layout
apps/playground-web/src/app/bridge/swap-widget/page.tsx, apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx
New route and top-level playground container wiring Left/Right sections, syncing global theme into playground options and providing responsive layout.
Playground integrations: code & UI logic
apps/playground-web/src/app/bridge/swap-widget/components/code.tsx, apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx, apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx
CodeGen builds a TypeScript example string from options; LeftSection edits options (theme, prefill, tokens, chain, amount, branding); RightSection toggles UI/Code tabs and renders SwapWidget preview (uses persistTokenSelections={false} for preview).
Payments adjustments
apps/playground-web/src/app/payments/components/LeftSection.tsx, apps/playground-web/src/app/payments/components/CodeGen.tsx
Makes lockedWidget required (removes widget chooser), replaces currency select with CurrencySelector, and updates LoadingDots import to alias path.
Removed utilities / components
apps/playground-web/src/components/blocks/multi-select.tsx (deleted), apps/playground-web/src/lib/useShowMore.ts (deleted)
Removes the MultiSelect component and useShowMore hook (multi-select + incremental-show behavior deleted).
Code formatting plugin change
packages/ui/src/components/code/getCodeHtml.tsx
Replaces Babel Prettier plugin usage with static estree and typescript plugins and sets Prettier parser to "typescript" for formatting.
Small wiring and state adjustments
apps/playground-web/src/app/wallets/sign-in/button/connect-button-page.tsx, apps/playground-web/src/components/blocks/NetworkSelectors.tsx, apps/playground-web/src/app/payments/components/CodeGen.tsx
Minor state initialization change in connect-button page, NetworkSelectors renamed/rewired to bridge data, and import path aliasing for LoadingDots.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant RS as Playground RightSection
  participant SW as SwapWidget
  participant LS as localStorage

  Note over RS: UI Tab active -> Live preview
  RS->>SW: Render SwapWidget(props including persistTokenSelections=false)
  alt persistTokenSelections = false
    SW->>LS: Skip reading last-used tokens
    Note right of SW: Initial tokens come from prefill only (if present)
  else persistTokenSelections = true
    SW->>LS: Read last-used tokens
    SW->>SW: Determine initial sell/buy tokens (avoid same-token)
  end
  SW->>U: Render widget UI
  alt persist enabled
    SW-->>LS: Write last-used tokens on change
  else persist disabled
    SW-->>LS: Do not write
  end
Loading
sequenceDiagram
  autonumber
  participant U as User
  participant RS as RightSection
  participant CG as CodeGen (lazy)
  participant SW as SwapWidget

  U->>RS: Toggle Tab (UI / Code)
  alt UI tab
    RS->>SW: Render live preview (keyed by options)
    SW->>U: Show interactive widget
  else Code tab
    RS-->>CG: Lazy-load CodeClient
    CG->>CG: Generate TS snippet from options
    CG->>U: Display formatted code
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR body includes a useful PR-Codex overview and a detailed summary of changes, but it fails to populate the repository's required template sections: the "Notes for the reviewer" and "How to test" fields are left only as commented placeholders and contain no concrete instructions. Because the template is designated as required, missing these sections reduces reviewer efficiency and testability. For that reason the description check does not meet the repository's template requirements. Please populate the template's "Notes for the reviewer" and "How to test" sections with concise reviewer guidance and concrete test steps (how to run the playground app, the URL to visit such as /bridge/swap-widget, steps to verify the SwapWidget and the new persistTokenSelections behavior, and any unit/integration tests to run), and call out the public API change (persistTokenSelections) and any migration/compat notes. After updating the PR description, rerun CI if needed and notify reviewers.
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 (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title "[MNY-190] Playground: Add SwapWidget" is concise and accurately reflects the primary change in the changeset — adding a SwapWidget playground and related UI/components. It uses the issue tag, is specific to the main feature, and avoids noise like file lists or emojis. This makes it clear for a teammate scanning PR history what the primary change is.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch mny-190

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (2)
  • MNY-190: GraphQL Error (Code: 502) -
<title>502 Server Error</title>

Error: Server Error

The server encountered a temporary error and could not complete your request.

Please try again in 30 seconds.

  • TEAM-0000: Entity not found: Issue - Could not find referenced Issue.

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

@MananTank MananTank requested review from a team as code owners September 18, 2025 19:22
@github-actions github-actions bot added Playground Changes involving the Playground codebase. packages SDK Involves changes to the thirdweb SDK labels Sep 18, 2025
Copy link
Member Author

MananTank commented Sep 18, 2025


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge-queue - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions
Copy link
Contributor

github-actions bot commented Sep 18, 2025

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
thirdweb (esm) 63.96 KB (0%) 1.3 s (0%) 532 ms (+220.71% 🔺) 1.9 s
thirdweb (cjs) 361.44 KB (0%) 7.3 s (0%) 1.5 s (+19.61% 🔺) 8.8 s
thirdweb (minimal + tree-shaking) 5.73 KB (0%) 115 ms (0%) 251 ms (+2518.33% 🔺) 365 ms
thirdweb/chains (tree-shaking) 526 B (0%) 11 ms (0%) 109 ms (+2268.48% 🔺) 120 ms
thirdweb/react (minimal + tree-shaking) 19.14 KB (0%) 383 ms (0%) 155 ms (+2103.56% 🔺) 538 ms

Copy link
Contributor

@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: 3

Caution

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

⚠️ Outside diff range comments (1)
apps/playground-web/src/app/payments/components/CodeGen.tsx (1)

29-71: Generated snippet misses defineChain import (non‑custom chain path) and has unused import scaffolding

  • The template always calls defineChain(...) but only conditionally imports it; in the base-chain path there’s no import → broken snippet.
  • imports.chains and imports.wallets are unused; createWallet is never imported/used.

Apply this minimal fix to always import defineChain and drop unused scaffolding:

 function getCode(options: BridgeComponentsPlaygroundOptions) {
-  const imports = {
-    chains: [] as string[],
-    react: ["PayEmbed"] as string[],
-    thirdweb: [] as string[],
-    wallets: [] as string[],
-  };
+  const imports = {
+    react: ["PayEmbed"] as string[],
+    thirdweb: ["defineChain"] as string[],
+  };

-  // Check if we have a custom chain (not base chain which has id 8453)
-  const isCustomChain =
-    options.payOptions.buyTokenChain &&
-    options.payOptions.buyTokenChain.id !== 8453;
-
-  if (isCustomChain) {
-    // Add defineChain to imports if using a custom chain
-    imports.thirdweb.push("defineChain");
-  } else {
-    // Otherwise use the base chain
-    imports.chains.push("base");
-  }
-
-  imports.wallets.push("createWallet");
+  // (optional) add more thirdweb imports here as needed

   const componentName = (() => {
@@
-  imports.chains.push("defineChain");
+  // defineChain is already included above
🧹 Nitpick comments (16)
apps/playground-web/src/app/payments/components/CodeGen.tsx (1)

5-9: Prefer next/dynamic over React.lazy in App Router

React.lazy is not recommended in Next.js App Router. next/dynamic gives better SSR/streaming semantics.

Example:

-import { lazy, Suspense } from "react";
+import dynamic from "next/dynamic";
+import { Suspense } from "react";
 
-const CodeClient = lazy(() =>
-  import("../../../components/code/code.client").then((m) => ({
-    default: m.CodeClient,
-  })),
-);
+const CodeClient = dynamic(
+  () => import("../../../components/code/code.client").then((m) => m.CodeClient),
+  { ssr: false, loading: () => <CodeLoading /> },
+);
apps/playground-web/src/lib/env.ts (1)

1-3: Derive isProd via getVercelEnv() for consistency

Avoid duplicating env resolution logic and potential divergence between server/client bundles.

Apply:

-export const isProd =
-  (process.env.VERCEL_ENV || process.env.NEXT_PUBLIC_VERCEL_ENV) ===
-  "production";
+export const isProd = getVercelEnv() === "production";
packages/ui/src/components/code/getCodeHtml.tsx (2)

1-2: Support JS/JSX parsing with Prettier’s Babel plugin

Using the TypeScript parser for JS/JSX can misformat edge cases. Include the Babel plugin and select parser per language.

Apply:

-import estreePlugin from "prettier/plugins/estree";
-import typescriptPlugin from "prettier/plugins/typescript";
+import estreePlugin from "prettier/plugins/estree";
+import typescriptPlugin from "prettier/plugins/typescript";
+import babelPlugin from "prettier/plugins/babel";

And:

-    ? await format(code, {
-        parser: "typescript",
-        plugins: [estreePlugin, typescriptPlugin],
+    ? await format(code, {
+        parser:
+          lang === "js" || lang === "jsx" || lang === "javascript"
+            ? "babel"
+            : "typescript",
+        plugins: [estreePlugin, typescriptPlugin, babelPlugin],
         printWidth: 60,
       }).catch((e) => {

1-2: Consider lazy‑loading Prettier plugins to keep bundles lean

Top‑level static imports pull heavy plugins into any consumer bundle. Loading inside getCodeHtml defers cost to actual usage.

If desired, switch to:

-import estreePlugin from "prettier/plugins/estree";
-import typescriptPlugin from "prettier/plugins/typescript";
+// defer plugin loading inside getCodeHtml to reduce initial bundle size

And inside getCodeHtml:

-  const formattedCode = isPrettierSupportedLang(lang)
-    ? await format(code, {
+  const formattedCode = isPrettierSupportedLang(lang)
+    ? await (async () => {
+        const [{ default: estree }, { default: ts }, { default: babel }] =
+          await Promise.all([
+            import("prettier/plugins/estree"),
+            import("prettier/plugins/typescript"),
+            import("prettier/plugins/babel"),
+          ]);
+        return format(code, {
           parser:
             lang === "js" || lang === "jsx" || lang === "javascript"
               ? "babel"
               : "typescript",
-        plugins: [estreePlugin, typescriptPlugin],
+          plugins: [estree, ts, babel],
           printWidth: 60,
-      }).catch((e) => {
+        });
+      })().catch((e) => {
apps/playground-web/src/app/payments/components/LeftSection.tsx (3)

40-45: lockedWidget is now required: simplify effectiveWidget

With lockedWidget required, the fallback to payOptions.widget is redundant.

-  const effectiveWidget = props.lockedWidget || payOptions.widget || "buy";
+  const effectiveWidget = props.lockedWidget;

72-72: Remove unused _payModeId

This ID is no longer used after removing the widget selector UI; leaving it may trigger lint warnings.

-  const _payModeId = useId();

30-30: Avoid importing types from package build outputs

../../../../../../packages/thirdweb/dist/types/... is brittle. Import from the public API.

-import type { SupportedFiatCurrency } from "../../../../../../packages/thirdweb/dist/types/pay/convert/type";
+import type { SupportedFiatCurrency } from "thirdweb/pay/convert/type";
packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx (1)

158-165: Prop doc looks good; consider adding a stability tag

To comply with packages/thirdweb doc standards, add a custom tag (e.g., @beta or @public) to the new public prop’s TSDoc.

 /**
  * Whether to persist the token selections to localStorage so that if the user revisits the widget, the last used tokens are pre-selected.
  * The last used tokens do not override the tokens specified in the `prefill` prop
  *
  * @default true
+ * @beta
  */
 persistTokenSelections?: boolean;
apps/playground-web/src/app/bridge/swap-widget/page.tsx (2)

13-20: OG image icon likely mismatched (“payments”)

This page is under Bridge; consider using a Bridge‑specific icon to avoid mixed branding.

Apply:

 export const metadata = createMetadata({
   description: ogDescription,
   title,
   image: {
-    icon: "payments",
+    icon: "bridge",
     title,
   },
 });

22-35: Add explicit return type

Prefer explicit return types per repo guidelines.

-export default function Page() {
+export default function Page(): JSX.Element {
apps/playground-web/src/hooks/chains.ts (4)

20-35: Set React Query staleTime/cacheTime

Guideline default ≥ 60s. Current queries will refetch too eagerly.

   const query = useQuery({
     queryFn: async () => {
@@
     },
-    queryKey: ["all-chains"],
+    queryKey: ["all-chains"],
+    staleTime: 60_000,
+    cacheTime: 5 * 60_000,
   });

43-52: Type reuse opportunity for BridgeChain

If BridgeChain is exported in the SDK, reuse it; otherwise consider a local types.ts barrel for cross‑component reuse.

Would you like me to check if BridgeChain is exported from thirdweb so we can import it instead of redefining?


54-70: Header usage is fine; add basic network error guard

Minor: check res.ok before res.json() for clearer failures.

 async function fetchBridgeSupportedChainsFromApi() {
-  const res = await fetch(
+  const res = await fetch(
     `https://bridge.${isProd ? "thirdweb.com" : "thirdweb-dev.com"}/v1/chains`,
     {
       headers: {
         "x-client-id": THIRDWEB_CLIENT.clientId,
       },
     },
   );
-  const json = await res.json();
+  if (!res.ok) {
+    throw new Error(`Failed to fetch bridge chains: ${res.status}`);
+  }
+  const json = await res.json();

72-79: Configure cache settings on bridge chains query

Same caching guidance as above.

 export function useBridgeSupportedChains() {
   return useQuery({
     queryFn: async () => {
       return fetchBridgeSupportedChainsFromApi();
     },
     queryKey: ["bridge-supported-chains"],
+    staleTime: 60_000,
+    cacheTime: 5 * 60_000,
   });
 }
apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx (1)

20-52: Add explicit return type and optional className pass‑through

Return type per guidelines; optional className improves override ergonomics for this client component.

-export function SwapWidgetPlayground() {
+export function SwapWidgetPlayground(props: { className?: string }): JSX.Element {
@@
-  return (
-    <div className="relative flex flex-col-reverse gap-6 xl:min-h-[900px] xl:flex-row xl:gap-6">
+  return (
+    <div
+      className={cn(
+        "relative flex flex-col-reverse gap-6 xl:min-h-[900px] xl:flex-row xl:gap-6",
+        props.className,
+      )}
+    >

Note: add cn import and update the single call site (page.tsx) with no props change required.

apps/playground-web/src/app/bridge/swap-widget/components/code.tsx (1)

19-27: Fix language specification inconsistency.

The lang prop is set to "ts" but based on the generated code content (which includes JSX), it should be "tsx" for proper syntax highlighting.

-        <CodeClient className="grow" code={getCode(props.options)} lang="ts" />
+        <CodeClient className="grow" code={getCode(props.options)} lang="tsx" />
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4482692 and 3cd85bf.

📒 Files selected for processing (17)
  • .changeset/honest-hands-clap.md (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/components/code.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/components/types.ts (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/page.tsx (1 hunks)
  • apps/playground-web/src/app/data/pages-metadata.ts (2 hunks)
  • apps/playground-web/src/app/navLinks.ts (3 hunks)
  • apps/playground-web/src/app/page.tsx (2 hunks)
  • apps/playground-web/src/app/payments/components/CodeGen.tsx (1 hunks)
  • apps/playground-web/src/app/payments/components/LeftSection.tsx (2 hunks)
  • apps/playground-web/src/components/blocks/NetworkSelectors.tsx (2 hunks)
  • apps/playground-web/src/hooks/chains.ts (2 hunks)
  • apps/playground-web/src/lib/env.ts (1 hunks)
  • packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx (5 hunks)
  • packages/ui/src/components/code/getCodeHtml.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose

**/*.{ts,tsx}: Use explicit function declarations and explicit return types in TypeScript
Limit each file to one stateless, single‑responsibility function
Re‑use shared types from @/types where applicable
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics when possible
Prefer composition over inheritance; use utility types (Partial, Pick, etc.)
Lazy‑import optional features and avoid top‑level side‑effects to reduce bundle size

Files:

  • apps/playground-web/src/app/payments/components/CodeGen.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/types.ts
  • apps/playground-web/src/hooks/chains.ts
  • apps/playground-web/src/app/bridge/swap-widget/page.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx
  • apps/playground-web/src/lib/env.ts
  • packages/ui/src/components/code/getCodeHtml.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/src/app/bridge/swap-widget/components/code.tsx
  • apps/playground-web/src/app/payments/components/LeftSection.tsx
  • apps/playground-web/src/app/page.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx
  • apps/playground-web/src/components/blocks/NetworkSelectors.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)

Files:

  • apps/playground-web/src/app/payments/components/CodeGen.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/types.ts
  • apps/playground-web/src/hooks/chains.ts
  • apps/playground-web/src/app/bridge/swap-widget/page.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx
  • apps/playground-web/src/lib/env.ts
  • packages/ui/src/components/code/getCodeHtml.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/src/app/bridge/swap-widget/components/code.tsx
  • apps/playground-web/src/app/payments/components/LeftSection.tsx
  • apps/playground-web/src/app/page.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx
  • apps/playground-web/src/components/blocks/NetworkSelectors.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from @/components/ui/* (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
Use NavLink for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Use cn() from @/lib/utils for conditional class logic
Use design system tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components (Node edge): Start files with import "server-only";
Client Components (browser): Begin files with 'use client';
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header – never embed tokens in URLs
Return typed results (e.g., Project[], User[]) – avoid any
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys for React Query cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-js in server components

Files:

  • apps/playground-web/src/app/payments/components/CodeGen.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/types.ts
  • apps/playground-web/src/hooks/chains.ts
  • apps/playground-web/src/app/bridge/swap-widget/page.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx
  • apps/playground-web/src/lib/env.ts
  • apps/playground-web/src/app/data/pages-metadata.ts
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/src/app/bridge/swap-widget/components/code.tsx
  • apps/playground-web/src/app/payments/components/LeftSection.tsx
  • apps/playground-web/src/app/page.tsx
  • apps/playground-web/src/components/blocks/NetworkSelectors.tsx
.changeset/*.md

📄 CodeRabbit inference engine (AGENTS.md)

.changeset/*.md: Each change in packages/* must include a changeset for the appropriate package
Version bump rules: patch for non‑API changes; minor for new/modified public API

Files:

  • .changeset/honest-hands-clap.md
**/types.ts

📄 CodeRabbit inference engine (AGENTS.md)

Provide and re‑use local type barrels in a types.ts file

Files:

  • apps/playground-web/src/app/bridge/swap-widget/components/types.ts
packages/thirdweb/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

packages/thirdweb/**/*.{ts,tsx}: Every public symbol must have comprehensive TSDoc with at least one compiling @example and a custom tag (@beta, @internal, @experimental, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Lazy‑load heavy dependencies inside async paths (e.g., const { jsPDF } = await import("jspdf"))

Files:

  • packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx
🧠 Learnings (13)
📚 Learning: 2025-08-29T15:37:38.513Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T15:37:38.513Z
Learning: Applies to apps/{dashboard,playground}/**/*.{ts,tsx} : Client Components must start with `'use client'`; handle interactivity with hooks and browser APIs

Applied to files:

  • apps/playground-web/src/app/payments/components/CodeGen.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx
📚 Learning: 2025-05-30T17:14:25.332Z
Learnt from: MananTank
PR: thirdweb-dev/js#7227
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/OpenEditionMetadata.tsx:26-26
Timestamp: 2025-05-30T17:14:25.332Z
Learning: The ModuleCardUIProps interface already includes a client prop of type ThirdwebClient, so when components use `Omit<ModuleCardUIProps, "children" | "updateButton">`, they inherit the client prop without needing to add it explicitly.

Applied to files:

  • apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Anything that consumes hooks from `tanstack/react-query` or thirdweb SDKs.

Applied to files:

  • apps/playground-web/src/hooks/chains.ts
📚 Learning: 2025-09-17T11:02:13.528Z
Learnt from: MananTank
PR: thirdweb-dev/js#8044
File: packages/thirdweb/src/react/web/ui/Bridge/swap-widget/use-tokens.ts:15-17
Timestamp: 2025-09-17T11:02:13.528Z
Learning: The thirdweb `client` object is serializable and can safely be used in React Query keys, similar to the `contract` object.

Applied to files:

  • apps/playground-web/src/hooks/chains.ts
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/layout.tsx : Building layout shells (`layout.tsx`) and top-level pages that mainly assemble data.

Applied to files:

  • apps/playground-web/src/app/bridge/swap-widget/page.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{tsx} : Expose `className` prop on root element of components for overrides

Applied to files:

  • apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).

Applied to files:

  • apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Client Components (browser): Begin files with `'use client';`

Applied to files:

  • apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Import UI primitives from `@/components/ui/*` (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps

Applied to files:

  • apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Use `NavLink` for internal navigation with automatic active states in dashboard and playground apps

Applied to files:

  • apps/playground-web/src/app/navLinks.ts
📚 Learning: 2025-07-07T21:21:47.488Z
Learnt from: saminacodes
PR: thirdweb-dev/js#7543
File: apps/portal/src/app/pay/page.mdx:4-4
Timestamp: 2025-07-07T21:21:47.488Z
Learning: In the thirdweb-dev/js repository, lucide-react icons must be imported with the "Icon" suffix (e.g., ExternalLinkIcon, RocketIcon) as required by the new linting rule, contrary to the typical lucide-react convention of importing without the suffix.

Applied to files:

  • apps/playground-web/src/app/navLinks.ts
📚 Learning: 2025-07-31T16:17:42.753Z
Learnt from: MananTank
PR: thirdweb-dev/js#7768
File: apps/playground-web/src/app/navLinks.ts:1-1
Timestamp: 2025-07-31T16:17:42.753Z
Learning: Configuration files that import and reference React components (like icon components from lucide-react) need the "use client" directive, even if they primarily export static data, because the referenced components need to be executed in a client context when used by other client components.

Applied to files:

  • apps/playground-web/src/app/navLinks.ts
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Icons come from `lucide-react` or the project-specific `…/icons` exports – never embed raw SVG.

Applied to files:

  • apps/playground-web/src/app/navLinks.ts
🧬 Code graph analysis (12)
apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx (5)
apps/playground-web/src/app/bridge/swap-widget/components/types.ts (1)
  • SwapWidgetPlaygroundOptions (3-12)
apps/playground-web/src/app/wallets/sign-in/components/CollapsibleSection.tsx (1)
  • CollapsibleSection (4-33)
apps/playground-web/src/app/wallets/sign-in/components/ColorFormGroup.tsx (1)
  • ColorFormGroup (9-111)
apps/playground-web/src/components/blocks/NetworkSelectors.tsx (1)
  • BridgeNetworkSelector (244-328)
apps/playground-web/src/lib/client.ts (1)
  • THIRDWEB_CLIENT (20-43)
apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx (4)
apps/playground-web/src/app/bridge/swap-widget/components/types.ts (1)
  • SwapWidgetPlaygroundOptions (3-12)
packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx (1)
  • SwapWidget (238-248)
apps/playground-web/src/lib/client.ts (1)
  • THIRDWEB_CLIENT (20-43)
apps/playground-web/src/app/bridge/swap-widget/components/code.tsx (1)
  • CodeGen (19-27)
apps/playground-web/src/app/bridge/swap-widget/components/types.ts (1)
packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx (1)
  • SwapWidgetProps (31-165)
apps/playground-web/src/hooks/chains.ts (3)
packages/thirdweb/src/bridge/types/Chain.ts (1)
  • BridgeChain (42-42)
apps/playground-web/src/lib/env.ts (1)
  • isProd (1-3)
apps/playground-web/src/lib/client.ts (1)
  • THIRDWEB_CLIENT (20-43)
apps/playground-web/src/app/bridge/swap-widget/page.tsx (2)
apps/playground-web/src/components/blocks/APIHeader.tsx (1)
  • PageLayout (49-70)
apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx (1)
  • SwapWidgetPlayground (20-52)
apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx (3)
apps/playground-web/src/app/bridge/swap-widget/components/types.ts (1)
  • SwapWidgetPlaygroundOptions (3-12)
apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx (1)
  • LeftSection (25-153)
apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx (1)
  • RightSection (13-77)
apps/playground-web/src/app/data/pages-metadata.ts (1)
apps/dashboard/src/@/icons/BridgeIcon.tsx (1)
  • BringToFrontIcon (1-1)
apps/playground-web/src/app/navLinks.ts (1)
apps/playground-web/src/components/blocks/full-width-sidebar-layout.tsx (1)
  • ShadcnSidebarLink (43-59)
apps/playground-web/src/app/bridge/swap-widget/components/code.tsx (2)
apps/playground-web/src/app/payments/components/CodeGen.tsx (1)
  • CodeGen (19-27)
apps/playground-web/src/app/bridge/swap-widget/components/types.ts (1)
  • SwapWidgetPlaygroundOptions (3-12)
apps/playground-web/src/app/page.tsx (1)
apps/playground-web/src/app/data/pages-metadata.ts (1)
  • bridgeFeatureCards (252-259)
packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx (1)
packages/thirdweb/src/react/web/ui/Bridge/swap-widget/storage.ts (2)
  • getLastUsedTokens (23-41)
  • setLastUsedTokens (43-56)
apps/playground-web/src/components/blocks/NetworkSelectors.tsx (2)
apps/playground-web/src/hooks/chains.ts (1)
  • useBridgeSupportedChains (72-79)
apps/playground-web/src/components/ui/select-with-search.tsx (1)
  • SelectWithSearch (38-198)
⏰ 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: Size
🔇 Additional comments (36)
apps/playground-web/src/app/payments/components/CodeGen.tsx (1)

2-2: Alias import LGTM

Switching to @/components/ui/LoadingDots aligns with our app alias convention.

packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx (1)

300-375: Persistence gating is correct and side‑effect is scoped

Nice job centralizing the flag and guarding both reads and writes to storage. The dependency list includes the flag, so toggling it at runtime behaves as expected.

Please confirm that apps/playground-web/.../right-section.tsx intentionally sets persistTokenSelections={false} for the demo and that CodeGen reflects this option where applicable.

apps/playground-web/src/app/payments/components/LeftSection.tsx (1)

35-41: Verified: all call sites of apps/playground-web/src/app/payments/components/LeftSection.tsx supply lockedWidget

TransactionPlayground, BuyPlayground, and CheckoutPlayground pass lockedWidget; other imports named LeftSection (swap-widget and connect-button) reference different files and are unaffected.

apps/playground-web/src/app/page.tsx (2)

6-6: Add Bridge cards import – looks good

Import aligns with new metadata export and keeps section data co-located.


51-51: Bridge section placement is consistent

Sits after Payments and before Account Abstraction, matching nav and metadata.

apps/playground-web/src/app/navLinks.ts (3)

3-8: Icon imports follow lint rule

lucide-react icons use the “Icon” suffix as required.


212-224: New Bridge submenu is well-formed

subMenu shape matches ShadcnSidebarLink and route is coherent with the new page.


261-261: Sidebar ordering OK

Inserting bridge after Payments matches the homepage section order.

apps/playground-web/src/app/data/pages-metadata.ts (2)

6-6: Icon import LGTM

Using BringToFrontIcon keeps visual language consistent across Bridge assets.


252-259: Bridge feature card reads correctly

Copy, link, and icon align with the new route. No issues.

apps/playground-web/src/components/blocks/NetworkSelectors.tsx (2)

8-8: Hook import is correct

Bridge chains hook is referenced from the new source of truth.


244-328: BridgeNetworkSelector — add explicit return type & validate ChainIcon source

Add ): JSX.Element return type; ensure ChainIcon receives ipfsSrc only for ipfs:// values and fallback to an http(s) src if not IPFS.

-export function BridgeNetworkSelector(props: {
+export function BridgeNetworkSelector(props: {
   chainId: number | undefined;
   onChange: (chainId: number) => void;
   className?: string;
   popoverContentClassName?: string;
   side?: "left" | "right" | "top" | "bottom";
   align?: "center" | "start" | "end";
   placeholder?: string;
-}) {
+}): JSX.Element {

Suggested rendering pattern:

<ChainIcon
  className="size-5"
  ipfsSrc={chain.icon?.startsWith?.("ipfs://") ? chain.icon : undefined}
  // src={chain.icon} // if ChainIcon supports http(s) via `src`
  loading="lazy"
/>

Provide the bridge chains API endpoint or a sample response showing the icon field and the ChainIcon component/package or file path so I can verify the correct prop to use; otherwise confirm whether chain.icon values are ipfs:// or if ChainIcon supports an http(s) src.

apps/playground-web/src/app/bridge/swap-widget/page.tsx (1)

26-29: SwapWidget docs URL verified

Canonical path matches https://portal.thirdweb.com/references/typescript/v5/SwapWidget.

apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx (9)

1-1: LGTM! Proper client directive for React hooks and interactivity.

Correctly uses "use client" since the component manages state and user interactions.


3-23: LGTM! Clean imports with proper type safety.

The imports are well-organized and use explicit type imports where appropriate. Good use of the shared UI components and proper separation of concerns.


25-39: LGTM! Well-structured component interface with proper type safety.

The component props are properly typed and the setThemeType function correctly maintains immutability while updating the nested theme state.


41-41: LGTM! Proper use of useId for unique identifiers.

Good practice using React's useId hook to generate stable unique IDs for form elements.


45-107: LGTM! Well-structured token selection UI.

The CollapsibleSection with currency selector and TokenFieldset components provides a clean, organized interface. The currency list is comprehensive and includes proper fallback to "USD" when no currency is selected.


109-152: LGTM! Comprehensive appearance controls.

The appearance section properly integrates theme switching, color customization via ColorFormGroup, and branding toggle. The checkbox logic correctly handles the boolean conversion.


155-196: LGTM! Smart token address clearing logic.

The TokenFieldset correctly clears the tokenAddress when the chain changes (line 188), which prevents invalid token/chain combinations. The chain selection properly updates the prefill state.


198-235: LGTM! Intelligent token selection with native token fallback.

The token selector logic is well-implemented:

  • Shows native token as fallback when no specific token is selected (lines 226-231)
  • Uses getAddress(NATIVE_TOKEN_ADDRESS) for consistent address formatting
  • Properly updates both chainId and tokenAddress when a token is selected

237-264: LGTM! Conditional amount input with proper state management.

The amount input is properly conditionally rendered only when a chain is selected, and the state update correctly preserves the chainId while updating the amount value.

apps/playground-web/src/app/bridge/swap-widget/components/code.tsx (6)

1-9: LGTM! Proper lazy loading implementation.

Good use of React lazy loading to optimize bundle size by deferring the CodeClient import until needed.


11-17: LGTM! Clean loading state component.

The CodeLoading component provides appropriate visual feedback with consistent styling.


29-54: LGTM! Robust theme prop generation logic.

The theme generation correctly handles both dark and light themes with proper conditional logic for color overrides. The imports tracking is implemented but not used in the final output, which is acceptable since the example shows a simplified import structure.


56-69: LGTM! Clean props building with sensible defaults.

The props object construction correctly handles:

  • Conditional inclusion based on actual values
  • Proper JSON stringification for complex prefill objects
  • USD currency default handling
  • Correct boolean handling for showThirdwebBranding

71-87: LGTM! Clean code template generation.

The generated code template is well-structured and provides a complete working example with proper imports and client initialization.


89-105: LGTM! Utility functions work correctly.

Both quotes and stringifyProps functions handle their respective formatting tasks properly, with stringifyProps correctly filtering out undefined and empty values.

apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx (8)

1-1: LGTM! Proper client directive for interactive features.

Correctly uses "use client" since the component manages state, uses Next.js routing hooks, and handles user interactions.


2-9: LGTM! Clean imports with proper type safety.

The imports are well-organized with proper usage of Next.js hooks and thirdweb components.


13-25: LGTM! Proper state management with URL synchronization.

The tab state management correctly initializes to "ui" and synchronizes changes with the browser URL via replaceState, maintaining a good user experience.


27-34: LGTM! Correct theme object construction.

The theme object is properly constructed based on the options, correctly applying color overrides for both dark and light themes.


36-51: LGTM! Well-structured tab interface.

The TabButtons component receives a clean, type-safe configuration array with proper active state management and click handlers.


53-76: LGTM! Smart content rendering with proper widget configuration.

The conditional rendering between UI and code views is clean. The SwapWidget is configured correctly with:

  • Proper client instance from THIRDWEB_CLIENT
  • Theme object constructed above
  • All relevant options passed through
  • Key prop forcing remount on options change for proper state reset
  • persistTokenSelections={false} for playground behavior

79-92: LGTM! Elegant background pattern with responsive design.

The BackgroundPattern component creates a subtle radial gradient effect that enhances the visual presentation without being distracting. The CSS mask provides a nice fade-out effect.


94-122: LGTM! Reusable tab button component with proper styling.

The TabButtons component is well-structured with:

  • Clean prop interface
  • Proper active/inactive state styling
  • Good use of Tailwind CSS utility classes
  • Consistent button styling via the Button component

@codecov
Copy link

codecov bot commented Sep 18, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 56.34%. Comparing base (f3b3b89) to head (32980f8).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8069      +/-   ##
==========================================
- Coverage   56.35%   56.34%   -0.01%     
==========================================
  Files         906      906              
  Lines       59168    59168              
  Branches     4172     4176       +4     
==========================================
- Hits        33342    33341       -1     
- Misses      25720    25721       +1     
  Partials      106      106              
Flag Coverage Δ
packages 56.34% <ø> (-0.01%) ⬇️
see 3 files with indirect coverage changes
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@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

🧹 Nitpick comments (25)
packages/ui/src/components/code/getCodeHtml.tsx (1)

6-15: Add explicit return types (TypeScript guideline)

Declare return types for clarity and to prevent accidental inference drift.

function isPrettierSupportedLang(lang: BundledLanguage): boolean { ... }

export async function getCodeHtml(
  code: string,
  lang: BundledLanguage,
  options?: { ignoreFormattingErrors?: boolean },
): Promise<{ formattedCode: string; html: string }> { ... }

Also applies to: 17-23

apps/playground-web/src/app/page.tsx (1)

14-15: Add explicit return types (and mark server-only) to match repo guidelines

Type the component returns and mark the file as server-only per app rules.

+import "server-only";
 import { FeatureCard } from "@/components/blocks/FeatureCard";
 import { ThirdwebIcon } from "../icons/ThirdwebMiniLogo";
@@
-export default function Page() {
+export default function Page(): JSX.Element {
@@
-function FeatureSection(props: {
+function FeatureSection(props: {
   featureCards: FeatureCardMetadata[];
   title: string;
-}) {
+}): JSX.Element {

Also applies to: 61-64

packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx (3)

157-165: Document stability tag and add a usage example for the new prop.

Add a custom stability tag (e.g., @beta) and a minimal example showing how to disable persistence.

Apply:

   /**
    * Whether to persist the token selections to localStorage so that if the user revisits the widget, the last used tokens are pre-selected.
    * The last used tokens do not override the tokens specified in the `prefill` prop
    *
    * @default true
+   * @beta
+   * @example
+   * ```tsx
+   * <SwapWidget client={client} persistTokenSelections={false} />
+   * ```
    */
   persistTokenSelections?: boolean;

371-375: Writes are properly gated; consider a tiny optimization.

Looks good. If you want, avoid double getLastUsedTokens() calls in the buy initializer by caching within that initializer.


237-248: Add explicit return types to exported functions.

Project guidelines prefer explicit return types for TS/TSX.

Apply:

-export function SwapWidget(props: SwapWidgetProps) {
+export function SwapWidget(props: SwapWidgetProps): React.JSX.Element {
   return (
     <SwapWidgetContainer
       theme={props.theme}
       style={props.style}
       className={props.className}
     >
       <SwapWidgetContent {...props} />
     </SwapWidgetContainer>
   );
 }

 export function SwapWidgetContainer(props: {
   theme: SwapWidgetProps["theme"];
   className: string | undefined;
   style?: React.CSSProperties | undefined;
   children: React.ReactNode;
-}) {
+}): React.JSX.Element {
   return (
     <CustomThemeProvider theme={props.theme || "dark"}>
       <EmbedContainer
         className={props.className}
         modalSize="compact"
         style={{
           ...props.style,
         }}
       >
         <DynamicHeight>{props.children}</DynamicHeight>
       </EmbedContainer>
     </CustomThemeProvider>
   );
 }

-function SwapWidgetContent(props: SwapWidgetProps) {
+function SwapWidgetContent(props: SwapWidgetProps): React.JSX.Element | null {
   const [screen, setScreen] = useState<SwapWidgetScreen>({ id: "1:swap-ui" });
   const activeWalletInfo = useActiveWalletInfo();
   const isPersistEnabled = props.persistTokenSelections !== false;
   ...
   return null;
 }

-function getInitialSellToken(
+function getInitialSellToken(
   prefill: SwapWidgetProps["prefill"],
   lastUsedSellToken: TokenSelection | undefined,
-) {
+): TokenSelection | undefined {
   if (prefill?.sellToken) {
     return {
       tokenAddress:
         prefill.sellToken.tokenAddress || getAddress(NATIVE_TOKEN_ADDRESS),
       chainId: prefill.sellToken.chainId,
     };
   }

   return lastUsedSellToken;
 }

Also applies to: 253-258, 299-304, 535-548

apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx (3)

21-28: Memoize theme object.

Avoid re-creating theme on every render.

Apply:

-import { useState } from "react";
+import { useMemo, useState } from "react";
...
-  const themeObj =
-    props.options.theme.type === "dark"
-      ? darkTheme({
-          colors: props.options.theme.darkColorOverrides,
-        })
-      : lightTheme({
-          colors: props.options.theme.lightColorOverrides,
-        });
+  const themeObj = useMemo(() => {
+    return props.options.theme.type === "dark"
+      ? darkTheme({ colors: props.options.theme.darkColorOverrides })
+      : lightTheme({ colors: props.options.theme.lightColorOverrides });
+  }, [props.options.theme]);

62-64: Stabilize the remount key.

Compute once with useMemo to avoid unnecessary stringify during render.

Apply:

-            key={JSON.stringify(props.options)}
+            key={useMemo(() => JSON.stringify(props.options), [props.options])}

73-86: Replace inline styles with Tailwind arbitrary utilities.

Playground code should avoid inline styles per guidelines.

Apply:

-function BackgroundPattern() {
-  const color = "hsl(var(--foreground)/15%)";
-  return (
-    <div
-      className="absolute inset-0 z-[-1]"
-      style={{
-        backgroundImage: `radial-gradient(${color} 1px, transparent 1px)`,
-        backgroundSize: "24px 24px",
-        maskImage:
-          "radial-gradient(ellipse 100% 100% at 50% 50%, black 30%, transparent 60%)",
-      }}
-    />
-  );
-}
+function BackgroundPattern() {
+  return (
+    <div
+      className={cn(
+        "absolute inset-0 z-[-1]",
+        // background pattern + size
+        "bg-[radial-gradient(hsl(var(--foreground)/15%)_1px,transparent_1px)]",
+        "bg-[length:24px_24px]",
+        // mask image
+        "[mask-image:radial-gradient(ellipse_100%_100%_at_50%_50%,black_30%,transparent_60%)]",
+      )}
+    />
+  );
+}
apps/playground-web/src/components/blocks/CurrencySelector.tsx (2)

12-21: Add an explicit return type and avoid the as cast.

Return type helps; derive the union from a constant list to eliminate the cast.

Apply:

-export function CurrencySelector(props: {
+export function CurrencySelector(props: {
   value: SwapWidgetProps["currency"];
   onChange: (value: SwapWidgetProps["currency"]) => void;
-}) {
+}): React.JSX.Element {
   return (
     <Select
-      value={props.value}
-      onValueChange={(value) => {
-        props.onChange(value as SwapWidgetProps["currency"]);
-      }}
+      value={props.value}
+      onValueChange={(value) => {
+        props.onChange(value as SwapWidgetProps["currency"]);
+      }}

Alternatively:

+const CURRENCIES = [
+  "USD","EUR","GBP","JPY","KRW","CNY","INR","NOK","SEK","CHF",
+  "AUD","CAD","NZD","MXN","BRL","CLP","CZK","DKK","HKD","HUF",
+  "IDR","ILS","ISK",
+] as const;
+
+type Currency = (typeof CURRENCIES)[number];
...
-    <Select
-      value={props.value}
-      onValueChange={(value) => {
-        props.onChange(value as SwapWidgetProps["currency"]);
-      }}
+    <Select
+      value={props.value}
+      onValueChange={(value: Currency) => {
+        props.onChange(value);
+      }}

26-50: DRY the options rendering.

Map over a constant list for maintainability.

Apply:

-      <SelectContent>
-        <SelectItem value="USD">USD - US Dollar</SelectItem>
-        <SelectItem value="EUR">EUR - Euro</SelectItem>
-        <SelectItem value="GBP">GBP - British Pound</SelectItem>
-        <SelectItem value="JPY">JPY - Japanese Yen</SelectItem>
-        <SelectItem value="KRW">KRW - Korean Won</SelectItem>
-        <SelectItem value="CNY">CNY - Chinese Yuan</SelectItem>
-        <SelectItem value="INR">INR - Indian Rupee</SelectItem>
-        <SelectItem value="NOK">NOK - Norwegian Krone</SelectItem>
-        <SelectItem value="SEK">SEK - Swedish Krona</SelectItem>
-        <SelectItem value="CHF">CHF - Swiss Franc</SelectItem>
-        <SelectItem value="AUD">AUD - Australian Dollar</SelectItem>
-        <SelectItem value="CAD">CAD - Canadian Dollar</SelectItem>
-        <SelectItem value="NZD">NZD - New Zealand Dollar</SelectItem>
-        <SelectItem value="MXN">MXN - Mexican Peso</SelectItem>
-        <SelectItem value="BRL">BRL - Brazilian Real</SelectItem>
-        <SelectItem value="CLP">CLP - Chilean Peso</SelectItem>
-        <SelectItem value="CZK">CZK - Czech Koruna</SelectItem>
-        <SelectItem value="DKK">DKK - Danish Krone</SelectItem>
-        <SelectItem value="HKD">HKD - Hong Kong Dollar</SelectItem>
-        <SelectItem value="HUF">HUF - Hungarian Forint</SelectItem>
-        <SelectItem value="IDR">IDR - Indonesian Rupiah</SelectItem>
-        <SelectItem value="ILS">ILS - Israeli Shekel</SelectItem>
-        <SelectItem value="ISK">ISK - Icelandic Krona</SelectItem>
-      </SelectContent>
+      <SelectContent>
+        {CURRENCIES.map((c) => (
+          <SelectItem key={c} value={c}>
+            {c}
+          </SelectItem>
+        ))}
+      </SelectContent>
apps/playground-web/src/hooks/chains.ts (3)

18-26: Add timeout and stricter error handling.

Abort long requests and check res.ok before parsing JSON.

Apply:

-async function fetchBridgeSupportedChainsFromApi() {
-  const res = await fetch(
+async function fetchBridgeSupportedChainsFromApi(): Promise<BridgeChain[]> {
+  const ctrl = new AbortController();
+  const timeout = setTimeout(() => ctrl.abort(), 15_000);
+  const res = await fetch(
     `https://bridge.${isProd ? "thirdweb.com" : "thirdweb-dev.com"}/v1/chains`,
     {
       headers: {
+        Accept: "application/json",
         "x-client-id": THIRDWEB_CLIENT.clientId,
       },
+      signal: ctrl.signal,
     },
   );
-  const json = await res.json();
+  try {
+    if (!res.ok) {
+      throw new Error(`Failed to fetch chains (${res.status})`);
+    }
+    const json = await res.json();
+    if (json.error) {
+      throw new Error(json.error.message);
+    }
+    return json.data as BridgeChain[];
+  } finally {
+    clearTimeout(timeout);
+  }
-
-  if (json.error) {
-    throw new Error(json.error.message);
-  }
-
-  return json.data as BridgeChain[];
 }

Also applies to: 33-34


36-43: Tune React Query cache settings and types.

Set sensible stale/gc times and annotate result type.

Apply:

-export function useBridgeSupportedChains() {
-  return useQuery({
-    queryFn: async () => {
-      return fetchBridgeSupportedChainsFromApi();
-    },
-    queryKey: ["bridge-supported-chains"],
-  });
+export function useBridgeSupportedChains() {
+  return useQuery<BridgeChain[]>({
+    queryFn: fetchBridgeSupportedChainsFromApi,
+    queryKey: ["bridge-supported-chains"] as const,
+    staleTime: 5 * 60 * 1000,
+    gcTime: 30 * 60 * 1000,
+    refetchOnWindowFocus: false,
+  });
 }

7-16: Import shared BridgeChain from the SDK instead of duplicating.
BridgeChain is exported at packages/thirdweb/src/bridge/types/Chain.ts:42 — replace the local type with an import, e.g.:
import type { BridgeChain } from 'packages/thirdweb/src/bridge/types/Chain'

apps/playground-web/src/components/ui/TokenSelector.tsx (1)

108-131: Minor UX: handle error state.

Consider disabling with an error placeholder when tokensQuery.isError.

Apply:

-      disabled={tokensQuery.isLoading || props.disabled}
+      disabled={tokensQuery.isLoading || tokensQuery.isError || props.disabled}
...
-        tokensQuery.isLoading
-          ? "Loading Tokens..."
-          : props.placeholder || "Select Token"
+        tokensQuery.isLoading
+          ? "Loading Tokens..."
+          : tokensQuery.isError
+            ? "Failed to load tokens"
+            : props.placeholder || "Select Token"
apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx (6)

42-49: Fix label association for currency selector (a11y).

Label uses htmlFor="currency", but no element has id="currency". Wire the label to the control.

Apply locally and extend CurrencySelector to accept/forward an id:

-            <Label htmlFor="currency">Display Currency</Label>
-            <CurrencySelector
+            <Label htmlFor={currencyId}>Display Currency</Label>
+            <CurrencySelector
+              id={currencyId}
               value={options.currency}
               onChange={(currency) => {
                 setOptions((v) => ({ ...v, currency }));
               }}
             />

Outside this file (CurrencySelector.tsx), add id support:

-export function CurrencySelector(props: {
-  value: SwapWidgetProps["currency"];
-  onChange: (value: SwapWidgetProps["currency"]) => void;
-}) {
+export function CurrencySelector(props: {
+  id?: string;
+  value: SwapWidgetProps["currency"];
+  onChange: (value: SwapWidgetProps["currency"]) => void;
+}) {
   return (
-    <Select
+    <Select
+      // associate trigger with external label
       value={props.value}
       onValueChange={(value) => {
         props.onChange(value as SwapWidgetProps["currency"]);
       }}
     >
-      <SelectTrigger className="bg-card">
+      <SelectTrigger id={props.id} className="bg-card">

Also declare in this file (near other ids):

const currencyId = useId();

74-83: Label htmlFor mismatch with radio group id (a11y).

htmlFor="theme" doesn’t match the radio group’s id={themeId}. Point the label to the correct id.

-          <Label htmlFor="theme"> Theme </Label>
+          <Label htmlFor={themeId}> Theme </Label>

160-196: Gate token selector on chain selection and avoid unnecessary queries.

The comment says “only show if chain is selected” but the control renders unconditionally and enabled={true} with possibly undefined chainId. Hide or disable until chain is chosen.

-        {/* Token selection - only show if chain is selected */}
-        <div className="space-y-2">
+        {/* Token selection - only show if chain is selected */}
+        {chainId && (
+          <div className="space-y-2">
           <Label>Token</Label>
           <TokenSelector
-            chainId={chainId}
+            chainId={chainId}
             client={THIRDWEB_CLIENT}
             disableAddress
-            enabled={true}
+            enabled={!!chainId}
             onChange={(token) => {
               setOptions((v) => ({
                 ...v,
                 prefill: {
                   ...v.prefill,
                   [props.type]: {
                     chainId: token.chainId,
                     tokenAddress: token.address,
                   },
                 },
               }));
             }}
             placeholder="Select a token"
             selectedToken={
               tokenAddress && chainId
                 ? {
                     address: tokenAddress,
                     chainId: chainId,
                   }
                 : chainId
                   ? {
                       address: getAddress(NATIVE_TOKEN_ADDRESS),
                       chainId: chainId,
                     }
                   : undefined
             }
             className="bg-card"
           />
-        </div>
+          </div>
+        )}

99-111: Avoid hard-coded ids; use useId for uniqueness.

Using id="branding" risks collisions if multiple instances render. Prefer a generated id.

-        <div className="my-4 flex items-center gap-2">
+        <div className="my-4 flex items-center gap-2">
+          {/*
+            ensure unique/consistent id for a11y and avoid collisions
+          */}
+          {(() => {
+            const brandingId = useId();
+            return (
+              <>
           <Checkbox
-            checked={options.showThirdwebBranding}
-            id={"branding"}
+            checked={options.showThirdwebBranding}
+            id={brandingId}
             onCheckedChange={(checked) => {
               setOptions((v) => ({
                 ...v,
                 showThirdwebBranding: checked === true,
               }));
             }}
           />
-          <Label htmlFor={"branding"}>Show Branding</Label>
+          <Label htmlFor={brandingId}>Show Branding</Label>
+              </>
+            );
+          })()}
         </div>

200-221: Associate “Token Amount” label with input (a11y).

The label isn’t connected to the input. Add an id and link it.

-          <div className="space-y-2">
-            <Label> Token Amount</Label>
-            <Input
+          <div className="space-y-2">
+            {(() => {
+              const amountId = useId();
+              return (
+                <>
+            <Label htmlFor={amountId}> Token Amount</Label>
+            <Input
+              id={amountId}
               className="bg-card"
               value={options.prefill?.[props.type]?.amount || ""}
               onChange={(e) => {
                 setOptions((v) => {
                   return {
                     ...v,
                     prefill: {
                       ...v.prefill,
                       [props.type]: {
                         ...v.prefill?.[props.type],
                         amount: e.target.value,
                         chainId: chainId,
                       },
                     },
                   };
                 });
               }}
               placeholder="0.01"
             />
+                </>
+              );
+            })()}
           </div>

20-23: Add explicit return types and consider extracting TokenFieldset.

Align with our TS guidelines: explicit return types; single-responsibility per file.

-export function LeftSection(props: {
+export function LeftSection(props: {
   options: SwapWidgetPlaygroundOptions;
   setOptions: React.Dispatch<React.SetStateAction<SwapWidgetPlaygroundOptions>>;
-}) {
+}): JSX.Element {
-function TokenFieldset(props: {
+function TokenFieldset(props: {
   type: "buyToken" | "sellToken";
   title: string;
   options: SwapWidgetPlaygroundOptions;
   setOptions: React.Dispatch<React.SetStateAction<SwapWidgetPlaygroundOptions>>;
-}) {
+}): JSX.Element {

Optionally, move TokenFieldset to its own file to satisfy “one component per file”.

Also applies to: 117-122

apps/playground-web/src/app/payments/components/LeftSection.tsx (4)

33-38: Simplify effectiveWidget now that lockedWidget is required.

lockedWidget is non-optional; fallback to payOptions.widget is dead.

-  const effectiveWidget = props.lockedWidget || payOptions.widget || "buy";
+  const effectiveWidget = props.lockedWidget;

118-129: Wire label to currency control (a11y) and forward id.

Same issue as in swap-widget LeftSection. Use an id and forward it to CurrencySelector (after adding id support there).

-            <Label htmlFor="currency">Display Currency</Label>
-            <CurrencySelector
+            <Label htmlFor={currencyId}>Display Currency</Label>
+            <CurrencySelector
+              id={currencyId}
               value={payOptions.currency}
               onChange={(currency) => {
                 setOptions((v) => ({
                   ...v,
                   payOptions: {
                     ...v.payOptions,
                     currency: currency,
                   },
                 }));
               }}
             />

Declare near other ids:

const currencyId = useId();

456-466: Fix theme label association (a11y).

Label htmlFor should match themeId.

-          <Label htmlFor="theme"> Theme </Label>
+          <Label htmlFor={themeId}> Theme </Label>

507-514: Harden external link security.

Add rel="noopener noreferrer" when using target="_blank".

-            <Link
+            <Link
               className="inline-flex items-center gap-2 text-muted-foreground hover:text-foreground"
               href="https://portal.thirdweb.com/wallets/sponsor-gas?utm_source=playground"
-              target="_blank"
+              target="_blank"
+              rel="noopener noreferrer"
             >
apps/playground-web/src/components/blocks/NetworkSelectors.tsx (1)

25-33: Minor perf/readability: precompute a chainsById map.

Both searchFn and renderOption repeatedly scan the array. A small map removes linear lookups.

-  const options = useMemo(() => {
-    return (chainsQuery.data || [])?.map((chain) => {
+  const { options, chainsById } = useMemo(() => {
+    const chains = chainsQuery.data || [];
+    const byId = new Map<number, (typeof chains)[number]>();
+    const opts = chains.map((chain) => {
+      byId.set(chain.chainId, chain);
       return {
         label: cleanChainName(chain.name),
         value: String(chain.chainId),
       };
-    });
-  }, [chainsQuery.data]);
+    });
+    return { options: opts, chainsById: byId };
+  }, [chainsQuery.data]);
-  const searchFn = useCallback(
+  const searchFn = useCallback(
     (option: Option, searchValue: string) => {
-      const chain = chainsQuery.data?.find(
-        (chain) => chain.chainId === Number(option.value),
-      );
+      const chain = chainsById.get(Number(option.value));
       if (!chain) {
         return false;
       }
@@
-    [chainsQuery.data],
+    [chainsById],
   );
-  const renderOption = useCallback(
+  const renderOption = useCallback(
     (option: Option) => {
-      const chain = chainsQuery.data?.find(
-        (chain) => chain.chainId === Number(option.value),
-      );
+      const chain = chainsById.get(Number(option.value));
       if (!chain) {
         return option.label;
       }
@@
-    [chainsQuery.data],
+    [chainsById],
   );

Also applies to: 34-49, 51-70

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3cd85bf and e11ace4.

📒 Files selected for processing (21)
  • .changeset/honest-hands-clap.md (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/components/code.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/components/types.ts (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/page.tsx (1 hunks)
  • apps/playground-web/src/app/data/pages-metadata.ts (2 hunks)
  • apps/playground-web/src/app/navLinks.ts (3 hunks)
  • apps/playground-web/src/app/page.tsx (2 hunks)
  • apps/playground-web/src/app/payments/components/CodeGen.tsx (1 hunks)
  • apps/playground-web/src/app/payments/components/LeftSection.tsx (5 hunks)
  • apps/playground-web/src/components/blocks/CurrencySelector.tsx (1 hunks)
  • apps/playground-web/src/components/blocks/NetworkSelectors.tsx (2 hunks)
  • apps/playground-web/src/components/blocks/multi-select.tsx (0 hunks)
  • apps/playground-web/src/components/ui/TokenSelector.tsx (2 hunks)
  • apps/playground-web/src/hooks/chains.ts (1 hunks)
  • apps/playground-web/src/lib/env.ts (1 hunks)
  • apps/playground-web/src/lib/useShowMore.ts (0 hunks)
  • packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx (5 hunks)
  • packages/ui/src/components/code/getCodeHtml.tsx (2 hunks)
💤 Files with no reviewable changes (2)
  • apps/playground-web/src/components/blocks/multi-select.tsx
  • apps/playground-web/src/lib/useShowMore.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx
  • .changeset/honest-hands-clap.md
  • apps/playground-web/src/app/data/pages-metadata.ts
  • apps/playground-web/src/app/payments/components/CodeGen.tsx
  • apps/playground-web/src/lib/env.ts
  • apps/playground-web/src/app/bridge/swap-widget/page.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/code.tsx
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/src/app/bridge/swap-widget/components/types.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose

**/*.{ts,tsx}: Use explicit function declarations and explicit return types in TypeScript
Limit each file to one stateless, single‑responsibility function
Re‑use shared types from @/types where applicable
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics when possible
Prefer composition over inheritance; use utility types (Partial, Pick, etc.)
Lazy‑import optional features and avoid top‑level side‑effects to reduce bundle size

Files:

  • apps/playground-web/src/components/ui/TokenSelector.tsx
  • apps/playground-web/src/components/blocks/CurrencySelector.tsx
  • apps/playground-web/src/app/page.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx
  • apps/playground-web/src/hooks/chains.ts
  • packages/ui/src/components/code/getCodeHtml.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx
  • apps/playground-web/src/app/payments/components/LeftSection.tsx
  • apps/playground-web/src/components/blocks/NetworkSelectors.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)

Files:

  • apps/playground-web/src/components/ui/TokenSelector.tsx
  • apps/playground-web/src/components/blocks/CurrencySelector.tsx
  • apps/playground-web/src/app/page.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx
  • apps/playground-web/src/hooks/chains.ts
  • packages/ui/src/components/code/getCodeHtml.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx
  • apps/playground-web/src/app/payments/components/LeftSection.tsx
  • apps/playground-web/src/components/blocks/NetworkSelectors.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from @/components/ui/* (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
Use NavLink for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Use cn() from @/lib/utils for conditional class logic
Use design system tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components (Node edge): Start files with import "server-only";
Client Components (browser): Begin files with 'use client';
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header – never embed tokens in URLs
Return typed results (e.g., Project[], User[]) – avoid any
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys for React Query cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-js in server components

Files:

  • apps/playground-web/src/components/ui/TokenSelector.tsx
  • apps/playground-web/src/components/blocks/CurrencySelector.tsx
  • apps/playground-web/src/app/page.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx
  • apps/playground-web/src/hooks/chains.ts
  • apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx
  • apps/playground-web/src/app/payments/components/LeftSection.tsx
  • apps/playground-web/src/components/blocks/NetworkSelectors.tsx
packages/thirdweb/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

packages/thirdweb/**/*.{ts,tsx}: Every public symbol must have comprehensive TSDoc with at least one compiling @example and a custom tag (@beta, @internal, @experimental, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Lazy‑load heavy dependencies inside async paths (e.g., const { jsPDF } = await import("jspdf"))

Files:

  • packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx
🧠 Learnings (6)
📓 Common learnings
Learnt from: MananTank
PR: thirdweb-dev/js#8069
File: apps/playground-web/src/app/bridge/swap-widget/components/types.ts:3-12
Timestamp: 2025-09-18T20:09:57.022Z
Learning: The SwapWidgetPlaygroundOptions type should not include persistTokenSelections - the playground intentionally hardcodes persistTokenSelections={false} and doesn't expose it as a configurable option to users.
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).

Applied to files:

  • apps/playground-web/src/components/ui/TokenSelector.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx
  • apps/playground-web/src/app/payments/components/LeftSection.tsx
📚 Learning: 2025-05-30T17:14:25.332Z
Learnt from: MananTank
PR: thirdweb-dev/js#7227
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/OpenEditionMetadata.tsx:26-26
Timestamp: 2025-05-30T17:14:25.332Z
Learning: The ModuleCardUIProps interface already includes a client prop of type ThirdwebClient, so when components use `Omit<ModuleCardUIProps, "children" | "updateButton">`, they inherit the client prop without needing to add it explicitly.

Applied to files:

  • apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Anything that consumes hooks from `tanstack/react-query` or thirdweb SDKs.

Applied to files:

  • apps/playground-web/src/hooks/chains.ts
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Use React Query (`tanstack/react-query`) for all client data fetching.

Applied to files:

  • apps/playground-web/src/hooks/chains.ts
📚 Learning: 2025-09-18T20:09:57.022Z
Learnt from: MananTank
PR: thirdweb-dev/js#8069
File: apps/playground-web/src/app/bridge/swap-widget/components/types.ts:3-12
Timestamp: 2025-09-18T20:09:57.022Z
Learning: The SwapWidgetPlaygroundOptions type should not include persistTokenSelections - the playground intentionally hardcodes persistTokenSelections={false} and doesn't expose it as a configurable option to users.

Applied to files:

  • packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx
🧬 Code graph analysis (8)
apps/playground-web/src/components/blocks/CurrencySelector.tsx (2)
packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx (1)
  • SwapWidgetProps (31-165)
apps/playground-web/src/components/ui/select.tsx (5)
  • Select (152-152)
  • SelectTrigger (155-155)
  • SelectValue (154-154)
  • SelectContent (156-156)
  • SelectItem (158-158)
apps/playground-web/src/app/page.tsx (1)
apps/playground-web/src/app/data/pages-metadata.ts (1)
  • bridgeFeatureCards (252-259)
apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx (7)
apps/playground-web/src/app/bridge/swap-widget/components/types.ts (1)
  • SwapWidgetPlaygroundOptions (3-12)
apps/playground-web/src/app/wallets/sign-in/components/CollapsibleSection.tsx (1)
  • CollapsibleSection (4-33)
apps/playground-web/src/components/blocks/CurrencySelector.tsx (1)
  • CurrencySelector (12-53)
apps/playground-web/src/app/wallets/sign-in/components/ColorFormGroup.tsx (1)
  • ColorFormGroup (9-111)
apps/playground-web/src/components/blocks/NetworkSelectors.tsx (1)
  • BridgeNetworkSelector (14-98)
apps/playground-web/src/components/ui/TokenSelector.tsx (1)
  • TokenSelector (14-132)
apps/playground-web/src/lib/client.ts (1)
  • THIRDWEB_CLIENT (20-43)
apps/playground-web/src/hooks/chains.ts (3)
packages/thirdweb/src/bridge/types/Chain.ts (1)
  • BridgeChain (42-42)
apps/playground-web/src/lib/env.ts (1)
  • isProd (1-3)
apps/playground-web/src/lib/client.ts (1)
  • THIRDWEB_CLIENT (20-43)
packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx (1)
packages/thirdweb/src/react/web/ui/Bridge/swap-widget/storage.ts (2)
  • getLastUsedTokens (23-41)
  • setLastUsedTokens (43-56)
apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx (4)
apps/playground-web/src/app/bridge/swap-widget/components/types.ts (1)
  • SwapWidgetPlaygroundOptions (3-12)
packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx (1)
  • SwapWidget (238-248)
apps/playground-web/src/lib/client.ts (1)
  • THIRDWEB_CLIENT (20-43)
apps/playground-web/src/app/bridge/swap-widget/components/code.tsx (1)
  • CodeGen (19-27)
apps/playground-web/src/app/payments/components/LeftSection.tsx (2)
apps/playground-web/src/components/blocks/CurrencySelector.tsx (1)
  • CurrencySelector (12-53)
apps/playground-web/src/components/blocks/NetworkSelectors.tsx (1)
  • BridgeNetworkSelector (14-98)
apps/playground-web/src/components/blocks/NetworkSelectors.tsx (1)
apps/playground-web/src/hooks/chains.ts (1)
  • useBridgeSupportedChains (36-43)
⏰ 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). (4)
  • GitHub Check: Lint Packages
  • GitHub Check: Size
  • GitHub Check: Unit Tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (13)
apps/playground-web/src/app/page.tsx (2)

6-6: Bridge metadata import — LGTM

Import aligns with pages-metadata and keeps concerns separated.


51-55: Bridge section placement — verified: route & nav wired; persistTokenSelections not exposed

  • Route: apps/playground-web/src/app/bridge/swap-widget/page.tsx.
  • Nav: apps/playground-web/src/app/navLinks.ts includes href "/bridge/swap-widget".
  • Types: persistTokenSelections is not part of SwapWidgetPlaygroundOptions (apps/playground-web/src/app/bridge/swap-widget/components/types.ts).
packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx (3)

302-302: Sane default for persistence.

Defaulting to enabled unless explicitly false is good.


336-339: Correctly bypass persisted buy token when disabled.

Early-return avoids reading/stomping state when persistence is off.


363-367: Sell-token initialization respects the flag.

Gating last-used on isPersistEnabled is correct.

apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx (2)

55-65: Playground hard-disables persistence — good.

Matches the decision to not expose persistence in the playground UI.


12-20: Confirm persistTokenSelections is not exposed in SwapWidgetPlaygroundOptions

rg in the sandbox errored ("unrecognized file type: tsx"); manually verify that SwapWidgetPlaygroundOptions (apps/playground-web/src/app/bridge/swap-widget/components/types.ts) does NOT include persistTokenSelections and that persistTokenSelections is only set to false in the playground preview (apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx). Local checks: rg -n 'persistTokenSelections' -S; rg -n 'type\s+SwapWidgetPlaygroundOptions' -S

apps/playground-web/src/components/blocks/CurrencySelector.tsx (1)

12-21: Confirmed — playground CurrencySelector matches SDK SupportedFiatCurrency.

The SelectItem values in apps/playground-web/src/components/blocks/CurrencySelector.tsx exactly match the keys in packages/thirdweb/src/pay/convert/type.ts (SupportedFiatCurrency); no unsupported fiat currencies are exposed.

apps/playground-web/src/components/ui/TokenSelector.tsx (1)

30-37: Simplification looks fine — confirm native-token behavior

useTokensData (apps/playground-web/src/hooks/useTokensData.ts) fetches bridge.thirdweb.com/v1/tokens and does not augment native tokens; confirm the API returns native tokens for the requested chain or restore the addNativeTokenIfMissing behavior in TokenSelector.

apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx (2)

140-157: Confirm desired persistence of “native token” default in options.

UI defaults to native token when a chain is chosen, but options.prefill.[type].tokenAddress stays undefined until a token is explicitly selected. If codegen/examples should mirror the UI, consider setting tokenAddress to NATIVE_TOKEN_ADDRESS on chain change.

Do you want the playground to persist the native token in options, or keep it undefined until user action?


18-23: Playground should not expose persistTokenSelections.

Types correctly omit persistTokenSelections per product decision; keep it hardcoded in the playground.

Confirm that SwapWidget usage in this route passes persistTokenSelections={false} (and does not surface a toggle).

apps/playground-web/src/app/payments/components/LeftSection.tsx (1)

15-16: BridgeNetworkSelector integration looks good.

Import and usage align with the new selector API; loading and value handling are correct.

Also applies to: 138-144

apps/playground-web/src/components/blocks/NetworkSelectors.tsx (1)

72-90: Nice loading/placeholder handling.

Disabling the control while chains load avoids broken states; placeholder messaging is clear.

Copy link
Contributor

@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: 2

🧹 Nitpick comments (4)
packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx (1)

157-164: New prop looks good but TSDoc could be improved.

The persistTokenSelections prop is a useful addition for controlling localStorage persistence. The implementation logic is sound and maintains backward compatibility with the default value of true.

Consider improving the TSDoc with clearer examples:

  /**
   * Whether to persist the token selections to localStorage so that if the user revisits the widget, the last used tokens are pre-selected.
-  * The last used tokens do not override the tokens specified in the `prefill` prop
+  * The last used tokens do not override the tokens specified in the `prefill` prop.
   *
+  * @example
+  * ```tsx
+  * // Disable token persistence
+  * <SwapWidget client={client} persistTokenSelections={false} />
+  * 
+  * // Enable persistence (default behavior)
+  * <SwapWidget client={client} persistTokenSelections={true} />
+  * ```
+  *
   * @default true
   */
apps/playground-web/src/app/payments/components/LeftSection.tsx (3)

23-23: Verify the import path structure.

The import path uses a relative path (../../../components/blocks/CurrencySelector) instead of the aliased path (@/components/blocks/CurrencySelector). According to the coding guidelines, you should use consistent import patterns.

Apply this diff to use the aliased import path:

-import { CurrencySelector } from "../../../components/blocks/CurrencySelector";
+import { CurrencySelector } from "@/components/blocks/CurrencySelector";

125-125: Unnecessary explicit type assignment.

The currency parameter is already typed through CurrencySelector's props definition, so the explicit assignment currency: currency can be simplified.

Apply this diff for cleaner code:

-                    currency: currency,
+                    currency,

37-37: Dead code path for payOptions.widget.

With lockedWidget now being required, the fallback to payOptions.widget in the expression props.lockedWidget || payOptions.widget || "buy" becomes unreachable. The widget switching UI has been removed, making payOptions.widget obsolete.

Consider simplifying the logic since lockedWidget is always provided:

-  const effectiveWidget = props.lockedWidget || payOptions.widget || "buy";
+  const effectiveWidget = props.lockedWidget;

Additionally, consider removing the widget field from payOptions type definition if it's no longer used elsewhere in the codebase.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e11ace4 and d9f6028.

📒 Files selected for processing (22)
  • .changeset/honest-hands-clap.md (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/components/code.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/components/types.ts (1 hunks)
  • apps/playground-web/src/app/bridge/swap-widget/page.tsx (1 hunks)
  • apps/playground-web/src/app/data/pages-metadata.ts (2 hunks)
  • apps/playground-web/src/app/navLinks.ts (3 hunks)
  • apps/playground-web/src/app/page.tsx (2 hunks)
  • apps/playground-web/src/app/payments/components/CodeGen.tsx (1 hunks)
  • apps/playground-web/src/app/payments/components/LeftSection.tsx (5 hunks)
  • apps/playground-web/src/app/wallets/sign-in/button/connect-button-page.tsx (1 hunks)
  • apps/playground-web/src/components/blocks/CurrencySelector.tsx (1 hunks)
  • apps/playground-web/src/components/blocks/NetworkSelectors.tsx (2 hunks)
  • apps/playground-web/src/components/blocks/multi-select.tsx (0 hunks)
  • apps/playground-web/src/components/ui/TokenSelector.tsx (2 hunks)
  • apps/playground-web/src/hooks/chains.ts (1 hunks)
  • apps/playground-web/src/lib/env.ts (1 hunks)
  • apps/playground-web/src/lib/useShowMore.ts (0 hunks)
  • packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx (5 hunks)
  • packages/ui/src/components/code/getCodeHtml.tsx (2 hunks)
💤 Files with no reviewable changes (2)
  • apps/playground-web/src/lib/useShowMore.ts
  • apps/playground-web/src/components/blocks/multi-select.tsx
🚧 Files skipped from review as they are similar to previous changes (15)
  • apps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/types.ts
  • apps/playground-web/src/app/payments/components/CodeGen.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
  • apps/playground-web/src/components/blocks/NetworkSelectors.tsx
  • apps/playground-web/src/components/blocks/CurrencySelector.tsx
  • apps/playground-web/src/components/ui/TokenSelector.tsx
  • packages/ui/src/components/code/getCodeHtml.tsx
  • apps/playground-web/src/app/bridge/swap-widget/page.tsx
  • .changeset/honest-hands-clap.md
  • apps/playground-web/src/hooks/chains.ts
  • apps/playground-web/src/app/bridge/swap-widget/components/right-section.tsx
  • apps/playground-web/src/lib/env.ts
  • apps/playground-web/src/app/navLinks.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose

**/*.{ts,tsx}: Use explicit function declarations and explicit return types in TypeScript
Limit each file to one stateless, single‑responsibility function
Re‑use shared types from @/types where applicable
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics when possible
Prefer composition over inheritance; use utility types (Partial, Pick, etc.)
Lazy‑import optional features and avoid top‑level side‑effects to reduce bundle size

Files:

  • apps/playground-web/src/app/wallets/sign-in/button/connect-button-page.tsx
  • apps/playground-web/src/app/page.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/code.tsx
  • apps/playground-web/src/app/payments/components/LeftSection.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)

Files:

  • apps/playground-web/src/app/wallets/sign-in/button/connect-button-page.tsx
  • apps/playground-web/src/app/page.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/code.tsx
  • apps/playground-web/src/app/payments/components/LeftSection.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from @/components/ui/* (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
Use NavLink for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Use cn() from @/lib/utils for conditional class logic
Use design system tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components (Node edge): Start files with import "server-only";
Client Components (browser): Begin files with 'use client';
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header – never embed tokens in URLs
Return typed results (e.g., Project[], User[]) – avoid any
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys for React Query cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-js in server components

Files:

  • apps/playground-web/src/app/wallets/sign-in/button/connect-button-page.tsx
  • apps/playground-web/src/app/page.tsx
  • apps/playground-web/src/app/bridge/swap-widget/components/code.tsx
  • apps/playground-web/src/app/payments/components/LeftSection.tsx
packages/thirdweb/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

packages/thirdweb/**/*.{ts,tsx}: Every public symbol must have comprehensive TSDoc with at least one compiling @example and a custom tag (@beta, @internal, @experimental, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Lazy‑load heavy dependencies inside async paths (e.g., const { jsPDF } = await import("jspdf"))

Files:

  • packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx
🧠 Learnings (3)
📓 Common learnings
Learnt from: MananTank
PR: thirdweb-dev/js#8069
File: apps/playground-web/src/app/bridge/swap-widget/components/types.ts:3-12
Timestamp: 2025-09-18T20:09:57.022Z
Learning: The SwapWidgetPlaygroundOptions type should not include persistTokenSelections - the playground intentionally hardcodes persistTokenSelections={false} and doesn't expose it as a configurable option to users.
📚 Learning: 2025-09-18T20:09:57.022Z
Learnt from: MananTank
PR: thirdweb-dev/js#8069
File: apps/playground-web/src/app/bridge/swap-widget/components/types.ts:3-12
Timestamp: 2025-09-18T20:09:57.022Z
Learning: The SwapWidgetPlaygroundOptions type should not include persistTokenSelections - the playground intentionally hardcodes persistTokenSelections={false} and doesn't expose it as a configurable option to users.

Applied to files:

  • packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).

Applied to files:

  • apps/playground-web/src/app/payments/components/LeftSection.tsx
🧬 Code graph analysis (5)
apps/playground-web/src/app/wallets/sign-in/button/connect-button-page.tsx (1)
apps/playground-web/src/app/wallets/sign-in/components/types.ts (1)
  • ConnectPlaygroundOptions (4-26)
apps/playground-web/src/app/page.tsx (1)
apps/playground-web/src/app/data/pages-metadata.ts (1)
  • bridgeFeatureCards (252-259)
packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx (1)
packages/thirdweb/src/react/web/ui/Bridge/swap-widget/storage.ts (2)
  • getLastUsedTokens (23-41)
  • setLastUsedTokens (43-56)
apps/playground-web/src/app/bridge/swap-widget/components/code.tsx (2)
apps/playground-web/src/app/payments/components/CodeGen.tsx (1)
  • CodeGen (19-27)
apps/playground-web/src/app/bridge/swap-widget/components/types.ts (1)
  • SwapWidgetPlaygroundOptions (3-12)
apps/playground-web/src/app/payments/components/LeftSection.tsx (2)
apps/playground-web/src/components/blocks/CurrencySelector.tsx (1)
  • CurrencySelector (12-53)
apps/playground-web/src/components/blocks/NetworkSelectors.tsx (1)
  • BridgeNetworkSelector (14-98)
⏰ 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). (3)
  • GitHub Check: Lint Packages
  • GitHub Check: Size
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (18)
packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx (4)

302-302: Clean implementation of persistence control.

The centralized isPersistEnabled flag makes the persistence logic consistent throughout the component.


336-339: Correct early return for disabled persistence.

When persistence is disabled, returning undefined for the buyToken ensures no localStorage values are used, which is the expected behavior.


363-367: Good conditional handling for sellToken initialization.

The conditional passing of lastUsedSellToken ensures that when persistence is disabled, the function receives undefined instead of the stored value.


371-374: Proper guarding of localStorage writes.

The conditional check prevents unnecessary localStorage operations when persistence is disabled, and including isPersistEnabled in the dependencies ensures the effect runs when the persistence setting changes.

apps/playground-web/src/app/bridge/swap-widget/components/code.tsx (6)

1-9: Good lazy loading implementation.

The lazy loading of CodeClient follows best practices for reducing initial bundle size in playground applications.


19-27: Clean component structure with proper fallback.

The component correctly uses Suspense with a meaningful loading state and follows the established pattern from similar components in the codebase.


29-54: Well-structured theme handling logic.

The conditional theme prop generation correctly handles different theme scenarios and only includes theme helpers when color overrides are present, which keeps the generated code clean.


56-69: Good prop filtering for clean code generation.

The props object construction properly filters out default values (like "USD" for currency) to generate cleaner example code. The logic for showThirdwebBranding correctly only includes it when explicitly set to false.


71-87: Well-formatted generated code template.

The template produces clean, readable TypeScript code with proper indentation and structure. The use of stringifyProps ensures proper formatting of the JSX props.


93-105: Robust prop stringification logic.

The stringifyProps function correctly filters out undefined and empty values, and properly formats the props for JSX output with appropriate indentation.

apps/playground-web/src/app/page.tsx (2)

6-6: LGTM! Import properly added for new Bridge section.

The import for bridgeFeatureCards is correctly added to support the new Bridge feature section.


51-51: LGTM! Bridge section placement and implementation are appropriate.

The new Bridge section using bridgeFeatureCards is properly positioned between Payments and Account Abstraction sections, maintaining logical feature grouping. The implementation follows the existing pattern and structure used by other feature sections.

apps/playground-web/src/app/wallets/sign-in/button/connect-button-page.tsx (1)

62-62: LGTM! Theme synchronization approach is consistent.

The change from dynamic initialization to static initialization using defaultConnectOptions aligns with the established pattern used in the SwapWidget playground. This creates consistency across playground components where theme synchronization is handled via useEffect rather than during state initialization.

apps/playground-web/src/app/payments/components/LeftSection.tsx (5)

15-15: LGTM!

The migration to BridgeNetworkSelector is appropriate for supporting bridge-specific chains.


118-129: LGTM! Clean migration to CurrencySelector.

The migration from Select-based UI to the dedicated CurrencySelector component improves code organization and reusability.


138-143: LGTM! Consistent use of BridgeNetworkSelector.

The replacement of SingleNetworkSelector with BridgeNetworkSelector is consistent with the bridge integration theme of this PR.


150-158: Confirm removal of addNativeTokenIfMissing — ensure native token still selectable

  • Found: apps/playground-web/src/components/ui/TokenSelector.tsx does not accept addNativeTokenIfMissing and builds options only from useTokensData (native token won't be auto-added). apps/dashboard/src/@/components/blocks/TokenSelector.tsx still exposes addNativeTokenIfMissing and prepends a synthetic native token when missing.
  • Action: Confirm this removal is intentional; if native token must be selectable in payments/LeftSection, either ensure tokensQuery returns the native token for the selected chain, add equivalent "prepend native token" logic to the playground TokenSelector, or reintroduce the addNativeTokenIfMissing prop.

33-33: No action required — payments LeftSection consumers already pass required lockedWidget.

Confirmed usages: apps/playground-web/src/app/payments/fund-wallet/BuyPlayground.tsx (lockedWidget="buy"), apps/playground-web/src/app/payments/commerce/CheckoutPlayground.tsx (lockedWidget="checkout"), apps/playground-web/src/app/payments/transactions/TransactionPlayground.tsx (lockedWidget="transaction").

return (
<div className="flex w-full grow flex-col">
<Suspense fallback={<CodeLoading />}>
<CodeClient className="grow" code={getCode(props.options)} lang="ts" />
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Verify consistency with the existing CodeGen pattern.

The language is set to "ts" while the similar component in apps/playground-web/src/app/payments/components/CodeGen.tsx uses "tsx". Consider whether this should be consistent.


🏁 Script executed:

#!/bin/bash
# Check other CodeGen components to see what language they use
rg -n "lang=" apps/playground-web/src/app/ --type=tsx

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Search for all lang= usages in TSX files under the playground-web app
rg -n "lang=" -g "*.tsx" apps/playground-web/src/app/

# Inspect the CodeClient invocation in the CodeGen component for its lang value
rg -n "<CodeClient" -g "*.tsx" apps/playground-web/src/app/payments/components/CodeGen.tsx

Length of output: 3398


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== swap-widget file (first 300 lines) ==="
sed -n '1,300p' apps/playground-web/src/app/bridge/swap-widget/components/code.tsx || true

echo
echo "=== Search for getCode usages ==="
rg -n "getCode" apps/playground-web/src/app -S || true

echo
echo "=== payments CodeGen (first 200 lines) ==="
sed -n '1,200p' apps/playground-web/src/app/payments/components/CodeGen.tsx || true

echo
echo "=== All occurrences of <CodeClient ...> ==="
rg -n "<CodeClient" apps/playground-web/src/app -S || true

echo
echo "=== Locate CodeClient definition (likely component) ==="
rg -n "export (default )?.*CodeClient|function CodeClient|const CodeClient" -S || true

echo
echo "=== Fallback: search for files exporting Code components (CodeServer, CodeClient) ==="
rg -n "CodeServer|CodeClient" -S || true

Length of output: 22935


Change lang from "ts" to "tsx" in swap-widget CodeGen.
getCode(...) returns JSX (<SwapWidget ... />); use "tsx" to match other CodeGen components and ensure correct highlighting.
File: apps/playground-web/src/app/bridge/swap-widget/components/code.tsx:23

🤖 Prompt for AI Agents
In apps/playground-web/src/app/bridge/swap-widget/components/code.tsx around
line 23, the CodeClient component is using lang="ts" but getCode(...) returns
JSX (<SwapWidget ... />); change the lang prop to "tsx" so the generated code is
highlighted/treated correctly (update the prop from "ts" to "tsx" on the
CodeClient invocation).

});

const payModeId = useId();
const _payModeId = useId();
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove unused variable _payModeId.

The variable _payModeId is created with useId() but never used in the component. This appears to be a leftover from removing the widget selection UI.

Apply this diff to remove the unused variable:

-  const _payModeId = useId();
📝 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
const _payModeId = useId();
🤖 Prompt for AI Agents
In apps/playground-web/src/app/payments/components/LeftSection.tsx around line
65, the variable `_payModeId` is declared with `useId()` but is not used
anywhere in the component; remove the declaration to eliminate the unused
variable. Delete the line declaring `_payModeId` (const _payModeId = useId();)
and ensure no other references rely on it; run TypeScript/ESLint to confirm no
remaining unused identifiers.

@graphite-app
Copy link
Contributor

graphite-app bot commented Sep 19, 2025

Merge activity

<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces significant updates to the `SwapWidget` component, including a new `persistTokenSelections` prop for managing token selection persistence. It also enhances the UI components and integrates a new bridge feature.

### Detailed summary
- Added `persistTokenSelections` prop to `SwapWidget`.
- Updated `SwapWidgetPlayground` for better token management.
- Introduced `BridgeNetworkSelector` and `CurrencySelector` components.
- Added `bridgeFeatureCards` to the feature metadata.
- Enhanced `LeftSection` for improved token and currency selection.
- Updated the sidebar to include the bridge section.
- Refactored network selection logic to use the bridge API.
- Improved code formatting and organization in multiple files.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- New Features
  - SwapWidget gains a persistTokenSelections prop to opt out of saving token choices.
  - Added a Swap Widget Playground with live preview, generated code view, theme/currency/prefill/branding controls, and UI/Code tabs.
  - Playground is integrated into site navigation and feature cards under a new Bridge section.

- Chores
  - Playground now uses bridge-supported chains and improved TypeScript formatting for generated snippets.
  - Added a reusable currency selector for widget configuration.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

packages Playground Changes involving the Playground codebase. SDK Involves changes to the thirdweb SDK

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants