Skip to content

Feat/new home + switch wallet + ux change#547

Merged
Agilulfo1820 merged 76 commits into
mainfrom
feat/new-home
Dec 22, 2025
Merged

Feat/new home + switch wallet + ux change#547
Agilulfo1820 merged 76 commits into
mainfrom
feat/new-home

Conversation

@Agilulfo1820

@Agilulfo1820 Agilulfo1820 commented Dec 3, 2025

Copy link
Copy Markdown
Member

New homepage + back up old homepage, will be useful later + changed vechainkit navigation + implemented switch wallet functionality

Summary by CodeRabbit

  • New Features

    • Full multilingual support (7 languages), language selector, translations, and translation tooling.
    • New homepage sections and interactive components: carousel, app showcase, testimonials, FAQ, floating Get Started button, and playground demos (transactions, signing, smart account, data reading).
  • Improvements

    • Homepage redesign and rebranding to "VeKit" with updated metadata.
    • Enhanced account/profile and modal flows, wallet switching, theme and button styles, enforced light-mode behavior.
  • Chores

    • Added translation script, config, and dev tooling.

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

@coderabbitai

coderabbitai Bot commented Dec 3, 2025

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a localized homepage and a new playground app, introduces many reusable UI components (carousel, feature sections, header, floating CTA), updates theme tokens, expands i18n resources, implements language detection/sync and wallet-switching flows, and makes multiple modal and AccountModal UX adjustments.

Changes

Cohort / File(s) Summary
Homepage core & layout
examples/homepage/src/app/layout.tsx, examples/homepage/src/app/pages/Home.tsx, examples/homepage/src/app/globals.css
Switches to light theme, injects ColorModeScript and ForceLightMode, updates metadata/OG images, replaces wallet-conditional homepage with a composed homepage using new sections, removes previous body centering CSS.
Homepage i18n & config
examples/homepage/i18n.ts, examples/homepage/translo.config.json, examples/homepage/package.json, examples/homepage/.env.example, examples/homepage/src/app/languages/*
Adds a custom language detector (localStorage → browser → fallback), a cache helper, translo config, translate script & dev dep, and expands translation JSONs for multiple languages.
Homepage components — features
examples/homepage/src/app/components/features/* (HeroSection, AppShowcase, AppCard, appData, FAQSection, FeatureSection, InfoSection, LoginMethodsSection, ScrollableInfoSections, LanguagesSection, TestimonialSection, FloatingGetStartedButton)
Adds many feature components: hero, app showcase + carousel usage, scrollable info sections (with progress/refs), login methods, languages grid, testimonial, refactored FAQ (i18n-driven), and a floating "Get Started" CTA.
Homepage layout & ui
examples/homepage/src/app/components/layout/Header/*, examples/homepage/src/app/components/ui/Carousel/*, examples/homepage/src/app/components/ForceLightMode.tsx
New Header + LanguageDropdown, a generic Carousel (infinite-loop, drag/snap, pagination), and a ForceLightMode client component clearing color-mode storage.
Homepage theme & tokens
examples/homepage/src/app/theme/* (theme.tsx, button.ts, card.ts, colors.ts)
Renames darkTheme→theme, sets initialColorMode to light, adds hero fontSizes/space tokens, new button variants, card feature variants, and featureBackgrounds color tokens.
Playground app (new)
examples/playground/* (layout, page, providers, i18n, theme, components, constants, config, package.json, tsconfig, env examples, README, public)
Adds a standalone playground Next.js example with layout, VechainKitProviderWrapper, i18n, theme, many demo components, translo config, env examples, next.config, eslint, tsconfig, and README.
Playground components & UI
examples/playground/src/app/components/features/*, examples/playground/src/app/components/ui/*, examples/playground/src/app/layout.tsx, examples/playground/src/app/globals.css
Adds demo components (AccountInfo, ConnectionInfo, DaoInfo, DataReading, TransactionExamples, SigningExample, FeaturesToTry, CollapsibleCard, ThemeToggle, LanguageSelector, etc.) and a playground global stylesheet.
VeChain Kit: wallet & modal features
packages/vechain-kit/src/hooks/api/wallet/useSwitchWallet.ts, packages/vechain-kit/src/hooks/api/wallet/index.ts, packages/vechain-kit/src/hooks/modals/*, packages/vechain-kit/src/hooks/modals/useWalletModal.tsx
Introduces useSwitchWallet hook (switchWallet + isSwitching), re-exports it, adds useSettingsModal hook/provider, and updates useWalletModal.open to accept AccountModalOptions.
VeChain Kit: AccountModal & profile flows
packages/vechain-kit/src/components/AccountModal/*, packages/vechain-kit/src/components/common/*, packages/vechain-kit/src/components/WalletButton/WalletButton.tsx
Changes AccountModal default initialContent ('main'→'profile'), adds setCurrentContent props to ProfileCard/AddressDisplay and related flows, introduces in-app wallet-switch UI, WalletButton gains optional label prop, and many Box wrappers replaced with fragments.
VeChain Kit: provider & types
packages/vechain-kit/src/providers/VechainKitProviderWrapper.tsx, packages/vechain-kit/src/types/veworld.d.ts, packages/vechain-kit/package.json
VechainKitProviderWrapper adds LanguageSync (storage listener + polling) and passes localized homepage translations; adds Window.vechain type declarations; bumps @vechain/dapp-kit-react versions.
VeChain Kit: accessibility & minor UI
packages/vechain-kit/src/components/common/BaseBottomSheet.tsx, various AccountModal content files
Replaces VisuallyHidden usage with visually-hidden Drawer.Title, removes extra Box wrappers in many modal contents, updates FeatureAnnouncementCard navigation target to 'profile', and small UI adjustments.
Localization (packages)
packages/vechain-kit/src/languages/* (en, de, es, fr, it, ja, zh)
Expands/updates translation catalogs across locales with many new keys (backup, fee/gas token prefs, insufficient-balance variants, token-priority UI, copied! messages, etc.).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas needing extra attention:

  • Language detection/sync in VechainKitProviderWrapper (storage events, polling, race conditions).
  • Modal navigation/default content changes across AccountModal, ProfileContent, FeatureAnnouncementCard.
  • Carousel infinite-loop, real-index mapping, drag/scroll snapping and edge cases.
  • useSwitchWallet hook and in-app browser wallet-switch UX (isSwitching state, error handling, user feedback).
  • AddressDisplay / ProfileCard setCurrentContent integration and form removal that affects submission behavior.
  • TransactionExamples transaction build/send flows and retry/toast/modal integration.

Possibly related PRs

  • Fix/UI #553 — Overlapping AccountModal and modal navigation refactors (settings/profile flows).
  • Feat/bottomsheet #555 — Related BaseBottomSheet / ConnectModal / bottom-sheet and modal behavior changes.
  • Fix/transaction modal #554 — Related TransactionModal / TransactionExamples and toast/modal interactions.

Suggested reviewers

  • victorkl400
  • mikeredmond

"🐰 I hopped through code and left a trail,
New languages, carousels, and a floating tail.
Wallets that switch with a gentle flip,
Themes in the daylight, translations on my lip.
Hooray — the homepage blooms; I take a celebratory nibble! 🥕"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.38% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feat/new home + switch wallet + ux change' is related to the changeset, covering multiple real aspects: new homepage, wallet switching, and UX changes. However, it is overly broad and does not clearly prioritize the main change—it reads more like a bulleted summary than a focused description of the primary objective.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/new-home

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

❤️ Share

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

@github-actions

github-actions Bot commented Dec 3, 2025

Copy link
Copy Markdown
Contributor

Size Change: +87.3 kB (+1.59%)

Total Size: 5.58 MB

Filename Size Change
packages/vechain-kit/dist/index-5_zBNaxA.d.cts 0 B -146 kB (removed) 🏆
packages/vechain-kit/dist/index-5_zBNaxA.d.cts.map 0 B -42.2 kB (removed) 🏆
packages/vechain-kit/dist/index-Da5GOsB_.d.mts 0 B -5.63 kB (removed) 🏆
packages/vechain-kit/dist/index-Da5GOsB_.d.mts.map 0 B -2.99 kB (removed) 🏆
packages/vechain-kit/dist/index-Zan35pzN.d.mts 0 B -146 kB (removed) 🏆
packages/vechain-kit/dist/index-Zan35pzN.d.mts.map 0 B -42.2 kB (removed) 🏆
packages/vechain-kit/dist/index.cjs 597 kB +17.2 kB (+2.96%)
packages/vechain-kit/dist/index.cjs.map 1.8 MB +25.2 kB (+1.42%)
packages/vechain-kit/dist/index.mjs 564 kB +17.1 kB (+3.12%)
packages/vechain-kit/dist/index.mjs.map 1.75 MB +25.2 kB (+1.46%)
packages/vechain-kit/dist/index--hSO7Xv4.d.mts 5.63 kB +5.63 kB (new file) 🆕
packages/vechain-kit/dist/index--hSO7Xv4.d.mts.map 2.99 kB +2.99 kB (new file) 🆕
packages/vechain-kit/dist/index-Buk9QlAS.d.cts 147 kB +147 kB (new file) 🆕
packages/vechain-kit/dist/index-Buk9QlAS.d.cts.map 42.4 kB +42.4 kB (new file) 🆕
packages/vechain-kit/dist/index-D-EYvpMZ.d.mts 147 kB +147 kB (new file) 🆕
packages/vechain-kit/dist/index-D-EYvpMZ.d.mts.map 42.4 kB +42.4 kB (new file) 🆕
ℹ️ View Unchanged
Filename Size Change
packages/vechain-kit/dist/assets 4.1 kB 0 B
packages/vechain-kit/dist/assets-aAdDxPJu.mjs 50.1 kB 0 B
packages/vechain-kit/dist/assets-aAdDxPJu.mjs.map 70.2 kB 0 B
packages/vechain-kit/dist/assets-DXVXPy3w.cjs 54.8 kB 0 B
packages/vechain-kit/dist/assets-DXVXPy3w.cjs.map 71.6 kB 0 B
packages/vechain-kit/dist/assets/index.cjs 716 B 0 B
packages/vechain-kit/dist/assets/index.d.cts 973 B 0 B
packages/vechain-kit/dist/assets/index.d.mts 973 B 0 B
packages/vechain-kit/dist/assets/index.mjs 718 B 0 B
packages/vechain-kit/dist/index-DSMUkHcK.d.cts 5.63 kB 0 B
packages/vechain-kit/dist/index-DSMUkHcK.d.cts.map 2.99 kB 0 B
packages/vechain-kit/dist/index.d.cts 20.1 kB +194 B (+0.98%)
packages/vechain-kit/dist/index.d.mts 20.1 kB +194 B (+0.98%)
packages/vechain-kit/dist/utils 4.1 kB 0 B
packages/vechain-kit/dist/utils-Bl-JeVTg.cjs 26.2 kB 0 B
packages/vechain-kit/dist/utils-Bl-JeVTg.cjs.map 63 kB 0 B
packages/vechain-kit/dist/utils-DAs6kMGs.mjs 21.1 kB 0 B
packages/vechain-kit/dist/utils-DAs6kMGs.mjs.map 62.7 kB 0 B
packages/vechain-kit/dist/utils/index.cjs 1.91 kB 0 B
packages/vechain-kit/dist/utils/index.d.cts 2.94 kB 0 B
packages/vechain-kit/dist/utils/index.d.mts 2.94 kB 0 B
packages/vechain-kit/dist/utils/index.mjs 1.93 kB 0 B

compressed-size-action

@github-actions

github-actions Bot commented Dec 4, 2025

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Dec 4, 2025

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Dec 4, 2025

Copy link
Copy Markdown
Contributor

1 similar comment
@github-actions

github-actions Bot commented Dec 4, 2025

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Dec 4, 2025

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Dec 4, 2025

Copy link
Copy Markdown
Contributor

This reverts commit 89f72d5, reversing
changes made to d325bb7.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (3)
packages/vechain-kit/src/languages/zh.json (1)

267-267: Translate "Read More" and "Show Less" for consistency.

These English strings stand out as untranslated in an otherwise fully localized Chinese language file. For consistency with the rest of the translations, consider translating these to Chinese.

🔎 Suggested translations
-  "Read More": "Read More",
+  "Read More": "阅读更多",
-  "Show Less": "Show Less",
+  "Show Less": "收起",

Also applies to: 303-303

packages/vechain-kit/src/languages/fr.json (2)

267-267: Consider translating these UI labels.

Lines 267 and 303 contain English text ("Read More" and "Show Less") in the French translation file. While other UI strings are translated, these remain in English.

🔎 Suggested translations
-  "Read More": "Read More",
+  "Read More": "Lire la suite",
-  "Show Less": "Show Less",
+  "Show Less": "Afficher moins",

Also applies to: 303-303


1-418: Translate untranslated UI labels to French.

The French translation file contains two English strings that should be translated: "Read More" and "Show Less". These should have French equivalents (e.g., "Lire la suite" / "Lire plus" and "Afficher moins") to match the pattern used for "Show More" ("Afficher plus") and maintain translation completeness.

♻️ Duplicate comments (4)
packages/vechain-kit/src/languages/en.json (1)

166-166: Fix typo: "enchanced" → "enhanced"

The typo "enchanced security" should be "enhanced security" in the multiclause transaction message.

🔎 Suggested fix
-  "Good news! Multiclause transactions are now fully supported for smart accounts. You can now enjoy a better user experience, lower gas costs, and enchanced security.": "Good news! Multiclause transactions are now fully supported for smart accounts. You can now enjoy a better user experience, lower gas costs, and enchanced security.",
+  "Good news! Multiclause transactions are now fully supported for smart accounts. You can now enjoy a better user experience, lower gas costs, and enhanced security.": "Good news! Multiclause transactions are now fully supported for smart accounts. You can now enjoy a better user experience, lower gas costs, and enhanced security.",
packages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsx (3)

28-33: Remove unused props from the type definition.

The props setCurrentContent (lines 28-30) and onClose (line 33) are declared in the Props type but commented out in the component destructuring (lines 40, 43). This creates an inconsistent public API where consumers can pass these props, but they have no effect. Either restore their usage or remove them from the type definition entirely.

🔎 Suggested fix
 type Props = {
     wallet: Wallet;
-    setCurrentContent: React.Dispatch<
-        React.SetStateAction<AccountModalContentTypes>
-    >;
     size?: string;
     onClick?: () => void;
-    onClose: () => void;
     mt?: number;
     style?: StackProps;
 };

Also applies to: 40-40, 43-43


66-69: Remove commented-out code.

The handleLogout function is commented out. If this functionality is no longer needed, remove the commented code entirely to keep the codebase clean. If it's temporarily disabled, add a TODO comment explaining when it should be restored.


141-158: Remove commented-out logout button.

The entire logout button implementation is commented out. Remove this dead code to improve maintainability, or add a TODO comment if restoration is planned.

🧹 Nitpick comments (3)
packages/vechain-kit/src/languages/es.json (2)

152-152: Consider standardizing fee terminology for consistency.

The translations use both "comisión" and "tarifa" to refer to fees:

  • Line 152: "Fee" → "Comisión"
  • Line 154: "Fee token" → "Token de comisión"
  • Line 163: "Gas fee" → "Tarifa de gas"
  • Line 162: "Gas Token Preferences" → "Preferencias de tokens de gas"
  • Line 289: Uses "tarifa" for fee

For better user experience and consistency, consider standardizing to one term throughout. "Tarifa" is more commonly used in blockchain/financial contexts in Spanish.

🔎 Suggested standardization
-  "Fee": "Comisión",
+  "Fee": "Tarifa",
-  "Fee token": "Token de comisión",
+  "Fee token": "Token de tarifa",

Also applies to: 154-154, 162-163, 289-289


333-333: Minor: Consider plural form for "tokens".

Since "Token Priority Order" refers to ordering multiple tokens, the plural form would be more grammatically accurate.

🔎 Suggested change
-  "Token Priority Order": "Orden de prioridad de token",
+  "Token Priority Order": "Orden de prioridad de tokens",
packages/vechain-kit/src/languages/zh.json (1)

418-418: Add newline at end of file.

JSON files should end with a newline character per standard formatting conventions.

🔎 Proposed fix
 }
+
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89f72d5 and 92fc295.

📒 Files selected for processing (13)
  • packages/vechain-kit/src/components/AccountModal/AccountModal.tsx
  • packages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsx
  • packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx
  • packages/vechain-kit/src/components/ConnectModal/Contents/MainContent.tsx
  • packages/vechain-kit/src/hooks/api/wallet/index.ts
  • packages/vechain-kit/src/hooks/api/wallet/useSwitchWallet.ts
  • packages/vechain-kit/src/languages/de.json
  • packages/vechain-kit/src/languages/en.json
  • packages/vechain-kit/src/languages/es.json
  • packages/vechain-kit/src/languages/fr.json
  • packages/vechain-kit/src/languages/it.json
  • packages/vechain-kit/src/languages/ja.json
  • packages/vechain-kit/src/languages/zh.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/vechain-kit/src/hooks/api/wallet/useSwitchWallet.ts
  • packages/vechain-kit/src/languages/ja.json
  • packages/vechain-kit/src/components/AccountModal/AccountModal.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/migration-guide-to-v2.mdc)

**/*.{ts,tsx}: In VeChain Kit Version 2, use useThor instead of useConnex for contract interactions
For single contract read operations, use the useCallClause hook with the pattern: import dependencies, define ABI and method as const, create query key function using getCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use the executeMultipleClausesCall utility wrapped in a useQuery hook with the pattern: define query key function, use executeMultipleClausesCall in queryFn mapping items to clause objects, and transform results
For transaction building and sending, use the useBuildTransaction hook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Always provide an arguments array for contract calls, even when no parameters are required - use an empty array for parameter-less functions to enable TypeScript type checking
Always conditionally enable queries using the enabled property to prevent unnecessary contract calls, checking for all required parameters: enabled: !!requiredParam && !!otherRequiredParam
Use the select option in useCallClause or transform data in queryFn to handle data transformation, particularly for converting BigInt values to strings and normalizing contract return data
Maintain consistent query key patterns: use getCallClauseQueryKeyWithArgs for contract calls with arguments and getCallClauseQueryKey for calls without arguments to ensure proper caching and invalidation
Use TypeScript as const assertions for method names and as 0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions

