-
Notifications
You must be signed in to change notification settings - Fork 615
[MNY-190] Playground: Add SwapWidget #8069
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 32980f8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
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 |
WalkthroughAdds 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
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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (2)
Error: Server ErrorThe server encountered a temporary error and could not complete your request.Please try again in 30 seconds.
Comment |
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
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. |
size-limit report 📦
|
There was a problem hiding this 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 missesdefineChainimport (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.chainsandimports.walletsare unused;createWalletis never imported/used.Apply this minimal fix to always import
defineChainand 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: Prefernext/dynamicoverReact.lazyin App Router
React.lazyis not recommended in Next.js App Router.next/dynamicgives 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: DeriveisProdviagetVercelEnv()for consistencyAvoid 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 pluginUsing 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 leanTop‑level static imports pull heavy plugins into any consumer bundle. Loading inside
getCodeHtmldefers 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 sizeAnd 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:lockedWidgetis now required: simplifyeffectiveWidgetWith
lockedWidgetrequired, the fallback topayOptions.widgetis redundant.- const effectiveWidget = props.lockedWidget || payOptions.widget || "buy"; + const effectiveWidget = props.lockedWidget;
72-72: Remove unused_payModeIdThis 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 tagTo comply with
packages/thirdwebdoc standards, add a custom tag (e.g.,@betaor@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 typePrefer 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/cacheTimeGuideline 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 BridgeChainIf
BridgeChainis exported in the SDK, reuse it; otherwise consider a localtypes.tsbarrel for cross‑component reuse.Would you like me to check if
BridgeChainis exported fromthirdwebso we can import it instead of redefining?
54-70: Header usage is fine; add basic network error guardMinor: check
res.okbeforeres.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 querySame 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‑throughReturn type per guidelines; optional
classNameimproves 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
cnimport 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
langprop 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.
📒 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@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless 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@/typeswhere applicable
Prefertypealiases overinterfaceexcept for nominal shapes
Avoidanyandunknownunless 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.tsxapps/playground-web/src/app/bridge/swap-widget/components/left-section.tsxapps/playground-web/src/app/bridge/swap-widget/components/right-section.tsxapps/playground-web/src/app/bridge/swap-widget/components/types.tsapps/playground-web/src/hooks/chains.tsapps/playground-web/src/app/bridge/swap-widget/page.tsxapps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsxapps/playground-web/src/lib/env.tspackages/ui/src/components/code/getCodeHtml.tsxapps/playground-web/src/app/data/pages-metadata.tsapps/playground-web/src/app/navLinks.tsapps/playground-web/src/app/bridge/swap-widget/components/code.tsxapps/playground-web/src/app/payments/components/LeftSection.tsxapps/playground-web/src/app/page.tsxpackages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsxapps/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.tsxapps/playground-web/src/app/bridge/swap-widget/components/left-section.tsxapps/playground-web/src/app/bridge/swap-widget/components/right-section.tsxapps/playground-web/src/app/bridge/swap-widget/components/types.tsapps/playground-web/src/hooks/chains.tsapps/playground-web/src/app/bridge/swap-widget/page.tsxapps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsxapps/playground-web/src/lib/env.tspackages/ui/src/components/code/getCodeHtml.tsxapps/playground-web/src/app/data/pages-metadata.tsapps/playground-web/src/app/navLinks.tsapps/playground-web/src/app/bridge/swap-widget/components/code.tsxapps/playground-web/src/app/payments/components/LeftSection.tsxapps/playground-web/src/app/page.tsxpackages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsxapps/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
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/playground-web/src/app/payments/components/CodeGen.tsxapps/playground-web/src/app/bridge/swap-widget/components/left-section.tsxapps/playground-web/src/app/bridge/swap-widget/components/right-section.tsxapps/playground-web/src/app/bridge/swap-widget/components/types.tsapps/playground-web/src/hooks/chains.tsapps/playground-web/src/app/bridge/swap-widget/page.tsxapps/playground-web/src/app/bridge/swap-widget/components/swap-widget-playground.tsxapps/playground-web/src/lib/env.tsapps/playground-web/src/app/data/pages-metadata.tsapps/playground-web/src/app/navLinks.tsapps/playground-web/src/app/bridge/swap-widget/components/code.tsxapps/playground-web/src/app/payments/components/LeftSection.tsxapps/playground-web/src/app/page.tsxapps/playground-web/src/components/blocks/NetworkSelectors.tsx
.changeset/*.md
📄 CodeRabbit inference engine (AGENTS.md)
.changeset/*.md: Each change inpackages/*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.tsfile
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@exampleand 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.tsxapps/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 LGTMSwitching to
@/components/ui/LoadingDotsaligns 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 scopedNice 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.tsxintentionally setspersistTokenSelections={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 supplylockedWidgetTransactionPlayground, 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 goodImport aligns with new metadata export and keeps section data co-located.
51-51: Bridge section placement is consistentSits after Payments and before Account Abstraction, matching nav and metadata.
apps/playground-web/src/app/navLinks.ts (3)
3-8: Icon imports follow lint rulelucide-react icons use the “Icon” suffix as required.
212-224: New Bridge submenu is well-formed
subMenushape matchesShadcnSidebarLinkand route is coherent with the new page.
261-261: Sidebar ordering OKInserting
bridgeafter Payments matches the homepage section order.apps/playground-web/src/app/data/pages-metadata.ts (2)
6-6: Icon import LGTMUsing BringToFrontIcon keeps visual language consistent across Bridge assets.
252-259: Bridge feature card reads correctlyCopy, link, and icon align with the new route. No issues.
apps/playground-web/src/components/blocks/NetworkSelectors.tsx (2)
8-8: Hook import is correctBridge chains hook is referenced from the new source of truth.
244-328: BridgeNetworkSelector — add explicit return type & validate ChainIcon sourceAdd
): JSX.Elementreturn type; ensureChainIconreceivesipfsSrconly foripfs://values and fallback to an http(s)srcif 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
iconfield and the ChainIcon component/package or file path so I can verify the correct prop to use; otherwise confirm whetherchain.iconvalues areipfs://or if ChainIcon supports an http(s)src.apps/playground-web/src/app/bridge/swap-widget/page.tsx (1)
26-29: SwapWidget docs URL verifiedCanonical 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
setThemeTypefunction correctly maintains immutability while updating the nested theme state.
41-41: LGTM! Proper use of useId for unique identifiers.Good practice using React's
useIdhook 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
tokenAddresswhen 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
quotesandstringifyPropsfunctions handle their respective formatting tasks properly, withstringifyPropscorrectly 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 Report✅ All modified and coverable lines are covered by tests. 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
🚀 New features to boost your workflow:
|
There was a problem hiding this 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 guidelinesType 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 theascast.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.okbefore 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.
📒 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@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless 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@/typeswhere applicable
Prefertypealiases overinterfaceexcept for nominal shapes
Avoidanyandunknownunless 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.tsxapps/playground-web/src/components/blocks/CurrencySelector.tsxapps/playground-web/src/app/page.tsxapps/playground-web/src/app/bridge/swap-widget/components/left-section.tsxapps/playground-web/src/hooks/chains.tspackages/ui/src/components/code/getCodeHtml.tsxpackages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsxapps/playground-web/src/app/bridge/swap-widget/components/right-section.tsxapps/playground-web/src/app/payments/components/LeftSection.tsxapps/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.tsxapps/playground-web/src/components/blocks/CurrencySelector.tsxapps/playground-web/src/app/page.tsxapps/playground-web/src/app/bridge/swap-widget/components/left-section.tsxapps/playground-web/src/hooks/chains.tspackages/ui/src/components/code/getCodeHtml.tsxpackages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsxapps/playground-web/src/app/bridge/swap-widget/components/right-section.tsxapps/playground-web/src/app/payments/components/LeftSection.tsxapps/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
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/playground-web/src/components/ui/TokenSelector.tsxapps/playground-web/src/components/blocks/CurrencySelector.tsxapps/playground-web/src/app/page.tsxapps/playground-web/src/app/bridge/swap-widget/components/left-section.tsxapps/playground-web/src/hooks/chains.tsapps/playground-web/src/app/bridge/swap-widget/components/right-section.tsxapps/playground-web/src/app/payments/components/LeftSection.tsxapps/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@exampleand 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.tsxapps/playground-web/src/app/bridge/swap-widget/components/right-section.tsxapps/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.tsxapps/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 — LGTMImport 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
isPersistEnabledis 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 SwapWidgetPlaygroundOptionsrg 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 behavioruseTokensData (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.
There was a problem hiding this 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
persistTokenSelectionsprop is a useful addition for controlling localStorage persistence. The implementation logic is sound and maintains backward compatibility with the default value oftrue.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
currencyparameter is already typed throughCurrencySelector's props definition, so the explicit assignmentcurrency: currencycan be simplified.Apply this diff for cleaner code:
- currency: currency, + currency,
37-37: Dead code path forpayOptions.widget.With
lockedWidgetnow being required, the fallback topayOptions.widgetin the expressionprops.lockedWidget || payOptions.widget || "buy"becomes unreachable. The widget switching UI has been removed, makingpayOptions.widgetobsolete.Consider simplifying the logic since
lockedWidgetis always provided:- const effectiveWidget = props.lockedWidget || payOptions.widget || "buy"; + const effectiveWidget = props.lockedWidget;Additionally, consider removing the
widgetfield frompayOptionstype 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.
📒 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@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless 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@/typeswhere applicable
Prefertypealiases overinterfaceexcept for nominal shapes
Avoidanyandunknownunless 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.tsxapps/playground-web/src/app/page.tsxpackages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsxapps/playground-web/src/app/bridge/swap-widget/components/code.tsxapps/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.tsxapps/playground-web/src/app/page.tsxpackages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsxapps/playground-web/src/app/bridge/swap-widget/components/code.tsxapps/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
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/playground-web/src/app/wallets/sign-in/button/connect-button-page.tsxapps/playground-web/src/app/page.tsxapps/playground-web/src/app/bridge/swap-widget/components/code.tsxapps/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@exampleand 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
isPersistEnabledflag makes the persistence logic consistent throughout the component.
336-339: Correct early return for disabled persistence.When persistence is disabled, returning
undefinedfor 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
lastUsedSellTokenensures that when persistence is disabled, the function receivesundefinedinstead of the stored value.
371-374: Proper guarding of localStorage writes.The conditional check prevents unnecessary localStorage operations when persistence is disabled, and including
isPersistEnabledin 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
CodeClientfollows 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 forshowThirdwebBrandingcorrectly only includes it when explicitly set tofalse.
71-87: Well-formatted generated code template.The template produces clean, readable TypeScript code with proper indentation and structure. The use of
stringifyPropsensures proper formatting of the JSX props.
93-105: Robust prop stringification logic.The
stringifyPropsfunction 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
bridgeFeatureCardsis correctly added to support the new Bridge feature section.
51-51: LGTM! Bridge section placement and implementation are appropriate.The new Bridge section using
bridgeFeatureCardsis 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
defaultConnectOptionsaligns 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
BridgeNetworkSelectoris appropriate for supporting bridge-specific chains.
118-129: LGTM! Clean migration to CurrencySelector.The migration from Select-based UI to the dedicated
CurrencySelectorcomponent improves code organization and reusability.
138-143: LGTM! Consistent use of BridgeNetworkSelector.The replacement of
SingleNetworkSelectorwithBridgeNetworkSelectoris 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 requiredlockedWidget.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" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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=tsxLength 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.tsxLength 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 || trueLength 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(); |
There was a problem hiding this comment.
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.
| 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.
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 -->
d9f6028 to
32980f8
Compare

PR-Codex overview
This PR introduces a new
persistTokenSelectionsprop for theSwapWidget, allowing developers to control whether token selections persist in local storage. It also enhances theBridgeNetworkSelectorand updates various components to improve functionality and organization.Detailed summary
persistTokenSelectionsprop toSwapWidget.BridgeNetworkSelectorto useuseBridgeSupportedChainshook.LeftSectionto useCurrencySelectorfor currency selection.SwapWidgetPlaygroundfor better widget customization.TokenSelectorandCurrencySelectorcomponents.bridgeFeatureCardsto metadata.Summary by CodeRabbit
New Features
Chores