Files:

  • packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx
  • packages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsx
  • packages/vechain-kit/src/components/ConnectModal/Contents/MainContent.tsx
  • packages/vechain-kit/src/hooks/api/wallet/index.ts
🧠 Learnings (3)
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript `as const` assertions for method names and `as `0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions

Applied to files:

  • packages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsx
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : Use the `select` option in useCallClause or transform data in queryFn to handle data transformation, particularly for converting BigInt values to strings and normalizing contract return data

Applied to files:

  • packages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsx
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : In VeChain Kit Version 2, use `useThor` instead of `useConnex` for contract interactions

Applied to files:

  • packages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsx
  • packages/vechain-kit/src/components/ConnectModal/Contents/MainContent.tsx
  • packages/vechain-kit/src/hooks/api/wallet/index.ts
🧬 Code graph analysis (2)
packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx (4)
packages/vechain-kit/src/hooks/api/wallet/useWallet.ts (1)
  • useWallet (61-275)
packages/vechain-kit/src/hooks/api/wallet/useSwitchWallet.ts (1)
  • useSwitchWallet (13-39)
packages/vechain-kit/src/components/common/StickyHeaderContainer.tsx (1)
  • StickyHeaderContainer (16-140)
packages/vechain-kit/src/components/common/ModalSettingsButton.tsx (1)
  • ModalSettingsButton (10-67)
packages/vechain-kit/src/components/ConnectModal/Contents/MainContent.tsx (2)
packages/vechain-kit/src/hooks/api/wallet/useWallet.ts (1)
  • useWallet (61-275)
packages/vechain-kit/src/hooks/index.ts (1)
  • useWallet (17-17)
⏰ 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: deploy
  • GitHub Check: Lint & Build
  • GitHub Check: build-size-check
  • GitHub Check: e2e-test
🔇 Additional comments (18)
packages/vechain-kit/src/languages/es.json (1)

1-418: Overall translation quality looks good.

The Spanish translations are accurate, contextually appropriate, and maintain proper grammar and punctuation throughout. Placeholders are correctly preserved, and the expanded localization coverage aligns well with the PR's objective to support the new homepage and enhanced UX.

packages/vechain-kit/src/languages/zh.json (1)

1-418: Well-structured translations with proper placeholder preservation.

The translation additions are comprehensive and properly maintain all placeholders (e.g., {{domain}}, {{email}}, {{symbol}}) and HTML tags (e.g., <bold>). The JSON structure is valid, and the new keys support the wallet switching and gas token preference features mentioned in the PR objectives.

packages/vechain-kit/src/languages/de.json (3)

5-5: LGTM! Privacy banner and new keys properly translated.

The privacy banner translation has been improved with better punctuation and clarity while preserving the HTML bold tags. New translation keys for "Apply" and "Backup" are correctly added.

Also applies to: 37-37, 49-49


57-59: Excellent expansion of gas token and domain management translations.

The new translation keys comprehensively cover:

  • Domain confirmation and management workflows (lines 57-59)
  • Gas token preferences and fee handling (lines 68, 152, 154, 162, 289, 333, 355)
  • Context-specific insufficient balance messages (lines 177-181)
  • Advanced UI options (lines 302, 393, 396)

All variable interpolations ({{domain}}, {{symbol}}, {{appName}}) are correctly preserved, and the translations maintain consistent terminology throughout.

Also applies to: 62-62, 68-68, 134-134, 152-152, 154-154, 160-162, 169-169, 177-181, 289-289, 302-302, 333-333, 355-355, 393-393, 396-396


116-116: New UX translations align with PR objectives.

The additions include:

  • "Switch" (line 318) - directly supports the wallet switching functionality mentioned in the PR
  • "Explore ecosystem" (line 146) - enhances navigation UX
  • User feedback messages ("Copied!", "Loading quote...") for improved user experience

All translations maintain proper formatting and punctuation.

Also applies to: 146-146, 205-205, 318-318

packages/vechain-kit/src/components/ConnectModal/Contents/MainContent.tsx (4)

14-15: LGTM! Imports are correctly added.

The useEffect and useWallet imports are necessary for the new auto-close functionality.


20-27: LGTM! Props updated correctly.

The addition of the onClose prop with the correct type signature enables the component to automatically close the modal upon successful connection.


29-29: LGTM! Efficient hook usage.

Correctly destructures only the connection object needed for the auto-close logic.


46-50: LGTM! Auto-close logic correctly implemented.

The effect properly closes the modal when connection succeeds, providing good UX. The dependencies are correct, and the pattern follows React best practices.

packages/vechain-kit/src/languages/it.json (1)

1-418: LGTM! High-quality Italian localization work.

The Italian translations are grammatically correct, contextually appropriate, and maintain proper preservation of all placeholders ({{domain}}, {{symbol}}, etc.) and HTML tags (). The JSON syntax is valid and all entries are properly formatted.

Key strengths:

  • Consistent terminology for blockchain/crypto terms (commissione, token, gas)
  • Proper Italian grammar and sentence structure throughout
  • Clear and user-friendly error messages
  • Appropriate handling of technical terms (keeping some English terms like "Backup", "token", "gas" which is common in Italian tech contexts)
packages/vechain-kit/src/languages/en.json (1)

1-165: LGTM! Translation strings are comprehensive and well-structured.

The English translation file provides excellent coverage of wallet functionality, blockchain concepts, and user interactions. The strings are clear, user-friendly, and professionally written. The JSON structure is valid and consistent throughout.

Also applies to: 167-361, 363-406

packages/vechain-kit/src/languages/fr.json (1)

5-5: LGTM: Placeholder preservation is correct.

All template variables ({{domain}}, {{symbol}}, {{email}}, {{name}}, etc.) and special formatting tags () are properly preserved in the French translations, ensuring proper runtime substitution.

Also applies to: 57-57, 58-58, 59-59, 182-182, 248-248, 327-327, 398-398

packages/vechain-kit/src/hooks/api/wallet/index.ts (1)

21-21: LGTM! Clean addition of the new hook export.

The useSwitchWallet export is correctly added to the barrel file and follows the established pattern.

packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx (3)

32-33: Proper integration of wallet switching functionality.

The hooks are correctly used with appropriate destructuring. The connection object provides isInAppBrowser detection, and switchWallet/isSwitching enable the new wallet-switching flow.


102-144: Clean conditional rendering for in-app browser wallet switching.

The logic correctly distinguishes between in-app browser environments (showing Switch button) and regular browsers (showing Logout button). The switch flow uses the new switchWallet hook with proper loading/disabled states, while the logout flow maintains the existing disconnect-confirm pattern with proper navigation callbacks.


60-72: The review comment is incorrect. ProfileCard actively uses the setCurrentContent prop in multiple ways:

  1. To conditionally apply cursor styling (line 83)
  2. Directly in an onClick handler to navigate to account-customization (lines 84-96)
  3. To conditionally render the edit pencil icon (line 112)
  4. To pass to the AddressDisplay component (line 204)

Passing setCurrentContent to ProfileCard is not redundant; it is necessary for the component to function correctly. The onEditClick callback in ProfileContent independently handles edit button clicks, while the setCurrentContent prop enables ProfileCard's internal avatar click interaction.

Likely an incorrect or invalid review comment.

packages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsx (2)

100-103: Good internationalization of copy feedback.

The copied state now displays a translated "Copied!" message, properly supporting multiple languages. The fallback to domain/address display is also correct.


116-139: Clean conditional UI for wallet switching vs. copy.

The implementation correctly renders a switch wallet button with loading states for in-app browsers, and a copy-to-clipboard button for regular browsers. The icon choices (LuArrowLeftRight for switch, LuCopy/LuCheck for copy) are intuitive, and the sizing/styling is consistent.

"VeChain Kit": "VeChain Kit",
"VeChain, headquartered in San Marino, Europe, is a pioneering blockchain ecosystem and creator of VeChainThor, a world-class smart contract platform driving real-world blockchain adoption. Founded in 2015 by Sunny Lu, VeChain has consistently worked to deliver a transparent, efficient, scalable, and adaptable blockchain solution.": "VeChain, headquartered in San Marino, Europe, is a pioneering blockchain ecosystem and creator of VeChainThor, a world-class smart contract platform driving real-world blockchain adoption. Founded in 2015 by Sunny Lu, VeChain has consistently worked to deliver a transparent, efficient, scalable, and adaptable blockchain solution.",
"VeWorld Wallet": "VeWorld Wallet",
"Vechain Kit Policy": "Vechain Kit Policy",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix brand name capitalization: "Vechain" → "VeChain"

Line 362 uses "Vechain Kit Policy" but should be "VeChain Kit Policy" to maintain consistent brand capitalization throughout the application. Compare with Line 359 which correctly uses "VeChain Kit".

🔎 Suggested fix
-  "Vechain Kit Policy": "Vechain Kit Policy",
+  "VeChain Kit Policy": "VeChain Kit Policy",
📝 Committable suggestion

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

Suggested change
"Vechain Kit Policy": "Vechain Kit Policy",
"VeChain Kit Policy": "VeChain Kit Policy",
🤖 Prompt for AI Agents
In packages/vechain-kit/src/languages/en.json at line 362, the string value
"Vechain Kit Policy" uses incorrect brand capitalization; change it to "VeChain
Kit Policy" so it matches the brand casing used elsewhere (e.g., line 359).
Update only the value string while preserving the JSON key and punctuation.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

♻️ Duplicate comments (8)
examples/homepage/src/app/components/features/InfoSection/InfoSection.tsx (2)

78-93: Add accessibility attributes to the video element.

The autoplaying video still lacks accessibility labeling. Please add an aria-label attribute to provide context for screen reader users.

🔎 Suggested fix
                    <Box
                        as="video"
                        src={finalImageSrc || imageSrc}
                        autoPlay
                        loop
                        muted
                        playsInline
                        w={['100%', imageWidth]}
                        borderRadius="md"
+                       aria-label={imageAlt}
                        sx={{
                            backgroundColor: 'transparent',
                            background: 'transparent',
                            display: 'block',
                        }}
                    />

103-106: Fix malformed gradient stops in mask properties.

The gradient stops are in incorrect order: 80%, 99%, 95%. Gradient stops should be monotonically increasing. The 99% stop comes after the 95% stop, which may not render as intended.

🔎 Proposed fix
                            WebkitMaskImage:
-                               'radial-gradient(ellipse at center, rgba(0,0,0,1) 80%, rgba(0,0,0,0) 99%, rgba(0,0,0,0) 95%)',
+                               'radial-gradient(ellipse at center, rgba(0,0,0,1) 80%, rgba(0,0,0,0) 95%)',
                            maskImage:
-                               'radial-gradient(ellipse at center, rgba(0,0,0,1) 80%,rgba(0,0,0,0) 99%, rgba(0,0,0,0) 95%)',
+                               'radial-gradient(ellipse at center, rgba(0,0,0,1) 80%, rgba(0,0,0,0) 95%)',
examples/homepage/src/app/languages/fr.json (1)

13-13: Remove placeholder demo text.

This placeholder was flagged in previous reviews and should be removed before production deployment.

examples/homepage/src/app/languages/it.json (1)

13-13: Remove placeholder demo text.

This placeholder was flagged in previous reviews and should be removed before production deployment.

examples/homepage/src/app/languages/zh.json (1)

13-13: Remove placeholder demo text.

This placeholder was flagged in previous reviews and should be removed before production deployment.

examples/homepage/src/app/languages/de.json (1)

13-13: Remove placeholder demo text.

This placeholder was flagged in previous reviews and should be removed before production deployment.

examples/homepage/src/app/languages/en.json (1)

13-13: Remove placeholder demo text.

This placeholder was flagged in previous reviews and should be removed before production deployment.

examples/homepage/src/app/languages/ja.json (1)

13-13: Remove placeholder demo text.

This placeholder was flagged in previous reviews and should be removed before production deployment.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 92fc295 and a4b789c.

⛔ Files ignored due to path filters (6)
  • examples/homepage/public/images/favicon/apple-touch-icon.png is excluded by !**/*.png
  • examples/homepage/public/images/favicon/favicon-96x96.png is excluded by !**/*.png
  • examples/homepage/public/images/favicon/favicon.ico is excluded by !**/*.ico
  • examples/homepage/public/images/favicon/favicon.svg is excluded by !**/*.svg
  • examples/homepage/public/images/favicon/web-app-manifest-192x192.png is excluded by !**/*.png
  • examples/homepage/public/images/favicon/web-app-manifest-512x512.png is excluded by !**/*.png
📒 Files selected for processing (10)
  • examples/homepage/src/app/components/features/HeroSection/HeroSection.tsx
  • examples/homepage/src/app/components/features/InfoSection/InfoSection.tsx
  • examples/homepage/src/app/components/features/ScrollableInfoSections/ScrollableInfoSections.tsx
  • examples/homepage/src/app/languages/de.json
  • examples/homepage/src/app/languages/en.json
  • examples/homepage/src/app/languages/es.json
  • examples/homepage/src/app/languages/fr.json
  • examples/homepage/src/app/languages/it.json
  • examples/homepage/src/app/languages/ja.json
  • examples/homepage/src/app/languages/zh.json
✅ Files skipped from review due to trivial changes (1)
  • examples/homepage/src/app/languages/es.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/homepage/src/app/components/features/ScrollableInfoSections/ScrollableInfoSections.tsx
  • examples/homepage/src/app/components/features/HeroSection/HeroSection.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/migration-guide-to-v2.mdc)

**/*.{ts,tsx}: In VeChain Kit Version 2, use useThor instead of useConnex for contract interactions
For single contract read operations, use the useCallClause hook with the pattern: import dependencies, define ABI and method as const, create query key function using getCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use the executeMultipleClausesCall utility wrapped in a useQuery hook with the pattern: define query key function, use executeMultipleClausesCall in queryFn mapping items to clause objects, and transform results
For transaction building and sending, use the useBuildTransaction hook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Always provide an arguments array for contract calls, even when no parameters are required - use an empty array for parameter-less functions to enable TypeScript type checking
Always conditionally enable queries using the enabled property to prevent unnecessary contract calls, checking for all required parameters: enabled: !!requiredParam && !!otherRequiredParam
Use the select option in useCallClause or transform data in queryFn to handle data transformation, particularly for converting BigInt values to strings and normalizing contract return data
Maintain consistent query key patterns: use getCallClauseQueryKeyWithArgs for contract calls with arguments and getCallClauseQueryKey for calls without arguments to ensure proper caching and invalidation
Use TypeScript as const assertions for method names and as 0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions

Files:

  • examples/homepage/src/app/components/features/InfoSection/InfoSection.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Lint & Build
  • GitHub Check: e2e-test
  • GitHub Check: deploy
  • GitHub Check: build-size-check
🔇 Additional comments (5)
examples/homepage/src/app/components/features/InfoSection/InfoSection.tsx (5)

1-13: LGTM!

Imports are well-organized and include all necessary Chakra UI components and hooks.


15-23: LGTM!

The interface is well-defined with sensible required and optional properties for the component.


25-27: LGTM!

The video file detection logic using regex is appropriate for static file paths.


29-43: LGTM!

The component signature and hook usage demonstrate proper responsive behavior and theme awareness.


45-76: LGTM!

The responsive layout and color mode-aware styling are implemented correctly.

Comment on lines +42 to +43
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "Verwendet eine eigene Privy-Konfiguration für Social-Login OAuth2-basierte Methoden wie Google und Apple",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Verwendet eine eigene Privy-Konfiguration für Social-Login OAuth2-basierte Methoden wie Google und Apple.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove duplicate translation key.

Lines 42 and 43 contain nearly identical keys that differ only by a trailing period. This creates ambiguity and potential lookup inconsistencies. Remove one entry to maintain a single source of truth for this translation.

🔎 Proposed fix
-  "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "Verwendet eine eigene Privy-Konfiguration für Social-Login OAuth2-basierte Methoden wie Google und Apple",
   "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Verwendet eine eigene Privy-Konfiguration für Social-Login OAuth2-basierte Methoden wie Google und Apple.",
📝 Committable suggestion

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

Suggested change
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "Verwendet eine eigene Privy-Konfiguration für Social-Login OAuth2-basierte Methoden wie Google und Apple",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Verwendet eine eigene Privy-Konfiguration für Social-Login OAuth2-basierte Methoden wie Google und Apple.",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Verwendet eine eigene Privy-Konfiguration für Social-Login OAuth2-basierte Methoden wie Google und Apple.",
🤖 Prompt for AI Agents
In examples/homepage/src/app/languages/de.json around lines 42 to 43, there are
two nearly identical translation entries that differ only by a trailing period
which creates a duplicate key ambiguity; remove one of the duplicate entries
(either the version with or without the trailing period) so only a single
translation key remains, and ensure any code or references use the retained key
consistently.

Comment on lines +42 to +43
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove duplicate translation key.

Lines 42 and 43 contain nearly identical keys that differ only by a trailing period. As this is the source language file, this duplicate propagates to all other language files. Remove one entry to maintain consistency across all translations.

🔎 Proposed fix
-  "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple",
   "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.",
📝 Committable suggestion

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

Suggested change
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.",
🤖 Prompt for AI Agents
In examples/homepage/src/app/languages/en.json around lines 42 to 43, there are
two duplicate translation keys that differ only by a trailing period; remove one
of the entries (preferably keep the one matching the canonical project's
phrasing) so the source language file contains a single unique key/value pair,
then run a quick grep across other language files to ensure no unintended
duplicates were introduced.

Comment on lines +42 to +43
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "Utilise sa propre configuration Privy pour les méthodes de connexion sociale OAuth2 telles que Google et Apple",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Utilise sa propre configuration Privy pour les méthodes de connexion sociale OAuth2 telles que Google et Apple.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove duplicate translation key.

Lines 42 and 43 contain nearly identical keys that differ only by a trailing period. This creates ambiguity and potential lookup inconsistencies. Remove one entry to maintain a single source of truth for this translation.

🔎 Proposed fix
-  "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "Utilise sa propre configuration Privy pour les méthodes de connexion sociale OAuth2 telles que Google et Apple",
   "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Utilise sa propre configuration Privy pour les méthodes de connexion sociale OAuth2 telles que Google et Apple.",
📝 Committable suggestion

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

Suggested change
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "Utilise sa propre configuration Privy pour les méthodes de connexion sociale OAuth2 telles que Google et Apple",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Utilise sa propre configuration Privy pour les méthodes de connexion sociale OAuth2 telles que Google et Apple.",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Utilise sa propre configuration Privy pour les méthodes de connexion sociale OAuth2 telles que Google et Apple.",
🤖 Prompt for AI Agents
In examples/homepage/src/app/languages/fr.json around lines 42-43 there are two
duplicate translation entries that differ only by a trailing period; remove one
of them (preferably delete the entry with the trailing period) so only a single
key/value pair remains, and ensure any code that references this string uses the
remaining key.

Comment on lines +42 to +43
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "Utilizza una configurazione Privy propria per i metodi di login social OAuth2 come Google e Apple",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Utilizza una configurazione Privy propria per i metodi di login social OAuth2 come Google e Apple.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove duplicate translation key.

Lines 42 and 43 contain nearly identical keys that differ only by a trailing period. This creates ambiguity and potential lookup inconsistencies. Remove one entry to maintain a single source of truth for this translation.

🔎 Proposed fix
-  "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "Utilizza una configurazione Privy propria per i metodi di login social OAuth2 come Google e Apple",
   "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Utilizza una configurazione Privy propria per i metodi di login social OAuth2 come Google e Apple.",
📝 Committable suggestion

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

Suggested change
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "Utilizza una configurazione Privy propria per i metodi di login social OAuth2 come Google e Apple",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Utilizza una configurazione Privy propria per i metodi di login social OAuth2 come Google e Apple.",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Utilizza una configurazione Privy propria per i metodi di login social OAuth2 come Google e Apple.",
🤖 Prompt for AI Agents
In examples/homepage/src/app/languages/it.json around lines 42 to 43 there are
duplicate translation keys that differ only by a trailing period; remove one of
the two entries so only a single key/value pair remains (choose the canonical
key form used elsewhere in the project), ensure the surrounding JSON commas
remain valid after deletion, and run a quick search for the key in the codebase
to confirm references match the retained key.

Comment on lines +42 to +43
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "Google や Apple などの OAuth2 ベースのソーシャルログインには独自の Privy 設定を使用します",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Google や Apple などの OAuth2 ベースのソーシャルログインには独自の Privy 設定を使用します。",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove duplicate translation key.

Lines 42 and 43 contain nearly identical keys that differ only by a trailing period. This creates ambiguity and potential lookup inconsistencies. Remove one entry to maintain a single source of truth for this translation.

🔎 Proposed fix
-  "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "Google や Apple などの OAuth2 ベースのソーシャルログインには独自の Privy 設定を使用します",
   "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Google や Apple などの OAuth2 ベースのソーシャルログインには独自の Privy 設定を使用します。",
📝 Committable suggestion

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

Suggested change
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "Google や Apple などの OAuth2 ベースのソーシャルログインには独自の Privy 設定を使用します",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Google や Apple などの OAuth2 ベースのソーシャルログインには独自の Privy 設定を使用します。",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "Google や Apple などの OAuth2 ベースのソーシャルログインには独自の Privy 設定を使用します。",
🤖 Prompt for AI Agents
In examples/homepage/src/app/languages/ja.json around lines 42 to 43, there are
two duplicate translation keys that differ only by a trailing period; remove one
of the entries so only a single key remains (prefer keeping the key format
consistent with other keys in the file), and ensure any code or tests
referencing the removed variant are updated to use the remaining key if
necessary.

Comment on lines +42 to +43
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "为 Google 和 Apple 等基于 OAuth2 的社交登录方式使用自有 Privy 配置",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "为 Google 和 Apple 等基于 OAuth2 的社交登录方式使用自有 Privy 配置。",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove duplicate translation key.

Lines 42 and 43 contain nearly identical keys that differ only by a trailing period. This creates ambiguity and potential lookup inconsistencies. Remove one entry to maintain a single source of truth for this translation.

🔎 Proposed fix
-  "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "为 Google 和 Apple 等基于 OAuth2 的社交登录方式使用自有 Privy 配置",
   "Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "为 Google 和 Apple 等基于 OAuth2 的社交登录方式使用自有 Privy 配置。",
📝 Committable suggestion

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

Suggested change
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple": "为 Google 和 Apple 等基于 OAuth2 的社交登录方式使用自有 Privy 配置",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "为 Google 和 Apple 等基于 OAuth2 的社交登录方式使用自有 Privy 配置。",
"Uses its own Privy setup for social login OAuth2 based methods like Google and Apple.": "为 Google 和 Apple 等基于 OAuth2 的社交登录方式使用自有 Privy 配置。",
🤖 Prompt for AI Agents
In examples/homepage/src/app/languages/zh.json around lines 42 to 43, there are
two nearly identical translation keys that differ only by a trailing period;
remove one of the duplicate entries so the JSON has a single key for that
translation (choose the form consistent with other keys in the file—either keep
the key without the trailing period or the one with it—and delete the other),
then validate the JSON for syntax and run a quick lookup test to ensure the
remaining key is used correctly.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 18

Caution

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

⚠️ Outside diff range comments (1)
packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx (1)

77-89: Extract duplicated disconnect-confirm logic into a helper function.

The disconnect-confirm flow is duplicated in two places (ProfileCard's onLogout callback and the Logout button's onClick handler). Extract this into a reusable helper to follow DRY principles and improve maintainability.

🔎 Proposed refactor to eliminate duplication
 export const ProfileContent = ({
     setCurrentContent,
     onLogoutSuccess,
 }: ProfileContentProps) => {
     const { t } = useTranslation();
     const { account, disconnect, connection } = useWallet();
     const { switchWallet, isSwitching } = useSwitchWallet();
     const { isolatedView } = useAccountModalOptions();
+
+    const handleDisconnectConfirm = () => {
+        setCurrentContent({
+            type: 'disconnect-confirm',
+            props: {
+                onDisconnect: () => {
+                    disconnect();
+                    onLogoutSuccess?.();
+                },
+                onBack: () => setCurrentContent?.('profile'),
+            },
+        });
+    };

     return (
         <>
             {/* ... */}
             <ProfileCard
                 onEditClick={() => {
                     setCurrentContent({
                         type: 'account-customization',
                         props: {
                             setCurrentContent,
                             initialContentSource: 'profile',
                         },
                     });
                 }}
                 address={account?.address ?? ''}
                 showHeader={false}
                 setCurrentContent={setCurrentContent}
-                onLogout={() => {
-                    setCurrentContent?.({
-                        type: 'disconnect-confirm',
-                        props: {
-                            onDisconnect: () => {
-                                disconnect();
-                                onLogoutSuccess?.();
-                            },
-                            onBack: () =>
-                                setCurrentContent?.('profile'),
-                        },
-                    });
-                }}
+                onLogout={handleDisconnectConfirm}
             />
             {/* ... */}
             <Button
                 size="md"
                 width="full"
                 height="40px"
                 variant="vechainKitSecondary"
                 leftIcon={<Icon as={LuLogOut} />}
                 colorScheme="red"
-                onClick={() =>
-                    setCurrentContent({
-                        type: 'disconnect-confirm',
-                        props: {
-                            onDisconnect: () => {
-                                disconnect();
-                                onLogoutSuccess?.();
-                            },
-                            onBack: () =>
-                                setCurrentContent?.('profile'),
-                        },
-                    })
-                }
+                onClick={handleDisconnectConfirm}
                 data-testid="logout-button"
             >
                 {t('Logout')}
             </Button>

Also applies to: 131-142

♻️ Duplicate comments (7)
examples/playground/src/app/components/features/LoginUIControl/LoginUIControl.tsx (3)

159-163: Typo in note text.

"configuring you the VeChainKitProvider" should be "configuring your VeChainKitProvider".

🔎 Proposed fix
                     {t(
-                        'Note: The modal variant is the default login button variant. You can pass an additional description and Image to the modal when configuring you the VeChainKitProvider.',
+                        'Note: The modal variant is the default login button variant. You can pass an additional description and Image to the modal when configuring your VeChainKitProvider.',
                     )}

186-200: Unhandled promise rejection on OAuth initiation.

initOAuth is async and can throw (as confirmed in the hook implementation). The promise is fired without error handling, which may cause unhandled rejection warnings.

🔎 Proposed fix
                         <Button
                             onClick={() =>
-                                initOAuth({ provider: 'google' })
+                                initOAuth({ provider: 'google' }).catch(console.error)
                             }

Or wrap in a handler that provides user feedback on failure.


218-232: Same unhandled rejection issue for GitHub OAuth.

Apply similar error handling as suggested for the Google OAuth button.

🔎 Proposed fix
                         <Button
                             onClick={() =>
-                                initOAuth({ provider: 'github' })
+                                initOAuth({ provider: 'github' }).catch(console.error)
                             }
examples/playground/src/app/components/features/ConnectionInfo/ConnectionInfo.tsx (1)

8-22: Add a guard for disconnected state.

The component accesses connection.source.type and connection.network without checking if the wallet is connected. This is the same issue flagged in previous reviews and can cause runtime errors or display undefined values when disconnected.

🔎 Suggested fix: early return when disconnected
 export function ConnectionInfo() {
     const { connection } = useWallet();

+    if (!connection.isConnected) {
+        return null;
+    }
+
     const getConnectionDescription = () => {
         switch (connection.source.type) {
examples/homepage/src/app/layout.tsx (2)

55-58: Accessibility issue: maximum-scale=1.0 prevents user zooming.

This restricts zoom functionality, violating WCAG 2.1 Level AA guidelines (Success Criteria 1.4.4 and 1.4.10). Users with visual impairments need zoom capability.

🔎 Proposed fix
                 <meta
                     name="viewport"
-                    content="width=device-width, initial-scale=1.0, maximum-scale=1.0"
+                    content="width=device-width, initial-scale=1.0"
                 />

125-146: Static analysis flag: dangerouslySetInnerHTML usage.

The static analysis tools flag this for potential XSS, but in this case the script content is hardcoded with no user input, making the risk minimal. The purpose (clearing localStorage color mode preferences) is valid, though as noted in past reviews, this is part of redundant light mode enforcement mechanisms that could be simplified.

examples/playground/src/app/providers/VechainKitProviderWrapper.tsx (1)

168-170: Add validation for required environment variables.

Environment variables are accessed with non-null assertions (!) without validation. If these are missing at runtime, it will cause cryptic errors.

This was flagged in a past review. Consider adding early validation or fallback handling for NEXT_PUBLIC_PRIVY_APP_ID, NEXT_PUBLIC_PRIVY_CLIENT_ID, and NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID.

🧹 Nitpick comments (32)
examples/homepage/.env.example (1)

8-8: Consider alphabetical ordering of environment variables.

The static analysis tool flags that NEXT_PUBLIC_NETWORK_TYPE (line 8) should be ordered before NEXT_PUBLIC_PRIVY_APP_ID (line 2). While the current structure groups variables logically by purpose (Privy config, Wallet Connect, Network, Translations), consider aligning with project conventions or the linter's expectations for consistency.

examples/homepage/src/app/components/features/LanguagesSection/LanguagesSection.tsx (2)

14-14: Consider using a path alias for cleaner imports.

The deep relative import path ../../../../../i18n makes the code harder to maintain and refactor. Consider configuring a path alias (e.g., @/i18n or ~/i18n) in your TypeScript/Next.js configuration for cleaner imports.


22-31: Add missing 'it' entry to maintain consistency.

The languageToCountryCode mapping is missing an entry for 'it' (Italian), even though supportedLanguages includes it. While the fallback in getFlagUrl prevents breakage, explicitly including all supported languages improves code clarity and maintainability.

🔎 Suggested fix
 const languageToCountryCode: Record<string, string> = {
     en: 'us',
     de: 'de',
+    it: 'it',
     fr: 'fr',
     es: 'es',
     zh: 'cn',
     ja: 'jp',
 };
examples/playground/src/app/constants.ts (2)

1-2: Consider adding type assertions for Ethereum addresses.

Per coding guidelines, use as \0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions.

🔎 Proposed fix
-export const b3trMainnetAddress = '0x5ef79995FE8a89e0812330E4378eB2660ceDe699';
-export const b3trTestnetAddress = '0xbf64cf86894Ee0877C4e7d03936e35Ee8D8b864F';
+export const b3trMainnetAddress = '0x5ef79995FE8a89e0812330E4378eB2660ceDe699' as `0x${string}`;
+export const b3trTestnetAddress = '0xbf64cf86894Ee0877C4e7d03936e35Ee8D8b864F' as `0x${string}`;

3-26: Good use of as const for ABI type safety.

The comment on line 4 suggests this is a placeholder. Since this is a playground example, consider whether the actual B3TR ABI should be imported from @vechain/vechain-contract-types (as done in TransactionExamples.tsx with IB3TR__factory.abi) for consistency.

Would you like me to help consolidate the ABI usage to avoid maintaining a separate definition here?

packages/vechain-kit/src/hooks/modals/useSettingsModal.tsx (1)

23-27: Remove unused SettingsModalProvider or clarify its purpose.

The SettingsModalProvider component is a pass-through that renders children without providing any context or functionality. If it's not used elsewhere or serving a specific pattern, it should be removed to reduce code clutter.

Run the following script to verify if SettingsModalProvider is used anywhere in the codebase:

#!/bin/bash
# Description: Check if SettingsModalProvider is imported or used anywhere

# Search for imports of SettingsModalProvider
rg -n "SettingsModalProvider" --type=ts --type=tsx -C3
packages/vechain-kit/src/hooks/modals/useWalletModal.tsx (1)

38-40: Consider removing the no-op WalletModalProvider component.

This component simply renders its children without any additional functionality. If it's not used elsewhere or intended for future functionality, consider removing it to reduce code clutter.

examples/playground/src/app/components/features/FeaturesToTry/ThemeCard.tsx (1)

14-61: Consider extracting shared UI components.

A nearly identical ThemeCard component exists in examples/homepage/src/app/components/features/FeaturesToTry/ThemeCard.tsx. Consider extracting common UI components to a shared location (e.g., a shared package or monorepo workspace) to reduce duplication and improve maintainability.

examples/playground/src/app/components/layout/Header/LanguageDropdown.tsx (2)

56-69: Add error handling for flag image loading failures.

The Image components lack error handling for failed loads. If the CDN is unavailable or returns invalid content, users will see broken image icons. Consider adding an onError handler with a fallback (e.g., a generic globe icon or the language code as text).

🔎 Suggested implementation with error handling
+import { LuGlobe } from 'react-icons/lu';
+
+const FlagImage = ({ src, alt, ...props }: { src: string; alt: string; [key: string]: any }) => {
+  const [hasError, setHasError] = React.useState(false);
+  
+  if (hasError) {
+    return <Icon as={LuGlobe} boxSize="20px" />;
+  }
+  
+  return (
+    <Image
+      src={src}
+      alt={alt}
+      onError={() => setHasError(true)}
+      {...props}
+    />
+  );
+};

Then replace both <Image ... /> instances with <FlagImage ... />.

Also applies to: 116-129


64-68: Consider using Chakra UI semantic tokens instead of hardcoded color values.

The component uses numerous hardcoded RGBA values for borders, backgrounds, and shadows. While the theme-aware conditionals work correctly, using Chakra's semantic tokens (e.g., borderColor="whiteAlpha.200" or bg="gray.800") would improve maintainability and ensure consistency with the global theme.

Example for border colors:

borderColor={isDarkMode ? 'whiteAlpha.200' : 'gray.200'}
// Could become:
borderColor="border.subtle"  // if defined in theme

Also applies to: 77-85, 100-111, 124-128

packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx (1)

106-122: Error handling is silently implemented without user feedback.

The useSwitchWallet hook includes error handling (try-catch at lines 23-32), but catches errors silently and doesn't expose them to the component. This means if the wallet switch fails, the user sees only the loading state end with no error message or feedback. Consider exposing error information from the hook so the component can provide appropriate user feedback for failed wallet switches, network issues, or user cancellations.

examples/playground/package.json (1)

12-25: Consider standardizing dependency versioning strategy.

The dependencies use inconsistent versioning: exact versions for some packages (lines 13, 17: "2.8.2", "1.4.1") and caret ranges for others (lines 14-16, 19-20, 22-24). This inconsistency can lead to unpredictable behavior across environments.

Consider adopting a consistent strategy:

  • Use exact versions for stability-critical packages
  • Use caret ranges (^) for packages with stable APIs
  • Document the versioning strategy in a contributing guide
examples/playground/src/app/components/features/LoginToContinueBox/LoginToContinueBox.tsx (1)

34-34: Simplify onClick handler by removing unnecessary arrow function.

The arrow function wrapper is unnecessary since open accepts no arguments and can be passed directly.

🔎 Proposed simplification
-            <Button width="full" onClick={() => open()}>
+            <Button width="full" onClick={open}>
                 {t('Click here to sign in!')}
             </Button>
examples/playground/src/app/theme/button.ts (1)

17-20: Consider renaming or removing the testVariant for production.

The variant name testVariant suggests temporary or development code. For production readiness, consider:

  • Renaming it to reflect its purpose (e.g., primaryAccent, accentButton)
  • Removing it if it's no longer needed
  • Adding a comment explaining its intended use case
examples/playground/src/app/components/features/FeaturesToTry/LanguageCard.tsx (1)

14-14: Consider using path aliases instead of deep relative imports.

The import path ../../../../../i18n is fragile and difficult to maintain. If the file structure changes, this import will break.

🔎 Proposed improvement

Configure a path alias in tsconfig.json:

{
  "compilerOptions": {
    "paths": {
      "@/i18n": ["./i18n"],
      "@/components/*": ["./src/app/components/*"]
    }
  }
}

Then update the import:

-import { languageNames, supportedLanguages } from '../../../../../i18n';
+import { languageNames, supportedLanguages } from '@/i18n';
examples/playground/src/app/components/features/LanguageSelector/LanguageSelector.tsx (1)

5-5: Simplify deeply nested import path.

The import path ../../../../../i18n is fragile and error-prone during refactoring. Consider using a path alias or moving the i18n configuration to a more accessible location.

💡 Suggested approach

Configure a path alias in your TypeScript config:

{
  "compilerOptions": {
    "paths": {
      "@/i18n": ["./i18n.ts"]
    }
  }
}

Then import as:

import { languageNames, supportedLanguages } from '@/i18n';
examples/playground/src/app/components/features/DaoInfo/DaoInfo.tsx (1)

12-52: Handle loading and error states for better UX.

The component displays contract data without handling loading or error states from the useCurrentAllocationsRoundId and useIsPerson hooks. During data fetching, users will see empty or undefined values, which degrades the user experience.

💡 Suggested enhancement
 export function DaoInfo() {
     const { account } = useWallet();
-    const { data: currentAllocationsRoundId } = useCurrentAllocationsRoundId();
-    const { data: isValidPassport } = useIsPerson(account?.address);
+    const { data: currentAllocationsRoundId, isLoading: isLoadingRound } = useCurrentAllocationsRoundId();
+    const { data: isValidPassport, isLoading: isLoadingPassport } = useIsPerson(account?.address);

     return (
         <CollapsibleCard
             title="Contract Interactions"
             icon={LuBuilding2}
             style={{ bg: 'whiteAlpha.100' }}
         >
             <VStack spacing={6} align="stretch">
                 <Text textAlign="center">
                     VeChain Kit provides hooks to easily interact with popular
                     VeChain contracts. Here's how to use them in your
                     application.
                 </Text>

                 <SimpleGrid columns={{ base: 1, md: 2 }} spacing={6}>
                     {/* Current Implementation */}
                     <VStack
                         spacing={4}
                         p={6}
                         borderRadius="md"
                         bg="whiteAlpha.50"
                     >
                         <Text fontWeight="bold">Live VeBetterDAO Data</Text>
                         <VStack spacing={3} align="start" w="full">
                             <Text>
                                 <Text as="span" fontWeight="bold">
                                     Current Round ID:{' '}
                                 </Text>
-                                {currentAllocationsRoundId}
+                                {isLoadingRound ? 'Loading...' : currentAllocationsRoundId}
                             </Text>
                             <Text>
                                 <Text as="span" fontWeight="bold">
                                     Valid Passport:{' '}
                                 </Text>
-                                {isValidPassport?.toString()}
+                                {isLoadingPassport ? 'Loading...' : isValidPassport?.toString()}
                             </Text>
                         </VStack>
                     </VStack>
                 </SimpleGrid>
             </VStack>
         </CollapsibleCard>
     );
 }
examples/playground/src/app/components/features/ThemeToggle/ThemeToggle.tsx (1)

5-15: LGTM! Component logic is clean and correct.

The ThemeToggle component correctly uses Chakra UI's useColorMode hook and provides a clear toggle interface.

💡 Optional: Remove unnecessary Box wrapper

The Box wrapper doesn't add any functionality or styling. Consider returning the Button directly:

 export function ThemeToggle() {
     const { colorMode, toggleColorMode } = useColorMode();

     return (
-        <Box>
-            <Button onClick={toggleColorMode}>
-                Toggle {colorMode === 'light' ? 'Dark' : 'Light'} Mode
-            </Button>
-        </Box>
+        <Button onClick={toggleColorMode}>
+            Toggle {colorMode === 'light' ? 'Dark' : 'Light'} Mode
+        </Button>
     );
 }
examples/playground/src/app/globals.css (2)

17-41: Consider reducing reliance on !important flags.

The extensive use of !important on background and color properties may indicate specificity conflicts. This can make future style overrides difficult and suggests the selectors might benefit from increased specificity instead.

💡 Consider alternative approaches

If these !important flags are needed to override Chakra UI's inline styles, consider:

  1. Using Chakra's theme configuration to set these colors at the theme level
  2. Increasing selector specificity without !important
  3. Using Chakra's ColorModeProvider config to control default backgrounds

Example of increasing specificity:

html[data-chakra-ui-color-mode='dark'] body {
  background-color: #121213;
  color: #F7FAFC;
}

45-53: Use theme-aware border color.

The .container class uses a hardcoded black border (#000) that won't adapt to the theme. This will appear harsh in dark mode.

🔎 Suggested fix

Consider using a CSS variable or theme-aware color:

 .container {
   display: flex;
   flex-direction: column;
   align-items: center;
   justify-content: center;
-  border: 1px solid #000;
+  border: 1px solid rgba(0, 0, 0, 0.2);
   border-radius: 20px;
   padding: 20px;
 }
+
+[data-theme='dark'] .container,
+[data-chakra-ui-color-mode='dark'] .container {
+  border-color: rgba(255, 255, 255, 0.2);
+}
examples/playground/.env.example (1)

1-11: Add trailing newline to follow convention.

The file is missing a trailing newline, which is a common convention for text files.

🔎 Proposed fix
 # Translations 
 OPENAI_API_KEY=
+
examples/playground/src/app/theme/colors.ts (1)

28-38: Inconsistent key format compared to other palettes.

lightTertiary uses numeric keys (100, 200, etc.) while lightPrimary and lightSecondary use string keys ('50', '100', etc.). This inconsistency could cause issues when accessing colors programmatically if code expects consistent key types.

🔎 Proposed fix for consistency
 export const lightTertiary = {
-    100: '#e0f7fc',
-    200: '#c2eff9',
-    300: '#a3e7f6',
-    400: '#85dff3',
-    500: '#66d7f0',
-    600: '#47cfed',
-    700: '#29c7ea',
-    800: '#0abfe7',
-    900: '#00b7e0',
+    '100': '#e0f7fc',
+    '200': '#c2eff9',
+    '300': '#a3e7f6',
+    '400': '#85dff3',
+    '500': '#66d7f0',
+    '600': '#47cfed',
+    '700': '#29c7ea',
+    '800': '#0abfe7',
+    '900': '#00b7e0',
 };
examples/playground/src/app/components/features/AccountInfo/AccountInfo.tsx (1)

17-92: Potential code duplication with homepage AccountInfo component.

This component is nearly identical to examples/homepage/src/app/components/features/AccountInfo/AccountInfo.tsx (shown in relevant snippets). Consider extracting a shared component to avoid maintaining duplicate code across both example apps.

The functionality and rendering logic are correct. The conditional rendering for smart account and wallet type detection work as expected.

examples/playground/src/app/components/features/FeaturesToTry/FeaturesToTry.tsx (2)

3-38: Duplicate imports from react-icons/lu.

Icons are imported in two separate blocks (lines 4-10 and 31-37). Consolidate these into a single import statement for cleaner code.

🔎 Proposed fix
 import { VStack, Text, SimpleGrid } from '@chakra-ui/react';
 import {
     LuSquareUser,
     LuArrowLeftRight,
     LuUserCog,
     LuBell,
     LuCircleHelp,
+    LuUser,
+    LuArrowDownToLine,
+    LuRefreshCw,
+    LuSettings,
+    LuWallet,
 } from 'react-icons/lu';
-import {
-    LuUser,
-    LuArrowDownToLine,
-    LuRefreshCw,
-    LuSettings,
-    LuWallet,
-} from 'react-icons/lu';

86-93: "Customize Profile" duplicates "Profile" feature functionality.

Both "Profile" (line 69) and "Customize Profile" (line 92) call openProfileModal({ isolatedView: true }) with the same parameters. Consider consolidating into a single feature card or differentiating their behavior.

examples/playground/src/app/providers/VechainKitProviderWrapper.tsx (2)

24-65: Duplicate polling logic between LanguageSync and VechainKitProviderWrapper.

Both components poll localStorage every 500ms for language changes. This duplication means two intervals running simultaneously. Consider consolidating the language synchronization logic into a single location.


113-116: Consider memoizing playgroundTranslations.

This object is recomputed on every render. Since resources is static, memoize with useMemo to avoid unnecessary recalculations.

🔎 Proposed fix
+import { useEffect, useState, useMemo } from 'react';

     // Transform resources to match I18n type (extract translation objects)
-    const playgroundTranslations = Object.keys(resources).reduce((acc, lang) => {
+    const playgroundTranslations = useMemo(() => Object.keys(resources).reduce((acc, lang) => {
         acc[lang] = resources[lang as keyof typeof resources].translation;
         return acc;
-    }, {} as Record<string, Record<string, string>>);
+    }, {} as Record<string, Record<string, string>>), []);
examples/playground/i18n.ts (2)

13-34: Consider adding type constraints for language mappings.

The supportedLanguages, resources, and languageNames objects could benefit from a shared type to ensure consistency and catch typos at compile time.

🔎 Proposed type-safe approach
-// Define supported languages
-export const supportedLanguages = ['en', 'de', 'it', 'fr', 'es', 'zh', 'ja'];
+// Define supported languages
+export const supportedLanguages = ['en', 'de', 'it', 'fr', 'es', 'zh', 'ja'] as const;
+export type SupportedLanguage = (typeof supportedLanguages)[number];

-export const resources = {
+export const resources: Record<SupportedLanguage, { translation: typeof en }> = {
     en: { translation: en },
     // ...
 };

-export const languageNames = {
+export const languageNames: Record<SupportedLanguage, string> = {
     en: 'English',
     // ...
 };

39-57: Consider consolidating the window checks.

The two typeof window !== 'undefined' checks can be simplified since the second check is always true if the first one passed.

🔎 Simplified lookup function
     lookup: () => {
-        // Check localStorage first (for persistence across page refreshes)
         if (typeof window !== 'undefined') {
+            // Check localStorage first (for persistence across page refreshes)
             const storedLanguage = localStorage.getItem('i18nextLng');
             if (storedLanguage && supportedLanguages.includes(storedLanguage)) {
                 return storedLanguage;
             }
-        }
 
-        // Check if we're in a browser environment
-        if (typeof window !== 'undefined') {
             // Get browser language
             const browserLang = navigator.language.split('-')[0];
             if (browserLang && supportedLanguages.includes(browserLang)) {
                 return browserLang;
             }
         }

         return 'en'; // fallback
     },
examples/playground/src/app/components/features/FeaturesToTry/FeatureCard.tsx (3)

37-43: Unnecessary e.preventDefault() call.

The preventDefault() call has no effect on a <div> (which Box renders to) since divs don't have default click behavior. You can simplify the handler.

🔎 Simplified click handler
             onClick={(e) => {
-                if (disabled) {
-                    e.preventDefault();
-                    return;
-                }
-                content();
+                if (!disabled) {
+                    content();
+                }
             }}

102-102: Hardcoded string should use i18n.

Since the playground app has i18n support, "Click me!" should be translated. The same applies to the disabled message on line 117.

🔎 Use translations
+import { useTranslation } from 'react-i18next';
+
 export function FeatureCard({
     // ...
 }: FeatureCardProps) {
     const { colorMode } = useColorMode();
+    const { t } = useTranslation();
 
     // In the JSX:
-                            <Text ...>
-                                Click me!
-                            </Text>
+                            <Text ...>
+                                {t('featureCard.clickMe')}
+                            </Text>

-                    <Text fontSize="xs" opacity={0.5}>
-                        Only available for social login users.
-                    </Text>
+                    <Text fontSize="xs" opacity={0.5}>
+                        {t('featureCard.socialLoginOnly')}
+                    </Text>

36-57: Add accessibility attributes for the clickable card.

The interactive Box lacks keyboard support and ARIA semantics. Consider adding role, tabIndex, and keyboard handling for accessibility compliance.

🔎 Accessibility improvements
         <Box
             onClick={(e) => {
                 if (!disabled) {
                     content();
                 }
             }}
+            onKeyDown={(e) => {
+                if ((e.key === 'Enter' || e.key === ' ') && !disabled) {
+                    e.preventDefault();
+                    content();
+                }
+            }}
+            role="button"
+            tabIndex={disabled ? -1 : 0}
+            aria-disabled={disabled}
             p={4}
             // ...
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a4b789c and 97071cb.

⛔ Files ignored due to path filters (18)
  • examples/playground/public/images/favicon.png is excluded by !**/*.png
  • examples/playground/public/images/favicon/apple-touch-icon.png is excluded by !**/*.png
  • examples/playground/public/images/favicon/favicon-96x96.png is excluded by !**/*.png
  • examples/playground/public/images/favicon/favicon.ico is excluded by !**/*.ico
  • examples/playground/public/images/favicon/favicon.svg is excluded by !**/*.svg
  • examples/playground/public/images/favicon/web-app-manifest-192x192.png is excluded by !**/*.png
  • examples/playground/public/images/favicon/web-app-manifest-512x512.png is excluded by !**/*.png
  • examples/playground/public/images/logo.png is excluded by !**/*.png
  • examples/playground/public/images/metamask-logo.png is excluded by !**/*.png
  • examples/playground/public/images/rabby-logo.png is excluded by !**/*.png
  • examples/playground/public/images/spider-web-element-onblack-background-long.png is excluded by !**/*.png
  • examples/playground/public/images/spider-web-element-onblack-background.jpg is excluded by !**/*.jpg
  • examples/playground/public/images/vechain-kit-long.png is excluded by !**/*.png
  • examples/playground/public/images/veworld-logo.png is excluded by !**/*.png
  • examples/playground/public/images/wallet-connect-logo.png is excluded by !**/*.png
  • examples/playground/src/app/fonts/GeistMonoVF.woff is excluded by !**/*.woff
  • examples/playground/src/app/fonts/GeistVF.woff is excluded by !**/*.woff
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (71)
  • examples/homepage/.env.example
  • examples/homepage/src/app/components/features/LanguagesSection/LanguagesSection.tsx
  • examples/homepage/src/app/components/features/LoginMethodsSection/index.ts
  • examples/homepage/src/app/layout.tsx
  • examples/playground/.env.example
  • examples/playground/.eslintrc.js
  • examples/playground/.gitignore
  • examples/playground/README.md
  • examples/playground/i18n.ts
  • examples/playground/next.config.js
  • examples/playground/package.json
  • examples/playground/public/images/coinbase-wallet-logo.webp
  • examples/playground/public/images/favicon/site.webmanifest
  • examples/playground/public/images/rainbow-logo.webp
  • examples/playground/src/app/components/features/AccountInfo/AccountInfo.tsx
  • examples/playground/src/app/components/features/AccountInfo/index.ts
  • examples/playground/src/app/components/features/ConnectionInfo/ConnectionInfo.tsx
  • examples/playground/src/app/components/features/ConnectionInfo/index.ts
  • examples/playground/src/app/components/features/DaoInfo/DaoInfo.tsx
  • examples/playground/src/app/components/features/DaoInfo/index.ts
  • examples/playground/src/app/components/features/DataReading/DataReadingExample.tsx
  • examples/playground/src/app/components/features/DataReading/index.ts
  • examples/playground/src/app/components/features/FeaturesToTry/FeatureCard.tsx
  • examples/playground/src/app/components/features/FeaturesToTry/FeaturesToTry.tsx
  • examples/playground/src/app/components/features/FeaturesToTry/GithubCard.tsx
  • examples/playground/src/app/components/features/FeaturesToTry/LanguageCard.tsx
  • examples/playground/src/app/components/features/FeaturesToTry/ThemeCard.tsx
  • examples/playground/src/app/components/features/FeaturesToTry/index.ts
  • examples/playground/src/app/components/features/LanguageSelector/LanguageSelector.tsx
  • examples/playground/src/app/components/features/LanguageSelector/index.ts
  • examples/playground/src/app/components/features/LoginToContinueBox/LoginToContinueBox.tsx
  • examples/playground/src/app/components/features/LoginToContinueBox/index.ts
  • examples/playground/src/app/components/features/LoginUIControl/LoginUIControl.tsx
  • examples/playground/src/app/components/features/LoginUIControl/index.ts
  • examples/playground/src/app/components/features/Signing/SigningExample.tsx
  • examples/playground/src/app/components/features/Signing/index.ts
  • examples/playground/src/app/components/features/SmartAccountInfo/SmartAccountInfo.tsx
  • examples/playground/src/app/components/features/SmartAccountInfo/index.ts
  • examples/playground/src/app/components/features/ThemeToggle/ThemeToggle.tsx
  • examples/playground/src/app/components/features/ThemeToggle/index.ts
  • examples/playground/src/app/components/features/TransactionExamples/TransactionExamples.tsx
  • examples/playground/src/app/components/features/TransactionExamples/index.ts
  • examples/playground/src/app/components/layout/Header/LanguageDropdown.tsx
  • examples/playground/src/app/components/layout/Header/index.ts
  • examples/playground/src/app/components/ui/CollapsibleCard/CollapsibleCard.tsx
  • examples/playground/src/app/components/ui/CollapsibleCard/index.ts
  • examples/playground/src/app/constants.ts
  • examples/playground/src/app/globals.css
  • examples/playground/src/app/languages/de.json
  • examples/playground/src/app/languages/en.json
  • examples/playground/src/app/languages/es.json
  • examples/playground/src/app/languages/fr.json
  • examples/playground/src/app/languages/it.json
  • examples/playground/src/app/languages/ja.json
  • examples/playground/src/app/languages/zh.json
  • examples/playground/src/app/layout.tsx
  • examples/playground/src/app/page.tsx
  • examples/playground/src/app/pages/Home.tsx
  • examples/playground/src/app/providers/VechainKitProviderWrapper.tsx
  • examples/playground/src/app/theme/button.ts
  • examples/playground/src/app/theme/card.ts
  • examples/playground/src/app/theme/colors.ts
  • examples/playground/src/app/theme/index.ts
  • examples/playground/src/app/theme/modal.ts
  • examples/playground/src/app/theme/theme.tsx
  • examples/playground/translo.config.json
  • examples/playground/tsconfig.json
  • packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx
  • packages/vechain-kit/src/hooks/modals/index.ts
  • packages/vechain-kit/src/hooks/modals/useSettingsModal.tsx
  • packages/vechain-kit/src/hooks/modals/useWalletModal.tsx
✅ Files skipped from review due to trivial changes (12)
  • examples/playground/src/app/components/layout/Header/index.ts
  • examples/playground/src/app/components/features/LanguageSelector/index.ts
  • examples/playground/src/app/components/features/LoginUIControl/index.ts
  • examples/playground/src/app/components/features/LoginToContinueBox/index.ts
  • examples/playground/.gitignore
  • examples/playground/src/app/languages/zh.json
  • examples/playground/src/app/components/features/TransactionExamples/index.ts
  • examples/playground/src/app/components/features/SmartAccountInfo/index.ts
  • examples/playground/src/app/languages/it.json
  • examples/playground/README.md
  • examples/playground/public/images/favicon/site.webmanifest
  • examples/playground/src/app/languages/de.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/homepage/src/app/components/features/LoginMethodsSection/index.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/migration-guide-to-v2.mdc)

**/*.{ts,tsx}: In VeChain Kit Version 2, use useThor instead of useConnex for contract interactions
For single contract read operations, use the useCallClause hook with the pattern: import dependencies, define ABI and method as const, create query key function using getCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use the executeMultipleClausesCall utility wrapped in a useQuery hook with the pattern: define query key function, use executeMultipleClausesCall in queryFn mapping items to clause objects, and transform results
For transaction building and sending, use the useBuildTransaction hook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Always provide an arguments array for contract calls, even when no parameters are required - use an empty array for parameter-less functions to enable TypeScript type checking
Always conditionally enable queries using the enabled property to prevent unnecessary contract calls, checking for all required parameters: enabled: !!requiredParam && !!otherRequiredParam
Use the select option in useCallClause or transform data in queryFn to handle data transformation, particularly for converting BigInt values to strings and normalizing contract return data
Maintain consistent query key patterns: use getCallClauseQueryKeyWithArgs for contract calls with arguments and getCallClauseQueryKey for calls without arguments to ensure proper caching and invalidation
Use TypeScript as const assertions for method names and as 0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions

Files:

  • examples/playground/src/app/components/features/ConnectionInfo/index.ts
  • examples/playground/src/app/components/features/FeaturesToTry/LanguageCard.tsx
  • packages/vechain-kit/src/hooks/modals/index.ts
  • examples/playground/src/app/theme/theme.tsx
  • examples/playground/src/app/components/features/AccountInfo/index.ts
  • examples/playground/src/app/components/features/ThemeToggle/ThemeToggle.tsx
  • examples/playground/src/app/theme/colors.ts
  • examples/playground/src/app/components/features/DaoInfo/DaoInfo.tsx
  • examples/playground/src/app/components/features/DataReading/index.ts
  • examples/playground/src/app/components/features/ConnectionInfo/ConnectionInfo.tsx
  • examples/playground/src/app/components/features/FeaturesToTry/ThemeCard.tsx
  • examples/playground/src/app/theme/modal.ts
  • examples/playground/src/app/components/features/AccountInfo/AccountInfo.tsx
  • examples/playground/src/app/components/features/SmartAccountInfo/SmartAccountInfo.tsx
  • examples/playground/src/app/components/ui/CollapsibleCard/index.ts
  • examples/playground/src/app/components/features/LanguageSelector/LanguageSelector.tsx
  • examples/playground/src/app/components/features/LoginUIControl/LoginUIControl.tsx
  • examples/playground/src/app/components/features/FeaturesToTry/FeatureCard.tsx
  • examples/playground/src/app/components/layout/Header/LanguageDropdown.tsx
  • examples/playground/src/app/layout.tsx
  • examples/playground/src/app/theme/button.ts
  • examples/playground/src/app/components/features/Signing/SigningExample.tsx
  • examples/playground/src/app/components/features/LoginToContinueBox/LoginToContinueBox.tsx
  • examples/playground/src/app/components/features/FeaturesToTry/FeaturesToTry.tsx
  • examples/playground/src/app/theme/index.ts
  • examples/playground/src/app/theme/card.ts
  • examples/playground/src/app/components/features/DaoInfo/index.ts
  • examples/playground/src/app/constants.ts
  • examples/playground/src/app/components/features/DataReading/DataReadingExample.tsx
  • examples/playground/src/app/page.tsx
  • packages/vechain-kit/src/hooks/modals/useWalletModal.tsx
  • examples/playground/src/app/components/features/FeaturesToTry/GithubCard.tsx
  • examples/playground/src/app/components/features/Signing/index.ts
  • examples/playground/src/app/components/features/TransactionExamples/TransactionExamples.tsx
  • examples/playground/src/app/components/features/ThemeToggle/index.ts
  • examples/playground/src/app/components/ui/CollapsibleCard/CollapsibleCard.tsx
  • examples/playground/src/app/components/features/FeaturesToTry/index.ts
  • examples/playground/src/app/pages/Home.tsx
  • packages/vechain-kit/src/hooks/modals/useSettingsModal.tsx
  • packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx
  • examples/playground/i18n.ts
  • examples/homepage/src/app/components/features/LanguagesSection/LanguagesSection.tsx
  • examples/homepage/src/app/layout.tsx
  • examples/playground/src/app/providers/VechainKitProviderWrapper.tsx
🧠 Learnings (3)
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : In VeChain Kit Version 2, use `useThor` instead of `useConnex` for contract interactions

Applied to files:

  • packages/vechain-kit/src/hooks/modals/index.ts
  • examples/playground/src/app/components/features/ConnectionInfo/ConnectionInfo.tsx
  • examples/playground/src/app/components/features/TransactionExamples/TransactionExamples.tsx
  • examples/playground/src/app/providers/VechainKitProviderWrapper.tsx
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript `as const` assertions for method names and `as `0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions

Applied to files:

  • examples/playground/src/app/constants.ts
  • examples/playground/tsconfig.json
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : For transaction building and sending, use the `useBuildTransaction` hook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action

Applied to files:

  • examples/playground/src/app/components/features/TransactionExamples/TransactionExamples.tsx
🧬 Code graph analysis (19)
examples/playground/src/app/theme/theme.tsx (4)
examples/playground/src/app/theme/card.ts (1)
  • cardTheme (49-54)
examples/playground/src/app/theme/button.ts (1)
  • ButtonStyle (3-28)
examples/playground/src/app/theme/modal.ts (1)
  • modalTheme (12-17)
examples/playground/src/app/theme/colors.ts (1)
  • themeColors (40-44)
examples/playground/src/app/components/features/DaoInfo/DaoInfo.tsx (4)
packages/vechain-kit/src/hooks/api/wallet/useCurrentAllocationsRoundId.ts (1)
  • useCurrentAllocationsRoundId (20-35)
packages/vechain-kit/src/hooks/api/wallet/useIsPerson.ts (1)
  • useIsPerson (24-53)
examples/playground/src/app/components/ui/CollapsibleCard/CollapsibleCard.tsx (1)
  • CollapsibleCard (24-68)
examples/homepage/src/app/components/features/DaoInfo/DaoInfo.tsx (1)
  • DaoInfo (12-58)
examples/playground/src/app/components/features/ConnectionInfo/ConnectionInfo.tsx (2)
examples/playground/src/app/components/ui/CollapsibleCard/CollapsibleCard.tsx (1)
  • CollapsibleCard (24-68)
examples/homepage/src/app/components/features/ConnectionInfo/ConnectionInfo.tsx (1)
  • ConnectionInfo (8-47)
examples/playground/src/app/components/features/FeaturesToTry/ThemeCard.tsx (1)
examples/homepage/src/app/components/features/FeaturesToTry/ThemeCard.tsx (1)
  • ThemeCard (14-62)
examples/playground/src/app/components/features/AccountInfo/AccountInfo.tsx (1)
examples/homepage/src/app/components/features/AccountInfo/AccountInfo.tsx (1)
  • AccountInfo (17-92)
examples/playground/src/app/components/features/SmartAccountInfo/SmartAccountInfo.tsx (1)
examples/playground/src/app/components/ui/CollapsibleCard/CollapsibleCard.tsx (1)
  • CollapsibleCard (24-68)
examples/playground/src/app/components/features/LanguageSelector/LanguageSelector.tsx (1)
examples/playground/i18n.ts (2)
  • supportedLanguages (13-13)
  • languageNames (26-34)
examples/playground/src/app/components/features/LoginUIControl/LoginUIControl.tsx (2)
packages/vechain-kit/src/hooks/modals/useConnectModal.tsx (1)
  • useConnectModal (7-25)
packages/vechain-kit/src/hooks/login/useLoginWithOAuth.ts (1)
  • useLoginWithOAuth (15-53)
examples/playground/src/app/components/features/Signing/SigningExample.tsx (2)
packages/vechain-kit/src/hooks/signing/useSignMessage.ts (1)
  • useSignMessage (22-88)
packages/vechain-kit/src/hooks/signing/useSignTypedData.ts (1)
  • useSignTypedData (26-114)
examples/playground/src/app/components/features/LoginToContinueBox/LoginToContinueBox.tsx (1)
packages/vechain-kit/src/hooks/modals/useConnectModal.tsx (1)
  • useConnectModal (7-25)
examples/playground/src/app/components/features/DataReading/DataReadingExample.tsx (4)
packages/vechain-kit/src/hooks/api/wallet/useGetB3trBalance.ts (1)
  • useGetB3trBalance (12-30)
packages/vechain-kit/src/hooks/api/wallet/useGetVot3Balance.ts (1)
  • useGetVot3Balance (12-30)
packages/vechain-kit/src/hooks/api/wallet/useGetTokenUsdPrice.ts (1)
  • useGetTokenUsdPrice (44-66)
packages/vechain-kit/src/hooks/api/wallet/useCurrentAllocationsRoundId.ts (1)
  • useCurrentAllocationsRoundId (20-35)
examples/playground/src/app/page.tsx (1)
examples/playground/src/app/pages/Home.tsx (1)
  • Home (22-105)
packages/vechain-kit/src/hooks/modals/useWalletModal.tsx (2)
packages/vechain-kit/src/providers/ModalProvider.tsx (1)
  • AccountModalOptions (21-23)
tests/e2e/models/DashboardPage.ts (1)
  • openAccountModal (68-72)
examples/playground/next.config.js (2)
examples/test-tailwind-vck/postcss.config.mjs (1)
  • config (1-3)
examples/test-tailwind-vck/eslint.config.mjs (1)
  • __filename (5-5)
examples/playground/src/app/pages/Home.tsx (7)
examples/playground/src/app/components/layout/Header/index.ts (1)
  • LanguageDropdown (1-1)
examples/playground/src/app/components/features/LoginToContinueBox/LoginToContinueBox.tsx (1)
  • LoginToContinueBox (7-39)
examples/playground/src/app/components/features/LoginUIControl/LoginUIControl.tsx (1)
  • LoginUIControl (14-256)
examples/playground/src/app/components/features/FeaturesToTry/FeaturesToTry.tsx (1)
  • FeaturesToTry (40-169)
examples/playground/src/app/components/features/TransactionExamples/TransactionExamples.tsx (1)
  • TransactionExamples (18-136)
examples/playground/src/app/components/features/Signing/SigningExample.tsx (1)
  • SigningExample (40-162)
examples/playground/src/app/components/features/DataReading/DataReadingExample.tsx (1)
  • DataReadingExample (14-77)
packages/vechain-kit/src/hooks/modals/useSettingsModal.tsx (1)
packages/vechain-kit/src/providers/ModalProvider.tsx (2)
  • useModal (66-72)
  • AccountModalOptions (21-23)
examples/homepage/src/app/components/features/LanguagesSection/LanguagesSection.tsx (1)
examples/playground/i18n.ts (2)
  • supportedLanguages (13-13)
  • languageNames (26-34)
examples/homepage/src/app/layout.tsx (3)
examples/homepage/src/app/components/ForceLightMode.tsx (1)
  • ForceLightMode (10-38)
examples/homepage/src/app/providers/VechainKitProviderWrapper.tsx (1)
  • VechainKitProviderWrapper (66-204)
examples/homepage/src/app/theme/theme.tsx (1)
  • theme (38-46)
examples/playground/src/app/providers/VechainKitProviderWrapper.tsx (3)
packages/vechain-kit/src/providers/VeChainKitProvider.tsx (1)
  • VeChainKitProvider (339-745)
examples/playground/i18n.ts (1)
  • resources (15-23)
examples/homepage/src/app/theme/theme.tsx (1)
  • theme (38-46)
🪛 ast-grep (0.40.0)
examples/homepage/src/app/layout.tsx

[warning] 125-125: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
examples/homepage/src/app/layout.tsx

[error] 126-126: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🪛 dotenv-linter (4.0.0)
examples/homepage/.env.example

[warning] 8-8: [UnorderedKey] The NEXT_PUBLIC_NETWORK_TYPE key should go before the NEXT_PUBLIC_PRIVY_APP_ID key

(UnorderedKey)


[warning] 11-11: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)

examples/playground/.env.example

[warning] 6-6: [UnorderedKey] The NEXT_PUBLIC_DELEGATOR_URL key should go before the NEXT_PUBLIC_PRIVY_APP_ID key

(UnorderedKey)


[warning] 8-8: [UnorderedKey] The NEXT_PUBLIC_NETWORK_TYPE key should go before the NEXT_PUBLIC_PRIVY_APP_ID key

(UnorderedKey)


[warning] 11-11: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)

⏰ 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 & Build
  • GitHub Check: e2e-test
  • GitHub Check: deploy
  • GitHub Check: build-size-check

Comment on lines +10 to +11
#For translations
OPENAI_API_KEY= No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix comment spacing and add blank line at end of file.

Line 10 has inconsistent comment formatting (#For instead of # For), and the file is missing a trailing blank line.

🔎 Proposed fix
-#For translations
+# For translations
 OPENAI_API_KEY=
+
📝 Committable suggestion

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

Suggested change
#For translations
OPENAI_API_KEY=
# For translations
OPENAI_API_KEY=
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 11-11: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)

🤖 Prompt for AI Agents
In examples/homepage/.env.example around lines 10-11, the comment has
inconsistent spacing and the file lacks a trailing newline; change the comment
from "#For translations" to "# For translations" (add a space after the hash)
and ensure the file ends with a single blank line (newline at EOF).

Comment on lines +22 to +23
"react": "^18",
"react-dom": "^18",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

React 19 is required for the App Router in Next.js 15.

This is not optional. The Next.js 15 App Router cannot be used with React 18, as Next.js 15 is built to utilize React 19 RC under the hood. If this playground uses the App Router, upgrading is mandatory, not recommended.

While React 19 has breaking changes such as ref being handled as a regular prop instead of direct element access, codemods and automated tools are provided to ease the migration process.

🤖 Prompt for AI Agents
In examples/playground/package.json around lines 22-23, the React dependency
versions are pinned to "^18" but Next.js 15's App Router requires React 19;
update "react" and "react-dom" to "^19" (or the required 19.x release
candidate/version your project targets), then run a fresh install
(npm/yarn/pnpm), run any provided React 19 codemods to adjust ref handling and
other breaking changes, run the app and test suite to surface regressions, and
fix any compilation/runtime errors or dependency mismatches introduced by the
upgrade.

useGetVot3Balance(address);
const { data: vetPrice, isLoading: isLoadingVetPrice } =
useGetTokenUsdPrice('VET');
const { data: vbdCurrentRoundId } = useCurrentAllocationsRoundId();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add loading state and fallback for VeBetterDAO current round.

The vbdCurrentRoundId lacks loading state handling and a fallback value, unlike the other data fields (B3TR balance, VOT3 balance, VET price). This creates an inconsistent UX where this field may appear empty during loading or when data is unavailable.

🔎 Suggested fix to add loading state and fallback
-    const { data: vbdCurrentRoundId } = useCurrentAllocationsRoundId();
+    const { data: vbdCurrentRoundId, isLoading: isLoadingRoundId } = useCurrentAllocationsRoundId();
                    <VStack mt={4} align="start" spacing={1}>
                        <Heading size="sm">{t('VeBetterDAO')}</Heading>
                        <Text fontWeight="bold">
-                            {t('Current round')}: {vbdCurrentRoundId}
+                            {t('Current round')}: {isLoadingRoundId ? 'Loading...' : vbdCurrentRoundId || 'N/A'}
                        </Text>
                    </VStack>

Also applies to: 67-72

Comment on lines +66 to +82
<HStack
spacing={3}
animation="bounce-left 1s infinite"
justifyContent="center"
alignItems="center"
transform="rotate(-10deg)"
sx={{
'@keyframes bounce-left': {
'0%, 100%': {
transform: 'rotate(0deg) translateX(0)',
},
'50%': {
transform:
'rotate(0deg) translateX(-5px)',
},
},
}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Static transform is overridden by animation.

The transform: "rotate(-10deg)" on line 71 is immediately overridden by the keyframe animation which sets rotate(0deg). If you want the hint to appear rotated, incorporate the rotation into the keyframes.

🔎 Fix to include rotation in animation
                         <HStack
                             spacing={3}
                             animation="bounce-left 1s infinite"
                             justifyContent="center"
                             alignItems="center"
-                            transform="rotate(-10deg)"
                             sx={{
                                 '@keyframes bounce-left': {
                                     '0%, 100%': {
-                                        transform: 'rotate(0deg) translateX(0)',
+                                        transform: 'rotate(-10deg) translateX(0)',
                                     },
                                     '50%': {
                                         transform:
-                                            'rotate(0deg) translateX(-5px)',
+                                            'rotate(-10deg) translateX(-5px)',
                                     },
                                 },
                             }}
                         >
📝 Committable suggestion

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

Suggested change
<HStack
spacing={3}
animation="bounce-left 1s infinite"
justifyContent="center"
alignItems="center"
transform="rotate(-10deg)"
sx={{
'@keyframes bounce-left': {
'0%, 100%': {
transform: 'rotate(0deg) translateX(0)',
},
'50%': {
transform:
'rotate(0deg) translateX(-5px)',
},
},
}}
<HStack
spacing={3}
animation="bounce-left 1s infinite"
justifyContent="center"
alignItems="center"
sx={{
'@keyframes bounce-left': {
'0%, 100%': {
transform: 'rotate(-10deg) translateX(0)',
},
'50%': {
transform:
'rotate(-10deg) translateX(-5px)',
},
},
}}
🤖 Prompt for AI Agents
In examples/playground/src/app/components/features/FeaturesToTry/FeatureCard.tsx
around lines 66-82, the static transform="rotate(-10deg)" is being overridden by
the keyframe animation (which sets rotate(0deg)); remove the standalone
transform and move the rotation into the keyframes so the element stays rotated
while animating. Update the '@keyframes bounce-left' rules so 0% and 100% use
transform: 'rotate(-10deg) translateX(0)' and the 50% rule uses transform:
'rotate(-10deg) translateX(-5px)' (or similar values), and remove the separate
transform prop on the HStack so the animation controls the rotation
consistently.

Comment on lines +136 to +141
{
title: t('Upgrade Smart Account'),
description: t('Upgrade your smart account to the latest version'),
icon: LuRefreshCw,
content: openUpgradeSmartAccountModal,
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find the FeaturesToTry component and examine the surrounding context
find . -type f -name "FeaturesToTry.tsx" | head -5

Repository: vechain/vechain-kit

Length of output: 224


🏁 Script executed:

# Read the FeaturesToTry.tsx file from the playground example to see context
cat -n ./examples/playground/src/app/components/features/FeaturesToTry/FeaturesToTry.tsx | head -160

Repository: vechain/vechain-kit

Length of output: 6984


🏁 Script executed:

# Search for useUpgradeSmartAccountModal hook definition
fd -t f "useUpgradeSmartAccountModal" --type f
rg -l "useUpgradeSmartAccountModal" --type ts --type tsx | grep -v node_modules | head -10

Repository: vechain/vechain-kit

Length of output: 177


🏁 Script executed:

# Read the hook definition
cat -n ./packages/vechain-kit/src/hooks/modals/useUpgradeSmartAccountModal.tsx

Repository: vechain/vechain-kit

Length of output: 796


🏁 Script executed:

# Check what UpgradeSmartAccountModalStyle is
rg -n "UpgradeSmartAccountModalStyle" --type ts -B 5 -A 5 | head -40

Repository: vechain/vechain-kit

Length of output: 3478


🏁 Script executed:

# Find the UpgradeSmartAccountModalStyle definition
rg -n "type UpgradeSmartAccountModalStyle|interface UpgradeSmartAccountModalStyle" --type ts -A 10

Repository: vechain/vechain-kit

Length of output: 1331


🏁 Script executed:

# Check what isolatedView is and which modals support it
rg -n "isolatedView" --type ts -B 2 -A 2 | head -60

Repository: vechain/vechain-kit

Length of output: 5586


🏁 Script executed:

# Check what options the other modal hooks accept (e.g., useWalletModal, useProfileModal)
rg -n "useWalletModal|useProfileModal|useSendTokenModal" --type ts -A 5 | head -80

Repository: vechain/vechain-kit

Length of output: 7738


🏁 Script executed:

# Check the hook definitions for useProfileModal and useSendTokenModal
cat -n ./packages/vechain-kit/src/hooks/modals/useProfileModal.tsx

Repository: vechain/vechain-kit

Length of output: 844


🏁 Script executed:

# Let's also check useSendTokenModal to confirm the pattern
cat -n ./packages/vechain-kit/src/hooks/modals/useSendTokenModal.tsx

Repository: vechain/vechain-kit

Length of output: 1185


useUpgradeSmartAccountModal doesn't support isolatedView option.

Unlike other modals (wallet, profile, send token, etc.) that pass { isolatedView: true }, the upgrade smart account modal uses a different options type (UpgradeSmartAccountModalStyle) that only supports accentColor and modalSize. To maintain consistency with other modals, consider adding isolatedView support to UpgradeSmartAccountModalStyle and its hook.

🤖 Prompt for AI Agents
In
examples/playground/src/app/components/features/FeaturesToTry/FeaturesToTry.tsx
around lines 136–141, the upgrade smart account modal is opened via
openUpgradeSmartAccountModal but its options type
(UpgradeSmartAccountModalStyle) lacks the isolatedView flag used elsewhere;
update the UpgradeSmartAccountModalStyle type to include optional isolatedView?:
boolean, update the useUpgradeSmartAccountModal hook signature to accept and
forward isolatedView in its options, and ensure the underlying modal component
consumes isolatedView to render the same isolated view behavior (e.g., when
calling openUpgradeSmartAccountModal pass { isolatedView: true } like other
modals and propagate the flag through types and handler code).

Comment on lines +43 to +61
<HStack
justify="space-between"
align="center"
cursor={'pointer'}
onClick={() => setIsOpen(!isOpen)}
>
<HStack spacing={2}>
{icon && <Icon as={icon} boxSize={6} />}
<Heading size="sm" textAlign="center">
{title}
</Heading>
</HStack>
<IconButton
aria-label={isOpen ? 'Collapse' : 'Expand'}
icon={isOpen ? <LuChevronUp /> : <LuChevronDown />}
variant="ghost"
size="sm"
/>
</HStack>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix event bubbling causing double-toggle.

The IconButton is nested inside the HStack that has an onClick handler. When the IconButton is clicked, it triggers the toggle, and then the event bubbles up to the HStack's onClick, toggling again. This results in no visual change, making the button appear broken.

🔎 Proposed fix to prevent event bubbling
                <HStack
                    justify="space-between"
                    align="center"
                    cursor={'pointer'}
                    onClick={() => setIsOpen(!isOpen)}
                >
                    <HStack spacing={2}>
                        {icon && <Icon as={icon} boxSize={6} />}
                        <Heading size="sm" textAlign="center">
                            {title}
                        </Heading>
                    </HStack>
                    <IconButton
                        aria-label={isOpen ? 'Collapse' : 'Expand'}
                        icon={isOpen ? <LuChevronUp /> : <LuChevronDown />}
                        variant="ghost"
                        size="sm"
+                       onClick={(e) => {
+                           e.stopPropagation();
+                           setIsOpen(!isOpen);
+                       }}
                    />
                </HStack>
📝 Committable suggestion

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

Suggested change
<HStack
justify="space-between"
align="center"
cursor={'pointer'}
onClick={() => setIsOpen(!isOpen)}
>
<HStack spacing={2}>
{icon && <Icon as={icon} boxSize={6} />}
<Heading size="sm" textAlign="center">
{title}
</Heading>
</HStack>
<IconButton
aria-label={isOpen ? 'Collapse' : 'Expand'}
icon={isOpen ? <LuChevronUp /> : <LuChevronDown />}
variant="ghost"
size="sm"
/>
</HStack>
<HStack
justify="space-between"
align="center"
cursor={'pointer'}
onClick={() => setIsOpen(!isOpen)}
>
<HStack spacing={2}>
{icon && <Icon as={icon} boxSize={6} />}
<Heading size="sm" textAlign="center">
{title}
</Heading>
</HStack>
<IconButton
aria-label={isOpen ? 'Collapse' : 'Expand'}
icon={isOpen ? <LuChevronUp /> : <LuChevronDown />}
variant="ghost"
size="sm"
onClick={(e) => {
e.stopPropagation();
setIsOpen(!isOpen);
}}
/>
</HStack>
🤖 Prompt for AI Agents
In examples/playground/src/app/components/ui/CollapsibleCard/CollapsibleCard.tsx
around lines 43 to 61, the IconButton click bubbles up to the HStack' onClick
causing a double-toggle; prevent this by adding an onClick handler on the
IconButton that calls event.stopPropagation() and then toggles the open state
(e.g., call setIsOpen(!isOpen)) so clicking the icon only triggers a single
toggle and does not propagate to the parent HStack.

"Manage your settings and your preferences": "Manage your settings and your preferences",
"Manage your wallet and your assets": "Manage your wallet and your assets",
"Message Signing Examples": "Message Signing Examples",
"Note: The modal variant is the default login button variant. You can pass an additional description and Image to the modal when configuring you the VeChainKitProvider.": "Note: The modal variant is the default login button variant. You can pass an additional description and Image to the modal when configuring you the VeChainKitProvider.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix grammatical error in translation string.

The text contains an extra word "you" that makes the sentence grammatically incorrect.

🔎 Proposed fix
-  "Note: The modal variant is the default login button variant. You can pass an additional description and Image to the modal when configuring you the VeChainKitProvider.": "Note: The modal variant is the default login button variant. You can pass an additional description and Image to the modal when configuring you the VeChainKitProvider.",
+  "Note: The modal variant is the default login button variant. You can pass an additional description and Image to the modal when configuring the VeChainKitProvider.": "Note: The modal variant is the default login button variant. You can pass an additional description and Image to the modal when configuring the VeChainKitProvider.",
📝 Committable suggestion

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

Suggested change
"Note: The modal variant is the default login button variant. You can pass an additional description and Image to the modal when configuring you the VeChainKitProvider.": "Note: The modal variant is the default login button variant. You can pass an additional description and Image to the modal when configuring you the VeChainKitProvider.",
"Note: The modal variant is the default login button variant. You can pass an additional description and Image to the modal when configuring the VeChainKitProvider.": "Note: The modal variant is the default login button variant. You can pass an additional description and Image to the modal when configuring the VeChainKitProvider.",
🤖 Prompt for AI Agents
In examples/playground/src/app/languages/en.json around line 24, the translation
string contains an extra word "you" making it grammatically incorrect; update
the sentence to remove the extraneous "you" so it reads "...when configuring the
VeChainKitProvider." (i.e., replace "when configuring you the
VeChainKitProvider" with "when configuring the VeChainKitProvider").

Comment on lines +80 to +110
useEffect(() => {
// Sync VeChainKit language prop with localStorage changes
const handleStorageChange = (e: StorageEvent) => {
if (e.key === 'i18nextLng' && e.newValue) {
setKitLanguage(e.newValue);
}
};

const storedLanguage =
typeof window !== 'undefined'
? localStorage.getItem('i18nextLng')
: null;
if (storedLanguage) {
setKitLanguage(storedLanguage);
}

window.addEventListener('storage', handleStorageChange);

// Poll for changes
const interval = setInterval(() => {
const stored = localStorage.getItem('i18nextLng');
if (stored && stored !== kitLanguage) {
setKitLanguage(stored);
}
}, 500);

return () => {
window.removeEventListener('storage', handleStorageChange);
clearInterval(interval);
};
}, [kitLanguage]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential unnecessary re-renders from kitLanguage dependency.

The useEffect on line 80 includes kitLanguage in its dependency array, but also calls setKitLanguage inside. When stored !== kitLanguage, this triggers a state update, which re-runs the effect, potentially causing repeated updates. Consider using a ref to track the language or removing kitLanguage from dependencies.

🔎 Proposed fix using functional update
     useEffect(() => {
         // Sync VeChainKit language prop with localStorage changes
         const handleStorageChange = (e: StorageEvent) => {
             if (e.key === 'i18nextLng' && e.newValue) {
                 setKitLanguage(e.newValue);
             }
         };

         const storedLanguage =
             typeof window !== 'undefined'
                 ? localStorage.getItem('i18nextLng')
                 : null;
         if (storedLanguage) {
-            setKitLanguage(storedLanguage);
+            setKitLanguage(prev => prev !== storedLanguage ? storedLanguage : prev);
         }

         window.addEventListener('storage', handleStorageChange);

         // Poll for changes
         const interval = setInterval(() => {
             const stored = localStorage.getItem('i18nextLng');
-            if (stored && stored !== kitLanguage) {
-                setKitLanguage(stored);
-            }
+            setKitLanguage(prev => (stored && stored !== prev) ? stored : prev);
         }, 500);

         return () => {
             window.removeEventListener('storage', handleStorageChange);
             clearInterval(interval);
         };
-    }, [kitLanguage]);
+    }, []);
🤖 Prompt for AI Agents
In examples/playground/src/app/providers/VechainKitProviderWrapper.tsx around
lines 80 to 110, the effect depends on kitLanguage and calls setKitLanguage
which can cause unnecessary re-renders/loops; change the effect to not depend on
kitLanguage by removing it from the dependency array and instead compare against
a ref (e.g., currentKitLanguageRef) or use the functional state updater when
setting language so the effect reads from localStorage and updates state only
when different from the ref/current state without re-triggering the effect; keep
the same storage event listener, interval polling and cleanup logic but
reference and update the ref when you call setKitLanguage.

Comment on lines +1 to +17
import { createMultiStyleConfigHelpers } from '@chakra-ui/react';

const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(['modal']);

// define custom styles for funky variant
const variants = {
base: definePartsStyle({}),
};

// export variants in the component theme
export const modalTheme = defineMultiStyleConfig({
variants,
defaultProps: {
variant: 'base',
},
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use modalAnatomy from Chakra UI for proper modal theming.

Chakra UI modals have multiple themeable parts (overlay, dialog, header, body, footer, closeButton) that should be styled using modalAnatomy imported from '@chakra-ui/anatomy' with createMultiStyleConfigHelpers(parts.keys). Replace the custom ['modal'] array with the proper anatomy approach to enable complete styling flexibility for all modal subcomponents.

-import { createMultiStyleConfigHelpers } from '@chakra-ui/react';
+import { createMultiStyleConfigHelpers } from '@chakra-ui/react';
+import { modalAnatomy } from '@chakra-ui/anatomy';

 const { definePartsStyle, defineMultiStyleConfig } =
-    createMultiStyleConfigHelpers(['modal']);
+    createMultiStyleConfigHelpers(modalAnatomy.keys);

 // define custom styles for funky variant
 const variants = {
-    base: definePartsStyle({}),
+    base: definePartsStyle({
+        dialog: {
+            // Add your custom modal dialog styles here
+        },
+        overlay: {
+            // Add your custom overlay styles here
+        },
+    }),
 };

 // export variants in the component theme
 export const modalTheme = defineMultiStyleConfig({
     variants,
     defaultProps: {
         variant: 'base',
     },
 });
📝 Committable suggestion

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

Suggested change
import { createMultiStyleConfigHelpers } from '@chakra-ui/react';
const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(['modal']);
// define custom styles for funky variant
const variants = {
base: definePartsStyle({}),
};
// export variants in the component theme
export const modalTheme = defineMultiStyleConfig({
variants,
defaultProps: {
variant: 'base',
},
});
import { createMultiStyleConfigHelpers } from '@chakra-ui/react';
import { modalAnatomy } from '@chakra-ui/anatomy';
const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(modalAnatomy.keys);
// define custom styles for funky variant
const variants = {
base: definePartsStyle({
dialog: {
// Add your custom modal dialog styles here
},
overlay: {
// Add your custom overlay styles here
},
}),
};
// export variants in the component theme
export const modalTheme = defineMultiStyleConfig({
variants,
defaultProps: {
variant: 'base',
},
});
🤖 Prompt for AI Agents
In examples/playground/src/app/theme/modal.ts around lines 1 to 17, the theme
helper is currently created with a hard-coded ['modal'] parts array which
prevents styling individual modal parts; import modalAnatomy from
'@chakra-ui/anatomy' and call createMultiStyleConfigHelpers(modalAnatomy.keys)
instead, then keep using definePartsStyle and defineMultiStyleConfig to define
variants for the modal subcomponents (overlay, dialog, header, body, footer,
closeButton) so all anatomy parts can be themed correctly.

Comment on lines +21 to +25
//@ts-ignore
fonts: {
heading: `"Satoshi", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif`,
body: `"Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif`,
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove //@ts-ignore and properly type the fonts configuration.

The //@ts-ignore directive suppresses TypeScript errors without addressing the underlying type issue. Chakra UI's theme expects fonts to be typed correctly.

🔎 Proposed fix to properly type fonts
-    //@ts-ignore
     fonts: {
-        heading: `"Satoshi", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif`,
-        body: `"Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif`,
+        heading: `"Satoshi", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif` as const,
+        body: `"Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif` as const,
     },

Alternatively, if the type error persists, define a properly typed fonts object:

const fonts = {
    heading: `"Satoshi", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif`,
    body: `"Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif`,
} as const;

const exampleTheme = {
    // ...
    fonts,
};
🤖 Prompt for AI Agents
In examples/playground/src/app/theme/theme.tsx around lines 21 to 25, remove the
`//@ts-ignore` and fix the typing for the fonts config so it matches Chakra UI's
theme expectations: declare a strongly-typed fonts object (e.g., const fonts = {
heading: string, body: string } as const or typed to Chakra's Fonts type) and
use that object in the theme export; ensure imports include the correct Chakra
types if needed and replace the ignored block with the typed fonts reference so
TypeScript errors are resolved without suppressing them.

@victorkl400 victorkl400 mentioned this pull request Dec 22, 2025
4 tasks
@victorkl400 victorkl400 requested a review from Copilot December 22, 2025 11:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a comprehensive redesign and enhancement of the VeChain Kit package, featuring a new homepage, multilingual support across 7 languages, wallet switching functionality, and UX improvements. The changes include translation infrastructure, UI component updates for better accessibility and user experience, and new modal navigation flows.

Key Changes

  • Added comprehensive multilingual support with translation files for 7 languages (English, German, Italian, French, Spanish, Chinese, Japanese)
  • Implemented wallet switching functionality for in-app browser environments
  • Redesigned homepage with new sections (hero, testimonials, app showcase, FAQ) and updated branding to "VeKit"
  • Enhanced account modal navigation flow, moving from main-centric to profile-centric approach
  • Added translation tooling infrastructure and configuration

Reviewed changes

Copilot reviewed 139 out of 167 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/vechain-kit/src/languages/*.json Updated translation strings across all supported languages to reflect new features and UX changes
packages/vechain-kit/src/hooks/modals/useWalletModal.tsx Added support for passing account modal options to enable flexible modal opening
packages/vechain-kit/src/hooks/modals/useSettingsModal.tsx New hook for managing settings modal state independently
packages/vechain-kit/src/hooks/api/wallet/useSwitchWallet.ts New hook implementing wallet switching logic for VeWorld in-app browser
packages/vechain-kit/src/components/common/BaseBottomSheet.tsx Fixed accessibility issue by replacing VisuallyHidden with custom styling for drawer title
packages/vechain-kit/src/components/common/AddressDisplay.tsx Enhanced address display with edit functionality and improved copy-to-clipboard UX
packages/vechain-kit/src/components/WalletButton/WalletButton.tsx Added customizable label prop for wallet button text
packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx Redesigned profile content with settings button and wallet switching support
packages/vechain-kit/src/components/AccountModal/AccountModal.tsx Changed default initial content from 'main' to 'profile' and adjusted modal heights
packages/vechain-kit/package.json Updated dapp-kit-react dependency versions
examples/playground/* New playground example with comprehensive demos and multilingual support
examples/homepage/* Complete homepage redesign with new sections, forced light mode, and updated branding

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +27 to +29
} catch {
// Silently handle errors - wallet state will update automatically on success
// Errors are handled by dapp-kit internally

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

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

The catch block silently swallows all errors without any error handling or logging. Even if dapp-kit handles errors internally, it's better to at least log the error for debugging purposes or provide minimal error feedback. Consider adding console.error or similar logging mechanism.

Suggested change
} catch {
// Silently handle errors - wallet state will update automatically on success
// Errors are handled by dapp-kit internally
} catch (error) {
// Log errors for debugging while relying on dapp-kit for internal handling
console.error('Failed to switch wallet via dapp-kit:', error);

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +67
const handleDomainEdit = () => {
if (!setCurrentContent) return;

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

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

The handleDomainEdit function is defined unconditionally but only used when setCurrentContent is provided. Consider moving this function definition inside a conditional or useCallback with proper dependencies to avoid creating unnecessary function instances.

Copilot uses AI. Check for mistakes.
Comment thread examples/homepage/src/app/layout.tsx Outdated
Comment thread examples/homepage/i18n.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
examples/homepage/src/app/layout.tsx (2)

124-146: Safe usage of dangerouslySetInnerHTML, but part of redundant enforcement.

While static analysis flags the dangerouslySetInnerHTML usage, it's safe here since the content is a static string with no user input and runs pre-hydration to prevent color mode flashing.

However, as noted in the review of lines 19-33, this inline script is part of the redundant light mode enforcement strategy that should be simplified.


19-33: Redundant light mode enforcement remains.

As noted in previous reviews, the combination of ForceLightMode component, ColorModeScript (line 124), and the inline localStorage-clearing script (lines 125-146) represents three separate mechanisms to enforce light mode. The theme configuration already sets initialColorMode: 'light' and useSystemColorMode: false, which should be sufficient.

Consider removing the ForceLightMode component and the inline script, relying solely on the theme config and ColorModeScript.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f0e2bc5 and 42dc0ec.

📒 Files selected for processing (2)
  • examples/homepage/i18n.ts
  • examples/homepage/src/app/layout.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/migration-guide-to-v2.mdc)

**/*.{ts,tsx}: In VeChain Kit Version 2, use useThor instead of useConnex for contract interactions
For single contract read operations, use the useCallClause hook with the pattern: import dependencies, define ABI and method as const, create query key function using getCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use the executeMultipleClausesCall utility wrapped in a useQuery hook with the pattern: define query key function, use executeMultipleClausesCall in queryFn mapping items to clause objects, and transform results
For transaction building and sending, use the useBuildTransaction hook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Always provide an arguments array for contract calls, even when no parameters are required - use an empty array for parameter-less functions to enable TypeScript type checking
Always conditionally enable queries using the enabled property to prevent unnecessary contract calls, checking for all required parameters: enabled: !!requiredParam && !!otherRequiredParam
Use the select option in useCallClause or transform data in queryFn to handle data transformation, particularly for converting BigInt values to strings and normalizing contract return data
Maintain consistent query key patterns: use getCallClauseQueryKeyWithArgs for contract calls with arguments and getCallClauseQueryKey for calls without arguments to ensure proper caching and invalidation
Use TypeScript as const assertions for method names and as 0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions

Files:

  • examples/homepage/src/app/layout.tsx
  • examples/homepage/i18n.ts
🧬 Code graph analysis (1)
examples/homepage/src/app/layout.tsx (3)
examples/homepage/src/app/components/ForceLightMode.tsx (1)
  • ForceLightMode (10-38)
examples/homepage/src/app/providers/VechainKitProviderWrapper.tsx (1)
  • VechainKitProviderWrapper (66-204)
examples/homepage/src/app/theme/theme.tsx (1)
  • theme (38-46)
🪛 ast-grep (0.40.0)
examples/homepage/src/app/layout.tsx

[warning] 125-125: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
examples/homepage/src/app/layout.tsx

[error] 126-126: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

⏰ 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 & Build
  • GitHub Check: e2e-test
  • GitHub Check: deploy
  • GitHub Check: build-size-check
🔇 Additional comments (4)
examples/homepage/i18n.ts (1)

61-78: LGTM: i18n initialization is well-structured.

The initialization properly chains the custom language detector with React i18next and includes appropriate configuration options. Once the syntax error in customLanguageDetector is fixed, this will work correctly.

examples/homepage/src/app/layout.tsx (3)

3-8: LGTM: Imports properly support new i18n and theming features.

The added imports for ColorModeScript, the updated theme, and i18n initialization are all correctly utilized in the layout.


50-107: LGTM: Metadata updates are consistent and complete.

The branding changes to "VeKit" and updated descriptions are consistently applied across document title, Open Graph, and Twitter metadata. The switch to remote S3-hosted images is appropriate for production use.


154-154: LGTM: Theme provider correctly uses the updated theme.

The switch from darkTheme to theme aligns with the import changes and the theme configuration shown in the relevant snippets, which properly sets light mode defaults.

Comment thread examples/homepage/i18n.ts
Comment on lines +36 to +59
// Custom language detector that checks localStorage first, then browser
const customLanguageDetector = {
name: 'customDetector',
lookup: () => {
// Check localStorage first (for persistence across page refreshes)
lookup: () => {
if (typeof window === 'undefined') return 'en';

const stored = localStorage.getItem('i18nextLng');
const browser = navigator.language?.split('-')[0];

if (stored && supportedLanguages.includes(stored)) return stored;
if (browser && supportedLanguages.includes(browser)) return browser;
}


return 'en'; // fallback
},
cacheUserLanguage: (lng: string) => {
if (typeof window !== 'undefined') {
localStorage.setItem('i18nextLng', lng);
}
},
});
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical syntax error: duplicate lookup property declaration.

The customLanguageDetector object has two lookup property declarations (line 39 and line 41), which creates invalid JavaScript syntax. This will cause a parsing error and prevent the application from running.

Remove the duplicate declaration and keep only the inner implementation.

🔎 Proposed fix
 // Custom language detector that checks localStorage first, then browser
 const customLanguageDetector = {
     name: 'customDetector',
-    lookup: () => {
-        // Check localStorage first (for persistence across page refreshes)
-lookup: () => {
-    if (typeof window === 'undefined') return 'en';
-
-    const stored = localStorage.getItem('i18nextLng');
-    const browser = navigator.language?.split('-')[0];
-
-    if (stored && supportedLanguages.includes(stored)) return stored;
-    if (browser && supportedLanguages.includes(browser)) return browser;
-}
-
-
-        return 'en'; // fallback
+    lookup: () => {
+        if (typeof window === 'undefined') return 'en';
+
+        const stored = localStorage.getItem('i18nextLng');
+        const browser = navigator.language?.split('-')[0];
+
+        if (stored && supportedLanguages.includes(stored)) return stored;
+        if (browser && supportedLanguages.includes(browser)) return browser;
+        
+        return 'en'; // fallback
     },
     cacheUserLanguage: (lng: string) => {
         if (typeof window !== 'undefined') {
             localStorage.setItem('i18nextLng', lng);
         }
     },
 };
📝 Committable suggestion

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

Suggested change
// Custom language detector that checks localStorage first, then browser
const customLanguageDetector = {
name: 'customDetector',
lookup: () => {
// Check localStorage first (for persistence across page refreshes)
lookup: () => {
if (typeof window === 'undefined') return 'en';
const stored = localStorage.getItem('i18nextLng');
const browser = navigator.language?.split('-')[0];
if (stored && supportedLanguages.includes(stored)) return stored;
if (browser && supportedLanguages.includes(browser)) return browser;
}
return 'en'; // fallback
},
cacheUserLanguage: (lng: string) => {
if (typeof window !== 'undefined') {
localStorage.setItem('i18nextLng', lng);
}
},
});
};
// Custom language detector that checks localStorage first, then browser
const customLanguageDetector = {
name: 'customDetector',
lookup: () => {
if (typeof window === 'undefined') return 'en';
const stored = localStorage.getItem('i18nextLng');
const browser = navigator.language?.split('-')[0];
if (stored && supportedLanguages.includes(stored)) return stored;
if (browser && supportedLanguages.includes(browser)) return browser;
return 'en'; // fallback
},
cacheUserLanguage: (lng: string) => {
if (typeof window !== 'undefined') {
localStorage.setItem('i18nextLng', lng);
}
},
};
🤖 Prompt for AI Agents
In examples/homepage/i18n.ts around lines 36 to 59, the customLanguageDetector
object contains a duplicated lookup property which causes a syntax error; remove
the outer/duplicate lookup declaration and keep a single lookup function with
the inner implementation (check for window, read localStorage i18nextLng, derive
browser language from navigator.language, validate against supportedLanguages,
return stored/browser or 'en' fallback) and ensure cacheUserLanguage remains
unchanged.

@Agilulfo1820 Agilulfo1820 merged commit 5d98ffd into main Dec 22, 2025
8 checks passed
@Agilulfo1820 Agilulfo1820 deleted the feat/new-home branch December 22, 2025 11:57
This was referenced Dec 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants